// Copyright (c) 2011-2021 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 <chainlock/chainlock.h>
#include <chainlock/handler.h>
#include <chainparams.h>
#include <consensus/consensus.h>
#include <consensus/merkle.h>
#include <consensus/params.h>
#include <consensus/validation.h>
#include <deploymentstatus.h>
#include <crypto/sha256.h>
#include <crypto/x11/dispatch.h>
#include <index/txindex.h>
#include <init.h>
#include <interfaces/chain.h>
#include <net.h>
#include <net_processing.h>
#include <noui.h>
#include <node/blockstorage.h>
#include <node/chainstate.h>
#include <node/miner.h>
#include <node/sync_manager.h>
#include <policy/fees.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/index.h>
#include <test/util/net.h>
#include <test/util/txmempool.h>
#include <txdb.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/url.h>
#include <util/vector.h>
#include <validation.h>
#include <validationinterface.h>
#include <walletinitinterface.h>

#include <active/context.h>
#include <bls/bls.h>
#include <coinjoin/coinjoin.h>
#include <coinjoin/walletman.h>
#include <evo/cbtx.h>
#include <evo/chainhelper.h>
#include <evo/deterministicmns.h>
#include <evo/evodb.h>
#include <evo/simplifiedmns.h>
#include <evo/specialtx.h>
#include <evo/specialtxman.h>
#include <flat-database.h>
#include <governance/governance.h>
#include <llmq/context.h>
#include <llmq/signing.h>
#include <masternode/meta.h>
#include <masternode/sync.h>
#include <netfulfilledman.h>
#include <spork.h>
#include <stats/client.h>

#ifdef ENABLE_WALLET
#include <interfaces/coinjoin.h>
#include <interfaces/wallet.h>
#endif // ENABLE_WALLET

#include <algorithm>
#include <memory>
#include <stdexcept>

using node::BlockAssembler;
using node::CalculateCacheSizes;
using node::DashChainstateSetup;
using node::DashChainstateSetupClose;
using node::DEFAULT_ADDRESSINDEX;
using node::DEFAULT_SPENTINDEX;
using node::DEFAULT_TIMESTAMPINDEX;
using node::LoadChainstate;
using node::NodeContext;
using node::VerifyLoadedChainstate;
using node::fPruneMode;
using node::fReindex;

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;
}

std::unique_ptr<PeerManager> MakePeerManager(CConnman& connman,
                                             NodeContext& node,
                                             BanMan* banman,
                                             const CChainParams& chainparams,
                                             bool ignore_incoming_txs)
{
    return PeerManager::make(chainparams, connman, *node.addrman, banman, *node.dstxman, *node.chainman, *node.mempool, *node.mn_metaman,
                             *node.mn_sync, *node.govman, *node.sporkman, *node.chainlocks, *node.clhandler, node.active_ctx, node.dmnman, node.cj_walletman,
                             node.llmq_ctx, node.observer_ctx, ignore_incoming_txs);
}

void DashChainstateSetup(ChainstateManager& chainman,
                         NodeContext& node,
                         bool llmq_dbs_in_memory,
                         bool llmq_dbs_wipe,
                         const Consensus::Params& consensus_params)
{
    DashChainstateSetup(chainman, *Assert(node.govman.get()), *Assert(node.mn_metaman.get()), *Assert(node.mn_sync.get()),
                        *Assert(node.sporkman.get()), *Assert(node.chainlocks), node.chain_helper, node.dmnman, *node.evodb,
                        node.llmq_ctx, Assert(node.mempool.get()), node.args->GetDataDirNet(), llmq_dbs_in_memory, llmq_dbs_wipe,
                        llmq::DEFAULT_BLSCHECK_THREADS, llmq::DEFAULT_MAX_RECOVERED_SIGS_AGE, consensus_params);
}

void DashChainstateSetupClose(NodeContext& node)
{
    DashChainstateSetupClose(node.chain_helper, node.dmnman, node.llmq_ctx,
                             Assert(node.mempool.get()));
}

