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