@@ -75,6 +75,12 @@ pub struct CertKey {
7575 pub student : Address ,
7676}
7777
78+ #[ contracttype]
79+ #[ derive( Clone ) ]
80+ pub struct StudentCertificatesKey {
81+ pub student : Address ,
82+ }
83+
7884#[ contracttype]
7985#[ derive( Clone ) ]
8086pub struct MetaTxCallData {
@@ -324,6 +330,48 @@ impl CertificateContract {
324330 . set ( & DataKey :: MintedThisPeriod , & new_minted) ;
325331 }
326332
333+ fn persist_certificate ( env : & Env , cert : & Certificate ) {
334+ let key = CertKey {
335+ course_symbol : cert. course_symbol . clone ( ) ,
336+ student : cert. student . clone ( ) ,
337+ } ;
338+
339+ env. storage ( ) . persistent ( ) . set ( & key, cert) ;
340+ env. storage ( )
341+ . persistent ( )
342+ . extend_ttl ( & key, CERT_TTL_LEDGERS , CERT_TTL_LEDGERS ) ;
343+
344+ Self :: index_certificate_for_student ( env, & cert. student , & cert. course_symbol ) ;
345+ }
346+
347+ fn index_certificate_for_student ( env : & Env , student : & Address , course_symbol : & Symbol ) {
348+ let index_key = StudentCertificatesKey {
349+ student : student. clone ( ) ,
350+ } ;
351+ let mut course_symbols: Vec < Symbol > = env
352+ . storage ( )
353+ . persistent ( )
354+ . get ( & index_key)
355+ . unwrap_or_else ( || Vec :: new ( env) ) ;
356+
357+ let mut already_indexed = false ;
358+ for indexed_symbol in course_symbols. iter ( ) {
359+ if indexed_symbol == * course_symbol {
360+ already_indexed = true ;
361+ break ;
362+ }
363+ }
364+
365+ if !already_indexed {
366+ course_symbols. push_back ( course_symbol. clone ( ) ) ;
367+ env. storage ( ) . persistent ( ) . set ( & index_key, & course_symbols) ;
368+ }
369+
370+ env. storage ( )
371+ . persistent ( )
372+ . extend_ttl ( & index_key, CERT_TTL_LEDGERS , CERT_TTL_LEDGERS ) ;
373+ }
374+
327375 /// Returns true if `account` has `role`. `Admin` matches the three governance addresses only.
328376 pub fn has_role ( env : Env , account : Address , role : Role ) -> bool {
329377 match role {
@@ -536,11 +584,6 @@ impl CertificateContract {
536584 let mut issued: Vec < Certificate > = Vec :: new ( & env) ;
537585
538586 for student in students. iter ( ) {
539- let key = CertKey {
540- course_symbol : course_symbol. clone ( ) ,
541- student : student. clone ( ) ,
542- } ;
543-
544587 let cert = Certificate {
545588 course_symbol : course_symbol. clone ( ) ,
546589 student : student. clone ( ) ,
@@ -549,16 +592,11 @@ impl CertificateContract {
549592 revoked : false ,
550593 } ;
551594
552- env. storage ( ) . persistent ( ) . set ( & key, & cert) ;
553- env. storage ( )
554- . persistent ( )
555- . extend_ttl ( & key, CERT_TTL_LEDGERS , CERT_TTL_LEDGERS ) ;
556-
595+ Self :: persist_certificate ( & env, & cert) ;
557596 env. events ( ) . publish (
558597 ( Symbol :: new ( & env, "v1_cert_issued" ) , course_symbol. clone ( ) ) ,
559598 ( student. clone ( ) , course_name. clone ( ) ) ,
560599 ) ;
561-
562600 issued. push_back ( cert) ;
563601 }
564602
@@ -611,11 +649,6 @@ impl CertificateContract {
611649 let course_symbol = symbols. get ( i) . unwrap ( ) ;
612650 let student = students. get ( i) . unwrap ( ) ;
613651
614- let key = CertKey {
615- course_symbol : course_symbol. clone ( ) ,
616- student : student. clone ( ) ,
617- } ;
618-
619652 let cert = Certificate {
620653 course_symbol : course_symbol. clone ( ) ,
621654 student : student. clone ( ) ,
@@ -624,11 +657,7 @@ impl CertificateContract {
624657 revoked : false ,
625658 } ;
626659
627- // Batch storage operations for efficiency
628- env. storage ( ) . persistent ( ) . set ( & key, & cert) ;
629- env. storage ( )
630- . persistent ( )
631- . extend_ttl ( & key, CERT_TTL_LEDGERS , CERT_TTL_LEDGERS ) ;
660+ Self :: persist_certificate ( & env, & cert) ;
632661
633662 // Batch event emission (emit one event per certificate for transparency)
634663 env. events ( ) . publish (
@@ -699,6 +728,29 @@ impl CertificateContract {
699728 env. storage ( ) . persistent ( ) . get ( & key)
700729 }
701730
731+ /// Returns every certificate indexed for a student across all course symbols.
732+ pub fn get_certificates_by_student ( env : Env , student : Address ) -> Vec < Certificate > {
733+ let index_key = StudentCertificatesKey {
734+ student : student. clone ( ) ,
735+ } ;
736+ let course_symbols: Vec < Symbol > = env
737+ . storage ( )
738+ . persistent ( )
739+ . get ( & index_key)
740+ . unwrap_or_else ( || Vec :: new ( & env) ) ;
741+ let mut certificates = Vec :: new ( & env) ;
742+
743+ for course_symbol in course_symbols. iter ( ) {
744+ if let Some ( cert) =
745+ Self :: get_certificate ( env. clone ( ) , course_symbol. clone ( ) , student. clone ( ) )
746+ {
747+ certificates. push_back ( cert) ;
748+ }
749+ }
750+
751+ certificates
752+ }
753+
702754 /// Extend the TTL of a certificate entry in persistent storage.
703755 /// The student (or a governance admin) pays for the storage rent extension.
704756 pub fn renew_certificate ( env : Env , caller : Address , course_symbol : Symbol , student : Address ) {
@@ -781,11 +833,6 @@ impl CertificateContract {
781833 . set ( & nonce_key, & ( stored_nonce + 1 ) ) ;
782834
783835 let issue_date = env. ledger ( ) . timestamp ( ) ;
784- let key = CertKey {
785- course_symbol : call_data. course_symbol . clone ( ) ,
786- student : call_data. student . clone ( ) ,
787- } ;
788-
789836 let available = Self :: check_and_update_mint_tracking ( & env) ;
790837 if available < 1 {
791838 Self :: release_lock ( & env) ;
@@ -801,10 +848,7 @@ impl CertificateContract {
801848 revoked : false ,
802849 } ;
803850
804- env. storage ( ) . persistent ( ) . set ( & key, & cert) ;
805- env. storage ( )
806- . persistent ( )
807- . extend_ttl ( & key, CERT_TTL_LEDGERS , CERT_TTL_LEDGERS ) ;
851+ Self :: persist_certificate ( & env, & cert) ;
808852
809853 env. events ( ) . publish (
810854 (
0 commit comments