|
| 1 | +// Copyright (c) 2011-2014 The Bitcoin Core developers |
| 2 | +// Distributed under the MIT/X11 software license, see the accompanying |
| 3 | +// file COPYING or http://www.opensource.org/licenses/mit-license.php. |
| 4 | + |
| 5 | +#include "main.h" |
| 6 | +#include "miner.h" |
| 7 | +#include "uint256.h" |
| 8 | +#include "util.h" |
| 9 | + |
| 10 | +#include <boost/test/unit_test.hpp> |
| 11 | + |
| 12 | +// This test checks the majority rule which states that after 1000 v2 blocks no new v1 block can be part of that branch. |
| 13 | + |
| 14 | +BOOST_AUTO_TEST_SUITE(blockv2_tests) |
| 15 | + |
| 16 | +static CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; |
| 17 | + |
| 18 | +static void SetEmptyBlock(CBlock * pblock) |
| 19 | +{ |
| 20 | + pblock->nVersion = 2; |
| 21 | + pblock->nTime = chainActive.Tip()->GetMedianTimePast()+1; |
| 22 | + pblock->nNonce = 0; |
| 23 | +} |
| 24 | + |
| 25 | +static void SetBlockDefaultAttributesAndHeight(CBlock * pblock,bool addHeight,int heightDifference) |
| 26 | +{ |
| 27 | + SetEmptyBlock(pblock); |
| 28 | + |
| 29 | + // Add the coinbase |
| 30 | + CMutableTransaction txCoinbase(pblock->vtx[0]); |
| 31 | + |
| 32 | + if (addHeight) |
| 33 | + txCoinbase.vin[0].scriptSig = (CScript() << (chainActive.Height()+1+heightDifference) << 0); |
| 34 | + else |
| 35 | + txCoinbase.vin[0].scriptSig = (CScript() << heightDifference << 0); // At least size 2, this is a protocol spec |
| 36 | + |
| 37 | + txCoinbase.vout[0].scriptPubKey = CScript(); |
| 38 | + pblock->vtx[0] = CTransaction(txCoinbase); |
| 39 | + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); |
| 40 | +} |
| 41 | + |
| 42 | +void CheckSubsidyHalving(CBlockTemplate * &pblocktemplate, CBlock * &pblock) |
| 43 | +{ |
| 44 | + if ((chainActive.Height()+1) % Params().SubsidyHalvingInterval() == 0) |
| 45 | + { |
| 46 | + // The RegTest network has a low subsidy halving interval (150) so |
| 47 | + // we must recompute the coinbase subsidy if we reach the boundary. |
| 48 | + // The unittest network allows modifying this interval. We check it so this |
| 49 | + // test can work in any network. |
| 50 | + |
| 51 | + // preserve parent hash |
| 52 | + uint256 prevParent = pblock->hashPrevBlock; |
| 53 | + delete pblocktemplate; |
| 54 | + pblocktemplate = CreateNewBlock(scriptPubKey); |
| 55 | + pblock = &pblocktemplate->block; // pointer for convenience |
| 56 | + pblock->hashPrevBlock = prevParent; |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +void CheckBlockAddedToBestChainSuccessfully(CBlock *pblock) |
| 61 | +{ |
| 62 | + int PreviousHeight; |
| 63 | + CValidationState state; |
| 64 | + |
| 65 | + PreviousHeight = chainActive.Height(); |
| 66 | + BOOST_CHECK(ProcessBlock(state, NULL, pblock)); |
| 67 | + BOOST_CHECK(state.IsValid()); |
| 68 | + BOOST_CHECK((PreviousHeight+1) == chainActive.Height()); // to differentiate from orphan blocks, which also get accepted in ProcessBlock() |
| 69 | + |
| 70 | + // Previous checks do not assure the current best chain has pblock as tip. It could be the case that a because |
| 71 | + // of a malfunction in the chain reorganization code, a reorganization causes an increase of the chain length, but with another tip. |
| 72 | + // So we also check that. |
| 73 | + BOOST_CHECK(chainActive.Tip()->GetBlockHash()==pblock->GetHash()); |
| 74 | +} |
| 75 | + |
| 76 | +BOOST_AUTO_TEST_CASE(Blockv2testcase) |
| 77 | +{ |
| 78 | + assert(Params().NetworkID() == CBaseChainParams::UNITTEST); |
| 79 | + ModifiableParams()->setSkipProofOfWorkCheck(true); |
| 80 | + |
| 81 | + // We don't know the state of the block-chain here: it depends on which other tests are run before this test. |
| 82 | + // See https://github.com/bitcoin/bitcoin/pull/4688 for a patch that allows the re-creation of the block-chain |
| 83 | + // for each testcase that requires it. |
| 84 | + |
| 85 | + // If miner_tests.cpp is run before, the chain will be 100 blocks long, and all of them will be v1 |
| 86 | + |
| 87 | + |
| 88 | + LogPrintf("Blockv2test testcase starts\n"); |
| 89 | + |
| 90 | + CBlockTemplate *pblocktemplate; |
| 91 | + CScript script; |
| 92 | + uint256 hash; |
| 93 | + int PreviousHeight; |
| 94 | + |
| 95 | + LOCK(cs_main); |
| 96 | + |
| 97 | + // Simple block creation, nothing special yet. |
| 98 | + pblocktemplate = CreateNewBlock(scriptPubKey); |
| 99 | + CBlock *pblock = &pblocktemplate->block; // pointer for convenience |
| 100 | + |
| 101 | + LogPrintf("Blockv2test block v1 add begin\n"); |
| 102 | + |
| 103 | + //////////////////////////////////////////////////////////////////////////////////////// |
| 104 | + // First create a block v1, check that it is accepted. The block has an invalid height. |
| 105 | + //////////////////////////////////////////////////////////////////////////////////////// |
| 106 | + SetBlockDefaultAttributesAndHeight(pblock,false,5000); |
| 107 | + pblock->nVersion = 1; |
| 108 | + CValidationState state1; |
| 109 | + PreviousHeight = chainActive.Height(); |
| 110 | + BOOST_CHECK(ProcessBlock(state1, NULL, pblock)); |
| 111 | + BOOST_CHECK(state1.IsValid()); |
| 112 | + BOOST_CHECK((PreviousHeight+1) == chainActive.Height()); // to differentiate from orphan blocks, which also get accepted in ProcessBlock() |
| 113 | + pblock->hashPrevBlock = pblock->GetHash(); // update parent |
| 114 | + |
| 115 | + |
| 116 | + |
| 117 | + // First check that the supermajority threshold is exactly 1000 blocks |
| 118 | + BOOST_CHECK(Params().ToCheckBlockUpgradeMajority()==1000); // |
| 119 | + BOOST_CHECK(Params().EnforceBlockUpgradeMajority()==750); |
| 120 | + BOOST_CHECK(Params().RejectBlockOutdatedMajority()==950); |
| 121 | + |
| 122 | + // Over the last 1000 blocks, 750 blocks must be v2 to switch to v2-only mode. |
| 123 | + // Here we're testing only the last 750, not any subset. |
| 124 | + |
| 125 | + LogPrintf("Blockv2test BIP30 repetition begin\n"); |
| 126 | + |
| 127 | + /////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 128 | + // First, if we try to add a block v2 with the same coinbase tx, we should get |
| 129 | + // "bad-txns-BIP30" because the coinbase tx has the same hash as the previous. |
| 130 | + // Even if ConnectBlock returns a "bad-txns-BIP30", ActivateBestChainStep clears |
| 131 | + // the state, so we get true here and the "bad-txns-BIP30" reason is lost. But this |
| 132 | + // is the intended behaviour: Receiving a single block can cause zero or multiple blocks to be |
| 133 | + // connected, and ActivateBestChain's responsibility is just switching the best block whatsoever. |
| 134 | + // Feedback about failures causes a reject message to be sent to the peer from which we received |
| 135 | + // the actual block (not necessarily the same as from whom we got the block that caused the reorg), |
| 136 | + // for which we remember the peerid. |
| 137 | + // Because we cannot access the failure reason here, we just verify instead that the chain |
| 138 | + // height has not been incremented. |
| 139 | + ////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 140 | + |
| 141 | + CValidationState state7; |
| 142 | + PreviousHeight = chainActive.Height(); |
| 143 | + CheckSubsidyHalving(pblocktemplate,pblock); |
| 144 | + SetBlockDefaultAttributesAndHeight(pblock,false,5000); // |
| 145 | + pblock->nVersion = 2; |
| 146 | + BOOST_CHECK(ProcessBlock(state7, NULL, pblock)); // should we care about the return value? |
| 147 | + BOOST_CHECK(state7.IsValid()); |
| 148 | + BOOST_CHECK(PreviousHeight == chainActive.Height()); // we check the block has not been added. |
| 149 | + |
| 150 | + LogPrintf("Blockv2test 750 v2 blocks begin\n"); |
| 151 | + |
| 152 | + //////////////////////////// |
| 153 | + // Now create 750 v2 blocks |
| 154 | + //////////////////////////// |
| 155 | + |
| 156 | + for (int i=0;i<750;i++) |
| 157 | + { |
| 158 | + |
| 159 | + LogPrintf("Blockv2test block %d begin\n",i); |
| 160 | + |
| 161 | + CheckSubsidyHalving(pblocktemplate,pblock); |
| 162 | + |
| 163 | + // We add a value to the height to make is NOT equal to the actual height. |
| 164 | + SetBlockDefaultAttributesAndHeight(pblock,true,1000); // blocks version 2 without height are allowed! for only 750 blocks |
| 165 | + pblock->nVersion = 2; |
| 166 | + |
| 167 | + CheckBlockAddedToBestChainSuccessfully(pblock); |
| 168 | + pblock->hashPrevBlock = pblock->GetHash(); // update parent |
| 169 | + } |
| 170 | + |
| 171 | + LogPrintf("Blockv2test v2 without height rejected begin\n"); |
| 172 | + |
| 173 | + ///////////////////////////////////////////////////////////////////////////////////// |
| 174 | + // Now we try to add a block v2, with an invalid height and it should be rejected. |
| 175 | + // We use 2000 as argument heightDifference because is not in the range [1000..1750]. |
| 176 | + ///////////////////////////////////////////////////////////////////////////////////// |
| 177 | + CheckSubsidyHalving(pblocktemplate,pblock); |
| 178 | + SetBlockDefaultAttributesAndHeight(pblock,true,2000); // |
| 179 | + pblock->nVersion = 2; |
| 180 | + CValidationState state0; |
| 181 | + BOOST_CHECK(ProcessBlock(state0, NULL, pblock)==false); |
| 182 | + BOOST_CHECK(!state0.IsValid()); |
| 183 | + BOOST_CHECK(state0.GetRejectReason()=="bad-cb-height"); |
| 184 | + // Do not update parent since block has failed |
| 185 | + |
| 186 | + LogPrintf("Blockv2test v2 with height accepted begin\n"); |
| 187 | + |
| 188 | + ///////////////////////////////////////////////////////////// |
| 189 | + // Now we add 200 additional blocks, until we get 950 |
| 190 | + // (the threshold where v1 blocks are not accepted anymore) |
| 191 | + ///////////////////////////////////////////////////////////// |
| 192 | + for (int i=0;i<200;i++) |
| 193 | + { |
| 194 | + |
| 195 | + LogPrintf("Blockv2test v2block %d begin\n",i); |
| 196 | + CheckSubsidyHalving(pblocktemplate,pblock); |
| 197 | + SetBlockDefaultAttributesAndHeight(pblock,true,0); |
| 198 | + pblock->nVersion = 2; |
| 199 | + CheckBlockAddedToBestChainSuccessfully(pblock); |
| 200 | + pblock->hashPrevBlock = pblock->GetHash(); // update parent |
| 201 | + } |
| 202 | + |
| 203 | + |
| 204 | + LogPrintf("Blockv2test block v1 rejected\n"); |
| 205 | + |
| 206 | + ///////////////////////////////////////////////////////////////////////////////////////// |
| 207 | + // Now we try to add a block v1, it should be rejected, even if it hash the height field |
| 208 | + ///////////////////////////////////////////////////////////////////////////////////////// |
| 209 | + CheckSubsidyHalving(pblocktemplate,pblock); |
| 210 | + SetBlockDefaultAttributesAndHeight(pblock,true,0); |
| 211 | + pblock->nVersion = 1; |
| 212 | + CValidationState state2; |
| 213 | + BOOST_CHECK(ProcessBlock(state2, NULL, pblock)==false); |
| 214 | + BOOST_CHECK(!state2.IsValid()); |
| 215 | + BOOST_CHECK(state2.GetRejectReason()=="bad-version"); |
| 216 | + // Do not update parent since block has failed |
| 217 | + |
| 218 | + //////////////////////////////////////////////////////// |
| 219 | + // Block time too old check |
| 220 | + //////////////////////////////////////////////////////// |
| 221 | + CheckSubsidyHalving(pblocktemplate,pblock); |
| 222 | + SetBlockDefaultAttributesAndHeight(pblock,true,0); |
| 223 | + pblock->nVersion = 2; |
| 224 | + pblock->nTime = chainActive.Tip()->GetMedianTimePast()-1; |
| 225 | + CValidationState state4; |
| 226 | + BOOST_CHECK(ProcessBlock(state4, NULL, pblock)==false); |
| 227 | + BOOST_CHECK(!state4.IsValid()); |
| 228 | + BOOST_CHECK(state4.GetRejectReason()=="time-too-old"); |
| 229 | + // Do not update parent since block has failed |
| 230 | + |
| 231 | + //////////////////////////////////////////////////////// |
| 232 | + // Adding a non-final coinbase, must modify coinbase |
| 233 | + //////////////////////////////////////////////////////// |
| 234 | + CheckSubsidyHalving(pblocktemplate,pblock); |
| 235 | + SetEmptyBlock(pblock); |
| 236 | + // Use a mutable coinbase to change nLockTime and nSequence |
| 237 | + CMutableTransaction txCoinbase(pblock->vtx[0]); |
| 238 | + txCoinbase.vin[0].scriptSig = (CScript() << chainActive.Height() << 0); |
| 239 | + txCoinbase.nLockTime = LOCKTIME_THRESHOLD-1; // refers to height |
| 240 | + txCoinbase.vin[0].nSequence = 1; // non-zero sequence |
| 241 | + pblock->vtx[0] = CTransaction(txCoinbase); |
| 242 | + pblock->nVersion = 2; |
| 243 | + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); |
| 244 | + CValidationState state5; |
| 245 | + BOOST_CHECK(ProcessBlock(state5, NULL, pblock)==false); |
| 246 | + BOOST_CHECK(!state5.IsValid()); |
| 247 | + BOOST_CHECK(state5.GetRejectReason()=="bad-txns-nonfinal"); |
| 248 | + // Do not update parent since block has failed |
| 249 | + |
| 250 | + ModifiableParams()->setSkipProofOfWorkCheck(false); |
| 251 | + |
| 252 | + //////////////////////////////////////////////////////////////////////////////// |
| 253 | + // Just to be sure that proof-of-work skipping is working ok, we submit a block |
| 254 | + // without enought proof of work and it must be rejected. |
| 255 | + //////////////////////////////////////////////////////////////////////////////// |
| 256 | + CheckSubsidyHalving(pblocktemplate,pblock); |
| 257 | + SetBlockDefaultAttributesAndHeight(pblock,true,0); |
| 258 | + pblock->nVersion = 2; |
| 259 | + CValidationState state6; |
| 260 | + BOOST_CHECK(ProcessBlock(state6, NULL, pblock)==false); |
| 261 | + BOOST_CHECK(!state6.IsValid()); |
| 262 | + BOOST_CHECK(state6.GetRejectReason()=="high-hash"); |
| 263 | + |
| 264 | + delete pblocktemplate; |
| 265 | + |
| 266 | + LogPrintf("Blockv2test testcase ends\n"); |
| 267 | +} |
| 268 | + |
| 269 | +BOOST_AUTO_TEST_SUITE_END() |
0 commit comments