Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion rust/rbac-registration/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ thiserror = "2.0.11"

c509-certificate = { version = "0.0.3", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "c509-certificate-v0.0.3" }
cbork-utils = { version = "0.0.2", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "cbork-utils-v0.0.2" }
cardano-blockchain-types = { version = "0.0.8", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "cardano-blockchain-types/v0.0.8" }
cardano-blockchain-types = { version = "0.0.9", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "cardano-blockchain-types/v0.0.9" }
catalyst-types = { version = "0.0.10", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "catalyst-types/v0.0.10" }
70 changes: 49 additions & 21 deletions rust/rbac-registration/src/cardano/cip509/cip509.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use catalyst_types::{
uuid::UuidV4,
};
use cbork_utils::decode_helper::{decode_bytes, decode_helper, decode_map_len};
use ed25519_dalek::VerifyingKey;
use minicbor::{
decode::{self},
Decode, Decoder,
Expand All @@ -32,6 +33,7 @@ use uuid::Uuid;

use crate::cardano::cip509::{
decode_context::DecodeContext,
extract_key,
rbac::Cip509RbacMetadata,
types::{PaymentHistory, TxInputHash, ValidationSignature},
utils::Cip0134UriSet,
Expand All @@ -40,7 +42,7 @@ use crate::cardano::cip509::{
validate_txn_inputs_hash,
},
x509_chunks::X509Chunks,
Payment, PointTxnIdx, RoleData,
C509Cert, LocalRefInt, Payment, PointTxnIdx, RoleData, SimplePublicKeyType, X509DerCert,
};

/// A x509 metadata envelope.
Expand Down Expand Up @@ -227,6 +229,48 @@ impl Cip509 {
self.metadata.as_ref().and_then(|m| m.role_data.get(&role))
}

/// Returns signing public key for a role.
/// Would return only signing public keys for the present ceritificates,
/// if certificate marked as deleted or undefined it would be skipped.
#[must_use]
pub(crate) fn signing_pk_for_role(
&self,
role: RoleId,
) -> Option<VerifyingKey> {
self.metadata.as_ref().and_then(|m| {
let key_ref = m.role_data.get(&role).and_then(|d| d.signing_key())?;
match key_ref.local_ref {
LocalRefInt::X509Certs => {
m.x509_certs.get(key_ref.key_offset).and_then(|c| {
if let X509DerCert::X509Cert(c) = c {
extract_key::x509_key(&c).ok()
} else {
None
}
})
},
LocalRefInt::C509Certs => {
m.c509_certs.get(key_ref.key_offset).and_then(|c| {
if let C509Cert::C509Certificate(c) = c {
extract_key::c509_key(&c).ok()
} else {
None
}
})
},
LocalRefInt::PubKeys => {
m.pub_keys.get(key_ref.key_offset).and_then(|c| {
if let SimplePublicKeyType::Ed25519(c) = c {
Some(c.clone())
} else {
None
}
})
},
}
})
}

/// Returns a purpose of this registration.
#[must_use]
pub fn purpose(&self) -> Option<UuidV4> {
Expand Down Expand Up @@ -313,26 +357,10 @@ impl Cip509 {
.unwrap_or_default()
}

/// Returns `Cip509` fields consuming the structure if it was successfully decoded and
/// validated otherwise return the problem report that contains all the encountered
/// issues.
///
/// # Errors
///
/// - `Err(ProblemReport)`
pub fn consume(self) -> Result<(UuidV4, Cip509RbacMetadata, PaymentHistory), ProblemReport> {
match (
self.purpose,
self.txn_inputs_hash,
self.metadata,
self.validation_signature,
) {
(Some(purpose), Some(_), Some(metadata), Some(_)) if !self.report.is_problematic() => {
Ok((purpose, metadata, self.payment_history))
},

_ => Err(self.report),
}
/// Returns a payment history map.
#[must_use]
pub fn payment_history(&self) -> &PaymentHistory {
&self.payment_history
}
}

Expand Down
73 changes: 65 additions & 8 deletions rust/rbac-registration/src/cardano/cip509/utils/cip134_uri_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ struct Cip0134UriSetInner {
x_uris: UrisMap,
/// URIs from c509 certificates.
c_uris: UrisMap,
/// URIs which are taken by another certificates.
taken_uris: HashSet<Cip0134Uri>,
}

impl Cip0134UriSet {
Expand All @@ -51,7 +53,12 @@ impl Cip0134UriSet {
) -> Self {
let x_uris = extract_x509_uris(x509_certs, report);
let c_uris = extract_c509_uris(c509_certs, report);
Self(Arc::new(Cip0134UriSetInner { x_uris, c_uris }))
let taken_uris = HashSet::new();
Self(Arc::new(Cip0134UriSetInner {
x_uris,
c_uris,
taken_uris,
}))
}

/// Returns a mapping from the x509 certificate index to URIs contained within.
Expand All @@ -66,6 +73,15 @@ impl Cip0134UriSet {
&self.0.c_uris
}

/// Returns an iterator over of active (without taken) `Cip0134Uri`.
pub(crate) fn values(&self) -> impl Iterator<Item = &Cip0134Uri> {
self.x_uris()
.values()
.chain(self.c_uris().values())
.flat_map(|uris| uris.iter())
.filter(|v| !self.0.taken_uris.contains(v))
}

/// Returns `true` if both x509 and c509 certificate maps are empty.
#[must_use]
pub fn is_empty(&self) -> bool {
Expand All @@ -90,7 +106,7 @@ impl Cip0134UriSet {
result
}

/// Returns a list of stake addresses by the given role.
/// Returns a set of stake addresses by the given role.
#[must_use]
pub fn role_stake_addresses(
&self,
Expand All @@ -107,13 +123,10 @@ impl Cip0134UriSet {
.collect()
}

/// Returns a list of all stake addresses.
/// Returns a set of all stake addresses.
#[must_use]
pub fn stake_addresses(&self) -> HashSet<StakeAddress> {
self.x_uris()
.values()
.chain(self.c_uris().values())
.flat_map(|uris| uris.iter())
self.values()
.filter_map(|uri| {
match uri.address() {
Address::Stake(a) => Some(a.clone().into()),
Expand Down Expand Up @@ -154,6 +167,7 @@ impl Cip0134UriSet {
let Cip0134UriSetInner {
mut x_uris,
mut c_uris,
mut taken_uris,
} = Arc::unwrap_or_clone(self.0);

for (index, cert) in metadata.x509_certs.iter().enumerate() {
Expand All @@ -166,6 +180,9 @@ impl Cip0134UriSet {
},
X509DerCert::X509Cert(_) => {
if let Some(uris) = metadata.certificate_uris.x_uris().get(&index) {
uris.iter().for_each(|v| {
taken_uris.remove(v);
});
x_uris.insert(index, uris.clone());
}
},
Expand All @@ -185,13 +202,53 @@ impl Cip0134UriSet {
},
C509Cert::C509Certificate(_) => {
if let Some(uris) = metadata.certificate_uris.c_uris().get(&index) {
uris.iter().for_each(|v| {
taken_uris.remove(v);
});
c_uris.insert(index, uris.clone());
}
},
}
}

Self(Arc::new(Cip0134UriSetInner { x_uris, c_uris }))
Self(Arc::new(Cip0134UriSetInner {
x_uris,
c_uris,
taken_uris,
}))
}

/// Return the updated URIs set where the provided URIs were taken by other
/// registration chains.
///
/// Updates the current URI set by removing the taken URIs from it.
#[must_use]
pub fn update_taken_uris(
self,
reg: &Cip509RbacMetadata,
) -> Self {
let current_uris_set = self.values().collect::<HashSet<_>>();

let latest_taken_uris = reg
.certificate_uris
.values()
.filter(|v| current_uris_set.contains(v))
.cloned()
.collect::<Vec<_>>();

let Cip0134UriSetInner {
x_uris,
c_uris,
mut taken_uris,
} = Arc::unwrap_or_clone(self.0);

taken_uris.extend(latest_taken_uris);

Self(Arc::new(Cip0134UriSetInner {
x_uris,
c_uris,
taken_uris,
}))
}
}

Expand Down
18 changes: 5 additions & 13 deletions rust/rbac-registration/src/cardano/cip509/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,7 @@ fn extract_stake_addresses(uris: Option<&Cip0134UriSet>) -> Vec<(VKeyHash, Strin
return Vec::new();
};

uris.x_uris()
.iter()
.chain(uris.c_uris())
.flat_map(|(_index, uris)| uris.iter())
uris.values()
.filter_map(|uri| {
if let Address::Stake(a) = uri.address() {
let bech32 = uri.address().to_string();
Expand All @@ -185,10 +182,7 @@ fn extract_payment_addresses(uris: Option<&Cip0134UriSet>) -> Vec<(VKeyHash, Str
return Vec::new();
};

uris.x_uris()
.iter()
.chain(uris.c_uris())
.flat_map(|(_index, uris)| uris.iter())
uris.values()
.filter_map(|uri| {
if let Address::Shelley(a) = uri.address() {
match a.payment() {
Expand Down Expand Up @@ -593,8 +587,7 @@ mod tests {
assert_eq!(origin.txn_index(), data.txn_index);
assert_eq!(origin.point().slot_or_default(), data.slot);

// The consume function must return the problem report contained within the registration.
let report = registration.consume().unwrap_err();
let report = registration.report();
assert!(report.is_problematic());
let report = format!("{report:?}");
assert!(report.contains("is not present in the transaction witness set, and can not be verified as owned and spendable"));
Expand All @@ -616,7 +609,7 @@ mod tests {
assert_eq!(origin.txn_index(), data.txn_index);
assert_eq!(origin.point().slot_or_default(), data.slot);

let report = registration.consume().unwrap_err();
let report = registration.report();
assert!(report.is_problematic());
let report = format!("{report:?}");
assert!(report
Expand All @@ -637,8 +630,7 @@ mod tests {
assert_eq!(origin.txn_index(), data.txn_index);
assert_eq!(origin.point().slot_or_default(), data.slot);

// The consume function must return the problem report contained within the registration.
let report = registration.consume().unwrap_err();
let report = registration.report();
assert!(report.is_problematic());
let report = format!("{report:?}");
assert!(report.contains("Unknown role found: 4"));
Expand Down
1 change: 1 addition & 0 deletions rust/rbac-registration/src/cardano/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Cardano module

pub mod cip509;
pub mod state;
54 changes: 54 additions & 0 deletions rust/rbac-registration/src/cardano/state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//! Cardano RBAC state traits, which are used during different statefull validation
//! procedures.
use std::future::Future;

use cardano_blockchain_types::{hashes::TransactionId, StakeAddress};
use catalyst_types::catalyst_id::CatalystId;
use ed25519_dalek::VerifyingKey;

use crate::registration::cardano::RegistrationChain;

/// RBAC chains state trait
pub trait RBACState {
/// Returns RBAC chain for the given Catalyst ID.
fn chain(
&self,
id: &CatalystId,
) -> impl Future<Output = anyhow::Result<Option<RegistrationChain>>> + Send;

/// Returns `true` if a RBAC chain with the given Catalyst ID already exists.
fn is_chain_known(
&self,
id: &CatalystId,
) -> impl Future<Output = anyhow::Result<bool>> + Send;

/// Returns a current valid RBAC chain Catalyst ID corresponding to the given stake
/// address.
fn chain_catalyst_id_from_stake_address(
&self,
address: &StakeAddress,
) -> impl Future<Output = anyhow::Result<Option<CatalystId>>> + Send;

/// Returns a corresponding to the RBAC chain's Catalyst ID corresponding by the given
/// public key.
fn chain_catalyst_id_from_public_key(
&self,
key: &VerifyingKey,
) -> impl Future<Output = anyhow::Result<Option<CatalystId>>> + Send;

/// Returns a corresponding to the RBAC chain's Catalyst ID corresponding by the given
/// transaction hash.
fn chain_catalyst_id_from_txn_id(
&self,
txn_id: &TransactionId,
) -> impl Future<Output = anyhow::Result<Option<CatalystId>>> + Send;

/// Update the update by "taking" the given `StakeAddress` for the correspoding RBAC
/// chain's by the given `CatalystId`.
fn take_stake_address_from_chain(
&mut self,
id: &CatalystId,
address: &StakeAddress,
) -> impl Future<Output = anyhow::Result<()>> + Send;
}
1 change: 0 additions & 1 deletion rust/rbac-registration/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! This crate provides functionalities for RBAC registration.

pub mod cardano;
pub mod providers;
pub mod registration;

mod utils;
Loading
Loading