diff --git a/contracts/identity-registry-contract/src/contract.rs b/contracts/identity-registry-contract/src/contract.rs index 31249f4..6e9f581 100644 --- a/contracts/identity-registry-contract/src/contract.rs +++ b/contracts/identity-registry-contract/src/contract.rs @@ -1,8 +1,7 @@ -use soroban_sdk::{Address, Env}; +use soroban_sdk::{Address, Env, Vec}; use crate::storage; -use crate::error::RegistryError; -use crate::types::ExpertStatus; use crate::events; +use crate::{error::RegistryError, types::ExpertStatus}; pub fn initialize_registry(env: &Env, admin: &Address) -> Result<(), RegistryError> { if storage::has_admin(env) { @@ -14,6 +13,27 @@ pub fn initialize_registry(env: &Env, admin: &Address) -> Result<(), RegistryErr 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(()) +} + pub fn verify_expert(env: &Env, expert: &Address) -> Result<(), RegistryError> { let admin = storage::get_admin(env).ok_or(RegistryError::NotInitialized)?; 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 6e5de09..d03207f 100644 --- a/contracts/identity-registry-contract/src/lib.rs +++ b/contracts/identity-registry-contract/src/lib.rs @@ -8,8 +8,8 @@ mod types; #[cfg(test)] mod test; -use soroban_sdk::{contract, contractimpl, Address, Env}; -use crate::error::RegistryError; +use soroban_sdk::{contract, contractimpl, Address, Env, Vec}; +use crate::{error::RegistryError}; #[contract] pub struct IdentityRegistryContract; @@ -21,8 +21,13 @@ impl IdentityRegistryContract { contract::initialize_registry(&env, &admin) } + /// Batch Add an expert to the whitelist (Admin only) + pub fn batch_add_experts(env: Env, experts: Vec
) -> Result<(), RegistryError> { + contract::batch_add_experts(env, experts) + } + /// Add an expert to the whitelist (Admin only) pub fn add_expert(env: Env, expert: Address) -> Result<(), RegistryError> { contract::verify_expert(&env, &expert) } -} \ 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 b76bacf..b729f57 100644 --- a/contracts/identity-registry-contract/src/test.rs +++ b/contracts/identity-registry-contract/src/test.rs @@ -3,7 +3,8 @@ extern crate std; use crate::{IdentityRegistryContract, IdentityRegistryContractClient}; -use soroban_sdk::{Env, testutils::Address as _, Symbol, Address, IntoVal, TryIntoVal}; +use crate::{storage, types::ExpertStatus}; +use soroban_sdk::{Env, testutils::Address as _, Symbol, Address, IntoVal, TryIntoVal, Vec, vec}; use soroban_sdk::testutils::{AuthorizedFunction, AuthorizedInvocation, Events}; #[test] @@ -24,6 +25,77 @@ fn test_initialization() { assert!(res_duplicate.is_err()); } +#[test] +#[should_panic] +fn test_batch_verification_no_admin() { + let env = Env::default(); + + 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 = 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); +} + #[test] fn test_add_expert() { let env = Env::default();