Skip to content

Commit 48dbf9d

Browse files
Introduce Cip0134Uri type
1 parent d911863 commit 48dbf9d

File tree

4 files changed

+109
-35
lines changed

4 files changed

+109
-35
lines changed

rust/rbac-registration/Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,11 @@ workspace = true
2121
hex = "0.4.3"
2222
anyhow = "1.0.89"
2323
strum_macros = "0.26.4"
24-
regex = "1.11.0"
2524
minicbor = { version = "0.25.1", features = ["alloc", "derive", "half"] }
2625
brotli = "7.0.0"
2726
zstd = "0.13.2"
2827
x509-cert = "0.2.5"
2928
der-parser = "9.0.0"
30-
bech32 = "0.11.0"
3129
dashmap = "6.1.0"
3230
blake2b_simd = "1.0.2"
3331
tracing = "0.1.40"

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

Lines changed: 97 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,91 @@
11
//! Utility functions for CIP-0134 address.
22
3+
// Ignore URIs that are used in tests and doc-examples.
4+
// cSpell:ignoreRegExp web\+cardano:.+
5+
6+
use std::fmt::{Display, Formatter};
7+
38
use anyhow::{anyhow, Context, Result};
49
use pallas::ledger::addresses::Address;
510

6-
/// Parses CIP-0134 URI and returns an address.
7-
///
8-
/// # Errors
9-
/// - Invalid URI.
11+
/// An URI in the CIP-0134 format.
1012
///
11-
/// # Examples
13+
/// See the [proposal] for more details.
1214
///
13-
/// ```
14-
/// use pallas::ledger::addresses::{Address, Network};
15-
/// use rbac_registration::cardano::cip509::utils::parse_cip0134_uri;
16-
///
17-
/// let uri = "web+cardano://addr/stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw";
18-
/// let Address::Stake(address) = parse_cip0134_uri(uri).unwrap() else {
19-
/// panic!("Unexpected address type");
20-
/// };
21-
/// assert_eq!(address.network(), Network::Mainnet);
22-
/// ```
23-
pub fn parse_cip0134_uri(uri: &str) -> Result<Address> {
24-
let bech32 = uri
25-
.strip_prefix("web+cardano://addr/")
26-
.ok_or_else(|| anyhow!("Missing schema part of URI"))?;
27-
Address::from_bech32(bech32).context("Unable to parse bech32 part of URI")
15+
/// [proposal]: https://github.com/cardano-foundation/CIPs/pull/888
16+
#[derive(Debug)]
17+
pub struct Cip0134Uri {
18+
/// A URI string.
19+
uri: String,
20+
/// An address parsed from the URI.
21+
address: Address,
22+
}
23+
24+
impl Cip0134Uri {
25+
/// Creates a new `Cip0134Uri` instance by parsing the given URI.
26+
///
27+
/// # Errors
28+
/// - Invalid URI.
29+
///
30+
/// # Examples
31+
///
32+
/// ```
33+
/// use rbac_registration::cardano::cip509::utils::Cip0134Uri;
34+
///
35+
/// let uri = "web+cardano://addr/stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw";
36+
/// let cip0134_uri = Cip0134Uri::parse(uri).unwrap();
37+
/// ```
38+
pub fn parse(uri: &str) -> Result<Self> {
39+
let bech32 = uri
40+
.strip_prefix("web+cardano://addr/")
41+
.ok_or_else(|| anyhow!("Missing schema part of URI"))?;
42+
let address = Address::from_bech32(bech32).context("Unable to parse bech32 part of URI")?;
43+
44+
Ok(Self {
45+
uri: uri.to_owned(),
46+
address,
47+
})
48+
}
49+
50+
/// Returns a URI string.
51+
///
52+
/// # Examples
53+
///
54+
/// ```
55+
/// use rbac_registration::cardano::cip509::utils::Cip0134Uri;
56+
///
57+
/// let uri = "web+cardano://addr/stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw";
58+
/// let cip0134_uri = Cip0134Uri::parse(uri).unwrap();
59+
/// assert_eq!(cip0134_uri.uri(), uri);
60+
#[must_use]
61+
pub fn uri(&self) -> &str {
62+
&self.uri
63+
}
64+
65+
/// Returns a URI string.
66+
///
67+
/// # Examples
68+
///
69+
/// ```
70+
/// use pallas::ledger::addresses::{Address, Network};
71+
/// use rbac_registration::cardano::cip509::utils::Cip0134Uri;
72+
///
73+
/// let uri = "web+cardano://addr/stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw";
74+
/// let cip0134_uri = Cip0134Uri::parse(uri).unwrap();
75+
/// let Address::Stake(address) = cip0134_uri.address() else {
76+
/// panic!("Unexpected address type");
77+
/// };
78+
/// assert_eq!(address.network(), Network::Mainnet);
79+
#[must_use]
80+
pub fn address(&self) -> &Address {
81+
&self.address
82+
}
83+
}
84+
85+
impl Display for Cip0134Uri {
86+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
87+
write!(f, "{}", self.uri())
88+
}
2889
}
2990

