Skip to content

Commit b403820

Browse files
committed
fix(cardano-blockchain-types): handle unset data
Signed-off-by: bkioshn <[email protected]>
1 parent 70d068c commit b403820

File tree

3 files changed

+77
-62
lines changed

3 files changed

+77
-62
lines changed

rust/cardano-blockchain-types/src/metadata/cip36/key_registration.rs

Lines changed: 43 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,33 @@ use super::voting_pk::VotingPubKey;
3030
#[derive(Clone, Default, Debug)]
3131
pub(crate) struct Cip36KeyRegistration {
3232
/// Is this CIP36 or CIP15 format.
33+
/// None if not either CIP36 or CIP15.
3334
pub is_cip36: Option<bool>,
3435
/// Voting public keys (called Delegations in the CIP-36 Spec).
3536
/// Field 1 in the CIP-36 61284 Spec.
3637
pub voting_pks: Vec<VotingPubKey>,
3738
/// Stake public key to associate with the voting keys.
3839
/// Field 2 in the CIP-36 61284 Spec.
39-
pub stake_pk: VerifyingKey,
40+
/// None if it is not set.
41+
pub stake_pk: Option<VerifyingKey>,
4042
/// Payment Address to associate with the voting keys.
4143
/// Field 3 in the CIP-36 61284 Spec.
44+
/// None if it is not set.
4245
pub payment_addr: Option<ShelleyAddress>,
4346
/// Nonce (nonce that has been slot corrected).
4447
/// Field 4 in the CIP-36 61284 Spec.
45-
pub nonce: u64,
48+
/// None if it is not set.
49+
pub nonce: Option<u64>,
4650
/// Registration Purpose (Always 0 for Catalyst).
4751
/// Field 5 in the CIP-36 61284 Spec.
48-
pub purpose: u64,
52+
/// None if it is not set.
53+
pub purpose: Option<u64>,
4954
/// Raw nonce (nonce that has not had slot correction applied).
50-
pub raw_nonce: u64,
55+
/// None if it is not set.
56+
pub raw_nonce: Option<u64>,
5157
/// Is payment address payable? (not a script)
52-
pub is_payable: bool,
58+
/// None if it is not set.
59+
pub is_payable: Option<bool>,
5360
}
5461

5562
/// Enum of CIP36 registration (61284) with its associated unsigned integer key.
@@ -74,9 +81,6 @@ impl Decode<'_, ()> for Cip36KeyRegistration {
7481

7582
let mut cip36_key_registration = Cip36KeyRegistration::default();
7683

77-
// Record of founded keys. Check for duplicate keys in the map
78-
let mut found_keys: Vec<Cip36KeyRegistrationKeys> = Vec::new();
79-
8084
// Record of errors found during decoding
8185
let mut err_report = Vec::new();
8286

@@ -86,11 +90,11 @@ impl Decode<'_, ()> for Cip36KeyRegistration {
8690
if let Some(key) = Cip36KeyRegistrationKeys::from_repr(key) {
8791
match key {
8892
Cip36KeyRegistrationKeys::VotingKey => {
89-
if found_keys.contains(&key) {
93+
if !cip36_key_registration.voting_pks.is_empty() {
9094
err_report.push(format!(
9195
"Duplicate key in CIP36 Key Registration voting key at item {} in map", index + 1),
9296
);
93-
continue;
97+
continue;
9498
}
9599
if let Some((is_cip36, voting_keys)) = decode_voting_key(d, &mut err_report)
96100
{
@@ -99,77 +103,67 @@ impl Decode<'_, ()> for Cip36KeyRegistration {
99103
}
100104
},
101105
Cip36KeyRegistrationKeys::StakePk => {
102-
if found_keys.contains(&key) {
106+
if cip36_key_registration.stake_pk.is_some() {
103107
err_report.push(format!(
104108
"Duplicate key in CIP36 Key Registration stake public key at item {} in map", index + 1),
105109
);
106110
continue;
107111
}
108112
if let Some(stake_pk) = decode_stake_pk(d, &mut err_report) {
109-
cip36_key_registration.stake_pk = stake_pk;
113+
cip36_key_registration.stake_pk = Some(stake_pk);
110114
}
111115
},
112116
Cip36KeyRegistrationKeys::PaymentAddr => {
113-
if found_keys.contains(&key) {
117+
if cip36_key_registration.payment_addr.is_some() {
114118
err_report.push(format!(
115119
"Duplicate key in CIP36 Key Registration payment address at item {} in map", index + 1),
116120
);
117121
continue;
118122
}
119123
if let Some(shelley_addr) = decode_payment_addr(d, &mut err_report) {
120124
cip36_key_registration.payment_addr = Some(shelley_addr.clone());
121-
cip36_key_registration.is_payable = !shelley_addr.payment().is_script();
125+
cip36_key_registration.is_payable = Some(!shelley_addr.payment().is_script());
122126
}
123127
},
124128
Cip36KeyRegistrationKeys::Nonce => {
125-
if found_keys.contains(&key) {
129+
if cip36_key_registration.raw_nonce.is_some() {
126130
err_report.push(format!(
127-
"Duplicate key in CIP36 Key Registration nonce at item {} in map",
128-
index + 1
129-
));
131+
"Duplicate key in CIP36 Key Registration nonce at item {} in map", index + 1),
132+
);
130133
continue;
131134
}
132135
if let Some(nonce) = decode_nonce(d, &mut err_report) {
133-
cip36_key_registration.nonce = nonce;
136+
cip36_key_registration.raw_nonce = Some(nonce);
134137
}
135138
},
136139
Cip36KeyRegistrationKeys::Purpose => {
137-
if found_keys.contains(&key) {
140+
if cip36_key_registration.purpose.is_some() {
138141
err_report.push(format!(
139-
"Duplicate key in CIP36 Key Registration purpose at item {} in map",
140-
index + 1
141-
));
142+
"Duplicate key in CIP36 Key Registration purpose at item {} in map", index + 1),
143+
);
142144
continue;
143145
}
144146
if let Some(purpose) = decode_purpose(d, &mut err_report) {
145-
cip36_key_registration.purpose = purpose;
147+
cip36_key_registration.purpose = Some(purpose);
146148
}
147149
},
148150
}
149-
150-
// Update the founded keys.
151-
found_keys.push(key);
152151
}
153152
}
154153

