fork_chain_tests.cpp raw

   1  // Copyright (c) 2025 The Limenka developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #include <test/fork_util.h>
   6  
   7  #include <arith_uint256.h>
   8  #include <chainparams.h>
   9  #include <consensus/params.h>
  10  #include <kernel/chainparams.h>
  11  #include <chain.h>
  12  #include <deque>
  13  #include <pow.h>
  14  #include <uint256.h>
  15  #include <util/time.h>
  16  #include <validation.h>
  17  #include <addresstype.h>
  18  #include <key.h>
  19  #include <script/sign.h>
  20  #include <script/signingprovider.h>
  21  #include <script/solver.h>
  22  #include <test/util/transaction_utils.h>
  23  #include <util/chaintype.h>
  24  
  25  #include <boost/test/unit_test.hpp>
  26  
  27  // Test the CChainParams::Fork() factory directly — no fixture needed.
  28  BOOST_AUTO_TEST_SUITE(fork_chain_tests)
  29  
  30  BOOST_AUTO_TEST_CASE(fork_params_factory)
  31  {
  32      auto params = CChainParams::Fork();
  33      const auto& p = params->GetConsensus();
  34  
  35      BOOST_CHECK(params->GetChainType() == ChainType::FORK);
  36      BOOST_CHECK_EQUAL(p.nForkActivationMTP, int64_t(1792328400));
  37      BOOST_CHECK_EQUAL(p.nForkActivationBias, int64_t(30000));
  38      BOOST_CHECK_EQUAL(p.nForkMTPWindow, 101);
  39      BOOST_CHECK_EQUAL(p.nForkFutureLimit, int64_t(60));
  40      BOOST_CHECK_EQUAL(p.nForkIntervalTarget, 617);
  41      BOOST_CHECK_EQUAL(p.nForkDelaySteps, uint64_t{8589934592ull});
  42      BOOST_CHECK_GT(p.nForkKp, 0);
  43      BOOST_CHECK_GT(p.nForkKiUp, 0);
  44      BOOST_CHECK_GT(p.nForkKiDown, 0);
  45      BOOST_CHECK_GT(p.nForkKiDown, p.nForkKiUp);
  46  }
  47  
  48  BOOST_AUTO_TEST_CASE(fork_is_fork_active_mtp_gate)
  49  {
  50      auto params = CChainParams::Fork();
  51      const auto& p = params->GetConsensus();
  52  
  53      // Build a small index chain with known timestamps and check the
  54      // MTP-based gate: MTP101(prev) + 30000 >= 1792328400.
  55      // CBlockIndex is non-copyable/non-movable, so use a deque (pointer
  56      // stable on push_back).
  57      std::deque<CBlockIndex> blocks;
  58      for (int i = 0; i < 120; i++) {
  59          CBlockHeader h;
  60          h.nVersion = 4;
  61          h.nTime = 1792328400 - (120 - i) * 600 - 8 * 3600 + i * 600;
  62          blocks.emplace_back(h);
  63      }
  64      for (int i = 0; i < 120; i++) {
  65          blocks[i].nHeight = i;
  66          if (i > 0) blocks[i].pprev = &blocks[i - 1];
  67      }
  68      // The median of the last 101 sits ~50 blocks back: far below the
  69      // activation time -> inactive.
  70      BOOST_CHECK(!IsForkActive(&blocks[100], p));
  71      // A chain whose stamps are all well past the activation time ->
  72      // active.
  73      auto params2 = CChainParams::Fork();
  74      const_cast<Consensus::Params&>(params2->GetConsensus()).nForkActivationMTP = 0;
  75      const auto& p2 = params2->GetConsensus();
  76      BOOST_CHECK(IsForkActive(&blocks[100], p2));
  77      BOOST_CHECK(!IsForkActive(nullptr, p2));
  78  }
  79  
  80  BOOST_AUTO_TEST_CASE(fork_network_identity)
  81  {
  82      auto params = CChainParams::Fork();
  83      auto ms = params->MessageStart();
  84  
  85      // Own network identity (decision 7): distinct magic so fork blocks
  86      // never propagate onto the parent network.
  87      BOOST_CHECK_EQUAL(ms[0], uint8_t(0xfa));
  88      BOOST_CHECK_EQUAL(ms[1], uint8_t(0xbf));
  89      BOOST_CHECK_EQUAL(ms[2], uint8_t(0xd5));
  90      BOOST_CHECK_EQUAL(ms[3], uint8_t(0xc6));
  91      BOOST_CHECK_EQUAL(params->GetDefaultPort(), uint16_t(19444));
  92      BOOST_CHECK_EQUAL(params->Bech32HRP(), "bf");
  93  }
  94  
  95  BOOST_AUTO_TEST_CASE(fork_pow_limit_matches_mainnet)
  96  {
  97      auto params = CChainParams::Fork();
  98      const auto& cp = params->GetConsensus();
  99  
 100      arith_uint256 mainnet_limit = UintToArith256(
 101          uint256{"00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"});
 102      // The fork continues the parent chain's 600s cadence, so it uses the same
 103      // powLimit — no difficulty reset at activation.
 104      BOOST_CHECK_EQUAL(UintToArith256(cp.powLimit), mainnet_limit);
 105  }
 106  
 107  // The fork must inherit mainnet's pre-activation consensus parameters so it
 108  // stays on the parent chain until the MTP activation gate.
 109  BOOST_AUTO_TEST_CASE(fork_inherits_mainnet_consensus)
 110  {
 111      auto fork_params = CChainParams::Fork();
 112      auto mainnet_params = CChainParams::Main();
 113      const auto& f = fork_params->GetConsensus();
 114      const auto& m = mainnet_params->GetConsensus();
 115  
 116      // Buried soft-fork activation heights.
 117      BOOST_CHECK_EQUAL(f.BIP34Height, m.BIP34Height);
 118      BOOST_CHECK_EQUAL(f.BIP34Hash, m.BIP34Hash);
 119      BOOST_CHECK_EQUAL(f.BIP65Height, m.BIP65Height);
 120      BOOST_CHECK_EQUAL(f.BIP66Height, m.BIP66Height);
 121      BOOST_CHECK_EQUAL(f.CSVHeight, m.CSVHeight);
 122      BOOST_CHECK_EQUAL(f.SegwitHeight, m.SegwitHeight);
 123  
 124      // The two script flag exceptions (BIP16 and Taproot violating blocks).
 125      BOOST_CHECK_EQUAL(f.script_flag_exceptions.size(), m.script_flag_exceptions.size());
 126      BOOST_CHECK(f.script_flag_exceptions == m.script_flag_exceptions);
 127  
 128      // Chain work and assumevalid for reusing the parent chainstate.
 129      BOOST_CHECK_EQUAL(f.nMinimumChainWork, m.nMinimumChainWork);
 130      BOOST_CHECK_EQUAL(f.defaultAssumeValid, m.defaultAssumeValid);
 131  
 132      // powLimit matches mainnet (no fork difficulty reset).
 133      BOOST_CHECK_EQUAL(f.powLimit, m.powLimit);
 134  }
 135  
 136  
 137  // ---------------------------------------------------------------------------
 138  // Fork timestamp rules through the real ContextualCheckBlockHeader
 139  // ---------------------------------------------------------------------------
 140  
 141  BOOST_FIXTURE_TEST_CASE(fork_timestamp_rules, ForkTestingSetup)
 142  {
 143      // Build a 4-deep fork index chain with strictly increasing stamps.
 144      std::deque<CBlockIndex> blocks;
 145      const int64_t base = GetTime() - 3000;
 146      for (int i = 0; i < 4; i++) {
 147          CBlockHeader h;
 148          h.nVersion = 4;
 149          h.nTime = base + i * 600;
 150          blocks.emplace_back(h);
 151      }
 152      for (int i = 0; i < 4; i++) {
 153          blocks[i].nHeight = i;
 154          if (i > 0) blocks[i].pprev = &blocks[i - 1];
 155      }
 156  
 157      LOCK(cs_main);
 158      auto& chainman = *m_node.chainman;
 159      auto& blockman = m_node.chainman->m_blockman;
 160      const CBlockIndex* prev = &blocks[3];
 161  
 162      // Valid: strictly greater stamp, within 60s of now.
 163      {
 164          CBlockHeader h;
 165          h.nVersion = 4;
 166          h.nTime = prev->nTime + 1;
 167          BlockValidationState state;
 168          BOOST_CHECK(ContextualCheckBlockHeader(h, state, blockman, chainman, prev));
 169      }
 170  
 171      // Non-monotonic: nTime equal to the previous stamp.
 172      {
 173          CBlockHeader h;
 174          h.nVersion = 4;
 175          h.nTime = prev->nTime;
 176          BlockValidationState state;
 177          BOOST_CHECK(!ContextualCheckBlockHeader(h, state, blockman, chainman, prev));
 178          BOOST_CHECK_EQUAL(state.GetRejectReason(), "time-nonmonotonic");
 179      }
 180  
 181      // Future: nTime more than 60s ahead of now.
 182      {
 183          CBlockHeader h;
 184          h.nVersion = 4;
 185          h.nTime = GetTime() + 61;
 186          BlockValidationState state;
 187          BOOST_CHECK(!ContextualCheckBlockHeader(h, state, blockman, chainman, prev));
 188          BOOST_CHECK_EQUAL(state.GetRejectReason(), "time-too-new-fork");
 189      }
 190  }
 191  
 192  BOOST_AUTO_TEST_SUITE_END()
 193