Trees
Blockchain Data
From zcashd
, zebrad
or lightwalletd
, download
Blocks
or CompactBlocks
and extract the OrchardAction
#![allow(unused)] fn main() { type Hash = [u8; 32]; struct Action { nf: Hash, cmx: Hash, } async fn download_blocks_lwd(url: &str, start: u32, end: u32) -> Vec<Action>; async fn download_blocks_zcashd(url: &str, start: u32, end: u32) -> Vec<Action>; }
Orchard Hash Function
The commitment tree hash function that combines two nodes or two leaves is the following.
#![allow(unused)] fn main() { /// Orchard hash of two nodes of the CMX tree pub fn cmx_hash(level: u8, left: &Hash, right: &Hash) -> [Hash] { let left = MerkleHashOrchard::from_bytes(left).unwrap(); let right = MerkleHashOrchard::from_bytes(right).unwrap(); let h = MerkleHashOrchard::combine(Altitude::from(level), &left, &right); h.to_bytes() } }
where level is 0 for the leaves, and increments by 1 on every layer.
If the tree has an odd number of leaves, it should be padded by adding an empty leaf with the "empty leaf" hash value.
#![allow(unused)] fn main() { /// Empty Orchard CMX hash pub fn empty_hash() -> [u8; 32] { MerkleHashOrchard::empty_leaf().to_bytes() } }
Logically speaking, the tree is a binary tree of depth 32 and starts
full of empty leaves. The cmx_hash
combines leaves on depth 0 to
depth 1, then depth 1 to depth 2, and so on so forth, until
we reach the root.
At this point, every level of the tree has the same value obtained by hashing two identical values from the previous layer.
As nodes are added, they replace empty leaves, progressively from the position 0. However, empty subtrees have the same hash value as before.
Merkle Root
The Root hash of the tree is a public value. Transactions must include it in their transactions.
It must match the root hash calculated at the end of a block (from zcash or vote chain).
Merkle Path
The Merkle Path is the combination of the following data.
- The value of the leaf (
cmx
ornf
), - The position of the leaf (starting at 0 for the first leaf),
- The list of 32 ommers. They are the sibling nodes on the direct path from the leaf to the root.
The Merkle Path is an input to the ZKP circuits and should not be sent out.
#![allow(unused)] fn main() { pub struct MerklePath { pub value: Hash, pub position: u32, pub path: [Hash; DEPTH], } pub fn calculate_merkle_paths( positions: &[u32], hashes: &[Hash], ) -> (Hash, Vec<MerklePath>); }
where DEPTH = 32
calculate_merkle_paths
takes a list of leaf positions and the list
of leaves and calculates the Merkle Path for every leaf position given.
CMX Tree
The CMX Tree is the Merkle Tree where each leaf is the cmx
value
from the Action in the order they appear on the Blockchain.
NF Tree
The NF Tree is the Merkle Tree of nullifiers. They must be sorted and a list of complement intervals created. \[ \forall i, n_i \not\in \bigcup {[a_i, b_i]} \]
Nullifiers are integers in the base field of the Pallas curve.
#![allow(unused)] fn main() { type Nullifier = Fp; fn build_nf_ranges(nfs: impl IntoIterator<Item = Nullifier>) -> Vec<Nullifier>; }
build_nf_ranges
returns a list of flatten intervals as a list
\([a_0, b_0, a_1, b_1, \dots ]\).