Showing with 89 additions and 29 deletions.
  1. +2 −2 src/consensus/consensus.h
  2. +45 −4 src/consensus/merkle.cpp
  3. +2 −1 src/consensus/merkle.h
  4. +17 −4 src/main.cpp
  5. +2 −2 src/main.h
  6. +21 −16 src/test/block_size_tests.cpp
4 changes: 2 additions & 2 deletions src/consensus/consensus.h
Expand Up @@ -11,8 +11,8 @@
static const uint64_t BIP102_FORK_TIME = 1462406400; // May 5 2016, midnight UTC

/** The maximum allowed size for a serialized block, in bytes (network rule) */
inline unsigned int MaxBlockSize(uint64_t nTime) {
if (nTime < BIP102_FORK_TIME)
inline unsigned int MaxBlockSize(uint64_t nTime, bool legacy = false) {
if (legacy || nTime < BIP102_FORK_TIME)
return 1000*1000;

return (2*1000*1000);
Expand Down
49 changes: 45 additions & 4 deletions src/consensus/merkle.cpp
@@ -1,3 +1,4 @@
#include "consensus.h"
#include "merkle.h"
#include "hash.h"
#include "utilstrencodings.h"
Expand Down Expand Up @@ -151,12 +152,19 @@ uint256 ComputeMerkleRootFromBranch(const uint256& leaf, const std::vector<uint2
return hash;
}

uint256 BlockMerkleRoot(const CBlock& block, bool* mutated)
uint256 BlockMerkleRoot(const CBlock& block, bool* mutated, bool legacy)
{
std::vector<uint256> leaves;
leaves.resize(block.vtx.size());
for (size_t s = 0; s < block.vtx.size(); s++) {
leaves[s] = block.vtx[s].GetHash();
if (legacy || block.nTime < BIP102_FORK_TIME) {
leaves.resize(block.vtx.size());
for (size_t s = 0; s < block.vtx.size(); s++) {
leaves[s] = block.vtx[s].GetHash();
}
}
else {
CMutableTransaction legacyCoinbase;
MakeLegacyCoinbaseTransaction(legacyCoinbase, block, mutated);
leaves.push_back(legacyCoinbase.GetHash());
}
return ComputeMerkleRoot(leaves, mutated);
}
Expand All @@ -170,3 +178,36 @@ std::vector<uint256> BlockMerkleBranch(const CBlock& block, uint32_t position)
}
return ComputeMerkleBranch(leaves, position);
}

void MakeLegacyCoinbaseTransaction(CMutableTransaction &coinbase, const CBlock& block, bool *mutated) {
// Calculate Merkle root
std::vector<uint256> leaves;
leaves.resize(block.vtx.size());
for (size_t s = 0; s < block.vtx.size(); s++) {
leaves[s] = block.vtx[s].GetHash();
}
const uint256 merkleRoot = ComputeMerkleRoot(leaves, mutated);

// Parse nHeight (BIP34 handling)
const CScript& script = block.vtx[0].vin[0].scriptSig;
CScript::const_iterator pc = script.begin();
unsigned int nHeight = 0;
opcodetype opcode;
std::vector<unsigned char> vch;
if (script.GetOp(pc, opcode, vch) && vch.size() > 0 && vch.size() <= 4) {
for (size_t i = 0; i != vch.size(); ++i)
nHeight |= static_cast<unsigned int>(vch[i]) << 8*i;
}

// Construct legacy coinbase tx
coinbase.nVersion = 1;
coinbase.nLockTime = 0;
coinbase.vin.resize(1);
coinbase.vin[0].prevout.SetNull();
coinbase.vin[0].scriptSig = CScript() << nHeight << OP_0;
coinbase.vin[0].nSequence = std::numeric_limits<uint32_t>::max();
coinbase.vout.resize(1);
coinbase.vout[0].scriptPubKey = CScript() << OP_RETURN << ToByteVector(merkleRoot);
coinbase.vout[0].nValue = 0;
}

3 changes: 2 additions & 1 deletion src/consensus/merkle.h
Expand Up @@ -20,7 +20,8 @@ uint256 ComputeMerkleRootFromBranch(const uint256& leaf, const std::vector<uint2
* Compute the Merkle root of the transactions in a block.
* *mutated is set to true if a duplicated subtree was found.
*/
uint256 BlockMerkleRoot(const CBlock& block, bool* mutated = NULL);
uint256 BlockMerkleRoot(const CBlock& block, bool* mutated = NULL, bool legacy = false);
void MakeLegacyCoinbaseTransaction(CMutableTransaction &coinbase, const CBlock& block, bool *mutated = NULL);

/*
* Compute the Merkle branch for the tree of transactions in a block, for a
Expand Down
21 changes: 17 additions & 4 deletions src/main.cpp
Expand Up @@ -2952,7 +2952,7 @@ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool f
return true;
}

bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bool fCheckMerkleRoot)
bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckLegacyBlock)
{
// These are checks that are independent of context.

Expand All @@ -2967,7 +2967,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
// Check the merkle root.
if (fCheckMerkleRoot) {
bool mutated;
uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated);
uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated, fCheckLegacyBlock);
if (block.hashMerkleRoot != hashMerkleRoot2)
return state.DoS(100, error("CheckBlock(): hashMerkleRoot mismatch"),
REJECT_INVALID, "bad-txnmrklroot", true);
Expand All @@ -2985,7 +2985,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
// because we receive the wrong transactions for it.

// Size limits
unsigned int nMaxSize = MaxBlockSize(block.GetBlockTime());
unsigned int nMaxSize = MaxBlockSize(block.GetBlockTime(), fCheckLegacyBlock);
if (block.vtx.empty() || block.vtx.size() > nMaxSize || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > nMaxSize)
return state.DoS(100, error("CheckBlock(): size limits failed"),
REJECT_INVALID, "bad-blk-length");
Expand Down Expand Up @@ -3071,7 +3071,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta
return true;
}

bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex * const pindexPrev)
bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex * const pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot)
{
const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1;
const Consensus::Params& consensusParams = Params().GetConsensus();
Expand All @@ -3098,6 +3098,19 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
}
}

// Enforce the BIP102s firmfork rule that the empty block containing nothing but the legacy coinbase tx MUST be valid under the old rules.
if (block.nTime >= BIP102_FORK_TIME)
{
CBlock legacyBlock(block.GetBlockHeader());
CMutableTransaction txLegacyCoinbase;
MakeLegacyCoinbaseTransaction(txLegacyCoinbase, block);
legacyBlock.vtx.push_back(txLegacyCoinbase);
bool fCheckLegacyBlock = true;
if (!CheckBlock(legacyBlock, state, fCheckPOW, fCheckMerkleRoot, fCheckLegacyBlock)) {
return false;
}
}

return true;
}

Expand Down
4 changes: 2 additions & 2 deletions src/main.h
Expand Up @@ -401,11 +401,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin

/** Context-independent validity checks */
bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true);
bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = true, bool fCheckMerkleRoot = true, bool fCheckLegacyBlock = false);

