Skip to content

Commit fd99222

Browse files
committed
tmp
1 parent 5c88927 commit fd99222

File tree

3 files changed

+147
-165
lines changed

3 files changed

+147
-165
lines changed

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use cardano_blockchain_types::{
1313
pallas_addresses::{Address, ShelleyAddress},
1414
pallas_primitives::{conway, Nullable},
1515
pallas_traverse::MultiEraTx,
16-
MetadatumLabel, MultiEraBlock, TxnIndex,
16+
MetadatumLabel, MultiEraBlock, StakeAddress, TxnIndex,
1717
};
1818
use catalyst_types::{
1919
catalyst_id::{role_index::RoleId, CatalystId},
@@ -305,6 +305,14 @@ impl Cip509 {
305305
self.metadata.as_ref()
306306
}
307307

308+
/// Returns a set of stake addresses.
309+
#[must_use]
310+
pub fn stake_addresses(&self) -> HashSet<StakeAddress> {
311+
self.certificate_uris()
312+
.map(Cip0134UriSet::stake_addresses)
313+
.unwrap_or_default()
314+
}
315+
308316
/// Returns `Cip509` fields consuming the structure if it was successfully decoded and
309317
/// validated otherwise return the problem report that contains all the encountered
310318
/// issues.

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

Lines changed: 134 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,13 @@ use update_rbac::{
2323
};
2424
use x509_cert::certificate::Certificate as X509Certificate;
2525

26-
use crate::cardano::cip509::{
27-
CertKeyHash, CertOrPk, Cip0134UriSet, Cip509, PaymentHistory, PointData, RoleData,
28-
RoleDataRecord, ValidationSignature,
26+
use crate::{
27+
cardano::cip509::{
28+
CertKeyHash, CertOrPk, Cip0134UriSet, Cip509, PaymentHistory, PointData, RoleData,
29+
RoleDataRecord, ValidationSignature,
30+
},
31+
providers::RbacRegistrationProvider,
32+
registration::cardano::validation::RbacValidationError,
2933
};
3034

3135
/// Registration chains.
@@ -84,6 +88,47 @@ impl RegistrationChain {
8488
})
8589
}
8690

91+
/// Validates that none of the signing keys in a given RBAC registration chain
92+
/// have been used by any other existing chain, ensuring global key uniqueness
93+
/// across all Catalyst registrations.
94+
///
95+
/// # Returns
96+
/// Returns a [`Result<HashSet<VerifyingKey>>`] containing all unique public keys
97+
/// extracted from the registration chain if validation passes successfully.
98+
///
99+
/// # Errors
100+
/// - Propagates any I/O or provider-level errors encountered while checking key
101+
/// ownership (e.g., database lookup failures).
102+
pub async fn validate_public_keys<Provider>(
103+
&self,
104+
report: &ProblemReport,
105+
provider: &Provider,
106+
) -> Result<HashSet<VerifyingKey>, RbacValidationError>
107+
where
108+
Provider: RbacRegistrationProvider,
109+
{
110+
let mut keys = HashSet::new();
111+
112+
let roles: Vec<_> = self.role_data_history().keys().collect();
113+
let catalyst_id = self.catalyst_id().as_short_id();
114+
115+
for role in roles {
116+
if let Some((key, _)) = self.get_latest_signing_pk_for_role(role) {
117+
keys.insert(key);
118+
if let Some(previous) = provider.catalyst_id_from_public_key(key).await? {
119+
if previous != catalyst_id {
120+
report.functional_validation(
121+
&format!("An update to {catalyst_id} registration chain uses the same public key ({key:?}) as {previous} chain"),
122+
"It isn't allowed to use role 0 signing (certificate subject public) key in different chains",
123+
);
124+
}
125+
}
126+
}
127+
}
128+
129+
Ok(keys)
130+
}
131+
87132
/// Returns a Catalyst ID.
88133
#[must_use]
89134
pub fn catalyst_id(&self) -> &CatalystId {
@@ -257,6 +302,14 @@ impl RegistrationChain {
257302
}
258303
}
259304

