//! Sprout serialization legacy code. //! //! We create a [`LegacyNoteCommitmentTree`] which is a copy of [`NoteCommitmentTree`] but where serialization and //! deserialization can be derived. //! To do this we create a [`LegacyFrontier`] which is a legacy `Frontier` structure that can be found in [1], //! In order to make [`LegacyFrontier`] serializable we also have our own versions of `NonEmptyFrontier` ([`LegacyNonEmptyFrontier`]), //! `Leaf`([`LegacyLeaf`]) and `Position`([`LegacyPosition`]) that can be found in [1] or [2]. //! //! Conversions methods to/from [`LegacyNoteCommitmentTree`] to/from [`NoteCommitmentTree`] are defined also in this file. //! //! [1]: https://github.com/zcash/incrementalmerkletree/blob/incrementalmerkletree-v0.3.1/src/bridgetree.rs //! [2]: https://github.com/zcash/incrementalmerkletree/blob/incrementalmerkletree-v0.3.1/src/lib.rs use incrementalmerkletree::{frontier::Frontier, Position}; use super::{Node, NoteCommitmentTree, Root, MERKLE_DEPTH}; /// A legacy version of [`NoteCommitmentTree`]. #[derive(Debug, Serialize, Deserialize)] #[serde(rename = "NoteCommitmentTree")] pub struct LegacyNoteCommitmentTree { inner: LegacyFrontier, cached_root: std::sync::RwLock>, } impl From for LegacyNoteCommitmentTree { fn from(nct: NoteCommitmentTree) -> Self { LegacyNoteCommitmentTree { inner: nct.inner.into(), cached_root: nct.cached_root, } } } impl From for NoteCommitmentTree { fn from(nct: LegacyNoteCommitmentTree) -> Self { NoteCommitmentTree { inner: nct.inner.into(), cached_root: nct.cached_root, } } } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename = "Frontier")] #[allow(missing_docs)] pub struct LegacyFrontier { frontier: Option>, } impl From> for Frontier { fn from(legacy_frontier: LegacyFrontier) -> Self { if let Some(legacy_frontier_data) = legacy_frontier.frontier { let mut ommers = legacy_frontier_data.ommers; let position = Position::from( u64::try_from(legacy_frontier_data.position.0) .expect("old `usize` always fits in `u64`"), ); let leaf = match legacy_frontier_data.leaf { LegacyLeaf::Left(a) => a, LegacyLeaf::Right(a, b) => { ommers.insert(0, a); b } }; Frontier::from_parts( position, leaf, ommers, ) .expect("We should be able to construct a frontier from parts given legacy frontier is not empty") } else { Frontier::empty() } } } impl From> for LegacyFrontier { fn from(frontier: Frontier) -> Self { if let Some(frontier_data) = frontier.value() { let leaf_from_frontier = *frontier_data.leaf(); let mut leaf = LegacyLeaf::Left(leaf_from_frontier); let mut ommers = frontier_data.ommers().to_vec(); let position = usize::try_from(u64::from(frontier_data.position())) .expect("new position should fit in a `usize`"); if frontier_data.position().is_odd() { let left = ommers.remove(0); leaf = LegacyLeaf::Right(left, leaf_from_frontier); } LegacyFrontier { frontier: Some(LegacyNonEmptyFrontier { position: LegacyPosition(position), leaf, ommers: ommers.to_vec(), }), } } else { LegacyFrontier { frontier: None } } } } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename = "NonEmptyFrontier")] struct LegacyNonEmptyFrontier { position: LegacyPosition, leaf: LegacyLeaf, ommers: Vec, } /// A set of leaves of a Merkle tree. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename = "Leaf")] enum LegacyLeaf { Left(A), Right(A, A), } #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] #[repr(transparent)] struct LegacyPosition(usize);