diff --git a/src/lib.rs b/src/lib.rs index 70ecac2..015237b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,10 +34,12 @@ use soroban_sdk::{contract, contractimpl, Address, Env, String, Vec}; use crate::events::Events; use crate::storage::Storage; use crate::types::{ - Attestation, AttestationRequest, AttestationStatus, AuditAction, AuditEntry, ClaimTypeInfo, - ContractConfig, ContractMetadata, Endorsement, Error, FeeConfig, GlobalStats, HealthStatus, - IssuerMetadata, IssuerStats, IssuerTier, MultiSigProposal, RateLimitConfig, RequestStatus, - StorageLimits, TtlConfig, ATTESTATION_REQUEST_TTL_SECS, MULTISIG_PROPOSAL_TTL_SECS, + AdminCouncil, Attestation, AttestationRequest, AttestationStatus, AttestationTemplate, + AttestationVersionSnapshot, AuditAction, AuditEntry, ClaimTypeInfo, CouncilProposal, + ContractConfig, ContractMetadata, DecayConfig, Delegation, DisputeRecord, Endorsement, Error, + ExpirationHook, FeeConfig, GlobalStats, HealthStatus, IssuerMetadata, IssuerStats, IssuerTier, + MultiSigProposal, PendingAdminTransfer, RateLimitConfig, RequestStatus, StorageLimits, + TtlConfig, ATTESTATION_REQUEST_TTL_SECS, MULTISIG_PROPOSAL_TTL_SECS, SECS_PER_DAY, }; #[contract] @@ -711,20 +713,6 @@ impl TrustLinkContract { multisig::get_multisig_proposal(&env, proposal_id) } - #[must_use] - pub fn get_multisig_ttl(env: Env) -> u32 { - multisig::get_multisig_ttl(&env) - } - - if proposal.cancelled { - return Err(Error::ProposalExpired); - } - - let current_time = env.ledger().timestamp(); - if current_time >= proposal.expires_at { - return Err(Error::ProposalExpired); - } - pub fn request_attestation(env: Env, subject: Address, issuer: Address, claim_type: String) -> Result { request::request_attestation(&env, subject, issuer, claim_type) } diff --git a/src/storage.rs b/src/storage.rs index 256036c..aff6927 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -344,6 +344,10 @@ impl Storage { env.storage().persistent().extend_ttl(&key, ttl, ttl); } + pub fn remove_issuer_attestation(env: &Env, issuer: &Address, attestation_id: &String) { + ChunkedIndex::remove_issuer(env, issuer, attestation_id); + } + /// Persist `metadata` for `issuer` and refresh its TTL. pub fn set_issuer_metadata(env: &Env, issuer: &Address, metadata: &IssuerMetadata) { let key = StorageKey::IssuerMetadata(issuer.clone()); @@ -393,6 +397,10 @@ impl Storage { env.storage().persistent().extend_ttl(&key, ttl, ttl); } + pub fn get_claim_type_constraints(env: &Env, claim_type: &String) -> Option { + env.storage().persistent().get(&StorageKey::ClaimTypeConstraints(claim_type.clone())) + } + pub fn set_whitelist_mode(env: &Env, issuer: &Address, enabled: bool) { let key = StorageKey::IssuerWhitelistMode(issuer.clone()); let ttl = get_ttl_lifetime(env); @@ -518,6 +526,12 @@ impl Storage { env.storage().persistent().extend_ttl(&key, ttl, ttl); } + pub fn increment_issuer_stats(env: &Env, issuer: &Address, count: u32) { + let mut stats = Self::get_issuer_stats(env, issuer); + stats.total_issued = stats.total_issued.saturating_add(count as u64); + Self::set_issuer_stats(env, issuer, &stats); + } + pub fn set_issuer_tier(env: &Env, issuer: &Address, tier: &IssuerTier) { let key = StorageKey::IssuerTier(issuer.clone()); let ttl = get_ttl_lifetime(env); diff --git a/src/test.rs b/src/test.rs index 4df7e25..df756ac 100644 --- a/src/test.rs +++ b/src/test.rs @@ -8779,3 +8779,77 @@ fn test_get_issuer_expiring_attestations_sorted_by_expiration() { assert_eq!(result.get(1).unwrap().expiration, Some(1000 + 10 * 86_400)); assert_eq!(result.get(2).unwrap().expiration, Some(1000 + 20 * 86_400)); } + +#[test] +fn test_issue_922_get_claim_type_constraints() { + let env = Env::default(); + env.mock_all_auths(); + + let claim_type = String::from_str(&env, "email"); + let constraints = crate::types::ClaimTypeConstraints { + min_metadata_len: Some(5), + max_metadata_len: Some(255), + require_metadata: true, + }; + + Storage::set_claim_type_constraints(&env, &claim_type, &constraints); + + let retrieved = Storage::get_claim_type_constraints(&env, &claim_type); + assert!(retrieved.is_some()); + + let retrieved_constraints = retrieved.unwrap(); + assert_eq!(retrieved_constraints.min_metadata_len, Some(5)); + assert_eq!(retrieved_constraints.max_metadata_len, Some(255)); + assert_eq!(retrieved_constraints.require_metadata, true); + + let nonexistent = Storage::get_claim_type_constraints(&env, &String::from_str(&env, "nonexistent")); + assert!(nonexistent.is_none()); +} + +#[test] +fn test_issue_924_increment_issuer_stats() { + let env = Env::default(); + env.mock_all_auths(); + + let issuer = Address::generate(&env); + + let initial_stats = Storage::get_issuer_stats(&env, &issuer); + assert_eq!(initial_stats.total_issued, 0); + + Storage::increment_issuer_stats(&env, &issuer, 5); + + let after_first = Storage::get_issuer_stats(&env, &issuer); + assert_eq!(after_first.total_issued, 5); + + Storage::increment_issuer_stats(&env, &issuer, 3); + + let after_second = Storage::get_issuer_stats(&env, &issuer); + assert_eq!(after_second.total_issued, 8); + + Storage::increment_issuer_stats(&env, &issuer, 0); + let after_zero = Storage::get_issuer_stats(&env, &issuer); + assert_eq!(after_zero.total_issued, 8); +} + +#[test] +fn test_issue_923_remove_issuer_attestation() { + let env = Env::default(); + env.mock_all_auths(); + + let (_, issuer, client) = setup(&env); + let subject = Address::generate(&env); + let claim_type = String::from_str(&env, "KYC_PASSED"); + + client.create_attestation(&issuer, &subject, &claim_type, &None, &None, &None); + let attestations_before = client.get_issuer_attestations(&issuer, &0, &100); + assert!(attestations_before.len() > 0); + + let first_attestation_id = attestations_before.get(0).unwrap().id; + + Storage::remove_issuer_attestation(&env, &issuer, &first_attestation_id); + + let attestations_after = client.get_issuer_attestations(&issuer, &0, &100); + for att in attestations_after.iter() { + assert_ne!(att.id, first_attestation_id); + } +} diff --git a/src/types.rs b/src/types.rs index 548007b..c00d11e 100644 --- a/src/types.rs +++ b/src/types.rs @@ -94,19 +94,6 @@ pub struct MultiSigProposal { pub cancelled: bool, } -/// Full contract configuration snapshot returned by `get_config`. -#[contracttype] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ContractConfig { - pub ttl_config: TtlConfig, - pub fee_config: FeeConfig, - pub contract_name: String, - pub contract_version: String, - pub contract_description: String, - /// Configurable TTL for multisig proposals in days (default: 7). - pub multisig_ttl_days: u32, -} - #[contracttype] #[derive(Clone, Debug, Eq, PartialEq)] pub struct ContractMetadata { @@ -152,13 +139,6 @@ pub struct HealthStatus { pub total_attestations: u64, } -/// Issuer statistics. -#[contracttype] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct IssuerStats { - pub total_issued: u64, -} - /// TTL configuration. #[contracttype] #[derive(Clone, Debug, Eq, PartialEq)] @@ -300,21 +280,6 @@ pub struct Endorsement { } /// A multi-signature attestation proposal requiring threshold signatures. -#[contracttype] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct MultiSigProposal { - pub id: String, - pub proposer: Address, - pub subject: Address, - pub claim_type: String, - pub required_signers: Vec
, - pub threshold: u32, - pub signers: Vec
, - pub created_at: u64, - pub expires_at: u64, - pub finalized: bool, -} - /// Configurable storage limits to prevent exhaustion attacks. #[contracttype] #[derive(Clone, Debug, Eq, PartialEq)] @@ -332,14 +297,6 @@ impl Default for StorageLimits { } } -/// Expiration notification hook configuration. -#[contracttype] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ExpirationHook { - pub callback_contract: Address, - pub notify_days_before: u32, -} - /// Delegation from an issuer to a sub-issuer for specific claim types. #[contracttype] #[derive(Clone, Debug, Eq, PartialEq)]