比特币区块检查

比特币采用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 }
IncrementExtraNonce

第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 }
CheckBlock

在该函数中,会对区块进行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 }
ContextualCheckBlockHeader

在该函数中会依次进行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/

posted @ 2024-06-12 19:12  weiwei22844  阅读(38)  评论(0编辑  收藏  举报