Skip to content

Commit

Permalink
Consensus: API proposal for Consensus::VerifyBlockHeader()
Browse files Browse the repository at this point in the history
  • Loading branch information
jtimon committed Apr 19, 2015
1 parent 8c4c0d9 commit d850a6b
Show file tree
Hide file tree
Showing 11 changed files with 79 additions and 52 deletions.
1 change: 1 addition & 0 deletions src/Makefile.am
Expand Up @@ -89,6 +89,7 @@ BITCOIN_CORE_H = \
compressor.h \
consensus/consensus.h \
consensus/params.h \
consensus/types.h \
consensus/validation.h \
core_io.h \
wallet/db.h \
Expand Down
5 changes: 5 additions & 0 deletions src/chain.cpp
Expand Up @@ -106,3 +106,8 @@ void CBlockIndex::BuildSkip()
if (pprev)
pskip = pprev->GetAncestor(GetSkipHeight(nHeight));
}

const CBlockIndexBase* GetPrevIndex(const CBlockIndexBase* pindex)
{
return ((CBlockIndex*)pindex)->pprev;
}
19 changes: 8 additions & 11 deletions src/chain.h
Expand Up @@ -7,6 +7,7 @@
#define BITCOIN_CHAIN_H

#include "arith_uint256.h"
#include "consensus/types.h"
#include "primitives/block.h"
#include "tinyformat.h"
#include "uint256.h"
Expand Down Expand Up @@ -93,12 +94,18 @@ enum BlockStatus {
BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD,
};

/**
* This getter is used by in bitcoin core when a PrevIndexGetter
* function pointer is needed in consensus checks.
*/
const CBlockIndexBase* GetPrevIndex(const CBlockIndexBase* pindex);

/** The block chain is a tree shaped structure starting with the
* genesis block at the root, with each block potentially having multiple
* candidates to be the next block. A blockindex may have multiple pprev pointing
* to it, but at most one of them can be part of the currently active branch.
*/
class CBlockIndex
class CBlockIndex : public CBlockIndexBase
{
public:
//! pointer to the hash of the block, if any. Memory is owned by this CBlockIndex
Expand All @@ -110,9 +117,6 @@ class CBlockIndex
//! pointer to the index of some further predecessor of this block
CBlockIndex* pskip;

//! height of the entry in the chain. The genesis block has height 0
int nHeight;

//! Which # file this block is stored in (blk?????.dat)
int nFile;

Expand All @@ -137,13 +141,6 @@ class CBlockIndex
//! Verification status of this block. See enum BlockStatus
unsigned int nStatus;

//! block header
int nVersion;
uint256 hashMerkleRoot;
unsigned int nTime;
unsigned int nBits;
unsigned int nNonce;

//! (memory only) Sequential id assigned to distinguish order in which blocks are received.
uint32_t nSequenceId;

Expand Down
35 changes: 17 additions & 18 deletions src/consensus/blockverify.cpp
Expand Up @@ -6,7 +6,6 @@
#include "consensus/consensus.h"

#include "arith_uint256.h"
#include "chain.h"
#include "consensus/validation.h"
#include "primitives/block.h"
#include "tinyformat.h"
Expand All @@ -15,7 +14,7 @@

static const unsigned int MEDIAN_TIME_SPAN = 11;

uint32_t GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
uint32_t GetNextWorkRequired(const CBlockIndexBase* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params, PrevIndexGetter indexGetter)
{
uint32_t nProofOfWorkLimit = UintToArith256(params.powLimit).GetCompact();

Expand All @@ -36,25 +35,25 @@ uint32_t GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *
else
{
// Return the last non-special-min-difficulty-rules-block
const CBlockIndex* pindex = pindexLast;
while (pindex->pprev && pindex->nHeight % params.DifficultyAdjustmentInterval() != 0 && pindex->nBits == nProofOfWorkLimit)
pindex = pindex->pprev;
const CBlockIndexBase* pindex = pindexLast;
while (indexGetter(pindex) && pindex->nHeight % params.DifficultyAdjustmentInterval() != 0 && pindex->nBits == nProofOfWorkLimit)
pindex = indexGetter(pindex);
return pindex->nBits;
}
}
return pindexLast->nBits;
}

