1#![cfg_attr(not(feature = "std"), no_std)]
6#![deny(missing_docs)]
7
8pub mod byte_string;
9pub mod crypto;
10pub mod mainchain_epoch;
11
12extern crate alloc;
13extern crate core;
14extern crate num_derive;
15
16pub use alloc::collections::btree_map::BTreeMap;
17#[cfg(feature = "std")]
18use alloc::format;
19pub use alloc::vec::Vec;
20use alloc::{str::FromStr, string::String, string::ToString, vec};
21use byte_string_derive::byte_string;
22use core::{
23 fmt::{Display, Formatter},
24 ops::Deref,
25 u32,
26};
27use crypto::blake2b;
28use derive_more::{From, Into};
29use num_derive::*;
30use parity_scale_codec::{
31 Compact, Decode, DecodeWithMemTracking, Encode, MaxEncodedLen, WrapperTypeEncode,
32 decode_vec_with_len,
33};
34use plutus_datum_derive::*;
35use scale_info::TypeInfo;
36use sp_core::{
37 ConstU32,
38 bounded::BoundedVec,
39 crypto::{
40 KeyTypeId,
41 key_types::{AURA, GRANDPA},
42 },
43 ecdsa, ed25519, sr25519,
44};
45#[cfg(feature = "serde")]
46use {
47 derive_more::FromStr,
48 serde::{Deserialize, Deserializer, Serialize, Serializer},
49};
50
51pub const DATA_MC_EPOCH_OFFSET: u32 = 2;
54
55pub fn offset_data_epoch(epoch: &McEpochNumber) -> Result<McEpochNumber, u32> {
57 Ok(McEpochNumber(epoch.0.checked_sub(DATA_MC_EPOCH_OFFSET).ok_or(DATA_MC_EPOCH_OFFSET)?))
58}
59
60#[derive(
61 Default,
62 Debug,
63 Copy,
64 Clone,
65 PartialEq,
66 Eq,
67 Encode,
68 Decode,
69 DecodeWithMemTracking,
70 Hash,
71 TypeInfo,
72 Ord,
73 PartialOrd,
74)]
75#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, FromStr))]
76pub struct McEpochNumber(pub u32);
78
79impl McEpochNumber {
80 pub fn next(&self) -> Self {
82 Self(&self.0 + 1)
83 }
84}
85
86impl Display for McEpochNumber {
87 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
88 u32::fmt(&self.0, f)
89 }
90}
91#[derive(
92 Default,
93 Clone,
94 Copy,
95 Debug,
96 Encode,
97 Decode,
98 DecodeWithMemTracking,
99 TypeInfo,
100 PartialEq,
101 Eq,
102 PartialOrd,
103 Ord,
104)]
105#[cfg_attr(feature = "serde", derive(Serialize))]
106pub struct StakeDelegation(pub u64);
108
109impl StakeDelegation {
110 pub fn is_zero(&self) -> bool {
112 self.0 == 0
113 }
114}
115
116#[derive(
117 Default,
118 Clone,
119 Copy,
120 Debug,
121 Encode,
122 Decode,
123 DecodeWithMemTracking,
124 TypeInfo,
125 PartialEq,
126 Eq,
127 From,
128 MaxEncodedLen,
129)]
130#[cfg_attr(feature = "serde", derive(Serialize))]
131pub struct NativeTokenAmount(pub u128);
133
134impl Display for NativeTokenAmount {
135 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
136 u128::fmt(&self.0, f)
137 }
138}
139
140#[derive(
141 Default,
142 Clone,
143 Copy,
144 Debug,
145 Encode,
146 Decode,
147 DecodeWithMemTracking,
148 PartialEq,
149 Eq,
150 PartialOrd,
151 Ord,
152 scale_info::TypeInfo,
153)]
154#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, FromStr))]
155pub struct McBlockNumber(pub u32);
157
158impl Display for McBlockNumber {
159 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
160 u32::fmt(&self.0, f)
161 }
162}
163
164#[derive(
165 Default,
166 Debug,
167 Copy,
168 Clone,
169 PartialEq,
170 Eq,
171 PartialOrd,
172 Encode,
173 Decode,
174 DecodeWithMemTracking,
175 TypeInfo,
176 Hash,
177)]
178#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, FromStr))]
179pub struct McSlotNumber(pub u64);
181
182impl Display for McSlotNumber {
183 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
184 u64::fmt(&self.0, f)
185 }
186}
187
188#[derive(
189 Default,
190 Debug,
191 Copy,
192 Clone,
193 PartialEq,
194 Eq,
195 Encode,
196 Decode,
197 DecodeWithMemTracking,
198 TypeInfo,
199 Hash,
200 MaxEncodedLen,
201)]
202#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, FromStr))]
203pub struct ScSlotNumber(pub u64);
205
206#[derive(Debug, Clone, PartialEq, Default)]
208#[cfg_attr(feature = "serde", derive(serde::Serialize))]
209pub struct MainchainBlock {
210 pub number: McBlockNumber,
212 pub hash: McBlockHash,
214 pub epoch: McEpochNumber,
216 pub slot: McSlotNumber,
218 pub timestamp: u64, }
221
222#[derive(
223 Default,
224 Debug,
225 Copy,
226 Clone,
227 PartialEq,
228 Eq,
229 Encode,
230 Decode,
231 DecodeWithMemTracking,
232 PartialOrd,
233 Ord,
234 TypeInfo,
235 MaxEncodedLen,
236)]
237#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
238pub struct McTxIndexInBlock(pub u32);
240
241#[cfg(feature = "serde")]
242impl FromStr for McTxIndexInBlock {
243 type Err = sp_std::num::ParseIntError;
244 fn from_str(s: &str) -> Result<Self, Self::Err> {
245 let parsed = u32::from_str(s)?;
246 let _check_overflow = i32::from_str(s)?;
247 Ok(Self(parsed))
248 }
249}
250
251const MAX_MAINCHAIN_ADDRESS_BYTES: u32 = 120;
253
254#[derive(
258 Clone, Default, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen,
259)]
260#[byte_string(debug)]
261pub struct MainchainAddress(BoundedVec<u8, ConstU32<MAX_MAINCHAIN_ADDRESS_BYTES>>);
262
263impl MainchainAddress {
264 pub fn bytes(&self) -> Vec<u8> {
266 self.0.to_vec()
267 }
268}
269
270#[cfg(feature = "serde")]
271impl FromStr for MainchainAddress {
272 type Err = &'static str;
273
274 fn from_str(s: &str) -> Result<Self, Self::Err> {
275 let bytes: Vec<u8> = s.as_bytes().to_vec();
276 let bounded = BoundedVec::try_from(bytes).map_err(|_| "Invalid length")?;
277 Ok(MainchainAddress(bounded))
278 }
279}
280
281impl Display for MainchainAddress {
282 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
283 let s = String::from_utf8(self.0.to_vec())
284 .expect("MainchainAddressString is always properly encoded UTF-8");
285 write!(f, "{}", s)
286 }
287}
288
289#[cfg(feature = "serde")]
290impl serde::Serialize for MainchainAddress {
291 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
292 where
293 S: serde::Serializer,
294 {
295 let s = String::from_utf8(self.0.to_vec()).expect("MainchainAddress is always valid UTF-8");
296 serializer.serialize_str(&s)
297 }
298}
299
300#[cfg(feature = "serde")]
301impl<'de> serde::Deserialize<'de> for MainchainAddress {
302 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
304 where
305 D: serde::Deserializer<'de>,
306 {
307 let s = String::deserialize(deserializer)?;
308 let bytes = sp_core::bytes::from_hex(&s).unwrap_or_else(|_| s.as_bytes().to_vec());
309 let bounded = BoundedVec::try_from(bytes)
310 .map_err(|_| serde::de::Error::custom("MainchainAddress is too long"))?;
311 Ok(MainchainAddress(bounded))
312 }
313}
314
315const POLICY_ID_LEN: usize = 28;
317#[derive(
318 Clone,
319 Default,
320 PartialEq,
321 Eq,
322 Encode,
323 Decode,
324 DecodeWithMemTracking,
325 ToDatum,
326 TypeInfo,
327 MaxEncodedLen,
328 Hash,
329)]
330#[byte_string(debug, decode_hex, hex_serialize, hex_deserialize)]
331#[cfg_attr(feature = "std", byte_string(to_hex_string))]
332pub struct PolicyId(pub [u8; POLICY_ID_LEN]);
334
335#[cfg(feature = "std")]
336impl Display for PolicyId {
337 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
338 write!(f, "{}", self.to_hex_string())
339 }
340}
341
342pub type ScriptHash = PolicyId;
344
345pub const MAX_ASSET_NAME_LEN: u32 = 32;
347
348#[derive(
350 Clone, Default, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen,
351)]
352#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
353#[cfg_attr(feature = "std", byte_string(to_hex_string))]
354pub struct AssetName(pub BoundedVec<u8, ConstU32<MAX_ASSET_NAME_LEN>>);
355
356impl AssetName {
357 pub fn empty() -> Self {
359 Self(BoundedVec::new())
360 }
361}
362
363#[cfg(feature = "std")]
364impl Display for AssetName {
365 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
366 write!(f, "{}", self.to_hex_string())
367 }
368}
369
370#[derive(
371 Clone,
372 Debug,
373 PartialEq,
374 Eq,
375 Encode,
376 Decode,
377 DecodeWithMemTracking,
378 TypeInfo,
379 MaxEncodedLen,
380 Default,
381)]
382#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
383pub struct AssetId {
385 pub policy_id: PolicyId,
387 pub asset_name: AssetName,
389}
390
391#[cfg(feature = "std")]
392impl FromStr for AssetId {
393 type Err = String;
394
395 fn from_str(s: &str) -> Result<Self, Self::Err> {
396 match s.split_once(".") {
397 Some((policy_id, asset_name)) => {
398 let policy_id = PolicyId::from_str(policy_id)
399 .map_err(|e| format!("{} is invalid Policy ID: {}", policy_id, e))?;
400 let asset_name = AssetName::from_str(asset_name)
401 .map_err(|e| format!("{} is invalid Asset Name: {}", asset_name, e))?;
402 Ok(Self { policy_id, asset_name })
403 },
404 None => {
405 Err("AssetId should be <hex encoded Policy ID>.<hex encoded Asset Name>"
406 .to_string())
407 },
408 }
409 }
410}
411
412const STAKE_POOL_PUBLIC_KEY_LEN: usize = 32;
414
415#[derive(
416 Clone,
417 PartialEq,
418 Eq,
419 Encode,
420 Decode,
421 DecodeWithMemTracking,
422 TypeInfo,
423 MaxEncodedLen,
424 Hash,
425 Ord,
426 PartialOrd,
427)]
428#[cfg_attr(feature = "std", byte_string(to_hex_string))]
429#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
430pub struct StakePoolPublicKey(pub [u8; STAKE_POOL_PUBLIC_KEY_LEN]);
432
433impl StakePoolPublicKey {
434 pub fn hash(&self) -> MainchainKeyHash {
436 MainchainKeyHash::from_vkey(&self.0)
437 }
438}
439
440impl TryFrom<Vec<u8>> for StakePoolPublicKey {
441 type Error = &'static str;
442
443 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
444 <[u8; 32]>::try_from(value)
445 .map_err(|_| "Mainchain public key must be 32 bytes long")
446 .map(StakePoolPublicKey)
447 }
448}
449
450const STAKE_PUBLIC_KEY_LEN: usize = 32;
452
453#[derive(
455 Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen, Hash,
456)]
457#[cfg_attr(feature = "std", byte_string(to_hex_string))]
458#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
459pub struct StakePublicKey(pub [u8; STAKE_PUBLIC_KEY_LEN]);
460
461impl StakePublicKey {
462 pub fn hash(&self) -> MainchainKeyHash {
464 MainchainKeyHash(blake2b(&self.0))
465 }
466}
467
468const MAINCHAIN_KEY_HASH_LEN: usize = 28;
470
471#[derive(
472 Clone,
473 Copy,
474 Decode,
475 DecodeWithMemTracking,
476 Default,
477 Encode,
478 Hash,
479 MaxEncodedLen,
480 Eq,
481 PartialEq,
482 Ord,
483 PartialOrd,
484 TypeInfo,
485)]
486#[byte_string(debug)]
487#[cfg_attr(feature = "std", byte_string(to_hex_string, decode_hex))]
488#[cfg_attr(feature = "serde", byte_string(hex_serialize, hex_deserialize))]
489pub struct MainchainKeyHash(pub [u8; MAINCHAIN_KEY_HASH_LEN]);
492
493impl Display for MainchainKeyHash {
494 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
495 let hash = sp_core::hexdisplay::HexDisplay::from(&self.0);
496 write!(f, "0x{}", hash)
497 }
498}
499
500impl MainchainKeyHash {
501 pub fn from_vkey(vkey: &[u8; 32]) -> Self {
503 Self(blake2b(vkey))
504 }
505}
506
507pub const MAINCHAIN_SIGNATURE_LEN: usize = 64;
509
510#[derive(Clone, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
511#[cfg_attr(feature = "std", byte_string(to_hex_string))]
512#[byte_string(debug, hex_serialize, decode_hex)]
513pub struct MainchainSignature(pub [u8; MAINCHAIN_SIGNATURE_LEN]);
519
520impl From<[u8; MAINCHAIN_SIGNATURE_LEN]> for MainchainSignature {
521 fn from(raw: [u8; MAINCHAIN_SIGNATURE_LEN]) -> Self {
522 Self(raw)
523 }
524}
525
526impl WrapperTypeEncode for MainchainSignature {}
527impl Deref for MainchainSignature {
528 type Target = [u8];
529
530 fn deref(&self) -> &Self::Target {
531 &self.0
532 }
533}
534impl Decode for MainchainSignature {
535 fn decode<I: parity_scale_codec::Input>(
536 input: &mut I,
537 ) -> Result<Self, parity_scale_codec::Error> {
538 let vec: Vec<u8> = Decode::decode(input)?;
539 let arr = vec.try_into().map_err(|_| "Incorrect MainchainSignature size")?;
540 Ok(MainchainSignature(arr))
541 }
542}
543
544impl MainchainSignature {
545 pub fn verify(&self, public_key: &StakePoolPublicKey, signed_message: &[u8]) -> bool {
547 let mainchain_signature = ed25519::Signature::from(self.0);
548
549 sp_io::crypto::ed25519_verify(
550 &mainchain_signature,
551 signed_message,
552 &ed25519::Public::from(public_key.0),
553 )
554 }
555}
556
557pub const STAKE_KEY_SIGNATURE_LEN: usize = 64;
559
560#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, Hash, TypeInfo)]
561#[byte_string(debug, hex_serialize, decode_hex)]
562pub struct StakeKeySignature(pub [u8; STAKE_KEY_SIGNATURE_LEN]);
564
565impl From<[u8; STAKE_KEY_SIGNATURE_LEN]> for StakeKeySignature {
566 fn from(raw: [u8; STAKE_KEY_SIGNATURE_LEN]) -> Self {
567 Self(raw)
568 }
569}
570
571impl StakeKeySignature {
572 pub fn verify(&self, public_key: &StakePublicKey, message: &[u8]) -> bool {
574 let signature = ed25519::Signature::from(self.0);
575 sp_io::crypto::ed25519_verify(&signature, message, &ed25519::Public::from(public_key.0))
576 }
577}
578
579#[derive(
580 Clone,
581 Copy,
582 Debug,
583 Encode,
584 Decode,
585 DecodeWithMemTracking,
586 PartialEq,
587 TypeInfo,
588 ToDatum,
589 MaxEncodedLen,
590 Default,
591 PartialOrd,
592 Ord,
593 Eq,
594 Zero,
595 One,
596 NumOps,
597 Num,
598 From,
599 Into,
600)]
601#[cfg_attr(feature = "serde", derive(Serialize))]
602pub struct ScEpochNumber(pub u64);
604
605impl Display for ScEpochNumber {
606 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
607 u64::fmt(&self.0, f)
608 }
609}
610
611impl ScEpochNumber {
612 pub fn next(&self) -> Self {
614 Self(self.0 + 1)
615 }
616 pub fn prev(&self) -> Option<Self> {
618 self.0.checked_sub(1).map(Self)
619 }
620}
621
622#[derive(
623 Clone,
624 PartialEq,
625 Eq,
626 Encode,
627 Decode,
628 DecodeWithMemTracking,
629 ToDatum,
630 TypeInfo,
631 PartialOrd,
632 Ord,
633 Hash,
634)]
635#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex, as_ref)]
636pub struct SidechainPublicKey(pub Vec<u8>);
642
643impl From<ecdsa::Public> for SidechainPublicKey {
644 fn from(value: ecdsa::Public) -> Self {
645 Self(value.0.to_vec())
646 }
647}
648
649#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
651#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
652pub struct TransactionCbor(pub Vec<u8>);
653
654#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
656#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
657pub struct VKeyWitnessCbor(pub Vec<u8>);
658
659#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
661#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
662pub struct SidechainSignature(pub Vec<u8>);
663
664#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
668#[byte_string(debug, hex_serialize, hex_deserialize)]
669pub struct CrossChainPublicKey(pub Vec<u8>);
670
671impl CrossChainPublicKey {
672 pub fn hash(&self) -> CrossChainKeyHash {
674 CrossChainKeyHash(blake2b(&self.0))
675 }
676}
677
678impl From<k256::PublicKey> for CrossChainPublicKey {
679 fn from(value: k256::PublicKey) -> Self {
680 Self(value.to_sec1_bytes().to_vec())
681 }
682}
683
684impl From<CrossChainPublicKey> for k256::PublicKey {
685 fn from(value: CrossChainPublicKey) -> Self {
686 k256::PublicKey::from_sec1_bytes(&value.0)
687 .expect("CrossChainPublicKey converts to valid secp256k1::PublicKey")
688 }
689}
690
691const CROSS_CHAIN_KEY_HASH_LEN: usize = 28;
693
694#[derive(
695 Clone,
696 Copy,
697 Decode,
698 DecodeWithMemTracking,
699 Default,
700 Encode,
701 Hash,
702 MaxEncodedLen,
703 Eq,
704 PartialEq,
705 Ord,
706 PartialOrd,
707 TypeInfo,
708)]
709#[byte_string(debug, to_hex_string)]
710#[cfg_attr(feature = "std", byte_string(decode_hex))]
711#[cfg_attr(feature = "serde", byte_string(hex_serialize, hex_deserialize))]
712pub struct CrossChainKeyHash(pub [u8; CROSS_CHAIN_KEY_HASH_LEN]);
714
715impl Display for CrossChainKeyHash {
716 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
717 f.write_str(&self.to_hex_string())
718 }
719}
720
721#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
723#[byte_string(debug, hex_serialize)]
724pub struct CrossChainSignature(pub Vec<u8>);
725
726impl CrossChainSignature {
727 pub fn verify(
729 &self,
730 cross_chain_pubkey: &CrossChainPublicKey,
731 data: &[u8],
732 ) -> Result<(), k256::ecdsa::signature::Error> {
733 use k256::ecdsa::signature::Verifier;
734
735 let vkey = k256::ecdsa::VerifyingKey::from_sec1_bytes(&cross_chain_pubkey.0[..])?;
736 let signature = k256::ecdsa::Signature::from_slice(&self.0[..])?;
737 vkey.verify(data, &signature)
738 }
739}
740
741const EPOCH_NONCE_LEN: usize = 32;
743
744#[derive(Default, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
751#[byte_string(debug, hex_serialize)]
752pub struct EpochNonce(pub Vec<u8>);
753
754impl EpochNonce {
755 pub fn as_array(&self) -> [u8; EPOCH_NONCE_LEN] {
757 let mut epoch_nonce = self.0.clone();
758 epoch_nonce.resize_with(32, || 0);
759 epoch_nonce.try_into().expect("Should never fail after being resized")
760 }
761}
762
763#[derive(
764 Default,
765 Debug,
766 Copy,
767 Clone,
768 PartialEq,
769 Eq,
770 Encode,
771 Decode,
772 DecodeWithMemTracking,
773 ToDatum,
774 TypeInfo,
775 MaxEncodedLen,
776 Hash,
777)]
778pub struct UtxoId {
786 pub tx_hash: McTxHash,
788 pub index: UtxoIndex,
790}
791
792impl UtxoId {
793 pub const fn new(hash: [u8; TX_HASH_SIZE], index: u16) -> UtxoId {
795 UtxoId { tx_hash: McTxHash(hash), index: UtxoIndex(index) }
796 }
797}
798
799#[cfg(feature = "serde")]
800impl Serialize for UtxoId {
801 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
802 where
803 S: Serializer,
804 {
805 serializer.serialize_str(&self.to_string())
806 }
807}
808
809#[cfg(feature = "serde")]
810impl<'de> Deserialize<'de> for UtxoId {
811 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
812 where
813 D: Deserializer<'de>,
814 {
815 alloc::string::String::deserialize(deserializer).and_then(|string| {
816 Self::from_str(&string).map_err(|err| serde::de::Error::custom(err.to_string()))
817 })
818 }
819}
820
821#[cfg(feature = "serde")]
822impl FromStr for UtxoId {
823 type Err = &'static str;
824
825 fn from_str(s: &str) -> Result<Self, Self::Err> {
826 let split: Vec<&str> = s.split('#').collect();
827 let &[hash_str, index_str] = split.as_slice() else {
828 return Err("UtxoId string must conform to format: '<hash>#<index>'");
829 };
830
831 Ok(UtxoId {
832 tx_hash: McTxHash::from_str(hash_str)
833 .map_err(|_| "invalid string input for McTxHash")?,
834 index: UtxoIndex::from_str(index_str)
835 .map_err(|_| "invalid string input for OutputIndex")?,
836 })
837 }
838}
839
840impl Display for UtxoId {
841 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
842 let hash = sp_core::hexdisplay::HexDisplay::from(&self.tx_hash.0);
843 write!(f, "{}#{}", hash, self.index.0)
844 }
845}
846
847#[derive(
848 Default,
849 Debug,
850 Copy,
851 Clone,
852 PartialEq,
853 Eq,
854 Encode,
855 Decode,
856 DecodeWithMemTracking,
857 PartialOrd,
858 Ord,
859 ToDatum,
860 TypeInfo,
861 MaxEncodedLen,
862 Hash,
863)]
864#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
865pub struct UtxoIndex(pub u16);
867
868#[cfg(feature = "serde")]
869impl FromStr for UtxoIndex {
870 type Err = sp_std::num::ParseIntError;
871 fn from_str(s: &str) -> Result<Self, Self::Err> {
872 let parsed = u16::from_str(s)?;
873 let _check_overflow = i16::from_str(s)?;
874 Ok(Self(parsed))
875 }
876}
877
878pub const TX_HASH_SIZE: usize = 32;
880
881#[derive(
882 Default,
883 Copy,
884 Clone,
885 Hash,
886 PartialEq,
887 Eq,
888 Encode,
889 Decode,
890 DecodeWithMemTracking,
891 ToDatum,
892 TypeInfo,
893 MaxEncodedLen,
894)]
895#[byte_string(debug, from_bytes, decode_hex, hex_serialize, hex_deserialize)]
896#[constructor_datum]
897pub struct McTxHash(pub [u8; TX_HASH_SIZE]);
901
902impl TryFrom<Vec<u8>> for McTxHash {
903 type Error = &'static str;
904
905 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
906 <[u8; 32]>::try_from(value)
907 .map_err(|_| "McTxHash must be 32 bytes long")
908 .map(McTxHash)
909 }
910}
911
912#[derive(
913 Default,
914 Clone,
915 Decode,
916 DecodeWithMemTracking,
917 Encode,
918 PartialEq,
919 Eq,
920 TypeInfo,
921 MaxEncodedLen,
922 Hash,
923)]
924#[byte_string(debug, decode_hex, hex_serialize, hex_deserialize)]
925pub struct McBlockHash(pub [u8; 32]);
929
930impl Display for McBlockHash {
931 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
932 let hash = sp_core::hexdisplay::HexDisplay::from(&self.0);
933 write!(f, "{}", hash)
934 }
935}
936
937#[derive(
939 Default, Debug, Copy, Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo,
940)]
941#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
942#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
943pub struct UtxoInfo {
944 pub utxo_id: UtxoId,
946 pub epoch_number: McEpochNumber,
948 pub block_number: McBlockNumber,
950 pub slot_number: McSlotNumber,
952 pub tx_index_within_block: McTxIndexInBlock,
954}
955
956#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
961pub struct UtxoInfoOrderingKey {
962 pub block_number: McBlockNumber,
964 pub tx_index_within_block: McTxIndexInBlock,
966 pub utxo_id_index: UtxoIndex,
968}
969
970impl UtxoInfo {
971 pub fn ordering_key(&self) -> UtxoInfoOrderingKey {
973 UtxoInfoOrderingKey {
974 block_number: self.block_number,
975 tx_index_within_block: self.tx_index_within_block,
976 utxo_id_index: self.utxo_id.index,
977 }
978 }
979}
980
981#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
988#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
989#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
990pub enum NetworkType {
991 Mainnet,
993 #[default]
995 Testnet,
996}
997
998#[cfg(feature = "std")]
999impl std::fmt::Display for NetworkType {
1000 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1001 let str = match self {
1002 Self::Mainnet => "mainnet",
1003 Self::Testnet => "testnet",
1004 };
1005 write!(f, "{}", str)
1006 }
1007}
1008
1009#[derive(Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
1018#[cfg_attr(feature = "serde", derive(Serialize))]
1019pub struct RegistrationData {
1020 pub registration_utxo: UtxoId,
1022 pub sidechain_signature: SidechainSignature,
1024 pub mainchain_signature: MainchainSignature,
1026 pub cross_chain_signature: CrossChainSignature,
1028 pub sidechain_pub_key: SidechainPublicKey,
1030 pub cross_chain_pub_key: CrossChainPublicKey,
1032 pub utxo_info: UtxoInfo,
1034 pub tx_inputs: Vec<UtxoId>,
1036 pub keys: CandidateKeys,
1038}
1039
1040#[derive(Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
1042#[cfg_attr(feature = "serde", derive(Serialize))]
1043pub struct CandidateRegistrations {
1044 pub stake_pool_public_key: StakePoolPublicKey,
1046 pub registrations: Vec<RegistrationData>,
1048 pub stake_delegation: Option<StakeDelegation>,
1050}
1051
1052impl CandidateRegistrations {
1053 pub fn new(
1055 stake_pool_public_key: StakePoolPublicKey,
1056 stake_delegation: Option<StakeDelegation>,
1057 registrations: Vec<RegistrationData>,
1058 ) -> Self {
1059 Self { stake_pool_public_key, registrations, stake_delegation }
1060 }
1061
1062 pub fn mainchain_pub_key(&self) -> &StakePoolPublicKey {
1064 &self.stake_pool_public_key
1065 }
1066
1067 pub fn registrations(&self) -> &[RegistrationData] {
1069 &self.registrations
1070 }
1071}
1072
1073#[derive(
1075 Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialOrd, Ord, Hash,
1076)]
1077#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
1078pub struct AuraPublicKey(pub Vec<u8>);
1079impl AuraPublicKey {
1080 pub fn try_into_sr25519(&self) -> Option<sr25519::Public> {
1082 Some(sr25519::Public::from_raw(self.0.clone().try_into().ok()?))
1083 }
1084}
1085
1086impl From<sr25519::Public> for AuraPublicKey {
1087 fn from(value: sr25519::Public) -> Self {
1088 Self(value.0.to_vec())
1089 }
1090}
1091
1092impl From<AuraPublicKey> for CandidateKey {
1093 fn from(value: AuraPublicKey) -> Self {
1094 Self { id: AURA.0, bytes: value.0 }
1095 }
1096}
1097
1098#[derive(
1100 Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialOrd, Ord, Hash,
1101)]
1102#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
1103pub struct GrandpaPublicKey(pub Vec<u8>);
1104impl GrandpaPublicKey {
1105 pub fn try_into_ed25519(&self) -> Option<ed25519::Public> {
1107 Some(ed25519::Public::from_raw(self.0.clone().try_into().ok()?))
1108 }
1109}
1110
1111impl From<ed25519::Public> for GrandpaPublicKey {
1112 fn from(value: ed25519::Public) -> Self {
1113 Self(value.0.to_vec())
1114 }
1115}
1116
1117impl From<GrandpaPublicKey> for CandidateKey {
1118 fn from(value: GrandpaPublicKey) -> Self {
1119 Self { id: GRANDPA.0, bytes: value.0 }
1120 }
1121}
1122
1123#[derive(
1124 Debug,
1125 Clone,
1126 PartialEq,
1127 Decode,
1128 DecodeWithMemTracking,
1129 Encode,
1130 MaxEncodedLen,
1131 TypeInfo,
1132 Eq,
1133 Hash,
1134)]
1135#[cfg_attr(feature = "serde", derive(Serialize))]
1136pub struct DParameter {
1144 pub num_permissioned_candidates: u16,
1146 pub num_registered_candidates: u16,
1148}
1149
1150impl DParameter {
1151 pub fn new(num_permissioned_candidates: u16, num_registered_candidates: u16) -> Self {
1153 Self { num_permissioned_candidates, num_registered_candidates }
1154 }
1155}
1156
1157#[derive(
1159 Debug,
1160 Clone,
1161 PartialEq,
1162 Eq,
1163 Decode,
1164 DecodeWithMemTracking,
1165 Encode,
1166 TypeInfo,
1167 PartialOrd,
1168 Ord,
1169 Hash,
1170)]
1171#[cfg_attr(feature = "serde", derive(Serialize))]
1172pub struct CandidateKey {
1173 pub id: [u8; 4],
1175 pub bytes: Vec<u8>,
1177}
1178
1179impl FromStr for CandidateKey {
1180 type Err = &'static str;
1181
1182 fn from_str(s: &str) -> Result<Self, Self::Err> {
1183 if let Some((id, bytes)) = s.split_once(':') {
1184 let id: [u8; 4] = id.as_bytes().try_into().map_err(|_| "invalid key type id")?;
1185 let bytes = bytes.trim_start_matches("0x");
1186 let bytes = hex::decode(bytes).map_err(|_| "key bytes are not in hex format")?;
1187 Ok(CandidateKey { id, bytes })
1188 } else {
1189 Err("invalid format of CandidateKey, expected '<key type>:<key>'")
1190 }
1191 }
1192}
1193
1194pub const CROSS_CHAIN_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"crch");
1196
1197impl CandidateKey {
1198 pub fn new(id: KeyTypeId, bytes: Vec<u8>) -> Self {
1200 Self { id: id.0, bytes }
1201 }
1202}
1203
1204#[derive(Debug, Clone, PartialEq, Eq, DecodeWithMemTracking, TypeInfo, PartialOrd, Ord, Hash)]
1205#[cfg_attr(feature = "serde", derive(Serialize))]
1206pub struct CandidateKeys(pub Vec<CandidateKey>);
1208
1209impl CandidateKeys {
1210 pub fn find(&self, id: KeyTypeId) -> Option<Vec<u8>> {
1212 self.0
1213 .iter()
1214 .find_map(|e| if e.id == id.0 { Some(e.bytes.clone()) } else { None })
1215 }
1216
1217 pub fn find_or_empty(&self, id: KeyTypeId) -> Vec<u8> {
1219 self.find(id).unwrap_or_default()
1220 }
1221
1222 pub fn has_only_aura_and_grandpa_keys(&self) -> bool {
1224 self.0.len() == 2 && self.find(AURA).is_some() && self.find(GRANDPA).is_some()
1225 }
1226}
1227
1228impl From<Vec<([u8; 4], Vec<u8>)>> for CandidateKeys {
1229 fn from(value: Vec<([u8; 4], Vec<u8>)>) -> Self {
1230 Self(value.into_iter().map(|(id, bytes)| CandidateKey { id, bytes }).collect())
1231 }
1232}
1233
1234impl Encode for CandidateKeys {
1245 fn size_hint(&self) -> usize {
1246 if self.has_only_aura_and_grandpa_keys() {
1247 Encode::size_hint(&AuraPublicKey(self.find_or_empty(AURA)))
1248 .saturating_add(Encode::size_hint(&GrandpaPublicKey(self.find_or_empty(GRANDPA))))
1249 } else {
1250 Encode::size_hint(&Compact(u32::MAX)).saturating_add(Encode::size_hint(&self.0))
1251 }
1252 }
1253
1254 fn encode_to<T: parity_scale_codec::Output + ?Sized>(&self, dest: &mut T) {
1255 if self.has_only_aura_and_grandpa_keys() {
1256 Encode::encode_to(&AuraPublicKey(self.find_or_empty(AURA)), dest);
1257 Encode::encode_to(&GrandpaPublicKey(self.find_or_empty(GRANDPA)), dest)
1258 } else {
1259 Encode::encode_to(&Compact(u32::MAX), dest);
1262 Encode::encode_to(&self.0, dest)
1263 }
1264 }
1265}
1266
1267impl Decode for CandidateKeys {
1269 fn decode<I: parity_scale_codec::Input>(
1270 input: &mut I,
1271 ) -> Result<Self, parity_scale_codec::Error> {
1272 let marker_or_aura_size: u32 = <Compact<u32>>::decode(input)?.0;
1274 if marker_or_aura_size == u32::MAX {
1275 let keys = Vec::<CandidateKey>::decode(input)?;
1276 Ok(Self(keys))
1277 } else {
1278 let aura_bytes: Vec<u8> = decode_vec_with_len(input, marker_or_aura_size as usize)?;
1279 let grandpa = GrandpaPublicKey::decode(input)?;
1280 Ok(Self(vec![AuraPublicKey(aura_bytes).into(), grandpa.into()]))
1281 }
1282 }
1283}
1284
1285#[derive(
1286 Debug,
1287 Clone,
1288 PartialEq,
1289 Eq,
1290 Decode,
1291 DecodeWithMemTracking,
1292 Encode,
1293 TypeInfo,
1294 PartialOrd,
1295 Ord,
1296 Hash,
1297)]
1298#[cfg_attr(feature = "serde", derive(Serialize))]
1299pub struct PermissionedCandidateData {
1305 pub sidechain_public_key: SidechainPublicKey,
1307 pub keys: CandidateKeys,
1309}
1310
1311#[cfg(feature = "std")]
1313impl FromStr for PermissionedCandidateData {
1314 type Err = alloc::boxed::Box<dyn core::error::Error + Send + Sync>;
1315
1316 fn from_str(partner_chain_public_keys: &str) -> Result<Self, Self::Err> {
1317 fn is_legacy_format(line: &str) -> bool {
1318 line.contains(':') && !line.contains(',')
1319 }
1320
1321 fn parse_legacy_format(
1322 line: &str,
1323 ) -> Result<
1324 PermissionedCandidateData,
1325 alloc::boxed::Box<dyn core::error::Error + Send + Sync>,
1326 > {
1327 let line = line.replace("0x", "");
1328 if let [sidechain_pub_key, aura_pub_key, grandpa_pub_key] =
1329 line.split(":").collect::<Vec<_>>()[..]
1330 {
1331 Ok(PermissionedCandidateData {
1332 sidechain_public_key: SidechainPublicKey(
1333 hex::decode(sidechain_pub_key).map_err(|e| e.to_string())?,
1334 ),
1335 keys: CandidateKeys(vec![
1336 AuraPublicKey(hex::decode(aura_pub_key).map_err(|e| e.to_string())?).into(),
1337 GrandpaPublicKey(hex::decode(grandpa_pub_key).map_err(|e| e.to_string())?)
1338 .into(),
1339 ]),
1340 })
1341 } else {
1342 Err(format!("Failed to parse partner chain public keys (legacy) from '{line}'")
1343 .into())
1344 }
1345 }
1346
1347 fn parse_generic_format(
1348 line: &str,
1349 ) -> Result<
1350 PermissionedCandidateData,
1351 alloc::boxed::Box<dyn core::error::Error + Send + Sync>,
1352 > {
1353 let mut columns = line.split(",");
1354 if let Some(partner_chains_key) = columns.next() {
1355 let partner_chains_key = SidechainPublicKey(
1356 hex::decode(partner_chains_key.trim_start_matches("0x"))
1357 .map_err(|e| e.to_string())?,
1358 );
1359 let mut keys = vec![];
1360 for column in columns {
1361 let key = CandidateKey::from_str(column)?;
1362 keys.push(key);
1363 }
1364 Ok(PermissionedCandidateData {
1365 sidechain_public_key: partner_chains_key,
1366 keys: CandidateKeys(keys),
1367 })
1368 } else {
1369 Err("Failed to parse partner chain public keys (generic) from '{line}'.".into())
1370 }
1371 }
1372
1373 if is_legacy_format(&partner_chain_public_keys) {
1374 parse_legacy_format(&partner_chain_public_keys)
1375 } else {
1376 parse_generic_format(&partner_chain_public_keys)
1377 }
1378 }
1379}
1380
1381#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1383pub struct CandidateRegistration {
1384 pub stake_ownership: AdaBasedStaking,
1386 pub partner_chain_pub_key: SidechainPublicKey,
1388 pub partner_chain_signature: SidechainSignature,
1390 pub own_pkh: MainchainKeyHash,
1392 pub registration_utxo: UtxoId,
1394 pub keys: CandidateKeys,
1396}
1397
1398impl CandidateRegistration {
1399 pub fn matches_keys(&self, other: &Self) -> bool {
1401 self.stake_ownership == other.stake_ownership
1402 && self.partner_chain_pub_key == other.partner_chain_pub_key
1403 && self.partner_chain_signature == other.partner_chain_signature
1404 && self.keys.0.iter().all(|key| other.keys.0.contains(key))
1405 }
1406}
1407
1408#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1412pub struct AdaBasedStaking {
1413 pub pub_key: StakePoolPublicKey,
1415 pub signature: MainchainSignature,
1417}
1418
1419#[derive(
1420 Clone,
1421 PartialEq,
1422 Eq,
1423 Ord,
1424 PartialOrd,
1425 TypeInfo,
1426 MaxEncodedLen,
1427 Encode,
1428 Decode,
1429 DecodeWithMemTracking,
1430)]
1431pub enum DelegatorKey {
1433 StakeKeyHash([u8; 28]),
1435 ScriptKeyHash {
1437 hash_raw: [u8; 28],
1439 script_hash: [u8; 28],
1441 },
1442}
1443
1444impl alloc::fmt::Debug for DelegatorKey {
1445 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
1446 let s = match self {
1447 Self::ScriptKeyHash { hash_raw, script_hash } => alloc::format!(
1448 "ScriptKeyHash{{ hash_raw: {}, script_hash: {} }}",
1449 hex::encode(hash_raw),
1450 hex::encode(script_hash)
1451 ),
1452 Self::StakeKeyHash(hash) => alloc::format!("StakeKeyHash({})", hex::encode(hash)),
1453 };
1454
1455 f.write_str(&s)
1456 }
1457}
1458
1459#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
1461pub struct DelegatorStakeAmount(pub u64);
1462
1463impl<T: Into<u64>> From<T> for DelegatorStakeAmount {
1464 fn from(value: T) -> Self {
1465 Self(value.into())
1466 }
1467}
1468
1469#[derive(Debug, Clone, Default)]
1474pub struct StakeDistribution(pub BTreeMap<MainchainKeyHash, PoolDelegation>);
1475
1476#[derive(Debug, Clone, Default, PartialEq)]
1478pub struct PoolDelegation {
1479 pub total_stake: StakeDelegation,
1481 pub delegators: BTreeMap<DelegatorKey, DelegatorStakeAmount>,
1483}
1484
1485pub trait FromStrStdErr:
1487 FromStr<Err: Into<alloc::boxed::Box<dyn core::error::Error + Send + Sync + 'static>>>
1488{
1489}
1490impl<T: FromStr<Err: Into<alloc::boxed::Box<dyn core::error::Error + Send + Sync + 'static>>>>
1491 FromStrStdErr for T
1492{
1493}
1494
1495#[cfg(test)]
1496mod tests {
1497 use super::*;
1498 use core::str::FromStr;
1499 use hex_literal::hex;
1500 use parity_scale_codec::{Decode, Encode};
1501
1502 #[test]
1503 fn main_chain_address_string_serialize_deserialize_round_trip() {
1504 let address = MainchainAddress::from_str(
1505 "addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc",
1506 )
1507 .unwrap();
1508 let serialized = serde_json::to_value(&address).unwrap();
1509 assert_eq!(
1510 serialized,
1511 serde_json::json!("addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc")
1512 );
1513 let deserialized = serde_json::from_value(serialized).unwrap();
1514 assert_eq!(address, deserialized);
1515 }
1516
1517 #[test]
1518 fn main_chain_address_deserialization_of_hex_encoded_bytes() {
1519 let address = MainchainAddress::from_str("addr_test1wz5q").unwrap();
1520 let serialized = serde_json::json!("0x616464725f7465737431777a3571");
1521 assert_eq!(address, serde_json::from_value(serialized).unwrap());
1522 let serialized = serde_json::json!("616464725f7465737431777a3571");
1523 assert_eq!(address, serde_json::from_value(serialized).unwrap());
1524 }
1525
1526 #[test]
1527 fn main_chain_address_string_from_str_to_string_round_trip() {
1528 let address = MainchainAddress::from_str(
1529 "addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc",
1530 )
1531 .unwrap();
1532 let str = address.to_string();
1533 let from_str = MainchainAddress::from_str(&str).unwrap();
1534 assert_eq!(address, from_str);
1535 }
1536
1537 #[test]
1538 fn main_chain_signature_should_be_backward_compatible_with_vec() {
1539 #[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
1540 #[byte_string(debug, hex_serialize, decode_hex)]
1541 struct LegacyMCSignature(pub Vec<u8>);
1542
1543 let legacy_encoded = LegacyMCSignature(vec![10; 64]).encode();
1544
1545 let legacy_decoded = MainchainSignature::decode(&mut legacy_encoded.as_slice())
1546 .expect("Encoded legacy should decode to current type");
1547
1548 assert_eq!(legacy_decoded.0, [10; MAINCHAIN_SIGNATURE_LEN]);
1549
1550 let current_encoded = MainchainSignature([9; MAINCHAIN_SIGNATURE_LEN]).encode();
1551
1552 let current_decoded = LegacyMCSignature::decode(&mut current_encoded.as_slice())
1553 .expect("Encoded current should decode to legacy");
1554
1555 assert_eq!(current_decoded.0, vec![9; 64]);
1556 }
1557
1558 #[test]
1559 fn cross_chain_signature_verify_works() {
1560 let signature = CrossChainSignature(
1561 hex!("d1e02e4a5484c3b7202ce6b844577048e7578dc62901cf8f51e6d74bbd3adb091688feacedd8343d0b04a0f5862b2e06148934a75e678e42051fde5431eca33d").to_vec()
1562 );
1563 let pubkey = CrossChainPublicKey(
1564 hex!("020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1").to_vec(),
1565 );
1566 let signed_data = hex!(
1567 "84020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a16c68747470733a2f2f636f6f6c2e73747566662f73706f2e6a736f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
1568 );
1569
1570 assert!(signature.verify(&pubkey, &signed_data).is_ok())
1571 }
1572
1573 #[derive(Decode, Encode, PartialEq, Eq, Debug)]
1574 struct TestCandidateKeys {
1575 field_before: Option<u32>,
1576 keys: CandidateKeys,
1577 field_after: Option<u32>,
1579 }
1580
1581 #[test]
1582 fn encode_decode_round_trip_for_generic_candidate_keys() {
1583 let keys = TestCandidateKeys {
1584 field_before: Some(42),
1585 keys: CandidateKeys(vec![
1586 CandidateKey { id: *b"abcd", bytes: [7u8; 32].to_vec() },
1587 CandidateKey { id: *b"efgh", bytes: [9u8; 32].to_vec() },
1588 ]),
1589 field_after: Some(15),
1590 };
1591 let bytes: Vec<u8> = Encode::encode(&keys);
1592 let mut bytes: &[u8] = &bytes;
1593 let decoded = TestCandidateKeys::decode(&mut bytes).unwrap();
1594 assert_eq!(keys, decoded)
1595 }
1596
1597 #[test]
1598 fn encode_decode_round_trip_for_aura_and_grandpa_candidate_keys() {
1599 let keys = TestCandidateKeys {
1600 field_before: Some(42),
1601 keys: CandidateKeys(vec![
1602 AuraPublicKey([7u8; 32].to_vec()).into(),
1603 GrandpaPublicKey([9u8; 32].to_vec()).into(),
1604 ]),
1605 field_after: Some(15),
1606 };
1607 let bytes: Vec<u8> = Encode::encode(&keys);
1608 let mut bytes: &[u8] = &bytes;
1609 let decoded = TestCandidateKeys::decode(&mut bytes).unwrap();
1610 assert_eq!(keys, decoded)
1611 }
1612}