Skip to content

Commit a088f41

Browse files
committed
feat: start_new_chain
1 parent 36592c6 commit a088f41

File tree

2 files changed

+105
-24
lines changed

2 files changed

+105
-24
lines changed

rust/rbac-registration/src/providers.rs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,19 +51,3 @@ pub trait RbacRegistrationProvider {
5151
is_persistent: bool,
5252
) -> impl Future<Output = anyhow::Result<Option<CatalystId>>> + Send;
5353
}
54-
55-
/// `RbacCache` Provider trait
56-
pub trait RbacCacheProvider {
57-
/// Add (or update) a persistent chain to the cache.
58-
fn cache_persistent_rbac_chain(
59-
&self,
60-
id: CatalystId,
61-
chain: RegistrationChain,
62-
);
63-
64-
/// Returns a cached persistent chain by the given Catalyst ID.
65-
fn cached_persistent_rbac_chain(
66-
&self,
67-
id: &CatalystId,
68-
) -> Option<RegistrationChain>;
69-
}

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

Lines changed: 105 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
//! Utilities for RBAC registrations validation.
22
3-
use std::collections::HashSet;
3+
use std::collections::{HashMap, HashSet};
44

55
use anyhow::{Context, Result};
66
use cardano_chain_follower::{hashes::TransactionId, StakeAddress};
7-
use catalyst_types::problem_report::ProblemReport;
7+
use catalyst_types::{
8+
catalyst_id::{role_index::RoleId, CatalystId},
9+
problem_report::ProblemReport,
10+
};
811
use ed25519_dalek::VerifyingKey;
912

1013
use crate::{
1114
cardano::cip509::{Cip0134UriSet, Cip509},
12-
providers::{RbacCacheProvider, RbacRegistrationProvider},
15+
providers::RbacRegistrationProvider,
1316
registration::cardano::{
1417
validation_result::{RbacValidationError, RbacValidationResult, RbacValidationSuccess},
1518
RegistrationChain,
@@ -24,7 +27,7 @@ async fn update_chain<Provider>(
2427
provider: &Provider,
2528
) -> RbacValidationResult
2629
where
27-
Provider: RbacRegistrationProvider + RbacCacheProvider,
30+
Provider: RbacRegistrationProvider,
2831
{
2932
let purpose = reg.purpose();
3033
let report = reg.report().to_owned();
@@ -86,10 +89,6 @@ where
8689
});
8790
}
8891

89-
if is_persistent {
90-
provider.cache_persistent_rbac_chain(catalyst_id.clone(), new_chain);
91-
}
92-
9392
Ok(RbacValidationSuccess {
9493
catalyst_id,
9594
stake_addresses,
@@ -101,6 +100,104 @@ where
101100
})
102101
}
103102

103+
/// Tries to start a new RBAC chain.
104+
async fn start_new_chain<Provider>(
105+
reg: Cip509,
106+
is_persistent: bool,
107+
provider: &Provider,
108+
) -> RbacValidationResult
109+
where
110+
Provider: RbacRegistrationProvider,
111+
{
112+
let catalyst_id = reg.catalyst_id().map(CatalystId::as_short_id);
113+
let purpose = reg.purpose();
114+
let report = reg.report().to_owned();
115+
116+
// Try to start a new chain.
117+
let new_chain = RegistrationChain::new(reg).ok_or_else(|| {
118+
if let Some(catalyst_id) = catalyst_id {
119+
RbacValidationError::InvalidRegistration {
120+
catalyst_id,
121+
purpose,
122+
report: report.clone(),
123+
}
124+
} else {
125+
RbacValidationError::UnknownCatalystId
126+
}
127+
})?;
128+
129+
// Verify that a Catalyst ID of this chain is unique.
130+
let catalyst_id = new_chain.catalyst_id().as_short_id();
131+
if provider
132+
.is_chain_known(catalyst_id.clone(), is_persistent)
133+
.await?
134+
{
135+
report.functional_validation(
136+
&format!("{catalyst_id} is already used"),
137+
"It isn't allowed to use same Catalyst ID (certificate subject public key) in multiple registration chains",
138+
);
139+
return Err(RbacValidationError::InvalidRegistration {
140+
catalyst_id,
141+
purpose,
142+
report,
143+
});
144+
}
145+
146+
// Validate stake addresses.
147+
let new_addresses = new_chain.stake_addresses();
148+
let mut updated_chains: HashMap<_, HashSet<StakeAddress>> = HashMap::new();
149+
for address in &new_addresses {
150+
if let Some(id) = provider
151+
.catalyst_id_from_stake_address(address, is_persistent)
152+
.await?
153+
{
154+
// If an address is used in existing chain then a new chain must have different role 0
155+
// signing key.
156+
let previous_chain = provider.chain(id.clone(), is_persistent)
157+
.await?
158+
.context("{id} is present in 'catalyst_id_for_stake_address', but not in 'rbac_registration'")?;
159+
if previous_chain.get_latest_signing_pk_for_role(&RoleId::Role0)
160+
== new_chain.get_latest_signing_pk_for_role(&RoleId::Role0)
161+
{
162+
report.functional_validation(
163+
&format!("A new registration ({catalyst_id}) uses the same public key as the previous one ({})",
164+
previous_chain.catalyst_id().as_short_id()
165+
),
166+
"It is only allowed to override the existing chain by using different public key",
167+
);
168+
} else {
169+
// The new root registration "takes" an address(es) from the existing chain, so that
170+
// chain needs to be updated.
171+
updated_chains
172+
.entry(id)
173+
.and_modify(|e| {
174+
e.insert(address.clone());
175+
})
176+
.or_insert([address.clone()].into_iter().collect());
177+
}
178+
}
179+
}
180+
181+
// Check that new public keys aren't used by other chains.
182+
let public_keys = validate_public_keys(&new_chain, is_persistent, &report, provider).await?;
183+
184+
if report.is_problematic() {
185+
return Err(RbacValidationError::InvalidRegistration {
186+
catalyst_id,
187+
purpose,
188+
report,
189+
});
190+
}
191+
192+
Ok(RbacValidationSuccess {
193+
catalyst_id,
194+
stake_addresses: new_addresses,
195+
public_keys,
196+
modified_chains: updated_chains.into_iter().collect(),
197+
purpose,
198+
})
199+
}
200+
104201
/// Checks that a new registration doesn't contain a signing key that was used by any
105202
/// other chain. Returns a list of public keys in the registration.
106203
async fn validate_public_keys<Provider>(

0 commit comments

Comments
 (0)