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