From a4982cf40ac89cb69f3c51442a3c8d86d0fa6ab3 Mon Sep 17 00:00:00 2001 From: KevinMB0220 Date: Thu, 26 Mar 2026 12:36:24 -0600 Subject: [PATCH 1/4] feat(identity-registry): add NotBanned error variant Adds RegistryError::NotBanned (code 10) to handle the case where unban_expert is called on an expert who is not currently banned. Co-Authored-By: Claude Sonnet 4.6 --- contracts/identity-registry-contract/src/error.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/identity-registry-contract/src/error.rs b/contracts/identity-registry-contract/src/error.rs index e4462b5..5ba9d39 100644 --- a/contracts/identity-registry-contract/src/error.rs +++ b/contracts/identity-registry-contract/src/error.rs @@ -18,4 +18,5 @@ pub enum RegistryError { ExpertVecMax = 7, NotVerified = 8, UriTooLong = 9, + NotBanned = 10, } From afcb769aa095045097971d655d962687ca1937ae Mon Sep 17 00:00:00 2001 From: KevinMB0220 Date: Thu, 26 Mar 2026 12:36:32 -0600 Subject: [PATCH 2/4] feat(identity-registry): implement unban_expert in contract logic Adds unban_expert function that requires admin auth, validates the expert is currently Banned, reverts status to Verified while preserving the existing data_uri, and emits a status_change event (Banned -> Verified). Total expert count is not incremented since the expert is already indexed. Co-Authored-By: Claude Sonnet 4.6 --- .../src/contract.rs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/contracts/identity-registry-contract/src/contract.rs b/contracts/identity-registry-contract/src/contract.rs index e5eb67e..6939685 100644 --- a/contracts/identity-registry-contract/src/contract.rs +++ b/contracts/identity-registry-contract/src/contract.rs @@ -117,6 +117,32 @@ pub fn ban_expert(env: &Env, expert: &Address) -> Result<(), RegistryError> { Ok(()) } +/// Unban an expert by reverting their status to Verified (Admin only) +pub fn unban_expert(env: &Env, expert: &Address) -> Result<(), RegistryError> { + let admin = storage::get_admin(env).ok_or(RegistryError::NotInitialized)?; + admin.require_auth(); + + let current_status = storage::get_expert_status(env, expert); + + if current_status != ExpertStatus::Banned { + return Err(RegistryError::NotBanned); + } + + // Preserve existing data_uri when unbanning + let existing = storage::get_expert_record(env, expert); + storage::set_expert_record(env, expert, ExpertStatus::Verified, existing.data_uri); + + events::emit_status_change( + env, + expert.clone(), + current_status, + ExpertStatus::Verified, + admin, + ); + + Ok(()) +} + /// Get the total number of verified experts ever indexed pub fn get_total_experts(env: &Env) -> u64 { storage::get_total_experts(env) From 4cd0c5f0f9e2dbf96633f90c011f2cf7d43f950f Mon Sep 17 00:00:00 2001 From: KevinMB0220 Date: Thu, 26 Mar 2026 12:36:39 -0600 Subject: [PATCH 3/4] feat(identity-registry): expose unban_expert in contractimpl Adds pub fn unban_expert(env: Env, expert: Address) -> Result<(), RegistryError> to the IdentityRegistryContract public interface. Co-Authored-By: Claude Sonnet 4.6 --- contracts/identity-registry-contract/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contracts/identity-registry-contract/src/lib.rs b/contracts/identity-registry-contract/src/lib.rs index c2d5f7b..3504ccb 100644 --- a/contracts/identity-registry-contract/src/lib.rs +++ b/contracts/identity-registry-contract/src/lib.rs @@ -43,6 +43,11 @@ impl IdentityRegistryContract { contract::ban_expert(&env, &expert) } + /// Unban an expert and restore their verification status (Admin only) + pub fn unban_expert(env: Env, expert: Address) -> Result<(), RegistryError> { + contract::unban_expert(&env, &expert) + } + /// Get the total number of verified experts ever added to the directory pub fn get_total_experts(env: Env) -> u64 { contract::get_total_experts(&env) From 4a3ba578f67948f1284b1f70bb9027f115f406ef Mon Sep 17 00:00:00 2001 From: KevinMB0220 Date: Thu, 26 Mar 2026 12:36:52 -0600 Subject: [PATCH 4/4] test(identity-registry): add test_unban_expert coverage Tests the full ban -> unban lifecycle: verifies status transitions (Verified -> Banned -> Verified), data_uri preservation, that total expert count does not increment on unban, and that NotBanned is returned when unbanning a non-banned expert. --- .../identity-registry-contract/src/test.rs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/contracts/identity-registry-contract/src/test.rs b/contracts/identity-registry-contract/src/test.rs index 02c6a41..b153af2 100644 --- a/contracts/identity-registry-contract/src/test.rs +++ b/contracts/identity-registry-contract/src/test.rs @@ -746,3 +746,44 @@ fn test_expert_pagination() { } } +#[test] +fn test_unban_expert() { + let env = Env::default(); + let contract_id = env.register(IdentityRegistryContract, ()); + let client = IdentityRegistryContractClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let expert = Address::generate(&env); + + client.init(&admin); + + env.mock_all_auths(); + let data_uri = String::from_str(&env, "ipfs://unban"); + client.add_expert(&expert, &data_uri); + + // Initial status: Verified + assert_eq!(client.get_status(&expert), ExpertStatus::Verified); + let initial_total = client.get_total_experts(); + + // Ban the expert + client.ban_expert(&expert); + assert_eq!(client.get_status(&expert), ExpertStatus::Banned); + + // Unban the expert + client.unban_expert(&expert); + assert_eq!(client.get_status(&expert), ExpertStatus::Verified); + + // Data URI should be preserved + env.as_contract(&contract_id, || { + let rec = storage::get_expert_record(&env, &expert); + assert_eq!(rec.data_uri, data_uri); + }); + + // Total experts should NOT have increased + assert_eq!(client.get_total_experts(), initial_total); + + // Test: Try to unban a non-banned expert (should fail with NotBanned) + let result = client.try_unban_expert(&expert); + assert_eq!(result, Err(Ok(RegistryError::NotBanned))); +} +