partner_chains_plutus_data/
version_oracle.rs

1//! Plutus types for the script versioning/script caching system.
2use crate::{
3	DataDecodingError, DecodingResult, PlutusDataExtensions, ScriptHash, decoding_error_and_log,
4};
5use cardano_serialization_lib::{BigInt, BigNum, ConstrPlutusData, PlutusData, PlutusList};
6
7/// Datum attached to 'VersionOraclePolicy' tokens stored on the 'VersionOracleValidator' script.
8/// This datum is not versioned intentionally, as it is not subject to observation.
9///
10/// Original definition in the smart contracts:
11/// ```haskell
12/// data VersionOracleDatum = VersionOracleDatum
13/// { versionOracle :: VersionOracle
14/// -- ^ VersionOracle which identifies the script.
15/// , currencySymbol :: CurrencySymbol
16/// -- ^ Currency Symbol of the VersioningOraclePolicy tokens.
17/// }
18/// ```
19///
20/// See https://preview.cexplorer.io/tx/70923772056f153d646488c56ac04d1bc2f1326f074773e4f262c63e03b72a3d/contract#data
21/// for an example of transaction outputting this datum.
22#[derive(Clone, Debug, PartialEq)]
23pub struct VersionOracleDatum {
24	/// 'VersionOracle' of the script.
25	pub version_oracle: VersionOracle,
26	/// Script hash of the oracle policy where cached/versioned scripts are stored.
27	pub currency_symbol: ScriptHash,
28}
29
30impl TryFrom<PlutusData> for VersionOracleDatum {
31	type Error = DataDecodingError;
32	fn try_from(datum: PlutusData) -> DecodingResult<Self> {
33		datum
34			.as_list()
35			.filter(|datum| datum.len() == 2)
36			.and_then(|items| {
37				Some(VersionOracleDatum {
38					version_oracle: items.get(0).try_into().ok()?,
39					currency_symbol: items.get(1).try_into().ok()?,
40				})
41			})
42			.ok_or_else(|| {
43				decoding_error_and_log(&datum, "VersionOracleDatum", "Expected [u32, [u8;32]]")
44			})
45	}
46}
47
48impl From<VersionOracleDatum> for PlutusData {
49	fn from(datum: VersionOracleDatum) -> Self {
50		PlutusData::new_list(&{
51			let mut list = PlutusList::new();
52			list.add(&datum.version_oracle.into());
53			list.add(&datum.currency_symbol.into());
54			list
55		})
56	}
57}
58
59/// Original definition in the smart contracts:
60/// ```haskell
61/// newtype VersionOracle = VersionOracle
62///   { scriptId :: Integer
63///   -- ^ Unique identifier of the validator.
64///   }
65/// ```
66#[derive(Clone, Debug, PartialEq)]
67pub struct VersionOracle {
68	/// Id of the script in the script cache/versioning system. See [raw_scripts::ScriptId].
69	pub script_id: u32,
70}
71
72impl TryFrom<PlutusData> for VersionOracle {
73	type Error = DataDecodingError;
74	fn try_from(datum: PlutusData) -> DecodingResult<Self> {
75		datum
76			.as_u32()
77			.and_then(|script_id| Some(VersionOracle { script_id }))
78			.ok_or_else(|| decoding_error_and_log(&datum, "VersionOracle", "Expected u32"))
79	}
80}
81
82impl From<VersionOracle> for PlutusData {
83	fn from(datum: VersionOracle) -> Self {
84		PlutusData::new_integer(&BigInt::from(datum.script_id as u32))
85	}
86}
87
88impl From<raw_scripts::ScriptId> for VersionOracle {
89	fn from(script_id: raw_scripts::ScriptId) -> Self {
90		VersionOracle { script_id: script_id as u32 }
91	}
92}
93
94/// Redeemer type for VersioningOracle minting policy.
95///
96/// Original definition in the smart contracts:
97/// ```haskell
98/// data VersionOraclePolicyRedeemer
99///   = -- | Mint initial versioning token. Used during Partner Chain initialization.
100///     InitializeVersionOracle VersionOracle ScriptHash
101///   | -- | Mint a new versioning token ensuring it contains correct datum and
102///     -- reference script.
103///     MintVersionOracle VersionOracle ScriptHash
104///   | -- | Burn existing versioning token.
105///     BurnVersionOracle VersionOracle
106/// ```
107#[derive(Clone, Debug, PartialEq)]
108pub enum VersionOraclePolicyRedeemer {
109	/// Mint initial versioning token. Used during Partner Chain initialization.
110	InitializeVersionOracle(VersionOracle, ScriptHash),
111	/// Mint a new versioning token ensuring it contains correct datum and
112	MintVersionOracle(VersionOracle, ScriptHash),
113	/// Burn existing versioning token.
114	BurnVersionOracle(VersionOracle),
115}
116
117impl TryFrom<PlutusData> for VersionOraclePolicyRedeemer {
118	type Error = DataDecodingError;
119	fn try_from(redeemer: PlutusData) -> DecodingResult<Self> {
120		redeemer
121			.as_constr_plutus_data()
122			.and_then(|constr| match From::<BigNum>::from(constr.alternative()) {
123				0u64 if constr.data().len() == 2 => {
124					Some(VersionOraclePolicyRedeemer::InitializeVersionOracle(
125						constr.data().get(0).try_into().ok()?,
126						constr.data().get(1).try_into().ok()?,
127					))
128				},
129				1u64 if constr.data().len() == 2 => {
130					Some(VersionOraclePolicyRedeemer::MintVersionOracle(
131						constr.data().get(0).try_into().ok()?,
132						constr.data().get(1).try_into().ok()?,
133					))
134				},
135				2u64 if constr.data().len() == 1 => {
136					Some(VersionOraclePolicyRedeemer::BurnVersionOracle(
137						constr.data().get(0).try_into().ok()?,
138					))
139				},
140				_ => None,
141			})
142			.ok_or_else(|| {
143				decoding_error_and_log(
144					&redeemer,
145					"VersionOraclePolicyRedeemer",
146					"Expected one of Constr 0 [u32, [u8;32]], Constr 1 [u32, [u8;32]], or Constr 2 [u32]",
147				)
148			})
149	}
150}
151
152impl From<VersionOraclePolicyRedeemer> for PlutusData {
153	fn from(redeemer: VersionOraclePolicyRedeemer) -> Self {
154		match redeemer {
155			VersionOraclePolicyRedeemer::InitializeVersionOracle(version_oracle, script_hash) => {
156				PlutusData::new_constr_plutus_data(&ConstrPlutusData::new(&0u64.into(), &{
157					let mut list = PlutusList::new();
158					list.add(&version_oracle.into());
159					list.add(&script_hash.into());
160					list
161				}))
162			},
163			VersionOraclePolicyRedeemer::MintVersionOracle(version_oracle, script_hash) => {
164				PlutusData::new_constr_plutus_data(&ConstrPlutusData::new(&1u64.into(), &{
165					let mut list = PlutusList::new();
166					list.add(&version_oracle.into());
167					list.add(&script_hash.into());
168					list
169				}))
170			},
171			VersionOraclePolicyRedeemer::BurnVersionOracle(version_oracle) => {
172				PlutusData::new_constr_plutus_data(&ConstrPlutusData::new(&2u64.into(), &{
173					let mut list = PlutusList::new();
174					list.add(&version_oracle.into());
175					list
176				}))
177			},
178		}
179	}
180}
181
182#[cfg(test)]
183mod tests {
184	use super::*;
185	use crate::test_helpers::*;
186	use hex_literal::hex;
187	use pretty_assertions::assert_eq;
188	use raw_scripts::ScriptId;
189
190	#[test]
191	fn decoding() {
192		let plutus_data = test_plutus_data!({"list": [
193			{"int": 32},
194			{"bytes": "e50a076eed80e645499abc26a5b33b61bef32f8cb1ab29b1ffcc1b88"}
195		]});
196
197		let expected_datum = VersionOracleDatum {
198			version_oracle: VersionOracle { script_id: ScriptId::GovernancePolicy as u32 },
199			currency_symbol: ScriptHash(hex!(
200				"e50a076eed80e645499abc26a5b33b61bef32f8cb1ab29b1ffcc1b88"
201			)),
202		};
203
204		assert_eq!(VersionOracleDatum::try_from(plutus_data).unwrap(), expected_datum)
205	}
206
207	#[test]
208	fn encoding() {
209		let datum = VersionOracleDatum {
210			version_oracle: VersionOracle { script_id: ScriptId::GovernancePolicy as u32 },
211			currency_symbol: ScriptHash(hex!(
212				"e50a076eed80e645499abc26a5b33b61bef32f8cb1ab29b1ffcc1b88"
213			)),
214		};
215
216		let expected_plutus_data = test_plutus_data!({"list": [
217			{"int": 32},
218			{"bytes": "e50a076eed80e645499abc26a5b33b61bef32f8cb1ab29b1ffcc1b88"}
219		]});
220
221		assert_eq!(PlutusData::from(datum), expected_plutus_data)
222	}
223}