Skip to content

Commit d60fb50

Browse files
bkioshnstevenj
andauthored
fix(rust/rbac-registration): Tracking payment key type (#93)
* fix: payment key shelley address and add test for registration chain Signed-off-by: bkioshn <[email protected]> * chore: comment * fix: format * fix: use ShelleyPaymentPart Signed-off-by: bkioshn <[email protected]> * fix: track ShelleyAddress Signed-off-by: bkioshn <[email protected]> * fix: remove tracking_payment_key and use tracking_payment_history Signed-off-by: bkioshn <[email protected]> --------- Signed-off-by: bkioshn <[email protected]> Co-authored-by: Steven Johnson <[email protected]>
1 parent 8cbe99d commit d60fb50

File tree

3 files changed

+156
-57
lines changed

3 files changed

+156
-57
lines changed

rust/rbac-registration/src/registration/cardano/mod.rs

+149-53
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ use std::{collections::HashMap, sync::Arc};
99
use anyhow::bail;
1010
use c509_certificate::c509::C509;
1111
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+
},
1317
network::miniprotocols::Point,
1418
};
1519
use payment_history::PaymentHistory;
@@ -51,8 +55,8 @@ impl RegistrationChain {
5155
///
5256
/// Returns an error if data is invalid
5357
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,
5660
) -> anyhow::Result<Self> {
5761
let inner = RegistrationChainInner::new(cip509, tracking_payment_keys, point, tx_idx, txn)?;
5862

@@ -124,16 +128,10 @@ impl RegistrationChain {
124128
&self.inner.role_data
125129
}
126130

127-
/// Get the list of payment keys to track.
131+
/// Get the map of tracked payment keys to its history.
128132
#[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
137135
}
138136
}
139137

@@ -158,10 +156,8 @@ struct RegistrationChainInner {
158156
// Role
159157
/// Map of role number to point, transaction index, and role data.
160158
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>>,
165161
}
166162

167163
impl RegistrationChainInner {
@@ -179,7 +175,7 @@ impl RegistrationChainInner {
179175
///
180176
/// Returns an error if data is invalid
181177
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,
183179
txn: &MultiEraTx,
184180
) -> anyhow::Result<Self> {
185181
// Should be chain root, return immediately if not
@@ -207,12 +203,13 @@ impl RegistrationChainInner {
207203
let revocations = revocations_list(registration.revocation_list, &point_tx_idx);
208204
let role_data_map = chain_root_role_data(registration.role_set, txn, &point_tx_idx)?;
209205

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());
215210
}
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)?;
216213

217214
Ok(Self {
218215
purpose,
@@ -222,8 +219,7 @@ impl RegistrationChainInner {
222219
simple_keys: public_key_map,
223220
revocations,
224221
role_data: role_data_map,
225-
tracking_payment_keys: Arc::new(tracking_payment_keys),
226-
payment_history,
222+
tracking_payment_history,
227223
})
228224
}
229225

@@ -280,16 +276,11 @@ impl RegistrationChainInner {
280276

281277
update_role_data(&mut new_inner, registration.role_set, txn, &point_tx_idx)?;
282278

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+
)?;
293284

294285
Ok(new_inner)
295286
}
@@ -448,7 +439,7 @@ fn chain_root_role_data(
448439
let encryption_key = role_data.role_encryption_key.clone();
449440

450441
// 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)?;
452443

453444
// Map of role number to point and role data
454445
role_data_map.insert(
@@ -496,7 +487,7 @@ fn update_role_data(
496487
}
497488
},
498489
};
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)?;
500491

501492
// Map of role number to point and role data
502493
// Note that new role data will overwrite the old one
@@ -517,10 +508,10 @@ fn update_role_data(
517508
Ok(())
518509
}
519510

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(
522513
txn: &MultiEraTx, payment_key_ref: Option<i16>,
523-
) -> anyhow::Result<Ed25519PublicKey> {
514+
) -> anyhow::Result<Option<ShelleyPaymentPart>> {
524515
// The index should exist since it pass the basic validation
525516
if let Some(key_ref) = payment_key_ref {
526517
if let MultiEraTx::Conway(tx) = txn {
@@ -533,11 +524,13 @@ fn get_payment_key_from_tx(
533524
pallas::ledger::primitives::conway::PseudoTransactionOutput::PostAlonzo(
534525
o,
535526
) => {
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");
541534
},
542535
// Not support legacy form of transaction output
543536
pallas::ledger::primitives::conway::PseudoTransactionOutput::Legacy(_) => {
@@ -552,26 +545,34 @@ fn get_payment_key_from_tx(
552545
bail!("Unsupported payment key reference to transaction input");
553546
}
554547
}
555-
Ok(Ed25519PublicKey::default())
548+
Ok(None)
556549
}
557550

558551
/// 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<()> {
563556
if let MultiEraTx::Conway(tx) = txn {
564557
// Conway era -> Post alonzo tx output
565558
for (index, output) in tx.transaction_body.outputs.iter().enumerate() {
566559
match output {
567560
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) {
570571
let output_index: u16 = index.try_into().map_err(|_| {
571572
anyhow::anyhow!("Cannot convert usize to u16 in update payment history")
572573
})?;
573574

574-
payment_history.push(PaymentHistory::new(
575+
vec.push(PaymentHistory::new(
575576
point_tx_idx.clone(),
576577
txn.hash(),
577578
output_index,
@@ -585,5 +586,100 @@ fn update_payment_history(
585586
}
586587
}
587588
}
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+
}
589685
}

rust/rbac-registration/src/registration/cardano/role_data.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
33
use std::collections::HashMap;
44

5-
use crate::cardano::cip509::rbac::{pub_key::Ed25519PublicKey, role_data::KeyLocalRef};
5+
use pallas::ledger::addresses::ShelleyPaymentPart;
6+
7+
use crate::cardano::cip509::rbac::role_data::KeyLocalRef;
68

79
/// Role data
810
#[derive(Clone)]
@@ -12,7 +14,7 @@ pub struct RoleData {
1214
/// An encryption keys to the data within registration.
1315
encryption_ref: Option<KeyLocalRef>,
1416
/// A payment key where reward will be distributed to.
15-
payment_key: Ed25519PublicKey,
17+
payment_key: Option<ShelleyPaymentPart>,
1618
/// Map of role extended data (10-99) to its data
1719
role_extended_data: HashMap<u8, Vec<u8>>,
1820
}
@@ -21,7 +23,7 @@ impl RoleData {
2123
/// Create an instance of role data.
2224
pub(crate) fn new(
2325
signing_key_ref: Option<KeyLocalRef>, encryption_ref: Option<KeyLocalRef>,
24-
payment_key: Ed25519PublicKey, role_extended_data: HashMap<u8, Vec<u8>>,
26+
payment_key: Option<ShelleyPaymentPart>, role_extended_data: HashMap<u8, Vec<u8>>,
2527
) -> Self {
2628
RoleData {
2729
signing_key_ref,
@@ -45,7 +47,7 @@ impl RoleData {
4547

4648
/// Get the payment key.
4749
#[must_use]
48-
pub fn payment_key(&self) -> &Ed25519PublicKey {
50+
pub fn payment_key(&self) -> &Option<ShelleyPaymentPart> {
4951
&self.payment_key
5052
}
5153

0 commit comments

Comments
 (0)