From db947c55f1ac822ff38a2c8e0c7afbe3eb2d4302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Thu, 28 Mar 2024 23:15:13 +0000 Subject: [PATCH 1/3] feat(circuits): add tests and Noir circuits for pedersen and poseidon2 proving --- package.json | 1 + packages/circuits/noir/Nargo.toml | 2 +- .../{smt_bn254 => smt_pedersen}/Nargo.toml | 2 +- .../noir/crates/smt_pedersen/src/lib.nr | 200 ++++++++++++ .../noir/crates/smt_pedersen/src/utils.nr | 118 +++++++ .../noir/crates/smt_poseidon/Nargo.toml | 5 + .../{smt_bn254 => smt_poseidon}/src/lib.nr | 2 +- .../{smt_bn254 => smt_poseidon}/src/utils.nr | 0 .../noir/crates/smt_poseidon2/Nargo.toml | 5 + .../noir/crates/smt_poseidon2/src/lib.nr | 199 ++++++++++++ .../noir/crates/smt_poseidon2/src/utils.nr | 119 +++++++ packages/smt/tests/index.test.ts | 293 +++++++++++------- yarn.lock | 26 +- 13 files changed, 851 insertions(+), 121 deletions(-) rename packages/circuits/noir/crates/{smt_bn254 => smt_pedersen}/Nargo.toml (78%) create mode 100644 packages/circuits/noir/crates/smt_pedersen/src/lib.nr create mode 100644 packages/circuits/noir/crates/smt_pedersen/src/utils.nr create mode 100644 packages/circuits/noir/crates/smt_poseidon/Nargo.toml rename packages/circuits/noir/crates/{smt_bn254 => smt_poseidon}/src/lib.nr (99%) rename packages/circuits/noir/crates/{smt_bn254 => smt_poseidon}/src/utils.nr (100%) create mode 100644 packages/circuits/noir/crates/smt_poseidon2/Nargo.toml create mode 100644 packages/circuits/noir/crates/smt_poseidon2/src/lib.nr create mode 100644 packages/circuits/noir/crates/smt_poseidon2/src/utils.nr diff --git a/package.json b/package.json index ebeec795d..8813e85db 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ ], "packageManager": "yarn@4.1.0", "devDependencies": { + "@aztec/bb.js": "^0.32.0", "@commitlint/cli": "^18.6.1", "@commitlint/config-conventional": "^18.6.2", "@types/glob": "^7.2.0", diff --git a/packages/circuits/noir/Nargo.toml b/packages/circuits/noir/Nargo.toml index 60e8a4b40..d1365ebc8 100644 --- a/packages/circuits/noir/Nargo.toml +++ b/packages/circuits/noir/Nargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["crates/smt_bn254"] \ No newline at end of file +members = ["crates/smt_pedersen", "crates/smt_poseidon", "crates/smt_poseidon2"] diff --git a/packages/circuits/noir/crates/smt_bn254/Nargo.toml b/packages/circuits/noir/crates/smt_pedersen/Nargo.toml similarity index 78% rename from packages/circuits/noir/crates/smt_bn254/Nargo.toml rename to packages/circuits/noir/crates/smt_pedersen/Nargo.toml index bace4ce60..e68a4a305 100644 --- a/packages/circuits/noir/crates/smt_bn254/Nargo.toml +++ b/packages/circuits/noir/crates/smt_pedersen/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "smt_bn254" +name = "smt_pedersen" authors = ["fabianschu"] type = "lib" compiler_version = ">=0.19.3" diff --git a/packages/circuits/noir/crates/smt_pedersen/src/lib.nr b/packages/circuits/noir/crates/smt_pedersen/src/lib.nr new file mode 100644 index 000000000..333304471 --- /dev/null +++ b/packages/circuits/noir/crates/smt_pedersen/src/lib.nr @@ -0,0 +1,200 @@ +use dep::std::option::Option; + +mod utils; + +global TREE_DEPTH: u32 = 256; + +/** + * Verifies a membership or a non-membership proof, ie it calculates the tree root + * based on an entry or matching entry and all siblings and compares that calculated root + * with the root that is passed to this function. + * @param entry Contains key and value of an entry: [key, value] + * @param matching_entry Contains [key, value] of a matching entry only for non-membership proofs + * @param siblings Contains array of siblings of entry / matching_entry + * @param root The expected root of the tree + */ +pub fn verify(entry: [Field; 2], matching_entry: [Option; 2], siblings: [Field; TREE_DEPTH], root: Field) { + let mut calculcated_root: Field = 0; + let path = utils::key_to_path(entry[0]); + // if there is no matching_entry it is a membership proof + // if there is a matching_entry it is a non_membership proof + if matching_entry[0].is_none() | matching_entry[1].is_none() { + // membership proof: the root is calculated based on the entry, the siblings, + // and the path determined by the key of entry through consecutive hashing + calculcated_root = utils::calculcate_root(entry, siblings, path); + } else { + // non-membership proof: the root is calculated based on the matching_entry, the siblings + // and the path that is determined by the key of entry. This makes sure that matching_entry is in fact + // a matching entry for entry meaning that it shares the same first bits as path + calculcated_root = utils::calculcate_root([matching_entry[0].unwrap(), matching_entry[1].unwrap()], siblings, path); + } + assert(calculcated_root == root); +} + +/** + * Adds a NEW entry to an existing tree. Based on the siblings first validates the correctness of + * the old root. Then uses the new entry and the siblings to calculate the new tree root. + * NOTE: this function doesn't validate if the key for the new entry already exists in the tree, ie + * if the operation is actually an update. For this operation there is a separate function. + * @param entry Contains key and value of an entry: [key, value] + * @param old_root The root of the tree before the new entry is added + * @param siblings Contains array of siblings of entry / matching_entry + * @returns The new root after the addition + */ +pub fn add(new_entry: [Field; 2], old_root: Field, siblings: [Field; TREE_DEPTH]) -> Field { + // if the root node is zero the first leaf is added to the tree in which case + // the new root equals H(k,v,1) + // otherwise the correctness of the old root is validated based on the siblings after which + // the new root is calculated and returned + if (old_root == 0) { + utils::hash(new_entry[0], new_entry[1], true) + } else { + let (old, new) = utils::calculate_two_roots(new_entry, siblings); + assert(old == old_root); + new + } +} + +/** + * Deletes an existing entry from a tree. Based on the siblings first does a membership proof + * of that existing entry and then calculates the new root (without the entry). + * @param entry Contains key and value of the to-be-deleted entry: [key, value] + * @param old_root The root of the tree if the entry is still included + * @param sigbils Contains array of siblings of entry + * @returns The new root after the deletion + */ +pub fn delete(entry: [Field; 2], old_root: Field, siblings: [Field; TREE_DEPTH]) -> Field { + // proves membership of entry in the old root, then calculates and returns the new root + let (new, old) = utils::calculate_two_roots(entry, siblings); + assert(old == old_root); + new +} + +/** + * Updates the value of an existing entry in a tree. Based on the siblings first does a membership proof + * first verifies the membership of the old entry. Then recalculates the new root. + * @param new_value The new value to be added (instead of old_entry[1]) + * @param old_entry Contains key and value of the entry to be updated: [key, value] + * @param old_root The root of the tree before the update + * @param siblings Contains an array of siblings of old_entry + * @returns The new root after the update + */ +pub fn update(new_value: Field, old_entry: [Field; 2], old_root: Field, siblings: [Field; TREE_DEPTH]) -> Field { + let key = old_entry[0]; + let old_value = old_entry[1]; + // both the old entry and new entry share the same key that is used to calculate the path + let path = utils::key_to_path(key); + // old_parent is a container to temporarily store the nodes that ultimately lead to the OLD root + let mut old_parent: Field = utils::hash(key, old_value, true); + // new_parent is a container to temporarily store the nodes that ultimately lead to the NEW root + let mut new_parent: Field = utils::hash(key, new_value, true); + // starting from the bottom of the tree, for each level it checks whether there is a sibling and if + // that is the case, it hashes the two containers with the sibling and updates the containers with the + // resulting hashes until the uppermost level is reached aka the root node + for i in 0..TREE_DEPTH { + let sibling = siblings[i]; + if sibling != 0 { + if path[i] == 0 { + new_parent = utils::hash(new_parent, sibling, false); + old_parent = utils::hash(old_parent, sibling, false); + } else { + new_parent = utils::hash(sibling, new_parent, false); + old_parent = utils::hash(sibling, old_parent, false); + } + } + } + assert(old_parent == old_root); + new_parent +} + +#[test] +fn test_verify_membership_proof() { + let root = 10163036226218365628416274178218539053881692695713984759452839539868301499377; + let key = 20438969296305830531522370305156029982566273432331621236661483041446048135547; + let value = 17150136040889237739751319962368206600863150289695545292530539263327413090784; + let entry = [key, value]; + let matching_entry = [Option::none(), Option::none()]; + let mut siblings: [Field; TREE_DEPTH] = [0; TREE_DEPTH]; + siblings[253] = 16005544925904787818741660841048975184697157987475781759237359957800253937014; + siblings[254] = 652515607053222641420986801717363174355390181496524320106921145388058577161; + siblings[255] = 2708335068696962380008982791185936221130805655713417587265130133162794982262; + verify(entry, matching_entry, siblings, root); +} + +#[test] +fn test_verify_non_membership_proof() { + let small_tree_root = 10163036226218365628416274178218539053881692695713984759452839539868301499377; + let key = 20438969296305830531522380305156029982566273432331621236661483041446048135547; + let value = 17150136040889237739751319962368206600863250289695545292530539263327413090784; + let entry = [key, value]; + let matching_entry = [ + Option::some(20438969296305830531522370305156029982566273432331621236661483041446048135547), + Option::some(17150136040889237739751319962368206600863150289695545292530539263327413090784) + ]; + let mut siblings: [Field; TREE_DEPTH] = [0; TREE_DEPTH]; + siblings[253] = 16005544925904787818741660841048975184697157987475781759237359957800253937014; + siblings[254] = 652515607053222641420986801717363174355390181496524320106921145388058577161; + siblings[255] = 2708335068696962380008982791185936221130805655713417587265130133162794982262; + verify(entry, matching_entry, siblings, small_tree_root); +} + +#[test] +fn test_add_first_element() { + let key = 20438969296305830531522370305156029982566273432331621236661483041446048135547; + let value = 17150136040889237739751319962368206600863150289695545292530539263327413090784; + let entry = [key, value]; + let siblings: [Field; TREE_DEPTH] = [0; TREE_DEPTH]; + let zero_node = 0; + assert(add(entry, zero_node, siblings) == 2422708535743783816563452741494007579003622904961533867614614610167375232032); +} + +#[test] +fn test_add_element_to_one_element_tree() { + let key = 7374494214024125590767526270082639043919066776944047470878693145844636921798; + let value = 3315292394704439116482935361251007857050519763420264982454883186141315324846; + let entry = [key, value]; + let old_root = 2422708535743783816563452741494007579003622904961533867614614610167375232032; + let mut siblings: [Field; TREE_DEPTH] = [0; TREE_DEPTH]; + siblings[255] = 2422708535743783816563452741494007579003622904961533867614614610167375232032; + assert(add(entry, old_root, siblings) == 13995819305603022633355680906127521476353407789113491617487780281225566393218); +} + +#[test] +fn test_add_element_to_existing_tree() { + let key = 12467743582502009806452203915647380852106587605639139696405928234368558796420; + let value = 7661601117049907361128926075270530269257730340678343102988736234309528818274; + let entry = [key, value]; + let root = 13995819305603022633355680906127521476353407789113491617487780281225566393218; + let mut siblings: [Field; TREE_DEPTH] = [0; TREE_DEPTH]; + siblings[254] = 18033090709903916521866304938786912938158112601014366947614987606463992198712; + siblings[255] = 2422708535743783816563452741494007579003622904961533867614614610167375232032; + let big_tree_root = 8307334591379324778417663235463648615723981385559143500721691536202573318888; + assert(add(entry, root, siblings) == big_tree_root); +} + +#[test] +fn test_delete() { + let key = 12467743582502009806452203915647380852106587605639139696405928234368558796420; + let value = 7661601117049907361128926075270530269257730340678343102988736234309528818274; + let entry = [key, value]; + let big_tree_root = 8307334591379324778417663235463648615723981385559143500721691536202573318888; + let mut siblings: [Field; TREE_DEPTH] = [0; TREE_DEPTH]; + siblings[254] = 18033090709903916521866304938786912938158112601014366947614987606463992198712; + siblings[255] = 2422708535743783816563452741494007579003622904961533867614614610167375232032; + let small_tree_root = 13995819305603022633355680906127521476353407789113491617487780281225566393218; + assert(delete(entry, big_tree_root, siblings) == small_tree_root); +} + +#[test] +fn test_update() { + let key = 12467743582502009806452203915647380852106587605639139696405928234368558796420; + let old_value = 7661601117049907361128926075270530269257730340678343102988736234309528818274; + let new_value = 7661601117049907361128926075270530269257730340678343102988736234309528818275; + let old_entry = [key, old_value]; + let old_root = 8307334591379324778417663235463648615723981385559143500721691536202573318888; + let mut siblings: [Field; TREE_DEPTH] = [0; TREE_DEPTH]; + siblings[254] = 18033090709903916521866304938786912938158112601014366947614987606463992198712; + siblings[255] = 2422708535743783816563452741494007579003622904961533867614614610167375232032; + let big_tree_root = 15109801937676825792951435733056938044336635984778954078779396173428619028936; + assert(update(new_value, old_entry, old_root, siblings) == big_tree_root); +} diff --git a/packages/circuits/noir/crates/smt_pedersen/src/utils.nr b/packages/circuits/noir/crates/smt_pedersen/src/utils.nr new file mode 100644 index 000000000..baefdd670 --- /dev/null +++ b/packages/circuits/noir/crates/smt_pedersen/src/utils.nr @@ -0,0 +1,118 @@ +use dep::std::hash::pedersen_hash; +use crate::TREE_DEPTH; + +/* + * Transforms the key into into a big endian array of bits so that when determining the position + * of a tree entry starting from the root node, the first array element to look at is the last. + * @param key The key of a tree entry + * @returns The path that determines the position of a key in the tree + */ +pub fn key_to_path(key: Field) -> [u1] { + key.to_be_bits(TREE_DEPTH) +} + +/* + * Calculates the poseidon bn254 hash. If a leaf node is created, the number 1 is appended to + * the hashed values as follows: H(k,v,1). + * @param left The left element of the hashing pair + * @param right The right element of the hashing pair + * @param is_leaf Whether what is created is a leaf node or not + * @returns The poseidon hash + */ +pub fn hash(left: Field, right: Field, is_leaf: bool) -> Field { + if (is_leaf) { + pedersen_hash([left, right, 1]) + } else { + pedersen_hash([left, right]) + } +} + + +/* + * Calculates the root for a given tree entry based on the passed array of siblings and the passed path. + * @param entry The key and value of an entry [k, v] + * @param siblings Contains the siblings from bottom to top + * @param path The position of the entry in the tree as represented by bits from bottom to top + * @returns The calculated root node + */ +pub fn calculcate_root(entry: [Field; 2], siblings: [Field; TREE_DEPTH], path: [u1]) -> Field { + // serves as container for hashes and is initialized to be the leaf node + let mut node = hash(entry[0], entry[1], true); + // iterates over the list of siblings until the first sibling is found + // arbitrarily assigns the sibling to be the left and the node to be the + // right element of the hashing pair unless the path indicates the opposite + // order in which case the order is changed. The new hash is stored in the container + // until the root node is reached and returned. + for i in 0..TREE_DEPTH { + let sibling = siblings[i]; + if sibling != 0 { + let mut left = sibling; + let mut right = node; + if path[i] == 0 { + left = node; + right = sibling; + } + node = hash(left, right, false); + } + } + node +} + +/* + * Calculates two roots for a given leaf entry based on the passed array of siblings: one root + * for if the leaf entry was included in the tree and one for if the leaf entry was not included + * in the tree. This is useful for efficiently proving the membership of leaf entries for a + * tree while simultaneously modifying the tree. + * @param entry The key and value of an entry [k, v] + * @param siblings Contains the siblings from bottom to top + * @returns Two root nodes: the first one doesn't include entry, the second does + */ +pub fn calculate_two_roots(entry: [Field; 2], siblings: [Field; TREE_DEPTH]) -> (Field, Field) { + let path = key_to_path(entry[0]); + // long_path_node is a container for hashes to derive the root node for the tree that + // includes the entry + let mut long_path_node = hash(entry[0], entry[1], true); + // long_path_node is a container for hashes to derive the root node for the tree that + // doesn't include the entry + let mut short_path_node: Field = 0; + // iterate over the levels of the tree from bottom to top + for i in 0..TREE_DEPTH { + let sibling = siblings[i]; + // After the first sibling is found, the processes are started to calculate the two root nodes. + // The calulcation of the root node that includes the entry is comparable to `calculate_root`. + // To calc the root node that doesn't include entry, the first sibling is put into the container + // and starting from each SUBSEQUENT iteration it is hashed with its sibling and the resulting hash + // again stored in the container until the root is reached + if sibling != 0 { + if siblings[i - 1] == 0 { + short_path_node = siblings[i]; + } + if path[i] == 0 { + long_path_node = hash(long_path_node, sibling, false); + if(short_path_node != sibling) { + short_path_node = hash(short_path_node, sibling, false); + } + } else { + long_path_node = hash(sibling, long_path_node, false); + if(short_path_node != sibling) { + short_path_node = hash(sibling, short_path_node, false); + } + } + } + } + (short_path_node, long_path_node) +} + +#[test] +fn test_hash_leaf_node() { + let key = 20438969296305830531522370305156029982566273432331621236661483041446048135547; + let value = 17150136040889237739751319962368206600863150289695545292530539263327413090784; + assert(hash(key, value, true) == 0x055b34560562b842e236f919e9a74ee345d7523d70f711e0ccdb22466c767c20); +} + +#[test] +fn test_hash_node() { + let left = 7374494214024125590767526270082639043919066776944047470878693145844636921798; + let right = 3315292394704439116482935361251007857050519763420264982454883186141315324846; + assert(hash(left, right, false) == 0x05f8a24a037ad8567c042c14c5a8299649e0fe13df1cec40d381a3671bae9245); +} diff --git a/packages/circuits/noir/crates/smt_poseidon/Nargo.toml b/packages/circuits/noir/crates/smt_poseidon/Nargo.toml new file mode 100644 index 000000000..52822ec3d --- /dev/null +++ b/packages/circuits/noir/crates/smt_poseidon/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "smt_poseidon" +authors = ["fabianschu"] +type = "lib" +compiler_version = ">=0.19.3" diff --git a/packages/circuits/noir/crates/smt_bn254/src/lib.nr b/packages/circuits/noir/crates/smt_poseidon/src/lib.nr similarity index 99% rename from packages/circuits/noir/crates/smt_bn254/src/lib.nr rename to packages/circuits/noir/crates/smt_poseidon/src/lib.nr index 6af24dd55..029ae3d40 100644 --- a/packages/circuits/noir/crates/smt_bn254/src/lib.nr +++ b/packages/circuits/noir/crates/smt_poseidon/src/lib.nr @@ -243,4 +243,4 @@ fn test_update() { siblings[255] = 7842913321420301106140788486336995496832503825951977327575501561489697540557; let big_tree_root = 4657474665007910823901096287220097081233671466281873230928277896829046731272; assert(update(new_value, old_entry, old_root, siblings) == big_tree_root); -} \ No newline at end of file +} diff --git a/packages/circuits/noir/crates/smt_bn254/src/utils.nr b/packages/circuits/noir/crates/smt_poseidon/src/utils.nr similarity index 100% rename from packages/circuits/noir/crates/smt_bn254/src/utils.nr rename to packages/circuits/noir/crates/smt_poseidon/src/utils.nr diff --git a/packages/circuits/noir/crates/smt_poseidon2/Nargo.toml b/packages/circuits/noir/crates/smt_poseidon2/Nargo.toml new file mode 100644 index 000000000..f413aa215 --- /dev/null +++ b/packages/circuits/noir/crates/smt_poseidon2/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "smt_poseidon2" +authors = ["fabianschu"] +type = "lib" +compiler_version = ">=0.19.3" diff --git a/packages/circuits/noir/crates/smt_poseidon2/src/lib.nr b/packages/circuits/noir/crates/smt_poseidon2/src/lib.nr new file mode 100644 index 000000000..076933673 --- /dev/null +++ b/packages/circuits/noir/crates/smt_poseidon2/src/lib.nr @@ -0,0 +1,199 @@ +use dep::std::option::Option; + +mod utils; + +global TREE_DEPTH: u32 = 256; + +/** + * Verifies a membership or a non-membership proof, ie it calculates the tree root + * based on an entry or matching entry and all siblings and compares that calculated root + * with the root that is passed to this function. + * @param entry Contains key and value of an entry: [key, value] + * @param matching_entry Contains [key, value] of a matching entry only for non-membership proofs + * @param siblings Contains array of siblings of entry / matching_entry + * @param root The expected root of the tree + */ +pub fn verify(entry: [Field; 2], matching_entry: [Option; 2], siblings: [Field; TREE_DEPTH], root: Field) { + let mut calculcated_root: Field = 0; + let path = utils::key_to_path(entry[0]); + // if there is no matching_entry it is a membership proof + // if there is a matching_entry it is a non_membership proof + if matching_entry[0].is_none() | matching_entry[1].is_none() { + // membership proof: the root is calculated based on the entry, the siblings, + // and the path determined by the key of entry through consecutive hashing + calculcated_root = utils::calculcate_root(entry, siblings, path); + } else { + // non-membership proof: the root is calculated based on the matching_entry, the siblings + // and the path that is determined by the key of entry. This makes sure that matching_entry is in fact + // a matching entry for entry meaning that it shares the same first bits as path + calculcated_root = utils::calculcate_root([matching_entry[0].unwrap(), matching_entry[1].unwrap()], siblings, path); + } + assert(calculcated_root == root); +} + +/** + * Adds a NEW entry to an existing tree. Based on the siblings first validates the correctness of + * the old root. Then uses the new entry and the siblings to calculate the new tree root. + * NOTE: this function doesn't validate if the key for the new entry already exists in the tree, ie + * if the operation is actually an update. For this operation there is a separate function. + * @param entry Contains key and value of an entry: [key, value] + * @param old_root The root of the tree before the new entry is added + * @param siblings Contains array of siblings of entry / matching_entry + * @returns The new root after the addition + */ +pub fn add(new_entry: [Field; 2], old_root: Field, siblings: [Field; TREE_DEPTH]) -> Field { + // if the root node is zero the first leaf is added to the tree in which case + // the new root equals H(k,v,1) + // otherwise the correctness of the old root is validated based on the siblings after which + // the new root is calculated and returned + if (old_root == 0) { + utils::hash(new_entry[0], new_entry[1], true) + } else { + let (old, new) = utils::calculate_two_roots(new_entry, siblings); + assert(old == old_root); + new + } +} + +/** + * Deletes an existing entry from a tree. Based on the siblings first does a membership proof + * of that existing entry and then calculates the new root (without the entry). + * @param entry Contains key and value of the to-be-deleted entry: [key, value] + * @param old_root The root of the tree if the entry is still included + * @param sigbils Contains array of siblings of entry + * @returns The new root after the deletion + */ +pub fn delete(entry: [Field; 2], old_root: Field, siblings: [Field; TREE_DEPTH]) -> Field { + // proves membership of entry in the old root, then calculates and returns the new root + let (new, old) = utils::calculate_two_roots(entry, siblings); + assert(old == old_root); + new +} + +/** + * Updates the value of an existing entry in a tree. Based on the siblings first does a membership proof + * first verifies the membership of the old entry. Then recalculates the new root. + * @param new_value The new value to be added (instead of old_entry[1]) + * @param old_entry Contains key and value of the entry to be updated: [key, value] + * @param old_root The root of the tree before the update + * @param siblings Contains an array of siblings of old_entry + * @returns The new root after the update + */ +pub fn update(new_value: Field, old_entry: [Field; 2], old_root: Field, siblings: [Field; TREE_DEPTH]) -> Field { + let key = old_entry[0]; + let old_value = old_entry[1]; + // both the old entry and new entry share the same key that is used to calculate the path + let path = utils::key_to_path(key); + // old_parent is a container to temporarily store the nodes that ultimately lead to the OLD root + let mut old_parent: Field = utils::hash(key, old_value, true); + // new_parent is a container to temporarily store the nodes that ultimately lead to the NEW root + let mut new_parent: Field = utils::hash(key, new_value, true); + // starting from the bottom of the tree, for each level it checks whether there is a sibling and if + // that is the case, it hashes the two containers with the sibling and updates the containers with the + // resulting hashes until the uppermost level is reached aka the root node + for i in 0..TREE_DEPTH { + let sibling = siblings[i]; + if sibling != 0 { + if path[i] == 0 { + new_parent = utils::hash(new_parent, sibling, false); + old_parent = utils::hash(old_parent, sibling, false); + } else { + new_parent = utils::hash(sibling, new_parent, false); + old_parent = utils::hash(sibling, old_parent, false); + } + } + } + assert(old_parent == old_root); + new_parent +} + + +#[test] +fn test_verify_membership_proof() { + let root = 10716768800230645516898989000381415039830058019558458336422156297954793868480; + let key = 7374494214024125590767526270082639043919066776944047470878693145844636921798; + let value = 3315292394704439116482935361251007857050519763420264982454883186141315324846; + let entry = [key, value]; + let matching_entry = [Option::none(), Option::none()]; + let mut siblings: [Field; TREE_DEPTH] = [0; TREE_DEPTH]; + siblings[254] = 20148760655853336892200452855328368119414597152890422934846243993411437261435; + siblings[255] = 2796791779811082648195305392369065609295497570366911757412326402199992122860; + verify(entry, matching_entry, siblings, root); +} + +#[test] +fn test_verify_non_membership_proof() { + let root = 7374500236777583547067945018169347524165767081414637328827735314573881617427; + let key = 7374494214024125590767526270082639043919066776944047470878693145844636921798; + let value = 10025543891561726230897112378978303079225878620021923798073814166479632519483; + let entry = [key, value]; + let matching_entry = [ + Option::some(7374494214025125590767526270082639043919066776944047470878693145844636921798), + Option::some(3315292394704439116482935361251007857050519763420264982454883186141315324846) + ]; + let mut siblings: [Field; TREE_DEPTH] = [0; TREE_DEPTH]; + siblings[254] = 20148760655853336892200452855328368119414597152890422934846243993411437261435; + siblings[255] = 2796791779811082648195305392369065609295497570366911757412326402199992122860; + verify(entry, matching_entry, siblings, root); +} + +#[test] +fn test_add_first_element() { + let key = 20438969296305830531522370305156029982566273432331621236661483041446048135547; + let value = 17150136040889237739751319962368206600863150289695545292530539263327413090784; + let entry = [key, value]; + let siblings: [Field; TREE_DEPTH] = [0; TREE_DEPTH]; + let zero_node = 0; + assert(add(entry, zero_node, siblings) == 4234307959542627966802357671703046216228508835025652132690233312312451999811); +} + +#[test] +fn test_add_element_to_one_element_tree() { + let key = 7374494214024125590767526270082639043919066776944047470878693145844636921798; + let value = 3315292394704439116482935361251007857050519763420264982454883186141315324846; + let entry = [key, value]; + let old_root = 4234307959542627966802357671703046216228508835025652132690233312312451999811; + let mut siblings: [Field; TREE_DEPTH] = [0; TREE_DEPTH]; + siblings[255] = 4234307959542627966802357671703046216228508835025652132690233312312451999811; + assert(add(entry, old_root, siblings) == 20776251139619034284861007481547174315767321885984369040570901580313616282707); +} + +#[test] +fn test_add_element_to_existing_tree() { + let key = 12467743582502009806452203915647380852106587605639139696405928234368558796420; + let value = 7661601117049907361128926075270530269257730340678343102988736234309528818274; + let entry = [key, value]; + let root = 20776251139619034284861007481547174315767321885984369040570901580313616282707; + let mut siblings: [Field; TREE_DEPTH] = [0; TREE_DEPTH]; + siblings[254] = 18985036353284234328889211451392298609888327910581614855129954359333652022377; + siblings[255] = 4234307959542627966802357671703046216228508835025652132690233312312451999811; + let big_tree_root = 17243332362925384208896062537177789753675609853711705170581735815629040102816; + assert(add(entry, root, siblings) == big_tree_root); +} + +#[test] +fn test_delete() { + let key = 12467743582502009806452203915647380852106587605639139696405928234368558796420; + let value = 7661601117049907361128926075270530269257730340678343102988736234309528818274; + let entry = [key, value]; + let big_tree_root = 17243332362925384208896062537177789753675609853711705170581735815629040102816; + let mut siblings: [Field; TREE_DEPTH] = [0; TREE_DEPTH]; + siblings[254] = 18985036353284234328889211451392298609888327910581614855129954359333652022377; + siblings[255] = 4234307959542627966802357671703046216228508835025652132690233312312451999811; + let small_tree_root = 20776251139619034284861007481547174315767321885984369040570901580313616282707; + assert(delete(entry, big_tree_root, siblings) == small_tree_root); +} + +#[test] +fn test_update() { + let key = 12467743582502009806452203915647380852106587605639139696405928234368558796420; + let old_value = 7661601117049907361128926075270530269257730340678343102988736234309528818274; + let new_value = 7661601117049907361128926075270530269257730340678343102988736234309528818275; + let old_entry = [key, old_value]; + let old_root = 17243332362925384208896062537177789753675609853711705170581735815629040102816; + let mut siblings: [Field; TREE_DEPTH] = [0; TREE_DEPTH]; + siblings[254] = 18985036353284234328889211451392298609888327910581614855129954359333652022377; + siblings[255] = 4234307959542627966802357671703046216228508835025652132690233312312451999811; + let big_tree_root = 15638616379013578914321087511154669410909643746666558940281708627312673365379; + assert(update(new_value, old_entry, old_root, siblings) == big_tree_root); +} diff --git a/packages/circuits/noir/crates/smt_poseidon2/src/utils.nr b/packages/circuits/noir/crates/smt_poseidon2/src/utils.nr new file mode 100644 index 000000000..8a784f6fe --- /dev/null +++ b/packages/circuits/noir/crates/smt_poseidon2/src/utils.nr @@ -0,0 +1,119 @@ +use dep::std::hash::poseidon2::Poseidon2; +use crate::TREE_DEPTH; + +/* + * Transforms the key into into a big endian array of bits so that when determining the position + * of a tree entry starting from the root node, the first array element to look at is the last. + * @param key The key of a tree entry + * @returns The path that determines the position of a key in the tree + */ +pub fn key_to_path(key: Field) -> [u1] { + key.to_be_bits(TREE_DEPTH) +} + +/* + * Calculates the poseidon bn254 hash. If a leaf node is created, the number 1 is appended to + * the hashed values as follows: H(k,v,1). + * @param left The left element of the hashing pair + * @param right The right element of the hashing pair + * @param is_leaf Whether what is created is a leaf node or not + * @returns The poseidon hash + */ +pub fn hash(left: Field, right: Field, is_leaf: bool) -> Field { + if (is_leaf) { + Poseidon2::hash([left, right, 1], 3) + } else { + Poseidon2::hash([left, right], 2) + } +} + + +/* + * Calculates the root for a given tree entry based on the passed array of siblings and the passed path. + * @param entry The key and value of an entry [k, v] + * @param siblings Contains the siblings from bottom to top + * @param path The position of the entry in the tree as represented by bits from bottom to top + * @returns The calculated root node + */ +pub fn calculcate_root(entry: [Field; 2], siblings: [Field; TREE_DEPTH], path: [u1]) -> Field { + // serves as container for hashes and is initialized to be the leaf node + let mut node = hash(entry[0], entry[1], true); + // iterates over the list of siblings until the first sibling is found + // arbitrarily assigns the sibling to be the left and the node to be the + // right element of the hashing pair unless the path indicates the opposite + // order in which case the order is changed. The new hash is stored in the container + // until the root node is reached and returned. + for i in 0..TREE_DEPTH { + let sibling = siblings[i]; + if sibling != 0 { + let mut left = sibling; + let mut right = node; + if path[i] == 0 { + left = node; + right = sibling; + } + node = hash(left, right, false); + } + } + node +} + +/* + * Calculates two roots for a given leaf entry based on the passed array of siblings: one root + * for if the leaf entry was included in the tree and one for if the leaf entry was not included + * in the tree. This is useful for efficiently proving the membership of leaf entries for a + * tree while simultaneously modifying the tree. + * @param entry The key and value of an entry [k, v] + * @param siblings Contains the siblings from bottom to top + * @returns Two root nodes: the first one doesn't include entry, the second does + */ +pub fn calculate_two_roots(entry: [Field; 2], siblings: [Field; TREE_DEPTH]) -> (Field, Field) { + let path = key_to_path(entry[0]); + // long_path_node is a container for hashes to derive the root node for the tree that + // includes the entry + let mut long_path_node = hash(entry[0], entry[1], true); + // long_path_node is a container for hashes to derive the root node for the tree that + // doesn't include the entry + let mut short_path_node: Field = 0; + // iterate over the levels of the tree from bottom to top + for i in 0..TREE_DEPTH { + let sibling = siblings[i]; + // After the first sibling is found, the processes are started to calculate the two root nodes. + // The calulcation of the root node that includes the entry is comparable to `calculate_root`. + // To calc the root node that doesn't include entry, the first sibling is put into the container + // and starting from each SUBSEQUENT iteration it is hashed with its sibling and the resulting hash + // again stored in the container until the root is reached + if sibling != 0 { + if siblings[i - 1] == 0 { + short_path_node = siblings[i]; + } + if path[i] == 0 { + long_path_node = hash(long_path_node, sibling, false); + if(short_path_node != sibling) { + short_path_node = hash(short_path_node, sibling, false); + } + } else { + long_path_node = hash(sibling, long_path_node, false); + if(short_path_node != sibling) { + short_path_node = hash(sibling, short_path_node, false); + } + } + } + } + + (short_path_node, long_path_node) +} + +#[test] +fn test_hash_leaf_node() { + let key = 20438969296305830531522370305156029982566273432331621236661483041446048135547; + let value = 17150136040889237739751319962368206600863150289695545292530539263327413090784; + assert(hash(key, value, true) == 0x095c888b463f391872a3fa42d94c2f82e23db9b832b61812e6880adf4b752843); +} + +#[test] +fn test_hash_node() { + let left = 7374494214024125590767526270082639043919066776944047470878693145844636921798; + let right = 3315292394704439116482935361251007857050519763420264982454883186141315324846; + assert(hash(left, right, false) == 0x263a1c92ef5134e65851f4cc34c60648342b2f2c3c540bda8ac30965cc16e854); +} diff --git a/packages/smt/tests/index.test.ts b/packages/smt/tests/index.test.ts index c1f5aa421..137ea48c6 100644 --- a/packages/smt/tests/index.test.ts +++ b/packages/smt/tests/index.test.ts @@ -1,30 +1,54 @@ import { poseidon, smt } from "circomlibjs" import sha256 from "crypto-js/sha256" +import { BarretenbergSync, Fr } from "@aztec/bb.js" import { ChildNodes, SMT } from "../src" describe("SMT", () => { - const hash = (childNodes: ChildNodes) => sha256(childNodes.join("")).toString() + const hashes = { + sha256: (childNodes: ChildNodes) => childNodes.join(""), + poseidon2: (childNodes: ChildNodes) => childNodes.join(""), + pedersen: (childNodes: ChildNodes) => childNodes.join(""), + poseidon: (childNodes: ChildNodes) => childNodes.join("") + } + const testKeys = ["a", "3", "2b", "20", "9", "17"] + beforeAll(async () => { + const bb = await BarretenbergSync.new() + hashes.sha256 = (childNodes: ChildNodes) => sha256(childNodes.join("")).toString() + hashes.poseidon2 = (childNodes: ChildNodes) => + bb + .poseidonHash([Fr.fromString(childNodes.join(""))]) + .toString() + .slice(2) + hashes.pedersen = (childNodes: ChildNodes) => + bb + .pedersenHash([Fr.fromString(childNodes.join(""))], 0) + .toString() + .slice(2) + hashes.poseidon = (childNodes: ChildNodes) => poseidon(childNodes) + }) + describe("Create hexadecimal trees", () => { - it("Should create an empty sparse Merkle tree", () => { - const tree = new SMT(hash) + it.each([["sha256"], ["poseidon2"], ["pedersen"]])("Should create an empty sparse Merkle tree - %s", (hash) => { + const tree = new SMT(hashes[hash as keyof typeof hashes]) expect(tree.root).toBe("0") }) - it("Should not create a hexadecimal tree if the hash function does not return a hexadecimal", () => { - const hash = (childNodes: ChildNodes) => poseidon(childNodes) + it.each([["poseidon"]])( + "Should not create a hexadecimal tree if the hash function does not return a hexadecimal", + (hash) => { + const fun = () => new SMT(hashes[hash as keyof typeof hashes]) - const fun = () => new SMT(hash) - - expect(fun).toThrow() - }) + expect(fun).toThrow() + } + ) }) describe("Add new entries (key/value) in the tree", () => { - it("Should add a new entry", () => { - const tree = new SMT(hash) + it.each([["sha256"], ["poseidon2"], ["pedersen"]])("Should add a new entry - %s", (hash) => { + const tree = new SMT(hashes[hash as keyof typeof hashes]) const oldRoot = tree.root tree.add("2", "a") @@ -32,85 +56,115 @@ describe("SMT", () => { expect(tree.root).not.toEqual(oldRoot) }) - it("Should not add a new non-hexadecimal entry", () => { - const tree = new SMT(hash) + it.each([["sha256"], ["poseidon2"], ["pedersen"]])( + "Should not add a new non-hexadecimal entry - %s", + (hash) => { + const tree = new SMT(hashes[hash as keyof typeof hashes]) - const fun = () => tree.add(BigInt(2), BigInt(4)) + const fun = () => tree.add(BigInt(2), BigInt(4)) - expect(fun).toThrow() - }) + expect(fun).toThrow() + } + ) - it("Should not add a new entry with an existing key", () => { - const tree = new SMT(hash) + it.each([["sha256"], ["poseidon2"], ["pedersen"]])( + "Should not add a new entry with an existing key - %s", + (hash) => { + const tree = new SMT(hashes[hash as keyof typeof hashes]) - tree.add("2", "a") - const fun = () => tree.add("2", "a") + tree.add("2", "a") + const fun = () => tree.add("2", "a") - expect(fun).toThrow() - }) + expect(fun).toThrow() + } + ) - it("Should add 6 new entries and create the correct root hash", () => { - const tree = new SMT(hash) + it.each([ + ["sha256", "40770450d00520bdab58e115dd4439c20cd39028252f3973e81fb15b02eb28f7"], + ["poseidon2", "2d7cc420e66d3accf1c6fc78528ed5ce2453cc5fd7341e58ad283735f9b1f819"], + ["pedersen", "034fab1996e408dab73e2bfdc79f2d4e108eac5f015c86d4e01c3c23ea4c2a91"] + ])("Should add 6 new entries and create the correct root hash - %s", (hash, expected) => { + const tree = new SMT(hashes[hash as keyof typeof hashes]) for (const key of testKeys) { tree.add(key, key) } - expect(tree.root).toBe("40770450d00520bdab58e115dd4439c20cd39028252f3973e81fb15b02eb28f7") + expect(tree.root).toBe(expected) }) }) describe("Get values from the tree", () => { - it("Should get a value from the tree using an existing key", () => { - const tree = new SMT(hash) + it.each([["sha256"], ["poseidon2"], ["pedersen"]])( + "Should get a value from the tree using an existing key - %s", + (hash) => { + const tree = new SMT(hashes[hash as keyof typeof hashes]) - tree.add("2", "a") - const value = tree.get("2") + tree.add("2", "a") + const value = tree.get("2") - expect(value).toBe("a") - }) + expect(value).toBe("a") + } + ) - it("Should not get a value from the tree using a non-existing key", () => { - const tree = new SMT(hash) + it.each([["sha256"], ["poseidon2"], ["pedersen"]])( + "Should not get a value from the tree using a non-existing key", + (hash) => { + const tree = new SMT(hashes[hash as keyof typeof hashes]) - tree.add("2", "a") - const value = tree.get("1") + tree.add("2", "a") + const value = tree.get("1") - expect(value).toBeUndefined() - }) + expect(value).toBeUndefined() + } + ) }) describe("Update values in the tree", () => { - it("Should update a value of an existing key", () => { - const tree = new SMT(hash) + it.each([ + ["sha256", "c75d3f1f5bcd6914d0331ce5ec17c0db8f2070a2d4285f8e3ff11c6ca19168ff"], + ["poseidon2", "0dbd8e6fcb7142f82c1020310bd60ebcae77810fd0709f77ff416bdd03fe672e"], + ["pedersen", "1b75020b0ce47cc48a4f00209aa225406c54a3288d8a1ef92e39cf5e80e7d7a5"] + ])("Should update a value of an existing key - Poseidon1 - %s", (hash, expected) => { + const tree = new SMT(hashes[hash as keyof typeof hashes]) tree.add("2", "a") tree.update("2", "5") - expect(tree.root).toBe("c75d3f1f5bcd6914d0331ce5ec17c0db8f2070a2d4285f8e3ff11c6ca19168ff") + expect(tree.root).toBe(expected) }) - it("Should not update a value with a non-existing key", () => { - const tree = new SMT(hash) + it.each([["sha256"], ["poseidon2"], ["pedersen"]])( + "Should not update a value with a non-existing key - %s", + (hash) => { + const tree = new SMT(hashes[hash as keyof typeof hashes]) - const fun = () => tree.update("1", "5") + const fun = () => tree.update("1", "5") - expect(fun).toThrow() - }) + expect(fun).toThrow() + } + ) }) describe("Delete entries from the tree", () => { - it("Should delete an entry with an existing key", () => { - const tree = new SMT(hash) + it.each([["sha256"], ["poseidon2"], ["pedersen"]])( + "Should delete an entry with an existing key - %s", + (hash) => { + const tree = new SMT(hashes[hash as keyof typeof hashes]) - tree.add("2", "a") - tree.delete("2") + tree.add("2", "a") + tree.delete("2") - expect(tree.root).toBe("0") - }) + expect(tree.root).toBe("0") + } + ) - it("Should delete 3 entries and create the correct root hash", () => { - const tree = new SMT(hash) + it.each([ + ["sha256", "5d2bfda7c24d9e9e59fe89a271f7d0a3435892c98bc1121b9b590d800deeca10"], + ["poseidon2", "18dca814d672b1829da81727f3a1db8c36e4eaa87d727a76498367abdd1a6a84"], + ["pedersen", "1e6e5d4a34fa89594da39f111a71aff6c7a55f03b593e6e87a73a877061568f8"] + ])("Should delete 3 entries and create the correct root hash - %s", (hash, expected) => { + const tree = new SMT(hashes[hash as keyof typeof hashes]) for (const key of testKeys) { tree.add(key, key) @@ -120,41 +174,47 @@ describe("SMT", () => { tree.delete(testKeys[3]) tree.delete(testKeys[4]) - expect(tree.root).toBe("5d2bfda7c24d9e9e59fe89a271f7d0a3435892c98bc1121b9b590d800deeca10") + expect(tree.root).toBe(expected) }) - it("Should not delete an entry with a non-existing key", () => { - const tree = new SMT(hash) + it.each([["sha256"], ["poseidon2"], ["pedersen"]])( + "Should not delete an entry with a non-existing key - %s", + (hash) => { + const tree = new SMT(hashes[hash as keyof typeof hashes]) - const fun = () => tree.delete("1") + const fun = () => tree.delete("1") - expect(fun).toThrow() - }) + expect(fun).toThrow() + } + ) }) describe("Create Merkle proofs and verify them", () => { - it("Should create some Merkle proofs and verify them", () => { - const tree = new SMT(hash) + it.each([["sha256"], ["poseidon2"], ["pedersen"]])( + "Should create some Merkle proofs and verify them - %s", + (hash) => { + const tree = new SMT(hashes[hash as keyof typeof hashes]) - for (const key of testKeys) { - tree.add(key, key) - } + for (const key of testKeys) { + tree.add(key, key) + } - for (let i = 0; i < 100; i += 1) { - const randomKey = Math.floor(Math.random() * 100).toString(16) - const proof = tree.createProof(randomKey) + for (let i = 0; i < 100; i += 1) { + const randomKey = Math.floor(Math.random() * 100).toString(16) + const proof = tree.createProof(randomKey) - expect(tree.verifyProof(proof)).toBeTruthy() - } + expect(tree.verifyProof(proof)).toBeTruthy() + } - tree.add("12", "1") + tree.add("12", "1") - const proof = tree.createProof("6") - expect(tree.verifyProof(proof)).toBeTruthy() - }) + const proof = tree.createProof("6") + expect(tree.verifyProof(proof)).toBeTruthy() + } + ) - it("Should not verify a wrong Merkle proof", () => { - const tree = new SMT(hash) + it.each([["sha256"], ["poseidon2"], ["pedersen"]])("Should not verify a wrong Merkle proof - %s", (hash) => { + const tree = new SMT(hashes[hash as keyof typeof hashes]) for (const key of testKeys) { tree.add(key, key) @@ -168,24 +228,23 @@ describe("SMT", () => { }) describe("Create big number trees", () => { - const hash = (childNodes: ChildNodes) => poseidon(childNodes) - - it("Should create a big number tree", () => { - const tree = new SMT(hash, true) + it.each([["poseidon"]])("Should create a big number tree - %s", (hash) => { + const tree = new SMT(hashes[hash as keyof typeof hashes], true) expect(tree.root).toEqual(BigInt(0)) }) - it("Should not create a big number tree if the hash function does not return a big number", () => { - const hash = (childNodes: ChildNodes) => sha256(childNodes.join("")).toString() - - const fun = () => new SMT(hash, true) + it.each([["sha256"]])( + "Should not create a big number tree if the hash function does not return a big number", + (hash) => { + const fun = () => new SMT(hashes[hash as keyof typeof hashes], true) - expect(fun).toThrow() - }) + expect(fun).toThrow() + } + ) - it("Should add a big number new entry", () => { - const tree = new SMT(hash, true) + it.each([["poseidon"]])("Should add a big number new entry - %s", (hash) => { + const tree = new SMT(hashes[hash as keyof typeof hashes], true) const oldRoot = tree.root tree.add(BigInt(2), BigInt(4)) @@ -193,8 +252,8 @@ describe("SMT", () => { expect(tree.root).not.toEqual(oldRoot) }) - it("Should not add a new non-big number entry", () => { - const tree = new SMT(hash, true) + it.each([["poseidon"]])("Should not add a new non-big number entry - %s", (hash) => { + const tree = new SMT(hashes[hash as keyof typeof hashes], true) const fun = () => tree.add("2", "a") @@ -203,35 +262,37 @@ describe("SMT", () => { }) describe("Matching with Circomlib smt implementation", () => { - it("Should create two trees with different implementations and match their root nodes", async () => { - const hash = (childNodes: ChildNodes) => poseidon(childNodes) - const tree = new SMT(hash, true) - const tree2 = await smt.newMemEmptyTrie() - const entries: any = [ - [ - BigInt("20438969296305830531522370305156029982566273432331621236661483041446048135547"), - BigInt("17150136040889237739751319962368206600863150289695545292530539263327413090784") - ], - [ - BigInt("8459688297517826598613412977307486050019239051864711035321718508109192087854"), - BigInt("8510347201346963732943571140849185725417245763047403804445415726302354045170") - ], - [ - BigInt("18746990989203767017840856832962652635369613415011636432610873672704085238844"), - BigInt("10223238458026721676606706894638558676629446348345239719814856822628482567791") - ], - [ - BigInt("13924553918840562069536446401916499801909138643922241340476956069386532478098"), - BigInt("13761779908325789083343687318102407319424329800042729673292939195255502025802") + it.each([["poseidon"]])( + "Should create two trees with different implementations and match their root nodes", + async (hash) => { + const tree = new SMT(hashes[hash as keyof typeof hashes], true) + const tree2 = await smt.newMemEmptyTrie() + const entries: any = [ + [ + BigInt("20438969296305830531522370305156029982566273432331621236661483041446048135547"), + BigInt("17150136040889237739751319962368206600863150289695545292530539263327413090784") + ], + [ + BigInt("8459688297517826598613412977307486050019239051864711035321718508109192087854"), + BigInt("8510347201346963732943571140849185725417245763047403804445415726302354045170") + ], + [ + BigInt("18746990989203767017840856832962652635369613415011636432610873672704085238844"), + BigInt("10223238458026721676606706894638558676629446348345239719814856822628482567791") + ], + [ + BigInt("13924553918840562069536446401916499801909138643922241340476956069386532478098"), + BigInt("13761779908325789083343687318102407319424329800042729673292939195255502025802") + ] ] - ] - for await (const entry of entries) { - tree.add(entry[0], entry[1]) - await tree2.insert(entry[0], entry[1]) - } + for await (const entry of entries) { + tree.add(entry[0], entry[1]) + await tree2.insert(entry[0], entry[1]) + } - expect(tree.root).toEqual(tree2.root) - }) + expect(tree.root).toEqual(tree2.root) + } + ) }) }) diff --git a/yarn.lock b/yarn.lock index 1b7acf9dd..461e28a93 100644 --- a/yarn.lock +++ b/yarn.lock @@ -80,6 +80,20 @@ __metadata: languageName: node linkType: hard +"@aztec/bb.js@npm:^0.32.0": + version: 0.32.0 + resolution: "@aztec/bb.js@npm:0.32.0" + dependencies: + comlink: "npm:^4.4.1" + commander: "npm:^10.0.1" + debug: "npm:^4.3.4" + tslib: "npm:^2.4.0" + bin: + bb.js: dest/node/main.js + checksum: 10/92258f8dbee1534263f5ab3b89ba9b40a888f309c34250675fe04c239e51b6f5620dcce55c2566172b448ecc2c11d9b009ea2abab719934eb12f3e6898517bd7 + languageName: node + linkType: hard + "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5": version: 7.23.5 resolution: "@babel/code-frame@npm:7.23.5" @@ -6059,6 +6073,13 @@ __metadata: languageName: node linkType: hard +"comlink@npm:^4.4.1": + version: 4.4.1 + resolution: "comlink@npm:4.4.1" + checksum: 10/3d953e987874bf0d60c02a3dd96e9f661141e58709eaab82097369c585382935651ce61f004e7bb1a5eb868ca8d8734d1cec885965ae53c56567d91b9539f2d0 + languageName: node + linkType: hard + "command-exists@npm:^1.2.8": version: 1.2.9 resolution: "command-exists@npm:1.2.9" @@ -6104,7 +6125,7 @@ __metadata: languageName: node linkType: hard -"commander@npm:^10.0.0": +"commander@npm:^10.0.0, commander@npm:^10.0.1": version: 10.0.1 resolution: "commander@npm:10.0.1" checksum: 10/8799faa84a30da985802e661cc9856adfaee324d4b138413013ef7f087e8d7924b144c30a1f1405475f0909f467665cd9e1ce13270a2f41b141dab0b7a58f3fb @@ -17090,7 +17111,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.1.0, tslib@npm:^2.6.2": +"tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.6.2": version: 2.6.2 resolution: "tslib@npm:2.6.2" checksum: 10/bd26c22d36736513980091a1e356378e8b662ded04204453d353a7f34a4c21ed0afc59b5f90719d4ba756e581a162ecbf93118dc9c6be5acf70aa309188166ca @@ -18719,6 +18740,7 @@ __metadata: version: 0.0.0-use.local resolution: "zk-kit@workspace:." dependencies: + "@aztec/bb.js": "npm:^0.32.0" "@commitlint/cli": "npm:^18.6.1" "@commitlint/config-conventional": "npm:^18.6.2" "@types/glob": "npm:^7.2.0" From 00ed86bf7929657976c226a45cec6b1323ecce3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Tue, 9 Apr 2024 09:14:35 +0100 Subject: [PATCH 2/3] Apply suggestions from code review Co-authored-by: Vivian Plasencia --- packages/circuits/noir/crates/smt_pedersen/src/lib.nr | 2 +- packages/circuits/noir/crates/smt_poseidon2/src/lib.nr | 2 +- packages/circuits/noir/crates/smt_poseidon2/src/utils.nr | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/circuits/noir/crates/smt_pedersen/src/lib.nr b/packages/circuits/noir/crates/smt_pedersen/src/lib.nr index 333304471..3d0015900 100644 --- a/packages/circuits/noir/crates/smt_pedersen/src/lib.nr +++ b/packages/circuits/noir/crates/smt_pedersen/src/lib.nr @@ -60,7 +60,7 @@ pub fn add(new_entry: [Field; 2], old_root: Field, siblings: [Field; TREE_DEPTH] * of that existing entry and then calculates the new root (without the entry). * @param entry Contains key and value of the to-be-deleted entry: [key, value] * @param old_root The root of the tree if the entry is still included - * @param sigbils Contains array of siblings of entry + * @param siblings Contains array of siblings of entry * @returns The new root after the deletion */ pub fn delete(entry: [Field; 2], old_root: Field, siblings: [Field; TREE_DEPTH]) -> Field { diff --git a/packages/circuits/noir/crates/smt_poseidon2/src/lib.nr b/packages/circuits/noir/crates/smt_poseidon2/src/lib.nr index 076933673..cd4850ade 100644 --- a/packages/circuits/noir/crates/smt_poseidon2/src/lib.nr +++ b/packages/circuits/noir/crates/smt_poseidon2/src/lib.nr @@ -60,7 +60,7 @@ pub fn add(new_entry: [Field; 2], old_root: Field, siblings: [Field; TREE_DEPTH] * of that existing entry and then calculates the new root (without the entry). * @param entry Contains key and value of the to-be-deleted entry: [key, value] * @param old_root The root of the tree if the entry is still included - * @param sigbils Contains array of siblings of entry + * @param siblings Contains array of siblings of entry * @returns The new root after the deletion */ pub fn delete(entry: [Field; 2], old_root: Field, siblings: [Field; TREE_DEPTH]) -> Field { diff --git a/packages/circuits/noir/crates/smt_poseidon2/src/utils.nr b/packages/circuits/noir/crates/smt_poseidon2/src/utils.nr index 8a784f6fe..94229106e 100644 --- a/packages/circuits/noir/crates/smt_poseidon2/src/utils.nr +++ b/packages/circuits/noir/crates/smt_poseidon2/src/utils.nr @@ -2,7 +2,7 @@ use dep::std::hash::poseidon2::Poseidon2; use crate::TREE_DEPTH; /* - * Transforms the key into into a big endian array of bits so that when determining the position + * Transforms the key into a big endian array of bits so that when determining the position * of a tree entry starting from the root node, the first array element to look at is the last. * @param key The key of a tree entry * @returns The path that determines the position of a key in the tree From 6ab210f2d6828395d1d0e8d137150b021d638456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Tue, 9 Apr 2024 09:15:26 +0100 Subject: [PATCH 3/3] fix(circuits): fix typo --- packages/circuits/noir/crates/smt_pedersen/src/lib.nr | 8 ++++---- packages/circuits/noir/crates/smt_poseidon/src/lib.nr | 8 ++++---- packages/circuits/noir/crates/smt_poseidon2/src/lib.nr | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/circuits/noir/crates/smt_pedersen/src/lib.nr b/packages/circuits/noir/crates/smt_pedersen/src/lib.nr index 3d0015900..611bc1605 100644 --- a/packages/circuits/noir/crates/smt_pedersen/src/lib.nr +++ b/packages/circuits/noir/crates/smt_pedersen/src/lib.nr @@ -14,21 +14,21 @@ global TREE_DEPTH: u32 = 256; * @param root The expected root of the tree */ pub fn verify(entry: [Field; 2], matching_entry: [Option; 2], siblings: [Field; TREE_DEPTH], root: Field) { - let mut calculcated_root: Field = 0; + let mut calculated_root: Field = 0; let path = utils::key_to_path(entry[0]); // if there is no matching_entry it is a membership proof // if there is a matching_entry it is a non_membership proof if matching_entry[0].is_none() | matching_entry[1].is_none() { // membership proof: the root is calculated based on the entry, the siblings, // and the path determined by the key of entry through consecutive hashing - calculcated_root = utils::calculcate_root(entry, siblings, path); + calculated_root = utils::calculcate_root(entry, siblings, path); } else { // non-membership proof: the root is calculated based on the matching_entry, the siblings // and the path that is determined by the key of entry. This makes sure that matching_entry is in fact // a matching entry for entry meaning that it shares the same first bits as path - calculcated_root = utils::calculcate_root([matching_entry[0].unwrap(), matching_entry[1].unwrap()], siblings, path); + calculated_root = utils::calculcate_root([matching_entry[0].unwrap(), matching_entry[1].unwrap()], siblings, path); } - assert(calculcated_root == root); + assert(calculated_root == root); } /** diff --git a/packages/circuits/noir/crates/smt_poseidon/src/lib.nr b/packages/circuits/noir/crates/smt_poseidon/src/lib.nr index 029ae3d40..b794446b0 100644 --- a/packages/circuits/noir/crates/smt_poseidon/src/lib.nr +++ b/packages/circuits/noir/crates/smt_poseidon/src/lib.nr @@ -14,21 +14,21 @@ global TREE_DEPTH: u32 = 256; * @param root The expected root of the tree */ pub fn verify(entry: [Field; 2], matching_entry: [Option; 2], siblings: [Field; TREE_DEPTH], root: Field) { - let mut calculcated_root: Field = 0; + let mut calculated_root: Field = 0; let path = utils::key_to_path(entry[0]); // if there is no matching_entry it is a membership proof // if there is a matching_entry it is a non_membership proof if matching_entry[0].is_none() | matching_entry[1].is_none() { // membership proof: the root is calculated based on the entry, the siblings, // and the path determined by the key of entry through consecutive hashing - calculcated_root = utils::calculcate_root(entry, siblings, path); + calculated_root = utils::calculcate_root(entry, siblings, path); } else { // non-membership proof: the root is calculated based on the matching_entry, the siblings // and the path that is determined by the key of entry. This makes sure that matching_entry is in fact // a matching entry for entry meaning that it shares the same first bits as path - calculcated_root = utils::calculcate_root([matching_entry[0].unwrap(), matching_entry[1].unwrap()], siblings, path); + calculated_root = utils::calculcate_root([matching_entry[0].unwrap(), matching_entry[1].unwrap()], siblings, path); } - assert(calculcated_root == root); + assert(calculated_root == root); } /** diff --git a/packages/circuits/noir/crates/smt_poseidon2/src/lib.nr b/packages/circuits/noir/crates/smt_poseidon2/src/lib.nr index cd4850ade..151688223 100644 --- a/packages/circuits/noir/crates/smt_poseidon2/src/lib.nr +++ b/packages/circuits/noir/crates/smt_poseidon2/src/lib.nr @@ -14,21 +14,21 @@ global TREE_DEPTH: u32 = 256; * @param root The expected root of the tree */ pub fn verify(entry: [Field; 2], matching_entry: [Option; 2], siblings: [Field; TREE_DEPTH], root: Field) { - let mut calculcated_root: Field = 0; + let mut calculated_root: Field = 0; let path = utils::key_to_path(entry[0]); // if there is no matching_entry it is a membership proof // if there is a matching_entry it is a non_membership proof if matching_entry[0].is_none() | matching_entry[1].is_none() { // membership proof: the root is calculated based on the entry, the siblings, // and the path determined by the key of entry through consecutive hashing - calculcated_root = utils::calculcate_root(entry, siblings, path); + calculated_root = utils::calculcate_root(entry, siblings, path); } else { // non-membership proof: the root is calculated based on the matching_entry, the siblings // and the path that is determined by the key of entry. This makes sure that matching_entry is in fact // a matching entry for entry meaning that it shares the same first bits as path - calculcated_root = utils::calculcate_root([matching_entry[0].unwrap(), matching_entry[1].unwrap()], siblings, path); + calculated_root = utils::calculcate_root([matching_entry[0].unwrap(), matching_entry[1].unwrap()], siblings, path); } - assert(calculcated_root == root); + assert(calculated_root == root); } /**