Further remove todos for v3, and reduce duplication in transaction evaluation

This commit is contained in:
KtorZ 2024-08-08 16:20:04 +02:00
parent ff4a480242
commit 445ffc483d
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
6 changed files with 468 additions and 633 deletions

View File

@ -4,14 +4,13 @@ use crate::{
PlutusData, PlutusData,
}; };
use error::Error; use error::Error;
use eval::get_script_and_datum_lookup_table;
use pallas_primitives::{ use pallas_primitives::{
conway::{CostMdls, MintedTx, Redeemer, TransactionInput, TransactionOutput}, conway::{CostMdls, MintedTx, Redeemer, TransactionInput, TransactionOutput},
Fragment, Fragment,
}; };
use pallas_traverse::{Era, MultiEraTx}; use pallas_traverse::{Era, MultiEraTx};
pub use phase_one::eval_phase_one; pub use phase_one::eval_phase_one;
pub use script_context::{ResolvedInput, SlotConfig}; pub use script_context::{DataLookupTable, ResolvedInput, SlotConfig};
pub mod error; pub mod error;
pub mod eval; pub mod eval;
@ -37,7 +36,7 @@ pub fn eval_phase_two(
) -> Result<Vec<Redeemer>, Error> { ) -> Result<Vec<Redeemer>, Error> {
let redeemers = tx.transaction_witness_set.redeemer.as_ref(); let redeemers = tx.transaction_witness_set.redeemer.as_ref();
let lookup_table = get_script_and_datum_lookup_table(tx, utxos); let lookup_table = DataLookupTable::from_transaction(tx, utxos);
if run_phase_one { if run_phase_one {
// subset of phase 1 check on redeemers and scripts // subset of phase 1 check on redeemers and scripts
@ -123,9 +122,6 @@ pub fn eval_phase_two_raw(
}; };
match multi_era_tx { match multi_era_tx {
MultiEraTx::Babbage(_) => {
todo!("convert Babbage's tx into Conway's")
}
MultiEraTx::Conway(tx) => { MultiEraTx::Conway(tx) => {
match eval_phase_two( match eval_phase_two(
&tx, &tx,
@ -143,7 +139,12 @@ pub fn eval_phase_two_raw(
Err(err) => Err(err), Err(err) => Err(err),
} }
} }
_ => todo!("Wrong era. Please use a more recent transaction format"), _ => unimplemented!(
r#"The transaction is serialized in an old era format. Because we're slightly lazy to
maintain backward compatibility with every possible transaction format AND, because
those formats are mostly forward-compatible, you are kindly expected to provide a
transaction in a format suitable for the Conway era."#
),
} }
} }

View File

@ -29,8 +29,10 @@ pub enum Error {
ExtraneousRedeemer, ExtraneousRedeemer,
#[error("Resolved Input not found.")] #[error("Resolved Input not found.")]
ResolvedInputNotFound(TransactionInput), ResolvedInputNotFound(TransactionInput),
#[error("A key hash cannot be the hash of a script.")] #[error("Redeemer points to a non-script withdrawal.")]
ScriptKeyHash, NonScriptWithdrawal,
#[error("Stake credential points to a non-script withdrawal.")]
NonScriptStakeCredential,
#[error("Cost model not found for language: {:?}.", .0)] #[error("Cost model not found for language: {:?}.", .0)]
CostModelNotFound(Language), CostModelNotFound(Language),
#[error("Wrong era, Please use Babbage or Alonzo: {0}")] #[error("Wrong era, Please use Babbage or Alonzo: {0}")]
@ -49,14 +51,16 @@ pub enum Error {
MissingRequiredScript { hash: String }, MissingRequiredScript { hash: String },
#[error("Missing required inline datum or datum hash in script input.")] #[error("Missing required inline datum or datum hash in script input.")]
MissingRequiredInlineDatumOrHash, MissingRequiredInlineDatumOrHash,
#[error("Only stake deregistration and delegation are valid certificate script purposes.")] #[error("Redeemer points to an unsupported certificate type.")]
OnlyStakeDeregAndDelegAllowed, UnsupportedCertificateType,
#[error("Redeemer ({}, {}): {}", tag, index, err)] #[error("Redeemer ({}, {}): {}", tag, index, err)]
RedeemerError { RedeemerError {
tag: String, tag: String,
index: u32, index: u32,
err: Box<Error>, err: Box<Error>,
}, },
#[error("Missing script for redeemer")]
MissingScriptForRedeemer,
#[error("Failed to apply parameters to Plutus script.")] #[error("Failed to apply parameters to Plutus script.")]
ApplyParamsError, ApplyParamsError,
} }

View File

@ -1,391 +1,48 @@
use super::{ use super::{
script_context::{ResolvedInput, ScriptContext, ScriptPurpose, SlotConfig, TxInfo}, script_context::{find_script, ResolvedInput, ScriptContext, SlotConfig, TxInfo},
to_plutus_data::ToPlutusData, to_plutus_data::ToPlutusData,
Error, Error,
}; };
use crate::{ use crate::{
ast::{FakeNamedDeBruijn, NamedDeBruijn, Program}, ast::{FakeNamedDeBruijn, NamedDeBruijn, Program},
machine::cost_model::ExBudget, machine::cost_model::ExBudget,
tx::script_context::{TxInfoV1, TxInfoV2, TxInfoV3}, tx::script_context::{DataLookupTable, ScriptVersion, TxInfoV1, TxInfoV2},
PlutusData, PlutusData,
}; };
use pallas_addresses::{Address, ScriptHash, StakePayload}; use pallas_codec::utils::Bytes;
use pallas_codec::utils::{Bytes, NonEmptyKeyValuePairs, NonEmptySet};
use pallas_crypto::hash::Hash;
use pallas_primitives::conway::{ use pallas_primitives::conway::{
Certificate, CostMdls, CostModel, DatumHash, DatumOption, ExUnits, Language, Mint, MintedTx, CostMdls, CostModel, ExUnits, Language, MintedTx, Redeemer, RedeemerTag,
NativeScript, PlutusV1Script, PlutusV2Script, PlutusV3Script, PolicyId, PseudoScript, Redeemer,
RedeemerTag, RewardAccount, StakeCredential, TransactionInput, TransactionOutput, Withdrawals,
}; };
use pallas_traverse::{ComputeHash, OriginalHash};
use std::{collections::HashMap, convert::TryInto, vec};
fn redeemer_tag_to_string(redeemer_tag: &RedeemerTag) -> String { pub fn eval_redeemer(
match redeemer_tag {
RedeemerTag::Spend => "Spend".to_string(),
RedeemerTag::Mint => "Mint".to_string(),
RedeemerTag::Cert => "Cert".to_string(),
RedeemerTag::Reward => "Reward".to_string(),
tag => todo!("redeemer_tag_to_string for {tag:?}"),
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum ScriptVersion {
Native(NativeScript),
V1(PlutusV1Script),
V2(PlutusV2Script),
V3(PlutusV3Script),
}
#[derive(Debug, PartialEq, Clone)]
enum ExecutionPurpose {
WithDatum(ScriptVersion, PlutusData), // Spending
NoDatum(ScriptVersion), // Minting, Wdrl, DCert
}
pub struct DataLookupTable {
datum: HashMap<DatumHash, PlutusData>,
scripts: HashMap<ScriptHash, ScriptVersion>,
}
impl DataLookupTable {
pub fn scripts(&self) -> HashMap<ScriptHash, ScriptVersion> {
self.scripts.clone()
}
}
fn get_script_purpose(
redeemer: &Redeemer,
inputs: &[TransactionInput],
mint: &Option<Mint>,
dcert: &Option<NonEmptySet<Certificate>>,
wdrl: &Option<Withdrawals>,
) -> Result<ScriptPurpose, Error> {
// sorting according to specs section 4.1: https://hydra.iohk.io/build/18583827/download/1/alonzo-changes.pdf
let tag = redeemer.tag;
let index = redeemer.index;
match tag {
RedeemerTag::Mint => {
// sort lexical by policy id
let mut policy_ids = mint
.as_ref()
.unwrap_or(&NonEmptyKeyValuePairs::Indef(vec![]))
.iter()
.map(|(policy_id, _)| *policy_id)
.collect::<Vec<PolicyId>>();
policy_ids.sort();
match policy_ids.get(index as usize) {
Some(policy_id) => Ok(ScriptPurpose::Minting(*policy_id)),
None => Err(Error::ExtraneousRedeemer),
}
}
RedeemerTag::Spend => {
// sort lexical by tx_hash and index
let mut inputs = inputs.to_vec();
inputs.sort();
match inputs.get(index as usize) {
Some(input) => Ok(ScriptPurpose::Spending(input.clone())),
None => Err(Error::ExtraneousRedeemer),
}
}
RedeemerTag::Reward => {
// sort lexical by reward account
let mut reward_accounts = wdrl
.as_ref()
.unwrap_or(&NonEmptyKeyValuePairs::Indef(vec![]))
.iter()
.map(|(racnt, _)| racnt.clone())
.collect::<Vec<RewardAccount>>();
reward_accounts.sort();
let reward_account = match reward_accounts.get(index as usize) {
Some(ra) => ra.clone(),
None => return Err(Error::ExtraneousRedeemer),
};
let address = Address::from_bytes(&reward_account)?;
let credential = match address {
Address::Stake(stake_address) => match stake_address.payload() {
StakePayload::Script(script_hash) => StakeCredential::Scripthash(*script_hash),
StakePayload::Stake(_) => {
return Err(Error::ScriptKeyHash);
}
},
_ => return Err(Error::BadWithdrawalAddress),
};
Ok(ScriptPurpose::Rewarding(credential))
}
RedeemerTag::Cert => {
// sort by order given in the tx (just take it as it is basically)
match dcert.as_deref().unwrap_or(&vec![]).get(index as usize) {
Some(cert) => Ok(ScriptPurpose::Certifying(cert.clone())),
None => Err(Error::ExtraneousRedeemer),
}
}
tag => todo!("get_script_purpose for {tag:?}"),
}
}
fn get_execution_purpose(
utxos: &[ResolvedInput],
script_purpose: &ScriptPurpose,
lookup_table: &DataLookupTable,
) -> Result<ExecutionPurpose, Error> {
match script_purpose {
ScriptPurpose::Minting(policy_id) => {
let policy_id_array: [u8; 28] = policy_id.to_vec().try_into().unwrap();
let hash = Hash::from(policy_id_array);
let script = match lookup_table.scripts.get(&hash) {
Some(s) => s.clone(),
None => {
return Err(Error::MissingRequiredScript {
hash: hash.to_string(),
});
}
};
Ok(ExecutionPurpose::NoDatum(script))
}
ScriptPurpose::Spending(out_ref) => {
let utxo = match utxos.iter().find(|utxo| utxo.input == *out_ref) {
Some(resolved) => resolved,
None => return Err(Error::ResolvedInputNotFound(out_ref.clone())),
};
match &utxo.output {
TransactionOutput::Legacy(output) => {
let address = Address::from_bytes(&output.address).unwrap();
match address {
Address::Shelley(shelley_address) => {
let hash = shelley_address.payment().as_hash();
let script = match lookup_table.scripts.get(hash) {
Some(s) => s.clone(),
None => {
return Err(Error::MissingRequiredScript {
hash: hash.to_string(),
});
}
};
let datum_hash = match &output.datum_hash {
Some(hash) => hash,
None => return Err(Error::MissingRequiredInlineDatumOrHash),
};
let datum = match lookup_table.datum.get(datum_hash) {
Some(d) => d.clone(),
None => {
return Err(Error::MissingRequiredDatum {
hash: datum_hash.to_string(),
});
}
};
Ok(ExecutionPurpose::WithDatum(script, datum))
}
_ => Err(Error::ScriptKeyHash),
}
}
TransactionOutput::PostAlonzo(output) => {
let address = Address::from_bytes(&output.address).unwrap();
match address {
Address::Shelley(shelley_address) => {
let hash = shelley_address.payment().as_hash();
let script = match lookup_table.scripts.get(hash) {
Some(s) => s.clone(),
None => {
return Err(Error::MissingRequiredScript {
hash: hash.to_string(),
});
}
};
let datum = match &output.datum_option {
Some(DatumOption::Hash(hash)) => {
match lookup_table.datum.get(hash) {
Some(d) => d.clone(),
None => {
return Err(Error::MissingRequiredDatum {
hash: hash.to_string(),
});
}
}
}
Some(DatumOption::Data(data)) => data.0.clone(),
_ => return Err(Error::MissingRequiredInlineDatumOrHash),
};
Ok(ExecutionPurpose::WithDatum(script, datum))
}
_ => Err(Error::ScriptKeyHash),
}
}
}
}
ScriptPurpose::Rewarding(stake_credential) => {
let script_hash = match stake_credential {
StakeCredential::Scripthash(hash) => *hash,
_ => return Err(Error::ScriptKeyHash),
};
let script = match lookup_table.scripts.get(&script_hash) {
Some(s) => s.clone(),
None => {
return Err(Error::MissingRequiredScript {
hash: script_hash.to_string(),
});
}
};
Ok(ExecutionPurpose::NoDatum(script))
}
ScriptPurpose::Certifying(cert) => match cert {
Certificate::StakeDeregistration(stake_credential) => {
let script_hash = match stake_credential {
StakeCredential::Scripthash(hash) => *hash,
_ => return Err(Error::ScriptKeyHash),
};
let script = match lookup_table.scripts.get(&script_hash) {
Some(s) => s.clone(),
None => {
return Err(Error::MissingRequiredScript {
hash: script_hash.to_string(),
});
}
};
Ok(ExecutionPurpose::NoDatum(script))
}
Certificate::StakeDelegation(stake_credential, _) => {
let script_hash = match stake_credential {
StakeCredential::Scripthash(hash) => *hash,
_ => return Err(Error::ScriptKeyHash),
};
let script = match lookup_table.scripts.get(&script_hash) {
Some(s) => s.clone(),
None => {
return Err(Error::MissingRequiredScript {
hash: script_hash.to_string(),
});
}
};
Ok(ExecutionPurpose::NoDatum(script))
}
_ => Err(Error::OnlyStakeDeregAndDelegAllowed),
},
}
}
pub fn get_script_and_datum_lookup_table(
tx: &MintedTx, tx: &MintedTx,
utxos: &[ResolvedInput], utxos: &[ResolvedInput],
) -> DataLookupTable { slot_config: &SlotConfig,
let mut datum = HashMap::new(); redeemer: &Redeemer,
let mut scripts = HashMap::new(); lookup_table: &DataLookupTable,
cost_mdls_opt: Option<&CostMdls>,
// discovery in witness set initial_budget: &ExBudget,
) -> Result<Redeemer, Error> {
let plutus_data_witnesses = tx fn do_eval_redeemer(
.transaction_witness_set
.plutus_data
.clone()
.map(|s| s.to_vec())
.unwrap_or_default();
let scripts_native_witnesses = tx
.transaction_witness_set
.native_script
.clone()
.map(|s| s.to_vec())
.unwrap_or_default();
let scripts_v1_witnesses = tx
.transaction_witness_set
.plutus_v1_script
.clone()
.map(|s| s.to_vec())
.unwrap_or_default();
let scripts_v2_witnesses = tx
.transaction_witness_set
.plutus_v2_script
.clone()
.map(|s| s.to_vec())
.unwrap_or_default();
let scripts_v3_witnesses = tx
.transaction_witness_set
.plutus_v3_script
.clone()
.map(|s| s.to_vec())
.unwrap_or_default();
for plutus_data in plutus_data_witnesses.iter() {
datum.insert(plutus_data.original_hash(), plutus_data.clone().unwrap());
}
for script in scripts_native_witnesses.iter() {
scripts.insert(
script.compute_hash(),
ScriptVersion::Native(script.clone().unwrap()),
);
}
for script in scripts_v1_witnesses.iter() {
scripts.insert(script.compute_hash(), ScriptVersion::V1(script.clone()));
}
for script in scripts_v2_witnesses.iter() {
scripts.insert(script.compute_hash(), ScriptVersion::V2(script.clone()));
}
for script in scripts_v3_witnesses.iter() {
scripts.insert(script.compute_hash(), ScriptVersion::V3(script.clone()));
}
// discovery in utxos (script ref)
for utxo in utxos.iter() {
match &utxo.output {
TransactionOutput::Legacy(_) => {}
TransactionOutput::PostAlonzo(output) => {
if let Some(script) = &output.script_ref {
match &script.0 {
PseudoScript::NativeScript(ns) => {
scripts.insert(ns.compute_hash(), ScriptVersion::Native(ns.clone()));
}
PseudoScript::PlutusV1Script(v1) => {
scripts.insert(v1.compute_hash(), ScriptVersion::V1(v1.clone()));
}
PseudoScript::PlutusV2Script(v2) => {
scripts.insert(v2.compute_hash(), ScriptVersion::V2(v2.clone()));
}
PseudoScript::PlutusV3Script(v3) => {
scripts.insert(v3.compute_hash(), ScriptVersion::V3(v3.clone()));
}
}
}
}
}
}
DataLookupTable { datum, scripts }
}
fn mk_redeemer_with_datum(
cost_mdl_opt: Option<&CostModel>, cost_mdl_opt: Option<&CostModel>,
initial_budget: &ExBudget, initial_budget: &ExBudget,
lang: &Language, lang: &Language,
datum: PlutusData, datum: Option<PlutusData>,
(redeemer, purpose): (&Redeemer, ScriptPurpose), redeemer: &Redeemer,
tx_info: TxInfo, tx_info: TxInfo,
program: Program<NamedDeBruijn>, program: Program<NamedDeBruijn>,
) -> Result<Redeemer, Error> { ) -> Result<Redeemer, Error> {
let purpose = tx_info
.purpose(redeemer)
.expect("redeemer's purpose shall be known by this point.");
let script_context = ScriptContext { tx_info, purpose }; let script_context = ScriptContext { tx_info, purpose };
let program = program let program = if let Some(datum) = datum {
.apply_data(datum) program.apply_data(datum)
} else {
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());
@ -414,25 +71,7 @@ fn mk_redeemer_with_datum(
}; };
Ok(new_redeemer) Ok(new_redeemer)
} }
pub fn eval_redeemer(
tx: &MintedTx,
utxos: &[ResolvedInput],
slot_config: &SlotConfig,
redeemer: &Redeemer,
lookup_table: &DataLookupTable,
cost_mdls_opt: Option<&CostMdls>,
initial_budget: &ExBudget,
) -> Result<Redeemer, Error> {
let result = || {
let purpose = get_script_purpose(
redeemer,
&tx.transaction_body.inputs,
&tx.transaction_body.mint,
&tx.transaction_body.certificates,
&tx.transaction_body.withdrawals,
)?;
let program = |script: Bytes| { let program = |script: Bytes| {
let mut buffer = Vec::new(); let mut buffer = Vec::new();
@ -440,29 +79,27 @@ pub fn eval_redeemer(
.map(Into::<Program<NamedDeBruijn>>::into) .map(Into::<Program<NamedDeBruijn>>::into)
}; };
let execution_purpose: ExecutionPurpose = match find_script(redeemer, tx, utxos, lookup_table)? {
get_execution_purpose(utxos, &purpose, lookup_table)?; (ScriptVersion::Native(_), _) => Err(Error::NativeScriptPhaseTwo),
match execution_purpose { (ScriptVersion::V1(script), datum) => do_eval_redeemer(
ExecutionPurpose::WithDatum(script_version, datum) => match script_version {
ScriptVersion::V1(script) => mk_redeemer_with_datum(
cost_mdls_opt cost_mdls_opt
.map(|cost_mdls| { .map(|cost_mdls| {
cost_mdls cost_mdls
.plutus_v1 .plutus_v1
.as_ref() .as_ref()
.ok_or(Error::CostModelNotFound(Language::PlutusV1)) .ok_or(Error::CostModelNotFound(Language::PlutusV2))
}) })
.transpose()?, .transpose()?,
initial_budget, initial_budget,
&Language::PlutusV1, &Language::PlutusV1,
datum, datum,
(redeemer, purpose), redeemer,
TxInfoV1::from_transaction(tx, utxos, slot_config)?, TxInfoV1::from_transaction(tx, utxos, slot_config)?,
program(script.0)?, program(script.0)?,
), ),
ScriptVersion::V2(script) => mk_redeemer_with_datum( (ScriptVersion::V2(script), datum) => do_eval_redeemer(
cost_mdls_opt cost_mdls_opt
.map(|cost_mdls| { .map(|cost_mdls| {
cost_mdls cost_mdls
@ -474,139 +111,28 @@ pub fn eval_redeemer(
initial_budget, initial_budget,
&Language::PlutusV2, &Language::PlutusV2,
datum, datum,
(redeemer, purpose), redeemer,
TxInfoV2::from_transaction(tx, utxos, slot_config)?, TxInfoV2::from_transaction(tx, utxos, slot_config)?,
program(script.0)?, program(script.0)?,
), ),
ScriptVersion::V3(script) => mk_redeemer_with_datum( (ScriptVersion::V3(_script), _datum) => todo!(),
cost_mdls_opt
.map(|cost_mdls| {
cost_mdls
.plutus_v3
.as_ref()
.ok_or(Error::CostModelNotFound(Language::PlutusV3))
})
.transpose()?,
initial_budget,
&Language::PlutusV2,
datum,
(redeemer, purpose),
TxInfoV3::from_transaction(tx, utxos, slot_config)?,
program(script.0)?,
),
ScriptVersion::Native(_) => Err(Error::NativeScriptPhaseTwo),
},
ExecutionPurpose::NoDatum(script_version) => match script_version {
ScriptVersion::V1(script) => {
let tx_info = TxInfoV1::from_transaction(tx, utxos, slot_config)?;
let script_context = ScriptContext { tx_info, purpose };
let program: Program<NamedDeBruijn> = {
let mut buffer = Vec::new();
let prog = Program::<FakeNamedDeBruijn>::from_cbor(&script.0, &mut buffer)?;
prog.into()
};
let program = program
.apply_data(redeemer.data.clone())
.apply_data(script_context.to_plutus_data());
let mut eval_result = if let Some(cost_mdls) = cost_mdls_opt {
let costs = if let Some(costs) = &cost_mdls.plutus_v1 {
costs
} else {
return Err(Error::CostModelNotFound(Language::PlutusV1));
};
program.eval_as(&Language::PlutusV1, costs, Some(initial_budget))
} else {
program.eval_version(ExBudget::default(), &Language::PlutusV1)
};
let cost = eval_result.cost();
let logs = eval_result.logs();
match eval_result.result() {
Ok(_) => (),
Err(err) => return Err(Error::Machine(err, cost, logs)),
} }
.map_err(|err| Error::RedeemerError {
let new_redeemer = Redeemer {
tag: redeemer.tag,
index: redeemer.index,
data: redeemer.data.clone(),
ex_units: ExUnits {
mem: cost.mem as u64,
steps: cost.cpu as u64,
},
};
Ok(new_redeemer)
}
ScriptVersion::V2(script) => {
let tx_info = TxInfoV2::from_transaction(tx, utxos, slot_config)?;
let script_context = ScriptContext { tx_info, purpose };
let program: Program<NamedDeBruijn> = {
let mut buffer = Vec::new();
let prog = Program::<FakeNamedDeBruijn>::from_cbor(&script.0, &mut buffer)?;
prog.into()
};
let program = program
.apply_data(redeemer.data.clone())
.apply_data(script_context.to_plutus_data());
let mut eval_result = if let Some(cost_mdls) = cost_mdls_opt {
let costs = if let Some(costs) = &cost_mdls.plutus_v2 {
costs
} else {
return Err(Error::CostModelNotFound(Language::PlutusV2));
};
program.eval_as(&Language::PlutusV2, costs, Some(initial_budget))
} else {
program.eval(ExBudget::default())
};
let cost = eval_result.cost();
let logs = eval_result.logs();
match eval_result.result() {
Ok(_) => (),
Err(err) => return Err(Error::Machine(err, cost, logs)),
}
let new_redeemer = Redeemer {
tag: redeemer.tag,
index: redeemer.index,
data: redeemer.data.clone(),
ex_units: ExUnits {
mem: cost.mem as u64,
steps: cost.cpu as u64,
},
};
Ok(new_redeemer)
}
ScriptVersion::V3(_script) => todo!(),
ScriptVersion::Native(_) => Err(Error::NativeScriptPhaseTwo),
},
}
};
match result() {
Ok(r) => Ok(r),
Err(err) => Err(Error::RedeemerError {
tag: redeemer_tag_to_string(&redeemer.tag), tag: redeemer_tag_to_string(&redeemer.tag),
index: redeemer.index, index: redeemer.index,
err: Box::new(err), err: Box::new(err),
}), })
} }
fn redeemer_tag_to_string(redeemer_tag: &RedeemerTag) -> String {
match redeemer_tag {
RedeemerTag::Spend => "Spend",
RedeemerTag::Mint => "Mint",
RedeemerTag::Reward => "Withdraw",
RedeemerTag::Cert => "Publish",
RedeemerTag::Propose => "Propose",
RedeemerTag::Vote => "Vote",
}
.to_string()
} }

View File

@ -1,7 +1,6 @@
use super::{ use super::{
error::Error, error::Error,
eval::{DataLookupTable, ScriptVersion}, script_context::{DataLookupTable, ResolvedInput, ScriptPurpose, ScriptVersion},
script_context::{ResolvedInput, ScriptPurpose},
}; };
use itertools::Itertools; use itertools::Itertools;
use pallas_addresses::{Address, ScriptHash, ShelleyPaymentPart, StakePayload}; use pallas_addresses::{Address, ScriptHash, ShelleyPaymentPart, StakePayload};

View File

@ -7,14 +7,14 @@ use pallas_primitives::{
alonzo, alonzo,
conway::{ conway::{
AddrKeyhash, Certificate, Coin, DatumHash, DatumOption, Mint, MintedTransactionBody, AddrKeyhash, Certificate, Coin, DatumHash, DatumOption, Mint, MintedTransactionBody,
MintedTransactionOutput, MintedTx, MintedWitnessSet, PlutusData, PolicyId, MintedTransactionOutput, MintedTx, MintedWitnessSet, NativeScript, PlutusData,
PostAlonzoTransactionOutput, PseudoDatumOption, Redeemer, RedeemerTag, RedeemersKey, PlutusV1Script, PlutusV2Script, PlutusV3Script, PolicyId, PostAlonzoTransactionOutput,
RequiredSigners, RewardAccount, StakeCredential, TransactionInput, TransactionOutput, PseudoDatumOption, PseudoScript, Redeemer, RedeemerTag, RedeemersKey, RequiredSigners,
Value, RewardAccount, ScriptHash, StakeCredential, TransactionInput, TransactionOutput, Value,
}, },
}; };
use pallas_traverse::OriginalHash; use pallas_traverse::{ComputeHash, OriginalHash};
use std::{cmp::Ordering, ops::Deref}; use std::{cmp::Ordering, collections::HashMap, ops::Deref};
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct ResolvedInput { pub struct ResolvedInput {
@ -33,6 +33,30 @@ pub enum TxOut {
V2(TransactionOutput), V2(TransactionOutput),
} }
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),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub enum ScriptPurpose { pub enum ScriptPurpose {
Minting(PolicyId), Minting(PolicyId),
@ -41,17 +65,133 @@ pub enum ScriptPurpose {
Certifying(Certificate), Certifying(Certificate),
} }
#[derive(Debug, PartialEq, Clone)]
pub enum ScriptVersion {
Native(NativeScript),
V1(PlutusV1Script),
V2(PlutusV2Script),
V3(PlutusV3Script),
}
pub struct DataLookupTable {
datum: HashMap<DatumHash, PlutusData>,
scripts: HashMap<ScriptHash, ScriptVersion>,
}
impl DataLookupTable {
pub fn from_transaction(tx: &MintedTx, utxos: &[ResolvedInput]) -> DataLookupTable {
let mut datum = HashMap::new();
let mut scripts = HashMap::new();
// discovery in witness set
let plutus_data_witnesses = tx
.transaction_witness_set
.plutus_data
.clone()
.map(|s| s.to_vec())
.unwrap_or_default();
let scripts_native_witnesses = tx
.transaction_witness_set
.native_script
.clone()
.map(|s| s.to_vec())
.unwrap_or_default();
let scripts_v1_witnesses = tx
.transaction_witness_set
.plutus_v1_script
.clone()
.map(|s| s.to_vec())
.unwrap_or_default();
let scripts_v2_witnesses = tx
.transaction_witness_set
.plutus_v2_script
.clone()
.map(|s| s.to_vec())
.unwrap_or_default();
let scripts_v3_witnesses = tx
.transaction_witness_set
.plutus_v3_script
.clone()
.map(|s| s.to_vec())
.unwrap_or_default();
for plutus_data in plutus_data_witnesses.iter() {
datum.insert(plutus_data.original_hash(), plutus_data.clone().unwrap());
}
for script in scripts_native_witnesses.iter() {
scripts.insert(
script.compute_hash(),
ScriptVersion::Native(script.clone().unwrap()),
);
}
for script in scripts_v1_witnesses.iter() {
scripts.insert(script.compute_hash(), ScriptVersion::V1(script.clone()));
}
for script in scripts_v2_witnesses.iter() {
scripts.insert(script.compute_hash(), ScriptVersion::V2(script.clone()));
}
for script in scripts_v3_witnesses.iter() {
scripts.insert(script.compute_hash(), ScriptVersion::V3(script.clone()));
}
// discovery in utxos (script ref)
for utxo in utxos.iter() {
match &utxo.output {
TransactionOutput::Legacy(_) => {}
TransactionOutput::PostAlonzo(output) => {
if let Some(script) = &output.script_ref {
match &script.0 {
PseudoScript::NativeScript(ns) => {
scripts
.insert(ns.compute_hash(), ScriptVersion::Native(ns.clone()));
}
PseudoScript::PlutusV1Script(v1) => {
scripts.insert(v1.compute_hash(), ScriptVersion::V1(v1.clone()));
}
PseudoScript::PlutusV2Script(v2) => {
scripts.insert(v2.compute_hash(), ScriptVersion::V2(v2.clone()));
}
PseudoScript::PlutusV3Script(v3) => {
scripts.insert(v3.compute_hash(), ScriptVersion::V3(v3.clone()));
}
}
}
}
}
}
DataLookupTable { datum, scripts }
}
}
impl DataLookupTable {
pub fn scripts(&self) -> HashMap<ScriptHash, ScriptVersion> {
self.scripts.clone()
}
}
#[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<TxOut>,
pub fee: Value, pub fee: Value,
pub mint: MintValue, pub mint: MintValue,
pub dcert: Vec<Certificate>, pub certificates: Vec<Certificate>,
pub wdrl: Vec<(Address, Coin)>, pub withdrawals: Vec<(Address, Coin)>,
pub valid_range: TimeRange, pub valid_range: TimeRange,
pub signatories: Vec<AddrKeyhash>, pub signatories: Vec<AddrKeyhash>,
pub data: Vec<(DatumHash, PlutusData)>, pub data: Vec<(DatumHash, PlutusData)>,
pub redeemers: KeyValuePairs<ScriptPurpose, Redeemer>,
pub id: Hash<32>, pub id: Hash<32>,
} }
@ -65,16 +205,28 @@ impl TxInfoV1 {
return Err(Error::ScriptAndInputRefNotAllowed); return Err(Error::ScriptAndInputRefNotAllowed);
} }
let inputs = get_tx_in_info_v1(&tx.transaction_body.inputs, utxos)?;
let certificates = get_certificates_info(&tx.transaction_body.certificates);
let withdrawals =
KeyValuePairs::from(get_withdrawal_info(&tx.transaction_body.withdrawals));
let mint = get_mint_info(&tx.transaction_body.mint);
let redeemers = get_redeemers_info(
&tx.transaction_witness_set,
script_purpose_builder(&inputs[..], &mint, &certificates, &withdrawals),
)?;
Ok(TxInfo::V1(TxInfoV1 { Ok(TxInfo::V1(TxInfoV1 {
inputs: get_tx_in_info_v1(&tx.transaction_body.inputs, utxos)?, inputs,
outputs: get_outputs_info(TxOut::V1, &tx.transaction_body.outputs[..]), outputs: get_outputs_info(TxOut::V1, &tx.transaction_body.outputs[..]),
fee: get_fee_info(&tx.transaction_body.fee), fee: get_fee_info(&tx.transaction_body.fee),
mint: get_mint_info(&tx.transaction_body.mint), mint,
dcert: get_certificates_info(&tx.transaction_body.certificates), certificates,
wdrl: get_withdrawal_info(&tx.transaction_body.withdrawals), withdrawals: withdrawals.into(),
valid_range: get_validity_range_info(&tx.transaction_body, slot_config), valid_range: get_validity_range_info(&tx.transaction_body, slot_config),
signatories: get_signatories_info(&tx.transaction_body.required_signers), signatories: get_signatories_info(&tx.transaction_body.required_signers),
data: get_data_info(&tx.transaction_witness_set), data: get_data_info(&tx.transaction_witness_set),
redeemers,
id: tx.transaction_body.original_hash(), id: tx.transaction_body.original_hash(),
})) }))
} }
@ -87,8 +239,8 @@ pub struct TxInfoV2 {
pub outputs: Vec<TxOut>, pub outputs: Vec<TxOut>,
pub fee: Value, pub fee: Value,
pub mint: MintValue, pub mint: MintValue,
pub dcert: Vec<Certificate>, pub certificates: Vec<Certificate>,
pub wdrl: KeyValuePairs<Address, Coin>, pub withdrawals: KeyValuePairs<Address, Coin>,
pub valid_range: TimeRange, pub valid_range: TimeRange,
pub signatories: Vec<AddrKeyhash>, pub signatories: Vec<AddrKeyhash>,
pub redeemers: KeyValuePairs<ScriptPurpose, Redeemer>, pub redeemers: KeyValuePairs<ScriptPurpose, Redeemer>,
@ -103,13 +255,14 @@ impl TxInfoV2 {
slot_config: &SlotConfig, slot_config: &SlotConfig,
) -> Result<TxInfo, Error> { ) -> Result<TxInfo, Error> {
let inputs = get_tx_in_info_v2(&tx.transaction_body.inputs, utxos)?; let inputs = get_tx_in_info_v2(&tx.transaction_body.inputs, utxos)?;
let dcert = get_certificates_info(&tx.transaction_body.certificates); let certificates = get_certificates_info(&tx.transaction_body.certificates);
let wdrl = KeyValuePairs::from(get_withdrawal_info(&tx.transaction_body.withdrawals)); let withdrawals =
KeyValuePairs::from(get_withdrawal_info(&tx.transaction_body.withdrawals));
let mint = get_mint_info(&tx.transaction_body.mint); let mint = get_mint_info(&tx.transaction_body.mint);
let redeemers = get_redeemers_info( let redeemers = get_redeemers_info(
&tx.transaction_witness_set, &tx.transaction_witness_set,
script_purpose_builder(&inputs[..], &mint, &dcert, &wdrl), script_purpose_builder(&inputs[..], &mint, &certificates, &withdrawals),
)?; )?;
let reference_inputs = tx let reference_inputs = tx
@ -126,8 +279,8 @@ impl TxInfoV2 {
outputs: get_outputs_info(TxOut::V2, &tx.transaction_body.outputs[..]), outputs: get_outputs_info(TxOut::V2, &tx.transaction_body.outputs[..]),
fee: get_fee_info(&tx.transaction_body.fee), fee: get_fee_info(&tx.transaction_body.fee),
mint, mint,
dcert, certificates,
wdrl, withdrawals,
valid_range: get_validity_range_info(&tx.transaction_body, slot_config), valid_range: get_validity_range_info(&tx.transaction_body, slot_config),
signatories: get_signatories_info(&tx.transaction_body.required_signers), signatories: get_signatories_info(&tx.transaction_body.required_signers),
data: KeyValuePairs::from(get_data_info(&tx.transaction_witness_set)), data: KeyValuePairs::from(get_data_info(&tx.transaction_witness_set)),
@ -155,6 +308,50 @@ pub enum TxInfo {
V2(TxInfoV2), V2(TxInfoV2),
} }
impl TxInfo {
pub fn purpose(&self, needle: &Redeemer) -> Option<ScriptPurpose> {
match self {
TxInfo::V1(TxInfoV1 { redeemers, .. }) | TxInfo::V2(TxInfoV2 { redeemers, .. }) => {
redeemers.iter().find_map(|(purpose, redeemer)| {
if redeemer == needle {
Some(purpose.clone())
} else {
None
}
})
}
}
}
pub fn inputs(&self) -> &[TxInInfo] {
match self {
TxInfo::V1(info) => &info.inputs,
TxInfo::V2(info) => &info.inputs,
}
}
pub fn mint(&self) -> &MintValue {
match self {
TxInfo::V1(info) => &info.mint,
TxInfo::V2(info) => &info.mint,
}
}
pub fn withdrawals(&self) -> &[(Address, Coin)] {
match self {
TxInfo::V1(info) => &info.withdrawals[..],
TxInfo::V2(info) => &info.withdrawals[..],
}
}
pub fn certificates(&self) -> &[Certificate] {
match self {
TxInfo::V1(info) => &info.certificates[..],
TxInfo::V2(info) => &info.certificates[..],
}
}
}
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct ScriptContext { pub struct ScriptContext {
pub tx_info: TxInfo, pub tx_info: TxInfo,
@ -235,7 +432,7 @@ pub fn get_tx_in_info_v1(
.collect() .collect()
} }
fn get_tx_in_info_v2( pub fn get_tx_in_info_v2(
inputs: &[TransactionInput], inputs: &[TransactionInput],
utxos: &[ResolvedInput], utxos: &[ResolvedInput],
) -> Result<Vec<TxInInfo>, Error> { ) -> Result<Vec<TxInInfo>, Error> {
@ -271,7 +468,7 @@ fn get_tx_in_info_v2(
.collect() .collect()
} }
fn get_mint_info(mint: &Option<Mint>) -> MintValue { pub fn get_mint_info(mint: &Option<Mint>) -> MintValue {
MintValue { MintValue {
mint_value: mint mint_value: mint
.as_ref() .as_ref()
@ -280,7 +477,7 @@ fn get_mint_info(mint: &Option<Mint>) -> MintValue {
} }
} }
fn get_outputs_info( pub fn get_outputs_info(
to_tx_out: fn(TransactionOutput) -> TxOut, to_tx_out: fn(TransactionOutput) -> TxOut,
outputs: &[MintedTransactionOutput], outputs: &[MintedTransactionOutput],
) -> Vec<TxOut> { ) -> Vec<TxOut> {
@ -291,15 +488,15 @@ fn get_outputs_info(
.collect() .collect()
} }
fn get_fee_info(fee: &Coin) -> Value { pub fn get_fee_info(fee: &Coin) -> Value {
Value::Coin(*fee) Value::Coin(*fee)
} }
fn get_certificates_info(certificates: &Option<NonEmptySet<Certificate>>) -> Vec<Certificate> { pub fn get_certificates_info(certificates: &Option<NonEmptySet<Certificate>>) -> Vec<Certificate> {
certificates.clone().map(|s| s.to_vec()).unwrap_or_default() certificates.clone().map(|s| s.to_vec()).unwrap_or_default()
} }
fn get_withdrawal_info( pub fn get_withdrawal_info(
withdrawals: &Option<NonEmptyKeyValuePairs<RewardAccount, Coin>>, withdrawals: &Option<NonEmptyKeyValuePairs<RewardAccount, Coin>>,
) -> Vec<(Address, Coin)> { ) -> Vec<(Address, Coin)> {
withdrawals withdrawals
@ -313,7 +510,10 @@ fn get_withdrawal_info(
.unwrap_or_default() .unwrap_or_default()
} }
fn get_validity_range_info(body: &MintedTransactionBody, slot_config: &SlotConfig) -> TimeRange { pub fn get_validity_range_info(
body: &MintedTransactionBody,
slot_config: &SlotConfig,
) -> TimeRange {
fn slot_to_begin_posix_time(slot: u64, sc: &SlotConfig) -> u64 { fn slot_to_begin_posix_time(slot: u64, sc: &SlotConfig) -> u64 {
let ms_after_begin = (slot - sc.zero_slot) * sc.slot_length as u64; let ms_after_begin = (slot - sc.zero_slot) * sc.slot_length as u64;
sc.zero_time + ms_after_begin sc.zero_time + ms_after_begin
@ -339,14 +539,14 @@ fn get_validity_range_info(body: &MintedTransactionBody, slot_config: &SlotConfi
) )
} }
fn get_signatories_info(signers: &Option<RequiredSigners>) -> Vec<AddrKeyhash> { pub fn get_signatories_info(signers: &Option<RequiredSigners>) -> Vec<AddrKeyhash> {
signers signers
.as_deref() .as_deref()
.map(|s| s.iter().cloned().sorted().collect()) .map(|s| s.iter().cloned().sorted().collect())
.unwrap_or_default() .unwrap_or_default()
} }
fn get_data_info(witness_set: &MintedWitnessSet) -> Vec<(DatumHash, PlutusData)> { pub fn get_data_info(witness_set: &MintedWitnessSet) -> Vec<(DatumHash, PlutusData)> {
witness_set witness_set
.plutus_data .plutus_data
.as_deref() .as_deref()
@ -360,7 +560,7 @@ fn get_data_info(witness_set: &MintedWitnessSet) -> Vec<(DatumHash, PlutusData)>
.unwrap_or_default() .unwrap_or_default()
} }
fn get_redeemers_info<'a>( pub fn get_redeemers_info<'a>(
witness_set: &'a MintedWitnessSet, witness_set: &'a MintedWitnessSet,
to_script_purpose: impl Fn(&'a RedeemersKey) -> Result<ScriptPurpose, Error>, to_script_purpose: impl Fn(&'a RedeemersKey) -> Result<ScriptPurpose, Error>,
) -> Result<KeyValuePairs<ScriptPurpose, Redeemer>, Error> { ) -> Result<KeyValuePairs<ScriptPurpose, Redeemer>, Error> {
@ -391,8 +591,8 @@ fn get_redeemers_info<'a>(
fn script_purpose_builder<'a>( fn script_purpose_builder<'a>(
inputs: &'a [TxInInfo], inputs: &'a [TxInInfo],
mint: &'a MintValue, mint: &'a MintValue,
dcert: &'a [Certificate], certificates: &'a [Certificate],
wdrl: &'a KeyValuePairs<Address, Coin>, withdrawals: &'a KeyValuePairs<Address, Coin>,
) -> impl Fn(&'a RedeemersKey) -> Result<ScriptPurpose, Error> { ) -> impl Fn(&'a RedeemersKey) -> Result<ScriptPurpose, Error> {
move |redeemer: &'a RedeemersKey| { move |redeemer: &'a RedeemersKey| {
let tag = redeemer.tag; let tag = redeemer.tag;
@ -406,8 +606,11 @@ fn script_purpose_builder<'a>(
.get(index) .get(index)
.cloned() .cloned()
.map(|i| ScriptPurpose::Spending(i.out_ref)), .map(|i| ScriptPurpose::Spending(i.out_ref)),
RedeemerTag::Cert => dcert.get(index).cloned().map(ScriptPurpose::Certifying), RedeemerTag::Cert => certificates
RedeemerTag::Reward => wdrl .get(index)
.cloned()
.map(ScriptPurpose::Certifying),
RedeemerTag::Reward => withdrawals
.get(index) .get(index)
.cloned() .cloned()
.map(|(address, _)| match address { .map(|(address, _)| match address {
@ -415,7 +618,7 @@ fn script_purpose_builder<'a>(
StakePayload::Script(script_hash) => Ok(ScriptPurpose::Rewarding( StakePayload::Script(script_hash) => Ok(ScriptPurpose::Rewarding(
StakeCredential::Scripthash(*script_hash), StakeCredential::Scripthash(*script_hash),
)), )),
StakePayload::Stake(_) => Err(Error::ScriptKeyHash), StakePayload::Stake(_) => Err(Error::NonScriptWithdrawal),
}, },
_ => Err(Error::BadWithdrawalAddress), _ => Err(Error::BadWithdrawalAddress),
}) })
@ -426,6 +629,108 @@ fn script_purpose_builder<'a>(
} }
} }
pub fn find_script(
redeemer: &Redeemer,
tx: &MintedTx,
utxos: &[ResolvedInput],
lookup_table: &DataLookupTable,
) -> Result<(ScriptVersion, Option<PlutusData>), Error> {
let lookup_script = |script_hash: &ScriptHash| match lookup_table.scripts.get(script_hash) {
Some(s) => Ok((s.clone(), None)),
None => Err(Error::MissingRequiredScript {
hash: script_hash.to_string(),
}),
};
let lookup_datum = |datum: Option<DatumOption>| 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()),
_ => Err(Error::MissingRequiredInlineDatumOrHash),
};
match redeemer.tag {
RedeemerTag::Mint => get_mint_info(&tx.transaction_body.mint)
.mint_value
.get(redeemer.index as usize)
.ok_or(Error::MissingScriptForRedeemer)
.and_then(|(policy_id, _)| {
let policy_id_array: [u8; 28] = policy_id.to_vec().try_into().unwrap();
let hash = Hash::from(policy_id_array);
lookup_script(&hash)
}),
RedeemerTag::Reward => get_withdrawal_info(&tx.transaction_body.withdrawals)
.get(redeemer.index as usize)
.ok_or(Error::MissingScriptForRedeemer)
.and_then(|(addr, _)| {
let stake_addr = if let Address::Stake(stake_addr) = addr {
stake_addr
} else {
unreachable!("withdrawal always contains stake addresses")
};
if let StakePayload::Script(hash) = stake_addr.payload() {
lookup_script(hash)
} else {
Err(Error::NonScriptWithdrawal)
}
}),
RedeemerTag::Cert => get_certificates_info(&tx.transaction_body.certificates)
.get(redeemer.index as usize)
.ok_or(Error::MissingScriptForRedeemer)
.and_then(|cert| match cert {
Certificate::StakeDeregistration(stake_credential) => match stake_credential {
StakeCredential::Scripthash(hash) => Ok(hash),
_ => Err(Error::NonScriptStakeCredential),
},
Certificate::StakeDelegation(stake_credential, _) => match stake_credential {
StakeCredential::Scripthash(hash) => Ok(hash),
_ => Err(Error::NonScriptStakeCredential),
},
Certificate::PoolRetirement { .. } | Certificate::PoolRegistration { .. } => {
Err(Error::UnsupportedCertificateType)
}
_ => {
todo!("remaining certificate types")
}
})
.and_then(lookup_script),
RedeemerTag::Spend => get_tx_in_info_v2(&tx.transaction_body.inputs, utxos)
.or_else(|err| {
if matches!(err, Error::ByronAddressNotAllowed) {
get_tx_in_info_v1(&tx.transaction_body.inputs, utxos)
} else {
Err(err)
}
})?
.get(redeemer.index as usize)
.ok_or(Error::MissingScriptForRedeemer)
.and_then(|input| match input.resolved.address() {
Address::Shelley(shelley_address) => {
let hash = shelley_address.payment().as_hash();
let script = lookup_script(hash);
let datum = lookup_datum(input.resolved.datum());
script.and_then(|(script, _)| Ok((script, Some(datum?))))
}
_ => Err(Error::NonScriptStakeCredential),
}),
RedeemerTag::Propose => todo!("find_script: RedeemerTag::Propose"),
RedeemerTag::Vote => todo!("find_script: RedeemerTag::Vote"),
}
}
fn from_alonzo_value(value: &alonzo::Value) -> Value { 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),

View File

@ -591,8 +591,8 @@ impl ToPlutusData for TxInfo {
tx_info.outputs.to_plutus_data(), tx_info.outputs.to_plutus_data(),
tx_info.fee.to_plutus_data(), tx_info.fee.to_plutus_data(),
tx_info.mint.to_plutus_data(), tx_info.mint.to_plutus_data(),
tx_info.dcert.to_plutus_data(), tx_info.certificates.to_plutus_data(),
tx_info.wdrl.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.data.to_plutus_data(), tx_info.data.to_plutus_data(),
@ -607,8 +607,8 @@ impl ToPlutusData for TxInfo {
tx_info.outputs.to_plutus_data(), tx_info.outputs.to_plutus_data(),
tx_info.fee.to_plutus_data(), tx_info.fee.to_plutus_data(),
tx_info.mint.to_plutus_data(), tx_info.mint.to_plutus_data(),
tx_info.dcert.to_plutus_data(), tx_info.certificates.to_plutus_data(),
tx_info.wdrl.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(), tx_info.redeemers.to_plutus_data(),