Auto merge of #5227 - str4d:v2-history-tree, r=str4d
Integrate V2 history tree Closes #5037.
This commit is contained in:
commit
9253e2828e
|
@ -473,7 +473,7 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equihash"
|
name = "equihash"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/zcash/librustzcash.git?rev=d88e40113c8dadea751dfcdd72ee90868f9655ff#d88e40113c8dadea751dfcdd72ee90868f9655ff"
|
source = "git+https://github.com/zcash/librustzcash.git?rev=fe4b63c8fe7300ebc9cd7a1242867525ccfe1e13#fe4b63c8fe7300ebc9cd7a1242867525ccfe1e13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2b_simd",
|
"blake2b_simd",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
@ -1677,8 +1677,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_history"
|
name = "zcash_history"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/zcash/librustzcash.git?rev=fe4b63c8fe7300ebc9cd7a1242867525ccfe1e13#fe4b63c8fe7300ebc9cd7a1242867525ccfe1e13"
|
||||||
checksum = "abfbab9accba014bbf3098d5aa66c1714d0db4abe25b999b8400bbd626ccd2f4"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bigint",
|
"bigint",
|
||||||
"blake2b_simd",
|
"blake2b_simd",
|
||||||
|
@ -1688,7 +1687,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_note_encryption"
|
name = "zcash_note_encryption"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://github.com/zcash/librustzcash.git?rev=d88e40113c8dadea751dfcdd72ee90868f9655ff#d88e40113c8dadea751dfcdd72ee90868f9655ff"
|
source = "git+https://github.com/zcash/librustzcash.git?rev=fe4b63c8fe7300ebc9cd7a1242867525ccfe1e13#fe4b63c8fe7300ebc9cd7a1242867525ccfe1e13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2b_simd",
|
"blake2b_simd",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
@ -1702,7 +1701,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_primitives"
|
name = "zcash_primitives"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
source = "git+https://github.com/zcash/librustzcash.git?rev=d88e40113c8dadea751dfcdd72ee90868f9655ff#d88e40113c8dadea751dfcdd72ee90868f9655ff"
|
source = "git+https://github.com/zcash/librustzcash.git?rev=fe4b63c8fe7300ebc9cd7a1242867525ccfe1e13#fe4b63c8fe7300ebc9cd7a1242867525ccfe1e13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"bitvec",
|
"bitvec",
|
||||||
|
@ -1732,7 +1731,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_proofs"
|
name = "zcash_proofs"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
source = "git+https://github.com/zcash/librustzcash.git?rev=d88e40113c8dadea751dfcdd72ee90868f9655ff#d88e40113c8dadea751dfcdd72ee90868f9655ff"
|
source = "git+https://github.com/zcash/librustzcash.git?rev=fe4b63c8fe7300ebc9cd7a1242867525ccfe1e13#fe4b63c8fe7300ebc9cd7a1242867525ccfe1e13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bellman",
|
"bellman",
|
||||||
"blake2b_simd",
|
"blake2b_simd",
|
||||||
|
|
|
@ -62,6 +62,7 @@ codegen-units = 1
|
||||||
ed25519-zebra = { git = "https://github.com/ZcashFoundation/ed25519-zebra.git", rev = "d3512400227a362d08367088ffaa9bd4142a69c7" }
|
ed25519-zebra = { git = "https://github.com/ZcashFoundation/ed25519-zebra.git", rev = "d3512400227a362d08367088ffaa9bd4142a69c7" }
|
||||||
halo2 = { git = "https://github.com/zcash/halo2.git", rev = "d04b532368d05b505e622f8cac4c0693574fbd93" }
|
halo2 = { git = "https://github.com/zcash/halo2.git", rev = "d04b532368d05b505e622f8cac4c0693574fbd93" }
|
||||||
orchard = { git = "https://github.com/zcash/orchard.git", rev = "f7c64e0437040d831e61711cd9e5695b001cb5cb" }
|
orchard = { git = "https://github.com/zcash/orchard.git", rev = "f7c64e0437040d831e61711cd9e5695b001cb5cb" }
|
||||||
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "d88e40113c8dadea751dfcdd72ee90868f9655ff" }
|
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "fe4b63c8fe7300ebc9cd7a1242867525ccfe1e13" }
|
||||||
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "d88e40113c8dadea751dfcdd72ee90868f9655ff" }
|
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "fe4b63c8fe7300ebc9cd7a1242867525ccfe1e13" }
|
||||||
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "d88e40113c8dadea751dfcdd72ee90868f9655ff" }
|
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "fe4b63c8fe7300ebc9cd7a1242867525ccfe1e13" }
|
||||||
|
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "fe4b63c8fe7300ebc9cd7a1242867525ccfe1e13" }
|
||||||
|
|
|
@ -8,10 +8,14 @@ from test_framework.flyclient import (ZcashMMRNode, append, delete, make_root_co
|
||||||
from test_framework.mininode import (CBlockHeader)
|
from test_framework.mininode import (CBlockHeader)
|
||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
from test_framework.util import (
|
from test_framework.util import (
|
||||||
|
BLOSSOM_BRANCH_ID,
|
||||||
HEARTWOOD_BRANCH_ID,
|
HEARTWOOD_BRANCH_ID,
|
||||||
|
CANOPY_BRANCH_ID,
|
||||||
|
NU5_BRANCH_ID,
|
||||||
assert_equal,
|
assert_equal,
|
||||||
bytes_to_hex_str,
|
bytes_to_hex_str,
|
||||||
hex_str_to_bytes,
|
hex_str_to_bytes,
|
||||||
|
nuparams,
|
||||||
start_nodes,
|
start_nodes,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,19 +34,51 @@ class Zip221Test(BitcoinTestFramework):
|
||||||
|
|
||||||
def setup_nodes(self):
|
def setup_nodes(self):
|
||||||
return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[
|
return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[
|
||||||
'-nuparams=2bb40e60:1', # Blossom
|
nuparams(BLOSSOM_BRANCH_ID, 1),
|
||||||
'-nuparams=f5b9230b:10', # Heartwood
|
nuparams(HEARTWOOD_BRANCH_ID, 10),
|
||||||
|
nuparams(CANOPY_BRANCH_ID, 32),
|
||||||
|
nuparams(NU5_BRANCH_ID, 35),
|
||||||
'-nurejectoldversions=false',
|
'-nurejectoldversions=false',
|
||||||
]] * self.num_nodes)
|
]] * self.num_nodes)
|
||||||
|
|
||||||
def node_for_block(self, height):
|
def node_for_block(self, height):
|
||||||
|
if height >= 35:
|
||||||
|
epoch = NU5_BRANCH_ID
|
||||||
|
elif height >= 32:
|
||||||
|
epoch = CANOPY_BRANCH_ID
|
||||||
|
else:
|
||||||
|
epoch = HEARTWOOD_BRANCH_ID
|
||||||
|
|
||||||
block_header = CBlockHeader()
|
block_header = CBlockHeader()
|
||||||
block_header.deserialize(BytesIO(hex_str_to_bytes(
|
block_header.deserialize(BytesIO(hex_str_to_bytes(
|
||||||
self.nodes[0].getblock(str(height), 0))))
|
self.nodes[0].getblock(str(height), 0))))
|
||||||
sapling_root = hex_str_to_bytes(
|
sapling_root = hex_str_to_bytes(
|
||||||
self.nodes[0].getblock(str(height))["finalsaplingroot"])[::-1]
|
self.nodes[0].getblock(str(height))["finalsaplingroot"])[::-1]
|
||||||
|
|
||||||
|
if height >= 35:
|
||||||
|
orchard_root = hex_str_to_bytes(
|
||||||
|
self.nodes[0].getblock(str(height))["finalorchardroot"])[::-1]
|
||||||
|
v2_data = (orchard_root, 0)
|
||||||
|
else:
|
||||||
|
v2_data = None
|
||||||
|
|
||||||
return ZcashMMRNode.from_block(
|
return ZcashMMRNode.from_block(
|
||||||
block_header, height, sapling_root, 0, HEARTWOOD_BRANCH_ID)
|
block_header, height, sapling_root, 0, epoch, v2_data)
|
||||||
|
|
||||||
|
def new_tree(self, height):
|
||||||
|
newRoot = self.node_for_block(height - 1)
|
||||||
|
assert_equal(
|
||||||
|
self.nodes[0].getblock(str(height))["chainhistoryroot"],
|
||||||
|
bytes_to_hex_str(make_root_commitment(newRoot)[::-1]))
|
||||||
|
return newRoot
|
||||||
|
|
||||||
|
def update_tree(self, root, height):
|
||||||
|
leaf = self.node_for_block(height - 1)
|
||||||
|
root = append(root, leaf)
|
||||||
|
assert_equal(
|
||||||
|
self.nodes[0].getblock(str(height))["chainhistoryroot"],
|
||||||
|
bytes_to_hex_str(make_root_commitment(root)[::-1]))
|
||||||
|
return root
|
||||||
|
|
||||||
def run_test(self):
|
def run_test(self):
|
||||||
self.nodes[0].generate(10)
|
self.nodes[0].generate(10)
|
||||||
|
@ -130,6 +166,25 @@ class Zip221Test(BitcoinTestFramework):
|
||||||
self.nodes[2].getblock(str(height))["chainhistoryroot"],
|
self.nodes[2].getblock(str(height))["chainhistoryroot"],
|
||||||
bytes_to_hex_str(make_root_commitment(root)[::-1]))
|
bytes_to_hex_str(make_root_commitment(root)[::-1]))
|
||||||
|
|
||||||
|
# Generate 6 blocks on node 0 to activate Canopy and NU5.
|
||||||
|
print("Mining 6 blocks on node 0")
|
||||||
|
self.nodes[0].generate(6)
|
||||||
|
self.sync_all()
|
||||||
|
|
||||||
|
# The Canopy activation block should commit to the final Heartwood tree.
|
||||||
|
root = self.update_tree(root, 32)
|
||||||
|
|
||||||
|
# The two blocks after Canopy activation should be a new tree.
|
||||||
|
canopyRoot = self.new_tree(33)
|
||||||
|
canopyRoot = self.update_tree(canopyRoot, 34)
|
||||||
|
|
||||||
|
# The NU5 activation block should commit to the final Heartwood tree.
|
||||||
|
canopyRoot = self.update_tree(canopyRoot, 35)
|
||||||
|
|
||||||
|
# The two blocks after NU5 activation should be a V2 tree.
|
||||||
|
nu5Root = self.new_tree(36)
|
||||||
|
nu5Root = self.update_tree(nu5Root, 37)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
Zip221Test().main()
|
Zip221Test().main()
|
||||||
|
|
|
@ -3,6 +3,10 @@ import struct
|
||||||
from typing import (List, Optional)
|
from typing import (List, Optional)
|
||||||
|
|
||||||
from .mininode import (CBlockHeader, block_work_from_compact, ser_compactsize, ser_uint256)
|
from .mininode import (CBlockHeader, block_work_from_compact, ser_compactsize, ser_uint256)
|
||||||
|
from .util import (
|
||||||
|
NU5_BRANCH_ID,
|
||||||
|
assert_equal,
|
||||||
|
)
|
||||||
|
|
||||||
def H(msg: bytes, consensusBranchId: int) -> bytes:
|
def H(msg: bytes, consensusBranchId: int) -> bytes:
|
||||||
digest = blake2b(
|
digest = blake2b(
|
||||||
|
@ -28,12 +32,29 @@ class ZcashMMRNode():
|
||||||
nEarliestHeight: int
|
nEarliestHeight: int
|
||||||
nLatestHeight: int
|
nLatestHeight: int
|
||||||
nSaplingTxCount: int # number of Sapling transactions in block
|
nSaplingTxCount: int # number of Sapling transactions in block
|
||||||
|
# NU5 only.
|
||||||
|
hashEarliestOrchardRoot: Optional[bytes] # left child's Orchard root
|
||||||
|
hashLatestOrchardRoot: Optional[bytes] # right child's Orchard root
|
||||||
|
nOrchardTxCount: Optional[int] # number of Orchard transactions in block
|
||||||
|
|
||||||
consensusBranchId: bytes
|
consensusBranchId: bytes
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_block(Z, block: CBlockHeader, height, sapling_root, sapling_tx_count, consensusBranchId) -> 'ZcashMMRNode':
|
def from_block(
|
||||||
|
Z, block: CBlockHeader, height,
|
||||||
|
sapling_root, sapling_tx_count,
|
||||||
|
consensusBranchId,
|
||||||
|
v2_data=None
|
||||||
|
) -> 'ZcashMMRNode':
|
||||||
'''Create a leaf node from a block'''
|
'''Create a leaf node from a block'''
|
||||||
|
if v2_data is not None:
|
||||||
|
assert_equal(consensusBranchId, NU5_BRANCH_ID)
|
||||||
|
orchard_root = v2_data[0]
|
||||||
|
orchard_tx_count = v2_data[1]
|
||||||
|
else:
|
||||||
|
orchard_root = None
|
||||||
|
orchard_tx_count = None
|
||||||
|
|
||||||
node = Z()
|
node = Z()
|
||||||
node.left_child = None
|
node.left_child = None
|
||||||
node.right_child = None
|
node.right_child = None
|
||||||
|
@ -48,6 +69,9 @@ class ZcashMMRNode():
|
||||||
node.nEarliestHeight = height
|
node.nEarliestHeight = height
|
||||||
node.nLatestHeight = height
|
node.nLatestHeight = height
|
||||||
node.nSaplingTxCount = sapling_tx_count
|
node.nSaplingTxCount = sapling_tx_count
|
||||||
|
node.hashEarliestOrchardRoot = orchard_root
|
||||||
|
node.hashLatestOrchardRoot = orchard_root
|
||||||
|
node.nOrchardTxCount = orchard_tx_count
|
||||||
node.consensusBranchId = consensusBranchId
|
node.consensusBranchId = consensusBranchId
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
@ -65,6 +89,10 @@ class ZcashMMRNode():
|
||||||
buf += ser_compactsize(self.nEarliestHeight)
|
buf += ser_compactsize(self.nEarliestHeight)
|
||||||
buf += ser_compactsize(self.nLatestHeight)
|
buf += ser_compactsize(self.nLatestHeight)
|
||||||
buf += ser_compactsize(self.nSaplingTxCount)
|
buf += ser_compactsize(self.nSaplingTxCount)
|
||||||
|
if self.hashEarliestOrchardRoot is not None:
|
||||||
|
buf += self.hashEarliestOrchardRoot
|
||||||
|
buf += self.hashLatestOrchardRoot
|
||||||
|
buf += ser_compactsize(self.nOrchardTxCount)
|
||||||
return buf
|
return buf
|
||||||
|
|
||||||
def make_parent(
|
def make_parent(
|
||||||
|
@ -87,6 +115,12 @@ def make_parent(
|
||||||
parent.nEarliestHeight = left_child.nEarliestHeight
|
parent.nEarliestHeight = left_child.nEarliestHeight
|
||||||
parent.nLatestHeight = right_child.nLatestHeight
|
parent.nLatestHeight = right_child.nLatestHeight
|
||||||
parent.nSaplingTxCount = left_child.nSaplingTxCount + right_child.nSaplingTxCount
|
parent.nSaplingTxCount = left_child.nSaplingTxCount + right_child.nSaplingTxCount
|
||||||
|
parent.hashEarliestOrchardRoot = left_child.hashEarliestOrchardRoot
|
||||||
|
parent.hashLatestOrchardRoot = right_child.hashLatestOrchardRoot
|
||||||
|
parent.nOrchardTxCount = (
|
||||||
|
left_child.nOrchardTxCount + right_child.nOrchardTxCount
|
||||||
|
if left_child.nOrchardTxCount is not None and right_child.nOrchardTxCount is not None
|
||||||
|
else None)
|
||||||
parent.consensusBranchId = left_child.consensusBranchId
|
parent.consensusBranchId = left_child.consensusBranchId
|
||||||
return parent
|
return parent
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
HistoryNode getLeafN(uint64_t block_num) {
|
HistoryNode getLeafN(uint64_t block_num) {
|
||||||
HistoryNode node = libzcash::NewLeaf(
|
HistoryNode node = libzcash::NewV1Leaf(
|
||||||
uint256(),
|
uint256(),
|
||||||
block_num*10,
|
block_num*10,
|
||||||
block_num*13,
|
block_num*13,
|
||||||
|
@ -81,34 +81,36 @@ TEST(History, Smoky) {
|
||||||
FakeCoinsViewDB fakeDB;
|
FakeCoinsViewDB fakeDB;
|
||||||
CCoinsViewCache view(&fakeDB);
|
CCoinsViewCache view(&fakeDB);
|
||||||
|
|
||||||
|
uint32_t epochId = 0;
|
||||||
|
|
||||||
// Test initial value
|
// Test initial value
|
||||||
EXPECT_EQ(view.GetHistoryLength(0), 0);
|
EXPECT_EQ(view.GetHistoryLength(epochId), 0);
|
||||||
|
|
||||||
view.PushHistoryNode(1, getLeafN(1));
|
view.PushHistoryNode(epochId, getLeafN(1));
|
||||||
|
|
||||||
EXPECT_EQ(view.GetHistoryLength(1), 1);
|
EXPECT_EQ(view.GetHistoryLength(epochId), 1);
|
||||||
|
|
||||||
view.PushHistoryNode(1, getLeafN(2));
|
view.PushHistoryNode(epochId, getLeafN(2));
|
||||||
|
|
||||||
EXPECT_EQ(view.GetHistoryLength(1), 3);
|
EXPECT_EQ(view.GetHistoryLength(epochId), 3);
|
||||||
|
|
||||||
view.PushHistoryNode(1, getLeafN(3));
|
view.PushHistoryNode(epochId, getLeafN(3));
|
||||||
|
|
||||||
EXPECT_EQ(view.GetHistoryLength(1), 4);
|
EXPECT_EQ(view.GetHistoryLength(epochId), 4);
|
||||||
|
|
||||||
view.PushHistoryNode(1, getLeafN(4));
|
view.PushHistoryNode(epochId, getLeafN(4));
|
||||||
|
|
||||||
uint256 h4Root = view.GetHistoryRoot(1);
|
uint256 h4Root = view.GetHistoryRoot(epochId);
|
||||||
|
|
||||||
EXPECT_EQ(view.GetHistoryLength(1), 7);
|
EXPECT_EQ(view.GetHistoryLength(epochId), 7);
|
||||||
|
|
||||||
view.PushHistoryNode(1, getLeafN(5));
|
view.PushHistoryNode(epochId, getLeafN(5));
|
||||||
EXPECT_EQ(view.GetHistoryLength(1), 8);
|
EXPECT_EQ(view.GetHistoryLength(epochId), 8);
|
||||||
|
|
||||||
view.PopHistoryNode(1);
|
view.PopHistoryNode(epochId);
|
||||||
|
|
||||||
EXPECT_EQ(view.GetHistoryLength(1), 7);
|
EXPECT_EQ(view.GetHistoryLength(epochId), 7);
|
||||||
EXPECT_EQ(h4Root, view.GetHistoryRoot(1));
|
EXPECT_EQ(h4Root, view.GetHistoryRoot(epochId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,56 +119,64 @@ TEST(History, EpochBoundaries) {
|
||||||
FakeCoinsViewDB fakeDB;
|
FakeCoinsViewDB fakeDB;
|
||||||
CCoinsViewCache view(&fakeDB);
|
CCoinsViewCache view(&fakeDB);
|
||||||
|
|
||||||
view.PushHistoryNode(1, getLeafN(1));
|
// Test with the Heartwood and Canopy epochs
|
||||||
|
uint32_t epoch1 = 0xf5b9230b;
|
||||||
|
uint32_t epoch2 = 0xe9ff75a6;
|
||||||
|
|
||||||
EXPECT_EQ(view.GetHistoryLength(1), 1);
|
view.PushHistoryNode(epoch1, getLeafN(1));
|
||||||
|
|
||||||
view.PushHistoryNode(1, getLeafN(2));
|
EXPECT_EQ(view.GetHistoryLength(epoch1), 1);
|
||||||
|
|
||||||
EXPECT_EQ(view.GetHistoryLength(1), 3);
|
view.PushHistoryNode(epoch1, getLeafN(2));
|
||||||
|
|
||||||
view.PushHistoryNode(1, getLeafN(3));
|
EXPECT_EQ(view.GetHistoryLength(epoch1), 3);
|
||||||
|
|
||||||
EXPECT_EQ(view.GetHistoryLength(1), 4);
|
view.PushHistoryNode(epoch1, getLeafN(3));
|
||||||
|
|
||||||
view.PushHistoryNode(1, getLeafN(4));
|
EXPECT_EQ(view.GetHistoryLength(epoch1), 4);
|
||||||
|
|
||||||
uint256 h4Root = view.GetHistoryRoot(1);
|
view.PushHistoryNode(epoch1, getLeafN(4));
|
||||||
|
|
||||||
EXPECT_EQ(view.GetHistoryLength(1), 7);
|
uint256 h4Root = view.GetHistoryRoot(epoch1);
|
||||||
|
|
||||||
view.PushHistoryNode(1, getLeafN(5));
|
EXPECT_EQ(view.GetHistoryLength(epoch1), 7);
|
||||||
EXPECT_EQ(view.GetHistoryLength(1), 8);
|
|
||||||
|
view.PushHistoryNode(epoch1, getLeafN(5));
|
||||||
|
EXPECT_EQ(view.GetHistoryLength(epoch1), 8);
|
||||||
|
|
||||||
|
|
||||||
// New Epoch(2)
|
// Move to Canopy epoch
|
||||||
view.PushHistoryNode(2, getLeafN(6));
|
view.PushHistoryNode(epoch2, getLeafN(6));
|
||||||
EXPECT_EQ(view.GetHistoryLength(1), 8);
|
EXPECT_EQ(view.GetHistoryLength(epoch1), 8);
|
||||||
EXPECT_EQ(view.GetHistoryLength(2), 1);
|
EXPECT_EQ(view.GetHistoryLength(epoch2), 1);
|
||||||
|
|
||||||
view.PushHistoryNode(2, getLeafN(7));
|
view.PushHistoryNode(epoch2, getLeafN(7));
|
||||||
EXPECT_EQ(view.GetHistoryLength(1), 8);
|
EXPECT_EQ(view.GetHistoryLength(epoch1), 8);
|
||||||
EXPECT_EQ(view.GetHistoryLength(2), 3);
|
EXPECT_EQ(view.GetHistoryLength(epoch2), 3);
|
||||||
|
|
||||||
view.PushHistoryNode(2, getLeafN(8));
|
view.PushHistoryNode(epoch2, getLeafN(8));
|
||||||
EXPECT_EQ(view.GetHistoryLength(1), 8);
|
EXPECT_EQ(view.GetHistoryLength(epoch1), 8);
|
||||||
EXPECT_EQ(view.GetHistoryLength(2), 4);
|
EXPECT_EQ(view.GetHistoryLength(epoch2), 4);
|
||||||
|
|
||||||
// Rolling epoch back to 1
|
// Rolling epoch back to 1
|
||||||
view.PopHistoryNode(2);
|
view.PopHistoryNode(epoch2);
|
||||||
EXPECT_EQ(view.GetHistoryLength(2), 3);
|
EXPECT_EQ(view.GetHistoryLength(epoch2), 3);
|
||||||
|
|
||||||
view.PopHistoryNode(2);
|
view.PopHistoryNode(epoch2);
|
||||||
EXPECT_EQ(view.GetHistoryLength(2), 1);
|
EXPECT_EQ(view.GetHistoryLength(epoch2), 1);
|
||||||
EXPECT_EQ(view.GetHistoryLength(1), 8);
|
EXPECT_EQ(view.GetHistoryLength(epoch1), 8);
|
||||||
|
|
||||||
// And even rolling epoch 1 back a bit
|
// And even rolling epoch 1 back a bit
|
||||||
view.PopHistoryNode(1);
|
view.PopHistoryNode(epoch1);
|
||||||
EXPECT_EQ(view.GetHistoryLength(1), 7);
|
EXPECT_EQ(view.GetHistoryLength(epoch1), 7);
|
||||||
|
|
||||||
// And also rolling epoch 2 back to 0
|
// And also rolling epoch 2 back to 0
|
||||||
view.PopHistoryNode(2);
|
view.PopHistoryNode(epoch2);
|
||||||
EXPECT_EQ(view.GetHistoryLength(2), 0);
|
EXPECT_EQ(view.GetHistoryLength(epoch2), 0);
|
||||||
|
|
||||||
|
// Trying to truncate an empty tree is a no-op
|
||||||
|
view.PopHistoryNode(epoch2);
|
||||||
|
EXPECT_EQ(view.GetHistoryLength(epoch2), 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
21
src/main.cpp
21
src/main.cpp
|
@ -2864,6 +2864,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||||
auto prevConsensusBranchId = CurrentEpochBranchId(pindex->nHeight - 1, chainparams.GetConsensus());
|
auto prevConsensusBranchId = CurrentEpochBranchId(pindex->nHeight - 1, chainparams.GetConsensus());
|
||||||
|
|
||||||
size_t total_sapling_tx = 0;
|
size_t total_sapling_tx = 0;
|
||||||
|
size_t total_orchard_tx = 0;
|
||||||
|
|
||||||
std::vector<PrecomputedTransactionData> txdata;
|
std::vector<PrecomputedTransactionData> txdata;
|
||||||
txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated
|
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())) {
|
if (!(tx.vShieldedSpend.empty() && tx.vShieldedOutput.empty())) {
|
||||||
total_sapling_tx += 1;
|
total_sapling_tx += 1;
|
||||||
}
|
}
|
||||||
|
if (tx.GetOrchardBundle().IsPresent()) {
|
||||||
|
total_orchard_tx += 1;
|
||||||
|
}
|
||||||
|
|
||||||
vPos.push_back(std::make_pair(tx.GetHash(), pos));
|
vPos.push_back(std::make_pair(tx.GetHash(), pos));
|
||||||
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
|
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
|
||||||
|
@ -3090,7 +3094,21 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||||
|
|
||||||
// History read/write is started with Heartwood update.
|
// History read/write is started with Heartwood update.
|
||||||
if (chainparams.GetConsensus().NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_HEARTWOOD)) {
|
if (chainparams.GetConsensus().NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_HEARTWOOD)) {
|
||||||
auto historyNode = libzcash::NewLeaf(
|
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.GetHash(),
|
||||||
block.nTime,
|
block.nTime,
|
||||||
block.nBits,
|
block.nBits,
|
||||||
|
@ -3099,6 +3117,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||||
pindex->nHeight,
|
pindex->nHeight,
|
||||||
total_sapling_tx
|
total_sapling_tx
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
view.PushHistoryNode(consensusBranchId, historyNode);
|
view.PushHistoryNode(consensusBranchId, historyNode);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,10 @@ public:
|
||||||
inner.reset(bundle);
|
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.
|
/// Queues this bundle's signatures for validation.
|
||||||
///
|
///
|
||||||
/// `txid` must be for the transaction this bundle is within.
|
/// `txid` must be for the transaction this bundle is within.
|
||||||
|
|
|
@ -222,6 +222,9 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex)
|
||||||
UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false)
|
UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false)
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs_main);
|
AssertLockHeld(cs_main);
|
||||||
|
bool nu5Active = Params().GetConsensus().NetworkUpgradeActive(
|
||||||
|
blockindex->nHeight, Consensus::UPGRADE_NU5);
|
||||||
|
|
||||||
UniValue result(UniValue::VOBJ);
|
UniValue result(UniValue::VOBJ);
|
||||||
result.pushKV("hash", block.GetHash().GetHex());
|
result.pushKV("hash", block.GetHash().GetHex());
|
||||||
int confirmations = -1;
|
int confirmations = -1;
|
||||||
|
@ -236,6 +239,9 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
|
||||||
result.pushKV("blockcommitments", blockindex->hashBlockCommitments.GetHex());
|
result.pushKV("blockcommitments", blockindex->hashBlockCommitments.GetHex());
|
||||||
result.pushKV("authdataroot", blockindex->hashAuthDataRoot.GetHex());
|
result.pushKV("authdataroot", blockindex->hashAuthDataRoot.GetHex());
|
||||||
result.pushKV("finalsaplingroot", blockindex->hashFinalSaplingRoot.GetHex());
|
result.pushKV("finalsaplingroot", blockindex->hashFinalSaplingRoot.GetHex());
|
||||||
|
if (nu5Active) {
|
||||||
|
result.pushKV("finalorchardroot", blockindex->hashFinalOrchardRoot.GetHex());
|
||||||
|
}
|
||||||
result.pushKV("chainhistoryroot", blockindex->hashChainHistoryRoot.GetHex());
|
result.pushKV("chainhistoryroot", blockindex->hashChainHistoryRoot.GetHex());
|
||||||
UniValue txs(UniValue::VARR);
|
UniValue txs(UniValue::VARR);
|
||||||
for (const CTransaction&tx : block.vtx)
|
for (const CTransaction&tx : block.vtx)
|
||||||
|
@ -706,6 +712,9 @@ UniValue getblock(const UniValue& params, bool fHelp)
|
||||||
" \"version\" : n, (numeric) The block version\n"
|
" \"version\" : n, (numeric) The block version\n"
|
||||||
" \"merkleroot\" : \"xxxx\", (string) The merkle root\n"
|
" \"merkleroot\" : \"xxxx\", (string) The merkle root\n"
|
||||||
" \"finalsaplingroot\" : \"xxxx\", (string) The root of the Sapling commitment tree after applying this block\n"
|
" \"finalsaplingroot\" : \"xxxx\", (string) The root of the Sapling commitment tree after applying this block\n"
|
||||||
|
" \"finalorchardroot\" : \"xxxx\", (string) The root of the Orchard commitment tree after applying this block.\n"
|
||||||
|
" Omitted for blocks prior to NU5 activation. This will be the null\n"
|
||||||
|
" hash if this block has never been connected to a main chain.\n"
|
||||||
" \"tx\" : [ (array of string) The transaction ids\n"
|
" \"tx\" : [ (array of string) The transaction ids\n"
|
||||||
" \"transactionid\" (string) The transaction id\n"
|
" \"transactionid\" (string) The transaction id\n"
|
||||||
" ,...\n"
|
" ,...\n"
|
||||||
|
|
|
@ -11,25 +11,6 @@
|
||||||
#include <stdalign.h>
|
#include <stdalign.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define NODE_SERIALIZED_LENGTH 171
|
|
||||||
#define ENTRY_SERIALIZED_LENGTH (NODE_SERIALIZED_LENGTH + 9)
|
|
||||||
|
|
||||||
typedef struct HistoryNode {
|
|
||||||
unsigned char bytes[NODE_SERIALIZED_LENGTH];
|
|
||||||
} HistoryNode;
|
|
||||||
static_assert(
|
|
||||||
sizeof(HistoryNode) == NODE_SERIALIZED_LENGTH,
|
|
||||||
"HistoryNode struct is not the same size as the underlying byte array");
|
|
||||||
static_assert(alignof(HistoryNode) == 1, "HistoryNode struct alignment is not 1");
|
|
||||||
|
|
||||||
typedef struct HistoryEntry {
|
|
||||||
unsigned char bytes[ENTRY_SERIALIZED_LENGTH];
|
|
||||||
} HistoryEntry;
|
|
||||||
static_assert(
|
|
||||||
sizeof(HistoryEntry) == ENTRY_SERIALIZED_LENGTH,
|
|
||||||
"HistoryEntry struct is not the same size as the underlying byte array");
|
|
||||||
static_assert(alignof(HistoryEntry) == 1, "HistoryEntry struct alignment is not 1");
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -333,33 +314,6 @@ extern "C" {
|
||||||
unsigned char *addr_ret
|
unsigned char *addr_ret
|
||||||
);
|
);
|
||||||
|
|
||||||
uint32_t librustzcash_mmr_append(
|
|
||||||
uint32_t cbranch,
|
|
||||||
uint32_t t_len,
|
|
||||||
const uint32_t *ni_ptr,
|
|
||||||
const HistoryEntry *n_ptr,
|
|
||||||
size_t p_len,
|
|
||||||
const HistoryNode *nn_ptr,
|
|
||||||
unsigned char *rt_ret,
|
|
||||||
HistoryNode *buf_ret
|
|
||||||
);
|
|
||||||
|
|
||||||
uint32_t librustzcash_mmr_delete(
|
|
||||||
uint32_t cbranch,
|
|
||||||
uint32_t t_len,
|
|
||||||
const uint32_t *ni_ptr,
|
|
||||||
const HistoryEntry *n_ptr,
|
|
||||||
size_t p_len,
|
|
||||||
size_t e_len,
|
|
||||||
unsigned char *rt_ret
|
|
||||||
);
|
|
||||||
|
|
||||||
uint32_t librustzcash_mmr_hash_node(
|
|
||||||
uint32_t cbranch,
|
|
||||||
const HistoryNode *n_ptr,
|
|
||||||
unsigned char *h_ret
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Fills the provided buffer with random bytes. This is intended to
|
/// Fills the provided buffer with random bytes. This is intended to
|
||||||
/// be a cryptographically secure RNG; it uses Rust's `OsRng`, which
|
/// be a cryptographically secure RNG; it uses Rust's `OsRng`, which
|
||||||
/// is implemented in terms of the `getrandom` crate. The first call
|
/// is implemented in terms of the `getrandom` crate. The first call
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
// 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 .
|
||||||
|
|
||||||
|
#ifndef ZCASH_RUST_INCLUDE_RUST_HISTORY_H
|
||||||
|
#define ZCASH_RUST_INCLUDE_RUST_HISTORY_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define NODE_V1_SERIALIZED_LENGTH 171
|
||||||
|
#define NODE_SERIALIZED_LENGTH 244
|
||||||
|
#define ENTRY_SERIALIZED_LENGTH (NODE_SERIALIZED_LENGTH + 9)
|
||||||
|
|
||||||
|
typedef struct HistoryNode {
|
||||||
|
unsigned char bytes[NODE_SERIALIZED_LENGTH];
|
||||||
|
} HistoryNode;
|
||||||
|
static_assert(
|
||||||
|
sizeof(HistoryNode) == NODE_SERIALIZED_LENGTH,
|
||||||
|
"HistoryNode struct is not the same size as the underlying byte array");
|
||||||
|
static_assert(alignof(HistoryNode) == 1, "HistoryNode struct alignment is not 1");
|
||||||
|
|
||||||
|
typedef struct HistoryEntry {
|
||||||
|
unsigned char bytes[ENTRY_SERIALIZED_LENGTH];
|
||||||
|
} HistoryEntry;
|
||||||
|
static_assert(
|
||||||
|
sizeof(HistoryEntry) == ENTRY_SERIALIZED_LENGTH,
|
||||||
|
"HistoryEntry struct is not the same size as the underlying byte array");
|
||||||
|
static_assert(alignof(HistoryEntry) == 1, "HistoryEntry struct alignment is not 1");
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/// Appends a leaf to the given history tree.
|
||||||
|
///
|
||||||
|
/// `t_len` must be at least 1.
|
||||||
|
///
|
||||||
|
/// Aborts if `cbranch` is not a valid consensus branch ID.
|
||||||
|
uint32_t librustzcash_mmr_append(
|
||||||
|
uint32_t cbranch,
|
||||||
|
uint32_t t_len,
|
||||||
|
const uint32_t* ni_ptr,
|
||||||
|
const HistoryEntry* n_ptr,
|
||||||
|
size_t p_len,
|
||||||
|
const HistoryNode* nn_ptr,
|
||||||
|
unsigned char* rt_ret,
|
||||||
|
HistoryNode* buf_ret);
|
||||||
|
|
||||||
|
/// Deletes the most recently-appended leaf from the given history tree.
|
||||||
|
///
|
||||||
|
/// `t_len` must be at least 1.
|
||||||
|
///
|
||||||
|
/// Aborts if `cbranch` is not a valid consensus branch ID.
|
||||||
|
uint32_t librustzcash_mmr_delete(
|
||||||
|
uint32_t cbranch,
|
||||||
|
uint32_t t_len,
|
||||||
|
const uint32_t* ni_ptr,
|
||||||
|
const HistoryEntry* n_ptr,
|
||||||
|
size_t p_len,
|
||||||
|
size_t e_len,
|
||||||
|
unsigned char* rt_ret);
|
||||||
|
|
||||||
|
/// Returns the hash of the given history tree node.
|
||||||
|
///
|
||||||
|
/// Aborts if `cbranch` is not a valid consensus branch ID.
|
||||||
|
uint32_t librustzcash_mmr_hash_node(
|
||||||
|
uint32_t cbranch,
|
||||||
|
const HistoryNode* n_ptr,
|
||||||
|
unsigned char* h_ret);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // ZCASH_RUST_INCLUDE_RUST_HISTORY_H
|
|
@ -0,0 +1,264 @@
|
||||||
|
use std::{convert::TryFrom, slice};
|
||||||
|
|
||||||
|
use libc::{c_uchar, size_t};
|
||||||
|
use zcash_history::{Entry as MMREntry, Tree as MMRTree, Version, V1, V2};
|
||||||
|
use zcash_primitives::consensus::BranchId;
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
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,
|
||||||
|
) -> Result<MMRTree<V>, &'static str> {
|
||||||
|
let (indices, nodes) = unsafe {
|
||||||
|
(
|
||||||
|
slice::from_raw_parts(ni_ptr, p_len + e_len),
|
||||||
|
slice::from_raw_parts(n_ptr, p_len + e_len),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut peaks: Vec<_> = indices
|
||||||
|
.iter()
|
||||||
|
.zip(nodes.iter())
|
||||||
|
.map(
|
||||||
|
|(index, node)| match MMREntry::from_bytes(cbranch, &node[..]) {
|
||||||
|
Ok(entry) => Ok((*index, entry)),
|
||||||
|
Err(_) => Err("Invalid encoding"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
let extra = peaks.split_off(p_len);
|
||||||
|
|
||||||
|
Ok(MMRTree::new(t_len, peaks, extra))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "system" fn librustzcash_mmr_append(
|
||||||
|
// 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 {
|
||||||
|
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() {
|
||||||
|
Some(r) => r,
|
||||||
|
None => {
|
||||||
|
return 0;
|
||||||
|
} // Null pointer passed, error
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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 V::from_bytes(cbranch, &new_node_bytes[..]) {
|
||||||
|
Ok(node) => node,
|
||||||
|
_ => {
|
||||||
|
return 0;
|
||||||
|
} // error
|
||||||
|
};
|
||||||
|
|
||||||
|
let appended = match tree.append_leaf(node) {
|
||||||
|
Ok(appended) => appended,
|
||||||
|
_ => {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let return_count = appended.len();
|
||||||
|
|
||||||
|
let root_node = tree
|
||||||
|
.root_node()
|
||||||
|
.expect("Just added, should resolve always; qed");
|
||||||
|
unsafe {
|
||||||
|
*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()
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return_count as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "system" fn librustzcash_mmr_delete(
|
||||||
|
// 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 {
|
||||||
|
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;
|
||||||
|
} // error
|
||||||
|
};
|
||||||
|
|
||||||
|
let truncate_len = match tree.truncate_leaf() {
|
||||||
|
Ok(v) => v,
|
||||||
|
_ => {
|
||||||
|
return 0;
|
||||||
|
} // Error
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*rt_ret = V::hash(
|
||||||
|
tree.root_node()
|
||||||
|
.expect("Just generated without errors, root should be resolving")
|
||||||
|
.data(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
truncate_len
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
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() {
|
||||||
|
Some(r) => r,
|
||||||
|
None => return 1,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let node = match V::from_bytes(cbranch, &node_bytes[..]) {
|
||||||
|
Ok(n) => n,
|
||||||
|
_ => return 1, // error
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*h_ret = V::hash(&node);
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
|
@ -61,14 +61,13 @@ use zcash_proofs::{
|
||||||
sprout,
|
sprout,
|
||||||
};
|
};
|
||||||
|
|
||||||
use zcash_history::{Entry as MMREntry, NodeData as MMRNodeData, Tree as MMRTree};
|
|
||||||
|
|
||||||
mod blake2b;
|
mod blake2b;
|
||||||
mod ed25519;
|
mod ed25519;
|
||||||
mod metrics_ffi;
|
mod metrics_ffi;
|
||||||
mod streams_ffi;
|
mod streams_ffi;
|
||||||
mod tracing_ffi;
|
mod tracing_ffi;
|
||||||
|
|
||||||
|
mod history_ffi;
|
||||||
mod orchard_ffi;
|
mod orchard_ffi;
|
||||||
mod transaction_ffi;
|
mod transaction_ffi;
|
||||||
|
|
||||||
|
@ -1092,183 +1091,6 @@ pub extern "C" fn librustzcash_zip32_xfvk_address(
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn construct_mmr_tree(
|
|
||||||
// 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,
|
|
||||||
) -> Result<MMRTree, &'static str> {
|
|
||||||
let (indices, nodes) = unsafe {
|
|
||||||
(
|
|
||||||
slice::from_raw_parts(ni_ptr, p_len + e_len),
|
|
||||||
slice::from_raw_parts(n_ptr, p_len + e_len),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut peaks: Vec<_> = indices
|
|
||||||
.iter()
|
|
||||||
.zip(nodes.iter())
|
|
||||||
.map(
|
|
||||||
|(index, node)| match MMREntry::from_bytes(cbranch, &node[..]) {
|
|
||||||
Ok(entry) => Ok((*index, entry)),
|
|
||||||
Err(_) => Err("Invalid encoding"),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.collect::<Result<_, _>>()?;
|
|
||||||
let extra = peaks.split_off(p_len);
|
|
||||||
|
|
||||||
Ok(MMRTree::new(t_len, peaks, extra))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "system" fn librustzcash_mmr_append(
|
|
||||||
// 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() {
|
|
||||||
Some(r) => r,
|
|
||||||
None => {
|
|
||||||
return 0;
|
|
||||||
} // Null pointer passed, error
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut tree = match construct_mmr_tree(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[..]) {
|
|
||||||
Ok(node) => node,
|
|
||||||
_ => {
|
|
||||||
return 0;
|
|
||||||
} // error
|
|
||||||
};
|
|
||||||
|
|
||||||
let appended = match tree.append_leaf(node) {
|
|
||||||
Ok(appended) => appended,
|
|
||||||
_ => {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let return_count = appended.len();
|
|
||||||
|
|
||||||
let root_node = tree
|
|
||||||
.root_node()
|
|
||||||
.expect("Just added, should resolve always; qed");
|
|
||||||
unsafe {
|
|
||||||
*rt_ret = root_node.data().hash();
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return_count as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "system" fn librustzcash_mmr_delete(
|
|
||||||
// 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(cbranch, t_len, ni_ptr, n_ptr, p_len, e_len) {
|
|
||||||
Ok(t) => t,
|
|
||||||
_ => {
|
|
||||||
return 0;
|
|
||||||
} // error
|
|
||||||
};
|
|
||||||
|
|
||||||
let truncate_len = match tree.truncate_leaf() {
|
|
||||||
Ok(v) => v,
|
|
||||||
_ => {
|
|
||||||
return 0;
|
|
||||||
} // Error
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
*rt_ret = tree
|
|
||||||
.root_node()
|
|
||||||
.expect("Just generated without errors, root should be resolving")
|
|
||||||
.data()
|
|
||||||
.hash();
|
|
||||||
}
|
|
||||||
|
|
||||||
truncate_len
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
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 {
|
|
||||||
let node_bytes: &[u8; zcash_history::MAX_NODE_DATA_SIZE] = unsafe {
|
|
||||||
match n_ptr.as_ref() {
|
|
||||||
Some(r) => r,
|
|
||||||
None => return 1,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let node = match MMRNodeData::from_bytes(cbranch, &node_bytes[..]) {
|
|
||||||
Ok(n) => n,
|
|
||||||
_ => return 1, // error
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
*h_ret = node.hash();
|
|
||||||
}
|
|
||||||
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn librustzcash_getrandom(buf: *mut u8, buf_len: usize) {
|
pub extern "C" fn librustzcash_getrandom(buf: *mut u8, buf_len: usize) {
|
||||||
let buf = unsafe { slice::from_raw_parts_mut(buf, buf_len) };
|
let buf = unsafe { slice::from_raw_parts_mut(buf, buf_len) };
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
use zcash_history::{Entry, EntryLink, NodeData};
|
use zcash_history::{Entry, EntryLink, NodeData, V1};
|
||||||
|
|
||||||
use crate::{librustzcash_mmr_append, librustzcash_mmr_delete};
|
use crate::history_ffi::{librustzcash_mmr_append, librustzcash_mmr_delete};
|
||||||
|
|
||||||
const NODE_DATA_16L: &[u8] = include_bytes!("./res/tree16.dat");
|
const NODE_DATA_16L: &[u8] = include_bytes!("./res/tree16.dat");
|
||||||
const NODE_DATA_1023L: &[u8] = include_bytes!("./res/tree1023.dat");
|
const NODE_DATA_1023L: &[u8] = include_bytes!("./res/tree1023.dat");
|
||||||
|
|
||||||
struct TreeView {
|
struct TreeView {
|
||||||
peaks: Vec<(u32, Entry)>,
|
peaks: Vec<(u32, Entry<V1>)>,
|
||||||
extra: Vec<(u32, Entry)>,
|
extra: Vec<(u32, Entry<V1>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draft(into: &mut Vec<(u32, Entry)>, nodes: &[NodeData], peak_pos: usize, h: u32) {
|
fn draft(into: &mut Vec<(u32, Entry<V1>)>, nodes: &[NodeData], peak_pos: usize, h: u32) {
|
||||||
let node_data = nodes[peak_pos - 1].clone();
|
let node_data = nodes[peak_pos - 1].clone();
|
||||||
let peak: Entry = match h {
|
let peak = match h {
|
||||||
0 => node_data.into(),
|
0 => Entry::new_leaf(node_data),
|
||||||
_ => Entry::new(
|
_ => Entry::new(
|
||||||
node_data,
|
node_data,
|
||||||
EntryLink::Stored((peak_pos - (1 << h) - 1) as u32),
|
EntryLink::Stored((peak_pos - (1 << h) - 1) as u32),
|
||||||
|
|
17
src/txdb.cpp
17
src/txdb.cpp
|
@ -10,6 +10,7 @@
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "pow.h"
|
#include "pow.h"
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
|
#include "zcash/History.hpp"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
@ -145,6 +146,21 @@ HistoryNode CCoinsViewDB::GetHistoryAt(uint32_t epochId, HistoryIndex index) con
|
||||||
throw runtime_error("History data inconsistent - reindex?");
|
throw runtime_error("History data inconsistent - reindex?");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// Read mmrNode into tmp std::array
|
||||||
std::array<unsigned char, NODE_SERIALIZED_LENGTH> tmpMmrNode;
|
std::array<unsigned char, NODE_SERIALIZED_LENGTH> tmpMmrNode;
|
||||||
|
|
||||||
|
@ -153,6 +169,7 @@ HistoryNode CCoinsViewDB::GetHistoryAt(uint32_t epochId, HistoryIndex index) con
|
||||||
}
|
}
|
||||||
|
|
||||||
std::copy(std::begin(tmpMmrNode), std::end(tmpMmrNode), mmrNode.bytes);
|
std::copy(std::begin(tmpMmrNode), std::end(tmpMmrNode), mmrNode.bytes);
|
||||||
|
}
|
||||||
|
|
||||||
return mmrNode;
|
return mmrNode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
|
||||||
|
#include "consensus/upgrades.h"
|
||||||
#include "serialize.h"
|
#include "serialize.h"
|
||||||
#include "streams.h"
|
#include "streams.h"
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
|
@ -46,10 +47,13 @@ HistoryNode NewNode(
|
||||||
uint32_t endTarget,
|
uint32_t endTarget,
|
||||||
uint256 startSaplingRoot,
|
uint256 startSaplingRoot,
|
||||||
uint256 endSaplingRoot,
|
uint256 endSaplingRoot,
|
||||||
|
std::optional<uint256> startOrchardRoot,
|
||||||
|
std::optional<uint256> endOrchardRoot,
|
||||||
uint256 subtreeTotalWork,
|
uint256 subtreeTotalWork,
|
||||||
uint64_t startHeight,
|
uint64_t startHeight,
|
||||||
uint64_t endHeight,
|
uint64_t endHeight,
|
||||||
uint64_t saplingTxCount
|
uint64_t saplingTxCount,
|
||||||
|
std::optional<uint64_t> orchardTxCount
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
CDataStream buf(SER_DISK, 0);
|
CDataStream buf(SER_DISK, 0);
|
||||||
|
@ -66,13 +70,19 @@ HistoryNode NewNode(
|
||||||
buf << COMPACTSIZE(startHeight);
|
buf << COMPACTSIZE(startHeight);
|
||||||
buf << COMPACTSIZE(endHeight);
|
buf << COMPACTSIZE(endHeight);
|
||||||
buf << COMPACTSIZE(saplingTxCount);
|
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);
|
assert(buf.size() <= NODE_SERIALIZED_LENGTH);
|
||||||
std::copy(std::begin(buf), std::end(buf), result.bytes);
|
std::copy(std::begin(buf), std::end(buf), result.bytes);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryNode NewLeaf(
|
HistoryNode NewV1Leaf(
|
||||||
uint256 commitment,
|
uint256 commitment,
|
||||||
uint32_t time,
|
uint32_t time,
|
||||||
uint32_t target,
|
uint32_t target,
|
||||||
|
@ -89,10 +99,42 @@ HistoryNode NewLeaf(
|
||||||
target,
|
target,
|
||||||
saplingRoot,
|
saplingRoot,
|
||||||
saplingRoot,
|
saplingRoot,
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
totalWork,
|
totalWork,
|
||||||
height,
|
height,
|
||||||
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;
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#include "streams.h"
|
#include "streams.h"
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
|
|
||||||
|
#include <rust/history.h>
|
||||||
|
|
||||||
#include "librustzcash.h"
|
#include "librustzcash.h"
|
||||||
|
|
||||||
namespace libzcash {
|
namespace libzcash {
|
||||||
|
@ -41,8 +43,8 @@ public:
|
||||||
void Truncate(HistoryIndex newLength);
|
void Truncate(HistoryIndex newLength);
|
||||||
};
|
};
|
||||||
|
|
||||||
// New history node with metadata based on block state.
|
// New V1 history node with metadata based on block state.
|
||||||
HistoryNode NewLeaf(
|
HistoryNode NewV1Leaf(
|
||||||
uint256 commitment,
|
uint256 commitment,
|
||||||
uint32_t time,
|
uint32_t time,
|
||||||
uint32_t target,
|
uint32_t target,
|
||||||
|
@ -52,12 +54,28 @@ HistoryNode NewLeaf(
|
||||||
uint64_t saplingTxCount
|
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)
|
// Convert history node to tree node (with children references)
|
||||||
HistoryEntry NodeToEntry(const HistoryNode node, uint32_t left, uint32_t right);
|
HistoryEntry NodeToEntry(const HistoryNode node, uint32_t left, uint32_t right);
|
||||||
|
|
||||||
// Convert history node to leaf node (end nodes without children)
|
// Convert history node to leaf node (end nodes without children)
|
||||||
HistoryEntry LeafToEntry(const HistoryNode node);
|
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;
|
typedef libzcash::HistoryCache HistoryCache;
|
||||||
|
|
Loading…
Reference in New Issue