mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-24 12:41:41 -05:00

fReindex is one of the last remaining globals exposed by the kernel library, so move it into the blockstorage class to reduce the amount of global mutable state and make the kernel library a bit less awkward to use.
587 lines
30 KiB
C++
587 lines
30 KiB
C++
// Copyright (c) 2011-2022 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include <config/bitcoin-config.h> // IWYU pragma: keep
|
|
|
|
#include <test/util/setup_common.h>
|
|
|
|
#include <kernel/validation_cache_sizes.h>
|
|
|
|
#include <addrman.h>
|
|
#include <banman.h>
|
|
#include <chainparams.h>
|
|
#include <common/system.h>
|
|
#include <consensus/consensus.h>
|
|
#include <consensus/params.h>
|
|
#include <consensus/validation.h>
|
|
#include <crypto/sha256.h>
|
|
#include <init.h>
|
|
#include <init/common.h>
|
|
#include <interfaces/chain.h>
|
|
#include <kernel/mempool_entry.h>
|
|
#include <logging.h>
|
|
#include <net.h>
|
|
#include <net_processing.h>
|
|
#include <node/blockstorage.h>
|
|
#include <node/chainstate.h>
|
|
#include <node/context.h>
|
|
#include <node/kernel_notifications.h>
|
|
#include <node/mempool_args.h>
|
|
#include <node/miner.h>
|
|
#include <node/peerman_args.h>
|
|
#include <node/validation_cache_args.h>
|
|
#include <noui.h>
|
|
#include <policy/fees.h>
|
|
#include <policy/fees_args.h>
|
|
#include <pow.h>
|
|
#include <random.h>
|
|
#include <rpc/blockchain.h>
|
|
#include <rpc/register.h>
|
|
#include <rpc/server.h>
|
|
#include <scheduler.h>
|
|
#include <script/sigcache.h>
|
|
#include <streams.h>
|
|
#include <test/util/net.h>
|
|
#include <test/util/random.h>
|
|
#include <test/util/txmempool.h>
|
|
#include <txdb.h>
|
|
#include <txmempool.h>
|
|
#include <util/chaintype.h>
|
|
#include <util/check.h>
|
|
#include <util/fs_helpers.h>
|
|
#include <util/rbf.h>
|
|
#include <util/strencodings.h>
|
|
#include <util/string.h>
|
|
#include <util/thread.h>
|
|
#include <util/threadnames.h>
|
|
#include <util/time.h>
|
|
#include <util/translation.h>
|
|
#include <util/vector.h>
|
|
#include <validation.h>
|
|
#include <validationinterface.h>
|
|
#include <walletinitinterface.h>
|
|
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include <stdexcept>
|
|
|
|
using kernel::BlockTreeDB;
|
|
using kernel::ValidationCacheSizes;
|
|
using node::ApplyArgsManOptions;
|
|
using node::BlockAssembler;
|
|
using node::BlockManager;
|
|
using node::CalculateCacheSizes;
|
|
using node::KernelNotifications;
|
|
using node::LoadChainstate;
|
|
using node::RegenerateCommitments;
|
|
using node::VerifyLoadedChainstate;
|
|
|
|
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
|
|
|
|
/** Random context to get unique temp data dirs. Separate from g_insecure_rand_ctx, which can be seeded from a const env var */
|
|
static FastRandomContext g_insecure_rand_ctx_temp_path;
|
|
|
|
std::ostream& operator<<(std::ostream& os, const uint256& num)
|
|
{
|
|
os << num.ToString();
|
|
return os;
|
|
}
|
|
|
|
struct NetworkSetup
|
|
{
|
|
NetworkSetup()
|
|
{
|
|
Assert(SetupNetworking());
|
|
}
|
|
};
|
|
static NetworkSetup g_networksetup_instance;
|
|
|
|
/** Register test-only arguments */
|
|
static void SetupUnitTestArgs(ArgsManager& argsman)
|
|
{
|
|
argsman.AddArg("-testdatadir", strprintf("Custom data directory (default: %s<random_string>)", fs::PathToString(fs::temp_directory_path() / "test_common_" PACKAGE_NAME / "")),
|
|
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
|
|
}
|
|
|
|
/** Test setup failure */
|
|
static void ExitFailure(std::string_view str_err)
|
|
{
|
|
std::cerr << str_err << std::endl;
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vector<const char*>& extra_args)
|
|
: m_args{}
|
|
{
|
|
m_node.shutdown = &m_interrupt;
|
|
m_node.args = &gArgs;
|
|
std::vector<const char*> arguments = Cat(
|
|
{
|
|
"dummy",
|
|
"-printtoconsole=0",
|
|
"-logsourcelocations",
|
|
"-logtimemicros",
|
|
"-logthreadnames",
|
|
"-loglevel=trace",
|
|
"-debug",
|
|
"-debugexclude=libevent",
|
|
"-debugexclude=leveldb",
|
|
},
|
|
extra_args);
|
|
if (G_TEST_COMMAND_LINE_ARGUMENTS) {
|
|
arguments = Cat(arguments, G_TEST_COMMAND_LINE_ARGUMENTS());
|
|
}
|
|
util::ThreadRename("test");
|
|
gArgs.ClearPathCache();
|
|
{
|
|
SetupServerArgs(*m_node.args);
|
|
SetupUnitTestArgs(*m_node.args);
|
|
std::string error;
|
|
if (!m_node.args->ParseParameters(arguments.size(), arguments.data(), error)) {
|
|
m_node.args->ClearArgs();
|
|
throw std::runtime_error{error};
|
|
}
|
|
}
|
|
|
|
if (!m_node.args->IsArgSet("-testdatadir")) {
|
|
// By default, the data directory has a random name
|
|
const auto rand_str{g_insecure_rand_ctx_temp_path.rand256().ToString()};
|
|
m_path_root = fs::temp_directory_path() / "test_common_" PACKAGE_NAME / rand_str;
|
|
TryCreateDirectories(m_path_root);
|
|
} else {
|
|
// Custom data directory
|
|
m_has_custom_datadir = true;
|
|
fs::path root_dir{m_node.args->GetPathArg("-testdatadir")};
|
|
if (root_dir.empty()) ExitFailure("-testdatadir argument is empty, please specify a path");
|
|
|
|
root_dir = fs::absolute(root_dir);
|
|
const std::string test_path{G_TEST_GET_FULL_NAME ? G_TEST_GET_FULL_NAME() : ""};
|
|
m_path_lock = root_dir / "test_common_" PACKAGE_NAME / fs::PathFromString(test_path);
|
|
m_path_root = m_path_lock / "datadir";
|
|
|
|
// Try to obtain the lock; if unsuccessful don't disturb the existing test.
|
|
TryCreateDirectories(m_path_lock);
|
|
if (util::LockDirectory(m_path_lock, ".lock", /*probe_only=*/false) != util::LockResult::Success) {
|
|
ExitFailure("Cannot obtain a lock on test data lock directory " + fs::PathToString(m_path_lock) + '\n' + "The test executable is probably already running.");
|
|
}
|
|
|
|
// Always start with a fresh data directory; this doesn't delete the .lock file located one level above.
|
|
fs::remove_all(m_path_root);
|
|
if (!TryCreateDirectories(m_path_root)) ExitFailure("Cannot create test data directory");
|
|
|
|
// Print the test directory name if custom.
|
|
std::cout << "Test directory (will not be deleted): " << m_path_root << std::endl;
|
|
}
|
|
m_args.ForceSetArg("-datadir", fs::PathToString(m_path_root));
|
|
gArgs.ForceSetArg("-datadir", fs::PathToString(m_path_root));
|
|
|
|
SelectParams(chainType);
|
|
SeedInsecureRand();
|
|
if (G_TEST_LOG_FUN) LogInstance().PushBackCallback(G_TEST_LOG_FUN);
|
|
InitLogging(*m_node.args);
|
|
AppInitParameterInteraction(*m_node.args);
|
|
LogInstance().StartLogging();
|
|
m_node.kernel = std::make_unique<kernel::Context>();
|
|
m_node.ecc_context = std::make_unique<ECC_Context>();
|
|
SetupEnvironment();
|
|
|
|
ValidationCacheSizes validation_cache_sizes{};
|
|
ApplyArgsManOptions(*m_node.args, validation_cache_sizes);
|
|
Assert(InitSignatureCache(validation_cache_sizes.signature_cache_bytes));
|
|
Assert(InitScriptExecutionCache(validation_cache_sizes.script_execution_cache_bytes));
|
|
|
|
m_node.chain = interfaces::MakeChain(m_node);
|
|
static bool noui_connected = false;
|
|
if (!noui_connected) {
|
|
noui_connect();
|
|
noui_connected = true;
|
|
}
|
|
}
|
|
|
|
BasicTestingSetup::~BasicTestingSetup()
|
|
{
|
|
m_node.ecc_context.reset();
|
|
m_node.kernel.reset();
|
|
SetMockTime(0s); // Reset mocktime for following tests
|
|
LogInstance().DisconnectTestLogger();
|
|
if (m_has_custom_datadir) {
|
|
// Only remove the lock file, preserve the data directory.
|
|
UnlockDirectory(m_path_lock, ".lock");
|
|
fs::remove(m_path_lock / ".lock");
|
|
} else {
|
|
fs::remove_all(m_path_root);
|
|
}
|
|
gArgs.ClearArgs();
|
|
}
|
|
|
|
ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vector<const char*>& extra_args)
|
|
: BasicTestingSetup(chainType, extra_args)
|
|
{
|
|
const CChainParams& chainparams = Params();
|
|
|
|
// We have to run a scheduler thread to prevent ActivateBestChain
|
|
// from blocking due to queue overrun.
|
|
m_node.scheduler = std::make_unique<CScheduler>();
|
|
m_node.scheduler->m_service_thread = std::thread(util::TraceThread, "scheduler", [&] { m_node.scheduler->serviceQueue(); });
|
|
m_node.validation_signals = std::make_unique<ValidationSignals>(std::make_unique<SerialTaskRunner>(*m_node.scheduler));
|
|
|
|
m_node.fee_estimator = std::make_unique<CBlockPolicyEstimator>(FeeestPath(*m_node.args), DEFAULT_ACCEPT_STALE_FEE_ESTIMATES);
|
|
m_node.mempool = std::make_unique<CTxMemPool>(MemPoolOptionsForTest(m_node));
|
|
|
|
m_cache_sizes = CalculateCacheSizes(m_args);
|
|
|
|
m_node.notifications = std::make_unique<KernelNotifications>(*Assert(m_node.shutdown), m_node.exit_status);
|
|
|
|
const ChainstateManager::Options chainman_opts{
|
|
.chainparams = chainparams,
|
|
.datadir = m_args.GetDataDirNet(),
|
|
.check_block_index = true,
|
|
.notifications = *m_node.notifications,
|
|
.signals = m_node.validation_signals.get(),
|
|
.worker_threads_num = 2,
|
|
};
|
|
const BlockManager::Options blockman_opts{
|
|
.chainparams = chainman_opts.chainparams,
|
|
.blocks_dir = m_args.GetBlocksDirPath(),
|
|
.notifications = chainman_opts.notifications,
|
|
};
|
|
m_node.chainman = std::make_unique<ChainstateManager>(*Assert(m_node.shutdown), chainman_opts, blockman_opts);
|
|
m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<BlockTreeDB>(DBParams{
|
|
.path = m_args.GetDataDirNet() / "blocks" / "index",
|
|
.cache_bytes = static_cast<size_t>(m_cache_sizes.block_tree_db),
|
|
.memory_only = true});
|
|
}
|
|
|
|
ChainTestingSetup::~ChainTestingSetup()
|
|
{
|
|
if (m_node.scheduler) m_node.scheduler->stop();
|
|
m_node.validation_signals->FlushBackgroundCallbacks();
|
|
m_node.connman.reset();
|
|
m_node.banman.reset();
|
|
m_node.addrman.reset();
|
|
m_node.netgroupman.reset();
|
|
m_node.args = nullptr;
|
|
m_node.mempool.reset();
|
|
m_node.fee_estimator.reset();
|
|
m_node.chainman.reset();
|
|
m_node.validation_signals.reset();
|
|
m_node.scheduler.reset();
|
|
}
|
|
|
|
void ChainTestingSetup::LoadVerifyActivateChainstate()
|
|
{
|
|
auto& chainman{*Assert(m_node.chainman)};
|
|
node::ChainstateLoadOptions options;
|
|
options.mempool = Assert(m_node.mempool.get());
|
|
options.block_tree_db_in_memory = m_block_tree_db_in_memory;
|
|
options.coins_db_in_memory = m_coins_db_in_memory;
|
|
options.reindex = chainman.m_blockman.m_reindexing;
|
|
options.reindex_chainstate = m_args.GetBoolArg("-reindex-chainstate", false);
|
|
options.prune = chainman.m_blockman.IsPruneMode();
|
|
options.check_blocks = m_args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
|
|
options.check_level = m_args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL);
|
|
options.require_full_verification = m_args.IsArgSet("-checkblocks") || m_args.IsArgSet("-checklevel");
|
|
auto [status, error] = LoadChainstate(chainman, m_cache_sizes, options);
|
|
assert(status == node::ChainstateLoadStatus::SUCCESS);
|
|
|
|
std::tie(status, error) = VerifyLoadedChainstate(chainman, options);
|
|
assert(status == node::ChainstateLoadStatus::SUCCESS);
|
|
|
|
BlockValidationState state;
|
|
if (!chainman.ActiveChainstate().ActivateBestChain(state)) {
|
|
throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString()));
|
|
}
|
|
}
|
|
|
|
TestingSetup::TestingSetup(
|
|
const ChainType chainType,
|
|
const std::vector<const char*>& extra_args,
|
|
const bool coins_db_in_memory,
|
|
const bool block_tree_db_in_memory)
|
|
: ChainTestingSetup(chainType, extra_args)
|
|
{
|
|
m_coins_db_in_memory = coins_db_in_memory;
|
|
m_block_tree_db_in_memory = block_tree_db_in_memory;
|
|
// Ideally we'd move all the RPC tests to the functional testing framework
|
|
// instead of unit tests, but for now we need these here.
|
|
RegisterAllCoreRPCCommands(tableRPC);
|
|
|
|
LoadVerifyActivateChainstate();
|
|
|
|
m_node.netgroupman = std::make_unique<NetGroupManager>(/*asmap=*/std::vector<bool>());
|
|
m_node.addrman = std::make_unique<AddrMan>(*m_node.netgroupman,
|
|
/*deterministic=*/false,
|
|
m_node.args->GetIntArg("-checkaddrman", 0));
|
|
m_node.banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
|
|
m_node.connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman, Params()); // Deterministic randomness for tests.
|
|
PeerManager::Options peerman_opts;
|
|
ApplyArgsManOptions(*m_node.args, peerman_opts);
|
|
peerman_opts.deterministic_rng = true;
|
|
m_node.peerman = PeerManager::make(*m_node.connman, *m_node.addrman,
|
|
m_node.banman.get(), *m_node.chainman,
|
|
*m_node.mempool, peerman_opts);
|
|
|
|
{
|
|
CConnman::Options options;
|
|
options.m_msgproc = m_node.peerman.get();
|
|
m_node.connman->Init(options);
|
|
}
|
|
}
|
|
|
|
TestChain100Setup::TestChain100Setup(
|
|
const ChainType chain_type,
|
|
const std::vector<const char*>& extra_args,
|
|
const bool coins_db_in_memory,
|
|
const bool block_tree_db_in_memory)
|
|
: TestingSetup{ChainType::REGTEST, extra_args, coins_db_in_memory, block_tree_db_in_memory}
|
|
{
|
|
SetMockTime(1598887952);
|
|
constexpr std::array<unsigned char, 32> vchKey = {
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}};
|
|
coinbaseKey.Set(vchKey.begin(), vchKey.end(), true);
|
|
|
|
// Generate a 100-block chain:
|
|
this->mineBlocks(COINBASE_MATURITY);
|
|
|
|
{
|
|
LOCK(::cs_main);
|
|
assert(
|
|
m_node.chainman->ActiveChain().Tip()->GetBlockHash().ToString() ==
|
|
"571d80a9967ae599cec0448b0b0ba1cfb606f584d8069bd7166b86854ba7a191");
|
|
}
|
|
}
|
|
|
|
void TestChain100Setup::mineBlocks(int num_blocks)
|
|
{
|
|
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
|
|
for (int i = 0; i < num_blocks; i++) {
|
|
std::vector<CMutableTransaction> noTxns;
|
|
CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey);
|
|
SetMockTime(GetTime() + 1);
|
|
m_coinbase_txns.push_back(b.vtx[0]);
|
|
}
|
|
}
|
|
|
|
CBlock TestChain100Setup::CreateBlock(
|
|
const std::vector<CMutableTransaction>& txns,
|
|
const CScript& scriptPubKey,
|
|
Chainstate& chainstate)
|
|
{
|
|
CBlock block = BlockAssembler{chainstate, nullptr}.CreateNewBlock(scriptPubKey)->block;
|
|
|
|
Assert(block.vtx.size() == 1);
|
|
for (const CMutableTransaction& tx : txns) {
|
|
block.vtx.push_back(MakeTransactionRef(tx));
|
|
}
|
|
RegenerateCommitments(block, *Assert(m_node.chainman));
|
|
|
|
while (!CheckProofOfWork(block.GetHash(), block.nBits, m_node.chainman->GetConsensus())) ++block.nNonce;
|
|
|
|
return block;
|
|
}
|
|
|
|
CBlock TestChain100Setup::CreateAndProcessBlock(
|
|
const std::vector<CMutableTransaction>& txns,
|
|
const CScript& scriptPubKey,
|
|
Chainstate* chainstate)
|
|
{
|
|
if (!chainstate) {
|
|
chainstate = &Assert(m_node.chainman)->ActiveChainstate();
|
|
}
|
|
|
|
CBlock block = this->CreateBlock(txns, scriptPubKey, *chainstate);
|
|
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
|
|
Assert(m_node.chainman)->ProcessNewBlock(shared_pblock, true, true, nullptr);
|
|
|
|
return block;
|
|
}
|
|
|
|
std::pair<CMutableTransaction, CAmount> TestChain100Setup::CreateValidTransaction(const std::vector<CTransactionRef>& input_transactions,
|
|
const std::vector<COutPoint>& inputs,
|
|
int input_height,
|
|
const std::vector<CKey>& input_signing_keys,
|
|
const std::vector<CTxOut>& outputs,
|
|
const std::optional<CFeeRate>& feerate,
|
|
const std::optional<uint32_t>& fee_output)
|
|
{
|
|
CMutableTransaction mempool_txn;
|
|
mempool_txn.vin.reserve(inputs.size());
|
|
mempool_txn.vout.reserve(outputs.size());
|
|
|
|
for (const auto& outpoint : inputs) {
|
|
mempool_txn.vin.emplace_back(outpoint, CScript(), MAX_BIP125_RBF_SEQUENCE);
|
|
}
|
|
mempool_txn.vout = outputs;
|
|
|
|
// - Add the signing key to a keystore
|
|
FillableSigningProvider keystore;
|
|
for (const auto& input_signing_key : input_signing_keys) {
|
|
keystore.AddKey(input_signing_key);
|
|
}
|
|
// - Populate a CoinsViewCache with the unspent output
|
|
CCoinsView coins_view;
|
|
CCoinsViewCache coins_cache(&coins_view);
|
|
for (const auto& input_transaction : input_transactions) {
|
|
AddCoins(coins_cache, *input_transaction.get(), input_height);
|
|
}
|
|
// Build Outpoint to Coin map for SignTransaction
|
|
std::map<COutPoint, Coin> input_coins;
|
|
CAmount inputs_amount{0};
|
|
for (const auto& outpoint_to_spend : inputs) {
|
|
// - Use GetCoin to properly populate utxo_to_spend,
|
|
Coin utxo_to_spend;
|
|
assert(coins_cache.GetCoin(outpoint_to_spend, utxo_to_spend));
|
|
input_coins.insert({outpoint_to_spend, utxo_to_spend});
|
|
inputs_amount += utxo_to_spend.out.nValue;
|
|
}
|
|
// - Default signature hashing type
|
|
int nHashType = SIGHASH_ALL;
|
|
std::map<int, bilingual_str> input_errors;
|
|
assert(SignTransaction(mempool_txn, &keystore, input_coins, nHashType, input_errors));
|
|
CAmount current_fee = inputs_amount - std::accumulate(outputs.begin(), outputs.end(), CAmount(0),
|
|
[](const CAmount& acc, const CTxOut& out) {
|
|
return acc + out.nValue;
|
|
});
|
|
// Deduct fees from fee_output to meet feerate if set
|
|
if (feerate.has_value()) {
|
|
assert(fee_output.has_value());
|
|
assert(fee_output.value() < mempool_txn.vout.size());
|
|
CAmount target_fee = feerate.value().GetFee(GetVirtualTransactionSize(CTransaction{mempool_txn}));
|
|
CAmount deduction = target_fee - current_fee;
|
|
if (deduction > 0) {
|
|
// Only deduct fee if there's anything to deduct. If the caller has put more fees than
|
|
// the target feerate, don't change the fee.
|
|
mempool_txn.vout[fee_output.value()].nValue -= deduction;
|
|
// Re-sign since an output has changed
|
|
input_errors.clear();
|
|
assert(SignTransaction(mempool_txn, &keystore, input_coins, nHashType, input_errors));
|
|
current_fee = target_fee;
|
|
}
|
|
}
|
|
return {mempool_txn, current_fee};
|
|
}
|
|
|
|
CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(const std::vector<CTransactionRef>& input_transactions,
|
|
const std::vector<COutPoint>& inputs,
|
|
int input_height,
|
|
const std::vector<CKey>& input_signing_keys,
|
|
const std::vector<CTxOut>& outputs,
|
|
bool submit)
|
|
{
|
|
CMutableTransaction mempool_txn = CreateValidTransaction(input_transactions, inputs, input_height, input_signing_keys, outputs, std::nullopt, std::nullopt).first;
|
|
// If submit=true, add transaction to the mempool.
|
|
if (submit) {
|
|
LOCK(cs_main);
|
|
const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(mempool_txn));
|
|
assert(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
|
|
}
|
|
return mempool_txn;
|
|
}
|
|
|
|
CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactionRef input_transaction,
|
|
uint32_t input_vout,
|
|
int input_height,
|
|
CKey input_signing_key,
|
|
CScript output_destination,
|
|
CAmount output_amount,
|
|
bool submit)
|
|
{
|
|
COutPoint input{input_transaction->GetHash(), input_vout};
|
|
CTxOut output{output_amount, output_destination};
|
|
return CreateValidMempoolTransaction(/*input_transactions=*/{input_transaction},
|
|
/*inputs=*/{input},
|
|
/*input_height=*/input_height,
|
|
/*input_signing_keys=*/{input_signing_key},
|
|
/*outputs=*/{output},
|
|
/*submit=*/submit);
|
|
}
|
|
|
|
std::vector<CTransactionRef> TestChain100Setup::PopulateMempool(FastRandomContext& det_rand, size_t num_transactions, bool submit)
|
|
{
|
|
std::vector<CTransactionRef> mempool_transactions;
|
|
std::deque<std::pair<COutPoint, CAmount>> unspent_prevouts;
|
|
std::transform(m_coinbase_txns.begin(), m_coinbase_txns.end(), std::back_inserter(unspent_prevouts),
|
|
[](const auto& tx){ return std::make_pair(COutPoint(tx->GetHash(), 0), tx->vout[0].nValue); });
|
|
while (num_transactions > 0 && !unspent_prevouts.empty()) {
|
|
// The number of inputs and outputs are random, between 1 and 24.
|
|
CMutableTransaction mtx = CMutableTransaction();
|
|
const size_t num_inputs = det_rand.randrange(24) + 1;
|
|
CAmount total_in{0};
|
|
for (size_t n{0}; n < num_inputs; ++n) {
|
|
if (unspent_prevouts.empty()) break;
|
|
const auto& [prevout, amount] = unspent_prevouts.front();
|
|
mtx.vin.emplace_back(prevout, CScript());
|
|
total_in += amount;
|
|
unspent_prevouts.pop_front();
|
|
}
|
|
const size_t num_outputs = det_rand.randrange(24) + 1;
|
|
const CAmount fee = 100 * det_rand.randrange(30);
|
|
const CAmount amount_per_output = (total_in - fee) / num_outputs;
|
|
for (size_t n{0}; n < num_outputs; ++n) {
|
|
CScript spk = CScript() << CScriptNum(num_transactions + n);
|
|
mtx.vout.emplace_back(amount_per_output, spk);
|
|
}
|
|
CTransactionRef ptx = MakeTransactionRef(mtx);
|
|
mempool_transactions.push_back(ptx);
|
|
if (amount_per_output > 3000) {
|
|
// If the value is high enough to fund another transaction + fees, keep track of it so
|
|
// it can be used to build a more complex transaction graph. Insert randomly into
|
|
// unspent_prevouts for extra randomness in the resulting structures.
|
|
for (size_t n{0}; n < num_outputs; ++n) {
|
|
unspent_prevouts.emplace_back(COutPoint(ptx->GetHash(), n), amount_per_output);
|
|
std::swap(unspent_prevouts.back(), unspent_prevouts[det_rand.randrange(unspent_prevouts.size())]);
|
|
}
|
|
}
|
|
if (submit) {
|
|
LOCK2(cs_main, m_node.mempool->cs);
|
|
LockPoints lp;
|
|
m_node.mempool->addUnchecked(CTxMemPoolEntry(ptx, /*fee=*/(total_in - num_outputs * amount_per_output),
|
|
/*time=*/0, /*entry_height=*/1, /*entry_sequence=*/0,
|
|
/*spends_coinbase=*/false, /*sigops_cost=*/4, lp));
|
|
}
|
|
--num_transactions;
|
|
}
|
|
return mempool_transactions;
|
|
}
|
|
|
|
void TestChain100Setup::MockMempoolMinFee(const CFeeRate& target_feerate)
|
|
{
|
|
LOCK2(cs_main, m_node.mempool->cs);
|
|
// Transactions in the mempool will affect the new minimum feerate.
|
|
assert(m_node.mempool->size() == 0);
|
|
// The target feerate cannot be too low...
|
|
// ...otherwise the transaction's feerate will need to be negative.
|
|
assert(target_feerate > m_node.mempool->m_opts.incremental_relay_feerate);
|
|
// ...otherwise this is not meaningful. The feerate policy uses the maximum of both feerates.
|
|
assert(target_feerate > m_node.mempool->m_opts.min_relay_feerate);
|
|
|
|
// Manually create an invalid transaction. Manually set the fee in the CTxMemPoolEntry to
|
|
// achieve the exact target feerate.
|
|
CMutableTransaction mtx = CMutableTransaction();
|
|
mtx.vin.emplace_back(COutPoint{Txid::FromUint256(g_insecure_rand_ctx.rand256()), 0});
|
|
mtx.vout.emplace_back(1 * COIN, GetScriptForDestination(WitnessV0ScriptHash(CScript() << OP_TRUE)));
|
|
const auto tx{MakeTransactionRef(mtx)};
|
|
LockPoints lp;
|
|
// The new mempool min feerate is equal to the removed package's feerate + incremental feerate.
|
|
const auto tx_fee = target_feerate.GetFee(GetVirtualTransactionSize(*tx)) -
|
|
m_node.mempool->m_opts.incremental_relay_feerate.GetFee(GetVirtualTransactionSize(*tx));
|
|
m_node.mempool->addUnchecked(CTxMemPoolEntry(tx, /*fee=*/tx_fee,
|
|
/*time=*/0, /*entry_height=*/1, /*entry_sequence=*/0,
|
|
/*spends_coinbase=*/true, /*sigops_cost=*/1, lp));
|
|
m_node.mempool->TrimToSize(0);
|
|
assert(m_node.mempool->GetMinFee() == target_feerate);
|
|
}
|
|
/**
|
|
* @returns a real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af)
|
|
* with 9 txs.
|
|
*/
|
|
CBlock getBlock13b8a()
|
|
{
|
|
CBlock block;
|
|
DataStream stream{
|
|
ParseHex("0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb6800000000000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558da2fdb261b4d4c86041b1ab1bf930901000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0146ffffffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf254bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada902780da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100dab24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd022100fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb401ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369ed2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98ec706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21adc6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e8625a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d898235e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff0280969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd388ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c52292d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2fafd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae740605658022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e01003614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b48304502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddcce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad56529371864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd90111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d001410443bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffffffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a12322d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70ae67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b95600db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b2179010000008b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3ca2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b491d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144af553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1af03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d98a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a038fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f63512850811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e91349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e8860c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb6042034aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000"),
|
|
};
|
|
stream >> TX_WITH_WITNESS(block);
|
|
return block;
|
|
}
|