Merge #17407: node: Add reference to mempool in NodeContext
fa538813b1
scripted-diff: Replace ::mempool with m_node.mempool in tests (MarcoFalke)8888ad02e2
test: Replace recursive lock with locking annotations (MarcoFalke)fac07f2038
node: Add reference to mempool in NodeContext (MarcoFalke) Pull request description: This is the first step toward making the mempool a global that is not initialized before main. #### Motivation Currently the mempool is a global that is initialized before the `main` function. This is confusing and easy to get wrong. E.g. the mempool constructor queries state that has not been initialized, like randomness (fixed), or command line arguments (not an issue last time I checked). Also without having the chainstate (chain tip) initialized first, it doesn't make conceptually sense to have a mempool, since the mempool builds txs on top of the utxo set (chain tip). Finally, in the future someone might want to run a consensus-only full node (`-nowallet -noblockfilter -no... -nomempool` command line options) that only verifies blocks and updates the utxo set. This is conceptually the same change that has already been done for the connection manager `CConnman`. ACKs for top commit: jnewbery: utACKfa538813b1
ariard: Tested ACKfa53881
. Tree-SHA512: 2c446a8a51476354aad7126c2b833500d36b24490caa94f847b2bdc622054de0dae28980f23e3d91b1b492dc32931656d98dbd019af9e4e58f2f8c5375aac694
This commit is contained in:
commit
ae6943620a
|
@ -284,6 +284,7 @@ void Shutdown(NodeContext& node)
|
|||
GetMainSignals().UnregisterWithMempoolSignals(mempool);
|
||||
globalVerifyHandle.reset();
|
||||
ECC_Stop();
|
||||
if (node.mempool) node.mempool = nullptr;
|
||||
LogPrintf("%s: done\n", __func__);
|
||||
}
|
||||
|
||||
|
@ -1635,6 +1636,11 @@ bool AppInitMain(NodeContext& node)
|
|||
return false;
|
||||
}
|
||||
|
||||
// Now that the chain state is loaded, make mempool generally available in the node context. For example the
|
||||
// connection manager, wallet, or RPC threads, which are all started after this, may use it from the node context.
|
||||
assert(!node.mempool);
|
||||
node.mempool = &::mempool;
|
||||
|
||||
fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
|
||||
CAutoFile est_filein(fsbridge::fopen(est_path, "rb"), SER_DISK, CLIENT_VERSION);
|
||||
// Allowed to fail as this file IS missing on first startup.
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
class BanMan;
|
||||
class CConnman;
|
||||
class CTxMemPool;
|
||||
class PeerLogicValidation;
|
||||
namespace interfaces {
|
||||
class Chain;
|
||||
|
@ -22,13 +23,13 @@ class ChainClient;
|
|||
//! This is used by init, rpc, and test code to pass object references around
|
||||
//! without needing to declare the same variables and parameters repeatedly, or
|
||||
//! to use globals. More variables could be added to this struct (particularly
|
||||
//! references to validation and mempool objects) to eliminate use of globals
|
||||
//! references to validation objects) to eliminate use of globals
|
||||
//! and make code more modular and testable. The struct isn't intended to have
|
||||
//! any member functions. It should just be a collection of references that can
|
||||
//! be used without pulling in unwanted dependencies or functionality.
|
||||
struct NodeContext
|
||||
{
|
||||
struct NodeContext {
|
||||
std::unique_ptr<CConnman> connman;
|
||||
CTxMemPool* mempool{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
|
||||
std::unique_ptr<PeerLogicValidation> peer_logic;
|
||||
std::unique_ptr<BanMan> banman;
|
||||
std::unique_ptr<interfaces::Chain> chain;
|
||||
|
|
|
@ -32,7 +32,6 @@ void RPCNestedTests::rpcNestedTests()
|
|||
// do some test setup
|
||||
// could be moved to a more generic place when we add more tests on QT level
|
||||
tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]);
|
||||
//mempool.setSanityCheck(1.0);
|
||||
|
||||
TestingSetup test;
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <hash.h>
|
||||
#include <index/blockfilterindex.h>
|
||||
#include <node/coinstats.h>
|
||||
#include <node/context.h>
|
||||
#include <node/utxo_snapshot.h>
|
||||
#include <policy/feerate.h>
|
||||
#include <policy/policy.h>
|
||||
|
@ -53,6 +54,15 @@ static Mutex cs_blockchange;
|
|||
static std::condition_variable cond_blockchange;
|
||||
static CUpdatedBlock latestblock;
|
||||
|
||||
CTxMemPool& EnsureMemPool()
|
||||
{
|
||||
CHECK_NONFATAL(g_rpc_node);
|
||||
if (!g_rpc_node->mempool) {
|
||||
throw JSONRPCError(RPC_CLIENT_MEMPOOL_DISABLED, "Mempool disabled or instance not found");
|
||||
}
|
||||
return *g_rpc_node->mempool;
|
||||
}
|
||||
|
||||
/* Calculate the difficulty for a given block index.
|
||||
*/
|
||||
double GetDifficulty(const CBlockIndex* blockindex)
|
||||
|
|
|
@ -52,4 +52,6 @@ void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES],
|
|||
//! direct way to pass in state to RPC methods without globals.
|
||||
extern NodeContext* g_rpc_node;
|
||||
|
||||
CTxMemPool& EnsureMemPool();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -63,6 +63,9 @@ enum RPCErrorCode
|
|||
RPC_CLIENT_INVALID_IP_OR_SUBNET = -30, //!< Invalid IP/Subnet
|
||||
RPC_CLIENT_P2P_DISABLED = -31, //!< No valid connection manager instance found
|
||||
|
||||
//! Chain errors
|
||||
RPC_CLIENT_MEMPOOL_DISABLED = -33, //!< No mempool instance found
|
||||
|
||||
//! Wallet errors
|
||||
RPC_WALLET_ERROR = -4, //!< Unspecified problem with wallet (key not found etc.)
|
||||
RPC_WALLET_INSUFFICIENT_FUNDS = -6, //!< Not enough funds in wallet or account
|
||||
|
|
|
@ -23,7 +23,17 @@
|
|||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(miner_tests, TestingSetup)
|
||||
namespace miner_tests {
|
||||
struct MinerTestingSetup : public TestingSetup {
|
||||
void TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
|
||||
bool TestSequenceLocks(const CTransaction& tx, int flags) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs)
|
||||
{
|
||||
return CheckSequenceLocks(*m_node.mempool, tx, flags);
|
||||
}
|
||||
};
|
||||
} // namespace miner_tests
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(miner_tests, MinerTestingSetup)
|
||||
|
||||
// BOOST_CHECK_EXCEPTION predicates to check the specific validation error
|
||||
class HasReason {
|
||||
|
@ -89,16 +99,10 @@ static CBlockIndex CreateBlockIndex(int nHeight) EXCLUSIVE_LOCKS_REQUIRED(cs_mai
|
|||
return index;
|
||||
}
|
||||
|
||||
static bool TestSequenceLocks(const CTransaction &tx, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||
{
|
||||
LOCK(::mempool.cs);
|
||||
return CheckSequenceLocks(::mempool, tx, flags);
|
||||
}
|
||||
|
||||
// Test suite for ancestor feerate transaction selection.
|
||||
// Implemented as an additional function, rather than a separate test case,
|
||||
// to allow reusing the blockchain created in CreateNewBlock_validity.
|
||||
static void TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs)
|
||||
void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst)
|
||||
{
|
||||
// Test the ancestor feerate transaction selection.
|
||||
TestMemPoolEntryHelper entry;
|
||||
|
@ -114,19 +118,19 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
|
|||
tx.vout[0].nValue = 5000000000LL - 1000;
|
||||
// This tx has a low fee: 1000 satoshis
|
||||
uint256 hashParentTx = tx.GetHash(); // save this txid for later use
|
||||
mempool.addUnchecked(entry.Fee(1000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(1000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
|
||||
// This tx has a medium fee: 10000 satoshis
|
||||
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
|
||||
tx.vout[0].nValue = 5000000000LL - 10000;
|
||||
uint256 hashMediumFeeTx = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Fee(10000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(10000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
|
||||
// This tx has a high fee, but depends on the first transaction
|
||||
tx.vin[0].prevout.hash = hashParentTx;
|
||||
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 50k satoshi fee
|
||||
uint256 hashHighFeeTx = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
|
||||
|
||||
std::unique_ptr<CBlockTemplate> pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
|
||||
BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashParentTx);
|
||||
|
@ -137,7 +141,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
|
|||
tx.vin[0].prevout.hash = hashHighFeeTx;
|
||||
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 0 fee
|
||||
uint256 hashFreeTx = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Fee(0).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(0).FromTx(tx));
|
||||
size_t freeTxSize = ::GetSerializeSize(tx, PROTOCOL_VERSION);
|
||||
|
||||
// Calculate a fee on child transaction that will put the package just
|
||||
|
@ -147,7 +151,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
|
|||
tx.vin[0].prevout.hash = hashFreeTx;
|
||||
tx.vout[0].nValue = 5000000000LL - 1000 - 50000 - feeToUse;
|
||||
uint256 hashLowFeeTx = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Fee(feeToUse).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(feeToUse).FromTx(tx));
|
||||
pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
|
||||
// Verify that the free tx and the low fee tx didn't get selected
|
||||
for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
|
||||
|
@ -158,10 +162,10 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
|
|||
// Test that packages above the min relay fee do get included, even if one
|
||||
// of the transactions is below the min relay fee
|
||||
// Remove the low fee transaction and replace with a higher fee transaction
|
||||
mempool.removeRecursive(CTransaction(tx), MemPoolRemovalReason::REPLACED);
|
||||
m_node.mempool->removeRecursive(CTransaction(tx), MemPoolRemovalReason::REPLACED);
|
||||
tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee
|
||||
hashLowFeeTx = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Fee(feeToUse+2).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(feeToUse+2).FromTx(tx));
|
||||
pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
|
||||
BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashFreeTx);
|
||||
BOOST_CHECK(pblocktemplate->block.vtx[5]->GetHash() == hashLowFeeTx);
|
||||
|
@ -174,7 +178,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
|
|||
tx.vout[0].nValue = 5000000000LL - 100000000;
|
||||
tx.vout[1].nValue = 100000000; // 1BTC output
|
||||
uint256 hashFreeTx2 = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Fee(0).SpendsCoinbase(true).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(0).SpendsCoinbase(true).FromTx(tx));
|
||||
|
||||
// This tx can't be mined by itself
|
||||
tx.vin[0].prevout.hash = hashFreeTx2;
|
||||
|
@ -182,7 +186,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
|
|||
feeToUse = blockMinFeeRate.GetFee(freeTxSize);
|
||||
tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse;
|
||||
uint256 hashLowFeeTx2 = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
|
||||
pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
|
||||
|
||||
// Verify that this tx isn't selected.
|
||||
|
@ -195,7 +199,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
|
|||
// as well.
|
||||
tx.vin[0].prevout.n = 1;
|
||||
tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee
|
||||
mempool.addUnchecked(entry.Fee(10000).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(10000).FromTx(tx));
|
||||
pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
|
||||
BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2);
|
||||
}
|
||||
|
@ -252,7 +256,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
}
|
||||
|
||||
LOCK(cs_main);
|
||||
LOCK(::mempool.cs);
|
||||
LOCK(m_node.mempool->cs);
|
||||
|
||||
// Just to make sure we can still make simple blocks
|
||||
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
|
||||
|
@ -276,12 +280,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
hash = tx.GetHash();
|
||||
bool spendsCoinbase = i == 0; // only first tx spends coinbase
|
||||
// If we don't set the # of sig ops in the CTxMemPoolEntry, template creation fails
|
||||
mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
|
||||
tx.vin[0].prevout.hash = hash;
|
||||
}
|
||||
|
||||
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-blk-sigops"));
|
||||
mempool.clear();
|
||||
m_node.mempool->clear();
|
||||
|
||||
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
|
||||
tx.vout[0].nValue = BLOCKSUBSIDY;
|
||||
|
@ -291,11 +295,11 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
hash = tx.GetHash();
|
||||
bool spendsCoinbase = i == 0; // only first tx spends coinbase
|
||||
// If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes
|
||||
mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOpsCost(80).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOpsCost(80).FromTx(tx));
|
||||
tx.vin[0].prevout.hash = hash;
|
||||
}
|
||||
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
|
||||
mempool.clear();
|
||||
m_node.mempool->clear();
|
||||
|
||||
// block size > limit
|
||||
tx.vin[0].scriptSig = CScript();
|
||||
|
@ -311,24 +315,24 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
tx.vout[0].nValue -= LOWFEE;
|
||||
hash = tx.GetHash();
|
||||
bool spendsCoinbase = i == 0; // only first tx spends coinbase
|
||||
mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
|
||||
tx.vin[0].prevout.hash = hash;
|
||||
}
|
||||
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
|
||||
mempool.clear();
|
||||
m_node.mempool->clear();
|
||||
|
||||
// orphan in mempool, template creation fails
|
||||
// orphan in *m_node.mempool, template creation fails
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx));
|
||||
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
|
||||
mempool.clear();
|
||||
m_node.mempool->clear();
|
||||
|
||||
// child with higher feerate than parent
|
||||
tx.vin[0].scriptSig = CScript() << OP_1;
|
||||
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
|
||||
tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
tx.vin[0].prevout.hash = hash;
|
||||
tx.vin.resize(2);
|
||||
tx.vin[1].scriptSig = CScript() << OP_1;
|
||||
|
@ -336,34 +340,34 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
tx.vin[1].prevout.n = 0;
|
||||
tx.vout[0].nValue = tx.vout[0].nValue+BLOCKSUBSIDY-HIGHERFEE; //First txn output + fresh coinbase - new txn fee
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
|
||||
mempool.clear();
|
||||
m_node.mempool->clear();
|
||||
|
||||
// coinbase in mempool, template creation fails
|
||||
// coinbase in *m_node.mempool, template creation fails
|
||||
tx.vin.resize(1);
|
||||
tx.vin[0].prevout.SetNull();
|
||||
tx.vin[0].scriptSig = CScript() << OP_0 << OP_1;
|
||||
tx.vout[0].nValue = 0;
|
||||
hash = tx.GetHash();
|
||||
// give it a fee so it'll get mined
|
||||
mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
|
||||
// Should throw bad-cb-multiple
|
||||
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple"));
|
||||
mempool.clear();
|
||||
m_node.mempool->clear();
|
||||
|
||||
// double spend txn pair in mempool, template creation fails
|
||||
// double spend txn pair in *m_node.mempool, template creation fails
|
||||
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
|
||||
tx.vin[0].scriptSig = CScript() << OP_1;
|
||||
tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE;
|
||||
tx.vout[0].scriptPubKey = CScript() << OP_1;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
tx.vout[0].scriptPubKey = CScript() << OP_2;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
|
||||
mempool.clear();
|
||||
m_node.mempool->clear();
|
||||
|
||||
// subsidy changing
|
||||
int nHeight = ::ChainActive().Height();
|
||||
|
@ -392,7 +396,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
}
|
||||
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
|
||||
|
||||
// invalid p2sh txn in mempool, template creation fails
|
||||
// invalid p2sh txn in *m_node.mempool, template creation fails
|
||||
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
|
||||
tx.vin[0].prevout.n = 0;
|
||||
tx.vin[0].scriptSig = CScript() << OP_1;
|
||||
|
@ -400,15 +404,15 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
script = CScript() << OP_0;
|
||||
tx.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(script));
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
tx.vin[0].prevout.hash = hash;
|
||||
tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end());
|
||||
tx.vout[0].nValue -= LOWFEE;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
|
||||
// Should throw block-validation-failed
|
||||
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("block-validation-failed"));
|
||||
mempool.clear();
|
||||
m_node.mempool->clear();
|
||||
|
||||
// Delete the dummy blocks again.
|
||||
while (::ChainActive().Tip()->nHeight > nHeight) {
|
||||
|
@ -439,7 +443,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
tx.vout[0].scriptPubKey = CScript() << OP_1;
|
||||
tx.nLockTime = 0;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
|
||||
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
|
||||
BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, &prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 2))); // Sequence locks pass on 2nd block
|
||||
|
@ -449,7 +453,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((::ChainActive().Tip()->GetMedianTimePast()+1-::ChainActive()[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block
|
||||
prevheights[0] = baseheight + 2;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
|
||||
BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
|
||||
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
|
||||
|
||||
|
@ -465,7 +469,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
prevheights[0] = baseheight + 3;
|
||||
tx.nLockTime = ::ChainActive().Tip()->nHeight + 1;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
|
||||
BOOST_CHECK(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails
|
||||
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
|
||||
BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
|
||||
|
@ -476,7 +480,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
prevheights.resize(1);
|
||||
prevheights[0] = baseheight + 4;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
|
||||
m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
|
||||
BOOST_CHECK(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails
|
||||
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
|
||||
BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
|
||||
|
@ -513,7 +517,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
|
||||
::ChainActive().Tip()->nHeight--;
|
||||
SetMockTime(0);
|
||||
mempool.clear();
|
||||
m_node.mempool->clear();
|
||||
|
||||
TestPackageSelection(chainparams, scriptPubKey, txFirst);
|
||||
|
||||
|
|
|
@ -34,17 +34,17 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
|
|||
|
||||
LOCK(cs_main);
|
||||
|
||||
unsigned int initialPoolSize = mempool.size();
|
||||
unsigned int initialPoolSize = m_node.mempool->size();
|
||||
|
||||
BOOST_CHECK_EQUAL(
|
||||
false,
|
||||
AcceptToMemoryPool(mempool, state, MakeTransactionRef(coinbaseTx),
|
||||
AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(coinbaseTx),
|
||||
nullptr /* plTxnReplaced */,
|
||||
true /* bypass_limits */,
|
||||
0 /* nAbsurdFee */));
|
||||
|
||||
// Check that the transaction hasn't been added to mempool.
|
||||
BOOST_CHECK_EQUAL(mempool.size(), initialPoolSize);
|
||||
BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
|
||||
|
||||
// Check that the validation state reflects the unsuccessful attempt.
|
||||
BOOST_CHECK(state.IsInvalid());
|
||||
|
|
|
@ -17,16 +17,6 @@ bool CheckInputs(const CTransaction& tx, TxValidationState &state, const CCoinsV
|
|||
|
||||
BOOST_AUTO_TEST_SUITE(tx_validationcache_tests)
|
||||
|
||||
static bool
|
||||
ToMemPool(const CMutableTransaction& tx)
|
||||
{
|
||||
LOCK(cs_main);
|
||||
|
||||
TxValidationState state;
|
||||
return AcceptToMemoryPool(mempool, state, MakeTransactionRef(tx),
|
||||
nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
|
||||
{
|
||||
// Make sure skipping validation of transactions that were
|
||||
|
@ -35,6 +25,14 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
|
|||
|
||||
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
|
||||
|
||||
const auto ToMemPool = [this](const CMutableTransaction& tx) {
|
||||
LOCK(cs_main);
|
||||
|
||||
TxValidationState state;
|
||||
return AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(tx),
|
||||
nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */);
|
||||
};
|
||||
|
||||
// Create a double-spend of mature coinbase txn:
|
||||
std::vector<CMutableTransaction> spends;
|
||||
spends.resize(2);
|
||||
|
@ -72,7 +70,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
|
|||
LOCK(cs_main);
|
||||
BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
|
||||
}
|
||||
mempool.clear();
|
||||
m_node.mempool->clear();
|
||||
|
||||
// Test 3: ... and should be rejected if spend2 is in the memory pool
|
||||
BOOST_CHECK(ToMemPool(spends[1]));
|
||||
|
@ -81,9 +79,9 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
|
|||
LOCK(cs_main);
|
||||
BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
|
||||
}
|
||||
mempool.clear();
|
||||
m_node.mempool->clear();
|
||||
|
||||
// Final sanity test: first spend in mempool, second in block, that's OK:
|
||||
// Final sanity test: first spend in *m_node.mempool, second in block, that's OK:
|
||||
std::vector<CMutableTransaction> oneSpend;
|
||||
oneSpend.push_back(spends[0]);
|
||||
BOOST_CHECK(ToMemPool(spends[1]));
|
||||
|
@ -94,7 +92,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
|
|||
}
|
||||
// spends[1] should have been removed from the mempool when the
|
||||
// block with spends[0] is accepted:
|
||||
BOOST_CHECK_EQUAL(mempool.size(), 0U);
|
||||
BOOST_CHECK_EQUAL(m_node.mempool->size(), 0U);
|
||||
}
|
||||
|
||||
// Run CheckInputs (using CoinsTip()) on the given transaction, for all script
|
||||
|
|
|
@ -107,7 +107,6 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
|
|||
threadGroup.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
|
||||
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
|
||||
|
||||
mempool.setSanityCheck(1.0);
|
||||
pblocktree.reset(new CBlockTreeDB(1 << 20, true));
|
||||
g_chainstate = MakeUnique<CChainState>();
|
||||
::ChainstateActive().InitCoinsDB(
|
||||
|
@ -131,6 +130,8 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
|
|||
}
|
||||
g_parallel_script_checks = true;
|
||||
|
||||
m_node.mempool = &::mempool;
|
||||
m_node.mempool->setSanityCheck(1.0);
|
||||
m_node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
|
||||
m_node.connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
|
||||
}
|
||||
|
@ -144,6 +145,7 @@ TestingSetup::~TestingSetup()
|
|||
g_rpc_node = nullptr;
|
||||
m_node.connman.reset();
|
||||
m_node.banman.reset();
|
||||
m_node.mempool = nullptr;
|
||||
UnloadBlockIndex();
|
||||
g_chainstate.reset();
|
||||
pblocktree.reset();
|
||||
|
|
|
@ -279,7 +279,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
|
|||
std::list<CTransactionRef> plTxnReplaced;
|
||||
for (const auto& tx : txs) {
|
||||
BOOST_REQUIRE(AcceptToMemoryPool(
|
||||
::mempool,
|
||||
*m_node.mempool,
|
||||
state,
|
||||
tx,
|
||||
&plTxnReplaced,
|
||||
|
@ -290,8 +290,8 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
|
|||
|
||||
// Check that all txs are in the pool
|
||||
{
|
||||
LOCK(::mempool.cs);
|
||||
BOOST_CHECK_EQUAL(::mempool.mapTx.size(), txs.size());
|
||||
LOCK(m_node.mempool->cs);
|
||||
BOOST_CHECK_EQUAL(m_node.mempool->mapTx.size(), txs.size());
|
||||
}
|
||||
|
||||
// Run a thread that simulates an RPC caller that is polling while
|
||||
|
@ -301,8 +301,8 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
|
|||
// the transactions invalidated by the reorg, or none of them, and
|
||||
// not some intermediate amount.
|
||||
while (true) {
|
||||
LOCK(::mempool.cs);
|
||||
if (::mempool.mapTx.size() == 0) {
|
||||
LOCK(m_node.mempool->cs);
|
||||
if (m_node.mempool->mapTx.size() == 0) {
|
||||
// We are done with the reorg
|
||||
break;
|
||||
}
|
||||
|
@ -311,7 +311,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
|
|||
// be atomic. So the caller assumes that the returned mempool
|
||||
// is consistent. That is, it has all txs that were there
|
||||
// before the reorg.
|
||||
assert(::mempool.mapTx.size() == txs.size());
|
||||
assert(m_node.mempool->mapTx.size() == txs.size());
|
||||
continue;
|
||||
}
|
||||
LOCK(cs_main);
|
||||
|
|
Loading…
Reference in a new issue