BasicTestingSetup::BasicTestingSetup(const std::string& chainName, 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(chainName);
    SeedInsecureRand();
    if (G_TEST_LOG_FUN) LogInstance().PushBackCallback(G_TEST_LOG_FUN);
    InitLogging(*m_node.args);
    AppInitParameterInteraction(*m_node.args);
    LogInstance().StartLogging();
    SapphireAutoDetect();
    SHA256AutoDetect();
    ECC_Start();
    BLSInit();
    SetupEnvironment();
    SetupNetworking();
    InitSignatureCache();
    InitScriptExecutionCache();

    m_node.chain = interfaces::MakeChain(m_node);
    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));

    std::string sem_str = m_args.GetArg("-socketevents", DEFAULT_SOCKETEVENTS);
    ::g_socket_events_mode = SEMFromString(sem_str);
    if (::g_socket_events_mode == SocketEventsMode::Unknown) {
        throw std::runtime_error(
            strprintf("Invalid -socketevents ('%s') specified. Only these modes are supported: %s",
                      sem_str, GetSupportedSocketEventsStr()));
    }

    {
        auto stats_client = StatsdClient::make(*m_node.args);
        if (!stats_client) {
            throw std::runtime_error{strprintf("Cannot init Statsd client (%s)", util::ErrorString(stats_client).original)};
        }
        ::g_stats_client = std::move(*stats_client);
    }

    m_node.connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman); // Deterministic randomness for tests.

    fCheckBlockIndex = true;

    m_node.mn_metaman = std::make_unique<CMasternodeMetaMan>();
    m_node.netfulfilledman = std::make_unique<CNetFulfilledRequestManager>();
    m_node.sporkman = std::make_unique<CSporkManager>();
    m_node.chainlocks = std::make_unique<chainlock::Chainlocks>(*m_node.sporkman);
    m_node.evodb = std::make_unique<CEvoDB>(util::DbWrapperParams{.path = m_node.args->GetDataDirNet(), .memory = true, .wipe = true});

    static bool noui_connected = false;
    if (!noui_connected) {
        noui_connect();
        noui_connected = true;
    }

    bls::bls_legacy_scheme.store(true);
}

BasicTestingSetup::~BasicTestingSetup()
{
    SetMockTime(0s); // Reset mocktime for following tests
    LogInstance().DisconnectTestLogger();
    fs::remove_all(m_path_root);
    gArgs.ClearArgs();

    m_node.evodb.reset();
    m_node.sporkman.reset();
    m_node.netfulfilledman.reset();
    m_node.mn_metaman.reset();
    m_node.connman.reset();
    ::g_stats_client.reset();
    ::g_socket_events_mode = SocketEventsMode::Unknown;
    m_node.addrman.reset();
    m_node.netgroupman.reset();
    m_node.args = nullptr;

    ECC_Stop();
}

ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::vector<const char*>& extra_args)
    : BasicTestingSetup(chainName, 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>();
    m_node.mempool = std::make_unique<CTxMemPool>(m_node.fee_estimator.get(), m_node.args->GetIntArg("-checkmempool", 1));

    m_cache_sizes = CalculateCacheSizes(m_args);

    m_node.chainman = std::make_unique<ChainstateManager>(chainparams);
    m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<CBlockTreeDB>(m_cache_sizes.block_tree_db, true);

    m_node.mn_sync = std::make_unique<CMasternodeSync>(std::make_unique<NodeSyncNotifierImpl>(*m_node.connman, *m_node.netfulfilledman));
    m_node.govman = std::make_unique<CGovernanceManager>(*m_node.mn_metaman, *m_node.chainman, m_node.dmnman, *m_node.mn_sync);

    m_node.clhandler = std::make_unique<chainlock::ChainlockHandler>(*m_node.chainlocks, *m_node.chainman, *m_node.mempool, *m_node.mn_sync);

    // Start script-checking threads. Set g_parallel_script_checks to true so they are used.
    constexpr int script_check_threads = 2;
    StartScriptCheckWorkerThreads(script_check_threads);
    g_parallel_script_checks = true;
}

ChainTestingSetup::~ChainTestingSetup()
{
    m_node.scheduler->stop();
    StopScriptCheckWorkerThreads();
    GetMainSignals().FlushBackgroundCallbacks();
    GetMainSignals().UnregisterBackgroundSignalScheduler();
    m_node.govman.reset();
    m_node.mn_sync.reset();
    m_node.chainman.reset();
    m_node.mempool.reset();
    m_node.fee_estimator.reset();
    m_node.scheduler.reset();
}

TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const char*>& extra_args)
    : ChainTestingSetup(chainName, extra_args)
{
    const CChainParams& chainparams = Params();
    // 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);

    auto maybe_load_error = LoadChainstate(fReindex.load(),
                                           *Assert(m_node.chainman.get()),
                                           *Assert(m_node.govman.get()),
                                           *Assert(m_node.mn_metaman.get()),
                                           *Assert(m_node.mn_sync.get()),
                                           *Assert(m_node.sporkman.get()),
                                           *Assert(m_node.chainlocks.get()),
                                           m_node.chain_helper,
                                           m_node.dmnman,
                                           m_node.evodb,
                                           m_node.llmq_ctx,
                                           Assert(m_node.mempool.get()),
                                           Assert(m_node.args)->GetDataDirNet(),
                                           fPruneMode,
                                           m_args.GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX),
                                           m_args.GetBoolArg("-spentindex", DEFAULT_SPENTINDEX),
                                           m_args.GetBoolArg("-timestampindex", DEFAULT_TIMESTAMPINDEX),
                                           chainparams.GetConsensus(),
                                           m_args.GetBoolArg("-reindex-chainstate", false),
                                           m_cache_sizes.block_tree_db,
                                           m_cache_sizes.coins_db,
                                           m_cache_sizes.coins,
                                           /*block_tree_db_in_memory=*/true,
                                           /*coins_db_in_memory=*/true,
                                           /*dash_dbs_in_memory=*/true,
                                           llmq::DEFAULT_BLSCHECK_THREADS,
                                           llmq::DEFAULT_MAX_RECOVERED_SIGS_AGE);
    assert(!maybe_load_error.has_value());

    auto maybe_verify_error = VerifyLoadedChainstate(
        *Assert(m_node.chainman),
        *Assert(m_node.evodb.get()),
        fReindex.load(),
        m_args.GetBoolArg("-reindex-chainstate", false),
        chainparams.GetConsensus(),
        m_args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS),
        m_args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL),
        /*get_unix_time_seconds=*/static_cast<int64_t(*)()>(GetTime),
        [](bool bls_state) {
            LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls_state);
        });
    assert(!maybe_verify_error.has_value());

    m_node.dstxman = std::make_unique<CDSTXManager>(*Assert(m_node.chainlocks));
#ifdef ENABLE_WALLET
    // The test suite doesn't use masternode mode, so we may initialize it
    m_node.cj_walletman = CJWalletManager::make(*m_node.chainman, *m_node.dmnman, *m_node.mn_metaman, *m_node.mempool,
                                                *m_node.mn_sync, *m_node.llmq_ctx->isman, /*relay_txes=*/true);
    assert(m_node.cj_walletman);

    // WalletInit::Construct()-like logic needed for wallet tests that run on
    // TestingSetup and its children (e.g. TestChain100Setup) instead of
    // WalletTestingSetup
    m_node.coinjoin_loader = interfaces::MakeCoinJoinLoader(m_node);

    auto wallet_loader = interfaces::MakeWalletLoader(*m_node.chain, *m_node.args, m_node, *m_node.coinjoin_loader);
    m_node.wallet_loader = wallet_loader.get();
    m_node.chain_clients.emplace_back(std::move(wallet_loader));
#endif // ENABLE_WALLET

    m_node.banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
    m_node.peerman = MakePeerManager(*m_node.connman, m_node, m_node.banman.get(), chainparams,
                                     /*ignore_incoming_txs=*/false);
    {
        CConnman::Options options;
        options.m_msgproc = m_node.peerman.get();
        options.socketEventsMode = ::g_socket_events_mode;
        m_node.connman->Init(options);
    }

    BlockValidationState state;
    if (!m_node.chainman->ActiveChainstate().ActivateBestChain(state)) {
        throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString()));
    }
}

