sidechain_domain/
lib.rs

1//! # Partner Chains domain types
2//!
3//! This crate defines common domain and utility types used in the Partner Chain Toolkit.
4
5#![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
53/// The number of main chain epochs back a Partner Chain queries for committee selection inputs.
54/// This offset is necessary to ensure that data is present and stable.
55pub const DATA_MC_EPOCH_OFFSET: u32 = 2;
56
57/// Shifts given epoch back by [DATA_MC_EPOCH_OFFSET] accounting for underflow.
58pub 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))]
78/// Cardano epoch number. In range [0, 2^31-1].
79pub struct McEpochNumber(pub u32);
80
81impl McEpochNumber {
82	/// Returns next Cardano epoch number
83	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))]
108/// Amount of Lovelace (which is a fraction of 1 ADA) staked/locked on Cardano
109pub struct StakeDelegation(pub u64);
110
111impl StakeDelegation {
112	/// Checks if stake delegation is zero
113	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))]
133/// The amount of a Cardano native token
134pub 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))]
158/// Cardano block number. In range [0, 2^31-1].
159pub 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	/// Adds `rhs` to the block number without overflow
169	pub fn saturating_add<Rhs: Into<u32>>(self, rhs: Rhs) -> Self {
170		Self(self.0.saturating_add(rhs.into()))
171	}
172
173	/// Subtracts `rhs` from the block number without overflow
174	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))]
194/// Cardano slot number. In range [0, 2^63-1].
195pub 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))]
220/// Partner Chain slot number
221pub struct ScSlotNumber(pub u64);
222
223/// Data describing a Cardano block
224#[derive(Debug, Clone, PartialEq, Default)]
225#[cfg_attr(feature = "serde", derive(serde::Serialize))]
226pub struct MainchainBlock {
227	/// Block number
228	pub number: McBlockNumber,
229	/// Block hash
230	pub hash: McBlockHash,
231	/// Block's epoch number
232	pub epoch: McEpochNumber,
233	/// Block's slot number
234	pub slot: McSlotNumber,
235	/// Block timestamp
236	pub timestamp: u64, // seconds since UNIX_EPOCH
237}
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))]
255/// An index of transaction in a block. In range [0, 2^31-1].
256pub 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
268/// Maximum length of a Cardano address in UTF-8 bytes
269const MAX_MAINCHAIN_ADDRESS_BYTES: u32 = 120;
270
271/// Wraps UTF-8 bytes of Mainchain Address in bech32 format.
272/// Example: utf-8 bytes of "addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc" are
273/// "0x616464725f7465737431777a35716337666b327061743030353877347a77766b77333579747074656a336e7563336a65326b6774616e356471337274347363"
274#[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	/// Returns raw bytes of this Cardano address
282	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	/// Deserialized MainchainAddress from both hexstring of ASCII bytes and plain String
320	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
332/// Cardano Policy Id is a 224 bits blake2b hash.
333const 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))]
349/// Cardano Policy Id
350pub 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
359/// Cardano script hash
360pub type ScriptHash = PolicyId;
361
362/// Maximum length of a Cardano native asset's name in UTF-8 bytes
363pub const MAX_ASSET_NAME_LEN: u32 = 32;
364
365/// Cardano native asset name
366#[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	/// Constructs an empty [AssetName]
375	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))]
400/// Full data identifying a Cardano native asset
401pub struct AssetId {
402	/// Policy ID
403	pub policy_id: PolicyId,
404	/// Asset name
405	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
429/// Length of Cardano stake pool key
430const 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)]
447/// Cardano stake pool public key (ed25519)
448pub struct StakePoolPublicKey(pub [u8; STAKE_POOL_PUBLIC_KEY_LEN]);
449
450impl StakePoolPublicKey {
451	/// Computes the blake2b_224 hash of this Cardano stake pool public key
452	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
467/// Length of Cardano staking public key
468const STAKE_PUBLIC_KEY_LEN: usize = 32;
469
470/// Cardano staking public key (ed25519)
471#[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	/// Computes the blake2b_224 hash of this Cardano staking public key
480	pub fn hash(&self) -> MainchainKeyHash {
481		MainchainKeyHash(blake2b(&self.0))
482	}
483}
484
485/// Length of Cardano key hash
486const 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))]
506/// blake2b_224 hash of a Cardano Verification (Public) Key.
507/// It can be a hash of Payment Verification, Payment Extended Verification, Stake Pool Verification Key or Staking Verification Key.
508pub 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	/// Computes the blake2b_224 hash of the given ed25519 public key bytes
519	pub fn from_vkey(vkey: &[u8; 32]) -> Self {
520		Self(blake2b(vkey))
521	}
522}
523
524/// Length of Cardano signature (EDDSA)
525pub 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)]
530/// Cardano signature type (EDDSA)
531///
532/// WARNING: This type needs to be backwards compatible with a legacy schema wrapping `Vec<u8>`.
533///          Because of this, it is not handled correctly by PolkadotJS. If you need to accept
534///          this type as extrinsic argument, use raw `[u8; MAINCHAIN_SIGNATURE_LEN]` instead.
535pub 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	/// Verifies whether `self` is a valid signature of `signed_message` for `public_key`
563	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
574/// Length of Cardano staking key signature (EDDSA)
575pub 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)]
579/// Cardano staking key signature type (EDDSA)
580pub 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	/// Verifies whether `self` is a valid signature of `message` for `public_key`
590	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))]
619/// Partner Chain epoch number
620pub 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	/// Returns next epoch number
630	pub fn next(&self) -> Self {
631		Self(self.0 + 1)
632	}
633	/// Returns previous epoch number accounting for underflow
634	pub fn prev(&self) -> Option<Self> {
635		self.0.checked_sub(1).map(Self)
636	}
637	/// Subtracts `other` from the epoch number, saturating at lower bound
638	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)]
657/// Partner Chain public key
658///
659/// This public key is used as the identity of a Partner Chain network participant on a specific Partner Chain,
660/// ie. a network participant can use different [SidechainPublicKey] for each Partner Chain they are active on
661/// as opposed to [CrossChainPublicKey].
662pub 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/// CBOR bytes of Plutus smart contract.
671#[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/// CBOR bytes of Cardano Transaction.
676#[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/// CBOR bytes of Cardano VKeyWitness.
681#[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/// Cross-chain signature type (ECDSA) created using [SidechainPublicKey]
686#[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/// Cross-chain public key (ECDSA)
691///
692/// This public key is used as the universal identity of Partner Chain network participants across all Partner Chains.
693#[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	/// Computes the blake2b_224 hash of this cross-chain public key
699	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
717/// Length of the cross-chain public key hash
718const 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))]
738/// blake2b_224 hash of a cross-chain public key
739pub 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/// Cross-chain signature created using [CrossChainPublicKey]
748#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
749#[byte_string(debug, hex_serialize)]
750pub struct CrossChainSignature(pub Vec<u8>);
751
752impl CrossChainSignature {
753	/// Verifies that `self` is a valid signature of `data` for `cross_chain_pubkey`
754	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
767/// Length of Cardano epoch nonce
768const EPOCH_NONCE_LEN: usize = 32;
769
770/// Cardano epoch nonce
771///
772/// This value is a 32-byte hash generated at the start of each epoch on Cardano using
773/// a verifiable random function as part of normal chain operation by Cardano block producers.
774/// Because it is subject to Cardano's consensus mechanism and has strong cryptographic guarantees,
775/// this value can be used as a tamper-proof shared randomness seed by Partner Chain Toolkit components.
776#[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	/// Returns epoch nonce as byte array.
782	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)]
804/// Identifies a Cardano UTxO (unspent transaction output)
805///
806/// A UTxO is uniquely identified by the hash of the transaction that produced it and its (zero-based)
807/// index in the transaction's output.
808///
809/// Standard semi-human-readable encoding of a UTxO id uses a hash sign to divide the two components:
810/// `0000000000000000000000000000000000000000000000000000000000000000#0`
811pub struct UtxoId {
812	/// Transaction hash
813	pub tx_hash: McTxHash,
814	/// Output index
815	pub index: UtxoIndex,
816}
817
818impl UtxoId {
819	/// Creates new [UtxoId] from primitive type arguments
820	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))]
891/// An index of output of a transaction. In range [0, 2^15-1].
892pub 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
904/// Size of a Cardano transaction hash
905pub 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]
923/// Cardano transaction hash
924///
925/// This hash uniquely identifies a transaction in the Cardano ledger.
926pub 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)]
958/// Cardano block hash
959///
960/// This hash uniquely identifies a Cardano block
961pub 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/// Extended information about a UTxO in Cardano ledger
971#[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	/// Output ID
978	pub utxo_id: UtxoId,
979	/// Epoch number in which the output was produced
980	pub epoch_number: McEpochNumber,
981	/// Block number in which the output was produced
982	pub block_number: McBlockNumber,
983	/// Slot number in which the output was produced
984	pub slot_number: McSlotNumber,
985	/// Index in block of the transaction that produced the output
986	pub tx_index_within_block: McTxIndexInBlock,
987}
988
989/// Key type used for ordering transaction outputs
990///
991/// This ordering key is used in contexts where a common ordering of the data must be used
992/// by all nodes participating in a Partner Chain due to it being subject to consensus.
993#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
994pub struct UtxoInfoOrderingKey {
995	/// Block number on which the output was created
996	pub block_number: McBlockNumber,
997	/// Index within the block of the transaction that created the output
998	pub tx_index_within_block: McTxIndexInBlock,
999	/// Index of the output in the transaction outputs
1000	pub utxo_id_index: UtxoIndex,
1001}
1002
1003impl UtxoInfo {
1004	/// Returns the ordering key for this UTxO
1005	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/// Type of Cardano network
1015///
1016/// Cardano defines two network types:
1017/// - mainnet: the unique, production Cardano network
1018/// - testnet: various public and private testnets. These testnets are further differentiated
1019///            by their respective "testnet magic" numbers.
1020#[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	/// The Cardano mainnet (unique network)
1025	Mainnet,
1026	/// A Cardano testnet
1027	#[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/// Cardano SPO registration data
1043///
1044/// This data describes a single registration done by a Cardano SPO for the sake of being considered
1045/// for selection to the block producing committee on a given Partner Chain.
1046///
1047/// This registration is represented as a UTxO in the Cardano ledger containing a Plutus datum with
1048/// public keys that are being registered, together with signatures that prove the registrant's
1049/// control of these keys.
1050#[derive(Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
1051#[cfg_attr(feature = "serde", derive(Serialize))]
1052pub struct RegistrationData {
1053	/// UTXO that is an input parameter to the registration transaction
1054	pub registration_utxo: UtxoId,
1055	/// Signature confirming the registrant's ownership of `sidechain_pub_key`
1056	pub sidechain_signature: SidechainSignature,
1057	/// Signature confirming the registrant's ownership of the main chain public key used in the registration
1058	pub mainchain_signature: MainchainSignature,
1059	/// Signature confirming the registrant's ownership of `cross_chain_pub_key`
1060	pub cross_chain_signature: CrossChainSignature,
1061	/// Registering SPO's sidechain public key
1062	pub sidechain_pub_key: SidechainPublicKey,
1063	/// Registering SPO's cross-chain public key
1064	pub cross_chain_pub_key: CrossChainPublicKey,
1065	/// Information about the UTxO containing the registration data
1066	pub utxo_info: UtxoInfo,
1067	/// List of inputs to the registration transaction
1068	pub tx_inputs: Vec<UtxoId>,
1069	/// Registering SPO's additional keys
1070	pub keys: CandidateKeys,
1071}
1072
1073/// Information about an Authority Candidate's Registrations at some block.
1074#[derive(Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
1075#[cfg_attr(feature = "serde", derive(Serialize))]
1076pub struct CandidateRegistrations {
1077	/// Stake pool public key of the registering Cardano SPO
1078	pub stake_pool_public_key: StakePoolPublicKey,
1079	/// List of registrations done by the registering Cardano SPO
1080	pub registrations: Vec<RegistrationData>,
1081	/// Stake delegation of the registering Cardano SPO
1082	pub stake_delegation: Option<StakeDelegation>,
1083}
1084
1085impl CandidateRegistrations {
1086	/// Creates a new [CandidateRegistrations] from its members
1087	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	/// Return the stake pool public key of the registering SPO
1096	pub fn mainchain_pub_key(&self) -> &StakePoolPublicKey {
1097		&self.stake_pool_public_key
1098	}
1099
1100	/// Return the list of registrations of the SPO
1101	pub fn registrations(&self) -> &[RegistrationData] {
1102		&self.registrations
1103	}
1104}
1105
1106/// Sr25519 public key used by Aura consensus algorithm. Not validated
1107#[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	/// Attempts to cast this public key to a valid [sr25519::Public]
1114	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/// Ed25519 public key used by the Grandpa finality gadget. Not validated
1132#[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	/// Attempts to cast this public key to a valid [ed25519::Public]
1139	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))]
1169/// Parameter controlling the number and proportion of registered and permissioned candidates
1170/// selected into a Partner Chain committee, used by the Ariadne family of selection algorithms.
1171///
1172/// The core idea behind the D-Param is to enable a Partner Chain to bootstrap its operation by
1173/// relying on a hand-picked set of trusted block producers for security, and to later incrementally
1174/// shift block production onto trustless network participants as the chain grows and it becomes
1175/// harder for malicious actors to manipulate the chain.
1176pub struct DParameter {
1177	/// Expected number of permissioned candidates selected for a committee
1178	pub num_permissioned_candidates: u16,
1179	/// Expected number of registered candidates selected for a committee
1180	pub num_registered_candidates: u16,
1181}
1182
1183impl DParameter {
1184	/// Creates a new [DParameter] from member values
1185	pub fn new(num_permissioned_candidates: u16, num_registered_candidates: u16) -> Self {
1186		Self { num_permissioned_candidates, num_registered_candidates }
1187	}
1188}
1189
1190/// Opaque key bytes with a 4 bytes identifier
1191#[derive(
1192	Clone, PartialEq, Eq, Decode, DecodeWithMemTracking, Encode, TypeInfo, PartialOrd, Ord, Hash,
1193)]
1194#[cfg_attr(feature = "serde", derive(Serialize))]
1195pub struct CandidateKey {
1196	/// Key type id
1197	pub id: [u8; 4],
1198	/// Bytes of the key
1199	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
1226/// Key type id of Partner Chains cross-chain key, used with ECDSA cryptography
1227pub const CROSS_CHAIN_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"crch");
1228
1229impl CandidateKey {
1230	/// Constructor
1231	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))]
1238/// Bytes of CandidateKeys that come from Cardano or other input.
1239pub struct CandidateKeys(pub Vec<CandidateKey>);
1240
1241impl CandidateKeys {
1242	/// Gets copy of key bytes identified by given id
1243	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	/// Gets copy of key bytes identified by given id or empty bytes if key is not present
1250	pub fn find_or_empty(&self, id: KeyTypeId) -> Vec<u8> {
1251		self.find(id).unwrap_or_default()
1252	}
1253
1254	/// True for keys that are only AURA and Grandpa
1255	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
1266/// Backward compatible [Encode] for [CandidateKeys].
1267/// After node update InherentData would be encoded with this version but runtime would still know [PermissionedCandidateData] and [CandidateRegistration]
1268/// that have [AuraPublicKey] and [GrandpaPublicKey] fields instead of [CandidateKeys] field.
1269/// To support existing chains, when keys are AURA and Grandpa, [CandidateKeys] is encoded exactly like in previous version.
1270/// If other key set is used in the place where AURA vector lenght is encoded, Compact(u32::MAX) is encoded to enable discrimination between the types that
1271/// has to be decoded.
1272/// The represtation is either:
1273/// Legacy: AuraKeySize, AuraKeyBytes, GrandpaKeySize, GrandpaKeyBytes
1274/// Generic: u32::MAX, CandidateKeysVector
1275/// AuraKeySize cannot be u32::MAX (won't fit on Cardano), so when it is encountered, we know that the generic representation is used.
1276impl 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			// Compact(u32::MAX) is used to signal that a vector of CandidateKey should be decoded
1292			// It has to be this type, be it is the item that AuraPublicKey::decode expects.
1293			Encode::encode_to(&Compact(u32::MAX), dest);
1294			Encode::encode_to(&self.0, dest)
1295		}
1296	}
1297}
1298
1299/// Custom backward compatibile Decode. See comment on the Encode implementation.
1300impl Decode for CandidateKeys {
1301	fn decode<I: parity_scale_codec::Input>(
1302		input: &mut I,
1303	) -> Result<Self, parity_scale_codec::Error> {
1304		// See Encode instance
1305		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))]
1331/// Information about a permissioned committee member candidate
1332///
1333/// Permissioned candidates are nominated by the Partner Chain's governance authority to be
1334/// eligible for participation in block producer committee without controlling any ADA stake
1335/// on Cardano and registering as SPOs.
1336pub struct PermissionedCandidateData {
1337	/// Sidechain public key of the permissioned candidate
1338	pub sidechain_public_key: SidechainPublicKey,
1339	/// Additional keys of the permissioned candidate
1340	pub keys: CandidateKeys,
1341}
1342
1343/// Parses public keys formatted as PARTNER_CHAINS_KEY:AURA_KEY:GRANDPA_KEY or PARTNER_CHAINS_KEY,KEY_ID_1:KEY_1,...,KEY_ID_N:KEY_N
1344#[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/// Cardano SPO registration. This is a stripped-down version of [RegistrationData].
1414#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1415pub struct CandidateRegistration {
1416	/// Information on ADA stake pool ownership
1417	pub stake_ownership: AdaBasedStaking,
1418	/// Registering SPO's sidechain public key
1419	pub partner_chain_pub_key: SidechainPublicKey,
1420	/// Signature confirming registering SPO's ownership of `partner_chain_pub_key`
1421	pub partner_chain_signature: SidechainSignature,
1422	/// Hash of the registering SPO's Cardano public key
1423	pub own_pkh: MainchainKeyHash,
1424	/// UTxO containing the registration data
1425	pub registration_utxo: UtxoId,
1426	/// Additional keys of the registered candidate
1427	pub keys: CandidateKeys,
1428}
1429
1430impl CandidateRegistration {
1431	/// Checks whether `self` and `other` contain the same keys
1432	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/// Information on ADA stake pool ownership
1441///
1442/// AdaBasedStaking is a variant of Plutus type StakeOwnership. The other variant, TokenBasedStaking, is not supported.
1443#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1444pub struct AdaBasedStaking {
1445	/// Public key of the stake pool operator
1446	pub pub_key: StakePoolPublicKey,
1447	/// Signature confirming ownership of `pub_key`
1448	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)]
1463/// Represents a Cardano ADA delegator
1464pub enum DelegatorKey {
1465	/// Represents a staking address that is controlled by a user delegator
1466	StakeKeyHash([u8; 28]),
1467	/// Represents a staking address that is locked by a Plutus script
1468	ScriptKeyHash {
1469		/// Raw stake address hash
1470		hash_raw: [u8; 28],
1471		/// Hash of the Plutus script controlling the staking address
1472		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/// Amount of Lovelace staked by a Cardano delegator to a single stake pool
1492#[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/// A mapping between Cardano SPOs and the information about ADA delegation of their stake pools
1502///
1503/// This mapping can be used to calculate relative share of the total delegation for the
1504/// purpose of weighing during block producer selection.
1505#[derive(Debug, Clone, Default)]
1506pub struct StakeDistribution(pub BTreeMap<MainchainKeyHash, PoolDelegation>);
1507
1508/// ADA delegation data for a single Cardano SPO
1509#[derive(Debug, Clone, Default, PartialEq)]
1510pub struct PoolDelegation {
1511	/// Total amount delegated to the stake pool
1512	pub total_stake: StakeDelegation,
1513	/// Delegated amount for each delegator of the stake pool
1514	pub delegators: BTreeMap<DelegatorKey, DelegatorStakeAmount>,
1515}
1516
1517/// [FromStr] trait with [FromStr::Err] fixed to a type compatible with `clap`'s `value_parser` macro.
1518pub 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		// ensures that Decode of CandidateKeys does not consume the whole input
1610		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}