diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 506317b4..ce7c31fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,28 @@ concurrency: cancel-in-progress: true jobs: + changes: + runs-on: ubuntu-latest + outputs: + frontend: ${{ steps.filter.outputs.frontend }} + backend: ${{ steps.filter.outputs.backend }} + contracts: ${{ steps.filter.outputs.contracts }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + frontend: + - 'frontend/**' + backend: + - 'backend/**' + contracts: + - 'stellar-contracts/**' + frontend: + needs: changes + if: needs.changes.outputs.frontend == 'true' runs-on: ubuntu-latest defaults: run: @@ -33,6 +54,8 @@ jobs: run: npm run build backend: + needs: changes + if: needs.changes.outputs.backend == 'true' runs-on: ubuntu-latest defaults: run: @@ -54,6 +77,8 @@ jobs: run: npm run build contracts: + needs: changes + if: needs.changes.outputs.contracts == 'true' runs-on: ubuntu-latest defaults: run: diff --git a/stellar-contracts/src/crl.rs b/stellar-contracts/src/crl.rs index 37b466d7..2075f615 100644 --- a/stellar-contracts/src/crl.rs +++ b/stellar-contracts/src/crl.rs @@ -1,4 +1,4 @@ -use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, String, Vec}; +use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, IntoVal, String, Vec}; const DEFAULT_UPDATE_WINDOW_SECONDS: u64 = 7 * 24 * 60 * 60; @@ -43,6 +43,7 @@ enum DataKey { Info, Revocation(String), RevokedCertificates, + CertContract, } #[contract] @@ -50,7 +51,7 @@ pub struct CRLContract; #[contractimpl] impl CRLContract { - pub fn initialize(env: Env, issuer: Address) { + pub fn initialize(env: Env, issuer: Address, certificate_contract: Address) { if env.storage().instance().has(&DataKey::Issuer) { panic!("CRL already initialized"); } @@ -68,6 +69,9 @@ impl CRLContract { }; env.storage().instance().set(&DataKey::Issuer, &issuer); + env.storage() + .instance() + .set(&DataKey::CertContract, &certificate_contract); env.storage() .instance() .set(&DataKey::RevokedCertificates, &Vec::::new(&env)); @@ -83,6 +87,21 @@ impl CRLContract { let issuer = Self::get_issuer(&env); issuer.require_auth(); + // Verify the certificate exists in the CertificateContract (#414) + let cert_contract: Address = env + .storage() + .instance() + .get(&DataKey::CertContract) + .expect("CRL not initialized"); + let cert_exists: bool = env.invoke_contract( + &cert_contract, + &soroban_sdk::Symbol::new(&env, "certificate_exists"), + soroban_sdk::vec![&env, certificate_id.clone().into_val(&env)], + ); + if !cert_exists { + panic!("Certificate does not exist"); + } + let revocation_key = DataKey::Revocation(certificate_id.clone()); if env.storage().instance().has(&revocation_key) { panic!("Certificate already revoked"); @@ -112,37 +131,6 @@ impl CRLContract { env.storage().instance().set(&DataKey::Info, &crl_info); } - pub fn unrevoke_certificate(env: Env, certificate_id: String) { - let issuer = Self::get_issuer(&env); - issuer.require_auth(); - - let revocation_key = DataKey::Revocation(certificate_id.clone()); - let revocation_info: RevocationInfo = env - .storage() - .instance() - .get(&revocation_key) - .expect("Certificate not found in revocation list"); - - if revocation_info.issuer != issuer { - panic!("Only the original issuer can unrevoke"); - } - - env.storage().instance().remove(&revocation_key); - - let mut revoked_certificates = Self::get_revoked_certificate_ids(&env); - Self::remove_certificate_id(&mut revoked_certificates, &certificate_id); - env.storage() - .instance() - .set(&DataKey::RevokedCertificates, &revoked_certificates); - - let mut crl_info = Self::get_crl_info_internal(&env); - if crl_info.revoked_count > 0 { - crl_info.revoked_count -= 1; - } - Self::refresh_crl_info(&env, &mut crl_info); - env.storage().instance().set(&DataKey::Info, &crl_info); - } - pub fn is_revoked(env: Env, certificate_id: String) -> bool { env.storage() .instance() @@ -247,19 +235,6 @@ impl CRLContract { } } - fn remove_certificate_id(revoked_certificates: &mut Vec, certificate_id: &String) { - let mut index = 0; - while index < revoked_certificates.len() { - if let Some(existing_id) = revoked_certificates.get(index) { - if existing_id == certificate_id.clone() { - revoked_certificates.remove(index); - break; - } - } - index += 1; - } - } - fn refresh_crl_info(env: &Env, crl_info: &mut CRLInfo) { crl_info.crl_number += 1; crl_info.this_update = env.ledger().timestamp(); diff --git a/stellar-contracts/src/lib.rs b/stellar-contracts/src/lib.rs index f9f19ed0..81ce0dde 100644 --- a/stellar-contracts/src/lib.rs +++ b/stellar-contracts/src/lib.rs @@ -124,6 +124,13 @@ impl CertificateContract { ); } + /// Check if a certificate exists + pub fn certificate_exists(env: Env, id: String) -> bool { + env.storage() + .instance() + .has(&DataKey::Certificate(id)) + } + /// Get certificate details pub fn get_certificate(env: Env, id: String) -> Option { env.storage().instance().get(&DataKey::Certificate(id)) @@ -580,9 +587,14 @@ impl CertificateContract { .instance() .get::<_, Certificate>(&DataKey::Certificate(id.clone())) { + let is_expired_by_time = cert + .expires_at + .map_or(false, |exp| env.ledger().timestamp() >= exp); + let is_revoked = cert.status == CertificateStatus::Revoked || cert.status == CertificateStatus::Suspended - || cert.status == CertificateStatus::Expired; + || cert.status == CertificateStatus::Expired + || is_expired_by_time; if !is_revoked { successful += 1; diff --git a/stellar-contracts/src/storage.rs b/stellar-contracts/src/storage.rs index 8b6a2ce2..81f314bb 100644 --- a/stellar-contracts/src/storage.rs +++ b/stellar-contracts/src/storage.rs @@ -1,8 +1,10 @@ use soroban_sdk::{contracttype, Address}; +/// Storage keys for the admin multisig contract. +/// Named `StorageKey` to avoid conflict with the `DataKey` enum in `types.rs`. #[contracttype] #[derive(Clone)] -pub enum DataKey { +pub enum StorageKey { Core(CoreDataKey), Admin(AdminDataKey), } @@ -19,4 +21,4 @@ pub enum CoreDataKey { pub enum AdminDataKey { Owners, Threshold, -} \ No newline at end of file +} diff --git a/stellar-contracts/src/storage_helpers.rs b/stellar-contracts/src/storage_helpers.rs index 081f1ce5..8abb8847 100644 --- a/stellar-contracts/src/storage_helpers.rs +++ b/stellar-contracts/src/storage_helpers.rs @@ -1,22 +1,22 @@ use soroban_sdk::{Env, Address}; -use crate::storage::{DataKey, CoreDataKey, AdminDataKey}; +use crate::storage::{StorageKey, CoreDataKey, AdminDataKey}; pub fn set_admin(env: &Env, admin: &Address) { env.storage().set( - &DataKey::Core(CoreDataKey::Admin), + &StorageKey::Core(CoreDataKey::Admin), admin, ); } pub fn get_admin(env: &Env) -> Address { env.storage() - .get(&DataKey::Core(CoreDataKey::Admin)) + .get(&StorageKey::Core(CoreDataKey::Admin)) .unwrap() } pub fn set_owners(env: &Env, owners: &Vec
) { env.storage().set( - &DataKey::Admin(AdminDataKey::Owners), + &StorageKey::Admin(AdminDataKey::Owners), owners, ); } \ No newline at end of file