diff --git a/crates/aiken/src/cmd/tx/simulate.rs b/crates/aiken/src/cmd/tx/simulate.rs index 3db907ba..d3efe67b 100644 --- a/crates/aiken/src/cmd/tx/simulate.rs +++ b/crates/aiken/src/cmd/tx/simulate.rs @@ -149,7 +149,14 @@ pub fn exec( ); } Err(err) => { - eprintln!("{}", display_tx_error(&err)); + eprintln!( + "{} {}", + " Error" + .if_supports_color(Stderr, |s| s.red()) + .if_supports_color(Stderr, |s| s.bold()), + err.red() + ); + process::exit(1); } } @@ -157,46 +164,3 @@ pub fn exec( Ok(()) } - -fn display_tx_error(err: &tx::error::Error) -> String { - let mut msg = format!( - "{} {}", - " Error" - .if_supports_color(Stderr, |s| s.red()) - .if_supports_color(Stderr, |s| s.bold()), - err.red() - ); - match err { - tx::error::Error::RedeemerError { err, .. } => { - msg.push_str(&format!( - "\n{}", - display_tx_error(err) - .lines() - .skip(1) - .collect::>() - .join("\n"), - )); - msg - } - tx::error::Error::Machine(_, _, traces) => { - msg.push_str( - traces - .iter() - .map(|s| { - format!( - "\n{} {}", - " Trace" - .if_supports_color(Stderr, |s| s.yellow()) - .if_supports_color(Stderr, |s| s.bold()), - s.if_supports_color(Stderr, |s| s.yellow()) - ) - }) - .collect::>() - .join("") - .as_str(), - ); - msg - } - _ => msg, - } -} diff --git a/crates/uplc/src/machine/error.rs b/crates/uplc/src/machine/error.rs index 5e9b9e47..57698a34 100644 --- a/crates/uplc/src/machine/error.rs +++ b/crates/uplc/src/machine/error.rs @@ -11,7 +11,7 @@ pub enum Error { InvalidStepKind(u8), #[error("Cannot evaluate an open term:\\n\\n{}", .0.to_pretty())] OpenTermEvaluated(Term), - #[error("The provided Plutus code called 'error'.")] + #[error("The validator crashed / exited prematurely")] EvaluationFailure, #[error("Attempted to instantiate a non-polymorphic term:\n\n{0:#?}")] NonPolymorphicInstantiation(Value), @@ -29,9 +29,13 @@ pub enum Error { PairTypeMismatch(Type), #[error("Empty List:\n\n{0:#?}")] EmptyList(Value), - #[error("A builtin received a term argument when something else was expected:\n\n{0}\n\nYou probably forgot to wrap the builtin with a force.")] + #[error( + "A builtin received a term argument when something else was expected:\n\n{0}\n\nYou probably forgot to wrap the builtin with a force." + )] UnexpectedBuiltinTermArgument(Term), - #[error("A builtin expected a term argument, but something else was received:\n\n{0}\n\nYou probably have an extra force wrapped around a builtin")] + #[error( + "A builtin expected a term argument, but something else was received:\n\n{0}\n\nYou probably have an extra force wrapped around a builtin" + )] BuiltinTermArgumentExpected(Term), #[error("Unable to unlift value because it is not a constant:\n\n{0:#?}")] NotAConstant(Value), diff --git a/crates/uplc/src/tx/error.rs b/crates/uplc/src/tx/error.rs index 97dd4089..c1063090 100644 --- a/crates/uplc/src/tx/error.rs +++ b/crates/uplc/src/tx/error.rs @@ -8,59 +8,93 @@ use pallas_primitives::conway::Language; pub enum Error { #[error("{0}")] Address(#[from] pallas_addresses::Error), - #[error("Only shelley reward addresses can be a part of withdrawals")] + #[error("only shelley reward addresses can be a part of withdrawals")] BadWithdrawalAddress, #[error("{0}")] FlatDecode(#[from] pallas_codec::flat::de::Error), #[error("{0}")] FragmentDecode(#[from] pallas_primitives::Error), - #[error("{}\n\n{:#?}\n\n{}", .0, .1, .2.join("\n"))] + #[error("{}{}", .0, .2.iter() + .map(|trace| { + format!( + "\n{:>13} {}", + "Trace", + if trace.contains("\n") { + trace.lines() + .enumerate() + .map(|(ix, row)| { + if ix == 0 { + row.to_string() + } else { + format!("{:>13} {}", "", + row + ) + } + }) + .collect::>() + .join("\n") + } else { + trace.to_string() + } + ) + }) + .collect::>() + .join("") + .as_str() + )] Machine(machine::Error, ExBudget, Vec), - #[error("Native script can't be executed in phase-two")] + + #[error("native script can't be executed in phase-two")] NativeScriptPhaseTwo, - #[error("Can't eval without redeemers")] + #[error("can't eval without redeemers")] NoRedeemers, - #[error("Mismatch in required redeemers: {} {}", .missing.join(" "), .extra.join(" "))] + #[error( + "mismatch in expected redeemers\n{:>13} {}\n{:>13} {}", + "Missing", + if .missing.is_empty() { "ø".to_string() } else { .missing.join(&format!("\n{:>13}", "")) }, + "Unexpected", + if .extra.is_empty() { "ø".to_string() } else { .extra.join(&format!("\n{:>13}", "")) }, + )] RequiredRedeemersMismatch { missing: Vec, extra: Vec, }, - #[error("Extraneous redeemer")] + #[error("extraneous redeemer")] ExtraneousRedeemer, - #[error("Resolved Input not found.")] + #[error("resolved Input not found")] ResolvedInputNotFound(TransactionInput), - #[error("Redeemer points to a non-script withdrawal.")] + #[error("redeemer points to a non-script withdrawal")] NonScriptWithdrawal, - #[error("Stake credential points to a non-script withdrawal.")] + #[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\n{:>13} {:?}", "Language", .0)] CostModelNotFound(Language), - #[error("Wrong era, Please use Babbage or Alonzo: {0}")] + #[error("unsupported era, please use Conway\n{:>13} {0}", "Decoder error")] WrongEra(#[from] pallas_codec::minicbor::decode::Error), - #[error("Byron address not allowed in Plutus.")] + #[error("byron address not allowed when PlutusV2 scripts are present")] ByronAddressNotAllowed, - #[error("Inline datum not allowed in PlutusV1.")] + #[error("inline datum not allowed when PlutusV1 scripts are present")] InlineDatumNotAllowed, - #[error("Script and input reference not allowed in PlutusV1.")] + #[error("script and input reference not allowed in PlutusV1")] ScriptAndInputRefNotAllowed, - #[error("Address doesn't contain a payment credential.")] + #[error("address doesn't contain a payment credential")] NoPaymentCredential, - #[error("Missing required datum: {}", hash)] + #[error("missing required datum\n{:>13} {}", "Datum", hash)] MissingRequiredDatum { hash: String }, - #[error("Missing required script: {}", hash)] + #[error("missing required script\n{:>13} {}", "Script", hash)] 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, - #[error("Redeemer points to an unsupported certificate type.")] + #[error("redeemer points to an unsupported certificate type")] UnsupportedCertificateType, - #[error("Redeemer ({}, {}): {}", tag, index, err)] + #[error("failed script execution\n{:>13} {}", format!("{}({})", tag, index), err)] RedeemerError { tag: String, index: u32, err: Box, }, - #[error("Missing script for redeemer")] + #[error("missing script for redeemer")] MissingScriptForRedeemer, - #[error("Failed to apply parameters to Plutus script.")] + #[error("failed to apply parameters to Plutus script")] ApplyParamsError, } diff --git a/crates/uplc/src/tx/phase_one.rs b/crates/uplc/src/tx/phase_one.rs index 67864070..c86436bb 100644 --- a/crates/uplc/src/tx/phase_one.rs +++ b/crates/uplc/src/tx/phase_one.rs @@ -39,23 +39,17 @@ pub fn validate_missing_scripts( .clone() .into_iter() .filter(|x| !received_hashes.contains(x)) - .map(|x| format!("[Missing (sh: {x})]")) + .map(|x| x.to_string()) .collect(); let extra: Vec<_> = received_hashes .into_iter() .filter(|x| !needed_hashes.contains(x)) - .map(|x| format!("[Extraneous (sh: {x:?})]")) + .map(|x| x.to_string()) .collect(); if !missing.is_empty() || !extra.is_empty() { - let missing_errors = missing.join(" "); - let extra_errors = extra.join(" "); - - unreachable!( - "Mismatch in required scripts: {} {}", - missing_errors, extra_errors - ); + return Err(Error::RequiredRedeemersMismatch { missing, extra }); } Ok(()) @@ -191,18 +185,13 @@ pub fn has_exact_set_of_redeemers( let missing: Vec<_> = redeemers_needed .into_iter() .filter(|x| !wits_redeemer_keys.contains(&&x.0)) - .map(|x| { - format!( - "[Missing (redeemer_key: {:?}, script_purpose: {:?}, script_hash: {})]", - x.0, x.1, x.2, - ) - }) + .map(|x| format!("{} (key: {:?}, purpose: {:?})", x.2, x.0, x.1,)) .collect(); let extra: Vec<_> = wits_redeemer_keys .into_iter() .filter(|x| !needed_redeemer_keys.contains(x)) - .map(|x| format!("[Extraneous (redeemer_key: {x:?})]")) + .map(|x| format!("{x:?}")) .collect(); if !missing.is_empty() || !extra.is_empty() {