|
| 1 | +#![no_std] |
| 2 | + |
| 3 | +use soroban_sdk::{ |
| 4 | + contract, contractevent, contractimpl, symbol_short, Address, Bytes, BytesN, Env, |
| 5 | +}; |
| 6 | + |
| 7 | +#[contractevent] |
| 8 | +#[derive(Clone, Debug, Eq, PartialEq)] |
| 9 | +pub struct Crediss { |
| 10 | + pub credential_hash: BytesN<32>, |
| 11 | + pub issuer_did: Bytes, |
| 12 | + pub subject_did: Bytes, |
| 13 | + pub metadata_ptr: Bytes, |
| 14 | + pub expires_at: i128, |
| 15 | +} |
| 16 | + |
| 17 | +#[contractevent] |
| 18 | +#[derive(Clone, Debug, Eq, PartialEq)] |
| 19 | +pub struct Credrev { |
| 20 | + pub credential_hash: BytesN<32>, |
| 21 | + pub issuer_did: Bytes, |
| 22 | + pub subject_did: Bytes, |
| 23 | +} |
| 24 | +#[contract] |
| 25 | +pub struct CredentialRegistryContract; |
| 26 | + |
| 27 | +#[derive(Clone)] |
| 28 | +pub enum CredentialStatus { |
| 29 | + Active, |
| 30 | + Revoked, |
| 31 | + Expired, |
| 32 | +} |
| 33 | + |
| 34 | +#[contractimpl] |
| 35 | +impl CredentialRegistryContract { |
| 36 | + // Issue a credential by storing its hash and metadata pointer. |
| 37 | + // `credential_hash` should be a deterministic hash (e.g., SHA-256) of the full VC JSON. |
| 38 | + pub fn issue_credential( |
| 39 | + env: &Env, |
| 40 | + credential_hash: BytesN<32>, |
| 41 | + issuer: Address, |
| 42 | + issuer_did: Bytes, |
| 43 | + subject_did: Bytes, |
| 44 | + metadata_ptr: Bytes, |
| 45 | + expires_at: i128, |
| 46 | + ) { |
| 47 | + issuer.require_auth(); |
| 48 | + let key = (symbol_short!("cred"), credential_hash.clone()); |
| 49 | + assert!( |
| 50 | + !env.storage().persistent().has(&key), |
| 51 | + "credential already exists" |
| 52 | + ); |
| 53 | + let record: (Bytes, Bytes, Bytes, i128, i32) = ( |
| 54 | + issuer_did.clone(), |
| 55 | + subject_did.clone(), |
| 56 | + metadata_ptr.clone(), |
| 57 | + expires_at, |
| 58 | + 0i32, |
| 59 | + ); |
| 60 | + env.storage().persistent().set(&key, &record); |
| 61 | + Crediss { |
| 62 | + credential_hash, |
| 63 | + issuer_did, |
| 64 | + subject_did, |
| 65 | + metadata_ptr, |
| 66 | + expires_at, |
| 67 | + } |
| 68 | + .publish(env); |
| 69 | + } |
| 70 | + |
| 71 | + // Revoke a credential. Caller must be issuer (signed address) |
| 72 | + pub fn revoke_credential(env: &Env, credential_hash: BytesN<32>, issuer: Address) { |
| 73 | + issuer.require_auth(); |
| 74 | + let key = (symbol_short!("cred"), credential_hash.clone()); |
| 75 | + let opt: Option<(Bytes, Bytes, Bytes, i128, i32)> = env.storage().persistent().get(&key); |
| 76 | + match opt { |
| 77 | + Some((issuer_did, subject_did, metadata_ptr, expires_at, _status)) => { |
| 78 | + let record: (Bytes, Bytes, Bytes, i128, i32) = ( |
| 79 | + issuer_did.clone(), |
| 80 | + subject_did.clone(), |
| 81 | + metadata_ptr.clone(), |
| 82 | + expires_at, |
| 83 | + 1i32, |
| 84 | + ); |
| 85 | + env.storage().persistent().set(&key, &record); |
| 86 | + Credrev { |
| 87 | + credential_hash, |
| 88 | + issuer_did, |
| 89 | + subject_did, |
| 90 | + } |
| 91 | + .publish(env); |
| 92 | + } |
| 93 | + None => panic!("credential not found"), |
| 94 | + } |
| 95 | + } |
| 96 | + |
| 97 | + // Get credential record: returns (issuer_did, subject_did, metadata_ptr, expires_at, status) |
| 98 | + pub fn get_credential( |
| 99 | + env: &Env, |
| 100 | + credential_hash: BytesN<32>, |
| 101 | + ) -> Option<(Bytes, Bytes, Bytes, i128, i32)> { |
| 102 | + let key = (symbol_short!("cred"), credential_hash.clone()); |
| 103 | + env.storage().persistent().get(&key) |
| 104 | + } |
| 105 | + |
| 106 | + // Check if credential is active (not revoked and not expired) |
| 107 | + pub fn is_active(env: &Env, credential_hash: BytesN<32>, now_ts: i128) -> bool { |
| 108 | + match Self::get_credential(env, credential_hash.clone()) { |
| 109 | + Some((_issuer, _subject, _meta, expires_at, status)) => { |
| 110 | + if status == 1 { |
| 111 | + return false; |
| 112 | + } |
| 113 | + if expires_at > 0 && now_ts > expires_at { |
| 114 | + return false; |
| 115 | + } |
| 116 | + true |
| 117 | + } |
| 118 | + None => false, |
| 119 | + } |
| 120 | + } |
| 121 | +} |
| 122 | + |
| 123 | +fn main() {} |
0 commit comments