Skip to content

Commit af20476

Browse files
author
Vicky08100
committed
chore: migrate certificate contract to soroban sdk v22 and fix compilation
1 parent e1a5e4a commit af20476

5 files changed

Lines changed: 65 additions & 56 deletions

File tree

contracts/certificate/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ categories = ["cryptography::cryptocurrencies", "web-programming"]
1515
crate-type = ["lib", "cdylib"]
1616

1717
[dependencies]
18-
soroban-sdk = { workspace = true }
18+
soroban-sdk = { workspace = true, features = ["alloc"] }
1919
shared = { path = "../shared" }
2020

2121
[dev-dependencies]

contracts/certificate/src/events.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ use shared::emit_certification_event;
22
use shared::event_schema::{
33
BatchCompletedEvent, CertificateReissuedEvent, CertificateSharedEvent,
44
CertificateVerifiedEvent, CertificationEventData, CertificationIssuedEvent,
5-
CertificationRevokedEvent, ComplianceCheckedEvent, MultisigApprovalGrantedEvent,
6-
MultisigConfigUpdatedEvent, MultisigRequestApprovedEvent, MultisigRequestCreatedEvent,
7-
MultisigRequestRejectedEvent, TemplateCreatedEvent,
5+
CertificationRevokedEvent, ComplianceCheckedEvent, ComplianceViolationEvent,
6+
MultisigApprovalGrantedEvent, MultisigConfigUpdatedEvent, MultisigRequestApprovedEvent,
7+
MultisigRequestCreatedEvent, MultisigRequestRejectedEvent, TemplateCreatedEvent,
88
};
99
use soroban_sdk::{symbol_short, Address, BytesN, Env, String};
1010

@@ -187,7 +187,7 @@ pub fn emit_compliance_violation(
187187
env: &Env,
188188
certificate_id: &BytesN<32>,
189189
standard: &str,
190-
details: &str,
190+
details: String,
191191
) {
192192
emit_certification_event!(
193193
env,
@@ -196,7 +196,7 @@ pub fn emit_compliance_violation(
196196
CertificationEventData::ComplianceViolation(ComplianceViolationEvent {
197197
certificate_id: certificate_id.clone(),
198198
standard: String::from_str(env, standard),
199-
violation_details: String::from_str(env, details),
199+
violation_details: details,
200200
})
201201
);
202202
}

contracts/certificate/src/lib.rs

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,15 +1133,7 @@ impl CertificateContract {
11331133
}
11341134

11351135
// Perform substitution in description
1136-
let mut description = params.description.clone();
1137-
for (name, value) in field_values.iter() {
1138-
// Substitution logic note:
1139-
// While full string replacement is expensive on-chain, we store the values
1140-
// here to ensure the certificate remains fully audit-compliant and
1141-
// substitution can be verified deterministically.
1142-
let _ = (name, value);
1143-
}
1144-
1136+
let description = params.description.clone();
11451137
storage::set_template_values(&env, &params.certificate_id, &field_values);
11461138

11471139
let anchor = generate_blockchain_anchor(&env, &params.certificate_id);
@@ -1183,7 +1175,13 @@ impl CertificateContract {
11831175
a.active_certificates += 1;
11841176
});
11851177

1186-
record_audit(&env, &params.certificate_id, AuditAction::Executed, &admin, "Issued with template");
1178+
record_audit(
1179+
&env,
1180+
&params.certificate_id,
1181+
AuditAction::Executed,
1182+
&admin,
1183+
"Issued with template",
1184+
);
11871185

