@@ -9,7 +9,11 @@ use std::{collections::HashMap, sync::Arc};
9
9
use anyhow:: bail;
10
10
use c509_certificate:: c509:: C509 ;
11
11
use pallas:: {
12
- codec:: utils:: Bytes , crypto:: hash:: Hash , ledger:: traverse:: MultiEraTx ,
12
+ crypto:: hash:: Hash ,
13
+ ledger:: {
14
+ addresses:: { Address , ShelleyAddress , ShelleyPaymentPart } ,
15
+ traverse:: MultiEraTx ,
16
+ } ,
13
17
network:: miniprotocols:: Point ,
14
18
} ;
15
19
use payment_history:: PaymentHistory ;
@@ -51,8 +55,8 @@ impl RegistrationChain {
51
55
///
52
56
/// Returns an error if data is invalid
53
57
pub fn new (
54
- & self , point : Point , tracking_payment_keys : Vec < Ed25519PublicKey > , tx_idx : usize ,
55
- txn : & MultiEraTx , cip509 : Cip509 ,
58
+ point : Point , tracking_payment_keys : & [ ShelleyAddress ] , tx_idx : usize , txn : & MultiEraTx ,
59
+ cip509 : Cip509 ,
56
60
) -> anyhow:: Result < Self > {
57
61
let inner = RegistrationChainInner :: new ( cip509, tracking_payment_keys, point, tx_idx, txn) ?;
58
62
@@ -124,16 +128,10 @@ impl RegistrationChain {
124
128
& self . inner . role_data
125
129
}
126
130
127
- /// Get the list of payment keys to track .
131
+ /// Get the map of tracked payment keys to its history .
128
132
#[ must_use]
129
- pub fn tracking_payment_keys ( & self ) -> & Vec < Ed25519PublicKey > {
130
- & self . inner . tracking_payment_keys
131
- }
132
-
133
- /// Get the map of payment key to its history.
134
- #[ must_use]
135
- pub fn payment_history ( & self ) -> & HashMap < Ed25519PublicKey , Vec < PaymentHistory > > {
136
- & self . inner . payment_history
133
+ pub fn tracking_payment_history ( & self ) -> & HashMap < ShelleyAddress , Vec < PaymentHistory > > {
134
+ & self . inner . tracking_payment_history
137
135
}
138
136
}
139
137
@@ -158,10 +156,8 @@ struct RegistrationChainInner {
158
156
// Role
159
157
/// Map of role number to point, transaction index, and role data.
160
158
role_data : HashMap < u8 , ( PointTxIdx , RoleData ) > ,
161
- /// List of payment keys to track.
162
- tracking_payment_keys : Arc < Vec < Ed25519PublicKey > > ,
163
- /// Map of payment key to its history.
164
- payment_history : HashMap < Ed25519PublicKey , Vec < PaymentHistory > > ,
159
+ /// Map of tracked payment key to its history.
160
+ tracking_payment_history : HashMap < ShelleyAddress , Vec < PaymentHistory > > ,
165
161
}
166
162
167
163
impl RegistrationChainInner {
@@ -179,7 +175,7 @@ impl RegistrationChainInner {
179
175
///
180
176
/// Returns an error if data is invalid
181
177
fn new (
182
- cip509 : Cip509 , tracking_payment_keys : Vec < Ed25519PublicKey > , point : Point , tx_idx : usize ,
178
+ cip509 : Cip509 , tracking_payment_keys : & [ ShelleyAddress ] , point : Point , tx_idx : usize ,
183
179
txn : & MultiEraTx ,
184
180
) -> anyhow:: Result < Self > {
185
181
// Should be chain root, return immediately if not
@@ -207,12 +203,13 @@ impl RegistrationChainInner {
207
203
let revocations = revocations_list ( registration. revocation_list , & point_tx_idx) ;
208
204
let role_data_map = chain_root_role_data ( registration. role_set , txn, & point_tx_idx) ?;
209
205
210
- let mut payment_history = HashMap :: new ( ) ;
211
- for tracking_key in & tracking_payment_keys {
212
- // Keep record of payment history, the payment key that we want to track
213
- let histories = update_payment_history ( tracking_key, txn, & point_tx_idx) ?;
214
- payment_history. insert ( tracking_key. clone ( ) , histories) ;
206
+ let mut tracking_payment_history = HashMap :: new ( ) ;
207
+ // Create a payment history for each tracking payment key
208
+ for tracking_key in tracking_payment_keys {
209
+ tracking_payment_history. insert ( tracking_key. clone ( ) , Vec :: new ( ) ) ;
215
210
}
211
+ // Keep record of payment history, the payment key that we want to track
212
+ update_tracking_payment_history ( & mut tracking_payment_history, txn, & point_tx_idx) ?;
216
213
217
214
Ok ( Self {
218
215
purpose,
@@ -222,8 +219,7 @@ impl RegistrationChainInner {
222
219
simple_keys : public_key_map,
223
220
revocations,
224
221
role_data : role_data_map,
225
- tracking_payment_keys : Arc :: new ( tracking_payment_keys) ,
226
- payment_history,
222
+ tracking_payment_history,
227
223
} )
228
224
}
229
225
@@ -280,16 +276,11 @@ impl RegistrationChainInner {
280
276
281
277
update_role_data ( & mut new_inner, registration. role_set , txn, & point_tx_idx) ?;
282
278
283
- for tracking_key in self . tracking_payment_keys . iter ( ) {
284
- let histories = update_payment_history ( tracking_key, txn, & point_tx_idx) ?;
285
- // If tracking payment key doesn't exist, insert an empty vector,
286
- // then add the histories to the history vector
287
- new_inner
288
- . payment_history
289
- . entry ( tracking_key. clone ( ) )
290
- . or_default ( )
291
- . extend ( histories) ;
292
- }
279
+ update_tracking_payment_history (
280
+ & mut new_inner. tracking_payment_history ,
281
+ txn,
282
+ & point_tx_idx,
283
+ ) ?;
293
284
294
285
Ok ( new_inner)
295
286
}
@@ -448,7 +439,7 @@ fn chain_root_role_data(
448
439
let encryption_key = role_data. role_encryption_key . clone ( ) ;
449
440
450
441
// Get the payment key
451
- let payment_key = get_payment_key_from_tx ( txn, role_data. payment_key ) ?;
442
+ let payment_key = get_payment_addr_from_tx ( txn, role_data. payment_key ) ?;
452
443
453
444
// Map of role number to point and role data
454
445
role_data_map. insert (
@@ -496,7 +487,7 @@ fn update_role_data(
496
487
}
497
488
} ,
498
489
} ;
499
- let payment_key = get_payment_key_from_tx ( txn, role_data. payment_key ) ?;
490
+ let payment_key = get_payment_addr_from_tx ( txn, role_data. payment_key ) ?;
500
491
501
492
// Map of role number to point and role data
502
493
// Note that new role data will overwrite the old one
@@ -517,10 +508,10 @@ fn update_role_data(
517
508
Ok ( ( ) )
518
509
}
519
510
520
- /// Helper function for retrieving the payment key from the transaction.
521
- fn get_payment_key_from_tx (
511
+ /// Helper function for retrieving the Shelley address from the transaction.
512
+ fn get_payment_addr_from_tx (
522
513
txn : & MultiEraTx , payment_key_ref : Option < i16 > ,
523
- ) -> anyhow:: Result < Ed25519PublicKey > {
514
+ ) -> anyhow:: Result < Option < ShelleyPaymentPart > > {
524
515
// The index should exist since it pass the basic validation
525
516
if let Some ( key_ref) = payment_key_ref {
526
517
if let MultiEraTx :: Conway ( tx) = txn {
@@ -533,11 +524,13 @@ fn get_payment_key_from_tx(
533
524
pallas:: ledger:: primitives:: conway:: PseudoTransactionOutput :: PostAlonzo (
534
525
o,
535
526
) => {
536
- let payment_key: Ed25519PublicKey =
537
- o. address . clone ( ) . try_into ( ) . map_err ( |_| {
538
- anyhow:: anyhow!( "Failed to convert Vec<u8> to Ed25519PublicKey in payment key reference" )
539
- } ) ?;
540
- return Ok ( payment_key) ;
527
+ let address =
528
+ Address :: from_bytes ( & o. address ) . map_err ( |e| anyhow:: anyhow!( e) ) ?;
529
+
530
+ if let Address :: Shelley ( addr) = address {
531
+ return Ok ( Some ( addr. payment ( ) . clone ( ) ) ) ;
532
+ }
533
+ bail ! ( "Unsupported address type in payment key reference" ) ;
541
534
} ,
542
535
// Not support legacy form of transaction output
543
536
pallas:: ledger:: primitives:: conway:: PseudoTransactionOutput :: Legacy ( _) => {
@@ -552,26 +545,34 @@ fn get_payment_key_from_tx(
552
545
bail ! ( "Unsupported payment key reference to transaction input" ) ;
553
546
}
554
547
}
555
- Ok ( Ed25519PublicKey :: default ( ) )
548
+ Ok ( None )
556
549
}
557
550
558
551
/// Update the payment history given the tracking payment keys.
559
- fn update_payment_history (
560
- tracking_key : & Ed25519PublicKey , txn : & MultiEraTx , point_tx_idx : & PointTxIdx ,
561
- ) -> anyhow :: Result < Vec < PaymentHistory > > {
562
- let mut payment_history = Vec :: new ( ) ;
552
+ fn update_tracking_payment_history (
553
+ tracking_payment_history : & mut HashMap < ShelleyAddress , Vec < PaymentHistory > > , txn : & MultiEraTx ,
554
+ point_tx_idx : & PointTxIdx ,
555
+ ) -> anyhow :: Result < ( ) > {
563
556
if let MultiEraTx :: Conway ( tx) = txn {
564
557
// Conway era -> Post alonzo tx output
565
558
for ( index, output) in tx. transaction_body . outputs . iter ( ) . enumerate ( ) {
566
559
match output {
567
560
pallas:: ledger:: primitives:: conway:: PseudoTransactionOutput :: PostAlonzo ( o) => {
568
- let address_bytes: Bytes = tracking_key. clone ( ) . into ( ) ;
569
- if address_bytes == o. address {
561
+ let address =
562
+ Address :: from_bytes ( & o. address ) . map_err ( |e| anyhow:: anyhow!( e) ) ?;
563
+ let shelley_payment = if let Address :: Shelley ( addr) = address {
564
+ addr. clone ( )
565
+ } else {
566
+ bail ! ( "Unsupported address type in update payment history" ) ;
567
+ } ;
568
+ // If the payment key from the output exist in the payment history, add the
569
+ // history
570
+ if let Some ( vec) = tracking_payment_history. get_mut ( & shelley_payment) {
570
571
let output_index: u16 = index. try_into ( ) . map_err ( |_| {
571
572
anyhow:: anyhow!( "Cannot convert usize to u16 in update payment history" )
572
573
} ) ?;
573
574
574
- payment_history . push ( PaymentHistory :: new (
575
+ vec . push ( PaymentHistory :: new (
575
576
point_tx_idx. clone ( ) ,
576
577
txn. hash ( ) ,
577
578
output_index,
@@ -585,5 +586,100 @@ fn update_payment_history(
585
586
}
586
587
}
587
588
}
588
- Ok ( payment_history)
589
+ Ok ( ( ) )
590
+ }
591
+
592
+ #[ cfg( test) ]
593
+ mod test {
594
+ use minicbor:: { Decode , Decoder } ;
595
+ use pallas:: { ledger:: traverse:: MultiEraTx , network:: miniprotocols:: Point } ;
596
+
597
+ use super :: RegistrationChain ;
598
+ use crate :: cardano:: { cip509:: Cip509 , transaction:: raw_aux_data:: RawAuxData } ;
599
+
600
+ fn cip_509_aux_data ( tx : & MultiEraTx < ' _ > ) -> Vec < u8 > {
601
+ let raw_auxiliary_data = tx
602
+ . as_conway ( )
603
+ . unwrap ( )
604
+ . clone ( )
605
+ . auxiliary_data
606
+ . map ( |aux| aux. raw_cbor ( ) ) ;
607
+
608
+ let raw_cbor_data = match raw_auxiliary_data {
609
+ pallas:: codec:: utils:: Nullable :: Some ( data) => Ok ( data) ,
610
+ _ => Err ( "Auxiliary data not found" ) ,
611
+ } ;
612
+
613
+ let auxiliary_data = RawAuxData :: new ( raw_cbor_data. expect ( "Failed to get raw cbor data" ) ) ;
614
+ auxiliary_data
615
+ . get_metadata ( 509 )
616
+ . expect ( "Failed to get metadata" )
617
+ . to_vec ( )
618
+ }
619
+
620
+ fn conway_1 ( ) -> Vec < u8 > {
621
+ hex:: decode ( include_str ! ( "../../test_data/cardano/conway_1.block" ) )
622
+ . expect ( "Failed to decode hex block." )
623
+ }
624
+
625
+ fn conway_4 ( ) -> Vec < u8 > {
626
+ hex:: decode ( include_str ! ( "../../test_data/cardano/conway_4.block" ) )
627
+ . expect ( "Failed to decode hex block." )
628
+ }
629
+
630
+ #[ test]
631
+ fn test_new_and_update_registration ( ) {
632
+ let conway_block_data_1 = conway_1 ( ) ;
633
+ let point_1 = Point :: new (
634
+ 77_429_134 ,
635
+ hex:: decode ( "62483f96613b4c48acd28de482eb735522ac180df61766bdb476a7bf83e7bb98" )
636
+ . unwrap ( ) ,
637
+ ) ;
638
+ let multi_era_block_1 =
639
+ pallas:: ledger:: traverse:: MultiEraBlock :: decode ( & conway_block_data_1)
640
+ . expect ( "Failed to decode MultiEraBlock" ) ;
641
+
642
+ let transactions_1 = multi_era_block_1. txs ( ) ;
643
+ // Forth transaction of this test data contains the CIP509 auxiliary data
644
+ let tx_1 = transactions_1
645
+ . get ( 3 )
646
+ . expect ( "Failed to get transaction index" ) ;
647
+
648
+ let aux_data_1 = cip_509_aux_data ( tx_1) ;
649
+ let mut decoder = Decoder :: new ( aux_data_1. as_slice ( ) ) ;
650
+ let cip509_1 = Cip509 :: decode ( & mut decoder, & mut ( ) ) . expect ( "Failed to decode Cip509" ) ;
651
+ let tracking_payment_keys = vec ! [ ] ;
652
+
653
+ let registration_chain =
654
+ RegistrationChain :: new ( point_1. clone ( ) , & tracking_payment_keys, 3 , tx_1, cip509_1) ;
655
+ // Able to add chain root to the registration chain
656
+ assert ! ( registration_chain. is_ok( ) ) ;
657
+
658
+ let conway_block_data_4 = conway_4 ( ) ;
659
+ let point_4 = Point :: new (
660
+ 77_436_369 ,
661
+ hex:: decode ( "b174fc697126f05046b847d47e60d66cbedaf25240027f9c07f27150889aac24" )
662
+ . unwrap ( ) ,
663
+ ) ;
664
+
665
+ let multi_era_block_4 =
666
+ pallas:: ledger:: traverse:: MultiEraBlock :: decode ( & conway_block_data_4)
667
+ . expect ( "Failed to decode MultiEraBlock" ) ;
668
+
669
+ let transactions_4 = multi_era_block_4. txs ( ) ;
670
+ // Second transaction of this test data contains the CIP509 auxiliary data
671
+ let tx = transactions_4
672
+ . get ( 1 )
673
+ . expect ( "Failed to get transaction index" ) ;
674
+
675
+ let aux_data_4 = cip_509_aux_data ( tx) ;
676
+ let mut decoder = Decoder :: new ( aux_data_4. as_slice ( ) ) ;
677
+ let cip509 = Cip509 :: decode ( & mut decoder, & mut ( ) ) . expect ( "Failed to decode Cip509" ) ;
678
+
679
+ // Update the registration chain
680
+ assert ! ( registration_chain
681
+ . unwrap( )
682
+ . update( point_4. clone( ) , 1 , tx, cip509)
683
+ . is_ok( ) ) ;
684
+ }
589
685
}
0 commit comments