1#![allow(missing_docs)]
6
7mod tests;
8
9use std::{collections::BTreeMap, io, sync::Arc};
10
11use serde_big_array::BigArray;
12pub use zcash_history::{V1, V2, V3};
13
14use crate::{
15 block::{Block, ChainHistoryMmrRootHash},
16 orchard,
17 parameters::{Network, NetworkUpgrade},
18 sapling,
19};
20
21#[derive(Clone, Copy)]
27pub struct BlockCommitmentTreeRoots<'a> {
28 pub sapling: &'a sapling::tree::Root,
30 pub orchard: &'a orchard::tree::Root,
32 pub ironwood: &'a orchard::tree::Root,
34}
35
36pub trait Version: zcash_history::Version {
38 fn block_to_history_node(
42 block: Arc<Block>,
43 network: &Network,
44 roots: BlockCommitmentTreeRoots,
45 ) -> Self::NodeData;
46}
47
48pub struct Tree<V: zcash_history::Version> {
53 network: Network,
54 network_upgrade: NetworkUpgrade,
55 inner: zcash_history::Tree<V>,
56}
57
58pub struct NodeData {
60 inner: [u8; zcash_history::MAX_NODE_DATA_SIZE],
61}
62
63impl From<&zcash_history::NodeData> for NodeData {
64 fn from(inner_node: &zcash_history::NodeData) -> Self {
66 let mut node = NodeData {
67 inner: [0; zcash_history::MAX_NODE_DATA_SIZE],
68 };
69 inner_node
70 .write(&mut &mut node.inner[..])
71 .expect("buffer has the proper size");
72 node
73 }
74}
75
76#[derive(Clone, Debug, Serialize, Deserialize)]
80pub struct Entry {
81 #[serde(with = "BigArray")]
82 inner: [u8; zcash_history::MAX_ENTRY_SIZE],
83}
84
85impl Entry {
86 pub fn from_raw_bytes_padded(bytes: &[u8]) -> Self {
96 let mut inner = [0; zcash_history::MAX_ENTRY_SIZE];
97 let len = bytes.len().min(inner.len());
98 inner[..len].copy_from_slice(&bytes[..len]);
99 Entry { inner }
100 }
101
102 fn new_leaf<V: Version>(
105 block: Arc<Block>,
106 network: &Network,
107 roots: BlockCommitmentTreeRoots,
108 ) -> Self {
109 let node_data = V::block_to_history_node(block, network, roots);
110 let inner_entry = zcash_history::Entry::<V>::new_leaf(node_data);
111 let mut entry = Entry {
112 inner: [0; zcash_history::MAX_ENTRY_SIZE],
113 };
114 inner_entry
115 .write(&mut &mut entry.inner[..])
116 .expect("buffer has the proper size");
117 entry
118 }
119}
120
121impl<V: Version> Tree<V> {
122 #[allow(clippy::unwrap_in_result)]
135 pub fn new_from_cache(
136 network: &Network,
137 network_upgrade: NetworkUpgrade,
138 length: u32,
139 peaks: &BTreeMap<u32, Entry>,
140 extra: &BTreeMap<u32, Entry>,
141 ) -> Result<Self, io::Error> {
142 let branch_id = network_upgrade
143 .branch_id()
144 .expect("unexpected pre-Overwinter MMR history tree");
145 let mut peaks_vec = Vec::new();
146 for (idx, entry) in peaks {
147 let inner_entry = zcash_history::Entry::from_bytes(branch_id.into(), entry.inner)?;
148 peaks_vec.push((*idx, inner_entry));
149 }
150 let mut extra_vec = Vec::new();
151 for (idx, entry) in extra {
152 let inner_entry = zcash_history::Entry::from_bytes(branch_id.into(), entry.inner)?;
153 extra_vec.push((*idx, inner_entry));
154 }
155 let inner = zcash_history::Tree::new(length, peaks_vec, extra_vec);
156 Ok(Tree {
157 network: network.clone(),
158 network_upgrade,
159 inner,
160 })
161 }
162
163 #[allow(clippy::unwrap_in_result)]
167 pub fn new_from_block(
168 network: &Network,
169 block: Arc<Block>,
170 roots: BlockCommitmentTreeRoots,
171 ) -> Result<(Self, Entry), io::Error> {
172 let height = block
173 .coinbase_height()
174 .expect("block must have coinbase height during contextual verification");
175 let network_upgrade = NetworkUpgrade::current(network, height);
176 let entry0 = Entry::new_leaf::<V>(block, network, roots);
177 let mut peaks = BTreeMap::new();
178 peaks.insert(0u32, entry0);
179 Ok((
180 Tree::new_from_cache(network, network_upgrade, 1, &peaks, &BTreeMap::new())?,
181 peaks
182 .remove(&0u32)
183 .expect("must work since it was just added"),
184 ))
185 }
186
187 #[allow(clippy::unwrap_in_result)]
198 pub fn append_leaf(
199 &mut self,
200 block: Arc<Block>,
201 roots: BlockCommitmentTreeRoots,
202 ) -> Result<Vec<Entry>, zcash_history::Error> {
203 let height = block
204 .coinbase_height()
205 .expect("block must have coinbase height during contextual verification");
206 let network_upgrade = NetworkUpgrade::current(&self.network, height);
207
208 assert!(
209 network_upgrade == self.network_upgrade,
210 "added block from network upgrade {:?} but history tree is restricted to {:?}",
211 network_upgrade,
212 self.network_upgrade
213 );
214
215 let node_data = V::block_to_history_node(block, &self.network, roots);
216 let appended = self.inner.append_leaf(node_data)?;
217
218 let mut new_nodes = Vec::new();
219 for entry_link in appended {
220 let mut entry = Entry {
221 inner: [0; zcash_history::MAX_ENTRY_SIZE],
222 };
223 self.inner
224 .resolve_link(entry_link)
225 .expect("entry was just generated so it must be valid")
226 .node()
227 .write(&mut &mut entry.inner[..])
228 .expect("buffer was created with enough capacity");
229 new_nodes.push(entry);
230 }
231 Ok(new_nodes)
232 }
233 pub fn hash(&self) -> ChainHistoryMmrRootHash {
235 V::hash(self.inner.root_node().expect("must have root node").data()).into()
238 }
239}
240
241impl<V: zcash_history::Version> std::fmt::Debug for Tree<V> {
242 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243 f.debug_struct("Tree")
244 .field("network", &self.network)
245 .field("network_upgrade", &self.network_upgrade)
246 .finish()
247 }
248}
249
250impl Version for zcash_history::V1 {
251 fn block_to_history_node(
255 block: Arc<Block>,
256 network: &Network,
257 roots: BlockCommitmentTreeRoots,
258 ) -> Self::NodeData {
259 let sapling_root = roots.sapling;
260 let height = block
261 .coinbase_height()
262 .expect("block must have coinbase height during contextual verification");
263 let network_upgrade = NetworkUpgrade::current(network, height);
264 let branch_id = network_upgrade
265 .branch_id()
266 .expect("must have branch ID for chain history network upgrades");
267 let block_hash = block.hash().0;
268 let time: u32 = block
269 .header
270 .time
271 .timestamp()
272 .try_into()
273 .expect("deserialized and generated timestamps are u32 values");
274 let target = block.header.difficulty_threshold.0;
275 let sapling_root: [u8; 32] = sapling_root.into();
276 let work = block
277 .header
278 .difficulty_threshold
279 .to_work()
280 .expect("work must be valid during contextual verification");
281 let work = primitive_types::U256::from_big_endian(&work.as_u128().to_be_bytes());
283
284 let sapling_tx_count = block.sapling_transactions_count();
285
286 match network_upgrade {
287 NetworkUpgrade::Genesis
288 | NetworkUpgrade::BeforeOverwinter
289 | NetworkUpgrade::Overwinter
290 | NetworkUpgrade::Sapling
291 | NetworkUpgrade::Blossom => {
292 panic!("HistoryTree does not exist for pre-Heartwood upgrades")
293 }
294 NetworkUpgrade::Heartwood
297 | NetworkUpgrade::Canopy
298 | NetworkUpgrade::Nu5
299 | NetworkUpgrade::Nu6
300 | NetworkUpgrade::Nu6_1
301 | NetworkUpgrade::Nu6_2
302 | NetworkUpgrade::Nu6_3
303 | NetworkUpgrade::Nu7 => {}
304 #[cfg(zcash_unstable = "zfuture")]
305 NetworkUpgrade::ZFuture => {}
306 };
307
308 zcash_history::NodeData {
309 consensus_branch_id: branch_id.into(),
310 subtree_commitment: block_hash,
311 start_time: time,
312 end_time: time,
313 start_target: target,
314 end_target: target,
315 start_sapling_root: sapling_root,
316 end_sapling_root: sapling_root,
317 subtree_total_work: work,
318 start_height: height.0 as u64,
319 end_height: height.0 as u64,
320 sapling_tx: sapling_tx_count,
321 }
322 }
323}
324
325impl Version for V2 {
326 fn block_to_history_node(
330 block: Arc<Block>,
331 network: &Network,
332 roots: BlockCommitmentTreeRoots,
333 ) -> Self::NodeData {
334 let orchard_tx_count = block.orchard_transactions_count();
335 let node_data_v1 = V1::block_to_history_node(block, network, roots);
336 let orchard_root: [u8; 32] = roots.orchard.into();
337 Self::NodeData {
338 v1: node_data_v1,
339 start_orchard_root: orchard_root,
340 end_orchard_root: orchard_root,
341 orchard_tx: orchard_tx_count,
342 }
343 }
344}
345
346impl Version for V3 {
347 fn block_to_history_node(
352 block: Arc<Block>,
353 network: &Network,
354 roots: BlockCommitmentTreeRoots,
355 ) -> Self::NodeData {
356 let ironwood_tx_count = block.ironwood_transactions_count();
357 let node_data_v2 = V2::block_to_history_node(block, network, roots);
358 let ironwood_root: [u8; 32] = roots.ironwood.into();
359 Self::NodeData {
360 v2: node_data_v2,
361 start_ironwood_root: ironwood_root,
362 end_ironwood_root: ironwood_root,
363 ironwood_tx: ironwood_tx_count,
364 }
365 }
366}