11881186
Ok(params.certificate_id)
11891187
}
@@ -1199,6 +1197,13 @@ impl CertificateContract {
11991197
let template =
12001198
storage::get_template(&env, &template_id).ok_or(CertificateError::TemplateNotFound)?;
12011199

1200+
// Validate required fields
1201+
for field in template.fields.iter() {
1202+
if field.is_required && !field_values.contains_key(field.field_name.clone()) {
1203+
return Err(CertificateError::MissingRequiredField);
1204+
}
1205+
}
1206+
12021207
Ok(Certificate {
12031208
certificate_id: BytesN::from_array(&env, &[0u8; 32]),
12041209
course_id,
@@ -1397,28 +1402,24 @@ impl CertificateContract {
13971402
// 1. Expiry Check
13981403
if cert.expiry_date != 0 && env.ledger().timestamp() > cert.expiry_date {
13991404
is_compliant = false;
1400-
violation_details = String::from_str(&env, "Certificate has expired; ");
1405+
violation_details = String::from_str(&env, "Expired");
14011406
}
1402-
14031407
// 2. Status Check
1404-
if cert.status != CertificateStatus::Active {
1408+
else if cert.status != CertificateStatus::Active {
14051409
is_compliant = false;
1406-
let status_msg = match cert.status {
1407-
CertificateStatus::Revoked => "Revoked",
1408-
CertificateStatus::Suspended => "Suspended",
1409-
CertificateStatus::Expired => "Expired",
1410-
CertificateStatus::Reissued => "Reissued",
1411-
_ => "Inactive",
1410+
violation_details = match cert.status {
1411+
CertificateStatus::Revoked => String::from_str(&env, "Status: Revoked"),
1412+
CertificateStatus::Suspended => String::from_str(&env, "Status: Suspended"),
1413+
CertificateStatus::Expired => String::from_str(&env, "Status: Expired"),
1414+
CertificateStatus::Reissued => String::from_str(&env, "Status: Reissued"),
1415+
CertificateStatus::NonCompliant => String::from_str(&env, "Status: Non-Compliant"),
1416+
_ => String::from_str(&env, "Status: Inactive"),
14121417
};
1413-
violation_details = String::from_str(&env, "Certificate status is ");
1414-
violation_details.append(&String::from_str(&env, status_msg));
1415-
violation_details.append(&String::from_str(&env, "; "));
14161418
}
1417-
14181419
// 3. Provenance Check
1419-
if cert.blockchain_anchor.is_none() {
1420+
else if cert.blockchain_anchor.is_none() {
14201421
is_compliant = false;
1421-
violation_details = String::from_str(&env, "Blockchain anchor is missing; ");
1422+
violation_details = String::from_str(&env, "Missing Anchor");
14221423
}
14231424

14241425
if !is_compliant {
@@ -1440,7 +1441,7 @@ impl CertificateContract {
14401441
&env,
14411442
&certificate_id,
14421443
"Standard Automated Check",
1443-
&violation_details.to_string(),
1444+
violation_details,
14441445
);
14451446
record_audit(
14461447
&env,
@@ -1472,8 +1473,8 @@ impl CertificateContract {
14721473
require_initialized(&env)?;
14731474
require_compliance_officer(&env, &officer)?;
14741475

1475-
let mut cert =
1476-
storage::get_certificate(&env, &certificate_id).ok_or(CertificateError::CertificateNotFound)?;
1476+
let mut cert = storage::get_certificate(&env, &certificate_id)
1477+
.ok_or(CertificateError::CertificateNotFound)?;
14771478
cert.status = CertificateStatus::NonCompliant;
14781479
storage::set_certificate(&env, &certificate_id, &cert);
14791480

@@ -1485,7 +1486,7 @@ impl CertificateContract {
14851486
&env,
14861487
&certificate_id,
14871488
"Manual Compliance Override",
1488-
&notes,
1489+
notes.clone(),
14891490
);
14901491

14911492
record_audit(
@@ -1512,6 +1513,7 @@ impl CertificateContract {
15121513
CertificateStatus::Expired => String::from_str(&env, "Expired"),
15131514
CertificateStatus::Suspended => String::from_str(&env, "Suspended"),
15141515
CertificateStatus::Reissued => String::from_str(&env, "Reissued"),
1516+
CertificateStatus::NonCompliant => String::from_str(&env, "Non-Compliant"),
15151517
},
15161518
);
15171519
report.set(

contracts/certificate/src/storage.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use soroban_sdk::{IntoVal, RawVal, Address, BytesN, Env, Map, String, Vec};
1+
use soroban_sdk::{Address, BytesN, Env, IntoVal, Map, String, Val, Vec};
22

33
use crate::types::{
44
CertDataKey, Certificate, CertificateAnalytics, CertificateBackup, CertificateTemplate,
@@ -14,7 +14,7 @@ const MIN_PERSISTENT_TTL: u32 = 518_400; // ~30 days in ledgers (assuming 5s per
1414
/// TTL Extension: 90 days
1515
const EXTEND_TTL_TO: u32 = 1_555_200; // ~90 days
1616

17-
fn extend_ttl_persistent<K: IntoVal<Env, RawVal>>(env: &Env, key: &K) {
17+
fn extend_ttl_persistent<K: IntoVal<Env, Val>>(env: &Env, key: &K) {
1818
env.storage().persistent().extend_ttl(key, MIN_PERSISTENT_TTL, EXTEND_TTL_TO);
1919
}
2020

@@ -222,15 +222,15 @@ pub fn set_template(env: &Env, template_id: &String, template: &CertificateTempl
222222
// Add to template list if not already present
223223
let mut list: Vec<String> =
224224
env.storage().persistent().get(&CertDataKey::TemplateList).unwrap_or_else(|| Vec::new(env));
225-
225+
226226
let mut exists = false;
227227
for id in list.iter() {
228228
if id == *template_id {
229229
exists = true;
230230
break;
231231
}
232232
}
233-
233+
234234
if !exists {
235235
list.push_back(template_id.clone());
236236
env.storage().persistent().set(&CertDataKey::TemplateList, &list);
@@ -286,6 +286,7 @@ pub fn get_analytics(env: &Env) -> CertificateAnalytics {
286286
active_certificates: 0,
287287
pending_requests: 0,
288288
avg_approval_time: 0,
289+
compliance_violations_count: 0,
289290
last_updated: 0,
290291
})
291292
}

contracts/certificate/src/test.rs

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use shared::monitoring::ContractHealthStatus;
22
use soroban_sdk::{
33
testutils::{Address as _, Ledger as _},
4-
Address, BytesN, Env, String, Vec,
4+
Address, BytesN, Env, Map, String, Vec,
55
};
66

77
use crate::{
88
types::{
9-
CertificatePriority, CertificateStatus, ComplianceStandard, FieldType,
10-
MintCertificateParams, MultiSigConfig, MultiSigRequestStatus, TemplateField,
9+
CertDataKey, CertRateLimitConfig, CertificatePriority, CertificateStatus,
10+
ComplianceStandard, FieldType, MintCertificateParams, MultiSigConfig,
11+
MultiSigRequestStatus, TemplateField,
1112
},
1213
CertificateContract, CertificateContractClient,
1314
};
@@ -583,7 +584,8 @@ fn test_create_and_use_template() {
583584

584585
let mut field_values: Map<String, String> = Map::new(&env);
585586
field_values.set(String::from_str(&env, "student_name"), String::from_str(&env, "John Doe"));
586-
field_values.set(String::from_str(&env, "completion_date"), String::from_str(&env, "2026-02-25"));
587+
field_values
588+
.set(String::from_str(&env, "completion_date"), String::from_str(&env, "2026-02-25"));
587589

588590
let cert_id = client.issue_with_template(&admin, &template_id, &params, &field_values);
589591
assert_eq!(cert_id, params.certificate_id);
@@ -645,7 +647,12 @@ fn test_automated_compliance_audit() {
645647
assert!(is_compliant);
646648

647649
// Revoke and check again
648-
client.revoke_certificate(&admin, &params.certificate_id, &String::from_str(&env, "Revoked"), &false);
650+
client.revoke_certificate(
651+
&admin,
652+
&params.certificate_id,
653+
&String::from_str(&env, "Revoked"),
654+
&false,
655+
);
649656
let is_compliant_after = client.automated_compliance_audit(&params.certificate_id);
650657
assert!(!is_compliant_after);
651658
}
@@ -1131,44 +1138,43 @@ fn test_health_check_before_init() {
11311138
fn test_compliance_officer_and_manual_override() {
11321139
let (env, client, admin) = setup_env();
11331140
let officer = Address::generate(&env);
1134-
1141+
11351142
// Set officer
11361143
client.set_compliance_officer(&admin, &officer);
1137-
1144+
11381145
// Issue certificate
11391146
let student = Address::generate(&env);
11401147
let params = make_cert_params(&env, "OVERRIDE_COURSE", &student);
11411148
let mut list = Vec::new(&env);
11421149
list.push_back(params.clone());
11431150
client.batch_issue_certificates(&admin, &list);
1144-
1151+
11451152
// Manual override
11461153
client.manual_compliance_override(
11471154
&officer,
11481155
&params.certificate_id,
11491156
&String::from_str(&env, "Flagged for manual review"),
11501157
);
1151-
1158+
11521159
let cert = client.get_certificate(&params.certificate_id).unwrap();
11531160
assert_eq!(cert.status, CertificateStatus::NonCompliant);
1154-
1161+
11551162
let analytics = client.get_analytics();
11561163
assert_eq!(analytics.compliance_violations_count, 1);
11571164
}
11581165

11591166
#[test]
11601167
fn test_automated_compliance_audit_rate_limit() {
11611168
let (env, client, admin) = setup_env();
1162-
1169+
11631170
// Issue certificate
11641171
let student = Address::generate(&env);
11651172
let params = make_cert_params(&env, "RL_COURSE", &student);
11661173
let mut list = Vec::new(&env);
11671174
list.push_back(params.clone());
11681175
client.batch_issue_certificates(&admin, &list);
1169-
1176+
11701177
// Configure strict rate limit (3 calls per day)
1171-
client.set_admin(&admin, &admin); // Ensure admin can configure
11721178
env.as_contract(&client.address, || {
11731179
env.storage().instance().set(
11741180
&CertDataKey::RateLimitCfg,
@@ -1177,10 +1183,10 @@ fn test_automated_compliance_audit_rate_limit() {
11771183
});
11781184

11791185
// Call 1, 2, 3: Succeed
1180-
assert!(client.automated_compliance_audit(&params.certificate_id).is_ok());
1181-
assert!(client.automated_compliance_audit(&params.certificate_id).is_ok());
1182-
assert!(client.automated_compliance_audit(&params.certificate_id).is_ok());
1183-
1186+
assert!(client.try_automated_compliance_audit(&params.certificate_id).is_ok());
1187+
assert!(client.try_automated_compliance_audit(&params.certificate_id).is_ok());
1188+
assert!(client.try_automated_compliance_audit(&params.certificate_id).is_ok());
1189+
11841190
// Call 4: Fail
11851191
let res = client.try_automated_compliance_audit(&params.certificate_id);
11861192
assert!(res.is_err());

0 commit comments

Comments
 (0)