/** Context-dependent validity checks */
bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex *pindexPrev);
bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex *pindexPrev);
bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex *pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true);

/** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */
bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
Expand Down
37 changes: 21 additions & 16 deletions src/test/block_size_tests.cpp
Expand Up @@ -9,6 +9,7 @@
#include "uint256.h"
#include "util.h"
#include "consensus/consensus.h"
#include "consensus/merkle.h"
#include "consensus/validation.h"

#include "test/test_bitcoin.h"
Expand All @@ -19,7 +20,7 @@ BOOST_FIXTURE_TEST_SUITE(block_size_tests, TestingSetup)

// Fill block with dummy transactions until it's serialized size is exactly nSize
static void
FillBlock(CBlock& block, unsigned int nSize)
FillBlock(CBlock& block, const CChainParams& chainparams, unsigned int nSize)
{
assert(block.vtx.size() > 0); // Start with at least a coinbase

Expand All @@ -32,7 +33,9 @@ FillBlock(CBlock& block, unsigned int nSize)
// Create a block that is exactly 1,000,000 bytes, serialized:
CMutableTransaction tx;
tx.vin.resize(1);
tx.vin[0].scriptSig = CScript() << OP_11;
CBlockIndex* pindexPrev = chainActive.Tip();
int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1;
tx.vin[0].scriptSig = CScript() << nHeight << OP_11;
tx.vin[0].prevout.hash = block.vtx[0].GetHash(); // passes CheckBlock, would fail if we checked inputs.
tx.vin[0].prevout.n = 0;
tx.vout.resize(1);
Expand Down Expand Up @@ -60,13 +63,15 @@ FillBlock(CBlock& block, unsigned int nSize)
assert(nBlockSize == nSize);
}

