diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 859f2adf..39e4cc51 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -54,7 +54,27 @@ fn main() -> anyhow::Result<()> { slot_length, }; - tx::eval_phase_two(tx_babbage, &resolved_inputs, None, &slot_config, true)?; + let result = tx::eval_phase_two( + tx_babbage, + &resolved_inputs, + None, + None, + &slot_config, + true, + ); + + match result { + Ok(redeemers) => { + println!("\nResult\n------\n\n"); + + for redeemer in redeemers { + println!("{:#?}", redeemer) + } + } + Err(err) => { + eprintln!("\nError\n-----\n\n{}\n", err); + } + } } } }, diff --git a/crates/uplc/src/ast.rs b/crates/uplc/src/ast.rs index f5a9e66b..dbfcc2c8 100644 --- a/crates/uplc/src/ast.rs +++ b/crates/uplc/src/ast.rs @@ -526,7 +526,7 @@ impl Program { Vec, ) { let budget = match initial_budget { - Some(b) => b.clone(), + Some(b) => *b, None => ExBudget::default(), }; @@ -556,3 +556,9 @@ impl Program { program.eval() } } + +impl Term { + pub fn is_valid_script_result(&self) -> bool { + matches!(self, Term::Constant(Constant::Unit)) + } +} diff --git a/crates/uplc/src/tx/error.rs b/crates/uplc/src/tx/error.rs index fe563144..1d4befa2 100644 --- a/crates/uplc/src/tx/error.rs +++ b/crates/uplc/src/tx/error.rs @@ -1,17 +1,22 @@ -use crate::machine; +use crate::{ + ast::{NamedDeBruijn, Term}, + machine::{self, cost_model::ExBudget}, +}; #[derive(thiserror::Error, Debug)] pub enum Error { #[error("{0}")] Address(#[from] pallas_addresses::Error), + #[error("{}\n\n{:#?}\n\n{}", .0, .1, .2.join("\n"))] + BadTerm(Term, ExBudget, Vec), #[error("Only shelley reward addresses can be a part of withdrawals")] BadWithdrawalAddress, #[error("{0}")] FlatDecode(#[from] flat_rs::de::Error), #[error("{0}")] FragmentDecode(#[from] pallas_primitives::Error), - #[error("{0}")] - Machine(#[from] machine::Error), + #[error("{}\n\n{:#?}\n\n{}", .0, .1, .2.join("\n"))] + Machine(machine::Error, ExBudget, Vec), #[error("Native script can't be executed in phase-two")] NativeScriptPhaseTwo, #[error("Can't eval without redeemers")] diff --git a/crates/uplc/src/tx/eval.rs b/crates/uplc/src/tx/eval.rs index 54c76eb2..4dad060f 100644 --- a/crates/uplc/src/tx/eval.rs +++ b/crates/uplc/src/tx/eval.rs @@ -168,12 +168,10 @@ fn get_script_purpose( policy_ids.sort(); match policy_ids.get(index as usize) { Some(policy_id) => Ok(ScriptPurpose::Minting(*policy_id)), - None => { - return Err(Error::ExtraneousRedeemer { - tag: "Mint".to_string(), - index, - }) - } + None => Err(Error::ExtraneousRedeemer { + tag: "Mint".to_string(), + index, + }), } } RedeemerTag::Spend => { @@ -182,12 +180,10 @@ fn get_script_purpose( inputs.sort(); match inputs.get(index as usize) { Some(input) => Ok(ScriptPurpose::Spending(input.clone())), - None => { - return Err(Error::ExtraneousRedeemer { - tag: "Spend".to_string(), - index, - }) - } + None => Err(Error::ExtraneousRedeemer { + tag: "Spend".to_string(), + index, + }), } } RedeemerTag::Reward => { @@ -228,12 +224,10 @@ fn get_script_purpose( .get(index as usize) { Some(cert) => Ok(ScriptPurpose::Certifying(cert.clone())), - None => { - return Err(Error::ExtraneousRedeemer { - tag: "Cert".to_string(), - index, - }) - } + None => Err(Error::ExtraneousRedeemer { + tag: "Cert".to_string(), + index, + }), } } } @@ -436,7 +430,7 @@ fn get_execution_purpose( Ok(ExecutionPurpose::WithDatum(script, datum)) } - _ => return Err(Error::ScriptKeyHash), + _ => Err(Error::ScriptKeyHash), } } TransactionOutput::PostAlonzo(output) => { @@ -470,7 +464,7 @@ fn get_execution_purpose( Ok(ExecutionPurpose::WithDatum(script, datum)) } - _ => return Err(Error::ScriptKeyHash), + _ => Err(Error::ScriptKeyHash), } } } @@ -527,7 +521,7 @@ fn get_execution_purpose( Ok(ExecutionPurpose::NoDatum(script)) } - _ => return Err(Error::OnlyStakeDeregAndDelegAllowed), + _ => Err(Error::OnlyStakeDeregAndDelegAllowed), }, } } @@ -645,7 +639,7 @@ pub fn eval_redeemer( .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()); - let (result, budget, _) = if let Some(cost_mdls) = cost_mdls_opt { + let (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt { let costs = if let Some(costs) = &cost_mdls.plutus_v1 { costs } else { @@ -657,11 +651,17 @@ pub fn eval_redeemer( program.eval_v1() }; - // TODO: do we want the logs in the error? - result?; + match result { + Ok(term) => { + if !term.is_valid_script_result() { + return Err(Error::BadTerm(term, budget, logs)); + } + } + Err(err) => return Err(Error::Machine(err, budget, logs)), + } let initial_budget = match initial_budget { - Some(b) => b.clone(), + Some(b) => *b, None => ExBudget::default(), }; @@ -694,7 +694,7 @@ pub fn eval_redeemer( .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()); - let (result, budget, _) = if let Some(cost_mdls) = cost_mdls_opt { + let (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt { let costs = if let Some(costs) = &cost_mdls.plutus_v2 { costs } else { @@ -706,11 +706,17 @@ pub fn eval_redeemer( program.eval() }; - // TODO: do we want the logs in the error? - result?; + match result { + Ok(term) => { + if !term.is_valid_script_result() { + return Err(Error::BadTerm(term, budget, logs)); + } + } + Err(err) => return Err(Error::Machine(err, budget, logs)), + } let initial_budget = match initial_budget { - Some(b) => b.clone(), + Some(b) => *b, None => ExBudget::default(), }; @@ -745,7 +751,7 @@ pub fn eval_redeemer( .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()); - let (result, budget, _) = if let Some(cost_mdls) = cost_mdls_opt { + let (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt { let costs = if let Some(costs) = &cost_mdls.plutus_v1 { costs } else { @@ -757,11 +763,17 @@ pub fn eval_redeemer( program.eval_v1() }; - // TODO: do we want the logs in the error? - result?; + match result { + Ok(term) => { + if !term.is_valid_script_result() { + return Err(Error::BadTerm(term, budget, logs)); + } + } + Err(err) => return Err(Error::Machine(err, budget, logs)), + } let initial_budget = match initial_budget { - Some(b) => b.clone(), + Some(b) => *b, None => ExBudget::default(), }; @@ -793,7 +805,7 @@ pub fn eval_redeemer( .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()); - let (result, budget, _) = if let Some(cost_mdls) = cost_mdls_opt { + let (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt { let costs = if let Some(costs) = &cost_mdls.plutus_v2 { costs } else { @@ -805,11 +817,17 @@ pub fn eval_redeemer( program.eval() }; - // TODO: do we want the logs in the error? - result?; + match result { + Ok(term) => { + if !term.is_valid_script_result() { + return Err(Error::BadTerm(term, budget, logs)); + } + } + Err(err) => return Err(Error::Machine(err, budget, logs)), + } let initial_budget = match initial_budget { - Some(b) => b.clone(), + Some(b) => *b, None => ExBudget::default(), };