Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 6 additions & 18 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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<String, Error> {
request::request_attestation(&env, subject, issuer, claim_type)
}
Expand Down
14 changes: 14 additions & 0 deletions src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -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<crate::types::ClaimTypeConstraints> {
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);
Expand Down Expand Up @@ -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);
Expand Down
74 changes: 74 additions & 0 deletions src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
43 changes: 0 additions & 43 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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<Address>,
pub threshold: u32,
pub signers: Vec<Address>,
pub created_at: u64,
pub expires_at: u64,
pub finalized: bool,
}

/// Configurable storage limits to prevent exhaustion attacks.
#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
Expand All @@ -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)]
Expand Down