bitcoin/src/test/util/setup_common.cpp
glozow f34fe0806a
Merge bitcoin/bitcoin#31122: cluster mempool: Implement changeset interface for mempool
5736d1ddac tracing: pass if replaced by tx/pkg to tracepoint (0xb10c)
a4ec07f194 doc: add comments for CTxMemPool::ChangeSet (Suhas Daftuar)
83f814b1d1 Remove m_all_conflicts from SubPackageState (Suhas Daftuar)
d3c8e7dfb6 Ensure that we don't add duplicate transactions in rbf fuzz tests (Suhas Daftuar)
d7dc9fd2f7 Move CalculateChunksForRBF() to the mempool changeset (Suhas Daftuar)
284a1d33f1 Move prioritisation into changeset (Suhas Daftuar)
446b08b599 Don't distinguish between direct conflicts and all conflicts when doing cluster-size-2-rbf checks (Suhas Daftuar)
b53041021a Duplicate transactions are not permitted within a changeset (Suhas Daftuar)
b447416fdd Public mempool removal methods Assume() no changeset is outstanding (Suhas Daftuar)
2b30f4d36c Make RemoveStaged() private (Suhas Daftuar)
18829194ca Enforce that there is only one changeset at a time (Suhas Daftuar)
7fb62f7db6 Apply mempool changeset transactions directly into the mempool (Suhas Daftuar)
34b6c5833d Clean up FinalizeSubpackage to avoid workspace-specific information (Suhas Daftuar)
57983b8add Move LimitMempoolSize to take place outside FinalizeSubpackage (Suhas Daftuar)
01e145b975 Move changeset from workspace to subpackage (Suhas Daftuar)
802214c083 Introduce mempool changesets (Suhas Daftuar)
87d92fa340 test: Add unit test coverage of package rbf + prioritisetransaction (Suhas Daftuar)
15d982f91e Add package hash to package-rbf log message (Suhas Daftuar)

Pull request description:

  part of cluster mempool: #30289

  It became clear while working on cluster mempool that it would be helpful for transaction validation if we could consider a full set of proposed changes to the mempool -- consisting of a set of transactions to add, and a set of transactions (ie conflicts) to simultaneously remove -- and perform calculations on what the mempool would look like if the proposed changes were to be applied.  Two specific examples of where we'd like to do this:

  - Determining if ancestor/descendant/TRUC limits would be violated (in the future, cluster limits) if either a single transaction or a package of transactions were to be accepted
  - Determining if an RBF would make the mempool "better", however that idea is defined, both in the single transaction and package of transaction cases

  In preparation for cluster mempool, I have pulled this reworking of the mempool interface out of #28676 so it can be reviewed on its own.  I have not re-implemented ancestor/descendant limits to be run through the changeset, since with cluster mempool those limits will be going away, so this seems like wasted effort.  However, I have rebased #28676 on top of this branch so reviewers can see what the new mempool interface could look like in the cluster mempool setting.

  There are some minor behavior changes here, which I believe are inconsequential:
  - In the package validation setting, transactions would be added to the mempool before the `ConsensusScriptChecks()` are run. In theory, `ConsensusScriptChecks()` should always pass if the `PolicyScriptChecks()` have passed and it's just a belt-and-suspenders for us, but if somehow they were to diverge then there could be some small behavior change from adding transactions and then removing them, versus never adding them at all.
  - The error reporting on `CheckConflictTopology()` has slightly changed due to no longer distinguishing between direct conflicts and indirect conflicts. I believe this should be entirely inconsequential because there shouldn't be a logical difference between those two ideas from the perspective of this function, but I did have to update some error strings in some tests.
  - Because, in a package setting, RBFs now happen as part of the entire package being accepted, the logging has changed slightly because we do not know which transaction specifically evicted a given removed transaction.
    -  Specifically, the "package hash" is now used to reference the set of transactions that are being accepted, rather than any single txid. The log message relating to package RBF that happen in the `TXPACKAGES` category has been updated as well to include the package hash, so that it's possible to see which specific set of transactions are being referenced by that package hash.
    - Relatedly, the tracepoint logging in the package rbf case has been updated as well to reference the package hash, rather than a transaction hash.

ACKs for top commit:
  naumenkogs:
    ACK 5736d1ddac
  instagibbs:
    ACK 5736d1ddac
  ismaelsadeeq:
    reACK 5736d1ddac
  glozow:
    ACK 5736d1ddac

Tree-SHA512: 21810872e082920d337c89ac406085aa71c5f8e5151ab07aedf41e6601f60a909b22fbf462ef3b735d5d5881e9b76142c53957158e674dd5dfe6f6aabbdf630b
2024-11-20 07:48:29 -05:00

616 lines
31 KiB
C++