305+
impl From<RegistrationChainInner> for RegistrationChain {
306+
fn from(value: RegistrationChainInner) -> Self {
307+
Self {
308+
inner: Arc::new(value),
309+
}
310+
}
311+
}
312+
260313
/// Inner structure of registration chain.
261314
#[derive(Debug, Clone)]
262315
struct RegistrationChainInner {
@@ -491,6 +544,84 @@ impl RegistrationChainInner {
491544

492545
Some(new_inner)
493546
}
547+
548+
/// Attempts to update an existing RBAC registration chain
549+
/// with a new CIP-509 registration, validating address and key usage consistency.
550+
///
551+
/// # Returns
552+
/// - `Ok((new_chain, validation_result))` if the chain was successfully updated and
553+
/// validated.
554+
///
555+
/// # Errors
556+
/// - Returns [`RbacValidationError::UnknownCatalystId`] if no Catalyst chain is found
557+
/// for `previous_txn`.
558+
/// - Returns [`RbacValidationError::InvalidRegistration`] if address/key duplication
559+
/// or validation inconsistencies are detected.
560+
pub async fn update_from_previous_txn<Provider>(
561+
&self,
562+
reg: Cip509,
563+
previous_txn: TransactionId,
564+
provider: &Provider,
565+
) -> Result<RegistrationChain, RbacValidationError>
566+
where
567+
Provider: RbacRegistrationProvider,
568+
{
569+
let purpose = reg.purpose();
570+
let report = reg.report().to_owned();
571+
572+
// Find a chain this registration belongs to.
573+
let Some(catalyst_id) = provider.catalyst_id_from_txn_id(previous_txn).await? else {
574+
// We are unable to determine a Catalyst ID, so there is no sense to update the problem
575+
// report because we would be unable to store this registration anyway.
576+
return Err(RbacValidationError::UnknownCatalystId);
577+
};
578+
let chain = provider.chain(catalyst_id.clone()).await?
579+
.context("{catalyst_id} is present in 'catalyst_id_for_txn_id' table, but not in 'rbac_registration'")?;
580+
581+
// Check that addresses from the new registration aren't used in other chains.
582+
let previous_addresses = chain.stake_addresses();
583+
let reg_addresses = reg.stake_addresses();
584+
let new_addresses: Vec<_> = reg_addresses.difference(&previous_addresses).collect();
585+
for address in &new_addresses {
586+
match provider.catalyst_id_from_stake_address(address).await? {
587+
None => {
588+
// All good: the address wasn't used before.
589+
},
590+
Some(_) => {
591+
report.functional_validation(
592+
&format!("{address} stake addresses is already used"),
593+
"It isn't allowed to use same stake address in multiple registration chains",
594+
);
595+
},
596+
}
597+
}
598+
599+
// Store values before consuming the registration.
600+
let stake_addresses = reg.stake_addresses();
601+
602+
// Try to add a new registration to the chain.
603+
let new_chain = chain.update(reg.clone()).ok_or_else(|| {
604+
RbacValidationError::InvalidRegistration {
605+
catalyst_id: catalyst_id.clone(),
606+
purpose,
607+
report: report.clone(),
608+
}
609+
})?;
610+
611+
// Check that new public keys aren't used by other chains.
612+
let public_keys = new_chain.validate_public_keys(&report, provider).await?;
613+
614+
// Return an error if any issues were recorded in the report.
615+
if report.is_problematic() {
616+
return Err(RbacValidationError::InvalidRegistration {
617+
catalyst_id,
618+
purpose,
619+
report,
620+
});
621+
}
622+
623+
Ok(new_chain)
624+
}
494625
}
495626

496627
/// Perform a check on the validation signature.

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

Lines changed: 4 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,19 @@
22
33
use std::collections::{HashMap, HashSet};
44

