Auto merge of #5220 - str4d:5197-header-commitments, r=str4d
NU5 header commitments and consensus rules Part of zcash/zcash#5197.
This commit is contained in:
commit
b0e6119551
|
@ -116,6 +116,7 @@ BASE_SCRIPTS= [
|
|||
'framework.py',
|
||||
'sapling_rewind_check.py',
|
||||
'feature_zip221.py',
|
||||
'feature_zip244_blockcommitments.py',
|
||||
'upgrade_golden.py',
|
||||
'post_heartwood_rollback.py',
|
||||
'feature_logging.py',
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2020 The Zcash developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or https://www.opensource.org/licenses/mit-license.php .
|
||||
|
||||
|
||||
from test_framework.blocktools import derive_block_commitments_hash
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
bytes_to_hex_str,
|
||||
hex_str_to_bytes,
|
||||
start_nodes,
|
||||
)
|
||||
|
||||
TERMINATOR = b'\x00' * 32
|
||||
|
||||
# Verify block header field 'hashLightClientRoot' is set correctly for NU5 blocks.
|
||||
class AuthDataRootTest(BitcoinTestFramework):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.num_nodes = 4
|
||||
|
||||
def setup_nodes(self):
|
||||
return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[
|
||||
'-nuparams=5ba81b19:1', # Overwinter
|
||||
'-nuparams=76b809bb:1', # Sapling
|
||||
'-nuparams=2bb40e60:201', # Blossom
|
||||
'-nuparams=f5b9230b:201', # Heartwood
|
||||
'-nuparams=e9ff75a6:201', # Canopy
|
||||
'-nuparams=f919a198:205', # NU5
|
||||
'-nurejectoldversions=false',
|
||||
]] * self.num_nodes)
|
||||
|
||||
def run_test(self):
|
||||
# Generate a block so we are on Canopy rules.
|
||||
self.nodes[0].generate(2)
|
||||
self.sync_all()
|
||||
|
||||
# For blocks prior to NU5 activation, the hashBlockCommitments field of
|
||||
# the block header contains the root of the ZIP 221 history tree.
|
||||
for i in range(3):
|
||||
block_before = self.nodes[0].getblock('%d' % (202 + i))
|
||||
assert_equal(block_before['blockcommitments'], block_before['chainhistoryroot'])
|
||||
|
||||
self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
|
||||
# From NU5 activation, the hashBlockCommitments field of the block
|
||||
# header contains a hash of various block commitments (per ZIP 244).
|
||||
for i in range(2):
|
||||
block_after = self.nodes[0].getblock('%d' % (205 + i))
|
||||
block_commitments = bytes_to_hex_str(derive_block_commitments_hash(
|
||||
hex_str_to_bytes(block_after['chainhistoryroot'])[::-1],
|
||||
hex_str_to_bytes(block_after['authdataroot'])[::-1],
|
||||
)[::-1])
|
||||
assert_equal(block_after['blockcommitments'], block_commitments)
|
||||
|
||||
self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
AuthDataRootTest().main()
|
|
@ -4,6 +4,8 @@
|
|||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or https://www.opensource.org/licenses/mit-license.php .
|
||||
|
||||
from pyblake2 import blake2b
|
||||
|
||||
from .mininode import CBlock, CTransaction, CTxIn, CTxOut, COutPoint
|
||||
from .script import CScript, OP_0, OP_EQUAL, OP_HASH160, OP_TRUE, OP_CHECKSIG
|
||||
|
||||
|
@ -29,6 +31,15 @@ def create_block(hashprev, coinbase, nTime=None, nBits=None, hashFinalSaplingRoo
|
|||
block.calc_sha256()
|
||||
return block
|
||||
|
||||
def derive_block_commitments_hash(chain_history_root, auth_data_root):
|
||||
digest = blake2b(
|
||||
digest_size=32,
|
||||
person=b'ZcashBlockCommit')
|
||||
digest.update(chain_history_root)
|
||||
digest.update(auth_data_root)
|
||||
digest.update(b'\x00' * 32)
|
||||
return digest.digest()
|
||||
|
||||
def serialize_script_num(value):
|
||||
r = bytearray(0)
|
||||
if value == 0:
|
||||
|
|
52
src/chain.h
52
src/chain.h
|
@ -18,6 +18,7 @@
|
|||
static const int SPROUT_VALUE_VERSION = 1001400;
|
||||
static const int SAPLING_VALUE_VERSION = 1010100;
|
||||
static const int CHAIN_HISTORY_ROOT_VERSION = 2010200;
|
||||
static const int NU5_DATA_VERSION = 4050000;
|
||||
|
||||
/**
|
||||
* Maximum amount of time that a block timestamp is allowed to be ahead of the
|
||||
|
@ -231,6 +232,14 @@ public:
|
|||
//! Persisted at each activation height, memory-only for intervening blocks.
|
||||
std::optional<uint32_t> nCachedBranchId;
|
||||
|
||||
//! Root of the ZIP 244 authorizing data commitment tree for this block.
|
||||
//!
|
||||
//! - For blocks prior to (not including) the NU5 activation block, this is always
|
||||
//! null.
|
||||
//! - For blocks including and after the NU5 activation block, this is only set once
|
||||
//! a block has been connected to the main chain, and will be null otherwise.
|
||||
uint256 hashAuthDataRoot;
|
||||
|
||||
//! The anchor for the tree state up to the start of this block
|
||||
uint256 hashSproutAnchor;
|
||||
|
||||
|
@ -258,23 +267,34 @@ public:
|
|||
//! Root of the Sapling commitment tree as of the end of this block.
|
||||
//!
|
||||
//! - For blocks prior to (not including) the Heartwood activation block, this is
|
||||
//! always equal to hashLightClientRoot.
|
||||
//! always equal to hashBlockCommitments.
|
||||
//! - For blocks including and after the Heartwood activation block, this is only set
|
||||
//! once a block has been connected to the main chain, and will be null otherwise.
|
||||
uint256 hashFinalSaplingRoot;
|
||||
|
||||
//! Root of the Orchard commitment tree as of the end of this block.
|
||||
//!
|
||||
//! - For blocks prior to (not including) the NU5 activation block, this is always
|
||||
//! null.
|
||||
//! - For blocks including and after the NU5 activation block, this is only set
|
||||
//! once a block has been connected to the main chain, and will be null otherwise.
|
||||
uint256 hashFinalOrchardRoot;
|
||||
|
||||
//! Root of the ZIP 221 history tree as of the end of the previous block.
|
||||
//!
|
||||
//! - For blocks prior to and including the Heartwood activation block, this is
|
||||
//! always null.
|
||||
//! - For blocks after (not including) the Heartwood activation block, this is
|
||||
//! always equal to hashLightClientRoot.
|
||||
//! - For blocks after (not including) the Heartwood activation block, and prior to
|
||||
//! (not including) the NU5 activation block, this is always equal to
|
||||
//! hashBlockCommitments.
|
||||
//! - For blocks including and after the NU5 activation block, this is only set
|
||||
//! once a block has been connected to the main chain, and will be null otherwise.
|
||||
uint256 hashChainHistoryRoot;
|
||||
|
||||
//! block header
|
||||
int nVersion;
|
||||
uint256 hashMerkleRoot;
|
||||
uint256 hashLightClientRoot;
|
||||
uint256 hashBlockCommitments;
|
||||
unsigned int nTime;
|
||||
unsigned int nBits;
|
||||
uint256 nNonce;
|
||||
|
@ -297,8 +317,12 @@ public:
|
|||
nChainTx = 0;
|
||||
nStatus = 0;
|
||||
nCachedBranchId = std::nullopt;
|
||||
hashAuthDataRoot = uint256();
|
||||
hashSproutAnchor = uint256();
|
||||
hashFinalSproutRoot = uint256();
|
||||
hashFinalSaplingRoot = uint256();
|
||||
hashFinalOrchardRoot = uint256();
|
||||
hashChainHistoryRoot = uint256();
|
||||
nSequenceId = 0;
|
||||
nSproutValue = std::nullopt;
|
||||
nChainSproutValue = std::nullopt;
|
||||
|
@ -307,7 +331,7 @@ public:
|
|||
|
||||
nVersion = 0;
|
||||
hashMerkleRoot = uint256();
|
||||
hashLightClientRoot = uint256();
|
||||
hashBlockCommitments = uint256();
|
||||
nTime = 0;
|
||||
nBits = 0;
|
||||
nNonce = uint256();
|
||||
|
@ -325,7 +349,7 @@ public:
|
|||
|
||||
nVersion = block.nVersion;
|
||||
hashMerkleRoot = block.hashMerkleRoot;
|
||||
hashLightClientRoot = block.hashLightClientRoot;
|
||||
hashBlockCommitments = block.hashBlockCommitments;
|
||||
nTime = block.nTime;
|
||||
nBits = block.nBits;
|
||||
nNonce = block.nNonce;
|
||||
|
@ -357,7 +381,7 @@ public:
|
|||
if (pprev)
|
||||
block.hashPrevBlock = pprev->GetBlockHash();
|
||||
block.hashMerkleRoot = hashMerkleRoot;
|
||||
block.hashLightClientRoot = hashLightClientRoot;
|
||||
block.hashBlockCommitments = hashBlockCommitments;
|
||||
block.nTime = nTime;
|
||||
block.nBits = nBits;
|
||||
block.nNonce = nNonce;
|
||||
|
@ -486,7 +510,7 @@ public:
|
|||
READWRITE(this->nVersion);
|
||||
READWRITE(hashPrev);
|
||||
READWRITE(hashMerkleRoot);
|
||||
READWRITE(hashLightClientRoot);
|
||||
READWRITE(hashBlockCommitments);
|
||||
READWRITE(nTime);
|
||||
READWRITE(nBits);
|
||||
READWRITE(nNonce);
|
||||
|
@ -512,7 +536,15 @@ public:
|
|||
} else if (ser_action.ForRead()) {
|
||||
// For block indices written before the client was Heartwood-aware,
|
||||
// these are always identical.
|
||||
hashFinalSaplingRoot = hashLightClientRoot;
|
||||
hashFinalSaplingRoot = hashBlockCommitments;
|
||||
}
|
||||
|
||||
// Only read/write NU5 data if the client version used to create this
|
||||
// index was storing them. For block indices written before the client
|
||||
// was NU5-aware, these are always null / zero.
|
||||
if ((s.GetType() & SER_DISK) && (nVersion >= NU5_DATA_VERSION)) {
|
||||
READWRITE(hashAuthDataRoot);
|
||||
READWRITE(hashFinalOrchardRoot);
|
||||
}
|
||||
|
||||
// If you have just added new serialized fields above, remember to add
|
||||
|
@ -525,7 +557,7 @@ public:
|
|||
block.nVersion = nVersion;
|
||||
block.hashPrevBlock = hashPrev;
|
||||
block.hashMerkleRoot = hashMerkleRoot;
|
||||
block.hashLightClientRoot = hashLightClientRoot;
|
||||
block.hashBlockCommitments = hashBlockCommitments;
|
||||
block.nTime = nTime;
|
||||
block.nBits = nBits;
|
||||
block.nNonce = nNonce;
|
||||
|
|
88
src/main.cpp
88
src/main.cpp
|
@ -2732,7 +2732,8 @@ static bool ShouldCheckTransactions(const CChainParams& chainparams, const CBloc
|
|||
}
|
||||
|
||||
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex,
|
||||
CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck)
|
||||
CCoinsViewCache& view, const CChainParams& chainparams,
|
||||
bool fJustCheck, bool fCheckAuthDataRoot)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
|
||||
|
@ -2998,6 +2999,17 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
|
||||
}
|
||||
|
||||
// Derive the various block commitments.
|
||||
// We only derive them if they will be used for this block.
|
||||
std::optional<uint256> hashAuthDataRoot;
|
||||
std::optional<uint256> hashChainHistoryRoot;
|
||||
if (chainparams.GetConsensus().NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_NU5)) {
|
||||
hashAuthDataRoot = block.BuildAuthDataMerkleTree();
|
||||
}
|
||||
if (chainparams.GetConsensus().NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_HEARTWOOD)) {
|
||||
hashChainHistoryRoot = view.GetHistoryRoot(prevConsensusBranchId);
|
||||
}
|
||||
|
||||
view.PushAnchor(sprout_tree);
|
||||
view.PushAnchor(sapling_tree);
|
||||
if (!fJustCheck) {
|
||||
|
@ -3012,19 +3024,48 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||
if (chainparams.GetConsensus().NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_HEARTWOOD)) {
|
||||
pindex->hashFinalSaplingRoot = sapling_tree.root();
|
||||
}
|
||||
|
||||
// - If this block is before NU5 activation:
|
||||
// - hashAuthDataRoot and hashFinalOrchardRoot are always null.
|
||||
// - We don't set hashChainHistoryRoot here to maintain the invariant
|
||||
// documented in CBlockIndex (which was ensured in AddToBlockIndex).
|
||||
// - If this block is on or after NU5 activation, this is where we set
|
||||
// the correct values of hashAuthDataRoot, hashFinalOrchardRoot, and
|
||||
// hashChainHistoryRoot; in particular, blocks that are never passed
|
||||
// to ConnectBlock() (and thus never on the main chain) will stay with
|
||||
// these set to null.
|
||||
if (chainparams.GetConsensus().NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_NU5)) {
|
||||
pindex->hashAuthDataRoot = hashAuthDataRoot.value();
|
||||
pindex->hashFinalOrchardRoot = uint256(); // TODO: replace with Orchard tree root
|
||||
pindex->hashChainHistoryRoot = hashChainHistoryRoot.value();
|
||||
}
|
||||
}
|
||||
blockundo.old_sprout_tree_root = old_sprout_tree_root;
|
||||
|
||||
if (IsActivationHeight(pindex->nHeight, chainparams.GetConsensus(), Consensus::UPGRADE_HEARTWOOD)) {
|
||||
// In the block that activates ZIP 221, block.hashLightClientRoot MUST
|
||||
if (chainparams.GetConsensus().NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_NU5)) {
|
||||
if (fCheckAuthDataRoot) {
|
||||
// If NU5 is active, block.hashBlockCommitments must be the top digest
|
||||
// of the ZIP 244 block commitments linked list.
|
||||
// https://zips.z.cash/zip-0244#block-header-changes
|
||||
uint256 hashBlockCommitments = DeriveBlockCommitmentsHash(
|
||||
hashChainHistoryRoot.value(),
|
||||
hashAuthDataRoot.value());
|
||||
if (block.hashBlockCommitments != hashBlockCommitments) {
|
||||
return state.DoS(100,
|
||||
error("ConnectBlock(): block's hashBlockCommitments is incorrect (should be ZIP 244 block commitment)"),
|
||||
REJECT_INVALID, "bad-block-commitments-hash");
|
||||
}
|
||||
}
|
||||
} else if (IsActivationHeight(pindex->nHeight, chainparams.GetConsensus(), Consensus::UPGRADE_HEARTWOOD)) {
|
||||
// In the block that activates ZIP 221, block.hashBlockCommitments MUST
|
||||
// be set to all zero bytes.
|
||||
if (!block.hashLightClientRoot.IsNull()) {
|
||||
if (!block.hashBlockCommitments.IsNull()) {
|
||||
return state.DoS(100,
|
||||
error("ConnectBlock(): block's hashLightClientRoot is incorrect (should be null)"),
|
||||
error("ConnectBlock(): block's hashBlockCommitments is incorrect (should be null)"),
|
||||
REJECT_INVALID, "bad-heartwood-root-in-block");
|
||||
}
|
||||
} else if (chainparams.GetConsensus().NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_HEARTWOOD)) {
|
||||
// If Heartwood is active, block.hashLightClientRoot must be the same as
|
||||
// If Heartwood is active, block.hashBlockCommitments must be the same as
|
||||
// the root of the history tree for the previous block. We only store
|
||||
// one tree per epoch, so we have two possible cases:
|
||||
// - If the previous block is in the previous epoch, this block won't
|
||||
|
@ -3032,17 +3073,17 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||
// - If the previous block is in this epoch, this block would affect
|
||||
// this epoch's tree root, but as we haven't updated the tree for this
|
||||
// block yet, view.GetHistoryRoot() returns the root we need.
|
||||
if (block.hashLightClientRoot != view.GetHistoryRoot(prevConsensusBranchId)) {
|
||||
if (block.hashBlockCommitments != hashChainHistoryRoot.value()) {
|
||||
return state.DoS(100,
|
||||
error("ConnectBlock(): block's hashLightClientRoot is incorrect (should be history tree root)"),
|
||||
error("ConnectBlock(): block's hashBlockCommitments is incorrect (should be history tree root)"),
|
||||
REJECT_INVALID, "bad-heartwood-root-in-block");
|
||||
}
|
||||
} else if (chainparams.GetConsensus().NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_SAPLING)) {
|
||||
// If Sapling is active, block.hashLightClientRoot must be the
|
||||
// If Sapling is active, block.hashBlockCommitments must be the
|
||||
// same as the root of the Sapling tree
|
||||
if (block.hashLightClientRoot != sapling_tree.root()) {
|
||||
if (block.hashBlockCommitments != sapling_tree.root()) {
|
||||
return state.DoS(100,
|
||||
error("ConnectBlock(): block's hashLightClientRoot is incorrect (should be Sapling tree root)"),
|
||||
error("ConnectBlock(): block's hashBlockCommitments is incorrect (should be Sapling tree root)"),
|
||||
REJECT_INVALID, "bad-sapling-root-in-block");
|
||||
}
|
||||
}
|
||||
|
@ -3910,15 +3951,26 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block, const Consensus::Params&
|
|||
pindexNew->pprev = (*miPrev).second;
|
||||
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
|
||||
|
||||
if (IsActivationHeight(pindexNew->nHeight, consensusParams, Consensus::UPGRADE_HEARTWOOD)) {
|
||||
// hashFinalSaplingRoot is currently null, and will be set correctly in ConnectBlock.
|
||||
if (consensusParams.NetworkUpgradeActive(pindexNew->nHeight, Consensus::UPGRADE_NU5)) {
|
||||
// The following hashes will be null if this block has never been
|
||||
// connected to a main chain; they will be (re)set correctly in
|
||||
// ConnectBlock:
|
||||
// - hashFinalSaplingRoot
|
||||
// - hashFinalOrchardRoot
|
||||
// - hashChainHistoryRoot
|
||||
} else if (IsActivationHeight(pindexNew->nHeight, consensusParams, Consensus::UPGRADE_HEARTWOOD)) {
|
||||
// hashFinalSaplingRoot and hashFinalOrchardRoot will be null if this block has
|
||||
// never been connected to a main chain; they will be (re)set correctly in
|
||||
// ConnectBlock.
|
||||
// hashChainHistoryRoot is null.
|
||||
} else if (consensusParams.NetworkUpgradeActive(pindexNew->nHeight, Consensus::UPGRADE_HEARTWOOD)) {
|
||||
// hashFinalSaplingRoot is currently null, and will be set correctly in ConnectBlock.
|
||||
pindexNew->hashChainHistoryRoot = pindexNew->hashLightClientRoot;
|
||||
// hashFinalSaplingRoot and hashFinalOrchardRoot will be null if this block has
|
||||
// never been connected to a main chain; they will be (re)set correctly in
|
||||
// ConnectBlock.
|
||||
pindexNew->hashChainHistoryRoot = pindexNew->hashBlockCommitments;
|
||||
} else {
|
||||
// hashChainHistoryRoot is null.
|
||||
pindexNew->hashFinalSaplingRoot = pindexNew->hashLightClientRoot;
|
||||
// hashFinalOrchardRoot and hashChainHistoryRoot are null.
|
||||
pindexNew->hashFinalSaplingRoot = pindexNew->hashBlockCommitments;
|
||||
}
|
||||
|
||||
pindexNew->BuildSkip();
|
||||
|
@ -4565,7 +4617,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
|
|||
return false;
|
||||
if (!ContextualCheckBlock(block, state, chainparams, pindexPrev, true))
|
||||
return false;
|
||||
if (!ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true))
|
||||
if (!ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true, fCheckMerkleRoot))
|
||||
return false;
|
||||
assert(state.IsValid());
|
||||
|
||||
|
|
|
@ -486,7 +486,8 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state,
|
|||
* Validity checks that depend on the UTXO set are also done; ConnectBlock()
|
||||
* can fail if those validity checks fail (among other reasons). */
|
||||
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins,
|
||||
const CChainParams& chainparams, bool fJustCheck = false);
|
||||
const CChainParams& chainparams,
|
||||
bool fJustCheck = false, bool fCheckAuthDataRoot = true);
|
||||
|
||||
/**
|
||||
* Check a block is completely valid from start to finish (only works on top
|
||||
|
|
|
@ -621,12 +621,17 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const MinerAddre
|
|||
|
||||
// Fill in header
|
||||
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
|
||||
if (IsActivationHeight(nHeight, chainparams.GetConsensus(), Consensus::UPGRADE_HEARTWOOD)) {
|
||||
pblock->hashLightClientRoot.SetNull();
|
||||
if (chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_NU5)) {
|
||||
// hashBlockCommitments depends on the block transactions, so we have to
|
||||
// update it whenever the coinbase transaction changes. Leave it unset here,
|
||||
// like hashMerkleRoot, and instead cache what we will need to calculate it.
|
||||
pblocktemplate->hashChainHistoryRoot = view.GetHistoryRoot(prevConsensusBranchId);
|
||||
} else if (IsActivationHeight(nHeight, chainparams.GetConsensus(), Consensus::UPGRADE_HEARTWOOD)) {
|
||||
pblock->hashBlockCommitments.SetNull();
|
||||
} else if (chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_HEARTWOOD)) {
|
||||
pblock->hashLightClientRoot = view.GetHistoryRoot(prevConsensusBranchId);
|
||||
pblock->hashBlockCommitments = view.GetHistoryRoot(prevConsensusBranchId);
|
||||
} else {
|
||||
pblock->hashLightClientRoot = sapling_tree.root();
|
||||
pblock->hashBlockCommitments = sapling_tree.root();
|
||||
}
|
||||
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
|
||||
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
|
||||
|
@ -682,8 +687,13 @@ void GetMinerAddress(MinerAddress &minerAddress)
|
|||
}
|
||||
}
|
||||
|
||||
void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
|
||||
void IncrementExtraNonce(
|
||||
CBlockTemplate* pblocktemplate,
|
||||
const CBlockIndex* pindexPrev,
|
||||
unsigned int& nExtraNonce,
|
||||
const Consensus::Params& consensusParams)
|
||||
{
|
||||
CBlock *pblock = &pblocktemplate->block;
|
||||
// Update nExtraNonce
|
||||
static uint256 hashPrevBlock;
|
||||
if (hashPrevBlock != pblock->hashPrevBlock)
|
||||
|
@ -699,6 +709,11 @@ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned
|
|||
|
||||
pblock->vtx[0] = txCoinbase;
|
||||
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
|
||||
if (consensusParams.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_NU5)) {
|
||||
pblock->hashBlockCommitments = DeriveBlockCommitmentsHash(
|
||||
pblocktemplate->hashChainHistoryRoot,
|
||||
pblock->BuildAuthDataMerkleTree());
|
||||
}
|
||||
}
|
||||
|
||||
static bool ProcessBlockFound(const CBlock* pblock, const CChainParams& chainparams)
|
||||
|
@ -797,7 +812,7 @@ void static BitcoinMiner(const CChainParams& chainparams)
|
|||
return;
|
||||
}
|
||||
CBlock *pblock = &pblocktemplate->block;
|
||||
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
|
||||
IncrementExtraNonce(pblocktemplate.get(), pindexPrev, nExtraNonce, chainparams.GetConsensus());
|
||||
|
||||
LogPrintf("Running ZcashMiner with %u transactions in block (%u bytes)\n", pblock->vtx.size(),
|
||||
::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION));
|
||||
|
|
|
@ -67,6 +67,9 @@ public:
|
|||
struct CBlockTemplate
|
||||
{
|
||||
CBlock block;
|
||||
// Cached whenever we update `block`, so we can update hashBlockCommitments
|
||||
// when we change the coinbase transaction.
|
||||
uint256 hashChainHistoryRoot;
|
||||
std::vector<CAmount> vTxFees;
|
||||
std::vector<int64_t> vTxSigOps;
|
||||
};
|
||||
|
@ -80,7 +83,11 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const MinerAddre
|
|||
/** Get -mineraddress */
|
||||
void GetMinerAddress(MinerAddress &minerAddress);
|
||||
/** Modify the extranonce in a block */
|
||||
void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
|
||||
void IncrementExtraNonce(
|
||||
CBlockTemplate* pblocktemplate,
|
||||
const CBlockIndex* pindexPrev,
|
||||
unsigned int& nExtraNonce,
|
||||
const Consensus::Params& consensusParams);
|
||||
/** Run the miner threads */
|
||||
void GenerateBitcoins(bool fGenerate, int nThreads, const CChainParams& chainparams);
|
||||
#endif
|
||||
|
|
|
@ -14,6 +14,20 @@
|
|||
|
||||
const unsigned char ZCASH_AUTH_DATA_HASH_PERSONALIZATION[BLAKE2bPersonalBytes] =
|
||||
{'Z','c','a','s','h','A','u','t','h','D','a','t','H','a','s','h'};
|
||||
const unsigned char ZCASH_BLOCK_COMMITMENTS_HASH_PERSONALIZATION[BLAKE2bPersonalBytes] =
|
||||
{'Z','c','a','s','h','B','l','o','c','k','C','o','m','m','i','t'};
|
||||
|
||||
uint256 DeriveBlockCommitmentsHash(
|
||||
uint256 hashChainHistoryRoot,
|
||||
uint256 hashAuthDataRoot)
|
||||
{
|
||||
// https://zips.z.cash/zip-0244#block-header-changes
|
||||
CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_BLOCK_COMMITMENTS_HASH_PERSONALIZATION);
|
||||
ss << hashChainHistoryRoot;
|
||||
ss << hashAuthDataRoot;
|
||||
ss << uint256(); // terminator
|
||||
return ss.GetHash();
|
||||
}
|
||||
|
||||
uint256 CBlockHeader::GetHash() const
|
||||
{
|
||||
|
@ -164,12 +178,12 @@ uint256 CBlock::BuildAuthDataMerkleTree() const
|
|||
std::string CBlock::ToString() const
|
||||
{
|
||||
std::stringstream s;
|
||||
s << strprintf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, hashLightClientRoot=%s, nTime=%u, nBits=%08x, nNonce=%s, vtx=%u)\n",
|
||||
s << strprintf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, hashBlockCommitments=%s, nTime=%u, nBits=%08x, nNonce=%s, vtx=%u)\n",
|
||||
GetHash().ToString(),
|
||||
nVersion,
|
||||
hashPrevBlock.ToString(),
|
||||
hashMerkleRoot.ToString(),
|
||||
hashLightClientRoot.ToString(),
|
||||
hashBlockCommitments.ToString(),
|
||||
nTime, nBits, nNonce.ToString(),
|
||||
vtx.size());
|
||||
for (unsigned int i = 0; i < vtx.size(); i++)
|
||||
|
|
|
@ -10,6 +10,11 @@
|
|||
#include "serialize.h"
|
||||
#include "uint256.h"
|
||||
|
||||
// Derives the ZIP 244 block commitments hash.
|
||||
uint256 DeriveBlockCommitmentsHash(
|
||||
uint256 hashChainHistoryRoot,
|
||||
uint256 hashAuthDataRoot);
|
||||
|
||||
/** Nodes collect new transactions into a block, hash them into a hash tree,
|
||||
* and scan through nonce values to make the block's hash satisfy proof-of-work
|
||||
* requirements. When they solve the proof-of-work, they broadcast the block
|
||||
|
@ -26,7 +31,7 @@ public:
|
|||
int32_t nVersion;
|
||||
uint256 hashPrevBlock;
|
||||
uint256 hashMerkleRoot;
|
||||
uint256 hashLightClientRoot;
|
||||
uint256 hashBlockCommitments;
|
||||
uint32_t nTime;
|
||||
uint32_t nBits;
|
||||
uint256 nNonce;
|
||||
|
@ -44,7 +49,7 @@ public:
|
|||
READWRITE(this->nVersion);
|
||||
READWRITE(hashPrevBlock);
|
||||
READWRITE(hashMerkleRoot);
|
||||
READWRITE(hashLightClientRoot);
|
||||
READWRITE(hashBlockCommitments);
|
||||
READWRITE(nTime);
|
||||
READWRITE(nBits);
|
||||
READWRITE(nNonce);
|
||||
|
@ -56,7 +61,7 @@ public:
|
|||
nVersion = CBlockHeader::CURRENT_VERSION;
|
||||
hashPrevBlock.SetNull();
|
||||
hashMerkleRoot.SetNull();
|
||||
hashLightClientRoot.SetNull();
|
||||
hashBlockCommitments.SetNull();
|
||||
nTime = 0;
|
||||
nBits = 0;
|
||||
nNonce = uint256();
|
||||
|
@ -118,7 +123,7 @@ public:
|
|||
block.nVersion = nVersion;
|
||||
block.hashPrevBlock = hashPrevBlock;
|
||||
block.hashMerkleRoot = hashMerkleRoot;
|
||||
block.hashLightClientRoot = hashLightClientRoot;
|
||||
block.hashBlockCommitments = hashBlockCommitments;
|
||||
block.nTime = nTime;
|
||||
block.nBits = nBits;
|
||||
block.nNonce = nNonce;
|
||||
|
@ -163,7 +168,7 @@ public:
|
|||
READWRITE(this->nVersion);
|
||||
READWRITE(hashPrevBlock);
|
||||
READWRITE(hashMerkleRoot);
|
||||
READWRITE(hashLightClientRoot);
|
||||
READWRITE(hashBlockCommitments);
|
||||
READWRITE(nTime);
|
||||
READWRITE(nBits);
|
||||
}
|
||||
|
|
|
@ -252,6 +252,7 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) {
|
|||
*const_cast<Ed25519Signature*>(&joinSplitSig) = tx.joinSplitSig;
|
||||
*const_cast<binding_sig_t*>(&bindingSig) = tx.bindingSig;
|
||||
*const_cast<uint256*>(&hash) = tx.hash;
|
||||
*const_cast<uint256*>(&authDigest) = tx.authDigest;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -233,6 +233,8 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
|
|||
result.pushKV("height", blockindex->nHeight);
|
||||
result.pushKV("version", block.nVersion);
|
||||
result.pushKV("merkleroot", block.hashMerkleRoot.GetHex());
|
||||
result.pushKV("blockcommitments", blockindex->hashBlockCommitments.GetHex());
|
||||
result.pushKV("authdataroot", blockindex->hashAuthDataRoot.GetHex());
|
||||
result.pushKV("finalsaplingroot", blockindex->hashFinalSaplingRoot.GetHex());
|
||||
result.pushKV("chainhistoryroot", blockindex->hashChainHistoryRoot.GetHex());
|
||||
UniValue txs(UniValue::VARR);
|
||||
|
|
|
@ -218,7 +218,7 @@ UniValue generate(const UniValue& params, bool fHelp)
|
|||
CBlock *pblock = &pblocktemplate->block;
|
||||
{
|
||||
LOCK(cs_main);
|
||||
IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce);
|
||||
IncrementExtraNonce(pblocktemplate.get(), chainActive.Tip(), nExtraNonce, Params().GetConsensus());
|
||||
}
|
||||
|
||||
// Hash state
|
||||
|
@ -444,7 +444,8 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
|
|||
"{\n"
|
||||
" \"version\" : n, (numeric) The block version\n"
|
||||
" \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n"
|
||||
" \"lightclientroothash\" : \"xxxx\", (string) The hash of the light client root field in the block header\n"
|
||||
" \"blockcommitmentshash\" : \"xxxx\", (string) The hash of the block commitments field in the block header\n"
|
||||
" \"lightclientroothash\" : \"xxxx\", (string) (DEPRECATED) The hash of the light client root field in the block header\n"
|
||||
" \"finalsaplingroothash\" : \"xxxx\", (string) (DEPRECATED) The hash of the light client root field in the block header\n"
|
||||
" \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n"
|
||||
" {\n"
|
||||
|
@ -756,9 +757,11 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
|
|||
result.pushKV("capabilities", aCaps);
|
||||
result.pushKV("version", pblock->nVersion);
|
||||
result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex());
|
||||
result.pushKV("lightclientroothash", pblock->hashLightClientRoot.GetHex());
|
||||
result.pushKV("blockcommitmentshash", pblock->hashBlockCommitments.GetHex());
|
||||
// Deprecated; remove in a future release.
|
||||
result.pushKV("finalsaplingroothash", pblock->hashLightClientRoot.GetHex());
|
||||
result.pushKV("lightclientroothash", pblock->hashBlockCommitments.GetHex());
|
||||
// Deprecated; remove in a future release.
|
||||
result.pushKV("finalsaplingroothash", pblock->hashBlockCommitments.GetHex());
|
||||
result.pushKV("transactions", transactions);
|
||||
if (coinbasetxn) {
|
||||
assert(txCoinbase.isObject());
|
||||
|
|
|
@ -272,8 +272,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
}
|
||||
*/
|
||||
|
||||
// These tests assume null hashLightClientRoot (before Sapling)
|
||||
pblock->hashLightClientRoot = uint256();
|
||||
// These tests assume null hashBlockCommitments (before Sapling)
|
||||
pblock->hashBlockCommitments = uint256();
|
||||
|
||||
CValidationState state;
|
||||
BOOST_CHECK(ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL));
|
||||
|
|
|
@ -162,7 +162,7 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>&
|
|||
block.vtx.push_back(tx);
|
||||
// IncrementExtraNonce creates a valid coinbase and merkleRoot
|
||||
unsigned int extraNonce = 0;
|
||||
IncrementExtraNonce(&block, chainActive.Tip(), extraNonce);
|
||||
IncrementExtraNonce(pblocktemplate, chainActive.Tip(), extraNonce, chainparams.GetConsensus());
|
||||
|
||||
// Hash state
|
||||
eh_HashState eh_state;
|
||||
|
|
23
src/txdb.cpp
23
src/txdb.cpp
|
@ -549,7 +549,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts(
|
|||
pindexNew->hashSproutAnchor = diskindex.hashSproutAnchor;
|
||||
pindexNew->nVersion = diskindex.nVersion;
|
||||
pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
|
||||
pindexNew->hashLightClientRoot = diskindex.hashLightClientRoot;
|
||||
pindexNew->hashBlockCommitments = diskindex.hashBlockCommitments;
|
||||
pindexNew->nTime = diskindex.nTime;
|
||||
pindexNew->nBits = diskindex.nBits;
|
||||
pindexNew->nNonce = diskindex.nNonce;
|
||||
|
@ -560,7 +560,9 @@ bool CBlockTreeDB::LoadBlockIndexGuts(
|
|||
pindexNew->nSproutValue = diskindex.nSproutValue;
|
||||
pindexNew->nSaplingValue = diskindex.nSaplingValue;
|
||||
pindexNew->hashFinalSaplingRoot = diskindex.hashFinalSaplingRoot;
|
||||
pindexNew->hashFinalOrchardRoot = diskindex.hashFinalOrchardRoot;
|
||||
pindexNew->hashChainHistoryRoot = diskindex.hashChainHistoryRoot;
|
||||
pindexNew->hashAuthDataRoot = diskindex.hashAuthDataRoot;
|
||||
|
||||
// Consistency checks
|
||||
auto header = pindexNew->GetBlockHeader();
|
||||
|
@ -588,18 +590,23 @@ bool CBlockTreeDB::LoadBlockIndexGuts(
|
|||
// a non-upgraded peer. However that case the entry will be
|
||||
// marked as consensus-invalid.
|
||||
//
|
||||
if (diskindex.nClientVersion >= CHAIN_HISTORY_ROOT_VERSION &&
|
||||
if (diskindex.nClientVersion >= NU5_DATA_VERSION &&
|
||||
chainParams.GetConsensus().NetworkUpgradeActive(pindexNew->nHeight, Consensus::UPGRADE_NU5)) {
|
||||
// From NU5 onwards we don't enforce a consistency check, because
|
||||
// after ZIP 244, hashBlockCommitments will not match any stored
|
||||
// commitment.
|
||||
} else if (diskindex.nClientVersion >= CHAIN_HISTORY_ROOT_VERSION &&
|
||||
chainParams.GetConsensus().NetworkUpgradeActive(pindexNew->nHeight, Consensus::UPGRADE_HEARTWOOD)) {
|
||||
if (pindexNew->hashLightClientRoot != pindexNew->hashChainHistoryRoot) {
|
||||
if (pindexNew->hashBlockCommitments != pindexNew->hashChainHistoryRoot) {
|
||||
return error(
|
||||
"LoadBlockIndex(): block index inconsistency detected (post-Heartwood; hashLightClientRoot %s != hashChainHistoryRoot %s): %s",
|
||||
pindexNew->hashLightClientRoot.ToString(), pindexNew->hashChainHistoryRoot.ToString(), pindexNew->ToString());
|
||||
"LoadBlockIndex(): block index inconsistency detected (post-Heartwood; hashBlockCommitments %s != hashChainHistoryRoot %s): %s",
|
||||
pindexNew->hashBlockCommitments.ToString(), pindexNew->hashChainHistoryRoot.ToString(), pindexNew->ToString());
|
||||
}
|
||||
} else {
|
||||
if (pindexNew->hashLightClientRoot != pindexNew->hashFinalSaplingRoot) {
|
||||
if (pindexNew->hashBlockCommitments != pindexNew->hashFinalSaplingRoot) {
|
||||
return error(
|
||||
"LoadBlockIndex(): block index inconsistency detected (pre-Heartwood; hashLightClientRoot %s != hashFinalSaplingRoot %s): %s",
|
||||
pindexNew->hashLightClientRoot.ToString(), pindexNew->hashFinalSaplingRoot.ToString(), pindexNew->ToString());
|
||||
"LoadBlockIndex(): block index inconsistency detected (pre-Heartwood; hashBlockCommitments %s != hashFinalSaplingRoot %s): %s",
|
||||
pindexNew->hashBlockCommitments.ToString(), pindexNew->hashFinalSaplingRoot.ToString(), pindexNew->ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue