This commit is contained in:
NikVolf 2019-09-24 12:45:23 +02:00
parent ce2416623f
commit 7879b63321
4 changed files with 48 additions and 0 deletions

View File

@ -2,8 +2,10 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crate::{EntryKind, NodeData, Error, EntryLink, MAX_NODE_DATA_SIZE}; use crate::{EntryKind, NodeData, Error, EntryLink, MAX_NODE_DATA_SIZE};
/// Max serialized length of entry data.
pub const MAX_ENTRY_SIZE: usize = MAX_NODE_DATA_SIZE + 9; pub const MAX_ENTRY_SIZE: usize = MAX_NODE_DATA_SIZE + 9;
/// MMR Entry.
#[derive(Debug)] #[derive(Debug)]
pub struct Entry { pub struct Entry {
pub(crate) kind: EntryKind, pub(crate) kind: EntryKind,
@ -11,23 +13,28 @@ pub struct Entry {
} }
impl Entry { impl Entry {
/// Update siblings of the entry (to promote it from leaf to node)
pub fn update_siblings(&mut self, left: EntryLink, right: EntryLink) { pub fn update_siblings(&mut self, left: EntryLink, right: EntryLink) {
self.kind = EntryKind::Node(left, right); self.kind = EntryKind::Node(left, right);
} }
/// Returns if is this node complete (has total of 2^N leaves)
pub fn complete(&self) -> bool { pub fn complete(&self) -> bool {
let leaves = self.leaf_count(); let leaves = self.leaf_count();
leaves & (leaves - 1) == 0 leaves & (leaves - 1) == 0
} }
/// Number of leaves under this node.
pub fn leaf_count(&self) -> u64 { pub fn leaf_count(&self) -> u64 {
self.data.end_height - self.data.start_height + 1 self.data.end_height - self.data.start_height + 1
} }
/// Is this node a leaf.
pub fn is_leaf(&self) -> bool { pub fn is_leaf(&self) -> bool {
if let EntryKind::Leaf = self.kind { true } else { false } if let EntryKind::Leaf = self.kind { true } else { false }
} }
/// Left child
pub fn left(&self) -> Result<EntryLink, Error> { pub fn left(&self) -> Result<EntryLink, Error> {
match self.kind { match self.kind {
EntryKind::Leaf => { Err(Error::ExpectedNode) } EntryKind::Leaf => { Err(Error::ExpectedNode) }
@ -35,6 +42,7 @@ impl Entry {
} }
} }
/// Right child.
pub fn right(&self) -> Result<EntryLink, Error> { pub fn right(&self) -> Result<EntryLink, Error> {
match self.kind { match self.kind {
EntryKind::Leaf => { Err(Error::ExpectedNode) } EntryKind::Leaf => { Err(Error::ExpectedNode) }
@ -42,6 +50,7 @@ impl Entry {
} }
} }
/// Read from byte representation.
pub fn read<R: std::io::Read>(consensus_branch_id: u32, r: &mut R) -> std::io::Result<Self> { pub fn read<R: std::io::Read>(consensus_branch_id: u32, r: &mut R) -> std::io::Result<Self> {
let kind = { let kind = {
match r.read_u8()? { match r.read_u8()? {
@ -67,6 +76,7 @@ impl Entry {
}) })
} }
/// Write to byte representation.
pub fn write<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> { pub fn write<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
match self.kind { match self.kind {
EntryKind::Node(EntryLink::Stored(left), EntryLink::Stored(right)) => { EntryKind::Node(EntryLink::Stored(left), EntryLink::Stored(right)) => {
@ -85,6 +95,7 @@ impl Entry {
Ok(()) Ok(())
} }
/// Convert from byte representation.
pub fn from_bytes<T: AsRef<[u8]>>(consensus_branch_id: u32, buf: T) -> std::io::Result<Self> { pub fn from_bytes<T: AsRef<[u8]>>(consensus_branch_id: u32, buf: T) -> std::io::Result<Self> {
let mut cursor = std::io::Cursor::new(buf); let mut cursor = std::io::Cursor::new(buf);
Self::read(consensus_branch_id, &mut cursor) Self::read(consensus_branch_id, &mut cursor)

View File

@ -1,19 +1,25 @@
//! MMR library for Zcash //! MMR library for Zcash
//! //!
//! To be used in zebra and via FFI bindings in zcashd //! To be used in zebra and via FFI bindings in zcashd
#![warn(missing_docs)]
mod tree; mod tree;
mod node_data; mod node_data;
mod entry; mod entry;
pub use tree::Tree; pub use tree::Tree;
pub use node_data::{NodeData, MAX_NODE_DATA_SIZE}; pub use node_data::{NodeData, MAX_NODE_DATA_SIZE};
pub use entry::{Entry, MAX_ENTRY_SIZE}; pub use entry::{Entry, MAX_ENTRY_SIZE};
/// Crate-level error type
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
/// Entry expected to be presented in the tree view while it was not.
ExpectedInMemory(EntryLink), ExpectedInMemory(EntryLink),
/// Entry expected to be a node.
ExpectedNode, ExpectedNode,
/// Entry expected to be a node (specifying for which link this is not true).
ExpectedNodeForLink(EntryLink), ExpectedNodeForLink(EntryLink),
} }
@ -50,7 +56,9 @@ impl std::fmt::Display for EntryLink {
#[repr(C)] #[repr(C)]
#[derive(Debug)] #[derive(Debug)]
pub enum EntryKind { pub enum EntryKind {
/// Leaf entry.
Leaf, Leaf,
/// Node entry with children links.
Node(EntryLink, EntryLink), Node(EntryLink, EntryLink),
} }

View File

@ -2,23 +2,36 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt, ByteOrder};
use bigint::U256; use bigint::U256;
use blake2::Params as Blake2Params; use blake2::Params as Blake2Params;
/// Maximum serialized size of the node metadata.
pub const MAX_NODE_DATA_SIZE: usize = 32 + 4 + 4 + 4 + 4 + 32 + 32 + 32 + 9 + 9 + 9; // 171 pub const MAX_NODE_DATA_SIZE: usize = 32 + 4 + 4 + 4 + 4 + 32 + 32 + 32 + 9 + 9 + 9; // 171
/// Node metadata. /// Node metadata.
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct NodeData { pub struct NodeData {
/// Consensus branch id, should be provided by deserializing node.
pub consensus_branch_id: u32, pub consensus_branch_id: u32,
/// Subtree commitment - either block hash for leaves or hashsum of children for nodes.
pub subtree_commitment: [u8; 32], pub subtree_commitment: [u8; 32],
/// Start time.
pub start_time: u32, pub start_time: u32,
/// End time.
pub end_time: u32, pub end_time: u32,
/// Start target.
pub start_target: u32, pub start_target: u32,
/// End target.
pub end_target: u32, pub end_target: u32,
/// Start sapling tree root.
pub start_sapling_root: [u8; 32], pub start_sapling_root: [u8; 32],
/// End sapling tree root.
pub end_sapling_root: [u8; 32], pub end_sapling_root: [u8; 32],
/// Part of tree total work.
pub subtree_total_work: U256, pub subtree_total_work: U256,
/// Start height.
pub start_height: u64, pub start_height: u64,
/// End height
pub end_height: u64, pub end_height: u64,
/// Number of shielded transactions.
pub shielded_tx: u64, pub shielded_tx: u64,
} }
@ -42,6 +55,7 @@ fn personalization(branch_id: u32) -> [u8; 16] {
} }
impl NodeData { impl NodeData {
/// Combine two nodes metadata.
pub fn combine(left: &NodeData, right: &NodeData) -> NodeData { pub fn combine(left: &NodeData, right: &NodeData) -> NodeData {
assert_eq!(left.consensus_branch_id, right.consensus_branch_id); assert_eq!(left.consensus_branch_id, right.consensus_branch_id);
@ -106,6 +120,7 @@ impl NodeData {
Ok(result) Ok(result)
} }
/// Write to the byte representation.
pub fn write<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> { pub fn write<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
w.write_all(&self.subtree_commitment)?; w.write_all(&self.subtree_commitment)?;
w.write_u32::<LittleEndian>(self.start_time)?; w.write_u32::<LittleEndian>(self.start_time)?;
@ -125,6 +140,7 @@ impl NodeData {
Ok(()) Ok(())
} }
/// Read from the byte representation.
pub fn read<R: std::io::Read>(consensus_branch_id: u32, r: &mut R) -> std::io::Result<Self> { pub fn read<R: std::io::Read>(consensus_branch_id: u32, r: &mut R) -> std::io::Result<Self> {
let mut data = Self::default(); let mut data = Self::default();
data.consensus_branch_id = consensus_branch_id; data.consensus_branch_id = consensus_branch_id;
@ -147,6 +163,7 @@ impl NodeData {
Ok(data) Ok(data)
} }
/// Convert to byte representation.
pub fn to_bytes(&self) -> Vec<u8> { pub fn to_bytes(&self) -> Vec<u8> {
let mut buf = [0u8; MAX_NODE_DATA_SIZE]; let mut buf = [0u8; MAX_NODE_DATA_SIZE];
let pos = { let pos = {
@ -158,6 +175,7 @@ impl NodeData {
buf[0..pos].to_vec() buf[0..pos].to_vec()
} }
/// Convert from byte representation.
pub fn from_bytes<T: AsRef<[u8]>>(consensus_branch_id: u32, buf: T) -> std::io::Result<Self> { pub fn from_bytes<T: AsRef<[u8]>>(consensus_branch_id: u32, buf: T) -> std::io::Result<Self> {
let mut cursor = std::io::Cursor::new(buf); let mut cursor = std::io::Cursor::new(buf);
Self::read(consensus_branch_id, &mut cursor) Self::read(consensus_branch_id, &mut cursor)

View File

@ -24,6 +24,7 @@ pub struct Tree {
} }
impl Tree { impl Tree {
/// Resolve link originated from this tree
pub fn resolve_link(&self, link: EntryLink) -> Result<IndexedNode, Error> { pub fn resolve_link(&self, link: EntryLink) -> Result<IndexedNode, Error> {
match link { match link {
EntryLink::Generated(index) => { EntryLink::Generated(index) => {
@ -80,6 +81,12 @@ impl Tree {
} }
} }
/// New view into the the tree array representation
///
/// `length` is total length of the array representation
/// `peaks` is peaks of the mmr tree
/// `extra` is some extra nodes that calculated to be required during next one or more
/// operations on the tree.
pub fn new( pub fn new(
length: u32, length: u32,
peaks: Vec<(u32, Entry)>, peaks: Vec<(u32, Entry)>,
@ -267,6 +274,7 @@ impl Tree {
} }
} }
/// Reference to the node with link attached.
#[derive(Debug)] #[derive(Debug)]
pub struct IndexedNode<'a> { pub struct IndexedNode<'a> {
node: &'a Entry, node: &'a Entry,
@ -283,14 +291,17 @@ impl<'a> IndexedNode<'a> {
self.node.right().map_err(|e| e.augment(self.link)) self.node.right().map_err(|e| e.augment(self.link))
} }
/// Reference to the entry struct.
pub fn node(&self) -> &Entry { pub fn node(&self) -> &Entry {
self.node self.node
} }
/// Reference to the entry metadata.
pub fn data(&self) -> &NodeData { pub fn data(&self) -> &NodeData {
&self.node.data &self.node.data
} }
/// Actual link by what this node was resolved.
pub fn link(&self) -> EntryLink { pub fn link(&self) -> EntryLink {
self.link self.link
} }