mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-25 12:51:55 -05:00

Add a blocks_dir field to the BlockManager options. Move functions relying on the global gArgs to get the blocks_dir into the BlockManager class. This should eventually allow users of the BlockManager to not rely on the global Args and instead pass in their own options.
479 lines
25 KiB
C++
479 lines
25 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 <test/util/setup_common.h>
|
|
|
|
#include <kernel/validation_cache_sizes.h>
|
|
|
|
#include <addrman.h>
|
|
#include <banman.h>
|
|
#include <chainparams.h>
|
|
#include <common/url.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 <net.h>
|
|
#include <net_processing.h>
|
|
#include <node/blockstorage.h>
|
|
#include <node/chainstate.h>
|
|
#include <node/context.h>
|
|
#include <node/mempool_args.h>
|
|
#include <node/miner.h>
|
|
#include <node/validation_cache_args.h>
|
|
#include <noui.h>
|
|
#include <policy/fees.h>
|
|
#include <policy/fees_args.h>
|
|
#include <pow.h>
|
|
#include <rpc/blockchain.h>
|
|
#include <rpc/register.h>
|
|
#include <rpc/server.h>
|
|
#include <scheduler.h>
|
|
#include <script/sigcache.h>
|
|
#include <shutdown.h>
|
|
#include <streams.h>
|
|
#include <test/util/net.h>
|
|
#include <test/util/txmempool.h>
|
|
#include <timedata.h>
|
|
#include <txdb.h>
|
|
#include <txmempool.h>
|
|
#include <util/chaintype.h>
|
|
#include <util/strencodings.h>
|
|
#include <util/string.h>
|
|
#include <util/system.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::ValidationCacheSizes;
|
|
using node::ApplyArgsManOptions;
|
|
using node::BlockAssembler;
|
|
using node::BlockManager;
|
|
using node::CalculateCacheSizes;
|
|
using node::LoadChainstate;
|
|
using node::RegenerateCommitments;
|
|
using node::VerifyLoadedChainstate;
|
|
|
|
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
|
|
UrlDecodeFn* const URL_DECODE = nullptr;
|
|
|
|
FastRandomContext g_insecure_rand_ctx;
|
|
/** 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;
|
|
|
|
/** Return the unsigned from the environment var if available, otherwise 0 */
|
|
static uint256 GetUintFromEnv(const std::string& env_name)
|
|
{
|
|
const char* num = std::getenv(env_name.c_str());
|
|
if (!num) return {};
|
|
return uint256S(num);
|
|
}
|
|
|
|
void Seed(FastRandomContext& ctx)
|
|
{
|
|
// Should be enough to get the seed once for the process
|
|
static uint256 seed{};
|
|
static const std::string RANDOM_CTX_SEED{"RANDOM_CTX_SEED"};
|
|
if (seed.IsNull()) seed = GetUintFromEnv(RANDOM_CTX_SEED);
|
|
if (seed.IsNull()) seed = GetRandHash();
|
|
LogPrintf("%s: Setting random seed for current tests to %s=%s\n", __func__, RANDOM_CTX_SEED, seed.GetHex());
|
|
ctx = FastRandomContext(seed);
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os, const uint256& num)
|
|
{
|
|
os << num.ToString();
|
|
return os;
|
|
}
|
|
|
|
BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vector<const char*>& extra_args)
|
|
: m_path_root{fs::temp_directory_path() / "test_common_" PACKAGE_NAME / g_insecure_rand_ctx_temp_path.rand256().ToString()},
|
|
m_args{}
|
|
{
|
|
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");
|
|
fs::create_directories(m_path_root);
|
|
m_args.ForceSetArg("-datadir", fs::PathToString(m_path_root));
|
|
gArgs.ForceSetArg("-datadir", fs::PathToString(m_path_root));
|
|
gArgs.ClearPathCache();
|
|
{
|
|
SetupServerArgs(*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};
|
|
}
|
|
}
|
|
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>();
|
|
SetupEnvironment();
|
|
SetupNetworking();
|
|
|
|
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()
|
|
{
|
|
SetMockTime(0s); // Reset mocktime for following tests
|
|
LogInstance().DisconnectTestLogger();
|
|
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(); });
|
|
GetMainSignals().RegisterBackgroundSignalScheduler(*m_node.scheduler);
|
|
|
|
m_node.fee_estimator = std::make_unique<CBlockPolicyEstimator>(FeeestPath(*m_node.args));
|
|
m_node.mempool = std::make_unique<CTxMemPool>(MemPoolOptionsForTest(m_node));
|
|
|
|
m_cache_sizes = CalculateCacheSizes(m_args);
|
|
|
|
const ChainstateManager::Options chainman_opts{
|
|
.chainparams = chainparams,
|
|
.datadir = m_args.GetDataDirNet(),
|
|
.adjusted_time_callback = GetAdjustedTime,
|
|
.check_block_index = true,
|
|
};
|
|
const BlockManager::Options blockman_opts{
|
|
.chainparams = chainman_opts.chainparams,
|
|
.blocks_dir = m_args.GetBlocksDirPath(),
|
|
};
|
|
m_node.chainman = std::make_unique<ChainstateManager>(chainman_opts, blockman_opts);
|
|
m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<CBlockTreeDB>(DBParams{
|
|
.path = m_args.GetDataDirNet() / "blocks" / "index",
|
|
.cache_bytes = static_cast<size_t>(m_cache_sizes.block_tree_db),
|
|
.memory_only = true});
|
|
|
|
constexpr int script_check_threads = 2;
|
|
StartScriptCheckWorkerThreads(script_check_threads);
|
|
}
|
|
|
|
ChainTestingSetup::~ChainTestingSetup()
|
|
{
|
|
if (m_node.scheduler) m_node.scheduler->stop();
|
|
StopScriptCheckWorkerThreads();
|
|
GetMainSignals().FlushBackgroundCallbacks();
|
|
GetMainSignals().UnregisterBackgroundSignalScheduler();
|
|
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.scheduler.reset();
|
|
m_node.chainman.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 = node::fReindex;
|
|
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); // Deterministic randomness for tests.
|
|
m_node.peerman = PeerManager::make(*m_node.connman, *m_node.addrman,
|
|
m_node.banman.get(), *m_node.chainman,
|
|
*m_node.mempool, false);
|
|
{
|
|
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;
|
|
}
|
|
|
|
|
|
CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactionRef input_transaction,
|
|
int input_vout,
|
|
int input_height,
|
|
CKey input_signing_key,
|
|
CScript output_destination,
|
|
CAmount output_amount,
|
|
bool submit)
|
|
{
|
|
// Transaction we will submit to the mempool
|
|
CMutableTransaction mempool_txn;
|
|
|
|
// Create an input
|
|
COutPoint outpoint_to_spend(input_transaction->GetHash(), input_vout);
|
|
CTxIn input(outpoint_to_spend);
|
|
mempool_txn.vin.push_back(input);
|
|
|
|
// Create an output
|
|
CTxOut output(output_amount, output_destination);
|
|
mempool_txn.vout.push_back(output);
|
|
|
|
// Sign the transaction
|
|
// - Add the signing key to a keystore
|
|
FillableSigningProvider keystore;
|
|
keystore.AddKey(input_signing_key);
|
|
// - Populate a CoinsViewCache with the unspent output
|
|
CCoinsView coins_view;
|
|
CCoinsViewCache coins_cache(&coins_view);
|
|
AddCoins(coins_cache, *input_transaction.get(), input_height);
|
|
// - Use GetCoin to properly populate utxo_to_spend,
|
|
Coin utxo_to_spend;
|
|
assert(coins_cache.GetCoin(outpoint_to_spend, utxo_to_spend));
|
|
// - Then add it to a map to pass in to SignTransaction
|
|
std::map<COutPoint, Coin> input_coins;
|
|
input_coins.insert({outpoint_to_spend, utxo_to_spend});
|
|
// - 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));
|
|
|
|
// 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;
|
|
}
|
|
|
|
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.push_back(CTxIn(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.push_back(CTxOut(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.push_back(std::make_pair(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,
|
|
/*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_incremental_relay_feerate);
|
|
// ...otherwise this is not meaningful. The feerate policy uses the maximum of both feerates.
|
|
assert(target_feerate > m_node.mempool->m_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.push_back(CTxIn{COutPoint{g_insecure_rand_ctx.rand256(), 0}});
|
|
mtx.vout.push_back(CTxOut(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_incremental_relay_feerate.GetFee(GetVirtualTransactionSize(*tx));
|
|
m_node.mempool->addUnchecked(CTxMemPoolEntry(tx, /*fee=*/tx_fee,
|
|
/*time=*/0, /*entry_height=*/1,
|
|
/*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;
|
|
CDataStream stream(ParseHex("0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb6800000000000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558da2fdb261b4d4c86041b1ab1bf930901000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0146ffffffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf254bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada902780da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100dab24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd022100fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb401ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369ed2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98ec706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21adc6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e8625a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d898235e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff0280969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd388ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c52292d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2fafd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae740605658022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e01003614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b48304502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddcce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad56529371864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd90111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d001410443bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffffffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a12322d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70ae67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b95600db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b2179010000008b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3ca2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b491d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144af553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1af03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d98a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a038fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f63512850811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e91349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e8860c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb6042034aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000"), SER_NETWORK, PROTOCOL_VERSION);
|
|
stream >> block;
|
|
return block;
|
|
}
|