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;
9pub mod crypto;
10pub mod mainchain_epoch;
11
12extern crate alloc;
13extern crate core;
14extern crate num_derive;
15
16pub use alloc::collections::btree_map::BTreeMap;
17#[cfg(feature = "std")]
18use alloc::format;
19pub use alloc::vec::Vec;
20use alloc::{str::FromStr, string::String, string::ToString, vec};
21use byte_string_derive::byte_string;
22use core::{
23	fmt::{Display, Formatter},
24	ops::Deref,
25	u32,
26};
27use crypto::blake2b;
28use derive_more::{From, Into};
29use num_derive::*;
30use parity_scale_codec::{
31	Compact, Decode, DecodeWithMemTracking, Encode, MaxEncodedLen, WrapperTypeEncode,
32	decode_vec_with_len,
33};
34use plutus_datum_derive::*;
35use scale_info::TypeInfo;
36use sp_core::{
37	ConstU32,
38	bounded::BoundedVec,
39	crypto::{
40		KeyTypeId,
41		key_types::{AURA, GRANDPA},
42	},
43	ecdsa, ed25519, sr25519,
44};
45#[cfg(feature = "serde")]
46use {
47	derive_more::FromStr,
48	serde::{Deserialize, Deserializer, Serialize, Serializer},
49};
50
51/// The number of main chain epochs back a Partner Chain queries for committee selection inputs.
52/// This offset is necessary to ensure that data is present and stable.
53pub const DATA_MC_EPOCH_OFFSET: u32 = 2;
54
55/// Shifts given epoch back by [DATA_MC_EPOCH_OFFSET] accounting for underflow.
56pub fn offset_data_epoch(epoch: &McEpochNumber) -> Result<McEpochNumber, u32> {
57	Ok(McEpochNumber(epoch.0.checked_sub(DATA_MC_EPOCH_OFFSET).ok_or(DATA_MC_EPOCH_OFFSET)?))
58}
59
60#[derive(
61	Default,
62	Debug,
63	Copy,
64	Clone,
65	PartialEq,
66	Eq,
67	Encode,
68	Decode,
69	DecodeWithMemTracking,
70	Hash,
71	TypeInfo,
72	Ord,
73	PartialOrd,
74)]
75#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, FromStr))]
76/// Cardano epoch number. In range [0, 2^31-1].
77pub struct McEpochNumber(pub u32);
78
79impl McEpochNumber {
80	/// Returns next Cardano epoch number
81	pub fn next(&self) -> Self {
82		Self(&self.0 + 1)
83	}
84}
85
86impl Display for McEpochNumber {
87	fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
88		u32::fmt(&self.0, f)
89	}
90}
91#[derive(
92	Default,
93	Clone,
94	Copy,
95	Debug,
96	Encode,
97	Decode,
98	DecodeWithMemTracking,
99	TypeInfo,
100	PartialEq,
101	Eq,
102	PartialOrd,
103	Ord,
104)]
105#[cfg_attr(feature = "serde", derive(Serialize))]
106/// Amount of Lovelace (which is a fraction of 1 ADA) staked/locked on Cardano
107pub struct StakeDelegation(pub u64);
108
109impl StakeDelegation {
110	/// Checks if stake delegation is zero
111	pub fn is_zero(&self) -> bool {
112		self.0 == 0
113	}
114}
115
116#[derive(
117	Default,
118	Clone,
119	Copy,
120	Debug,
121	Encode,
122	Decode,
123	DecodeWithMemTracking,
124	TypeInfo,
125	PartialEq,
126	Eq,
127	From,
128	MaxEncodedLen,
129)]
130#[cfg_attr(feature = "serde", derive(Serialize))]
131/// The amount of a Cardano native token
132pub struct NativeTokenAmount(pub u128);
133
134impl Display for NativeTokenAmount {
135	fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
136		u128::fmt(&self.0, f)
137	}
138}
139
140#[derive(
141	Default,
142	Clone,
143	Copy,
144	Debug,
145	Encode,
146	Decode,
147	DecodeWithMemTracking,
148	PartialEq,
149	Eq,
150	PartialOrd,
151	Ord,
152	scale_info::TypeInfo,
153	MaxEncodedLen,
154)]
155#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, FromStr))]
156/// Cardano block number. In range [0, 2^31-1].
157pub struct McBlockNumber(pub u32);
158
159impl Display for McBlockNumber {
160	fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
161		u32::fmt(&self.0, f)
162	}
163}
164
165#[derive(
166	Default,
167	Debug,
168	Copy,
169	Clone,
170	PartialEq,
171	Eq,
172	PartialOrd,
173	Encode,
174	Decode,
175	DecodeWithMemTracking,
176	TypeInfo,
177	Hash,
178)]
179#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, FromStr))]
180/// Cardano slot number. In range [0, 2^63-1].
181pub struct McSlotNumber(pub u64);
182
183impl Display for McSlotNumber {
184	fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
185		u64::fmt(&self.0, f)
186	}
187}
188
189#[derive(
190	Default,
191	Debug,
192	Copy,
193	Clone,
194	PartialEq,
195	Eq,
196	Encode,
197	Decode,
198	DecodeWithMemTracking,
199	TypeInfo,
200	Hash,
201	MaxEncodedLen,
202)]
203#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, FromStr))]
204/// Partner Chain slot number
205pub struct ScSlotNumber(pub u64);
206
207/// Data describing a Cardano block
208#[derive(Debug, Clone, PartialEq, Default)]
209#[cfg_attr(feature = "serde", derive(serde::Serialize))]
210pub struct MainchainBlock {
211	/// Block number
212	pub number: McBlockNumber,
213	/// Block hash
214	pub hash: McBlockHash,
215	/// Block's epoch number
216	pub epoch: McEpochNumber,
217	/// Block's slot number
218	pub slot: McSlotNumber,
219	/// Block timestamp
220	pub timestamp: u64, // seconds since UNIX_EPOCH
221}
222
223#[derive(
224	Default,
225	Debug,
226	Copy,
227	Clone,
228	PartialEq,
229	Eq,
230	Encode,
231	Decode,
232	DecodeWithMemTracking,
233	PartialOrd,
234	Ord,
235	TypeInfo,
236	MaxEncodedLen,
237)]
238#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
239/// An index of transaction in a block. In range [0, 2^31-1].
240pub struct McTxIndexInBlock(pub u32);
241
242#[cfg(feature = "serde")]
243impl FromStr for McTxIndexInBlock {
244	type Err = sp_std::num::ParseIntError;
245	fn from_str(s: &str) -> Result<Self, Self::Err> {
246		let parsed = u32::from_str(s)?;
247		let _check_overflow = i32::from_str(s)?;
248		Ok(Self(parsed))
249	}
250}
251
252/// Maximum length of a Cardano address in UTF-8 bytes
253const MAX_MAINCHAIN_ADDRESS_BYTES: u32 = 120;
254
255/// Wraps UTF-8 bytes of Mainchain Address in bech32 format.
256/// Example: utf-8 bytes of "addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc" are
257/// "0x616464725f7465737431777a35716337666b327061743030353877347a77766b77333579747074656a336e7563336a65326b6774616e356471337274347363"
258#[derive(
259	Clone, Default, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen,
260)]
261#[byte_string(debug)]
262pub struct MainchainAddress(BoundedVec<u8, ConstU32<MAX_MAINCHAIN_ADDRESS_BYTES>>);
263
264impl MainchainAddress {
265	/// Returns raw bytes of this Cardano address
266	pub fn bytes(&self) -> Vec<u8> {
267		self.0.to_vec()
268	}
269}
270
271#[cfg(feature = "serde")]
272impl FromStr for MainchainAddress {
273	type Err = &'static str;
274
275	fn from_str(s: &str) -> Result<Self, Self::Err> {
276		let bytes: Vec<u8> = s.as_bytes().to_vec();
277		let bounded = BoundedVec::try_from(bytes).map_err(|_| "Invalid length")?;
278		Ok(MainchainAddress(bounded))
279	}
280}
281
282impl Display for MainchainAddress {
283	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
284		let s = String::from_utf8(self.0.to_vec())
285			.expect("MainchainAddressString is always properly encoded UTF-8");
286		write!(f, "{}", s)
287	}
288}
289
290#[cfg(feature = "serde")]
291impl serde::Serialize for MainchainAddress {
292	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
293	where
294		S: serde::Serializer,
295	{
296		let s = String::from_utf8(self.0.to_vec()).expect("MainchainAddress is always valid UTF-8");
297		serializer.serialize_str(&s)
298	}
299}
300
301#[cfg(feature = "serde")]
302impl<'de> serde::Deserialize<'de> for MainchainAddress {
303	/// Deserialized MainchainAddress from both hexstring of ASCII bytes and plain String
304	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
305	where
306		D: serde::Deserializer<'de>,
307	{
308		let s = String::deserialize(deserializer)?;
309		let bytes = sp_core::bytes::from_hex(&s).unwrap_or_else(|_| s.as_bytes().to_vec());
310		let bounded = BoundedVec::try_from(bytes)
311			.map_err(|_| serde::de::Error::custom("MainchainAddress is too long"))?;
312		Ok(MainchainAddress(bounded))
313	}
314}
315
316/// Cardano Policy Id is a 224 bits blake2b hash.
317const POLICY_ID_LEN: usize = 28;
318#[derive(
319	Clone,
320	Default,
321	PartialEq,
322	Eq,
323	Encode,
324	Decode,
325	DecodeWithMemTracking,
326	ToDatum,
327	TypeInfo,
328	MaxEncodedLen,
329	Hash,
330)]
331#[byte_string(debug, decode_hex, hex_serialize, hex_deserialize)]
332#[cfg_attr(feature = "std", byte_string(to_hex_string))]
333/// Cardano Policy Id
334pub struct PolicyId(pub [u8; POLICY_ID_LEN]);
335
336#[cfg(feature = "std")]
337impl Display for PolicyId {
338	fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
339		write!(f, "{}", self.to_hex_string())
340	}
341}
342
343/// Cardano script hash
344pub type ScriptHash = PolicyId;
345
346/// Maximum length of a Cardano native asset's name in UTF-8 bytes
347pub const MAX_ASSET_NAME_LEN: u32 = 32;
348
349/// Cardano native asset name
350#[derive(
351	Clone, Default, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen,
352)]
353#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
354#[cfg_attr(feature = "std", byte_string(to_hex_string))]
355pub struct AssetName(pub BoundedVec<u8, ConstU32<MAX_ASSET_NAME_LEN>>);
356
357impl AssetName {
358	/// Constructs an empty [AssetName]
359	pub fn empty() -> Self {
360		Self(BoundedVec::new())
361	}
362}
363
364#[cfg(feature = "std")]
365impl Display for AssetName {
366	fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
367		write!(f, "{}", self.to_hex_string())
368	}
369}
370
371#[derive(
372	Clone,
373	Debug,
374	PartialEq,
375	Eq,
376	Encode,
377	Decode,
378	DecodeWithMemTracking,
379	TypeInfo,
380	MaxEncodedLen,
381	Default,
382)]
383#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
384/// Full data identifying a Cardano native asset
385pub struct AssetId {
386	/// Policy ID
387	pub policy_id: PolicyId,
388	/// Asset name
389	pub asset_name: AssetName,
390}
391
392#[cfg(feature = "std")]
393impl FromStr for AssetId {
394	type Err = String;
395
396	fn from_str(s: &str) -> Result<Self, Self::Err> {
397		match s.split_once(".") {
398			Some((policy_id, asset_name)) => {
399				let policy_id = PolicyId::from_str(policy_id)
400					.map_err(|e| format!("{} is invalid Policy ID: {}", policy_id, e))?;
401				let asset_name = AssetName::from_str(asset_name)
402					.map_err(|e| format!("{} is invalid Asset Name: {}", asset_name, e))?;
403				Ok(Self { policy_id, asset_name })
404			},
405			None => {
406				Err("AssetId should be <hex encoded Policy ID>.<hex encoded Asset Name>"
407					.to_string())
408			},
409		}
410	}
411}
412
413/// Length of Cardano stake pool key
414const STAKE_POOL_PUBLIC_KEY_LEN: usize = 32;
415
416#[derive(
417	Clone,
418	PartialEq,
419	Eq,
420	Encode,
421	Decode,
422	DecodeWithMemTracking,
423	TypeInfo,
424	MaxEncodedLen,
425	Hash,
426	Ord,
427	PartialOrd,
428)]
429#[cfg_attr(feature = "std", byte_string(to_hex_string))]
430#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
431/// Cardano stake pool public key (ed25519)
432pub struct StakePoolPublicKey(pub [u8; STAKE_POOL_PUBLIC_KEY_LEN]);
433
434impl StakePoolPublicKey {
435	/// Computes the blake2b_224 hash of this Cardano stake pool public key
436	pub fn hash(&self) -> MainchainKeyHash {
437		MainchainKeyHash::from_vkey(&self.0)
438	}
439}
440
441impl TryFrom<Vec<u8>> for StakePoolPublicKey {
442	type Error = &'static str;
443
444	fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
445		<[u8; 32]>::try_from(value)
446			.map_err(|_| "Mainchain public key must be 32 bytes long")
447			.map(StakePoolPublicKey)
448	}
449}
450
451/// Length of Cardano staking public key
452const STAKE_PUBLIC_KEY_LEN: usize = 32;
453
454/// Cardano staking public key (ed25519)
455#[derive(
456	Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen, Hash,
457)]
458#[cfg_attr(feature = "std", byte_string(to_hex_string))]
459#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
460pub struct StakePublicKey(pub [u8; STAKE_PUBLIC_KEY_LEN]);
461
462impl StakePublicKey {
463	/// Computes the blake2b_224 hash of this Cardano staking public key
464	pub fn hash(&self) -> MainchainKeyHash {
465		MainchainKeyHash(blake2b(&self.0))
466	}
467}
468
469/// Length of Cardano key hash
470const MAINCHAIN_KEY_HASH_LEN: usize = 28;
471
472#[derive(
473	Clone,
474	Copy,
475	Decode,
476	DecodeWithMemTracking,
477	Default,
478	Encode,
479	Hash,
480	MaxEncodedLen,
481	Eq,
482	PartialEq,
483	Ord,
484	PartialOrd,
485	TypeInfo,
486)]
487#[byte_string(debug)]
488#[cfg_attr(feature = "std", byte_string(to_hex_string, decode_hex))]
489#[cfg_attr(feature = "serde", byte_string(hex_serialize, hex_deserialize))]
490/// blake2b_224 hash of a Cardano Verification (Public) Key.
491/// It can be a hash of Payment Verification, Payment Extended Verification, Stake Pool Verification Key or Staking Verification Key.
492pub struct MainchainKeyHash(pub [u8; MAINCHAIN_KEY_HASH_LEN]);
493
494impl Display for MainchainKeyHash {
495	fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
496		let hash = sp_core::hexdisplay::HexDisplay::from(&self.0);
497		write!(f, "0x{}", hash)
498	}
499}
500
501impl MainchainKeyHash {
502	/// Computes the blake2b_224 hash of the given ed25519 public key bytes
503	pub fn from_vkey(vkey: &[u8; 32]) -> Self {
504		Self(blake2b(vkey))
505	}
506}
507
508/// Length of Cardano signature (EDDSA)
509pub const MAINCHAIN_SIGNATURE_LEN: usize = 64;
510
511#[derive(Clone, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
512#[cfg_attr(feature = "std", byte_string(to_hex_string))]
513#[byte_string(debug, hex_serialize, decode_hex)]
514/// Cardano signature type (EDDSA)
515///
516/// WARNING: This type needs to be backwards compatible with a legacy schema wrapping `Vec<u8>`.
517///          Because of this, it is not handled correctly by PolkadotJS. If you need to accept
518///          this type as extrinsic argument, use raw `[u8; MAINCHAIN_SIGNATURE_LEN]` instead.
519pub struct MainchainSignature(pub [u8; MAINCHAIN_SIGNATURE_LEN]);
520
521impl From<[u8; MAINCHAIN_SIGNATURE_LEN]> for MainchainSignature {
522	fn from(raw: [u8; MAINCHAIN_SIGNATURE_LEN]) -> Self {
523		Self(raw)
524	}
525}
526
527impl WrapperTypeEncode for MainchainSignature {}
528impl Deref for MainchainSignature {
529	type Target = [u8];
530
531	fn deref(&self) -> &Self::Target {
532		&self.0
533	}
534}
535impl Decode for MainchainSignature {
536	fn decode<I: parity_scale_codec::Input>(
537		input: &mut I,
538	) -> Result<Self, parity_scale_codec::Error> {
539		let vec: Vec<u8> = Decode::decode(input)?;
540		let arr = vec.try_into().map_err(|_| "Incorrect MainchainSignature size")?;
541		Ok(MainchainSignature(arr))
542	}
543}
544
545impl MainchainSignature {
546	/// Verifies whether `self` is a valid signature of `signed_message` for `public_key`
547	pub fn verify(&self, public_key: &StakePoolPublicKey, signed_message: &[u8]) -> bool {
548		let mainchain_signature = ed25519::Signature::from(self.0);
549
550		sp_io::crypto::ed25519_verify(
551			&mainchain_signature,
552			signed_message,
553			&ed25519::Public::from(public_key.0),
554		)
555	}
556}
557
558/// Length of Cardano staking key signature (EDDSA)
559pub const STAKE_KEY_SIGNATURE_LEN: usize = 64;
560
561#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, Hash, TypeInfo)]
562#[byte_string(debug, hex_serialize, decode_hex)]
563/// Cardano staking key signature type (EDDSA)
564pub struct StakeKeySignature(pub [u8; STAKE_KEY_SIGNATURE_LEN]);
565
566impl From<[u8; STAKE_KEY_SIGNATURE_LEN]> for StakeKeySignature {
567	fn from(raw: [u8; STAKE_KEY_SIGNATURE_LEN]) -> Self {
568		Self(raw)
569	}
570}
571
572impl StakeKeySignature {
573	/// Verifies whether `self` is a valid signature of `message` for `public_key`
574	pub fn verify(&self, public_key: &StakePublicKey, message: &[u8]) -> bool {
575		let signature = ed25519::Signature::from(self.0);
576		sp_io::crypto::ed25519_verify(&signature, message, &ed25519::Public::from(public_key.0))
577	}
578}
579
580#[derive(
581	Clone,
582	Copy,
583	Debug,
584	Encode,
585	Decode,
586	DecodeWithMemTracking,
587	PartialEq,
588	TypeInfo,
589	ToDatum,
590	MaxEncodedLen,
591	Default,
592	PartialOrd,
593	Ord,
594	Eq,
595	Zero,
596	One,
597	NumOps,
598	Num,
599	From,
600	Into,
601)]
602#[cfg_attr(feature = "serde", derive(Serialize))]
603/// Partner Chain epoch number
604pub struct ScEpochNumber(pub u64);
605
606impl Display for ScEpochNumber {
607	fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
608		u64::fmt(&self.0, f)
609	}
610}
611
612impl ScEpochNumber {
613	/// Returns next epoch number
614	pub fn next(&self) -> Self {
615		Self(self.0 + 1)
616	}
617	/// Returns previous epoch number accounting for underflow
618	pub fn prev(&self) -> Option<Self> {
619		self.0.checked_sub(1).map(Self)
620	}
621}
622
623#[derive(
624	Clone,
625	PartialEq,
626	Eq,
627	Encode,
628	Decode,
629	DecodeWithMemTracking,
630	ToDatum,
631	TypeInfo,
632	PartialOrd,
633	Ord,
634	Hash,
635)]
636#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex, as_ref)]
637/// Partner Chain public key
638///
639/// This public key is used as the identity of a Partner Chain network participant on a specific Partner Chain,
640/// ie. a network participant can use different [SidechainPublicKey] for each Partner Chain they are active on
641/// as opposed to [CrossChainPublicKey].
642pub struct SidechainPublicKey(pub Vec<u8>);
643
644impl From<ecdsa::Public> for SidechainPublicKey {
645	fn from(value: ecdsa::Public) -> Self {
646		Self(value.0.to_vec())
647	}
648}
649
650/// CBOR bytes of Cardano Transaction.
651#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
652#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
653pub struct TransactionCbor(pub Vec<u8>);
654
655/// CBOR bytes of Cardano VKeyWitness.
656#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
657#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
658pub struct VKeyWitnessCbor(pub Vec<u8>);
659
660/// Cross-chain signature type (ECDSA) created using [SidechainPublicKey]
661#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
662#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
663pub struct SidechainSignature(pub Vec<u8>);
664
665/// Cross-chain public key (ECDSA)
666///
667/// This public key is used as the universal identity of Partner Chain network participants across all Partner Chains.
668#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
669#[byte_string(debug, hex_serialize, hex_deserialize)]
670pub struct CrossChainPublicKey(pub Vec<u8>);
671
672impl CrossChainPublicKey {
673	/// Computes the blake2b_224 hash of this cross-chain public key
674	pub fn hash(&self) -> CrossChainKeyHash {
675		CrossChainKeyHash(blake2b(&self.0))
676	}
677}
678
679impl From<k256::PublicKey> for CrossChainPublicKey {
680	fn from(value: k256::PublicKey) -> Self {
681		Self(value.to_sec1_bytes().to_vec())
682	}
683}
684
685impl From<CrossChainPublicKey> for k256::PublicKey {
686	fn from(value: CrossChainPublicKey) -> Self {
687		k256::PublicKey::from_sec1_bytes(&value.0)
688			.expect("CrossChainPublicKey converts to valid secp256k1::PublicKey")
689	}
690}
691
692/// Length of the cross-chain public key hash
693const CROSS_CHAIN_KEY_HASH_LEN: usize = 28;
694
695#[derive(
696	Clone,
697	Copy,
698	Decode,
699	DecodeWithMemTracking,
700	Default,
701	Encode,
702	Hash,
703	MaxEncodedLen,
704	Eq,
705	PartialEq,
706	Ord,
707	PartialOrd,
708	TypeInfo,
709)]
710#[byte_string(debug, to_hex_string)]
711#[cfg_attr(feature = "std", byte_string(decode_hex))]
712#[cfg_attr(feature = "serde", byte_string(hex_serialize, hex_deserialize))]
713/// blake2b_224 hash of a cross-chain public key
714pub struct CrossChainKeyHash(pub [u8; CROSS_CHAIN_KEY_HASH_LEN]);
715
716impl Display for CrossChainKeyHash {
717	fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
718		f.write_str(&self.to_hex_string())
719	}
720}
721
722/// Cross-chain signature created using [CrossChainPublicKey]
723#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
724#[byte_string(debug, hex_serialize)]
725pub struct CrossChainSignature(pub Vec<u8>);
726
727impl CrossChainSignature {
728	/// Verifies that `self` is a valid signature of `data` for `cross_chain_pubkey`
729	pub fn verify(
730		&self,
731		cross_chain_pubkey: &CrossChainPublicKey,
732		data: &[u8],
733	) -> Result<(), k256::ecdsa::signature::Error> {
734		use k256::ecdsa::signature::Verifier;
735
736		let vkey = k256::ecdsa::VerifyingKey::from_sec1_bytes(&cross_chain_pubkey.0[..])?;
737		let signature = k256::ecdsa::Signature::from_slice(&self.0[..])?;
738		vkey.verify(data, &signature)
739	}
740}
741
742/// Length of Cardano epoch nonce
743const EPOCH_NONCE_LEN: usize = 32;
744
745/// Cardano epoch nonce
746///
747/// This value is a 32-byte hash generated at the start of each epoch on Cardano using
748/// a verifiable random function as part of normal chain operation by Cardano block producers.
749/// Because it is subject to Cardano's consensus mechanism and has strong cryptographic guarantees,
750/// this value can be used as a tamper-proof shared randomness seed by Partner Chain Toolkit components.
751#[derive(Default, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
752#[byte_string(debug, hex_serialize)]
753pub struct EpochNonce(pub Vec<u8>);
754
755impl EpochNonce {
756	/// Returns epoch nonce as byte array.
757	pub fn as_array(&self) -> [u8; EPOCH_NONCE_LEN] {
758		let mut epoch_nonce = self.0.clone();
759		epoch_nonce.resize_with(32, || 0);
760		epoch_nonce.try_into().expect("Should never fail after being resized")
761	}
762}
763
764#[derive(
765	Default,
766	Debug,
767	Copy,
768	Clone,
769	PartialEq,
770	Eq,
771	Encode,
772	Decode,
773	DecodeWithMemTracking,
774	ToDatum,
775	TypeInfo,
776	MaxEncodedLen,
777	Hash,
778)]
779/// Identifies a Cardano UTxO (unspent transaction output)
780///
781/// A UTxO is uniquely identified by the hash of the transaction that produced it and its (zero-based)
782/// index in the transaction's output.
783///
784/// Standard semi-human-readable encoding of a UTxO id uses a hash sign to divide the two components:
785/// `0000000000000000000000000000000000000000000000000000000000000000#0`
786pub struct UtxoId {
787	/// Transaction hash
788	pub tx_hash: McTxHash,
789	/// Output index
790	pub index: UtxoIndex,
791}
792
793impl UtxoId {
794	/// Creates new [UtxoId] from primitive type arguments
795	pub const fn new(hash: [u8; TX_HASH_SIZE], index: u16) -> UtxoId {
796		UtxoId { tx_hash: McTxHash(hash), index: UtxoIndex(index) }
797	}
798}
799
800#[cfg(feature = "serde")]
801impl Serialize for UtxoId {
802	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
803	where
804		S: Serializer,
805	{
806		serializer.serialize_str(&self.to_string())
807	}
808}
809
810#[cfg(feature = "serde")]
811impl<'de> Deserialize<'de> for UtxoId {
812	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
813	where
814		D: Deserializer<'de>,
815	{
816		alloc::string::String::deserialize(deserializer).and_then(|string| {
817			Self::from_str(&string).map_err(|err| serde::de::Error::custom(err.to_string()))
818		})
819	}
820}
821
822#[cfg(feature = "serde")]
823impl FromStr for UtxoId {
824	type Err = &'static str;
825
826	fn from_str(s: &str) -> Result<Self, Self::Err> {
827		let split: Vec<&str> = s.split('#').collect();
828		let &[hash_str, index_str] = split.as_slice() else {
829			return Err("UtxoId string must conform to format: '<hash>#<index>'");
830		};
831
832		Ok(UtxoId {
833			tx_hash: McTxHash::from_str(hash_str)
834				.map_err(|_| "invalid string input for McTxHash")?,
835			index: UtxoIndex::from_str(index_str)
836				.map_err(|_| "invalid string input for OutputIndex")?,
837		})
838	}
839}
840
841impl Display for UtxoId {
842	fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
843		let hash = sp_core::hexdisplay::HexDisplay::from(&self.tx_hash.0);
844		write!(f, "{}#{}", hash, self.index.0)
845	}
846}
847
848#[derive(
849	Default,
850	Debug,
851	Copy,
852	Clone,
853	PartialEq,
854	Eq,
855	Encode,
856	Decode,
857	DecodeWithMemTracking,
858	PartialOrd,
859	Ord,
860	ToDatum,
861	TypeInfo,
862	MaxEncodedLen,
863	Hash,
864)]
865#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
866/// An index of output of a transaction. In range [0, 2^15-1].
867pub struct UtxoIndex(pub u16);
868
869#[cfg(feature = "serde")]
870impl FromStr for UtxoIndex {
871	type Err = sp_std::num::ParseIntError;
872	fn from_str(s: &str) -> Result<Self, Self::Err> {
873		let parsed = u16::from_str(s)?;
874		let _check_overflow = i16::from_str(s)?;
875		Ok(Self(parsed))
876	}
877}
878
879/// Size of a Cardano transaction hash
880pub const TX_HASH_SIZE: usize = 32;
881
882#[derive(
883	Default,
884	Copy,
885	Clone,
886	Hash,
887	PartialEq,
888	Eq,
889	Encode,
890	Decode,
891	DecodeWithMemTracking,
892	ToDatum,
893	TypeInfo,
894	MaxEncodedLen,
895)]
896#[byte_string(debug, from_bytes, decode_hex, hex_serialize, hex_deserialize)]
897#[constructor_datum]
898/// Cardano transaction hash
899///
900/// This hash uniquely identifies a transaction in the Cardano ledger.
901pub struct McTxHash(pub [u8; TX_HASH_SIZE]);
902
903impl TryFrom<Vec<u8>> for McTxHash {
904	type Error = &'static str;
905
906	fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
907		<[u8; 32]>::try_from(value)
908			.map_err(|_| "McTxHash must be 32 bytes long")
909			.map(McTxHash)
910	}
911}
912
913#[derive(
914	Default,
915	Clone,
916	Decode,
917	DecodeWithMemTracking,
918	Encode,
919	PartialEq,
920	Eq,
921	TypeInfo,
922	MaxEncodedLen,
923	Hash,
924)]
925#[byte_string(debug, decode_hex, hex_serialize, hex_deserialize)]
926/// Cardano block hash
927///
928/// This hash uniquely identifies a Cardano block
929pub struct McBlockHash(pub [u8; 32]);
930
931impl Display for McBlockHash {
932	fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
933		let hash = sp_core::hexdisplay::HexDisplay::from(&self.0);
934		write!(f, "{}", hash)
935	}
936}
937
938/// Extended information about a UTxO in Cardano ledger
939#[derive(
940	Default, Debug, Copy, Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo,
941)]
942#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
943#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
944pub struct UtxoInfo {
945	/// Output ID
946	pub utxo_id: UtxoId,
947	/// Epoch number in which the output was produced
948	pub epoch_number: McEpochNumber,
949	/// Block number in which the output was produced
950	pub block_number: McBlockNumber,
951	/// Slot number in which the output was produced
952	pub slot_number: McSlotNumber,
953	/// Index in block of the transaction that produced the output
954	pub tx_index_within_block: McTxIndexInBlock,
955}
956
957/// Key type used for ordering transaction outputs
958///
959/// This ordering key is used in contexts where a common ordering of the data must be used
960/// by all nodes participating in a Partner Chain due to it being subject to consensus.
961#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
962pub struct UtxoInfoOrderingKey {
963	/// Block number on which the output was created
964	pub block_number: McBlockNumber,
965	/// Index within the block of the transaction that created the output
966	pub tx_index_within_block: McTxIndexInBlock,
967	/// Index of the output in the transaction outputs
968	pub utxo_id_index: UtxoIndex,
969}
970
971impl UtxoInfo {
972	/// Returns the ordering key for this UTxO
973	pub fn ordering_key(&self) -> UtxoInfoOrderingKey {
974		UtxoInfoOrderingKey {
975			block_number: self.block_number,
976			tx_index_within_block: self.tx_index_within_block,
977			utxo_id_index: self.utxo_id.index,
978		}
979	}
980}
981
982/// Type of Cardano network
983///
984/// Cardano defines two network types:
985/// - mainnet: the unique, production Cardano network
986/// - testnet: various public and private testnets. These testnets are further differentiated
987///            by their respective "testnet magic" numbers.
988#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
989#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
990#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
991pub enum NetworkType {
992	/// The Cardano mainnet (unique network)
993	Mainnet,
994	/// A Cardano testnet
995	#[default]
996	Testnet,
997}
998
999#[cfg(feature = "std")]
1000impl std::fmt::Display for NetworkType {
1001	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1002		let str = match self {
1003			Self::Mainnet => "mainnet",
1004			Self::Testnet => "testnet",
1005		};
1006		write!(f, "{}", str)
1007	}
1008}
1009
1010/// Cardano SPO registration data
1011///
1012/// This data describes a single registration done by a Cardano SPO for the sake of being considered
1013/// for selection to the block producing committee on a given Partner Chain.
1014///
1015/// This registration is represented as a UTxO in the Cardano ledger containing a Plutus datum with
1016/// public keys that are being registered, together with signatures that prove the registrant's
1017/// control of these keys.
1018#[derive(Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
1019#[cfg_attr(feature = "serde", derive(Serialize))]
1020pub struct RegistrationData {
1021	/// UTXO that is an input parameter to the registration transaction
1022	pub registration_utxo: UtxoId,
1023	/// Signature confirming the registrant's ownership of `sidechain_pub_key`
1024	pub sidechain_signature: SidechainSignature,
1025	/// Signature confirming the registrant's ownership of the main chain public key used in the registration
1026	pub mainchain_signature: MainchainSignature,
1027	/// Signature confirming the registrant's ownership of `cross_chain_pub_key`
1028	pub cross_chain_signature: CrossChainSignature,
1029	/// Registering SPO's sidechain public key
1030	pub sidechain_pub_key: SidechainPublicKey,
1031	/// Registering SPO's cross-chain public key
1032	pub cross_chain_pub_key: CrossChainPublicKey,
1033	/// Information about the UTxO containing the registration data
1034	pub utxo_info: UtxoInfo,
1035	/// List of inputs to the registration transaction
1036	pub tx_inputs: Vec<UtxoId>,
1037	/// Registering SPO's additional keys
1038	pub keys: CandidateKeys,
1039}
1040
1041/// Information about an Authority Candidate's Registrations at some block.
1042#[derive(Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo)]
1043#[cfg_attr(feature = "serde", derive(Serialize))]
1044pub struct CandidateRegistrations {
1045	/// Stake pool public key of the registering Cardano SPO
1046	pub stake_pool_public_key: StakePoolPublicKey,
1047	/// List of registrations done by the registering Cardano SPO
1048	pub registrations: Vec<RegistrationData>,
1049	/// Stake delegation of the registering Cardano SPO
1050	pub stake_delegation: Option<StakeDelegation>,
1051}
1052
1053impl CandidateRegistrations {
1054	/// Creates a new [CandidateRegistrations] from its members
1055	pub fn new(
1056		stake_pool_public_key: StakePoolPublicKey,
1057		stake_delegation: Option<StakeDelegation>,
1058		registrations: Vec<RegistrationData>,
1059	) -> Self {
1060		Self { stake_pool_public_key, registrations, stake_delegation }
1061	}
1062
1063	/// Return the stake pool public key of the registering SPO
1064	pub fn mainchain_pub_key(&self) -> &StakePoolPublicKey {
1065		&self.stake_pool_public_key
1066	}
1067
1068	/// Return the list of registrations of the SPO
1069	pub fn registrations(&self) -> &[RegistrationData] {
1070		&self.registrations
1071	}
1072}
1073
1074/// Sr25519 public key used by Aura consensus algorithm. Not validated
1075#[derive(
1076	Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialOrd, Ord, Hash,
1077)]
1078#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
1079pub struct AuraPublicKey(pub Vec<u8>);
1080impl AuraPublicKey {
1081	/// Attempts to cast this public key to a valid [sr25519::Public]
1082	pub fn try_into_sr25519(&self) -> Option<sr25519::Public> {
1083		Some(sr25519::Public::from_raw(self.0.clone().try_into().ok()?))
1084	}
1085}
1086
1087impl From<sr25519::Public> for AuraPublicKey {
1088	fn from(value: sr25519::Public) -> Self {
1089		Self(value.0.to_vec())
1090	}
1091}
1092
1093impl From<AuraPublicKey> for CandidateKey {
1094	fn from(value: AuraPublicKey) -> Self {
1095		Self { id: AURA.0, bytes: value.0 }
1096	}
1097}
1098
1099/// Ed25519 public key used by the Grandpa finality gadget. Not validated
1100#[derive(
1101	Clone, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialOrd, Ord, Hash,
1102)]
1103#[byte_string(debug, hex_serialize, hex_deserialize, decode_hex)]
1104pub struct GrandpaPublicKey(pub Vec<u8>);
1105impl GrandpaPublicKey {
1106	/// Attempts to cast this public key to a valid [ed25519::Public]
1107	pub fn try_into_ed25519(&self) -> Option<ed25519::Public> {
1108		Some(ed25519::Public::from_raw(self.0.clone().try_into().ok()?))
1109	}
1110}
1111
1112impl From<ed25519::Public> for GrandpaPublicKey {
1113	fn from(value: ed25519::Public) -> Self {
1114		Self(value.0.to_vec())
1115	}
1116}
1117
1118impl From<GrandpaPublicKey> for CandidateKey {
1119	fn from(value: GrandpaPublicKey) -> Self {
1120		Self { id: GRANDPA.0, bytes: value.0 }
1121	}
1122}
1123
1124#[derive(
1125	Debug,
1126	Clone,
1127	PartialEq,
1128	Decode,
1129	DecodeWithMemTracking,
1130	Encode,
1131	MaxEncodedLen,
1132	TypeInfo,
1133	Eq,
1134	Hash,
1135)]
1136#[cfg_attr(feature = "serde", derive(Serialize))]
1137/// Parameter controlling the number and proportion of registered and permissioned candidates
1138/// selected into a Partner Chain committee, used by the Ariadne family of selection algorithms.
1139///
1140/// The core idea behind the D-Param is to enable a Partner Chain to bootstrap its operation by
1141/// relying on a hand-picked set of trusted block producers for security, and to later incrementally
1142/// shift block production onto trustless network participants as the chain grows and it becomes
1143/// harder for malicious actors to manipulate the chain.
1144pub struct DParameter {
1145	/// Expected number of permissioned candidates selected for a committee
1146	pub num_permissioned_candidates: u16,
1147	/// Expected number of registered candidates selected for a committee
1148	pub num_registered_candidates: u16,
1149}
1150
1151impl DParameter {
1152	/// Creates a new [DParameter] from member values
1153	pub fn new(num_permissioned_candidates: u16, num_registered_candidates: u16) -> Self {
1154		Self { num_permissioned_candidates, num_registered_candidates }
1155	}
1156}
1157
1158/// Opaque key bytes with a 4 bytes identifier
1159#[derive(
1160	Debug,
1161	Clone,
1162	PartialEq,
1163	Eq,
1164	Decode,
1165	DecodeWithMemTracking,
1166	Encode,
1167	TypeInfo,
1168	PartialOrd,
1169	Ord,
1170	Hash,
1171)]
1172#[cfg_attr(feature = "serde", derive(Serialize))]
1173pub struct CandidateKey {
1174	/// Key type id
1175	pub id: [u8; 4],
1176	/// Bytes of the key
1177	pub bytes: Vec<u8>,
1178}
1179
1180impl FromStr for CandidateKey {
1181	type Err = &'static str;
1182
1183	fn from_str(s: &str) -> Result<Self, Self::Err> {
1184		if let Some((id, bytes)) = s.split_once(':') {
1185			let id: [u8; 4] = id.as_bytes().try_into().map_err(|_| "invalid key type id")?;
1186			let bytes = bytes.trim_start_matches("0x");
1187			let bytes = hex::decode(bytes).map_err(|_| "key bytes are not in hex format")?;
1188			Ok(CandidateKey { id, bytes })
1189		} else {
1190			Err("invalid format of CandidateKey, expected '<key type>:<key>'")
1191		}
1192	}
1193}
1194
1195/// Key type id of Partner Chains cross-chain key, used with ECDSA cryptography
1196pub const CROSS_CHAIN_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"crch");
1197
1198impl CandidateKey {
1199	/// Constructor
1200	pub fn new(id: KeyTypeId, bytes: Vec<u8>) -> Self {
1201		Self { id: id.0, bytes }
1202	}
1203}
1204
1205#[derive(Debug, Clone, PartialEq, Eq, DecodeWithMemTracking, TypeInfo, PartialOrd, Ord, Hash)]
1206#[cfg_attr(feature = "serde", derive(Serialize))]
1207/// Bytes of CandidateKeys that come from Cardano or other input.
1208pub struct CandidateKeys(pub Vec<CandidateKey>);
1209
1210impl CandidateKeys {
1211	/// Gets copy of key bytes identified by given id
1212	pub fn find(&self, id: KeyTypeId) -> Option<Vec<u8>> {
1213		self.0
1214			.iter()
1215			.find_map(|e| if e.id == id.0 { Some(e.bytes.clone()) } else { None })
1216	}
1217
1218	/// Gets copy of key bytes identified by given id or empty bytes if key is not present
1219	pub fn find_or_empty(&self, id: KeyTypeId) -> Vec<u8> {
1220		self.find(id).unwrap_or_default()
1221	}
1222
1223	/// True for keys that are only AURA and Grandpa
1224	pub fn has_only_aura_and_grandpa_keys(&self) -> bool {
1225		self.0.len() == 2 && self.find(AURA).is_some() && self.find(GRANDPA).is_some()
1226	}
1227}
1228
1229impl From<Vec<([u8; 4], Vec<u8>)>> for CandidateKeys {
1230	fn from(value: Vec<([u8; 4], Vec<u8>)>) -> Self {
1231		Self(value.into_iter().map(|(id, bytes)| CandidateKey { id, bytes }).collect())
1232	}
1233}
1234
1235/// Backward compatible [Encode] for [CandidateKeys].
1236/// After node update InherentData would be encoded with this version but runtime would still know [PermissionedCandidateData] and [CandidateRegistration]
1237/// that have [AuraPublicKey] and [GrandpaPublicKey] fields instead of [CandidateKeys] field.
1238/// To support existing chains, when keys are AURA and Grandpa, [CandidateKeys] is encoded exactly like in previous version.
1239/// 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
1240/// has to be decoded.
1241/// The represtation is either:
1242/// Legacy: AuraKeySize, AuraKeyBytes, GrandpaKeySize, GrandpaKeyBytes
1243/// Generic: u32::MAX, CandidateKeysVector
1244/// AuraKeySize cannot be u32::MAX (won't fit on Cardano), so when it is encountered, we know that the generic representation is used.
1245impl Encode for CandidateKeys {
1246	fn size_hint(&self) -> usize {
1247		if self.has_only_aura_and_grandpa_keys() {
1248			Encode::size_hint(&AuraPublicKey(self.find_or_empty(AURA)))
1249				.saturating_add(Encode::size_hint(&GrandpaPublicKey(self.find_or_empty(GRANDPA))))
1250		} else {
1251			Encode::size_hint(&Compact(u32::MAX)).saturating_add(Encode::size_hint(&self.0))
1252		}
1253	}
1254
1255	fn encode_to<T: parity_scale_codec::Output + ?Sized>(&self, dest: &mut T) {
1256		if self.has_only_aura_and_grandpa_keys() {
1257			Encode::encode_to(&AuraPublicKey(self.find_or_empty(AURA)), dest);
1258			Encode::encode_to(&GrandpaPublicKey(self.find_or_empty(GRANDPA)), dest)
1259		} else {
1260			// Compact(u32::MAX) is used to signal that a vector of CandidateKey should be decoded
1261			// It has to be this type, be it is the item that AuraPublicKey::decode expects.
1262			Encode::encode_to(&Compact(u32::MAX), dest);
1263			Encode::encode_to(&self.0, dest)
1264		}
1265	}
1266}
1267
1268/// Custom backward compatibile Decode. See comment on the Encode implementation.
1269impl Decode for CandidateKeys {
1270	fn decode<I: parity_scale_codec::Input>(
1271		input: &mut I,
1272	) -> Result<Self, parity_scale_codec::Error> {
1273		// See Encode instance
1274		let marker_or_aura_size: u32 = <Compact<u32>>::decode(input)?.0;
1275		if marker_or_aura_size == u32::MAX {
1276			let keys = Vec::<CandidateKey>::decode(input)?;
1277			Ok(Self(keys))
1278		} else {
1279			let aura_bytes: Vec<u8> = decode_vec_with_len(input, marker_or_aura_size as usize)?;
1280			let grandpa = GrandpaPublicKey::decode(input)?;
1281			Ok(Self(vec![AuraPublicKey(aura_bytes).into(), grandpa.into()]))
1282		}
1283	}
1284}
1285
1286#[derive(
1287	Debug,
1288	Clone,
1289	PartialEq,
1290	Eq,
1291	Decode,
1292	DecodeWithMemTracking,
1293	Encode,
1294	TypeInfo,
1295	PartialOrd,
1296	Ord,
1297	Hash,
1298)]
1299#[cfg_attr(feature = "serde", derive(Serialize))]
1300/// Information about a permissioned committee member candidate
1301///
1302/// Permissioned candidates are nominated by the Partner Chain's governance authority to be
1303/// eligible for participation in block producer committee without controlling any ADA stake
1304/// on Cardano and registering as SPOs.
1305pub struct PermissionedCandidateData {
1306	/// Sidechain public key of the permissioned candidate
1307	pub sidechain_public_key: SidechainPublicKey,
1308	/// Additional keys of the permissioned candidate
1309	pub keys: CandidateKeys,
1310}
1311
1312/// 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
1313#[cfg(feature = "std")]
1314impl FromStr for PermissionedCandidateData {
1315	type Err = alloc::boxed::Box<dyn core::error::Error + Send + Sync>;
1316
1317	fn from_str(partner_chain_public_keys: &str) -> Result<Self, Self::Err> {
1318		fn is_legacy_format(line: &str) -> bool {
1319			line.contains(':') && !line.contains(',')
1320		}
1321
1322		fn parse_legacy_format(
1323			line: &str,
1324		) -> Result<
1325			PermissionedCandidateData,
1326			alloc::boxed::Box<dyn core::error::Error + Send + Sync>,
1327		> {
1328			let line = line.replace("0x", "");
1329			if let [sidechain_pub_key, aura_pub_key, grandpa_pub_key] =
1330				line.split(":").collect::<Vec<_>>()[..]
1331			{
1332				Ok(PermissionedCandidateData {
1333					sidechain_public_key: SidechainPublicKey(
1334						hex::decode(sidechain_pub_key).map_err(|e| e.to_string())?,
1335					),
1336					keys: CandidateKeys(vec![
1337						AuraPublicKey(hex::decode(aura_pub_key).map_err(|e| e.to_string())?).into(),
1338						GrandpaPublicKey(hex::decode(grandpa_pub_key).map_err(|e| e.to_string())?)
1339							.into(),
1340					]),
1341				})
1342			} else {
1343				Err(format!("Failed to parse partner chain public keys (legacy) from '{line}'")
1344					.into())
1345			}
1346		}
1347
1348		fn parse_generic_format(
1349			line: &str,
1350		) -> Result<
1351			PermissionedCandidateData,
1352			alloc::boxed::Box<dyn core::error::Error + Send + Sync>,
1353		> {
1354			let mut columns = line.split(",");
1355			if let Some(partner_chains_key) = columns.next() {
1356				let partner_chains_key = SidechainPublicKey(
1357					hex::decode(partner_chains_key.trim_start_matches("0x"))
1358						.map_err(|e| e.to_string())?,
1359				);
1360				let mut keys = vec![];
1361				for column in columns {
1362					let key = CandidateKey::from_str(column)?;
1363					keys.push(key);
1364				}
1365				Ok(PermissionedCandidateData {
1366					sidechain_public_key: partner_chains_key,
1367					keys: CandidateKeys(keys),
1368				})
1369			} else {
1370				Err("Failed to parse partner chain public keys (generic) from '{line}'.".into())
1371			}
1372		}
1373
1374		if is_legacy_format(&partner_chain_public_keys) {
1375			parse_legacy_format(&partner_chain_public_keys)
1376		} else {
1377			parse_generic_format(&partner_chain_public_keys)
1378		}
1379	}
1380}
1381
1382/// Cardano SPO registration. This is a stripped-down version of [RegistrationData].
1383#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1384pub struct CandidateRegistration {
1385	/// Information on ADA stake pool ownership
1386	pub stake_ownership: AdaBasedStaking,
1387	/// Registering SPO's sidechain public key
1388	pub partner_chain_pub_key: SidechainPublicKey,
1389	/// Signature confirming registering SPO's ownership of `partner_chain_pub_key`
1390	pub partner_chain_signature: SidechainSignature,
1391	/// Hash of the registering SPO's Cardano public key
1392	pub own_pkh: MainchainKeyHash,
1393	/// UTxO containing the registration data
1394	pub registration_utxo: UtxoId,
1395	/// Additional keys of the registered candidate
1396	pub keys: CandidateKeys,
1397}
1398
1399impl CandidateRegistration {
1400	/// Checks whether `self` and `other` contain the same keys
1401	pub fn matches_keys(&self, other: &Self) -> bool {
1402		self.stake_ownership == other.stake_ownership
1403			&& self.partner_chain_pub_key == other.partner_chain_pub_key
1404			&& self.partner_chain_signature == other.partner_chain_signature
1405			&& self.keys.0.iter().all(|key| other.keys.0.contains(key))
1406	}
1407}
1408
1409/// Information on ADA stake pool ownership
1410///
1411/// AdaBasedStaking is a variant of Plutus type StakeOwnership. The other variant, TokenBasedStaking, is not supported.
1412#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1413pub struct AdaBasedStaking {
1414	/// Public key of the stake pool operator
1415	pub pub_key: StakePoolPublicKey,
1416	/// Signature confirming ownership of `pub_key`
1417	pub signature: MainchainSignature,
1418}
1419
1420#[derive(
1421	Clone,
1422	PartialEq,
1423	Eq,
1424	Ord,
1425	PartialOrd,
1426	TypeInfo,
1427	MaxEncodedLen,
1428	Encode,
1429	Decode,
1430	DecodeWithMemTracking,
1431)]
1432/// Represents a Cardano ADA delegator
1433pub enum DelegatorKey {
1434	/// Represents a staking address that is controlled by a user delegator
1435	StakeKeyHash([u8; 28]),
1436	/// Represents a staking address that is locked by a Plutus script
1437	ScriptKeyHash {
1438		/// Raw stake address hash
1439		hash_raw: [u8; 28],
1440		/// Hash of the Plutus script controlling the staking address
1441		script_hash: [u8; 28],
1442	},
1443}
1444
1445impl alloc::fmt::Debug for DelegatorKey {
1446	fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
1447		let s = match self {
1448			Self::ScriptKeyHash { hash_raw, script_hash } => alloc::format!(
1449				"ScriptKeyHash{{ hash_raw: {}, script_hash: {} }}",
1450				hex::encode(hash_raw),
1451				hex::encode(script_hash)
1452			),
1453			Self::StakeKeyHash(hash) => alloc::format!("StakeKeyHash({})", hex::encode(hash)),
1454		};
1455
1456		f.write_str(&s)
1457	}
1458}
1459
1460/// Amount of Lovelace staked by a Cardano delegator to a single stake pool
1461#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
1462pub struct DelegatorStakeAmount(pub u64);
1463
1464impl<T: Into<u64>> From<T> for DelegatorStakeAmount {
1465	fn from(value: T) -> Self {
1466		Self(value.into())
1467	}
1468}
1469
1470/// A mapping between Cardano SPOs and the information about ADA delegation of their stake pools
1471///
1472/// This mapping can be used to calculate relative share of the total delegation for the
1473/// purpose of weighing during block producer selection.
1474#[derive(Debug, Clone, Default)]
1475pub struct StakeDistribution(pub BTreeMap<MainchainKeyHash, PoolDelegation>);
1476
1477/// ADA delegation data for a single Cardano SPO
1478#[derive(Debug, Clone, Default, PartialEq)]
1479pub struct PoolDelegation {
1480	/// Total amount delegated to the stake pool
1481	pub total_stake: StakeDelegation,
1482	/// Delegated amount for each delegator of the stake pool
1483	pub delegators: BTreeMap<DelegatorKey, DelegatorStakeAmount>,
1484}
1485
1486/// [FromStr] trait with [FromStr::Err] fixed to a type compatible with `clap`'s `value_parser` macro.
1487pub trait FromStrStdErr:
1488	FromStr<Err: Into<alloc::boxed::Box<dyn core::error::Error + Send + Sync + 'static>>>
1489{
1490}
1491impl<T: FromStr<Err: Into<alloc::boxed::Box<dyn core::error::Error + Send + Sync + 'static>>>>
1492	FromStrStdErr for T
1493{
1494}
1495
1496#[cfg(test)]
1497mod tests {
1498	use super::*;
1499	use core::str::FromStr;
1500	use hex_literal::hex;
1501	use parity_scale_codec::{Decode, Encode};
1502
1503	#[test]
1504	fn main_chain_address_string_serialize_deserialize_round_trip() {
1505		let address = MainchainAddress::from_str(
1506			"addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc",
1507		)
1508		.unwrap();
1509		let serialized = serde_json::to_value(&address).unwrap();
1510		assert_eq!(
1511			serialized,
1512			serde_json::json!("addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc")
1513		);
1514		let deserialized = serde_json::from_value(serialized).unwrap();
1515		assert_eq!(address, deserialized);
1516	}
1517
1518	#[test]
1519	fn main_chain_address_deserialization_of_hex_encoded_bytes() {
1520		let address = MainchainAddress::from_str("addr_test1wz5q").unwrap();
1521		let serialized = serde_json::json!("0x616464725f7465737431777a3571");
1522		assert_eq!(address, serde_json::from_value(serialized).unwrap());
1523		let serialized = serde_json::json!("616464725f7465737431777a3571");
1524		assert_eq!(address, serde_json::from_value(serialized).unwrap());
1525	}
1526
1527	#[test]
1528	fn main_chain_address_string_from_str_to_string_round_trip() {
1529		let address = MainchainAddress::from_str(
1530			"addr_test1wz5qc7fk2pat0058w4zwvkw35ytptej3nuc3je2kgtan5dq3rt4sc",
1531		)
1532		.unwrap();
1533		let str = address.to_string();
1534		let from_str = MainchainAddress::from_str(&str).unwrap();
1535		assert_eq!(address, from_str);
1536	}
1537
1538	#[test]
1539	fn main_chain_signature_should_be_backward_compatible_with_vec() {
1540		#[derive(Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq, Hash)]
1541		#[byte_string(debug, hex_serialize, decode_hex)]
1542		struct LegacyMCSignature(pub Vec<u8>);
1543
1544		let legacy_encoded = LegacyMCSignature(vec![10; 64]).encode();
1545
1546		let legacy_decoded = MainchainSignature::decode(&mut legacy_encoded.as_slice())
1547			.expect("Encoded legacy should decode to current type");
1548
1549		assert_eq!(legacy_decoded.0, [10; MAINCHAIN_SIGNATURE_LEN]);
1550
1551		let current_encoded = MainchainSignature([9; MAINCHAIN_SIGNATURE_LEN]).encode();
1552
1553		let current_decoded = LegacyMCSignature::decode(&mut current_encoded.as_slice())
1554			.expect("Encoded current should decode to legacy");
1555
1556		assert_eq!(current_decoded.0, vec![9; 64]);
1557	}
1558
1559	#[test]
1560	fn cross_chain_signature_verify_works() {
1561		let signature =	CrossChainSignature(
1562			hex!("d1e02e4a5484c3b7202ce6b844577048e7578dc62901cf8f51e6d74bbd3adb091688feacedd8343d0b04a0f5862b2e06148934a75e678e42051fde5431eca33d").to_vec()
1563		);
1564		let pubkey = CrossChainPublicKey(
1565			hex!("020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1").to_vec(),
1566		);
1567		let signed_data = hex!(
1568			"84020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a16c68747470733a2f2f636f6f6c2e73747566662f73706f2e6a736f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
1569		);
1570
1571		assert!(signature.verify(&pubkey, &signed_data).is_ok())
1572	}
1573
1574	#[derive(Decode, Encode, PartialEq, Eq, Debug)]
1575	struct TestCandidateKeys {
1576		field_before: Option<u32>,
1577		keys: CandidateKeys,
1578		// ensures that Decode of CandidateKeys does not consume the whole input
1579		field_after: Option<u32>,
1580	}
1581
1582	#[test]
1583	fn encode_decode_round_trip_for_generic_candidate_keys() {
1584		let keys = TestCandidateKeys {
1585			field_before: Some(42),
1586			keys: CandidateKeys(vec![
1587				CandidateKey { id: *b"abcd", bytes: [7u8; 32].to_vec() },
1588				CandidateKey { id: *b"efgh", bytes: [9u8; 32].to_vec() },
1589			]),
1590			field_after: Some(15),
1591		};
1592		let bytes: Vec<u8> = Encode::encode(&keys);
1593		let mut bytes: &[u8] = &bytes;
1594		let decoded = TestCandidateKeys::decode(&mut bytes).unwrap();
1595		assert_eq!(keys, decoded)
1596	}
1597
1598	#[test]
1599	fn encode_decode_round_trip_for_aura_and_grandpa_candidate_keys() {
1600		let keys = TestCandidateKeys {
1601			field_before: Some(42),
1602			keys: CandidateKeys(vec![
1603				AuraPublicKey([7u8; 32].to_vec()).into(),
1604				GrandpaPublicKey([9u8; 32].to_vec()).into(),
1605			]),
1606			field_after: Some(15),
1607		};
1608		let bytes: Vec<u8> = Encode::encode(&keys);
1609		let mut bytes: &[u8] = &bytes;
1610		let decoded = TestCandidateKeys::decode(&mut bytes).unwrap();
1611		assert_eq!(keys, decoded)
1612	}
1613}