diff --git a/contracts/identity-registry-contract/src/contract.rs b/contracts/identity-registry-contract/src/contract.rs
index f19d671..e5eb67e 100644
--- a/contracts/identity-registry-contract/src/contract.rs
+++ b/contracts/identity-registry-contract/src/contract.rs
@@ -158,6 +158,43 @@ pub fn update_profile(env: &Env, expert: &Address, new_uri: String) -> Result<()
Ok(())
}
+/// Batch update expert profiles (Admin only)
+/// Allows admins to update multiple expert metadata URIs in a single transaction
+pub fn batch_update_profiles(
+ env: &Env,
+ updates: Vec<(Address, String, u32)>,
+) -> Result<(), RegistryError> {
+ // Limit batch size to prevent DoS
+ if updates.len() > 20 {
+ return Err(RegistryError::ExpertVecMax);
+ }
+
+ let admin = storage::get_admin(env).ok_or(RegistryError::NotInitialized)?;
+ admin.require_auth();
+
+ for update in updates {
+ let (expert, new_uri, status_u32) = update;
+
+ // Validate URI length
+ if new_uri.len() > 64 {
+ return Err(RegistryError::UriTooLong);
+ }
+
+ // Convert u32 to ExpertStatus
+ let status = match status_u32 {
+ 0 => ExpertStatus::Unverified,
+ 1 => ExpertStatus::Verified,
+ 2 => ExpertStatus::Banned,
+ _ => return Err(RegistryError::NotVerified), // Invalid status value
+ };
+
+ // Update the expert record
+ storage::set_expert_record(env, &expert, status, new_uri);
+ }
+
+ Ok(())
+}
+
/// Get a paginated list of experts
/// Returns a vector of expert addresses from start_index to start_index + limit
pub fn get_experts_paginated(env: &Env, start_index: u64, limit: u64) -> Vec
{
@@ -179,3 +216,4 @@ pub fn get_experts_paginated(env: &Env, start_index: u64, limit: u64) -> Vec,
+ ) -> Result<(), RegistryError> {
+ contract::batch_update_profiles(&env, updates)
+ }
+
/// Get a paginated list of experts
/// Returns a vector of expert addresses from start_index to start_index + limit
pub fn get_experts_paginated(env: Env, start_index: u64, limit: u64) -> Vec {
contract::get_experts_paginated(&env, start_index, limit)
}
+
}
diff --git a/contracts/identity-registry-contract/src/test.rs b/contracts/identity-registry-contract/src/test.rs
index 5c03da1..02c6a41 100644
--- a/contracts/identity-registry-contract/src/test.rs
+++ b/contracts/identity-registry-contract/src/test.rs
@@ -582,6 +582,128 @@ fn test_expert_directory_via_batch_add() {
assert_eq!(client.get_expert_by_index(&2u64), expert3);
}
+#[test]
+fn test_batch_update_profiles() {
+ let env = Env::default();
+ env.mock_all_auths();
+
+ let contract_id = env.register(IdentityRegistryContract, ());
+ let client = IdentityRegistryContractClient::new(&env, &contract_id);
+
+ let admin = Address::generate(&env);
+ client.init(&admin);
+
+ // Create 5 experts and verify them
+ let expert1 = Address::generate(&env);
+ let expert2 = Address::generate(&env);
+ let expert3 = Address::generate(&env);
+ let expert4 = Address::generate(&env);
+ let expert5 = Address::generate(&env);
+
+ let uri1 = String::from_str(&env, "ipfs://original1");
+ let uri2 = String::from_str(&env, "ipfs://original2");
+ let uri3 = String::from_str(&env, "ipfs://original3");
+ let uri4 = String::from_str(&env, "ipfs://original4");
+ let uri5 = String::from_str(&env, "ipfs://original5");
+
+ client.add_expert(&expert1, &uri1);
+ client.add_expert(&expert2, &uri2);
+ client.add_expert(&expert3, &uri3);
+ client.add_expert(&expert4, &uri4);
+ client.add_expert(&expert5, &uri5);
+
+ // Prepare batch updates with new URIs
+ let new_uri1 = String::from_str(&env, "ipfs://updated1");
+ let new_uri2 = String::from_str(&env, "ipfs://updated2");
+ let new_uri3 = String::from_str(&env, "ipfs://updated3");
+ let new_uri4 = String::from_str(&env, "ipfs://updated4");
+ let new_uri5 = String::from_str(&env, "ipfs://updated5");
+
+ let updates = vec![
+ &env,
+ (expert1.clone(), new_uri1.clone(), 1u32), // Verified
+ (expert2.clone(), new_uri2.clone(), 1u32), // Verified
+ (expert3.clone(), new_uri3.clone(), 1u32), // Verified
+ (expert4.clone(), new_uri4.clone(), 1u32), // Verified
+ (expert5.clone(), new_uri5.clone(), 1u32), // Verified
+ ];
+
+ // Execute batch update
+ client.batch_update_profiles(&updates);
+
+ // Verify all 5 profiles have the new URIs
+ env.as_contract(&contract_id, || {
+ let rec1 = storage::get_expert_record(&env, &expert1);
+ let rec2 = storage::get_expert_record(&env, &expert2);
+ let rec3 = storage::get_expert_record(&env, &expert3);
+ let rec4 = storage::get_expert_record(&env, &expert4);
+ let rec5 = storage::get_expert_record(&env, &expert5);
+
+ assert_eq!(rec1.data_uri, new_uri1);
+ assert_eq!(rec2.data_uri, new_uri2);
+ assert_eq!(rec3.data_uri, new_uri3);
+ assert_eq!(rec4.data_uri, new_uri4);
+ assert_eq!(rec5.data_uri, new_uri5);
+
+ // Verify all remain verified
+ assert_eq!(rec1.status, ExpertStatus::Verified);
+ assert_eq!(rec2.status, ExpertStatus::Verified);
+ assert_eq!(rec3.status, ExpertStatus::Verified);
+ assert_eq!(rec4.status, ExpertStatus::Verified);
+ assert_eq!(rec5.status, ExpertStatus::Verified);
+ });
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #7)")]
+fn test_batch_update_profiles_max_vec() {
+ let env = Env::default();
+ env.mock_all_auths();
+
+ let contract_id = env.register(IdentityRegistryContract, ());
+ let client = IdentityRegistryContractClient::new(&env, &contract_id);
+
+ let admin = Address::generate(&env);
+ client.init(&admin);
+
+ // Create updates exceeding the limit (>20)
+ let mut updates = vec![&env];
+ for _ in 0..21 {
+ let expert = Address::generate(&env);
+ let uri = String::from_str(&env, "ipfs://test");
+ updates.push_back((expert, uri, 1u32));
+ }
+
+ // This should fail with ExpertVecMax error
+ client.batch_update_profiles(&updates);
+}
+
+#[test]
+fn test_batch_update_profiles_uri_too_long() {
+ let env = Env::default();
+ env.mock_all_auths();
+
+ let contract_id = env.register(IdentityRegistryContract, ());
+ let client = IdentityRegistryContractClient::new(&env, &contract_id);
+
+ let admin = Address::generate(&env);
+ client.init(&admin);
+
+ let expert = Address::generate(&env);
+ let uri = String::from_str(&env, "ipfs://initial");
+ client.add_expert(&expert, &uri);
+
+ // Create update with URI that's too long (>64 chars)
+ let long_str = "a".repeat(65);
+ let long_uri = String::from_str(&env, long_str.as_str());
+
+ let updates = vec![&env, (expert.clone(), long_uri, 1u32)];
+
+ // This should fail with UriTooLong error
+ let result = client.try_batch_update_profiles(&updates);
+ assert_eq!(result, Err(Ok(RegistryError::UriTooLong)));
+}
+
#[test]
fn test_expert_pagination() {
let env = Env::default();
@@ -623,3 +745,4 @@ fn test_expert_pagination() {
assert_eq!(page2.get(i as u32).unwrap(), experts.get((i + 10) as u32).unwrap());
}
}
+