static bool TestCheckBlock(CBlock& block, uint64_t nTime, unsigned int nSize)
static bool TestCheckBlock(CBlock& block, const CChainParams& chainparams, uint64_t nTime, unsigned int nSize)
{
SetMockTime(nTime);
block.nTime = nTime;
FillBlock(block, nSize);
FillBlock(block, chainparams, nSize);
CValidationState validationState;
bool fResult = CheckBlock(block, validationState, false, false) && validationState.IsValid();
CBlockIndex* pindexPrev = chainActive.Tip();
fResult = fResult && ContextualCheckBlock(block, validationState, pindexPrev, false, false) && validationState.IsValid();
SetMockTime(0);
return fResult;
}
Expand All @@ -86,25 +91,25 @@ BOOST_AUTO_TEST_CASE(TwoMegFork)
CBlock *pblock = &pblocktemplate->block;

// Before fork time...
BOOST_CHECK(TestCheckBlock(*pblock, BIP102_FORK_TIME-1, 1000*1000)); // 1MB : valid
BOOST_CHECK(!TestCheckBlock(*pblock, BIP102_FORK_TIME-1, 1000*1000+1)); // >1MB : invalid
BOOST_CHECK(!TestCheckBlock(*pblock, BIP102_FORK_TIME-1, 2*1000*1000)); // 2MB : invalid
BOOST_CHECK(TestCheckBlock(*pblock, chainparams, BIP102_FORK_TIME-1, 1000*1000)); // 1MB : valid
BOOST_CHECK(!TestCheckBlock(*pblock, chainparams, BIP102_FORK_TIME-1, 1000*1000+1)); // >1MB : invalid
BOOST_CHECK(!TestCheckBlock(*pblock, chainparams, BIP102_FORK_TIME-1, 2*1000*1000)); // 2MB : invalid

// Exactly at fork time...
BOOST_CHECK(TestCheckBlock(*pblock, BIP102_FORK_TIME, 1000*1000)); // 1MB : valid
BOOST_CHECK(TestCheckBlock(*pblock, BIP102_FORK_TIME, 2*1000*1000)); // 2MB : valid
BOOST_CHECK(!TestCheckBlock(*pblock, BIP102_FORK_TIME, 2*1000*1000+1)); // >2MB : invalid
BOOST_CHECK(TestCheckBlock(*pblock, chainparams, BIP102_FORK_TIME, 1000*1000)); // 1MB : valid
BOOST_CHECK(TestCheckBlock(*pblock, chainparams, BIP102_FORK_TIME, 2*1000*1000)); // 2MB : valid
BOOST_CHECK(!TestCheckBlock(*pblock, chainparams, BIP102_FORK_TIME, 2*1000*1000+1)); // >2MB : invalid

// Fork height + 10 min...
BOOST_CHECK(!TestCheckBlock(*pblock, BIP102_FORK_TIME+600, 2*1000*1000+20)); // 2MB+20 : invalid
BOOST_CHECK(!TestCheckBlock(*pblock, chainparams, BIP102_FORK_TIME+600, 2*1000*1000+20)); // 2MB+20 : invalid

// A year after fork time:
unsigned int yearAfter = BIP102_FORK_TIME + (365 * 24 * 60 * 60);
BOOST_CHECK(TestCheckBlock(*pblock, yearAfter, 1000*1000)); // 1MB : valid
BOOST_CHECK(TestCheckBlock(*pblock, yearAfter, 2*1000*1000)); // 2MB : valid
BOOST_CHECK(!TestCheckBlock(*pblock, yearAfter, 2*1000*1000+1)); // >2MB : invalid
BOOST_CHECK(!TestCheckBlock(*pblock, yearAfter, 3*1000*1000)); // 3MB : invalid
BOOST_CHECK(!TestCheckBlock(*pblock, yearAfter, 4*1000*1000)); // 4MB : invalid
BOOST_CHECK(TestCheckBlock(*pblock, chainparams, yearAfter, 1000*1000)); // 1MB : valid
BOOST_CHECK(TestCheckBlock(*pblock, chainparams, yearAfter, 2*1000*1000)); // 2MB : valid
BOOST_CHECK(!TestCheckBlock(*pblock, chainparams, yearAfter, 2*1000*1000+1)); // >2MB : invalid
BOOST_CHECK(!TestCheckBlock(*pblock, chainparams, yearAfter, 3*1000*1000)); // 3MB : invalid
BOOST_CHECK(!TestCheckBlock(*pblock, chainparams, yearAfter, 4*1000*1000)); // 4MB : invalid
}

BOOST_AUTO_TEST_SUITE_END()