155-
if !found_keys.contains(&Cip36KeyRegistrationKeys::VotingKey) {
156-
err_report
157-
.push("Missing required key in CIP36 Key Registration: Voting Key".to_string());
154+
if cip36_key_registration.voting_pks.is_empty() {
155+
err_report.push("Missing required key in CIP36 Key Registration: Voting Key".to_string());
158156
}
159157

160-
if !found_keys.contains(&Cip36KeyRegistrationKeys::StakePk) {
161-
err_report.push(
162-
"Missing required key in CIP36 Key Registration: Stake Public Key".to_string(),
163-
);
158+
if cip36_key_registration.stake_pk.is_none() {
159+
err_report.push("Missing required key in CIP36 Key Registration: Stake Public Key".to_string());
164160
}
165161

166-
if !found_keys.contains(&Cip36KeyRegistrationKeys::PaymentAddr) {
167-
err_report.push(
168-
"Missing required key in CIP36 Key Registration: Payment Address".to_string(),
169-
);
162+
if cip36_key_registration.payment_addr.is_none() {
163+
err_report.push("Missing required key in CIP36 Key Registration: Payment Address".to_string());
170164
}
171165

172-
if !found_keys.contains(&Cip36KeyRegistrationKeys::Nonce) {
166+
if cip36_key_registration.raw_nonce.is_none() {
173167
err_report.push("Missing required key in CIP36 Key Registration: Nonce".to_string());
174168
}
175169

@@ -459,4 +453,14 @@ mod tests {
459453
assert!(!is_cip36.unwrap());
460454
assert_eq!(voting_pk.len(), 1);
461455
}
456+
457+
#[test]
458+
fn test_decode_nonce() {
459+
let hex_data = hex::decode("1A014905D1").expect("cannot decode hex");
460+
let mut decoder = Decoder::new(&hex_data);
461+
let mut err_report = Vec::new();
462+
let nonce = decode_nonce(&mut decoder, &mut err_report);
463+
assert!(err_report.is_empty());
464+
assert_eq!(nonce.unwrap(), 21562833);
465+
}
462466
}

rust/cardano-blockchain-types/src/metadata/cip36/mod.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ impl Cip36 {
7575

7676
let key_registration = match Cip36KeyRegistration::decode(&mut key_registration, &mut ()) {
7777
Ok(mut metadata) => {
78-
let nonce = if is_catalyst_strict && metadata.raw_nonce > slot {
79-
slot
78+
let nonce = if is_catalyst_strict && metadata.raw_nonce > Some(slot) {
79+
Some(slot)
8080
} else {
8181
metadata.raw_nonce
8282
};
@@ -133,8 +133,8 @@ impl Cip36 {
133133

134134
/// Get the stake public key from the registration.
135135
#[must_use]
136-
pub fn stake_pk(&self) -> VerifyingKey {
137-
self.key_registration.stake_pk
136+
pub fn stake_pk(&self) -> Option<&VerifyingKey> {
137+
self.key_registration.stake_pk.as_ref()
138138
}
139139

140140
/// Get the payment address from the registration.
@@ -145,25 +145,25 @@ impl Cip36 {
145145

146146
/// Get the nonce from the registration.
147147
#[must_use]
148-
pub fn nonce(&self) -> u64 {
148+
pub fn nonce(&self) -> Option<u64> {
149149
self.key_registration.nonce
150150
}
151151

152152
/// Get the purpose from the registration.
153153
#[must_use]
154-
pub fn purpose(&self) -> u64 {
154+
pub fn purpose(&self) -> Option<u64> {
155155
self.key_registration.purpose
156156
}
157157

158158
/// Get the raw nonce from the registration.
159159
#[must_use]
160-
pub fn raw_nonce(&self) -> u64 {
160+
pub fn raw_nonce(&self) -> Option<u64> {
161161
self.key_registration.raw_nonce
162162
}
163163

164164
/// Is the payment address in the registration payable?
165165
#[must_use]
166-
pub fn is_payable(&self) -> bool {
166+
pub fn is_payable(&self) -> Option<bool> {
167167
self.key_registration.is_payable
168168
}
169169

rust/cardano-blockchain-types/src/metadata/cip36/validation.rs

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,16 @@ pub(crate) fn validate_signature(
2929
return false;
3030
};
3131

32-
if let Ok(()) = cip36.stake_pk().verify_strict(hash.as_bytes(), &sig) {
33-
true
32+
if let Some(stake_pk) = cip36.stake_pk() {
33+
if let Ok(()) = stake_pk.verify_strict(hash.as_bytes(), &sig) {
34+
return true;
35+
} else {
36+
validation_report.push("Validate CIP36 Signature, cannot verify signature".to_string());
37+
return false;
38+
}
3439
} else {
35-
validation_report.push("Validate CIP36 Signature, cannot verify signature".to_string());
36-
false
40+
validation_report.push("Validate CIP36 Signature, stake public key is missing".to_string());
41+
return false;
3742
}
3843
}
3944

@@ -73,14 +78,19 @@ pub(crate) fn validate_voting_keys(cip36: &Cip36, validation_report: &mut Vec<St
7378

7479
/// Validate the purpose.
7580
pub(crate) fn validate_purpose(cip36: &Cip36, validation_report: &mut Vec<String>) -> bool {
76-
if cip36.is_strict_catalyst() && cip36.purpose() != PROJECT_CATALYST_PURPOSE {
77-
validation_report.push(format!(
78-
"Validate CIP-36 Purpose, registration contains unknown purpose: {}",
79-
cip36.purpose()
80-
));
81-
return false;
81+
if let Some(purpose) = cip36.purpose() {
82+
if cip36.is_strict_catalyst() && purpose != PROJECT_CATALYST_PURPOSE {
83+
validation_report.push(format!(
84+
"Validate CIP-36 Purpose, registration contains unknown purpose: {purpose}"
85+
));
86+
return false;
87+
}
88+
true
89+
} else {
90+
validation_report
91+
.push("Validate CIP-36 Purpose, registration purpose is missing".to_string());
92+
false
8293
}
83-
true
8494
}
8595

8696
#[cfg(test)]
@@ -186,20 +196,21 @@ mod tests {
186196

187197
#[test]
188198
fn test_validate_purpose() {
189-
let cip36 = create_empty_cip36(true);
199+
let mut cip36 = create_empty_cip36(true);
200+
cip36.key_registration.purpose = Some(0);
190201
let mut report = Vec::new();
191202

192203
let valid = validate_purpose(&cip36, &mut report);
193204

194205
assert_eq!(report.len(), 0);
195-
assert_eq!(cip36.purpose(), 0);
206+
assert_eq!(cip36.purpose(), Some(0));
196207
assert!(valid);
197208
}
198209

199210
#[test]
200211
fn test_validate_invalid_purpose() {
201212
let mut cip36 = create_empty_cip36(true);
202-
cip36.key_registration.purpose = 1;
213+
cip36.key_registration.purpose = Some(1);
203214
let mut report = Vec::new();
204215

205216
let valid = validate_purpose(&cip36, &mut report);
@@ -209,7 +220,7 @@ mod tests {
209220
.first()
210221
.expect("Failed to get the first index")
211222
.contains("unknown purpose"));
212-
assert_eq!(cip36.purpose(), 1);
223+
assert_eq!(cip36.purpose(), Some(1));
213224
assert!(!valid);
214225
}
215226
}

0 commit comments

Comments
 (0)