Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(rust/rbac-registration): Tracking payment key type #93

Merged
merged 10 commits into from
Dec 2, 2024
202 changes: 149 additions & 53 deletions rust/rbac-registration/src/registration/cardano/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ use std::{collections::HashMap, sync::Arc};
use anyhow::bail;
use c509_certificate::c509::C509;
use pallas::{
codec::utils::Bytes, crypto::hash::Hash, ledger::traverse::MultiEraTx,
crypto::hash::Hash,
ledger::{
addresses::{Address, ShelleyAddress, ShelleyPaymentPart},
traverse::MultiEraTx,
},
network::miniprotocols::Point,
};
use payment_history::PaymentHistory;
Expand Down Expand Up @@ -51,8 +55,8 @@ impl RegistrationChain {
///
/// Returns an error if data is invalid
pub fn new(
&self, point: Point, tracking_payment_keys: Vec<Ed25519PublicKey>, tx_idx: usize,
txn: &MultiEraTx, cip509: Cip509,
point: Point, tracking_payment_keys: &[ShelleyAddress], tx_idx: usize, txn: &MultiEraTx,
cip509: Cip509,
) -> anyhow::Result<Self> {
let inner = RegistrationChainInner::new(cip509, tracking_payment_keys, point, tx_idx, txn)?;

Expand Down Expand Up @@ -124,16 +128,10 @@ impl RegistrationChain {
&self.inner.role_data
}

/// Get the list of payment keys to track.
/// Get the map of tracked payment keys to its history.
#[must_use]
pub fn tracking_payment_keys(&self) -> &Vec<Ed25519PublicKey> {
&self.inner.tracking_payment_keys
}

/// Get the map of payment key to its history.
#[must_use]
pub fn payment_history(&self) -> &HashMap<Ed25519PublicKey, Vec<PaymentHistory>> {
&self.inner.payment_history
pub fn tracking_payment_history(&self) -> &HashMap<ShelleyAddress, Vec<PaymentHistory>> {
&self.inner.tracking_payment_history
}
}

Expand All @@ -158,10 +156,8 @@ struct RegistrationChainInner {
// Role
/// Map of role number to point, transaction index, and role data.
role_data: HashMap<u8, (PointTxIdx, RoleData)>,
/// List of payment keys to track.
tracking_payment_keys: Arc<Vec<Ed25519PublicKey>>,
/// Map of payment key to its history.
payment_history: HashMap<Ed25519PublicKey, Vec<PaymentHistory>>,
/// Map of tracked payment key to its history.
tracking_payment_history: HashMap<ShelleyAddress, Vec<PaymentHistory>>,
}

impl RegistrationChainInner {
Expand All @@ -179,7 +175,7 @@ impl RegistrationChainInner {
///
/// Returns an error if data is invalid
fn new(
cip509: Cip509, tracking_payment_keys: Vec<Ed25519PublicKey>, point: Point, tx_idx: usize,
cip509: Cip509, tracking_payment_keys: &[ShelleyAddress], point: Point, tx_idx: usize,
txn: &MultiEraTx,
) -> anyhow::Result<Self> {
// Should be chain root, return immediately if not
Expand Down Expand Up @@ -207,12 +203,13 @@ impl RegistrationChainInner {
let revocations = revocations_list(registration.revocation_list, &point_tx_idx);
let role_data_map = chain_root_role_data(registration.role_set, txn, &point_tx_idx)?;

let mut payment_history = HashMap::new();
for tracking_key in &tracking_payment_keys {
// Keep record of payment history, the payment key that we want to track
let histories = update_payment_history(tracking_key, txn, &point_tx_idx)?;
payment_history.insert(tracking_key.clone(), histories);
let mut tracking_payment_history = HashMap::new();
// Create a payment history for each tracking payment key
for tracking_key in tracking_payment_keys {
tracking_payment_history.insert(tracking_key.clone(), Vec::new());
}
// Keep record of payment history, the payment key that we want to track
update_tracking_payment_history(&mut tracking_payment_history, txn, &point_tx_idx)?;

Ok(Self {
purpose,
Expand All @@ -222,8 +219,7 @@ impl RegistrationChainInner {
simple_keys: public_key_map,
revocations,
role_data: role_data_map,
tracking_payment_keys: Arc::new(tracking_payment_keys),
payment_history,
tracking_payment_history,
})
}

Expand Down Expand Up @@ -280,16 +276,11 @@ impl RegistrationChainInner {

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

for tracking_key in self.tracking_payment_keys.iter() {
let histories = update_payment_history(tracking_key, txn, &point_tx_idx)?;
// If tracking payment key doesn't exist, insert an empty vector,
// then add the histories to the history vector
new_inner
.payment_history
.entry(tracking_key.clone())
.or_default()
.extend(histories);
}
update_tracking_payment_history(
&mut new_inner.tracking_payment_history,
txn,
&point_tx_idx,
)?;

Ok(new_inner)
}
Expand Down Expand Up @@ -448,7 +439,7 @@ fn chain_root_role_data(
let encryption_key = role_data.role_encryption_key.clone();

// Get the payment key
let payment_key = get_payment_key_from_tx(txn, role_data.payment_key)?;
let payment_key = get_payment_addr_from_tx(txn, role_data.payment_key)?;

// Map of role number to point and role data
role_data_map.insert(
Expand Down Expand Up @@ -496,7 +487,7 @@ fn update_role_data(
}
},
};
let payment_key = get_payment_key_from_tx(txn, role_data.payment_key)?;
let payment_key = get_payment_addr_from_tx(txn, role_data.payment_key)?;

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

/// Helper function for retrieving the payment key from the transaction.
fn get_payment_key_from_tx(
/// Helper function for retrieving the Shelley address from the transaction.
fn get_payment_addr_from_tx(
txn: &MultiEraTx, payment_key_ref: Option<i16>,
) -> anyhow::Result<Ed25519PublicKey> {
) -> anyhow::Result<Option<ShelleyPaymentPart>> {
// The index should exist since it pass the basic validation
if let Some(key_ref) = payment_key_ref {
if let MultiEraTx::Conway(tx) = txn {
Expand All @@ -533,11 +524,13 @@ fn get_payment_key_from_tx(
pallas::ledger::primitives::conway::PseudoTransactionOutput::PostAlonzo(
o,
) => {
let payment_key: Ed25519PublicKey =
o.address.clone().try_into().map_err(|_| {
anyhow::anyhow!("Failed to convert Vec<u8> to Ed25519PublicKey in payment key reference")
})?;
return Ok(payment_key);
let address =
Address::from_bytes(&o.address).map_err(|e| anyhow::anyhow!(e))?;

if let Address::Shelley(addr) = address {
return Ok(Some(addr.payment().clone()));
}
bail!("Unsupported address type in payment key reference");
},
// Not support legacy form of transaction output
pallas::ledger::primitives::conway::PseudoTransactionOutput::Legacy(_) => {
Expand All @@ -552,26 +545,34 @@ fn get_payment_key_from_tx(
bail!("Unsupported payment key reference to transaction input");
}
}
Ok(Ed25519PublicKey::default())
Ok(None)
}

/// Update the payment history given the tracking payment keys.
fn update_payment_history(
tracking_key: &Ed25519PublicKey, txn: &MultiEraTx, point_tx_idx: &PointTxIdx,
) -> anyhow::Result<Vec<PaymentHistory>> {
let mut payment_history = Vec::new();
fn update_tracking_payment_history(
tracking_payment_history: &mut HashMap<ShelleyAddress, Vec<PaymentHistory>>, txn: &MultiEraTx,
point_tx_idx: &PointTxIdx,
) -> anyhow::Result<()> {
if let MultiEraTx::Conway(tx) = txn {
// Conway era -> Post alonzo tx output
for (index, output) in tx.transaction_body.outputs.iter().enumerate() {
match output {
pallas::ledger::primitives::conway::PseudoTransactionOutput::PostAlonzo(o) => {
let address_bytes: Bytes = tracking_key.clone().into();
if address_bytes == o.address {
let address =
Address::from_bytes(&o.address).map_err(|e| anyhow::anyhow!(e))?;
let shelley_payment = if let Address::Shelley(addr) = address {
addr.clone()
} else {
bail!("Unsupported address type in update payment history");
};
// If the payment key from the output exist in the payment history, add the
// history
if let Some(vec) = tracking_payment_history.get_mut(&shelley_payment) {
let output_index: u16 = index.try_into().map_err(|_| {
anyhow::anyhow!("Cannot convert usize to u16 in update payment history")
})?;

payment_history.push(PaymentHistory::new(
vec.push(PaymentHistory::new(
point_tx_idx.clone(),
txn.hash(),
output_index,
Expand All @@ -585,5 +586,100 @@ fn update_payment_history(
}
}
}
Ok(payment_history)
Ok(())
}

#[cfg(test)]
mod test {
use minicbor::{Decode, Decoder};
use pallas::{ledger::traverse::MultiEraTx, network::miniprotocols::Point};

use super::RegistrationChain;
use crate::cardano::{cip509::Cip509, transaction::raw_aux_data::RawAuxData};

fn cip_509_aux_data(tx: &MultiEraTx<'_>) -> Vec<u8> {
let raw_auxiliary_data = tx
.as_conway()
.unwrap()
.clone()
.auxiliary_data
.map(|aux| aux.raw_cbor());

let raw_cbor_data = match raw_auxiliary_data {
pallas::codec::utils::Nullable::Some(data) => Ok(data),
_ => Err("Auxiliary data not found"),
};

let auxiliary_data = RawAuxData::new(raw_cbor_data.expect("Failed to get raw cbor data"));
auxiliary_data
.get_metadata(509)
.expect("Failed to get metadata")
.to_vec()
}

fn conway_1() -> Vec<u8> {
hex::decode(include_str!("../../test_data/cardano/conway_1.block"))
.expect("Failed to decode hex block.")
}

fn conway_4() -> Vec<u8> {
hex::decode(include_str!("../../test_data/cardano/conway_4.block"))
.expect("Failed to decode hex block.")
}

#[test]
fn test_new_and_update_registration() {
let conway_block_data_1 = conway_1();
let point_1 = Point::new(
77_429_134,
hex::decode("62483f96613b4c48acd28de482eb735522ac180df61766bdb476a7bf83e7bb98")
.unwrap(),
);
let multi_era_block_1 =
pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data_1)
.expect("Failed to decode MultiEraBlock");

let transactions_1 = multi_era_block_1.txs();
// Forth transaction of this test data contains the CIP509 auxiliary data
let tx_1 = transactions_1
.get(3)
.expect("Failed to get transaction index");

let aux_data_1 = cip_509_aux_data(tx_1);
let mut decoder = Decoder::new(aux_data_1.as_slice());
let cip509_1 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509");
let tracking_payment_keys = vec![];

let registration_chain =
RegistrationChain::new(point_1.clone(), &tracking_payment_keys, 3, tx_1, cip509_1);
// Able to add chain root to the registration chain
assert!(registration_chain.is_ok());

let conway_block_data_4 = conway_4();
let point_4 = Point::new(
77_436_369,
hex::decode("b174fc697126f05046b847d47e60d66cbedaf25240027f9c07f27150889aac24")
.unwrap(),
);

let multi_era_block_4 =
pallas::ledger::traverse::MultiEraBlock::decode(&conway_block_data_4)
.expect("Failed to decode MultiEraBlock");

let transactions_4 = multi_era_block_4.txs();
// Second transaction of this test data contains the CIP509 auxiliary data
let tx = transactions_4
.get(1)
.expect("Failed to get transaction index");

let aux_data_4 = cip_509_aux_data(tx);
let mut decoder = Decoder::new(aux_data_4.as_slice());
let cip509 = Cip509::decode(&mut decoder, &mut ()).expect("Failed to decode Cip509");

// Update the registration chain
assert!(registration_chain
.unwrap()
.update(point_4.clone(), 1, tx, cip509)
.is_ok());
}
}
10 changes: 6 additions & 4 deletions rust/rbac-registration/src/registration/cardano/role_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

use std::collections::HashMap;

use crate::cardano::cip509::rbac::{pub_key::Ed25519PublicKey, role_data::KeyLocalRef};
use pallas::ledger::addresses::ShelleyPaymentPart;

use crate::cardano::cip509::rbac::role_data::KeyLocalRef;

/// Role data
#[derive(Clone)]
Expand All @@ -12,7 +14,7 @@ pub struct RoleData {
/// An encryption keys to the data within registration.
encryption_ref: Option<KeyLocalRef>,
/// A payment key where reward will be distributed to.
payment_key: Ed25519PublicKey,
payment_key: Option<ShelleyPaymentPart>,
/// Map of role extended data (10-99) to its data
role_extended_data: HashMap<u8, Vec<u8>>,
}
Expand All @@ -21,7 +23,7 @@ impl RoleData {
/// Create an instance of role data.
pub(crate) fn new(
signing_key_ref: Option<KeyLocalRef>, encryption_ref: Option<KeyLocalRef>,
payment_key: Ed25519PublicKey, role_extended_data: HashMap<u8, Vec<u8>>,
payment_key: Option<ShelleyPaymentPart>, role_extended_data: HashMap<u8, Vec<u8>>,
) -> Self {
RoleData {
signing_key_ref,
Expand All @@ -45,7 +47,7 @@ impl RoleData {

/// Get the payment key.
#[must_use]
pub fn payment_key(&self) -> &Ed25519PublicKey {
pub fn payment_key(&self) -> &Option<ShelleyPaymentPart> {
&self.payment_key
}

Expand Down
Loading