From defce9be4e45b204a58ee4f1107e3658b809aa53 Mon Sep 17 00:00:00 2001 From: solidsnakedev Date: Wed, 11 Sep 2024 15:25:20 -0600 Subject: [PATCH] fix: Plutus V3 NoDatum - Error: missing required inline datum or datum hash in script input --- crates/uplc/src/tx/script_context.rs | 27 +++-- .../resolved_inputs.template | 5 + .../v3/ctx/simple_spend_no_datum/tx.template | 33 +++++++ .../v3/validators/simple_spend_no_datum.ak | 99 +++++++++++++++++++ 4 files changed, 157 insertions(+), 7 deletions(-) create mode 100644 examples/acceptance_tests/script_context/v3/ctx/simple_spend_no_datum/resolved_inputs.template create mode 100644 examples/acceptance_tests/script_context/v3/ctx/simple_spend_no_datum/tx.template create mode 100644 examples/acceptance_tests/script_context/v3/validators/simple_spend_no_datum.ak diff --git a/crates/uplc/src/tx/script_context.rs b/crates/uplc/src/tx/script_context.rs index 992b86e2..d36b0dbe 100644 --- a/crates/uplc/src/tx/script_context.rs +++ b/crates/uplc/src/tx/script_context.rs @@ -1,4 +1,7 @@ -use super::{to_plutus_data::MintValue, Error}; +use super::{ + to_plutus_data::{MintValue, ToPlutusData}, + Error, +}; use itertools::Itertools; use pallas_addresses::{Address, Network, StakePayload}; use pallas_codec::utils::{ @@ -839,6 +842,16 @@ pub fn find_script( Some(DatumOption::Data(data)) => Ok(data.0.clone()), _ => Err(Error::MissingRequiredInlineDatumOrHash), }; + let lookup_datum_v3 = |datum: Option| match datum { + Some(DatumOption::Hash(hash)) => match lookup_table.datum.get(&hash) { + Some(d) => Ok(d.clone()), + None => Err(Error::MissingRequiredDatum { + hash: hash.to_string(), + }), + }, + Some(DatumOption::Data(data)) => Ok(data.0.clone()), + _ => Ok(None::.to_plutus_data()), + }; match redeemer.tag { RedeemerTag::Mint => get_mint_info(&tx.transaction_body.mint) @@ -908,12 +921,12 @@ pub fn find_script( .and_then(|input| match output_address(&input.resolved) { Address::Shelley(shelley_address) => { let hash = shelley_address.payment().as_hash(); - - let script = lookup_script(hash); - - let datum = lookup_datum(output_datum(&input.resolved)); - - script.and_then(|(script, _)| Ok((script, Some(datum?)))) + let (script, _) = lookup_script(hash)?; + let datum = match script { + ScriptVersion::V3(_) => lookup_datum_v3(output_datum(&input.resolved)), + _ => lookup_datum(output_datum(&input.resolved)), + }?; + Ok((script, Some(datum))) } _ => Err(Error::NonScriptStakeCredential), }), diff --git a/examples/acceptance_tests/script_context/v3/ctx/simple_spend_no_datum/resolved_inputs.template b/examples/acceptance_tests/script_context/v3/ctx/simple_spend_no_datum/resolved_inputs.template new file mode 100644 index 00000000..ab2915ce --- /dev/null +++ b/examples/acceptance_tests/script_context/v3/ctx/simple_spend_no_datum/resolved_inputs.template @@ -0,0 +1,5 @@ +[ + { 0: h'70{{ simple_spend_no_datum.simple_spend.spend.hash }}' + , 1: 1000000000 + } +] diff --git a/examples/acceptance_tests/script_context/v3/ctx/simple_spend_no_datum/tx.template b/examples/acceptance_tests/script_context/v3/ctx/simple_spend_no_datum/tx.template new file mode 100644 index 00000000..7a477501 --- /dev/null +++ b/examples/acceptance_tests/script_context/v3/ctx/simple_spend_no_datum/tx.template @@ -0,0 +1,33 @@ +[ + { 0: + [ [h'0000000000000000000000000000000000000000000000000000000000000000', 0] + ] + + , 1: + [] + + , 2: 42 + + , 11: h'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' + + , 13: + [ [h'0000000000000000000000000000000000000000000000000000000000000000', 0] + ] + + , 16: + [ h'6000000000000000000000000000000000000000000000000000000000', 1000000000 + ] + + , 17: 1 + }, + + { 5: [[0, 0, 121([]), [1000000, 100000000]]] + + , 7: [h'{{ simple_spend_no_datum.simple_spend.spend.cbor }}'] + + }, + + true, + + null +] diff --git a/examples/acceptance_tests/script_context/v3/validators/simple_spend_no_datum.ak b/examples/acceptance_tests/script_context/v3/validators/simple_spend_no_datum.ak new file mode 100644 index 00000000..c711d8c5 --- /dev/null +++ b/examples/acceptance_tests/script_context/v3/validators/simple_spend_no_datum.ak @@ -0,0 +1,99 @@ +use aiken/collection/dict +use cardano/address.{Address, Script} +use cardano/assets +use cardano/transaction.{ + Input, NoDatum, Output, OutputReference, ScriptPurpose, Spend, Transaction, +} + +validator simple_spend { + spend( + _datum: Option, + _redeemer: Void, + output_ref: OutputReference, + transaction: Transaction, + ) { + assert_transaction_id(transaction.id) + + assert_script_info(output_ref) + + assert_inputs(transaction.inputs) + + expect [] = transaction.outputs + + expect [] = transaction.reference_inputs + + expect [] = transaction.extra_signatories + + expect 42 == transaction.fee + + assert_redeemers(transaction.redeemers) + + expect [] == dict.to_pairs(transaction.datums) + + True + } + + else(_ctx) { + fail + } +} + +fn assert_transaction_id(id: ByteArray) { + expect + #"c6fbd346681a8f8337f6b3e51e6ec973f1509367eabc3a44c849af58a1d8471b" == id + Void +} + +fn assert_script_info(info: OutputReference) { + expect + OutputReference { + transaction_id: #"0000000000000000000000000000000000000000000000000000000000000000", + output_index: 0, + } == info + Void +} + +fn assert_inputs(inputs: List) { + expect [ + Input { + output_reference: OutputReference { transaction_id, output_index: 0 }, + output: Output { + address, + value: resolved_input_value, + datum: NoDatum, + reference_script: None, + }, + }, + ] = inputs + + expect + transaction_id == #"0000000000000000000000000000000000000000000000000000000000000000" + + expect resolved_input_value == assets.from_lovelace(1000000000) + + expect Address { payment_credential: Script(_), stake_credential: None } = + address + + Void +} + +fn assert_redeemers(redeemers: Pairs) { + expect + [ + Pair( + Spend( + OutputReference { + transaction_id: #"0000000000000000000000000000000000000000000000000000000000000000", + output_index: 0, + }, + ), + void(), + ), + ] == redeemers + Void +} + +fn void() -> Data { + let void: Data = Void + void +}