5-
use anyhow::{Context, Result};
6-
use cardano_blockchain_types::{hashes::TransactionId, StakeAddress};
5+
use anyhow::Context;
6+
use cardano_blockchain_types::StakeAddress;
77
use catalyst_types::{
88
catalyst_id::{role_index::RoleId, CatalystId},
99
problem_report::ProblemReport,
1010
uuid::UuidV4,
1111
};
12-
use ed25519_dalek::VerifyingKey;
1312

1413
use crate::{
15-
cardano::cip509::{Cip0134UriSet, Cip509},
16-
providers::RbacRegistrationProvider,
14+
cardano::cip509::Cip509, providers::RbacRegistrationProvider,
1715
registration::cardano::RegistrationChain,
1816
};
1917

20-
/// A return value of the `validate_rbac_registration` method.
21-
pub type RbacValidationResult = Result<RbacValidationSuccess, RbacValidationError>;
22-
2318
/// An error returned from the `validate_rbac_registration` method.
2419
#[allow(clippy::large_enum_variant)]
2520
pub enum RbacValidationError {
@@ -53,109 +48,6 @@ impl From<anyhow::Error> for RbacValidationError {
5348
}
5449
}
5550

56-
/// Represents the result yielded by `update_chain` or `start_new_chain` upon successful
57-
/// execution.
58-
pub struct RbacValidationSuccess {
59-
/// A Catalyst ID of the chain this registration belongs to.
60-
pub catalyst_id: CatalystId,
61-
/// A list of stake addresses that were added to the chain.
62-
pub stake_addresses: HashSet<StakeAddress>,
63-
/// A list of role public keys used in this registration.
64-
pub public_keys: HashSet<VerifyingKey>,
65-
/// A list of updates to other chains containing Catalyst IDs and removed stake
66-
/// addresses.
67-
///
68-
/// A new RBAC registration can take ownership of stake addresses of other chains.
69-
pub modified_chains: Vec<(CatalystId, HashSet<StakeAddress>)>,
70-
/// An updated registration chain.
71-
pub chain: RegistrationChain,
72-
}
73-
74-
/// Attempts to update an existing RBAC registration chain
75-
/// with a new CIP-509 registration, validating address and key usage consistency.
76-
///
77-
/// # Returns
78-
/// - `Ok((new_chain, validation_result))` if the chain was successfully updated and
79-
/// validated.
80-
///
81-
/// # Errors
82-
/// - Returns [`RbacValidationError::UnknownCatalystId`] if no Catalyst chain is found for
83-
/// `previous_txn`.
84-
/// - Returns [`RbacValidationError::InvalidRegistration`] if address/key duplication or
85-
/// validation inconsistencies are detected.
86-
pub async fn update_chain<Provider>(
87-
reg: Cip509,
88-
previous_txn: TransactionId,
89-
provider: &Provider,
90-
) -> RbacValidationResult
91-
where
92-
Provider: RbacRegistrationProvider,
93-
{
94-
let purpose = reg.purpose();
95-
let report = reg.report().to_owned();
96-
97-
// Find a chain this registration belongs to.
98-
let Some(catalyst_id) = provider.catalyst_id_from_txn_id(previous_txn).await? else {
99-
// We are unable to determine a Catalyst ID, so there is no sense to update the problem
100-
// report because we would be unable to store this registration anyway.
101-
return Err(RbacValidationError::UnknownCatalystId);
102-
};
103-
let chain = provider.chain(catalyst_id.clone()).await?
104-
.context("{catalyst_id} is present in 'catalyst_id_for_txn_id' table, but not in 'rbac_registration'")?;
105-
106-
// Check that addresses from the new registration aren't used in other chains.
107-
let previous_addresses = chain.stake_addresses();
108-
let reg_addresses = cip509_stake_addresses(&reg);
109-
let new_addresses: Vec<_> = reg_addresses.difference(&previous_addresses).collect();
110-
for address in &new_addresses {
111-
match provider.catalyst_id_from_stake_address(address).await? {
112-
None => {
113-
// All good: the address wasn't used before.
114-
},
115-
Some(_) => {
116-
report.functional_validation(
117-
&format!("{address} stake addresses is already used"),
118-
"It isn't allowed to use same stake address in multiple registration chains",
119-
);
120-
},
121-
}
122-
}
123-
124-
// Store values before consuming the registration.
125-
let stake_addresses = cip509_stake_addresses(&reg);
126-
127-
// Try to add a new registration to the chain.
128-
let new_chain = chain.update(reg.clone()).ok_or_else(|| {
129-
RbacValidationError::InvalidRegistration {
130-
catalyst_id: catalyst_id.clone(),
131-
purpose,
132-
report: report.clone(),
133-
}
134-
})?;
135-
136-
// Check that new public keys aren't used by other chains.
137-
let public_keys = validate_public_keys(&new_chain, &report, provider).await?;
138-
139-
// Return an error if any issues were recorded in the report.
140-
if report.is_problematic() {
141-
return Err(RbacValidationError::InvalidRegistration {
142-
catalyst_id,
143-
purpose,
144-
report,
145-
});
146-
}
147-
148-
Ok(RbacValidationSuccess {
149-
catalyst_id,
150-
stake_addresses,
151-
public_keys,
152-
// Only new chains can take ownership of stake addresses of existing chains, so in this
153-
// case other chains aren't affected.
154-
modified_chains: Vec::new(),
155-
chain: new_chain,
156-
})
157-
}
158-
15951
/// Attempts to initialize a new RBAC registration chain
16052
/// from a given CIP-509 registration, ensuring uniqueness of Catalyst ID, stake
16153
/// addresses, and associated public keys.
@@ -239,7 +131,7 @@ where
239131
}
240132

