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(Debug, Clone, PartialEq, Default)]
205#[cfg_attr(feature = "serde", derive(serde::Serialize))]
206pub struct MainchainBlock {
207 pub number: McBlockNumber,
209 pub hash: McBlockHash,
211 pub epoch: McEpochNumber,
213 pub slot: McSlotNumber,
215 pub timestamp: u64, }
218
219#[derive(
220 Default,
221 Debug,
222 Copy,
223 Clone,
224 PartialEq,
225 Eq,
226 Encode,
227 Decode,
228 DecodeWithMemTracking,
229 PartialOrd,
230 Ord,
231 TypeInfo,
232 MaxEncodedLen,
233)]
234#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
235pub struct McTxIndexInBlock(pub u32);
237
238#[cfg(feature = "serde")]
239impl FromStr for McTxIndexInBlock {
240 type Err = sp_std::num::ParseIntError;
241 fn from_str(s: &str) -> Result<Self, Self::Err> {
242 let parsed = u32::from_str(s)?;
243 let _check_overflow = i32::from_str(s)?;
244 Ok(Self(parsed))
245 }
246}
247
248const MAX_MAINCHAIN_ADDRESS_BYTES: u32 = 120;
250
251#[derive(
255 Clone, Default, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen,
256)]
257#[byte_string(debug)]
258pub struct MainchainAddress(BoundedVec<u8, ConstU32<MAX_MAINCHAIN_ADDRESS_BYTES>>);
259
260impl MainchainAddress {
261 pub fn bytes(&self) -> Vec<u8> {
263 self.0.to_vec()
264 }
265}
266
267#[cfg(feature = "serde")]
268impl FromStr for MainchainAddress {
269 type Err = &'static str;
270
271 fn from_str(s: &str) -> Result<Self, Self::Err> {
272 let bytes: Vec<u8> = s.as_bytes().to_vec();
273 let bounded = BoundedVec::try_from(bytes).map_err(|_| "Invalid length")?;
274 Ok(MainchainAddress(bounded))
275 }
276}
277
278impl Display for MainchainAddress {
279 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
280 let s = String::from_utf8(self.0.to_vec())
281 .expect("MainchainAddressString is always properly encoded UTF-8");
282 write!(f, "{}", s)
283 }
284}
285
286#[cfg(feature = "serde")]
287impl serde::Serialize for MainchainAddress {
288 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
289 where
290 S: serde::Serializer,
291 {
292 let s = String::from_utf8(self.0.to_vec()).expect("MainchainAddress is always valid UTF-8");
293 serializer.serialize_str(&s)
294 }
295}
296
297#[cfg(feature = "serde")]
298impl<'de> serde::Deserialize<'de> for MainchainAddress {
299 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
301 where
302 D: serde::Deserializer<'de>,
303 {
304 let s = String::deserialize(deserializer)?;
305 let bytes = sp_core::bytes::from_hex(&s).unwrap_or_else(|_| s.as_bytes().to_vec());
306 let bounded = BoundedVec::try_from(bytes)
307 .map_err(|_| serde::de::Error::custom("MainchainAddress is too long"))?;
308 Ok(MainchainAddress(bounded))
309 }
310}
311
312const POLICY_ID_LEN: usize = 28;
314#[derive(
315 Clone,
316 Default,
317 PartialEq,
318 Eq,
319 Encode,
320 Decode,
321 DecodeWithMemTracking,
322 ToDatum,
323 TypeInfo,
324 MaxEncodedLen,
325 Hash,
326)]
327#[byte_string(debug, decode_hex, hex_serialize, hex_deserialize)]
328#[cfg_attr(feature = "std", byte_string(to_hex_string))]
329pub struct PolicyId(pub [u8; POLICY_ID_LEN]);
331
332#[cfg(feature = "std")]
333impl Display for PolicyId {
334 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
335 write!(f, "{}", self.to_hex_string())
336 }
337}
338
339pub type ScriptHash = PolicyId;
341
342pub const MAX_ASSET_NAME_LEN: u32 = 32;
344
345#[derive(
347 Clone, Default, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen,
348)]
349#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
350#[cfg_attr(feature = "std", byte_string(to_hex_string))]
351pub struct AssetName(pub BoundedVec<u8, ConstU32<MAX_ASSET_NAME_LEN>>);
352
353impl AssetName {
354 pub fn empty() -> Self {
356 Self(BoundedVec::new())
357 }
358}
359
360#[cfg(feature = "std")]
361impl Display for AssetName {
362 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
363 write!(f, "{}", self.to_hex_string())
364 }
365}
366
367#[derive(
368 Clone,
369 Debug,
370 PartialEq,
371 Eq,
372 Encode,
373 Decode,
374 DecodeWithMemTracking,
375 TypeInfo,
376 MaxEncodedLen,
377 Default,
378)]
379#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
380pub struct AssetId {
382 pub policy_id: PolicyId,
384 pub asset_name: AssetName,
386}
387
388#[cfg(feature = "std")]
389impl FromStr for AssetId {
390 type Err = String;
391
392 fn from_str(s: &str) -> Result<Self, Self::Err> {
393 match s.split_once(".") {
394 Some((policy_id, asset_name)) => {
395 let policy_id = PolicyId::from_str(policy_id)
396 .map_err(|e| format!("{} is invalid Policy ID: {}", policy_id, e))?;
397 let asset_name = AssetName::from_str(asset_name)
398 .map_err(|e| format!("{} is invalid Asset Name: {}", asset_name, e))?;
399 Ok(Self { policy_id, asset_name })
400 },
401 None => {
402 Err("AssetId should be <hex encoded Policy ID>.<hex encoded Asset Name>"
403 .to_string())
404 },
405 }
406 }
407}
408
409const STAKE_POOL_PUBLIC_KEY_LEN: usize = 32;
411
412#[derive(
413 Clone,
414 PartialEq,
415 Eq,
416 Encode,
417 Decode,
418 DecodeWithMemTracking,
419 TypeInfo,
420 MaxEncodedLen,
421 Hash,
422 Ord,
423 PartialOrd,
424)]
425#[cfg_attr(feature = "std", byte_string(to_hex_string))]
426#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
427pub struct StakePoolPublicKey(pub [u8; STAKE_POOL_PUBLIC_KEY_LEN]);
429
430impl StakePoolPublicKey {
431 pub fn hash(&self) -> MainchainKeyHash {
433 MainchainKeyHash::from_vkey(&self.0)
434 }
435}
436
437impl TryFrom<Vec<u8>> for StakePoolPublicKey {
438 type Error = &'static str;
439
440 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
441 <[u8; 32]>::try_from(value)
442 .map_err(|_| "Mainchain public key must be 32 bytes long")
443 .map(StakePoolPublicKey)
444 }
445}
446
447const STAKE_PUBLIC_KEY_LEN: usize = 32;
449
450#[derive(
452 Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen, Hash,
453)]
454#[cfg_attr(feature = "std", byte_string(to_hex_string))]
455#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
456pub struct StakePublicKey(pub [u8; STAKE_PUBLIC_KEY_LEN]);
457
458impl StakePublicKey {
459 pub fn hash(&self) -> MainchainKeyHash {
461 MainchainKeyHash(blake2b(&self.0))
462 }
463}
464
465const MAINCHAIN_KEY_HASH_LEN: usize = 28;
467
468#[derive(
469 Clone,
470 Copy,
471 Decode,
472 DecodeWithMemTracking,
473 Default,
474 Encode,
475 Hash,
476 MaxEncodedLen,
477 Eq,
478 PartialEq,
479 Ord,
480 PartialOrd,
481 TypeInfo,
482)]
483#[byte_string(debug)]
484#[cfg_attr(feature = "std", byte_string(to_hex_string, decode_hex))]
485#[cfg_attr(feature = "serde", byte_string(hex_serialize, hex_deserialize))]
486pub struct MainchainKeyHash(pub [u8; MAINCHAIN_KEY_HASH_LEN]);
489
490impl Display for MainchainKeyHash {
491 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
492 let hash = sp_core::hexdisplay::HexDisplay::from(&self.0);
493 write!(f, "0x{}", hash)
494 }
495}
496
497impl MainchainKeyHash {
498 pub fn from_vkey(vkey: &[u8; 32]) -> Self {
500 Self(blake2b(vkey))
501 }
502}
503
504pub const MAINCHAIN_SIGNATURE_LEN: usize = 64;
506
507#[derive(Clone, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
508#[cfg_attr(feature = "std", byte_string(to_hex_string))]
509#[byte_string(debug, hex_serialize, decode_hex)]
510pub struct MainchainSignature(pub [u8; MAINCHAIN_SIGNATURE_LEN]);
516
517impl From<[u8; MAINCHAIN_SIGNATURE_LEN]> for MainchainSignature {
518 fn from(raw: [u8; MAINCHAIN_SIGNATURE_LEN]) -> Self {
519 Self(raw)
520 }
521}
522
523impl WrapperTypeEncode for MainchainSignature {}
524impl Deref for MainchainSignature {
525 type Target = [u8];
526
527 fn deref(&self) -> &Self::Target {
528 &self.0
529 }
530}
531impl Decode for MainchainSignature {
532 fn decode<I: parity_scale_codec::Input>(
533 input: &mut I,
534 ) -> Result<Self, parity_scale_codec::Error> {
535 let vec: Vec<u8> = Decode::decode(input)?;
536 let arr = vec.try_into().map_err(|_| "Incorrect MainchainSignature size")?;
537 Ok(MainchainSignature(arr))
538 }
539}
540
541impl MainchainSignature {
542 pub fn verify(&self, public_key: &StakePoolPublicKey, signed_message: &[u8]) -> bool {
544 let mainchain_signature = ed25519::Signature::from(self.0);
545
546 sp_io::crypto::ed25519_verify(
547 &mainchain_signature,
548 signed_message,
549 &ed25519::Public::from(public_key.0),
550 )
551 }
552}
553
554pub const STAKE_KEY_SIGNATURE_LEN: usize = 64;
556
557#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, Hash, TypeInfo)]
558#[byte_string(debug, hex_serialize, decode_hex)]
559pub struct StakeKeySignature(pub [u8; STAKE_KEY_SIGNATURE_LEN]);
561
562impl From<[u8; STAKE_KEY_SIGNATURE_LEN]> for StakeKeySignature {
563 fn from(raw: [u8; STAKE_KEY_SIGNATURE_LEN]) -> Self {
564 Self(raw)
565 }
566}
567
568impl StakeKeySignature {
569 pub fn verify(&self, public_key: &StakePublicKey, message: &[u8]) -> bool {
571 let signature = ed25519::Signature::from(self.0);
572 sp_io::crypto::ed25519_verify(&signature, message, &ed25519::Public::from(public_key.0))
573 }
574}
575
576#[derive(
577 Clone,
578 Copy,
579 Debug,
580 Encode,
581 Decode,
582 DecodeWithMemTracking,
583 PartialEq,
584 TypeInfo,
585 ToDatum,
586 MaxEncodedLen,
587 Default,
588 PartialOrd,
589 Ord,
590 Eq,
591 Zero,
592 One,
593 NumOps,
594 Num,
595 From,
596 Into,
597)]
598#[cfg_attr(feature = "serde", derive(Serialize))]
599pub struct ScEpochNumber(pub u64);
601
602impl Display for ScEpochNumber {
603 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
604 u64::fmt(&self.0, f)
605 }
606}
607
608impl ScEpochNumber {
609 pub fn next(&self) -> Self {
611 Self(self.0 + 1)
612 }
613 pub fn prev(&self) -> Option<Self> {
615 self.0.checked_sub(1).map(Self)
616 }
617 pub fn saturating_sub(&self, other: u64) -> Self {
619 Self(self.0.saturating_sub(other.into()))
620 }
621}
622
623#[derive(
624 Clone,
625 Copy,
626 Debug,
627 Encode,
628 Decode,
629 DecodeWithMemTracking,
630 TypeInfo,
631 MaxEncodedLen,
632 PartialEq,
633 Eq,
634 Serialize,
635 Deserialize,
636)]
637pub struct ScEpochDuration(u64);
639
640impl ScEpochDuration {
641 pub fn from_millis(millis: u64) -> Self {
643 Self(millis)
644 }
645
646 pub fn millis(&self) -> u64 {
648 self.0
649 }
650}
651
652impl Default for ScEpochDuration {
653 fn default() -> Self {
654 Self(60_000)
655 }
656}
657
658#[derive(
659 Clone,
660 PartialEq,
661 Eq,
662 Encode,
663 Decode,
664 DecodeWithMemTracking,
665 ToDatum,
666 TypeInfo,
667 PartialOrd,
668 Ord,
669 Hash,
670)]
671#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex, as_ref)]
672pub struct SidechainPublicKey(pub Vec<u8>);
678
679impl From<ecdsa::Public> for SidechainPublicKey {
680 fn from(value: ecdsa::Public) -> Self {
681 Self(value.0.to_vec())
682 }
683}
684
685#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
687#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
688pub struct PlutusScriptCbor(pub Vec<u8>);
689
690#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
692#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
693pub struct TransactionCbor(pub Vec<u8>);
694
695#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
697#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
698pub struct VKeyWitnessCbor(pub Vec<u8>);
699
700#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
702#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
703pub struct SidechainSignature(pub Vec<u8>);
704
705#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
709#[byte_string(debug, hex_serialize, hex_deserialize)]
710pub struct CrossChainPublicKey(pub Vec<u8>);
711
712impl CrossChainPublicKey {
713 pub fn hash(&self) -> CrossChainKeyHash {
715 CrossChainKeyHash(blake2b(&self.0))
716 }
717}
718
719impl From<k256::PublicKey> for CrossChainPublicKey {
720 fn from(value: k256::PublicKey) -> Self {
721 Self(value.to_sec1_bytes().to_vec())
722 }
723}
724
725impl From<CrossChainPublicKey> for k256::PublicKey {
726 fn from(value: CrossChainPublicKey) -> Self {
727 k256::PublicKey::from_sec1_bytes(&value.0)
728 .expect("CrossChainPublicKey converts to valid secp256k1::PublicKey")
729 }
730}
731
732const CROSS_CHAIN_KEY_HASH_LEN: usize = 28;
734
735#[derive(
736 Clone,
737 Copy,
738 Decode,
739 DecodeWithMemTracking,
740 Default,
741 Encode,
742 Hash,
743 MaxEncodedLen,
744 Eq,
745 PartialEq,
746 Ord,
747 PartialOrd,
748 TypeInfo,
749)]
750#[byte_string(debug, to_hex_string)]
751#[cfg_attr(feature = "std", byte_string(decode_hex))]
752#[cfg_attr(feature = "serde", byte_string(hex_serialize, hex_deserialize))]
753pub struct CrossChainKeyHash(pub [u8; CROSS_CHAIN_KEY_HASH_LEN]);
755
756impl Display for CrossChainKeyHash {
757 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
758 f.write_str(&self.to_hex_string())
759 }
760}
761
762#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
764#[byte_string(debug, hex_serialize)]
765pub struct CrossChainSignature(pub Vec<u8>);
766
767impl CrossChainSignature {
768 pub fn verify(
770 &self,
771 cross_chain_pubkey: &CrossChainPublicKey,
772 data: &[u8],
773 ) -> Result<(), k256::ecdsa::signature::Error> {
774 use k256::ecdsa::signature::Verifier;
775
776 let vkey = k256::ecdsa::VerifyingKey::from_sec1_bytes(&cross_chain_pubkey.0[..])?;
777 let signature = k256::ecdsa::Signature::from_slice(&self.0[..])?;
778 vkey.verify(data, &signature)
779 }
780}
781
782const EPOCH_NONCE_LEN: usize = 32;
784
785#[derive(Default, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
792#[byte_string(debug, hex_serialize, decode_hex)]
793pub struct EpochNonce(pub Vec<u8>);
794
795impl EpochNonce {
796 pub fn as_array(&self) -> [u8; EPOCH_NONCE_LEN] {
798 let mut epoch_nonce = self.0.clone();
799 epoch_nonce.resize_with(32, || 0);
800 epoch_nonce.try_into().expect("Should never fail after being resized")
801 }
802}
803
804#[derive(
805 Default,
806 Debug,
807 Copy,
808 Clone,
809 PartialEq,
810 Eq,
811 Encode,
812 Decode,
813 DecodeWithMemTracking,
814 ToDatum,
815 TypeInfo,
816 MaxEncodedLen,
817 Hash,
818)]
819pub struct UtxoId {
827 pub tx_hash: McTxHash,
829 pub index: UtxoIndex,
831}
832
833impl UtxoId {
834 pub const fn new(hash: [u8; TX_HASH_SIZE], index: u16) -> UtxoId {
836 UtxoId { tx_hash: McTxHash(hash), index: UtxoIndex(index) }
837 }
838}
839
840#[cfg(feature = "serde")]
841impl Serialize for UtxoId {
842 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
843 where
844 S: Serializer,
845 {
846 serializer.serialize_str(&self.to_string())
847 }
848}
849
850#[cfg(feature = "serde")]
851impl<'de> Deserialize<'de> for UtxoId {
852 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
853 where
854 D: Deserializer<'de>,
855 {
856 alloc::string::String::deserialize(deserializer).and_then(|string| {
857 Self::from_str(&string).map_err(|err| serde::de::Error::custom(err.to_string()))
858 })
859 }
860}
861
862#[cfg(feature = "serde")]
863impl FromStr for UtxoId {
864 type Err = &'static str;
865
866 fn from_str(s: &str) -> Result<Self, Self::Err> {
867 let split: Vec<&str> = s.split('#').collect();
868 let &[hash_str, index_str] = split.as_slice() else {
869 return Err("UtxoId string must conform to format: '<hash>#<index>'");
870 };
871
872 Ok(UtxoId {
873 tx_hash: McTxHash::from_str(hash_str)
874 .map_err(|_| "invalid string input for McTxHash")?,
875 index: UtxoIndex::from_str(index_str)
876 .map_err(|_| "invalid string input for OutputIndex")?,
877 })
878 }
879}
880
881impl Display for UtxoId {
882 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
883 let hash = sp_core::hexdisplay::HexDisplay::from(&self.tx_hash.0);
884 write!(f, "{}#{}", hash, self.index.0)
885 }
886}
887
888#[derive(
889 Default,
890 Debug,
891 Copy,
892 Clone,
893 PartialEq,
894 Eq,
895 Encode,
896 Decode,
897 DecodeWithMemTracking,
898 PartialOrd,
899 Ord,
900 ToDatum,
901 TypeInfo,
902 MaxEncodedLen,
903 Hash,
904)]
905#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
906pub struct UtxoIndex(pub u16);
908
909#[cfg(feature = "serde")]
910impl FromStr for UtxoIndex {
911 type Err = sp_std::num::ParseIntError;
912 fn from_str(s: &str) -> Result<Self, Self::Err> {
913 let parsed = u16::from_str(s)?;
914 let _check_overflow = i16::from_str(s)?;
915 Ok(Self(parsed))
916 }
917}
918
919pub const TX_HASH_SIZE: usize = 32;
921
922#[derive(
923 Default,
924 Copy,
925 Clone,
926 Hash,
927 PartialEq,
928 Eq,
929 Encode,
930 Decode,
931 DecodeWithMemTracking,
932 ToDatum,
933 TypeInfo,
934 MaxEncodedLen,
935)]
936#[byte_string(debug, from_bytes, decode_hex, hex_serialize, hex_deserialize)]
937#[constructor_datum]
938pub struct McTxHash(pub [u8; TX_HASH_SIZE]);
942
943impl TryFrom<Vec<u8>> for McTxHash {
944 type Error = &'static str;
945
946 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
947 <[u8; 32]>::try_from(value)
948 .map_err(|_| "McTxHash must be 32 bytes long")
949 .map(McTxHash)
950 }
951}
952
953impl Display for McTxHash {
954 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
955 let hash = sp_core::hexdisplay::HexDisplay::from(&self.0);
956 write!(f, "{}", hash)
957 }
958}
959
960#[derive(
961 Default,
962 Clone,
963 Decode,
964 DecodeWithMemTracking,
965 Encode,
966 PartialEq,
967 Eq,
968 TypeInfo,
969 MaxEncodedLen,
970 Hash,
971)]
972#[byte_string(debug, decode_hex, hex_serialize, hex_deserialize)]
973pub struct McBlockHash(pub [u8; 32]);
977
978impl Display for McBlockHash {
979 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
980 let hash = sp_core::hexdisplay::HexDisplay::from(&self.0);
981 write!(f, "{}", hash)
982 }
983}
984
985#[derive(
987 Default, Debug, Copy, Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo,
988)]
989#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
990#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
991pub struct UtxoInfo {
992 pub utxo_id: UtxoId,
994 pub epoch_number: McEpochNumber,
996 pub block_number: McBlockNumber,
998 pub slot_number: McSlotNumber,
1000 pub tx_index_within_block: McTxIndexInBlock,
1002}
1003
1004#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
1009pub struct UtxoInfoOrderingKey {
1010 pub block_number: McBlockNumber,
1012 pub tx_index_within_block: McTxIndexInBlock,
1014 pub utxo_id_index: UtxoIndex,
1016}
1017
1018impl UtxoInfo {
1019 pub fn ordering_key(&self) -> UtxoInfoOrderingKey {
1021 UtxoInfoOrderingKey {
1022 block_number: self.block_number,
1023 tx_index_within_block: self.tx_index_within_block,
1024 utxo_id_index: self.utxo_id.index,
1025 }
1026 }
1027}
1028
1029#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
1036#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1037#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
1038pub enum NetworkType {
1039 Mainnet,
1041 #[default]
1043 Testnet,
1044}
1045
1046#[cfg(feature = "std")]
1047impl std::fmt::Display for NetworkType {
1048 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1049 let str = match self {
1050 Self::Mainnet => "mainnet",
1051 Self::Testnet => "testnet",
1052 };
1053 write!(f, "{}", str)
1054 }
1055}
1056
1057#[derive(Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
1066#[cfg_attr(feature = "serde", derive(Serialize))]
1067pub struct RegistrationData {
1068 pub registration_utxo: UtxoId,
1070 pub sidechain_signature: SidechainSignature,
1072 pub mainchain_signature: MainchainSignature,
1074 pub cross_chain_signature: CrossChainSignature,
1076 pub sidechain_pub_key: SidechainPublicKey,
1078 pub cross_chain_pub_key: CrossChainPublicKey,
1080 pub utxo_info: UtxoInfo,
1082 pub tx_inputs: Vec<UtxoId>,
1084 pub keys: CandidateKeys,
1086}
1087
1088#[derive(Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
1090#[cfg_attr(feature = "serde", derive(Serialize))]
1091pub struct CandidateRegistrations {
1092 pub stake_pool_public_key: StakePoolPublicKey,
1094 pub registrations: Vec<RegistrationData>,
1096 pub stake_delegation: Option<StakeDelegation>,
1098}
1099
1100impl CandidateRegistrations {
1101 pub fn new(
1103 stake_pool_public_key: StakePoolPublicKey,
1104 stake_delegation: Option<StakeDelegation>,
1105 registrations: Vec<RegistrationData>,
1106 ) -> Self {
1107 Self { stake_pool_public_key, registrations, stake_delegation }
1108 }
1109
1110 pub fn mainchain_pub_key(&self) -> &StakePoolPublicKey {
1112 &self.stake_pool_public_key
1113 }
1114
1115 pub fn registrations(&self) -> &[RegistrationData] {
1117 &self.registrations
1118 }
1119}
1120
1121#[derive(
1123 Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialOrd, Ord, Hash,
1124)]
1125#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
1126pub struct AuraPublicKey(pub Vec<u8>);
1127impl AuraPublicKey {
1128 pub fn try_into_sr25519(&self) -> Option<sr25519::Public> {
1130 Some(sr25519::Public::from_raw(self.0.clone().try_into().ok()?))
1131 }
1132}
1133
1134impl From<sr25519::Public> for AuraPublicKey {
1135 fn from(value: sr25519::Public) -> Self {
1136 Self(value.0.to_vec())
1137 }
1138}
1139
1140impl From<AuraPublicKey> for CandidateKey {
1141 fn from(value: AuraPublicKey) -> Self {
1142 Self { id: AURA.0, bytes: value.0 }
1143 }
1144}
1145
1146#[derive(
1148 Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialOrd, Ord, Hash,
1149)]
1150#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
1151pub struct GrandpaPublicKey(pub Vec<u8>);
1152impl GrandpaPublicKey {
1153 pub fn try_into_ed25519(&self) -> Option<ed25519::Public> {
1155 Some(ed25519::Public::from_raw(self.0.clone().try_into().ok()?))
1156 }
1157}
1158
1159impl From<ed25519::Public> for GrandpaPublicKey {
1160 fn from(value: ed25519::Public) -> Self {
1161 Self(value.0.to_vec())
1162 }
1163}
1164
1165impl From<GrandpaPublicKey> for CandidateKey {
1166 fn from(value: GrandpaPublicKey) -> Self {
1167 Self { id: GRANDPA.0, bytes: value.0 }
1168 }
1169}
1170
1171#[derive(
1172 Debug,
1173 Clone,
1174 PartialEq,
1175 Decode,
1176 DecodeWithMemTracking,
1177 Encode,
1178 MaxEncodedLen,
1179 TypeInfo,
1180 Eq,
1181 Hash,
1182)]
1183#[cfg_attr(feature = "serde", derive(Serialize))]
1184pub struct DParameter {
1192 pub num_permissioned_candidates: u16,
1194 pub num_registered_candidates: u16,
1196}
1197
1198impl DParameter {
1199 pub fn new(num_permissioned_candidates: u16, num_registered_candidates: u16) -> Self {
1201 Self { num_permissioned_candidates, num_registered_candidates }
1202 }
1203}
1204
1205#[derive(
1207 Clone, PartialEq, Eq, Decode, DecodeWithMemTracking, Encode, TypeInfo, PartialOrd, Ord, Hash,
1208)]
1209#[cfg_attr(feature = "serde", derive(Serialize))]
1210pub struct CandidateKey {
1211 pub id: [u8; 4],
1213 pub bytes: Vec<u8>,
1215}
1216
1217impl alloc::fmt::Debug for CandidateKey {
1218 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
1219 let id = hex::encode(self.id);
1220 let id_text = String::from_utf8_lossy(&self.id);
1221 let bytes = hex::encode(&self.bytes);
1222 f.write_str(&alloc::format!("CandidateKey {{ id: {id}({id_text}), bytes: {bytes} }}"))
1223 }
1224}
1225
1226impl FromStr for CandidateKey {
1227 type Err = &'static str;
1228
1229 fn from_str(s: &str) -> Result<Self, Self::Err> {
1230 if let Some((id, bytes)) = s.split_once(':') {
1231 let id: [u8; 4] = id.as_bytes().try_into().map_err(|_| "invalid key type id")?;
1232 let bytes = bytes.trim_start_matches("0x");
1233 let bytes = hex::decode(bytes).map_err(|_| "key bytes are not in hex format")?;
1234 Ok(CandidateKey { id, bytes })
1235 } else {
1236 Err("invalid format of CandidateKey, expected '<key type>:<key>'")
1237 }
1238 }
1239}
1240
1241pub const CROSS_CHAIN_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"crch");
1243
1244impl CandidateKey {
1245 pub fn new(id: KeyTypeId, bytes: Vec<u8>) -> Self {
1247 Self { id: id.0, bytes }
1248 }
1249}
1250
1251#[derive(Debug, Clone, PartialEq, Eq, DecodeWithMemTracking, TypeInfo, PartialOrd, Ord, Hash)]
1252#[cfg_attr(feature = "serde", derive(Serialize))]
1253pub struct CandidateKeys(pub Vec<CandidateKey>);
1255
1256impl CandidateKeys {
1257 pub fn find(&self, id: KeyTypeId) -> Option<Vec<u8>> {
1259 self.0
1260 .iter()
1261 .find_map(|e| if e.id == id.0 { Some(e.bytes.clone()) } else { None })
1262 }
1263
1264 pub fn find_or_empty(&self, id: KeyTypeId) -> Vec<u8> {
1266 self.find(id).unwrap_or_default()
1267 }
1268
1269 pub fn has_only_aura_and_grandpa_keys(&self) -> bool {
1271 self.0.len() == 2 && self.find(AURA).is_some() && self.find(GRANDPA).is_some()
1272 }
1273}
1274
1275impl From<Vec<([u8; 4], Vec<u8>)>> for CandidateKeys {
1276 fn from(value: Vec<([u8; 4], Vec<u8>)>) -> Self {
1277 Self(value.into_iter().map(|(id, bytes)| CandidateKey { id, bytes }).collect())
1278 }
1279}
1280
1281impl Encode for CandidateKeys {
1292 fn size_hint(&self) -> usize {
1293 if self.has_only_aura_and_grandpa_keys() {
1294 Encode::size_hint(&AuraPublicKey(self.find_or_empty(AURA)))
1295 .saturating_add(Encode::size_hint(&GrandpaPublicKey(self.find_or_empty(GRANDPA))))
1296 } else {
1297 Encode::size_hint(&Compact(u32::MAX)).saturating_add(Encode::size_hint(&self.0))
1298 }
1299 }
1300
1301 fn encode_to<T: parity_scale_codec::Output + ?Sized>(&self, dest: &mut T) {
1302 if self.has_only_aura_and_grandpa_keys() {
1303 Encode::encode_to(&AuraPublicKey(self.find_or_empty(AURA)), dest);
1304 Encode::encode_to(&GrandpaPublicKey(self.find_or_empty(GRANDPA)), dest)
1305 } else {
1306 Encode::encode_to(&Compact(u32::MAX), dest);
1309 Encode::encode_to(&self.0, dest)
1310 }
1311 }
1312}
1313
1314impl Decode for CandidateKeys {
1316 fn decode<I: parity_scale_codec::Input>(
1317 input: &mut I,
1318 ) -> Result<Self, parity_scale_codec::Error> {
1319 let marker_or_aura_size: u32 = <Compact<u32>>::decode(input)?.0;
1321 if marker_or_aura_size == u32::MAX {
1322 let keys = Vec::<CandidateKey>::decode(input)?;
1323 Ok(Self(keys))
1324 } else {
1325 let aura_bytes: Vec<u8> = decode_vec_with_len(input, marker_or_aura_size as usize)?;
1326 let grandpa = GrandpaPublicKey::decode(input)?;
1327 Ok(Self(vec![AuraPublicKey(aura_bytes).into(), grandpa.into()]))
1328 }
1329 }
1330}
1331
1332#[derive(
1333 Debug,
1334 Clone,
1335 PartialEq,
1336 Eq,
1337 Decode,
1338 DecodeWithMemTracking,
1339 Encode,
1340 TypeInfo,
1341 PartialOrd,
1342 Ord,
1343 Hash,
1344)]
1345#[cfg_attr(feature = "serde", derive(Serialize))]
1346pub struct PermissionedCandidateData {
1352 pub sidechain_public_key: SidechainPublicKey,
1354 pub keys: CandidateKeys,
1356}
1357
1358#[cfg(feature = "std")]
1360impl FromStr for PermissionedCandidateData {
1361 type Err = alloc::boxed::Box<dyn core::error::Error + Send + Sync>;
1362
1363 fn from_str(partner_chain_public_keys: &str) -> Result<Self, Self::Err> {
1364 fn is_legacy_format(line: &str) -> bool {
1365 line.contains(':') && !line.contains(',')
1366 }
1367
1368 fn parse_legacy_format(
1369 line: &str,
1370 ) -> Result<
1371 PermissionedCandidateData,
1372 alloc::boxed::Box<dyn core::error::Error + Send + Sync>,
1373 > {
1374 let line = line.replace("0x", "");
1375 if let [sidechain_pub_key, aura_pub_key, grandpa_pub_key] =
1376 line.split(":").collect::<Vec<_>>()[..]
1377 {
1378 Ok(PermissionedCandidateData {
1379 sidechain_public_key: SidechainPublicKey(
1380 hex::decode(sidechain_pub_key).map_err(|e| e.to_string())?,
1381 ),
1382 keys: CandidateKeys(vec![
1383 AuraPublicKey(hex::decode(aura_pub_key).map_err(|e| e.to_string())?).into(),
1384 GrandpaPublicKey(hex::decode(grandpa_pub_key).map_err(|e| e.to_string())?)
1385 .into(),
1386 ]),
1387 })
1388 } else {
1389 Err(format!("Failed to parse partner chain public keys (legacy) from '{line}'")
1390 .into())
1391 }
1392 }
1393
1394 fn parse_generic_format(
1395 line: &str,
1396 ) -> Result<
1397 PermissionedCandidateData,
1398 alloc::boxed::Box<dyn core::error::Error + Send + Sync>,
1399 > {
1400 let mut columns = line.split(",");
1401 if let Some(partner_chains_key) = columns.next() {
1402 let partner_chains_key = SidechainPublicKey(
1403 hex::decode(partner_chains_key.trim_start_matches("0x"))
1404 .map_err(|e| e.to_string())?,
1405 );
1406 let mut keys = vec![];
1407 for column in columns {
1408 let key = CandidateKey::from_str(column)?;
1409 keys.push(key);
1410 }
1411 Ok(PermissionedCandidateData {
1412 sidechain_public_key: partner_chains_key,
1413 keys: CandidateKeys(keys),
1414 })
1415 } else {
1416 Err("Failed to parse partner chain public keys (generic) from '{line}'.".into())
1417 }
1418 }
1419
1420 if is_legacy_format(&partner_chain_public_keys) {
1421 parse_legacy_format(&partner_chain_public_keys)
1422 } else {
1423 parse_generic_format(&partner_chain_public_keys)
1424 }
1425 }
1426}
1427
1428#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1430pub struct CandidateRegistration {
1431 pub stake_ownership: AdaBasedStaking,
1433 pub partner_chain_pub_key: SidechainPublicKey,
1435 pub partner_chain_signature: SidechainSignature,
1437 pub own_pkh: MainchainKeyHash,
1439 pub registration_utxo: UtxoId,
1441 pub keys: CandidateKeys,
1443}
1444
1445impl CandidateRegistration {
1446 pub fn matches_keys(&self, other: &Self) -> bool {
1448 self.stake_ownership == other.stake_ownership
1449 && self.partner_chain_pub_key == other.partner_chain_pub_key
1450 && self.partner_chain_signature == other.partner_chain_signature
1451 && self.keys.0.iter().all(|key| other.keys.0.contains(key))
1452 }
1453}
1454
1455#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1459pub struct AdaBasedStaking {
1460 pub pub_key: StakePoolPublicKey,
1462 pub signature: MainchainSignature,
1464}
1465
1466#[derive(
1467 Clone,
1468 PartialEq,
1469 Eq,
1470 Ord,
1471 PartialOrd,
1472 TypeInfo,
1473 MaxEncodedLen,
1474 Encode,
1475 Decode,
1476 DecodeWithMemTracking,
1477)]
1478pub enum DelegatorKey {
1480 StakeKeyHash([u8; 28]),
1482 ScriptKeyHash {
1484 hash_raw: [u8; 28],
1486 script_hash: [u8; 28],
1488 },
1489}
1490
1491impl alloc::fmt::Debug for DelegatorKey {
1492 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
1493 let s = match self {
1494 Self::ScriptKeyHash { hash_raw, script_hash } => alloc::format!(
1495 "ScriptKeyHash{{ hash_raw: {}, script_hash: {} }}",
1496 hex::encode(hash_raw),
1497 hex::encode(script_hash)
1498 ),
1499 Self::StakeKeyHash(hash) => alloc::format!("StakeKeyHash({})", hex::encode(hash)),
1500 };
1501
1502 f.write_str(&s)
1503 }
1504}
1505
1506#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
1508pub struct DelegatorStakeAmount(pub u64);
1509
1510impl<T: Into<u64>> From<T> for DelegatorStakeAmount {
1511 fn from(value: T) -> Self {
1512 Self(value.into())
1513 }
1514}
1515
1516#[derive(Debug, Clone, Default)]
1521pub struct StakeDistribution(pub BTreeMap<MainchainKeyHash, PoolDelegation>);
1522
1523#[derive(Debug, Clone, Default, PartialEq)]
1525pub struct PoolDelegation {
1526 pub total_stake: StakeDelegation,
1528 pub delegators: BTreeMap<DelegatorKey, DelegatorStakeAmount>,
1530}
1531
1532pub trait FromStrStdErr:
1534 FromStr<Err: Into<alloc::boxed::Box<dyn core::error::Error + Send + Sync + 'static>>>
1535{
1536}
1537impl<T: FromStr<Err: Into<alloc::boxed::Box<dyn core::error::Error + Send + Sync + 'static>>>>
1538 FromStrStdErr for T
1539{
1540}
1541
1542#[cfg(test)]
1543mod tests {
1544 use super::*;
1545 use core::str::FromStr;
1546 use hex_literal::hex;
1547 use parity_scale_codec::{Decode, Encode};
1548
1549 #[test]
1550 fn main_chain_address_string_serialize_deserialize_round_trip() {
1551 let address = MainchainAddress::from_str(
1552 "addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc",
1553 )
1554 .unwrap();
1555 let serialized = serde_json::to_value(&address).unwrap();
1556 assert_eq!(
1557 serialized,
1558 serde_json::json!("addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc")
1559 );
1560 let deserialized = serde_json::from_value(serialized).unwrap();
1561 assert_eq!(address, deserialized);
1562 }
1563
1564 #[test]
1565 fn main_chain_address_deserialization_of_hex_encoded_bytes() {
1566 let address = MainchainAddress::from_str("addr_test1wz5q").unwrap();
1567 let serialized = serde_json::json!("0x616464725f7465737431777a3571");
1568 assert_eq!(address, serde_json::from_value(serialized).unwrap());
1569 let serialized = serde_json::json!("616464725f7465737431777a3571");
1570 assert_eq!(address, serde_json::from_value(serialized).unwrap());
1571 }
1572
1573 #[test]
1574 fn main_chain_address_string_from_str_to_string_round_trip() {
1575 let address = MainchainAddress::from_str(
1576 "addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc",
1577 )
1578 .unwrap();
1579 let str = address.to_string();
1580 let from_str = MainchainAddress::from_str(&str).unwrap();
1581 assert_eq!(address, from_str);
1582 }
1583
1584 #[test]
1585 fn main_chain_signature_should_be_backward_compatible_with_vec() {
1586 #[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
1587 #[byte_string(debug, hex_serialize, decode_hex)]
1588 struct LegacyMCSignature(pub Vec<u8>);
1589
1590 let legacy_encoded = LegacyMCSignature(vec![10; 64]).encode();
1591
1592 let legacy_decoded = MainchainSignature::decode(&mut legacy_encoded.as_slice())
1593 .expect("Encoded legacy should decode to current type");
1594
1595 assert_eq!(legacy_decoded.0, [10; MAINCHAIN_SIGNATURE_LEN]);
1596
1597 let current_encoded = MainchainSignature([9; MAINCHAIN_SIGNATURE_LEN]).encode();
1598
1599 let current_decoded = LegacyMCSignature::decode(&mut current_encoded.as_slice())
1600 .expect("Encoded current should decode to legacy");
1601
1602 assert_eq!(current_decoded.0, vec![9; 64]);
1603 }
1604
1605 #[test]
1606 fn cross_chain_signature_verify_works() {
1607 let signature = CrossChainSignature(
1608 hex!("d1e02e4a5484c3b7202ce6b844577048e7578dc62901cf8f51e6d74bbd3adb091688feacedd8343d0b04a0f5862b2e06148934a75e678e42051fde5431eca33d").to_vec()
1609 );
1610 let pubkey = CrossChainPublicKey(
1611 hex!("020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1").to_vec(),
1612 );
1613 let signed_data = hex!(
1614 "84020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a16c68747470733a2f2f636f6f6c2e73747566662f73706f2e6a736f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
1615 );
1616
1617 assert!(signature.verify(&pubkey, &signed_data).is_ok())
1618 }
1619
1620 #[derive(Decode, Encode, PartialEq, Eq, Debug)]
1621 struct TestCandidateKeys {
1622 field_before: Option<u32>,
1623 keys: CandidateKeys,
1624 field_after: Option<u32>,
1626 }
1627
1628 #[test]
1629 fn encode_decode_round_trip_for_generic_candidate_keys() {
1630 let keys = TestCandidateKeys {
1631 field_before: Some(42),
1632 keys: CandidateKeys(vec![
1633 CandidateKey { id: *b"abcd", bytes: [7u8; 32].to_vec() },
1634 CandidateKey { id: *b"efgh", bytes: [9u8; 32].to_vec() },
1635 ]),
1636 field_after: Some(15),
1637 };
1638 let bytes: Vec<u8> = Encode::encode(&keys);
1639 let mut bytes: &[u8] = &bytes;
1640 let decoded = TestCandidateKeys::decode(&mut bytes).unwrap();
1641 assert_eq!(keys, decoded)
1642 }
1643
1644 #[test]
1645 fn encode_decode_round_trip_for_aura_and_grandpa_candidate_keys() {
1646 let keys = TestCandidateKeys {
1647 field_before: Some(42),
1648 keys: CandidateKeys(vec![
1649 AuraPublicKey([7u8; 32].to_vec()).into(),
1650 GrandpaPublicKey([9u8; 32].to_vec()).into(),
1651 ]),
1652 field_after: Some(15),
1653 };
1654 let bytes: Vec<u8> = Encode::encode(&keys);
1655 let mut bytes: &[u8] = &bytes;
1656 let decoded = TestCandidateKeys::decode(&mut bytes).unwrap();
1657 assert_eq!(keys, decoded)
1658 }
1659}