partner_chains_plutus_data/
registered_candidates.rs

1//! Plutus data types for registered candidates.
2use crate::{
3	PlutusDataExtensions, VersionedDatum, VersionedDatumWithLegacy, VersionedGenericDatum,
4	candidate_keys::*,
5};
6use cardano_serialization_lib::*;
7use sidechain_domain::*;
8use sp_core::crypto::key_types::{AURA, GRANDPA};
9
10use crate::{DataDecodingError, DecodingResult};
11
12/** Representation of the plutus type in the mainchain contract (rev 4ed2cc66c554ec8c5bec7b90ad9273e9069a1fb4)
13 *
14 * Note that the ECDSA secp256k1 public key is serialized in compressed format and the
15 * sidechain signature does not contain the recovery bytes (it's just r an s concatenated).
16 *
17 * data BlockProducerRegistration = BlockProducerRegistration
18 * { -- | Verification keys required by the stake ownership model
19 *   -- | @since v4.0.0
20 *  stakeOwnership :: StakeOwnership
21 * , -- | public key in the sidechain's desired format
22 *  sidechainPubKey :: LedgerBytes
23 * , -- | Signature of the sidechain
24 *   -- | @since v4.0.0
25 *  sidechainSignature :: Signature
26 * , -- | A UTxO that must be spent by the transaction
27 *   -- | @since v4.0.0
28 *  inputUtxo :: TxOutRef
29 * , -- | Owner public key hash
30 *   -- | @since v4.0.0
31 *  ownPkh :: PubKeyHash
32 * , -- | Sidechain authority discovery key
33 *   -- | @since Unreleased
34 *   auraKey :: LedgerBytes
35 * , -- | Sidechain grandpa key
36 *   -- | @since Unreleased
37 *   grandpaKey :: LedgerBytes
38 * }
39 */
40#[derive(Clone, Debug, PartialEq, Eq)]
41pub enum RegisterValidatorDatum {
42	/// Initial/legacy datum schema. If a datum doesn't contain a version, it is assumed to be V0
43	V0 {
44		/// Stake ownership information of registered candidate.
45		stake_ownership: AdaBasedStaking,
46		/// Sidechain public key of the candidate. See [SidechainPublicKey] for more details.
47		sidechain_pub_key: SidechainPublicKey,
48		/// Sidechain key signature of the registration message.
49		sidechain_signature: SidechainSignature,
50		/// UTxO id that is a part of the signed registration message.
51		/// It is spent during the registration process. Prevents replay attacks.
52		registration_utxo: UtxoId,
53		/// Hash of the registering SPO's Cardano public key.
54		/// Used by offchain code to find the registration UTXO when re-registering or de-registering.
55		own_pkh: MainchainKeyHash,
56		/// Registering SPO's Aura public key
57		aura_pub_key: AuraPublicKey,
58		/// Registering SPO's GRANDPA public key
59		grandpa_pub_key: GrandpaPublicKey,
60	},
61	/// V1 datum with support for generic keys
62	V1 {
63		/// Stake ownership information of registered candidate.
64		stake_ownership: AdaBasedStaking,
65		/// Sidechain public key of the candidate. See [SidechainPublicKey] for more details.
66		sidechain_pub_key: SidechainPublicKey,
67		/// Sidechain key signature of the registration message.
68		sidechain_signature: SidechainSignature,
69		/// UTxO id that is a part of the signed registration message.
70		/// It is spent during the registration process. Prevents replay attacks.
71		registration_utxo: UtxoId,
72		/// Hash of the registering SPO's Cardano public key.
73		/// Used by offchain code to find the registration UTXO when re-registering or de-registering.
74		own_pkh: MainchainKeyHash,
75		/// Additional keys of the registered candidate, these are specific to the keys used by the partner chain
76		keys: CandidateKeys,
77	},
78}
79
80impl TryFrom<PlutusData> for RegisterValidatorDatum {
81	type Error = DataDecodingError;
82
83	fn try_from(datum: PlutusData) -> DecodingResult<Self> {
84		Self::decode(&datum)
85	}
86}
87
88impl VersionedDatumWithLegacy for RegisterValidatorDatum {
89	const NAME: &str = "RegisterValidatorDatum";
90
91	fn decode_legacy(data: &PlutusData) -> Result<Self, String> {
92		decode_legacy_register_validator_datum(data).ok_or_else(|| "Invalid data".into())
93	}
94
95	fn decode_versioned(
96		version: u64,
97		datum: &PlutusData,
98		appendix: &PlutusData,
99	) -> Result<Self, String> {
100		match version {
101			0 => decode_v0_register_validator_datum(datum, appendix)
102				.ok_or("Can not parse appendix".to_string()),
103			1 => decode_v1_register_validator_datum(datum, appendix)
104				.ok_or("Can not parse appendix".to_string()),
105			_ => Err(format!("Unknown version: {version}")),
106		}
107	}
108}
109
110/// Converts [CandidateRegistration] domain type to [RegisterValidatorDatum::V0] encoded as [PlutusData].
111pub fn candidate_registration_to_plutus_data(
112	candidate_registration: &CandidateRegistration,
113) -> PlutusData {
114	let datum = if candidate_registration.keys.has_only_aura_and_grandpa_keys() {
115		RegisterValidatorDatum::V0 {
116			stake_ownership: candidate_registration.stake_ownership.clone(),
117			sidechain_pub_key: candidate_registration.partner_chain_pub_key.clone(),
118			sidechain_signature: candidate_registration.partner_chain_signature.clone(),
119			registration_utxo: candidate_registration.registration_utxo,
120			own_pkh: candidate_registration.own_pkh,
121			aura_pub_key: AuraPublicKey(candidate_registration.keys.find_or_empty(AURA)),
122			grandpa_pub_key: GrandpaPublicKey(candidate_registration.keys.find_or_empty(GRANDPA)),
123		}
124	} else {
125		RegisterValidatorDatum::V1 {
126			stake_ownership: candidate_registration.stake_ownership.clone(),
127			sidechain_pub_key: candidate_registration.partner_chain_pub_key.clone(),
128			sidechain_signature: candidate_registration.partner_chain_signature.clone(),
129			registration_utxo: candidate_registration.registration_utxo,
130			own_pkh: candidate_registration.own_pkh,
131			keys: candidate_registration.keys.clone(),
132		}
133	};
134	datum.into()
135}
136
137impl From<RegisterValidatorDatum> for CandidateRegistration {
138	fn from(value: RegisterValidatorDatum) -> Self {
139		match value {
140			RegisterValidatorDatum::V0 {
141				stake_ownership,
142				sidechain_pub_key,
143				sidechain_signature,
144				registration_utxo,
145				own_pkh,
146				aura_pub_key,
147				grandpa_pub_key,
148			} => CandidateRegistration {
149				stake_ownership,
150				partner_chain_pub_key: sidechain_pub_key,
151				partner_chain_signature: sidechain_signature,
152				registration_utxo,
153				own_pkh,
154				keys: CandidateKeys(vec![aura_pub_key.into(), grandpa_pub_key.into()]),
155			},
156			RegisterValidatorDatum::V1 {
157				stake_ownership,
158				sidechain_pub_key,
159				sidechain_signature,
160				registration_utxo,
161				own_pkh,
162				keys,
163			} => CandidateRegistration {
164				stake_ownership,
165				partner_chain_pub_key: sidechain_pub_key,
166				partner_chain_signature: sidechain_signature,
167				registration_utxo,
168				own_pkh,
169				keys,
170			},
171		}
172	}
173}
174
175fn decode_v0_register_validator_datum(
176	datum: &PlutusData,
177	appendix: &PlutusData,
178) -> Option<RegisterValidatorDatum> {
179	let fields = appendix
180		.as_constr_plutus_data()
181		.filter(|datum| datum.alternative().is_zero())
182		.filter(|datum| datum.data().len() >= 6)?
183		.data();
184	let stake_ownership = decode_ada_based_staking_datum(fields.get(0))?;
185	let sidechain_pub_key = fields.get(1).as_bytes().map(SidechainPublicKey)?;
186	let sidechain_signature = fields.get(2).as_bytes().map(SidechainSignature)?;
187	let registration_utxo = decode_utxo_id_datum(fields.get(3))?;
188	let aura_pub_key = fields.get(4).as_bytes().map(AuraPublicKey)?;
189	let grandpa_pub_key = fields.get(5).as_bytes().map(GrandpaPublicKey)?;
190
191	let own_pkh = MainchainKeyHash(datum.as_bytes()?.try_into().ok()?);
192	Some(RegisterValidatorDatum::V0 {
193		stake_ownership,
194		sidechain_pub_key,
195		sidechain_signature,
196		registration_utxo,
197		own_pkh,
198		aura_pub_key,
199		grandpa_pub_key,
200	})
201}
202
203fn decode_v1_register_validator_datum(
204	datum: &PlutusData,
205	appendix: &PlutusData,
206) -> Option<RegisterValidatorDatum> {
207	let fields = appendix
208		.as_constr_plutus_data()
209		.filter(|datum| datum.alternative().is_zero())
210		.filter(|datum| datum.data().len() == 5)?
211		.data();
212
213	let stake_ownership = decode_ada_based_staking_datum(fields.get(0))?;
214	let sidechain_pub_key = fields.get(1).as_bytes().map(SidechainPublicKey)?;
215	let sidechain_signature = fields.get(2).as_bytes().map(SidechainSignature)?;
216	let registration_utxo = decode_utxo_id_datum(fields.get(3))?;
217	let keys = decode_candidate_keys(&fields.get(4))?;
218
219	let own_pkh = MainchainKeyHash(datum.as_bytes()?.try_into().ok()?);
220	Some(RegisterValidatorDatum::V1 {
221		stake_ownership,
222		sidechain_pub_key,
223		sidechain_signature,
224		registration_utxo,
225		own_pkh,
226		keys,
227	})
228}
229
230/// Parses plutus data schema that was used before datum versioning was added. Kept for backwards compatibility.
231fn decode_legacy_register_validator_datum(datum: &PlutusData) -> Option<RegisterValidatorDatum> {
232	let fields = datum
233		.as_constr_plutus_data()
234		.filter(|datum| datum.alternative().is_zero())
235		.filter(|datum| datum.data().len() >= 7)?
236		.data();
237	let stake_ownership = decode_ada_based_staking_datum(fields.get(0))?;
238	let sidechain_pub_key = fields.get(1).as_bytes().map(SidechainPublicKey)?;
239	let sidechain_signature = fields.get(2).as_bytes().map(SidechainSignature)?;
240	let registration_utxo = decode_utxo_id_datum(fields.get(3))?;
241	let own_pkh = MainchainKeyHash(fields.get(4).as_bytes()?.try_into().ok()?);
242	let aura_pub_key = fields.get(5).as_bytes().map(AuraPublicKey)?;
243	let grandpa_pub_key = fields.get(6).as_bytes().map(GrandpaPublicKey)?;
244	Some(RegisterValidatorDatum::V0 {
245		stake_ownership,
246		sidechain_pub_key,
247		sidechain_signature,
248		registration_utxo,
249		own_pkh,
250		aura_pub_key,
251		grandpa_pub_key,
252	})
253}
254
255fn decode_ada_based_staking_datum(datum: PlutusData) -> Option<AdaBasedStaking> {
256	let fields = datum
257		.as_constr_plutus_data()
258		.filter(|datum| datum.alternative().is_zero())
259		.filter(|datum| datum.data().len() >= 2)?
260		.data();
261	let pub_key = TryFrom::try_from(fields.get(0).as_bytes()?).ok()?;
262	let signature = MainchainSignature(fields.get(1).as_bytes()?.try_into().ok()?);
263	Some(AdaBasedStaking { pub_key, signature })
264}
265fn decode_utxo_id_datum(datum: PlutusData) -> Option<UtxoId> {
266	let fields = datum
267		.as_constr_plutus_data()
268		.filter(|datum| datum.alternative().is_zero())
269		.filter(|datum| datum.data().len() >= 2)?
270		.data();
271	let tx_hash = decode_tx_hash_datum(fields.get(0))?;
272	let index = UtxoIndex(fields.get(1).as_u16()?);
273	Some(UtxoId { tx_hash, index })
274}
275/// Plutus type for TxHash is a sum type, we can parse only variant with constructor 0.
276fn decode_tx_hash_datum(datum: PlutusData) -> Option<McTxHash> {
277	let constructor_datum = datum
278		.as_constr_plutus_data()
279		.filter(|datum| datum.alternative().is_zero())
280		.filter(|datum| datum.data().len() >= 1)?;
281	let bytes = constructor_datum.data().get(0).as_bytes()?;
282
283	Some(McTxHash(TryFrom::try_from(bytes).ok()?))
284}
285
286impl From<RegisterValidatorDatum> for PlutusData {
287	fn from(value: RegisterValidatorDatum) -> Self {
288		match value {
289			RegisterValidatorDatum::V0 {
290				stake_ownership,
291				sidechain_pub_key,
292				sidechain_signature,
293				registration_utxo,
294				own_pkh,
295				aura_pub_key,
296				grandpa_pub_key,
297			} => {
298				let mut appendix_fields = PlutusList::new();
299				appendix_fields.add(&stake_ownership_to_plutus_data(stake_ownership));
300				appendix_fields.add(&PlutusData::new_bytes(sidechain_pub_key.0));
301				appendix_fields.add(&PlutusData::new_bytes(sidechain_signature.0));
302				appendix_fields.add(&utxo_id_to_plutus_data(registration_utxo));
303				appendix_fields.add(&PlutusData::new_bytes(aura_pub_key.0));
304				appendix_fields.add(&PlutusData::new_bytes(grandpa_pub_key.0));
305				let appendix = ConstrPlutusData::new(&BigNum::zero(), &appendix_fields);
306				VersionedGenericDatum {
307					datum: PlutusData::new_bytes(own_pkh.0.to_vec()),
308					appendix: PlutusData::new_constr_plutus_data(&appendix),
309					version: 0,
310				}
311				.into()
312			},
313			RegisterValidatorDatum::V1 {
314				stake_ownership,
315				sidechain_pub_key,
316				sidechain_signature,
317				registration_utxo,
318				own_pkh,
319				keys,
320			} => {
321				let mut appendix_fields = PlutusList::new();
322				appendix_fields.add(&stake_ownership_to_plutus_data(stake_ownership));
323				appendix_fields.add(&PlutusData::new_bytes(sidechain_pub_key.0));
324				appendix_fields.add(&PlutusData::new_bytes(sidechain_signature.0));
325				appendix_fields.add(&utxo_id_to_plutus_data(registration_utxo));
326				appendix_fields.add(&candidate_keys_to_plutus(&keys));
327				let appendix = ConstrPlutusData::new(&BigNum::zero(), &appendix_fields);
328				VersionedGenericDatum {
329					datum: PlutusData::new_bytes(own_pkh.0.to_vec()),
330					appendix: PlutusData::new_constr_plutus_data(&appendix),
331					version: 1,
332				}
333				.into()
334			},
335		}
336	}
337}
338
339fn stake_ownership_to_plutus_data(v: AdaBasedStaking) -> PlutusData {
340	let mut fields = PlutusList::new();
341	fields.add(&PlutusData::new_bytes(v.pub_key.0.to_vec()));
342	fields.add(&PlutusData::new_bytes(v.signature.0.to_vec()));
343	PlutusData::new_constr_plutus_data(&ConstrPlutusData::new(&BigNum::zero(), &fields))
344}
345
346fn utxo_id_to_plutus_data(v: UtxoId) -> PlutusData {
347	let mut fields = PlutusList::new();
348	fields.add(&tx_hash_to_plutus_data(v.tx_hash));
349	fields.add(&PlutusData::new_integer(&v.index.0.into()));
350	PlutusData::new_constr_plutus_data(&ConstrPlutusData::new(&BigNum::zero(), &fields))
351}
352
353fn tx_hash_to_plutus_data(v: McTxHash) -> PlutusData {
354	let mut fields = PlutusList::new();
355	fields.add(&PlutusData::new_bytes(v.0.to_vec()));
356	PlutusData::new_constr_plutus_data(&ConstrPlutusData::new(&BigNum::zero(), &fields))
357}
358
359#[cfg(test)]
360mod tests {
361	use super::*;
362	use crate::test_helpers::*;
363	use hex_literal::hex;
364	use pretty_assertions::assert_eq;
365
366	fn test_datum_v0() -> RegisterValidatorDatum {
367		RegisterValidatorDatum::V0 {
368			stake_ownership: AdaBasedStaking {
369				pub_key: StakePoolPublicKey(hex!("bfbee74ab533f40979101057f96de62e95233f2a5216eb16b54106f09fd7350d")),
370				signature: MainchainSignature(hex!("28d1c3b7df297a60d24a3f88bc53d7029a8af35e8dd876764fd9e7a24203a3482a98263cc8ba2ddc7dc8e7faea31c2e7bad1f00e28c43bc863503e3172dc6b0a").into()),
371			},
372			sidechain_pub_key: SidechainPublicKey(hex!("02fe8d1eb1bcb3432b1db5833ff5f2226d9cb5e65cee430558c18ed3a3c86ce1af").into()),
373			sidechain_signature: SidechainSignature(hex!("f8ec6c7f935d387aaa1693b3bf338cbb8f53013da8a5a234f9c488bacac01af259297e69aee0df27f553c0a1164df827d016125c16af93c99be2c19f36d2f66e").into()),
374			registration_utxo: UtxoId {
375				tx_hash: McTxHash(hex!("cdefe62b0a0016c2ccf8124d7dda71f6865283667850cc7b471f761d2bc1eb13")),
376				index: UtxoIndex(1),
377			},
378			own_pkh: MainchainKeyHash(hex!("aabbccddeeff00aabbccddeeff00aabbccddeeff00aabbccddeeff00")),
379			aura_pub_key: AuraPublicKey(hex!("d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d").into()),
380			grandpa_pub_key: GrandpaPublicKey(hex!("88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee").into()),
381		}
382	}
383
384	fn test_datum_v1() -> RegisterValidatorDatum {
385		RegisterValidatorDatum::V1 {
386			stake_ownership: AdaBasedStaking {
387				pub_key: StakePoolPublicKey(hex!("bfbee74ab533f40979101057f96de62e95233f2a5216eb16b54106f09fd7350d")),
388				signature: MainchainSignature(hex!("28d1c3b7df297a60d24a3f88bc53d7029a8af35e8dd876764fd9e7a24203a3482a98263cc8ba2ddc7dc8e7faea31c2e7bad1f00e28c43bc863503e3172dc6b0a").into()),
389			},
390			sidechain_pub_key: SidechainPublicKey(hex!("02fe8d1eb1bcb3432b1db5833ff5f2226d9cb5e65cee430558c18ed3a3c86ce1af").into()),
391			sidechain_signature: SidechainSignature(hex!("f8ec6c7f935d387aaa1693b3bf338cbb8f53013da8a5a234f9c488bacac01af259297e69aee0df27f553c0a1164df827d016125c16af93c99be2c19f36d2f66e").into()),
392			registration_utxo: UtxoId {
393				tx_hash: McTxHash(hex!("cdefe62b0a0016c2ccf8124d7dda71f6865283667850cc7b471f761d2bc1eb13")),
394				index: UtxoIndex(1),
395			},
396			own_pkh: MainchainKeyHash(hex!("aabbccddeeff00aabbccddeeff00aabbccddeeff00aabbccddeeff00")),
397			keys: CandidateKeys(vec![
398				CandidateKey { id: [0; 4], bytes: [1; 32].to_vec() },
399				CandidateKey { id: [2; 4], bytes: [3; 32].to_vec() },
400			]),
401		}
402	}
403
404	#[test]
405	fn valid_legacy_registration() {
406		let plutus_data = test_plutus_data!({
407			"constructor": 0,
408			"fields": [
409				{
410					"constructor": 0,
411					"fields": [
412						{ "bytes": "bfbee74ab533f40979101057f96de62e95233f2a5216eb16b54106f09fd7350d" },
413						{ "bytes": "28d1c3b7df297a60d24a3f88bc53d7029a8af35e8dd876764fd9e7a24203a3482a98263cc8ba2ddc7dc8e7faea31c2e7bad1f00e28c43bc863503e3172dc6b0a" }
414					]
415				},
416				{ "bytes": "02fe8d1eb1bcb3432b1db5833ff5f2226d9cb5e65cee430558c18ed3a3c86ce1af" },
417				{ "bytes": "f8ec6c7f935d387aaa1693b3bf338cbb8f53013da8a5a234f9c488bacac01af259297e69aee0df27f553c0a1164df827d016125c16af93c99be2c19f36d2f66e" },
418				{
419					"fields": [
420						{
421							"constructor": 0,
422							"fields": [ { "bytes": "cdefe62b0a0016c2ccf8124d7dda71f6865283667850cc7b471f761d2bc1eb13"} ]
423						},
424						{ "int": 1 }
425					],
426					"constructor": 0
427				},
428				{ "bytes": "aabbccddeeff00aabbccddeeff00aabbccddeeff00aabbccddeeff00" },
429				{ "bytes": "d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d" },
430				{ "bytes": "88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee" }
431			]
432		});
433		assert_eq!(RegisterValidatorDatum::try_from(plutus_data).unwrap(), test_datum_v0())
434	}
435
436	fn test_versioned_datum_v0_json() -> serde_json::Value {
437		serde_json::json!({
438			"list": [
439				{ "bytes": "aabbccddeeff00aabbccddeeff00aabbccddeeff00aabbccddeeff00" },
440				{
441					"constructor": 0,
442					"fields": [
443						{
444							"constructor": 0,
445							"fields": [
446								{ "bytes": "bfbee74ab533f40979101057f96de62e95233f2a5216eb16b54106f09fd7350d" },
447								{ "bytes": "28d1c3b7df297a60d24a3f88bc53d7029a8af35e8dd876764fd9e7a24203a3482a98263cc8ba2ddc7dc8e7faea31c2e7bad1f00e28c43bc863503e3172dc6b0a" }
448							]
449						},
450						{ "bytes": "02fe8d1eb1bcb3432b1db5833ff5f2226d9cb5e65cee430558c18ed3a3c86ce1af" },
451						{ "bytes": "f8ec6c7f935d387aaa1693b3bf338cbb8f53013da8a5a234f9c488bacac01af259297e69aee0df27f553c0a1164df827d016125c16af93c99be2c19f36d2f66e" },
452						{
453							"fields": [
454								{
455									"constructor": 0,
456									"fields": [ { "bytes": "cdefe62b0a0016c2ccf8124d7dda71f6865283667850cc7b471f761d2bc1eb13"} ]
457								},
458								{ "int": 1 }
459							],
460							"constructor": 0
461						},
462						{ "bytes": "d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d" },
463						{ "bytes": "88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee" }
464					]
465				},
466				{ "int": 0 }
467			]
468		})
469	}
470
471	fn test_versioned_datum_v1_json() -> serde_json::Value {
472		serde_json::json!({
473			"list": [
474				{"bytes": "aabbccddeeff00aabbccddeeff00aabbccddeeff00aabbccddeeff00" },
475				{
476					"constructor": 0,
477					"fields": [
478						{
479							"constructor": 0,
480							"fields": [
481								{"bytes": "bfbee74ab533f40979101057f96de62e95233f2a5216eb16b54106f09fd7350d" },
482								{"bytes": "28d1c3b7df297a60d24a3f88bc53d7029a8af35e8dd876764fd9e7a24203a3482a98263cc8ba2ddc7dc8e7faea31c2e7bad1f00e28c43bc863503e3172dc6b0a" }
483							]
484						},
485						{"bytes": "02fe8d1eb1bcb3432b1db5833ff5f2226d9cb5e65cee430558c18ed3a3c86ce1af"},
486						{"bytes": "f8ec6c7f935d387aaa1693b3bf338cbb8f53013da8a5a234f9c488bacac01af259297e69aee0df27f553c0a1164df827d016125c16af93c99be2c19f36d2f66e" },
487						{
488							"fields": [
489								{
490									"constructor": 0,
491									"fields": [ {"bytes": "cdefe62b0a0016c2ccf8124d7dda71f6865283667850cc7b471f761d2bc1eb13"} ]
492								},
493								{"int": 1 }
494							],
495							"constructor": 0
496						},
497						{"list": [
498							{"list": [{"bytes": "00000000"}, {"bytes": "0101010101010101010101010101010101010101010101010101010101010101" }]},
499							{"list": [{"bytes": "02020202"}, {"bytes": "0303030303030303030303030303030303030303030303030303030303030303" }]}
500						]}
501					]
502				},
503				{"int": 1 }
504			]
505		})
506	}
507
508	#[test]
509	fn valid_v0_registration_from_plutus_data() {
510		let plutus_data = json_to_plutus_data(test_versioned_datum_v0_json());
511		assert_eq!(RegisterValidatorDatum::try_from(plutus_data).unwrap(), test_datum_v0())
512	}
513
514	#[test]
515	fn valid_v1_registration_from_plutus_data() {
516		let plutus_data = json_to_plutus_data(test_versioned_datum_v1_json());
517		assert_eq!(RegisterValidatorDatum::try_from(plutus_data).unwrap(), test_datum_v1())
518	}
519
520	#[test]
521	fn v0_registration_to_plutus_data() {
522		let plutus_data: PlutusData = test_datum_v0().into();
523		assert_eq!(plutus_data_to_json(plutus_data), test_versioned_datum_v0_json())
524	}
525
526	#[test]
527	fn valid_v1_registration_to_plutus_data() {
528		let plutus_data: PlutusData = test_datum_v1().into();
529		assert_eq!(plutus_data_to_json(plutus_data), test_versioned_datum_v1_json())
530	}
531}