Skip to content

Commit 5c0b665

Browse files
authored
feat(rust/rbac-registration): RBAC stolen StakeAddress handling. (#631)
* wip * wip * wip * wip * revert * wip * add stolen_uris field * wip * wip * wip * fix * wip * wip * fix spelling * fix * wip * wip * wip * wip * cleanup * fix test * fix clippy * wip * wip * fix * wip * fix clippy * fix * wip * wip
1 parent ac4093c commit 5c0b665

File tree

10 files changed

+442
-362
lines changed

10 files changed

+442
-362
lines changed

rust/rbac-registration/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,5 @@ thiserror = "2.0.11"
3434

3535
c509-certificate = { version = "0.0.3", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "c509-certificate-v0.0.3" }
3636
cbork-utils = { version = "0.0.2", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "cbork-utils-v0.0.2" }
37-
cardano-blockchain-types = { version = "0.0.8", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "cardano-blockchain-types/v0.0.8" }
37+
cardano-blockchain-types = { version = "0.0.9", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "cardano-blockchain-types/v0.0.9" }
3838
catalyst-types = { version = "0.0.10", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "catalyst-types/v0.0.10" }

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

Lines changed: 60 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use catalyst_types::{
2222
uuid::UuidV4,
2323
};
2424
use cbork_utils::decode_helper::{decode_bytes, decode_helper, decode_map_len};
25+
use ed25519_dalek::VerifyingKey;
2526
use minicbor::{
2627
decode::{self},
2728
Decode, Decoder,
@@ -32,6 +33,7 @@ use uuid::Uuid;
3233

3334
use crate::cardano::cip509::{
3435
decode_context::DecodeContext,
36+
extract_key,
3537
rbac::Cip509RbacMetadata,
3638
types::{PaymentHistory, TxInputHash, ValidationSignature},
3739
utils::Cip0134UriSet,
@@ -40,7 +42,7 @@ use crate::cardano::cip509::{
4042
validate_txn_inputs_hash,
4143
},
4244
x509_chunks::X509Chunks,
43-
Payment, PointTxnIdx, RoleData,
45+
C509Cert, LocalRefInt, Payment, PointTxnIdx, RoleData, SimplePublicKeyType, X509DerCert,
4446
};
4547

4648
/// A x509 metadata envelope.
@@ -80,7 +82,8 @@ pub struct Cip509 {
8082
origin: PointTxnIdx,
8183
/// A catalyst ID.
8284
///
83-
/// This field is only present in role 0 registrations.
85+
/// This field is only present in role 0 registrations and only for the first
86+
/// registration, which defines a `CatalystId` for the chain.
8487
catalyst_id: Option<CatalystId>,
8588
/// Raw aux data associated with the transaction that CIP509 is attached to,
8689
raw_aux_data: Vec<u8>,
@@ -180,6 +183,12 @@ impl Cip509 {
180183
validate_self_sign_cert(metadata, &report);
181184
}
182185

186+
// We want to keep `catalyst_id` field only for the first registration,
187+
// which starts a new chain
188+
if cip509.prv_tx_id.is_some() {
189+
cip509.catalyst_id = None;
190+
}
191+
183192
Ok(Some(cip509))
184193
}
185194

@@ -227,6 +236,48 @@ impl Cip509 {
227236
self.metadata.as_ref().and_then(|m| m.role_data.get(&role))
228237
}
229238

239+
/// Returns signing public key for a role.
240+
/// Would return only signing public keys for the present certificates,
241+
/// if certificate marked as deleted or undefined it would be skipped.
242+
#[must_use]
243+
pub fn signing_pk_for_role(
244+
&self,
245+
role: RoleId,
246+
) -> Option<VerifyingKey> {
247+
self.metadata.as_ref().and_then(|m| {
248+
let key_ref = m.role_data.get(&role).and_then(|d| d.signing_key())?;
249+
match key_ref.local_ref {
250+
LocalRefInt::X509Certs => {
251+
m.x509_certs.get(key_ref.key_offset).and_then(|c| {
252+
if let X509DerCert::X509Cert(c) = c {
253+
extract_key::x509_key(c).ok()
254+
} else {
255+
None
256+
}
257+
})
258+
},
259+
LocalRefInt::C509Certs => {
260+
m.c509_certs.get(key_ref.key_offset).and_then(|c| {
261+
if let C509Cert::C509Certificate(c) = c {
262+
extract_key::c509_key(c).ok()
263+
} else {
264+
None
265+
}
266+
})
267+
},
268+
LocalRefInt::PubKeys => {
269+
m.pub_keys.get(key_ref.key_offset).and_then(|c| {
270+
if let SimplePublicKeyType::Ed25519(c) = c {
271+
Some(*c)
272+
} else {
273+
None
274+
}
275+
})
276+
},
277+
}
278+
})
279+
}
280+
230281
/// Returns a purpose of this registration.
231282
#[must_use]
232283
pub fn purpose(&self) -> Option<UuidV4> {
@@ -259,7 +310,7 @@ impl Cip509 {
259310

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

@@ -269,24 +320,13 @@ impl Cip509 {
269320
self.txn_inputs_hash.as_ref()
270321
}
271322

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

278-
/// Returns a list of addresses extracted from certificate URIs of a specific role.
279-
#[must_use]
280-
pub fn certificate_addresses(
281-
&self,
282-
role: usize,
283-
) -> HashSet<Address> {
284-
self.metadata
285-
.as_ref()
286-
.map(|m| m.certificate_uris.role_addresses(role))
287-
.unwrap_or_default()
288-
}
289-
290330
/// Return validation signature.
291331
#[must_use]
292332
pub fn validation_signature(&self) -> Option<&ValidationSignature> {
@@ -313,26 +353,10 @@ impl Cip509 {
313353
.unwrap_or_default()
314354
}
315355

316-
/// Returns `Cip509` fields consuming the structure if it was successfully decoded and
317-
/// validated otherwise return the problem report that contains all the encountered
318-
/// issues.
319-
///
320-
/// # Errors
321-
///
322-
/// - `Err(ProblemReport)`
323-
pub fn consume(self) -> Result<(UuidV4, Cip509RbacMetadata, PaymentHistory), ProblemReport> {
324-
match (
325-
self.purpose,
326-
self.txn_inputs_hash,
327-
self.metadata,
328-
self.validation_signature,
329-
) {
330-
(Some(purpose), Some(_), Some(metadata), Some(_)) if !self.report.is_problematic() => {
331-
Ok((purpose, metadata, self.payment_history))
332-
},
333-
334-
_ => Err(self.report),
335-
}
356+
/// Returns a payment history map.
357+
#[must_use]
358+
pub fn payment_history(&self) -> &PaymentHistory {
359+
&self.payment_history
336360
}
337361
}
338362

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

Lines changed: 82 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -39,87 +39,100 @@ struct Cip0134UriSetInner {
3939
x_uris: UrisMap,
4040
/// URIs from c509 certificates.
4141
c_uris: UrisMap,
42+
/// `StakeAddress` which are taken by another chains.
43+
taken_stake_addresses: HashSet<StakeAddress>,
4244
}
4345

4446
impl Cip0134UriSet {
4547
/// Creates a new `Cip0134UriSet` instance from the given certificates.
4648
#[must_use]
47-
pub fn new(
49+
pub(crate) fn new(
4850
x509_certs: &[X509DerCert],
4951
c509_certs: &[C509Cert],
5052
report: &ProblemReport,
5153
) -> Self {
5254
let x_uris = extract_x509_uris(x509_certs, report);
5355
let c_uris = extract_c509_uris(c509_certs, report);
54-
Self(Arc::new(Cip0134UriSetInner { x_uris, c_uris }))
56+
let taken_stake_addresses = HashSet::new();
57+
Self(Arc::new(Cip0134UriSetInner {
58+
x_uris,
59+
c_uris,
60+
taken_stake_addresses,
61+
}))
5562
}
5663

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

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

76+
/// Returns an iterator over of `Cip0134Uri`.
77+
pub(crate) fn values(&self) -> impl Iterator<Item = &Cip0134Uri> {
78+
self.x_uris()
79+
.values()
80+
.chain(self.c_uris().values())
81+
.flat_map(|uris| uris.iter())
82+
}
83+
6984
/// Returns `true` if both x509 and c509 certificate maps are empty.
70-
#[must_use]
71-
pub fn is_empty(&self) -> bool {
85+
#[cfg(test)]
86+
pub(crate) fn is_empty(&self) -> bool {
7287
self.x_uris().is_empty() && self.c_uris().is_empty()
7388
}
7489

75-
/// Returns a list of addresses by the given role.
90+
/// Returns a list of URIs by the given role.
7691
#[must_use]
77-
pub fn role_addresses(
92+
pub(crate) fn role_uris(
7893
&self,
7994
role: usize,
80-
) -> HashSet<Address> {
95+
) -> HashSet<Cip0134Uri> {
8196
let mut result = HashSet::new();
8297

8398
if let Some(uris) = self.x_uris().get(&role) {
84-
result.extend(uris.iter().map(|uri| uri.address().clone()));
99+
result.extend(uris.iter().cloned());
85100
}
86101
if let Some(uris) = self.c_uris().get(&role) {
87-
result.extend(uris.iter().map(|uri| uri.address().clone()));
102+
result.extend(uris.iter().cloned());
88103
}
89104

90105
result
91106
}
92107

93-
/// Returns a list of stake addresses by the given role.
108+
/// Returns a set of stake addresses by the given role.
94109
#[must_use]
95-
pub fn role_stake_addresses(
110+
pub(crate) fn role_stake_addresses(
96111
&self,
97112
role: usize,
98113
) -> HashSet<StakeAddress> {
99-
self.role_addresses(role)
114+
self.role_uris(role)
100115
.iter()
101-
.filter_map(|address| {
102-
match address {
116+
.filter_map(|uri| {
117+
match uri.address() {
103118
Address::Stake(a) => Some(a.clone().into()),
104119
_ => None,
105120
}
106121
})
107122
.collect()
108123
}
109124

110-
/// Returns a list of all stake addresses.
125+
/// Returns a set of all active (without taken) stake addresses.
111126
#[must_use]
112-
pub fn stake_addresses(&self) -> HashSet<StakeAddress> {
113-
self.x_uris()
114-
.values()
115-
.chain(self.c_uris().values())
116-
.flat_map(|uris| uris.iter())
127+
pub(crate) fn stake_addresses(&self) -> HashSet<StakeAddress> {
128+
self.values()
117129
.filter_map(|uri| {
118130
match uri.address() {
119131
Address::Stake(a) => Some(a.clone().into()),
120132
_ => None,
121133
}
122134
})
135+
.filter(|v| !self.0.taken_stake_addresses.contains(v))
123136
.collect()
124137
}
125138

@@ -142,7 +155,7 @@ impl Cip0134UriSet {
142155
/// 2: [uri_4]
143156
/// ```
144157
#[must_use]
145-
pub fn update(
158+
pub(crate) fn update(
146159
self,
147160
metadata: &Cip509RbacMetadata,
148161
) -> Self {
@@ -154,6 +167,7 @@ impl Cip0134UriSet {
154167
let Cip0134UriSetInner {
155168
mut x_uris,
156169
mut c_uris,
170+
mut taken_stake_addresses,
157171
} = Arc::unwrap_or_clone(self.0);
158172

159173
for (index, cert) in metadata.x509_certs.iter().enumerate() {
@@ -191,7 +205,50 @@ impl Cip0134UriSet {
191205
}
192206
}
193207

194-
Self(Arc::new(Cip0134UriSetInner { x_uris, c_uris }))
208+
metadata
209+
.certificate_uris
210+
.stake_addresses()
211+
.iter()
212+
.for_each(|v| {
213+
taken_stake_addresses.remove(v);
214+
});
215+
216+
Self(Arc::new(Cip0134UriSetInner {
217+
x_uris,
218+
c_uris,
219+
taken_stake_addresses,
220+
}))
221+
}
222+
223+
/// Return the updated URIs set where the provided URIs were taken by other
224+
/// registration chains.
225+
///
226+
/// Updates the current URI set by marking URIs as taken.
227+
#[must_use]
228+
pub(crate) fn update_taken_uris(
229+
self,
230+
reg: &Cip509RbacMetadata,
231+
) -> Self {
232+
let current_stake_addresses = self.stake_addresses();
233+
let latest_taken_stake_addresses = reg
234+
.certificate_uris
235+
.stake_addresses()
236+
.into_iter()
237+
.filter(|v| current_stake_addresses.contains(v));
238+
239+
let Cip0134UriSetInner {
240+
x_uris,
241+
c_uris,
242+
mut taken_stake_addresses,
243+
} = Arc::unwrap_or_clone(self.0);
244+
245+
taken_stake_addresses.extend(latest_taken_stake_addresses);
246+
247+
Self(Arc::new(Cip0134UriSetInner {
248+
x_uris,
249+
c_uris,
250+
taken_stake_addresses,
251+
}))
195252
}
196253
}
197254

@@ -344,7 +401,7 @@ mod tests {
344401
let set = cip509.certificate_uris().unwrap();
345402
assert!(!set.is_empty());
346403
assert!(set.c_uris().is_empty());
347-
assert_eq!(set.role_addresses(0).len(), 1);
404+
assert_eq!(set.role_uris(0).len(), 1);
348405
assert_eq!(set.role_stake_addresses(0).len(), 1);
349406
assert_eq!(set.stake_addresses().len(), 1);
350407

0 commit comments

Comments
 (0)