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 pub fn saturating_sub(&self, other: u64) -> Self {
639 Self(self.0.saturating_sub(other.into()))
640 }
641}
642
643#[derive(
644 Clone,
645 PartialEq,
646 Eq,
647 Encode,
648 Decode,
649 DecodeWithMemTracking,
650 ToDatum,
651 TypeInfo,
652 PartialOrd,
653 Ord,
654 Hash,
655)]
656#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex, as_ref)]
657pub struct SidechainPublicKey(pub Vec<u8>);
663
664impl From<ecdsa::Public> for SidechainPublicKey {
665 fn from(value: ecdsa::Public) -> Self {
666 Self(value.0.to_vec())
667 }
668}
669
670#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
672#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
673pub struct PlutusScriptCbor(pub Vec<u8>);
674
675#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
677#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
678pub struct TransactionCbor(pub Vec<u8>);
679
680#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
682#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
683pub struct VKeyWitnessCbor(pub Vec<u8>);
684
685#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
687#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
688pub struct SidechainSignature(pub Vec<u8>);
689
690#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
694#[byte_string(debug, hex_serialize, hex_deserialize)]
695pub struct CrossChainPublicKey(pub Vec<u8>);
696
697impl CrossChainPublicKey {
698 pub fn hash(&self) -> CrossChainKeyHash {
700 CrossChainKeyHash(blake2b(&self.0))
701 }
702}
703
704impl From<k256::PublicKey> for CrossChainPublicKey {
705 fn from(value: k256::PublicKey) -> Self {
706 Self(value.to_sec1_bytes().to_vec())
707 }
708}
709
710impl From<CrossChainPublicKey> for k256::PublicKey {
711 fn from(value: CrossChainPublicKey) -> Self {
712 k256::PublicKey::from_sec1_bytes(&value.0)
713 .expect("CrossChainPublicKey converts to valid secp256k1::PublicKey")
714 }
715}
716
717const CROSS_CHAIN_KEY_HASH_LEN: usize = 28;
719
720#[derive(
721 Clone,
722 Copy,
723 Decode,
724 DecodeWithMemTracking,
725 Default,
726 Encode,
727 Hash,
728 MaxEncodedLen,
729 Eq,
730 PartialEq,
731 Ord,
732 PartialOrd,
733 TypeInfo,
734)]
735#[byte_string(debug, to_hex_string)]
736#[cfg_attr(feature = "std", byte_string(decode_hex))]
737#[cfg_attr(feature = "serde", byte_string(hex_serialize, hex_deserialize))]
738pub struct CrossChainKeyHash(pub [u8; CROSS_CHAIN_KEY_HASH_LEN]);
740
741impl Display for CrossChainKeyHash {
742 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
743 f.write_str(&self.to_hex_string())
744 }
745}
746
747#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
749#[byte_string(debug, hex_serialize)]
750pub struct CrossChainSignature(pub Vec<u8>);
751
752impl CrossChainSignature {
753 pub fn verify(
755 &self,
756 cross_chain_pubkey: &CrossChainPublicKey,
757 data: &[u8],
758 ) -> Result<(), k256::ecdsa::signature::Error> {
759 use k256::ecdsa::signature::Verifier;
760
761 let vkey = k256::ecdsa::VerifyingKey::from_sec1_bytes(&cross_chain_pubkey.0[..])?;
762 let signature = k256::ecdsa::Signature::from_slice(&self.0[..])?;
763 vkey.verify(data, &signature)
764 }
765}
766
767const EPOCH_NONCE_LEN: usize = 32;
769
770#[derive(Default, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
777#[byte_string(debug, hex_serialize, decode_hex)]
778pub struct EpochNonce(pub Vec<u8>);
779
780impl EpochNonce {
781 pub fn as_array(&self) -> [u8; EPOCH_NONCE_LEN] {
783 let mut epoch_nonce = self.0.clone();
784 epoch_nonce.resize_with(32, || 0);
785 epoch_nonce.try_into().expect("Should never fail after being resized")
786 }
787}
788
789#[derive(
790 Default,
791 Debug,
792 Copy,
793 Clone,
794 PartialEq,
795 Eq,
796 Encode,
797 Decode,
798 DecodeWithMemTracking,
799 ToDatum,
800 TypeInfo,
801 MaxEncodedLen,
802 Hash,
803)]
804pub struct UtxoId {
812 pub tx_hash: McTxHash,
814 pub index: UtxoIndex,
816}
817
818impl UtxoId {
819 pub const fn new(hash: [u8; TX_HASH_SIZE], index: u16) -> UtxoId {
821 UtxoId { tx_hash: McTxHash(hash), index: UtxoIndex(index) }
822 }
823}
824
825#[cfg(feature = "serde")]
826impl Serialize for UtxoId {
827 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
828 where
829 S: Serializer,
830 {
831 serializer.serialize_str(&self.to_string())
832 }
833}
834
835#[cfg(feature = "serde")]
836impl<'de> Deserialize<'de> for UtxoId {
837 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
838 where
839 D: Deserializer<'de>,
840 {
841 alloc::string::String::deserialize(deserializer).and_then(|string| {
842 Self::from_str(&string).map_err(|err| serde::de::Error::custom(err.to_string()))
843 })
844 }
845}
846
847#[cfg(feature = "serde")]
848impl FromStr for UtxoId {
849 type Err = &'static str;
850
851 fn from_str(s: &str) -> Result<Self, Self::Err> {
852 let split: Vec<&str> = s.split('#').collect();
853 let &[hash_str, index_str] = split.as_slice() else {
854 return Err("UtxoId string must conform to format: '<hash>#<index>'");
855 };
856
857 Ok(UtxoId {
858 tx_hash: McTxHash::from_str(hash_str)
859 .map_err(|_| "invalid string input for McTxHash")?,
860 index: UtxoIndex::from_str(index_str)
861 .map_err(|_| "invalid string input for OutputIndex")?,
862 })
863 }
864}
865
866impl Display for UtxoId {
867 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
868 let hash = sp_core::hexdisplay::HexDisplay::from(&self.tx_hash.0);
869 write!(f, "{}#{}", hash, self.index.0)
870 }
871}
872
873#[derive(
874 Default,
875 Debug,
876 Copy,
877 Clone,
878 PartialEq,
879 Eq,
880 Encode,
881 Decode,
882 DecodeWithMemTracking,
883 PartialOrd,
884 Ord,
885 ToDatum,
886 TypeInfo,
887 MaxEncodedLen,
888 Hash,
889)]
890#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
891pub struct UtxoIndex(pub u16);
893
894#[cfg(feature = "serde")]
895impl FromStr for UtxoIndex {
896 type Err = sp_std::num::ParseIntError;
897 fn from_str(s: &str) -> Result<Self, Self::Err> {
898 let parsed = u16::from_str(s)?;
899 let _check_overflow = i16::from_str(s)?;
900 Ok(Self(parsed))
901 }
902}
903
904pub const TX_HASH_SIZE: usize = 32;
906
907#[derive(
908 Default,
909 Copy,
910 Clone,
911 Hash,
912 PartialEq,
913 Eq,
914 Encode,
915 Decode,
916 DecodeWithMemTracking,
917 ToDatum,
918 TypeInfo,
919 MaxEncodedLen,
920)]
921#[byte_string(debug, from_bytes, decode_hex, hex_serialize, hex_deserialize)]
922#[constructor_datum]
923pub struct McTxHash(pub [u8; TX_HASH_SIZE]);
927
928impl TryFrom<Vec<u8>> for McTxHash {
929 type Error = &'static str;
930
931 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
932 <[u8; 32]>::try_from(value)
933 .map_err(|_| "McTxHash must be 32 bytes long")
934 .map(McTxHash)
935 }
936}
937
938impl Display for McTxHash {
939 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
940 let hash = sp_core::hexdisplay::HexDisplay::from(&self.0);
941 write!(f, "{}", hash)
942 }
943}
944
945#[derive(
946 Default,
947 Clone,
948 Decode,
949 DecodeWithMemTracking,
950 Encode,
951 PartialEq,
952 Eq,
953 TypeInfo,
954 MaxEncodedLen,
955 Hash,
956)]
957#[byte_string(debug, decode_hex, hex_serialize, hex_deserialize)]
958pub struct McBlockHash(pub [u8; 32]);
962
963impl Display for McBlockHash {
964 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
965 let hash = sp_core::hexdisplay::HexDisplay::from(&self.0);
966 write!(f, "{}", hash)
967 }
968}
969
970#[derive(
972 Default, Debug, Copy, Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo,
973)]
974#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
975#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
976pub struct UtxoInfo {
977 pub utxo_id: UtxoId,
979 pub epoch_number: McEpochNumber,
981 pub block_number: McBlockNumber,
983 pub slot_number: McSlotNumber,
985 pub tx_index_within_block: McTxIndexInBlock,
987}
988
989#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
994pub struct UtxoInfoOrderingKey {
995 pub block_number: McBlockNumber,
997 pub tx_index_within_block: McTxIndexInBlock,
999 pub utxo_id_index: UtxoIndex,
1001}
1002
1003impl UtxoInfo {
1004 pub fn ordering_key(&self) -> UtxoInfoOrderingKey {
1006 UtxoInfoOrderingKey {
1007 block_number: self.block_number,
1008 tx_index_within_block: self.tx_index_within_block,
1009 utxo_id_index: self.utxo_id.index,
1010 }
1011 }
1012}
1013
1014#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
1021#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1022#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
1023pub enum NetworkType {
1024 Mainnet,
1026 #[default]
1028 Testnet,
1029}
1030
1031#[cfg(feature = "std")]
1032impl std::fmt::Display for NetworkType {
1033 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1034 let str = match self {
1035 Self::Mainnet => "mainnet",
1036 Self::Testnet => "testnet",
1037 };
1038 write!(f, "{}", str)
1039 }
1040}
1041
1042#[derive(Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
1051#[cfg_attr(feature = "serde", derive(Serialize))]
1052pub struct RegistrationData {
1053 pub registration_utxo: UtxoId,
1055 pub sidechain_signature: SidechainSignature,
1057 pub mainchain_signature: MainchainSignature,
1059 pub cross_chain_signature: CrossChainSignature,
1061 pub sidechain_pub_key: SidechainPublicKey,
1063 pub cross_chain_pub_key: CrossChainPublicKey,
1065 pub utxo_info: UtxoInfo,
1067 pub tx_inputs: Vec<UtxoId>,
1069 pub keys: CandidateKeys,
1071}
1072
1073#[derive(Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
1075#[cfg_attr(feature = "serde", derive(Serialize))]
1076pub struct CandidateRegistrations {
1077 pub stake_pool_public_key: StakePoolPublicKey,
1079 pub registrations: Vec<RegistrationData>,
1081 pub stake_delegation: Option<StakeDelegation>,
1083}
1084
1085impl CandidateRegistrations {
1086 pub fn new(
1088 stake_pool_public_key: StakePoolPublicKey,
1089 stake_delegation: Option<StakeDelegation>,
1090 registrations: Vec<RegistrationData>,
1091 ) -> Self {
1092 Self { stake_pool_public_key, registrations, stake_delegation }
1093 }
1094
1095 pub fn mainchain_pub_key(&self) -> &StakePoolPublicKey {
1097 &self.stake_pool_public_key
1098 }
1099
1100 pub fn registrations(&self) -> &[RegistrationData] {
1102 &self.registrations
1103 }
1104}
1105
1106#[derive(
1108 Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialOrd, Ord, Hash,
1109)]
1110#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
1111pub struct AuraPublicKey(pub Vec<u8>);
1112impl AuraPublicKey {
1113 pub fn try_into_sr25519(&self) -> Option<sr25519::Public> {
1115 Some(sr25519::Public::from_raw(self.0.clone().try_into().ok()?))
1116 }
1117}
1118
1119impl From<sr25519::Public> for AuraPublicKey {
1120 fn from(value: sr25519::Public) -> Self {
1121 Self(value.0.to_vec())
1122 }
1123}
1124
1125impl From<AuraPublicKey> for CandidateKey {
1126 fn from(value: AuraPublicKey) -> Self {
1127 Self { id: AURA.0, bytes: value.0 }
1128 }
1129}
1130
1131#[derive(
1133 Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialOrd, Ord, Hash,
1134)]
1135#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
1136pub struct GrandpaPublicKey(pub Vec<u8>);
1137impl GrandpaPublicKey {
1138 pub fn try_into_ed25519(&self) -> Option<ed25519::Public> {
1140 Some(ed25519::Public::from_raw(self.0.clone().try_into().ok()?))
1141 }
1142}
1143
1144impl From<ed25519::Public> for GrandpaPublicKey {
1145 fn from(value: ed25519::Public) -> Self {
1146 Self(value.0.to_vec())
1147 }
1148}
1149
1150impl From<GrandpaPublicKey> for CandidateKey {
1151 fn from(value: GrandpaPublicKey) -> Self {
1152 Self { id: GRANDPA.0, bytes: value.0 }
1153 }
1154}
1155
1156#[derive(
1157 Debug,
1158 Clone,
1159 PartialEq,
1160 Decode,
1161 DecodeWithMemTracking,
1162 Encode,
1163 MaxEncodedLen,
1164 TypeInfo,
1165 Eq,
1166 Hash,
1167)]
1168#[cfg_attr(feature = "serde", derive(Serialize))]
1169pub struct DParameter {
1177 pub num_permissioned_candidates: u16,
1179 pub num_registered_candidates: u16,
1181}
1182
1183impl DParameter {
1184 pub fn new(num_permissioned_candidates: u16, num_registered_candidates: u16) -> Self {
1186 Self { num_permissioned_candidates, num_registered_candidates }
1187 }
1188}
1189
1190#[derive(
1192 Clone, PartialEq, Eq, Decode, DecodeWithMemTracking, Encode, TypeInfo, PartialOrd, Ord, Hash,
1193)]
1194#[cfg_attr(feature = "serde", derive(Serialize))]
1195pub struct CandidateKey {
1196 pub id: [u8; 4],
1198 pub bytes: Vec<u8>,
1200}
1201
1202impl alloc::fmt::Debug for CandidateKey {
1203 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
1204 let id = hex::encode(self.id);
1205 let id_text = String::from_utf8_lossy(&self.id);
1206 let bytes = hex::encode(&self.bytes);
1207 f.write_str(&alloc::format!("CandidateKey {{ id: {id}({id_text}), bytes: {bytes} }}"))
1208 }
1209}
1210
1211impl FromStr for CandidateKey {
1212 type Err = &'static str;
1213
1214 fn from_str(s: &str) -> Result<Self, Self::Err> {
1215 if let Some((id, bytes)) = s.split_once(':') {
1216 let id: [u8; 4] = id.as_bytes().try_into().map_err(|_| "invalid key type id")?;
1217 let bytes = bytes.trim_start_matches("0x");
1218 let bytes = hex::decode(bytes).map_err(|_| "key bytes are not in hex format")?;
1219 Ok(CandidateKey { id, bytes })
1220 } else {
1221 Err("invalid format of CandidateKey, expected '<key type>:<key>'")
1222 }
1223 }
1224}
1225
1226pub const CROSS_CHAIN_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"crch");
1228
1229impl CandidateKey {
1230 pub fn new(id: KeyTypeId, bytes: Vec<u8>) -> Self {
1232 Self { id: id.0, bytes }
1233 }
1234}
1235
1236#[derive(Debug, Clone, PartialEq, Eq, DecodeWithMemTracking, TypeInfo, PartialOrd, Ord, Hash)]
1237#[cfg_attr(feature = "serde", derive(Serialize))]
1238pub struct CandidateKeys(pub Vec<CandidateKey>);
1240
1241impl CandidateKeys {
1242 pub fn find(&self, id: KeyTypeId) -> Option<Vec<u8>> {
1244 self.0
1245 .iter()
1246 .find_map(|e| if e.id == id.0 { Some(e.bytes.clone()) } else { None })
1247 }
1248
1249 pub fn find_or_empty(&self, id: KeyTypeId) -> Vec<u8> {
1251 self.find(id).unwrap_or_default()
1252 }
1253
1254 pub fn has_only_aura_and_grandpa_keys(&self) -> bool {
1256 self.0.len() == 2 && self.find(AURA).is_some() && self.find(GRANDPA).is_some()
1257 }
1258}
1259
1260impl From<Vec<([u8; 4], Vec<u8>)>> for CandidateKeys {
1261 fn from(value: Vec<([u8; 4], Vec<u8>)>) -> Self {
1262 Self(value.into_iter().map(|(id, bytes)| CandidateKey { id, bytes }).collect())
1263 }
1264}
1265
1266impl Encode for CandidateKeys {
1277 fn size_hint(&self) -> usize {
1278 if self.has_only_aura_and_grandpa_keys() {
1279 Encode::size_hint(&AuraPublicKey(self.find_or_empty(AURA)))
1280 .saturating_add(Encode::size_hint(&GrandpaPublicKey(self.find_or_empty(GRANDPA))))
1281 } else {
1282 Encode::size_hint(&Compact(u32::MAX)).saturating_add(Encode::size_hint(&self.0))
1283 }
1284 }
1285
1286 fn encode_to<T: parity_scale_codec::Output + ?Sized>(&self, dest: &mut T) {
1287 if self.has_only_aura_and_grandpa_keys() {
1288 Encode::encode_to(&AuraPublicKey(self.find_or_empty(AURA)), dest);
1289 Encode::encode_to(&GrandpaPublicKey(self.find_or_empty(GRANDPA)), dest)
1290 } else {
1291 Encode::encode_to(&Compact(u32::MAX), dest);
1294 Encode::encode_to(&self.0, dest)
1295 }
1296 }
1297}
1298
1299impl Decode for CandidateKeys {
1301 fn decode<I: parity_scale_codec::Input>(
1302 input: &mut I,
1303 ) -> Result<Self, parity_scale_codec::Error> {
1304 let marker_or_aura_size: u32 = <Compact<u32>>::decode(input)?.0;
1306 if marker_or_aura_size == u32::MAX {
1307 let keys = Vec::<CandidateKey>::decode(input)?;
1308 Ok(Self(keys))
1309 } else {
1310 let aura_bytes: Vec<u8> = decode_vec_with_len(input, marker_or_aura_size as usize)?;
1311 let grandpa = GrandpaPublicKey::decode(input)?;
1312 Ok(Self(vec![AuraPublicKey(aura_bytes).into(), grandpa.into()]))
1313 }
1314 }
1315}
1316
1317#[derive(
1318 Debug,
1319 Clone,
1320 PartialEq,
1321 Eq,
1322 Decode,
1323 DecodeWithMemTracking,
1324 Encode,
1325 TypeInfo,
1326 PartialOrd,
1327 Ord,
1328 Hash,
1329)]
1330#[cfg_attr(feature = "serde", derive(Serialize))]
1331pub struct PermissionedCandidateData {
1337 pub sidechain_public_key: SidechainPublicKey,
1339 pub keys: CandidateKeys,
1341}
1342
1343#[cfg(feature = "std")]
1345impl FromStr for PermissionedCandidateData {
1346 type Err = alloc::boxed::Box<dyn core::error::Error + Send + Sync>;
1347
1348 fn from_str(partner_chain_public_keys: &str) -> Result<Self, Self::Err> {
1349 fn is_legacy_format(line: &str) -> bool {
1350 line.contains(':') && !line.contains(',')
1351 }
1352
1353 fn parse_legacy_format(
1354 line: &str,
1355 ) -> Result<
1356 PermissionedCandidateData,
1357 alloc::boxed::Box<dyn core::error::Error + Send + Sync>,
1358 > {
1359 let line = line.replace("0x", "");
1360 if let [sidechain_pub_key, aura_pub_key, grandpa_pub_key] =
1361 line.split(":").collect::<Vec<_>>()[..]
1362 {
1363 Ok(PermissionedCandidateData {
1364 sidechain_public_key: SidechainPublicKey(
1365 hex::decode(sidechain_pub_key).map_err(|e| e.to_string())?,
1366 ),
1367 keys: CandidateKeys(vec![
1368 AuraPublicKey(hex::decode(aura_pub_key).map_err(|e| e.to_string())?).into(),
1369 GrandpaPublicKey(hex::decode(grandpa_pub_key).map_err(|e| e.to_string())?)
1370 .into(),
1371 ]),
1372 })
1373 } else {
1374 Err(format!("Failed to parse partner chain public keys (legacy) from '{line}'")
1375 .into())
1376 }
1377 }
1378
1379 fn parse_generic_format(
1380 line: &str,
1381 ) -> Result<
1382 PermissionedCandidateData,
1383 alloc::boxed::Box<dyn core::error::Error + Send + Sync>,
1384 > {
1385 let mut columns = line.split(",");
1386 if let Some(partner_chains_key) = columns.next() {
1387 let partner_chains_key = SidechainPublicKey(
1388 hex::decode(partner_chains_key.trim_start_matches("0x"))
1389 .map_err(|e| e.to_string())?,
1390 );
1391 let mut keys = vec![];
1392 for column in columns {
1393 let key = CandidateKey::from_str(column)?;
1394 keys.push(key);
1395 }
1396 Ok(PermissionedCandidateData {
1397 sidechain_public_key: partner_chains_key,
1398 keys: CandidateKeys(keys),
1399 })
1400 } else {
1401 Err("Failed to parse partner chain public keys (generic) from '{line}'.".into())
1402 }
1403 }
1404
1405 if is_legacy_format(&partner_chain_public_keys) {
1406 parse_legacy_format(&partner_chain_public_keys)
1407 } else {
1408 parse_generic_format(&partner_chain_public_keys)
1409 }
1410 }
1411}
1412
1413#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1415pub struct CandidateRegistration {
1416 pub stake_ownership: AdaBasedStaking,
1418 pub partner_chain_pub_key: SidechainPublicKey,
1420 pub partner_chain_signature: SidechainSignature,
1422 pub own_pkh: MainchainKeyHash,
1424 pub registration_utxo: UtxoId,
1426 pub keys: CandidateKeys,
1428}
1429
1430impl CandidateRegistration {
1431 pub fn matches_keys(&self, other: &Self) -> bool {
1433 self.stake_ownership == other.stake_ownership
1434 && self.partner_chain_pub_key == other.partner_chain_pub_key
1435 && self.partner_chain_signature == other.partner_chain_signature
1436 && self.keys.0.iter().all(|key| other.keys.0.contains(key))
1437 }
1438}
1439
1440#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1444pub struct AdaBasedStaking {
1445 pub pub_key: StakePoolPublicKey,
1447 pub signature: MainchainSignature,
1449}
1450
1451#[derive(
1452 Clone,
1453 PartialEq,
1454 Eq,
1455 Ord,
1456 PartialOrd,
1457 TypeInfo,
1458 MaxEncodedLen,
1459 Encode,
1460 Decode,
1461 DecodeWithMemTracking,
1462)]
1463pub enum DelegatorKey {
1465 StakeKeyHash([u8; 28]),
1467 ScriptKeyHash {
1469 hash_raw: [u8; 28],
1471 script_hash: [u8; 28],
1473 },
1474}
1475
1476impl alloc::fmt::Debug for DelegatorKey {
1477 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
1478 let s = match self {
1479 Self::ScriptKeyHash { hash_raw, script_hash } => alloc::format!(
1480 "ScriptKeyHash{{ hash_raw: {}, script_hash: {} }}",
1481 hex::encode(hash_raw),
1482 hex::encode(script_hash)
1483 ),
1484 Self::StakeKeyHash(hash) => alloc::format!("StakeKeyHash({})", hex::encode(hash)),
1485 };
1486
1487 f.write_str(&s)
1488 }
1489}
1490
1491#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
1493pub struct DelegatorStakeAmount(pub u64);
1494
1495impl<T: Into<u64>> From<T> for DelegatorStakeAmount {
1496 fn from(value: T) -> Self {
1497 Self(value.into())
1498 }
1499}
1500
1501#[derive(Debug, Clone, Default)]
1506pub struct StakeDistribution(pub BTreeMap<MainchainKeyHash, PoolDelegation>);
1507
1508#[derive(Debug, Clone, Default, PartialEq)]
1510pub struct PoolDelegation {
1511 pub total_stake: StakeDelegation,
1513 pub delegators: BTreeMap<DelegatorKey, DelegatorStakeAmount>,
1515}
1516
1517pub trait FromStrStdErr:
1519 FromStr<Err: Into<alloc::boxed::Box<dyn core::error::Error + Send + Sync + 'static>>>
1520{
1521}
1522impl<T: FromStr<Err: Into<alloc::boxed::Box<dyn core::error::Error + Send + Sync + 'static>>>>
1523 FromStrStdErr for T
1524{
1525}
1526
1527#[cfg(test)]
1528mod tests {
1529 use super::*;
1530 use core::str::FromStr;
1531 use hex_literal::hex;
1532 use parity_scale_codec::{Decode, Encode};
1533
1534 #[test]
1535 fn main_chain_address_string_serialize_deserialize_round_trip() {
1536 let address = MainchainAddress::from_str(
1537 "addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc",
1538 )
1539 .unwrap();
1540 let serialized = serde_json::to_value(&address).unwrap();
1541 assert_eq!(
1542 serialized,
1543 serde_json::json!("addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc")
1544 );
1545 let deserialized = serde_json::from_value(serialized).unwrap();
1546 assert_eq!(address, deserialized);
1547 }
1548
1549 #[test]
1550 fn main_chain_address_deserialization_of_hex_encoded_bytes() {
1551 let address = MainchainAddress::from_str("addr_test1wz5q").unwrap();
1552 let serialized = serde_json::json!("0x616464725f7465737431777a3571");
1553 assert_eq!(address, serde_json::from_value(serialized).unwrap());
1554 let serialized = serde_json::json!("616464725f7465737431777a3571");
1555 assert_eq!(address, serde_json::from_value(serialized).unwrap());
1556 }
1557
1558 #[test]
1559 fn main_chain_address_string_from_str_to_string_round_trip() {
1560 let address = MainchainAddress::from_str(
1561 "addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc",
1562 )
1563 .unwrap();
1564 let str = address.to_string();
1565 let from_str = MainchainAddress::from_str(&str).unwrap();
1566 assert_eq!(address, from_str);
1567 }
1568
1569 #[test]
1570 fn main_chain_signature_should_be_backward_compatible_with_vec() {
1571 #[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
1572 #[byte_string(debug, hex_serialize, decode_hex)]
1573 struct LegacyMCSignature(pub Vec<u8>);
1574
1575 let legacy_encoded = LegacyMCSignature(vec![10; 64]).encode();
1576
1577 let legacy_decoded = MainchainSignature::decode(&mut legacy_encoded.as_slice())
1578 .expect("Encoded legacy should decode to current type");
1579
1580 assert_eq!(legacy_decoded.0, [10; MAINCHAIN_SIGNATURE_LEN]);
1581
1582 let current_encoded = MainchainSignature([9; MAINCHAIN_SIGNATURE_LEN]).encode();
1583
1584 let current_decoded = LegacyMCSignature::decode(&mut current_encoded.as_slice())
1585 .expect("Encoded current should decode to legacy");
1586
1587 assert_eq!(current_decoded.0, vec![9; 64]);
1588 }
1589
1590 #[test]
1591 fn cross_chain_signature_verify_works() {
1592 let signature = CrossChainSignature(
1593 hex!("d1e02e4a5484c3b7202ce6b844577048e7578dc62901cf8f51e6d74bbd3adb091688feacedd8343d0b04a0f5862b2e06148934a75e678e42051fde5431eca33d").to_vec()
1594 );
1595 let pubkey = CrossChainPublicKey(
1596 hex!("020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1").to_vec(),
1597 );
1598 let signed_data = hex!(
1599 "84020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a16c68747470733a2f2f636f6f6c2e73747566662f73706f2e6a736f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
1600 );
1601
1602 assert!(signature.verify(&pubkey, &signed_data).is_ok())
1603 }
1604
1605 #[derive(Decode, Encode, PartialEq, Eq, Debug)]
1606 struct TestCandidateKeys {
1607 field_before: Option<u32>,
1608 keys: CandidateKeys,
1609 field_after: Option<u32>,
1611 }
1612
1613 #[test]
1614 fn encode_decode_round_trip_for_generic_candidate_keys() {
1615 let keys = TestCandidateKeys {
1616 field_before: Some(42),
1617 keys: CandidateKeys(vec![
1618 CandidateKey { id: *b"abcd", bytes: [7u8; 32].to_vec() },
1619 CandidateKey { id: *b"efgh", bytes: [9u8; 32].to_vec() },
1620 ]),
1621 field_after: Some(15),
1622 };
1623 let bytes: Vec<u8> = Encode::encode(&keys);
1624 let mut bytes: &[u8] = &bytes;
1625 let decoded = TestCandidateKeys::decode(&mut bytes).unwrap();
1626 assert_eq!(keys, decoded)
1627 }
1628
1629 #[test]
1630 fn encode_decode_round_trip_for_aura_and_grandpa_candidate_keys() {
1631 let keys = TestCandidateKeys {
1632 field_before: Some(42),
1633 keys: CandidateKeys(vec![
1634 AuraPublicKey([7u8; 32].to_vec()).into(),
1635 GrandpaPublicKey([9u8; 32].to_vec()).into(),
1636 ]),
1637 field_after: Some(15),
1638 };
1639 let bytes: Vec<u8> = Encode::encode(&keys);
1640 let mut bytes: &[u8] = &bytes;
1641 let decoded = TestCandidateKeys::decode(&mut bytes).unwrap();
1642 assert_eq!(keys, decoded)
1643 }
1644}