// Go back by what we want to be 14 days worth of blocks
const CBlockIndex* pindexFirst = pindexLast;
const CBlockIndexBase* pindexFirst = pindexLast;
for (int i = 0; pindexFirst && i < params.DifficultyAdjustmentInterval()-1; i++)
pindexFirst = pindexFirst->pprev;
pindexFirst = indexGetter(pindexFirst);
assert(pindexFirst);

return CalculateNextWorkRequired(pindexLast, (int64_t)pindexFirst->nTime, params);
}

uint32_t CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params)
uint32_t CalculateNextWorkRequired(const CBlockIndexBase* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params)
{
// Limit adjustment step
int64_t nActualTimespan = (int64_t)pindexLast->nTime - nFirstBlockTime;
Expand Down Expand Up @@ -97,27 +96,27 @@ bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params&
return true;
}

int64_t GetMedianTimePast(const CBlockIndex* pindex)
int64_t GetMedianTimePast(const CBlockIndexBase* pindex, PrevIndexGetter indexGetter)
{
int64_t pmedian[MEDIAN_TIME_SPAN];
int64_t* pbegin = &pmedian[MEDIAN_TIME_SPAN];
int64_t* pend = &pmedian[MEDIAN_TIME_SPAN];

for (unsigned int i = 0; i < MEDIAN_TIME_SPAN && pindex; i++, pindex = pindex->pprev)
for (unsigned int i = 0; i < MEDIAN_TIME_SPAN && pindex; i++, pindex = indexGetter(pindex))
*(--pbegin) = (int64_t)pindex->nTime;

std::sort(pbegin, pend);
return pbegin[(pend - pbegin)/2];
}

bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned nRequired, const Consensus::Params& consensusParams)
bool IsSuperMajority(int minVersion, const CBlockIndexBase* pstart, unsigned nRequired, const Consensus::Params& consensusParams, PrevIndexGetter indexGetter)
{
unsigned int nFound = 0;
for (int i = 0; i < consensusParams.nMajorityWindow && nFound < nRequired && pstart != NULL; i++)
{
if (pstart->nVersion >= minVersion)
++nFound;
pstart = pstart->pprev;
pstart = indexGetter(pstart);
}
return (nFound >= nRequired);
}
Expand All @@ -135,29 +134,29 @@ bool Consensus::CheckBlockHeader(const CBlockHeader& block, CValidationState& st
return true;
}

bool Consensus::ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
bool Consensus::ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndexBase* pindexPrev, PrevIndexGetter indexGetter)
{
// Check proof of work
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams))
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams, indexGetter))
return state.DoS(100, false, REJECT_INVALID, "bad-diffbits");

// Check timestamp against prev
if (block.GetBlockTime() <= GetMedianTimePast(pindexPrev))
if (block.GetBlockTime() <= GetMedianTimePast(pindexPrev, indexGetter))
return state.Invalid(false, REJECT_INVALID, "time-too-old");

// Reject block.nVersion=n blocks when 95% (75% on testnet) of the network has upgraded (last version=3):
for (int i = 2; i <= 3; i++)
if (block.nVersion < i && IsSuperMajority(i, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams))
if (block.nVersion < i && IsSuperMajority(i, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams, indexGetter))
return state.Invalid(false, REJECT_OBSOLETE, strprintf("bad-version nVersion=%d", i-1));

return true;
}

bool Consensus::VerifyBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& params, int64_t nTime, CBlockIndex* pindexPrev)
bool Consensus::VerifyBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& params, int64_t nTime, CBlockIndexBase* pindexPrev, PrevIndexGetter indexGetter)
{
if (!Consensus::CheckBlockHeader(block, state, params, nTime, true))
return false;
if (!Consensus::ContextualCheckBlockHeader(block, state, params, pindexPrev))
if (!Consensus::ContextualCheckBlockHeader(block, state, params, pindexPrev, indexGetter))
return false;
return true;
}
14 changes: 7 additions & 7 deletions src/consensus/consensus.h
Expand Up @@ -7,11 +7,11 @@
#define BITCOIN_CONSENSUS_CONSENSUS_H

#include "consensus/params.h"
#include "consensus/types.h"

#include <stdint.h>

class CBlockHeader;
class CBlockIndex;
class CValidationState;

