Remove mempool transactions which commit to an unmineable branch ID
This commit is contained in:
@@ -29,6 +29,7 @@ testScripts=(
|
|||||||
'mempool_spendcoinbase.py'
|
'mempool_spendcoinbase.py'
|
||||||
'mempool_reorg.py'
|
'mempool_reorg.py'
|
||||||
'mempool_tx_input_limit.py'
|
'mempool_tx_input_limit.py'
|
||||||
|
'mempool_nu_activation.py'
|
||||||
'httpbasics.py'
|
'httpbasics.py'
|
||||||
'zapwallettxes.py'
|
'zapwallettxes.py'
|
||||||
'proxy_test.py'
|
'proxy_test.py'
|
||||||
|
|||||||
119
qa/rpc-tests/mempool_nu_activation.py
Executable file
119
qa/rpc-tests/mempool_nu_activation.py
Executable file
@@ -0,0 +1,119 @@
|
|||||||
|
#!/usr/bin/env python2
|
||||||
|
# Copyright (c) 2018 The Zcash developers
|
||||||
|
# Distributed under the MIT software license, see the accompanying
|
||||||
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
|
from test_framework.util import assert_equal, initialize_chain_clean, \
|
||||||
|
start_node, connect_nodes, wait_and_assert_operationid_status
|
||||||
|
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
# Test mempool behaviour around network upgrade activation
|
||||||
|
class MempoolUpgradeActivationTest(BitcoinTestFramework):
|
||||||
|
|
||||||
|
alert_filename = None # Set by setup_network
|
||||||
|
|
||||||
|
def setup_network(self):
|
||||||
|
args = ["-checkmempool", "-debug=mempool", "-blockmaxsize=4000", "-nuparams=5ba81b19:200"]
|
||||||
|
self.nodes = []
|
||||||
|
self.nodes.append(start_node(0, self.options.tmpdir, args))
|
||||||
|
self.nodes.append(start_node(1, self.options.tmpdir, args))
|
||||||
|
connect_nodes(self.nodes[1], 0)
|
||||||
|
self.is_network_split = False
|
||||||
|
self.sync_all
|
||||||
|
|
||||||
|
def setup_chain(self):
|
||||||
|
print "Initializing test directory "+self.options.tmpdir
|
||||||
|
initialize_chain_clean(self.options.tmpdir, 2)
|
||||||
|
|
||||||
|
def run_test(self):
|
||||||
|
self.nodes[1].generate(100)
|
||||||
|
self.sync_all()
|
||||||
|
|
||||||
|
# Mine 97 blocks. After this, nodes[1] blocks
|
||||||
|
# 1 to 97 are spend-able.
|
||||||
|
self.nodes[0].generate(97)
|
||||||
|
self.sync_all()
|
||||||
|
|
||||||
|
# Shield some ZEC
|
||||||
|
node1_taddr = self.nodes[1].getnewaddress()
|
||||||
|
node0_zaddr = self.nodes[0].z_getnewaddress()
|
||||||
|
recipients = [{'address': node0_zaddr, 'amount': Decimal('10')}]
|
||||||
|
myopid = self.nodes[1].z_sendmany(node1_taddr, recipients, 1, Decimal('0'))
|
||||||
|
print wait_and_assert_operationid_status(self.nodes[1], myopid)
|
||||||
|
self.sync_all()
|
||||||
|
|
||||||
|
# Mine block 198. After this, the mempool expects
|
||||||
|
# block 199, which is the last Sprout block.
|
||||||
|
self.nodes[0].generate(1)
|
||||||
|
self.sync_all()
|
||||||
|
|
||||||
|
# Mempool should be empty.
|
||||||
|
assert_equal(set(self.nodes[0].getrawmempool()), set())
|
||||||
|
|
||||||
|
# Check node 0 shielded balance
|
||||||
|
assert_equal(self.nodes[0].z_getbalance(node0_zaddr), Decimal('10'))
|
||||||
|
|
||||||
|
# Fill the mempool with twice as many transactions as can fit into blocks
|
||||||
|
node0_taddr = self.nodes[0].getnewaddress()
|
||||||
|
sprout_txids = []
|
||||||
|
while self.nodes[1].getmempoolinfo()['bytes'] < 2 * 4000:
|
||||||
|
sprout_txids.append(self.nodes[1].sendtoaddress(node0_taddr, Decimal('0.001')))
|
||||||
|
self.sync_all()
|
||||||
|
|
||||||
|
# Spends should be in the mempool
|
||||||
|
sprout_mempool = set(self.nodes[0].getrawmempool())
|
||||||
|
assert_equal(sprout_mempool, set(sprout_txids))
|
||||||
|
|
||||||
|
# Mine block 199. After this, the mempool expects
|
||||||
|
# block 200, which is the first Overwinter block.
|
||||||
|
self.nodes[0].generate(1)
|
||||||
|
self.sync_all()
|
||||||
|
|
||||||
|
# mempool should be empty.
|
||||||
|
assert_equal(set(self.nodes[0].getrawmempool()), set())
|
||||||
|
|
||||||
|
# Block 199 should contain a subset of the original mempool
|
||||||
|
# (with all other transactions having been dropped)
|
||||||
|
block_txids = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['tx']
|
||||||
|
assert(len(block_txids) < len(sprout_txids))
|
||||||
|
for txid in block_txids[1:]: # Exclude coinbase
|
||||||
|
assert(txid in sprout_txids)
|
||||||
|
|
||||||
|
# Create some transparent Overwinter transactions
|
||||||
|
overwinter_txids = [self.nodes[1].sendtoaddress(node0_taddr, Decimal('0.001')) for i in range(10)]
|
||||||
|
self.sync_all()
|
||||||
|
|
||||||
|
# Create a shielded Overwinter transaction
|
||||||
|
recipients = [{'address': node0_taddr, 'amount': Decimal('10')}]
|
||||||
|
myopid = self.nodes[0].z_sendmany(node0_zaddr, recipients, 1, Decimal('0'))
|
||||||
|
shielded = wait_and_assert_operationid_status(self.nodes[0], myopid)
|
||||||
|
assert(shielded != None)
|
||||||
|
overwinter_txids.append(shielded)
|
||||||
|
self.sync_all()
|
||||||
|
|
||||||
|
# Spends should be in the mempool
|
||||||
|
assert_equal(set(self.nodes[0].getrawmempool()), set(overwinter_txids))
|
||||||
|
|
||||||
|
# Node 0 note should be unspendable
|
||||||
|
assert_equal(self.nodes[0].z_getbalance(node0_zaddr), Decimal('0'))
|
||||||
|
|
||||||
|
# Invalidate block 199.
|
||||||
|
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
|
||||||
|
|
||||||
|
# BUG: Ideally, the mempool should now only contain the transactions
|
||||||
|
# that were in block 199, the Overwinter transactions having been dropped.
|
||||||
|
# However, because chainActive is not updated until after the transactions
|
||||||
|
# in the disconnected block have been re-added to the mempool, the height
|
||||||
|
# seen by AcceptToMemoryPool is one greater than it should be. This causes
|
||||||
|
# the block 199 transactions to be validated against the Overwinter rules,
|
||||||
|
# and rejected because they (obviously) fail.
|
||||||
|
#assert_equal(set(self.nodes[0].getrawmempool()), set(block_txids[1:]))
|
||||||
|
assert_equal(set(self.nodes[0].getrawmempool()), set())
|
||||||
|
|
||||||
|
# Node 0 note should be spendable again
|
||||||
|
assert_equal(self.nodes[0].z_getbalance(node0_zaddr), Decimal('10'))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
MempoolUpgradeActivationTest().main()
|
||||||
@@ -26,6 +26,9 @@ struct NUInfo {
|
|||||||
|
|
||||||
extern const struct NUInfo NetworkUpgradeInfo[];
|
extern const struct NUInfo NetworkUpgradeInfo[];
|
||||||
|
|
||||||
|
// Consensus branch id to identify pre-overwinter (Sprout) consensus rules.
|
||||||
|
static const uint32_t SPROUT_BRANCH_ID = NetworkUpgradeInfo[Consensus::BASE_SPROUT].nBranchId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the state of a given network upgrade based on block height.
|
* Checks the state of a given network upgrade based on block height.
|
||||||
* Caller must check that the height is >= 0 (and handle unknown heights).
|
* Caller must check that the height is >= 0 (and handle unknown heights).
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <gtest/gtest-spi.h>
|
#include <gtest/gtest-spi.h>
|
||||||
|
|
||||||
|
#include "consensus/upgrades.h"
|
||||||
#include "consensus/validation.h"
|
#include "consensus/validation.h"
|
||||||
#include "core_io.h"
|
#include "core_io.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
@@ -83,7 +84,7 @@ TEST(Mempool, PriorityStatsDoNotCrash) {
|
|||||||
unsigned int nHeight = 92045;
|
unsigned int nHeight = 92045;
|
||||||
double dPriority = view.GetPriority(tx, nHeight);
|
double dPriority = view.GetPriority(tx, nHeight);
|
||||||
|
|
||||||
CTxMemPoolEntry entry(tx, nFees, nTime, dPriority, nHeight, true, false);
|
CTxMemPoolEntry entry(tx, nFees, nTime, dPriority, nHeight, true, false, SPROUT_BRANCH_ID);
|
||||||
|
|
||||||
// Check it does not crash (ie. the death test fails)
|
// Check it does not crash (ie. the death test fails)
|
||||||
EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(testPool.addUnchecked(tx.GetHash(), entry), ""), "");
|
EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(testPool.addUnchecked(tx.GetHash(), entry), ""), "");
|
||||||
|
|||||||
19
src/main.cpp
19
src/main.cpp
@@ -1199,7 +1199,12 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), mempool.HasNoInputsOf(tx), fSpendsCoinbase);
|
// Grab the branch ID we expect this transaction to commit to. We don't
|
||||||
|
// yet know if it does, but if the entry gets added to the mempool, then
|
||||||
|
// it has passed ContextualCheckInputs and therefore this is correct.
|
||||||
|
auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus());
|
||||||
|
|
||||||
|
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), mempool.HasNoInputsOf(tx), fSpendsCoinbase, consensusBranchId);
|
||||||
unsigned int nSize = entry.GetTxSize();
|
unsigned int nSize = entry.GetTxSize();
|
||||||
|
|
||||||
// Accept a tx if it contains joinsplits and has at least the default fee specified by z_sendmany.
|
// Accept a tx if it contains joinsplits and has at least the default fee specified by z_sendmany.
|
||||||
@@ -2430,7 +2435,10 @@ void static UpdateTip(CBlockIndex *pindexNew) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Disconnect chainActive's tip. You probably want to call mempool.removeForReorg after this, with cs_main held. */
|
/**
|
||||||
|
* Disconnect chainActive's tip. You probably want to call mempool.removeForReorg and
|
||||||
|
* mempool.removeWithoutBranchId after this, with cs_main held.
|
||||||
|
*/
|
||||||
bool static DisconnectTip(CValidationState &state, bool fBare = false) {
|
bool static DisconnectTip(CValidationState &state, bool fBare = false) {
|
||||||
CBlockIndex *pindexDelete = chainActive.Tip();
|
CBlockIndex *pindexDelete = chainActive.Tip();
|
||||||
assert(pindexDelete);
|
assert(pindexDelete);
|
||||||
@@ -2493,6 +2501,7 @@ static int64_t nTimePostConnect = 0;
|
|||||||
/**
|
/**
|
||||||
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
|
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
|
||||||
* corresponding to pindexNew, to bypass loading it again from disk.
|
* corresponding to pindexNew, to bypass loading it again from disk.
|
||||||
|
* You probably want to call mempool.removeWithoutBranchId after this, with cs_main held.
|
||||||
*/
|
*/
|
||||||
bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *pblock) {
|
bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *pblock) {
|
||||||
assert(pindexNew->pprev == chainActive.Tip());
|
assert(pindexNew->pprev == chainActive.Tip());
|
||||||
@@ -2691,6 +2700,8 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo
|
|||||||
if (fBlocksDisconnected) {
|
if (fBlocksDisconnected) {
|
||||||
mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
|
mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
|
||||||
}
|
}
|
||||||
|
mempool.removeWithoutBranchId(
|
||||||
|
CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, Params().GetConsensus()));
|
||||||
mempool.check(pcoinsTip);
|
mempool.check(pcoinsTip);
|
||||||
|
|
||||||
// Callbacks/notifications for a new best chain.
|
// Callbacks/notifications for a new best chain.
|
||||||
@@ -2778,6 +2789,8 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) {
|
|||||||
// unconditionally valid already, so force disconnect away from it.
|
// unconditionally valid already, so force disconnect away from it.
|
||||||
if (!DisconnectTip(state)) {
|
if (!DisconnectTip(state)) {
|
||||||
mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
|
mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
|
||||||
|
mempool.removeWithoutBranchId(
|
||||||
|
CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, Params().GetConsensus()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2794,6 +2807,8 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) {
|
|||||||
|
|
||||||
InvalidChainFound(pindex);
|
InvalidChainFound(pindex);
|
||||||
mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
|
mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
|
||||||
|
mempool.removeWithoutBranchId(
|
||||||
|
CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, Params().GetConsensus()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Distributed under the MIT software license, see the accompanying
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "consensus/upgrades.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "txmempool.h"
|
#include "txmempool.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@@ -156,4 +157,59 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
|
|||||||
BOOST_CHECK(it == pool.mapTx.get<1>().end());
|
BOOST_CHECK(it == pool.mapTx.get<1>().end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(RemoveWithoutBranchId) {
|
||||||
|
CTxMemPool pool(CFeeRate(0));
|
||||||
|
TestMemPoolEntryHelper entry;
|
||||||
|
entry.nFee = 10000LL;
|
||||||
|
entry.hadNoDependencies = true;
|
||||||
|
|
||||||
|
// Add some Sprout transactions
|
||||||
|
for (auto i = 1; i < 11; i++) {
|
||||||
|
CMutableTransaction tx = CMutableTransaction();
|
||||||
|
tx.vout.resize(1);
|
||||||
|
tx.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
|
||||||
|
tx.vout[0].nValue = i * COIN;
|
||||||
|
pool.addUnchecked(tx.GetHash(), entry.BranchId(NetworkUpgradeInfo[Consensus::BASE_SPROUT].nBranchId).FromTx(tx));
|
||||||
|
}
|
||||||
|
BOOST_CHECK_EQUAL(pool.size(), 10);
|
||||||
|
|
||||||
|
// Check the pool only contains Sprout transactions
|
||||||
|
for (CTxMemPool::indexed_transaction_set::const_iterator it = pool.mapTx.begin(); it != pool.mapTx.end(); it++) {
|
||||||
|
BOOST_CHECK_EQUAL(it->GetValidatedBranchId(), NetworkUpgradeInfo[Consensus::BASE_SPROUT].nBranchId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add some dummy transactions
|
||||||
|
for (auto i = 1; i < 11; i++) {
|
||||||
|
CMutableTransaction tx = CMutableTransaction();
|
||||||
|
tx.vout.resize(1);
|
||||||
|
tx.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
|
||||||
|
tx.vout[0].nValue = i * COIN + 100;
|
||||||
|
pool.addUnchecked(tx.GetHash(), entry.BranchId(NetworkUpgradeInfo[Consensus::UPGRADE_TESTDUMMY].nBranchId).FromTx(tx));
|
||||||
|
}
|
||||||
|
BOOST_CHECK_EQUAL(pool.size(), 20);
|
||||||
|
|
||||||
|
// Add some Overwinter transactions
|
||||||
|
for (auto i = 1; i < 11; i++) {
|
||||||
|
CMutableTransaction tx = CMutableTransaction();
|
||||||
|
tx.vout.resize(1);
|
||||||
|
tx.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
|
||||||
|
tx.vout[0].nValue = i * COIN + 200;
|
||||||
|
pool.addUnchecked(tx.GetHash(), entry.BranchId(NetworkUpgradeInfo[Consensus::UPGRADE_OVERWINTER].nBranchId).FromTx(tx));
|
||||||
|
}
|
||||||
|
BOOST_CHECK_EQUAL(pool.size(), 30);
|
||||||
|
|
||||||
|
// Remove transactions that are not for Overwinter
|
||||||
|
pool.removeWithoutBranchId(NetworkUpgradeInfo[Consensus::UPGRADE_OVERWINTER].nBranchId);
|
||||||
|
BOOST_CHECK_EQUAL(pool.size(), 10);
|
||||||
|
|
||||||
|
// Check the pool only contains Overwinter transactions
|
||||||
|
for (CTxMemPool::indexed_transaction_set::const_iterator it = pool.mapTx.begin(); it != pool.mapTx.end(); it++) {
|
||||||
|
BOOST_CHECK_EQUAL(it->GetValidatedBranchId(), NetworkUpgradeInfo[Consensus::UPGRADE_OVERWINTER].nBranchId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Roll back to Sprout
|
||||||
|
pool.removeWithoutBranchId(NetworkUpgradeInfo[Consensus::BASE_SPROUT].nBranchId);
|
||||||
|
BOOST_CHECK_EQUAL(pool.size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|||||||
@@ -106,7 +106,8 @@ TestingSetup::~TestingSetup()
|
|||||||
|
|
||||||
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPool *pool) {
|
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPool *pool) {
|
||||||
return CTxMemPoolEntry(tx, nFee, nTime, dPriority, nHeight,
|
return CTxMemPoolEntry(tx, nFee, nTime, dPriority, nHeight,
|
||||||
pool ? pool->HasNoInputsOf(tx) : hadNoDependencies, spendsCoinbase);
|
pool ? pool->HasNoInputsOf(tx) : hadNoDependencies,
|
||||||
|
spendsCoinbase, nBranchId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown(void* parg)
|
void Shutdown(void* parg)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#ifndef BITCOIN_TEST_TEST_BITCOIN_H
|
#ifndef BITCOIN_TEST_TEST_BITCOIN_H
|
||||||
#define BITCOIN_TEST_TEST_BITCOIN_H
|
#define BITCOIN_TEST_TEST_BITCOIN_H
|
||||||
|
|
||||||
|
#include "consensus/upgrades.h"
|
||||||
#include "pubkey.h"
|
#include "pubkey.h"
|
||||||
#include "txdb.h"
|
#include "txdb.h"
|
||||||
|
|
||||||
@@ -48,10 +49,12 @@ struct TestMemPoolEntryHelper
|
|||||||
unsigned int nHeight;
|
unsigned int nHeight;
|
||||||
bool hadNoDependencies;
|
bool hadNoDependencies;
|
||||||
bool spendsCoinbase;
|
bool spendsCoinbase;
|
||||||
|
uint32_t nBranchId;
|
||||||
|
|
||||||
TestMemPoolEntryHelper() :
|
TestMemPoolEntryHelper() :
|
||||||
nFee(0), nTime(0), dPriority(0.0), nHeight(1),
|
nFee(0), nTime(0), dPriority(0.0), nHeight(1),
|
||||||
hadNoDependencies(false), spendsCoinbase(false) { }
|
hadNoDependencies(false), spendsCoinbase(false),
|
||||||
|
nBranchId(SPROUT_BRANCH_ID) { }
|
||||||
|
|
||||||
CTxMemPoolEntry FromTx(CMutableTransaction &tx, CTxMemPool *pool = NULL);
|
CTxMemPoolEntry FromTx(CMutableTransaction &tx, CTxMemPool *pool = NULL);
|
||||||
|
|
||||||
@@ -62,5 +65,6 @@ struct TestMemPoolEntryHelper
|
|||||||
TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; }
|
TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; }
|
||||||
TestMemPoolEntryHelper &HadNoDependencies(bool _hnd) { hadNoDependencies = _hnd; return *this; }
|
TestMemPoolEntryHelper &HadNoDependencies(bool _hnd) { hadNoDependencies = _hnd; return *this; }
|
||||||
TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; }
|
TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; }
|
||||||
|
TestMemPoolEntryHelper &BranchId(uint32_t _branchId) { nBranchId = _branchId; return *this; }
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ CTxMemPoolEntry::CTxMemPoolEntry():
|
|||||||
CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
|
CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
|
||||||
int64_t _nTime, double _dPriority,
|
int64_t _nTime, double _dPriority,
|
||||||
unsigned int _nHeight, bool poolHasNoInputsOf,
|
unsigned int _nHeight, bool poolHasNoInputsOf,
|
||||||
bool _spendsCoinbase):
|
bool _spendsCoinbase, uint32_t _nBranchId):
|
||||||
tx(_tx), nFee(_nFee), nTime(_nTime), dPriority(_dPriority), nHeight(_nHeight),
|
tx(_tx), nFee(_nFee), nTime(_nTime), dPriority(_dPriority), nHeight(_nHeight),
|
||||||
hadNoDependencies(poolHasNoInputsOf),
|
hadNoDependencies(poolHasNoInputsOf),
|
||||||
spendsCoinbase(_spendsCoinbase)
|
spendsCoinbase(_spendsCoinbase), nBranchId(_nBranchId)
|
||||||
{
|
{
|
||||||
nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
||||||
nModSize = tx.CalculateModifiedSize(nTxSize);
|
nModSize = tx.CalculateModifiedSize(nTxSize);
|
||||||
@@ -283,6 +283,28 @@ void CTxMemPool::removeForBlock(const std::vector<CTransaction>& vtx, unsigned i
|
|||||||
minerPolicyEstimator->processBlock(nBlockHeight, entries, fCurrentEstimate);
|
minerPolicyEstimator->processBlock(nBlockHeight, entries, fCurrentEstimate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called whenever the tip changes. Removes transactions which don't commit to
|
||||||
|
* the given branch ID from the mempool.
|
||||||
|
*/
|
||||||
|
void CTxMemPool::removeWithoutBranchId(uint32_t nMemPoolBranchId)
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
std::list<CTransaction> transactionsToRemove;
|
||||||
|
|
||||||
|
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
|
||||||
|
const CTransaction& tx = it->GetTx();
|
||||||
|
if (it->GetValidatedBranchId() != nMemPoolBranchId) {
|
||||||
|
transactionsToRemove.push_back(tx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const CTransaction& tx : transactionsToRemove) {
|
||||||
|
std::list<CTransaction> removed;
|
||||||
|
remove(tx, removed, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CTxMemPool::clear()
|
void CTxMemPool::clear()
|
||||||
{
|
{
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
|
|||||||
@@ -51,11 +51,12 @@ private:
|
|||||||
unsigned int nHeight; //! Chain height when entering the mempool
|
unsigned int nHeight; //! Chain height when entering the mempool
|
||||||
bool hadNoDependencies; //! Not dependent on any other txs when it entered the mempool
|
bool hadNoDependencies; //! Not dependent on any other txs when it entered the mempool
|
||||||
bool spendsCoinbase; //! keep track of transactions that spend a coinbase
|
bool spendsCoinbase; //! keep track of transactions that spend a coinbase
|
||||||
|
uint32_t nBranchId; //! Branch ID this transaction is known to commit to, cached for efficiency
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
|
CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
|
||||||
int64_t _nTime, double _dPriority, unsigned int _nHeight,
|
int64_t _nTime, double _dPriority, unsigned int _nHeight,
|
||||||
bool poolHasNoInputsOf, bool spendsCoinbase);
|
bool poolHasNoInputsOf, bool spendsCoinbase, uint32_t nBranchId);
|
||||||
CTxMemPoolEntry();
|
CTxMemPoolEntry();
|
||||||
CTxMemPoolEntry(const CTxMemPoolEntry& other);
|
CTxMemPoolEntry(const CTxMemPoolEntry& other);
|
||||||
|
|
||||||
@@ -70,6 +71,7 @@ public:
|
|||||||
size_t DynamicMemoryUsage() const { return nUsageSize; }
|
size_t DynamicMemoryUsage() const { return nUsageSize; }
|
||||||
|
|
||||||
bool GetSpendsCoinbase() const { return spendsCoinbase; }
|
bool GetSpendsCoinbase() const { return spendsCoinbase; }
|
||||||
|
uint32_t GetValidatedBranchId() const { return nBranchId; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// extracts a TxMemPoolEntry's transaction hash
|
// extracts a TxMemPoolEntry's transaction hash
|
||||||
@@ -168,6 +170,7 @@ public:
|
|||||||
void removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed);
|
void removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed);
|
||||||
void removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight,
|
void removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight,
|
||||||
std::list<CTransaction>& conflicts, bool fCurrentEstimate = true);
|
std::list<CTransaction>& conflicts, bool fCurrentEstimate = true);
|
||||||
|
void removeWithoutBranchId(uint32_t nMemPoolBranchId);
|
||||||
void clear();
|
void clear();
|
||||||
void queryHashes(std::vector<uint256>& vtxid);
|
void queryHashes(std::vector<uint256>& vtxid);
|
||||||
void pruneSpent(const uint256& hash, CCoins &coins);
|
void pruneSpent(const uint256& hash, CCoins &coins);
|
||||||
|
|||||||
Reference in New Issue
Block a user