1use crate::{
3 PlutusDataExtensions, VersionedDatum, VersionedDatumWithLegacy, VersionedGenericDatum,
4 candidate_keys::*,
5};
6use cardano_serialization_lib::*;
7use sidechain_domain::*;
8use sp_core::crypto::key_types::{AURA, GRANDPA};
9
10use crate::{DataDecodingError, DecodingResult};
11
12#[derive(Clone, Debug, PartialEq, Eq)]
41pub enum RegisterValidatorDatum {
42 V0 {
44 stake_ownership: AdaBasedStaking,
46 sidechain_pub_key: SidechainPublicKey,
48 sidechain_signature: SidechainSignature,
50 registration_utxo: UtxoId,
53 own_pkh: MainchainKeyHash,
56 aura_pub_key: AuraPublicKey,
58 grandpa_pub_key: GrandpaPublicKey,
60 },
61 V1 {
63 stake_ownership: AdaBasedStaking,
65 sidechain_pub_key: SidechainPublicKey,
67 sidechain_signature: SidechainSignature,
69 registration_utxo: UtxoId,
72 own_pkh: MainchainKeyHash,
75 keys: CandidateKeys,
77 },
78}
79
80impl TryFrom<PlutusData> for RegisterValidatorDatum {
81 type Error = DataDecodingError;
82
83 fn try_from(datum: PlutusData) -> DecodingResult<Self> {
84 Self::decode(&datum)
85 }
86}
87
88impl VersionedDatumWithLegacy for RegisterValidatorDatum {
89 const NAME: &str = "RegisterValidatorDatum";
90
91 fn decode_legacy(data: &PlutusData) -> Result<Self, String> {
92 decode_legacy_register_validator_datum(data).ok_or_else(|| "Invalid data".into())
93 }
94
95 fn decode_versioned(
96 version: u64,
97 datum: &PlutusData,
98 appendix: &PlutusData,
99 ) -> Result<Self, String> {
100 match version {
101 0 => decode_v0_register_validator_datum(datum, appendix)
102 .ok_or("Can not parse appendix".to_string()),
103 1 => decode_v1_register_validator_datum(datum, appendix)
104 .ok_or("Can not parse appendix".to_string()),
105 _ => Err(format!("Unknown version: {version}")),
106 }
107 }
108}
109
110pub fn candidate_registration_to_plutus_data(
112 candidate_registration: &CandidateRegistration,
113) -> PlutusData {
114 let datum = if candidate_registration.keys.has_only_aura_and_grandpa_keys() {
115 RegisterValidatorDatum::V0 {
116 stake_ownership: candidate_registration.stake_ownership.clone(),
117 sidechain_pub_key: candidate_registration.partner_chain_pub_key.clone(),
118 sidechain_signature: candidate_registration.partner_chain_signature.clone(),
119 registration_utxo: candidate_registration.registration_utxo,
120 own_pkh: candidate_registration.own_pkh,
121 aura_pub_key: AuraPublicKey(candidate_registration.keys.find_or_empty(AURA)),
122 grandpa_pub_key: GrandpaPublicKey(candidate_registration.keys.find_or_empty(GRANDPA)),
123 }
124 } else {
125 RegisterValidatorDatum::V1 {
126 stake_ownership: candidate_registration.stake_ownership.clone(),
127 sidechain_pub_key: candidate_registration.partner_chain_pub_key.clone(),
128 sidechain_signature: candidate_registration.partner_chain_signature.clone(),
129 registration_utxo: candidate_registration.registration_utxo,
130 own_pkh: candidate_registration.own_pkh,
131 keys: candidate_registration.keys.clone(),
132 }
133 };
134 datum.into()
135}
136
137impl From<RegisterValidatorDatum> for CandidateRegistration {
138 fn from(value: RegisterValidatorDatum) -> Self {
139 match value {
140 RegisterValidatorDatum::V0 {
141 stake_ownership,
142 sidechain_pub_key,
143 sidechain_signature,
144 registration_utxo,
145 own_pkh,
146 aura_pub_key,
147 grandpa_pub_key,
148 } => CandidateRegistration {
149 stake_ownership,
150 partner_chain_pub_key: sidechain_pub_key,
151 partner_chain_signature: sidechain_signature,
152 registration_utxo,
153 own_pkh,
154 keys: CandidateKeys(vec![aura_pub_key.into(), grandpa_pub_key.into()]),
155 },
156 RegisterValidatorDatum::V1 {
157 stake_ownership,
158 sidechain_pub_key,
159 sidechain_signature,
160 registration_utxo,
161 own_pkh,
162 keys,
163 } => CandidateRegistration {
164 stake_ownership,
165 partner_chain_pub_key: sidechain_pub_key,
166 partner_chain_signature: sidechain_signature,
167 registration_utxo,
168 own_pkh,
169 keys,
170 },
171 }
172 }
173}
174
175fn decode_v0_register_validator_datum(
176 datum: &PlutusData,
177 appendix: &PlutusData,
178) -> Option<RegisterValidatorDatum> {
179 let fields = appendix
180 .as_constr_plutus_data()
181 .filter(|datum| datum.alternative().is_zero())
182 .filter(|datum| datum.data().len() >= 6)?
183 .data();
184 let stake_ownership = decode_ada_based_staking_datum(fields.get(0))?;
185 let sidechain_pub_key = fields.get(1).as_bytes().map(SidechainPublicKey)?;
186 let sidechain_signature = fields.get(2).as_bytes().map(SidechainSignature)?;
187 let registration_utxo = decode_utxo_id_datum(fields.get(3))?;
188 let aura_pub_key = fields.get(4).as_bytes().map(AuraPublicKey)?;
189 let grandpa_pub_key = fields.get(5).as_bytes().map(GrandpaPublicKey)?;
190
191 let own_pkh = MainchainKeyHash(datum.as_bytes()?.try_into().ok()?);
192 Some(RegisterValidatorDatum::V0 {
193 stake_ownership,
194 sidechain_pub_key,
195 sidechain_signature,
196 registration_utxo,
197 own_pkh,
198 aura_pub_key,
199 grandpa_pub_key,
200 })
201}
202
203fn decode_v1_register_validator_datum(
204 datum: &PlutusData,
205 appendix: &PlutusData,
206) -> Option<RegisterValidatorDatum> {
207 let fields = appendix
208 .as_constr_plutus_data()
209 .filter(|datum| datum.alternative().is_zero())
210 .filter(|datum| datum.data().len() == 5)?
211 .data();
212
213 let stake_ownership = decode_ada_based_staking_datum(fields.get(0))?;
214 let sidechain_pub_key = fields.get(1).as_bytes().map(SidechainPublicKey)?;
215 let sidechain_signature = fields.get(2).as_bytes().map(SidechainSignature)?;
216 let registration_utxo = decode_utxo_id_datum(fields.get(3))?;
217 let keys = decode_candidate_keys(&fields.get(4))?;
218
219 let own_pkh = MainchainKeyHash(datum.as_bytes()?.try_into().ok()?);
220 Some(RegisterValidatorDatum::V1 {
221 stake_ownership,
222 sidechain_pub_key,
223 sidechain_signature,
224 registration_utxo,
225 own_pkh,
226 keys,
227 })
228}
229
230fn decode_legacy_register_validator_datum(datum: &PlutusData) -> Option<RegisterValidatorDatum> {
232 let fields = datum
233 .as_constr_plutus_data()
234 .filter(|datum| datum.alternative().is_zero())
235 .filter(|datum| datum.data().len() >= 7)?
236 .data();
237 let stake_ownership = decode_ada_based_staking_datum(fields.get(0))?;
238 let sidechain_pub_key = fields.get(1).as_bytes().map(SidechainPublicKey)?;
239 let sidechain_signature = fields.get(2).as_bytes().map(SidechainSignature)?;
240 let registration_utxo = decode_utxo_id_datum(fields.get(3))?;
241 let own_pkh = MainchainKeyHash(fields.get(4).as_bytes()?.try_into().ok()?);
242 let aura_pub_key = fields.get(5).as_bytes().map(AuraPublicKey)?;
243 let grandpa_pub_key = fields.get(6).as_bytes().map(GrandpaPublicKey)?;
244 Some(RegisterValidatorDatum::V0 {
245 stake_ownership,
246 sidechain_pub_key,
247 sidechain_signature,
248 registration_utxo,
249 own_pkh,
250 aura_pub_key,
251 grandpa_pub_key,
252 })
253}
254
255fn decode_ada_based_staking_datum(datum: PlutusData) -> Option<AdaBasedStaking> {
256 let fields = datum
257 .as_constr_plutus_data()
258 .filter(|datum| datum.alternative().is_zero())
259 .filter(|datum| datum.data().len() >= 2)?
260 .data();
261 let pub_key = TryFrom::try_from(fields.get(0).as_bytes()?).ok()?;
262 let signature = MainchainSignature(fields.get(1).as_bytes()?.try_into().ok()?);
263 Some(AdaBasedStaking { pub_key, signature })
264}
265fn decode_utxo_id_datum(datum: PlutusData) -> Option<UtxoId> {
266 let fields = datum
267 .as_constr_plutus_data()
268 .filter(|datum| datum.alternative().is_zero())
269 .filter(|datum| datum.data().len() >= 2)?
270 .data();
271 let tx_hash = decode_tx_hash_datum(fields.get(0))?;
272 let index = UtxoIndex(fields.get(1).as_u16()?);
273 Some(UtxoId { tx_hash, index })
274}
275fn decode_tx_hash_datum(datum: PlutusData) -> Option<McTxHash> {
277 let constructor_datum = datum
278 .as_constr_plutus_data()
279 .filter(|datum| datum.alternative().is_zero())
280 .filter(|datum| datum.data().len() >= 1)?;
281 let bytes = constructor_datum.data().get(0).as_bytes()?;
282
283 Some(McTxHash(TryFrom::try_from(bytes).ok()?))
284}
285
286impl From<RegisterValidatorDatum> for PlutusData {
287 fn from(value: RegisterValidatorDatum) -> Self {
288 match value {
289 RegisterValidatorDatum::V0 {
290 stake_ownership,
291 sidechain_pub_key,
292 sidechain_signature,
293 registration_utxo,
294 own_pkh,
295 aura_pub_key,
296 grandpa_pub_key,
297 } => {
298 let mut appendix_fields = PlutusList::new();
299 appendix_fields.add(&stake_ownership_to_plutus_data(stake_ownership));
300 appendix_fields.add(&PlutusData::new_bytes(sidechain_pub_key.0));
301 appendix_fields.add(&PlutusData::new_bytes(sidechain_signature.0));
302 appendix_fields.add(&utxo_id_to_plutus_data(registration_utxo));
303 appendix_fields.add(&PlutusData::new_bytes(aura_pub_key.0));
304 appendix_fields.add(&PlutusData::new_bytes(grandpa_pub_key.0));
305 let appendix = ConstrPlutusData::new(&BigNum::zero(), &appendix_fields);
306 VersionedGenericDatum {
307 datum: PlutusData::new_bytes(own_pkh.0.to_vec()),
308 appendix: PlutusData::new_constr_plutus_data(&appendix),
309 version: 0,
310 }
311 .into()
312 },
313 RegisterValidatorDatum::V1 {
314 stake_ownership,
315 sidechain_pub_key,
316 sidechain_signature,
317 registration_utxo,
318 own_pkh,
319 keys,
320 } => {
321 let mut appendix_fields = PlutusList::new();
322 appendix_fields.add(&stake_ownership_to_plutus_data(stake_ownership));
323 appendix_fields.add(&PlutusData::new_bytes(sidechain_pub_key.0));
324 appendix_fields.add(&PlutusData::new_bytes(sidechain_signature.0));
325 appendix_fields.add(&utxo_id_to_plutus_data(registration_utxo));
326 appendix_fields.add(&candidate_keys_to_plutus(&keys));
327 let appendix = ConstrPlutusData::new(&BigNum::zero(), &appendix_fields);
328 VersionedGenericDatum {
329 datum: PlutusData::new_bytes(own_pkh.0.to_vec()),
330 appendix: PlutusData::new_constr_plutus_data(&appendix),
331 version: 1,
332 }
333 .into()
334 },
335 }
336 }
337}
338
339fn stake_ownership_to_plutus_data(v: AdaBasedStaking) -> PlutusData {
340 let mut fields = PlutusList::new();
341 fields.add(&PlutusData::new_bytes(v.pub_key.0.to_vec()));
342 fields.add(&PlutusData::new_bytes(v.signature.0.to_vec()));
343 PlutusData::new_constr_plutus_data(&ConstrPlutusData::new(&BigNum::zero(), &fields))
344}
345
346fn utxo_id_to_plutus_data(v: UtxoId) -> PlutusData {
347 let mut fields = PlutusList::new();
348 fields.add(&tx_hash_to_plutus_data(v.tx_hash));
349 fields.add(&PlutusData::new_integer(&v.index.0.into()));
350 PlutusData::new_constr_plutus_data(&ConstrPlutusData::new(&BigNum::zero(), &fields))
351}
352
353fn tx_hash_to_plutus_data(v: McTxHash) -> PlutusData {
354 let mut fields = PlutusList::new();
355 fields.add(&PlutusData::new_bytes(v.0.to_vec()));
356 PlutusData::new_constr_plutus_data(&ConstrPlutusData::new(&BigNum::zero(), &fields))
357}
358
359#[cfg(test)]
360mod tests {
361 use super::*;
362 use crate::test_helpers::*;
363 use hex_literal::hex;
364 use pretty_assertions::assert_eq;
365
366 fn test_datum_v0() -> RegisterValidatorDatum {
367 RegisterValidatorDatum::V0 {
368 stake_ownership: AdaBasedStaking {
369 pub_key: StakePoolPublicKey(hex!("bfbee74ab533f40979101057f96de62e95233f2a5216eb16b54106f09fd7350d")),
370 signature: MainchainSignature(hex!("28d1c3b7df297a60d24a3f88bc53d7029a8af35e8dd876764fd9e7a24203a3482a98263cc8ba2ddc7dc8e7faea31c2e7bad1f00e28c43bc863503e3172dc6b0a").into()),
371 },
372 sidechain_pub_key: SidechainPublicKey(hex!("02fe8d1eb1bcb3432b1db5833ff5f2226d9cb5e65cee430558c18ed3a3c86ce1af").into()),
373 sidechain_signature: SidechainSignature(hex!("f8ec6c7f935d387aaa1693b3bf338cbb8f53013da8a5a234f9c488bacac01af259297e69aee0df27f553c0a1164df827d016125c16af93c99be2c19f36d2f66e").into()),
374 registration_utxo: UtxoId {
375 tx_hash: McTxHash(hex!("cdefe62b0a0016c2ccf8124d7dda71f6865283667850cc7b471f761d2bc1eb13")),
376 index: UtxoIndex(1),
377 },
378 own_pkh: MainchainKeyHash(hex!("aabbccddeeff00aabbccddeeff00aabbccddeeff00aabbccddeeff00")),
379 aura_pub_key: AuraPublicKey(hex!("d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d").into()),
380 grandpa_pub_key: GrandpaPublicKey(hex!("88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee").into()),
381 }
382 }
383
384 fn test_datum_v1() -> RegisterValidatorDatum {
385 RegisterValidatorDatum::V1 {
386 stake_ownership: AdaBasedStaking {
387 pub_key: StakePoolPublicKey(hex!("bfbee74ab533f40979101057f96de62e95233f2a5216eb16b54106f09fd7350d")),
388 signature: MainchainSignature(hex!("28d1c3b7df297a60d24a3f88bc53d7029a8af35e8dd876764fd9e7a24203a3482a98263cc8ba2ddc7dc8e7faea31c2e7bad1f00e28c43bc863503e3172dc6b0a").into()),
389 },
390 sidechain_pub_key: SidechainPublicKey(hex!("02fe8d1eb1bcb3432b1db5833ff5f2226d9cb5e65cee430558c18ed3a3c86ce1af").into()),
391 sidechain_signature: SidechainSignature(hex!("f8ec6c7f935d387aaa1693b3bf338cbb8f53013da8a5a234f9c488bacac01af259297e69aee0df27f553c0a1164df827d016125c16af93c99be2c19f36d2f66e").into()),
392 registration_utxo: UtxoId {
393 tx_hash: McTxHash(hex!("cdefe62b0a0016c2ccf8124d7dda71f6865283667850cc7b471f761d2bc1eb13")),
394 index: UtxoIndex(1),
395 },
396 own_pkh: MainchainKeyHash(hex!("aabbccddeeff00aabbccddeeff00aabbccddeeff00aabbccddeeff00")),
397 keys: CandidateKeys(vec![
398 CandidateKey { id: [0; 4], bytes: [1; 32].to_vec() },
399 CandidateKey { id: [2; 4], bytes: [3; 32].to_vec() },
400 ]),
401 }
402 }
403
404 #[test]
405 fn valid_legacy_registration() {
406 let plutus_data = test_plutus_data!({
407 "constructor": 0,
408 "fields": [
409 {
410 "constructor": 0,
411 "fields": [
412 { "bytes": "bfbee74ab533f40979101057f96de62e95233f2a5216eb16b54106f09fd7350d" },
413 { "bytes": "28d1c3b7df297a60d24a3f88bc53d7029a8af35e8dd876764fd9e7a24203a3482a98263cc8ba2ddc7dc8e7faea31c2e7bad1f00e28c43bc863503e3172dc6b0a" }
414 ]
415 },
416 { "bytes": "02fe8d1eb1bcb3432b1db5833ff5f2226d9cb5e65cee430558c18ed3a3c86ce1af" },
417 { "bytes": "f8ec6c7f935d387aaa1693b3bf338cbb8f53013da8a5a234f9c488bacac01af259297e69aee0df27f553c0a1164df827d016125c16af93c99be2c19f36d2f66e" },
418 {
419 "fields": [
420 {
421 "constructor": 0,
422 "fields": [ { "bytes": "cdefe62b0a0016c2ccf8124d7dda71f6865283667850cc7b471f761d2bc1eb13"} ]
423 },
424 { "int": 1 }
425 ],
426 "constructor": 0
427 },
428 { "bytes": "aabbccddeeff00aabbccddeeff00aabbccddeeff00aabbccddeeff00" },
429 { "bytes": "d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d" },
430 { "bytes": "88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee" }
431 ]
432 });
433 assert_eq!(RegisterValidatorDatum::try_from(plutus_data).unwrap(), test_datum_v0())
434 }
435
436 fn test_versioned_datum_v0_json() -> serde_json::Value {
437 serde_json::json!({
438 "list": [
439 { "bytes": "aabbccddeeff00aabbccddeeff00aabbccddeeff00aabbccddeeff00" },
440 {
441 "constructor": 0,
442 "fields": [
443 {
444 "constructor": 0,
445 "fields": [
446 { "bytes": "bfbee74ab533f40979101057f96de62e95233f2a5216eb16b54106f09fd7350d" },
447 { "bytes": "28d1c3b7df297a60d24a3f88bc53d7029a8af35e8dd876764fd9e7a24203a3482a98263cc8ba2ddc7dc8e7faea31c2e7bad1f00e28c43bc863503e3172dc6b0a" }
448 ]
449 },
450 { "bytes": "02fe8d1eb1bcb3432b1db5833ff5f2226d9cb5e65cee430558c18ed3a3c86ce1af" },
451 { "bytes": "f8ec6c7f935d387aaa1693b3bf338cbb8f53013da8a5a234f9c488bacac01af259297e69aee0df27f553c0a1164df827d016125c16af93c99be2c19f36d2f66e" },
452 {
453 "fields": [
454 {
455 "constructor": 0,
456 "fields": [ { "bytes": "cdefe62b0a0016c2ccf8124d7dda71f6865283667850cc7b471f761d2bc1eb13"} ]
457 },
458 { "int": 1 }
459 ],
460 "constructor": 0
461 },
462 { "bytes": "d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d" },
463 { "bytes": "88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee" }
464 ]
465 },
466 { "int": 0 }
467 ]
468 })
469 }
470
471 fn test_versioned_datum_v1_json() -> serde_json::Value {
472 serde_json::json!({
473 "list": [
474 {"bytes": "aabbccddeeff00aabbccddeeff00aabbccddeeff00aabbccddeeff00" },
475 {
476 "constructor": 0,
477 "fields": [
478 {
479 "constructor": 0,
480 "fields": [
481 {"bytes": "bfbee74ab533f40979101057f96de62e95233f2a5216eb16b54106f09fd7350d" },
482 {"bytes": "28d1c3b7df297a60d24a3f88bc53d7029a8af35e8dd876764fd9e7a24203a3482a98263cc8ba2ddc7dc8e7faea31c2e7bad1f00e28c43bc863503e3172dc6b0a" }
483 ]
484 },
485 {"bytes": "02fe8d1eb1bcb3432b1db5833ff5f2226d9cb5e65cee430558c18ed3a3c86ce1af"},
486 {"bytes": "f8ec6c7f935d387aaa1693b3bf338cbb8f53013da8a5a234f9c488bacac01af259297e69aee0df27f553c0a1164df827d016125c16af93c99be2c19f36d2f66e" },
487 {
488 "fields": [
489 {
490 "constructor": 0,
491 "fields": [ {"bytes": "cdefe62b0a0016c2ccf8124d7dda71f6865283667850cc7b471f761d2bc1eb13"} ]
492 },
493 {"int": 1 }
494 ],
495 "constructor": 0
496 },
497 {"list": [
498 {"list": [{"bytes": "00000000"}, {"bytes": "0101010101010101010101010101010101010101010101010101010101010101" }]},
499 {"list": [{"bytes": "02020202"}, {"bytes": "0303030303030303030303030303030303030303030303030303030303030303" }]}
500 ]}
501 ]
502 },
503 {"int": 1 }
504 ]
505 })
506 }
507
508 #[test]
509 fn valid_v0_registration_from_plutus_data() {
510 let plutus_data = json_to_plutus_data(test_versioned_datum_v0_json());
511 assert_eq!(RegisterValidatorDatum::try_from(plutus_data).unwrap(), test_datum_v0())
512 }
513
514 #[test]
515 fn valid_v1_registration_from_plutus_data() {
516 let plutus_data = json_to_plutus_data(test_versioned_datum_v1_json());
517 assert_eq!(RegisterValidatorDatum::try_from(plutus_data).unwrap(), test_datum_v1())
518 }
519
520 #[test]
521 fn v0_registration_to_plutus_data() {
522 let plutus_data: PlutusData = test_datum_v0().into();
523 assert_eq!(plutus_data_to_json(plutus_data), test_versioned_datum_v0_json())
524 }
525
526 #[test]
527 fn valid_v1_registration_to_plutus_data() {
528 let plutus_data: PlutusData = test_datum_v1().into();
529 assert_eq!(plutus_data_to_json(plutus_data), test_versioned_datum_v1_json())
530 }
531}