/** The maximum allowed size for a serialized block, in bytes (network rule) */
Expand All @@ -32,23 +32,23 @@ namespace Consensus {

/** Block header validation functions */

bool VerifyBlockHeader(const CBlockHeader&, CValidationState&, const Consensus::Params&, int64_t nTime, CBlockIndex*);
bool VerifyBlockHeader(const CBlockHeader&, CValidationState&, const Consensus::Params&, int64_t nTime, CBlockIndexBase*, PrevIndexGetter);
bool CheckBlockHeader(const CBlockHeader&, CValidationState&, const Consensus::Params&, int64_t nTime, bool fCheckPOW = true);
bool ContextualCheckBlockHeader(const CBlockHeader&, CValidationState&, const Consensus::Params&, const CBlockIndex*);
bool ContextualCheckBlockHeader(const CBlockHeader&, CValidationState&, const Consensus::Params&, const CBlockIndexBase*, PrevIndexGetter);

} // namespace Consensus

/** Block header validation utility functions */

int64_t GetMedianTimePast(const CBlockIndex* pindex);
unsigned int GetNextWorkRequired(const CBlockIndex*, const CBlockHeader*, const Consensus::Params&);
unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params&);
int64_t GetMedianTimePast(const CBlockIndexBase* pindex, PrevIndexGetter indexGetter);
unsigned int GetNextWorkRequired(const CBlockIndexBase*, const CBlockHeader*, const Consensus::Params&, PrevIndexGetter);
unsigned int CalculateNextWorkRequired(const CBlockIndexBase* pindexLast, int64_t nFirstBlockTime, const Consensus::Params&);
/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params&);
/**
* Returns true if there are nRequired or more blocks of minVersion or above
* in the last Consensus::Params::nMajorityWindow blocks, starting at pstart and going backwards.
*/
bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned nRequired, const Consensus::Params& consensusParams);
bool IsSuperMajority(int minVersion, const CBlockIndexBase* pstart, unsigned nRequired, const Consensus::Params& consensusParams, PrevIndexGetter);

#endif // BITCOIN_CONSENSUS_CONSENSUS_H
25 changes: 25 additions & 0 deletions src/consensus/types.h
@@ -0,0 +1,25 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2013 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_CONSENSUS_TYPES_H
#define BITCOIN_CONSENSUS_TYPES_H

#include "uint256.h"

struct CBlockIndexBase
{
//! block header
int32_t nVersion;
uint256 hashMerkleRoot;
uint32_t nTime;
uint32_t nBits;
uint32_t nNonce;
//! height of the entry in the chain. The genesis block has height 0
int nHeight;
};

typedef const CBlockIndexBase* (*PrevIndexGetter)(const CBlockIndexBase*);

#endif // BITCOIN_CONSENSUS_TYPES_H
8 changes: 4 additions & 4 deletions src/main.cpp
Expand Up @@ -1758,7 +1758,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE;

// Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks, when 75% of the network has upgraded:
if (block.nVersion >= 3 && IsSuperMajority(3, pindex->pprev, chainparams.GetConsensus().nMajorityEnforceBlockUpgrade, chainparams.GetConsensus())) {
if (block.nVersion >= 3 && IsSuperMajority(3, pindex->pprev, chainparams.GetConsensus().nMajorityEnforceBlockUpgrade, chainparams.GetConsensus(), GetPrevIndex)) {
flags |= SCRIPT_VERIFY_DERSIG;
}

Expand Down Expand Up @@ -2583,7 +2583,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn

// Enforce block.nVersion=2 rule that the coinbase starts with serialized block height
// if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet):
if (block.nVersion >= 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams))
if (block.nVersion >= 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams, GetPrevIndex))
{
CScript expect = CScript() << nHeight;
if (block.vtx[0].vin[0].scriptSig.size() < expect.size() ||
Expand Down Expand Up @@ -2629,7 +2629,7 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc
if (!CheckIndexAgainstCheckpoint(pindexPrev, state, chainparams, mapBlockIndex))
return error("%s: CheckIndexAgainstCheckpoint(): %s", __func__, state.GetRejectReason().c_str());

if (!Consensus::ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev))
if (!Consensus::ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev, GetPrevIndex))
return error("%s: Consensus::CheckBlockHeader(): ", __func__, state.GetRejectReason().c_str());

if (pindex == NULL)
Expand Down Expand Up @@ -2729,7 +2729,7 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex
indexDummy.nHeight = pindexPrev->nHeight + 1;

