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