Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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" }
96 changes: 60 additions & 36 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 @@ -80,7 +82,8 @@ pub struct Cip509 {
origin: PointTxnIdx,
/// A catalyst ID.
///
/// This field is only present in role 0 registrations.
/// This field is only present in role 0 registrations and only for the first
/// registration, which defines a `CatalystId` for the chain.
catalyst_id: Option<CatalystId>,
/// Raw aux data associated with the transaction that CIP509 is attached to,
raw_aux_data: Vec<u8>,
Expand Down Expand Up @@ -180,6 +183,12 @@ impl Cip509 {
validate_self_sign_cert(metadata, &report);
}

// We want to keep `catalyst_id` field only for the first registration,
// which starts a new chain
if cip509.prv_tx_id.is_some() {
cip509.catalyst_id = None;
}

Ok(Some(cip509))
}

Expand Down Expand Up @@ -227,6 +236,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 certificates,
/// if certificate marked as deleted or undefined it would be skipped.
#[must_use]
pub 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)
} else {
None
}
})
},
}
})
}

/// Returns a purpose of this registration.
#[must_use]
pub fn purpose(&self) -> Option<UuidV4> {
Expand Down Expand Up @@ -259,7 +310,7 @@ impl Cip509 {

/// Returns URIs contained in both x509 and c509 certificates of `Cip509` metadata.
#[must_use]
pub fn certificate_uris(&self) -> Option<&Cip0134UriSet> {
pub(crate) fn certificate_uris(&self) -> Option<&Cip0134UriSet> {
self.metadata.as_ref().map(|m| &m.certificate_uris)
}

Expand All @@ -269,24 +320,13 @@ impl Cip509 {
self.txn_inputs_hash.as_ref()
}

/// Returns a Catalyst ID of this registration if role 0 is present.
/// Returns a Catalyst ID of this registration if role 0 is present and if its a first
/// registration, which defines a `CatalystId` for the chain.
#[must_use]
pub fn catalyst_id(&self) -> Option<&CatalystId> {
self.catalyst_id.as_ref()
}

/// Returns a list of addresses extracted from certificate URIs of a specific role.
#[must_use]
pub fn certificate_addresses(
&self,
role: usize,
) -> HashSet<Address> {
self.metadata
.as_ref()
.map(|m| m.certificate_uris.role_addresses(role))
.unwrap_or_default()
}

/// Return validation signature.
#[must_use]
pub fn validation_signature(&self) -> Option<&ValidationSignature> {
Expand All @@ -313,26 +353,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
107 changes: 82 additions & 25 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,87 +39,100 @@ struct Cip0134UriSetInner {
x_uris: UrisMap,
/// URIs from c509 certificates.
c_uris: UrisMap,
/// `StakeAddress` which are taken by another chains.
taken_stake_addresses: HashSet<StakeAddress>,
}

impl Cip0134UriSet {
/// Creates a new `Cip0134UriSet` instance from the given certificates.
#[must_use]
pub fn new(
pub(crate) fn new(
x509_certs: &[X509DerCert],
c509_certs: &[C509Cert],
report: &ProblemReport,
) -> 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_stake_addresses = HashSet::new();
Self(Arc::new(Cip0134UriSetInner {
x_uris,
c_uris,
taken_stake_addresses,
}))
}

/// Returns a mapping from the x509 certificate index to URIs contained within.
#[must_use]
pub fn x_uris(&self) -> &UrisMap {
pub(crate) fn x_uris(&self) -> &UrisMap {
&self.0.x_uris
}

/// Returns a mapping from the c509 certificate index to URIs contained within.
#[must_use]
pub fn c_uris(&self) -> &UrisMap {
pub(crate) fn c_uris(&self) -> &UrisMap {
&self.0.c_uris
}

/// Returns an iterator over of `Cip0134Uri`.
pub(crate) fn values(&self) -> impl Iterator<Item = &Cip0134Uri> {
self.x_uris()
.values()
.chain(self.c_uris().values())
.flat_map(|uris| uris.iter())
}

/// Returns `true` if both x509 and c509 certificate maps are empty.
#[must_use]
pub fn is_empty(&self) -> bool {
#[cfg(test)]
pub(crate) fn is_empty(&self) -> bool {
self.x_uris().is_empty() && self.c_uris().is_empty()
}

/// Returns a list of addresses by the given role.
/// Returns a list of URIs by the given role.
#[must_use]
pub fn role_addresses(
pub(crate) fn role_uris(
&self,
role: usize,
) -> HashSet<Address> {
) -> HashSet<Cip0134Uri> {
let mut result = HashSet::new();

if let Some(uris) = self.x_uris().get(&role) {
result.extend(uris.iter().map(|uri| uri.address().clone()));
result.extend(uris.iter().cloned());
}
if let Some(uris) = self.c_uris().get(&role) {
result.extend(uris.iter().map(|uri| uri.address().clone()));
result.extend(uris.iter().cloned());
}

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(
pub(crate) fn role_stake_addresses(
&self,
role: usize,
) -> HashSet<StakeAddress> {
self.role_addresses(role)
self.role_uris(role)
.iter()
.filter_map(|address| {
match address {
.filter_map(|uri| {
match uri.address() {
Address::Stake(a) => Some(a.clone().into()),
_ => None,
}
})
.collect()
}

/// Returns a list of all stake addresses.
/// Returns a set of all active (without taken) 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())
pub(crate) fn stake_addresses(&self) -> HashSet<StakeAddress> {
self.values()
.filter_map(|uri| {
match uri.address() {
Address::Stake(a) => Some(a.clone().into()),
_ => None,
}
})
.filter(|v| !self.0.taken_stake_addresses.contains(v))
.collect()
}

Expand All @@ -142,7 +155,7 @@ impl Cip0134UriSet {
/// 2: [uri_4]
/// ```
#[must_use]
pub fn update(
pub(crate) fn update(
self,
metadata: &Cip509RbacMetadata,
) -> Self {
Expand All @@ -154,6 +167,7 @@ impl Cip0134UriSet {
let Cip0134UriSetInner {
mut x_uris,
mut c_uris,
mut taken_stake_addresses,
} = Arc::unwrap_or_clone(self.0);

for (index, cert) in metadata.x509_certs.iter().enumerate() {
Expand Down Expand Up @@ -191,7 +205,50 @@ impl Cip0134UriSet {
}
}

Self(Arc::new(Cip0134UriSetInner { x_uris, c_uris }))
metadata
.certificate_uris
.stake_addresses()
.iter()
.for_each(|v| {
taken_stake_addresses.remove(v);
});

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

/// Return the updated URIs set where the provided URIs were taken by other
/// registration chains.
///
/// Updates the current URI set by marking URIs as taken.
#[must_use]
pub(crate) fn update_taken_uris(
self,
reg: &Cip509RbacMetadata,
) -> Self {
let current_stake_addresses = self.stake_addresses();
let latest_taken_stake_addresses = reg
.certificate_uris
.stake_addresses()
.into_iter()
.filter(|v| current_stake_addresses.contains(v));

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

taken_stake_addresses.extend(latest_taken_stake_addresses);

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

Expand Down Expand Up @@ -344,7 +401,7 @@ mod tests {
let set = cip509.certificate_uris().unwrap();
assert!(!set.is_empty());
assert!(set.c_uris().is_empty());
assert_eq!(set.role_addresses(0).len(), 1);
assert_eq!(set.role_uris(0).len(), 1);
assert_eq!(set.role_stake_addresses(0).len(), 1);
assert_eq!(set.stake_addresses().len(), 1);

Expand Down
Loading
Loading