1use cardano_serialization_lib::{BigNum, PlutusData, PlutusList};
3use sidechain_domain::*;
4
5use crate::{
6 DataDecodingError, DecodingResult, VersionedDatum, VersionedDatumWithLegacy,
7 VersionedGenericDatum, candidate_keys::*,
8};
9
10#[derive(Clone, Debug, PartialEq)]
11pub enum PermissionedCandidateDatums {
13 V0(Vec<PermissionedCandidateDatumV0>),
15 V1(Vec<PermissionedCandidateDatumV1>),
17}
18
19#[derive(Clone, Debug, PartialEq)]
20pub struct PermissionedCandidateDatumV0 {
22 pub sidechain_public_key: SidechainPublicKey,
24 pub aura_public_key: AuraPublicKey,
26 pub grandpa_public_key: GrandpaPublicKey,
28}
29
30impl From<PermissionedCandidateDatumV1> for PermissionedCandidateData {
31 fn from(value: PermissionedCandidateDatumV1) -> Self {
34 let PermissionedCandidateDatumV1 { partner_chains_key, keys } = value;
35 PermissionedCandidateData { sidechain_public_key: partner_chains_key, keys }
36 }
37}
38
39#[derive(Clone, Debug, PartialEq)]
40pub struct PermissionedCandidateDatumV1 {
42 pub partner_chains_key: SidechainPublicKey,
44 pub keys: CandidateKeys,
46}
47
48impl TryFrom<PlutusData> for PermissionedCandidateDatums {
49 type Error = DataDecodingError;
50 fn try_from(datum: PlutusData) -> DecodingResult<Self> {
51 Self::decode(&datum)
52 }
53}
54
55impl From<PermissionedCandidateDatumV0> for PermissionedCandidateData {
56 fn from(value: PermissionedCandidateDatumV0) -> Self {
57 Self {
58 sidechain_public_key: value.sidechain_public_key,
59 keys: CandidateKeys(vec![
60 value.aura_public_key.into(),
61 value.grandpa_public_key.into(),
62 ]),
63 }
64 }
65}
66
67impl From<PermissionedCandidateDatums> for Vec<PermissionedCandidateData> {
68 fn from(value: PermissionedCandidateDatums) -> Self {
69 match value {
70 PermissionedCandidateDatums::V0(v) => v.into_iter().map(|d| d.into()).collect(),
71 PermissionedCandidateDatums::V1(v) => v.into_iter().map(|d| d.into()).collect(),
72 }
73 }
74}
75
76pub fn permissioned_candidates_to_plutus_data(
133 candidates: &[PermissionedCandidateData],
134) -> PlutusData {
135 fn candidates_to_plutus_data_v0(candidates: &[PermissionedCandidateData]) -> PlutusData {
136 let mut list = PlutusList::new();
137 for candidate in candidates {
138 let mut candidate_datum = PlutusList::new();
139 candidate_datum.add(&PlutusData::new_bytes(candidate.sidechain_public_key.0.clone()));
140 for key in candidate.keys.0.iter() {
141 candidate_datum.add(&PlutusData::new_bytes(key.bytes.clone()));
142 }
143 list.add(&PlutusData::new_list(&candidate_datum));
144 }
145 let appendix = PlutusData::new_list(&list);
146 VersionedGenericDatum {
147 datum: PlutusData::new_empty_constr_plutus_data(&BigNum::zero()),
148 appendix,
149 version: 0,
150 }
151 .into()
152 }
153
154 fn candidates_to_plutus_data_v1(candidates: &[PermissionedCandidateData]) -> PlutusData {
155 let mut list = PlutusList::new();
156 for candidate in candidates {
157 let mut candidate_datum = PlutusList::new();
158 candidate_datum.add(&PlutusData::new_bytes(candidate.sidechain_public_key.0.clone()));
159 candidate_datum.add(&candidate_keys_to_plutus(&candidate.keys));
160 list.add(&PlutusData::new_list(&candidate_datum));
161 }
162 VersionedGenericDatum {
163 datum: PlutusData::new_empty_constr_plutus_data(&BigNum::zero()),
164 appendix: PlutusData::new_list(&list),
165 version: 1,
166 }
167 .into()
168 }
169
170 if candidates.iter().all(|c| c.keys.has_only_aura_and_grandpa_keys()) {
171 candidates_to_plutus_data_v0(candidates)
172 } else {
173 candidates_to_plutus_data_v1(candidates)
174 }
175}
176
177impl PermissionedCandidateDatums {
178 fn decode_v1_appendix(data: &PlutusData) -> Result<Self, String> {
180 let permissioned_candidates = data
181 .as_list()
182 .and_then(|list_datums| {
183 list_datums
184 .into_iter()
185 .map(decode_v1_candidate_datum)
186 .collect::<Option<Vec<PermissionedCandidateDatumV1>>>()
187 })
188 .ok_or("Expected [[ByteString, ByteString], [[ByteString, ByteString], ... ]]")?;
189 Ok(Self::V1(permissioned_candidates))
190 }
191}
192
193impl VersionedDatumWithLegacy for PermissionedCandidateDatums {
194 const NAME: &str = "PermissionedCandidateDatums";
195
196 fn decode_legacy(data: &PlutusData) -> Result<Self, String> {
198 let permissioned_candidates = data
199 .as_list()
200 .and_then(|list_datums| {
201 list_datums
202 .into_iter()
203 .map(decode_legacy_candidate_datum)
204 .collect::<Option<Vec<PermissionedCandidateDatumV0>>>()
205 })
206 .ok_or("Expected [[ByteString, ByteString, ByteString]]")?;
207
208 Ok(Self::V0(permissioned_candidates))
209 }
210
211 fn decode_versioned(
212 version: u64,
213 _datum: &PlutusData,
214 appendix: &PlutusData,
215 ) -> Result<Self, String> {
216 match version {
217 0 => PermissionedCandidateDatums::decode_legacy(appendix)
219 .map_err(|msg| format!("Cannot parse appendix: {msg}")),
220 1 => PermissionedCandidateDatums::decode_v1_appendix(appendix)
221 .map_err(|msg| format!("Cannot parse appendix: {msg}")),
222 _ => Err(format!("Unknown version: {version}")),
223 }
224 }
225}
226
227fn decode_legacy_candidate_datum(datum: &PlutusData) -> Option<PermissionedCandidateDatumV0> {
229 let datums = datum.as_list().filter(|datums| datums.len() == 3)?;
230
231 let sc = datums.get(0).as_bytes()?;
232 let aura = datums.get(1).as_bytes()?;
233 let grandpa = datums.get(2).as_bytes()?;
234
235 Some(PermissionedCandidateDatumV0 {
236 sidechain_public_key: SidechainPublicKey(sc),
237 aura_public_key: AuraPublicKey(aura),
238 grandpa_public_key: GrandpaPublicKey(grandpa),
239 })
240}
241
242fn decode_v1_candidate_datum(datum: &PlutusData) -> Option<PermissionedCandidateDatumV1> {
244 let outer_list = datum.as_list().filter(|l| l.len() == 2)?;
246 let partner_chains_key = SidechainPublicKey(outer_list.get(0).as_bytes()?);
247 let keys = decode_candidate_keys(&outer_list.get(1))?;
248 Some(PermissionedCandidateDatumV1 { partner_chains_key, keys })
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254 use crate::test_helpers::*;
255 use hex_literal::hex;
256 use pretty_assertions::assert_eq;
257 use sp_core::crypto::key_types::{AURA, GRANDPA};
258
259 #[test]
260 fn valid_legacy_permissioned_candidates() {
261 let plutus_data = test_plutus_data!({"list": [
262 {"list": [
263 {"bytes": "cb6df9de1efca7a3998a8ead4e02159d5fa99c3e0d4fd6432667390bb4726854"},
264 {"bytes": "bf20afa1c1a72af3341fa7a447e3f9eada9f3d054a7408fb9e49ad4d6e6559ec"},
265 {"bytes": "9042a40b0b1baa9adcead024432a923eac706be5e1a89d7f2f2d58bfa8f3c26d"}
266 ]},
267 {"list": [
268 {"bytes": "79c3b7fc0b7697b9414cb87adcb37317d1cab32818ae18c0e97ad76395d1fdcf"},
269 {"bytes": "56d1da82e56e4cb35b13de25f69a3e9db917f3e13d6f786321f4b0a9dc153b19"},
270 {"bytes": "7392f3ea668aa2be7997d82c07bcfbec3ee4a9a4e01e3216d92b8f0d0a086c32"}
271 ]}
272 ]});
273
274 let expected_datum = PermissionedCandidateDatums::V0(vec![
275 PermissionedCandidateDatumV0 {
276 sidechain_public_key: SidechainPublicKey(
277 hex!("cb6df9de1efca7a3998a8ead4e02159d5fa99c3e0d4fd6432667390bb4726854").into(),
278 ),
279 aura_public_key: AuraPublicKey(
280 hex!("bf20afa1c1a72af3341fa7a447e3f9eada9f3d054a7408fb9e49ad4d6e6559ec").into(),
281 ),
282 grandpa_public_key: GrandpaPublicKey(
283 hex!("9042a40b0b1baa9adcead024432a923eac706be5e1a89d7f2f2d58bfa8f3c26d").into(),
284 ),
285 },
286 PermissionedCandidateDatumV0 {
287 sidechain_public_key: SidechainPublicKey(
288 hex!("79c3b7fc0b7697b9414cb87adcb37317d1cab32818ae18c0e97ad76395d1fdcf").into(),
289 ),
290 aura_public_key: AuraPublicKey(
291 hex!("56d1da82e56e4cb35b13de25f69a3e9db917f3e13d6f786321f4b0a9dc153b19").into(),
292 ),
293 grandpa_public_key: GrandpaPublicKey(
294 hex!("7392f3ea668aa2be7997d82c07bcfbec3ee4a9a4e01e3216d92b8f0d0a086c32").into(),
295 ),
296 },
297 ]);
298
299 assert_eq!(PermissionedCandidateDatums::try_from(plutus_data).unwrap(), expected_datum)
300 }
301
302 fn v0_datum_json() -> serde_json::Value {
303 serde_json::json!({
304 "list": [
305 { "constructor": 0, "fields": [] },
306 { "list": [
307 {"list": [
308 {"bytes": "cb6df9de1efca7a3998a8ead4e02159d5fa99c3e0d4fd6432667390bb4726854"},
309 {"bytes": "bf20afa1c1a72af3341fa7a447e3f9eada9f3d054a7408fb9e49ad4d6e6559ec"},
310 {"bytes": "9042a40b0b1baa9adcead024432a923eac706be5e1a89d7f2f2d58bfa8f3c26d"}
311 ]},
312 {"list": [
313 {"bytes": "79c3b7fc0b7697b9414cb87adcb37317d1cab32818ae18c0e97ad76395d1fdcf"},
314 {"bytes": "56d1da82e56e4cb35b13de25f69a3e9db917f3e13d6f786321f4b0a9dc153b19"},
315 {"bytes": "7392f3ea668aa2be7997d82c07bcfbec3ee4a9a4e01e3216d92b8f0d0a086c32"}
316 ]}
317 ]},
318 { "int": 0 }
319 ]
320 })
321 }
322
323 fn v1_datum_json() -> serde_json::Value {
324 serde_json::json!({
325 "list": [
326 { "constructor": 0, "fields": [] },
327 {"list": [
328 {"list":[
329 {"bytes": "cb6df9de1efca7a3998a8ead4e02159d5fa99c3e0d4fd6432667390bb4726854"},
330 {"list": [
331 {"list": [{"bytes": hex::encode(b"aura")}, {"bytes": "bf20afa1c1a72af3341fa7a447e3f9eada9f3d054a7408fb9e49ad4d6e6559ec"}]},
332 {"list": [{"bytes": hex::encode(b"gran")}, {"bytes": "9042a40b0b1baa9adcead024432a923eac706be5e1a89d7f2f2d58bfa8f3c26d"}]}
333 ]}
334 ]},
335 {"list":[
336 {"bytes": "79c3b7fc0b7697b9414cb87adcb37317d1cab32818ae18c0e97ad76395d1fdcf"},
337 {"list": [
338 {"list": [{"bytes": hex::encode(b"aura")}, {"bytes": "56d1da82e56e4cb35b13de25f69a3e9db917f3e13d6f786321f4b0a9dc153b19"}]},
339 {"list": [{"bytes": hex::encode(b"gran")}, {"bytes": "7392f3ea668aa2be7997d82c07bcfbec3ee4a9a4e01e3216d92b8f0d0a086c32"}]}
340 ]}
341 ]}
342 ]},
343 { "int": 1 }
344 ]
345 })
346 }
347
348 #[test]
349 fn permissioned_candidates_to_plutus_data_outputs_v0_for_aura_and_grandpa_keys() {
350 let expected_plutus_data = json_to_plutus_data(v0_datum_json());
351
352 let domain_data = vec![
353 PermissionedCandidateData {
354 sidechain_public_key: SidechainPublicKey(
355 hex!("cb6df9de1efca7a3998a8ead4e02159d5fa99c3e0d4fd6432667390bb4726854")
356 .to_vec(),
357 ),
358 keys: CandidateKeys(vec![
359 AuraPublicKey(
360 hex!("bf20afa1c1a72af3341fa7a447e3f9eada9f3d054a7408fb9e49ad4d6e6559ec")
361 .to_vec(),
362 )
363 .into(),
364 GrandpaPublicKey(
365 hex!("9042a40b0b1baa9adcead024432a923eac706be5e1a89d7f2f2d58bfa8f3c26d")
366 .to_vec(),
367 )
368 .into(),
369 ]),
370 },
371 PermissionedCandidateData {
372 sidechain_public_key: SidechainPublicKey(
373 hex!("79c3b7fc0b7697b9414cb87adcb37317d1cab32818ae18c0e97ad76395d1fdcf")
374 .to_vec(),
375 ),
376 keys: CandidateKeys(vec![
377 AuraPublicKey(
378 hex!("56d1da82e56e4cb35b13de25f69a3e9db917f3e13d6f786321f4b0a9dc153b19")
379 .to_vec(),
380 )
381 .into(),
382 GrandpaPublicKey(
383 hex!("7392f3ea668aa2be7997d82c07bcfbec3ee4a9a4e01e3216d92b8f0d0a086c32")
384 .to_vec(),
385 )
386 .into(),
387 ]),
388 },
389 ];
390 assert_eq!(permissioned_candidates_to_plutus_data(&domain_data), expected_plutus_data)
391 }
392
393 #[test]
394 fn permissioned_candidates_to_plutus_data_outputs_v1() {
395 let domain_data = vec![
396 PermissionedCandidateData {
397 sidechain_public_key: SidechainPublicKey([1; 33].to_vec()),
398 keys: CandidateKeys(vec![
399 CandidateKey { id: [2; 4], bytes: [3; 32].to_vec() },
400 CandidateKey { id: [4; 4], bytes: [5; 32].to_vec() },
401 ]),
402 },
403 PermissionedCandidateData {
404 sidechain_public_key: SidechainPublicKey([6; 33].to_vec()),
405 keys: CandidateKeys(vec![
406 CandidateKey { id: [7; 4], bytes: [8; 32].to_vec() },
407 CandidateKey { id: [9; 4], bytes: [10u8; 32].to_vec() },
408 ]),
409 },
410 ];
411 let json = serde_json::json!({
412 "list": [
413 { "constructor": 0, "fields": [] },
414 {"list": [
415 {"list":[
416 {"bytes": "010101010101010101010101010101010101010101010101010101010101010101"},
417 {"list": [
418 {"list": [{"bytes": "02020202"}, {"bytes": "0303030303030303030303030303030303030303030303030303030303030303"}]},
419 {"list": [{"bytes": "04040404"}, {"bytes": "0505050505050505050505050505050505050505050505050505050505050505"}]}
420 ]}
421 ]},
422 {"list":[
423 {"bytes": "060606060606060606060606060606060606060606060606060606060606060606"},
424 {"list": [
425 {"list": [{"bytes": "07070707"}, {"bytes": "0808080808080808080808080808080808080808080808080808080808080808"}]},
426 {"list": [{"bytes": "09090909"}, {"bytes": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a"}]}
427 ]}
428 ]}
429 ]},
430 { "int": 1 }
431 ]
432 });
433 let expected_plutus_data = json_to_plutus_data(json);
434 assert_eq!(permissioned_candidates_to_plutus_data(&domain_data), expected_plutus_data)
435 }
436
437 #[test]
438 fn valid_v0_permissioned_candidates() {
439 let plutus_data = json_to_plutus_data(v0_datum_json());
440
441 let expected_datum = PermissionedCandidateDatums::V0(vec![
442 PermissionedCandidateDatumV0 {
443 sidechain_public_key: SidechainPublicKey(
444 hex!("cb6df9de1efca7a3998a8ead4e02159d5fa99c3e0d4fd6432667390bb4726854").into(),
445 ),
446 aura_public_key: AuraPublicKey(
447 hex!("bf20afa1c1a72af3341fa7a447e3f9eada9f3d054a7408fb9e49ad4d6e6559ec").into(),
448 ),
449 grandpa_public_key: GrandpaPublicKey(
450 hex!("9042a40b0b1baa9adcead024432a923eac706be5e1a89d7f2f2d58bfa8f3c26d").into(),
451 ),
452 },
453 PermissionedCandidateDatumV0 {
454 sidechain_public_key: SidechainPublicKey(
455 hex!("79c3b7fc0b7697b9414cb87adcb37317d1cab32818ae18c0e97ad76395d1fdcf").into(),
456 ),
457 aura_public_key: AuraPublicKey(
458 hex!("56d1da82e56e4cb35b13de25f69a3e9db917f3e13d6f786321f4b0a9dc153b19").into(),
459 ),
460 grandpa_public_key: GrandpaPublicKey(
461 hex!("7392f3ea668aa2be7997d82c07bcfbec3ee4a9a4e01e3216d92b8f0d0a086c32").into(),
462 ),
463 },
464 ]);
465
466 assert_eq!(PermissionedCandidateDatums::try_from(plutus_data).unwrap(), expected_datum)
467 }
468
469 #[test]
470 fn valid_v1_permissioned_candidates() {
471 let plutus_data = json_to_plutus_data(v1_datum_json());
472
473 let expected_datum = PermissionedCandidateDatums::V1(vec![
474 PermissionedCandidateDatumV1 {
475 partner_chains_key: SidechainPublicKey(
476 hex!("cb6df9de1efca7a3998a8ead4e02159d5fa99c3e0d4fd6432667390bb4726854").into(),
477 ),
478 keys: CandidateKeys(vec![
479 CandidateKey::new(
480 AURA,
481 hex!("bf20afa1c1a72af3341fa7a447e3f9eada9f3d054a7408fb9e49ad4d6e6559ec")
482 .into(),
483 ),
484 CandidateKey::new(
485 GRANDPA,
486 hex!("9042a40b0b1baa9adcead024432a923eac706be5e1a89d7f2f2d58bfa8f3c26d")
487 .into(),
488 ),
489 ]),
490 },
491 PermissionedCandidateDatumV1 {
492 partner_chains_key: SidechainPublicKey(
493 hex!("79c3b7fc0b7697b9414cb87adcb37317d1cab32818ae18c0e97ad76395d1fdcf").into(),
494 ),
495 keys: CandidateKeys(vec![
496 CandidateKey::new(
497 AURA,
498 hex!("56d1da82e56e4cb35b13de25f69a3e9db917f3e13d6f786321f4b0a9dc153b19")
499 .into(),
500 ),
501 CandidateKey::new(
502 GRANDPA,
503 hex!("7392f3ea668aa2be7997d82c07bcfbec3ee4a9a4e01e3216d92b8f0d0a086c32")
504 .into(),
505 ),
506 ]),
507 },
508 ]);
509
510 assert_eq!(PermissionedCandidateDatums::try_from(plutus_data).unwrap(), expected_datum)
511 }
512}