diff --git a/src/config.h b/src/config.h --- a/src/config.h +++ b/src/config.h @@ -110,7 +110,7 @@ void SetRPCCORSDomain(std::string corsDomain) override{}; std::string GetRPCCORSDomain() const override { return ""; }; -private: +protected: std::unique_ptr chainParams; }; diff --git a/src/test/checkpoints_tests.cpp b/src/test/checkpoints_tests.cpp --- a/src/test/checkpoints_tests.cpp +++ b/src/test/checkpoints_tests.cpp @@ -9,13 +9,17 @@ #include "checkpoints.h" -#include "chainparams.h" +#include "chainparams.cpp" +#include "config.h" +#include "consensus/validation.h" +#include "policy/policy.h" #include "test/test_bitcoin.h" #include "uint256.h" +#include "validation.h" #include -BOOST_FIXTURE_TEST_SUITE(checkpoints_tests, BasicTestingSetup) +BOOST_FIXTURE_TEST_SUITE(checkpoints_tests, TestingSetup) BOOST_AUTO_TEST_CASE(sanity) { const auto params = CreateChainParams(CBaseChainParams::MAIN); @@ -36,4 +40,190 @@ BOOST_CHECK(Checkpoints::CheckBlock(checkpoints, 134444 + 1, p11111)); } +BOOST_AUTO_TEST_CASE(ban_fork_at_genesis_block) { + DummyConfig config; + + // Sanity check that a checkpoint exists at the genesis block + auto checkpoints = config.GetChainParams().Checkpoints().mapCheckpoints; + assert(checkpoints.find(0) != checkpoints.end()); + + // Another precomputed genesis block (height 0) should conflict with the + // regnet genesis block checkpoint, but not be accepted or stored in memory + CBlockHeader header; + CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); + stream << ParseHex( + "0100000000000000000000000000000000000000000000000000000000000000000000" + "003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adbe5" + "494dffff7f200200000001010000000100000000000000000000000000000000000000" + "00000000000000000000000000ffffffff4d04ffff001d0104455468652054696d6573" + "2030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66" + "207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01" + "000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f" + "61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f" + "ac00000000"); + stream >> header; + + // Header should not be accepted + CValidationState state; + CBlockHeader invalid; + const CBlockIndex *pindex = nullptr; + BOOST_CHECK( + !ProcessNewBlockHeaders(config, {header}, state, &pindex, &invalid)); + BOOST_CHECK(state.IsInvalid()); + BOOST_CHECK(pindex == nullptr); + BOOST_CHECK(invalid.GetHash() == header.GetHash()); + + // Sanity check to ensure header was not saved in memory + BOOST_CHECK(mapBlockIndex.find(header.GetHash()) == mapBlockIndex.end()); +} + +class TestChainParams : public CMainParams { +public: + TestChainParams() : CMainParams() { + checkpointData = {.mapCheckpoints = { + {2, uint256S("000000006a625f06636b8bb6ac7b960a8d0" + "3705d1ace08b1a19da3fdcc99ddbd")}, + }}; + } +}; + +class TestConfig : public DummyConfig { +public: + TestConfig() : DummyConfig() { + chainParams = std::unique_ptr(new TestChainParams()); + } + + uint64_t GetMaxBlockSize() const override { return DEFAULT_MAX_BLOCK_SIZE; } +}; + +/** + * This test has 4 precomputed blocks mined ontop of the genesis block: + * G ---> A ---> AA (checkpointed) + * \ \ + * \--> B \-> AB + * After the node has accepted only A and AA, these rejects should occur: + * * B should be rejected for forking prior to an accepted checkpoint + * * AB should be rejected for forking at an accepted checkpoint + */ +BOOST_AUTO_TEST_CASE(ban_fork_prior_to_and_at_checkpoints) { + TestConfig config; + + CValidationState state; + CBlockHeader invalid; + const CBlockIndex *pindex = nullptr; + + CBlockHeader headerG; + CDataStream stream( + ParseHex( + "010000000000000000000000000000000000000000000000000000000000000000" + "0000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e" + "5e4a29ab5f49ffff001d1dac2b7c01010000000100000000000000000000000000" + "00000000000000000000000000000000000000ffffffff4d04ffff001d01044554" + "68652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f" + "6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e" + "6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7" + "105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de" + "5c384df7ba0b8d578a4c702b6bf11d5fac00000000"), + SER_NETWORK, PROTOCOL_VERSION); + stream >> headerG; + BOOST_CHECK(headerG.GetHash() == + uint256S("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f" + "1b60a8ce26f")); + BOOST_CHECK( + ProcessNewBlockHeaders(config, {headerG}, state, &pindex, &invalid)); + pindex = nullptr; + + CBlockHeader headerA, headerB, headerAA, headerAB; + stream = CDataStream( + ParseHex( + "010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000" + "000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e85723" + "3e0e61bc6649ffff001d01e3629901010000000100000000000000000000000000" + "00000000000000000000000000000000000000ffffffff0704ffff001d0104ffff" + "ffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390" + "813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166" + "bf621e73a82cbf2342c858eeac00000000"), + SER_NETWORK, PROTOCOL_VERSION); + stream >> headerA; + BOOST_CHECK(headerA.GetHash() == + uint256S("00000000839a8e6886ab5951d76f411475428afc90947ee320161" + "bbf18eb6048")); + + stream = CDataStream( + ParseHex( + "010000004860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a8300" + "000000d5fdcc541e25de1c7a5addedf24858b8bb665c9f36ef744ee42c316022c9" + "0f9bb0bc6649ffff001d08d2bd6101010000000100000000000000000000000000" + "00000000000000000000000000000000000000ffffffff0704ffff001d010bffff" + "ffff0100f2052a010000004341047211a824f55b505228e4c3d5194c1fcfaa15a4" + "56abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b4" + "17ab79a0fcae412ae3316b77ac00000000"), + SER_NETWORK, PROTOCOL_VERSION); + stream >> headerAA; + BOOST_CHECK(headerAA.GetHash() == + uint256S("000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da" + "3fdcc99ddbd")); + + stream = CDataStream( + ParseHex( + "000000206fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000" + "000000bff4e0fd76ec3e9c8853811dec34dda8d5debb24d4113d94235fd4b24bb2" + "92981b70995cffff001d4e6e050001020000000100000000000000000000000000" + "00000000000000000000000000000000000000ffffffff0d51026302082f454233" + "322e302fffffffff0100f2052a01000000232103c91f2fa16c94c92d08629eeb8f" + "d681658d49f2b3016b13336d67d79f858dbc71ac000000001"), + SER_NETWORK, PROTOCOL_VERSION); + stream >> headerB; + + stream = CDataStream( + ParseHex( + "000000204860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a8300" + "0000003800b1dd09f3f1a1c9e62ce8dca6d1e6caacc9a02d178ef6ad95527b49ff" + "863f8282995cffff001d2cc70f0001020000000100000000000000000000000000" + "00000000000000000000000000000000000000ffffffff0d52024902082f454233" + "322e302fffffffff0100f2052a010000002321020a56690eb0e2454c1f362d3599" + "89198a0b23505578be4164a65521ee7751eb1dac00000000"), + SER_NETWORK, PROTOCOL_VERSION); + stream >> headerAB; + + // Headers A and AA should be accepted + BOOST_CHECK( + ProcessNewBlockHeaders(config, {headerA}, state, &pindex, &invalid)); + BOOST_CHECK(state.IsValid()); + BOOST_CHECK(pindex != nullptr); + pindex = nullptr; + BOOST_CHECK(invalid.IsNull()); + + BOOST_CHECK( + ProcessNewBlockHeaders(config, {headerAA}, state, &pindex, &invalid)); + BOOST_CHECK(state.IsValid()); + BOOST_CHECK(pindex != nullptr); + pindex = nullptr; + BOOST_CHECK(invalid.IsNull()); + + // Header B should be rejected + BOOST_CHECK( + !ProcessNewBlockHeaders(config, {headerB}, state, &pindex, &invalid)); + BOOST_CHECK(state.IsInvalid()); + BOOST_CHECK(state.GetRejectCode() == REJECT_CHECKPOINT); + BOOST_CHECK(state.GetRejectReason() == "bad-fork-prior-to-checkpoint"); + BOOST_CHECK(pindex == nullptr); + BOOST_CHECK(invalid.GetHash() == headerB.GetHash()); + + // Sanity check to ensure header was not saved in memory + BOOST_CHECK(mapBlockIndex.find(headerB.GetHash()) == mapBlockIndex.end()); + + // Header AB should be rejected + BOOST_CHECK( + !ProcessNewBlockHeaders(config, {headerAB}, state, &pindex, &invalid)); + BOOST_CHECK(state.IsInvalid()); + BOOST_CHECK(state.GetRejectCode() == REJECT_CHECKPOINT); + BOOST_CHECK(state.GetRejectReason() == "checkpoint mismatch"); + BOOST_CHECK(pindex == nullptr); + BOOST_CHECK(invalid.GetHash() == headerAB.GetHash()); + + // Sanity check to ensure header was not saved in memory + BOOST_CHECK(mapBlockIndex.find(headerAB.GetHash()) == mapBlockIndex.end()); +} + BOOST_AUTO_TEST_SUITE_END()