From f1496ef14d6d0b6d543cad3c01a3243e1debe417 Mon Sep 17 00:00:00 2001 From: Hakeem Kazeem Date: Wed, 21 Jan 2026 19:36:48 +0100 Subject: [PATCH 1/5] batch verification implementation --- .../identity-registry-contract/src/error.rs | 1 + .../identity-registry-contract/src/lib.rs | 29 +++++- .../identity-registry-contract/src/test.rs | 91 ++++++++++++++++++- 3 files changed, 117 insertions(+), 4 deletions(-) diff --git a/contracts/identity-registry-contract/src/error.rs b/contracts/identity-registry-contract/src/error.rs index 1893aaa..8bf9322 100644 --- a/contracts/identity-registry-contract/src/error.rs +++ b/contracts/identity-registry-contract/src/error.rs @@ -15,4 +15,5 @@ pub enum RegistryError { ExpertNotFound = 4, AlreadyVerified = 5, AlreadyBanned = 6, + ExpertVecMax = 7, } \ No newline at end of file diff --git a/contracts/identity-registry-contract/src/lib.rs b/contracts/identity-registry-contract/src/lib.rs index 85df4f3..d1a9fa5 100644 --- a/contracts/identity-registry-contract/src/lib.rs +++ b/contracts/identity-registry-contract/src/lib.rs @@ -8,8 +8,10 @@ mod types; #[cfg(test)] mod test; -use soroban_sdk::{contract, contractimpl, Address, Env}; -use crate::error::RegistryError; +use core::ops::Add; + +use soroban_sdk::{contract, contractimpl, Address, Env, Vec}; +use crate::{error::RegistryError, types::ExpertStatus}; #[contract] pub struct IdentityRegistryContract; @@ -20,4 +22,25 @@ impl IdentityRegistryContract { pub fn init(env: Env, admin: Address) -> Result<(), RegistryError> { contract::initialize_registry(&env, &admin) } -} \ No newline at end of file + + /// Batch Verification + pub fn batch_add_experts(env:Env, experts: Vec
) -> Result<(), RegistryError> { + if experts.len() > 20 { + return Err(RegistryError::ExpertVecMax); + } + + let admin = storage::get_admin(&env).unwrap(); + admin.require_auth(); + + for expert in experts { + let status = storage::get_expert_status(&env, &expert); + if status == ExpertStatus::Verified { + return Err(RegistryError::AlreadyVerified); + } + storage::set_expert_record(&env, &expert, ExpertStatus::Verified); + events::emit_status_change(&env, expert, ExpertStatus::Unverified, ExpertStatus::Verified, admin.clone()); + } + + Ok(()) + } +} \ No newline at end of file diff --git a/contracts/identity-registry-contract/src/test.rs b/contracts/identity-registry-contract/src/test.rs index a610c9f..dee65f1 100644 --- a/contracts/identity-registry-contract/src/test.rs +++ b/contracts/identity-registry-contract/src/test.rs @@ -1,6 +1,7 @@ #![cfg(test)] use crate::{IdentityRegistryContract, IdentityRegistryContractClient}; -use soroban_sdk::{Env, testutils::Address as _}; +use soroban_sdk::{Env, Vec, testutils::Address as _, Address, vec}; +use crate::{storage, types::ExpertStatus}; #[test] fn test_initialization() { @@ -18,4 +19,92 @@ fn test_initialization() { // 3. Call init again (Should fail) let res_duplicate = client.try_init(&admin); assert!(res_duplicate.is_err()); +} + +#[test] +fn test_batch_verification() { + let env = Env::default(); + env.mock_all_auths(); + + let contract_id = env.register_contract(None, IdentityRegistryContract); + let client = IdentityRegistryContractClient::new(&env, &contract_id); + + let admin = soroban_sdk::Address::generate(&env); + client.init(&admin); + + let experts = vec![&env, Address::generate(&env), Address::generate(&env), Address::generate(&env)]; + + client.batch_add_experts(&experts); +} + +#[test] +#[should_panic] +fn test_batch_verification_no_admin() { + let env = Env::default(); + env.mock_all_auths(); + + let contract_id = env.register_contract(None, IdentityRegistryContract); + let client = IdentityRegistryContractClient::new(&env, &contract_id); + + let experts = vec![&env, Address::generate(&env), Address::generate(&env), Address::generate(&env)]; + + client.batch_add_experts(&experts); +} + +#[test] +fn test_batch_verification_check_status() { + let env = Env::default(); + env.mock_all_auths(); + + let contract_id = env.register_contract(None, IdentityRegistryContract); + let client = IdentityRegistryContractClient::new(&env, &contract_id); + + let admin = soroban_sdk::Address::generate(&env); + client.init(&admin); + + let e1 = Address::generate(&env); + let e2 = Address::generate(&env); + let e3 = Address::generate(&env); + let e4 = Address::generate(&env); + let e5 = Address::generate(&env); + + let experts = vec![&env, e1.clone(), e2.clone(), e3.clone(), e4.clone(), e5.clone()]; + + client.batch_add_experts(&experts); + + env.as_contract(&contract_id, ||{ + assert_eq!(storage::get_expert_status(&env, &e1), ExpertStatus::Verified); + assert_eq!(storage::get_expert_status(&env, &e2), ExpertStatus::Verified); + assert_eq!(storage::get_expert_status(&env, &e3), ExpertStatus::Verified); + assert_eq!(storage::get_expert_status(&env, &e4), ExpertStatus::Verified); + assert_eq!(storage::get_expert_status(&env, &e5), ExpertStatus::Verified); + }) +} + +#[test] +#[should_panic(expected = "Error(Contract, #7)")] +fn test_batch_verification_max_vec() { + let env = Env::default(); + env.mock_all_auths(); + + let contract_id = env.register_contract(None, IdentityRegistryContract); + let client = IdentityRegistryContractClient::new(&env, &contract_id); + + let admin = soroban_sdk::Address::generate(&env); + client.init(&admin); + + let e1 = Address::generate(&env); + let e2 = Address::generate(&env); + let e3 = Address::generate(&env); + let e4 = Address::generate(&env); + + let experts = vec![&env, e1.clone(), e2.clone(), e3.clone(), e4.clone(), + e1.clone(), e2.clone(), e3.clone(), e4.clone(), + e1.clone(), e2.clone(), e3.clone(), e4.clone(), + e1.clone(), e2.clone(), e3.clone(), e4.clone(), + e1.clone(), e2.clone(), e3.clone(), e4.clone(), + e1.clone(), e2.clone(), e3.clone(), e4.clone() + ]; + + client.batch_add_experts(&experts); } \ No newline at end of file From 966d5aec77568e94d23d637f122fa444416d726c Mon Sep 17 00:00:00 2001 From: Hakeem Kazeem Date: Wed, 21 Jan 2026 21:46:53 +0100 Subject: [PATCH 2/5] fix issues --- contracts/identity-registry-contract/src/lib.rs | 2 +- contracts/identity-registry-contract/src/test.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/identity-registry-contract/src/lib.rs b/contracts/identity-registry-contract/src/lib.rs index d1a9fa5..877763d 100644 --- a/contracts/identity-registry-contract/src/lib.rs +++ b/contracts/identity-registry-contract/src/lib.rs @@ -29,7 +29,7 @@ impl IdentityRegistryContract { return Err(RegistryError::ExpertVecMax); } - let admin = storage::get_admin(&env).unwrap(); + let admin = storage::get_admin(&env).ok_or(RegistryError::NotInitialized)?; admin.require_auth(); for expert in experts { diff --git a/contracts/identity-registry-contract/src/test.rs b/contracts/identity-registry-contract/src/test.rs index dee65f1..ab55535 100644 --- a/contracts/identity-registry-contract/src/test.rs +++ b/contracts/identity-registry-contract/src/test.rs @@ -41,7 +41,6 @@ fn test_batch_verification() { #[should_panic] fn test_batch_verification_no_admin() { let env = Env::default(); - env.mock_all_auths(); let contract_id = env.register_contract(None, IdentityRegistryContract); let client = IdentityRegistryContractClient::new(&env, &contract_id); From 441fa4edb7771285ef98252bfae7287f067aaafd Mon Sep 17 00:00:00 2001 From: Hakeem Kazeem Date: Thu, 22 Jan 2026 13:45:05 +0100 Subject: [PATCH 3/5] moved logic to contract.rs --- .../src/contract.rs | 26 +++++++++++++++++-- .../identity-registry-contract/src/lib.rs | 26 +++---------------- .../identity-registry-contract/src/test.rs | 18 +------------ 3 files changed, 29 insertions(+), 41 deletions(-) diff --git a/contracts/identity-registry-contract/src/contract.rs b/contracts/identity-registry-contract/src/contract.rs index 23712ca..e3c85f3 100644 --- a/contracts/identity-registry-contract/src/contract.rs +++ b/contracts/identity-registry-contract/src/contract.rs @@ -1,6 +1,7 @@ -use soroban_sdk::{Address, Env}; +use soroban_sdk::{contract, contractimpl, Address, Env, Vec}; use crate::storage; -use crate::error::RegistryError; +use crate::events; +use crate::{error::RegistryError, types::ExpertStatus}; pub fn initialize_registry(env: &Env, admin: &Address) -> Result<(), RegistryError> { if storage::has_admin(env) { @@ -9,5 +10,26 @@ pub fn initialize_registry(env: &Env, admin: &Address) -> Result<(), RegistryErr storage::set_admin(env, admin); + Ok(()) +} + +/// Batch Verification +pub fn batch_add_experts(env:Env, experts: Vec
) -> Result<(), RegistryError> { + if experts.len() > 20 { + return Err(RegistryError::ExpertVecMax); + } + + let admin = storage::get_admin(&env).ok_or(RegistryError::NotInitialized)?; + admin.require_auth(); + + for expert in experts { + let status = storage::get_expert_status(&env, &expert); + if status == ExpertStatus::Verified { + return Err(RegistryError::AlreadyVerified); + } + storage::set_expert_record(&env, &expert, ExpertStatus::Verified); + events::emit_status_change(&env, expert, status, ExpertStatus::Verified, admin.clone()); + } + Ok(()) } \ No newline at end of file diff --git a/contracts/identity-registry-contract/src/lib.rs b/contracts/identity-registry-contract/src/lib.rs index 877763d..719957e 100644 --- a/contracts/identity-registry-contract/src/lib.rs +++ b/contracts/identity-registry-contract/src/lib.rs @@ -8,10 +8,8 @@ mod types; #[cfg(test)] mod test; -use core::ops::Add; - use soroban_sdk::{contract, contractimpl, Address, Env, Vec}; -use crate::{error::RegistryError, types::ExpertStatus}; +use crate::{error::RegistryError}; #[contract] pub struct IdentityRegistryContract; @@ -23,24 +21,8 @@ impl IdentityRegistryContract { contract::initialize_registry(&env, &admin) } - /// Batch Verification - pub fn batch_add_experts(env:Env, experts: Vec
) -> Result<(), RegistryError> { - if experts.len() > 20 { - return Err(RegistryError::ExpertVecMax); - } - - let admin = storage::get_admin(&env).ok_or(RegistryError::NotInitialized)?; - admin.require_auth(); - - for expert in experts { - let status = storage::get_expert_status(&env, &expert); - if status == ExpertStatus::Verified { - return Err(RegistryError::AlreadyVerified); - } - storage::set_expert_record(&env, &expert, ExpertStatus::Verified); - events::emit_status_change(&env, expert, ExpertStatus::Unverified, ExpertStatus::Verified, admin.clone()); - } - - Ok(()) + pub fn batch_add_experts(env: Env, experts: Vec
) -> Result<(), RegistryError> { + contract::batch_add_experts(env, experts) } + } \ No newline at end of file diff --git a/contracts/identity-registry-contract/src/test.rs b/contracts/identity-registry-contract/src/test.rs index ab55535..d7357cf 100644 --- a/contracts/identity-registry-contract/src/test.rs +++ b/contracts/identity-registry-contract/src/test.rs @@ -21,22 +21,6 @@ fn test_initialization() { assert!(res_duplicate.is_err()); } -#[test] -fn test_batch_verification() { - let env = Env::default(); - env.mock_all_auths(); - - let contract_id = env.register_contract(None, IdentityRegistryContract); - let client = IdentityRegistryContractClient::new(&env, &contract_id); - - let admin = soroban_sdk::Address::generate(&env); - client.init(&admin); - - let experts = vec![&env, Address::generate(&env), Address::generate(&env), Address::generate(&env)]; - - client.batch_add_experts(&experts); -} - #[test] #[should_panic] fn test_batch_verification_no_admin() { @@ -61,7 +45,7 @@ fn test_batch_verification_check_status() { let admin = soroban_sdk::Address::generate(&env); client.init(&admin); - let e1 = Address::generate(&env); + let e1: Address = Address::generate(&env); let e2 = Address::generate(&env); let e3 = Address::generate(&env); let e4 = Address::generate(&env); From f142583392dd5405e0393a68173ce62c257e2b89 Mon Sep 17 00:00:00 2001 From: Hakeem Kazeem Date: Thu, 22 Jan 2026 13:52:34 +0100 Subject: [PATCH 4/5] fix contract --- contracts/identity-registry-contract/src/contract.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/identity-registry-contract/src/contract.rs b/contracts/identity-registry-contract/src/contract.rs index 8844026..5fcdc80 100644 --- a/contracts/identity-registry-contract/src/contract.rs +++ b/contracts/identity-registry-contract/src/contract.rs @@ -2,7 +2,6 @@ use soroban_sdk::{contract, contractimpl, Address, Env, Vec}; use crate::storage; use crate::events; use crate::{error::RegistryError, types::ExpertStatus}; -use crate::types::ExpertStatus; pub fn initialize_registry(env: &Env, admin: &Address) -> Result<(), RegistryError> { if storage::has_admin(env) { @@ -31,6 +30,8 @@ pub fn batch_add_experts(env:Env, experts: Vec
) -> Result<(), RegistryE storage::set_expert_record(&env, &expert, ExpertStatus::Verified); events::emit_status_change(&env, expert, status, ExpertStatus::Verified, admin.clone()); } + + Ok(()) } pub fn verify_expert(env: &Env, expert: &Address) -> Result<(), RegistryError> { From 9f50c9d35b6e54567d9c6e789d70252c61b87b64 Mon Sep 17 00:00:00 2001 From: Hakeem Kazeem Date: Thu, 22 Jan 2026 14:28:57 +0100 Subject: [PATCH 5/5] fix --- contracts/identity-registry-contract/src/contract.rs | 2 +- contracts/identity-registry-contract/src/test.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/identity-registry-contract/src/contract.rs b/contracts/identity-registry-contract/src/contract.rs index 5fcdc80..6e9f581 100644 --- a/contracts/identity-registry-contract/src/contract.rs +++ b/contracts/identity-registry-contract/src/contract.rs @@ -1,4 +1,4 @@ -use soroban_sdk::{contract, contractimpl, Address, Env, Vec}; +use soroban_sdk::{Address, Env, Vec}; use crate::storage; use crate::events; use crate::{error::RegistryError, types::ExpertStatus}; diff --git a/contracts/identity-registry-contract/src/test.rs b/contracts/identity-registry-contract/src/test.rs index 1d14df0..b729f57 100644 --- a/contracts/identity-registry-contract/src/test.rs +++ b/contracts/identity-registry-contract/src/test.rs @@ -96,6 +96,7 @@ fn test_batch_verification_max_vec() { client.batch_add_experts(&experts); } +#[test] fn test_add_expert() { let env = Env::default(); env.mock_all_auths();