TestingSetup::~TestingSetup()
{
    m_node.peerman.reset();
    m_node.banman.reset();
#ifdef ENABLE_WALLET
    for (auto& client : m_node.chain_clients) {
        client.reset();
    }
    m_node.wallet_loader = nullptr;
    m_node.coinjoin_loader.reset();
    m_node.cj_walletman.reset();
#endif // ENABLE_WALLET
    m_node.dstxman.reset();

    // Interrupt() and PrepareShutdown() routines
    if (m_node.clhandler) {
        m_node.clhandler->Stop();
    }
    if (m_node.connman) {
        m_node.connman->Stop();
    }

    // DashChainstateSetup() is called by LoadChainstate() internally but
    // winding them down is our responsibility
    DashChainstateSetupClose(m_node);
}

TestChain100Setup::TestChain100Setup(const std::string& chain_name, const std::vector<const char*>& extra_args)
    : TestChainSetup{100, chain_name, extra_args}
{
}

TestChainSetup::TestChainSetup(int num_blocks, const std::string& chain_name, const std::vector<const char*>& extra_args)
    : TestingSetup{chain_name, extra_args}
{
    // The inherited mock timestamp predates the Cosanta regtest genesis
    // (nTime = 1618221600) by months, so mining fails time-too-new. Keep the
    // mock clock one minute behind the genesis instead: blocks then take the
    // canonical MTP+1 timestamps, and the ~1 minute drift stays within
    // MAX_POS_BLOCK_AHEAD_TIME (180s). The PoS allowance applies even to
    // these PoW blocks: on regtest the TESTDUMMY deployment signals version
    // bit 28, which collides with CBlockHeader::POS_BIT (0x10000000), so
    // ContextualCheckBlockHeader() classifies the templates as proof-of-stake
    SetMockTime(std::max<int64_t>(1598887952, Params().GenesisBlock().GetBlockTime() - 60));
    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 num_blocks length chain:
    this->mineBlocks(num_blocks);

    CCheckpointData checkpoints{
        {
            /*TestChainDATSetup=*/
            {   98, uint256S("0x3b5f67fa26da68346ac0d2cba05b25a01c2bb6b0bc3c26b1b4654e4046bcef53") },
            /*TestChain100Setup=*/
            {  100, uint256S("0x7d9cf98ecb9eccba615f88f0509e7abad1a6660cb6b3a91366f4082c9373d2cd") },
            /*TestChainDIP3BeforeActivationSetup=*/
            {  430, uint256S("0x4b3e51086bd4d0726ec06b2f4152c6518460dd10fd9a30148ca4acf514fd7546") },
            /*TestChainBRRBeforeActivationSetup=*/
            {  497, uint256S("0x07c890abcc4ad255d58a0461dc8c76dca0f03bd7b796adf25a1d720061507b18") },
            /*TestChainV19BeforeActivationSetup=*/
            {  494, uint256S("0x637640e17e8d63949fc325c5b5134514f51f64151f58a4ce74d36e2cb2fdb283") },
        }
    };

    {
        LOCK(::cs_main);
        const auto hash = checkpoints.mapCheckpoints.find(num_blocks);
        if (hash == checkpoints.mapCheckpoints.end()) {
            throw std::runtime_error(strprintf("TestChainSetup: no chain checkpoint defined for height %d", num_blocks));
        }
        const uint256 tip_hash = m_node.chainman->ActiveChain().Tip()->GetBlockHash();
        if (tip_hash != hash->second) {
            throw std::runtime_error(strprintf(
                "TestChainSetup: deterministic chain checkpoint mismatch at height %d: got %s, expected %s "
                "(update the checkpoint if the chain setup changed intentionally)",
                num_blocks, tip_hash.ToString(), hash->second.ToString()));
        }
    }

    // Initialize transaction index *after* chain has been constructed
    // (and after the checkpoint verification: a throw must not leak a
    // running global index into the next test case)
    g_txindex = std::make_unique<TxIndex>(1 << 20, true);
    assert(!g_txindex->BlockUntilSyncedToCurrentChain());
    if (!g_txindex->Start(m_node.chainman->ActiveChainstate())) {
        throw std::runtime_error("TxIndex::Start() failed.");
    }
    IndexWaitSynced(*g_txindex);

}

void TestChainSetup::mineBlocks(int num_blocks)
{
    CScript scriptPubKey = GetScriptForRawPubKey(coinbaseKey.GetPubKey());
    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]);
    }

    // Allow tx index to catch up with the block index.
    if (g_txindex) {
        IndexWaitSynced(*g_txindex);
    }
}