3091
#[cfg(test)]
@@ -35,23 +96,25 @@ mod tests {
3596

3697
#[test]
3798
fn invalid_prefix() {
99+
// cSpell:disable
38100
let test_uris = [
39101
"addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgse35a3x",
40102
"//addr/addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgse35a3x",
41103
"web+cardano:/addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgse35a3x",
42104
"somthing+unexpected://addr/addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgse35a3x",
43105
];
106+
// cSpell:enable
44107

45108
for uri in test_uris {
46-
let err = format!("{:?}", parse_cip0134_uri(uri).expect_err(&format!("{uri}")));
109+
let err = format!("{:?}", Cip0134Uri::parse(uri).expect_err(uri));
47110
assert_eq!("Missing schema part of URI", err);
48111
}
49112
}
50113

51114
#[test]
52115
fn invalid_bech32() {
53116
let uri = "web+cardano://addr/adr1qx2fxv2umyh";
54-
let err = format!("{:?}", parse_cip0134_uri(uri).unwrap_err());
117+
let err = format!("{:?}", Cip0134Uri::parse(uri).unwrap_err());
55118
assert!(err.starts_with("Unable to parse bech32 part of URI"));
56119
}
57120

@@ -76,7 +139,8 @@ mod tests {
76139
];
77140

78141
for (uri, network, payload) in test_data {
79-
let Address::Stake(address) = parse_cip0134_uri(uri).unwrap() else {
142+
let cip0134_uri = Cip0134Uri::parse(uri).expect(uri);
143+
let Address::Stake(address) = cip0134_uri.address else {
80144
panic!("Unexpected address type ({uri})");
81145
};
82146
assert_eq!(network, address.network());
@@ -102,10 +166,19 @@ mod tests {
102166
];
103167

104168
for (uri, network) in test_data {
105-
let Address::Shelley(address) = parse_cip0134_uri(uri).unwrap() else {
169+
let cip0134_uri = Cip0134Uri::parse(uri).expect(uri);
170+
let Address::Shelley(address) = cip0134_uri.address else {
106171
panic!("Unexpected address type ({uri})");
107172
};
108173
assert_eq!(network, address.network());
109174
}
110175
}
176+
177+
// The Display should return the original URI.
178+
#[test]
179+
fn display() {
180+
let uri = "web+cardano://addr/stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw";
181+
let cip0134_uri = Cip0134Uri::parse(uri).expect(uri);
182+
assert_eq!(uri, cip0134_uri.to_string());
183+
}
111184
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Utility functions for CIP-509
22
33
pub mod cip19;
4-
pub use cip134::parse_cip0134_uri;
4+
pub use cip134::Cip0134Uri;
55

66
mod cip134;

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

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use super::{
4040
},
4141
utils::{
4242
cip19::{compare_key_hash, extract_key_hash},
43-
parse_cip0134_uri,
43+
Cip0134Uri,
4444
},
4545
Cip509, TxInputHash, TxWitness,
4646
};
@@ -169,11 +169,12 @@ pub(crate) fn validate_stake_public_key(
169169

170170
// Extract the CIP19 hash and push into
171171
// array
172-
if let Ok(Address::Stake(a)) =
173-
parse_cip0134_uri(&addr)
174-
{
175-
pk_addrs
176-
.push(a.payload().as_hash().to_vec());
172+
if let Ok(uri) = Cip0134Uri::parse(&addr) {
173+
if let Address::Stake(a) = uri.address() {
174+
pk_addrs.push(
175+
a.payload().as_hash().to_vec(),
176+
);
177+
}
177178
}
178179
},
179180
Err(e) => {
@@ -222,8 +223,10 @@ pub(crate) fn validate_stake_public_key(
222223
if name.gn_type() == &c509_certificate::general_names::general_name::GeneralNameTypeRegistry::UniformResourceIdentifier {
223224
match name.gn_value() {
224225
GeneralNameValue::Text(s) => {
225-
if let Ok(Address::Stake(a)) = parse_cip0134_uri(s) {
226-
pk_addrs.push(a.payload().as_hash().to_vec());
226+
if let Ok(uri) = Cip0134Uri::parse(s) {
227+
if let Address::Stake(a) = uri.address() {
228+
pk_addrs.push(a.payload().as_hash().to_vec());
229+
}
227230
}
228231
},
229232
_ => {

0 commit comments

Comments
 (0)