diff --git a/packages/utreexo/src/lib.cairo b/packages/utreexo/src/lib.cairo index 1b1b80ca..402e16a5 100644 --- a/packages/utreexo/src/lib.cairo +++ b/packages/utreexo/src/lib.cairo @@ -2,11 +2,15 @@ pub mod vanilla { pub mod state; pub mod proof; pub mod accumulator; + #[cfg(test)] + mod accumulator_tests; } pub mod stump { pub mod state; pub mod proof; pub mod accumulator; + #[cfg(test)] + mod accumulator_tests; } pub mod test; diff --git a/packages/utreexo/src/stump/accumulator.cairo b/packages/utreexo/src/stump/accumulator.cairo index 370a7810..46cd219f 100644 --- a/packages/utreexo/src/stump/accumulator.cairo +++ b/packages/utreexo/src/stump/accumulator.cairo @@ -3,7 +3,6 @@ use crate::stump::state::UtreexoStumpState; use crate::stump::proof::{UtreexoBatchProof, UtreexoBatchProofTrait}; use crate::parent_hash; - #[generate_trait] pub impl StumpUtreexoAccumulatorImpl of StumpUtreexoAccumulator { /// Adds multiple items to the accumulator. @@ -50,25 +49,36 @@ pub impl StumpUtreexoAccumulatorImpl of StumpUtreexoAccumulator { fn verify( self: @UtreexoStumpState, proof: @UtreexoBatchProof, leaves: Span ) -> Result<(), ByteArray> { - let mut computed_roots = proof.compute_roots(leaves, *self.num_leaves)?; - let mut res = Result::Ok(()); - while let Option::Some(computed_root) = computed_roots.pop_front() { - let mut root_exists = false; - for root_opt in *self - .roots { - if let Option::Some(root) = *root_opt { - if root == computed_root { - root_exists = true; - break; - } + let computed_roots = proof.compute_roots(leaves, *self.num_leaves)?; + let num_computed = computed_roots.len(); + let mut their_roots = computed_roots.span(); + let mut our_roots = *self.roots; + let mut num_matched: usize = 0; + + while let Option::Some(their_root) = their_roots.pop_back() { + while let Option::Some(maybe_our_root) = our_roots.pop_front() { + if let Option::Some(our_root) = maybe_our_root { + if our_root == their_root { + num_matched += 1; + break; } - }; - if !root_exists { - res = Result::Err("Proof verification failed"); - break; + } } }; - res + + if num_matched != num_computed { + return Result::Err( + format!( + "Proof verification failed: computed {} roots, but only {} matched\nComputed: {:?}\nActual: {:?}", + num_computed, + num_matched, + computed_roots, + self.roots, + ) + ); + } + + Result::Ok(()) } /// Verifies that the specified leaves are part of the utreexo forest given the proof, @@ -76,29 +86,34 @@ pub impl StumpUtreexoAccumulatorImpl of StumpUtreexoAccumulator { fn verify_and_delete( self: @UtreexoStumpState, proof: @UtreexoBatchProof, leaves: Span ) -> Result { - let mut updated_roots = proof.compute_roots_with_deletion(leaves, *self.num_leaves)?; - let mut roots = array![]; + let mut computed_roots = proof + .compute_roots_with_deletion(leaves, *self.num_leaves)? + .span(); + let mut new_roots = array![]; - for root_opt in *self + // Note that roots are sorted top down + for maybe_root in *self .roots { - if let Option::Some(root) = root_opt { - if let Option::Some(updated_root) = updated_roots.get(0) { - let (old_root, new_root) = updated_root.unbox(); - if *root == *old_root { - updated_roots.pop_front().unwrap(); - roots.append(*new_root); + if let Option::Some(our_root) = maybe_root { + let computed_len = computed_roots.len(); + if computed_len != 0 { + // Computed roots are in reversed order (sorted bottom up) + let (their_root, new_root) = computed_roots[computed_len - 1]; + if our_root == their_root { + computed_roots.pop_back().unwrap(); + new_roots.append(*new_root); continue; } } } - roots.append(*root_opt); + new_roots.append(*maybe_root); }; - if !updated_roots.is_empty() { - return Result::Err("Proof verification failed"); + if !computed_roots.is_empty() { + return Result::Err("Proof verification / leaf deletion failed"); } - Result::Ok(UtreexoStumpState { roots: roots.span(), num_leaves: *self.num_leaves }) + Result::Ok(UtreexoStumpState { roots: new_roots.span(), num_leaves: *self.num_leaves }) } /// Legacy implementation of proof verification. @@ -134,1079 +149,3 @@ pub impl StumpUtreexoAccumulatorImpl of StumpUtreexoAccumulator { Result::Ok(()) } } - -#[cfg(test)] -mod tests { - use super::{UtreexoStumpState, StumpUtreexoAccumulator, UtreexoBatchProof}; - - #[test] - fn test_verification_1_legacy() { - let state = UtreexoStumpState { - roots: array![ - Option::Some(0x371cb6995ea5e7effcd2e174de264b5b407027a75a231a70c2c8d196107f0e7) - ] - .span(), - num_leaves: 2 - }; - let proof = UtreexoBatchProof { targets: array![0].span(), proof: array![2].span() }; - let del_hashes = array![1]; - - let result = state.verify_legacy(@proof, del_hashes.span(),); - assert_eq!(result, Result::Ok(())); - } - - #[test] - fn test_verification_2_legacy() { - let state = UtreexoStumpState { - roots: array![ - Option::Some(0x1702d734e291ad551b886a70b96446b99e19e405511e71fb5edfc4d2d83ce92), - Option::Some(0x770ad1be69d195e821c8c35051b32492e71592e230b950a99ebf87e98967ca), - Option::Some(0x2392042cbfda7371c81c9d7b456563533c2d6998b9e690a0d97421e6ae51a98), - Option::Some(0xf), - ] - .span(), - num_leaves: 15 - }; - let proof = UtreexoBatchProof { - targets: array![1, 3, 10, 13].span(), - proof: array![ - 0x1, - 0x3, - 0xC, - 0xD, - 0x436e91732c0a83fa238d71460463f4b1fe0dc0b1ebcbc10967a84cec9d13154, - 0xdc9cc50aff0bdadd82a05bbab54015a07fccf2a4e30fa528fdca5a35d5423f - ] - .span() - }; - let del_hashes = array![2, 4, 11, 14]; - - let result = state.verify_legacy(@proof, del_hashes.span(),); - assert_eq!(result, Result::Ok(())); - } - - #[test] - fn test_verification_3_legacy() { - let state = UtreexoStumpState { - roots: array![ - Option::Some(0x519631921e4905a63203f0cca7f6e6917082f30cef0930aa05bdc4323f6a398), - Option::Some(0x5198dcd61c969dfa8396dd27439ab776d120c2d67294fbcded0aa5f658f9150), - Option::Some(0x21d7ab8efac0146b5b47c8ad5431c3d14d9210319b0be7428fb2382ef115671), - Option::Some(0x74f794e653e00357d8a8ed45fcb74659841190c0821aa4e20bc4e30b2f3dd20), - ] - .span(), - num_leaves: 30 - }; - let proof = UtreexoBatchProof { - targets: array![4, 8, 12, 16, 20, 24, 28].span(), - proof: array![ - 0x6, - 0xA, - 0xE, - 0x12, - 0x16, - 0x1A, - 0x1E, - 0x2797a40dbb8ea4b69a4e3bb4a9ccaa21a9585fcc71f3e5bb053ccae27910f90, - 0x7877cc14d4c8e76cc51aa4c49aa7aadaade0cf475ad63bb37c27c324e145393, - 0x556ea8bad1db13c6bdc3150a8289cd12044fb7e03cf201f35924a8afd4265a6, - 0x41a4ec75a27497daa51261588a60f0956d3fd61e521634bbf36bba6343c3a1b, - 0x3ba731d3734536d7cd5382cb4004ca4c24f1325b6fbeae27bcd6b4f9c0ed714, - 0x117ed04a65093683f13c16cf73d2855f1f099a96581d1dad74eaf34c9a343c8, - 0x79b32f615bbd57783700ae5f8e7b1ef79677c3545c4c69dc31b3aecce1d8fa6 - ] - .span() - }; - - let del_hashes = array![5, 9, 13, 17, 21, 25, 29]; - - let result = state.verify_legacy(@proof, del_hashes.span(),); - assert_eq!(result, Result::Ok(())); - } - - #[test] - fn test_verification_1() { - let state = UtreexoStumpState { - roots: array![ - Option::Some(0x371cb6995ea5e7effcd2e174de264b5b407027a75a231a70c2c8d196107f0e7) - ] - .span(), - num_leaves: 2 - }; - let proof = UtreexoBatchProof { targets: array![0].span(), proof: array![2].span() }; - let del_hashes = array![1]; - - let result = state.verify(@proof, del_hashes.span(),); - assert_eq!(result, Result::Ok(())); - } - - #[test] - fn test_verification_2() { - let state = UtreexoStumpState { - roots: array![ - Option::Some(0x1702d734e291ad551b886a70b96446b99e19e405511e71fb5edfc4d2d83ce92), - Option::Some(0x770ad1be69d195e821c8c35051b32492e71592e230b950a99ebf87e98967ca), - Option::Some(0x2392042cbfda7371c81c9d7b456563533c2d6998b9e690a0d97421e6ae51a98), - Option::Some(0xf), - ] - .span(), - num_leaves: 15 - }; - let proof = UtreexoBatchProof { - targets: array![1, 3, 10, 13].span(), - proof: array![ - 0x1, - 0x3, - 0xC, - 0xD, - 0x436e91732c0a83fa238d71460463f4b1fe0dc0b1ebcbc10967a84cec9d13154, - 0xdc9cc50aff0bdadd82a05bbab54015a07fccf2a4e30fa528fdca5a35d5423f - ] - .span() - }; - let del_hashes = array![2, 4, 11, 14]; - - let result = state.verify(@proof, del_hashes.span(),); - assert_eq!(result, Result::Ok(())); - } - - #[test] - fn test_verification_3() { - let state = UtreexoStumpState { - roots: array![ - Option::Some(0x519631921e4905a63203f0cca7f6e6917082f30cef0930aa05bdc4323f6a398), - Option::Some(0x5198dcd61c969dfa8396dd27439ab776d120c2d67294fbcded0aa5f658f9150), - Option::Some(0x21d7ab8efac0146b5b47c8ad5431c3d14d9210319b0be7428fb2382ef115671), - Option::Some(0x74f794e653e00357d8a8ed45fcb74659841190c0821aa4e20bc4e30b2f3dd20), - ] - .span(), - num_leaves: 30 - }; - let proof = UtreexoBatchProof { - targets: array![4, 8, 12, 16, 20, 24, 28].span(), - proof: array![ - 0x6, - 0xA, - 0xE, - 0x12, - 0x16, - 0x1A, - 0x1E, - 0x2797a40dbb8ea4b69a4e3bb4a9ccaa21a9585fcc71f3e5bb053ccae27910f90, - 0x7877cc14d4c8e76cc51aa4c49aa7aadaade0cf475ad63bb37c27c324e145393, - 0x556ea8bad1db13c6bdc3150a8289cd12044fb7e03cf201f35924a8afd4265a6, - 0x41a4ec75a27497daa51261588a60f0956d3fd61e521634bbf36bba6343c3a1b, - 0x3ba731d3734536d7cd5382cb4004ca4c24f1325b6fbeae27bcd6b4f9c0ed714, - 0x117ed04a65093683f13c16cf73d2855f1f099a96581d1dad74eaf34c9a343c8, - 0x79b32f615bbd57783700ae5f8e7b1ef79677c3545c4c69dc31b3aecce1d8fa6 - ] - .span() - }; - - let del_hashes = array![5, 9, 13, 17, 21, 25, 29]; - - let result = state.verify(@proof, del_hashes.span(),); - assert_eq!(result, Result::Ok(())); - } - - #[test] - fn test_verification_4() { - let state = UtreexoStumpState { - roots: array![ - Option::None, - Option::None, - Option::None, - Option::None, - Option::Some( - 2778277074578782368986165095004756321440748237082580104984033528445453379385 - ) - ] - .span(), - num_leaves: 16 - }; - - assert_eq!( - state - .verify( - @UtreexoBatchProof { - targets: array![0].span(), - proof: array![ - 1, - 359114454570462701179676018441683730149326686283278794303413350979946254235, - 2920447154653459698578961030005574439730780339384884329678783637696763668074, - 361638375607381126502342109490869021742109399020176111227133094637834041180, - ] - .span() - }, - array![0].span() - ), - Result::Ok(()) - ); - - assert_eq!( - state - .verify( - @UtreexoBatchProof { - targets: array![15].span(), - proof: array![ - 14, - 3009277733733429566191908933097273596911357184294896938062356457901603258437, - 2224391216833402212724735773212940252356536072547264452695587483243907176367, - 1010147253610699894986241683624609428739638206544914327400350896031110287324, - ] - .span() - }, - array![15].span() - ), - Result::Ok(()) - ); - - assert_eq!( - state - .verify( - @UtreexoBatchProof { - targets: array![0, 2, 5].span(), - proof: array![ - 1, - 3, - 4, - 2476911194812244264213538976037850550079366744233323933541290896048104351430, - 361638375607381126502342109490869021742109399020176111227133094637834041180, - ] - .span() - }, - array![0, 2, 5].span() - ), - Result::Ok(()) - ); - - assert_eq!( - state - .verify( - @UtreexoBatchProof { - targets: array![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] - .span(), - proof: array![].span() - }, - array![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15].span() - ), - Result::Ok(()) - ); - - assert_eq!( - state - .verify( - @UtreexoBatchProof { - targets: array![16, 2, 5].span(), - proof: array![ - 3, - 4, - 2476911194812244264213538976037850550079366744233323933541290896048104351430, - 361638375607381126502342109490869021742109399020176111227133094637834041180, - ] - .span() - }, - array![ - 2808234728617536643410270558566096818021668177546200472079927509023409591575, - 2, - 5 - ] - .span() - ), - Result::Ok(()) - ); - - assert_eq!( - state - .verify( - @UtreexoBatchProof { - targets: array![24, 18, 10, 15].span(), - proof: array![ - 11, - 14, - 2476911194812244264213538976037850550079366744233323933541290896048104351430, - 3115762988631556491925147498418117978906005591291390166955707455104569660364, - 3009277733733429566191908933097273596911357184294896938062356457901603258437, - ] - .span() - }, - array![ - 1229371501456391789924831928153470943555736434402105893904574254763197682709, - 3613143053999770272842665473502706307602279676970981102529837756866990003067, - 10, - 15, - ] - .span() - ), - Result::Ok(()) - ); - - assert_eq!( - state - .verify( - @UtreexoBatchProof { - targets: array![24, 29].span(), - proof: array![ - 2920447154653459698578961030005574439730780339384884329678783637696763668074, - ] - .span() - }, - array![ - 1229371501456391789924831928153470943555736434402105893904574254763197682709, - 361638375607381126502342109490869021742109399020176111227133094637834041180, - ] - .span() - ), - Result::Ok(()) - ); - - assert_eq!( - state - .verify( - @UtreexoBatchProof { targets: array![28, 29].span(), proof: array![].span() }, - array![ - 1010147253610699894986241683624609428739638206544914327400350896031110287324, - 361638375607381126502342109490869021742109399020176111227133094637834041180, - ] - .span() - ), - Result::Ok(()) - ); - - assert_eq!( - state - .verify( - @UtreexoBatchProof { targets: array![30].span(), proof: array![].span() }, - array![ - 2778277074578782368986165095004756321440748237082580104984033528445453379385, - ] - .span() - ), - Result::Ok(()) - ); - } - - #[test] - fn test_verification_5() { - let state = UtreexoStumpState { - roots: array![ - Option::Some(14), - Option::Some( - 3009277733733429566191908933097273596911357184294896938062356457901603258437 - ), - Option::Some( - 2224391216833402212724735773212940252356536072547264452695587483243907176367 - ), - Option::Some( - 1010147253610699894986241683624609428739638206544914327400350896031110287324 - ) - ] - .span(), - num_leaves: 15 - }; - - assert_eq!( - state - .verify( - @UtreexoBatchProof { - targets: array![0].span(), - proof: array![ - 1, - 359114454570462701179676018441683730149326686283278794303413350979946254235, - 2920447154653459698578961030005574439730780339384884329678783637696763668074, - ] - .span() - }, - array![0].span() - ), - Result::Ok(()) - ); - - assert_eq!( - state - .verify( - @UtreexoBatchProof { targets: array![14].span(), proof: array![].span() }, - array![14].span() - ), - Result::Ok(()) - ); - - assert_eq!( - state - .verify( - @UtreexoBatchProof { targets: array![13].span(), proof: array![12].span() }, - array![13].span() - ), - Result::Ok(()) - ); - - assert_eq!( - state - .verify( - @UtreexoBatchProof { - targets: array![0, 2, 5, 14].span(), - proof: array![ - 1, - 3, - 4, - 2476911194812244264213538976037850550079366744233323933541290896048104351430, - ] - .span() - }, - array![0, 2, 5, 14].span() - ), - Result::Ok(()) - ); - - assert_eq!( - state - .verify( - @UtreexoBatchProof { - targets: array![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14].span(), - proof: array![].span() - }, - array![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14].span() - ), - Result::Ok(()) - ); - - assert_eq!( - state - .verify( - @UtreexoBatchProof { - targets: array![16, 2, 5, 14].span(), - proof: array![ - 3, - 4, - 2476911194812244264213538976037850550079366744233323933541290896048104351430, - ] - .span() - }, - array![ - 2808234728617536643410270558566096818021668177546200472079927509023409591575, - 2, - 5, - 14 - ] - .span() - ), - Result::Ok(()) - ); - - assert_eq!( - state - .verify( - @UtreexoBatchProof { targets: array![24, 25].span(), proof: array![].span() }, - array![ - 1229371501456391789924831928153470943555736434402105893904574254763197682709, - 2920447154653459698578961030005574439730780339384884329678783637696763668074, - ] - .span() - ), - Result::Ok(()) - ); - - assert_eq!( - state - .verify( - @UtreexoBatchProof { - targets: array![24, 26].span(), - proof: array![ - 2920447154653459698578961030005574439730780339384884329678783637696763668074 - ] - .span() - }, - array![ - 1229371501456391789924831928153470943555736434402105893904574254763197682709, - 2224391216833402212724735773212940252356536072547264452695587483243907176367, - ] - .span() - ), - Result::Ok(()) - ); - - assert_eq!( - state - .verify( - @UtreexoBatchProof { targets: array![28].span(), proof: array![].span() }, - array![ - 1010147253610699894986241683624609428739638206544914327400350896031110287324, - ] - .span() - ), - Result::Ok(()) - ); - } - - #[test] - fn test_deletion_1_1() { - let state = UtreexoStumpState { roots: array![Option::Some('a')].span(), num_leaves: 1 }; - - let proof = UtreexoBatchProof { targets: array![].span(), proof: array![].span() }; - - let leaves = array![]; - - let new_state = state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification failed'); - - assert_eq!(new_state.num_leaves, 1); - assert_eq!(new_state.roots, array![Option::Some('a')].span()); - } - - #[test] - fn test_deletion_1_2() { - let state = UtreexoStumpState { roots: array![Option::Some('a')].span(), num_leaves: 1 }; - - let proof = UtreexoBatchProof { targets: array![0].span(), proof: array![].span() }; - - let leaves = array!['a']; - - let new_state = state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification failed'); - - assert_eq!(new_state.num_leaves, 1); - assert_eq!(new_state.roots, array![Option::None].span()); - } - - #[test] - fn test_deletion_8_1() { - let state = UtreexoStumpState { - roots: array![ - Option::None, - Option::None, - Option::None, - Option::Some( - 555370024363052154426534073187628105642230667669796211052131558433241164031 - ) - ] - .span(), - num_leaves: 8 - }; - - let proof = UtreexoBatchProof { - targets: array![0].span(), - proof: array![ - 'b', - 1702961261074558847535372708423978610134065667337563473891781271138689292959, - 1970675917964935639615849678644334216784892342767290630432190461589093258001 - ] - .span() - }; - - let leaves = array!['a']; - - let new_state = state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification failed'); - - assert_eq!(new_state.num_leaves, 8); - assert_eq!( - new_state.roots, - array![ - Option::None, - Option::None, - Option::None, - Option::Some( - 3345421944025286217980549018138297349958141750618054570424649972274280624201 - ), - ] - .span() - ); - } - - #[test] - fn test_deletion_8_2() { - let state = UtreexoStumpState { - roots: array![ - Option::None, - Option::None, - Option::None, - Option::Some( - 555370024363052154426534073187628105642230667669796211052131558433241164031 - ) - ] - .span(), - num_leaves: 8 - }; - - let proof = UtreexoBatchProof { - targets: array![0, 1].span(), - proof: array![ - 1702961261074558847535372708423978610134065667337563473891781271138689292959, - 1970675917964935639615849678644334216784892342767290630432190461589093258001 - ] - .span() - }; - - let leaves = array!['a', 'b']; - - let new_state = state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification failed'); - - assert_eq!(new_state.num_leaves, 8); - assert_eq!( - new_state.roots, - array![ - Option::None, - Option::None, - Option::None, - Option::Some( - 2850064462912342803614938379907776937447719434508740518644801944855647065404 - ), - ] - .span() - ); - } - - #[test] - fn test_deletion_8_3() { - let state = UtreexoStumpState { - roots: array![ - Option::None, - Option::None, - Option::None, - Option::Some( - 555370024363052154426534073187628105642230667669796211052131558433241164031 - ) - ] - .span(), - num_leaves: 8 - }; - - let proof = UtreexoBatchProof { - targets: array![0, 1, 2].span(), - proof: array![ - 'd', 1970675917964935639615849678644334216784892342767290630432190461589093258001 - ] - .span() - }; - - let leaves = array!['a', 'b', 'c']; - - let new_state = state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification failed'); - - assert_eq!(new_state.num_leaves, 8); - assert_eq!( - new_state.roots, - array![ - Option::None, - Option::None, - Option::None, - Option::Some( - 3021045430882547327596950725247172779074256595054900316875788021760349090257 - ), - ] - .span() - ); - } - - #[test] - fn test_deletion_8_4() { - let state = UtreexoStumpState { - roots: array![ - Option::None, - Option::None, - Option::None, - Option::Some( - 555370024363052154426534073187628105642230667669796211052131558433241164031 - ) - ] - .span(), - num_leaves: 8 - }; - - let proof = UtreexoBatchProof { - targets: array![0, 1, 2, 3, 4, 5, 6].span(), proof: array!['h'].span() - }; - - let leaves = array!['a', 'b', 'c', 'd', 'e', 'f', 'g']; - - let new_state = state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification failed'); - - assert_eq!(new_state.num_leaves, 8); - assert_eq!( - new_state.roots, - array![Option::None, Option::None, Option::None, Option::Some('h')].span() - ); - } - - #[test] - fn test_deletion_8_5() { - let state = UtreexoStumpState { - roots: array![ - Option::None, - Option::None, - Option::None, - Option::Some( - 555370024363052154426534073187628105642230667669796211052131558433241164031 - ) - ] - .span(), - num_leaves: 8 - }; - - let proof = UtreexoBatchProof { - targets: array![0, 1, 2, 3, 4, 5, 6, 7].span(), proof: array![].span() - }; - - let leaves = array!['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; - - let new_state = state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification failed'); - - assert_eq!(new_state.num_leaves, 8); - assert_eq!( - new_state.roots, array![Option::None, Option::None, Option::None, Option::None].span() - ); - } - - #[test] - fn test_deletion_7_1() { - let state = UtreexoStumpState { - roots: array![ - Option::Some(103), - Option::Some( - 411226027934254579827031836427260754155547814753354544773152242745331653508 - ), - Option::Some( - 2858081605429646247202638041541950020464262679066089136848920825648707910133 - ) - ] - .span(), - num_leaves: 7 - }; - - let proof = UtreexoBatchProof { - targets: array![0].span(), - proof: array![ - 'b', 1702961261074558847535372708423978610134065667337563473891781271138689292959 - ] - .span() - }; - - let leaves = array!['a']; - - let new_state = state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification failed'); - - assert_eq!(new_state.num_leaves, 7); - assert_eq!( - new_state.roots, - array![ - Option::Some('g'), - Option::Some( - 411226027934254579827031836427260754155547814753354544773152242745331653508 - ), - Option::Some( - 1229642933490472299984329107953625663040621781839429280809499005981139964854 - ) - ] - .span() - ); - } - - #[test] - fn test_deletion_7_2() { - let state = UtreexoStumpState { - roots: array![ - Option::Some(103), - Option::Some( - 411226027934254579827031836427260754155547814753354544773152242745331653508 - ), - Option::Some( - 2858081605429646247202638041541950020464262679066089136848920825648707910133 - ) - ] - .span(), - num_leaves: 7 - }; - - let proof = UtreexoBatchProof { - targets: array![0, 2, 4, 6].span(), proof: array!['b', 'd', 'f'].span() - }; - - let leaves = array!['a', 'c', 'e', 'g']; - - let new_state = state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification failed'); - - assert_eq!(new_state.num_leaves, 7); - assert_eq!( - new_state.roots, - array![ - Option::None, - Option::Some('f'), - Option::Some( - 1012329657042087226062405582801477394059887655150284803644932492275110107160 - ) - ] - .span() - ); - } - - #[test] - fn test_deletion_7_3() { - let state = UtreexoStumpState { - roots: array![ - Option::Some(103), - Option::Some( - 411226027934254579827031836427260754155547814753354544773152242745331653508 - ), - Option::Some( - 2858081605429646247202638041541950020464262679066089136848920825648707910133 - ) - ] - .span(), - num_leaves: 7 - }; - - let proof = UtreexoBatchProof { - targets: array![1, 3, 5].span(), proof: array!['a', 'c', 'e'].span() - }; - - let leaves = array!['b', 'd', 'f']; - - let new_state = state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification failed'); - - assert_eq!(new_state.num_leaves, 7); - assert_eq!( - new_state.roots, - array![ - Option::Some('g'), - Option::Some('e'), - Option::Some( - 1442415957443312626722273931205158047965396421097404482753913691074484308909 - ) - ] - .span() - ); - } - - #[test] - fn test_deletion_7_4() { - let state = UtreexoStumpState { - roots: array![ - Option::Some(103), - Option::Some( - 411226027934254579827031836427260754155547814753354544773152242745331653508 - ), - Option::Some( - 2858081605429646247202638041541950020464262679066089136848920825648707910133 - ) - ] - .span(), - num_leaves: 7 - }; - - let proof = UtreexoBatchProof { - targets: array![0, 6].span(), - proof: array![ - 'b', 1702961261074558847535372708423978610134065667337563473891781271138689292959 - ] - .span() - }; - - let leaves = array!['a', 'g']; - - let new_state = state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification failed'); - - assert_eq!(new_state.num_leaves, 7); - assert_eq!( - new_state.roots, - array![ - Option::None, - Option::Some( - 411226027934254579827031836427260754155547814753354544773152242745331653508 - ), - Option::Some( - 1229642933490472299984329107953625663040621781839429280809499005981139964854 - ) - ] - .span() - ); - } - - #[test] - fn test_deletion_7_5() { - let state = UtreexoStumpState { - roots: array![ - Option::Some(103), - Option::Some( - 411226027934254579827031836427260754155547814753354544773152242745331653508 - ), - Option::Some( - 2858081605429646247202638041541950020464262679066089136848920825648707910133 - ) - ] - .span(), - num_leaves: 7 - }; - - let proof = UtreexoBatchProof { targets: array![4, 5, 6].span(), proof: array![].span() }; - - let leaves = array!['e', 'f', 'g']; - - let new_state = state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification failed'); - - assert_eq!(new_state.num_leaves, 7); - assert_eq!( - new_state.roots, - array![ - Option::None, - Option::None, - Option::Some( - 2858081605429646247202638041541950020464262679066089136848920825648707910133 - ) - ] - .span() - ); - } - - #[test] - fn test_deletion_7_6() { - let state = UtreexoStumpState { - roots: array![ - Option::Some(103), - Option::Some( - 411226027934254579827031836427260754155547814753354544773152242745331653508 - ), - Option::Some( - 2858081605429646247202638041541950020464262679066089136848920825648707910133 - ) - ] - .span(), - num_leaves: 7 - }; - - let proof = UtreexoBatchProof { - targets: array![0, 1, 2, 3, 4, 5, 6].span(), proof: array![].span() - }; - - let leaves = array!['a', 'b', 'c', 'd', 'e', 'f', 'g']; - - let new_state = state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification failed'); - - assert_eq!(new_state.num_leaves, 7); - assert_eq!(new_state.roots, array![Option::None, Option::None, Option::None].span()); - } - - #[test] - fn test_deletion_7_7() { - let state = UtreexoStumpState { - roots: array![ - Option::Some(103), - Option::Some( - 411226027934254579827031836427260754155547814753354544773152242745331653508 - ), - Option::Some( - 2858081605429646247202638041541950020464262679066089136848920825648707910133 - ) - ] - .span(), - num_leaves: 7 - }; - - // Remove 0 - - let proof = UtreexoBatchProof { - targets: array![0].span(), - proof: array![ - 'b', 1702961261074558847535372708423978610134065667337563473891781271138689292959 - ] - .span() - }; - - let leaves = array!['a']; - - let new_state = state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification 0 failed'); - - // Remove 1 - - let proof = UtreexoBatchProof { - targets: array![8].span(), - proof: array![ - 1702961261074558847535372708423978610134065667337563473891781271138689292959 - ] - .span() - }; - - let leaves = array!['b']; - - let new_state = new_state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification 1 failed'); - - // Remove 2 - - let proof = UtreexoBatchProof { targets: array![8].span(), proof: array!['d'].span() }; - - let leaves = array!['c']; - - let new_state = new_state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification 2 failed'); - - // Remove 3 - - let proof = UtreexoBatchProof { targets: array![12].span(), proof: array![].span() }; - - let leaves = array!['d']; - - let new_state = new_state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification 3 failed'); - - // Remove 4 - - let proof = UtreexoBatchProof { targets: array![4].span(), proof: array!['f'].span() }; - - let leaves = array!['e']; - - let new_state = new_state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification 4 failed'); - - // Remove 5 - - let proof = UtreexoBatchProof { targets: array![10].span(), proof: array![].span() }; - - let leaves = array!['f']; - - let new_state = new_state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification 5 failed'); - - // Remove 6 - - let proof = UtreexoBatchProof { targets: array![6].span(), proof: array![].span() }; - - let leaves = array!['g']; - - let new_state = new_state - .verify_and_delete(@proof, leaves.span()) - .expect('Verification 6 failed'); - - assert_eq!(new_state.num_leaves, 7); - assert_eq!(new_state.roots, array![Option::None, Option::None, Option::None].span()); - } -} diff --git a/packages/utreexo/src/stump/accumulator_tests.cairo b/packages/utreexo/src/stump/accumulator_tests.cairo new file mode 100644 index 00000000..9d9393ef --- /dev/null +++ b/packages/utreexo/src/stump/accumulator_tests.cairo @@ -0,0 +1,1042 @@ +use super::accumulator::StumpUtreexoAccumulator; +use super::proof::UtreexoBatchProof; +use super::state::UtreexoStumpState; + +#[test] +fn test_verification_1_legacy() { + let state = UtreexoStumpState { + roots: array![ + Option::Some(0x371cb6995ea5e7effcd2e174de264b5b407027a75a231a70c2c8d196107f0e7) + ] + .span(), + num_leaves: 2 + }; + let proof = UtreexoBatchProof { targets: array![0].span(), proof: array![2].span() }; + let del_hashes = array![1]; + + let result = state.verify_legacy(@proof, del_hashes.span(),); + assert_eq!(result, Result::Ok(())); +} + +#[test] +fn test_verification_2_legacy() { + let state = UtreexoStumpState { + roots: array![ + Option::Some(0x1702d734e291ad551b886a70b96446b99e19e405511e71fb5edfc4d2d83ce92), + Option::Some(0x770ad1be69d195e821c8c35051b32492e71592e230b950a99ebf87e98967ca), + Option::Some(0x2392042cbfda7371c81c9d7b456563533c2d6998b9e690a0d97421e6ae51a98), + Option::Some(0xf), + ] + .span(), + num_leaves: 15 + }; + let proof = UtreexoBatchProof { + targets: array![1, 3, 10, 13].span(), + proof: array![ + 0x1, + 0x3, + 0xC, + 0xD, + 0x436e91732c0a83fa238d71460463f4b1fe0dc0b1ebcbc10967a84cec9d13154, + 0xdc9cc50aff0bdadd82a05bbab54015a07fccf2a4e30fa528fdca5a35d5423f + ] + .span() + }; + let del_hashes = array![2, 4, 11, 14]; + + let result = state.verify_legacy(@proof, del_hashes.span(),); + assert_eq!(result, Result::Ok(())); +} + +#[test] +fn test_verification_3_legacy() { + let state = UtreexoStumpState { + roots: array![ + Option::Some(0x519631921e4905a63203f0cca7f6e6917082f30cef0930aa05bdc4323f6a398), + Option::Some(0x5198dcd61c969dfa8396dd27439ab776d120c2d67294fbcded0aa5f658f9150), + Option::Some(0x21d7ab8efac0146b5b47c8ad5431c3d14d9210319b0be7428fb2382ef115671), + Option::Some(0x74f794e653e00357d8a8ed45fcb74659841190c0821aa4e20bc4e30b2f3dd20), + ] + .span(), + num_leaves: 30 + }; + let proof = UtreexoBatchProof { + targets: array![4, 8, 12, 16, 20, 24, 28].span(), + proof: array![ + 0x6, + 0xA, + 0xE, + 0x12, + 0x16, + 0x1A, + 0x1E, + 0x2797a40dbb8ea4b69a4e3bb4a9ccaa21a9585fcc71f3e5bb053ccae27910f90, + 0x7877cc14d4c8e76cc51aa4c49aa7aadaade0cf475ad63bb37c27c324e145393, + 0x556ea8bad1db13c6bdc3150a8289cd12044fb7e03cf201f35924a8afd4265a6, + 0x41a4ec75a27497daa51261588a60f0956d3fd61e521634bbf36bba6343c3a1b, + 0x3ba731d3734536d7cd5382cb4004ca4c24f1325b6fbeae27bcd6b4f9c0ed714, + 0x117ed04a65093683f13c16cf73d2855f1f099a96581d1dad74eaf34c9a343c8, + 0x79b32f615bbd57783700ae5f8e7b1ef79677c3545c4c69dc31b3aecce1d8fa6 + ] + .span() + }; + + let del_hashes = array![5, 9, 13, 17, 21, 25, 29]; + + let result = state.verify_legacy(@proof, del_hashes.span(),); + assert_eq!(result, Result::Ok(())); +} + +#[test] +fn test_verification_1() { + let state = UtreexoStumpState { + roots: array![ + Option::Some(0x371cb6995ea5e7effcd2e174de264b5b407027a75a231a70c2c8d196107f0e7) + ] + .span(), + num_leaves: 2 + }; + let proof = UtreexoBatchProof { targets: array![0].span(), proof: array![2].span() }; + let del_hashes = array![1]; + + let result = state.verify(@proof, del_hashes.span(),); + assert_eq!(result, Result::Ok(())); +} + +#[test] +fn test_verification_2() { + let state = UtreexoStumpState { + roots: array![ + Option::Some(0x1702d734e291ad551b886a70b96446b99e19e405511e71fb5edfc4d2d83ce92), + Option::Some(0x770ad1be69d195e821c8c35051b32492e71592e230b950a99ebf87e98967ca), + Option::Some(0x2392042cbfda7371c81c9d7b456563533c2d6998b9e690a0d97421e6ae51a98), + Option::Some(0xf), + ] + .span(), + num_leaves: 15 + }; + let proof = UtreexoBatchProof { + targets: array![1, 3, 10, 13].span(), + proof: array![ + 0x1, + 0x3, + 0xC, + 0xD, + 0x436e91732c0a83fa238d71460463f4b1fe0dc0b1ebcbc10967a84cec9d13154, + 0xdc9cc50aff0bdadd82a05bbab54015a07fccf2a4e30fa528fdca5a35d5423f + ] + .span() + }; + let del_hashes = array![2, 4, 11, 14]; + + let result = state.verify(@proof, del_hashes.span(),); + assert_eq!(result, Result::Ok(())); +} + +#[test] +fn test_verification_3() { + let state = UtreexoStumpState { + roots: array![ + Option::Some(0x519631921e4905a63203f0cca7f6e6917082f30cef0930aa05bdc4323f6a398), + Option::Some(0x5198dcd61c969dfa8396dd27439ab776d120c2d67294fbcded0aa5f658f9150), + Option::Some(0x21d7ab8efac0146b5b47c8ad5431c3d14d9210319b0be7428fb2382ef115671), + Option::Some(0x74f794e653e00357d8a8ed45fcb74659841190c0821aa4e20bc4e30b2f3dd20), + ] + .span(), + num_leaves: 30 + }; + let proof = UtreexoBatchProof { + targets: array![4, 8, 12, 16, 20, 24, 28].span(), + proof: array![ + 0x6, + 0xA, + 0xE, + 0x12, + 0x16, + 0x1A, + 0x1E, + 0x2797a40dbb8ea4b69a4e3bb4a9ccaa21a9585fcc71f3e5bb053ccae27910f90, + 0x7877cc14d4c8e76cc51aa4c49aa7aadaade0cf475ad63bb37c27c324e145393, + 0x556ea8bad1db13c6bdc3150a8289cd12044fb7e03cf201f35924a8afd4265a6, + 0x41a4ec75a27497daa51261588a60f0956d3fd61e521634bbf36bba6343c3a1b, + 0x3ba731d3734536d7cd5382cb4004ca4c24f1325b6fbeae27bcd6b4f9c0ed714, + 0x117ed04a65093683f13c16cf73d2855f1f099a96581d1dad74eaf34c9a343c8, + 0x79b32f615bbd57783700ae5f8e7b1ef79677c3545c4c69dc31b3aecce1d8fa6 + ] + .span() + }; + + let del_hashes = array![5, 9, 13, 17, 21, 25, 29]; + + let result = state.verify(@proof, del_hashes.span(),); + assert_eq!(result, Result::Ok(())); +} + +#[test] +fn test_verification_4() { + let state = UtreexoStumpState { + roots: array![ + Option::Some( + 2778277074578782368986165095004756321440748237082580104984033528445453379385 + ), + Option::None, + Option::None, + Option::None, + Option::None, + ] + .span(), + num_leaves: 16 + }; + + assert_eq!( + state + .verify( + @UtreexoBatchProof { + targets: array![0].span(), + proof: array![ + 1, + 359114454570462701179676018441683730149326686283278794303413350979946254235, + 2920447154653459698578961030005574439730780339384884329678783637696763668074, + 361638375607381126502342109490869021742109399020176111227133094637834041180, + ] + .span() + }, + array![0].span() + ), + Result::Ok(()) + ); + + assert_eq!( + state + .verify( + @UtreexoBatchProof { + targets: array![15].span(), + proof: array![ + 14, + 3009277733733429566191908933097273596911357184294896938062356457901603258437, + 2224391216833402212724735773212940252356536072547264452695587483243907176367, + 1010147253610699894986241683624609428739638206544914327400350896031110287324, + ] + .span() + }, + array![15].span() + ), + Result::Ok(()) + ); + + assert_eq!( + state + .verify( + @UtreexoBatchProof { + targets: array![0, 2, 5].span(), + proof: array![ + 1, + 3, + 4, + 2476911194812244264213538976037850550079366744233323933541290896048104351430, + 361638375607381126502342109490869021742109399020176111227133094637834041180, + ] + .span() + }, + array![0, 2, 5].span() + ), + Result::Ok(()) + ); + + assert_eq!( + state + .verify( + @UtreexoBatchProof { + targets: array![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15].span(), + proof: array![].span() + }, + array![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15].span() + ), + Result::Ok(()) + ); + + assert_eq!( + state + .verify( + @UtreexoBatchProof { + targets: array![16, 2, 5].span(), + proof: array![ + 3, + 4, + 2476911194812244264213538976037850550079366744233323933541290896048104351430, + 361638375607381126502342109490869021742109399020176111227133094637834041180, + ] + .span() + }, + array![ + 2808234728617536643410270558566096818021668177546200472079927509023409591575, + 2, + 5 + ] + .span() + ), + Result::Ok(()) + ); + + assert_eq!( + state + .verify( + @UtreexoBatchProof { + targets: array![24, 18, 10, 15].span(), + proof: array![ + 11, + 14, + 2476911194812244264213538976037850550079366744233323933541290896048104351430, + 3115762988631556491925147498418117978906005591291390166955707455104569660364, + 3009277733733429566191908933097273596911357184294896938062356457901603258437, + ] + .span() + }, + array![ + 1229371501456391789924831928153470943555736434402105893904574254763197682709, + 3613143053999770272842665473502706307602279676970981102529837756866990003067, + 10, + 15, + ] + .span() + ), + Result::Ok(()) + ); + + assert_eq!( + state + .verify( + @UtreexoBatchProof { + targets: array![24, 29].span(), + proof: array![ + 2920447154653459698578961030005574439730780339384884329678783637696763668074, + ] + .span() + }, + array![ + 1229371501456391789924831928153470943555736434402105893904574254763197682709, + 361638375607381126502342109490869021742109399020176111227133094637834041180, + ] + .span() + ), + Result::Ok(()) + ); + + assert_eq!( + state + .verify( + @UtreexoBatchProof { targets: array![28, 29].span(), proof: array![].span() }, + array![ + 1010147253610699894986241683624609428739638206544914327400350896031110287324, + 361638375607381126502342109490869021742109399020176111227133094637834041180, + ] + .span() + ), + Result::Ok(()) + ); + + assert_eq!( + state + .verify( + @UtreexoBatchProof { targets: array![30].span(), proof: array![].span() }, + array![ + 2778277074578782368986165095004756321440748237082580104984033528445453379385, + ] + .span() + ), + Result::Ok(()) + ); +} + +#[test] +fn test_verification_5() { + let state = UtreexoStumpState { + roots: array![ + Option::Some( + 1010147253610699894986241683624609428739638206544914327400350896031110287324 + ), + Option::Some( + 2224391216833402212724735773212940252356536072547264452695587483243907176367 + ), + Option::Some( + 3009277733733429566191908933097273596911357184294896938062356457901603258437 + ), + Option::Some(14), + ] + .span(), + num_leaves: 15 + }; + + assert_eq!( + state + .verify( + @UtreexoBatchProof { + targets: array![0].span(), + proof: array![ + 1, + 359114454570462701179676018441683730149326686283278794303413350979946254235, + 2920447154653459698578961030005574439730780339384884329678783637696763668074, + ] + .span() + }, + array![0].span() + ), + Result::Ok(()) + ); + + assert_eq!( + state + .verify( + @UtreexoBatchProof { targets: array![14].span(), proof: array![].span() }, + array![14].span() + ), + Result::Ok(()) + ); + + assert_eq!( + state + .verify( + @UtreexoBatchProof { targets: array![13].span(), proof: array![12].span() }, + array![13].span() + ), + Result::Ok(()) + ); + + assert_eq!( + state + .verify( + @UtreexoBatchProof { + targets: array![0, 2, 5, 14].span(), + proof: array![ + 1, + 3, + 4, + 2476911194812244264213538976037850550079366744233323933541290896048104351430, + ] + .span() + }, + array![0, 2, 5, 14].span() + ), + Result::Ok(()) + ); + + assert_eq!( + state + .verify( + @UtreexoBatchProof { + targets: array![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14].span(), + proof: array![].span() + }, + array![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14].span() + ), + Result::Ok(()) + ); + + assert_eq!( + state + .verify( + @UtreexoBatchProof { + targets: array![16, 2, 5, 14].span(), + proof: array![ + 3, + 4, + 2476911194812244264213538976037850550079366744233323933541290896048104351430, + ] + .span() + }, + array![ + 2808234728617536643410270558566096818021668177546200472079927509023409591575, + 2, + 5, + 14 + ] + .span() + ), + Result::Ok(()) + ); + + assert_eq!( + state + .verify( + @UtreexoBatchProof { targets: array![24, 25].span(), proof: array![].span() }, + array![ + 1229371501456391789924831928153470943555736434402105893904574254763197682709, + 2920447154653459698578961030005574439730780339384884329678783637696763668074, + ] + .span() + ), + Result::Ok(()) + ); + + assert_eq!( + state + .verify( + @UtreexoBatchProof { + targets: array![24, 26].span(), + proof: array![ + 2920447154653459698578961030005574439730780339384884329678783637696763668074 + ] + .span() + }, + array![ + 1229371501456391789924831928153470943555736434402105893904574254763197682709, + 2224391216833402212724735773212940252356536072547264452695587483243907176367, + ] + .span() + ), + Result::Ok(()) + ); + + assert_eq!( + state + .verify( + @UtreexoBatchProof { targets: array![28].span(), proof: array![].span() }, + array![ + 1010147253610699894986241683624609428739638206544914327400350896031110287324, + ] + .span() + ), + Result::Ok(()) + ); +} + +#[test] +fn test_deletion_1_1() { + let state = UtreexoStumpState { roots: array![Option::Some('a')].span(), num_leaves: 1 }; + + let proof = UtreexoBatchProof { targets: array![].span(), proof: array![].span() }; + + let leaves = array![]; + + let new_state = state.verify_and_delete(@proof, leaves.span()).expect('Verification failed'); + + assert_eq!(new_state.num_leaves, 1); + assert_eq!(new_state.roots, array![Option::Some('a')].span()); +} + +#[test] +fn test_deletion_1_2() { + let state = UtreexoStumpState { roots: array![Option::Some('a')].span(), num_leaves: 1 }; + + let proof = UtreexoBatchProof { targets: array![0].span(), proof: array![].span() }; + + let leaves = array!['a']; + + let new_state = state.verify_and_delete(@proof, leaves.span()).expect('Verification failed'); + + assert_eq!(new_state.num_leaves, 1); + assert_eq!(new_state.roots, array![Option::None].span()); +} + +#[test] +fn test_deletion_8_1() { + let state = UtreexoStumpState { + roots: array![ + Option::Some( + 555370024363052154426534073187628105642230667669796211052131558433241164031 + ), + Option::None, + Option::None, + Option::None, + ] + .span(), + num_leaves: 8 + }; + + let proof = UtreexoBatchProof { + targets: array![0].span(), + proof: array![ + 'b', + 1702961261074558847535372708423978610134065667337563473891781271138689292959, + 1970675917964935639615849678644334216784892342767290630432190461589093258001 + ] + .span() + }; + + let leaves = array!['a']; + + let new_state = state.verify_and_delete(@proof, leaves.span()).expect('Verification failed'); + + assert_eq!(new_state.num_leaves, 8); + assert_eq!( + new_state.roots, + array![ + Option::Some( + 3345421944025286217980549018138297349958141750618054570424649972274280624201 + ), + Option::None, + Option::None, + Option::None, + ] + .span() + ); +} + +#[test] +fn test_deletion_8_2() { + let state = UtreexoStumpState { + roots: array![ + Option::Some( + 555370024363052154426534073187628105642230667669796211052131558433241164031 + ), + Option::None, + Option::None, + Option::None, + ] + .span(), + num_leaves: 8 + }; + + let proof = UtreexoBatchProof { + targets: array![0, 1].span(), + proof: array![ + 1702961261074558847535372708423978610134065667337563473891781271138689292959, + 1970675917964935639615849678644334216784892342767290630432190461589093258001 + ] + .span() + }; + + let leaves = array!['a', 'b']; + + let new_state = state.verify_and_delete(@proof, leaves.span()).expect('Verification failed'); + + assert_eq!(new_state.num_leaves, 8); + assert_eq!( + new_state.roots, + array![ + Option::Some( + 2850064462912342803614938379907776937447719434508740518644801944855647065404 + ), + Option::None, + Option::None, + Option::None, + ] + .span() + ); +} + +#[test] +fn test_deletion_8_3() { + let state = UtreexoStumpState { + roots: array![ + Option::Some( + 555370024363052154426534073187628105642230667669796211052131558433241164031 + ), + Option::None, + Option::None, + Option::None, + ] + .span(), + num_leaves: 8 + }; + + let proof = UtreexoBatchProof { + targets: array![0, 1, 2].span(), + proof: array![ + 'd', 1970675917964935639615849678644334216784892342767290630432190461589093258001 + ] + .span() + }; + + let leaves = array!['a', 'b', 'c']; + + let new_state = state.verify_and_delete(@proof, leaves.span()).expect('Verification failed'); + + assert_eq!(new_state.num_leaves, 8); + assert_eq!( + new_state.roots, + array![ + Option::Some( + 3021045430882547327596950725247172779074256595054900316875788021760349090257 + ), + Option::None, + Option::None, + Option::None, + ] + .span() + ); +} + +#[test] +fn test_deletion_8_4() { + let state = UtreexoStumpState { + roots: array![ + Option::Some( + 555370024363052154426534073187628105642230667669796211052131558433241164031 + ), + Option::None, + Option::None, + Option::None, + ] + .span(), + num_leaves: 8 + }; + + let proof = UtreexoBatchProof { + targets: array![0, 1, 2, 3, 4, 5, 6].span(), proof: array!['h'].span() + }; + + let leaves = array!['a', 'b', 'c', 'd', 'e', 'f', 'g']; + + let new_state = state.verify_and_delete(@proof, leaves.span()).expect('Verification failed'); + + assert_eq!(new_state.num_leaves, 8); + assert_eq!( + new_state.roots, array![Option::Some('h'), Option::None, Option::None, Option::None].span() + ); +} + +#[test] +fn test_deletion_8_5() { + let state = UtreexoStumpState { + roots: array![ + Option::Some( + 555370024363052154426534073187628105642230667669796211052131558433241164031 + ), + Option::None, + Option::None, + Option::None, + ] + .span(), + num_leaves: 8 + }; + + let proof = UtreexoBatchProof { + targets: array![0, 1, 2, 3, 4, 5, 6, 7].span(), proof: array![].span() + }; + + let leaves = array!['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; + + let new_state = state.verify_and_delete(@proof, leaves.span()).expect('Verification failed'); + + assert_eq!(new_state.num_leaves, 8); + assert_eq!( + new_state.roots, array![Option::None, Option::None, Option::None, Option::None].span() + ); +} + +#[test] +fn test_deletion_7_1() { + let state = UtreexoStumpState { + roots: array![ + Option::Some( + 2858081605429646247202638041541950020464262679066089136848920825648707910133 + ), + Option::Some( + 411226027934254579827031836427260754155547814753354544773152242745331653508 + ), + Option::Some(103), + ] + .span(), + num_leaves: 7 + }; + + let proof = UtreexoBatchProof { + targets: array![0].span(), + proof: array![ + 'b', 1702961261074558847535372708423978610134065667337563473891781271138689292959 + ] + .span() + }; + + let leaves = array!['a']; + + let new_state = state.verify_and_delete(@proof, leaves.span()).expect('Verification failed'); + + assert_eq!(new_state.num_leaves, 7); + assert_eq!( + new_state.roots, + array![ + Option::Some( + 1229642933490472299984329107953625663040621781839429280809499005981139964854 + ), + Option::Some( + 411226027934254579827031836427260754155547814753354544773152242745331653508 + ), + Option::Some('g'), + ] + .span() + ); +} + +#[test] +fn test_deletion_7_2() { + let state = UtreexoStumpState { + roots: array![ + Option::Some( + 2858081605429646247202638041541950020464262679066089136848920825648707910133 + ), + Option::Some( + 411226027934254579827031836427260754155547814753354544773152242745331653508 + ), + Option::Some(103), + ] + .span(), + num_leaves: 7 + }; + + let proof = UtreexoBatchProof { + targets: array![0, 2, 4, 6].span(), proof: array!['b', 'd', 'f'].span() + }; + + let leaves = array!['a', 'c', 'e', 'g']; + + let new_state = state.verify_and_delete(@proof, leaves.span()).expect('Verification failed'); + + assert_eq!(new_state.num_leaves, 7); + assert_eq!( + new_state.roots, + array![ + Option::Some( + 1012329657042087226062405582801477394059887655150284803644932492275110107160 + ), + Option::Some('f'), + Option::None, + ] + .span() + ); +} + +#[test] +fn test_deletion_7_3() { + let state = UtreexoStumpState { + roots: array![ + Option::Some( + 2858081605429646247202638041541950020464262679066089136848920825648707910133 + ), + Option::Some( + 411226027934254579827031836427260754155547814753354544773152242745331653508 + ), + Option::Some(103), + ] + .span(), + num_leaves: 7 + }; + + let proof = UtreexoBatchProof { + targets: array![1, 3, 5].span(), proof: array!['a', 'c', 'e'].span() + }; + + let leaves = array!['b', 'd', 'f']; + + let new_state = state.verify_and_delete(@proof, leaves.span()).expect('Verification failed'); + + assert_eq!(new_state.num_leaves, 7); + assert_eq!( + new_state.roots, + array![ + Option::Some( + 1442415957443312626722273931205158047965396421097404482753913691074484308909 + ), + Option::Some('e'), + Option::Some('g'), + ] + .span() + ); +} + +#[test] +fn test_deletion_7_4() { + let state = UtreexoStumpState { + roots: array![ + Option::Some( + 2858081605429646247202638041541950020464262679066089136848920825648707910133 + ), + Option::Some( + 411226027934254579827031836427260754155547814753354544773152242745331653508 + ), + Option::Some(103), + ] + .span(), + num_leaves: 7 + }; + + let proof = UtreexoBatchProof { + targets: array![0, 6].span(), + proof: array![ + 'b', 1702961261074558847535372708423978610134065667337563473891781271138689292959 + ] + .span() + }; + + let leaves = array!['a', 'g']; + + let new_state = state.verify_and_delete(@proof, leaves.span()).expect('Verification failed'); + + assert_eq!(new_state.num_leaves, 7); + assert_eq!( + new_state.roots, + array![ + Option::Some( + 1229642933490472299984329107953625663040621781839429280809499005981139964854 + ), + Option::Some( + 411226027934254579827031836427260754155547814753354544773152242745331653508 + ), + Option::None, + ] + .span() + ); +} + +#[test] +fn test_deletion_7_5() { + let state = UtreexoStumpState { + roots: array![ + Option::Some( + 2858081605429646247202638041541950020464262679066089136848920825648707910133 + ), + Option::Some( + 411226027934254579827031836427260754155547814753354544773152242745331653508 + ), + Option::Some(103), + ] + .span(), + num_leaves: 7 + }; + + let proof = UtreexoBatchProof { targets: array![4, 5, 6].span(), proof: array![].span() }; + + let leaves = array!['e', 'f', 'g']; + + let new_state = state.verify_and_delete(@proof, leaves.span()).expect('Verification failed'); + + assert_eq!(new_state.num_leaves, 7); + assert_eq!( + new_state.roots, + array![ + Option::Some( + 2858081605429646247202638041541950020464262679066089136848920825648707910133 + ), + Option::None, + Option::None, + ] + .span() + ); +} + +#[test] +fn test_deletion_7_6() { + let state = UtreexoStumpState { + roots: array![ + Option::Some( + 2858081605429646247202638041541950020464262679066089136848920825648707910133 + ), + Option::Some( + 411226027934254579827031836427260754155547814753354544773152242745331653508 + ), + Option::Some(103), + ] + .span(), + num_leaves: 7 + }; + + let proof = UtreexoBatchProof { + targets: array![0, 1, 2, 3, 4, 5, 6].span(), proof: array![].span() + }; + + let leaves = array!['a', 'b', 'c', 'd', 'e', 'f', 'g']; + + let new_state = state.verify_and_delete(@proof, leaves.span()).expect('Verification failed'); + + assert_eq!(new_state.num_leaves, 7); + assert_eq!(new_state.roots, array![Option::None, Option::None, Option::None].span()); +} + +#[test] +fn test_deletion_7_7() { + let state = UtreexoStumpState { + roots: array![ + Option::Some( + 2858081605429646247202638041541950020464262679066089136848920825648707910133 + ), + Option::Some( + 411226027934254579827031836427260754155547814753354544773152242745331653508 + ), + Option::Some(103), + ] + .span(), + num_leaves: 7 + }; + + // Remove 0 + + let proof = UtreexoBatchProof { + targets: array![0].span(), + proof: array![ + 'b', 1702961261074558847535372708423978610134065667337563473891781271138689292959 + ] + .span() + }; + + let leaves = array!['a']; + + let new_state = state.verify_and_delete(@proof, leaves.span()).expect('Verification 0 failed'); + + // Remove 1 + + let proof = UtreexoBatchProof { + targets: array![8].span(), + proof: array![1702961261074558847535372708423978610134065667337563473891781271138689292959] + .span() + }; + + let leaves = array!['b']; + + let new_state = new_state + .verify_and_delete(@proof, leaves.span()) + .expect('Verification 1 failed'); + + // Remove 2 + + let proof = UtreexoBatchProof { targets: array![8].span(), proof: array!['d'].span() }; + + let leaves = array!['c']; + + let new_state = new_state + .verify_and_delete(@proof, leaves.span()) + .expect('Verification 2 failed'); + + // Remove 3 + + let proof = UtreexoBatchProof { targets: array![12].span(), proof: array![].span() }; + + let leaves = array!['d']; + + let new_state = new_state + .verify_and_delete(@proof, leaves.span()) + .expect('Verification 3 failed'); + + // Remove 4 + + let proof = UtreexoBatchProof { targets: array![4].span(), proof: array!['f'].span() }; + + let leaves = array!['e']; + + let new_state = new_state + .verify_and_delete(@proof, leaves.span()) + .expect('Verification 4 failed'); + + // Remove 5 + + let proof = UtreexoBatchProof { targets: array![10].span(), proof: array![].span() }; + + let leaves = array!['f']; + + let new_state = new_state + .verify_and_delete(@proof, leaves.span()) + .expect('Verification 5 failed'); + + // Remove 6 + + let proof = UtreexoBatchProof { targets: array![6].span(), proof: array![].span() }; + + let leaves = array!['g']; + + let new_state = new_state + .verify_and_delete(@proof, leaves.span()) + .expect('Verification 6 failed'); + + assert_eq!(new_state.num_leaves, 7); + assert_eq!(new_state.roots, array![Option::None, Option::None, Option::None].span()); +} diff --git a/packages/utreexo/src/test.cairo b/packages/utreexo/src/test.cairo index 64d396e0..25d589bf 100644 --- a/packages/utreexo/src/test.cairo +++ b/packages/utreexo/src/test.cairo @@ -21,6 +21,14 @@ fn main(mut arguments: Span, _flags: felt252) { ) .expect('Failed to deserialize'); + match state.verify(@proof, leaves_to_del.span()) { + Result::Ok(_) => {}, + Result::Err(err) => { + println!("FAIL: gas_spent={} error='{:?}'", gas_before - get_available_gas(), err); + panic!(); + } + } + match state.verify_and_delete(@proof, leaves_to_del.span()) { Result::Ok(new_state) => { state = new_state; }, Result::Err(err) => { diff --git a/packages/utreexo/src/vanilla/accumulator.cairo b/packages/utreexo/src/vanilla/accumulator.cairo index 07ed2874..04d7f19a 100644 --- a/packages/utreexo/src/vanilla/accumulator.cairo +++ b/packages/utreexo/src/vanilla/accumulator.cairo @@ -92,342 +92,3 @@ pub impl VanillaUtreexoAccumulatorImpl of VanillaUtreexoAccumulator { } } } - -#[cfg(test)] -mod tests { - use super::{UtreexoState, VanillaUtreexoAccumulator, UtreexoProof}; - - /// Test the basic functionality of the Utreexo accumulator - /// - /// This test covers the following scenarios: - /// 1. Adding a single leaf and verifying it - /// 2. Adding a second leaf and verifying both leaves - /// 3. Adding a third leaf and verifying previous leaves - /// 4. Adding a fourth leaf and verifying all leaves - /// - /// For each scenario, the test: - /// - Initializes the UtreexoState with the appropriate roots and number of leaves - /// - Creates an OutPoint representing the leaf - /// - Constructs a UtreexoProof for the leaf - /// - Calls the verify function and asserts that it succeeds - /// - /// The test uses predefined txid values (0x111111..., 0x222222..., etc.) for simplicity. - /// It checks the correct root values at each stage of the Utreexo tree's growth. - #[test] - fn test_verify_inclusion() { - // Add the first leaf (0x111111111111111111111111) - let leaf1 = 0x111111111111111111111111; - - let mut utxo_state = UtreexoState { - roots: array![Option::Some(0x111111111111111111111111)].into(), - }; - let proof = UtreexoProof { leaf_index: 0, proof: array![].span(), }; - let result = utxo_state.verify(leaf1, @proof); - assert!(result.is_ok(), "Root at index 0 should be 0x111111111111111111111111"); - - // Add the second leaf (0x222222222222222222222222) - let leaf2 = 0x222222222222222222222222; - - utxo_state = - UtreexoState { - roots: array![ - Option::None, - Option::Some(0x05fb342b44641ae6d67310cf9da5566e1a398fd6b0121d40e2c5acd16e1ddb4a) - ] - .into(), - }; - - let proof = UtreexoProof { - leaf_index: 1, proof: array![0x111111111111111111111111].span(), - }; - let result = utxo_state.verify(leaf2, @proof); - assert!( - result.is_ok(), - "Root at index 1 should be 0x05fb342b44641ae6d67310cf9da5566e1a398fd6b0121d40e2c5acd16e1ddb4a" - ); - - // Add the third leaf (0x333333333333333333333333) - let leaf3 = 0x333333333333333333333333; - utxo_state = - UtreexoState { - roots: array![ - Option::Some(leaf3), - Option::Some(0x05fb342b44641ae6d67310cf9da5566e1a398fd6b0121d40e2c5acd16e1ddb4a) - ] - .into(), - }; - - let proof = UtreexoProof { - leaf_index: 1, proof: array![0x111111111111111111111111].span(), - }; - let result = utxo_state.verify(leaf2, @proof); - assert!( - result.is_ok(), - "Root at index 1 should be 0x05fb342b44641ae6d67310cf9da5566e1a398fd6b0121d40e2c5acd16e1ddb4a" - ); - - // Add the fourth leaf (0x444444444444444444444444) - - let leaf4 = 0x444444444444444444444444; - - utxo_state = - UtreexoState { - roots: array![ - Option::None, - Option::None, - Option::Some(0x018674e0c40577cb5ba4728d6ac7bedfd9548f4020161223261941b2a8ae84b2) - ] - .into(), - }; - - // Create the UtreexoProof for leaf 1 - let proof = UtreexoProof { - leaf_index: 0, - proof: array![ - 0x222222222222222222222222, - 0x02a6b2ae998d30e1ac356c32b2750c3126cd6b3ecf02e6918a93021d17b2b026 - ] - .span(), - }; - // Call the verify function - let result = utxo_state.verify(leaf1, @proof); - assert!(result.is_ok(), "verify leaf index 0 failed"); - // Create the UtreexoProof for leaf 2 - let proof = UtreexoProof { - leaf_index: 1, - proof: array![leaf1, 0x02a6b2ae998d30e1ac356c32b2750c3126cd6b3ecf02e6918a93021d17b2b026] - .span(), - }; - // Call the verify function - let result = utxo_state.verify(leaf2, @proof); - assert!(result.is_ok(), "verify leaf index 1 failed"); - - // Create the UtreexoProof for leaf 3 - let proof = UtreexoProof { - leaf_index: 2, - proof: array![leaf4, 0x05fb342b44641ae6d67310cf9da5566e1a398fd6b0121d40e2c5acd16e1ddb4a] - .span(), - }; - // Call the verify function - let result = utxo_state.verify(leaf3, @proof); - assert!(result.is_ok(), "verify leaf index 2 failed"); - - // Create the UtreexoProof for leaf 4 - let proof = UtreexoProof { - leaf_index: 3, - proof: array![leaf3, 0x05fb342b44641ae6d67310cf9da5566e1a398fd6b0121d40e2c5acd16e1ddb4a] - .span(), - }; - // Call the verify function - let result = utxo_state.verify(leaf4, @proof); - assert!(result.is_ok(), "verify leaf index 3 failed"); - - // Create the UtreexoProof for leaf 4 - let proof = UtreexoProof { - leaf_index: 3, - proof: array![leaf2, 0x05fb342b44641ae6d67310cf9da5566e1a398fd6b0121d40e2c5acd16e1ddb4a] - .span(), - }; - // Call the verify function - let result = utxo_state.verify(leaf4, @proof); - assert!(result.is_err(), "verify leaf index 3 should fail"); - } - - #[test] - fn test_utreexo_add() { - // Test data is generated using scripts/data/utreexo.py - - let mut utreexo_state: UtreexoState = Default::default(); - let outpoint: felt252 = 0x291F8F5FC449D42C715B529E542F24A80136D18F4A85DE28829CD3DCAAC1B9C; - - // add first leave to empty utreexo - utreexo_state = utreexo_state.add(outpoint); - - let expected: Span> = array![ - Option::Some(0x291F8F5FC449D42C715B529E542F24A80136D18F4A85DE28829CD3DCAAC1B9C), - Option::None - ] - .span(); - assert_eq!(utreexo_state.roots, expected, "cannot add first leave"); - - // add second leave - utreexo_state = utreexo_state.add(outpoint); - - let expected: Span> = array![ - Option::None, - Option::Some(0x738A7C495E564574993BBCB6A62D65C3C570BB81C63801066AF8934649F66F6), - Option::None - ] - .span(); - assert_eq!(utreexo_state.roots, expected, "cannot add second leave"); - - // add thirdth leave - utreexo_state = utreexo_state.add(outpoint); - - let expected: Span> = array![ - Option::Some(0x291F8F5FC449D42C715B529E542F24A80136D18F4A85DE28829CD3DCAAC1B9C), - Option::Some(0x738A7C495E564574993BBCB6A62D65C3C570BB81C63801066AF8934649F66F6), - Option::None - ] - .span(); - assert_eq!(utreexo_state.roots, expected, "cannot add thirdth leave"); - - // add fourth leave - utreexo_state = utreexo_state.add(outpoint); - - let expected: Span> = array![ - Option::None, - Option::None, - Option::Some(0x25D0DE35DD446E3D35504866FD7A04D4245E01B5908E19EAA70ABA84DD5A1F1), - Option::None - ] - .span(); - assert_eq!(utreexo_state.roots, expected, "cannot add fourth leave"); - - // add fifth leave - utreexo_state = utreexo_state.add(outpoint); - - let expected: Span> = array![ - Option::Some(0x291F8F5FC449D42C715B529E542F24A80136D18F4A85DE28829CD3DCAAC1B9C), - Option::None, - Option::Some(0x25D0DE35DD446E3D35504866FD7A04D4245E01B5908E19EAA70ABA84DD5A1F1), - Option::None - ] - .span(); - assert_eq!(utreexo_state.roots, expected, "cannot add fifth leave"); - - // add 3 leaves - for _ in 1..4_u8 { - utreexo_state = utreexo_state.add(outpoint); - }; - - let expected: Span> = array![ - Option::None, - Option::None, - Option::None, - Option::Some(0x708EB39E30B035376EC871F8F17CD3BADAE6A68406B13C3BB671009D56F5AD), - Option::None - ] - .span(); - assert_eq!(utreexo_state.roots, expected, "cannot add 3 leaves"); - - // add 22 leaves - for _ in 1..23_u8 { - utreexo_state = utreexo_state.add(outpoint); - }; - - let expected: Span> = [ - Option::None(()), - Option::Some(0x738A7C495E564574993BBCB6A62D65C3C570BB81C63801066AF8934649F66F6), - Option::Some(0x25D0DE35DD446E3D35504866FD7A04D4245E01B5908E19EAA70ABA84DD5A1F1), - Option::Some(0x708EB39E30B035376EC871F8F17CD3BADAE6A68406B13C3BB671009D56F5AD), - Option::Some(0x58D6BEF6CFC28638FB4C8271355961F50922BCC1577DD2B6D04E11B7A911702), - Option::None(()) - ].span(); - assert_eq!(utreexo_state.roots, expected, "cannot add 22 leaves"); - } - - #[test] - fn test_utreexo_delete() { - // Test data is generated using scripts/data/utreexo.py - - let mut utreexo_state: UtreexoState = Default::default(); - - // adds 16 leaves to empty utreexo - utreexo_state = utreexo_state - .add(0x111111111111111111111111) - .add(0x222222222222222222222222) - .add(0x333333333333333333333333) - .add(0x444444444444444444444444) - .add(0x555555555555555555555555) - .add(0x666666666666666666666666) - .add(0x777777777777777777777777) - .add(0x888888888888888888888888) - .add(0x999999999999999999999999) - .add(0xAAAAAAAAAAAAAAAAAAAAAAAA) - .add(0xBBBBBBBBBBBBBBBBBBBBBBBB) - .add(0xCCCCCCCCCCCCCCCCCCCCCCCC) - .add(0xEEEEEEEEEEEEEEEEEEEEEEEE) - .add(0xFFFFFFFFFFFFFFFFFFFFFFFF) - .add(0xFFFFFFFFFFFFFFFFFFFFFFF0) - .add(0xFFFFFFFFFFFFFFFFFFFFFFF1); - - let expected: Span> = array![ - Option::None, - Option::None, - Option::None, - Option::None, - Option::Some(0x03467aa210cc0b108229d9a7fc554f9175af4ee27ee08b128b96862f7beca2ea), - Option::None, - ] - .span(); - assert_eq!(utreexo_state.roots, expected, "cannot add second leave"); - - let proof_for_3rd_leaf: UtreexoProof = UtreexoProof { - leaf_index: 2, - proof: array![ - 0x0000000000000000000000000000000000000000444444444444444444444444, - 0x05fb342b44641ae6d67310cf9da5566e1a398fd6b0121d40e2c5acd16e1ddb4a, - 0x01670d29719eae8deaa34a1d75752368d180a2c3e53f08d344ad784f1a034be7, - 0x04448e395061d8b58524c81978a17837c66c7f3286ea3c1773c7fafd77d29f69 - ] - .span() - }; - - // deletes the 3rd leaf - utreexo_state = utreexo_state.delete(@proof_for_3rd_leaf); - - let expected: Span> = array![ - Option::Some(0x0000000000000000000000000000000000000000444444444444444444444444), - Option::Some(0x05fb342b44641ae6d67310cf9da5566e1a398fd6b0121d40e2c5acd16e1ddb4a), - Option::Some(0x01670d29719eae8deaa34a1d75752368d180a2c3e53f08d344ad784f1a034be7), - Option::Some(0x04448e395061d8b58524c81978a17837c66c7f3286ea3c1773c7fafd77d29f69), - Option::None, - Option::None, - ] - .span(); - - assert_eq!(utreexo_state.roots, expected, "cannot remove leave"); - } - - #[test] - fn test_utreexo_delete_2() { - // Test data is generated using scripts/data/utreexo.py - - let mut utreexo_state: UtreexoState = Default::default(); - - // adds 7 leaves to empty utreexo - utreexo_state = utreexo_state - .add(0x111111111111111111111111) - .add(0x222222222222222222222222) - .add(0x333333333333333333333333) - .add(0x444444444444444444444444) - .add(0x555555555555555555555555) - .add(0x666666666666666666666666) - .add(0x777777777777777777777777); - - let expected: Span> = array![ - Option::Some(0x0000000000000000000000000000000000000000777777777777777777777777), - Option::Some(0x0358bb901cdc1d0d68afdb06dfeb84f2472c254ea052a942d8640924386935a6), - Option::Some(0x018674e0c40577cb5ba4728d6ac7bedfd9548f4020161223261941b2a8ae84b2), - Option::None, - ] - .span(); - assert_eq!(utreexo_state.roots, expected, "cannot add second leave"); - - let proof: UtreexoProof = UtreexoProof { leaf_index: 6, proof: array![].span() }; - - // deletes the last added leaf which corresponds to the root at h=0 - utreexo_state = utreexo_state.delete(@proof); - - let expected: Span> = array![ - Option::None, - Option::Some(0x0358bb901cdc1d0d68afdb06dfeb84f2472c254ea052a942d8640924386935a6), - Option::Some(0x018674e0c40577cb5ba4728d6ac7bedfd9548f4020161223261941b2a8ae84b2), - Option::None, - ] - .span(); - - assert_eq!(utreexo_state.roots, expected, "cannot remove leave"); - } -} diff --git a/packages/utreexo/src/vanilla/accumulator_tests.cairo b/packages/utreexo/src/vanilla/accumulator_tests.cairo new file mode 100644 index 00000000..71818e4a --- /dev/null +++ b/packages/utreexo/src/vanilla/accumulator_tests.cairo @@ -0,0 +1,333 @@ +use super::accumulator::VanillaUtreexoAccumulator; +use super::proof::UtreexoProof; +use super::state::UtreexoState; + +/// Test the basic functionality of the Utreexo accumulator +/// +/// This test covers the following scenarios: +/// 1. Adding a single leaf and verifying it +/// 2. Adding a second leaf and verifying both leaves +/// 3. Adding a third leaf and verifying previous leaves +/// 4. Adding a fourth leaf and verifying all leaves +/// +/// For each scenario, the test: +/// - Initializes the UtreexoState with the appropriate roots and number of leaves +/// - Creates an OutPoint representing the leaf +/// - Constructs a UtreexoProof for the leaf +/// - Calls the verify function and asserts that it succeeds +/// +/// The test uses predefined txid values (0x111111..., 0x222222..., etc.) for simplicity. +/// It checks the correct root values at each stage of the Utreexo tree's growth. +#[test] +fn test_verify_inclusion() { + // Add the first leaf (0x111111111111111111111111) + let leaf1 = 0x111111111111111111111111; + + let mut utxo_state = UtreexoState { + roots: array![Option::Some(0x111111111111111111111111)].into(), + }; + let proof = UtreexoProof { leaf_index: 0, proof: array![].span(), }; + let result = utxo_state.verify(leaf1, @proof); + assert!(result.is_ok(), "Root at index 0 should be 0x111111111111111111111111"); + + // Add the second leaf (0x222222222222222222222222) + let leaf2 = 0x222222222222222222222222; + + utxo_state = + UtreexoState { + roots: array![ + Option::None, + Option::Some(0x05fb342b44641ae6d67310cf9da5566e1a398fd6b0121d40e2c5acd16e1ddb4a) + ] + .into(), + }; + + let proof = UtreexoProof { leaf_index: 1, proof: array![0x111111111111111111111111].span(), }; + let result = utxo_state.verify(leaf2, @proof); + assert!( + result.is_ok(), + "Root at index 1 should be 0x05fb342b44641ae6d67310cf9da5566e1a398fd6b0121d40e2c5acd16e1ddb4a" + ); + + // Add the third leaf (0x333333333333333333333333) + let leaf3 = 0x333333333333333333333333; + utxo_state = + UtreexoState { + roots: array![ + Option::Some(leaf3), + Option::Some(0x05fb342b44641ae6d67310cf9da5566e1a398fd6b0121d40e2c5acd16e1ddb4a) + ] + .into(), + }; + + let proof = UtreexoProof { leaf_index: 1, proof: array![0x111111111111111111111111].span(), }; + let result = utxo_state.verify(leaf2, @proof); + assert!( + result.is_ok(), + "Root at index 1 should be 0x05fb342b44641ae6d67310cf9da5566e1a398fd6b0121d40e2c5acd16e1ddb4a" + ); + + // Add the fourth leaf (0x444444444444444444444444) + + let leaf4 = 0x444444444444444444444444; + + utxo_state = + UtreexoState { + roots: array![ + Option::None, + Option::None, + Option::Some(0x018674e0c40577cb5ba4728d6ac7bedfd9548f4020161223261941b2a8ae84b2) + ] + .into(), + }; + + // Create the UtreexoProof for leaf 1 + let proof = UtreexoProof { + leaf_index: 0, + proof: array![ + 0x222222222222222222222222, + 0x02a6b2ae998d30e1ac356c32b2750c3126cd6b3ecf02e6918a93021d17b2b026 + ] + .span(), + }; + // Call the verify function + let result = utxo_state.verify(leaf1, @proof); + assert!(result.is_ok(), "verify leaf index 0 failed"); + // Create the UtreexoProof for leaf 2 + let proof = UtreexoProof { + leaf_index: 1, + proof: array![leaf1, 0x02a6b2ae998d30e1ac356c32b2750c3126cd6b3ecf02e6918a93021d17b2b026] + .span(), + }; + // Call the verify function + let result = utxo_state.verify(leaf2, @proof); + assert!(result.is_ok(), "verify leaf index 1 failed"); + + // Create the UtreexoProof for leaf 3 + let proof = UtreexoProof { + leaf_index: 2, + proof: array![leaf4, 0x05fb342b44641ae6d67310cf9da5566e1a398fd6b0121d40e2c5acd16e1ddb4a] + .span(), + }; + // Call the verify function + let result = utxo_state.verify(leaf3, @proof); + assert!(result.is_ok(), "verify leaf index 2 failed"); + + // Create the UtreexoProof for leaf 4 + let proof = UtreexoProof { + leaf_index: 3, + proof: array![leaf3, 0x05fb342b44641ae6d67310cf9da5566e1a398fd6b0121d40e2c5acd16e1ddb4a] + .span(), + }; + // Call the verify function + let result = utxo_state.verify(leaf4, @proof); + assert!(result.is_ok(), "verify leaf index 3 failed"); + + // Create the UtreexoProof for leaf 4 + let proof = UtreexoProof { + leaf_index: 3, + proof: array![leaf2, 0x05fb342b44641ae6d67310cf9da5566e1a398fd6b0121d40e2c5acd16e1ddb4a] + .span(), + }; + // Call the verify function + let result = utxo_state.verify(leaf4, @proof); + assert!(result.is_err(), "verify leaf index 3 should fail"); +} + +#[test] +fn test_utreexo_add() { + // Test data is generated using scripts/data/utreexo.py + + let mut utreexo_state: UtreexoState = Default::default(); + let outpoint: felt252 = 0x291F8F5FC449D42C715B529E542F24A80136D18F4A85DE28829CD3DCAAC1B9C; + + // add first leave to empty utreexo + utreexo_state = utreexo_state.add(outpoint); + + let expected: Span> = array![ + Option::Some(0x291F8F5FC449D42C715B529E542F24A80136D18F4A85DE28829CD3DCAAC1B9C), + Option::None + ] + .span(); + assert_eq!(utreexo_state.roots, expected, "cannot add first leave"); + + // add second leave + utreexo_state = utreexo_state.add(outpoint); + + let expected: Span> = array![ + Option::None, + Option::Some(0x738A7C495E564574993BBCB6A62D65C3C570BB81C63801066AF8934649F66F6), + Option::None + ] + .span(); + assert_eq!(utreexo_state.roots, expected, "cannot add second leave"); + + // add thirdth leave + utreexo_state = utreexo_state.add(outpoint); + + let expected: Span> = array![ + Option::Some(0x291F8F5FC449D42C715B529E542F24A80136D18F4A85DE28829CD3DCAAC1B9C), + Option::Some(0x738A7C495E564574993BBCB6A62D65C3C570BB81C63801066AF8934649F66F6), + Option::None + ] + .span(); + assert_eq!(utreexo_state.roots, expected, "cannot add thirdth leave"); + + // add fourth leave + utreexo_state = utreexo_state.add(outpoint); + + let expected: Span> = array![ + Option::None, + Option::None, + Option::Some(0x25D0DE35DD446E3D35504866FD7A04D4245E01B5908E19EAA70ABA84DD5A1F1), + Option::None + ] + .span(); + assert_eq!(utreexo_state.roots, expected, "cannot add fourth leave"); + + // add fifth leave + utreexo_state = utreexo_state.add(outpoint); + + let expected: Span> = array![ + Option::Some(0x291F8F5FC449D42C715B529E542F24A80136D18F4A85DE28829CD3DCAAC1B9C), + Option::None, + Option::Some(0x25D0DE35DD446E3D35504866FD7A04D4245E01B5908E19EAA70ABA84DD5A1F1), + Option::None + ] + .span(); + assert_eq!(utreexo_state.roots, expected, "cannot add fifth leave"); + + // add 3 leaves + for _ in 1..4_u8 { + utreexo_state = utreexo_state.add(outpoint); + }; + + let expected: Span> = array![ + Option::None, + Option::None, + Option::None, + Option::Some(0x708EB39E30B035376EC871F8F17CD3BADAE6A68406B13C3BB671009D56F5AD), + Option::None + ] + .span(); + assert_eq!(utreexo_state.roots, expected, "cannot add 3 leaves"); + + // add 22 leaves + for _ in 1..23_u8 { + utreexo_state = utreexo_state.add(outpoint); + }; + + let expected: Span> = [ + Option::None(()), + Option::Some(0x738A7C495E564574993BBCB6A62D65C3C570BB81C63801066AF8934649F66F6), + Option::Some(0x25D0DE35DD446E3D35504866FD7A04D4245E01B5908E19EAA70ABA84DD5A1F1), + Option::Some(0x708EB39E30B035376EC871F8F17CD3BADAE6A68406B13C3BB671009D56F5AD), + Option::Some(0x58D6BEF6CFC28638FB4C8271355961F50922BCC1577DD2B6D04E11B7A911702), + Option::None(()) + ].span(); + assert_eq!(utreexo_state.roots, expected, "cannot add 22 leaves"); +} + +#[test] +fn test_utreexo_delete() { + // Test data is generated using scripts/data/utreexo.py + + let mut utreexo_state: UtreexoState = Default::default(); + + // adds 16 leaves to empty utreexo + utreexo_state = utreexo_state + .add(0x111111111111111111111111) + .add(0x222222222222222222222222) + .add(0x333333333333333333333333) + .add(0x444444444444444444444444) + .add(0x555555555555555555555555) + .add(0x666666666666666666666666) + .add(0x777777777777777777777777) + .add(0x888888888888888888888888) + .add(0x999999999999999999999999) + .add(0xAAAAAAAAAAAAAAAAAAAAAAAA) + .add(0xBBBBBBBBBBBBBBBBBBBBBBBB) + .add(0xCCCCCCCCCCCCCCCCCCCCCCCC) + .add(0xEEEEEEEEEEEEEEEEEEEEEEEE) + .add(0xFFFFFFFFFFFFFFFFFFFFFFFF) + .add(0xFFFFFFFFFFFFFFFFFFFFFFF0) + .add(0xFFFFFFFFFFFFFFFFFFFFFFF1); + + let expected: Span> = array![ + Option::None, + Option::None, + Option::None, + Option::None, + Option::Some(0x03467aa210cc0b108229d9a7fc554f9175af4ee27ee08b128b96862f7beca2ea), + Option::None, + ] + .span(); + assert_eq!(utreexo_state.roots, expected, "cannot add second leave"); + + let proof_for_3rd_leaf: UtreexoProof = UtreexoProof { + leaf_index: 2, + proof: array![ + 0x0000000000000000000000000000000000000000444444444444444444444444, + 0x05fb342b44641ae6d67310cf9da5566e1a398fd6b0121d40e2c5acd16e1ddb4a, + 0x01670d29719eae8deaa34a1d75752368d180a2c3e53f08d344ad784f1a034be7, + 0x04448e395061d8b58524c81978a17837c66c7f3286ea3c1773c7fafd77d29f69 + ] + .span() + }; + + // deletes the 3rd leaf + utreexo_state = utreexo_state.delete(@proof_for_3rd_leaf); + + let expected: Span> = array![ + Option::Some(0x0000000000000000000000000000000000000000444444444444444444444444), + Option::Some(0x05fb342b44641ae6d67310cf9da5566e1a398fd6b0121d40e2c5acd16e1ddb4a), + Option::Some(0x01670d29719eae8deaa34a1d75752368d180a2c3e53f08d344ad784f1a034be7), + Option::Some(0x04448e395061d8b58524c81978a17837c66c7f3286ea3c1773c7fafd77d29f69), + Option::None, + Option::None, + ] + .span(); + + assert_eq!(utreexo_state.roots, expected, "cannot remove leave"); +} + +#[test] +fn test_utreexo_delete_2() { + // Test data is generated using scripts/data/utreexo.py + + let mut utreexo_state: UtreexoState = Default::default(); + + // adds 7 leaves to empty utreexo + utreexo_state = utreexo_state + .add(0x111111111111111111111111) + .add(0x222222222222222222222222) + .add(0x333333333333333333333333) + .add(0x444444444444444444444444) + .add(0x555555555555555555555555) + .add(0x666666666666666666666666) + .add(0x777777777777777777777777); + + let expected: Span> = array![ + Option::Some(0x0000000000000000000000000000000000000000777777777777777777777777), + Option::Some(0x0358bb901cdc1d0d68afdb06dfeb84f2472c254ea052a942d8640924386935a6), + Option::Some(0x018674e0c40577cb5ba4728d6ac7bedfd9548f4020161223261941b2a8ae84b2), + Option::None, + ] + .span(); + assert_eq!(utreexo_state.roots, expected, "cannot add second leave"); + + let proof: UtreexoProof = UtreexoProof { leaf_index: 6, proof: array![].span() }; + + // deletes the last added leaf which corresponds to the root at h=0 + utreexo_state = utreexo_state.delete(@proof); + + let expected: Span> = array![ + Option::None, + Option::Some(0x0358bb901cdc1d0d68afdb06dfeb84f2472c254ea052a942d8640924386935a6), + Option::Some(0x018674e0c40577cb5ba4728d6ac7bedfd9548f4020161223261941b2a8ae84b2), + Option::None, + ] + .span(); + + assert_eq!(utreexo_state.roots, expected, "cannot remove leave"); +} diff --git a/packages/utreexo/tests/data/cached_proof_test_case_0.json b/packages/utreexo/tests/data/cached_proof_test_case_0.json index 900432b4..baf0e1be 100644 --- a/packages/utreexo/tests/data/cached_proof_test_case_0.json +++ b/packages/utreexo/tests/data/cached_proof_test_case_0.json @@ -18,7 +18,10 @@ 1 ] }, - "leaves_to_del": [], + "leaves_to_del": [ + 0, + 1 + ], "leaves_to_add": [ 20, 21, diff --git a/packages/utreexo/tests/data/cached_proof_test_case_1.json b/packages/utreexo/tests/data/cached_proof_test_case_1.json index bfa464f9..904b89b4 100644 --- a/packages/utreexo/tests/data/cached_proof_test_case_1.json +++ b/packages/utreexo/tests/data/cached_proof_test_case_1.json @@ -20,7 +20,11 @@ 4 ] }, - "leaves_to_del": [], + "leaves_to_del": [ + 0, + 1, + 4 + ], "leaves_to_add": [ 8 ], diff --git a/packages/utreexo/tests/data/cached_proof_test_case_2.json b/packages/utreexo/tests/data/cached_proof_test_case_2.json index 41447912..2971e3b7 100644 --- a/packages/utreexo/tests/data/cached_proof_test_case_2.json +++ b/packages/utreexo/tests/data/cached_proof_test_case_2.json @@ -18,7 +18,9 @@ 4 ] }, - "leaves_to_del": [], + "leaves_to_del": [ + 4 + ], "leaves_to_add": [ 8 ], diff --git a/packages/utreexo/tests/data/cached_proof_test_case_3.json b/packages/utreexo/tests/data/cached_proof_test_case_3.json index cc1b7f10..7d3e623c 100644 --- a/packages/utreexo/tests/data/cached_proof_test_case_3.json +++ b/packages/utreexo/tests/data/cached_proof_test_case_3.json @@ -27,7 +27,12 @@ 7 ] }, - "leaves_to_del": [], + "leaves_to_del": [ + 4, + 5, + 6, + 7 + ], "leaves_to_add": [], "expected_state": { "roots": [ diff --git a/packages/utreexo/tests/data/cached_proof_test_case_4.json b/packages/utreexo/tests/data/cached_proof_test_case_4.json index bb15fe7c..23a5aa12 100644 --- a/packages/utreexo/tests/data/cached_proof_test_case_4.json +++ b/packages/utreexo/tests/data/cached_proof_test_case_4.json @@ -18,7 +18,9 @@ 0 ] }, - "leaves_to_del": [], + "leaves_to_del": [ + 0 + ], "leaves_to_add": [], "expected_state": { "roots": [ diff --git a/packages/utreexo/tests/data/cached_proof_test_case_5.json b/packages/utreexo/tests/data/cached_proof_test_case_5.json index 1092cfe1..b3f4d273 100644 --- a/packages/utreexo/tests/data/cached_proof_test_case_5.json +++ b/packages/utreexo/tests/data/cached_proof_test_case_5.json @@ -22,7 +22,10 @@ 6 ] }, - "leaves_to_del": [], + "leaves_to_del": [ + 3, + 6 + ], "leaves_to_add": [], "expected_state": { "roots": [ diff --git a/packages/utreexo/tests/data/cached_proof_test_case_6.json b/packages/utreexo/tests/data/cached_proof_test_case_6.json index fd5a2ab0..d1fef398 100644 --- a/packages/utreexo/tests/data/cached_proof_test_case_6.json +++ b/packages/utreexo/tests/data/cached_proof_test_case_6.json @@ -24,7 +24,10 @@ 6 ] }, - "leaves_to_del": [], + "leaves_to_del": [ + 3, + 6 + ], "leaves_to_add": [], "expected_state": { "roots": [ diff --git a/packages/utreexo/tests/data/cached_proof_test_case_7.json b/packages/utreexo/tests/data/cached_proof_test_case_7.json index 127aa66d..cc21de24 100644 --- a/packages/utreexo/tests/data/cached_proof_test_case_7.json +++ b/packages/utreexo/tests/data/cached_proof_test_case_7.json @@ -19,7 +19,10 @@ 5 ] }, - "leaves_to_del": [], + "leaves_to_del": [ + 4, + 5 + ], "leaves_to_add": [ 6, 7, diff --git a/packages/utreexo/tests/data/ignore b/packages/utreexo/tests/data/ignore index 3575a045..8b137891 100644 --- a/packages/utreexo/tests/data/ignore +++ b/packages/utreexo/tests/data/ignore @@ -1,21 +1 @@ -deletion_test_case_3.json -deletion_test_case_4.json -deletion_test_case_5.json -deletion_test_case_6.json -deletion_test_case_10.json -update_data_test_case_0.json -update_data_test_case_1.json -update_data_test_case_2.json -update_data_test_case_3.json -update_data_test_case_4.json -update_data_test_case_5.json -update_data_test_case_6.json -update_data_test_case_7.json -cached_proof_test_case_0.json -cached_proof_test_case_1.json -cached_proof_test_case_2.json -cached_proof_test_case_3.json -cached_proof_test_case_4.json -cached_proof_test_case_5.json -cached_proof_test_case_6.json -cached_proof_test_case_7.json + diff --git a/packages/utreexo/tests/data/update_data_test_case_5.json b/packages/utreexo/tests/data/update_data_test_case_5.json index de153bb3..e6eaaef6 100644 --- a/packages/utreexo/tests/data/update_data_test_case_5.json +++ b/packages/utreexo/tests/data/update_data_test_case_5.json @@ -21,7 +21,12 @@ 3 ] }, - "leaves_to_del": [], + "leaves_to_del": [ + 0, + 1, + 2, + 3 + ], "leaves_to_add": [ 0, 1, diff --git a/packages/utreexo/tests/data/update_data_test_case_6.json b/packages/utreexo/tests/data/update_data_test_case_6.json index f87dbb96..2ab028ba 100644 --- a/packages/utreexo/tests/data/update_data_test_case_6.json +++ b/packages/utreexo/tests/data/update_data_test_case_6.json @@ -26,7 +26,13 @@ 14 ] }, - "leaves_to_del": [], + "leaves_to_del": [ + 0, + 2, + 7, + 12, + 14 + ], "leaves_to_add": [ 0, 1, diff --git a/packages/utreexo/tests/data/update_data_test_case_7.json b/packages/utreexo/tests/data/update_data_test_case_7.json index 59c755ad..1db31980 100644 --- a/packages/utreexo/tests/data/update_data_test_case_7.json +++ b/packages/utreexo/tests/data/update_data_test_case_7.json @@ -33,7 +33,16 @@ 16 ] }, - "leaves_to_del": [], + "leaves_to_del": [ + 0, + 2, + 7, + 10, + 12, + 14, + 15, + 16 + ], "leaves_to_add": [ 0, 1,