Wrapped errors in redeemer error

This commit is contained in:
alessandrokonrad 2022-09-26 21:54:44 +02:00 committed by Lucas
parent 2a360ced3d
commit 3256bfbc32
2 changed files with 235 additions and 222 deletions

View File

@ -21,11 +21,11 @@ pub enum Error {
missing: Vec<String>, missing: Vec<String>,
extra: Vec<String>, extra: Vec<String>,
}, },
#[error("Extraneous redeemer found: Tag {:?}, Index {}", tag, index)] #[error("Extraneous redeemer")]
ExtraneousRedeemer { tag: String, index: u32 }, ExtraneousRedeemer,
#[error("Resolved Input not found")] #[error("Resolved Input not found.")]
ResolvedInputNotFound, ResolvedInputNotFound,
#[error("A key hash cannot be the hash of a script")] #[error("A key hash cannot be the hash of a script.")]
ScriptKeyHash, ScriptKeyHash,
#[error("PlutusV1 cost model not found.")] #[error("PlutusV1 cost model not found.")]
V1CostModelNotFound, V1CostModelNotFound,
@ -41,12 +41,18 @@ pub enum Error {
ScriptAndInputRefNotAllowed, ScriptAndInputRefNotAllowed,
#[error("Address doesn't contain a payment credential.")] #[error("Address doesn't contain a payment credential.")]
NoPaymentCredential, NoPaymentCredential,
#[error("Missing required datum in witness set. Datum hash: {}", hash)] #[error("Missing required datum: {}", hash)]
MissingRequiredDatum { hash: String }, MissingRequiredDatum { hash: String },
#[error("Missing required script. Script hash: {}", hash)] #[error("Missing required script: {}", hash)]
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("Only stake deregistration and delegation are valid certificate script purposes.")]
OnlyStakeDeregAndDelegAllowed, OnlyStakeDeregAndDelegAllowed,
#[error("Redeemer ({}, {}): {}", tag, index, err)]
RedeemerError {
tag: String,
index: u32,
err: Box<Error>,
},
} }

View File