241133
// Check that new public keys aren't used by other chains.
242-
let public_keys = validate_public_keys(&new_chain, &report, provider).await?;
134+
let public_keys = new_chain.validate_public_keys(&report, provider).await?;
243135

244136
if report.is_problematic() {
245137
return Err(RbacValidationError::InvalidRegistration {
@@ -257,52 +149,3 @@ where
257149
chain: new_chain,
258150
})
259151
}
260-
261-
/// Validates that none of the signing keys in a given RBAC registration chain
262-
/// have been used by any other existing chain, ensuring global key uniqueness
263-
/// across all Catalyst registrations.
264-
///
265-
/// # Returns
266-
/// Returns a [`Result<HashSet<VerifyingKey>>`] containing all unique public keys
267-
/// extracted from the registration chain if validation passes successfully.
268-
///
269-
/// # Errors
270-
/// - Propagates any I/O or provider-level errors encountered while checking key ownership
271-
/// (e.g., database lookup failures).
272-
pub async fn validate_public_keys<Provider>(
273-
chain: &RegistrationChain,
274-
report: &ProblemReport,
275-
provider: &Provider,
276-
) -> Result<HashSet<VerifyingKey>>
277-
where
278-
Provider: RbacRegistrationProvider,
279-
{
280-
let mut keys = HashSet::new();
281-
282-
let roles: Vec<_> = chain.role_data_history().keys().collect();
283-
let catalyst_id = chain.catalyst_id().as_short_id();
284-
285-
for role in roles {
286-
if let Some((key, _)) = chain.get_latest_signing_pk_for_role(role) {
287-
keys.insert(key);
288-
if let Some(previous) = provider.catalyst_id_from_public_key(key).await? {
289-
if previous != catalyst_id {
290-
report.functional_validation(
291-
&format!("An update to {catalyst_id} registration chain uses the same public key ({key:?}) as {previous} chain"),
292-
"It isn't allowed to use role 0 signing (certificate subject public) key in different chains",
293-
);
294-
}
295-
}
296-
}
297-
}
298-
299-
Ok(keys)
300-
}
301-
302-
/// Returns a set of stake addresses in the given registration.
303-
pub fn cip509_stake_addresses(cip509: &Cip509) -> HashSet<StakeAddress> {
304-
cip509
305-
.certificate_uris()
306-
.map(Cip0134UriSet::stake_addresses)
307-
.unwrap_or_default()
308-
}

0 commit comments

Comments
 (0)