diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2278,15 +2278,28 @@ // If this block is deactivating a fork, we move all mempool transactions // in front of disconnectpool for reprocessing in a future // updateMempoolForReorg call - if (pindexDelete->pprev != nullptr && - GetNextBlockScriptFlags(consensusParams, pindexDelete) != - GetNextBlockScriptFlags(consensusParams, pindexDelete->pprev)) { - LogPrint(BCLog::MEMPOOL, - "Disconnecting mempool due to rewind of upgrade block\n"); - if (disconnectpool) { - disconnectpool->importMempool(g_mempool); + if (pindexDelete->pprev != nullptr) { + bool changes = false; + changes |= + (GetNextBlockScriptFlags(consensusParams, pindexDelete) != + GetNextBlockScriptFlags(consensusParams, pindexDelete->pprev)); + + changes |= + (GetDefaultAncestorLimit(consensusParams, pindexDelete) != + GetDefaultAncestorLimit(consensusParams, pindexDelete->pprev)); + + changes |= + (GetDefaultDecendantLimit(consensusParams, pindexDelete) != + GetDefaultDecendantLimit(consensusParams, pindexDelete->pprev)); + + if (changes) { + LogPrint(BCLog::MEMPOOL, + "Disconnecting mempool due to rewind of upgrade block\n"); + if (disconnectpool) { + disconnectpool->importMempool(g_mempool); + } + g_mempool.clear(); } - g_mempool.clear(); } if (disconnectpool) { diff --git a/test/functional/abc-phonos-disconnectpool.py b/test/functional/abc-phonos-disconnectpool.py new file mode 100755 --- /dev/null +++ b/test/functional/abc-phonos-disconnectpool.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# Copyright (c) 2014-2019 The Bitcoin Core developers +# Copyright (c) 2020 The Bitcoin developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +"""Test that long chains are trimmed from mempool on phonos-rollback""" + +from decimal import Decimal + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.txtools import pad_raw_tx +from test_framework.util import ( + assert_equal, + satoshi_round, +) + +# Phonos dummy activation time +ACTIVATION_TIME = 2000000000 + +# Replay protection time needs to be moved beyond phonos activation +REPLAY_PROTECTION_TIME = ACTIVATION_TIME * 2 + +PREFORK_MAX_ANCESTORS = 25 +POSTFORK_MAX_ANCESTORS = 50 + + +class MempoolPhonosTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + + self.extra_args = [[ + "-phononactivationtime={}".format(ACTIVATION_TIME), + "-replayprotectionactivationtime={}".format( + REPLAY_PROTECTION_TIME), + "-maxorphantx=1000"]] + + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + + # Build a transaction that spends parent_txid:vout + # Return amount sent + def chain_transaction(self, node, parent_txid, vout, value): + fee = Decimal(0.0001) + send_value = satoshi_round(value - fee) + inputs = [{'txid': parent_txid, 'vout': vout}] + outputs = { + node.getnewaddress(): send_value + } + rawtx = node.createrawtransaction(inputs, outputs) + signedtx = node.signrawtransactionwithwallet(rawtx) + txid = node.sendrawtransaction(pad_raw_tx(signedtx['hex'])) + fulltx = node.getrawtransaction(txid, 1) + # make sure we didn't generate a change output + assert len(fulltx['vout']) == 1 + return (txid, send_value) + + def run_test(self): + # Mine some pre-fork blocks + node = self.nodes[0] + node.setmocktime(ACTIVATION_TIME - 10000) + node.generate(101) + + pre_fork_height = node.getblockcount() + + # Activate phonos + node.setmocktime(ACTIVATION_TIME) + self.nodes[0].generate(11) + + # Mine a POSTFORK_MAX_ANCESTORS length chain + utxo = self.nodes[0].listunspent(1)[0] + txid = utxo['txid'] + value = utxo['amount'] + + for i in range(POSTFORK_MAX_ANCESTORS): + (txid, sent_value) = self.chain_transaction( + self.nodes[0], txid, 0, value) + value = sent_value + + assert_equal(len(node.getrawmempool(True)), POSTFORK_MAX_ANCESTORS) + + # Rollback the fork + node.invalidateblock(node.getblockhash(pre_fork_height + 1)) + + # Assert that the mempool was trimmed to PREFORK_MAX_ANCESTORS + assert_equal(len(node.getrawmempool(True)), PREFORK_MAX_ANCESTORS) + + +if __name__ == '__main__': + MempoolPhonosTest().main()