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