From 28468c0819d2820e7a9c890e084653b1ce3a2713 Mon Sep 17 00:00:00 2001 From: Geff115 Date: Tue, 28 Apr 2026 05:27:03 +0100 Subject: [PATCH 1/2] feat(kyc): add revoke_kyc to clean up persistent storage on identity revocationCloses #251 --- src/amm/{ => src}/lib.rs | 0 src/batch/{ => src}/lib.rs | 0 src/bridge/{ => src}/lib.rs | 0 src/circuit_breaker/{ => src}/lib.rs | 0 src/escrow_multisig/{ => src}/lib.rs | 0 src/escrow_timelock/{ => src}/lib.rs | 0 src/flash_loan/{ => src}/lib.rs | 0 src/governance/{ => src}/lib.rs | 0 src/indexing/{ => src}/lib.rs | 0 src/kyc/Cargo.toml | 2 +- src/kyc/{ => src}/lib.rs | 19 +++++ src/kyc/src/tests.rs | 107 +++++++++++++++++++++++++ src/liquidation/{ => src}/lib.rs | 0 src/oracle_consumer/{ => src}/lib.rs | 0 src/oracle_medianizer/{ => src}/lib.rs | 0 src/random/{ => src}/lib.rs | 0 src/staking/{ => src}/lib.rs | 0 src/token/{ => src}/lib.rs | 0 src/upgradeable/{ => src}/lib.rs | 0 src/utils/{ => src}/lib.rs | 0 src/vesting/{ => src}/lib.rs | 0 21 files changed, 127 insertions(+), 1 deletion(-) rename src/amm/{ => src}/lib.rs (100%) rename src/batch/{ => src}/lib.rs (100%) rename src/bridge/{ => src}/lib.rs (100%) rename src/circuit_breaker/{ => src}/lib.rs (100%) rename src/escrow_multisig/{ => src}/lib.rs (100%) rename src/escrow_timelock/{ => src}/lib.rs (100%) rename src/flash_loan/{ => src}/lib.rs (100%) rename src/governance/{ => src}/lib.rs (100%) rename src/indexing/{ => src}/lib.rs (100%) rename src/kyc/{ => src}/lib.rs (82%) create mode 100644 src/kyc/src/tests.rs rename src/liquidation/{ => src}/lib.rs (100%) rename src/oracle_consumer/{ => src}/lib.rs (100%) rename src/oracle_medianizer/{ => src}/lib.rs (100%) rename src/random/{ => src}/lib.rs (100%) rename src/staking/{ => src}/lib.rs (100%) rename src/token/{ => src}/lib.rs (100%) rename src/upgradeable/{ => src}/lib.rs (100%) rename src/utils/{ => src}/lib.rs (100%) rename src/vesting/{ => src}/lib.rs (100%) diff --git a/src/amm/lib.rs b/src/amm/src/lib.rs similarity index 100% rename from src/amm/lib.rs rename to src/amm/src/lib.rs diff --git a/src/batch/lib.rs b/src/batch/src/lib.rs similarity index 100% rename from src/batch/lib.rs rename to src/batch/src/lib.rs diff --git a/src/bridge/lib.rs b/src/bridge/src/lib.rs similarity index 100% rename from src/bridge/lib.rs rename to src/bridge/src/lib.rs diff --git a/src/circuit_breaker/lib.rs b/src/circuit_breaker/src/lib.rs similarity index 100% rename from src/circuit_breaker/lib.rs rename to src/circuit_breaker/src/lib.rs diff --git a/src/escrow_multisig/lib.rs b/src/escrow_multisig/src/lib.rs similarity index 100% rename from src/escrow_multisig/lib.rs rename to src/escrow_multisig/src/lib.rs diff --git a/src/escrow_timelock/lib.rs b/src/escrow_timelock/src/lib.rs similarity index 100% rename from src/escrow_timelock/lib.rs rename to src/escrow_timelock/src/lib.rs diff --git a/src/flash_loan/lib.rs b/src/flash_loan/src/lib.rs similarity index 100% rename from src/flash_loan/lib.rs rename to src/flash_loan/src/lib.rs diff --git a/src/governance/lib.rs b/src/governance/src/lib.rs similarity index 100% rename from src/governance/lib.rs rename to src/governance/src/lib.rs diff --git a/src/indexing/lib.rs b/src/indexing/src/lib.rs similarity index 100% rename from src/indexing/lib.rs rename to src/indexing/src/lib.rs diff --git a/src/kyc/Cargo.toml b/src/kyc/Cargo.toml index c1709b7..42e799d 100644 --- a/src/kyc/Cargo.toml +++ b/src/kyc/Cargo.toml @@ -6,7 +6,7 @@ publish = false [lib] crate-type = ["cdylib"] -path = "lib.rs" +path = "src/lib.rs" doctest = false [dependencies] diff --git a/src/kyc/lib.rs b/src/kyc/src/lib.rs similarity index 82% rename from src/kyc/lib.rs rename to src/kyc/src/lib.rs index e92ad2a..a537637 100644 --- a/src/kyc/lib.rs +++ b/src/kyc/src/lib.rs @@ -74,4 +74,23 @@ impl KycVerifier { .instance() .set(&DataKey::VerifierPubKey, &new_pubkey); } + + pub fn revoke_kyc(env: Env, user: Address) { + let admin: Address = env.storage().instance().get(&DataKey::Admin).unwrap(); + admin.require_auth(); + + let key = DataKey::UserKyc(user.clone()); + + if !env.storage().persistent().has(&key) { + panic!("no KYC record found for user"); + } + + env.storage().persistent().remove(&key); + + env.events() + .publish((symbol_short!("kyc_rev"), user), ()); + } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/src/kyc/src/tests.rs b/src/kyc/src/tests.rs new file mode 100644 index 0000000..d81e49a --- /dev/null +++ b/src/kyc/src/tests.rs @@ -0,0 +1,107 @@ +use super::*; +use soroban_sdk::{ + testutils::{Address as _, Ledger}, + Address, BytesN, Env, +}; + +// ─── Helpers ──────────────────────────────────────────────────────────────── + +/// Registers the contract and returns (env, contract_id, admin_address). +/// Uses mock_all_auths so initialize() doesn't need a real signed tx. +fn setup(mock_auths: bool) -> (Env, Address, Address) { + let env = Env::default(); + if mock_auths { + env.mock_all_auths(); + } + + let contract_id = env.register_contract(None, KycVerifier); + let admin = Address::generate(&env); + + if mock_auths { + let client = KycVerifierClient::new(&env, &contract_id); + client.initialize(&admin, &BytesN::from_array(&env, &[1u8; 32])); + } + + (env, contract_id, admin) +} + +/// Injects a KYC record directly into persistent storage, +/// bypassing signature verification. Safe for test setup only. +fn inject_kyc(env: &Env, contract_id: &Address, user: &Address, expires_at: u64) { + env.as_contract(contract_id, || { + env.storage() + .persistent() + .set(&DataKey::UserKyc(user.clone()), &expires_at); + }); +} + +// ─── Tests ────────────────────────────────────────────────────────────────── + +/// Happy path: after revocation, is_kyc_valid returns false +/// and the storage entry is fully removed. +#[test] +fn test_revoke_kyc_removes_record() { + let (env, contract_id, admin) = setup(true); + let client = KycVerifierClient::new(&env, &contract_id); + + env.ledger().with_mut(|li| li.timestamp = 1_000); + + let user = Address::generate(&env); + inject_kyc(&env, &contract_id, &user, 999_999); + + assert!(client.is_kyc_valid(&user), "KYC should be valid before revocation"); + + client.revoke_kyc(&user); + + assert!(!client.is_kyc_valid(&user), "KYC should be invalid after revocation"); + + // Confirm the key is truly gone from storage, not just expired + let still_exists = env.as_contract(&contract_id, || { + env.storage() + .persistent() + .has(&DataKey::UserKyc(user.clone())) + }); + assert!(!still_exists, "Storage entry should be fully removed"); +} + +/// Revoking a user who never had a KYC record should panic. +#[test] +#[should_panic(expected = "no KYC record found for user")] +fn test_revoke_kyc_panics_on_missing_record() { + let (env, contract_id, _admin) = setup(true); + let client = KycVerifierClient::new(&env, &contract_id); + + let user = Address::generate(&env); + client.revoke_kyc(&user); // No record was ever set +} + +/// A non-admin caller must not be able to revoke KYC. +/// We set up state directly (no mock_all_auths) so require_auth() +/// runs without any authorization being present — it should panic. +#[test] +#[should_panic] +fn test_revoke_kyc_rejects_non_admin() { + let env = Env::default(); + // Intentionally NOT calling env.mock_all_auths() + + let contract_id = env.register_contract(None, KycVerifier); + let admin = Address::generate(&env); + let user = Address::generate(&env); + + // Bootstrap state directly, bypassing auth entirely + env.as_contract(&contract_id, || { + env.storage().instance().set(&DataKey::Admin, &admin); + env.storage() + .instance() + .set(&DataKey::VerifierPubKey, &BytesN::from_array(&env, &[1u8; 32])); + env.storage() + .persistent() + .set(&DataKey::UserKyc(user.clone()), &999_999_u64); + }); + + let client = KycVerifierClient::new(&env, &contract_id); + + // admin.require_auth() inside revoke_kyc will panic — + // no auth has been provided for this invocation + client.revoke_kyc(&user); +} \ No newline at end of file diff --git a/src/liquidation/lib.rs b/src/liquidation/src/lib.rs similarity index 100% rename from src/liquidation/lib.rs rename to src/liquidation/src/lib.rs diff --git a/src/oracle_consumer/lib.rs b/src/oracle_consumer/src/lib.rs similarity index 100% rename from src/oracle_consumer/lib.rs rename to src/oracle_consumer/src/lib.rs diff --git a/src/oracle_medianizer/lib.rs b/src/oracle_medianizer/src/lib.rs similarity index 100% rename from src/oracle_medianizer/lib.rs rename to src/oracle_medianizer/src/lib.rs diff --git a/src/random/lib.rs b/src/random/src/lib.rs similarity index 100% rename from src/random/lib.rs rename to src/random/src/lib.rs diff --git a/src/staking/lib.rs b/src/staking/src/lib.rs similarity index 100% rename from src/staking/lib.rs rename to src/staking/src/lib.rs diff --git a/src/token/lib.rs b/src/token/src/lib.rs similarity index 100% rename from src/token/lib.rs rename to src/token/src/lib.rs diff --git a/src/upgradeable/lib.rs b/src/upgradeable/src/lib.rs similarity index 100% rename from src/upgradeable/lib.rs rename to src/upgradeable/src/lib.rs diff --git a/src/utils/lib.rs b/src/utils/src/lib.rs similarity index 100% rename from src/utils/lib.rs rename to src/utils/src/lib.rs diff --git a/src/vesting/lib.rs b/src/vesting/src/lib.rs similarity index 100% rename from src/vesting/lib.rs rename to src/vesting/src/lib.rs From 2f14bdb19b36b255a40986f22395d0af5dd4121a Mon Sep 17 00:00:00 2001 From: Geff115 Date: Tue, 28 Apr 2026 06:05:09 +0100 Subject: [PATCH 2/2] feat(kyc): add revoke_kyc to clean up persistent storage on identity revocation, fixed merging conflicts. Closes #251 --- src/amm/{src => }/lib.rs | 0 src/batch/{src => }/lib.rs | 0 src/bridge/{src => }/lib.rs | 0 src/circuit_breaker/{src => }/lib.rs | 0 src/escrow_multisig/{src => }/lib.rs | 0 src/escrow_timelock/{src => }/lib.rs | 0 src/flash_loan/{src => }/lib.rs | 0 src/governance/{src => }/lib.rs | 0 src/indexing/{src => }/lib.rs | 0 src/kyc/Cargo.toml | 2 +- src/kyc/{src => }/lib.rs | 0 src/liquidation/{src => }/lib.rs | 0 src/oracle_consumer/{src => }/lib.rs | 0 src/oracle_medianizer/{src => }/lib.rs | 0 src/random/{src => }/lib.rs | 0 src/security_registry/{src => }/lib.rs | 0 src/staking/{src => }/lib.rs | 0 src/token/{src => }/lib.rs | 0 src/upgradeable/{src => }/lib.rs | 0 src/utils/{src => }/lib.rs | 0 src/vesting/{src => }/lib.rs | 0 21 files changed, 1 insertion(+), 1 deletion(-) rename src/amm/{src => }/lib.rs (100%) rename src/batch/{src => }/lib.rs (100%) rename src/bridge/{src => }/lib.rs (100%) rename src/circuit_breaker/{src => }/lib.rs (100%) rename src/escrow_multisig/{src => }/lib.rs (100%) rename src/escrow_timelock/{src => }/lib.rs (100%) rename src/flash_loan/{src => }/lib.rs (100%) rename src/governance/{src => }/lib.rs (100%) rename src/indexing/{src => }/lib.rs (100%) rename src/kyc/{src => }/lib.rs (100%) rename src/liquidation/{src => }/lib.rs (100%) rename src/oracle_consumer/{src => }/lib.rs (100%) rename src/oracle_medianizer/{src => }/lib.rs (100%) rename src/random/{src => }/lib.rs (100%) rename src/security_registry/{src => }/lib.rs (100%) rename src/staking/{src => }/lib.rs (100%) rename src/token/{src => }/lib.rs (100%) rename src/upgradeable/{src => }/lib.rs (100%) rename src/utils/{src => }/lib.rs (100%) rename src/vesting/{src => }/lib.rs (100%) diff --git a/src/amm/src/lib.rs b/src/amm/lib.rs similarity index 100% rename from src/amm/src/lib.rs rename to src/amm/lib.rs diff --git a/src/batch/src/lib.rs b/src/batch/lib.rs similarity index 100% rename from src/batch/src/lib.rs rename to src/batch/lib.rs diff --git a/src/bridge/src/lib.rs b/src/bridge/lib.rs similarity index 100% rename from src/bridge/src/lib.rs rename to src/bridge/lib.rs diff --git a/src/circuit_breaker/src/lib.rs b/src/circuit_breaker/lib.rs similarity index 100% rename from src/circuit_breaker/src/lib.rs rename to src/circuit_breaker/lib.rs diff --git a/src/escrow_multisig/src/lib.rs b/src/escrow_multisig/lib.rs similarity index 100% rename from src/escrow_multisig/src/lib.rs rename to src/escrow_multisig/lib.rs diff --git a/src/escrow_timelock/src/lib.rs b/src/escrow_timelock/lib.rs similarity index 100% rename from src/escrow_timelock/src/lib.rs rename to src/escrow_timelock/lib.rs diff --git a/src/flash_loan/src/lib.rs b/src/flash_loan/lib.rs similarity index 100% rename from src/flash_loan/src/lib.rs rename to src/flash_loan/lib.rs diff --git a/src/governance/src/lib.rs b/src/governance/lib.rs similarity index 100% rename from src/governance/src/lib.rs rename to src/governance/lib.rs diff --git a/src/indexing/src/lib.rs b/src/indexing/lib.rs similarity index 100% rename from src/indexing/src/lib.rs rename to src/indexing/lib.rs diff --git a/src/kyc/Cargo.toml b/src/kyc/Cargo.toml index 42e799d..c1709b7 100644 --- a/src/kyc/Cargo.toml +++ b/src/kyc/Cargo.toml @@ -6,7 +6,7 @@ publish = false [lib] crate-type = ["cdylib"] -path = "src/lib.rs" +path = "lib.rs" doctest = false [dependencies] diff --git a/src/kyc/src/lib.rs b/src/kyc/lib.rs similarity index 100% rename from src/kyc/src/lib.rs rename to src/kyc/lib.rs diff --git a/src/liquidation/src/lib.rs b/src/liquidation/lib.rs similarity index 100% rename from src/liquidation/src/lib.rs rename to src/liquidation/lib.rs diff --git a/src/oracle_consumer/src/lib.rs b/src/oracle_consumer/lib.rs similarity index 100% rename from src/oracle_consumer/src/lib.rs rename to src/oracle_consumer/lib.rs diff --git a/src/oracle_medianizer/src/lib.rs b/src/oracle_medianizer/lib.rs similarity index 100% rename from src/oracle_medianizer/src/lib.rs rename to src/oracle_medianizer/lib.rs diff --git a/src/random/src/lib.rs b/src/random/lib.rs similarity index 100% rename from src/random/src/lib.rs rename to src/random/lib.rs diff --git a/src/security_registry/src/lib.rs b/src/security_registry/lib.rs similarity index 100% rename from src/security_registry/src/lib.rs rename to src/security_registry/lib.rs diff --git a/src/staking/src/lib.rs b/src/staking/lib.rs similarity index 100% rename from src/staking/src/lib.rs rename to src/staking/lib.rs diff --git a/src/token/src/lib.rs b/src/token/lib.rs similarity index 100% rename from src/token/src/lib.rs rename to src/token/lib.rs diff --git a/src/upgradeable/src/lib.rs b/src/upgradeable/lib.rs similarity index 100% rename from src/upgradeable/src/lib.rs rename to src/upgradeable/lib.rs diff --git a/src/utils/src/lib.rs b/src/utils/lib.rs similarity index 100% rename from src/utils/src/lib.rs rename to src/utils/lib.rs diff --git a/src/vesting/src/lib.rs b/src/vesting/lib.rs similarity index 100% rename from src/vesting/src/lib.rs rename to src/vesting/lib.rs