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