Use V2 history trees from NU5 onward
This commit is contained in:
parent
66e2baa6da
commit
16317bc6af
|
@ -64,7 +64,7 @@ public:
|
|||
};
|
||||
|
||||
HistoryNode getLeafN(uint64_t block_num) {
|
||||
HistoryNode node = libzcash::NewLeaf(
|
||||
HistoryNode node = libzcash::NewV1Leaf(
|
||||
uint256(),
|
||||
block_num*10,
|
||||
block_num*13,
|
||||
|
|
37
src/main.cpp
37
src/main.cpp
|
@ -2864,6 +2864,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||
auto prevConsensusBranchId = CurrentEpochBranchId(pindex->nHeight - 1, chainparams.GetConsensus());
|
||||
|
||||
size_t total_sapling_tx = 0;
|
||||
size_t total_orchard_tx = 0;
|
||||
|
||||
std::vector<PrecomputedTransactionData> txdata;
|
||||
txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated
|
||||
|
@ -2994,6 +2995,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||
if (!(tx.vShieldedSpend.empty() && tx.vShieldedOutput.empty())) {
|
||||
total_sapling_tx += 1;
|
||||
}
|
||||
if (tx.GetOrchardBundle().IsPresent()) {
|
||||
total_orchard_tx += 1;
|
||||
}
|
||||
|
||||
vPos.push_back(std::make_pair(tx.GetHash(), pos));
|
||||
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
|
||||
|
@ -3090,15 +3094,30 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||
|
||||
// History read/write is started with Heartwood update.
|
||||
if (chainparams.GetConsensus().NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_HEARTWOOD)) {
|
||||
auto historyNode = libzcash::NewLeaf(
|
||||
block.GetHash(),
|
||||
block.nTime,
|
||||
block.nBits,
|
||||
pindex->hashFinalSaplingRoot,
|
||||
ArithToUint256(GetBlockProof(*pindex)),
|
||||
pindex->nHeight,
|
||||
total_sapling_tx
|
||||
);
|
||||
HistoryNode historyNode;
|
||||
if (chainparams.GetConsensus().NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_NU5)) {
|
||||
historyNode = libzcash::NewV2Leaf(
|
||||
block.GetHash(),
|
||||
block.nTime,
|
||||
block.nBits,
|
||||
pindex->hashFinalSaplingRoot,
|
||||
pindex->hashFinalOrchardRoot,
|
||||
ArithToUint256(GetBlockProof(*pindex)),
|
||||
pindex->nHeight,
|
||||
total_sapling_tx,
|
||||
total_orchard_tx
|
||||
);
|
||||
} else {
|
||||
historyNode = libzcash::NewV1Leaf(
|
||||
block.GetHash(),
|
||||
block.nTime,
|
||||
block.nBits,
|
||||
pindex->hashFinalSaplingRoot,
|
||||
ArithToUint256(GetBlockProof(*pindex)),
|
||||
pindex->nHeight,
|
||||
total_sapling_tx
|
||||
);
|
||||
}
|
||||
|
||||
view.PushHistoryNode(consensusBranchId, historyNode);
|
||||
}
|
||||
|
|
|
@ -56,6 +56,10 @@ public:
|
|||
inner.reset(bundle);
|
||||
}
|
||||
|
||||
/// Returns true if this contains an Orchard bundle, or false if there is no
|
||||
/// Orchard component.
|
||||
bool IsPresent() const { return (bool)inner; }
|
||||
|
||||
/// Queues this bundle's signatures for validation.
|
||||
///
|
||||
/// `txid` must be for the transaction this bundle is within.
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define NODE_SERIALIZED_LENGTH 171
|
||||
#define NODE_V1_SERIALIZED_LENGTH 171
|
||||
#define NODE_SERIALIZED_LENGTH 244
|
||||
#define ENTRY_SERIALIZED_LENGTH (NODE_SERIALIZED_LENGTH + 9)
|
||||
|
||||
typedef struct HistoryNode {
|
||||
|
|
|
@ -1,9 +1,22 @@
|
|||
use std::slice;
|
||||
use std::{convert::TryFrom, slice};
|
||||
|
||||
use libc::{c_uchar, size_t};
|
||||
use zcash_history::{Entry as MMREntry, NodeData as MMRNodeData, Tree as MMRTree, V1};
|
||||
use zcash_history::{Entry as MMREntry, Tree as MMRTree, Version, V1, V2};
|
||||
use zcash_primitives::consensus::BranchId;
|
||||
|
||||
fn construct_mmr_tree(
|
||||
/// Switch the tree version on the epoch it is for.
|
||||
fn dispatch<T>(cbranch: u32, v1: impl FnOnce() -> T, v2: impl FnOnce() -> T) -> T {
|
||||
match BranchId::try_from(cbranch).unwrap() {
|
||||
BranchId::Sprout
|
||||
| BranchId::Overwinter
|
||||
| BranchId::Sapling
|
||||
| BranchId::Heartwood
|
||||
| BranchId::Canopy => v1(),
|
||||
_ => v2(),
|
||||
}
|
||||
}
|
||||
|
||||
fn construct_mmr_tree<V: Version>(
|
||||
// Consensus branch id
|
||||
cbranch: u32,
|
||||
// Length of tree in array representation
|
||||
|
@ -18,7 +31,7 @@ fn construct_mmr_tree(
|
|||
p_len: size_t,
|
||||
// Extra nodes loaded (for deletion) count
|
||||
e_len: size_t,
|
||||
) -> Result<MMRTree<V1>, &'static str> {
|
||||
) -> Result<MMRTree<V>, &'static str> {
|
||||
let (indices, nodes) = unsafe {
|
||||
(
|
||||
slice::from_raw_parts(ni_ptr, p_len + e_len),
|
||||
|
@ -59,6 +72,39 @@ pub extern "system" fn librustzcash_mmr_append(
|
|||
rt_ret: *mut [u8; 32],
|
||||
// Return buffer for appended leaves, should be pre-allocated of ceiling(log2(t_len)) length
|
||||
buf_ret: *mut [c_uchar; zcash_history::MAX_NODE_DATA_SIZE],
|
||||
) -> u32 {
|
||||
dispatch(
|
||||
cbranch,
|
||||
|| {
|
||||
librustzcash_mmr_append_inner::<V1>(
|
||||
cbranch, t_len, ni_ptr, n_ptr, p_len, nn_ptr, rt_ret, buf_ret,
|
||||
)
|
||||
},
|
||||
|| {
|
||||
librustzcash_mmr_append_inner::<V2>(
|
||||
cbranch, t_len, ni_ptr, n_ptr, p_len, nn_ptr, rt_ret, buf_ret,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn librustzcash_mmr_append_inner<V: Version>(
|
||||
// Consensus branch id
|
||||
cbranch: u32,
|
||||
// Length of tree in array representation
|
||||
t_len: u32,
|
||||
// Indices of provided tree nodes, length of p_len
|
||||
ni_ptr: *const u32,
|
||||
// Provided tree nodes data, length of p_len
|
||||
n_ptr: *const [c_uchar; zcash_history::MAX_ENTRY_SIZE],
|
||||
// Peaks count
|
||||
p_len: size_t,
|
||||
// New node pointer
|
||||
nn_ptr: *const [u8; zcash_history::MAX_NODE_DATA_SIZE],
|
||||
// Return of root commitment
|
||||
rt_ret: *mut [u8; 32],
|
||||
// Return buffer for appended leaves, should be pre-allocated of ceiling(log2(t_len)) length
|
||||
buf_ret: *mut [c_uchar; zcash_history::MAX_NODE_DATA_SIZE],
|
||||
) -> u32 {
|
||||
let new_node_bytes: &[u8; zcash_history::MAX_NODE_DATA_SIZE] = unsafe {
|
||||
match nn_ptr.as_ref() {
|
||||
|
@ -69,14 +115,14 @@ pub extern "system" fn librustzcash_mmr_append(
|
|||
}
|
||||
};
|
||||
|
||||
let mut tree = match construct_mmr_tree(cbranch, t_len, ni_ptr, n_ptr, p_len, 0) {
|
||||
let mut tree = match construct_mmr_tree::<V>(cbranch, t_len, ni_ptr, n_ptr, p_len, 0) {
|
||||
Ok(t) => t,
|
||||
_ => {
|
||||
return 0;
|
||||
} // error
|
||||
};
|
||||
|
||||
let node = match MMRNodeData::from_bytes(cbranch, &new_node_bytes[..]) {
|
||||
let node = match V::from_bytes(cbranch, &new_node_bytes[..]) {
|
||||
Ok(node) => node,
|
||||
_ => {
|
||||
return 0;
|
||||
|
@ -96,17 +142,19 @@ pub extern "system" fn librustzcash_mmr_append(
|
|||
.root_node()
|
||||
.expect("Just added, should resolve always; qed");
|
||||
unsafe {
|
||||
*rt_ret = root_node.data().hash();
|
||||
*rt_ret = V::hash(root_node.data());
|
||||
|
||||
for (idx, next_buf) in slice::from_raw_parts_mut(buf_ret, return_count as usize)
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
{
|
||||
tree.resolve_link(appended[idx])
|
||||
.expect("This was generated by the tree and thus resolvable; qed")
|
||||
.data()
|
||||
.write(&mut &mut next_buf[..])
|
||||
.expect("Write using cursor with enough buffer size cannot fail; qed");
|
||||
V::write(
|
||||
tree.resolve_link(appended[idx])
|
||||
.expect("This was generated by the tree and thus resolvable; qed")
|
||||
.data(),
|
||||
&mut &mut next_buf[..],
|
||||
)
|
||||
.expect("Write using cursor with enough buffer size cannot fail; qed");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,7 +178,30 @@ pub extern "system" fn librustzcash_mmr_delete(
|
|||
// Return of root commitment
|
||||
rt_ret: *mut [u8; 32],
|
||||
) -> u32 {
|
||||
let mut tree = match construct_mmr_tree(cbranch, t_len, ni_ptr, n_ptr, p_len, e_len) {
|
||||
dispatch(
|
||||
cbranch,
|
||||
|| librustzcash_mmr_delete_inner::<V1>(cbranch, t_len, ni_ptr, n_ptr, p_len, e_len, rt_ret),
|
||||
|| librustzcash_mmr_delete_inner::<V2>(cbranch, t_len, ni_ptr, n_ptr, p_len, e_len, rt_ret),
|
||||
)
|
||||
}
|
||||
|
||||
fn librustzcash_mmr_delete_inner<V: Version>(
|
||||
// Consensus branch id
|
||||
cbranch: u32,
|
||||
// Length of tree in array representation
|
||||
t_len: u32,
|
||||
// Indices of provided tree nodes, length of p_len+e_len
|
||||
ni_ptr: *const u32,
|
||||
// Provided tree nodes data, length of p_len+e_len
|
||||
n_ptr: *const [c_uchar; zcash_history::MAX_ENTRY_SIZE],
|
||||
// Peaks count
|
||||
p_len: size_t,
|
||||
// Extra nodes loaded (for deletion) count
|
||||
e_len: size_t,
|
||||
// Return of root commitment
|
||||
rt_ret: *mut [u8; 32],
|
||||
) -> u32 {
|
||||
let mut tree = match construct_mmr_tree::<V>(cbranch, t_len, ni_ptr, n_ptr, p_len, e_len) {
|
||||
Ok(t) => t,
|
||||
_ => {
|
||||
return 0;
|
||||
|
@ -145,11 +216,11 @@ pub extern "system" fn librustzcash_mmr_delete(
|
|||
};
|
||||
|
||||
unsafe {
|
||||
*rt_ret = tree
|
||||
.root_node()
|
||||
.expect("Just generated without errors, root should be resolving")
|
||||
.data()
|
||||
.hash();
|
||||
*rt_ret = V::hash(
|
||||
tree.root_node()
|
||||
.expect("Just generated without errors, root should be resolving")
|
||||
.data(),
|
||||
);
|
||||
}
|
||||
|
||||
truncate_len
|
||||
|
@ -160,6 +231,18 @@ pub extern "system" fn librustzcash_mmr_hash_node(
|
|||
cbranch: u32,
|
||||
n_ptr: *const [u8; zcash_history::MAX_NODE_DATA_SIZE],
|
||||
h_ret: *mut [u8; 32],
|
||||
) -> u32 {
|
||||
dispatch(
|
||||
cbranch,
|
||||
|| librustzcash_mmr_hash_node_inner::<V1>(cbranch, n_ptr, h_ret),
|
||||
|| librustzcash_mmr_hash_node_inner::<V2>(cbranch, n_ptr, h_ret),
|
||||
)
|
||||
}
|
||||
|
||||
fn librustzcash_mmr_hash_node_inner<V: Version>(
|
||||
cbranch: u32,
|
||||
n_ptr: *const [u8; zcash_history::MAX_NODE_DATA_SIZE],
|
||||
h_ret: *mut [u8; 32],
|
||||
) -> u32 {
|
||||
let node_bytes: &[u8; zcash_history::MAX_NODE_DATA_SIZE] = unsafe {
|
||||
match n_ptr.as_ref() {
|
||||
|
@ -168,13 +251,13 @@ pub extern "system" fn librustzcash_mmr_hash_node(
|
|||
}
|
||||
};
|
||||
|
||||
let node = match MMRNodeData::from_bytes(cbranch, &node_bytes[..]) {
|
||||
let node = match V::from_bytes(cbranch, &node_bytes[..]) {
|
||||
Ok(n) => n,
|
||||
_ => return 1, // error
|
||||
};
|
||||
|
||||
unsafe {
|
||||
*h_ret = node.hash();
|
||||
*h_ret = V::hash(&node);
|
||||
}
|
||||
|
||||
0
|
||||
|
|
29
src/txdb.cpp
29
src/txdb.cpp
|
@ -10,6 +10,7 @@
|
|||
#include "main.h"
|
||||
#include "pow.h"
|
||||
#include "uint256.h"
|
||||
#include "zcash/History.hpp"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -145,15 +146,31 @@ HistoryNode CCoinsViewDB::GetHistoryAt(uint32_t epochId, HistoryIndex index) con
|
|||
throw runtime_error("History data inconsistent - reindex?");
|
||||
}
|
||||
|
||||
// Read mmrNode into tmp std::array
|
||||
std::array<unsigned char, NODE_SERIALIZED_LENGTH> tmpMmrNode;
|
||||
if (libzcash::IsV1HistoryTree(epochId)) {
|
||||
// History nodes serialized by `zcashd` versions that were unaware of NU5, used
|
||||
// the previous shorter maximum serialized length. Because we stored this as an
|
||||
// array, we can't just read the current (longer) maximum serialized length, as
|
||||
// it will result in an exception for those older nodes.
|
||||
//
|
||||
// Instead, we always read an array of the older length. This works as expected
|
||||
// for V1 nodes serialized by older clients, while for V1 nodes serialized by
|
||||
// NU5-aware clients this is guaranteed to ignore only trailing zero bytes.
|
||||
std::array<unsigned char, NODE_V1_SERIALIZED_LENGTH> tmpMmrNode;
|
||||
if (!db.Read(make_pair(DB_MMR_NODE, make_pair(epochId, index)), tmpMmrNode)) {
|
||||
throw runtime_error("History data inconsistent (expected node not found) - reindex?");
|
||||
}
|
||||
std::copy(std::begin(tmpMmrNode), std::end(tmpMmrNode), mmrNode.bytes);
|
||||
} else {
|
||||
// Read mmrNode into tmp std::array
|
||||
std::array<unsigned char, NODE_SERIALIZED_LENGTH> tmpMmrNode;
|
||||
|
||||
if (!db.Read(make_pair(DB_MMR_NODE, make_pair(epochId, index)), tmpMmrNode)) {
|
||||
throw runtime_error("History data inconsistent (expected node not found) - reindex?");
|
||||
if (!db.Read(make_pair(DB_MMR_NODE, make_pair(epochId, index)), tmpMmrNode)) {
|
||||
throw runtime_error("History data inconsistent (expected node not found) - reindex?");
|
||||
}
|
||||
|
||||
std::copy(std::begin(tmpMmrNode), std::end(tmpMmrNode), mmrNode.bytes);
|
||||
}
|
||||
|
||||
std::copy(std::begin(tmpMmrNode), std::end(tmpMmrNode), mmrNode.bytes);
|
||||
|
||||
return mmrNode;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <stdexcept>
|
||||
|
||||
|
||||
#include "consensus/upgrades.h"
|
||||
#include "serialize.h"
|
||||
#include "streams.h"
|
||||
#include "uint256.h"
|
||||
|
@ -46,10 +47,13 @@ HistoryNode NewNode(
|
|||
uint32_t endTarget,
|
||||
uint256 startSaplingRoot,
|
||||
uint256 endSaplingRoot,
|
||||
std::optional<uint256> startOrchardRoot,
|
||||
std::optional<uint256> endOrchardRoot,
|
||||
uint256 subtreeTotalWork,
|
||||
uint64_t startHeight,
|
||||
uint64_t endHeight,
|
||||
uint64_t saplingTxCount
|
||||
uint64_t saplingTxCount,
|
||||
std::optional<uint64_t> orchardTxCount
|
||||
)
|
||||
{
|
||||
CDataStream buf(SER_DISK, 0);
|
||||
|
@ -66,13 +70,19 @@ HistoryNode NewNode(
|
|||
buf << COMPACTSIZE(startHeight);
|
||||
buf << COMPACTSIZE(endHeight);
|
||||
buf << COMPACTSIZE(saplingTxCount);
|
||||
if (startOrchardRoot) {
|
||||
// If startOrchardRoot is provided, assume all V2 fields are.
|
||||
buf << startOrchardRoot.value();
|
||||
buf << endOrchardRoot.value();
|
||||
buf << COMPACTSIZE(orchardTxCount.value());
|
||||
}
|
||||
|
||||
assert(buf.size() <= NODE_SERIALIZED_LENGTH);
|
||||
std::copy(std::begin(buf), std::end(buf), result.bytes);
|
||||
return result;
|
||||
}
|
||||
|
||||
HistoryNode NewLeaf(
|
||||
HistoryNode NewV1Leaf(
|
||||
uint256 commitment,
|
||||
uint32_t time,
|
||||
uint32_t target,
|
||||
|
@ -89,10 +99,42 @@ HistoryNode NewLeaf(
|
|||
target,
|
||||
saplingRoot,
|
||||
saplingRoot,
|
||||
std::nullopt,
|
||||
std::nullopt,
|
||||
totalWork,
|
||||
height,
|
||||
height,
|
||||
saplingTxCount
|
||||
saplingTxCount,
|
||||
std::nullopt
|
||||
);
|
||||
}
|
||||
|
||||
HistoryNode NewV2Leaf(
|
||||
uint256 commitment,
|
||||
uint32_t time,
|
||||
uint32_t target,
|
||||
uint256 saplingRoot,
|
||||
uint256 orchardRoot,
|
||||
uint256 totalWork,
|
||||
uint64_t height,
|
||||
uint64_t saplingTxCount,
|
||||
uint64_t orchardTxCount
|
||||
) {
|
||||
return NewNode(
|
||||
commitment,
|
||||
time,
|
||||
time,
|
||||
target,
|
||||
target,
|
||||
saplingRoot,
|
||||
saplingRoot,
|
||||
orchardRoot,
|
||||
orchardRoot,
|
||||
totalWork,
|
||||
height,
|
||||
height,
|
||||
saplingTxCount,
|
||||
orchardTxCount
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -134,4 +176,15 @@ HistoryEntry LeafToEntry(const HistoryNode node) {
|
|||
return result;
|
||||
}
|
||||
|
||||
bool IsV1HistoryTree(uint32_t epochId) {
|
||||
return (
|
||||
epochId == NetworkUpgradeInfo[Consensus::BASE_SPROUT].nBranchId ||
|
||||
epochId == NetworkUpgradeInfo[Consensus::UPGRADE_OVERWINTER].nBranchId ||
|
||||
epochId == NetworkUpgradeInfo[Consensus::UPGRADE_SAPLING].nBranchId ||
|
||||
epochId == NetworkUpgradeInfo[Consensus::UPGRADE_BLOSSOM].nBranchId ||
|
||||
epochId == NetworkUpgradeInfo[Consensus::UPGRADE_HEARTWOOD].nBranchId ||
|
||||
epochId == NetworkUpgradeInfo[Consensus::UPGRADE_CANOPY].nBranchId
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -43,8 +43,8 @@ public:
|
|||
void Truncate(HistoryIndex newLength);
|
||||
};
|
||||
|
||||
// New history node with metadata based on block state.
|
||||
HistoryNode NewLeaf(
|
||||
// New V1 history node with metadata based on block state.
|
||||
HistoryNode NewV1Leaf(
|
||||
uint256 commitment,
|
||||
uint32_t time,
|
||||
uint32_t target,
|
||||
|
@ -54,12 +54,28 @@ HistoryNode NewLeaf(
|
|||
uint64_t saplingTxCount
|
||||
);
|
||||
|
||||
// New V2 history node with metadata based on block state.
|
||||
HistoryNode NewV2Leaf(
|
||||
uint256 commitment,
|
||||
uint32_t time,
|
||||
uint32_t target,
|
||||
uint256 saplingRoot,
|
||||
uint256 orchardRoot,
|
||||
uint256 totalWork,
|
||||
uint64_t height,
|
||||
uint64_t saplingTxCount,
|
||||
uint64_t orchardTxCount
|
||||
);
|
||||
|
||||
// Convert history node to tree node (with children references)
|
||||
HistoryEntry NodeToEntry(const HistoryNode node, uint32_t left, uint32_t right);
|
||||
|
||||
// Convert history node to leaf node (end nodes without children)
|
||||
HistoryEntry LeafToEntry(const HistoryNode node);
|
||||
|
||||
// Returns true if this epoch used the V1 history tree format.
|
||||
bool IsV1HistoryTree(uint32_t epochId);
|
||||
|
||||
}
|
||||
|
||||
typedef libzcash::HistoryCache HistoryCache;
|
||||
|
|
Loading…
Reference in New Issue