Skip to content

Commit eff9b6b

Browse files
committed
fix: payment key shelley address and add test for registration chain
Signed-off-by: bkioshn <[email protected]>
1 parent b97a095 commit eff9b6b

File tree

4 files changed

+180
-27
lines changed

4 files changed

+180
-27
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//! CIP-0019 Shelley Addresses
2+
//! there are currently 8 types of Shelley addresses
3+
//! Header type Payment Part Delegation Part
4+
//! (0) `PaymentKeyHash` `StakeKeyHash`
5+
//! (1) `ScriptHash` `StakeKeyHash`
6+
//! (2) `PaymentKeyHash` `ScriptHash`
7+
//! (3) `ScriptHash` `ScriptHash`
8+
//! (4) `PaymentKeyHash` `Pointer`
9+
//! (5) `ScriptHash` `Pointer`
10+
//! (6) `PaymentKeyHash` ø
11+
//! (7) `ScriptHash` ø
12+
13+
use pallas::codec::utils::Bytes;
14+
15+
/// CIP-0019 Shelley Addresses (only support type 0 - 5)
16+
#[derive(PartialEq, Clone, Eq, Hash)]
17+
pub struct Cip19ShelleyAddrs([u8; 57]);
18+
19+
impl From<[u8; 57]> for Cip19ShelleyAddrs {
20+
fn from(bytes: [u8; 57]) -> Self {
21+
Cip19ShelleyAddrs(bytes)
22+
}
23+
}
24+
25+
impl Default for Cip19ShelleyAddrs {
26+
fn default() -> Self {
27+
Self([0; 57])
28+
}
29+
}
30+
31+
impl TryFrom<Bytes> for Cip19ShelleyAddrs {
32+
type Error = &'static str;
33+
34+
fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
35+
let byte_vec: Vec<u8> = bytes.into();
36+
37+
if byte_vec.len() != 57 {
38+
return Err("Invalid length for Ed25519 public key: expected 57 bytes.");
39+
}
40+
41+
let byte_array: [u8; 57] = byte_vec
42+
.try_into()
43+
.map_err(|_| "Failed to convert Vec<u8> to [u8; 32]")?;
44+
45+
Ok(Cip19ShelleyAddrs::from(byte_array))
46+
}
47+
}
48+
49+
impl From<Cip19ShelleyAddrs> for Bytes {
50+
fn from(val: Cip19ShelleyAddrs) -> Self {
51+
let vec: Vec<u8> = val.0.to_vec();
52+
Bytes::from(vec)
53+
}
54+
}

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

Lines changed: 119 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Chain of Cardano registration data
22
3+
pub mod cip19_shelley_addr;
34
pub mod payment_history;
45
pub mod point_tx_idx;
56
pub mod role_data;
@@ -8,6 +9,7 @@ use std::{collections::HashMap, sync::Arc};
89

