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
165impl McBlockNumber {
166 pub fn saturating_add<Rhs: Into<u32>>(self, rhs: Rhs) -> Self {
168 Self(self.0.saturating_add(rhs.into()))
169 }
170
171 pub fn saturating_sub<Rhs: Into<u32>>(self, rhs: Rhs) -> Self {
173 Self(self.0.saturating_sub(rhs.into()))
174 }
175}
176
177#[derive(
178 Default,
179 Debug,
180 Copy,
181 Clone,
182 PartialEq,
183 Eq,
184 PartialOrd,
185 Encode,
186 Decode,
187 DecodeWithMemTracking,
188 TypeInfo,
189 Hash,
190)]
191#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, FromStr))]
192pub struct McSlotNumber(pub u64);
194
195impl Display for McSlotNumber {
196 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
197 u64::fmt(&self.0, f)
198 }
199}
200
201#[derive(
202 Default,
203 Debug,
204 Copy,
205 Clone,
206 PartialEq,
207 Eq,
208 Encode,
209 Decode,
210 DecodeWithMemTracking,
211 TypeInfo,
212 Hash,
213 MaxEncodedLen,
214 PartialOrd,
215 Ord,
216)]
217#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, FromStr))]
218pub struct ScSlotNumber(pub u64);
220
221#[derive(Debug, Clone, PartialEq, Default)]
223#[cfg_attr(feature = "serde", derive(serde::Serialize))]
224pub struct MainchainBlock {
225 pub number: McBlockNumber,
227 pub hash: McBlockHash,
229 pub epoch: McEpochNumber,
231 pub slot: McSlotNumber,
233 pub timestamp: u64, }
236
237#[derive(
238 Default,
239 Debug,
240 Copy,
241 Clone,
242 PartialEq,
243 Eq,
244 Encode,
245 Decode,
246 DecodeWithMemTracking,
247 PartialOrd,
248 Ord,
249 TypeInfo,
250 MaxEncodedLen,
251)]
252#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
253pub struct McTxIndexInBlock(pub u32);
255
256#[cfg(feature = "serde")]
257impl FromStr for McTxIndexInBlock {
258 type Err = sp_std::num::ParseIntError;
259 fn from_str(s: &str) -> Result<Self, Self::Err> {
260 let parsed = u32::from_str(s)?;
261 let _check_overflow = i32::from_str(s)?;
262 Ok(Self(parsed))
263 }
264}
265
266const MAX_MAINCHAIN_ADDRESS_BYTES: u32 = 120;
268
269#[derive(
273 Clone, Default, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen,
274)]
275#[byte_string(debug)]
276pub struct MainchainAddress(BoundedVec<u8, ConstU32<MAX_MAINCHAIN_ADDRESS_BYTES>>);
277
278impl MainchainAddress {
279 pub fn bytes(&self) -> Vec<u8> {
281 self.0.to_vec()
282 }
283}
284
285#[cfg(feature = "serde")]
286impl FromStr for MainchainAddress {
287 type Err = &'static str;
288
289 fn from_str(s: &str) -> Result<Self, Self::Err> {
290 let bytes: Vec<u8> = s.as_bytes().to_vec();
291 let bounded = BoundedVec::try_from(bytes).map_err(|_| "Invalid length")?;
292 Ok(MainchainAddress(bounded))
293 }
294}
295
296impl Display for MainchainAddress {
297 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
298 let s = String::from_utf8(self.0.to_vec())
299 .expect("MainchainAddressString is always properly encoded UTF-8");
300 write!(f, "{}", s)
301 }
302}
303
304#[cfg(feature = "serde")]
305impl serde::Serialize for MainchainAddress {
306 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
307 where
308 S: serde::Serializer,
309 {
310 let s = String::from_utf8(self.0.to_vec()).expect("MainchainAddress is always valid UTF-8");
311 serializer.serialize_str(&s)
312 }
313}
314
315#[cfg(feature = "serde")]
316impl<'de> serde::Deserialize<'de> for MainchainAddress {
317 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
319 where
320 D: serde::Deserializer<'de>,
321 {
322 let s = String::deserialize(deserializer)?;
323 let bytes = sp_core::bytes::from_hex(&s).unwrap_or_else(|_| s.as_bytes().to_vec());
324 let bounded = BoundedVec::try_from(bytes)
325 .map_err(|_| serde::de::Error::custom("MainchainAddress is too long"))?;
326 Ok(MainchainAddress(bounded))
327 }
328}
329
330const POLICY_ID_LEN: usize = 28;
332#[derive(
333 Clone,
334 Default,
335 PartialEq,
336 Eq,
337 Encode,
338 Decode,
339 DecodeWithMemTracking,
340 ToDatum,
341 TypeInfo,
342 MaxEncodedLen,
343 Hash,
344)]
345#[byte_string(debug, decode_hex, hex_serialize, hex_deserialize)]
346#[cfg_attr(feature = "std", byte_string(to_hex_string))]
347pub struct PolicyId(pub [u8; POLICY_ID_LEN]);
349
350#[cfg(feature = "std")]
351impl Display for PolicyId {
352 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
353 write!(f, "{}", self.to_hex_string())
354 }
355}
356
357pub type ScriptHash = PolicyId;
359
360pub const MAX_ASSET_NAME_LEN: u32 = 32;
362
363#[derive(
365 Clone, Default, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen,
366)]
367#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
368#[cfg_attr(feature = "std", byte_string(to_hex_string))]
369pub struct AssetName(pub BoundedVec<u8, ConstU32<MAX_ASSET_NAME_LEN>>);
370
371impl AssetName {
372 pub fn empty() -> Self {
374 Self(BoundedVec::new())
375 }
376}
377
378#[cfg(feature = "std")]
379impl Display for AssetName {
380 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
381 write!(f, "{}", self.to_hex_string())
382 }
383}
384
385#[derive(
386 Clone,
387 Debug,
388 PartialEq,
389 Eq,
390 Encode,
391 Decode,
392 DecodeWithMemTracking,
393 TypeInfo,
394 MaxEncodedLen,
395 Default,
396)]
397#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
398pub struct AssetId {
400 pub policy_id: PolicyId,
402 pub asset_name: AssetName,
404}
405
406#[cfg(feature = "std")]
407impl FromStr for AssetId {
408 type Err = String;
409
410 fn from_str(s: &str) -> Result<Self, Self::Err> {
411 match s.split_once(".") {
412 Some((policy_id, asset_name)) => {
413 let policy_id = PolicyId::from_str(policy_id)
414 .map_err(|e| format!("{} is invalid Policy ID: {}", policy_id, e))?;
415 let asset_name = AssetName::from_str(asset_name)
416 .map_err(|e| format!("{} is invalid Asset Name: {}", asset_name, e))?;
417 Ok(Self { policy_id, asset_name })
418 },
419 None => {
420 Err("AssetId should be <hex encoded Policy ID>.<hex encoded Asset Name>"
421 .to_string())
422 },
423 }
424 }
425}
426
427const STAKE_POOL_PUBLIC_KEY_LEN: usize = 32;
429
430#[derive(
431 Clone,
432 PartialEq,
433 Eq,
434 Encode,
435 Decode,
436 DecodeWithMemTracking,
437 TypeInfo,
438 MaxEncodedLen,
439 Hash,
440 Ord,
441 PartialOrd,
442)]
443#[cfg_attr(feature = "std", byte_string(to_hex_string))]
444#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
445pub struct StakePoolPublicKey(pub [u8; STAKE_POOL_PUBLIC_KEY_LEN]);
447
448impl StakePoolPublicKey {
449 pub fn hash(&self) -> MainchainKeyHash {
451 MainchainKeyHash::from_vkey(&self.0)
452 }
453}
454
455impl TryFrom<Vec<u8>> for StakePoolPublicKey {
456 type Error = &'static str;
457
458 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
459 <[u8; 32]>::try_from(value)
460 .map_err(|_| "Mainchain public key must be 32 bytes long")
461 .map(StakePoolPublicKey)
462 }
463}
464
465const STAKE_PUBLIC_KEY_LEN: usize = 32;
467
468#[derive(
470 Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen, Hash,
471)]
472#[cfg_attr(feature = "std", byte_string(to_hex_string))]
473#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
474pub struct StakePublicKey(pub [u8; STAKE_PUBLIC_KEY_LEN]);
475
476impl StakePublicKey {
477 pub fn hash(&self) -> MainchainKeyHash {
479 MainchainKeyHash(blake2b(&self.0))
480 }
481}
482
483const MAINCHAIN_KEY_HASH_LEN: usize = 28;
485
486#[derive(
487 Clone,
488 Copy,
489 Decode,
490 DecodeWithMemTracking,
491 Default,
492 Encode,
493 Hash,
494 MaxEncodedLen,
495 Eq,
496 PartialEq,
497 Ord,
498 PartialOrd,
499 TypeInfo,
500)]
501#[byte_string(debug)]
502#[cfg_attr(feature = "std", byte_string(to_hex_string, decode_hex))]
503#[cfg_attr(feature = "serde", byte_string(hex_serialize, hex_deserialize))]
504pub struct MainchainKeyHash(pub [u8; MAINCHAIN_KEY_HASH_LEN]);
507
508impl Display for MainchainKeyHash {
509 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
510 let hash = sp_core::hexdisplay::HexDisplay::from(&self.0);
511 write!(f, "0x{}", hash)
512 }
513}
514
515impl MainchainKeyHash {
516 pub fn from_vkey(vkey: &[u8; 32]) -> Self {
518 Self(blake2b(vkey))
519 }
520}
521
522pub const MAINCHAIN_SIGNATURE_LEN: usize = 64;
524
525#[derive(Clone, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
526#[cfg_attr(feature = "std", byte_string(to_hex_string))]
527#[byte_string(debug, hex_serialize, decode_hex)]
528pub struct MainchainSignature(pub [u8; MAINCHAIN_SIGNATURE_LEN]);
534
535impl From<[u8; MAINCHAIN_SIGNATURE_LEN]> for MainchainSignature {
536 fn from(raw: [u8; MAINCHAIN_SIGNATURE_LEN]) -> Self {
537 Self(raw)
538 }
539}
540
541impl WrapperTypeEncode for MainchainSignature {}
542impl Deref for MainchainSignature {
543 type Target = [u8];
544
545 fn deref(&self) -> &Self::Target {
546 &self.0
547 }
548}
549impl Decode for MainchainSignature {
550 fn decode<I: parity_scale_codec::Input>(
551 input: &mut I,
552 ) -> Result<Self, parity_scale_codec::Error> {
553 let vec: Vec<u8> = Decode::decode(input)?;
554 let arr = vec.try_into().map_err(|_| "Incorrect MainchainSignature size")?;
555 Ok(MainchainSignature(arr))
556 }
557}
558
559impl MainchainSignature {
560 pub fn verify(&self, public_key: &StakePoolPublicKey, signed_message: &[u8]) -> bool {
562 let mainchain_signature = ed25519::Signature::from(self.0);
563
564 sp_io::crypto::ed25519_verify(
565 &mainchain_signature,
566 signed_message,
567 &ed25519::Public::from(public_key.0),
568 )
569 }
570}
571
572pub const STAKE_KEY_SIGNATURE_LEN: usize = 64;
574
575#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, Hash, TypeInfo)]
576#[byte_string(debug, hex_serialize, decode_hex)]
577pub struct StakeKeySignature(pub [u8; STAKE_KEY_SIGNATURE_LEN]);
579
580impl From<[u8; STAKE_KEY_SIGNATURE_LEN]> for StakeKeySignature {
581 fn from(raw: [u8; STAKE_KEY_SIGNATURE_LEN]) -> Self {
582 Self(raw)
583 }
584}
585
586impl StakeKeySignature {
587 pub fn verify(&self, public_key: &StakePublicKey, message: &[u8]) -> bool {
589 let signature = ed25519::Signature::from(self.0);
590 sp_io::crypto::ed25519_verify(&signature, message, &ed25519::Public::from(public_key.0))
591 }
592}
593
594#[derive(
595 Clone,
596 Copy,
597 Debug,
598 Encode,
599 Decode,
600 DecodeWithMemTracking,
601 PartialEq,
602 TypeInfo,
603 ToDatum,
604 MaxEncodedLen,
605 Default,
606 PartialOrd,
607 Ord,
608 Eq,
609 Zero,
610 One,
611 NumOps,
612 Num,
613 From,
614 Into,
615)]
616#[cfg_attr(feature = "serde", derive(Serialize))]
617pub struct ScEpochNumber(pub u64);
619
620impl Display for ScEpochNumber {
621 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
622 u64::fmt(&self.0, f)
623 }
624}
625
626impl ScEpochNumber {
627 pub fn next(&self) -> Self {
629 Self(self.0 + 1)
630 }
631 pub fn prev(&self) -> Option<Self> {
633 self.0.checked_sub(1).map(Self)
634 }
635}
636
637#[derive(
638 Clone,
639 PartialEq,
640 Eq,
641 Encode,
642 Decode,
643 DecodeWithMemTracking,
644 ToDatum,
645 TypeInfo,
646 PartialOrd,
647 Ord,
648 Hash,
649)]
650#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex, as_ref)]
651pub struct SidechainPublicKey(pub Vec<u8>);
657
658impl From<ecdsa::Public> for SidechainPublicKey {
659 fn from(value: ecdsa::Public) -> Self {
660 Self(value.0.to_vec())
661 }
662}
663
664#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
666#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
667pub struct TransactionCbor(pub Vec<u8>);
668
669#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
671#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
672pub struct VKeyWitnessCbor(pub Vec<u8>);
673
674#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
676#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
677pub struct SidechainSignature(pub Vec<u8>);
678
679#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
683#[byte_string(debug, hex_serialize, hex_deserialize)]
684pub struct CrossChainPublicKey(pub Vec<u8>);
685
686impl CrossChainPublicKey {
687 pub fn hash(&self) -> CrossChainKeyHash {
689 CrossChainKeyHash(blake2b(&self.0))
690 }
691}
692
693impl From<k256::PublicKey> for CrossChainPublicKey {
694 fn from(value: k256::PublicKey) -> Self {
695 Self(value.to_sec1_bytes().to_vec())
696 }
697}
698
699impl From<CrossChainPublicKey> for k256::PublicKey {
700 fn from(value: CrossChainPublicKey) -> Self {
701 k256::PublicKey::from_sec1_bytes(&value.0)
702 .expect("CrossChainPublicKey converts to valid secp256k1::PublicKey")
703 }
704}
705
706const CROSS_CHAIN_KEY_HASH_LEN: usize = 28;
708
709#[derive(
710 Clone,
711 Copy,
712 Decode,
713 DecodeWithMemTracking,
714 Default,
715 Encode,
716 Hash,
717 MaxEncodedLen,
718 Eq,
719 PartialEq,
720 Ord,
721 PartialOrd,
722 TypeInfo,
723)]
724#[byte_string(debug, to_hex_string)]
725#[cfg_attr(feature = "std", byte_string(decode_hex))]
726#[cfg_attr(feature = "serde", byte_string(hex_serialize, hex_deserialize))]
727pub struct CrossChainKeyHash(pub [u8; CROSS_CHAIN_KEY_HASH_LEN]);
729
730impl Display for CrossChainKeyHash {
731 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
732 f.write_str(&self.to_hex_string())
733 }
734}
735
736#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
738#[byte_string(debug, hex_serialize)]
739pub struct CrossChainSignature(pub Vec<u8>);
740
741impl CrossChainSignature {
742 pub fn verify(
744 &self,
745 cross_chain_pubkey: &CrossChainPublicKey,
746 data: &[u8],
747 ) -> Result<(), k256::ecdsa::signature::Error> {
748 use k256::ecdsa::signature::Verifier;
749
750 let vkey = k256::ecdsa::VerifyingKey::from_sec1_bytes(&cross_chain_pubkey.0[..])?;
751 let signature = k256::ecdsa::Signature::from_slice(&self.0[..])?;
752 vkey.verify(data, &signature)
753 }
754}
755
756const EPOCH_NONCE_LEN: usize = 32;
758
759#[derive(Default, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
766#[byte_string(debug, hex_serialize)]
767pub struct EpochNonce(pub Vec<u8>);
768
769impl EpochNonce {
770 pub fn as_array(&self) -> [u8; EPOCH_NONCE_LEN] {
772 let mut epoch_nonce = self.0.clone();
773 epoch_nonce.resize_with(32, || 0);
774 epoch_nonce.try_into().expect("Should never fail after being resized")
775 }
776}
777
778#[derive(
779 Default,
780 Debug,
781 Copy,
782 Clone,
783 PartialEq,
784 Eq,
785 Encode,
786 Decode,
787 DecodeWithMemTracking,
788 ToDatum,
789 TypeInfo,
790 MaxEncodedLen,
791 Hash,
792)]
793pub struct UtxoId {
801 pub tx_hash: McTxHash,
803 pub index: UtxoIndex,
805}
806
807impl UtxoId {
808 pub const fn new(hash: [u8; TX_HASH_SIZE], index: u16) -> UtxoId {
810 UtxoId { tx_hash: McTxHash(hash), index: UtxoIndex(index) }
811 }
812}
813
814#[cfg(feature = "serde")]
815impl Serialize for UtxoId {
816 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
817 where
818 S: Serializer,
819 {
820 serializer.serialize_str(&self.to_string())
821 }
822}
823
824#[cfg(feature = "serde")]
825impl<'de> Deserialize<'de> for UtxoId {
826 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
827 where
828 D: Deserializer<'de>,
829 {
830 alloc::string::String::deserialize(deserializer).and_then(|string| {
831 Self::from_str(&string).map_err(|err| serde::de::Error::custom(err.to_string()))
832 })
833 }
834}
835
836#[cfg(feature = "serde")]
837impl FromStr for UtxoId {
838 type Err = &'static str;
839
840 fn from_str(s: &str) -> Result<Self, Self::Err> {
841 let split: Vec<&str> = s.split('#').collect();
842 let &[hash_str, index_str] = split.as_slice() else {
843 return Err("UtxoId string must conform to format: '<hash>#<index>'");
844 };
845
846 Ok(UtxoId {
847 tx_hash: McTxHash::from_str(hash_str)
848 .map_err(|_| "invalid string input for McTxHash")?,
849 index: UtxoIndex::from_str(index_str)
850 .map_err(|_| "invalid string input for OutputIndex")?,
851 })
852 }
853}
854
855impl Display for UtxoId {
856 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
857 let hash = sp_core::hexdisplay::HexDisplay::from(&self.tx_hash.0);
858 write!(f, "{}#{}", hash, self.index.0)
859 }
860}
861
862#[derive(
863 Default,
864 Debug,
865 Copy,
866 Clone,
867 PartialEq,
868 Eq,
869 Encode,
870 Decode,
871 DecodeWithMemTracking,
872 PartialOrd,
873 Ord,
874 ToDatum,
875 TypeInfo,
876 MaxEncodedLen,
877 Hash,
878)]
879#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
880pub struct UtxoIndex(pub u16);
882
883#[cfg(feature = "serde")]
884impl FromStr for UtxoIndex {
885 type Err = sp_std::num::ParseIntError;
886 fn from_str(s: &str) -> Result<Self, Self::Err> {
887 let parsed = u16::from_str(s)?;
888 let _check_overflow = i16::from_str(s)?;
889 Ok(Self(parsed))
890 }
891}
892
893pub const TX_HASH_SIZE: usize = 32;
895
896#[derive(
897 Default,
898 Copy,
899 Clone,
900 Hash,
901 PartialEq,
902 Eq,
903 Encode,
904 Decode,
905 DecodeWithMemTracking,
906 ToDatum,
907 TypeInfo,
908 MaxEncodedLen,
909)]
910#[byte_string(debug, from_bytes, decode_hex, hex_serialize, hex_deserialize)]
911#[constructor_datum]
912pub struct McTxHash(pub [u8; TX_HASH_SIZE]);
916
917impl TryFrom<Vec<u8>> for McTxHash {
918 type Error = &'static str;
919
920 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
921 <[u8; 32]>::try_from(value)
922 .map_err(|_| "McTxHash must be 32 bytes long")
923 .map(McTxHash)
924 }
925}
926
927#[derive(
928 Default,
929 Clone,
930 Decode,
931 DecodeWithMemTracking,
932 Encode,
933 PartialEq,
934 Eq,
935 TypeInfo,
936 MaxEncodedLen,
937 Hash,
938)]
939#[byte_string(debug, decode_hex, hex_serialize, hex_deserialize)]
940pub struct McBlockHash(pub [u8; 32]);
944
945impl Display for McBlockHash {
946 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
947 let hash = sp_core::hexdisplay::HexDisplay::from(&self.0);
948 write!(f, "{}", hash)
949 }
950}
951
952#[derive(
954 Default, Debug, Copy, Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo,
955)]
956#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
957#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
958pub struct UtxoInfo {
959 pub utxo_id: UtxoId,
961 pub epoch_number: McEpochNumber,
963 pub block_number: McBlockNumber,
965 pub slot_number: McSlotNumber,
967 pub tx_index_within_block: McTxIndexInBlock,
969}
970
971#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
976pub struct UtxoInfoOrderingKey {
977 pub block_number: McBlockNumber,
979 pub tx_index_within_block: McTxIndexInBlock,
981 pub utxo_id_index: UtxoIndex,
983}
984
985impl UtxoInfo {
986 pub fn ordering_key(&self) -> UtxoInfoOrderingKey {
988 UtxoInfoOrderingKey {
989 block_number: self.block_number,
990 tx_index_within_block: self.tx_index_within_block,
991 utxo_id_index: self.utxo_id.index,
992 }
993 }
994}
995
996#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
1003#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1004#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
1005pub enum NetworkType {
1006 Mainnet,
1008 #[default]
1010 Testnet,
1011}
1012
1013#[cfg(feature = "std")]
1014impl std::fmt::Display for NetworkType {
1015 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1016 let str = match self {
1017 Self::Mainnet => "mainnet",
1018 Self::Testnet => "testnet",
1019 };
1020 write!(f, "{}", str)
1021 }
1022}
1023
1024#[derive(Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
1033#[cfg_attr(feature = "serde", derive(Serialize))]
1034pub struct RegistrationData {
1035 pub registration_utxo: UtxoId,
1037 pub sidechain_signature: SidechainSignature,
1039 pub mainchain_signature: MainchainSignature,
1041 pub cross_chain_signature: CrossChainSignature,
1043 pub sidechain_pub_key: SidechainPublicKey,
1045 pub cross_chain_pub_key: CrossChainPublicKey,
1047 pub utxo_info: UtxoInfo,
1049 pub tx_inputs: Vec<UtxoId>,
1051 pub keys: CandidateKeys,
1053}
1054
1055#[derive(Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
1057#[cfg_attr(feature = "serde", derive(Serialize))]
1058pub struct CandidateRegistrations {
1059 pub stake_pool_public_key: StakePoolPublicKey,
1061 pub registrations: Vec<RegistrationData>,
1063 pub stake_delegation: Option<StakeDelegation>,
1065}
1066
1067impl CandidateRegistrations {
1068 pub fn new(
1070 stake_pool_public_key: StakePoolPublicKey,
1071 stake_delegation: Option<StakeDelegation>,
1072 registrations: Vec<RegistrationData>,
1073 ) -> Self {
1074 Self { stake_pool_public_key, registrations, stake_delegation }
1075 }
1076
1077 pub fn mainchain_pub_key(&self) -> &StakePoolPublicKey {
1079 &self.stake_pool_public_key
1080 }
1081
1082 pub fn registrations(&self) -> &[RegistrationData] {
1084 &self.registrations
1085 }
1086}
1087
1088#[derive(
1090 Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialOrd, Ord, Hash,
1091)]
1092#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
1093pub struct AuraPublicKey(pub Vec<u8>);
1094impl AuraPublicKey {
1095 pub fn try_into_sr25519(&self) -> Option<sr25519::Public> {
1097 Some(sr25519::Public::from_raw(self.0.clone().try_into().ok()?))
1098 }
1099}
1100
1101impl From<sr25519::Public> for AuraPublicKey {
1102 fn from(value: sr25519::Public) -> Self {
1103 Self(value.0.to_vec())
1104 }
1105}
1106
1107impl From<AuraPublicKey> for CandidateKey {
1108 fn from(value: AuraPublicKey) -> Self {
1109 Self { id: AURA.0, bytes: value.0 }
1110 }
1111}
1112
1113#[derive(
1115 Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialOrd, Ord, Hash,
1116)]
1117#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
1118pub struct GrandpaPublicKey(pub Vec<u8>);
1119impl GrandpaPublicKey {
1120 pub fn try_into_ed25519(&self) -> Option<ed25519::Public> {
1122 Some(ed25519::Public::from_raw(self.0.clone().try_into().ok()?))
1123 }
1124}
1125
1126impl From<ed25519::Public> for GrandpaPublicKey {
1127 fn from(value: ed25519::Public) -> Self {
1128 Self(value.0.to_vec())
1129 }
1130}
1131
1132impl From<GrandpaPublicKey> for CandidateKey {
1133 fn from(value: GrandpaPublicKey) -> Self {
1134 Self { id: GRANDPA.0, bytes: value.0 }
1135 }
1136}
1137
1138#[derive(
1139 Debug,
1140 Clone,
1141 PartialEq,
1142 Decode,
1143 DecodeWithMemTracking,
1144 Encode,
1145 MaxEncodedLen,
1146 TypeInfo,
1147 Eq,
1148 Hash,
1149)]
1150#[cfg_attr(feature = "serde", derive(Serialize))]
1151pub struct DParameter {
1159 pub num_permissioned_candidates: u16,
1161 pub num_registered_candidates: u16,
1163}
1164
1165impl DParameter {
1166 pub fn new(num_permissioned_candidates: u16, num_registered_candidates: u16) -> Self {
1168 Self { num_permissioned_candidates, num_registered_candidates }
1169 }
1170}
1171
1172#[derive(
1174 Debug,
1175 Clone,
1176 PartialEq,
1177 Eq,
1178 Decode,
1179 DecodeWithMemTracking,
1180 Encode,
1181 TypeInfo,
1182 PartialOrd,
1183 Ord,
1184 Hash,
1185)]
1186#[cfg_attr(feature = "serde", derive(Serialize))]
1187pub struct CandidateKey {
1188 pub id: [u8; 4],
1190 pub bytes: Vec<u8>,
1192}
1193
1194impl FromStr for CandidateKey {
1195 type Err = &'static str;
1196
1197 fn from_str(s: &str) -> Result<Self, Self::Err> {
1198 if let Some((id, bytes)) = s.split_once(':') {
1199 let id: [u8; 4] = id.as_bytes().try_into().map_err(|_| "invalid key type id")?;
1200 let bytes = bytes.trim_start_matches("0x");
1201 let bytes = hex::decode(bytes).map_err(|_| "key bytes are not in hex format")?;
1202 Ok(CandidateKey { id, bytes })
1203 } else {
1204 Err("invalid format of CandidateKey, expected '<key type>:<key>'")
1205 }
1206 }
1207}
1208
1209pub const CROSS_CHAIN_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"crch");
1211
1212impl CandidateKey {
1213 pub fn new(id: KeyTypeId, bytes: Vec<u8>) -> Self {
1215 Self { id: id.0, bytes }
1216 }
1217}
1218
1219#[derive(Debug, Clone, PartialEq, Eq, DecodeWithMemTracking, TypeInfo, PartialOrd, Ord, Hash)]
1220#[cfg_attr(feature = "serde", derive(Serialize))]
1221pub struct CandidateKeys(pub Vec<CandidateKey>);
1223
1224impl CandidateKeys {
1225 pub fn find(&self, id: KeyTypeId) -> Option<Vec<u8>> {
1227 self.0
1228 .iter()
1229 .find_map(|e| if e.id == id.0 { Some(e.bytes.clone()) } else { None })
1230 }
1231
1232 pub fn find_or_empty(&self, id: KeyTypeId) -> Vec<u8> {
1234 self.find(id).unwrap_or_default()
1235 }
1236
1237 pub fn has_only_aura_and_grandpa_keys(&self) -> bool {
1239 self.0.len() == 2 && self.find(AURA).is_some() && self.find(GRANDPA).is_some()
1240 }
1241}
1242
1243impl From<Vec<([u8; 4], Vec<u8>)>> for CandidateKeys {
1244 fn from(value: Vec<([u8; 4], Vec<u8>)>) -> Self {
1245 Self(value.into_iter().map(|(id, bytes)| CandidateKey { id, bytes }).collect())
1246 }
1247}
1248
1249impl Encode for CandidateKeys {
1260 fn size_hint(&self) -> usize {
1261 if self.has_only_aura_and_grandpa_keys() {
1262 Encode::size_hint(&AuraPublicKey(self.find_or_empty(AURA)))
1263 .saturating_add(Encode::size_hint(&GrandpaPublicKey(self.find_or_empty(GRANDPA))))
1264 } else {
1265 Encode::size_hint(&Compact(u32::MAX)).saturating_add(Encode::size_hint(&self.0))
1266 }
1267 }
1268
1269 fn encode_to<T: parity_scale_codec::Output + ?Sized>(&self, dest: &mut T) {
1270 if self.has_only_aura_and_grandpa_keys() {
1271 Encode::encode_to(&AuraPublicKey(self.find_or_empty(AURA)), dest);
1272 Encode::encode_to(&GrandpaPublicKey(self.find_or_empty(GRANDPA)), dest)
1273 } else {
1274 Encode::encode_to(&Compact(u32::MAX), dest);
1277 Encode::encode_to(&self.0, dest)
1278 }
1279 }
1280}
1281
1282impl Decode for CandidateKeys {
1284 fn decode<I: parity_scale_codec::Input>(
1285 input: &mut I,
1286 ) -> Result<Self, parity_scale_codec::Error> {
1287 let marker_or_aura_size: u32 = <Compact<u32>>::decode(input)?.0;
1289 if marker_or_aura_size == u32::MAX {
1290 let keys = Vec::<CandidateKey>::decode(input)?;
1291 Ok(Self(keys))
1292 } else {
1293 let aura_bytes: Vec<u8> = decode_vec_with_len(input, marker_or_aura_size as usize)?;
1294 let grandpa = GrandpaPublicKey::decode(input)?;
1295 Ok(Self(vec![AuraPublicKey(aura_bytes).into(), grandpa.into()]))
1296 }
1297 }
1298}
1299
1300#[derive(
1301 Debug,
1302 Clone,
1303 PartialEq,
1304 Eq,
1305 Decode,
1306 DecodeWithMemTracking,
1307 Encode,
1308 TypeInfo,
1309 PartialOrd,
1310 Ord,
1311 Hash,
1312)]
1313#[cfg_attr(feature = "serde", derive(Serialize))]
1314pub struct PermissionedCandidateData {
1320 pub sidechain_public_key: SidechainPublicKey,
1322 pub keys: CandidateKeys,
1324}
1325
1326#[cfg(feature = "std")]
1328impl FromStr for PermissionedCandidateData {
1329 type Err = alloc::boxed::Box<dyn core::error::Error + Send + Sync>;
1330
1331 fn from_str(partner_chain_public_keys: &str) -> Result<Self, Self::Err> {
1332 fn is_legacy_format(line: &str) -> bool {
1333 line.contains(':') && !line.contains(',')
1334 }
1335
1336 fn parse_legacy_format(
1337 line: &str,
1338 ) -> Result<
1339 PermissionedCandidateData,
1340 alloc::boxed::Box<dyn core::error::Error + Send + Sync>,
1341 > {
1342 let line = line.replace("0x", "");
1343 if let [sidechain_pub_key, aura_pub_key, grandpa_pub_key] =
1344 line.split(":").collect::<Vec<_>>()[..]
1345 {
1346 Ok(PermissionedCandidateData {
1347 sidechain_public_key: SidechainPublicKey(
1348 hex::decode(sidechain_pub_key).map_err(|e| e.to_string())?,
1349 ),
1350 keys: CandidateKeys(vec![
1351 AuraPublicKey(hex::decode(aura_pub_key).map_err(|e| e.to_string())?).into(),
1352 GrandpaPublicKey(hex::decode(grandpa_pub_key).map_err(|e| e.to_string())?)
1353 .into(),
1354 ]),
1355 })
1356 } else {
1357 Err(format!("Failed to parse partner chain public keys (legacy) from '{line}'")
1358 .into())
1359 }
1360 }
1361
1362 fn parse_generic_format(
1363 line: &str,
1364 ) -> Result<
1365 PermissionedCandidateData,
1366 alloc::boxed::Box<dyn core::error::Error + Send + Sync>,
1367 > {
1368 let mut columns = line.split(",");
1369 if let Some(partner_chains_key) = columns.next() {
1370 let partner_chains_key = SidechainPublicKey(
1371 hex::decode(partner_chains_key.trim_start_matches("0x"))
1372 .map_err(|e| e.to_string())?,
1373 );
1374 let mut keys = vec![];
1375 for column in columns {
1376 let key = CandidateKey::from_str(column)?;
1377 keys.push(key);
1378 }
1379 Ok(PermissionedCandidateData {
1380 sidechain_public_key: partner_chains_key,
1381 keys: CandidateKeys(keys),
1382 })
1383 } else {
1384 Err("Failed to parse partner chain public keys (generic) from '{line}'.".into())
1385 }
1386 }
1387
1388 if is_legacy_format(&partner_chain_public_keys) {
1389 parse_legacy_format(&partner_chain_public_keys)
1390 } else {
1391 parse_generic_format(&partner_chain_public_keys)
1392 }
1393 }
1394}
1395
1396#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1398pub struct CandidateRegistration {
1399 pub stake_ownership: AdaBasedStaking,
1401 pub partner_chain_pub_key: SidechainPublicKey,
1403 pub partner_chain_signature: SidechainSignature,
1405 pub own_pkh: MainchainKeyHash,
1407 pub registration_utxo: UtxoId,
1409 pub keys: CandidateKeys,
1411}
1412
1413impl CandidateRegistration {
1414 pub fn matches_keys(&self, other: &Self) -> bool {
1416 self.stake_ownership == other.stake_ownership
1417 && self.partner_chain_pub_key == other.partner_chain_pub_key
1418 && self.partner_chain_signature == other.partner_chain_signature
1419 && self.keys.0.iter().all(|key| other.keys.0.contains(key))
1420 }
1421}
1422
1423#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1427pub struct AdaBasedStaking {
1428 pub pub_key: StakePoolPublicKey,
1430 pub signature: MainchainSignature,
1432}
1433
1434#[derive(
1435 Clone,
1436 PartialEq,
1437 Eq,
1438 Ord,
1439 PartialOrd,
1440 TypeInfo,
1441 MaxEncodedLen,
1442 Encode,
1443 Decode,
1444 DecodeWithMemTracking,
1445)]
1446pub enum DelegatorKey {
1448 StakeKeyHash([u8; 28]),
1450 ScriptKeyHash {
1452 hash_raw: [u8; 28],
1454 script_hash: [u8; 28],
1456 },
1457}
1458
1459impl alloc::fmt::Debug for DelegatorKey {
1460 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
1461 let s = match self {
1462 Self::ScriptKeyHash { hash_raw, script_hash } => alloc::format!(
1463 "ScriptKeyHash{{ hash_raw: {}, script_hash: {} }}",
1464 hex::encode(hash_raw),
1465 hex::encode(script_hash)
1466 ),
1467 Self::StakeKeyHash(hash) => alloc::format!("StakeKeyHash({})", hex::encode(hash)),
1468 };
1469
1470 f.write_str(&s)
1471 }
1472}
1473
1474#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
1476pub struct DelegatorStakeAmount(pub u64);
1477
1478impl<T: Into<u64>> From<T> for DelegatorStakeAmount {
1479 fn from(value: T) -> Self {
1480 Self(value.into())
1481 }
1482}
1483
1484#[derive(Debug, Clone, Default)]
1489pub struct StakeDistribution(pub BTreeMap<MainchainKeyHash, PoolDelegation>);
1490
1491#[derive(Debug, Clone, Default, PartialEq)]
1493pub struct PoolDelegation {
1494 pub total_stake: StakeDelegation,
1496 pub delegators: BTreeMap<DelegatorKey, DelegatorStakeAmount>,
1498}
1499
1500pub trait FromStrStdErr:
1502 FromStr<Err: Into<alloc::boxed::Box<dyn core::error::Error + Send + Sync + 'static>>>
1503{
1504}
1505impl<T: FromStr<Err: Into<alloc::boxed::Box<dyn core::error::Error + Send + Sync + 'static>>>>
1506 FromStrStdErr for T
1507{
1508}
1509
1510#[cfg(test)]
1511mod tests {
1512 use super::*;
1513 use core::str::FromStr;
1514 use hex_literal::hex;
1515 use parity_scale_codec::{Decode, Encode};
1516
1517 #[test]
1518 fn main_chain_address_string_serialize_deserialize_round_trip() {
1519 let address = MainchainAddress::from_str(
1520 "addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc",
1521 )
1522 .unwrap();
1523 let serialized = serde_json::to_value(&address).unwrap();
1524 assert_eq!(
1525 serialized,
1526 serde_json::json!("addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc")
1527 );
1528 let deserialized = serde_json::from_value(serialized).unwrap();
1529 assert_eq!(address, deserialized);
1530 }
1531
1532 #[test]
1533 fn main_chain_address_deserialization_of_hex_encoded_bytes() {
1534 let address = MainchainAddress::from_str("addr_test1wz5q").unwrap();
1535 let serialized = serde_json::json!("0x616464725f7465737431777a3571");
1536 assert_eq!(address, serde_json::from_value(serialized).unwrap());
1537 let serialized = serde_json::json!("616464725f7465737431777a3571");
1538 assert_eq!(address, serde_json::from_value(serialized).unwrap());
1539 }
1540
1541 #[test]
1542 fn main_chain_address_string_from_str_to_string_round_trip() {
1543 let address = MainchainAddress::from_str(
1544 "addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc",
1545 )
1546 .unwrap();
1547 let str = address.to_string();
1548 let from_str = MainchainAddress::from_str(&str).unwrap();
1549 assert_eq!(address, from_str);
1550 }
1551
1552 #[test]
1553 fn main_chain_signature_should_be_backward_compatible_with_vec() {
1554 #[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
1555 #[byte_string(debug, hex_serialize, decode_hex)]
1556 struct LegacyMCSignature(pub Vec<u8>);
1557
1558 let legacy_encoded = LegacyMCSignature(vec![10; 64]).encode();
1559
1560 let legacy_decoded = MainchainSignature::decode(&mut legacy_encoded.as_slice())
1561 .expect("Encoded legacy should decode to current type");
1562
1563 assert_eq!(legacy_decoded.0, [10; MAINCHAIN_SIGNATURE_LEN]);
1564
1565 let current_encoded = MainchainSignature([9; MAINCHAIN_SIGNATURE_LEN]).encode();
1566
1567 let current_decoded = LegacyMCSignature::decode(&mut current_encoded.as_slice())
1568 .expect("Encoded current should decode to legacy");
1569
1570 assert_eq!(current_decoded.0, vec![9; 64]);
1571 }
1572
1573 #[test]
1574 fn cross_chain_signature_verify_works() {
1575 let signature = CrossChainSignature(
1576 hex!("d1e02e4a5484c3b7202ce6b844577048e7578dc62901cf8f51e6d74bbd3adb091688feacedd8343d0b04a0f5862b2e06148934a75e678e42051fde5431eca33d").to_vec()
1577 );
1578 let pubkey = CrossChainPublicKey(
1579 hex!("020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1").to_vec(),
1580 );
1581 let signed_data = hex!(
1582 "84020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a16c68747470733a2f2f636f6f6c2e73747566662f73706f2e6a736f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
1583 );
1584
1585 assert!(signature.verify(&pubkey, &signed_data).is_ok())
1586 }
1587
1588 #[derive(Decode, Encode, PartialEq, Eq, Debug)]
1589 struct TestCandidateKeys {
1590 field_before: Option<u32>,
1591 keys: CandidateKeys,
1592 field_after: Option<u32>,
1594 }
1595
1596 #[test]
1597 fn encode_decode_round_trip_for_generic_candidate_keys() {
1598 let keys = TestCandidateKeys {
1599 field_before: Some(42),
1600 keys: CandidateKeys(vec![
1601 CandidateKey { id: *b"abcd", bytes: [7u8; 32].to_vec() },
1602 CandidateKey { id: *b"efgh", bytes: [9u8; 32].to_vec() },
1603 ]),
1604 field_after: Some(15),
1605 };
1606 let bytes: Vec<u8> = Encode::encode(&keys);
1607 let mut bytes: &[u8] = &bytes;
1608 let decoded = TestCandidateKeys::decode(&mut bytes).unwrap();
1609 assert_eq!(keys, decoded)
1610 }
1611
1612 #[test]
1613 fn encode_decode_round_trip_for_aura_and_grandpa_candidate_keys() {
1614 let keys = TestCandidateKeys {
1615 field_before: Some(42),
1616 keys: CandidateKeys(vec![
1617 AuraPublicKey([7u8; 32].to_vec()).into(),
1618 GrandpaPublicKey([9u8; 32].to_vec()).into(),
1619 ]),
1620 field_after: Some(15),
1621 };
1622 let bytes: Vec<u8> = Encode::encode(&keys);
1623 let mut bytes: &[u8] = &bytes;
1624 let decoded = TestCandidateKeys::decode(&mut bytes).unwrap();
1625 assert_eq!(keys, decoded)
1626 }
1627}