Skip to content

Conversation

@Mr-Leshiy
Copy link
Contributor

@Mr-Leshiy Mr-Leshiy commented Nov 7, 2025

Description

Processing scenario when another registration chain could "stole" cip0134uri address, so it become not accessible for the current one after applying such registration.

Related Issue(s)

Part of #578

Description of Changes

  • adding taken_uris: HashSet<Cip0134Uri>, and update_taken_uris for Cip0134UriSet struct. Also modify the original update method, so it removes the taken_uris, which enables the functionality of returning back "stolen" addresses.
  • adding signing_pk_for_role method for the Cip509 type.
  • refactoring new, update methods for the RegistrationChain type.

Please confirm the following checks

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream module

@Mr-Leshiy Mr-Leshiy added squad: gatekeepers Catalyst App Backend, System Development & Integration Team draft Draft labels Nov 12, 2025
@Mr-Leshiy Mr-Leshiy moved this from New to 🏗 In progress in Catalyst Nov 12, 2025
@Mr-Leshiy Mr-Leshiy added the review me PR is ready for review label Nov 13, 2025
@Mr-Leshiy Mr-Leshiy marked this pull request as ready for review November 13, 2025 12:21
@Mr-Leshiy Mr-Leshiy removed the draft Draft label Nov 13, 2025
Copy link
Member

@stanislav-tkach stanislav-tkach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the change is ok in general, but I don't quite get the idea with the updated URI set. As far as I can see, we are still not storing the full history and only the final state in Cip0134UriSet. In that case why do we want to store "taken" addresses separately? Why not store the resulting final state? For example currently we would have something like this:

x_uris: [A, B, C],
c_uris: [D, E],
taken_uris: [B, C, D],

But it can be represented as

x_uris: [A],
c_uris: [E],

Am I missing something?

x_uris: UrisMap,
/// URIs from c509 certificates.
c_uris: UrisMap,
/// URIs which are taken by another certificates.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: I think it would be better to say "by another chains".

Comment on lines +109 to 111
/// Returns a set of stake addresses by the given role.
#[must_use]
pub fn role_stake_addresses(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As for now, the behavior of role_stake_addresses and role_addresses functions is inconsistent with the one of the stake_addresses function. The latter one doesn't include taken addresses, but the former ones do.

We should probably add unit tests for these methods.

/// URIs from c509 certificates.
c_uris: UrisMap,
/// URIs which are taken by another certificates.
taken_uris: HashSet<Cip0134Uri>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I can see, we can now mark as "taken" all address types, not just stake addresses. Is this desired behavior?

/// 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the wording is slightly confusing: this code doesn't remove URIs from the set, it adds (marks?) such URIs as taken.

Comment on lines 47 to 48
/// Update the update by "taking" the given `StakeAddress` for the correspoding RBAC
/// chain's by the given `CatalystId`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Update the update by "taking" the given `StakeAddress` for the correspoding RBAC
/// chain's by the given `CatalystId`.
/// Update the chain by transferring the given `StakeAddress` to the chain with the given `CatalystId`.

fn take_stake_address_from_chain(
&mut self,
id: &CatalystId,
address: &StakeAddress,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be better to pass a set (or an iterator) instead of calling this method multiple times?

Comment on lines +49 to +53
fn take_stake_address_from_chain(
&mut self,
id: &CatalystId,
address: &StakeAddress,
) -> impl Future<Output = anyhow::Result<()>> + Send;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand the updated workflow. If we always need a Cip509 object to update a chain (even if it belongs to another chain) then do we still need to provide stake addresses here?

Also I think it would be better to have a pull request to catalyst-gateway to see the usage of the updated API.

Comment on lines +71 to +72
// Checks that a new registration doesn't contain a signing key that was used by any
// other chain. Returns a list of public keys in the registration.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Checks that a new registration doesn't contain a signing key that was used by any
// other chain. Returns a list of public keys in the registration.
// Checks that a new registration doesn't contain a signing key that was used by any
// other chain.

Comment on lines +73 to +84
for role in cip509.all_roles() {
if let Some(key) = cip509.signing_pk_for_role(role) {
if let Some(previous) = state.chain_catalyst_id_from_public_key(&key).await? {
if &previous != cat_id {
cip509.report().functional_validation(
&format!("An update to {cat_id} registration chain uses the same public key ({key:?}) as {previous} chain"),
"It isn't allowed to use role 0 signing (certificate subject public) key in different chains",
);
}
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I can see, the exact same code is used in the update function. Can we reuse it?

Comment on lines +92 to +94
if let Some(id) = state.chain_catalyst_id_from_stake_address(address).await? {
state.take_stake_address_from_chain(&id, address).await?;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment that it is only allowed to take stake addresses from the existing chain if the new one uses a different signing key, but it is ok because all keys must be unique anyway.

@stanislav-tkach
Copy link
Member

Also I would like to see how we handle the caching on the catalyst-gateway side with the updated API.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review me PR is ready for review squad: gatekeepers Catalyst App Backend, System Development & Integration Team

Projects

Status: 🏗 In progress

Development

Successfully merging this pull request may close these issues.

3 participants