910
use anyhow::bail;
1011
use c509_certificate::c509::C509;
12+
use cip19_shelley_addr::Cip19ShelleyAddrs;
1113
use pallas::{
1214
codec::utils::Bytes, crypto::hash::Hash, ledger::traverse::MultiEraTx,
1315
network::miniprotocols::Point,
@@ -42,7 +44,7 @@ impl RegistrationChain {
4244
///
4345
/// # Arguments
4446
/// - `cip509` - The CIP509.
45-
/// - `tracking_payment_keys` - The list of payment keys to track.
47+
/// - `tracking_payment_keys` - The list of Shelley keys to track.
4648
/// - `point` - The point (slot) of the transaction.
4749
/// - `tx_idx` - The transaction index.
4850
/// - `txn` - The transaction.
@@ -51,7 +53,7 @@ impl RegistrationChain {
5153
///
5254
/// Returns an error if data is invalid
5355
pub fn new(
54-
&self, point: Point, tracking_payment_keys: Vec<Ed25519PublicKey>, tx_idx: usize,
56+
point: Point, tracking_payment_keys: Vec<Cip19ShelleyAddrs>, tx_idx: usize,
5557
txn: &MultiEraTx, cip509: Cip509,
5658
) -> anyhow::Result<Self> {
5759
let inner = RegistrationChainInner::new(cip509, tracking_payment_keys, point, tx_idx, txn)?;
@@ -124,15 +126,15 @@ impl RegistrationChain {
124126
&self.inner.role_data
125127
}
126128

127-
/// Get the list of payment keys to track.
129+
/// Get the list of Shelley keys to track.
128130
#[must_use]
129-
pub fn tracking_payment_keys(&self) -> &Vec<Ed25519PublicKey> {
131+
pub fn tracking_payment_keys(&self) -> &Vec<Cip19ShelleyAddrs> {
130132
&self.inner.tracking_payment_keys
131133
}
132134

133-
/// Get the map of payment key to its history.
135+
/// Get the map of tracked Shelley keys to its history.
134136
#[must_use]
135-
pub fn payment_history(&self) -> &HashMap<Ed25519PublicKey, Vec<PaymentHistory>> {
137+
pub fn payment_history(&self) -> &HashMap<Cip19ShelleyAddrs, Vec<PaymentHistory>> {
136138
&self.inner.payment_history
137139
}
138140
}
@@ -159,9 +161,9 @@ struct RegistrationChainInner {
159161
/// Map of role number to point, transaction index, and role data.
160162
role_data: HashMap<u8, (PointTxIdx, RoleData)>,
161163
/// List of payment keys to track.
162-
tracking_payment_keys: Arc<Vec<Ed25519PublicKey>>,
164+
tracking_payment_keys: Arc<Vec<Cip19ShelleyAddrs>>,
163165
/// Map of payment key to its history.
164-
payment_history: HashMap<Ed25519PublicKey, Vec<PaymentHistory>>,
166+
payment_history: HashMap<Cip19ShelleyAddrs, Vec<PaymentHistory>>,
165167
}
166168

167169
impl RegistrationChainInner {
@@ -170,7 +172,7 @@ impl RegistrationChainInner {
170172
///
171173
/// # Arguments
172174
/// - `cip509` - The CIP509.
173-
/// - `tracking_payment_keys` - The list of payment keys to track.
175+
/// - `tracking_payment_keys` - The list of Shelley keys to track.
174176
/// - `point` - The point (slot) of the transaction.
175177
/// - `tx_idx` - The transaction index.
176178
/// - `txn` - The transaction.
@@ -179,7 +181,7 @@ impl RegistrationChainInner {
179181
///
180182
/// Returns an error if data is invalid
181183
fn new(
182-
cip509: Cip509, tracking_payment_keys: Vec<Ed25519PublicKey>, point: Point, tx_idx: usize,
184+
cip509: Cip509, tracking_payment_keys: Vec<Cip19ShelleyAddrs>, point: Point, tx_idx: usize,
183185
txn: &MultiEraTx,
184186
) -> anyhow::Result<Self> {
185187
// Should be chain root, return immediately if not
@@ -189,7 +191,7 @@ impl RegistrationChainInner {
189191

190192
let mut validation_report = Vec::new();
191193
// Do the CIP509 validation, ensuring the basic validation pass.
192-
if !cip509.validate(txn, tx_idx, &mut validation_report) {
194+
if !cip509.validate(txn, &mut validation_report) {
193195
// Log out the error if any
194196
error!("CIP509 validation failed: {:?}", validation_report);
195197
bail!("CIP509 validation failed, {:?}", validation_report);
@@ -245,7 +247,7 @@ impl RegistrationChainInner {
245247

246248
let mut validation_report = Vec::new();
247249
// Do the CIP509 validation, ensuring the basic validation pass.
248-
if !cip509.validate(txn, tx_idx, &mut validation_report) {
250+
if !cip509.validate(txn, &mut validation_report) {
249251
error!("CIP509 validation failed: {:?}", validation_report);
250252
bail!("CIP509 validation failed, {:?}", validation_report);
251253
}
@@ -448,7 +450,7 @@ fn chain_root_role_data(
448450
let encryption_key = role_data.role_encryption_key.clone();
449451

450452
// Get the payment key
451-
let payment_key = get_payment_key_from_tx(txn, role_data.payment_key)?;
453+
let payment_key = get_shelley_addr_from_tx(txn, role_data.payment_key)?;
452454

453455
// Map of role number to point and role data
454456
role_data_map.insert(
@@ -496,7 +498,7 @@ fn update_role_data(
496498
}
497499
},
498500
};
499-
let payment_key = get_payment_key_from_tx(txn, role_data.payment_key)?;
501+
let payment_key = get_shelley_addr_from_tx(txn, role_data.payment_key)?;
500502

501503
// Map of role number to point and role data
502504
// Note that new role data will overwrite the old one
@@ -517,10 +519,10 @@ fn update_role_data(
517519
Ok(())
518520
}
519521

520-
/// Helper function for retrieving the payment key from the transaction.
521-
fn get_payment_key_from_tx(
522+
/// Helper function for retrieving the Shelley address from the transaction.
523+
fn get_shelley_addr_from_tx(
522524
txn: &MultiEraTx, payment_key_ref: Option<i16>,
523-
) -> anyhow::Result<Ed25519PublicKey> {
525+
) -> anyhow::Result<Cip19ShelleyAddrs> {
524526
// The index should exist since it pass the basic validation
525527
if let Some(key_ref) = payment_key_ref {
526528
if let MultiEraTx::Conway(tx) = txn {
@@ -533,11 +535,11 @@ fn get_payment_key_from_tx(
533535
pallas::ledger::primitives::conway::PseudoTransactionOutput::PostAlonzo(
534536
o,
535537
) => {
536-
let payment_key: Ed25519PublicKey =
538+
let shelley_addr: Cip19ShelleyAddrs =
537539
o.address.clone().try_into().map_err(|_| {
538-
anyhow::anyhow!("Failed to convert Vec<u8> to Ed25519PublicKey in payment key reference")
540+
anyhow::anyhow!("Failed to convert Vec<u8> to Cip19ShelleyAddrs in payment key reference")
539541
})?;
540-
return Ok(payment_key);
542+
return Ok(shelley_addr);
541543
},
542544
// Not support legacy form of transaction output
543545
pallas::ledger::primitives::conway::PseudoTransactionOutput::Legacy(_) => {
@@ -552,12 +554,12 @@ fn get_payment_key_from_tx(
552554
bail!("Unsupported payment key reference to transaction input");
553555
}
554556
}
555-
Ok(Ed25519PublicKey::default())
557+
Ok(Cip19ShelleyAddrs::default())
556558
}
557559

558560
/// Update the payment history given the tracking payment keys.
559561
fn update_payment_history(
560-
tracking_key: &Ed25519PublicKey, txn: &MultiEraTx, point_tx_idx: &PointTxIdx,
562+
tracking_key: &Cip19ShelleyAddrs, txn: &MultiEraTx, point_tx_idx: &PointTxIdx,
561563
) -> anyhow::Result<Vec<PaymentHistory>> {
562564
let mut payment_history = Vec::new();
563565
if let MultiEraTx::Conway(tx) = txn {
@@ -587,3 +589,98 @@ fn update_payment_history(
587589
}
588590
Ok(payment_history)
589591
}
592+
593+
#[cfg(test)]
594+
mod test {
595+
use minicbor::{Decode, Decoder};
596+
use pallas::{ledger::traverse::MultiEraTx, network::miniprotocols::Point};
597+
598+
use super::RegistrationChain;
599+
use crate::cardano::{cip509::Cip509, transaction::raw_aux_data::RawAuxData};
600+
601+
fn cip_509_aux_data(tx: &MultiEraTx<'_>) -> Vec<u8> {
602+
let raw_auxiliary_data = tx
603+
.as_conway()
604+
.unwrap()
605+
.clone()
606+
.auxiliary_data
607+
.map(|aux| aux.raw_cbor());
608+
609+
let raw_cbor_data = match raw_auxiliary_data {
610+
pallas::codec::utils::Nullable::Some(data) => Ok(data),
611+
_ => Err("Auxiliary data not found"),
612+
};
613+
614+
let auxiliary_data = RawAuxData::new(raw_cbor_data.expect("Failed to get raw cbor data"));
615+
auxiliary_data
616+
.get_metadata(509)
617+
.expect("Failed to get metadata")
618+
.to_vec()
619+
}
620+
621+
fn conway_1() -> Vec<u8> {
622+
hex::decode(include_str!("../../test_data/cardano/conway_1.block"))
623+
.expect("Failed to decode hex block.")
624+
}
625+
626+
fn conway_4() -> Vec<u8> {
627+
hex::decode(include_str!("../../test_data/cardano/conway_4.block"))
628+
.expect("Failed to decode hex block.")
629+
}
630+
631+
#[test]
632+
fn test_new_and_update_registration() {
633+
let conway_block_data_1 = conway_1();
634+
let point_1 = Point::new(
635+
77_429_134,
636+
hex::decode("62483f96613b4c48acd28de482eb735522ac180df61766bdb476a7bf83e7bb98")
637+
.unwrap(),
638+
);
639+
let multi_era_block_1 =
640+
pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data_1)
641+
.expect("Failed to decode MultiEraBlock");
642+
643+
let transactions_1 = multi_era_block_1.txs();
644+
// Forth transaction of this test data contains the CIP509 auxiliary data
645+
let tx_1 = transactions_1
646+
.get(3)
647+
.expect("Failed to get transaction index");
648+
649+
let aux_data_1 = cip_509_aux_data(tx_1);
650+
let mut decoder = Decoder::new(aux_data_1.as_slice());
651+
let cip509_1 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509");
652+
let tracking_payment_keys = vec![];
653+
654+
let registration_chain =
655+
RegistrationChain::new(point_1.clone(), tracking_payment_keys, 3, tx_1, cip509_1);
656+
// Able to add chain root to the registration chain
657+
assert!(registration_chain.is_ok());
658+
659+
let conway_block_data_4 = conway_4();
660+
let point_4 = Point::new(
661+
77_436_369,
662+
hex::decode("b174fc697126f05046b847d47e60d66cbedaf25240027f9c07f27150889aac24")
663+
.unwrap(),
664+
);
665+
666+
let multi_era_block_4 =
667+
pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data_4)
668+
.expect("Failed to decode MultiEraBlock");
669+
670+
let transactions_4 = multi_era_block_4.txs();
671+
// Second transaction of this test data contains the CIP509 auxiliary data
672+
let tx = transactions_4
673+
.get(1)
674+
.expect("Failed to get transaction index");
675+
676+
let aux_data_4 = cip_509_aux_data(tx);
677+
let mut decoder = Decoder::new(aux_data_4.as_slice());
678+
let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509");
679+
680+
// Update the registration chain
681+
assert!(registration_chain
682+
.unwrap()
683+
.update(point_4.clone(), 1, tx, cip509)
684+
.is_ok());
685+
}
686+
}

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
33
use std::collections::HashMap;
44

5-
use crate::cardano::cip509::rbac::{pub_key::Ed25519PublicKey, role_data::KeyLocalRef};
5+
use super::cip19_shelley_addr::Cip19ShelleyAddrs;
6+
use crate::cardano::cip509::rbac::role_data::KeyLocalRef;
67

78
/// Role data
89
#[derive(Clone)]
@@ -11,8 +12,8 @@ pub struct RoleData {
1112
signing_key_ref: Option<KeyLocalRef>,
1213
/// An encryption keys to the data within registration.
1314
encryption_ref: Option<KeyLocalRef>,
14-
/// A payment key where reward will be distributed to.
15-
payment_key: Ed25519PublicKey,
15+
/// A payment key (Shelley address) where reward will be distributed to.
16+
payment_key: Cip19ShelleyAddrs,
1617
/// Map of role extended data (10-99) to its data
1718
role_extended_data: HashMap<u8, Vec<u8>>,
1819
}
@@ -21,7 +22,7 @@ impl RoleData {
2122
/// Create an instance of role data.
2223
pub(crate) fn new(
2324
signing_key_ref: Option<KeyLocalRef>, encryption_ref: Option<KeyLocalRef>,
24-
payment_key: Ed25519PublicKey, role_extended_data: HashMap<u8, Vec<u8>>,
25+
payment_key: Cip19ShelleyAddrs, role_extended_data: HashMap<u8, Vec<u8>>,
2526
) -> Self {
2627
RoleData {
2728
signing_key_ref,
@@ -45,7 +46,7 @@ impl RoleData {
4546

4647
/// Get the payment key.
4748
#[must_use]
48-
pub fn payment_key(&self) -> &Ed25519PublicKey {
49+
pub fn payment_key(&self) -> &Cip19ShelleyAddrs {
4950
&self.payment_key
5051
}
5152

0 commit comments

Comments
 (0)