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 MaxEncodedLen,
154)]
155#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, FromStr))]
156pub struct McBlockNumber(pub u32);
158
159impl Display for McBlockNumber {
160 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
161 u32::fmt(&self.0, f)
162 }
163}
164
165#[derive(
166 Default,
167 Debug,
168 Copy,
169 Clone,
170 PartialEq,
171 Eq,
172 PartialOrd,
173 Encode,
174 Decode,
175 DecodeWithMemTracking,
176 TypeInfo,
177 Hash,
178)]
179#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, FromStr))]
180pub struct McSlotNumber(pub u64);
182
183impl Display for McSlotNumber {
184 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
185 u64::fmt(&self.0, f)
186 }
187}
188
189#[derive(
190 Default,
191 Debug,
192 Copy,
193 Clone,
194 PartialEq,
195 Eq,
196 Encode,
197 Decode,
198 DecodeWithMemTracking,
199 TypeInfo,
200 Hash,
201 MaxEncodedLen,
202)]
203#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, FromStr))]
204pub struct ScSlotNumber(pub u64);
206
207#[derive(Debug, Clone, PartialEq, Default)]
209#[cfg_attr(feature = "serde", derive(serde::Serialize))]
210pub struct MainchainBlock {
211 pub number: McBlockNumber,
213 pub hash: McBlockHash,
215 pub epoch: McEpochNumber,
217 pub slot: McSlotNumber,
219 pub timestamp: u64, }
222
223#[derive(
224 Default,
225 Debug,
226 Copy,
227 Clone,
228 PartialEq,
229 Eq,
230 Encode,
231 Decode,
232 DecodeWithMemTracking,
233 PartialOrd,
234 Ord,
235 TypeInfo,
236 MaxEncodedLen,
237)]
238#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
239pub struct McTxIndexInBlock(pub u32);
241
242#[cfg(feature = "serde")]
243impl FromStr for McTxIndexInBlock {
244 type Err = sp_std::num::ParseIntError;
245 fn from_str(s: &str) -> Result<Self, Self::Err> {
246 let parsed = u32::from_str(s)?;
247 let _check_overflow = i32::from_str(s)?;
248 Ok(Self(parsed))
249 }
250}
251
252const MAX_MAINCHAIN_ADDRESS_BYTES: u32 = 120;
254
255#[derive(
259 Clone, Default, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen,
260)]
261#[byte_string(debug)]
262pub struct MainchainAddress(BoundedVec<u8, ConstU32<MAX_MAINCHAIN_ADDRESS_BYTES>>);
263
264impl MainchainAddress {
265 pub fn bytes(&self) -> Vec<u8> {
267 self.0.to_vec()
268 }
269}
270
271#[cfg(feature = "serde")]
272impl FromStr for MainchainAddress {
273 type Err = &'static str;
274
275 fn from_str(s: &str) -> Result<Self, Self::Err> {
276 let bytes: Vec<u8> = s.as_bytes().to_vec();
277 let bounded = BoundedVec::try_from(bytes).map_err(|_| "Invalid length")?;
278 Ok(MainchainAddress(bounded))
279 }
280}
281
282impl Display for MainchainAddress {
283 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
284 let s = String::from_utf8(self.0.to_vec())
285 .expect("MainchainAddressString is always properly encoded UTF-8");
286 write!(f, "{}", s)
287 }
288}
289
290#[cfg(feature = "serde")]
291impl serde::Serialize for MainchainAddress {
292 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
293 where
294 S: serde::Serializer,
295 {
296 let s = String::from_utf8(self.0.to_vec()).expect("MainchainAddress is always valid UTF-8");
297 serializer.serialize_str(&s)
298 }
299}
300
301#[cfg(feature = "serde")]
302impl<'de> serde::Deserialize<'de> for MainchainAddress {
303 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
305 where
306 D: serde::Deserializer<'de>,
307 {
308 let s = String::deserialize(deserializer)?;
309 let bytes = sp_core::bytes::from_hex(&s).unwrap_or_else(|_| s.as_bytes().to_vec());
310 let bounded = BoundedVec::try_from(bytes)
311 .map_err(|_| serde::de::Error::custom("MainchainAddress is too long"))?;
312 Ok(MainchainAddress(bounded))
313 }
314}
315
316const POLICY_ID_LEN: usize = 28;
318#[derive(
319 Clone,
320 Default,
321 PartialEq,
322 Eq,
323 Encode,
324 Decode,
325 DecodeWithMemTracking,
326 ToDatum,
327 TypeInfo,
328 MaxEncodedLen,
329 Hash,
330)]
331#[byte_string(debug, decode_hex, hex_serialize, hex_deserialize)]
332#[cfg_attr(feature = "std", byte_string(to_hex_string))]
333pub struct PolicyId(pub [u8; POLICY_ID_LEN]);
335
336#[cfg(feature = "std")]
337impl Display for PolicyId {
338 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
339 write!(f, "{}", self.to_hex_string())
340 }
341}
342
343pub type ScriptHash = PolicyId;
345
346pub const MAX_ASSET_NAME_LEN: u32 = 32;
348
349#[derive(
351 Clone, Default, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen,
352)]
353#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
354#[cfg_attr(feature = "std", byte_string(to_hex_string))]
355pub struct AssetName(pub BoundedVec<u8, ConstU32<MAX_ASSET_NAME_LEN>>);
356
357impl AssetName {
358 pub fn empty() -> Self {
360 Self(BoundedVec::new())
361 }
362}
363
364#[cfg(feature = "std")]
365impl Display for AssetName {
366 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
367 write!(f, "{}", self.to_hex_string())
368 }
369}
370
371#[derive(
372 Clone,
373 Debug,
374 PartialEq,
375 Eq,
376 Encode,
377 Decode,
378 DecodeWithMemTracking,
379 TypeInfo,
380 MaxEncodedLen,
381 Default,
382)]
383#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
384pub struct AssetId {
386 pub policy_id: PolicyId,
388 pub asset_name: AssetName,
390}
391
392#[cfg(feature = "std")]
393impl FromStr for AssetId {
394 type Err = String;
395
396 fn from_str(s: &str) -> Result<Self, Self::Err> {
397 match s.split_once(".") {
398 Some((policy_id, asset_name)) => {
399 let policy_id = PolicyId::from_str(policy_id)
400 .map_err(|e| format!("{} is invalid Policy ID: {}", policy_id, e))?;
401 let asset_name = AssetName::from_str(asset_name)
402 .map_err(|e| format!("{} is invalid Asset Name: {}", asset_name, e))?;
403 Ok(Self { policy_id, asset_name })
404 },
405 None => {
406 Err("AssetId should be <hex encoded Policy ID>.<hex encoded Asset Name>"
407 .to_string())
408 },
409 }
410 }
411}
412
413const STAKE_POOL_PUBLIC_KEY_LEN: usize = 32;
415
416#[derive(
417 Clone,
418 PartialEq,
419 Eq,
420 Encode,
421 Decode,
422 DecodeWithMemTracking,
423 TypeInfo,
424 MaxEncodedLen,
425 Hash,
426 Ord,
427 PartialOrd,
428)]
429#[cfg_attr(feature = "std", byte_string(to_hex_string))]
430#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
431pub struct StakePoolPublicKey(pub [u8; STAKE_POOL_PUBLIC_KEY_LEN]);
433
434impl StakePoolPublicKey {
435 pub fn hash(&self) -> MainchainKeyHash {
437 MainchainKeyHash::from_vkey(&self.0)
438 }
439}
440
441impl TryFrom<Vec<u8>> for StakePoolPublicKey {
442 type Error = &'static str;
443
444 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
445 <[u8; 32]>::try_from(value)
446 .map_err(|_| "Mainchain public key must be 32 bytes long")
447 .map(StakePoolPublicKey)
448 }
449}
450
451const STAKE_PUBLIC_KEY_LEN: usize = 32;
453
454#[derive(
456 Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen, Hash,
457)]
458#[cfg_attr(feature = "std", byte_string(to_hex_string))]
459#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
460pub struct StakePublicKey(pub [u8; STAKE_PUBLIC_KEY_LEN]);
461
462impl StakePublicKey {
463 pub fn hash(&self) -> MainchainKeyHash {
465 MainchainKeyHash(blake2b(&self.0))
466 }
467}
468
469const MAINCHAIN_KEY_HASH_LEN: usize = 28;
471
472#[derive(
473 Clone,
474 Copy,
475 Decode,
476 DecodeWithMemTracking,
477 Default,
478 Encode,
479 Hash,
480 MaxEncodedLen,
481 Eq,
482 PartialEq,
483 Ord,
484 PartialOrd,
485 TypeInfo,
486)]
487#[byte_string(debug)]
488#[cfg_attr(feature = "std", byte_string(to_hex_string, decode_hex))]
489#[cfg_attr(feature = "serde", byte_string(hex_serialize, hex_deserialize))]
490pub struct MainchainKeyHash(pub [u8; MAINCHAIN_KEY_HASH_LEN]);
493
494impl Display for MainchainKeyHash {
495 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
496 let hash = sp_core::hexdisplay::HexDisplay::from(&self.0);
497 write!(f, "0x{}", hash)
498 }
499}
500
501impl MainchainKeyHash {
502 pub fn from_vkey(vkey: &[u8; 32]) -> Self {
504 Self(blake2b(vkey))
505 }
506}
507
508pub const MAINCHAIN_SIGNATURE_LEN: usize = 64;
510
511#[derive(Clone, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
512#[cfg_attr(feature = "std", byte_string(to_hex_string))]
513#[byte_string(debug, hex_serialize, decode_hex)]
514pub struct MainchainSignature(pub [u8; MAINCHAIN_SIGNATURE_LEN]);
520
521impl From<[u8; MAINCHAIN_SIGNATURE_LEN]> for MainchainSignature {
522 fn from(raw: [u8; MAINCHAIN_SIGNATURE_LEN]) -> Self {
523 Self(raw)
524 }
525}
526
527impl WrapperTypeEncode for MainchainSignature {}
528impl Deref for MainchainSignature {
529 type Target = [u8];
530
531 fn deref(&self) -> &Self::Target {
532 &self.0
533 }
534}
535impl Decode for MainchainSignature {
536 fn decode<I: parity_scale_codec::Input>(
537 input: &mut I,
538 ) -> Result<Self, parity_scale_codec::Error> {
539 let vec: Vec<u8> = Decode::decode(input)?;
540 let arr = vec.try_into().map_err(|_| "Incorrect MainchainSignature size")?;
541 Ok(MainchainSignature(arr))
542 }
543}
544
545impl MainchainSignature {
546 pub fn verify(&self, public_key: &StakePoolPublicKey, signed_message: &[u8]) -> bool {
548 let mainchain_signature = ed25519::Signature::from(self.0);
549
550 sp_io::crypto::ed25519_verify(
551 &mainchain_signature,
552 signed_message,
553 &ed25519::Public::from(public_key.0),
554 )
555 }
556}
557
558pub const STAKE_KEY_SIGNATURE_LEN: usize = 64;
560
561#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, Hash, TypeInfo)]
562#[byte_string(debug, hex_serialize, decode_hex)]
563pub struct StakeKeySignature(pub [u8; STAKE_KEY_SIGNATURE_LEN]);
565
566impl From<[u8; STAKE_KEY_SIGNATURE_LEN]> for StakeKeySignature {
567 fn from(raw: [u8; STAKE_KEY_SIGNATURE_LEN]) -> Self {
568 Self(raw)
569 }
570}
571
572impl StakeKeySignature {
573 pub fn verify(&self, public_key: &StakePublicKey, message: &[u8]) -> bool {
575 let signature = ed25519::Signature::from(self.0);
576 sp_io::crypto::ed25519_verify(&signature, message, &ed25519::Public::from(public_key.0))
577 }
578}
579
580#[derive(
581 Clone,
582 Copy,
583 Debug,
584 Encode,
585 Decode,
586 DecodeWithMemTracking,
587 PartialEq,
588 TypeInfo,
589 ToDatum,
590 MaxEncodedLen,
591 Default,
592 PartialOrd,
593 Ord,
594 Eq,
595 Zero,
596 One,
597 NumOps,
598 Num,
599 From,
600 Into,
601)]
602#[cfg_attr(feature = "serde", derive(Serialize))]
603pub struct ScEpochNumber(pub u64);
605
606impl Display for ScEpochNumber {
607 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
608 u64::fmt(&self.0, f)
609 }
610}
611
612impl ScEpochNumber {
613 pub fn next(&self) -> Self {
615 Self(self.0 + 1)
616 }
617 pub fn prev(&self) -> Option<Self> {
619 self.0.checked_sub(1).map(Self)
620 }
621}
622
623#[derive(
624 Clone,
625 PartialEq,
626 Eq,
627 Encode,
628 Decode,
629 DecodeWithMemTracking,
630 ToDatum,
631 TypeInfo,
632 PartialOrd,
633 Ord,
634 Hash,
635)]
636#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex, as_ref)]
637pub struct SidechainPublicKey(pub Vec<u8>);
643
644impl From<ecdsa::Public> for SidechainPublicKey {
645 fn from(value: ecdsa::Public) -> Self {
646 Self(value.0.to_vec())
647 }
648}
649
650#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
652#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
653pub struct TransactionCbor(pub Vec<u8>);
654
655#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
657#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
658pub struct VKeyWitnessCbor(pub Vec<u8>);
659
660#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
662#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
663pub struct SidechainSignature(pub Vec<u8>);
664
665#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
669#[byte_string(debug, hex_serialize, hex_deserialize)]
670pub struct CrossChainPublicKey(pub Vec<u8>);
671
672impl CrossChainPublicKey {
673 pub fn hash(&self) -> CrossChainKeyHash {
675 CrossChainKeyHash(blake2b(&self.0))
676 }
677}
678
679impl From<k256::PublicKey> for CrossChainPublicKey {
680 fn from(value: k256::PublicKey) -> Self {
681 Self(value.to_sec1_bytes().to_vec())
682 }
683}
684
685impl From<CrossChainPublicKey> for k256::PublicKey {
686 fn from(value: CrossChainPublicKey) -> Self {
687 k256::PublicKey::from_sec1_bytes(&value.0)
688 .expect("CrossChainPublicKey converts to valid secp256k1::PublicKey")
689 }
690}
691
692const CROSS_CHAIN_KEY_HASH_LEN: usize = 28;
694
695#[derive(
696 Clone,
697 Copy,
698 Decode,
699 DecodeWithMemTracking,
700 Default,
701 Encode,
702 Hash,
703 MaxEncodedLen,
704 Eq,
705 PartialEq,
706 Ord,
707 PartialOrd,
708 TypeInfo,
709)]
710#[byte_string(debug, to_hex_string)]
711#[cfg_attr(feature = "std", byte_string(decode_hex))]
712#[cfg_attr(feature = "serde", byte_string(hex_serialize, hex_deserialize))]
713pub struct CrossChainKeyHash(pub [u8; CROSS_CHAIN_KEY_HASH_LEN]);
715
716impl Display for CrossChainKeyHash {
717 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
718 f.write_str(&self.to_hex_string())
719 }
720}
721
722#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
724#[byte_string(debug, hex_serialize)]
725pub struct CrossChainSignature(pub Vec<u8>);
726
727impl CrossChainSignature {
728 pub fn verify(
730 &self,
731 cross_chain_pubkey: &CrossChainPublicKey,
732 data: &[u8],
733 ) -> Result<(), k256::ecdsa::signature::Error> {
734 use k256::ecdsa::signature::Verifier;
735
736 let vkey = k256::ecdsa::VerifyingKey::from_sec1_bytes(&cross_chain_pubkey.0[..])?;
737 let signature = k256::ecdsa::Signature::from_slice(&self.0[..])?;
738 vkey.verify(data, &signature)
739 }
740}
741
742const EPOCH_NONCE_LEN: usize = 32;
744
745#[derive(Default, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
752#[byte_string(debug, hex_serialize)]
753pub struct EpochNonce(pub Vec<u8>);
754
755impl EpochNonce {
756 pub fn as_array(&self) -> [u8; EPOCH_NONCE_LEN] {
758 let mut epoch_nonce = self.0.clone();
759 epoch_nonce.resize_with(32, || 0);
760 epoch_nonce.try_into().expect("Should never fail after being resized")
761 }
762}
763
764#[derive(
765 Default,
766 Debug,
767 Copy,
768 Clone,
769 PartialEq,
770 Eq,
771 Encode,
772 Decode,
773 DecodeWithMemTracking,
774 ToDatum,
775 TypeInfo,
776 MaxEncodedLen,
777 Hash,
778)]
779pub struct UtxoId {
787 pub tx_hash: McTxHash,
789 pub index: UtxoIndex,
791}
792
793impl UtxoId {
794 pub const fn new(hash: [u8; TX_HASH_SIZE], index: u16) -> UtxoId {
796 UtxoId { tx_hash: McTxHash(hash), index: UtxoIndex(index) }
797 }
798}
799
800#[cfg(feature = "serde")]
801impl Serialize for UtxoId {
802 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
803 where
804 S: Serializer,
805 {
806 serializer.serialize_str(&self.to_string())
807 }
808}
809
810#[cfg(feature = "serde")]
811impl<'de> Deserialize<'de> for UtxoId {
812 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
813 where
814 D: Deserializer<'de>,
815 {
816 alloc::string::String::deserialize(deserializer).and_then(|string| {
817 Self::from_str(&string).map_err(|err| serde::de::Error::custom(err.to_string()))
818 })
819 }
820}
821
822#[cfg(feature = "serde")]
823impl FromStr for UtxoId {
824 type Err = &'static str;
825
826 fn from_str(s: &str) -> Result<Self, Self::Err> {
827 let split: Vec<&str> = s.split('#').collect();
828 let &[hash_str, index_str] = split.as_slice() else {
829 return Err("UtxoId string must conform to format: '<hash>#<index>'");
830 };
831
832 Ok(UtxoId {
833 tx_hash: McTxHash::from_str(hash_str)
834 .map_err(|_| "invalid string input for McTxHash")?,
835 index: UtxoIndex::from_str(index_str)
836 .map_err(|_| "invalid string input for OutputIndex")?,
837 })
838 }
839}
840
841impl Display for UtxoId {
842 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
843 let hash = sp_core::hexdisplay::HexDisplay::from(&self.tx_hash.0);
844 write!(f, "{}#{}", hash, self.index.0)
845 }
846}
847
848#[derive(
849 Default,
850 Debug,
851 Copy,
852 Clone,
853 PartialEq,
854 Eq,
855 Encode,
856 Decode,
857 DecodeWithMemTracking,
858 PartialOrd,
859 Ord,
860 ToDatum,
861 TypeInfo,
862 MaxEncodedLen,
863 Hash,
864)]
865#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
866pub struct UtxoIndex(pub u16);
868
869#[cfg(feature = "serde")]
870impl FromStr for UtxoIndex {
871 type Err = sp_std::num::ParseIntError;
872 fn from_str(s: &str) -> Result<Self, Self::Err> {
873 let parsed = u16::from_str(s)?;
874 let _check_overflow = i16::from_str(s)?;
875 Ok(Self(parsed))
876 }
877}
878
879pub const TX_HASH_SIZE: usize = 32;
881
882#[derive(
883 Default,
884 Copy,
885 Clone,
886 Hash,
887 PartialEq,
888 Eq,
889 Encode,
890 Decode,
891 DecodeWithMemTracking,
892 ToDatum,
893 TypeInfo,
894 MaxEncodedLen,
895)]
896#[byte_string(debug, from_bytes, decode_hex, hex_serialize, hex_deserialize)]
897#[constructor_datum]
898pub struct McTxHash(pub [u8; TX_HASH_SIZE]);
902
903impl TryFrom<Vec<u8>> for McTxHash {
904 type Error = &'static str;
905
906 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
907 <[u8; 32]>::try_from(value)
908 .map_err(|_| "McTxHash must be 32 bytes long")
909 .map(McTxHash)
910 }
911}
912
913#[derive(
914 Default,
915 Clone,
916 Decode,
917 DecodeWithMemTracking,
918 Encode,
919 PartialEq,
920 Eq,
921 TypeInfo,
922 MaxEncodedLen,
923 Hash,
924)]
925#[byte_string(debug, decode_hex, hex_serialize, hex_deserialize)]
926pub struct McBlockHash(pub [u8; 32]);
930
931impl Display for McBlockHash {
932 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
933 let hash = sp_core::hexdisplay::HexDisplay::from(&self.0);
934 write!(f, "{}", hash)
935 }
936}
937
938#[derive(
940 Default, Debug, Copy, Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo,
941)]
942#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
943#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
944pub struct UtxoInfo {
945 pub utxo_id: UtxoId,
947 pub epoch_number: McEpochNumber,
949 pub block_number: McBlockNumber,
951 pub slot_number: McSlotNumber,
953 pub tx_index_within_block: McTxIndexInBlock,
955}
956
957#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
962pub struct UtxoInfoOrderingKey {
963 pub block_number: McBlockNumber,
965 pub tx_index_within_block: McTxIndexInBlock,
967 pub utxo_id_index: UtxoIndex,
969}
970
971impl UtxoInfo {
972 pub fn ordering_key(&self) -> UtxoInfoOrderingKey {
974 UtxoInfoOrderingKey {
975 block_number: self.block_number,
976 tx_index_within_block: self.tx_index_within_block,
977 utxo_id_index: self.utxo_id.index,
978 }
979 }
980}
981
982#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
989#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
990#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
991pub enum NetworkType {
992 Mainnet,
994 #[default]
996 Testnet,
997}
998
999#[cfg(feature = "std")]
1000impl std::fmt::Display for NetworkType {
1001 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1002 let str = match self {
1003 Self::Mainnet => "mainnet",
1004 Self::Testnet => "testnet",
1005 };
1006 write!(f, "{}", str)
1007 }
1008}
1009
1010#[derive(Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
1019#[cfg_attr(feature = "serde", derive(Serialize))]
1020pub struct RegistrationData {
1021 pub registration_utxo: UtxoId,
1023 pub sidechain_signature: SidechainSignature,
1025 pub mainchain_signature: MainchainSignature,
1027 pub cross_chain_signature: CrossChainSignature,
1029 pub sidechain_pub_key: SidechainPublicKey,
1031 pub cross_chain_pub_key: CrossChainPublicKey,
1033 pub utxo_info: UtxoInfo,
1035 pub tx_inputs: Vec<UtxoId>,
1037 pub keys: CandidateKeys,
1039}
1040
1041#[derive(Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
1043#[cfg_attr(feature = "serde", derive(Serialize))]
1044pub struct CandidateRegistrations {
1045 pub stake_pool_public_key: StakePoolPublicKey,
1047 pub registrations: Vec<RegistrationData>,
1049 pub stake_delegation: Option<StakeDelegation>,
1051}
1052
1053impl CandidateRegistrations {
1054 pub fn new(
1056 stake_pool_public_key: StakePoolPublicKey,
1057 stake_delegation: Option<StakeDelegation>,
1058 registrations: Vec<RegistrationData>,
1059 ) -> Self {
1060 Self { stake_pool_public_key, registrations, stake_delegation }
1061 }
1062
1063 pub fn mainchain_pub_key(&self) -> &StakePoolPublicKey {
1065 &self.stake_pool_public_key
1066 }
1067
1068 pub fn registrations(&self) -> &[RegistrationData] {
1070 &self.registrations
1071 }
1072}
1073
1074#[derive(
1076 Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialOrd, Ord, Hash,
1077)]
1078#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
1079pub struct AuraPublicKey(pub Vec<u8>);
1080impl AuraPublicKey {
1081 pub fn try_into_sr25519(&self) -> Option<sr25519::Public> {
1083 Some(sr25519::Public::from_raw(self.0.clone().try_into().ok()?))
1084 }
1085}
1086
1087impl From<sr25519::Public> for AuraPublicKey {
1088 fn from(value: sr25519::Public) -> Self {
1089 Self(value.0.to_vec())
1090 }
1091}
1092
1093impl From<AuraPublicKey> for CandidateKey {
1094 fn from(value: AuraPublicKey) -> Self {
1095 Self { id: AURA.0, bytes: value.0 }
1096 }
1097}
1098
1099#[derive(
1101 Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialOrd, Ord, Hash,
1102)]
1103#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
1104pub struct GrandpaPublicKey(pub Vec<u8>);
1105impl GrandpaPublicKey {
1106 pub fn try_into_ed25519(&self) -> Option<ed25519::Public> {
1108 Some(ed25519::Public::from_raw(self.0.clone().try_into().ok()?))
1109 }
1110}
1111
1112impl From<ed25519::Public> for GrandpaPublicKey {
1113 fn from(value: ed25519::Public) -> Self {
1114 Self(value.0.to_vec())
1115 }
1116}
1117
1118impl From<GrandpaPublicKey> for CandidateKey {
1119 fn from(value: GrandpaPublicKey) -> Self {
1120 Self { id: GRANDPA.0, bytes: value.0 }
1121 }
1122}
1123
1124#[derive(
1125 Debug,
1126 Clone,
1127 PartialEq,
1128 Decode,
1129 DecodeWithMemTracking,
1130 Encode,
1131 MaxEncodedLen,
1132 TypeInfo,
1133 Eq,
1134 Hash,
1135)]
1136#[cfg_attr(feature = "serde", derive(Serialize))]
1137pub struct DParameter {
1145 pub num_permissioned_candidates: u16,
1147 pub num_registered_candidates: u16,
1149}
1150
1151impl DParameter {
1152 pub fn new(num_permissioned_candidates: u16, num_registered_candidates: u16) -> Self {
1154 Self { num_permissioned_candidates, num_registered_candidates }
1155 }
1156}
1157
1158#[derive(
1160 Debug,
1161 Clone,
1162 PartialEq,
1163 Eq,
1164 Decode,
1165 DecodeWithMemTracking,
1166 Encode,
1167 TypeInfo,
1168 PartialOrd,
1169 Ord,
1170 Hash,
1171)]
1172#[cfg_attr(feature = "serde", derive(Serialize))]
1173pub struct CandidateKey {
1174 pub id: [u8; 4],
1176 pub bytes: Vec<u8>,
1178}
1179
1180impl FromStr for CandidateKey {
1181 type Err = &'static str;
1182
1183 fn from_str(s: &str) -> Result<Self, Self::Err> {
1184 if let Some((id, bytes)) = s.split_once(':') {
1185 let id: [u8; 4] = id.as_bytes().try_into().map_err(|_| "invalid key type id")?;
1186 let bytes = bytes.trim_start_matches("0x");
1187 let bytes = hex::decode(bytes).map_err(|_| "key bytes are not in hex format")?;
1188 Ok(CandidateKey { id, bytes })
1189 } else {
1190 Err("invalid format of CandidateKey, expected '<key type>:<key>'")
1191 }
1192 }
1193}
1194
1195pub const CROSS_CHAIN_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"crch");
1197
1198impl CandidateKey {
1199 pub fn new(id: KeyTypeId, bytes: Vec<u8>) -> Self {
1201 Self { id: id.0, bytes }
1202 }
1203}
1204
1205#[derive(Debug, Clone, PartialEq, Eq, DecodeWithMemTracking, TypeInfo, PartialOrd, Ord, Hash)]
1206#[cfg_attr(feature = "serde", derive(Serialize))]
1207pub struct CandidateKeys(pub Vec<CandidateKey>);
1209
1210impl CandidateKeys {
1211 pub fn find(&self, id: KeyTypeId) -> Option<Vec<u8>> {
1213 self.0
1214 .iter()
1215 .find_map(|e| if e.id == id.0 { Some(e.bytes.clone()) } else { None })
1216 }
1217
1218 pub fn find_or_empty(&self, id: KeyTypeId) -> Vec<u8> {
1220 self.find(id).unwrap_or_default()
1221 }
1222
1223 pub fn has_only_aura_and_grandpa_keys(&self) -> bool {
1225 self.0.len() == 2 && self.find(AURA).is_some() && self.find(GRANDPA).is_some()
1226 }
1227}
1228
1229impl From<Vec<([u8; 4], Vec<u8>)>> for CandidateKeys {
1230 fn from(value: Vec<([u8; 4], Vec<u8>)>) -> Self {
1231 Self(value.into_iter().map(|(id, bytes)| CandidateKey { id, bytes }).collect())
1232 }
1233}
1234
1235impl Encode for CandidateKeys {
1246 fn size_hint(&self) -> usize {
1247 if self.has_only_aura_and_grandpa_keys() {
1248 Encode::size_hint(&AuraPublicKey(self.find_or_empty(AURA)))
1249 .saturating_add(Encode::size_hint(&GrandpaPublicKey(self.find_or_empty(GRANDPA))))
1250 } else {
1251 Encode::size_hint(&Compact(u32::MAX)).saturating_add(Encode::size_hint(&self.0))
1252 }
1253 }
1254
1255 fn encode_to<T: parity_scale_codec::Output + ?Sized>(&self, dest: &mut T) {
1256 if self.has_only_aura_and_grandpa_keys() {
1257 Encode::encode_to(&AuraPublicKey(self.find_or_empty(AURA)), dest);
1258 Encode::encode_to(&GrandpaPublicKey(self.find_or_empty(GRANDPA)), dest)
1259 } else {
1260 Encode::encode_to(&Compact(u32::MAX), dest);
1263 Encode::encode_to(&self.0, dest)
1264 }
1265 }
1266}
1267
1268impl Decode for CandidateKeys {
1270 fn decode<I: parity_scale_codec::Input>(
1271 input: &mut I,
1272 ) -> Result<Self, parity_scale_codec::Error> {
1273 let marker_or_aura_size: u32 = <Compact<u32>>::decode(input)?.0;
1275 if marker_or_aura_size == u32::MAX {
1276 let keys = Vec::<CandidateKey>::decode(input)?;
1277 Ok(Self(keys))
1278 } else {
1279 let aura_bytes: Vec<u8> = decode_vec_with_len(input, marker_or_aura_size as usize)?;
1280 let grandpa = GrandpaPublicKey::decode(input)?;
1281 Ok(Self(vec![AuraPublicKey(aura_bytes).into(), grandpa.into()]))
1282 }
1283 }
1284}
1285
1286#[derive(
1287 Debug,
1288 Clone,
1289 PartialEq,
1290 Eq,
1291 Decode,
1292 DecodeWithMemTracking,
1293 Encode,
1294 TypeInfo,
1295 PartialOrd,
1296 Ord,
1297 Hash,
1298)]
1299#[cfg_attr(feature = "serde", derive(Serialize))]
1300pub struct PermissionedCandidateData {
1306 pub sidechain_public_key: SidechainPublicKey,
1308 pub keys: CandidateKeys,
1310}
1311
1312#[cfg(feature = "std")]
1314impl FromStr for PermissionedCandidateData {
1315 type Err = alloc::boxed::Box<dyn core::error::Error + Send + Sync>;
1316
1317 fn from_str(partner_chain_public_keys: &str) -> Result<Self, Self::Err> {
1318 fn is_legacy_format(line: &str) -> bool {
1319 line.contains(':') && !line.contains(',')
1320 }
1321
1322 fn parse_legacy_format(
1323 line: &str,
1324 ) -> Result<
1325 PermissionedCandidateData,
1326 alloc::boxed::Box<dyn core::error::Error + Send + Sync>,
1327 > {
1328 let line = line.replace("0x", "");
1329 if let [sidechain_pub_key, aura_pub_key, grandpa_pub_key] =
1330 line.split(":").collect::<Vec<_>>()[..]
1331 {
1332 Ok(PermissionedCandidateData {
1333 sidechain_public_key: SidechainPublicKey(
1334 hex::decode(sidechain_pub_key).map_err(|e| e.to_string())?,
1335 ),
1336 keys: CandidateKeys(vec![
1337 AuraPublicKey(hex::decode(aura_pub_key).map_err(|e| e.to_string())?).into(),
1338 GrandpaPublicKey(hex::decode(grandpa_pub_key).map_err(|e| e.to_string())?)
1339 .into(),
1340 ]),
1341 })
1342 } else {
1343 Err(format!("Failed to parse partner chain public keys (legacy) from '{line}'")
1344 .into())
1345 }
1346 }
1347
1348 fn parse_generic_format(
1349 line: &str,
1350 ) -> Result<
1351 PermissionedCandidateData,
1352 alloc::boxed::Box<dyn core::error::Error + Send + Sync>,
1353 > {
1354 let mut columns = line.split(",");
1355 if let Some(partner_chains_key) = columns.next() {
1356 let partner_chains_key = SidechainPublicKey(
1357 hex::decode(partner_chains_key.trim_start_matches("0x"))
1358 .map_err(|e| e.to_string())?,
1359 );
1360 let mut keys = vec![];
1361 for column in columns {
1362 let key = CandidateKey::from_str(column)?;
1363 keys.push(key);
1364 }
1365 Ok(PermissionedCandidateData {
1366 sidechain_public_key: partner_chains_key,
1367 keys: CandidateKeys(keys),
1368 })
1369 } else {
1370 Err("Failed to parse partner chain public keys (generic) from '{line}'.".into())
1371 }
1372 }
1373
1374 if is_legacy_format(&partner_chain_public_keys) {
1375 parse_legacy_format(&partner_chain_public_keys)
1376 } else {
1377 parse_generic_format(&partner_chain_public_keys)
1378 }
1379 }
1380}
1381
1382#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1384pub struct CandidateRegistration {
1385 pub stake_ownership: AdaBasedStaking,
1387 pub partner_chain_pub_key: SidechainPublicKey,
1389 pub partner_chain_signature: SidechainSignature,
1391 pub own_pkh: MainchainKeyHash,
1393 pub registration_utxo: UtxoId,
1395 pub keys: CandidateKeys,
1397}
1398
1399impl CandidateRegistration {
1400 pub fn matches_keys(&self, other: &Self) -> bool {
1402 self.stake_ownership == other.stake_ownership
1403 && self.partner_chain_pub_key == other.partner_chain_pub_key
1404 && self.partner_chain_signature == other.partner_chain_signature
1405 && self.keys.0.iter().all(|key| other.keys.0.contains(key))
1406 }
1407}
1408
1409#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1413pub struct AdaBasedStaking {
1414 pub pub_key: StakePoolPublicKey,
1416 pub signature: MainchainSignature,
1418}
1419
1420#[derive(
1421 Clone,
1422 PartialEq,
1423 Eq,
1424 Ord,
1425 PartialOrd,
1426 TypeInfo,
1427 MaxEncodedLen,
1428 Encode,
1429 Decode,
1430 DecodeWithMemTracking,
1431)]
1432pub enum DelegatorKey {
1434 StakeKeyHash([u8; 28]),
1436 ScriptKeyHash {
1438 hash_raw: [u8; 28],
1440 script_hash: [u8; 28],
1442 },
1443}
1444
1445impl alloc::fmt::Debug for DelegatorKey {
1446 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
1447 let s = match self {
1448 Self::ScriptKeyHash { hash_raw, script_hash } => alloc::format!(
1449 "ScriptKeyHash{{ hash_raw: {}, script_hash: {} }}",
1450 hex::encode(hash_raw),
1451 hex::encode(script_hash)
1452 ),
1453 Self::StakeKeyHash(hash) => alloc::format!("StakeKeyHash({})", hex::encode(hash)),
1454 };
1455
1456 f.write_str(&s)
1457 }
1458}
1459
1460#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
1462pub struct DelegatorStakeAmount(pub u64);
1463
1464impl<T: Into<u64>> From<T> for DelegatorStakeAmount {
1465 fn from(value: T) -> Self {
1466 Self(value.into())
1467 }
1468}
1469
1470#[derive(Debug, Clone, Default)]
1475pub struct StakeDistribution(pub BTreeMap<MainchainKeyHash, PoolDelegation>);
1476
1477#[derive(Debug, Clone, Default, PartialEq)]
1479pub struct PoolDelegation {
1480 pub total_stake: StakeDelegation,
1482 pub delegators: BTreeMap<DelegatorKey, DelegatorStakeAmount>,
1484}
1485
1486pub trait FromStrStdErr:
1488 FromStr<Err: Into<alloc::boxed::Box<dyn core::error::Error + Send + Sync + 'static>>>
1489{
1490}
1491impl<T: FromStr<Err: Into<alloc::boxed::Box<dyn core::error::Error + Send + Sync + 'static>>>>
1492 FromStrStdErr for T
1493{
1494}
1495
1496#[cfg(test)]
1497mod tests {
1498 use super::*;
1499 use core::str::FromStr;
1500 use hex_literal::hex;
1501 use parity_scale_codec::{Decode, Encode};
1502
1503 #[test]
1504 fn main_chain_address_string_serialize_deserialize_round_trip() {
1505 let address = MainchainAddress::from_str(
1506 "addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc",
1507 )
1508 .unwrap();
1509 let serialized = serde_json::to_value(&address).unwrap();
1510 assert_eq!(
1511 serialized,
1512 serde_json::json!("addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc")
1513 );
1514 let deserialized = serde_json::from_value(serialized).unwrap();
1515 assert_eq!(address, deserialized);
1516 }
1517
1518 #[test]
1519 fn main_chain_address_deserialization_of_hex_encoded_bytes() {
1520 let address = MainchainAddress::from_str("addr_test1wz5q").unwrap();
1521 let serialized = serde_json::json!("0x616464725f7465737431777a3571");
1522 assert_eq!(address, serde_json::from_value(serialized).unwrap());
1523 let serialized = serde_json::json!("616464725f7465737431777a3571");
1524 assert_eq!(address, serde_json::from_value(serialized).unwrap());
1525 }
1526
1527 #[test]
1528 fn main_chain_address_string_from_str_to_string_round_trip() {
1529 let address = MainchainAddress::from_str(
1530 "addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc",
1531 )
1532 .unwrap();
1533 let str = address.to_string();
1534 let from_str = MainchainAddress::from_str(&str).unwrap();
1535 assert_eq!(address, from_str);
1536 }
1537
1538 #[test]
1539 fn main_chain_signature_should_be_backward_compatible_with_vec() {
1540 #[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
1541 #[byte_string(debug, hex_serialize, decode_hex)]
1542 struct LegacyMCSignature(pub Vec<u8>);
1543
1544 let legacy_encoded = LegacyMCSignature(vec![10; 64]).encode();
1545
1546 let legacy_decoded = MainchainSignature::decode(&mut legacy_encoded.as_slice())
1547 .expect("Encoded legacy should decode to current type");
1548
1549 assert_eq!(legacy_decoded.0, [10; MAINCHAIN_SIGNATURE_LEN]);
1550
1551 let current_encoded = MainchainSignature([9; MAINCHAIN_SIGNATURE_LEN]).encode();
1552
1553 let current_decoded = LegacyMCSignature::decode(&mut current_encoded.as_slice())
1554 .expect("Encoded current should decode to legacy");
1555
1556 assert_eq!(current_decoded.0, vec![9; 64]);
1557 }
1558
1559 #[test]
1560 fn cross_chain_signature_verify_works() {
1561 let signature = CrossChainSignature(
1562 hex!("d1e02e4a5484c3b7202ce6b844577048e7578dc62901cf8f51e6d74bbd3adb091688feacedd8343d0b04a0f5862b2e06148934a75e678e42051fde5431eca33d").to_vec()
1563 );
1564 let pubkey = CrossChainPublicKey(
1565 hex!("020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1").to_vec(),
1566 );
1567 let signed_data = hex!(
1568 "84020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a16c68747470733a2f2f636f6f6c2e73747566662f73706f2e6a736f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
1569 );
1570
1571 assert!(signature.verify(&pubkey, &signed_data).is_ok())
1572 }
1573
1574 #[derive(Decode, Encode, PartialEq, Eq, Debug)]
1575 struct TestCandidateKeys {
1576 field_before: Option<u32>,
1577 keys: CandidateKeys,
1578 field_after: Option<u32>,
1580 }
1581
1582 #[test]
1583 fn encode_decode_round_trip_for_generic_candidate_keys() {
1584 let keys = TestCandidateKeys {
1585 field_before: Some(42),
1586 keys: CandidateKeys(vec![
1587 CandidateKey { id: *b"abcd", bytes: [7u8; 32].to_vec() },
1588 CandidateKey { id: *b"efgh", bytes: [9u8; 32].to_vec() },
1589 ]),
1590 field_after: Some(15),
1591 };
1592 let bytes: Vec<u8> = Encode::encode(&keys);
1593 let mut bytes: &[u8] = &bytes;
1594 let decoded = TestCandidateKeys::decode(&mut bytes).unwrap();
1595 assert_eq!(keys, decoded)
1596 }
1597
1598 #[test]
1599 fn encode_decode_round_trip_for_aura_and_grandpa_candidate_keys() {
1600 let keys = TestCandidateKeys {
1601 field_before: Some(42),
1602 keys: CandidateKeys(vec![
1603 AuraPublicKey([7u8; 32].to_vec()).into(),
1604 GrandpaPublicKey([9u8; 32].to_vec()).into(),
1605 ]),
1606 field_after: Some(15),
1607 };
1608 let bytes: Vec<u8> = Encode::encode(&keys);
1609 let mut bytes: &[u8] = &bytes;
1610 let decoded = TestCandidateKeys::decode(&mut bytes).unwrap();
1611 assert_eq!(keys, decoded)
1612 }
1613}