// Copyright (c) 2011-present 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 <test/util/setup_common.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/warnings.h>
#include <noui.h>
#include <policy/fees.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 namespace util::hex_literals;
using kernel::BlockTreeDB;
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;
constexpr inline auto TEST_DIR_PATH_ELEMENT{"test_common bitcoin"}; // Includes a space to catch possible path escape issues.
/** Random context to get unique temp data dirs. Separate from m_rng, which can be seeded from a const env var */
static FastRandomContext g_rng_temp_path;
static const bool g_rng_temp_path_init{[] {
// Must be initialized before any SeedRandomForTest
(void)g_rng_temp_path.rand64();
return true;
}()};
struct NetworkSetup
{
NetworkSetup()
{
Assert(SetupNetworking());
}
};
static NetworkSetup g_networksetup_instance;
void SetupCommonTestArgs(ArgsManager& argsman)
{
argsman.AddArg("-testdatadir", strprintf("Custom data directory (default: %s<random_string>)", fs::PathToString(fs::temp_directory_path() / TEST_DIR_PATH_ELEMENT / "")),
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, TestOpts opts)
: m_args{}
{
m_node.shutdown_signal = &m_interrupt;
m_node.shutdown_request = [this]{ return 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",
},
opts.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);
SetupCommonTestArgs(*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};
}
}
SeedRandomForTest(SeedRand::FIXED_SEED);
const std::string test_name{G_TEST_GET_FULL_NAME ? G_TEST_GET_FULL_NAME() : ""};
if (!m_node.args->IsArgSet("-testdatadir")) {
// To avoid colliding with a leftover prior datadir, and to allow
// tests, such as the fuzz tests to run in several processes at the
// same time, add a random element to the path. Keep it small enough to
// avoid a MAX_PATH violation on Windows.
const auto rand{HexStr(g_rng_temp_path.randbytes(10))};
m_path_root = fs::temp_directory_path() / TEST_DIR_PATH_ELEMENT / test_name / rand;
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);
m_path_lock = root_dir / TEST_DIR_PATH_ELEMENT / fs::PathFromString(test_name);
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);
if (G_TEST_LOG_FUN) LogInstance().PushBackCallback(G_TEST_LOG_FUN);
InitLogging(*m_node.args);
AppInitParameterInteraction(*m_node.args);
LogInstance().StartLogging();
m_node.warnings = std::make_unique<node::Warnings>();
m_node.kernel = std::make_unique<kernel::Context>();
m_node.ecc_context = std::make_unique<ECC_Context>();
SetupEnvironment();
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, TestOpts opts)
: BasicTestingSetup(chainType, opts)
{
const CChainParams& chainparams = Params();
// We have to run a scheduler thread to prevent ActivateBestChain
// from blocking due to queue overrun.
if (opts.setup_validation_interface) {
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));
}
bilingual_str error{};
m_node.mempool = std::make_unique<CTxMemPool>(MemPoolOptionsForTest(m_node), error);
Assert(error.empty());
m_node.warnings = std::make_unique<node::Warnings>();
m_cache_sizes = CalculateCacheSizes(m_args);
m_node.notifications = std::make_unique<KernelNotifications>(Assert(m_node.shutdown_request), m_node.exit_status, *Assert(m_node.warnings));
m_make_chainman = [this, &chainparams, opts] {
Assert(!m_node.chainman);
ChainstateManager::Options chainman_opts{
.chainparams = chainparams,
.datadir = m_args.GetDataDirNet(),
.check_block_index = 1,
.notifications = *m_node.notifications,
.signals = m_node.validation_signals.get(),
.worker_threads_num = 2,
};
if (opts.min_validation_cache) {
chainman_opts.script_execution_cache_bytes = 0;
chainman_opts.signature_cache_bytes = 0;
}
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_signal), chainman_opts, blockman_opts);
LOCK(m_node.chainman->GetMutex());
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,
});
};
m_make_chainman();
}
ChainTestingSetup::~ChainTestingSetup()
{
if (m_node.scheduler) m_node.scheduler->stop();
if (m_node.validation_signals) 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();
Assert(!m_node.fee_estimator); // Each test must create a local object, if they wish to use the fee_estimator
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.wipe_block_tree_db = m_args.GetBoolArg("-reindex", false);
options.wipe_chainstate_db = m_args.GetBoolArg("-reindex", false) || 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,
TestOpts opts)
: ChainTestingSetup(chainType, opts)
{
m_coins_db_in_memory = opts.coins_db_in_memory;
m_block_tree_db_in_memory = opts.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();
if (!opts.setup_net) return;
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, *m_node.warnings,
peerman_opts);
{
CConnman::Options options;
options.m_msgproc = m_node.peerman.get();
m_node.connman->Init(options);
}
}
TestChain100Setup::TestChain100Setup(
const ChainType chain_type,
TestOpts opts)
: TestingSetup{ChainType::REGTEST, opts}
{
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)
{
BlockAssembler::Options options;
CBlock block = BlockAssembler{chainstate, nullptr, options}.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
auto utxo_to_spend{coins_cache.GetCoin(outpoint_to_spend).value()};
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;
auto changeset = m_node.mempool->GetChangeSet();
changeset->StageAddition(ptx, /*fee=*/(total_in - num_outputs * amount_per_output),
/*time=*/0, /*entry_height=*/1, /*entry_sequence=*/0,
/*spends_coinbase=*/false, /*sigops_cost=*/4, lp);
changeset->Apply();
}
--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(m_rng.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));
{
auto changeset = m_node.mempool->GetChangeSet();
changeset->StageAddition(tx, /*fee=*/tx_fee,
/*time=*/0, /*entry_height=*/1, /*entry_sequence=*/0,
/*spends_coinbase=*/true, /*sigops_cost=*/1, lp);
changeset->Apply();
}
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{
"0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb6800000000000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558da2fdb261b4d4c86041b1ab1bf930901000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0146ffffffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf254bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada902780da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100dab24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd022100fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb401ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369ed2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98ec706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21adc6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e8625a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d898235e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff0280969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd388ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c52292d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2fafd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae740605658022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e01003614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b48304502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddcce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad56529371864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd90111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d001410443bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffffffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a12322d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70ae67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b95600db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b2179010000008b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3ca2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b491d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144af553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1af03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d98a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a038fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f63512850811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e91349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e8860c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb6042034aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000"_hex,
};
stream >> TX_WITH_WITNESS(block);
return block;
}
std::ostream& operator<<(std::ostream& os, const arith_uint256& num)
{
return os << num.ToString();
}
std::ostream& operator<<(std::ostream& os, const uint160& num)
{
return os << num.ToString();
}
std::ostream& operator<<(std::ostream& os, const uint256& num)
{
return os << num.ToString();
}