Fix ToPlutusData serializer for V3
This is intense, as we still want to preserve the serializer for V1 & V2, and I've tried as much as possible to avoid polluting the application layer with many enum types such as: ``` pub enum TxOut { V1(TransactionOutput), V2(TransactionOutput), V3(TransactionOutput), } ``` Those types make working with the script context cumbersome, and are only truly required to provide different serialisation strategies. So instead, we keep one top-level `TxInfo V1/V2/V3` type, and we ensure to pass serialization strategies as type wrappers. This way, the strategy propagates through the structure up until it's eliminated when it reaches the relevant types. All-in-all, this strikes a correct balance between maintainability and repetition; and it makes it possible to define _different but mostly identical_ encoders for the various versions. With it, I've been able to successfully encode a V3 script context and match it against one produced using the Haskell libraries. More to come.
This commit is contained in:
parent
f848bad3f2
commit
821f7bd8c7
|
@ -1942,7 +1942,6 @@ checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pallas-addresses"
|
name = "pallas-addresses"
|
||||||
version = "0.29.0"
|
version = "0.29.0"
|
||||||
source = "git+https://github.com/KtorZ/pallas.git?rev=8ea5a1adc9919b70b213dfe597e920d6e113120c#8ea5a1adc9919b70b213dfe597e920d6e113120c"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base58",
|
"base58",
|
||||||
"bech32",
|
"bech32",
|
||||||
|
@ -1957,7 +1956,6 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pallas-codec"
|
name = "pallas-codec"
|
||||||
version = "0.29.0"
|
version = "0.29.0"
|
||||||
source = "git+https://github.com/KtorZ/pallas.git?rev=8ea5a1adc9919b70b213dfe597e920d6e113120c#8ea5a1adc9919b70b213dfe597e920d6e113120c"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hex",
|
"hex",
|
||||||
"minicbor",
|
"minicbor",
|
||||||
|
@ -1969,7 +1967,6 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pallas-crypto"
|
name = "pallas-crypto"
|
||||||
version = "0.29.0"
|
version = "0.29.0"
|
||||||
source = "git+https://github.com/KtorZ/pallas.git?rev=8ea5a1adc9919b70b213dfe597e920d6e113120c#8ea5a1adc9919b70b213dfe597e920d6e113120c"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cryptoxide",
|
"cryptoxide",
|
||||||
"hex",
|
"hex",
|
||||||
|
@ -1982,7 +1979,6 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pallas-primitives"
|
name = "pallas-primitives"
|
||||||
version = "0.29.0"
|
version = "0.29.0"
|
||||||
source = "git+https://github.com/KtorZ/pallas.git?rev=8ea5a1adc9919b70b213dfe597e920d6e113120c#8ea5a1adc9919b70b213dfe597e920d6e113120c"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base58",
|
"base58",
|
||||||
"bech32",
|
"bech32",
|
||||||
|
@ -1997,7 +1993,6 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pallas-traverse"
|
name = "pallas-traverse"
|
||||||
version = "0.29.0"
|
version = "0.29.0"
|
||||||
source = "git+https://github.com/KtorZ/pallas.git?rev=8ea5a1adc9919b70b213dfe597e920d6e113120c#8ea5a1adc9919b70b213dfe597e920d6e113120c"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hex",
|
"hex",
|
||||||
"itertools 0.13.0",
|
"itertools 0.13.0",
|
||||||
|
@ -3209,6 +3204,7 @@ dependencies = [
|
||||||
"hex",
|
"hex",
|
||||||
"indexmap 1.9.3",
|
"indexmap 1.9.3",
|
||||||
"indoc",
|
"indoc",
|
||||||
|
"insta",
|
||||||
"itertools 0.10.5",
|
"itertools 0.10.5",
|
||||||
"k256",
|
"k256",
|
||||||
"miette 5.10.0",
|
"miette 5.10.0",
|
||||||
|
|
|
@ -42,5 +42,6 @@ k256 = { version = "0.13.0" }
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
indoc = "2.0.1"
|
indoc = "2.0.1"
|
||||||
|
insta.workspace = true
|
||||||
pretty_assertions = "1.3.0"
|
pretty_assertions = "1.3.0"
|
||||||
walkdir.workspace = true
|
walkdir.workspace = true
|
||||||
|
|
|
@ -32,19 +32,20 @@ pub fn eval_redeemer(
|
||||||
tx_info: TxInfo,
|
tx_info: TxInfo,
|
||||||
program: Program<NamedDeBruijn>,
|
program: Program<NamedDeBruijn>,
|
||||||
) -> Result<Redeemer, Error> {
|
) -> Result<Redeemer, Error> {
|
||||||
let purpose = tx_info
|
let script_context = tx_info
|
||||||
.purpose(redeemer)
|
.into_script_context(redeemer, datum.as_ref())
|
||||||
.expect("redeemer's purpose shall be known by this point.");
|
.expect("couldn't create script context from transaction?");
|
||||||
|
|
||||||
let script_context = ScriptContext { tx_info, purpose };
|
let program = match script_context {
|
||||||
|
ScriptContext::V1V2 { .. } => if let Some(datum) = datum {
|
||||||
let program = if let Some(datum) = datum {
|
program.apply_data(datum)
|
||||||
program.apply_data(datum)
|
} else {
|
||||||
} else {
|
program
|
||||||
program
|
}
|
||||||
}
|
.apply_data(redeemer.data.clone())
|
||||||
.apply_data(redeemer.data.clone())
|
.apply_data(script_context.to_plutus_data()),
|
||||||
.apply_data(script_context.to_plutus_data());
|
ScriptContext::V3 { .. } => program.apply_data(script_context.to_plutus_data()),
|
||||||
|
};
|
||||||
|
|
||||||
let mut eval_result = if let Some(costs) = cost_mdl_opt {
|
let mut eval_result = if let Some(costs) = cost_mdl_opt {
|
||||||
program.eval_as(lang, costs, Some(initial_budget))
|
program.eval_as(lang, costs, Some(initial_budget))
|
||||||
|
|
|
@ -81,7 +81,7 @@ pub fn scripts_needed(tx: &MintedTx, utxos: &[ResolvedInput]) -> Result<ScriptsN
|
||||||
|
|
||||||
if let Address::Shelley(a) = address {
|
if let Address::Shelley(a) = address {
|
||||||
if let ShelleyPaymentPart::Script(h) = a.payment() {
|
if let ShelleyPaymentPart::Script(h) = a.payment() {
|
||||||
spend.push((ScriptPurpose::Spending(input.clone()), *h));
|
spend.push((ScriptPurpose::Spending(input.clone(), ()), *h));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -241,7 +241,7 @@ fn build_redeemer_key(
|
||||||
Ok(redeemer_key)
|
Ok(redeemer_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptPurpose::Spending(txin) => {
|
ScriptPurpose::Spending(txin, ()) => {
|
||||||
let redeemer_key = tx_body
|
let redeemer_key = tx_body
|
||||||
.inputs
|
.inputs
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -25,47 +25,48 @@ pub struct ResolvedInput {
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct TxInInfo {
|
pub struct TxInInfo {
|
||||||
pub out_ref: TransactionInput,
|
pub out_ref: TransactionInput,
|
||||||
pub resolved: TxOut,
|
pub resolved: TransactionOutput,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
pub fn output_address(output: &TransactionOutput) -> Address {
|
||||||
pub enum TxOut {
|
match output {
|
||||||
V1(TransactionOutput),
|
TransactionOutput::Legacy(x) => Address::from_bytes(&x.address).unwrap(),
|
||||||
V2(TransactionOutput),
|
TransactionOutput::PostAlonzo(x) => Address::from_bytes(&x.address).unwrap(),
|
||||||
}
|
|
||||||
|
|
||||||
impl TxOut {
|
|
||||||
pub fn address(&self) -> Address {
|
|
||||||
let address_from_output = |output: &TransactionOutput| match output {
|
|
||||||
TransactionOutput::Legacy(x) => Address::from_bytes(&x.address).unwrap(),
|
|
||||||
TransactionOutput::PostAlonzo(x) => Address::from_bytes(&x.address).unwrap(),
|
|
||||||
};
|
|
||||||
match self {
|
|
||||||
TxOut::V1(output) => address_from_output(output),
|
|
||||||
TxOut::V2(output) => address_from_output(output),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn datum(&self) -> Option<DatumOption> {
|
|
||||||
let datum_from_output = |output: &TransactionOutput| match output {
|
|
||||||
TransactionOutput::Legacy(x) => x.datum_hash.map(DatumOption::Hash),
|
|
||||||
TransactionOutput::PostAlonzo(x) => x.datum_option.clone(),
|
|
||||||
};
|
|
||||||
match self {
|
|
||||||
TxOut::V1(output) => datum_from_output(output),
|
|
||||||
TxOut::V2(output) => datum_from_output(output),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn output_datum(output: &TransactionOutput) -> Option<DatumOption> {
|
||||||
|
match output {
|
||||||
|
TransactionOutput::Legacy(x) => x.datum_hash.map(DatumOption::Hash),
|
||||||
|
TransactionOutput::PostAlonzo(x) => x.datum_option.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The ScriptPurpose is part of the ScriptContext is the case of Plutus V1 and V2.
|
||||||
|
/// It is superseded by the ScriptInfo in PlutusV3.
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum ScriptPurpose {
|
pub enum ScriptInfo<T> {
|
||||||
Minting(PolicyId),
|
Minting(PolicyId),
|
||||||
Spending(TransactionInput),
|
Spending(TransactionInput, T),
|
||||||
Rewarding(StakeCredential),
|
Rewarding(StakeCredential),
|
||||||
Certifying(Certificate),
|
Certifying(Certificate),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type ScriptPurpose = ScriptInfo<()>;
|
||||||
|
|
||||||
|
impl ScriptPurpose {
|
||||||
|
pub fn into_script_info<T>(self, datum: T) -> ScriptInfo<T> {
|
||||||
|
match self {
|
||||||
|
Self::Minting(policy_id) => ScriptInfo::Minting(policy_id),
|
||||||
|
Self::Spending(transaction_output, ()) => {
|
||||||
|
ScriptInfo::Spending(transaction_output, datum)
|
||||||
|
}
|
||||||
|
Self::Rewarding(stake_credential) => ScriptInfo::Rewarding(stake_credential),
|
||||||
|
Self::Certifying(certificate) => ScriptInfo::Certifying(certificate),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum ScriptVersion {
|
pub enum ScriptVersion {
|
||||||
Native(NativeScript),
|
Native(NativeScript),
|
||||||
|
@ -184,7 +185,7 @@ impl DataLookupTable {
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct TxInfoV1 {
|
pub struct TxInfoV1 {
|
||||||
pub inputs: Vec<TxInInfo>,
|
pub inputs: Vec<TxInInfo>,
|
||||||
pub outputs: Vec<TxOut>,
|
pub outputs: Vec<TransactionOutput>,
|
||||||
pub fee: Value,
|
pub fee: Value,
|
||||||
pub mint: MintValue,
|
pub mint: MintValue,
|
||||||
pub certificates: Vec<Certificate>,
|
pub certificates: Vec<Certificate>,
|
||||||
|
@ -219,8 +220,8 @@ impl TxInfoV1 {
|
||||||
|
|
||||||
Ok(TxInfo::V1(TxInfoV1 {
|
Ok(TxInfo::V1(TxInfoV1 {
|
||||||
inputs,
|
inputs,
|
||||||
outputs: get_outputs_info(TxOut::V1, &tx.transaction_body.outputs[..]),
|
outputs: get_outputs_info(&tx.transaction_body.outputs[..]),
|
||||||
fee: get_fee_info(&tx.transaction_body.fee),
|
fee: Value::Coin(get_fee_info(&tx.transaction_body.fee)),
|
||||||
mint,
|
mint,
|
||||||
certificates,
|
certificates,
|
||||||
withdrawals: withdrawals.into(),
|
withdrawals: withdrawals.into(),
|
||||||
|
@ -237,7 +238,7 @@ impl TxInfoV1 {
|
||||||
pub struct TxInfoV2 {
|
pub struct TxInfoV2 {
|
||||||
pub inputs: Vec<TxInInfo>,
|
pub inputs: Vec<TxInInfo>,
|
||||||
pub reference_inputs: Vec<TxInInfo>,
|
pub reference_inputs: Vec<TxInInfo>,
|
||||||
pub outputs: Vec<TxOut>,
|
pub outputs: Vec<TransactionOutput>,
|
||||||
pub fee: Value,
|
pub fee: Value,
|
||||||
pub mint: MintValue,
|
pub mint: MintValue,
|
||||||
pub certificates: Vec<Certificate>,
|
pub certificates: Vec<Certificate>,
|
||||||
|
@ -277,8 +278,8 @@ impl TxInfoV2 {
|
||||||
Ok(TxInfo::V2(TxInfoV2 {
|
Ok(TxInfo::V2(TxInfoV2 {
|
||||||
inputs,
|
inputs,
|
||||||
reference_inputs,
|
reference_inputs,
|
||||||
outputs: get_outputs_info(TxOut::V2, &tx.transaction_body.outputs[..]),
|
outputs: get_outputs_info(&tx.transaction_body.outputs[..]),
|
||||||
fee: get_fee_info(&tx.transaction_body.fee),
|
fee: Value::Coin(get_fee_info(&tx.transaction_body.fee)),
|
||||||
mint,
|
mint,
|
||||||
certificates,
|
certificates,
|
||||||
withdrawals,
|
withdrawals,
|
||||||
|
@ -295,8 +296,8 @@ impl TxInfoV2 {
|
||||||
pub struct TxInfoV3 {
|
pub struct TxInfoV3 {
|
||||||
pub inputs: Vec<TxInInfo>,
|
pub inputs: Vec<TxInInfo>,
|
||||||
pub reference_inputs: Vec<TxInInfo>,
|
pub reference_inputs: Vec<TxInInfo>,
|
||||||
pub outputs: Vec<TxOut>,
|
pub outputs: Vec<TransactionOutput>,
|
||||||
pub fee: Value,
|
pub fee: Coin,
|
||||||
pub mint: MintValue,
|
pub mint: MintValue,
|
||||||
pub certificates: Vec<Certificate>,
|
pub certificates: Vec<Certificate>,
|
||||||
pub withdrawals: KeyValuePairs<Address, Coin>,
|
pub withdrawals: KeyValuePairs<Address, Coin>,
|
||||||
|
@ -323,7 +324,7 @@ impl TxInfoV3 {
|
||||||
inputs: tx_info_v2.inputs,
|
inputs: tx_info_v2.inputs,
|
||||||
reference_inputs: tx_info_v2.reference_inputs,
|
reference_inputs: tx_info_v2.reference_inputs,
|
||||||
outputs: tx_info_v2.outputs,
|
outputs: tx_info_v2.outputs,
|
||||||
fee: tx_info_v2.fee,
|
fee: get_fee_info(&tx.transaction_body.fee),
|
||||||
mint: tx_info_v2.mint,
|
mint: tx_info_v2.mint,
|
||||||
certificates: tx_info_v2.certificates,
|
certificates: tx_info_v2.certificates,
|
||||||
withdrawals: tx_info_v2.withdrawals,
|
withdrawals: tx_info_v2.withdrawals,
|
||||||
|
@ -347,19 +348,41 @@ pub enum TxInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TxInfo {
|
impl TxInfo {
|
||||||
pub fn purpose(&self, needle: &Redeemer) -> Option<ScriptPurpose> {
|
pub fn into_script_context(
|
||||||
|
self,
|
||||||
|
redeemer: &Redeemer,
|
||||||
|
datum: Option<&PlutusData>,
|
||||||
|
) -> Option<ScriptContext> {
|
||||||
match self {
|
match self {
|
||||||
TxInfo::V1(TxInfoV1 { redeemers, .. })
|
TxInfo::V1(TxInfoV1 { ref redeemers, .. })
|
||||||
| TxInfo::V2(TxInfoV2 { redeemers, .. })
|
| TxInfo::V2(TxInfoV2 { ref redeemers, .. }) => redeemers
|
||||||
| TxInfo::V3(TxInfoV3 { redeemers, .. }) => {
|
.iter()
|
||||||
redeemers.iter().find_map(|(purpose, redeemer)| {
|
.find_map(move |(purpose, some_redeemer)| {
|
||||||
if redeemer == needle {
|
if redeemer == some_redeemer {
|
||||||
Some(purpose.clone())
|
Some(purpose.clone())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
.map(move |purpose| ScriptContext::V1V2 {
|
||||||
|
tx_info: self,
|
||||||
|
purpose: purpose.clone().into(),
|
||||||
|
}),
|
||||||
|
|
||||||
|
TxInfo::V3(TxInfoV3 { ref redeemers, .. }) => redeemers
|
||||||
|
.iter()
|
||||||
|
.find_map(move |(purpose, some_redeemer)| {
|
||||||
|
if redeemer == some_redeemer {
|
||||||
|
Some(purpose.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(move |purpose| ScriptContext::V3 {
|
||||||
|
tx_info: self,
|
||||||
|
redeemer: redeemer.data.clone(),
|
||||||
|
purpose: purpose.clone().into_script_info(datum.cloned()),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,9 +420,16 @@ impl TxInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct ScriptContext {
|
pub enum ScriptContext {
|
||||||
pub tx_info: TxInfo,
|
V1V2 {
|
||||||
pub purpose: ScriptPurpose,
|
tx_info: TxInfo,
|
||||||
|
purpose: Box<ScriptPurpose>,
|
||||||
|
},
|
||||||
|
V3 {
|
||||||
|
tx_info: TxInfo,
|
||||||
|
redeemer: PlutusData,
|
||||||
|
purpose: ScriptInfo<Option<PlutusData>>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- Time conversion: slot range => posix time range
|
//---- Time conversion: slot range => posix time range
|
||||||
|
@ -470,7 +500,7 @@ pub fn get_tx_in_info_v1(
|
||||||
|
|
||||||
Ok(TxInInfo {
|
Ok(TxInInfo {
|
||||||
out_ref: utxo.input.clone(),
|
out_ref: utxo.input.clone(),
|
||||||
resolved: TxOut::V1(sort_tx_out_value(&utxo.output)),
|
resolved: sort_tx_out_value(&utxo.output),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -506,7 +536,7 @@ pub fn get_tx_in_info_v2(
|
||||||
|
|
||||||
Ok(TxInInfo {
|
Ok(TxInInfo {
|
||||||
out_ref: utxo.input.clone(),
|
out_ref: utxo.input.clone(),
|
||||||
resolved: TxOut::V2(sort_tx_out_value(&utxo.output)),
|
resolved: sort_tx_out_value(&utxo.output),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -521,19 +551,16 @@ pub fn get_mint_info(mint: &Option<Mint>) -> MintValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_outputs_info(
|
pub fn get_outputs_info(outputs: &[MintedTransactionOutput]) -> Vec<TransactionOutput> {
|
||||||
to_tx_out: fn(TransactionOutput) -> TxOut,
|
|
||||||
outputs: &[MintedTransactionOutput],
|
|
||||||
) -> Vec<TxOut> {
|
|
||||||
outputs
|
outputs
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|output| to_tx_out(sort_tx_out_value(&output.into())))
|
.map(|output| sort_tx_out_value(&output.into()))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_fee_info(fee: &Coin) -> Value {
|
pub fn get_fee_info(fee: &Coin) -> Coin {
|
||||||
Value::Coin(*fee)
|
*fee
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_certificates_info(certificates: &Option<NonEmptySet<Certificate>>) -> Vec<Certificate> {
|
pub fn get_certificates_info(certificates: &Option<NonEmptySet<Certificate>>) -> Vec<Certificate> {
|
||||||
|
@ -649,7 +676,7 @@ fn script_purpose_builder<'a>(
|
||||||
RedeemerTag::Spend => inputs
|
RedeemerTag::Spend => inputs
|
||||||
.get(index)
|
.get(index)
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|i| ScriptPurpose::Spending(i.out_ref)),
|
.map(|i| ScriptPurpose::Spending(i.out_ref, ())),
|
||||||
RedeemerTag::Cert => certificates
|
RedeemerTag::Cert => certificates
|
||||||
.get(index)
|
.get(index)
|
||||||
.cloned()
|
.cloned()
|
||||||
|
@ -756,13 +783,13 @@ pub fn find_script(
|
||||||
})?
|
})?
|
||||||
.get(redeemer.index as usize)
|
.get(redeemer.index as usize)
|
||||||
.ok_or(Error::MissingScriptForRedeemer)
|
.ok_or(Error::MissingScriptForRedeemer)
|
||||||
.and_then(|input| match input.resolved.address() {
|
.and_then(|input| match output_address(&input.resolved) {
|
||||||
Address::Shelley(shelley_address) => {
|
Address::Shelley(shelley_address) => {
|
||||||
let hash = shelley_address.payment().as_hash();
|
let hash = shelley_address.payment().as_hash();
|
||||||
|
|
||||||
let script = lookup_script(hash);
|
let script = lookup_script(hash);
|
||||||
|
|
||||||
let datum = lookup_datum(input.resolved.datum());
|
let datum = lookup_datum(output_datum(&input.resolved));
|
||||||
|
|
||||||
script.and_then(|(script, _)| Ok((script, Some(datum?))))
|
script.and_then(|(script, _)| Ok((script, Some(datum?))))
|
||||||
}
|
}
|
||||||
|
@ -775,7 +802,7 @@ pub fn find_script(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_alonzo_value(value: &alonzo::Value) -> Value {
|
pub fn from_alonzo_value(value: &alonzo::Value) -> Value {
|
||||||
match value {
|
match value {
|
||||||
alonzo::Value::Coin(coin) => Value::Coin(*coin),
|
alonzo::Value::Coin(coin) => Value::Coin(*coin),
|
||||||
alonzo::Value::Multiasset(coin, assets) if assets.is_empty() => Value::Coin(*coin),
|
alonzo::Value::Multiasset(coin, assets) if assets.is_empty() => Value::Coin(*coin),
|
||||||
|
@ -810,6 +837,15 @@ fn from_alonzo_value(value: &alonzo::Value) -> Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_alonzo_output(output: &alonzo::TransactionOutput) -> TransactionOutput {
|
||||||
|
TransactionOutput::PostAlonzo(PostAlonzoTransactionOutput {
|
||||||
|
address: output.address.clone(),
|
||||||
|
value: from_alonzo_value(&output.amount),
|
||||||
|
datum_option: output.datum_hash.map(DatumOption::Hash),
|
||||||
|
script_ref: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// --------------------- Sorting
|
// --------------------- Sorting
|
||||||
|
|
||||||
fn sort_tx_out_value(tx_output: &TransactionOutput) -> TransactionOutput {
|
fn sort_tx_out_value(tx_output: &TransactionOutput) -> TransactionOutput {
|
||||||
|
@ -882,3 +918,91 @@ fn sort_redeemers(a: &RedeemersKey, b: &RedeemersKey) -> Ordering {
|
||||||
redeemer_tag_as_usize(&a.tag).cmp(&redeemer_tag_as_usize(&b.tag))
|
redeemer_tag_as_usize(&a.tag).cmp(&redeemer_tag_as_usize(&b.tag))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{
|
||||||
|
ast::Data,
|
||||||
|
tx::{
|
||||||
|
script_context::{TxInfo, TxInfoV3},
|
||||||
|
to_plutus_data::ToPlutusData,
|
||||||
|
ResolvedInput, SlotConfig,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use pallas_primitives::{
|
||||||
|
conway::{ExUnits, PlutusData, Redeemer, RedeemerTag, TransactionInput, TransactionOutput},
|
||||||
|
Fragment,
|
||||||
|
};
|
||||||
|
use pallas_traverse::{Era, MultiEraTx};
|
||||||
|
|
||||||
|
fn fixture_tx_info(transaction: &str, inputs: &str, outputs: &str) -> TxInfo {
|
||||||
|
let transaction_bytes = hex::decode(transaction).unwrap();
|
||||||
|
let inputs_bytes = hex::decode(inputs).unwrap();
|
||||||
|
let outputs_bytes = hex::decode(outputs).unwrap();
|
||||||
|
|
||||||
|
let inputs = Vec::<TransactionInput>::decode_fragment(inputs_bytes.as_slice()).unwrap();
|
||||||
|
let outputs = Vec::<TransactionOutput>::decode_fragment(outputs_bytes.as_slice()).unwrap();
|
||||||
|
let resolved_inputs: Vec<ResolvedInput> = inputs
|
||||||
|
.iter()
|
||||||
|
.zip(outputs.iter())
|
||||||
|
.map(|(input, output)| ResolvedInput {
|
||||||
|
input: input.clone(),
|
||||||
|
output: output.clone(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
TxInfoV3::from_transaction(
|
||||||
|
MultiEraTx::decode_for_era(Era::Conway, transaction_bytes.as_slice())
|
||||||
|
.unwrap()
|
||||||
|
.as_conway()
|
||||||
|
.unwrap(),
|
||||||
|
&resolved_inputs,
|
||||||
|
&SlotConfig::default(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn from_haskell(data: &str) -> PlutusData {
|
||||||
|
PlutusData::decode_fragment(hex::decode(data).unwrap().as_slice()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tx_to_plutus_data() {
|
||||||
|
let datum = Some(Data::constr(0, Vec::new()));
|
||||||
|
|
||||||
|
let redeemer = Redeemer {
|
||||||
|
tag: RedeemerTag::Spend,
|
||||||
|
index: 0,
|
||||||
|
data: Data::constr(0, Vec::new()),
|
||||||
|
ex_units: ExUnits {
|
||||||
|
mem: 1000000,
|
||||||
|
steps: 100000000,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let script_context = fixture_tx_info(
|
||||||
|
"84a7008182582000000000000000000000000000000000000000000000000000\
|
||||||
|
0000000000000000018182581d60111111111111111111111111111111111111\
|
||||||
|
111111111111111111111a3b9aca0002182a0b5820ffffffffffffffffffffff\
|
||||||
|
ffffffffffffffffffffffffffffffffffffffffff0d81825820000000000000\
|
||||||
|
0000000000000000000000000000000000000000000000000000001082581d60\
|
||||||
|
000000000000000000000000000000000000000000000000000000001a3b9aca\
|
||||||
|
001101a20581840000d87980821a000f42401a05f5e100078152510101003222\
|
||||||
|
253330044a229309b2b2b9a1f5f6",
|
||||||
|
"8182582000000000000000000000000000000000000000000000000000000000\
|
||||||
|
0000000000",
|
||||||
|
"81a300581d7039f47fd3b388ef53c48f08de24766d3e55dade6cae908cc24e0f\
|
||||||
|
4f3e011a3b9aca00028201d81843d87980",
|
||||||
|
)
|
||||||
|
.into_script_context(&redeemer, datum.as_ref())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// NOTE: The initial snapshot has been generated using the Haskell
|
||||||
|
// implementation of the ledger library for that same serialized
|
||||||
|
// transactions. It is meant to control that our construction of the
|
||||||
|
// script context and its serialization matches exactly those
|
||||||
|
// from the Haskell ledger / cardano node.
|
||||||
|
insta::assert_debug_snapshot!(script_context.to_plutus_data())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,23 +1,20 @@
|
||||||
use super::script_context::{ScriptContext, ScriptPurpose, TimeRange, TxInInfo, TxInfo, TxOut};
|
use super::script_context::{
|
||||||
use crate::machine::runtime::{convert_constr_to_tag, ANY_TAG};
|
ScriptContext, ScriptInfo, ScriptPurpose, TimeRange, TxInInfo, TxInfo,
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
ast::Data,
|
||||||
|
machine::runtime::{convert_constr_to_tag, ANY_TAG},
|
||||||
|
tx::script_context::from_alonzo_output,
|
||||||
|
};
|
||||||
use pallas_addresses::{Address, ShelleyDelegationPart, ShelleyPaymentPart, StakePayload};
|
use pallas_addresses::{Address, ShelleyDelegationPart, ShelleyPaymentPart, StakePayload};
|
||||||
use pallas_codec::utils::{AnyUInt, Bytes, Int, KeyValuePairs};
|
use pallas_codec::utils::{AnyUInt, Bytes, Int, KeyValuePairs, NonEmptyKeyValuePairs};
|
||||||
use pallas_crypto::hash::Hash;
|
use pallas_crypto::hash::Hash;
|
||||||
use pallas_primitives::conway::{
|
use pallas_primitives::conway::{
|
||||||
AssetName, BigInt, Certificate, Constr, DatumOption, Mint, PlutusData, PseudoScript, Redeemer,
|
AssetName, BigInt, Certificate, Coin, Constr, DatumOption, Mint, PlutusData, PolicyId,
|
||||||
ScriptRef, StakeCredential, TransactionInput, TransactionOutput, Value,
|
PseudoScript, Redeemer, ScriptRef, StakeCredential, TransactionInput, TransactionOutput, Value,
|
||||||
};
|
};
|
||||||
use pallas_traverse::ComputeHash;
|
use pallas_traverse::ComputeHash;
|
||||||
|
|
||||||
fn wrap_with_constr(index: u64, data: PlutusData) -> PlutusData {
|
|
||||||
let converted = convert_constr_to_tag(index);
|
|
||||||
PlutusData::Constr(Constr {
|
|
||||||
tag: converted.unwrap_or(ANY_TAG),
|
|
||||||
any_constructor: converted.map_or(Some(index), |_| None),
|
|
||||||
fields: vec![data],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wrap_multiple_with_constr(index: u64, data: Vec<PlutusData>) -> PlutusData {
|
fn wrap_multiple_with_constr(index: u64, data: Vec<PlutusData>) -> PlutusData {
|
||||||
let converted = convert_constr_to_tag(index);
|
let converted = convert_constr_to_tag(index);
|
||||||
PlutusData::Constr(Constr {
|
PlutusData::Constr(Constr {
|
||||||
|
@ -27,17 +24,20 @@ fn wrap_multiple_with_constr(index: u64, data: Vec<PlutusData>) -> PlutusData {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn wrap_with_constr(index: u64, data: PlutusData) -> PlutusData {
|
||||||
|
wrap_multiple_with_constr(index, vec![data])
|
||||||
|
}
|
||||||
|
|
||||||
fn empty_constr(index: u64) -> PlutusData {
|
fn empty_constr(index: u64) -> PlutusData {
|
||||||
let converted = convert_constr_to_tag(index);
|
wrap_multiple_with_constr(index, vec![])
|
||||||
PlutusData::Constr(Constr {
|
|
||||||
tag: converted.unwrap_or(ANY_TAG),
|
|
||||||
any_constructor: converted.map_or(Some(index), |_| None),
|
|
||||||
fields: vec![],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WithWrappedTransactionId<'a, T>(&'a T);
|
struct WithWrappedTransactionId<'a, T>(&'a T);
|
||||||
|
|
||||||
|
struct WithZeroAdaAsset<'a, T>(&'a T);
|
||||||
|
|
||||||
|
struct WithOptionDatum<'a, T>(&'a T);
|
||||||
|
|
||||||
pub trait ToPlutusData {
|
pub trait ToPlutusData {
|
||||||
fn to_plutus_data(&self) -> PlutusData;
|
fn to_plutus_data(&self) -> PlutusData;
|
||||||
}
|
}
|
||||||
|
@ -165,6 +165,19 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> ToPlutusData for WithWrappedTransactionId<'a, KeyValuePairs<ScriptPurpose, Redeemer>> {
|
||||||
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
|
let mut data_vec: Vec<(PlutusData, PlutusData)> = vec![];
|
||||||
|
for (key, value) in self.0.iter() {
|
||||||
|
data_vec.push((
|
||||||
|
WithWrappedTransactionId(key).to_plutus_data(),
|
||||||
|
value.to_plutus_data(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
PlutusData::Map(KeyValuePairs::Def(data_vec))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<A: ToPlutusData> ToPlutusData for Option<A> {
|
impl<A: ToPlutusData> ToPlutusData for Option<A> {
|
||||||
fn to_plutus_data(&self) -> PlutusData {
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
match self {
|
match self {
|
||||||
|
@ -174,7 +187,6 @@ impl<A: ToPlutusData> ToPlutusData for Option<A> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does this here surely overwrite Option from above for DatumOption?
|
|
||||||
impl ToPlutusData for Option<DatumOption> {
|
impl ToPlutusData for Option<DatumOption> {
|
||||||
// NoOutputDatum = 0 | OutputDatumHash = 1 | OutputDatum = 2
|
// NoOutputDatum = 0 | OutputDatumHash = 1 | OutputDatum = 2
|
||||||
fn to_plutus_data(&self) -> PlutusData {
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
|
@ -224,68 +236,101 @@ impl ToPlutusData for u64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> ToPlutusData for WithZeroAdaAsset<'a, Value> {
|
||||||
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
|
match self.0 {
|
||||||
|
Value::Coin(coin) => {
|
||||||
|
PlutusData::Map(KeyValuePairs::Def(vec![coin_to_plutus_data(coin)]))
|
||||||
|
}
|
||||||
|
Value::Multiasset(coin, multiassets) => value_to_plutus_data(
|
||||||
|
multiassets.iter(),
|
||||||
|
|amount| u64::from(amount).to_plutus_data(),
|
||||||
|
vec![coin_to_plutus_data(coin)],
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToPlutusData for Value {
|
impl ToPlutusData for Value {
|
||||||
fn to_plutus_data(&self) -> PlutusData {
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
match self {
|
match self {
|
||||||
Value::Coin(coin) => PlutusData::Map(KeyValuePairs::Def(vec![(
|
Value::Coin(coin) => PlutusData::Map(KeyValuePairs::Def(if *coin > 0 {
|
||||||
|
vec![coin_to_plutus_data(coin)]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
})),
|
||||||
|
Value::Multiasset(coin, multiassets) => value_to_plutus_data(
|
||||||
|
multiassets.iter(),
|
||||||
|
|amount| u64::from(amount).to_plutus_data(),
|
||||||
|
if *coin > 0 {
|
||||||
|
vec![coin_to_plutus_data(coin)]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ToPlutusData for WithZeroAdaAsset<'a, MintValue> {
|
||||||
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
|
value_to_plutus_data(
|
||||||
|
self.0.mint_value.iter(),
|
||||||
|
|amount| i64::from(amount).to_plutus_data(),
|
||||||
|
vec![(
|
||||||
Bytes::from(vec![]).to_plutus_data(),
|
Bytes::from(vec![]).to_plutus_data(),
|
||||||
PlutusData::Map(KeyValuePairs::Def(vec![(
|
PlutusData::Map(KeyValuePairs::Def(vec![(
|
||||||
AssetName::from(vec![]).to_plutus_data(),
|
AssetName::from(vec![]).to_plutus_data(),
|
||||||
coin.to_plutus_data(),
|
0_i64.to_plutus_data(),
|
||||||
)])),
|
)])),
|
||||||
)])),
|
)],
|
||||||
Value::Multiasset(coin, multiassets) => {
|
)
|
||||||
let mut data_vec: Vec<(PlutusData, PlutusData)> = vec![(
|
|
||||||
Bytes::from(vec![]).to_plutus_data(),
|
|
||||||
PlutusData::Map(KeyValuePairs::Def(vec![(
|
|
||||||
AssetName::from(vec![]).to_plutus_data(),
|
|
||||||
coin.to_plutus_data(),
|
|
||||||
)])),
|
|
||||||
)];
|
|
||||||
|
|
||||||
for (policy_id, assets) in multiassets.iter() {
|
|
||||||
let mut assets_vec = vec![];
|
|
||||||
for (asset, amount) in assets.iter() {
|
|
||||||
assets_vec
|
|
||||||
.push((asset.to_plutus_data(), u64::from(amount).to_plutus_data()));
|
|
||||||
}
|
|
||||||
data_vec.push((
|
|
||||||
policy_id.to_plutus_data(),
|
|
||||||
PlutusData::Map(KeyValuePairs::Def(assets_vec)),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
PlutusData::Map(KeyValuePairs::Def(data_vec))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToPlutusData for MintValue {
|
impl ToPlutusData for MintValue {
|
||||||
fn to_plutus_data(&self) -> PlutusData {
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
let mut data_vec: Vec<(PlutusData, PlutusData)> = vec![(
|
value_to_plutus_data(
|
||||||
Bytes::from(vec![]).to_plutus_data(),
|
self.mint_value.iter(),
|
||||||
PlutusData::Map(KeyValuePairs::Def(vec![(
|
|amount| i64::from(amount).to_plutus_data(),
|
||||||
AssetName::from(vec![]).to_plutus_data(),
|
vec![],
|
||||||
0_i64.to_plutus_data(),
|
)
|
||||||
)])),
|
|
||||||
)];
|
|
||||||
|
|
||||||
for (policy_id, assets) in self.mint_value.iter() {
|
|
||||||
let mut assets_vec = vec![];
|
|
||||||
for (asset, amount) in assets.iter() {
|
|
||||||
assets_vec.push((asset.to_plutus_data(), i64::from(amount).to_plutus_data()));
|
|
||||||
}
|
|
||||||
data_vec.push((
|
|
||||||
policy_id.to_plutus_data(),
|
|
||||||
PlutusData::Map(KeyValuePairs::Def(assets_vec)),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
PlutusData::Map(KeyValuePairs::Def(data_vec))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn value_to_plutus_data<'a, I, Q>(
|
||||||
|
mint: I,
|
||||||
|
from_quantity: fn(&'a Q) -> PlutusData,
|
||||||
|
mut data_vec: Vec<(PlutusData, PlutusData)>,
|
||||||
|
) -> PlutusData
|
||||||
|
where
|
||||||
|
I: Iterator<Item = &'a (PolicyId, NonEmptyKeyValuePairs<AssetName, Q>)>,
|
||||||
|
Q: Clone,
|
||||||
|
{
|
||||||
|
for (policy_id, assets) in mint {
|
||||||
|
let mut assets_vec = vec![];
|
||||||
|
for (asset, amount) in assets.iter() {
|
||||||
|
assets_vec.push((asset.to_plutus_data(), from_quantity(amount)));
|
||||||
|
}
|
||||||
|
data_vec.push((
|
||||||
|
policy_id.to_plutus_data(),
|
||||||
|
PlutusData::Map(KeyValuePairs::Def(assets_vec)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
PlutusData::Map(KeyValuePairs::Def(data_vec))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn coin_to_plutus_data(coin: &Coin) -> (PlutusData, PlutusData) {
|
||||||
|
(
|
||||||
|
Bytes::from(vec![]).to_plutus_data(),
|
||||||
|
PlutusData::Map(KeyValuePairs::Def(vec![(
|
||||||
|
AssetName::from(vec![]).to_plutus_data(),
|
||||||
|
coin.to_plutus_data(),
|
||||||
|
)])),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
impl ToPlutusData for ScriptRef {
|
impl ToPlutusData for ScriptRef {
|
||||||
fn to_plutus_data(&self) -> PlutusData {
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
match &self {
|
match &self {
|
||||||
|
@ -299,67 +344,100 @@ impl ToPlutusData for ScriptRef {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToPlutusData for TxOut {
|
impl<'a> ToPlutusData for WithOptionDatum<'a, WithZeroAdaAsset<'a, Vec<TransactionOutput>>> {
|
||||||
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
|
PlutusData::Array(
|
||||||
|
self.0
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.map(|p| WithOptionDatum(&WithZeroAdaAsset(p)).to_plutus_data())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ToPlutusData for WithZeroAdaAsset<'a, Vec<TransactionOutput>> {
|
||||||
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
|
PlutusData::Array(
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.map(|p| WithZeroAdaAsset(p).to_plutus_data())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ToPlutusData for WithOptionDatum<'a, WithZeroAdaAsset<'a, TransactionOutput>> {
|
||||||
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
|
match self.0 .0 {
|
||||||
|
TransactionOutput::Legacy(legacy_output) => {
|
||||||
|
WithOptionDatum(&WithZeroAdaAsset(&from_alonzo_output(legacy_output)))
|
||||||
|
.to_plutus_data()
|
||||||
|
}
|
||||||
|
|
||||||
|
TransactionOutput::PostAlonzo(post_alonzo_output) => wrap_multiple_with_constr(
|
||||||
|
0,
|
||||||
|
vec![
|
||||||
|
Address::from_bytes(&post_alonzo_output.address)
|
||||||
|
.unwrap()
|
||||||
|
.to_plutus_data(),
|
||||||
|
WithZeroAdaAsset(&post_alonzo_output.value).to_plutus_data(),
|
||||||
|
match post_alonzo_output.datum_option {
|
||||||
|
Some(DatumOption::Hash(hash)) => Some(hash).to_plutus_data(),
|
||||||
|
_ => None::<Hash<32>>.to_plutus_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ToPlutusData for WithZeroAdaAsset<'a, TransactionOutput> {
|
||||||
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
|
match self.0 {
|
||||||
|
TransactionOutput::Legacy(legacy_output) => {
|
||||||
|
WithZeroAdaAsset(&from_alonzo_output(legacy_output)).to_plutus_data()
|
||||||
|
}
|
||||||
|
TransactionOutput::PostAlonzo(post_alonzo_output) => wrap_multiple_with_constr(
|
||||||
|
0,
|
||||||
|
vec![
|
||||||
|
Address::from_bytes(&post_alonzo_output.address)
|
||||||
|
.unwrap()
|
||||||
|
.to_plutus_data(),
|
||||||
|
WithZeroAdaAsset(&post_alonzo_output.value).to_plutus_data(),
|
||||||
|
post_alonzo_output.datum_option.to_plutus_data(),
|
||||||
|
post_alonzo_output
|
||||||
|
.script_ref
|
||||||
|
.as_ref()
|
||||||
|
.map(|s| s.clone().unwrap())
|
||||||
|
.to_plutus_data(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToPlutusData for TransactionOutput {
|
||||||
fn to_plutus_data(&self) -> PlutusData {
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
match self {
|
match self {
|
||||||
TxOut::V1(output) => match output {
|
TransactionOutput::Legacy(legacy_output) => {
|
||||||
// TransactionOutput::Legacy(legacy_output) => wrap_multiple_with_constr(
|
from_alonzo_output(legacy_output).to_plutus_data()
|
||||||
// 0,
|
}
|
||||||
// vec![
|
TransactionOutput::PostAlonzo(post_alonzo_output) => wrap_multiple_with_constr(
|
||||||
// Address::from_bytes(&legacy_output.address)
|
0,
|
||||||
// .unwrap()
|
vec![
|
||||||
// .to_plutus_data(),
|
Address::from_bytes(&post_alonzo_output.address)
|
||||||
// legacy_output.amount.to_plutus_data(),
|
.unwrap()
|
||||||
// legacy_output.datum_hash.to_plutus_data(),
|
.to_plutus_data(),
|
||||||
// ],
|
post_alonzo_output.value.to_plutus_data(),
|
||||||
// ),
|
post_alonzo_output.datum_option.to_plutus_data(),
|
||||||
TransactionOutput::Legacy(..) => unimplemented!("TransactionOutput::Legacy"),
|
post_alonzo_output
|
||||||
TransactionOutput::PostAlonzo(post_alonzo_output) => wrap_multiple_with_constr(
|
.script_ref
|
||||||
0,
|
.as_ref()
|
||||||
vec![
|
.map(|s| s.clone().unwrap())
|
||||||
Address::from_bytes(&post_alonzo_output.address)
|
.to_plutus_data(),
|
||||||
.unwrap()
|
],
|
||||||
.to_plutus_data(),
|
),
|
||||||
post_alonzo_output.value.to_plutus_data(),
|
|
||||||
match post_alonzo_output.datum_option {
|
|
||||||
Some(DatumOption::Hash(hash)) => Some(hash).to_plutus_data(),
|
|
||||||
_ => None::<Hash<32>>.to_plutus_data(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
),
|
|
||||||
},
|
|
||||||
TxOut::V2(output) => match output {
|
|
||||||
// TransactionOutput::Legacy(legacy_output) => wrap_multiple_with_constr(
|
|
||||||
// 0,
|
|
||||||
// vec![
|
|
||||||
// Address::from_bytes(&legacy_output.address)
|
|
||||||
// .unwrap()
|
|
||||||
// .to_plutus_data(),
|
|
||||||
// legacy_output.amount.to_plutus_data(),
|
|
||||||
// match legacy_output.datum_hash {
|
|
||||||
// Some(hash) => wrap_with_constr(1, hash.to_plutus_data()),
|
|
||||||
// _ => empty_constr(0),
|
|
||||||
// },
|
|
||||||
// None::<ScriptRef>.to_plutus_data(),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
TransactionOutput::Legacy(..) => unimplemented!("TransactionOutput::Legacy"),
|
|
||||||
TransactionOutput::PostAlonzo(post_alonzo_output) => wrap_multiple_with_constr(
|
|
||||||
0,
|
|
||||||
vec![
|
|
||||||
Address::from_bytes(&post_alonzo_output.address)
|
|
||||||
.unwrap()
|
|
||||||
.to_plutus_data(),
|
|
||||||
post_alonzo_output.value.to_plutus_data(),
|
|
||||||
post_alonzo_output.datum_option.to_plutus_data(),
|
|
||||||
post_alonzo_output
|
|
||||||
.script_ref
|
|
||||||
.as_ref()
|
|
||||||
.map(|s| s.clone().unwrap())
|
|
||||||
.to_plutus_data(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -570,24 +648,57 @@ impl ToPlutusData for TimeRange {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ToPlutusData for WithWrappedTransactionId<'a, Vec<TxInInfo>> {
|
impl<'a> ToPlutusData
|
||||||
|
for WithOptionDatum<'a, WithZeroAdaAsset<'a, WithWrappedTransactionId<'a, Vec<TxInInfo>>>>
|
||||||
|
{
|
||||||
fn to_plutus_data(&self) -> PlutusData {
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
PlutusData::Array(
|
PlutusData::Array(
|
||||||
self.0
|
self.0
|
||||||
|
.0
|
||||||
|
.0
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| WithWrappedTransactionId(p).to_plutus_data())
|
.map(|p| {
|
||||||
|
WithOptionDatum(&WithZeroAdaAsset(&WithWrappedTransactionId(p)))
|
||||||
|
.to_plutus_data()
|
||||||
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ToPlutusData for WithWrappedTransactionId<'a, TxInInfo> {
|
impl<'a> ToPlutusData for WithZeroAdaAsset<'a, WithWrappedTransactionId<'a, Vec<TxInInfo>>> {
|
||||||
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
|
PlutusData::Array(
|
||||||
|
self.0
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.map(|p| WithZeroAdaAsset(&WithWrappedTransactionId(p)).to_plutus_data())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ToPlutusData for WithZeroAdaAsset<'a, WithWrappedTransactionId<'a, TxInInfo>> {
|
||||||
fn to_plutus_data(&self) -> PlutusData {
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
wrap_multiple_with_constr(
|
wrap_multiple_with_constr(
|
||||||
0,
|
0,
|
||||||
vec![
|
vec![
|
||||||
WithWrappedTransactionId(&self.0.out_ref).to_plutus_data(),
|
WithWrappedTransactionId(&self.0 .0.out_ref).to_plutus_data(),
|
||||||
self.0.resolved.to_plutus_data(),
|
WithZeroAdaAsset(&self.0 .0.resolved).to_plutus_data(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ToPlutusData
|
||||||
|
for WithOptionDatum<'a, WithZeroAdaAsset<'a, WithWrappedTransactionId<'a, TxInInfo>>>
|
||||||
|
{
|
||||||
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
|
wrap_multiple_with_constr(
|
||||||
|
0,
|
||||||
|
vec![
|
||||||
|
WithWrappedTransactionId(&self.0 .0 .0.out_ref).to_plutus_data(),
|
||||||
|
WithOptionDatum(&WithZeroAdaAsset(&self.0 .0 .0.resolved)).to_plutus_data(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -605,11 +716,22 @@ impl ToPlutusData for TxInInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> ToPlutusData for WithWrappedTransactionId<'a, ScriptPurpose> {
|
||||||
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
|
match self.0 {
|
||||||
|
ScriptPurpose::Spending(out_ref, ()) => {
|
||||||
|
wrap_with_constr(1, WithWrappedTransactionId(out_ref).to_plutus_data())
|
||||||
|
}
|
||||||
|
otherwise => otherwise.to_plutus_data(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToPlutusData for ScriptPurpose {
|
impl ToPlutusData for ScriptPurpose {
|
||||||
fn to_plutus_data(&self) -> PlutusData {
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
match self {
|
match self {
|
||||||
ScriptPurpose::Minting(policy_id) => wrap_with_constr(0, policy_id.to_plutus_data()),
|
ScriptPurpose::Minting(policy_id) => wrap_with_constr(0, policy_id.to_plutus_data()),
|
||||||
ScriptPurpose::Spending(out_ref) => wrap_with_constr(1, out_ref.to_plutus_data()),
|
ScriptPurpose::Spending(out_ref, ()) => wrap_with_constr(1, out_ref.to_plutus_data()),
|
||||||
ScriptPurpose::Rewarding(stake_credential) => {
|
ScriptPurpose::Rewarding(stake_credential) => {
|
||||||
wrap_with_constr(2, stake_credential.to_plutus_data())
|
wrap_with_constr(2, stake_credential.to_plutus_data())
|
||||||
}
|
}
|
||||||
|
@ -618,16 +740,37 @@ impl ToPlutusData for ScriptPurpose {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> ToPlutusData for ScriptInfo<T>
|
||||||
|
where
|
||||||
|
T: ToPlutusData,
|
||||||
|
{
|
||||||
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
|
match self {
|
||||||
|
ScriptInfo::Minting(policy_id) => wrap_with_constr(0, policy_id.to_plutus_data()),
|
||||||
|
ScriptInfo::Spending(out_ref, datum) => {
|
||||||
|
wrap_multiple_with_constr(1, vec![out_ref.to_plutus_data(), datum.to_plutus_data()])
|
||||||
|
}
|
||||||
|
ScriptInfo::Rewarding(stake_credential) => {
|
||||||
|
wrap_with_constr(2, stake_credential.to_plutus_data())
|
||||||
|
}
|
||||||
|
ScriptInfo::Certifying(dcert) => wrap_with_constr(3, dcert.to_plutus_data()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToPlutusData for TxInfo {
|
impl ToPlutusData for TxInfo {
|
||||||
fn to_plutus_data(&self) -> PlutusData {
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
match self {
|
match self {
|
||||||
TxInfo::V1(tx_info) => wrap_multiple_with_constr(
|
TxInfo::V1(tx_info) => wrap_multiple_with_constr(
|
||||||
0,
|
0,
|
||||||
vec![
|
vec![
|
||||||
WithWrappedTransactionId(&tx_info.inputs).to_plutus_data(),
|
WithOptionDatum(&WithZeroAdaAsset(&WithWrappedTransactionId(
|
||||||
tx_info.outputs.to_plutus_data(),
|
&tx_info.inputs,
|
||||||
tx_info.fee.to_plutus_data(),
|
)))
|
||||||
tx_info.mint.to_plutus_data(),
|
.to_plutus_data(),
|
||||||
|
WithOptionDatum(&WithZeroAdaAsset(&tx_info.outputs)).to_plutus_data(),
|
||||||
|
WithZeroAdaAsset(&tx_info.fee).to_plutus_data(),
|
||||||
|
WithZeroAdaAsset(&tx_info.mint).to_plutus_data(),
|
||||||
tx_info.certificates.to_plutus_data(),
|
tx_info.certificates.to_plutus_data(),
|
||||||
tx_info.withdrawals.to_plutus_data(),
|
tx_info.withdrawals.to_plutus_data(),
|
||||||
tx_info.valid_range.to_plutus_data(),
|
tx_info.valid_range.to_plutus_data(),
|
||||||
|
@ -639,16 +782,17 @@ impl ToPlutusData for TxInfo {
|
||||||
TxInfo::V2(tx_info) => wrap_multiple_with_constr(
|
TxInfo::V2(tx_info) => wrap_multiple_with_constr(
|
||||||
0,
|
0,
|
||||||
vec![
|
vec![
|
||||||
WithWrappedTransactionId(&tx_info.inputs).to_plutus_data(),
|
WithZeroAdaAsset(&WithWrappedTransactionId(&tx_info.inputs)).to_plutus_data(),
|
||||||
WithWrappedTransactionId(&tx_info.reference_inputs).to_plutus_data(),
|
WithZeroAdaAsset(&WithWrappedTransactionId(&tx_info.reference_inputs))
|
||||||
tx_info.outputs.to_plutus_data(),
|
.to_plutus_data(),
|
||||||
tx_info.fee.to_plutus_data(),
|
WithZeroAdaAsset(&tx_info.outputs).to_plutus_data(),
|
||||||
tx_info.mint.to_plutus_data(),
|
WithZeroAdaAsset(&tx_info.fee).to_plutus_data(),
|
||||||
|
WithZeroAdaAsset(&tx_info.mint).to_plutus_data(),
|
||||||
tx_info.certificates.to_plutus_data(),
|
tx_info.certificates.to_plutus_data(),
|
||||||
tx_info.withdrawals.to_plutus_data(),
|
tx_info.withdrawals.to_plutus_data(),
|
||||||
tx_info.valid_range.to_plutus_data(),
|
tx_info.valid_range.to_plutus_data(),
|
||||||
tx_info.signatories.to_plutus_data(),
|
tx_info.signatories.to_plutus_data(),
|
||||||
tx_info.redeemers.to_plutus_data(),
|
WithWrappedTransactionId(&tx_info.redeemers).to_plutus_data(),
|
||||||
tx_info.data.to_plutus_data(),
|
tx_info.data.to_plutus_data(),
|
||||||
wrap_with_constr(0, tx_info.id.to_plutus_data()),
|
wrap_with_constr(0, tx_info.id.to_plutus_data()),
|
||||||
],
|
],
|
||||||
|
@ -668,6 +812,10 @@ impl ToPlutusData for TxInfo {
|
||||||
tx_info.redeemers.to_plutus_data(),
|
tx_info.redeemers.to_plutus_data(),
|
||||||
tx_info.data.to_plutus_data(),
|
tx_info.data.to_plutus_data(),
|
||||||
tx_info.id.to_plutus_data(),
|
tx_info.id.to_plutus_data(),
|
||||||
|
Data::map(vec![]), // TODO tx_info.votes :: Map Voter (Map GovernanceActionId Vote)
|
||||||
|
Data::list(vec![]), // TODO tx_info.proposal_procedures :: [ProposalProcedure]
|
||||||
|
empty_constr(1), // TODO tx_info.current_treasury_amount :: Haskell.Maybe V2.Lovelace
|
||||||
|
empty_constr(1), // TODO tx_info.treasury_donation :: Haskell.Maybe V2.Lovelace
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
@ -676,10 +824,27 @@ impl ToPlutusData for TxInfo {
|
||||||
|
|
||||||
impl ToPlutusData for ScriptContext {
|
impl ToPlutusData for ScriptContext {
|
||||||
fn to_plutus_data(&self) -> PlutusData {
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
wrap_multiple_with_constr(
|
match self {
|
||||||
0,
|
ScriptContext::V1V2 { tx_info, purpose } => wrap_multiple_with_constr(
|
||||||
vec![self.tx_info.to_plutus_data(), self.purpose.to_plutus_data()],
|
0,
|
||||||
)
|
vec![
|
||||||
|
tx_info.to_plutus_data(),
|
||||||
|
WithWrappedTransactionId(purpose.as_ref()).to_plutus_data(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
ScriptContext::V3 {
|
||||||
|
tx_info,
|
||||||
|
redeemer,
|
||||||
|
purpose,
|
||||||
|
} => wrap_multiple_with_constr(
|
||||||
|
0,
|
||||||
|
vec![
|
||||||
|
tx_info.to_plutus_data(),
|
||||||
|
redeemer.to_plutus_data(),
|
||||||
|
purpose.to_plutus_data(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
AIKEN=${1:-"cargo run -r --quiet --"}
|
||||||
|
|
||||||
TESTS=()
|
TESTS=()
|
||||||
for lang in $(ls script_context); do
|
for lang in $(ls script_context); do
|
||||||
for interaction in $(find script_context/$lang/validators -type f); do
|
for interaction in $(find script_context/$lang/validators -type f); do
|
||||||
title=$(basename $interaction)
|
title=$(basename $interaction)
|
||||||
title="${title%.*}"
|
title="${title%.*}"
|
||||||
cd script_context/$lang
|
cd script_context/$lang
|
||||||
./test.sh $title &
|
./test.sh $title "$AIKEN" &
|
||||||
TESTS+=("$title,$lang,$!")
|
TESTS+=("$title,$lang,$!")
|
||||||
cd - 1>/dev/null
|
cd - 1>/dev/null
|
||||||
done
|
done
|
||||||
|
|
|
@ -13,4 +13,4 @@ requirements = []
|
||||||
source = "github"
|
source = "github"
|
||||||
|
|
||||||
[etags]
|
[etags]
|
||||||
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723298787, nanos_since_epoch = 494542000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
|
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723298816, nanos_since_epoch = 935691000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
|
||||||
|
|
|
@ -12,6 +12,8 @@ if [ -z $TITLE ]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
AIKEN=${2:-"cargo run -r --quiet --"}
|
||||||
|
|
||||||
if ! command -v jq &> /dev/null
|
if ! command -v jq &> /dev/null
|
||||||
then
|
then
|
||||||
echo "\033[1mjq\033[0m missing from system but required."
|
echo "\033[1mjq\033[0m missing from system but required."
|
||||||
|
@ -24,7 +26,7 @@ then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cargo run -r --quiet -- build 2>/dev/null
|
$AIKEN build 2>/dev/null
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
exit $?
|
exit $?
|
||||||
fi
|
fi
|
||||||
|
@ -39,4 +41,4 @@ cp ctx/$TITLE/inputs.cbor.template ctx/$TITLE/inputs.cbor
|
||||||
sed "s/{{ VALIDATOR_HASH }}/$VALIDATOR_HASH/" ctx/$TITLE/outputs.cbor.template > ctx/$TITLE/outputs.cbor
|
sed "s/{{ VALIDATOR_HASH }}/$VALIDATOR_HASH/" ctx/$TITLE/outputs.cbor.template > ctx/$TITLE/outputs.cbor
|
||||||
sed "s/{{ VALIDATOR }}/$VALIDATOR/" ctx/$TITLE/tx.cbor.template | sed "s/{{ VALIDATOR_HASH }}/$VALIDATOR_HASH/" > ctx/$TITLE/tx.cbor
|
sed "s/{{ VALIDATOR }}/$VALIDATOR/" ctx/$TITLE/tx.cbor.template | sed "s/{{ VALIDATOR_HASH }}/$VALIDATOR_HASH/" > ctx/$TITLE/tx.cbor
|
||||||
|
|
||||||
cargo run -r --quiet -- tx simulate 1>$TITLE.log 2>&1 ctx/$TITLE/tx.cbor ctx/$TITLE/inputs.cbor ctx/$TITLE/outputs.cbor
|
$AIKEN tx simulate 1>$TITLE.log 2>&1 ctx/$TITLE/tx.cbor ctx/$TITLE/inputs.cbor ctx/$TITLE/outputs.cbor
|
||||||
|
|
Loading…
Reference in New Issue