From d778748fa4c2bbe977d8c6b5e05921afcdec4859 Mon Sep 17 00:00:00 2001 From: ChrisFernandezVivas Date: Sun, 3 Nov 2024 23:04:46 -0600 Subject: [PATCH] migrate to Soroban, important note , still imcomplete --- .../SemaphoreGroups/src/FSemaphoreGroups.rs | 161 +++++++++ .../contracts/SemaphoreGroups/src/hash.rs | 14 + .../contracts/SemaphoreGroups/src/imr.rs | 325 ++++++++++++++++++ .../contracts/SemaphoreGroups/src/lib.rs | 262 ++++++++++++++ .../contracts/SemaphoreGroups/src/test.rs | 21 ++ 5 files changed, 783 insertions(+) create mode 100644 packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/FSemaphoreGroups.rs create mode 100644 packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/hash.rs create mode 100644 packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/imr.rs create mode 100644 packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/lib.rs create mode 100644 packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/test.rs diff --git a/packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/FSemaphoreGroups.rs b/packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/FSemaphoreGroups.rs new file mode 100644 index 000000000..ee9755273 --- /dev/null +++ b/packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/FSemaphoreGroups.rs @@ -0,0 +1,161 @@ +#![no_std] +use soroban_sdk::{Address,Vec,String,Env}; + + +#[derive(Debug)] +pub enum SemaphoreError { + GroupDoesNotExist, + CallerIsNotTheGroupAdmin, + CallerIsNotThePendingGroupAdmin, + WrongSiblingNodes, + LeafGreaterThanSnarkScalarField, + LeafCannotBeZero, + LeafAlreadyExists, + LeafDoesNotExist, + +} + +//eventos afuera del trait luego llamamos dichos eventos en las funciones + + +/// @dev Event emitted when a new group is created. +/// @param groupId: Id of the group. + +pub struct GroupCreated { + pub groupId: u128, +} + +/// @dev Event emitted when a new admin is assigned to a group. +/// @param groupId: Id of the group. +/// @param oldAdmin: Old admin of the group. +/// @param newAdmin: New admin of the group. + +pub struct GroupAdminUpdated { + pub groupId: u128, + pub oldAdmin: Address, + pub newAdmin: Address, +} + +/// @dev Event emitted when a group admin is being updated. +/// @param groupId: Id of the group. +/// @param oldAdmin: Old admin of the group. +/// @param newAdmin: New admin of the group. + +pub struct GroupAdminPending { + pub groupId: u128, + pub oldAdmin: Address, + pub newAdmin: Address, +} + +/// @dev Event emitted when a new identity commitment is added. +/// @param groupId: Group id of the group. +/// @param index: Merkle tree leaf index. +/// @param identityCommitment: New identity commitment. +/// @param merkleTreeRoot: New root hash of the tree. + +pub struct MemberAdded { + pub groupId: u128, + pub index: u128, + pub identityCommitment: u128, + pub merkleTreeRoot: u128, +} + + +/// @dev Event emitted when many identity commitments are added at the same time. +/// @param groupId: Group id of the group. +/// @param startIndex: Index of the first element of the new identity commitments in the merkle tree. +/// @param identityCommitments: The new identity commitments. +/// @param merkleTreeRoot: New root hash of the tree. + +pub struct MembersAdded { + pub groupId: u128, + pub index: u128, + pub identityCommitment: Vec, + pub merkleTreeRoot: u128, +} + +/// @dev Event emitted when an identity commitment is updated. +/// @param groupId: Group id of the group. +/// @param index: Identity commitment index. +/// @param identityCommitment: Existing identity commitment to be updated. +/// @param newIdentityCommitment: New identity commitment. +/// @param merkleTreeRoot: New root hash of the tree. + +pub struct MemberUpdated { + pub groupId: u128, + pub index: u128, + pub identityCommitment: u128, + pub newIdentityCommitment: u128, + pub merkleTreeRoot: u128, + +} + +/// @dev Event emitted when a new identity commitment is removed. +/// @param groupId: Group id of the group. +/// @param index: Identity commitment index. +/// @param identityCommitment: Existing identity commitment to be removed. +/// @param merkleTreeRoot: New root hash of the tree. + +pub struct MemberRemoved { + pub groupId: u128, + pub index: u128, + pub identityCommitment: u128, + pub merkleTreeRoot: u128, +} + +//finished events + +pub trait ISemaphoreGroups{ + /// @dev Returns the address of the group admin. The group admin can be an Ethereum account or a smart contract. + /// @param groupId: Id of the group. + /// @return Address of the group admin. + fn getGroupAdmin(&self, groupId: u128) -> Address; + + /// @dev Returns true if a member exists in a group. + /// @param groupId: Id of the group. + /// @param identityCommitment: Identity commitment. + /// @return True if the member exists, false otherwise. + fn hasMember(&self,groupId: u128, identityCommitment: u128) -> bool; + + /// @dev Returns the index of a member. + /// @param groupId: Id of the group. + /// @param identityCommitment: Identity commitment. + /// @return Index of member. + fn indexOf(&self,groupId: u128, identityCommitment: u128) -> Result ; + + /// @dev Returns the last root hash of a group. + /// @param groupId: Id of the group. + /// @return Root hash of the group. + fn getMerkleTreeRoot(&self,groupId: u128) -> u128; + + /// @dev Returns the depth of the tree of a group. + /// @param groupId: Id of the group. + /// @return Depth of the group tree. + fn getMerkleTreeDepth(&self,groupId: u128) -> u128; + + /// @dev Returns the number of tree leaves of a group. + /// @param groupId: Id of the group. + /// @return Number of tree leaves. + fn getMerkleTreeSize(&self,groupId: u128) -> u128; + + fn _removeMember(&self,groupId:u128,identityCommitment: u128,merkleProofSiblings: Vec)-> u128; + + fn _updateMember(&self,groupId: u128, oldIdentityCommitment: u128, newIdentityCommitment: u128,merkleProofSiblings : Vec)-> u128; + + fn _addMember(&self,groupId: u128, identityCommitment: u128)->u128; + + fn hash_function(&self,nodes: Vec) -> String; + + fn _acceptGroupAdmin(&self,groupId: u128)-> Result<(), SemaphoreError>; + + fn _updateGroupAdmin(&self,env: Env,groupId: u128, newAdmin: Address); + + fn _createGroup(&self,env: Env,groupId: u128, admin: Address); + + fn only_existing_group(&self,groupId: u128) -> Result<(), SemaphoreError> ; + + + fn only_group_admin(&self,groupId: u128) -> Result<(), SemaphoreError>; + +} + diff --git a/packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/hash.rs b/packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/hash.rs new file mode 100644 index 000000000..ec2590d70 --- /dev/null +++ b/packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/hash.rs @@ -0,0 +1,14 @@ +use tiny_keccak::{Hasher, Keccak}; + +pub fn keccak256_hash_function(nodes: Vec) -> String { + let mut keccak = Keccak::v256(); + let mut result = [0u8; 32]; + + for node in nodes { + keccak.update(node.as_bytes()); + } + + keccak.finalize(&mut result); + + hex::encode(result) +} \ No newline at end of file diff --git a/packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/imr.rs b/packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/imr.rs new file mode 100644 index 000000000..2361e8933 --- /dev/null +++ b/packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/imr.rs @@ -0,0 +1,325 @@ +pub struct IMT { + nodes: Vec>, + zeroes: Vec, + hash: IMTHashFunction, + depth: usize, + arity: usize, +} + +pub struct IMTMerkleProof { + root: IMTNode, + leaf: IMTNode, + path_indices: Vec, + siblings: Vec>, +} + +pub type IMTNode = String; +pub type IMTHashFunction = fn(Vec) -> IMTNode; + +impl IMT { + pub fn new( + hash: IMTHashFunction, + depth: usize, + zero_value: IMTNode, + arity: usize, + leaves: Vec, + ) -> Result { + if leaves.len() > arity.pow(depth as u32) { + return Err("The tree cannot contain more than arity^depth leaves"); + } + + let mut imt = IMT { + nodes: vec![vec![]; depth + 1], + zeroes: vec![], + hash, + depth, + arity, + }; + + let mut current_zero = zero_value; + for _ in 0..depth { + imt.zeroes.push(current_zero.clone()); + current_zero = (imt.hash)(vec![current_zero; arity]); + } + + imt.nodes[0] = leaves; + + for level in 0..depth { + for index in 0..((imt.nodes[level].len() as f64 / arity as f64).ceil() as usize) { + let position = index * arity; + let children: Vec<_> = (0..arity) + .map(|i| { + imt.nodes[level] + .get(position + i) + .cloned() + .unwrap_or_else(|| imt.zeroes[level].clone()) + }) + .collect(); + + if let Some(next_level) = imt.nodes.get_mut(level + 1) { + next_level.push((imt.hash)(children)); + } + } + } + + Ok(imt) + } + + pub fn root(&mut self) -> Option { + self.nodes[self.depth].first().cloned() + } + + pub fn depth(&self) -> usize { + self.depth + } + + pub fn nodes(&self) -> Vec> { + self.nodes.clone() + } + + pub fn zeroes(&self) -> Vec { + self.zeroes.clone() + } + + pub fn leaves(&self) -> Vec { + self.nodes[0].clone() + } + + pub fn arity(&self) -> usize { + self.arity + } + + pub fn insert(&mut self, leaf: IMTNode) -> Result<(), &'static str> { + if self.nodes[0].len() >= self.arity.pow(self.depth as u32) { + return Err("The tree is full"); + } + + let index = self.nodes[0].len(); + self.nodes[0].push(leaf); + self.update(index, self.nodes[0][index].clone()) + } + + pub fn update(&mut self, mut index: usize, new_leaf: IMTNode) -> Result<(), &'static str> { + if index >= self.nodes[0].len() { + return Err("The leaf does not exist in this tree"); + } + + let mut node = new_leaf; + self.nodes[0][index].clone_from(&node); + + for level in 0..self.depth { + let position = index % self.arity; + let level_start_index = index - position; + let level_end_index = level_start_index + self.arity; + + let children: Vec<_> = (level_start_index..level_end_index) + .map(|i| { + self.nodes[level] + .get(i) + .cloned() + .unwrap_or_else(|| self.zeroes[level].clone()) + }) + .collect(); + + node = (self.hash)(children); + index /= self.arity; + + if self.nodes[level + 1].len() <= index { + self.nodes[level + 1].push(node.clone()); + } else { + self.nodes[level + 1][index].clone_from(&node); + } + } + + Ok(()) + } + + pub fn delete(&mut self, index: usize) -> Result<(), &'static str> { + self.update(index, self.zeroes[0].clone()) + } + + pub fn create_proof(&self, index: usize) -> Result { + if index >= self.nodes[0].len() { + return Err("The leaf does not exist in this tree"); + } + + let mut siblings = Vec::with_capacity(self.depth); + let mut path_indices = Vec::with_capacity(self.depth); + let mut current_index = index; + + for level in 0..self.depth { + let position = current_index % self.arity; + let level_start_index = current_index - position; + let level_end_index = level_start_index + self.arity; + + path_indices.push(position); + let mut level_siblings = Vec::new(); + + for i in level_start_index..level_end_index { + if i != current_index { + level_siblings.push( + self.nodes[level] + .get(i) + .cloned() + .unwrap_or_else(|| self.zeroes[level].clone()), + ); + } + } + + siblings.push(level_siblings); + current_index /= self.arity; + } + + Ok(IMTMerkleProof { + root: self.nodes[self.depth][0].clone(), + leaf: self.nodes[0][index].clone(), + path_indices, + siblings, + }) + } + + pub fn verify_proof(&self, proof: &IMTMerkleProof) -> bool { + let mut node = proof.leaf.clone(); + + for (i, sibling) in proof.siblings.iter().enumerate() { + let mut children = sibling.clone(); + children.insert(proof.path_indices[i], node); + + node = (self.hash)(children); + } + + node == proof.root + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn simple_hash_function(nodes: Vec) -> String { + nodes.join(",") + } + + #[test] + fn test_new_imt() { + let hash: IMTHashFunction = simple_hash_function; + let imt = IMT::new(hash, 3, "zero".to_string(), 2, vec![]); + + assert!(imt.is_ok()); + } + + #[test] + fn test_insertion() { + let hash: IMTHashFunction = simple_hash_function; + let mut imt = IMT::new(hash, 3, "zero".to_string(), 2, vec![]).unwrap(); + + assert!(imt.insert("leaf1".to_string()).is_ok()); + } + + #[test] + fn test_delete() { + let hash: IMTHashFunction = simple_hash_function; + let mut imt = IMT::new(hash, 3, "zero".to_string(), 2, vec!["leaf1".to_string()]).unwrap(); + + assert!(imt.delete(0).is_ok()); + } + + #[test] + fn test_update() { + let hash: IMTHashFunction = simple_hash_function; + let mut imt = IMT::new(hash, 3, "zero".to_string(), 2, vec!["leaf1".to_string()]).unwrap(); + + assert!(imt.update(0, "new_leaf".to_string()).is_ok()); + } + + #[test] + fn test_create_and_verify_proof() { + let hash: IMTHashFunction = simple_hash_function; + let mut imt = IMT::new(hash, 3, "zero".to_string(), 2, vec!["leaf1".to_string()]).unwrap(); + imt.insert("leaf2".to_string()).unwrap(); + + let proof = imt.create_proof(0); + assert!(proof.is_ok()); + + let proof = proof.unwrap(); + assert!(imt.verify_proof(&proof)); + } + + #[test] + fn should_not_initialize_with_too_many_leaves() { + let hash: IMTHashFunction = simple_hash_function; + let leaves = vec![ + "leaf1".to_string(), + "leaf2".to_string(), + "leaf3".to_string(), + "leaf4".to_string(), + "leaf5".to_string(), + ]; + let imt = IMT::new(hash, 2, "zero".to_string(), 2, leaves); + assert!(imt.is_err()); + } + + #[test] + fn should_not_insert_in_full_tree() { + let hash: IMTHashFunction = simple_hash_function; + let mut imt = IMT::new( + hash, + 1, + "zero".to_string(), + 2, + vec!["leaf1".to_string(), "leaf2".to_string()], + ) + .unwrap(); + + let result = imt.insert("leaf3".to_string()); + assert!(result.is_err()); + } + + #[test] + fn should_not_delete_nonexistent_leaf() { + let hash: IMTHashFunction = simple_hash_function; + let mut imt = IMT::new(hash, 3, "zero".to_string(), 2, vec!["leaf1".to_string()]).unwrap(); + + let result = imt.delete(1); + assert!(result.is_err()); + } + + #[test] + fn test_root() { + let hash: IMTHashFunction = simple_hash_function; + let mut imt = IMT::new( + hash, + 2, + "zero".to_string(), + 2, + vec!["leaf1".to_string(), "leaf2".to_string()], + ) + .unwrap(); + + assert_eq!(imt.root(), Some("leaf1,leaf2,zero,zero".to_string())); + } + + #[test] + fn test_leaves() { + let hash: IMTHashFunction = simple_hash_function; + let imt = IMT::new( + hash, + 2, + "zero".to_string(), + 2, + vec!["leaf1".to_string(), "leaf2".to_string()], + ) + .unwrap(); + + assert_eq!(imt.leaves(), vec!["leaf1".to_string(), "leaf2".to_string()]); + } + + #[test] + fn test_depth_and_arity() { + let hash: IMTHashFunction = simple_hash_function; + let imt = IMT::new(hash, 3, "zero".to_string(), 2, vec![]).unwrap(); + + assert_eq!(imt.depth(), 3); + assert_eq!(imt.arity(), 2); + } +} \ No newline at end of file diff --git a/packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/lib.rs b/packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/lib.rs new file mode 100644 index 000000000..36eb73fc7 --- /dev/null +++ b/packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/lib.rs @@ -0,0 +1,262 @@ +#![no_std] +use soroban_sdk::{contract, contractimpl, symbol_short, vec, Env, Symbol, Vec, Map,Address}; +mod FSemaphoreGroups; +use zk_kit_imt::imt::IMT; +use crate::FSemaphoreGroups::*; + +#[contract] +pub struct SemaphoreGroups{ + /// @dev Gets a group id and returns its tree data. + /// The tree is an Incremental Merkle Tree + /// which is called Lean Incremental Merkle Tree. + merkleTrees : Map, + + /// @dev Gets a group id and returns its admin. + /// The admin can be an Ethereum account or a smart contract. + admins: Map, + + /// @dev Gets a group id and returns any pending admin. + /// The pending admin can be an Ethereum account or a smart contract. + pendingAdmins : Map, +} + +#[contractimpl] +impl ISemaphoreGroups for SemaphoreGroups{ + + /// @dev Checks if the group admin is the transaction sender. + /// @param groupId: Id of the group. + fn only_group_admin(&self,&group_id: u128) -> Result<(), SemaphoreError> { + let caller_address = ; + let admin_address = self.admins.get(group_id).ok_or(SemaphoreError::CallerIsNotTheGroupAdmin)?; + + // Verificar si el llamador es el administrador + if caller_address != admin_address { + return Err(SemaphoreError::CallerIsNotTheGroupAdmin); + } + + Ok(()) // Si es el administrador, retorna éxito + } + + + /// @dev Checks if the group exists. + /// @param groupId: Id of the group. + fn only_existing_group(&self,groupId: u128) -> Result<(), SemaphoreError> { + + if self.admins.get(groupId).is_none() { + return Err(SemaphoreError::GroupDoesNotExist); + } + Ok(()) + } + + /// @dev Creates a new group. Only the admin will be able to add or remove members. + /// @param groupId: Id of the group. + /// @param admin: Admin of the group. + + fn _createGroup(&self,env: Env,groupId: u128, admin: Address){ + self.admins.set(groupId, admin); + + + let mut event = GroupCreated { groupId }; + env.events().publish((symbol_short!("group"), symbol_short!("created")), event); + + let mut event = GroupAdminUpdated { + groupId: groupId, + oldAdmin: Address::zero(), + newAdmin: admin, + }; + + env.events().publish((symbol_short("groupAdmin"), symbol_short("updated")), event); + } + + /// @dev Updates the group admin. In order for the new admin to actually be updated, + /// they must explicitly accept by calling `_acceptGroupAdmin`. + /// @param groupId: Id of the group. + /// @param newAdmin: New admin of the group. + + fn _updateGroupAdmin(&self,env: Env,groupId: u128, newAdmin: Address){ + // @dev onlygroupadmin function + self.only_group_admin(groupId)?; + + self.pendingAdmins.set(groupId, newAdmin); + let mut event = GroupAdminPending{groupId, + oldAdmin : Env::invoker(), + newAdmin, + }; + + env.events().publish((symbol_short("groupAdmin"), symbol_short("pending")), event); + } + + /// @dev Allows the new admin to accept to update the group admin with their address. + /// @param groupId: Id of the group. + + fn _acceptGroupAdmin(&self,groupId: u128)-> Result<(), SemaphoreError> { + let caller_address = Env::invoker(); + + if self.pendingAdmins.get(groupId).unwrap_or_default() != caller_address { + return Err(SemaphoreError::CallerIsNotThePendingGroupAdmin); + } + + let oldAdmin = self.admins.get(groupId).unwrap_or_default(); + self.admins.insert(groupId, caller_address); + self.pendingAdmins.remove(groupId); + + let mut event = GroupAdminUpdated { + groupId, + oldAdmin, + newAdmin: caller_address, + }; + + Env.events().publish((Symbol!("groupAdmin"), Symbol!("accept")), event); + + Ok(()) + } + + /// @dev Adds an identity commitment to an existing group. + /// @param groupId: Id of the group. + /// @param identityCommitment: New identity commitment. + /// @return merkleTreeRoot New root hash of the tree. + + + fn hash_function(&self,nodes: Vec) -> String { + nodes.join("-") + } + + fn _addMember(&self,groupId: u128, identityCommitment: u128)->u128{ + // @dev onlygroupadmin function + self.only_group_admin(groupId)?; + let index = Self::getMerkleTreeSize(groupId); + //initialize tree + let mut tree = self.merkleTrees + .get(groupId) + .unwrap_or_else(|| { + let newTree = IMT::new(self.hash_function, 10000,zero.clone(),2,vec![]).unwrap(); + self.merkleTrees.insert(groupId, newTree); + self.merkleTrees.get(groupId).unwrap() + }); + + let merkleTreeRoot = tree.insert(identityCommitment.to_string()).unwrap(); + + + Env::emit_event(MemberAdded { + groupId, + index, + identityCommitment, + merkleTreeRoot, + }); + + merkleTreeRoot + } + + /// @dev Adds new members to an existing group. + /// @param groupId: Id of the group. + /// @param identityCommitments: New identity commitments. + /// @return merkleTreeRoot New root hash of the tree. + + /*fn _addMembers(groupId: u128,identityCommitments: Vec)->u128{ + + // @dev onlygroupadmin function + self.only_group_admin(groupId)?; + let startIndex = self.getMerkleTreeSize(groupId); + + //@ dev incomplete + let merkleTreeRoot = self.merkleTrees + + + Env::emit_event(MembersAdded { + groupId, + startIndex, + identityCommitment, + merkleTreeRoot, + }); + + merkleTreeRoot + }*/ + + + /// @dev Updates an identity commitment of an existing group. A proof of membership is + /// needed to check if the node to be updated is part of the tree. + /// @param groupId: Id of the group. + /// @param oldIdentityCommitment: Existing identity commitment to be updated. + /// @param newIdentityCommitment: New identity commitment. + /// @param merkleProofSiblings: Array of the sibling nodes of the proof of membership. + /// @return merkleTreeRoot New root hash of the tree. + fn _updateMember(&self,groupId: u128, oldIdentityCommitment: u128, newIdentityCommitment: u128,merkleProofSiblings : Vec)-> u128{ + // @dev onlygroupadmin function + self.only_group_admin(groupId)?; + + let index = Self::indexOf(groupId,newIdentityCommitment); + let merkleTreeRoot = self.merkleTrees.get(groupId).unwrap_or_default().update(index,newIdentityCommitment); + + Env::emit_event(MemberUpdated{ + groupId, + index, + oldIdentityCommitment, + newIdentityCommitment, + }); + + merkleTreeRoot + + } + + /// @dev Removes an identity commitment from an existing group. A proof of membership is + /// needed to check if the node to be deleted is part of the tree. + /// @param groupId: Id of the group. + /// @param identityCommitment: Existing identity commitment to be removed. + /// @param merkleProofSiblings: Array of the sibling nodes of the proof of membership. + /// @return merkleTreeRoot New root hash of the tree. + fn _removeMember(&self,groupId:u128,identityCommitment: u128,merkleProofSiblings: Vec)-> u128{ + self.only_group_admin(groupId)?; + let index = Self::indexOf(&groupId,identityCommitment); + let merkleTreeRoot = self.merkleTrees.get(groupId).unwrap_or_default().delete(index); + + Env::emit_event(MemberRemoved{ + groupId, + index, + identityCommitment, + merkleTreeRoot, + }); + + merkleTreeRoot + } + + + + /// @dev See {ISemaphoreGroups-getGroupAdmin}. + fn getGroupAdmin(&self,groupId:u128) -> Address { + self.admins.get(groupId).unwrap_or_default() + } + + /// @dev See {ISemaphoreGroups-hasMember}. + fn hasMember(&self,groupId:u128,identityCommitment:u128) -> bool { + self.merkleTrees.get(groupId).unwrap_or_default().leaves.get(&identityCommitment).map_or(false, |&value| value != 0) + } + + + /// @dev See {ISemaphoreGroups-indexOf}. + fn indexOf(&self,groupId : u128, identityCommitment: u128) -> Result { + + match self.merkleTrees.get(groupId).unwrap_or_default().leaves.get(&identityCommitment) { + Some(&index) => Ok(index - 1), // Devolver el índice si existe + None => Err(SemaphoreError::LeafDoesNotExist), // Retornar error si no existe + } + + } + + /// @dev See {ISemaphoreGroups-getMerkleTreeRoot}. + fn getMerkleTreeRoot(&self,groupId:u128) -> u128 { + self.merkleTrees.get(groupId).unwrap_or_default().root() + } + + /// @dev See {ISemaphoreGroups-getMerkleTreeDepth}. + fn getMerkleTreeDepth(&self,groupId:u128) -> u128 { + self.merkleTrees.get(groupId).unwrap_or_default().depth() + } + + /// @dev See {ISemaphoreGroups-getMerkleTreeSize}. + fn getMerkleTreeSize(&self,groupId:u128) -> u128 { + self.merkleTrees.get(groupId).unwrap_or_default().leaves.len() + } + +} + +mod test; diff --git a/packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/test.rs b/packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/test.rs new file mode 100644 index 000000000..4b250446a --- /dev/null +++ b/packages/contracts/contracts/base/SemaphoreGroups/contracts/SemaphoreGroups/src/test.rs @@ -0,0 +1,21 @@ +#![cfg(test)] + +use super::*; +use soroban_sdk::{vec, Env, String}; + +#[test] +fn test() { + let env = Env::default(); + let contract_id = env.register_contract(None, HelloContract); + let client = HelloContractClient::new(&env, &contract_id); + + let words = client.hello(&String::from_str(&env, "Dev")); + assert_eq!( + words, + vec![ + &env, + String::from_str(&env, "Hello"), + String::from_str(&env, "Dev"), + ] + ); +}