// NOTE: CheckBlockHeader is called by CheckBlock
if (!Consensus::ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev))
if (!Consensus::ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev, GetPrevIndex))
return error("%s: Consensus::CheckBlockHeader(): ", __func__, state.GetRejectReason().c_str());
if (!CheckBlock(block, state, fCheckPOW, fCheckMerkleRoot))
return false;
Expand Down
5 changes: 3 additions & 2 deletions src/miner.cpp
Expand Up @@ -6,6 +6,7 @@
#include "miner.h"

#include "amount.h"
#include "chain.h"
#include "chainparams.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
Expand Down Expand Up @@ -82,7 +83,7 @@ class TxPriorityCompare

uint32_t GetNextWorkRequiredLog(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& consensusParams)
{
uint32_t nextChallenge = GetNextWorkRequired(pindexLast, pblock, consensusParams);
uint32_t nextChallenge = GetNextWorkRequired(pindexLast, pblock, consensusParams, GetPrevIndex);
/// debug print
LogPrintf("GetNextWorkRequired RETARGET\n");
LogPrintf("pindexLast->nTime = %d\n", (int64_t)pindexLast->nTime);
Expand All @@ -96,7 +97,7 @@ uint32_t GetNextWorkRequiredLog(const CBlockIndex* pindexLast, const CBlockHeade

void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
{
pblock->nTime = std::max(GetMedianTimePast(pindexPrev)+1, GetAdjustedTime());
pblock->nTime = std::max(GetMedianTimePast(pindexPrev, GetPrevIndex)+1, GetAdjustedTime());

// Updating time can change work required on testnet:
if (consensusParams.fPowAllowMinDifficultyBlocks)
Expand Down
2 changes: 1 addition & 1 deletion src/rpcmining.cpp
Expand Up @@ -574,7 +574,7 @@ Value getblocktemplate(const Array& params, bool fHelp)
result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
result.push_back(Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast)));
result.push_back(Pair("target", hashTarget.GetHex()));
result.push_back(Pair("mintime", (int64_t)GetMedianTimePast(pindexPrev)+1));
result.push_back(Pair("mintime", (int64_t)GetMedianTimePast(pindexPrev, GetPrevIndex)+1));
result.push_back(Pair("mutable", aMutable));
result.push_back(Pair("noncerange", "00000000ffffffff"));
result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS));
Expand Down
8 changes: 4 additions & 4 deletions src/test/miner_tests.cpp
Expand Up @@ -73,7 +73,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
pblock->nVersion = 1;
pblock->nTime = GetMedianTimePast(chainActive.Tip())+1;
pblock->nTime = GetMedianTimePast(chainActive.Tip(), GetPrevIndex)+1;
CMutableTransaction txCoinbase(pblock->vtx[0]);
txCoinbase.vin[0].scriptSig = CScript();
txCoinbase.vin[0].scriptSig.push_back(blockinfo[i].extranonce);
Expand Down Expand Up @@ -213,7 +213,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
chainActive.Tip()->nHeight = nHeight;

// non-final txs in mempool
SetMockTime(GetMedianTimePast(chainActive.Tip())+1);
SetMockTime(GetMedianTimePast(chainActive.Tip(), GetPrevIndex)+1);

// height locked
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
Expand All @@ -235,7 +235,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx2.vout.resize(1);
tx2.vout[0].nValue = 4900000000LL;
tx2.vout[0].scriptPubKey = CScript() << OP_1;
tx2.nLockTime = GetMedianTimePast(chainActive.Tip())+1;
tx2.nLockTime = GetMedianTimePast(chainActive.Tip(), GetPrevIndex)+1;
hash = tx2.GetHash();
mempool.addUnchecked(hash, CTxMemPoolEntry(tx2, 11, GetTime(), 111.0, 11));
BOOST_CHECK(!IsFinalTx(tx2));
Expand All @@ -248,7 +248,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)

// However if we advance height and time by one, both will.
chainActive.Tip()->nHeight++;
SetMockTime(GetMedianTimePast(chainActive.Tip())+2);
SetMockTime(GetMedianTimePast(chainActive.Tip(), GetPrevIndex)+2);

BOOST_CHECK(IsFinalTx(tx, chainActive.Tip()->nHeight + 1));
BOOST_CHECK(IsFinalTx(tx2));
Expand Down

0 comments on commit d850a6b

Please sign in to comment.