比特币区块检查
比特币采用Pow共识机制,即不断调整Nonce值,对区块头做双重SHA256哈希运算,使得结果满足给定数量前导0的哈希值的过程。其中前导0的个数,取决于挖矿难度,前导0的个数越多,挖矿难度越大。
1 块儿产生时的检查
首先,生成铸币交易,并与其它所有准备打包进区块的交易组成交易列表,生成Merkle根哈希值;
其次,将Merkle根哈希值,与区块头其它字段组成区块头,80字节长度的区块头作为Pow算法的输入;
再次,不断变更区块头的随机数Nonce,对变更后的区块头做SHA256哈希运算,与当前难度的目标值做对比,如果小于目标难度,即Pow完成;
最后,Pow完成的区块向全网广播,其它节点将验证其是否符合规则,如果验证有效,其它节点将接收此区块,并附加在已有区块链之后;
之后,将进入下一轮挖矿。
当新的区块产生或者其他节点发送过来一个新的区块时(generateBlocks/net_processing),都会进行合法性校验。
1 static UniValue generateBlocks(const CTxMemPool& mempool, const CScript& coinbase_script, int nGenerate, uint64_t nMaxTries) 2 { 3 int nHeightEnd = 0; 4 int nHeight = 0; 5 6 { // Don't keep cs_main locked 7 LOCK(cs_main); 8 nHeight = ::ChainActive().Height(); 9 nHeightEnd = nHeight+nGenerate; 10 } 11 unsigned int nExtraNonce = 0; 12 UniValue blockHashes(UniValue::VARR); 13 while (nHeight < nHeightEnd && !ShutdownRequested()) 14 { 15 std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(mempool, Params()).CreateNewBlock(coinbase_script)); 16 if (!pblocktemplate.get()) 17 throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); 18 CBlock *pblock = &pblocktemplate->block; 19 { 20 LOCK(cs_main); 21 IncrementExtraNonce(pblock, ::ChainActive().Tip(), nExtraNonce); 22 } 23 while (nMaxTries > 0 && pblock->nNonce < std::numeric_limits<uint32_t>::max() && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus()) && !ShutdownRequested()) { 24 ++pblock->nNonce; 25 --nMaxTries; 26 } 27 if (nMaxTries == 0 || ShutdownRequested()) { 28 break; 29 } 30 if (pblock->nNonce == std::numeric_limits<uint32_t>::max()) { 31 continue; 32 } 33 std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock); 34 if (!ProcessNewBlock(Params(), shared_pblock, true, nullptr)) 35 throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted"); 36 ++nHeight; 37 blockHashes.push_back(pblock->GetHash().GetHex()); 38 } 39 return blockHashes; 40 }
以上源码第21行IncrementExtraNonce函数中,会通过修改铸币交易中的ExtraNonce字段来更新coinbase交易,从而更新区块MerkleRoot。
1 void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce) 2 { 3 // Update nExtraNonce 4 static uint256 hashPrevBlock; 5 if (hashPrevBlock != pblock->hashPrevBlock) 6 { 7 nExtraNonce = 0; 8 hashPrevBlock = pblock->hashPrevBlock; 9 } 10 ++nExtraNonce; 11 unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2 12 CMutableTransaction txCoinbase(*pblock->vtx[0]); 13 txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)); 14 assert(txCoinbase.vin[0].scriptSig.size() <= 100); 15 16 pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase)); 17 pblock->hashMerkleRoot = BlockMerkleRoot(*pblock); 18 }
第23~26行通过不断更新nNonce来检查区块的double sha256哈希值是否满足难度要求。
第34行函数ProcessNewBlock中将对生成的新区块进行检查,主要功能参见CheckBlock函数。
1 bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW, bool fCheckMerkleRoot) 2 { 3 // These are checks that are independent of context. 4 5 if (block.fChecked) 6 return true; 7 8 // Check that the header is valid (particularly PoW). This is mostly 9 // redundant with the call in AcceptBlockHeader. 10 if (!CheckBlockHeader(block, state, consensusParams, fCheckPOW)) 11 return false; 12 13 // Check the merkle root. 14 if (fCheckMerkleRoot) { 15 bool mutated; 16 uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated); 17 if (block.hashMerkleRoot != hashMerkleRoot2) 18 return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "bad-txnmrklroot", "hashMerkleRoot mismatch"); 19 20 // Check for merkle tree malleability (CVE-2012-2459): repeating sequences 21 // of transactions in a block without affecting the merkle root of a block, 22 // while still invalidating it. 23 if (mutated) 24 return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "bad-txns-duplicate", "duplicate transaction"); 25 } 26 27 // All potential-corruption validation must be done before we do any 28 // transaction validation, as otherwise we may mark the header as invalid 29 // because we receive the wrong transactions for it. 30 // Note that witness malleability is checked in ContextualCheckBlock, so no 31 // checks that use witness data may be performed here. 32 33 // Size limits 34 if (block.vtx.empty() || block.vtx.size() * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT || ::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT) 35 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-blk-length", "size limits failed"); 36 37 // First transaction must be coinbase, the rest must not be 38 if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) 39 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-missing", "first tx is not coinbase"); 40 for (unsigned int i = 1; i < block.vtx.size(); i++) 41 if (block.vtx[i]->IsCoinBase()) 42 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-multiple", "more than one coinbase"); 43 44 // Check transactions 45 // Must check for duplicate inputs (see CVE-2018-17144) 46 for (const auto& tx : block.vtx) { 47 TxValidationState tx_state; 48 if (!CheckTransaction(*tx, tx_state)) { 49 // CheckBlock() does context-free validation checks. The only 50 // possible failures are consensus failures. 51 assert(tx_state.GetResult() == TxValidationResult::TX_CONSENSUS); 52 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, tx_state.GetRejectReason(), 53 strprintf("Transaction check failed (tx hash %s) %s", tx->GetHash().ToString(), tx_state.GetDebugMessage())); 54 } 55 } 56 unsigned int nSigOps = 0; 57 for (const auto& tx : block.vtx) 58 { 59 nSigOps += GetLegacySigOpCount(*tx); 60 } 61 if (nSigOps * WITNESS_SCALE_FACTOR > MAX_BLOCK_SIGOPS_COST) 62 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-blk-sigops", "out-of-bounds SigOpCount"); 63 64 if (fCheckPOW && fCheckMerkleRoot) 65 block.fChecked = true; 66 67 return true; 68 }
在该函数中,会对区块进行POW检查、merkleroot检查、块儿size limits检查、coinbase检查、transactions检查、签名检查等。
2 块儿接收及本地存储时的检查
主要结合AcceptBlockHeader函数进行讲解,其他细节请参考源码。
1 bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) 2 { 3 AssertLockHeld(cs_main); 4 // Check for duplicate 5 uint256 hash = block.GetHash(); 6 BlockMap::iterator miSelf = m_block_index.find(hash); 7 CBlockIndex *pindex = nullptr; 8 if (hash != chainparams.GetConsensus().hashGenesisBlock) { 9 if (miSelf != m_block_index.end()) { 10 // Block header is already known. 11 pindex = miSelf->second; 12 if (ppindex) 13 *ppindex = pindex; 14 if (pindex->nStatus & BLOCK_FAILED_MASK) { 15 LogPrintf("ERROR: %s: block %s is marked invalid\n", __func__, hash.ToString()); 16 return state.Invalid(BlockValidationResult::BLOCK_CACHED_INVALID, "duplicate"); 17 } 18 return true; 19 } 20 21 if (!CheckBlockHeader(block, state, chainparams.GetConsensus())) 22 return error("%s: Consensus::CheckBlockHeader: %s, %s", __func__, hash.ToString(), state.ToString()); 23 24 // Get prev block index 25 CBlockIndex* pindexPrev = nullptr; 26 BlockMap::iterator mi = m_block_index.find(block.hashPrevBlock); 27 if (mi == m_block_index.end()) { 28 LogPrintf("ERROR: %s: prev block not found\n", __func__); 29 return state.Invalid(BlockValidationResult::BLOCK_MISSING_PREV, "prev-blk-not-found"); 30 } 31 pindexPrev = (*mi).second; 32 if (pindexPrev->nStatus & BLOCK_FAILED_MASK) { 33 LogPrintf("ERROR: %s: prev block invalid\n", __func__); 34 return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk"); 35 } 36 if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime())) 37 return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), state.ToString()); 38 39 /* Determine if this block descends from any block which has been found 40 * invalid (m_failed_blocks), then mark pindexPrev and any blocks between 41 * them as failed. For example: 42 * 43 * D3 44 * / 45 * B2 - C2 46 * / \ 47 * A D2 - E2 - F2 48 * \ 49 * B1 - C1 - D1 - E1 50 * 51 * In the case that we attempted to reorg from E1 to F2, only to find 52 * C2 to be invalid, we would mark D2, E2, and F2 as BLOCK_FAILED_CHILD 53 * but NOT D3 (it was not in any of our candidate sets at the time). 54 * 55 * In any case D3 will also be marked as BLOCK_FAILED_CHILD at restart 56 * in LoadBlockIndex. 57 */ 58 if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) { 59 // The above does not mean "invalid": it checks if the previous block 60 // hasn't been validated up to BLOCK_VALID_SCRIPTS. This is a performance 61 // optimization, in the common case of adding a new block to the tip, 62 // we don't need to iterate over the failed blocks list. 63 for (const CBlockIndex* failedit : m_failed_blocks) { 64 if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) { 65 assert(failedit->nStatus & BLOCK_FAILED_VALID); 66 CBlockIndex* invalid_walk = pindexPrev; 67 while (invalid_walk != failedit) { 68 invalid_walk->nStatus |= BLOCK_FAILED_CHILD; 69 setDirtyBlockIndex.insert(invalid_walk); 70 invalid_walk = invalid_walk->pprev; 71 } 72 LogPrintf("ERROR: %s: prev block invalid\n", __func__); 73 return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk"); 74 } 75 } 76 } 77 } 78 if (pindex == nullptr) 79 pindex = AddToBlockIndex(block); 80 81 if (ppindex) 82 *ppindex = pindex; 83 84 return true; 85 }
第21行CheckBlockHeader函数主要进行POW检查。
第36行ContextualCheckBlockHeader函数进行上下文检查。
1 static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidationState& state, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(cs_main) 2 { 3 assert(pindexPrev != nullptr); 4 const int nHeight = pindexPrev->nHeight + 1; 5 6 // Check proof of work 7 const Consensus::Params& consensusParams = params.GetConsensus(); 8 if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) 9 return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "bad-diffbits", "incorrect proof of work"); 10 11 // Check against checkpoints 12 if (fCheckpointsEnabled) { 13 // Don't accept any forks from the main chain prior to last checkpoint. 14 // GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our 15 // g_blockman.m_block_index. 16 CBlockIndex* pcheckpoint = GetLastCheckpoint(params.Checkpoints()); 17 if (pcheckpoint && nHeight < pcheckpoint->nHeight) { 18 LogPrintf("ERROR: %s: forked chain older than last checkpoint (height %d)\n", __func__, nHeight); 19 return state.Invalid(BlockValidationResult::BLOCK_CHECKPOINT, "bad-fork-prior-to-checkpoint"); 20 } 21 } 22 23 // Check timestamp against prev 24 if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) 25 return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "time-too-old", "block's timestamp is too early"); 26 27 // Check timestamp 28 if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME) 29 return state.Invalid(BlockValidationResult::BLOCK_TIME_FUTURE, "time-too-new", "block timestamp too far in the future"); 30 31 // Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded: 32 // check for version 2, 3 and 4 upgrades 33 if((block.nVersion < 2 && nHeight >= consensusParams.BIP34Height) || 34 (block.nVersion < 3 && nHeight >= consensusParams.BIP66Height) || 35 (block.nVersion < 4 && nHeight >= consensusParams.BIP65Height)) 36 return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, strprintf("bad-version(0x%08x)", block.nVersion), 37 strprintf("rejected nVersion=0x%08x block", block.nVersion)); 38 39 return true; 40 }
在该函数中会依次进行nBits检查、checkpoints检查、时间戳检查、nVersion检查。
第58行之后代码将会进行父块儿有效性检查。
下面详细说明时间戳检查
2.1 当前产生块儿时间戳
首先确定当前产生区块儿的时间戳nTime,它并不是直接取本地系统时间,而是当前时间+偏移量(可以理解成网络延迟)。
偏移量的计算方法:
1 取网络上其他节点的系统时间x(建立节点时发出version请求)
2 计算出x与本地系统时间的差值y
3 取n个y并排序,n的值在5-200中间
4 取上述排序的中位数即为偏移量(网络延迟)
5 如其他节点数量小于5个,则偏移量为0
该部分内容详情可查看源码timedata.cpp中AddTimeData函数实现,之后还会调用UpdateTime函数进一步更新该时间戳,取nTime和前一块儿medianTime的较大值。
medianTime时间由GetMedianTimePast函数获取,该函数从当前块儿开始,取nMedianTimeSpan(默认11)个区块时间排序的中位值。
2.2 时间戳检查
时间戳检查时,从两个方面进行检查
1 过去时间中值(MPT)规则
向前比较,时间戳必须比过去 11 个区块的中值更大,否则认为块儿太老了(错误信息:block's timestamp is too early)。
2 未来区块时间规则
向后比较,根据MAX_FUTURE_BLOCK_TIME(2 * 60 * 60)常量,相比当前节点的中值时间,块儿时间戳不能出现在未来2个小时以上。
否则认为块儿太新了(错误信息:block timestamp too far in the future)。
参考:
https://blog.bitmex.com/zh_cn-bitcoins-block-timestamp-protection-rules/