CBlock TestChainSetup::CreateAndProcessBlock(
    const std::vector<CMutableTransaction>& txns,
    const CScript& scriptPubKey,
    CChainState* 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, nullptr);

    return block;
}

CBlock TestChainSetup::CreateBlock(
    const std::vector<CMutableTransaction>& txns,
    const CScript& scriptPubKey,
    CChainState& chainstate)
{
    const CChainParams& chainparams = Params();
    CTxMemPool empty_pool;
    CBlock block = *BlockAssembler(chainstate, m_node, &empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;

    std::vector<CTransactionRef> llmqCommitments;
    for (const auto& tx : block.vtx) {
        if (tx->IsSpecialTxVersion() && tx->nType == TRANSACTION_QUORUM_COMMITMENT) {
            llmqCommitments.emplace_back(tx);
        }
    }

    // Replace mempool-selected txns with just coinbase plus passed-in txns:
    block.vtx.resize(1);
    Assert(block.vtx.size() == 1);

    // Re-add quorum commitments
    block.vtx.insert(block.vtx.end(), llmqCommitments.begin(), llmqCommitments.end());
    for (const CMutableTransaction& tx : txns) {
        block.vtx.push_back(MakeTransactionRef(tx));
    }

    // Manually update CbTx as we modified the block here
    if (block.vtx[0]->nType == TRANSACTION_COINBASE) {
        LOCK(cs_main);
        auto cbTx = GetTxPayload<CCbTx>(*block.vtx[0]);
        Assert(cbTx.has_value());
        BlockValidationState state;
        CDeterministicMNList mn_list;
        if (!chainstate.ChainHelper().special_tx->BuildNewListFromBlock(block, chainstate.m_chain.Tip(), chainstate.CoinsTip(), true, state, mn_list)) {
            Assert(false);
        }
        if (!CalcCbTxMerkleRootMNList(cbTx->merkleRootMNList, mn_list.to_sml(), state)) {
            Assert(false);
        }
        if (!CalcCbTxMerkleRootQuorums(block, chainstate.m_chain.Tip(), *m_node.llmq_ctx->quorum_block_processor, cbTx->merkleRootQuorums, state)) {
            Assert(false);
        }
        CMutableTransaction tmpTx{*block.vtx[0]};
        SetTxPayload(tmpTx, *cbTx);
        block.vtx[0] = MakeTransactionRef(tmpTx);
    }

    // Create a valid coinbase and merkleRoot
    {
        LOCK(::cs_main);
        block.hashPrevBlock = chainstate.m_chain.Tip()->GetBlockHash();
        CMutableTransaction tx_coinbase{*block.vtx[0]};
        tx_coinbase.vin[0].scriptSig = CScript{} << (chainstate.m_chain.Height() + 1) << CScriptNum{1};
        block.vtx[0] = MakeTransactionRef(std::move(tx_coinbase));
        block.hashMerkleRoot = BlockMerkleRoot(block);
    }

    while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;

    CBlock result = block;
    return result;
}

CMutableTransaction TestChainSetup::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;
}

TestChainSetup::~TestChainSetup()
{
    // Allow tx index to catch up with the block index cause otherwise
    // we might be destroying it while scheduler still has some work for it
    // e.g. via BlockConnected signal
    IndexWaitSynced(*g_txindex);
    g_txindex->Interrupt();
    g_txindex->Stop();
    SyncWithValidationInterfaceQueue();
    g_txindex.reset();
}

std::vector<CTransactionRef> TestChainSetup::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;
        // Approximately 1000sat "fee," equal output amounts.
        const CAmount amount_per_output = (total_in - 1000) / 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 > 2000) {
            // 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(m_node.mempool->cs, cs_main);
            LockPoints lp;
            m_node.mempool->addUnchecked(CTxMemPoolEntry(ptx, 1000, 0, 1, false, 4, lp));
        }
        --num_transactions;
    }
    return mempool_transactions;
}

/**
 * @returns a real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af)
 *      with 9 txs.
 */
CBlock getBlock13b8a()
{
    CBlock block;
    CDataStream stream(ParseHex(" "), SER_NETWORK, PROTOCOL_VERSION);
    stream >> block;
    return block;
}