@ -39,6 +39,15 @@ fn slot_range_to_posix_time_range(slot_range: TimeRange, sc: &SlotConfig) -> Tim
} }
} }
fn redeemer_tag_to_string(redeemer_tag: &RedeemerTag) -> String {
match redeemer_tag {
RedeemerTag::Spend => "Spend".to_string(),
RedeemerTag::Mint => "Mint".to_string(),
RedeemerTag::Cert => "Cert".to_string(),
RedeemerTag::Reward => "Reward".to_string(),
}
}
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum ScriptVersion { pub enum ScriptVersion {
Native(NativeScript), Native(NativeScript),
@ -168,10 +177,7 @@ fn get_script_purpose(
policy_ids.sort(); policy_ids.sort();
match policy_ids.get(index as usize) { match policy_ids.get(index as usize) {
Some(policy_id) => Ok(ScriptPurpose::Minting(*policy_id)), Some(policy_id) => Ok(ScriptPurpose::Minting(*policy_id)),
None => Err(Error::ExtraneousRedeemer { None => Err(Error::ExtraneousRedeemer),
tag: "Mint".to_string(),
index,
}),
} }
} }
RedeemerTag::Spend => { RedeemerTag::Spend => {
@ -180,10 +186,7 @@ fn get_script_purpose(
inputs.sort(); inputs.sort();
match inputs.get(index as usize) { match inputs.get(index as usize) {
Some(input) => Ok(ScriptPurpose::Spending(input.clone())), Some(input) => Ok(ScriptPurpose::Spending(input.clone())),
None => Err(Error::ExtraneousRedeemer { None => Err(Error::ExtraneousRedeemer),
tag: "Spend".to_string(),
index,
}),
} }
} }
RedeemerTag::Reward => { RedeemerTag::Reward => {
@ -197,12 +200,7 @@ fn get_script_purpose(
reward_accounts.sort(); reward_accounts.sort();
let reward_account = match reward_accounts.get(index as usize) { let reward_account = match reward_accounts.get(index as usize) {
Some(ra) => ra.clone(), Some(ra) => ra.clone(),
None => { None => return Err(Error::ExtraneousRedeemer),
return Err(Error::ExtraneousRedeemer {
tag: "Reward".to_string(),
index,
})
}
}; };
let address = Address::from_bytes(&reward_account)?; let address = Address::from_bytes(&reward_account)?;
let credential = match address { let credential = match address {
@ -224,10 +222,7 @@ fn get_script_purpose(
.get(index as usize) .get(index as usize)
{ {
Some(cert) => Ok(ScriptPurpose::Certifying(cert.clone())), Some(cert) => Ok(ScriptPurpose::Certifying(cert.clone())),
None => Err(Error::ExtraneousRedeemer { None => Err(Error::ExtraneousRedeemer),
tag: "Cert".to_string(),
index,
}),
} }
} }
} }
@ -610,224 +605,236 @@ pub fn eval_redeemer(
cost_mdls_opt: Option<&CostMdls>, cost_mdls_opt: Option<&CostMdls>,
initial_budget: Option<&ExBudget>, initial_budget: Option<&ExBudget>,
) -> Result<Redeemer, Error> { ) -> Result<Redeemer, Error> {
let purpose = get_script_purpose( let result = || {
redeemer, let purpose = get_script_purpose(
&tx.transaction_body.inputs, redeemer,
&tx.transaction_body.mint, &tx.transaction_body.inputs,
&tx.transaction_body.certificates, &tx.transaction_body.mint,
&tx.transaction_body.withdrawals, &tx.transaction_body.certificates,
)?; &tx.transaction_body.withdrawals,
)?;
let execution_purpose: ExecutionPurpose = get_execution_purpose(utxos, &purpose, lookup_table)?; let execution_purpose: ExecutionPurpose =
get_execution_purpose(utxos, &purpose, lookup_table)?;
match execution_purpose { match execution_purpose {
ExecutionPurpose::WithDatum(script_version, datum) => match script_version { ExecutionPurpose::WithDatum(script_version, datum) => match script_version {
ScriptVersion::V1(script) => { ScriptVersion::V1(script) => {
let tx_info = get_tx_info_v1(tx, utxos, slot_config)?; let tx_info = get_tx_info_v1(tx, utxos, slot_config)?;
let script_context = ScriptContext { tx_info, purpose }; let script_context = ScriptContext { tx_info, purpose };
let program: Program<NamedDeBruijn> = { let program: Program<NamedDeBruijn> = {
let mut buffer = Vec::new(); let mut buffer = Vec::new();
let prog = Program::<FakeNamedDeBruijn>::from_cbor(&script.0, &mut buffer)?; let prog = Program::<FakeNamedDeBruijn>::from_cbor(&script.0, &mut buffer)?;
prog.into() prog.into()
};
let program = program
.apply_data(datum)
.apply_data(redeemer.data.clone())
.apply_data(script_context.to_plutus_data());
let (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt {
let costs = if let Some(costs) = &cost_mdls.plutus_v1 {
costs
} else {
return Err(Error::V1CostModelNotFound);
}; };
program.eval_as(&Language::PlutusV1, costs, initial_budget) let program = program
} else { .apply_data(datum)
program.eval_v1() .apply_data(redeemer.data.clone())
}; .apply_data(script_context.to_plutus_data());
match result { let (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt {
Ok(_) => (), let costs = if let Some(costs) = &cost_mdls.plutus_v1 {
Err(err) => return Err(Error::Machine(err, budget, logs)), costs
} } else {
return Err(Error::V1CostModelNotFound);
};
let initial_budget = match initial_budget { program.eval_as(&Language::PlutusV1, costs, initial_budget)
Some(b) => *b,
None => ExBudget::default(),
};
let new_redeemer = Redeemer {
tag: redeemer.tag.clone(),
index: redeemer.index,
data: redeemer.data.clone(),
ex_units: ExUnits {
mem: (initial_budget.mem - budget.mem) as u32,
steps: (initial_budget.cpu - budget.cpu) as u64,
},
};
Ok(new_redeemer)
}
ScriptVersion::V2(script) => {
let tx_info = get_tx_info_v2(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(datum)
.apply_data(redeemer.data.clone())
.apply_data(script_context.to_plutus_data());
let (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt {
let costs = if let Some(costs) = &cost_mdls.plutus_v2 {
costs
} else { } else {
return Err(Error::V2CostModelNotFound); program.eval_v1()
}; };
program.eval_as(&Language::PlutusV2, costs, initial_budget) match result {
} else { Ok(_) => (),
program.eval() Err(err) => return Err(Error::Machine(err, budget, logs)),
}; }
match result { let initial_budget = match initial_budget {
Ok(_) => (), Some(b) => *b,
Err(err) => return Err(Error::Machine(err, budget, logs)), None => ExBudget::default(),
}
let initial_budget = match initial_budget {
Some(b) => *b,
None => ExBudget::default(),
};
let new_redeemer = Redeemer {
tag: redeemer.tag.clone(),
index: redeemer.index,
data: redeemer.data.clone(),
ex_units: ExUnits {
mem: (initial_budget.mem - budget.mem) as u32,
steps: (initial_budget.cpu - budget.cpu) as u64,
},
};
Ok(new_redeemer)
}
ScriptVersion::Native(_) => Err(Error::NativeScriptPhaseTwo),
},
ExecutionPurpose::NoDatum(script_version) => match script_version {
ScriptVersion::V1(script) => {
let tx_info = get_tx_info_v1(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 (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt {
let costs = if let Some(costs) = &cost_mdls.plutus_v1 {
costs
} else {
return Err(Error::V1CostModelNotFound);
}; };
program.eval_as(&Language::PlutusV1, costs, initial_budget) let new_redeemer = Redeemer {
} else { tag: redeemer.tag.clone(),
program.eval_v1() index: redeemer.index,
}; data: redeemer.data.clone(),
ex_units: ExUnits {
match result { mem: (initial_budget.mem - budget.mem) as u32,
Ok(_) => (), steps: (initial_budget.cpu - budget.cpu) as u64,
Err(err) => return Err(Error::Machine(err, budget, logs)), },
}
let initial_budget = match initial_budget {
Some(b) => *b,
None => ExBudget::default(),
};
let new_redeemer = Redeemer {
tag: redeemer.tag.clone(),
index: redeemer.index,
data: redeemer.data.clone(),
ex_units: ExUnits {
mem: (initial_budget.mem - budget.mem) as u32,
steps: (initial_budget.cpu - budget.cpu) as u64,
},
};
Ok(new_redeemer)
}
ScriptVersion::V2(script) => {
let tx_info = get_tx_info_v2(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 (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt {
let costs = if let Some(costs) = &cost_mdls.plutus_v2 {
costs
} else {
return Err(Error::V2CostModelNotFound);
}; };
program.eval_as(&Language::PlutusV2, costs, initial_budget) Ok(new_redeemer)
} else {
program.eval()
};
match result {
Ok(_) => (),
Err(err) => return Err(Error::Machine(err, budget, logs)),
} }
ScriptVersion::V2(script) => {
let tx_info = get_tx_info_v2(tx, utxos, slot_config)?;
let script_context = ScriptContext { tx_info, purpose };
let initial_budget = match initial_budget { let program: Program<NamedDeBruijn> = {
Some(b) => *b, let mut buffer = Vec::new();
None => ExBudget::default(),
};
let new_redeemer = Redeemer { let prog = Program::<FakeNamedDeBruijn>::from_cbor(&script.0, &mut buffer)?;
tag: redeemer.tag.clone(),
index: redeemer.index,
data: redeemer.data.clone(),
ex_units: ExUnits {
mem: (initial_budget.mem - budget.mem) as u32,
steps: (initial_budget.cpu - budget.cpu) as u64,
},
};
Ok(new_redeemer) prog.into()
} };
ScriptVersion::Native(_) => Err(Error::NativeScriptPhaseTwo),
}, let program = program
.apply_data(datum)
.apply_data(redeemer.data.clone())
.apply_data(script_context.to_plutus_data());
let (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt {
let costs = if let Some(costs) = &cost_mdls.plutus_v2 {
costs
} else {
return Err(Error::V2CostModelNotFound);
};
program.eval_as(&Language::PlutusV2, costs, initial_budget)
} else {
program.eval()
};
match result {
Ok(_) => (),
Err(err) => return Err(Error::Machine(err, budget, logs)),
}
let initial_budget = match initial_budget {
Some(b) => *b,
None => ExBudget::default(),
};
let new_redeemer = Redeemer {
tag: redeemer.tag.clone(),
index: redeemer.index,
data: redeemer.data.clone(),
ex_units: ExUnits {
mem: (initial_budget.mem - budget.mem) as u32,
steps: (initial_budget.cpu - budget.cpu) as u64,
},
};
Ok(new_redeemer)
}
ScriptVersion::Native(_) => Err(Error::NativeScriptPhaseTwo),
},
ExecutionPurpose::NoDatum(script_version) => match script_version {
ScriptVersion::V1(script) => {
let tx_info = get_tx_info_v1(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 (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt {
let costs = if let Some(costs) = &cost_mdls.plutus_v1 {
costs
} else {
return Err(Error::V1CostModelNotFound);
};
program.eval_as(&Language::PlutusV1, costs, initial_budget)
} else {
program.eval_v1()
};
match result {
Ok(_) => (),
Err(err) => return Err(Error::Machine(err, budget, logs)),
}
let initial_budget = match initial_budget {
Some(b) => *b,
None => ExBudget::default(),
};
let new_redeemer = Redeemer {
tag: redeemer.tag.clone(),
index: redeemer.index,
data: redeemer.data.clone(),
ex_units: ExUnits {
mem: (initial_budget.mem - budget.mem) as u32,
steps: (initial_budget.cpu - budget.cpu) as u64,
},
};
Ok(new_redeemer)
}
ScriptVersion::V2(script) => {
let tx_info = get_tx_info_v2(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 (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt {
let costs = if let Some(costs) = &cost_mdls.plutus_v2 {
costs
} else {
return Err(Error::V2CostModelNotFound);
};
program.eval_as(&Language::PlutusV2, costs, initial_budget)
} else {
program.eval()
};
match result {
Ok(_) => (),
Err(err) => return Err(Error::Machine(err, budget, logs)),
}
let initial_budget = match initial_budget {
Some(b) => *b,
None => ExBudget::default(),
};
let new_redeemer = Redeemer {
tag: redeemer.tag.clone(),
index: redeemer.index,
data: redeemer.data.clone(),
ex_units: ExUnits {
mem: (initial_budget.mem - budget.mem) as u32,
steps: (initial_budget.cpu - budget.cpu) as u64,
},
};
Ok(new_redeemer)
}
ScriptVersion::Native(_) => Err(Error::NativeScriptPhaseTwo),
},
}
};
match result() {
Ok(r) => Ok(r),
Err(err) => Err(Error::RedeemerError {
tag: redeemer_tag_to_string(&redeemer.tag),
index: redeemer.index,
err: Box::new(err),
}),
} }
} }