partner_chains_plutus_data/
d_param.rs

1//! Plutus data types for D-param.
2use crate::{
3	DataDecodingError, DecodingResult, PlutusDataExtensions, VersionedDatum,
4	VersionedDatumWithLegacy, VersionedGenericDatum,
5};
6use cardano_serialization_lib::{PlutusData, PlutusList};
7
8#[derive(Clone, Debug, PartialEq)]
9/// Datum representing D-param.
10pub enum DParamDatum {
11	/// Initial/legacy datum schema. If a datum doesn't contain a version, it is assumed to be V0
12	V0 {
13		/// Number of permissioned candidates in committee.
14		num_permissioned_candidates: u16,
15		/// Number of registered candidates in committee.
16		num_registered_candidates: u16,
17	},
18}
19
20impl TryFrom<PlutusData> for DParamDatum {
21	type Error = DataDecodingError;
22	fn try_from(datum: PlutusData) -> DecodingResult<Self> {
23		Self::decode(&datum)
24	}
25}
26
27impl From<DParamDatum> for sidechain_domain::DParameter {
28	fn from(datum: DParamDatum) -> Self {
29		match datum {
30			DParamDatum::V0 { num_permissioned_candidates, num_registered_candidates } => {
31				Self { num_permissioned_candidates, num_registered_candidates }
32			},
33		}
34	}
35}
36
37/// Convert [DParameter] to [PlutusData].
38///
39/// Encoding:
40///   VersionedGenericDatum:
41///   - datum: ()
42///   - appendix:
43///     [ d_param.num_permissioned_candidates
44///     , d_param.num_registered_candidates
45///     ]
46///   - version: 0
47pub fn d_parameter_to_plutus_data(d_param: &sidechain_domain::DParameter) -> PlutusData {
48	let mut list = PlutusList::new();
49	list.add(&PlutusData::new_integer(&d_param.num_permissioned_candidates.into()));
50	list.add(&PlutusData::new_integer(&d_param.num_registered_candidates.into()));
51	let appendix = PlutusData::new_list(&list);
52	VersionedGenericDatum {
53		datum: PlutusData::new_empty_constr_plutus_data(&0u64.into()),
54		appendix,
55		version: 0,
56	}
57	.into()
58}
59
60impl VersionedDatumWithLegacy for DParamDatum {
61	const NAME: &str = "DParamDatum";
62
63	fn decode_legacy(data: &PlutusData) -> Result<Self, String> {
64		let d_parameter = data
65			.as_list()
66			.filter(|datum| datum.len() == 2)
67			.and_then(|items| {
68				Some(DParamDatum::V0 {
69					num_permissioned_candidates: items.get(0).as_u16()?,
70					num_registered_candidates: items.get(1).as_u16()?,
71				})
72			})
73			.ok_or("Expected [u16, u16]")?;
74
75		Ok(d_parameter)
76	}
77
78	fn decode_versioned(
79		version: u64,
80		_datum: &PlutusData,
81		appendix: &PlutusData,
82	) -> Result<Self, String> {
83		match version {
84			0 => DParamDatum::decode_legacy(appendix)
85				.map_err(|msg| format!("Cannot parse appendix: {msg}")),
86			_ => Err(format!("Unknown version: {version}")),
87		}
88	}
89}
90
91#[cfg(test)]
92mod tests {
93	use super::*;
94	use crate::test_helpers::*;
95	use pretty_assertions::assert_eq;
96
97	#[test]
98	fn valid_legacy_d_param() {
99		let plutus_data = test_plutus_data!({"list": [{"int": 1}, {"int": 2}]});
100
101		let expected_datum =
102			DParamDatum::V0 { num_permissioned_candidates: 1, num_registered_candidates: 2 };
103
104		assert_eq!(DParamDatum::try_from(plutus_data).unwrap(), expected_datum)
105	}
106
107	#[test]
108	fn domain_d_param_to_csl() {
109		let d_param = sidechain_domain::DParameter {
110			num_permissioned_candidates: 17,
111			num_registered_candidates: 42,
112		};
113
114		let expected_plutus_data = json_to_plutus_data(v0_datum_json());
115
116		assert_eq!(d_parameter_to_plutus_data(&d_param), expected_plutus_data)
117	}
118
119	fn v0_datum_json() -> serde_json::Value {
120		serde_json::json!({
121			"list": [
122				{ "constructor": 0, "fields": [] },
123				{ "list": [
124					{ "int": 17 },
125					{ "int": 42 }
126				] },
127				{ "int": 0 }
128			]
129		})
130	}
131
132	#[test]
133	fn valid_v0_d_param() {
134		let plutus_data = json_to_plutus_data(v0_datum_json());
135
136		let expected_datum =
137			DParamDatum::V0 { num_permissioned_candidates: 17, num_registered_candidates: 42 };
138
139		assert_eq!(DParamDatum::try_from(plutus_data).unwrap(), expected_datum)
140	}
141}