From 19a7caa552917ae3b7876b8d419272f82ae6c701 Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Tue, 20 Sep 2022 13:22:16 -0300 Subject: [PATCH 01/18] working addition --- src/accumulator/mod.rs | 3 + src/accumulator/pollard.rs | 625 +++++++------------------------------ 2 files changed, 111 insertions(+), 517 deletions(-) diff --git a/src/accumulator/mod.rs b/src/accumulator/mod.rs index 0c73891..439c835 100644 --- a/src/accumulator/mod.rs +++ b/src/accumulator/mod.rs @@ -1,3 +1,6 @@ +#![allow(dead_code)] + +pub mod pollard; pub mod proof; pub mod stump; pub mod types; diff --git a/src/accumulator/pollard.rs b/src/accumulator/pollard.rs index 7a96e3c..6506155 100644 --- a/src/accumulator/pollard.rs +++ b/src/accumulator/pollard.rs @@ -1,561 +1,152 @@ -// Rustreexo - -use std::collections::HashMap; -use std::mem; - -use super::{ - types, - util, - transform +use bitcoin_hashes::sha256::Hash; +use std::fmt::Debug; +#[allow(unused)] +use std::{ + cell::{Cell, RefCell}, + mem::{self, swap}, + rc::{Rc, Weak}, }; -use bitcoin::hashes::{sha256, Hash, HashEngine}; +use super::types; +type Node = Rc>; -/// Pollard is the sparse representation of the utreexo forest -/// It is a collection of multitude of trees with leaves that are -/// power of two. -/// -/// However, the allocated tree is always a power of two. The nodes -/// that are not necessary are kept as empty nodes. -/// -/// Its structure resembles of that of a binary tree, except that -/// the pointers point to aunts - nieces, not parents - children #[derive(Clone)] -pub struct Pollard { - /// Roots are the top-most nodes of the tree - /// There may be multiple roots as Utreexo is organized as a - /// collection of perfect trees. - pub roots: Option>, - - /// Total number of leaves (nodes on the bottom row) in the Pollard - pub num_leaves: u64, +pub struct PolNode { + data: Hash, + aunt: Option, + l_niece: RefCell>, + r_niece: RefCell>, } -impl Pollard { - /// Returns a new pollard - pub fn new() -> Pollard { - Pollard{roots: None, num_leaves:0, } - } - - /// Modify changes the Utreexo tree state given the utxos and stxos - /// stxos are denoted by their value - pub fn modify(&mut self, utxos: Vec, stxos: Vec) { - // Order matters here. Adding then removing will result in a different - // tree vs deleting then adding. For ease of use, only modify is visible - // for external crates. This is consensus critical. - Pollard::remove(self, stxos); - Pollard::add(self, utxos); +impl Debug for PolNode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.data) } +} - pub fn add(&mut self, adds: Vec) { - // General algo goes: - // 1 make a new node & assign data (no nieces; at bottom) - // 2 if this node is on a row where there's already a root, - // then swap nieces with that root, hash the two datas, and build a new - // node 1 higher pointing to them. - // goto 2. - - for add in adds { - //if add.remember { - // // TODO Should cache the add data - //} - Pollard::add_single(self, add.hash, false); +impl PolNode { + fn new( + data: Hash, + aunt: Option, + l_niece: Option, + r_niece: Option, + ) -> PolNode { + PolNode { + data, + aunt, + l_niece: RefCell::new(l_niece), + r_niece: RefCell::new(r_niece), } } - // AddSingle adds a single given utxo to the tree - // TODO activate caching (use remember). This isn't done in the - // Go repo either yet - fn add_single(&mut self, utxo: sha256::Hash, remember: bool) { - - // recurse from the right side of the tree until we hit a tree with no root - // Destorys roots along the way - fn add(pol: &mut Pollard, node: &mut PolNode, num_leaves: u64) -> PolNode{ - let mut return_node = node.clone(); - - if num_leaves & 1 == 1 { - match &mut pol.roots { - // if num_leaves & 1 is true, pol.roots can't be none - None => (), - Some(roots) => { - //let before_len = roots.clone().len(); - let mut left_root = roots.pop().unwrap(); - //assert_ne!(roots.clone().len(), before_len); // sanity - - mem::swap(&mut left_root.l_niece, &mut node.l_niece); - mem::swap(&mut left_root.r_niece, &mut node.r_niece); - - let n_hash = types::parent_hash(&left_root.data.clone(), &node.data.clone()); - let new_node = &mut PolNode { - data: n_hash, - l_niece: Some(Box::new(left_root)), - r_niece: Some(Box::new(node.clone())), - }; - - //new_node.prune(); - return_node = add(pol, new_node, num_leaves>>1); - }, - } - } - - return return_node; - } - - // init node. If the Pollard is perfect (meaning only one root), this will become a - // new root - let mut node = &mut PolNode { - data: utxo, - l_niece: None, - r_niece: None, + /// Creates a new [PolNode] and returns it as [Node], i.e [Rc>] + /// If you need a pure PolNode, use `new` instead. + fn new_node( + data: Hash, + aunt: Option, + l_niece: Option, + r_niece: Option, + ) -> Node { + let node = PolNode { + data, + aunt, + l_niece: RefCell::new(l_niece), + r_niece: RefCell::new(r_niece), }; - let add_node = add(self, &mut node, self.num_leaves); - - //match add_node.l_niece.clone() { - // None => { - // println!("NO left niceses"); - // }, - // Some(node) => { - // println!("YES"); - // println!("{:?}", node.data); - // } - //} - //match add_node.r_niece.clone() { - // None => { - // println!("NO right niceses"); - // }, - // Some(node) => { - // println!("YES"); - // println!("{:?}", node.data); - // } - //} - - - match &mut self.roots { - None => { - self.roots = Some(vec![add_node; 1]); - }, - Some(root) => { - root.push(add_node) - } - } - - // increment leaf count - self.num_leaves += 1; + node.as_rc() } - - fn remove(&mut self, mut dels: Vec) { - // if there is nothing to delete, return - if dels.len() == 0 { - return - } - - let pollard_rows = util::tree_rows(self.num_leaves); - - let leaves_after_del = self.num_leaves - dels.len() as u64; - - // get all the swaps, then apply them all - let swap_rows = transform::transform(dels, self.num_leaves, pollard_rows); - - println!("{:?}", swap_rows); - - //let hash_dirt: Vec; - //let next_hash_dirt: Vec; - //let prev_hash: u64; - - //for row in 0..pollard_rows { - //} - + /// Returns the current node as an Reference Counted Container "[Rc]". This is how + /// nodes are stored inside the tree. + fn as_rc(self) -> Node { + Rc::new(Box::new(self)) } - fn grab_pos(&mut self, pos: u64) -> Result<(PolNode, PolNode, HashableNode), String> { - // grabs nieces until it hits 1 above bottom. The nodes returned - // here are from row 1, not row 0 - fn grab_niece(mut p_node: PolNode, mut p_node_sib: PolNode, branch_len: u8, bits: u64) -> Option<(PolNode, PolNode, u8)> { - - // calculate the left root. 0 means left_niece, 1 means right_niece - let lr = bits>>branch_len & 1; - - // 0 is the left, 1 is the right as - if lr == 0 { - match &mut p_node.l_niece { - None => return None, - Some(niece) => { - p_node = *niece.clone() - } - } - - match &mut p_node.r_niece { - None => return None, - Some(niece) => { - p_node_sib = *niece.clone() - } - } - } else { - match &mut p_node.r_niece { - None => return None, - Some(niece) => { - p_node = *niece.clone() - } - } - - match &mut p_node.l_niece { - None => return None, - Some(niece) => { - p_node_sib = *niece.clone() - } - } - } - - // Check if next recurse is gonna be at the bottom - // Recurse as long as we're not at bottom - if branch_len >= 1 { - grab_niece(p_node.clone(), p_node_sib.clone(), branch_len-1, bits); - } - - return Some((p_node, p_node_sib, branch_len)); - } - - // Grab the tree that the position is at - let (tree, branch_len, bits) = util::detect_offset(pos, self.num_leaves); - - match &self.roots { - None => { - return Err("grab_pos called on an empty pollard".to_string()); - }, - Some(root) => { - if tree as usize >= root.len() { - return Err("Pos asked for out of bounds for the current pollard".to_string()); - } - - let node = root[tree as usize].clone(); - let node_sib = root[tree as usize].clone(); - - match grab_niece(node, node_sib, branch_len, bits) { - None => { - return Err("Pos asked for out of bounds for the current pollard".to_string()); - }, - Some((mut p_node, mut p_node_sib, branch_len)) => { - let hnode = HashableNode { - dest: Some(Box::new(p_node_sib.clone() )), - sib: Some(Box::new(p_node.clone() )), - position: pos - }; - - //let lr = bits>>branch_len & 1; - //let lr_sib = lr ^ 1; - - // 0 is the left, 1 is the right as - //if lr_sib == 0 { - // match &mut p_node.l_niece { - // None => (), - // Some(niece) => { - // p_node = *niece.clone() - // } - // } - - // match &mut p_node.r_niece { - // None => (), - // Some(niece) => { - // p_node_sib = *niece.clone() - // } - // } - //} else { - // match &mut p_node.r_niece { - // None => (), - // Some(niece) => { - // p_node = *niece.clone() - // } - // } - - // match &mut p_node.l_niece { - // None => (), - // Some(niece) => { - // p_node_sib = *niece.clone() - // } - // } - //} - - return Ok((p_node, p_node_sib, hnode)); - } - } - } + fn set_aunt(&self, aunt: Option) -> PolNode { + PolNode { + data: self.data, + aunt, + l_niece: self.clone().l_niece, + r_niece: self.clone().r_niece, } } -} - -/// PolNode represents a node in the utreexo pollard tree. It points -/// to its nieces -#[derive(Clone)] -pub struct PolNode { - // The hash - pub data: sha256::Hash, - - pub l_niece: Option>, - pub r_niece: Option>, -} - -impl PolNode { - /// aunt_op returns the hash of a nodes' nieces. Errors if called on nieces - /// that are nil. - fn aunt_op(&self) -> sha256::Hash { - types::parent_hash(&self.l_niece.as_ref().unwrap().data, &self.r_niece.as_ref().unwrap().data) - } - - fn dead_end(&self) -> bool { - self.l_niece.is_none() && self.r_niece.is_none() + fn set_nieces(&self, l_niece: Option, r_niece: Option) { + self.l_niece.replace(l_niece); + self.r_niece.replace(r_niece); } - - fn chop(&mut self) { - self.l_niece = None; - self.r_niece = None; - } - - fn prune(&mut self) { - match &mut self.l_niece { - None => (), - Some(node) => { - if node.dead_end() { - node.chop() - } - } - } - - match &mut self.r_niece { - None => (), - Some(node) => { - if node.dead_end() { - node.chop() - } - } - } + fn chop(&self) { + self.l_niece.replace(None); + self.r_niece.replace(None); } } - -//// hashableNode is the data needed to perform a hash -//pub struct HashableNode<'a> { -// sib: &'a PolNode, -// dest: &'a PolNode, -// position: u64 // doesn't really need to be there, but convenient for debugging -//} - -// hashableNode is the data needed to perform a hash -pub struct HashableNode { - pub sib: Option>, - pub dest: Option>, - pub position: u64 // doesn't really need to be there, but convenient for debugging -} - -// polSwap swaps the contents of two polNodes & leaves pointers -fn pol_swap<'a, 'b>(mut a: &'a mut PolNode, mut asib: &'b mut PolNode, mut b: &'a mut PolNode, mut bsib: &'b mut PolNode) { - mem::swap(&mut a, &mut b); - mem::swap(&mut asib,&mut bsib); +#[derive(Clone)] +pub struct Pollard { + leaves: usize, + roots: Vec, } +impl Pollard { + pub fn modify(&self, utxos: Vec, _stxos: Vec) { + let roots = self.roots.clone(); + let _roots = Pollard::add(roots, utxos, self.leaves); + } -#[cfg(test)] -mod tests { - fn pollard_add_five() { - use bitcoin::hashes::{sha256, Hash, HashEngine}; - use super::types; - - let mut pollard = super::Pollard::new(); - - for i in 1..5 { - // boilerplate hashgen - // TODO maybe there's a better way? - let mut engine = bitcoin::hashes::sha256::Hash::engine(); - let num: &[u8; 1] = &[i as u8]; - engine.input(num); - let h = sha256::Hash::from_engine(engine); - let leaf = types::Leaf{hash: h, remember: false}; - - // add one leaf - &pollard.modify(vec![leaf], vec![]); - - match i { - 1 => { - check_count(pollard.num_leaves, pollard.roots.clone().unwrap().len()); - assert_eq!(pollard.roots.clone().unwrap()[0].data, h); - } - - 2 => { - check_count(pollard.num_leaves, pollard.roots.clone().unwrap().len()); - assert_ne!(pollard.roots.clone().unwrap()[0].data, h); - } - - 3 => { - check_count(pollard.num_leaves, pollard.roots.clone().unwrap().len()); - assert_eq!(pollard.roots.clone().unwrap()[1].data, h); - } - - 4 => { - check_count(pollard.num_leaves, pollard.roots.clone().unwrap().len()); - assert_ne!(pollard.roots.clone().unwrap()[0].data, h); - } - - 5 => { - check_count(pollard.num_leaves, pollard.roots.clone().unwrap().len()); - assert_eq!(pollard.roots.clone().unwrap()[1].data, h); - } - - _ => () - } + fn add(mut roots: Vec, utxos: Vec, mut num_leaves: usize) -> Vec { + for utxo in utxos { + roots = Pollard::add_single(roots, utxo, num_leaves); + num_leaves += 1; } - } - // A Utreexo tree will always have a collection of trees that are a perfect power - // of two. The popcount of leaves should always equal the length of the root - fn check_count(num_leaves: u64, root_len: usize) { - let root_count = num_leaves.count_ones() as usize; - assert_eq!(root_count, root_len); + roots } - fn check_root() { - } + fn add_single(mut roots: Vec, node: Hash, mut num_leaves: usize) -> Vec { + let mut node = PolNode::new(node, None, None, None).as_rc(); - fn test_pol_del() { - use bitcoin::hashes::{sha256, Hash, HashEngine}; - use super::types; + while num_leaves & 1 == 1 { + // If num_leaves & 1 == 1, roots cannot be None + let left_root = roots.pop().unwrap(); - let mut pol = super::Pollard::new(); + left_root.l_niece.swap(&node.l_niece); + left_root.r_niece.swap(&node.r_niece); - for i in 0..5 { - // boilerplate hashgen - // TODO maybe there's a better way? - let mut engine = bitcoin::hashes::sha256::Hash::engine(); - let num: &[u8; 1] = &[i as u8]; - engine.input(num); - let h = sha256::Hash::from_engine(engine); - println!("for i {}: {:?}", i, &h); - let leaf = types::Leaf{hash: h, remember: false}; + let n_hash = types::parent_hash(&left_root.data.clone(), &node.data.clone()); + let new_node = PolNode::new_node(n_hash, None, None, None); - // add one leaf - &pol.modify(vec![leaf], vec![]); - } + let left_niece = left_root.set_aunt(Some(new_node.clone())).as_rc(); + let right_niece = node.set_aunt(Some(new_node.clone())).as_rc(); - for i in 0..4 { - let node = pol.grab_pos(i); - match node { - Err(e) => (panic!("no pollard node found")), - Ok((node, node_sib, hn)) => { - let mut engine = bitcoin::hashes::sha256::Hash::engine(); - let num: &[u8; 1] = &[i as u8]; - engine.input(num); - let h = sha256::Hash::from_engine(engine); + new_node.set_nieces(Some(left_niece), Some(right_niece)); + node = new_node; - println!("fetched node hash {}: {:?}", i, &node.l_niece.unwrap().data); - println!("fetched node_sib hash: {:?}", &node_sib.data); - println!("calculated 0 hash: {:?}", h); - } - } - } - let node = pol.grab_pos(14); - match node { - Err(e) => (panic!("no pollard node found")), - Ok((node, node_sib, hn)) => { - println!("fetched node hash {}: {:?}", 8, &node.l_niece.unwrap().data); - } + // left_root.aunt = new_node.clone(); + num_leaves >>= 1; } - - //pol.modify(vec![], vec![0]); + roots.push(node); + roots } +} - #[test] - fn test_pol_add() { - use bitcoin::hashes::{sha256, Hash, HashEngine}; - use super::types; - - let mut pol = super::Pollard::new(); - - for i in 0..50000 { - let mut engine = bitcoin::hashes::sha256::Hash::engine(); - let num: &[u8; 1] = &[(i % 255) as u8]; - engine.input(num); - let h = sha256::Hash::from_engine(engine); - let leaf = types::Leaf{hash: h, remember: false}; - - pol.modify(vec![leaf], vec![]); - - if i % 10000 == 0 { - check_count(pol.num_leaves, pol.roots.clone().unwrap().len()); - } - - // Check if power of two - //if (i != 0) && ((i & (i - 1)) == 0) { +mod test { + use super::{PolNode, Pollard}; + use bitcoin_hashes::{sha256::Hash as Data, Hash, HashEngine}; + fn hash_from_u8(value: u8) -> Data { + let mut engine = Data::engine(); - //} - } + engine.input(&[value]); - check_count(pol.num_leaves, pol.roots.clone().unwrap().len()); - - pollard_add_five(); + Hash::from_engine(engine) } #[test] - fn test_pol_swap() { - use bitcoin::hashes::{sha256, Hash, HashEngine}; - use std::mem; - - let mut engine = bitcoin::hashes::sha256::Hash::engine(); - let num: &[u8; 1] = &[1 as u8]; - engine.input(num); - let h1 = sha256::Hash::from_engine(engine); - let h1_copy = h1.clone(); - - let mut engine1 = bitcoin::hashes::sha256::Hash::engine(); - let num2: &[u8; 1] = &[2 as u8]; - engine1.input(num2); - let h2 = sha256::Hash::from_engine(engine1); - let h2_copy = h2.clone(); - - let mut engine2 = bitcoin::hashes::sha256::Hash::engine(); - let num3: &[u8; 1] = &[3 as u8]; - engine2.input(num3); - let h3 = sha256::Hash::from_engine(engine2); - let h3_copy = h3.clone(); - - let mut engine3 = bitcoin::hashes::sha256::Hash::engine(); - let num4: &[u8; 1] = &[3 as u8]; - engine3.input(num4); - let h4 = sha256::Hash::from_engine(engine3); - let h4_copy = h4.clone(); - - //let mut a = super::PolNode{ - // data: h1, - // nieces: [None, None], - //}; - - //assert_eq!(a.data, h1_copy); // sanity - - //let mut b = super::PolNode{ - // data: h2, - // nieces: [None, None], - //}; - - //assert_eq!(b.data, h2_copy); // sanity - - //let mut asib = super::PolNode{ - // data: h3, - // nieces: [None, None], - //}; - - //let mut bsib = super::PolNode{ - // data: h4, - // nieces: [None, None], - //}; - - //super::pol_swap(&mut a, &mut b, &mut asib, &mut bsib); - - //assert_eq!(a.data, h1_copy); - //assert_eq!(b.data, h2_copy); - - //assert_eq!(asib.data, h3_copy); - //assert_eq!(bsib.data, h4_copy); - - //mem::swap(&mut a, &mut b); - - //assert_eq!(a.data, h2_copy); - //assert_eq!(b.data, h1_copy); + fn test_add() { + let values = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let hashes = values.into_iter().map(|val| hash_from_u8(val)).collect(); + + let roots = Pollard::add(vec![], hashes, 0); + assert_eq!( + "b151a956139bb821d4effa34ea95c17560e0135d1e4661fc23cedc3af49dac42", + roots[0].data.to_string().as_str(), + ); } } From 0580b01d051b357b458ae968f05e40adcef63f58 Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Tue, 20 Sep 2022 13:23:36 -0300 Subject: [PATCH 02/18] drop box --- src/accumulator/pollard.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/accumulator/pollard.rs b/src/accumulator/pollard.rs index 6506155..83de7d4 100644 --- a/src/accumulator/pollard.rs +++ b/src/accumulator/pollard.rs @@ -8,7 +8,7 @@ use std::{ }; use super::types; -type Node = Rc>; +type Node = Rc; #[derive(Clone)] pub struct PolNode { @@ -57,9 +57,9 @@ impl PolNode { node.as_rc() } /// Returns the current node as an Reference Counted Container "[Rc]". This is how - /// nodes are stored inside the tree. + /// nodes are stored inside the tree. fn as_rc(self) -> Node { - Rc::new(Box::new(self)) + Rc::new(self) } fn set_aunt(&self, aunt: Option) -> PolNode { From ae764abb7c1002598a42156b5efe3d8e67eaf8fa Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Tue, 20 Sep 2022 23:11:22 -0300 Subject: [PATCH 03/18] Basic structures for a Pollard --- src/accumulator/pollard.rs | 283 +++++++++++++++++++++++++++++++++++-- src/accumulator/util.rs | 17 +++ 2 files changed, 288 insertions(+), 12 deletions(-) diff --git a/src/accumulator/pollard.rs b/src/accumulator/pollard.rs index 83de7d4..f8680d8 100644 --- a/src/accumulator/pollard.rs +++ b/src/accumulator/pollard.rs @@ -1,5 +1,8 @@ -use bitcoin_hashes::sha256::Hash; -use std::fmt::Debug; +use bitcoin_hashes::{hex::ToHex, sha256::Hash}; +use std::{ + borrow::Borrow, + fmt::{write, Debug}, +}; #[allow(unused)] use std::{ cell::{Cell, RefCell}, @@ -7,10 +10,15 @@ use std::{ rc::{Rc, Weak}, }; -use super::types; +use crate::accumulator::util::detect_row_hashes; + +use super::{ + types::{self, parent_hash}, + util::{detect_row, is_left_niece, next_pow2, num_roots, parent, tree_rows}, +}; type Node = Rc; -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq, Default)] pub struct PolNode { data: Hash, aunt: Option, @@ -20,7 +28,16 @@ pub struct PolNode { impl Debug for PolNode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.data) + write!(f, "\n{}\n|------\\\n", &self.data[0..2].to_hex())?; + let (left_children, right_children) = self.get_children(); + + if let Some(left_children) = left_children { + write!(f, "{} ", &left_children.data[0..2].to_hex())?; + } + if let Some(right_children) = right_children { + write!(f, "{}\n", &right_children.data[0..2].to_hex())?; + } + Ok(()) } } @@ -38,7 +55,86 @@ impl PolNode { r_niece: RefCell::new(r_niece), } } + fn get_children(&self) -> (Option, Option) { + let parent = self.get_parent(); + if let Some(parent) = parent { + // I'm the left children, so my sibling is the right one + if parent.get_l_niece().unwrap_or_default().as_ref().eq(self) { + if let Some(sibling) = parent.get_r_niece() { + return (sibling.get_l_niece(), sibling.get_r_niece()); + } + } else { + if let Some(sibling) = parent.get_l_niece() { + return (sibling.get_l_niece(), sibling.get_r_niece()); + } + } + } else { + return (self.get_l_niece(), self.get_r_niece()); + } + // This means I'm a root, + (None, None) + } + fn get_parent(&self) -> Option { + //I'm a root, so no parent + if let None = self.aunt { + return None; + } + + if let Some(aunt) = self.get_aunt() { + // If aunt also has an aunt, then I take his sibling, as he's my parent + if let Some(grandpa) = aunt.get_aunt() { + // If my grandparent's left niece is my aunt, then my parent is the right niece + if grandpa.get_l_niece().eq(&Some(aunt)) { + return grandpa.get_r_niece(); + } else { + // Here is the opposite + return grandpa.get_l_niece(); + } + } else { + // If my aunt has no aunt, it means he's a root, and roots points to it's children + return Some(aunt); + } + } + + None + } + /// This method is a little evolved, it takes an [Rc] to our aunt, then finds out + /// whether we are left or right nieces. With that info, we replace the corresponding + /// aunt's niece with our new self. + fn set_self_hash(&self, new: Node) { + if let Some(aunt) = self.get_aunt() { + if *aunt.get_l_niece().unwrap_or_default() == *self { + aunt.l_niece.replace(Some(new)); + } else { + aunt.r_niece.replace(Some(new)); + } + } + } + fn recompute_parent_hash(&self) -> Result { + if let (Some(l_niece), Some(r_niece)) = (self.get_l_niece(), self.get_r_niece()) { + let new_parent_hash = parent_hash(&l_niece.data, &r_niece.data); + let aunt = self.get_aunt(); + let new_node = + PolNode::new(new_parent_hash, aunt.clone(), Some(l_niece), Some(r_niece)); + if let Some(aunt) = aunt { + self.set_self_hash(new_node.as_rc()); + return aunt.recompute_parent_hash(); + } else { + return Ok(new_node.as_rc()); + } + } + Ok(self.clone().as_rc()) + } + fn get_aunt(&self) -> Option { + self.aunt.clone() + } + fn get_l_niece(&self) -> Option { + self.l_niece.borrow().clone() + } + fn get_r_niece(&self) -> Option { + self.r_niece.borrow().clone() + } /// Creates a new [PolNode] and returns it as [Node], i.e [Rc>] /// If you need a pure PolNode, use `new` instead. fn new_node( @@ -70,6 +166,7 @@ impl PolNode { r_niece: self.clone().r_niece, } } + fn set_nieces(&self, l_niece: Option, r_niece: Option) { self.l_niece.replace(l_niece); self.r_niece.replace(r_niece); @@ -81,16 +178,95 @@ impl PolNode { } #[derive(Clone)] pub struct Pollard { - leaves: usize, + leaves: u64, roots: Vec, } + impl Pollard { - pub fn modify(&self, utxos: Vec, _stxos: Vec) { + pub fn new() -> Pollard { + Pollard { + leaves: 0, + roots: vec![], + } + } + pub fn modify(&self, utxos: Vec, _stxos: Vec) -> Pollard { + let utxos_after_deletion = self.leaves + utxos.len() as u64; let roots = self.roots.clone(); - let _roots = Pollard::add(roots, utxos, self.leaves); + let roots = Pollard::add(roots, utxos, self.leaves); + + Pollard { + leaves: utxos_after_deletion, + roots, + } } - fn add(mut roots: Vec, utxos: Vec, mut num_leaves: usize) -> Vec { + /// Deletes a single node from a Pollard. The algorithm works as follows: + /// Grab a node, it's sibling and it's parent. + /// (i) if I'm deleting the node, but not it's sibling, then the sibling takes the parent's position + /// (ii) if both are being deleted, then the parent is also deleted + /// (iii) if my parent is a root, then the node's sibling becomes a root + /// (iv) if I'm a root myself, then this root becomes an empty root (PolNode::Default). + fn delete_single(&self, pos: u64) -> Result, String> { + // Grab the node we'll move up + let (from_node, _, to_node) = self.grab_node(pos)?; + // What is the position of our parent? + let parent_pos = parent(pos, tree_rows(self.leaves)); + // In what subtree we are? + let (tree, _, _) = super::util::detect_offset(pos, self.leaves); + let mut roots = self.roots.clone(); + + // If the position I'm moving to has an aunt, I'm not becoming a root. + if let Some(new_aunt) = &to_node.aunt { + from_node.chop(); + println!("To node:{:?}", new_aunt); + + let new_node = from_node.set_aunt(Some(new_aunt.clone())); + if is_left_niece(parent_pos) { + new_aunt.l_niece.replace(Some(new_node.as_rc())); + } else { + new_aunt.r_niece.replace(Some(new_node.as_rc())); + } + + roots[tree as usize] = roots[tree as usize].recompute_parent_hash()?; + } else { + // If it does not have a aunt, then I'm becoming a root + let from_node = from_node.set_aunt(None).as_rc(); + roots[tree as usize] = from_node; + } + + Ok(roots) + } + fn grab_node(&self, pos: u64) -> Result<(Node, Node, Node), String> { + let (tree, branch_len, bits) = super::util::detect_offset(pos, self.leaves); + let mut n = Some(self.roots[tree as usize].clone()); + let mut sibling = Some(self.roots[tree as usize].clone()); + let mut parent = sibling.clone(); + + for row in (0..(branch_len)).rev() { + // Parent is the sibling of the current node as each of the + // nodes point to their nieces. + parent = sibling; + + // Figure out which node we need to follow. + let niece_pos = ((bits >> row) & 1) as u8; + if let Some(node) = n { + if is_left_niece(niece_pos as u64) { + n = node.l_niece.clone().into_inner(); + sibling = node.r_niece.clone().into_inner(); + } else { + n = node.r_niece.clone().into_inner(); + sibling = node.l_niece.clone().into_inner(); + } + } else { + sibling = None; + } + } + if let (Some(node), Some(sibling), Some(parent)) = (n, sibling, parent) { + return Ok((node, sibling, parent)); + } + Err("node not found".to_string()) + } + fn add(mut roots: Vec, utxos: Vec, mut num_leaves: u64) -> Vec { for utxo in utxos { roots = Pollard::add_single(roots, utxo, num_leaves); num_leaves += 1; @@ -99,12 +275,14 @@ impl Pollard { roots } - fn add_single(mut roots: Vec, node: Hash, mut num_leaves: usize) -> Vec { + fn add_single(mut roots: Vec, node: Hash, mut num_leaves: u64) -> Vec { let mut node = PolNode::new(node, None, None, None).as_rc(); while num_leaves & 1 == 1 { // If num_leaves & 1 == 1, roots cannot be None - let left_root = roots.pop().unwrap(); + let left_root = roots + .pop() + .expect("add_single: num_leaves & 1 == 1 and no roots?"); left_root.l_niece.swap(&node.l_niece); left_root.r_niece.swap(&node.r_niece); @@ -127,6 +305,36 @@ impl Pollard { } } +impl std::fmt::Display for Pollard { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let trees = num_roots(self.leaves); + let rows = tree_rows(self.leaves) + 1; + let total_nodes = (2 as u64).pow(rows.into()) - 2; + let mut pos = total_nodes - 1; + for row in (0..=(rows - 1)).rev() { + while detect_row(pos, rows - 1) == row { + if let Ok((_, node, _)) = self.grab_node(pos.into()) { + write!(f, "{:?}", &node.data[0..2].to_hex())?; + } else { + write!(f, "--")?; + } + if pos == 0 { + break; + } + pos -= 1; + } + write!(f, "\n")?; + } + // for row in (0..=rows).rev() { + // for node in start_node..end_node { + + // } + + // } + + Ok(()) + } +} mod test { use super::{PolNode, Pollard}; use bitcoin_hashes::{sha256::Hash as Data, Hash, HashEngine}; @@ -137,16 +345,67 @@ mod test { Hash::from_engine(engine) } + #[test] + fn test_grab_aunt() { + let values = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]; + let hashes = values.into_iter().map(|val| hash_from_u8(val)).collect(); + + let p = Pollard::new().modify(hashes, vec![]); + let node = p.grab_node(13); + assert!(node.is_ok()); + + let hash = node.unwrap().1.data; + assert_eq!( + hash.to_string().as_str(), + "9d1e0e2d9459d06523ad13e28a4093c2316baafe7aec5b25f30eba2e113599c4" + ); + } #[test] - fn test_add() { + fn test_recompute_hashes() { + let values = vec![0, 1, 2, 3]; + let hashes = values.into_iter().map(|val| hash_from_u8(val)).collect(); + + let p = Pollard::new().modify(hashes, vec![]); + let node = p.grab_node(0); + if let Ok((_, _, parent)) = node { + let res = parent.recompute_parent_hash(); + println!("{:?}", res); + } + } + + #[test] + fn test_delete() { let values = vec![0, 1, 2, 3, 4, 5, 6, 7]; let hashes = values.into_iter().map(|val| hash_from_u8(val)).collect(); + let p = Pollard::new().modify(hashes, vec![]); + let roots = p.delete_single(0).expect("msg"); + + println!("{:?}", roots); + } + #[test] + fn test_add() { + let values = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]; + let hashes = values.into_iter().map(|val| hash_from_u8(val)).collect(); + let roots = Pollard::add(vec![], hashes, 0); + assert_eq!( "b151a956139bb821d4effa34ea95c17560e0135d1e4661fc23cedc3af49dac42", roots[0].data.to_string().as_str(), ); + assert_eq!( + "9c053db406c1a077112189469a3aca0573d3481bef09fa3d2eda3304d7d44be8", + roots[1].data.to_string().as_str(), + ); + assert_eq!( + "55d0a0ef8f5c25a9da266b36c0c5f4b31008ece82df2512c8966bddcc27a66a0", + roots[2].data.to_string().as_str(), + ); + assert_eq!( + "4d7b3ef7300acf70c892d8327db8272f54434adbc61a4e130a563cb59a0d0f47", + roots[3].data.to_string().as_str(), + ); } } diff --git a/src/accumulator/util.rs b/src/accumulator/util.rs index 6a23beb..6838fce 100644 --- a/src/accumulator/util.rs +++ b/src/accumulator/util.rs @@ -327,6 +327,23 @@ pub fn is_right_sibling(node: u64, next: u64) -> bool { node | 1 == next } +// get_roots_reverse gives you the positions of the tree roots, given a number of leaves. + +// next_pow2 returns the next power of 2 +// ex: n = 9 will return 16. n = 33 will return 64 +pub fn next_pow2(n: u64) -> u64 { + if n == 0 { + return 1; + } + let mut t = n - 1; + t |= t >> 1; + t |= t >> 2; + t |= t >> 4; + t |= t >> 8; + t |= t >> 16; + t |= t >> 32; + t + 1 +} /// Returns whether a and b are sibling or not fn is_sibling(a: u64, b: u64) -> bool { a ^ 1 == b From f5330177dc5c9a57761cf87093158ad630af7dda Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Wed, 21 Sep 2022 14:43:35 -0300 Subject: [PATCH 04/18] Basic structure for a Pollard 2/3 --- src/accumulator/pollard.rs | 291 +++++++++++++++++++++++++++++++------ 1 file changed, 249 insertions(+), 42 deletions(-) diff --git a/src/accumulator/pollard.rs b/src/accumulator/pollard.rs index f8680d8..6ea36d9 100644 --- a/src/accumulator/pollard.rs +++ b/src/accumulator/pollard.rs @@ -14,14 +14,14 @@ use crate::accumulator::util::detect_row_hashes; use super::{ types::{self, parent_hash}, - util::{detect_row, is_left_niece, next_pow2, num_roots, parent, tree_rows}, + util::{detect_row, is_left_niece, is_root_position, next_pow2, num_roots, parent, tree_rows}, }; type Node = Rc; #[derive(Clone, PartialEq, Eq, Default)] pub struct PolNode { data: Hash, - aunt: Option, + aunt: RefCell>, l_niece: RefCell>, r_niece: RefCell>, } @@ -50,11 +50,36 @@ impl PolNode { ) -> PolNode { PolNode { data, - aunt, + aunt: RefCell::new(aunt), l_niece: RefCell::new(l_niece), r_niece: RefCell::new(r_niece), } } + fn update_aunt(&self, aunt: Option, root: bool) { + let aunt = if root { + self.set_aunt(None); + aunt + } else { + if PolNode::is_l_niece(&aunt.clone().unwrap(), &self.clone().as_rc()) { + aunt.expect("msg").get_l_niece() + } else { + aunt.expect("msg").get_r_niece() + } + }; + if let Some(l_niece) = self.get_l_niece() { + l_niece.update_aunt(aunt.clone(), false); + } + if let Some(r_niece) = self.get_l_niece() { + r_niece.update_aunt(aunt, false); + } + } + fn is_l_niece(aunt: &Node, n: &Node) -> bool { + if aunt.get_l_niece().as_ref().eq(&Some(n)) { + true + } else { + false + } + } fn get_children(&self) -> (Option, Option) { let parent = self.get_parent(); if let Some(parent) = parent { @@ -74,9 +99,21 @@ impl PolNode { // This means I'm a root, (None, None) } + fn get_sibling(&self) -> Option { + let node = self.get_parent(); + if let Some(parent) = node { + if parent.get_l_niece().unwrap_or_default().as_ref().eq(self) { + return parent.get_r_niece(); + } else { + return parent.get_l_niece(); + } + } + + None + } fn get_parent(&self) -> Option { //I'm a root, so no parent - if let None = self.aunt { + if let None = self.get_aunt() { return None; } @@ -127,7 +164,7 @@ impl PolNode { Ok(self.clone().as_rc()) } fn get_aunt(&self) -> Option { - self.aunt.clone() + self.aunt.clone().into_inner() } fn get_l_niece(&self) -> Option { self.l_niece.borrow().clone() @@ -145,32 +182,31 @@ impl PolNode { ) -> Node { let node = PolNode { data, - aunt, + aunt: RefCell::new(aunt), l_niece: RefCell::new(l_niece), r_niece: RefCell::new(r_niece), }; node.as_rc() } + fn swap_aunt(&self, other: &Node) { + self.aunt.swap(&other.aunt); + } /// Returns the current node as an Reference Counted Container "[Rc]". This is how /// nodes are stored inside the tree. fn as_rc(self) -> Node { Rc::new(self) } - fn set_aunt(&self, aunt: Option) -> PolNode { - PolNode { - data: self.data, - aunt, - l_niece: self.clone().l_niece, - r_niece: self.clone().r_niece, - } + fn set_aunt(&self, aunt: Option) { + self.aunt.replace(aunt); } fn set_nieces(&self, l_niece: Option, r_niece: Option) { self.l_niece.replace(l_niece); self.r_niece.replace(r_niece); } + /// Chops down any subtree this node has fn chop(&self) { self.l_niece.replace(None); self.r_niece.replace(None); @@ -189,15 +225,15 @@ impl Pollard { roots: vec![], } } - pub fn modify(&self, utxos: Vec, _stxos: Vec) -> Pollard { + pub fn modify(&self, utxos: Vec, stxos: Vec) -> Result { let utxos_after_deletion = self.leaves + utxos.len() as u64; - let roots = self.roots.clone(); + let roots = self.delete(stxos)?; let roots = Pollard::add(roots, utxos, self.leaves); - Pollard { + Ok(Pollard { leaves: utxos_after_deletion, roots, - } + }) } /// Deletes a single node from a Pollard. The algorithm works as follows: @@ -207,30 +243,64 @@ impl Pollard { /// (iii) if my parent is a root, then the node's sibling becomes a root /// (iv) if I'm a root myself, then this root becomes an empty root (PolNode::Default). fn delete_single(&self, pos: u64) -> Result, String> { + let mut roots = self.roots.clone(); + // In what subtree we are? + let (tree, _, _) = super::util::detect_offset(pos, self.leaves); + + // If deleting a root, we just place a default node in it's place + if is_root_position(pos, self.leaves, tree_rows(self.leaves)) { + roots[tree as usize] = PolNode::default().as_rc(); + return Ok(roots); + } // Grab the node we'll move up + // from_node is however is moving up, to node is the position it will be + // after moved let (from_node, _, to_node) = self.grab_node(pos)?; // What is the position of our parent? let parent_pos = parent(pos, tree_rows(self.leaves)); - // In what subtree we are? - let (tree, _, _) = super::util::detect_offset(pos, self.leaves); - let mut roots = self.roots.clone(); // If the position I'm moving to has an aunt, I'm not becoming a root. - if let Some(new_aunt) = &to_node.aunt { - from_node.chop(); - println!("To node:{:?}", new_aunt); - - let new_node = from_node.set_aunt(Some(new_aunt.clone())); - if is_left_niece(parent_pos) { - new_aunt.l_niece.replace(Some(new_node.as_rc())); + // ancestor is the node right beneath the to_node, this is useful because + // either it or it's sibling points to the `to_node`. We need to update this. + if let Some(ancestor) = to_node.get_aunt() { + if let Some(new_aunt) = ancestor.get_sibling() { + // If my new ancestor has a sibling, it means my aunt/parent is not root + // and my aunt is pointing to me, *not* my parent. + from_node.chop(); + + from_node.set_aunt(Some(new_aunt.clone())); + + let new_node = from_node; + if is_left_niece(parent_pos) { + new_aunt.l_niece.replace(Some(new_node)); + } else { + new_aunt.r_niece.replace(Some(new_node)); + } + new_aunt.update_aunt(Some(new_aunt.clone()), true); + roots[tree as usize] = new_aunt.recompute_parent_hash()?; } else { - new_aunt.r_niece.replace(Some(new_node.as_rc())); + // If my ancestor has no sibling, then my new ancestor itself is a root + // and roots points to it's own children. + + // If we need to chop down OUR children, we have to taken then from our + // sibling. + if let Some(sibling) = to_node.get_sibling() { + sibling.chop(); + } + + from_node.set_aunt(Some(ancestor.clone())); + if is_left_niece(parent_pos) { + ancestor.l_niece.replace(Some(from_node)); + } else { + ancestor.r_niece.replace(Some(from_node)); + } + ancestor.update_aunt(Some(ancestor.clone()), true); + roots[tree as usize] = ancestor.recompute_parent_hash()?; } - - roots[tree as usize] = roots[tree as usize].recompute_parent_hash()?; } else { // If it does not have a aunt, then I'm becoming a root - let from_node = from_node.set_aunt(None).as_rc(); + from_node.set_aunt(None); + let from_node = from_node; roots[tree as usize] = from_node; } @@ -274,7 +344,14 @@ impl Pollard { roots } + fn delete(&self, stxos: Vec) -> Result, String> { + let mut roots = vec![]; + for stxo in stxos { + roots = self.delete_single(stxo)?; + } + Ok(roots) + } fn add_single(mut roots: Vec, node: Hash, mut num_leaves: u64) -> Vec { let mut node = PolNode::new(node, None, None, None).as_rc(); @@ -286,14 +363,24 @@ impl Pollard { left_root.l_niece.swap(&node.l_niece); left_root.r_niece.swap(&node.r_niece); + // Swap aunts + left_root + .get_l_niece() + .unwrap_or_default() + .swap_aunt(&node.get_l_niece().unwrap_or_default()); + + left_root + .get_r_niece() + .unwrap_or_default() + .swap_aunt(&node.get_r_niece().unwrap_or_default()); let n_hash = types::parent_hash(&left_root.data.clone(), &node.data.clone()); let new_node = PolNode::new_node(n_hash, None, None, None); - let left_niece = left_root.set_aunt(Some(new_node.clone())).as_rc(); - let right_niece = node.set_aunt(Some(new_node.clone())).as_rc(); + left_root.set_aunt(Some(new_node.clone())); + node.set_aunt(Some(new_node.clone())); - new_node.set_nieces(Some(left_niece), Some(right_niece)); + new_node.set_nieces(Some(left_root), Some(node)); node = new_node; // left_root.aunt = new_node.clone(); @@ -337,7 +424,7 @@ impl std::fmt::Display for Pollard { } mod test { use super::{PolNode, Pollard}; - use bitcoin_hashes::{sha256::Hash as Data, Hash, HashEngine}; + use bitcoin_hashes::{sha256::Hash as Data, sha256t, Hash, HashEngine}; fn hash_from_u8(value: u8) -> Data { let mut engine = Data::engine(); @@ -346,11 +433,13 @@ mod test { Hash::from_engine(engine) } #[test] - fn test_grab_aunt() { + fn test_grab_node() { let values = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]; let hashes = values.into_iter().map(|val| hash_from_u8(val)).collect(); - let p = Pollard::new().modify(hashes, vec![]); + let p = Pollard::new() + .modify(hashes, vec![]) + .expect("Pollard should not fail"); let node = p.grab_node(13); assert!(node.is_ok()); @@ -366,23 +455,69 @@ mod test { let values = vec![0, 1, 2, 3]; let hashes = values.into_iter().map(|val| hash_from_u8(val)).collect(); - let p = Pollard::new().modify(hashes, vec![]); + let p = Pollard::new() + .modify(hashes, vec![]) + .expect("Pollard should not fail"); let node = p.grab_node(0); if let Ok((_, _, parent)) = node { let res = parent.recompute_parent_hash(); println!("{:?}", res); } } + #[test] + fn test_aunt() { + let values = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]; + let hashes = values.into_iter().map(|val| hash_from_u8(val)).collect(); + + let p = Pollard::new() + .modify(hashes, vec![]) + .expect("Pollard should not fail"); + let node = p.grab_node(1); + assert!(node.is_ok()); + let node = node.unwrap(); + let sibling = node.0; + let self_node = node.1; + let parent = node.2; + + assert_eq!( + sibling.data.to_string().as_str(), + "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d" + ); + assert_eq!( + self_node.data.to_string().as_str(), + "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a" + ); + assert_eq!( + parent.data.to_string().as_str(), + "02242b37d8e851f1e86f46790298c7097df06893d6226b7c1453c213e91717de" + ); + assert_eq!( + self_node.get_aunt().unwrap().data.to_string().as_str(), + "9576f4ade6e9bc3a6458b506ce3e4e890df29cb14cb5d3d887672aef55647a2b" + ); + } #[test] fn test_delete() { let values = vec![0, 1, 2, 3, 4, 5, 6, 7]; let hashes = values.into_iter().map(|val| hash_from_u8(val)).collect(); - let p = Pollard::new().modify(hashes, vec![]); - let roots = p.delete_single(0).expect("msg"); - - println!("{:?}", roots); + let p = Pollard::new() + .modify(hashes, vec![]) + .expect("Pollard should not fail"); + let p = p.modify(vec![], vec![0]).expect("msg"); + + let root = p.roots[0].clone(); + let l_niece = root.get_l_niece(); + let r_niece = root.get_r_niece(); + let aunt = l_niece.clone().unwrap().get_aunt(); + + if let (Some(l_niece), Some(r_niece), Some(aunt)) = (l_niece, r_niece, aunt) { + println!( + "root: {:?}\n l_niece {:?}\n r_niece: {:?}\n aunt: {:?}", + root, l_niece, r_niece, aunt + ); + } } #[test] fn test_add() { @@ -408,4 +543,76 @@ mod test { roots[3].data.to_string().as_str(), ); } + #[test] + fn test_delete_roots_child() { + // Assuming the following tree: + // + // 02 + // |---\ + // 00 01 + // If I delete `01`, then `00` will become a root, moving it's hash to `02` + let values = vec![0, 1]; + let hashes: Vec = values.into_iter().map(|val| hash_from_u8(val)).collect(); + + let p = Pollard::new() + .modify(hashes.clone(), vec![]) + .expect("Pollard should not fail"); + let roots = p.delete_single(1).expect("msg"); + assert_eq!(roots.len(), 1); + + let root = roots[0].clone(); + assert_eq!(root.data, hashes[0]); + } + #[test] + fn test_delete_root() { + // Assuming the following tree: + // + // 02 + // |---\ + // 00 01 + // If I delete `02`, then `02` will become an empty root, it'll point to nothing + // and its data will be Data::default() + let values = vec![0, 1]; + let hashes: Vec = values.into_iter().map(|val| hash_from_u8(val)).collect(); + + let p = Pollard::new() + .modify(hashes, vec![]) + .expect("Pollard should not fail"); + let roots = p.delete_single(2).expect("msg"); + assert_eq!(roots.len(), 1); + + let root = roots[0].clone(); + assert_eq!(root, PolNode::default().as_rc()); + } + #[test] + fn test_delete_deeper() { + // Assuming this tree, if we delete `01`, 00 will move up to 04's position + // 06 + // |-------\ + // 04 05 + // |---\ |---\ + // 00 01 02 03 + + // Becoming something like this: + // 06 + // |-------\ + // 04 05 + // |---\ |---\ + // -- -- 02 03 + // Where 04's data is just 00's + + let values = vec![0, 1, 2, 3]; + let hashes: Vec = values.into_iter().map(|val| hash_from_u8(val)).collect(); + + let p = Pollard::new() + .modify(hashes.clone(), vec![]) + .expect("Pollard should not fail"); + let roots = p.delete_single(1).expect("msg"); + assert_eq!(roots.len(), 1); + + let root = roots[0].clone(); + let expected_pos = root.get_l_niece().expect("04 should still exist"); + + assert_eq!(expected_pos.data, hashes[0]); + } } From 932e7bfffebc2bd36794e7a02b59effc79e88267 Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Fri, 23 Sep 2022 14:22:39 -0300 Subject: [PATCH 05/18] Basic structure for a Pollard 3/3 --- src/accumulator/pollard.rs | 80 +++++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 9 deletions(-) diff --git a/src/accumulator/pollard.rs b/src/accumulator/pollard.rs index 6ea36d9..9be0c92 100644 --- a/src/accumulator/pollard.rs +++ b/src/accumulator/pollard.rs @@ -423,6 +423,8 @@ impl std::fmt::Display for Pollard { } } mod test { + use std::str::FromStr; + use super::{PolNode, Pollard}; use bitcoin_hashes::{sha256::Hash as Data, sha256t, Hash, HashEngine}; fn hash_from_u8(value: u8) -> Data { @@ -440,15 +442,24 @@ mod test { let p = Pollard::new() .modify(hashes, vec![]) .expect("Pollard should not fail"); - let node = p.grab_node(13); - - assert!(node.is_ok()); - - let hash = node.unwrap().1.data; - assert_eq!( - hash.to_string().as_str(), - "9d1e0e2d9459d06523ad13e28a4093c2316baafe7aec5b25f30eba2e113599c4" - ); + let node = p.grab_node(4).unwrap(); + let target: Data = "e52d9c508c502347344d8c07ad91cbd6068afc75ff6292f062a09ca381c89e71" + .parse() + .unwrap(); + let sibling: Data = "e77b9a9ae9e30b0dbdb6f510a264ef9de781501d7b6b92ae89eb059c5ab743db" + .parse() + .unwrap(); + let parent: Data = "9eec588c41d87b16b0ee226cb38da3864f9537632321d8be855a73d5616dcc73" + .parse() + .unwrap(); + + let found_target = node.1.clone().data; + let found_sibling = node.0.clone().data; + let found_parent = node.2.clone().data; + + assert_eq!(target, found_target); + assert_eq!(sibling, found_sibling); + assert_eq!(parent, found_parent); } #[test] fn test_recompute_hashes() { @@ -460,6 +471,11 @@ mod test { .expect("Pollard should not fail"); let node = p.grab_node(0); if let Ok((_, _, parent)) = node { + parent + .get_l_niece() + .unwrap() + .set_self_hash(PolNode::default().as_rc()); + let res = parent.recompute_parent_hash(); println!("{:?}", res); } @@ -564,6 +580,52 @@ mod test { assert_eq!(root.data, hashes[0]); } #[test] + fn test_get_children() { + let values = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let hashes: Vec = values.into_iter().map(|val| hash_from_u8(val)).collect(); + + let p = Pollard::new() + .modify(hashes.clone(), vec![]) + .expect("Pollard should not fail"); + + let (sibling, node, parent) = p.grab_node(8).expect("Node exists"); + assert_eq!( + Data::from_str("02242b37d8e851f1e86f46790298c7097df06893d6226b7c1453c213e91717de") + .unwrap(), + node.data + ); + + assert_eq!( + Data::from_str("9576f4ade6e9bc3a6458b506ce3e4e890df29cb14cb5d3d887672aef55647a2b") + .unwrap(), + sibling.data + ); + let (l_child, r_child) = node.get_children(); + assert!(l_child.is_some()); + assert!(r_child.is_some()); + + let l_child = l_child.unwrap(); + let r_child = r_child.unwrap(); + + assert_eq!(hashes[0], r_child.data); + assert_eq!(hashes[1], l_child.data); + } + fn test_get_parent() { + let values = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let hashes: Vec = values.into_iter().map(|val| hash_from_u8(val)).collect(); + + let p = Pollard::new() + .modify(hashes.clone(), vec![]) + .expect("Pollard should not fail"); + + let (sibling, node, parent) = p.grab_node(8).expect("Node exists"); + assert_eq!( + Data::from_str("02242b37d8e851f1e86f46790298c7097df06893d6226b7c1453c213e91717de") + .unwrap(), + node.data + ); + } + #[test] fn test_delete_root() { // Assuming the following tree: // From 72228740668656a0785b01aaf2c0d93364568634 Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Thu, 12 Jan 2023 16:13:02 -0300 Subject: [PATCH 06/18] Make get_siblings work --- src/accumulator/pollard.rs | 123 ++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 70 deletions(-) diff --git a/src/accumulator/pollard.rs b/src/accumulator/pollard.rs index 9be0c92..e5b0326 100644 --- a/src/accumulator/pollard.rs +++ b/src/accumulator/pollard.rs @@ -1,21 +1,10 @@ -use bitcoin_hashes::{hex::ToHex, sha256::Hash}; -use std::{ - borrow::Borrow, - fmt::{write, Debug}, -}; -#[allow(unused)] -use std::{ - cell::{Cell, RefCell}, - mem::{self, swap}, - rc::{Rc, Weak}, -}; - -use crate::accumulator::util::detect_row_hashes; - use super::{ types::{self, parent_hash}, - util::{detect_row, is_left_niece, is_root_position, next_pow2, num_roots, parent, tree_rows}, + util::{is_left_niece, is_root_position, parent, tree_rows}, }; +use bitcoin_hashes::{hex::ToHex, sha256::Hash}; +use std::fmt::Debug; +use std::{cell::RefCell, rc::Rc}; type Node = Rc; #[derive(Clone, PartialEq, Eq, Default)] @@ -81,26 +70,15 @@ impl PolNode { } } fn get_children(&self) -> (Option, Option) { - let parent = self.get_parent(); - if let Some(parent) = parent { - // I'm the left children, so my sibling is the right one - if parent.get_l_niece().unwrap_or_default().as_ref().eq(self) { - if let Some(sibling) = parent.get_r_niece() { - return (sibling.get_l_niece(), sibling.get_r_niece()); - } - } else { - if let Some(sibling) = parent.get_l_niece() { - return (sibling.get_l_niece(), sibling.get_r_niece()); - } - } - } else { - return (self.get_l_niece(), self.get_r_niece()); + let sibling = self.get_sibling(); + if let Some(sibling) = sibling { + return (sibling.get_l_niece(), sibling.get_r_niece()); } - // This means I'm a root, + // This means I'm a leaf (None, None) } fn get_sibling(&self) -> Option { - let node = self.get_parent(); + let node = self.get_aunt(); if let Some(parent) = node { if parent.get_l_niece().unwrap_or_default().as_ref().eq(self) { return parent.get_r_niece(); @@ -383,7 +361,6 @@ impl Pollard { new_node.set_nieces(Some(left_root), Some(node)); node = new_node; - // left_root.aunt = new_node.clone(); num_leaves >>= 1; } @@ -392,41 +369,12 @@ impl Pollard { } } -impl std::fmt::Display for Pollard { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let trees = num_roots(self.leaves); - let rows = tree_rows(self.leaves) + 1; - let total_nodes = (2 as u64).pow(rows.into()) - 2; - let mut pos = total_nodes - 1; - for row in (0..=(rows - 1)).rev() { - while detect_row(pos, rows - 1) == row { - if let Ok((_, node, _)) = self.grab_node(pos.into()) { - write!(f, "{:?}", &node.data[0..2].to_hex())?; - } else { - write!(f, "--")?; - } - if pos == 0 { - break; - } - pos -= 1; - } - write!(f, "\n")?; - } - // for row in (0..=rows).rev() { - // for node in start_node..end_node { - - // } - - // } - - Ok(()) - } -} +#[cfg(test)] mod test { use std::str::FromStr; use super::{PolNode, Pollard}; - use bitcoin_hashes::{sha256::Hash as Data, sha256t, Hash, HashEngine}; + use bitcoin_hashes::{sha256::Hash as Data, Hash, HashEngine}; fn hash_from_u8(value: u8) -> Data { let mut engine = Data::engine(); @@ -453,9 +401,9 @@ mod test { .parse() .unwrap(); - let found_target = node.1.clone().data; - let found_sibling = node.0.clone().data; - let found_parent = node.2.clone().data; + let found_target = node.1.data; + let found_sibling = node.0.data; + let found_parent = node.2.data; assert_eq!(target, found_target); assert_eq!(sibling, found_sibling); @@ -588,7 +536,7 @@ mod test { .modify(hashes.clone(), vec![]) .expect("Pollard should not fail"); - let (sibling, node, parent) = p.grab_node(8).expect("Node exists"); + let (sibling, node, _) = p.grab_node(8).expect("Node exists"); assert_eq!( Data::from_str("02242b37d8e851f1e86f46790298c7097df06893d6226b7c1453c213e91717de") .unwrap(), @@ -607,9 +555,39 @@ mod test { let l_child = l_child.unwrap(); let r_child = r_child.unwrap(); - assert_eq!(hashes[0], r_child.data); - assert_eq!(hashes[1], l_child.data); + assert_eq!(hashes[1], r_child.data); + assert_eq!(hashes[0], l_child.data); } + #[test] + fn test_get_sibling() { + let values = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let hashes: Vec = values.into_iter().map(|val| hash_from_u8(val)).collect(); + + let p = Pollard::new() + .modify(hashes.clone(), vec![]) + .expect("Pollard should not fail"); + + let (sibling, node, _) = p.grab_node(8).expect("Node exists"); + assert_eq!( + Data::from_str("02242b37d8e851f1e86f46790298c7097df06893d6226b7c1453c213e91717de") + .unwrap(), + node.data + ); + + assert_eq!( + Data::from_str("9576f4ade6e9bc3a6458b506ce3e4e890df29cb14cb5d3d887672aef55647a2b") + .unwrap(), + sibling.data + ); + let sibling = node.get_sibling(); + assert_eq!( + Data::from_str("9576f4ade6e9bc3a6458b506ce3e4e890df29cb14cb5d3d887672aef55647a2b") + .unwrap(), + sibling.unwrap().data + ); + // assert_eq!(sibling.unwrap().data.to_string(), String::from("")) + } + #[test] fn test_get_parent() { let values = vec![0, 1, 2, 3, 4, 5, 6, 7]; let hashes: Vec = values.into_iter().map(|val| hash_from_u8(val)).collect(); @@ -618,12 +596,17 @@ mod test { .modify(hashes.clone(), vec![]) .expect("Pollard should not fail"); - let (sibling, node, parent) = p.grab_node(8).expect("Node exists"); + let (_, node, _) = p.grab_node(8).expect("Node exists"); assert_eq!( Data::from_str("02242b37d8e851f1e86f46790298c7097df06893d6226b7c1453c213e91717de") .unwrap(), node.data ); + let parent = node.get_parent(); + assert_eq!( + parent.unwrap().data.to_string(), + String::from("df46b17be5f66f0750a4b3efa26d4679db170a72d41eb56c3e4ff75a58c65386") + ); } #[test] fn test_delete_root() { From f32e7c5d3aec8a1463f007abff3161b5dd50db5b Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Thu, 12 Jan 2023 20:10:39 -0300 Subject: [PATCH 07/18] Refactor deletion --- src/accumulator/pollard.rs | 180 +++++++++++++++++++++---------------- 1 file changed, 102 insertions(+), 78 deletions(-) diff --git a/src/accumulator/pollard.rs b/src/accumulator/pollard.rs index e5b0326..94110b3 100644 --- a/src/accumulator/pollard.rs +++ b/src/accumulator/pollard.rs @@ -1,6 +1,6 @@ use super::{ types::{self, parent_hash}, - util::{is_left_niece, is_root_position, parent, tree_rows}, + util::{is_left_niece, is_root_position, tree_rows}, }; use bitcoin_hashes::{hex::ToHex, sha256::Hash}; use std::fmt::Debug; @@ -17,15 +17,34 @@ pub struct PolNode { impl Debug for PolNode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "\n{}\n|------\\\n", &self.data[0..2].to_hex())?; - let (left_children, right_children) = self.get_children(); - - if let Some(left_children) = left_children { - write!(f, "{} ", &left_children.data[0..2].to_hex())?; - } - if let Some(right_children) = right_children { - write!(f, "{}\n", &right_children.data[0..2].to_hex())?; + fn fmt_row( + f: &mut std::fmt::Formatter<'_>, + row_nodes: Vec>>, + i: u32, + ) -> std::fmt::Result { + if row_nodes.iter().all(|el| el.is_none()) { + return Ok(()); + } + if i > 4 { + return Ok(()); + } + let mut next_nodes = vec![]; + for node in row_nodes { + if let Some(node) = node { + write!(f, "{} ", node.data[0..2].to_hex())?; + + let (l, r) = node.get_children(); + next_nodes.push(l); + next_nodes.push(r); + } else { + next_nodes.push(None); + write!(f, "---- ")?; + } + } + write!(f, "\n")?; + fmt_row(f, next_nodes, i + 1) } + fmt_row(f, vec![Some(self.clone().as_rc())], 0)?; Ok(()) } } @@ -62,6 +81,7 @@ impl PolNode { r_niece.update_aunt(aunt, false); } } + /// Returns whether or not `n` is `aunt's` left niece fn is_l_niece(aunt: &Node, n: &Node) -> bool { if aunt.get_l_niece().as_ref().eq(&Some(n)) { true @@ -69,14 +89,20 @@ impl PolNode { false } } + /// Returns whether or not `n` is `aunt's` right niece fn get_children(&self) -> (Option, Option) { let sibling = self.get_sibling(); if let Some(sibling) = sibling { return (sibling.get_l_niece(), sibling.get_r_niece()); } + // I'm a root, so I point to my children + if self.get_aunt().is_none() { + return (self.get_l_niece(), self.get_r_niece()); + } // This means I'm a leaf (None, None) } + /// Returns this node's sibling fn get_sibling(&self) -> Option { let node = self.get_aunt(); if let Some(parent) = node { @@ -89,6 +115,8 @@ impl PolNode { None } + /// Returns this node's parent, this is one of the most important methods, because + /// most of the data is retrieved from this. fn get_parent(&self) -> Option { //I'm a root, so no parent if let None = self.get_aunt() { @@ -125,6 +153,7 @@ impl PolNode { } } } + /// Transverses the tree upwards, updating the node's hashes after an deletion fn recompute_parent_hash(&self) -> Result { if let (Some(l_niece), Some(r_niece)) = (self.get_l_niece(), self.get_r_niece()) { let new_parent_hash = parent_hash(&l_niece.data, &r_niece.data); @@ -141,12 +170,15 @@ impl PolNode { Ok(self.clone().as_rc()) } + /// Returns this node's aunt as [Node] fn get_aunt(&self) -> Option { self.aunt.clone().into_inner() } + /// Returns this node's l_niece as [Node] fn get_l_niece(&self) -> Option { self.l_niece.borrow().clone() } + /// Returns this node's r_niece as [Node] fn get_r_niece(&self) -> Option { self.r_niece.borrow().clone() } @@ -167,6 +199,7 @@ impl PolNode { node.as_rc() } + /// Swap this node's aunt with `other` fn swap_aunt(&self, other: &Node) { self.aunt.swap(&other.aunt); } @@ -175,11 +208,11 @@ impl PolNode { fn as_rc(self) -> Node { Rc::new(self) } - + /// Set this node's aunt fn set_aunt(&self, aunt: Option) { self.aunt.replace(aunt); } - + /// Set both r_niece and l_niece fn set_nieces(&self, l_niece: Option, r_niece: Option) { self.l_niece.replace(l_niece); self.r_niece.replace(r_niece); @@ -190,10 +223,18 @@ impl PolNode { self.r_niece.replace(None); } } +/// A Pollard is a collection of trees that may or may not be pruned. We store all roots in +/// the `roots` field. If we choose not to prune, all nodes will be owned by its ancestor #[derive(Clone)] pub struct Pollard { + /// Leaves are the actual data in the accumulator. Each UTXO will be a leaf, this is how + /// many leafs there are this acc. Note that with swappiless deletion, deleting leaves don't change + /// this number. So this is more like "How many leaves have we ever added into the acc" leaves: u64, + /// The actual roots roots: Vec, + /// Whether or not we cache non-roots nodes + full: bool, } impl Pollard { @@ -201,6 +242,7 @@ impl Pollard { Pollard { leaves: 0, roots: vec![], + full: true, } } pub fn modify(&self, utxos: Vec, stxos: Vec) -> Result { @@ -211,6 +253,7 @@ impl Pollard { Ok(Pollard { leaves: utxos_after_deletion, roots, + full: self.full, }) } @@ -231,57 +274,33 @@ impl Pollard { return Ok(roots); } // Grab the node we'll move up - // from_node is however is moving up, to node is the position it will be + // from_node is whomever is moving up, to node is the position it will be // after moved let (from_node, _, to_node) = self.grab_node(pos)?; - // What is the position of our parent? - let parent_pos = parent(pos, tree_rows(self.leaves)); - // If the position I'm moving to has an aunt, I'm not becoming a root. // ancestor is the node right beneath the to_node, this is useful because // either it or it's sibling points to the `to_node`. We need to update this. - if let Some(ancestor) = to_node.get_aunt() { - if let Some(new_aunt) = ancestor.get_sibling() { - // If my new ancestor has a sibling, it means my aunt/parent is not root - // and my aunt is pointing to me, *not* my parent. - from_node.chop(); - - from_node.set_aunt(Some(new_aunt.clone())); - - let new_node = from_node; - if is_left_niece(parent_pos) { - new_aunt.l_niece.replace(Some(new_node)); - } else { - new_aunt.r_niece.replace(Some(new_node)); - } - new_aunt.update_aunt(Some(new_aunt.clone()), true); - roots[tree as usize] = new_aunt.recompute_parent_hash()?; - } else { - // If my ancestor has no sibling, then my new ancestor itself is a root - // and roots points to it's own children. - - // If we need to chop down OUR children, we have to taken then from our - // sibling. - if let Some(sibling) = to_node.get_sibling() { - sibling.chop(); - } - - from_node.set_aunt(Some(ancestor.clone())); - if is_left_niece(parent_pos) { - ancestor.l_niece.replace(Some(from_node)); - } else { - ancestor.r_niece.replace(Some(from_node)); - } - ancestor.update_aunt(Some(ancestor.clone()), true); - roots[tree as usize] = ancestor.recompute_parent_hash()?; - } + if let Some(new_aunt) = to_node.get_sibling() { + // If my new ancestor has a sibling, it means my aunt/parent is not root + // and my aunt is pointing to me, *not* my parent. + let new_node = PolNode { + aunt: to_node.aunt.clone(), + data: from_node.data, + l_niece: to_node.l_niece.clone(), + r_niece: to_node.r_niece.clone(), + }; + new_aunt.chop(); + to_node.set_self_hash(new_node.as_rc()); } else { - // If it does not have a aunt, then I'm becoming a root - from_node.set_aunt(None); - let from_node = from_node; - roots[tree as usize] = from_node; + // This means we are a root's sibling. We are becoming a root now + let new_node = PolNode { + aunt: to_node.aunt.clone(), + data: from_node.data, + l_niece: to_node.l_niece.clone(), + r_niece: to_node.r_niece.clone(), + }; + roots[tree as usize] = new_node.as_rc(); } - Ok(roots) } fn grab_node(&self, pos: u64) -> Result<(Node, Node, Node), String> { @@ -525,6 +544,8 @@ mod test { assert_eq!(roots.len(), 1); let root = roots[0].clone(); + println!("{:?}", root); + assert_eq!(root.data, hashes[0]); } #[test] @@ -630,34 +651,37 @@ mod test { assert_eq!(root, PolNode::default().as_rc()); } #[test] - fn test_delete_deeper() { - // Assuming this tree, if we delete `01`, 00 will move up to 04's position - // 06 - // |-------\ - // 04 05 - // |---\ |---\ - // 00 01 02 03 - - // Becoming something like this: - // 06 - // |-------\ - // 04 05 - // |---\ |---\ - // -- -- 02 03 - // Where 04's data is just 00's + fn test_delete_non_root() { + // Assuming this tree, if we delete `01`, 00 will move up to 08's position + // 14 + // |-----------------\ + // 12 13 + // |-------\ |--------\ + // 08 09 10 11 + // |----\ |----\ |----\ |----\ + // 00 01 02 03 04 05 06 07 + + // 14 + // |-----------------\ + // 12 13 + // |-------\ |--------\ + // 08 09 10 11 + // |----\ |----\ |----\ |----\ + // 00 01 02 03 04 05 06 07 + + // Where 08's data is just 00's - let values = vec![0, 1, 2, 3]; + let values = vec![0, 1, 2, 3, 4, 5, 6, 7]; let hashes: Vec = values.into_iter().map(|val| hash_from_u8(val)).collect(); let p = Pollard::new() .modify(hashes.clone(), vec![]) - .expect("Pollard should not fail"); - let roots = p.delete_single(1).expect("msg"); - assert_eq!(roots.len(), 1); - - let root = roots[0].clone(); - let expected_pos = root.get_l_niece().expect("04 should still exist"); + .expect("Pollard should not fail") + .modify(vec![], vec![1]) + .expect("Still should not fail"); - assert_eq!(expected_pos.data, hashes[0]); + assert_eq!(p.roots.len(), 1); + let (_, node, _) = p.grab_node(8).expect("This tree should have pos 8"); + assert_eq!(node.data, hashes[0]); } } From 51a802d08aa2e0da1f7734e147f42d69d00774fc Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Fri, 20 Jan 2023 16:15:38 -0300 Subject: [PATCH 08/18] Add docs to modify --- src/accumulator/pollard.rs | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/accumulator/pollard.rs b/src/accumulator/pollard.rs index 94110b3..00fa080 100644 --- a/src/accumulator/pollard.rs +++ b/src/accumulator/pollard.rs @@ -2,7 +2,7 @@ use super::{ types::{self, parent_hash}, util::{is_left_niece, is_root_position, tree_rows}, }; -use bitcoin_hashes::{hex::ToHex, sha256::Hash}; +use bitcoin_hashes::{hex::ToHex, sha256::{Hash, self}}; use std::fmt::Debug; use std::{cell::RefCell, rc::Rc}; type Node = Rc; @@ -63,6 +63,9 @@ impl PolNode { r_niece: RefCell::new(r_niece), } } + pub fn get_data(&self) -> Hash { + self.data + } fn update_aunt(&self, aunt: Option, root: bool) { let aunt = if root { self.set_aunt(None); @@ -245,6 +248,35 @@ impl Pollard { full: true, } } + /// Modify is the main API to a [Pollard]. Because order matters, you can only `modify` + /// a [Pollard], and internally it'll add and delete, in the correct order. + /// + /// Modify takes ownership over the [Pollard] and returns a new one. This is to avoid API + /// misuse, since modify is a **pure function**, it doesn't change any of it's arguments + /// and return a brand new [Pollard]. Taking ownership discourage people to use an old [Pollard] + /// state instead of using the new one. + /// + /// This method accepts two vectors as parameter, a vec of [Hash] and a vec of [u64]. The + /// first one is a vec of leaf hashes for the newly created UTXOs. The second one is the position + /// for the UTXOs being spent in this block as inputs. + /// + /// # Example + /// ``` + /// use rustreexo::accumulator::pollard::Pollard; + /// use bitcoin_hashes::{sha256::Hash as Data, Hash, HashEngine}; + /// let values = vec![0, 1, 2, 3, 4, 5, 6, 7]; + /// let hashes = values.into_iter().map(|val|{ + /// let mut engine = Data::engine(); + /// engine.input(&[val]); + /// Data::from_engine(engine) + /// }) + /// .collect(); + /// // Add 8 leaves to the pollard + /// let p = Pollard::new() + /// .modify(hashes, vec![]) + /// .expect("Pollard should not fail"); + /// assert_eq!(p.get_roots()[0].get_data().to_string(), String::from("b151a956139bb821d4effa34ea95c17560e0135d1e4661fc23cedc3af49dac42")); + /// ``` pub fn modify(&self, utxos: Vec, stxos: Vec) -> Result { let utxos_after_deletion = self.leaves + utxos.len() as u64; let roots = self.delete(stxos)?; @@ -256,7 +288,9 @@ impl Pollard { full: self.full, }) } - + pub fn get_roots(&self)-> &Vec { + &self.roots + } /// Deletes a single node from a Pollard. The algorithm works as follows: /// Grab a node, it's sibling and it's parent. /// (i) if I'm deleting the node, but not it's sibling, then the sibling takes the parent's position From 3a4ab203d128ea602352b1ad7c110f0d4612a9bb Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Fri, 20 Jan 2023 18:33:01 -0300 Subject: [PATCH 09/18] Make recompute hash work --- src/accumulator/pollard.rs | 73 ++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/src/accumulator/pollard.rs b/src/accumulator/pollard.rs index 00fa080..569b4cf 100644 --- a/src/accumulator/pollard.rs +++ b/src/accumulator/pollard.rs @@ -2,11 +2,11 @@ use super::{ types::{self, parent_hash}, util::{is_left_niece, is_root_position, tree_rows}, }; -use bitcoin_hashes::{hex::ToHex, sha256::{Hash, self}}; +use bitcoin_hashes::{hex::ToHex, sha256::Hash}; use std::fmt::Debug; use std::{cell::RefCell, rc::Rc}; -type Node = Rc; +type Node = Rc; #[derive(Clone, PartialEq, Eq, Default)] pub struct PolNode { data: Hash, @@ -158,14 +158,18 @@ impl PolNode { } /// Transverses the tree upwards, updating the node's hashes after an deletion fn recompute_parent_hash(&self) -> Result { - if let (Some(l_niece), Some(r_niece)) = (self.get_l_niece(), self.get_r_niece()) { + if let (Some(l_niece), Some(r_niece)) = self.get_children() { let new_parent_hash = parent_hash(&l_niece.data, &r_niece.data); - let aunt = self.get_aunt(); - let new_node = - PolNode::new(new_parent_hash, aunt.clone(), Some(l_niece), Some(r_niece)); - if let Some(aunt) = aunt { + let parent = self.get_parent(); + let new_node = PolNode::new( + new_parent_hash, + parent.clone(), + self.get_l_niece(), + self.get_r_niece(), + ); + if let Some(parent) = parent { self.set_self_hash(new_node.as_rc()); - return aunt.recompute_parent_hash(); + return parent.recompute_parent_hash(); } else { return Ok(new_node.as_rc()); } @@ -288,7 +292,7 @@ impl Pollard { full: self.full, }) } - pub fn get_roots(&self)-> &Vec { + pub fn get_roots(&self) -> &Vec { &self.roots } /// Deletes a single node from a Pollard. The algorithm works as follows: @@ -427,7 +431,11 @@ mod test { use std::str::FromStr; use super::{PolNode, Pollard}; - use bitcoin_hashes::{sha256::Hash as Data, Hash, HashEngine}; + use bitcoin_hashes::{ + hex::{FromHex, ToHex}, + sha256::{self, Hash as Data}, + Hash, HashEngine, + }; fn hash_from_u8(value: u8) -> Data { let mut engine = Data::engine(); @@ -471,14 +479,27 @@ mod test { .modify(hashes, vec![]) .expect("Pollard should not fail"); let node = p.grab_node(0); - if let Ok((_, _, parent)) = node { - parent - .get_l_niece() - .unwrap() - .set_self_hash(PolNode::default().as_rc()); - + if let Ok((node, _, parent)) = node { + node.set_self_hash( + PolNode::new( + sha256::Hash::from_hex( + "0100000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(), + node.get_aunt(), + node.get_l_niece(), + node.get_r_niece(), + ) + .as_rc(), + ); let res = parent.recompute_parent_hash(); - println!("{:?}", res); + + assert_eq!( + res.unwrap().get_data().to_hex(), + String::from("a3fab668c6917a4979627f04dd0be687ceb5601aaf161664747ddb1399b1a4fb") + ) + } else { + unreachable!() } } #[test] @@ -524,17 +545,11 @@ mod test { .expect("Pollard should not fail"); let p = p.modify(vec![], vec![0]).expect("msg"); - let root = p.roots[0].clone(); - let l_niece = root.get_l_niece(); - let r_niece = root.get_r_niece(); - let aunt = l_niece.clone().unwrap().get_aunt(); - - if let (Some(l_niece), Some(r_niece), Some(aunt)) = (l_niece, r_niece, aunt) { - println!( - "root: {:?}\n l_niece {:?}\n r_niece: {:?}\n aunt: {:?}", - root, l_niece, r_niece, aunt - ); - } + let (_, node, _) = p.grab_node(8).unwrap(); + assert_eq!( + String::from("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"), + node.get_data().to_string() + ); } #[test] fn test_add() { @@ -578,8 +593,6 @@ mod test { assert_eq!(roots.len(), 1); let root = roots[0].clone(); - println!("{:?}", root); - assert_eq!(root.data, hashes[0]); } #[test] From d69ae99fe18b4315b8c6676cce407056bb2d3699 Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Fri, 20 Jan 2023 19:20:36 -0300 Subject: [PATCH 10/18] Add some extra docs --- src/accumulator/pollard.rs | 61 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/accumulator/pollard.rs b/src/accumulator/pollard.rs index 569b4cf..50e546b 100644 --- a/src/accumulator/pollard.rs +++ b/src/accumulator/pollard.rs @@ -1,3 +1,50 @@ +//! This is the Rust implementation of the Pollard data structure, the algorithms are similar +//! to the go counterpart, but it's not a 1-to-1 re-implementation. A tight copy of the Go lib +//! is almost impossible in safe Rust, because it relies on passing pointers around. +//! +//! In a Pollard, nodes points to its niece instead of children. This helps with proving and +//! some updates, but creates some challenges when dealing with Rust. Furthermore, nodes also +//! points to its aunt, creating a link that is hard to express in safe Rust using basic +//! static borrow check and lifetime annotation. +//! During development of this struct, something much simpler like this code bellow have been tried, +//! but 'ancestor usually binds to the same lifetime as the whole tree, making it impossible to +//! hold a node and a `mut ref` to another one, or the tree itself (usually a vector of roots). We +//! need to mutate internally, and we need to mutate internal node data, like data, aunt and nieces +//! because nodes change their position as the tree get things added and deleted. If you try to +//! introduce more specifiers, you end up with a infinite recursion where you have to specify +//! all lifetimes to descendant and ancestor nodes; impossible to do. +//! ``` +//! use bitcoin_hashes::sha256::Hash; +//! use std::boxed::Box; +//! struct PolNode<'ancestor> { +//! data: Hash, +//! aunt: &'ancestor PolNode<'ancestor>, +//! l_niece: Box>, +//! r_niece: Box>, +//! } +//! ``` +//! Note: This [Box] is required because at declaration time, [PolNode] is an incomplete type +//! with no known compilation-time size (i.e !sized), hence, we can't use it as a concrete type. +//! Boxes are complete types, so we place a Box inside [PolNode] and somehow get a new [PolNode] +//! to put in there. +//! +//! The solution is to use [RefCell], a pointer-ish struct that holds a memory location with +//! dynamic borrow checking. Everything the borrow checker do on compilation time, [RefCell] do +//! at runtime. A bit of performance loss, but not that bad. With [RefCell] we can deal with +//! immutable references almost everywhere, and simplify things a lot. We also put a [PolNode] inside +//! a [Rc], so we can keep references to it easily, and even manipulate nodes by their own +//! without risking creating two different trees - If we just clone a node (node a [Rc] of a node) +//! we'll also clone all nodes bellow, creating two identical trees. Every change to this new +//! tree wouldn't reflect on the original one. +//! +//! Even though [RefCell] is considered safe code, it might panic in situations that the +//! borrow checker issues a compile-time error. For that reason, we should be careful when +//! using it. ** It is prohibited to get a mut ref (&mut) to a node inside any tree **, this +//! makes sure we don't run on a conflict of trying mutable borrow a node that is already +//! borrowed. We also avoid immutable refs (&), to the same reason. For all cases of interior +//! mutability, there is a simple specialized function to do the job, and the API gives you +//! a [Rc] over a node, not the [RefCell], so avoid using the [RefCell] directly. + use super::{ types::{self, parent_hash}, util::{is_left_niece, is_root_position, tree_rows}, @@ -6,7 +53,13 @@ use bitcoin_hashes::{hex::ToHex, sha256::Hash}; use std::fmt::Debug; use std::{cell::RefCell, rc::Rc}; +/// Type alias used throughout this lib, see the crate-level doc to understand why using [Rc] type Node = Rc; + +/// A PolNode is any node inside a tree, it can be a root, in which case aunt would be [None]. +/// A internal node, where all fields are filled, or a leaf, that has no child/niece. +/// Mutable references are discouraged, for mutating a [PolNode], use the appropriated +/// methods. #[derive(Clone, PartialEq, Eq, Default)] pub struct PolNode { data: Hash, @@ -15,6 +68,7 @@ pub struct PolNode { r_niece: RefCell>, } +//FIX-ME: Make this Debug more pleasant, like the Go one impl Debug for PolNode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt_row( @@ -50,6 +104,10 @@ impl Debug for PolNode { } impl PolNode { + /// Creates a new [PolNode] with provided data. For an empty node see [Default]. + /// If this node is a root, there's no aunt. If it's a leaf, then nieces is [None]. Finally + /// if node is a root child, [l_niece] and [r_niece] are actually r_child and l_child (and + /// aunt is actually parent). fn new( data: Hash, aunt: Option, @@ -63,9 +121,11 @@ impl PolNode { r_niece: RefCell::new(r_niece), } } + /// Returns the node's data, i.e the stored hash pub fn get_data(&self) -> Hash { self.data } + /// Updates this node's aunt fn update_aunt(&self, aunt: Option, root: bool) { let aunt = if root { self.set_aunt(None); @@ -292,6 +352,7 @@ impl Pollard { full: self.full, }) } + /// Returns a reference to this acc roots pub fn get_roots(&self) -> &Vec { &self.roots } From b14b8fd193803160edba6056bf8ee7297e2cc3fd Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Fri, 20 Jan 2023 21:57:49 -0300 Subject: [PATCH 11/18] Run all testacases --- src/accumulator/pollard.rs | 167 +++++++++++++++++++++++++++++-------- 1 file changed, 130 insertions(+), 37 deletions(-) diff --git a/src/accumulator/pollard.rs b/src/accumulator/pollard.rs index 50e546b..43d8ba3 100644 --- a/src/accumulator/pollard.rs +++ b/src/accumulator/pollard.rs @@ -47,7 +47,7 @@ use super::{ types::{self, parent_hash}, - util::{is_left_niece, is_root_position, tree_rows}, + util::{self, is_left_niece, is_root_position, tree_rows}, }; use bitcoin_hashes::{hex::ToHex, sha256::Hash}; use std::fmt::Debug; @@ -182,7 +182,7 @@ impl PolNode { /// most of the data is retrieved from this. fn get_parent(&self) -> Option { //I'm a root, so no parent - if let None = self.get_aunt() { + if self.get_aunt().is_none() { return None; } @@ -207,35 +207,42 @@ impl PolNode { /// This method is a little evolved, it takes an [Rc] to our aunt, then finds out /// whether we are left or right nieces. With that info, we replace the corresponding /// aunt's niece with our new self. - fn set_self_hash(&self, new: Node) { + fn set_self_hash(&self, new: Node) -> Result<(), String> { if let Some(aunt) = self.get_aunt() { + println!("{aunt:?}"); if *aunt.get_l_niece().unwrap_or_default() == *self { aunt.l_niece.replace(Some(new)); } else { aunt.r_niece.replace(Some(new)); } + return Ok(()); } + Err("Node doesn't have an aunt".into()) } /// Transverses the tree upwards, updating the node's hashes after an deletion - fn recompute_parent_hash(&self) -> Result { + fn recompute_parent_hash(&self) -> Node { if let (Some(l_niece), Some(r_niece)) = self.get_children() { let new_parent_hash = parent_hash(&l_niece.data, &r_niece.data); let parent = self.get_parent(); + let new_node = PolNode::new( new_parent_hash, - parent.clone(), + self.get_aunt(), self.get_l_niece(), self.get_r_niece(), ); + if let Some(parent) = parent { - self.set_self_hash(new_node.as_rc()); + self.set_self_hash(new_node.as_rc()).expect("msg"); return parent.recompute_parent_hash(); } else { - return Ok(new_node.as_rc()); + return new_node.as_rc(); } } - - Ok(self.clone().as_rc()) + if let Some(parent) = self.get_parent() { + return parent.recompute_parent_hash(); + } + unreachable!(); } /// Returns this node's aunt as [Node] fn get_aunt(&self) -> Option { @@ -341,15 +348,17 @@ impl Pollard { /// .expect("Pollard should not fail"); /// assert_eq!(p.get_roots()[0].get_data().to_string(), String::from("b151a956139bb821d4effa34ea95c17560e0135d1e4661fc23cedc3af49dac42")); /// ``` - pub fn modify(&self, utxos: Vec, stxos: Vec) -> Result { + pub fn modify(self, utxos: Vec, stxos: Vec) -> Result { let utxos_after_deletion = self.leaves + utxos.len() as u64; + let full = self.full; + let leaves = self.leaves; let roots = self.delete(stxos)?; - let roots = Pollard::add(roots, utxos, self.leaves); + let roots = Pollard::add(roots, utxos, leaves); Ok(Pollard { leaves: utxos_after_deletion, roots, - full: self.full, + full, }) } /// Returns a reference to this acc roots @@ -362,15 +371,14 @@ impl Pollard { /// (ii) if both are being deleted, then the parent is also deleted /// (iii) if my parent is a root, then the node's sibling becomes a root /// (iv) if I'm a root myself, then this root becomes an empty root (PolNode::Default). - fn delete_single(&self, pos: u64) -> Result, String> { - let mut roots = self.roots.clone(); + fn delete_single(&mut self, pos: u64) -> Result<(), String> { // In what subtree we are? let (tree, _, _) = super::util::detect_offset(pos, self.leaves); // If deleting a root, we just place a default node in it's place if is_root_position(pos, self.leaves, tree_rows(self.leaves)) { - roots[tree as usize] = PolNode::default().as_rc(); - return Ok(roots); + self.roots[tree as usize] = PolNode::default().as_rc(); + return Ok(()); } // Grab the node we'll move up // from_node is whomever is moving up, to node is the position it will be @@ -379,7 +387,7 @@ impl Pollard { // If the position I'm moving to has an aunt, I'm not becoming a root. // ancestor is the node right beneath the to_node, this is useful because // either it or it's sibling points to the `to_node`. We need to update this. - if let Some(new_aunt) = to_node.get_sibling() { + if let Some(sibling) = to_node.get_sibling() { // If my new ancestor has a sibling, it means my aunt/parent is not root // and my aunt is pointing to me, *not* my parent. let new_node = PolNode { @@ -388,8 +396,11 @@ impl Pollard { l_niece: to_node.l_niece.clone(), r_niece: to_node.r_niece.clone(), }; - new_aunt.chop(); - to_node.set_self_hash(new_node.as_rc()); + sibling.chop(); + println!("============="); + to_node.set_self_hash(new_node.as_rc())?; + println!("============="); + self.roots[tree as usize] = to_node.recompute_parent_hash(); } else { // This means we are a root's sibling. We are becoming a root now let new_node = PolNode { @@ -398,9 +409,9 @@ impl Pollard { l_niece: to_node.l_niece.clone(), r_niece: to_node.r_niece.clone(), }; - roots[tree as usize] = new_node.as_rc(); + self.roots[tree as usize] = new_node.as_rc(); } - Ok(roots) + Ok(()) } fn grab_node(&self, pos: u64) -> Result<(Node, Node, Node), String> { let (tree, branch_len, bits) = super::util::detect_offset(pos, self.leaves); @@ -430,7 +441,7 @@ impl Pollard { if let (Some(node), Some(sibling), Some(parent)) = (n, sibling, parent) { return Ok((node, sibling, parent)); } - Err("node not found".to_string()) + Err(format!("node {pos} not found")) } fn add(mut roots: Vec, utxos: Vec, mut num_leaves: u64) -> Vec { for utxo in utxos { @@ -440,13 +451,13 @@ impl Pollard { roots } - fn delete(&self, stxos: Vec) -> Result, String> { - let mut roots = vec![]; + fn delete(mut self, stxos: Vec) -> Result, String> { + let stxos = util::detwin(stxos, util::tree_rows(self.leaves)); for stxo in stxos { - roots = self.delete_single(stxo)?; + self.delete_single(stxo)?; } - Ok(roots) + Ok(self.roots) } fn add_single(mut roots: Vec, node: Hash, mut num_leaves: u64) -> Vec { let mut node = PolNode::new(node, None, None, None).as_rc(); @@ -497,6 +508,8 @@ mod test { sha256::{self, Hash as Data}, Hash, HashEngine, }; + use serde::Deserialize; + fn hash_from_u8(value: u8) -> Data { let mut engine = Data::engine(); @@ -552,11 +565,12 @@ mod test { node.get_r_niece(), ) .as_rc(), - ); + ) + .expect("should not err"); let res = parent.recompute_parent_hash(); assert_eq!( - res.unwrap().get_data().to_hex(), + res.get_data().to_hex(), String::from("a3fab668c6917a4979627f04dd0be687ceb5601aaf161664747ddb1399b1a4fb") ) } else { @@ -647,13 +661,13 @@ mod test { let values = vec![0, 1]; let hashes: Vec = values.into_iter().map(|val| hash_from_u8(val)).collect(); - let p = Pollard::new() + let mut p = Pollard::new() .modify(hashes.clone(), vec![]) .expect("Pollard should not fail"); - let roots = p.delete_single(1).expect("msg"); - assert_eq!(roots.len(), 1); + p.delete_single(1).expect("msg"); + assert_eq!(p.get_roots().len(), 1); - let root = roots[0].clone(); + let root = p.get_roots()[0].clone(); assert_eq!(root.data, hashes[0]); } #[test] @@ -714,7 +728,6 @@ mod test { .unwrap(), sibling.unwrap().data ); - // assert_eq!(sibling.unwrap().data.to_string(), String::from("")) } #[test] fn test_get_parent() { @@ -749,13 +762,13 @@ mod test { let values = vec![0, 1]; let hashes: Vec = values.into_iter().map(|val| hash_from_u8(val)).collect(); - let p = Pollard::new() + let mut p = Pollard::new() .modify(hashes, vec![]) .expect("Pollard should not fail"); - let roots = p.delete_single(2).expect("msg"); - assert_eq!(roots.len(), 1); + p.delete_single(2).expect("msg"); + assert_eq!(p.get_roots().len(), 1); - let root = roots[0].clone(); + let root = p.get_roots()[0].clone(); assert_eq!(root, PolNode::default().as_rc()); } #[test] @@ -792,4 +805,84 @@ mod test { let (_, node, _) = p.grab_node(8).expect("This tree should have pos 8"); assert_eq!(node.data, hashes[0]); } + #[derive(Debug, Deserialize)] + struct TestCase { + leaf_preimages: Vec, + target_values: Option>, + expected_roots: Vec, + } + fn run_single_addition_case(case: TestCase) { + let hashes = case + .leaf_preimages + .iter() + .map(|preimage| hash_from_u8(*preimage)) + .collect(); + let p = Pollard::new() + .modify(hashes, vec![]) + .expect("Test pollards are valid"); + assert_eq!(p.get_roots().len(), case.expected_roots.len()); + let expected_roots = case + .expected_roots + .iter() + .map(|root| sha256::Hash::from_hex(&root).unwrap()) + .collect::>(); + let roots = p + .get_roots() + .into_iter() + .map(|root| root.get_data()) + .collect::>(); + assert_eq!(expected_roots, roots, "Test case failed {case:?}"); + } + fn run_case_with_deletion(case: TestCase) { + let hashes = case + .leaf_preimages + .iter() + .map(|preimage| hash_from_u8(*preimage)) + .collect(); + let dels = case + .target_values + .clone() + .expect("Del test must have targets"); + let p = Pollard::new() + .modify(hashes, vec![]) + .expect("Test pollards are valid") + .modify(vec![], dels) + .expect("still should be valid"); + + assert_eq!(p.get_roots().len(), case.expected_roots.len()); + let expected_roots = case + .expected_roots + .iter() + .map(|root| sha256::Hash::from_hex(&root).unwrap()) + .collect::>(); + // println!("{:?}", p.get_roots()); + let roots = p + .get_roots() + .into_iter() + .map(|root| root.get_data()) + .collect::>(); + assert_eq!(expected_roots, roots, "Test case failed {case:?}"); + } + + #[test] + fn run_tests_from_cases() { + #[derive(Deserialize)] + struct TestsJSON { + insertion_tests: Vec, + deletion_tests: Vec, + } + + let contents = std::fs::read_to_string("test_values/test_cases.json") + .expect("Something went wrong reading the file"); + + let tests = serde_json::from_str::(contents.as_str()) + .expect("JSON deserialization error"); + + for i in tests.insertion_tests { + run_single_addition_case(i); + } + for i in tests.deletion_tests { + run_case_with_deletion(i); + } + } } From 9970efb9dc5cc8ea285d3112339d94469f768e1e Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Mon, 23 Jan 2023 22:50:57 -0300 Subject: [PATCH 12/18] Make pollard pass all tests from testcase.json --- src/accumulator/mod.rs | 2 - src/accumulator/pollard.rs | 256 ++++++++++++++++++++++--------------- 2 files changed, 151 insertions(+), 107 deletions(-) diff --git a/src/accumulator/mod.rs b/src/accumulator/mod.rs index 439c835..ef3d3da 100644 --- a/src/accumulator/mod.rs +++ b/src/accumulator/mod.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] - pub mod pollard; pub mod proof; pub mod stump; diff --git a/src/accumulator/pollard.rs b/src/accumulator/pollard.rs index 43d8ba3..9414371 100644 --- a/src/accumulator/pollard.rs +++ b/src/accumulator/pollard.rs @@ -6,6 +6,31 @@ //! some updates, but creates some challenges when dealing with Rust. Furthermore, nodes also //! points to its aunt, creating a link that is hard to express in safe Rust using basic //! static borrow check and lifetime annotation. +//! # Usage +//! ``` +//! use rustreexo::accumulator::pollard::Pollard; +//! use bitcoin_hashes::{sha256::Hash as Data, Hash, HashEngine, hex::ToHex}; +//! // Instead of hashing UTXOs, we hash integers for the sake of the example. But in a real +//! // case, you'll take the sha512_256 of LeafData +//! let values = vec![0, 1, 2, 3, 4, 5, 6, 7]; +//! // Hashes all values, using just sha256 +//! let hashes: Vec<_> = values.into_iter().map(|val|{ +//! let mut engine = Data::engine(); +//! engine.input(&[val]); +//! Data::from_engine(engine) +//! }) +//! .collect(); +//! // Creates a new Pollard with 8 leaves, then deletes the first and fourth elements (0, 3) +//! let p = Pollard::new() +//! .modify(hashes.clone(), vec![]) +//! .expect("Simple addition don't fail") +//! .modify(vec![], vec![0, 3]) +//! .expect("Nor should simple deletion with known to be in tree elements"); +//! // We should get this state after +//! assert_eq!(p.get_roots().len(), 1); +//! assert_eq!(p.get_roots()[0].get_data().to_hex(), String::from("dd015199c18dcd8b8606a4f0053a2ad4797e82f51944d9d2d3cc0c1fca872a22")); +//! ``` +//! # Implementation //! During development of this struct, something much simpler like this code bellow have been tried, //! but 'ancestor usually binds to the same lifetime as the whole tree, making it impossible to //! hold a node and a `mut ref` to another one, or the tree itself (usually a vector of roots). We @@ -49,8 +74,11 @@ use super::{ types::{self, parent_hash}, util::{self, is_left_niece, is_root_position, tree_rows}, }; -use bitcoin_hashes::{hex::ToHex, sha256::Hash}; -use std::fmt::Debug; +use bitcoin_hashes::{ + hex::ToHex, + sha256::{self, Hash}, +}; +use std::{cell::Cell, fmt::Debug}; use std::{cell::RefCell, rc::Rc}; /// Type alias used throughout this lib, see the crate-level doc to understand why using [Rc] @@ -62,7 +90,7 @@ type Node = Rc; /// methods. #[derive(Clone, PartialEq, Eq, Default)] pub struct PolNode { - data: Hash, + data: Cell, aunt: RefCell>, l_niece: RefCell>, r_niece: RefCell>, @@ -79,19 +107,18 @@ impl Debug for PolNode { if row_nodes.iter().all(|el| el.is_none()) { return Ok(()); } - if i > 4 { - return Ok(()); - } let mut next_nodes = vec![]; for node in row_nodes { if let Some(node) = node { - write!(f, "{} ", node.data[0..2].to_hex())?; + write!(f, "{} ", node.get_data()[0..2].to_hex())?; let (l, r) = node.get_children(); next_nodes.push(l); next_nodes.push(r); } else { next_nodes.push(None); + next_nodes.push(None); + write!(f, "---- ")?; } } @@ -115,7 +142,7 @@ impl PolNode { r_niece: Option, ) -> PolNode { PolNode { - data, + data: Cell::new(data), aunt: RefCell::new(aunt), l_niece: RefCell::new(l_niece), r_niece: RefCell::new(r_niece), @@ -123,29 +150,29 @@ impl PolNode { } /// Returns the node's data, i.e the stored hash pub fn get_data(&self) -> Hash { - self.data + self.data.to_owned().into_inner() } /// Updates this node's aunt - fn update_aunt(&self, aunt: Option, root: bool) { + fn _update_aunt(&self, aunt: Option, root: bool) { let aunt = if root { self.set_aunt(None); aunt } else { - if PolNode::is_l_niece(&aunt.clone().unwrap(), &self.clone().as_rc()) { + if PolNode::_is_l_niece(&aunt.clone().unwrap(), &self.clone().as_rc()) { aunt.expect("msg").get_l_niece() } else { aunt.expect("msg").get_r_niece() } }; if let Some(l_niece) = self.get_l_niece() { - l_niece.update_aunt(aunt.clone(), false); + l_niece._update_aunt(aunt.clone(), false); } if let Some(r_niece) = self.get_l_niece() { - r_niece.update_aunt(aunt, false); + r_niece._update_aunt(aunt, false); } } /// Returns whether or not `n` is `aunt's` left niece - fn is_l_niece(aunt: &Node, n: &Node) -> bool { + fn _is_l_niece(aunt: &Node, n: &Node) -> bool { if aunt.get_l_niece().as_ref().eq(&Some(n)) { true } else { @@ -185,7 +212,6 @@ impl PolNode { if self.get_aunt().is_none() { return None; } - if let Some(aunt) = self.get_aunt() { // If aunt also has an aunt, then I take his sibling, as he's my parent if let Some(grandpa) = aunt.get_aunt() { @@ -207,36 +233,20 @@ impl PolNode { /// This method is a little evolved, it takes an [Rc] to our aunt, then finds out /// whether we are left or right nieces. With that info, we replace the corresponding /// aunt's niece with our new self. - fn set_self_hash(&self, new: Node) -> Result<(), String> { - if let Some(aunt) = self.get_aunt() { - println!("{aunt:?}"); - if *aunt.get_l_niece().unwrap_or_default() == *self { - aunt.l_niece.replace(Some(new)); - } else { - aunt.r_niece.replace(Some(new)); - } - return Ok(()); - } - Err("Node doesn't have an aunt".into()) + fn set_self_hash(&self, new: Hash) { + self.data.replace(new); } /// Transverses the tree upwards, updating the node's hashes after an deletion - fn recompute_parent_hash(&self) -> Node { + fn recompute_parent_hash(&self) { if let (Some(l_niece), Some(r_niece)) = self.get_children() { - let new_parent_hash = parent_hash(&l_niece.data, &r_niece.data); + let new_parent_hash = parent_hash(&l_niece.get_data(), &r_niece.get_data()); let parent = self.get_parent(); - - let new_node = PolNode::new( - new_parent_hash, - self.get_aunt(), - self.get_l_niece(), - self.get_r_niece(), - ); + self.set_self_hash(new_parent_hash); if let Some(parent) = parent { - self.set_self_hash(new_node.as_rc()).expect("msg"); return parent.recompute_parent_hash(); } else { - return new_node.as_rc(); + return; } } if let Some(parent) = self.get_parent() { @@ -265,7 +275,7 @@ impl PolNode { r_niece: Option, ) -> Node { let node = PolNode { - data, + data: Cell::new(data), aunt: RefCell::new(aunt), l_niece: RefCell::new(l_niece), r_niece: RefCell::new(r_niece), @@ -312,6 +322,14 @@ pub struct Pollard { } impl Pollard { + /// Creates an empty Pollard + /// # Example + /// ``` + /// use rustreexo::accumulator::pollard::Pollard; + /// let p = Pollard::new(); + /// // Should have no roots + /// assert_eq!(p.get_roots().len(), 0); + /// ``` pub fn new() -> Pollard { Pollard { leaves: 0, @@ -361,7 +379,8 @@ impl Pollard { full, }) } - /// Returns a reference to this acc roots + /// Returns a reference to this acc roots. Usually, API consumers won't care much about + /// roots, serialization should use standard APIs. pub fn get_roots(&self) -> &Vec { &self.roots } @@ -390,29 +409,19 @@ impl Pollard { if let Some(sibling) = to_node.get_sibling() { // If my new ancestor has a sibling, it means my aunt/parent is not root // and my aunt is pointing to me, *not* my parent. - let new_node = PolNode { - aunt: to_node.aunt.clone(), - data: from_node.data, - l_niece: to_node.l_niece.clone(), - r_niece: to_node.r_niece.clone(), - }; sibling.chop(); - println!("============="); - to_node.set_self_hash(new_node.as_rc())?; - println!("============="); - self.roots[tree as usize] = to_node.recompute_parent_hash(); + to_node.set_self_hash(from_node.get_data()); + to_node.set_nieces(to_node.get_l_niece(), to_node.get_r_niece()); + to_node.recompute_parent_hash(); } else { // This means we are a root's sibling. We are becoming a root now - let new_node = PolNode { - aunt: to_node.aunt.clone(), - data: from_node.data, - l_niece: to_node.l_niece.clone(), - r_niece: to_node.r_niece.clone(), - }; - self.roots[tree as usize] = new_node.as_rc(); + to_node.set_self_hash(from_node.get_data()); + to_node.set_nieces(from_node.get_l_niece(), from_node.get_l_niece()); } Ok(()) } + /// Grabs node given its position in the tree. It returns a node's sibling, the node + /// itself and the parent. Error if node isn't on tree. fn grab_node(&self, pos: u64) -> Result<(Node, Node, Node), String> { let (tree, branch_len, bits) = super::util::detect_offset(pos, self.leaves); let mut n = Some(self.roots[tree as usize].clone()); @@ -441,8 +450,9 @@ impl Pollard { if let (Some(node), Some(sibling), Some(parent)) = (n, sibling, parent) { return Ok((node, sibling, parent)); } - Err(format!("node {pos} not found")) + Err(format!("node {} not found", pos)) } + /// Add nodes to the accumulator fn add(mut roots: Vec, utxos: Vec, mut num_leaves: u64) -> Vec { for utxo in utxos { roots = Pollard::add_single(roots, utxo, num_leaves); @@ -451,6 +461,7 @@ impl Pollard { roots } + /// Deletes nodes from the accumulator fn delete(mut self, stxos: Vec) -> Result, String> { let stxos = util::detwin(stxos, util::tree_rows(self.leaves)); for stxo in stxos { @@ -459,6 +470,8 @@ impl Pollard { Ok(self.roots) } + /// Adds a single node. Addition is a loop over all new nodes, calling add_single for each + /// of them fn add_single(mut roots: Vec, node: Hash, mut num_leaves: u64) -> Vec { let mut node = PolNode::new(node, None, None, None).as_rc(); @@ -467,10 +480,36 @@ impl Pollard { let left_root = roots .pop() .expect("add_single: num_leaves & 1 == 1 and no roots?"); - + //If the root that we're gonna hash with is empty, move the current + // node up to the position of the parent. + // + // Example: + // + // 12 + // |-------\ + // 08 09 + // |---\ |---\ + // 00 01 02 03 -- + // + // When we add 05 to this tree, 04 is empty so we move 05 to 10. + // The resulting tree looks like below. The hash at position 10 + // is not hash(04 || 05) but just the hash of 05. + // + // 12 + // |-------\ + // 08 09 10 + // |---\ |---\ |---\ + // 00 01 02 03 -- -- + // We accomplish this by just skipping the append-and-hash for 04 + + if left_root.get_data() == sha256::Hash::default() { + continue; + } + // Roots points to their children, but now they aren't roots, so they should + // point to their nieces instead left_root.l_niece.swap(&node.l_niece); left_root.r_niece.swap(&node.r_niece); - // Swap aunts + // Swap aunts for the nieces left_root .get_l_niece() .unwrap_or_default() @@ -480,10 +519,10 @@ impl Pollard { .get_r_niece() .unwrap_or_default() .swap_aunt(&node.get_r_niece().unwrap_or_default()); - - let n_hash = types::parent_hash(&left_root.data.clone(), &node.data.clone()); + // Creates a new node that is hash(left_root | node) + let n_hash = types::parent_hash(&left_root.get_data(), &node.get_data()); let new_node = PolNode::new_node(n_hash, None, None, None); - + // Turns left_root and node into new_nodes's children left_root.set_aunt(Some(new_node.clone())); node.set_aunt(Some(new_node.clone())); @@ -500,7 +539,7 @@ impl Pollard { #[cfg(test)] mod test { - use std::str::FromStr; + use std::{str::FromStr, vec}; use super::{PolNode, Pollard}; use bitcoin_hashes::{ @@ -536,9 +575,9 @@ mod test { .parse() .unwrap(); - let found_target = node.1.data; - let found_sibling = node.0.data; - let found_parent = node.2.data; + let found_target = node.1.get_data(); + let found_sibling = node.0.get_data(); + let found_parent = node.2.get_data(); assert_eq!(target, found_target); assert_eq!(sibling, found_sibling); @@ -553,24 +592,16 @@ mod test { .modify(hashes, vec![]) .expect("Pollard should not fail"); let node = p.grab_node(0); - if let Ok((node, _, parent)) = node { + if let Ok((node, _, _)) = node { node.set_self_hash( - PolNode::new( - sha256::Hash::from_hex( - "0100000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(), - node.get_aunt(), - node.get_l_niece(), - node.get_r_niece(), + sha256::Hash::from_hex( + "0100000000000000000000000000000000000000000000000000000000000000", ) - .as_rc(), - ) - .expect("should not err"); - let res = parent.recompute_parent_hash(); - + .unwrap(), + ); + node.recompute_parent_hash(); assert_eq!( - res.get_data().to_hex(), + p.get_roots()[0].get_data().to_hex(), String::from("a3fab668c6917a4979627f04dd0be687ceb5601aaf161664747ddb1399b1a4fb") ) } else { @@ -594,19 +625,24 @@ mod test { let parent = node.2; assert_eq!( - sibling.data.to_string().as_str(), + sibling.get_data().to_string().as_str(), "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d" ); assert_eq!( - self_node.data.to_string().as_str(), + self_node.get_data().to_string().as_str(), "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a" ); assert_eq!( - parent.data.to_string().as_str(), + parent.get_data().to_string().as_str(), "02242b37d8e851f1e86f46790298c7097df06893d6226b7c1453c213e91717de" ); assert_eq!( - self_node.get_aunt().unwrap().data.to_string().as_str(), + self_node + .get_aunt() + .unwrap() + .get_data() + .to_string() + .as_str(), "9576f4ade6e9bc3a6458b506ce3e4e890df29cb14cb5d3d887672aef55647a2b" ); } @@ -635,19 +671,19 @@ mod test { assert_eq!( "b151a956139bb821d4effa34ea95c17560e0135d1e4661fc23cedc3af49dac42", - roots[0].data.to_string().as_str(), + roots[0].get_data().to_string().as_str(), ); assert_eq!( "9c053db406c1a077112189469a3aca0573d3481bef09fa3d2eda3304d7d44be8", - roots[1].data.to_string().as_str(), + roots[1].get_data().to_string().as_str(), ); assert_eq!( "55d0a0ef8f5c25a9da266b36c0c5f4b31008ece82df2512c8966bddcc27a66a0", - roots[2].data.to_string().as_str(), + roots[2].get_data().to_string().as_str(), ); assert_eq!( "4d7b3ef7300acf70c892d8327db8272f54434adbc61a4e130a563cb59a0d0f47", - roots[3].data.to_string().as_str(), + roots[3].get_data().to_string().as_str(), ); } #[test] @@ -668,7 +704,7 @@ mod test { assert_eq!(p.get_roots().len(), 1); let root = p.get_roots()[0].clone(); - assert_eq!(root.data, hashes[0]); + assert_eq!(root.get_data(), hashes[0]); } #[test] fn test_get_children() { @@ -683,13 +719,13 @@ mod test { assert_eq!( Data::from_str("02242b37d8e851f1e86f46790298c7097df06893d6226b7c1453c213e91717de") .unwrap(), - node.data + node.get_data() ); assert_eq!( Data::from_str("9576f4ade6e9bc3a6458b506ce3e4e890df29cb14cb5d3d887672aef55647a2b") .unwrap(), - sibling.data + sibling.get_data() ); let (l_child, r_child) = node.get_children(); assert!(l_child.is_some()); @@ -698,8 +734,8 @@ mod test { let l_child = l_child.unwrap(); let r_child = r_child.unwrap(); - assert_eq!(hashes[1], r_child.data); - assert_eq!(hashes[0], l_child.data); + assert_eq!(hashes[1], r_child.get_data()); + assert_eq!(hashes[0], l_child.get_data()); } #[test] fn test_get_sibling() { @@ -714,19 +750,19 @@ mod test { assert_eq!( Data::from_str("02242b37d8e851f1e86f46790298c7097df06893d6226b7c1453c213e91717de") .unwrap(), - node.data + node.get_data() ); assert_eq!( Data::from_str("9576f4ade6e9bc3a6458b506ce3e4e890df29cb14cb5d3d887672aef55647a2b") .unwrap(), - sibling.data + sibling.get_data() ); let sibling = node.get_sibling(); assert_eq!( Data::from_str("9576f4ade6e9bc3a6458b506ce3e4e890df29cb14cb5d3d887672aef55647a2b") .unwrap(), - sibling.unwrap().data + sibling.unwrap().get_data() ); } #[test] @@ -742,11 +778,11 @@ mod test { assert_eq!( Data::from_str("02242b37d8e851f1e86f46790298c7097df06893d6226b7c1453c213e91717de") .unwrap(), - node.data + node.get_data() ); let parent = node.get_parent(); assert_eq!( - parent.unwrap().data.to_string(), + parent.unwrap().get_data().to_string(), String::from("df46b17be5f66f0750a4b3efa26d4679db170a72d41eb56c3e4ff75a58c65386") ); } @@ -765,9 +801,8 @@ mod test { let mut p = Pollard::new() .modify(hashes, vec![]) .expect("Pollard should not fail"); - p.delete_single(2).expect("msg"); + p.delete_single(2).expect("Node 2 should exist"); assert_eq!(p.get_roots().len(), 1); - let root = p.get_roots()[0].clone(); assert_eq!(root, PolNode::default().as_rc()); } @@ -803,7 +838,7 @@ mod test { assert_eq!(p.roots.len(), 1); let (_, node, _) = p.grab_node(8).expect("This tree should have pos 8"); - assert_eq!(node.data, hashes[0]); + assert_eq!(node.get_data(), hashes[0]); } #[derive(Debug, Deserialize)] struct TestCase { @@ -831,7 +866,7 @@ mod test { .into_iter() .map(|root| root.get_data()) .collect::>(); - assert_eq!(expected_roots, roots, "Test case failed {case:?}"); + assert_eq!(expected_roots, roots, "Test case failed {:?}", case); } fn run_case_with_deletion(case: TestCase) { let hashes = case @@ -855,13 +890,12 @@ mod test { .iter() .map(|root| sha256::Hash::from_hex(&root).unwrap()) .collect::>(); - // println!("{:?}", p.get_roots()); let roots = p .get_roots() .into_iter() .map(|root| root.get_data()) .collect::>(); - assert_eq!(expected_roots, roots, "Test case failed {case:?}"); + assert_eq!(expected_roots, roots, "Test case failed {:?}", case); } #[test] @@ -885,4 +919,16 @@ mod test { run_case_with_deletion(i); } } + #[test] + fn test() { + let values = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let hashes: Vec = values.into_iter().map(|val| hash_from_u8(val)).collect(); + + let p = Pollard::new() + .modify(hashes.clone(), vec![]) + .expect("Pollard should not fail") + .modify(vec![], vec![0, 3]) + .expect("Still should not fail"); + println!("{:?}", p.get_roots()[0].get_data()); + } } From 892034325c1e0d09087ea46cfe9ab4ddf0bc776f Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Tue, 24 Jan 2023 14:14:51 -0300 Subject: [PATCH 13/18] run clippy linting --- src/accumulator/pollard.rs | 72 +++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/src/accumulator/pollard.rs b/src/accumulator/pollard.rs index 9414371..7ec324f 100644 --- a/src/accumulator/pollard.rs +++ b/src/accumulator/pollard.rs @@ -122,10 +122,10 @@ impl Debug for PolNode { write!(f, "---- ")?; } } - write!(f, "\n")?; + writeln!(f)?; fmt_row(f, next_nodes, i + 1) } - fmt_row(f, vec![Some(self.clone().as_rc())], 0)?; + fmt_row(f, vec![Some(self.clone().into_rc())], 0)?; Ok(()) } } @@ -157,12 +157,10 @@ impl PolNode { let aunt = if root { self.set_aunt(None); aunt + } else if PolNode::_is_l_niece(&aunt.clone().unwrap(), &self.clone().into_rc()) { + aunt.expect("msg").get_l_niece() } else { - if PolNode::_is_l_niece(&aunt.clone().unwrap(), &self.clone().as_rc()) { - aunt.expect("msg").get_l_niece() - } else { - aunt.expect("msg").get_r_niece() - } + aunt.expect("msg").get_r_niece() }; if let Some(l_niece) = self.get_l_niece() { l_niece._update_aunt(aunt.clone(), false); @@ -173,11 +171,7 @@ impl PolNode { } /// Returns whether or not `n` is `aunt's` left niece fn _is_l_niece(aunt: &Node, n: &Node) -> bool { - if aunt.get_l_niece().as_ref().eq(&Some(n)) { - true - } else { - false - } + aunt.get_l_niece().as_ref().eq(&Some(n)) } /// Returns whether or not `n` is `aunt's` right niece fn get_children(&self) -> (Option, Option) { @@ -209,9 +203,7 @@ impl PolNode { /// most of the data is retrieved from this. fn get_parent(&self) -> Option { //I'm a root, so no parent - if self.get_aunt().is_none() { - return None; - } + self.get_aunt()?; if let Some(aunt) = self.get_aunt() { // If aunt also has an aunt, then I take his sibling, as he's my parent if let Some(grandpa) = aunt.get_aunt() { @@ -281,7 +273,7 @@ impl PolNode { r_niece: RefCell::new(r_niece), }; - node.as_rc() + node.into_rc() } /// Swap this node's aunt with `other` fn swap_aunt(&self, other: &Node) { @@ -289,7 +281,7 @@ impl PolNode { } /// Returns the current node as an Reference Counted Container "[Rc]". This is how /// nodes are stored inside the tree. - fn as_rc(self) -> Node { + fn into_rc(self) -> Node { Rc::new(self) } /// Set this node's aunt @@ -309,7 +301,7 @@ impl PolNode { } /// A Pollard is a collection of trees that may or may not be pruned. We store all roots in /// the `roots` field. If we choose not to prune, all nodes will be owned by its ancestor -#[derive(Clone)] +#[derive(Clone, Default)] pub struct Pollard { /// Leaves are the actual data in the accumulator. Each UTXO will be a leaf, this is how /// many leafs there are this acc. Note that with swappiless deletion, deleting leaves don't change @@ -396,7 +388,7 @@ impl Pollard { // If deleting a root, we just place a default node in it's place if is_root_position(pos, self.leaves, tree_rows(self.leaves)) { - self.roots[tree as usize] = PolNode::default().as_rc(); + self.roots[tree as usize] = PolNode::default().into_rc(); return Ok(()); } // Grab the node we'll move up @@ -473,7 +465,7 @@ impl Pollard { /// Adds a single node. Addition is a loop over all new nodes, calling add_single for each /// of them fn add_single(mut roots: Vec, node: Hash, mut num_leaves: u64) -> Vec { - let mut node = PolNode::new(node, None, None, None).as_rc(); + let mut node = PolNode::new(node, None, None, None).into_rc(); while num_leaves & 1 == 1 { // If num_leaves & 1 == 1, roots cannot be None @@ -559,7 +551,7 @@ mod test { #[test] fn test_grab_node() { let values = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]; - let hashes = values.into_iter().map(|val| hash_from_u8(val)).collect(); + let hashes = values.into_iter().map(hash_from_u8).collect(); let p = Pollard::new() .modify(hashes, vec![]) @@ -586,7 +578,7 @@ mod test { #[test] fn test_recompute_hashes() { let values = vec![0, 1, 2, 3]; - let hashes = values.into_iter().map(|val| hash_from_u8(val)).collect(); + let hashes = values.into_iter().map(hash_from_u8).collect(); let p = Pollard::new() .modify(hashes, vec![]) @@ -611,7 +603,7 @@ mod test { #[test] fn test_aunt() { let values = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]; - let hashes = values.into_iter().map(|val| hash_from_u8(val)).collect(); + let hashes = values.into_iter().map(hash_from_u8).collect(); let p = Pollard::new() .modify(hashes, vec![]) @@ -649,7 +641,7 @@ mod test { #[test] fn test_delete() { let values = vec![0, 1, 2, 3, 4, 5, 6, 7]; - let hashes = values.into_iter().map(|val| hash_from_u8(val)).collect(); + let hashes = values.into_iter().map(hash_from_u8).collect(); let p = Pollard::new() .modify(hashes, vec![]) @@ -665,7 +657,7 @@ mod test { #[test] fn test_add() { let values = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]; - let hashes = values.into_iter().map(|val| hash_from_u8(val)).collect(); + let hashes = values.into_iter().map(hash_from_u8).collect(); let roots = Pollard::add(vec![], hashes, 0); @@ -695,7 +687,7 @@ mod test { // 00 01 // If I delete `01`, then `00` will become a root, moving it's hash to `02` let values = vec![0, 1]; - let hashes: Vec = values.into_iter().map(|val| hash_from_u8(val)).collect(); + let hashes: Vec = values.into_iter().map(hash_from_u8).collect(); let mut p = Pollard::new() .modify(hashes.clone(), vec![]) @@ -709,7 +701,7 @@ mod test { #[test] fn test_get_children() { let values = vec![0, 1, 2, 3, 4, 5, 6, 7]; - let hashes: Vec = values.into_iter().map(|val| hash_from_u8(val)).collect(); + let hashes: Vec = values.into_iter().map(hash_from_u8).collect(); let p = Pollard::new() .modify(hashes.clone(), vec![]) @@ -740,10 +732,10 @@ mod test { #[test] fn test_get_sibling() { let values = vec![0, 1, 2, 3, 4, 5, 6, 7]; - let hashes: Vec = values.into_iter().map(|val| hash_from_u8(val)).collect(); + let hashes: Vec = values.into_iter().map(hash_from_u8).collect(); let p = Pollard::new() - .modify(hashes.clone(), vec![]) + .modify(hashes, vec![]) .expect("Pollard should not fail"); let (sibling, node, _) = p.grab_node(8).expect("Node exists"); @@ -768,10 +760,10 @@ mod test { #[test] fn test_get_parent() { let values = vec![0, 1, 2, 3, 4, 5, 6, 7]; - let hashes: Vec = values.into_iter().map(|val| hash_from_u8(val)).collect(); + let hashes: Vec = values.into_iter().map(hash_from_u8).collect(); let p = Pollard::new() - .modify(hashes.clone(), vec![]) + .modify(hashes, vec![]) .expect("Pollard should not fail"); let (_, node, _) = p.grab_node(8).expect("Node exists"); @@ -796,7 +788,7 @@ mod test { // If I delete `02`, then `02` will become an empty root, it'll point to nothing // and its data will be Data::default() let values = vec![0, 1]; - let hashes: Vec = values.into_iter().map(|val| hash_from_u8(val)).collect(); + let hashes: Vec = values.into_iter().map(hash_from_u8).collect(); let mut p = Pollard::new() .modify(hashes, vec![]) @@ -804,7 +796,7 @@ mod test { p.delete_single(2).expect("Node 2 should exist"); assert_eq!(p.get_roots().len(), 1); let root = p.get_roots()[0].clone(); - assert_eq!(root, PolNode::default().as_rc()); + assert_eq!(root, PolNode::default().into_rc()); } #[test] fn test_delete_non_root() { @@ -828,7 +820,7 @@ mod test { // Where 08's data is just 00's let values = vec![0, 1, 2, 3, 4, 5, 6, 7]; - let hashes: Vec = values.into_iter().map(|val| hash_from_u8(val)).collect(); + let hashes: Vec = values.into_iter().map(hash_from_u8).collect(); let p = Pollard::new() .modify(hashes.clone(), vec![]) @@ -859,11 +851,11 @@ mod test { let expected_roots = case .expected_roots .iter() - .map(|root| sha256::Hash::from_hex(&root).unwrap()) + .map(|root| sha256::Hash::from_hex(root).unwrap()) .collect::>(); let roots = p .get_roots() - .into_iter() + .iter() .map(|root| root.get_data()) .collect::>(); assert_eq!(expected_roots, roots, "Test case failed {:?}", case); @@ -888,11 +880,11 @@ mod test { let expected_roots = case .expected_roots .iter() - .map(|root| sha256::Hash::from_hex(&root).unwrap()) + .map(|root| sha256::Hash::from_hex(root).unwrap()) .collect::>(); let roots = p .get_roots() - .into_iter() + .iter() .map(|root| root.get_data()) .collect::>(); assert_eq!(expected_roots, roots, "Test case failed {:?}", case); @@ -922,10 +914,10 @@ mod test { #[test] fn test() { let values = vec![0, 1, 2, 3, 4, 5, 6, 7]; - let hashes: Vec = values.into_iter().map(|val| hash_from_u8(val)).collect(); + let hashes: Vec = values.into_iter().map(hash_from_u8).collect(); let p = Pollard::new() - .modify(hashes.clone(), vec![]) + .modify(hashes, vec![]) .expect("Pollard should not fail") .modify(vec![], vec![0, 3]) .expect("Still should not fail"); From 53bf5d52ae1f88b017e30dce944ae16c62334c5c Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Tue, 24 Jan 2023 14:25:17 -0300 Subject: [PATCH 14/18] Rebase with main --- src/accumulator/pollard.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/accumulator/pollard.rs b/src/accumulator/pollard.rs index 7ec324f..1a94e90 100644 --- a/src/accumulator/pollard.rs +++ b/src/accumulator/pollard.rs @@ -88,14 +88,23 @@ type Node = Rc; /// A internal node, where all fields are filled, or a leaf, that has no child/niece. /// Mutable references are discouraged, for mutating a [PolNode], use the appropriated /// methods. -#[derive(Clone, PartialEq, Eq, Default)] +#[derive(Clone, PartialEq, Eq)] pub struct PolNode { data: Cell, aunt: RefCell>, l_niece: RefCell>, r_niece: RefCell>, } - +impl Default for PolNode { + fn default() -> Self { + PolNode { + data: Cell::new(::all_zeros()), + aunt: RefCell::new(None), + l_niece: RefCell::new(None), + r_niece: RefCell::new(None), + } + } +} //FIX-ME: Make this Debug more pleasant, like the Go one impl Debug for PolNode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -494,7 +503,7 @@ impl Pollard { // 00 01 02 03 -- -- // We accomplish this by just skipping the append-and-hash for 04 - if left_root.get_data() == sha256::Hash::default() { + if left_root.get_data() == ::all_zeros() { continue; } // Roots points to their children, but now they aren't roots, so they should From 8cbd71fe7e8416733e968104fa32dc5d4ac50864 Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Tue, 24 Jan 2023 14:27:11 -0300 Subject: [PATCH 15/18] Remove debug test --- src/accumulator/pollard.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/accumulator/pollard.rs b/src/accumulator/pollard.rs index 1a94e90..50e0df9 100644 --- a/src/accumulator/pollard.rs +++ b/src/accumulator/pollard.rs @@ -920,16 +920,4 @@ mod test { run_case_with_deletion(i); } } - #[test] - fn test() { - let values = vec![0, 1, 2, 3, 4, 5, 6, 7]; - let hashes: Vec = values.into_iter().map(hash_from_u8).collect(); - - let p = Pollard::new() - .modify(hashes, vec![]) - .expect("Pollard should not fail") - .modify(vec![], vec![0, 3]) - .expect("Still should not fail"); - println!("{:?}", p.get_roots()[0].get_data()); - } } From e15b99c0d4c129b792168b26c93abcaed83328fb Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Tue, 24 Jan 2023 19:03:04 -0300 Subject: [PATCH 16/18] Implement proof for pollard --- Cargo.lock | 2 -- src/accumulator/pollard.rs | 50 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e84b912..074a8d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,7 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 - [[package]] name = "bitcoin_hashes" version = "0.11.0" diff --git a/src/accumulator/pollard.rs b/src/accumulator/pollard.rs index 50e0df9..478abd5 100644 --- a/src/accumulator/pollard.rs +++ b/src/accumulator/pollard.rs @@ -71,8 +71,9 @@ //! a [Rc] over a node, not the [RefCell], so avoid using the [RefCell] directly. use super::{ + proof::Proof, types::{self, parent_hash}, - util::{self, is_left_niece, is_root_position, tree_rows}, + util::{self, detwin, get_proof_positions, is_left_niece, is_root_position, tree_rows}, }; use bitcoin_hashes::{ hex::ToHex, @@ -346,7 +347,7 @@ impl Pollard { /// and return a brand new [Pollard]. Taking ownership discourage people to use an old [Pollard] /// state instead of using the new one. /// - /// This method accepts two vectors as parameter, a vec of [Hash] and a vec of [u64]. The + /// This method accepts two vectors as parameter, a vec of [struct@Hash] and a vec of [u64]. The /// first one is a vec of leaf hashes for the newly created UTXOs. The second one is the position /// for the UTXOs being spent in this block as inputs. /// @@ -385,6 +386,18 @@ impl Pollard { pub fn get_roots(&self) -> &Vec { &self.roots } + /// Proves that a given utxo is in the current Pollard + pub fn prove(&self, targets: &[u64]) -> Result { + let total_rows = tree_rows(self.leaves); + let targets = detwin(targets.into(), total_rows); + let needed_pos = get_proof_positions(&targets, self.leaves, total_rows); + let mut hashes = vec![]; + for pos in needed_pos { + let (_, node, _) = self.grab_node(pos)?; + hashes.push(node.get_data()); + } + Ok(Proof::new(targets, hashes)) + } /// Deletes a single node from a Pollard. The algorithm works as follows: /// Grab a node, it's sibling and it's parent. /// (i) if I'm deleting the node, but not it's sibling, then the sibling takes the parent's position @@ -542,6 +555,8 @@ impl Pollard { mod test { use std::{str::FromStr, vec}; + use crate::accumulator::{proof::Proof, stump::Stump}; + use super::{PolNode, Pollard}; use bitcoin_hashes::{ hex::{FromHex, ToHex}, @@ -808,6 +823,37 @@ mod test { assert_eq!(root, PolNode::default().into_rc()); } #[test] + fn test_prove() { + let values = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let hashes: Vec = values.into_iter().map(hash_from_u8).collect(); + let proof = Pollard::new() + .modify(hashes.clone(), vec![]) + .unwrap() + .modify(vec![], vec![0, 3]) + .unwrap() + .prove(&[5, 7]) + .unwrap(); + // Stump needs this proof + let del_proof_hashes = [ + "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a", + "dbc1b4c900ffe48d575b5da5c638040125f65db0fe3e24494b76ea986457d986", + "29590a14c1b09384b94a2c0e94bf821ca75b62eacebc47893397ca88e3bbcbd7", + ] + .iter() + .map(|hash| Data::from_hex(hash).unwrap()) + .collect(); + + let del_proof = Proof::new(vec![0, 3], del_proof_hashes); + let stump = Stump::new() + .modify(&hashes, &vec![], &Proof::default()) + .unwrap() + .0 + .modify(&vec![], &vec![hashes[0], hashes[3]], &del_proof) + .unwrap() + .0; + assert_eq!(proof.verify(&vec![hashes[5], hashes[7]], &stump), Ok(true)); + } + #[test] fn test_delete_non_root() { // Assuming this tree, if we delete `01`, 00 will move up to 08's position // 14 From 99aea0fac37834a364090e96f1ff1bd5f0a4218b Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Thu, 26 Jan 2023 21:29:16 -0300 Subject: [PATCH 17/18] Allow Pollard to Verify proofs While implementing the verify method, the overall way that verify is handle was changed. Instead of verify being a proof's method, it now is exposed by every type of accumulator. They all use a common low-level api from proof, but we need this because every acc handles roots differently. Moreover, asking for roots and leaves at API level is verbose and undesirable. --- src/accumulator/pollard.rs | 56 +++++++++++++++++++++++++++- src/accumulator/proof.rs | 76 ++++++++++++-------------------------- src/accumulator/stump.rs | 46 ++++++++++++++++++++++- 3 files changed, 121 insertions(+), 57 deletions(-) diff --git a/src/accumulator/pollard.rs b/src/accumulator/pollard.rs index 478abd5..fc62ac8 100644 --- a/src/accumulator/pollard.rs +++ b/src/accumulator/pollard.rs @@ -381,6 +381,53 @@ impl Pollard { full, }) } + /// Public interface for verifying proofs. Returns a result with a bool or an Error + /// True means the proof is true given the current stump, false means the proof is + /// not valid given the current stump. + ///# Examples + /// ``` + /// use bitcoin_hashes::{sha256::Hash as Sha256, Hash, HashEngine}; + /// use std::str::FromStr; + /// use rustreexo::accumulator::{pollard::Pollard, proof::Proof}; + /// let acc = Pollard::new(); + /// // Creates a tree with those values as leafs + /// let test_values:Vec = vec![0, 1, 2, 3, 4, 5, 6, 7]; + /// // Targets are nodes witch we intend to prove + /// let targets = vec![0]; + /// + /// let mut proof_hashes = Vec::new(); + /// // This tree will look like this + /// // 14 + /// // |-----------------\ + /// // 12 13 + /// // |---------\ |--------\ + /// // 08 09 10 11 + /// // |----\ |----\ |----\ |----\ + /// // 00 01 02 03 04 05 06 07 + /// // For proving 0, we need 01, 09 and 13's hashes. 00, 08, 12 and 14 can be calculated + /// proof_hashes.push(Sha256::from_str("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a").unwrap()); + /// proof_hashes.push(Sha256::from_str("9576f4ade6e9bc3a6458b506ce3e4e890df29cb14cb5d3d887672aef55647a2b").unwrap()); + /// proof_hashes.push(Sha256::from_str("29590a14c1b09384b94a2c0e94bf821ca75b62eacebc47893397ca88e3bbcbd7").unwrap()); + /// + /// let mut hashes = Vec::new(); + /// for i in test_values { + /// let mut engine = Sha256::engine(); + /// engine.input(&[i]); + /// let hash = Sha256::from_engine(engine); + /// hashes.push(hash); + /// } + /// let acc = acc.modify(hashes.clone(), vec![]).unwrap(); + /// let p = Proof::new(targets, proof_hashes); + /// assert!(acc.verify(&vec![hashes[0]] , &p).expect("This proof is valid")); + ///``` + pub fn verify(&self, del_hashes: &[sha256::Hash], proof: &Proof) -> Result { + let roots = self + .roots + .iter() + .map(|root| root.get_data()) + .collect::>(); + proof.verify(del_hashes, &roots, self.leaves) + } /// Returns a reference to this acc roots. Usually, API consumers won't care much about /// roots, serialization should use standard APIs. pub fn get_roots(&self) -> &Vec { @@ -388,6 +435,10 @@ impl Pollard { } /// Proves that a given utxo is in the current Pollard pub fn prove(&self, targets: &[u64]) -> Result { + // Nothing to prove + if self.roots.is_empty() || targets.is_empty() { + return Ok(Proof::default()); + } let total_rows = tree_rows(self.leaves); let targets = detwin(targets.into(), total_rows); let needed_pos = get_proof_positions(&targets, self.leaves, total_rows); @@ -842,7 +893,8 @@ mod test { .iter() .map(|hash| Data::from_hex(hash).unwrap()) .collect(); - + // Just for feature compatibility test, do the same with a Stump and see if the proof + // passes let del_proof = Proof::new(vec![0, 3], del_proof_hashes); let stump = Stump::new() .modify(&hashes, &vec![], &Proof::default()) @@ -851,7 +903,7 @@ mod test { .modify(&vec![], &vec![hashes[0], hashes[3]], &del_proof) .unwrap() .0; - assert_eq!(proof.verify(&vec![hashes[5], hashes[7]], &stump), Ok(true)); + assert_eq!(stump.verify(&vec![hashes[5], hashes[7]], &proof), Ok(true)); } #[test] fn test_delete_non_root() { diff --git a/src/accumulator/proof.rs b/src/accumulator/proof.rs index aaa5901..4c37dcf 100644 --- a/src/accumulator/proof.rs +++ b/src/accumulator/proof.rs @@ -1,6 +1,6 @@ use super::stump::UpdateData; use super::util::get_proof_positions; -use crate::accumulator::{stump::Stump, types, util}; +use crate::accumulator::{types, util}; use bitcoin_hashes::{sha256, Hash}; #[derive(Debug, Default)] @@ -73,59 +73,29 @@ impl Proof { hashes: hashes, } } - /// Public interface for verifying proofs. Returns a result with a bool or an Error - /// True means the proof is true given the current stump, false means the proof is - /// not valid given the current stump. - ///# Examples - /// ``` - /// use bitcoin_hashes::{sha256::Hash as Sha256, Hash, HashEngine}; - /// use std::str::FromStr; - /// use rustreexo::accumulator::{stump::Stump, proof::Proof}; - /// let s = Stump::new(); - /// // Creates a tree with those values as leafs - /// let test_values:Vec = vec![0, 1, 2, 3, 4, 5, 6, 7]; - /// // Targets are nodes witch we intend to prove - /// let targets = vec![0]; - /// - /// let mut proof_hashes = Vec::new(); - /// // This tree will look like this - /// // 14 - /// // |-----------------\ - /// // 12 13 - /// // |---------\ |--------\ - /// // 08 09 10 11 - /// // |----\ |----\ |----\ |----\ - /// // 00 01 02 03 04 05 06 07 - /// // For proving 0, we need 01, 09 and 13's hashes. 00, 08, 12 and 14 can be calculated - /// proof_hashes.push(Sha256::from_str("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a").unwrap()); - /// proof_hashes.push(Sha256::from_str("9576f4ade6e9bc3a6458b506ce3e4e890df29cb14cb5d3d887672aef55647a2b").unwrap()); - /// proof_hashes.push(Sha256::from_str("29590a14c1b09384b94a2c0e94bf821ca75b62eacebc47893397ca88e3bbcbd7").unwrap()); - /// - /// let mut hashes = Vec::new(); - /// for i in test_values { - /// let mut engine = Sha256::engine(); - /// engine.input(&[i]); - /// let hash = Sha256::from_engine(engine); - /// hashes.push(hash); - /// } - /// let s = s.modify(&hashes, &vec![], &Proof::default()).unwrap().0; - /// let p = Proof::new(targets, proof_hashes); - /// assert!(p.verify(&vec![hashes[0]] , &s).expect("This proof is valid")); - ///``` - pub fn verify(&self, del_hashes: &Vec, stump: &Stump) -> Result { + /// Low level implementation for proof verification. We don't expose this method, + /// because different accumulators hold their roots in different formats. Since you need + /// those roots (and the number of leaves), you should call verify from a given acc, and this + /// acc will call this method in the appropriated way + pub(crate) fn verify( + &self, + del_hashes: &[sha256::Hash], + roots: &[sha256::Hash], + leaves: u64, + ) -> Result { if self.targets.len() == 0 { return Ok(true); } let mut calculated_roots = self - .calculate_hashes(del_hashes, stump)? + .calculate_hashes(del_hashes, leaves)? .1 .into_iter() .peekable(); let mut number_matched_roots = 0; - for root in stump.roots.iter().rev() { + for root in roots.iter().rev() { if let Some(next_calculated_root) = calculated_roots.peek() { if *next_calculated_root == *root { number_matched_roots += 1; @@ -153,15 +123,15 @@ impl Proof { /// passing a `bitcoin_hashes::sha256::Hash::all_zeros()` instead of the actual hash. pub(crate) fn calculate_hashes( &self, - del_hashes: &Vec, - stump: &Stump, + del_hashes: &[sha256::Hash], + leaves: u64, ) -> Result<(Vec<(u64, sha256::Hash)>, Vec), String> { // Where all the root hashes that we've calculated will go to. - let total_rows = util::tree_rows(stump.leafs); + let total_rows = util::tree_rows(leaves); // Where all the parent hashes we've calculated in a given row will go to. let mut calculated_root_hashes = - Vec::::with_capacity(util::num_roots(stump.leafs) as usize); + Vec::::with_capacity(util::num_roots(leaves) as usize); // As we calculate nodes upwards, it accumulates here let mut nodes: Vec<_> = self @@ -188,7 +158,7 @@ impl Proof { while let Some((pos, hash)) = row_nodes.next() { let next_to_prove = util::parent(pos, total_rows); // If the current position is a root, we add that to our result and don't go any further - if util::is_root_position(pos, stump.leafs, total_rows) { + if util::is_root_position(pos, leaves, total_rows) { calculated_root_hashes.push(hash); continue; } @@ -665,7 +635,7 @@ mod tests { ) .unwrap(); - let res = cached_proof.verify(&cached_hashes, &stump); + let res = stump.verify(&cached_hashes, &cached_proof); let expected_roots: Vec<_> = case_values .expected_roots @@ -830,7 +800,7 @@ mod tests { ) .unwrap(); - let res = new_proof.verify(&vec![hash_from_u8(0), hash_from_u8(7)], &stump); + let res = stump.verify(&vec![hash_from_u8(0), hash_from_u8(7)], &new_proof); assert_eq!(res, Ok(true)); } fn hash_from_u8(value: u8) -> bitcoin_hashes::sha256::Hash { @@ -900,7 +870,7 @@ mod tests { .map(|hash| bitcoin_hashes::sha256::Hash::from_str(hash).unwrap()) .zip(&expected_pos); - let calculated = p.calculate_hashes(&del_hashes, &s); + let calculated = p.calculate_hashes(&del_hashes, s.leafs); // We don't expect any errors from this simple test assert!(calculated.is_ok()); @@ -940,7 +910,7 @@ mod tests { .target_preimages .into_iter() .map(|target| hash_from_u8(target)) - .collect(); + .collect::>(); let proof_hashes = case .proofhashes @@ -951,7 +921,7 @@ mod tests { let p = Proof::new(targets, proof_hashes); let expected = case.expected; - let res = p.verify(&del_hashes, &s); + let res = s.verify(&del_hashes, &p); assert!(Ok(expected) == res); } #[test] diff --git a/src/accumulator/stump.rs b/src/accumulator/stump.rs index d47b2e1..b1b1e4c 100644 --- a/src/accumulator/stump.rs +++ b/src/accumulator/stump.rs @@ -32,6 +32,48 @@ impl Stump { roots: Vec::new(), } } + /// Public interface for verifying proofs. Returns a result with a bool or an Error + /// True means the proof is true given the current stump, false means the proof is + /// not valid given the current stump. + ///# Examples + /// ``` + /// use bitcoin_hashes::{sha256::Hash as Sha256, Hash, HashEngine}; + /// use std::str::FromStr; + /// use rustreexo::accumulator::{stump::Stump, proof::Proof}; + /// let s = Stump::new(); + /// // Creates a tree with those values as leafs + /// let test_values:Vec = vec![0, 1, 2, 3, 4, 5, 6, 7]; + /// // Targets are nodes witch we intend to prove + /// let targets = vec![0]; + /// + /// let mut proof_hashes = Vec::new(); + /// // This tree will look like this + /// // 14 + /// // |-----------------\ + /// // 12 13 + /// // |---------\ |--------\ + /// // 08 09 10 11 + /// // |----\ |----\ |----\ |----\ + /// // 00 01 02 03 04 05 06 07 + /// // For proving 0, we need 01, 09 and 13's hashes. 00, 08, 12 and 14 can be calculated + /// proof_hashes.push(Sha256::from_str("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a").unwrap()); + /// proof_hashes.push(Sha256::from_str("9576f4ade6e9bc3a6458b506ce3e4e890df29cb14cb5d3d887672aef55647a2b").unwrap()); + /// proof_hashes.push(Sha256::from_str("29590a14c1b09384b94a2c0e94bf821ca75b62eacebc47893397ca88e3bbcbd7").unwrap()); + /// + /// let mut hashes = Vec::new(); + /// for i in test_values { + /// let mut engine = Sha256::engine(); + /// engine.input(&[i]); + /// let hash = Sha256::from_engine(engine); + /// hashes.push(hash); + /// } + /// let s = s.modify(&hashes, &vec![], &Proof::default()).unwrap().0; + /// let p = Proof::new(targets, proof_hashes); + /// assert!(s.verify(&vec![hashes[0]] , &p).expect("This proof is valid")); + ///``` + pub fn verify(&self, del_hashes: &[sha256::Hash], proof: &Proof) -> Result { + proof.verify(del_hashes, &self.roots, self.leafs) + } /// Modify is the external API to change the accumulator state. Since order /// matters, you can only modify, providing a list of utxos to be added, /// and txos to be removed, along with it's proof. Either may be @@ -56,7 +98,7 @@ impl Stump { proof: &Proof, ) -> Result<(Stump, UpdateData), String> { let mut root_candidates = proof - .calculate_hashes(del_hashes, self)? + .calculate_hashes(del_hashes, self.leafs)? .1 .into_iter() .rev() @@ -126,7 +168,7 @@ impl Stump { } let del_hashes = vec![sha256::Hash::all_zeros(); proof.targets()]; - proof.calculate_hashes(&del_hashes, self) + proof.calculate_hashes(&del_hashes, self.leafs) } /// Adds new leafs into the root fn add( From 9ee2d7ae9df6229f570aecf25929c30db004fb9c Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Tue, 31 Jan 2023 12:34:30 -0300 Subject: [PATCH 18/18] Add test for multiple adds/dels --- src/accumulator/pollard.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/accumulator/pollard.rs b/src/accumulator/pollard.rs index fc62ac8..da73503 100644 --- a/src/accumulator/pollard.rs +++ b/src/accumulator/pollard.rs @@ -527,7 +527,8 @@ impl Pollard { roots } /// Deletes nodes from the accumulator - fn delete(mut self, stxos: Vec) -> Result, String> { + fn delete(mut self, mut stxos: Vec) -> Result, String> { + stxos.sort(); let stxos = util::detwin(stxos, util::tree_rows(self.leaves)); for stxo in stxos { self.delete_single(stxo)?; @@ -996,7 +997,25 @@ mod test { .collect::>(); assert_eq!(expected_roots, roots, "Test case failed {:?}", case); } + #[test] + fn test_multiple_modify() { + let initial_values = [0_u8, 1, 2, 3, 4, 5, 6, 7, 8, 9] + .iter() + .map(|val| hash_from_u8(*val)) + .collect::>(); + let acc = Pollard::new().modify(initial_values, vec![]).unwrap(); + let acc = acc.modify(vec![], vec![0, 5, 7]).unwrap(); + let additional_values = [0_u8, 1, 2, 3, 4] + .iter() + .map(|val| hash_from_u8(*val)) + .collect::>(); + let acc = acc.modify(additional_values, vec![16, 18]).unwrap(); + assert_eq!( + acc.get_roots()[0].get_data().to_hex(), + String::from("4fa7bf57f49ac06a6a54c22d033a30abcde83f3244e04b94e161d6e99a530607") + ); + } #[test] fn run_tests_from_cases() { #[derive(Deserialize)]