diff --git a/stellar-contracts/src/lib.rs b/stellar-contracts/src/lib.rs index f189d637..833f9762 100644 --- a/stellar-contracts/src/lib.rs +++ b/stellar-contracts/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -use soroban_sdk::{contract, contractimpl, symbol_short, Address, BytesN, Env, String, Vec}; +use soroban_sdk::{contract, contractimpl, contracttype, Address, BytesN, Env, Vec, symbol_short};use soroban_sdk::{contract, contractimpl, contracttype, Address, BytesN, Env, Vec, symbol_short}; mod types; pub use types::*; @@ -25,6 +25,49 @@ mod issuer_test; #[cfg(test)] mod status_test; +// 1. Define the input structure for the batch array +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct CertBatchItem { + pub recipient: Address, + pub cert_hash: BytesN<32>, // The unique hash of the certificate data +} + +#[contract] +pub struct StellarCertContract; + +#[contractimpl] +impl StellarCertContract { + + // Existing single issue function (for context) + // pub fn issue_certificate(env: Env, issuer: Address, recipient: Address, cert_hash: BytesN<32>) { ... } + + // 2. Implement the Batch Function + pub fn batch_issue_certificates(env: Env, issuer: Address, certificates: Vec) { + // Require the issuer to authorize the entire batch operation once + issuer.require_auth(); + + // 3. Iterate through the Vec and apply the issuance logic + for cert in certificates.into_iter() { + // NOTE: Replace this with your contract's actual storage key pattern + let storage_key = cert.cert_hash.clone(); + + // Ensure the certificate doesn't already exist to prevent overwriting + if env.storage().persistent().has(&storage_key) { + panic!("Certificate hash already exists in this batch"); + } + + // Store the certificate + env.storage().persistent().set(&storage_key, &cert.recipient); + } + + // 4. Emit a single event for the batch to save on event emission costs + env.events().publish( + (symbol_short!("batch_iss"), issuer), + certificates.len(), // Log how many were issued + ); + } + #[contract] pub struct CertificateContract; diff --git a/stellar-contracts/src/test.rs b/stellar-contracts/src/test.rs index 548b6244..660d7488 100644 --- a/stellar-contracts/src/test.rs +++ b/stellar-contracts/src/test.rs @@ -1,9 +1,51 @@ #![cfg(test)] - use super::*; -use soroban_sdk::{testutils::Address as _, Address, Bytes, BytesN, Env, Vec}; +use soroban_sdk::{testutils::{Address as _, Events}, Vec, Address, Bytes, BytesN, Env, IntoVal}; use soroban_sdk::{Env, testutils::Address as _, Address, String}; +#[test] +fn test_batch_issue_certificates() { + let env = Env::default(); + env.mock_all_auths(); // Mock authorization for the test + + let contract_id = env.register_contract(None, StellarCertContract); + let client = StellarCertContractClient::new(&env, &contract_id); + + let issuer = Address::generate(&env); + let recipient_1 = Address::generate(&env); + let recipient_2 = Address::generate(&env); + + // Create mock certificate hashes + let hash_1 = BytesN::from_array(&env, &[1; 32]); + let hash_2 = BytesN::from_array(&env, &[2; 32]); + + // Construct the batch Vector + let batch = vec![ + &env, + CertBatchItem { recipient: recipient_1.clone(), cert_hash: hash_1.clone() }, + CertBatchItem { recipient: recipient_2.clone(), cert_hash: hash_2.clone() }, + ]; + + // Execute the batch issuance + client.batch_issue_certificates(&issuer, &batch); + + // Verify storage state (Assuming you have a 'get_certificate' or similar read function) + // assert_eq!(client.get_certificate(&hash_1), recipient_1); + // assert_eq!(client.get_certificate(&hash_2), recipient_2); + + // Verify the event was emitted correctly + let events = env.events().all(); + let last_event = events.last().unwrap(); + assert_eq!( + last_event.1, // Topics + (symbol_short!("batch_iss"), issuer).into_val(&env) + ); + assert_eq!( + last_event.2, // Data (Length of batch) + 2u32.into_val(&env) + ); +} + // Helper function to create a certificate version fn create_version(env: &Env, major: u32, minor: u32, patch: u32) -> CertificateVersion { CertificateVersion {