Skip to content

Commit 80bda85

Browse files
committed
chore: rbac update logic
1 parent 8014afb commit 80bda85

File tree

1 file changed

+44
-216
lines changed
  • rust/rbac-registration/src/registration/cardano

1 file changed

+44
-216
lines changed

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

Lines changed: 44 additions & 216 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ impl RegistrationChain {
5050
///
5151
/// Returns an error if data is invalid
5252
#[must_use]
53-
pub fn new(cip509: Cip509) -> Option<Self> {
54-
let inner = RegistrationChainInner::new(cip509)?;
53+
pub fn new_stateless(cip509: Cip509) -> Option<Self> {
54+
let inner = RegistrationChainInner::new_stateless(cip509)?;
5555

5656
Some(Self {
5757
inner: Arc::new(inner),
@@ -67,13 +67,13 @@ impl RegistrationChain {
6767
///
6868
/// Returns an error if data is invalid
6969
#[must_use]
70-
pub fn update(
70+
pub fn update_stateless(
7171
&self,
7272
cip509: Cip509,
7373
) -> Option<Self> {
7474
let latest_signing_pk = self.get_latest_signing_pk_for_role(&RoleId::Role0);
7575
let new_inner = if let Some((signing_pk, _)) = latest_signing_pk {
76-
self.inner.update(cip509, signing_pk)?
76+
self.inner.update_stateless(cip509, signing_pk)?
7777
} else {
7878
cip509.report().missing_field(
7979
"latest signing key for role 0",
@@ -91,32 +91,17 @@ impl RegistrationChain {
9191
/// If the given registration references a previous transaction, it attempts
9292
/// to update the existing chain using that previous transaction.
9393
/// Otherwise, it starts a new chain from the provided registration.
94-
///
95-
/// # Returns
96-
/// - `Ok(Self)` if the chain was successfully initialized or updated and validated.
97-
///
98-
/// # Errors
99-
/// - [`RbacValidationError::UnknownCatalystId`] if no Catalyst chain can be found or
100-
/// inferred.
101-
/// - [`RbacValidationError::InvalidRegistration`] if any validation, address, or key
102-
/// duplication inconsistencies are detected.
103-
pub async fn update_from_previous_txn<Provider>(
94+
pub async fn update<Provider>(
10495
reg: Cip509,
10596
provider: &Provider,
106-
) -> Result<(Self, Vec<(CatalystId, HashSet<StakeAddress>)>), RbacValidationError>
97+
) -> Option<Self>
10798
where
10899
Provider: RbacRegistrationProvider,
109100
{
110-
if let Some(previous_txn) = reg.previous_transaction() {
111-
let result =
112-
RegistrationChainInner::update_from_previous_txn(reg, previous_txn, provider)
113-
.await?;
114-
115-
// Only new chains can take ownership of stake addresses of existing chains, so in this
116-
// case other chains aren't affected.
117-
Ok((result, Vec::new()))
101+
if reg.previous_transaction().is_some() {
102+
RegistrationChainInner::update(reg, provider).await
118103
} else {
119-
RegistrationChainInner::start_from_provider(reg, provider).await
104+
RegistrationChainInner::new(reg, provider).await
120105
}
121106
}
122107

@@ -135,13 +120,14 @@ impl RegistrationChain {
135120
&self,
136121
report: &ProblemReport,
137122
provider: &Provider,
138-
) -> Result<(), RbacValidationError>
123+
) -> anyhow::Result<bool>
139124
where
140125
Provider: RbacRegistrationProvider,
141126
{
142127
let roles: Vec<_> = self.role_data_history().keys().collect();
143128
let catalyst_id = self.catalyst_id().as_short_id();
144129

130+
let mut result = true;
145131
for role in roles {
146132
if let Some((key, _)) = self.get_latest_signing_pk_for_role(role) {
147133
if let Some(previous) = provider.catalyst_id_from_public_key(key).await? {
@@ -150,12 +136,14 @@ impl RegistrationChain {
150136
&format!("An update to {catalyst_id} registration chain uses the same public key ({key:?}) as {previous} chain"),
151137
"It isn't allowed to use role 0 signing (certificate subject public) key in different chains",
152138
);
139+
140+
result = false
153141
}
154142
}
155143
}
156144
}
157145

158-
Ok(())
146+
Ok(result)
159147
}
160148

161149
/// Returns a Catalyst ID.
@@ -331,39 +319,6 @@ impl RegistrationChain {
331319
}
332320
}
333321

334-
/// An error returned from the `validate_rbac_registration` method.
335-
#[allow(clippy::large_enum_variant)]
336-
pub enum RbacValidationError {
337-
/// A registration is invalid (`report.is_problematic()` returns `true`).
338-
///
339-
/// This variant is inserted to the `rbac_invalid_registration` table.
340-
InvalidRegistration {
341-
/// A Catalyst ID.
342-
catalyst_id: CatalystId,
343-
/// A registration purpose.
344-
purpose: Option<UuidV4>,
345-
/// A problem report.
346-
report: ProblemReport,
347-
},
348-
/// Unable to determine a Catalyst ID of the registration.
349-
///
350-
/// This can happen if a previous transaction ID in the registration is incorrect.
351-
UnknownCatalystId,
352-
/// A "fatal" error occurred during validation.
353-
///
354-
/// This means that the validation wasn't performed properly (usually because of a
355-
/// database failure) and we cannot process the given registration. This error is
356-
/// propagated on a higher level, so there will be another attempt to index that
357-
/// block.
358-
Fatal(anyhow::Error),
359-
}
360-
361-
impl From<anyhow::Error> for RbacValidationError {
362-
fn from(e: anyhow::Error) -> Self {
363-
RbacValidationError::Fatal(e)
364-
}
365-
}
366-
367322
/// Inner structure of registration chain.
368323
#[derive(Debug, Clone)]
369324
struct RegistrationChainInner {
@@ -411,7 +366,7 @@ impl RegistrationChainInner {
411366
///
412367
/// Returns an error if data is invalid
413368
#[must_use]
414-
fn new(cip509: Cip509) -> Option<Self> {
369+
fn new_stateless(cip509: Cip509) -> Option<Self> {
415370
let context = "Registration Chain new";
416371
// Should be chain root, return immediately if not
417372
if cip509.previous_transaction().is_some() {
@@ -517,7 +472,7 @@ impl RegistrationChainInner {
517472
///
518473
/// Returns an error if data is invalid
519474
#[must_use]
520-
fn update(
475+
fn update_stateless(
521476
&self,
522477
cip509: Cip509,
523478
signing_pk: VerifyingKey,
@@ -601,172 +556,45 @@ impl RegistrationChainInner {
601556

602557
/// Attempts to update an existing RBAC registration chain
603558
/// with a new CIP-509 registration, validating address and key usage consistency.
604-
///
605-
/// # Returns
606-
/// - `Ok((new_chain, validation_result))` if the chain was successfully updated and
607-
/// validated.
608-
///
609-
/// # Errors
610-
/// - Returns [`RbacValidationError::UnknownCatalystId`] if no Catalyst chain is found
611-
/// for `previous_txn`.
612-
/// - Returns [`RbacValidationError::InvalidRegistration`] if address/key duplication
613-
/// or validation inconsistencies are detected.
614-
pub async fn update_from_previous_txn<Provider>(
559+
pub async fn update<Provider>(
615560
reg: Cip509,
616-
previous_txn: TransactionId,
617561
provider: &Provider,
618-
) -> Result<RegistrationChain, RbacValidationError>
562+
) -> Option<RegistrationChain>
619563
where
620564
Provider: RbacRegistrationProvider,
621565
{
622-
let purpose = reg.purpose();
623-
let report = reg.report().to_owned();
624-
625-
// Find a chain this registration belongs to.
626-
let Some(catalyst_id) = provider.catalyst_id_from_txn_id(previous_txn).await? else {
627-
// We are unable to determine a Catalyst ID, so there is no sense to update the problem
628-
// report because we would be unable to store this registration anyway.
629-
return Err(RbacValidationError::UnknownCatalystId);
630-
};
631-
let chain = provider.chain(catalyst_id.clone()).await?
632-
.context("{catalyst_id} is present in 'catalyst_id_for_txn_id' table, but not in 'rbac_registration'")?;
633-
634-
// Check that addresses from the new registration aren't used in other chains.
635-
let previous_addresses = chain.stake_addresses();
636-
let reg_addresses = reg.stake_addresses();
637-
let new_addresses: Vec<_> = reg_addresses.difference(&previous_addresses).collect();
638-
for address in &new_addresses {
639-
match provider.catalyst_id_from_stake_address(address).await? {
640-
None => {
641-
// All good: the address wasn't used before.
642-
},
643-
Some(_) => {
644-
report.functional_validation(
645-
&format!("{address} stake addresses is already used"),
646-
"It isn't allowed to use same stake address in multiple registration chains",
647-
);
648-
},
649-
}
650-
}
651-
652-
// Try to add a new registration to the chain.
653-
let new_chain = chain.update(reg.clone()).ok_or_else(|| {
654-
RbacValidationError::InvalidRegistration {
655-
catalyst_id: catalyst_id.clone(),
656-
purpose,
657-
report: report.clone(),
658-
}
659-
})?;
660-
661-
// Check that new public keys aren't used by other chains.
662-
new_chain.validate_public_keys(&report, provider).await?;
663-
664-
// Return an error if any issues were recorded in the report.
665-
if report.is_problematic() {
666-
return Err(RbacValidationError::InvalidRegistration {
667-
catalyst_id,
668-
purpose,
669-
report,
670-
});
671-
}
672-
673-
Ok(new_chain)
566+
// perform provider lookups and validations here
567+
// then build new RegistrationChain using update_stateless
568+
let previous_txn = reg.previous_transaction()?;
569+
let catalyst_id = provider
570+
.catalyst_id_from_txn_id(previous_txn)
571+
.await
572+
.ok()??;
573+
let chain = provider.chain(catalyst_id.clone()).await.ok()??;
574+
575+
let latest_signing_pk = chain.get_latest_signing_pk_for_role(&RoleId::Role0)?;
576+
let (signing_pk, _) = latest_signing_pk;
577+
578+
let new_inner = chain.inner.update_stateless(reg.clone(), signing_pk)?;
579+
Some(RegistrationChain {
580+
inner: Arc::new(new_inner),
581+
})
674582
}
675583

676584
/// Attempts to initialize a new RBAC registration chain
677585
/// from a given CIP-509 registration, ensuring uniqueness of Catalyst ID, stake
678586
/// addresses, and associated public keys.
679-
///
680-
/// # Returns
681-
/// - `Ok((new_chain, validation_result))` if the chain was successfully initialized
682-
/// and validated.
683-
///
684-
/// # Errors
685-
/// - [`RbacValidationError::UnknownCatalystId`]: if `reg` lacks a valid Catalyst ID.
686-
/// - [`RbacValidationError::InvalidRegistration`]: if any functional validation,
687-
/// stake address conflict, or public key duplication occurs.
688-
pub async fn start_from_provider<Provider>(
587+
pub async fn new<Provider>(
689588
reg: Cip509,
690-
provider: &Provider,
691-
) -> Result<(RegistrationChain, Vec<(CatalystId, HashSet<StakeAddress>)>), RbacValidationError>
589+
_provider: &Provider,
590+
) -> Option<RegistrationChain>
692591
where
693592
Provider: RbacRegistrationProvider,
694593
{
695-
let catalyst_id = reg.catalyst_id().map(CatalystId::as_short_id);
696-
let purpose = reg.purpose();
697-
let report = reg.report().to_owned();
698-
699-
// Try to start a new chain.
700-
let new_chain = RegistrationChain::new(reg).ok_or_else(|| {
701-
if let Some(catalyst_id) = catalyst_id {
702-
RbacValidationError::InvalidRegistration {
703-
catalyst_id,
704-
purpose,
705-
report: report.clone(),
706-
}
707-
} else {
708-
RbacValidationError::UnknownCatalystId
709-
}
710-
})?;
711-
712-
// Verify that a Catalyst ID of this chain is unique.
713-
let catalyst_id = new_chain.catalyst_id().as_short_id();
714-
if provider.is_chain_known(catalyst_id.clone()).await? {
715-
report.functional_validation(
716-
&format!("{catalyst_id} is already used"),
717-
"It isn't allowed to use same Catalyst ID (certificate subject public key) in multiple registration chains",
718-
);
719-
return Err(RbacValidationError::InvalidRegistration {
720-
catalyst_id,
721-
purpose,
722-
report,
723-
});
724-
}
725-
726-
// Validate stake addresses.
727-
let new_addresses = new_chain.stake_addresses();
728-
let mut updated_chains: HashMap<_, HashSet<StakeAddress>> = HashMap::new();
729-
for address in &new_addresses {
730-
if let Some(id) = provider.catalyst_id_from_stake_address(address).await? {
731-
// If an address is used in existing chain then a new chain must have different role
732-
// 0 signing key.
733-
let previous_chain = provider.chain(id.clone())
734-
.await?
735-
.context("{id} is present in 'catalyst_id_for_stake_address', but not in 'rbac_registration'")?;
736-
if previous_chain.get_latest_signing_pk_for_role(&RoleId::Role0)
737-
== new_chain.get_latest_signing_pk_for_role(&RoleId::Role0)
738-
{
739-
report.functional_validation(
740-
&format!("A new registration ({catalyst_id}) uses the same public key as the previous one ({})",
741-
previous_chain.catalyst_id().as_short_id()
742-
),
743-
"It is only allowed to override the existing chain by using different public key",
744-
);
745-
} else {
746-
// The new root registration "takes" an address(es) from the existing chain, so
747-
// that chain needs to be updated.
748-
updated_chains
749-
.entry(id)
750-
.and_modify(|e| {
751-
e.insert(address.clone());
752-
})
753-
.or_insert([address.clone()].into_iter().collect());
754-
}
755-
}
756-
}
757-
758-
// Check that new public keys aren't used by other chains.
759-
new_chain.validate_public_keys(&report, provider).await?;
760-
761-
if report.is_problematic() {
762-
return Err(RbacValidationError::InvalidRegistration {
763-
catalyst_id,
764-
purpose,
765-
report,
766-
});
767-
}
768-
769-
Ok((new_chain, updated_chains.into_iter().collect()))
594+
let inner = RegistrationChainInner::new_stateless(reg)?;
595+
Some(RegistrationChain {
596+
inner: Arc::new(inner),
597+
})
770598
}
771599
}
772600

@@ -827,7 +655,7 @@ mod test {
827655
data.assert_valid(&registration);
828656

829657
// Create a chain with the first registration.
830-
let chain = RegistrationChain::new(registration).unwrap();
658+
let chain = RegistrationChain::new_stateless(registration).unwrap();
831659
assert_eq!(chain.purpose(), &[data.purpose]);
832660
assert_eq!(1, chain.x509_certs().len());
833661
let origin = &chain.x509_certs().get(&0).unwrap().first().unwrap();
@@ -854,7 +682,7 @@ mod test {
854682
assert!(registration.report().is_problematic());
855683

856684
let report = registration.report().to_owned();
857-
assert!(chain.update(registration).is_none());
685+
assert!(chain.update_stateless(registration).is_none());
858686
let report = format!("{report:?}");
859687
assert!(
860688
report.contains("kind: InvalidValue { field: \"previous transaction ID\""),
@@ -868,7 +696,7 @@ mod test {
868696
.unwrap()
869697
.unwrap();
870698
data.assert_valid(&registration);
871-
let update = chain.update(registration).unwrap();
699+
let update = chain.update_stateless(registration).unwrap();
872700
// Current tx hash should be equal to the hash from block 4.
873701
assert_eq!(update.current_tx_id_hash(), data.txn_hash);
874702
assert!(update.role_data_record().contains_key(&data.role));

0 commit comments

Comments
 (0)