Upgrade pallas to v0.0.29, and start support for simulating transactions carrying Plutus v3 scripts.
This commit is contained in:
parent
0dec4dc533
commit
ff4a480242
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
10
Cargo.toml
|
@ -49,11 +49,11 @@ x86_64-unknown-linux-gnu = "ubuntu-22.04"
|
||||||
walkdir = "2.3.2"
|
walkdir = "2.3.2"
|
||||||
insta = { version = "1.30.0", features = ["yaml", "json", "redactions"] }
|
insta = { version = "1.30.0", features = ["yaml", "json", "redactions"] }
|
||||||
miette = { version = "7.2.0", features = ["fancy"] }
|
miette = { version = "7.2.0", features = ["fancy"] }
|
||||||
pallas-addresses = "0.22.0"
|
pallas-addresses = { git = "https://github.com/KtorZ/pallas.git", rev = "8ea5a1adc9919b70b213dfe597e920d6e113120c" }
|
||||||
pallas-codec = { version = "0.22.0", features = ["num-bigint"] }
|
pallas-codec = { git = "https://github.com/KtorZ/pallas.git", rev = "8ea5a1adc9919b70b213dfe597e920d6e113120c", features = ["num-bigint"] }
|
||||||
pallas-crypto = "0.22.0"
|
pallas-crypto = { git = "https://github.com/KtorZ/pallas.git", rev = "8ea5a1adc9919b70b213dfe597e920d6e113120c" }
|
||||||
pallas-primitives = "0.22.0"
|
pallas-primitives = { git = "https://github.com/KtorZ/pallas.git", rev = "8ea5a1adc9919b70b213dfe597e920d6e113120c" }
|
||||||
pallas-traverse = "0.22.0"
|
pallas-traverse = { git = "https://github.com/KtorZ/pallas.git", rev = "8ea5a1adc9919b70b213dfe597e920d6e113120c" }
|
||||||
|
|
||||||
[profile.dev.package.insta]
|
[profile.dev.package.insta]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use miette::IntoDiagnostic;
|
use miette::IntoDiagnostic;
|
||||||
use owo_colors::{OwoColorize, Stream::Stderr};
|
use owo_colors::{OwoColorize, Stream::Stderr};
|
||||||
use pallas_primitives::{
|
use pallas_primitives::{
|
||||||
babbage::{Redeemer, TransactionInput, TransactionOutput},
|
conway::{Redeemer, TransactionInput, TransactionOutput},
|
||||||
Fragment,
|
Fragment,
|
||||||
};
|
};
|
||||||
use pallas_traverse::{Era, MultiEraTx};
|
use pallas_traverse::{Era, MultiEraTx};
|
||||||
|
@ -101,7 +101,7 @@ pub fn exec(
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if let Some(tx_babbage) = tx.as_babbage() {
|
if let Some(tx_conway) = tx.as_conway() {
|
||||||
let slot_config = SlotConfig {
|
let slot_config = SlotConfig {
|
||||||
zero_time,
|
zero_time,
|
||||||
zero_slot,
|
zero_slot,
|
||||||
|
@ -120,7 +120,7 @@ pub fn exec(
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = tx::eval_phase_two(
|
let result = tx::eval_phase_two(
|
||||||
tx_babbage,
|
tx_conway,
|
||||||
&resolved_inputs,
|
&resolved_inputs,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
|
|
@ -1,13 +1,3 @@
|
||||||
use num_bigint::BigInt;
|
|
||||||
use pallas_codec::flat::{
|
|
||||||
de::{self, Decode, Decoder},
|
|
||||||
en::{self, Encode, Encoder},
|
|
||||||
Flat,
|
|
||||||
};
|
|
||||||
use pallas_primitives::{babbage::PlutusData, Fragment};
|
|
||||||
|
|
||||||
use std::{collections::VecDeque, fmt::Debug, rc::Rc};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
Constant, DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term, Type, Unique,
|
Constant, DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term, Type, Unique,
|
||||||
|
@ -15,6 +5,14 @@ use crate::{
|
||||||
builtins::DefaultFunction,
|
builtins::DefaultFunction,
|
||||||
machine::runtime::Compressable,
|
machine::runtime::Compressable,
|
||||||
};
|
};
|
||||||
|
use num_bigint::BigInt;
|
||||||
|
use pallas_codec::flat::{
|
||||||
|
de::{self, Decode, Decoder},
|
||||||
|
en::{self, Encode, Encoder},
|
||||||
|
Flat,
|
||||||
|
};
|
||||||
|
use pallas_primitives::{conway::PlutusData, Fragment};
|
||||||
|
use std::{collections::VecDeque, fmt::Debug, rc::Rc};
|
||||||
|
|
||||||
const BUILTIN_TAG_WIDTH: u32 = 7;
|
const BUILTIN_TAG_WIDTH: u32 = 7;
|
||||||
const CONST_TAG_WIDTH: u32 = 4;
|
const CONST_TAG_WIDTH: u32 = 4;
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
use std::{collections::VecDeque, mem::size_of, ops::Deref, rc::Rc};
|
use super::{runtime::BuiltinRuntime, Error};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Constant, NamedDeBruijn, Term, Type},
|
ast::{Constant, NamedDeBruijn, Term, Type},
|
||||||
builtins::DefaultFunction,
|
builtins::DefaultFunction,
|
||||||
};
|
};
|
||||||
use num_bigint::BigInt;
|
use num_bigint::BigInt;
|
||||||
use num_traits::{Signed, ToPrimitive, Zero};
|
use num_traits::{Signed, ToPrimitive, Zero};
|
||||||
use pallas_primitives::babbage::{self, PlutusData};
|
use pallas_primitives::conway::{self, PlutusData};
|
||||||
|
use std::{collections::VecDeque, mem::size_of, ops::Deref, rc::Rc};
|
||||||
use super::{runtime::BuiltinRuntime, Error};
|
|
||||||
|
|
||||||
pub(super) type Env = Rc<Vec<Value>>;
|
pub(super) type Env = Rc<Vec<Value>>;
|
||||||
|
|
||||||
|
@ -398,44 +396,41 @@ pub fn integer_log2(i: BigInt) -> i64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_pallas_bigint(n: &babbage::BigInt) -> BigInt {
|
pub fn from_pallas_bigint(n: &conway::BigInt) -> BigInt {
|
||||||
match n {
|
match n {
|
||||||
babbage::BigInt::Int(i) => i128::from(*i).into(),
|
conway::BigInt::Int(i) => i128::from(*i).into(),
|
||||||
babbage::BigInt::BigUInt(bytes) => BigInt::from_bytes_be(num_bigint::Sign::Plus, bytes),
|
conway::BigInt::BigUInt(bytes) => BigInt::from_bytes_be(num_bigint::Sign::Plus, bytes),
|
||||||
babbage::BigInt::BigNInt(bytes) => {
|
conway::BigInt::BigNInt(bytes) => BigInt::from_bytes_be(num_bigint::Sign::Minus, bytes) - 1,
|
||||||
BigInt::from_bytes_be(num_bigint::Sign::Minus, bytes) - 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_pallas_bigint(n: &BigInt) -> babbage::BigInt {
|
pub fn to_pallas_bigint(n: &BigInt) -> conway::BigInt {
|
||||||
if let Some(i) = n.to_i128() {
|
if let Some(i) = n.to_i128() {
|
||||||
if let Ok(i) = i.try_into() {
|
if let Ok(i) = i.try_into() {
|
||||||
let pallas_int: pallas_codec::utils::Int = i;
|
let pallas_int: pallas_codec::utils::Int = i;
|
||||||
return babbage::BigInt::Int(pallas_int);
|
return conway::BigInt::Int(pallas_int);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.is_positive() {
|
if n.is_positive() {
|
||||||
let (_, bytes) = n.to_bytes_be();
|
let (_, bytes) = n.to_bytes_be();
|
||||||
babbage::BigInt::BigUInt(bytes.into())
|
conway::BigInt::BigUInt(bytes.into())
|
||||||
} else {
|
} else {
|
||||||
// Note that this would break if n == 0
|
// Note that this would break if n == 0
|
||||||
// BUT n == 0 always fits into 64bits and hence would end up in the first branch.
|
// BUT n == 0 always fits into 64bits and hence would end up in the first branch.
|
||||||
let n: BigInt = n + 1;
|
let n: BigInt = n + 1;
|
||||||
let (_, bytes) = n.to_bytes_be();
|
let (_, bytes) = n.to_bytes_be();
|
||||||
babbage::BigInt::BigNInt(bytes.into())
|
conway::BigInt::BigNInt(bytes.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use num_bigint::BigInt;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::Constant,
|
ast::Constant,
|
||||||
machine::value::{integer_log2, Value},
|
machine::value::{integer_log2, Value},
|
||||||
};
|
};
|
||||||
|
use num_bigint::BigInt;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn to_ex_mem_bigint() {
|
fn to_ex_mem_bigint() {
|
||||||
|
|
|
@ -1,18 +1,14 @@
|
||||||
use std::{cmp::Ordering, iter, ops::Neg, rc::Rc, vec};
|
use super::interner::CodeGenInterner;
|
||||||
|
|
||||||
use indexmap::IndexMap;
|
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use pallas_primitives::babbage::{BigInt, PlutusData};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Constant, Data, Name, NamedDeBruijn, Program, Term, Type},
|
ast::{Constant, Data, Name, NamedDeBruijn, Program, Term, Type},
|
||||||
builder::{CONSTR_FIELDS_EXPOSER, CONSTR_INDEX_EXPOSER},
|
builder::{CONSTR_FIELDS_EXPOSER, CONSTR_INDEX_EXPOSER},
|
||||||
builtins::DefaultFunction,
|
builtins::DefaultFunction,
|
||||||
machine::cost_model::ExBudget,
|
machine::cost_model::ExBudget,
|
||||||
};
|
};
|
||||||
|
use indexmap::IndexMap;
|
||||||
use super::interner::CodeGenInterner;
|
use itertools::Itertools;
|
||||||
|
use pallas_primitives::conway::{BigInt, PlutusData};
|
||||||
|
use std::{cmp::Ordering, iter, ops::Neg, rc::Rc, vec};
|
||||||
|
|
||||||
#[derive(Eq, Hash, PartialEq, Clone, Debug, PartialOrd)]
|
#[derive(Eq, Hash, PartialEq, Clone, Debug, PartialOrd)]
|
||||||
pub enum ScopePath {
|
pub enum ScopePath {
|
||||||
|
@ -1981,18 +1977,15 @@ fn pop_lambdas_and_get_names(term: &Term<Name>) -> (Vec<Rc<Name>>, &Term<Name>)
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::NO_INLINE;
|
||||||
use pallas_primitives::babbage::{BigInt, PlutusData};
|
|
||||||
use pretty_assertions::assert_eq;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Constant, Data, Name, NamedDeBruijn, Program, Term},
|
ast::{Constant, Data, Name, NamedDeBruijn, Program, Term},
|
||||||
builder::{CONSTR_FIELDS_EXPOSER, CONSTR_INDEX_EXPOSER},
|
builder::{CONSTR_FIELDS_EXPOSER, CONSTR_INDEX_EXPOSER},
|
||||||
builtins::DefaultFunction,
|
builtins::DefaultFunction,
|
||||||
optimize::interner::CodeGenInterner,
|
optimize::interner::CodeGenInterner,
|
||||||
};
|
};
|
||||||
|
use pallas_primitives::conway::{BigInt, PlutusData};
|
||||||
use super::NO_INLINE;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
fn compare_optimization(
|
fn compare_optimization(
|
||||||
mut expected: Program<Name>,
|
mut expected: Program<Name>,
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Constant, Program, Term, Type},
|
ast::{Constant, Program, Term, Type},
|
||||||
flat::Binder,
|
flat::Binder,
|
||||||
machine::runtime::{convert_tag_to_constr, Compressable},
|
machine::{
|
||||||
machine::value::from_pallas_bigint,
|
runtime::{convert_tag_to_constr, Compressable},
|
||||||
|
value::from_pallas_bigint,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use pallas_primitives::babbage::{Constr, PlutusData};
|
use pallas_primitives::conway::{Constr, PlutusData};
|
||||||
use pretty::RcDoc;
|
use pretty::RcDoc;
|
||||||
use std::ascii::escape_default;
|
use std::ascii::escape_default;
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,17 @@
|
||||||
use pallas_primitives::{
|
|
||||||
babbage::{CostMdls, MintedTx, Redeemer, TransactionInput, TransactionOutput},
|
|
||||||
Fragment,
|
|
||||||
};
|
|
||||||
use pallas_traverse::{Era, MultiEraTx};
|
|
||||||
|
|
||||||
use error::Error;
|
|
||||||
pub use phase_one::eval_phase_one;
|
|
||||||
pub use script_context::{ResolvedInput, SlotConfig};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{DeBruijn, Program},
|
ast::{DeBruijn, Program},
|
||||||
machine::cost_model::ExBudget,
|
machine::cost_model::ExBudget,
|
||||||
PlutusData,
|
PlutusData,
|
||||||
};
|
};
|
||||||
|
use error::Error;
|
||||||
use eval::get_script_and_datum_lookup_table;
|
use eval::get_script_and_datum_lookup_table;
|
||||||
|
use pallas_primitives::{
|
||||||
|
conway::{CostMdls, MintedTx, Redeemer, TransactionInput, TransactionOutput},
|
||||||
|
Fragment,
|
||||||
|
};
|
||||||
|
use pallas_traverse::{Era, MultiEraTx};
|
||||||
|
pub use phase_one::eval_phase_one;
|
||||||
|
pub use script_context::{ResolvedInput, SlotConfig};
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod eval;
|
pub mod eval;
|
||||||
|
@ -53,14 +50,21 @@ pub fn eval_phase_two(
|
||||||
|
|
||||||
let mut remaining_budget = *initial_budget.unwrap_or(&ExBudget::default());
|
let mut remaining_budget = *initial_budget.unwrap_or(&ExBudget::default());
|
||||||
|
|
||||||
for redeemer in rs.iter() {
|
for (redeemer_key, redeemer_value) in rs.iter() {
|
||||||
with_redeemer(redeemer);
|
let redeemer = Redeemer {
|
||||||
|
tag: redeemer_key.tag,
|
||||||
|
index: redeemer_key.index,
|
||||||
|
data: redeemer_value.data.clone(),
|
||||||
|
ex_units: redeemer_value.ex_units,
|
||||||
|
};
|
||||||
|
|
||||||
|
with_redeemer(&redeemer);
|
||||||
|
|
||||||
let redeemer = eval::eval_redeemer(
|
let redeemer = eval::eval_redeemer(
|
||||||
tx,
|
tx,
|
||||||
utxos,
|
utxos,
|
||||||
slot_config,
|
slot_config,
|
||||||
redeemer,
|
&redeemer,
|
||||||
&lookup_table,
|
&lookup_table,
|
||||||
cost_mdls,
|
cost_mdls,
|
||||||
&remaining_budget,
|
&remaining_budget,
|
||||||
|
@ -119,7 +123,10 @@ pub fn eval_phase_two_raw(
|
||||||
};
|
};
|
||||||
|
|
||||||
match multi_era_tx {
|
match multi_era_tx {
|
||||||
MultiEraTx::Babbage(tx) => {
|
MultiEraTx::Babbage(_) => {
|
||||||
|
todo!("convert Babbage's tx into Conway's")
|
||||||
|
}
|
||||||
|
MultiEraTx::Conway(tx) => {
|
||||||
match eval_phase_two(
|
match eval_phase_two(
|
||||||
&tx,
|
&tx,
|
||||||
&utxos,
|
&utxos,
|
||||||
|
@ -136,15 +143,7 @@ pub fn eval_phase_two_raw(
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// MultiEraTx::AlonzoCompatible(tx, _) => match eval_tx(&tx, &utxos, &sc) {
|
_ => todo!("Wrong era. Please use a more recent transaction format"),
|
||||||
// Ok(redeemers) => Ok(redeemers
|
|
||||||
// .iter()
|
|
||||||
// .map(|r| r.encode_fragment().unwrap())
|
|
||||||
// .collect()),
|
|
||||||
// Err(_) => Err(()),
|
|
||||||
// },
|
|
||||||
// TODO: I probably did a mistake here with using MintedTx which is only compatible with Babbage tx.
|
|
||||||
_ => todo!("Wrong era. Please use babbage"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::{
|
||||||
machine::{self, cost_model::ExBudget},
|
machine::{self, cost_model::ExBudget},
|
||||||
TransactionInput,
|
TransactionInput,
|
||||||
};
|
};
|
||||||
|
use pallas_primitives::conway::Language;
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug, miette::Diagnostic)]
|
#[derive(thiserror::Error, Debug, miette::Diagnostic)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -30,10 +31,8 @@ pub enum Error {
|
||||||
ResolvedInputNotFound(TransactionInput),
|
ResolvedInputNotFound(TransactionInput),
|
||||||
#[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("Cost model not found for language: {:?}.", .0)]
|
||||||
V1CostModelNotFound,
|
CostModelNotFound(Language),
|
||||||
#[error("PlutusV2 cost model not found.")]
|
|
||||||
V2CostModelNotFound,
|
|
||||||
#[error("Wrong era, Please use Babbage or Alonzo: {0}")]
|
#[error("Wrong era, Please use Babbage or Alonzo: {0}")]
|
||||||
WrongEra(#[from] pallas_codec::minicbor::decode::Error),
|
WrongEra(#[from] pallas_codec::minicbor::decode::Error),
|
||||||
#[error("Byron address not allowed in Plutus.")]
|
#[error("Byron address not allowed in Plutus.")]
|
||||||
|
|
|
@ -1,46 +1,24 @@
|
||||||
use super::{
|
use super::{
|
||||||
script_context::{
|
script_context::{ResolvedInput, ScriptContext, ScriptPurpose, SlotConfig, TxInfo},
|
||||||
ResolvedInput, ScriptContext, ScriptPurpose, SlotConfig, TimeRange, TxInInfo, TxInfo,
|
to_plutus_data::ToPlutusData,
|
||||||
TxInfoV1, TxInfoV2, TxOut,
|
|
||||||
},
|
|
||||||
to_plutus_data::{MintValue, 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},
|
||||||
PlutusData,
|
PlutusData,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
|
||||||
use pallas_addresses::{Address, ScriptHash, StakePayload};
|
use pallas_addresses::{Address, ScriptHash, StakePayload};
|
||||||
use pallas_codec::utils::{KeyValuePairs, MaybeIndefArray};
|
use pallas_codec::utils::{Bytes, NonEmptyKeyValuePairs, NonEmptySet};
|
||||||
use pallas_crypto::hash::Hash;
|
use pallas_crypto::hash::Hash;
|
||||||
use pallas_primitives::{
|
use pallas_primitives::conway::{
|
||||||
babbage::{
|
Certificate, CostMdls, CostModel, DatumHash, DatumOption, ExUnits, Language, Mint, MintedTx,
|
||||||
Certificate, CostMdls, DatumHash, DatumOption, ExUnits, Mint, MintedTx, NativeScript,
|
NativeScript, PlutusV1Script, PlutusV2Script, PlutusV3Script, PolicyId, PseudoScript, Redeemer,
|
||||||
PlutusV1Script, PlutusV2Script, PolicyId, PseudoScript, Redeemer, RedeemerTag,
|
RedeemerTag, RewardAccount, StakeCredential, TransactionInput, TransactionOutput, Withdrawals,
|
||||||
RewardAccount, StakeCredential, TransactionInput, TransactionOutput, Value, Withdrawals,
|
|
||||||
},
|
|
||||||
conway::Language,
|
|
||||||
};
|
};
|
||||||
use pallas_traverse::{ComputeHash, OriginalHash};
|
use pallas_traverse::{ComputeHash, OriginalHash};
|
||||||
use std::{cmp::Ordering, collections::HashMap, convert::TryInto, ops::Deref, vec};
|
use std::{collections::HashMap, convert::TryInto, vec};
|
||||||
|
|
||||||
pub fn slot_to_begin_posix_time(slot: u64, sc: &SlotConfig) -> u64 {
|
|
||||||
let ms_after_begin = (slot - sc.zero_slot) * sc.slot_length as u64;
|
|
||||||
sc.zero_time + ms_after_begin
|
|
||||||
}
|
|
||||||
|
|
||||||
fn slot_range_to_posix_time_range(slot_range: TimeRange, sc: &SlotConfig) -> TimeRange {
|
|
||||||
TimeRange {
|
|
||||||
lower_bound: slot_range
|
|
||||||
.lower_bound
|
|
||||||
.map(|lower_bound| slot_to_begin_posix_time(lower_bound, sc)),
|
|
||||||
upper_bound: slot_range
|
|
||||||
.upper_bound
|
|
||||||
.map(|upper_bound| slot_to_begin_posix_time(upper_bound, sc)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn redeemer_tag_to_string(redeemer_tag: &RedeemerTag) -> String {
|
fn redeemer_tag_to_string(redeemer_tag: &RedeemerTag) -> String {
|
||||||
match redeemer_tag {
|
match redeemer_tag {
|
||||||
|
@ -48,54 +26,7 @@ fn redeemer_tag_to_string(redeemer_tag: &RedeemerTag) -> String {
|
||||||
RedeemerTag::Mint => "Mint".to_string(),
|
RedeemerTag::Mint => "Mint".to_string(),
|
||||||
RedeemerTag::Cert => "Cert".to_string(),
|
RedeemerTag::Cert => "Cert".to_string(),
|
||||||
RedeemerTag::Reward => "Reward".to_string(),
|
RedeemerTag::Reward => "Reward".to_string(),
|
||||||
}
|
tag => todo!("redeemer_tag_to_string for {tag:?}"),
|
||||||
}
|
|
||||||
|
|
||||||
fn sort_mint(mint: &Mint) -> Mint {
|
|
||||||
let mut mint_vec = vec![];
|
|
||||||
|
|
||||||
for m in mint.deref().iter().sorted() {
|
|
||||||
mint_vec.push((
|
|
||||||
m.0,
|
|
||||||
KeyValuePairs::Indef(m.1.deref().clone().into_iter().sorted().clone().collect()),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyValuePairs::Indef(mint_vec)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sort_value(value: &Value) -> Value {
|
|
||||||
match value {
|
|
||||||
Value::Coin(_) => value.clone(),
|
|
||||||
Value::Multiasset(coin, ma) => {
|
|
||||||
let mut ma_vec = vec![];
|
|
||||||
|
|
||||||
for m in ma.deref().iter().sorted() {
|
|
||||||
ma_vec.push((
|
|
||||||
m.0,
|
|
||||||
KeyValuePairs::Indef(
|
|
||||||
m.1.deref().clone().into_iter().sorted().clone().collect(),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::Multiasset(*coin, KeyValuePairs::Indef(ma_vec))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sort_tx_out_value(tx_output: &TransactionOutput) -> TransactionOutput {
|
|
||||||
match tx_output {
|
|
||||||
TransactionOutput::Legacy(output) => {
|
|
||||||
let mut new_output = output.clone();
|
|
||||||
new_output.amount = sort_value(&output.amount);
|
|
||||||
TransactionOutput::Legacy(new_output)
|
|
||||||
}
|
|
||||||
TransactionOutput::PostAlonzo(output) => {
|
|
||||||
let mut new_output = output.clone();
|
|
||||||
new_output.value = sort_value(&output.value);
|
|
||||||
TransactionOutput::PostAlonzo(new_output)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,6 +35,7 @@ pub enum ScriptVersion {
|
||||||
Native(NativeScript),
|
Native(NativeScript),
|
||||||
V1(PlutusV1Script),
|
V1(PlutusV1Script),
|
||||||
V2(PlutusV2Script),
|
V2(PlutusV2Script),
|
||||||
|
V3(PlutusV3Script),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
@ -123,107 +55,22 @@ impl DataLookupTable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tx_in_info_v1(
|
|
||||||
inputs: &[TransactionInput],
|
|
||||||
utxos: &[ResolvedInput],
|
|
||||||
) -> Result<Vec<TxInInfo>, Error> {
|
|
||||||
inputs
|
|
||||||
.iter()
|
|
||||||
.sorted()
|
|
||||||
.map(|input| {
|
|
||||||
let utxo = match utxos.iter().find(|utxo| utxo.input == *input) {
|
|
||||||
Some(resolved) => resolved,
|
|
||||||
None => return Err(Error::ResolvedInputNotFound(input.clone())),
|
|
||||||
};
|
|
||||||
let address = Address::from_bytes(match &utxo.output {
|
|
||||||
TransactionOutput::Legacy(output) => output.address.as_ref(),
|
|
||||||
TransactionOutput::PostAlonzo(output) => output.address.as_ref(),
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
match address {
|
|
||||||
Address::Byron(_) => {
|
|
||||||
return Err(Error::ByronAddressNotAllowed);
|
|
||||||
}
|
|
||||||
Address::Stake(_) => {
|
|
||||||
return Err(Error::NoPaymentCredential);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
|
|
||||||
match &utxo.output {
|
|
||||||
TransactionOutput::Legacy(_) => {}
|
|
||||||
TransactionOutput::PostAlonzo(output) => {
|
|
||||||
if let Some(DatumOption::Data(_)) = output.datum_option {
|
|
||||||
return Err(Error::InlineDatumNotAllowed);
|
|
||||||
}
|
|
||||||
|
|
||||||
if output.script_ref.is_some() {
|
|
||||||
return Err(Error::ScriptAndInputRefNotAllowed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(TxInInfo {
|
|
||||||
out_ref: utxo.input.clone(),
|
|
||||||
resolved: TxOut::V1(sort_tx_out_value(&utxo.output)),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_tx_in_info_v2(
|
|
||||||
inputs: &[TransactionInput],
|
|
||||||
utxos: &[ResolvedInput],
|
|
||||||
) -> Result<Vec<TxInInfo>, Error> {
|
|
||||||
inputs
|
|
||||||
.iter()
|
|
||||||
.sorted()
|
|
||||||
.map(|input| {
|
|
||||||
let utxo = match utxos.iter().find(|utxo| utxo.input == *input) {
|
|
||||||
Some(resolved) => resolved,
|
|
||||||
None => return Err(Error::ResolvedInputNotFound(input.clone())),
|
|
||||||
};
|
|
||||||
let address = Address::from_bytes(match &utxo.output {
|
|
||||||
TransactionOutput::Legacy(output) => output.address.as_ref(),
|
|
||||||
TransactionOutput::PostAlonzo(output) => output.address.as_ref(),
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
match address {
|
|
||||||
Address::Byron(_) => {
|
|
||||||
return Err(Error::ByronAddressNotAllowed);
|
|
||||||
}
|
|
||||||
Address::Stake(_) => {
|
|
||||||
return Err(Error::NoPaymentCredential);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(TxInInfo {
|
|
||||||
out_ref: utxo.input.clone(),
|
|
||||||
resolved: TxOut::V2(sort_tx_out_value(&utxo.output)),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_script_purpose(
|
fn get_script_purpose(
|
||||||
redeemer: &Redeemer,
|
redeemer: &Redeemer,
|
||||||
inputs: &[TransactionInput],
|
inputs: &[TransactionInput],
|
||||||
mint: &Option<Mint>,
|
mint: &Option<Mint>,
|
||||||
dcert: &Option<Vec<Certificate>>,
|
dcert: &Option<NonEmptySet<Certificate>>,
|
||||||
wdrl: &Option<Withdrawals>,
|
wdrl: &Option<Withdrawals>,
|
||||||
) -> Result<ScriptPurpose, Error> {
|
) -> Result<ScriptPurpose, Error> {
|
||||||
// sorting according to specs section 4.1: https://hydra.iohk.io/build/18583827/download/1/alonzo-changes.pdf
|
// sorting according to specs section 4.1: https://hydra.iohk.io/build/18583827/download/1/alonzo-changes.pdf
|
||||||
let tag = redeemer.tag.clone();
|
let tag = redeemer.tag;
|
||||||
let index = redeemer.index;
|
let index = redeemer.index;
|
||||||
match tag {
|
match tag {
|
||||||
RedeemerTag::Mint => {
|
RedeemerTag::Mint => {
|
||||||
// sort lexical by policy id
|
// sort lexical by policy id
|
||||||
let mut policy_ids = mint
|
let mut policy_ids = mint
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap_or(&KeyValuePairs::Indef(vec![]))
|
.unwrap_or(&NonEmptyKeyValuePairs::Indef(vec![]))
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(policy_id, _)| *policy_id)
|
.map(|(policy_id, _)| *policy_id)
|
||||||
.collect::<Vec<PolicyId>>();
|
.collect::<Vec<PolicyId>>();
|
||||||
|
@ -246,7 +93,7 @@ fn get_script_purpose(
|
||||||
// sort lexical by reward account
|
// sort lexical by reward account
|
||||||
let mut reward_accounts = wdrl
|
let mut reward_accounts = wdrl
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap_or(&KeyValuePairs::Indef(vec![]))
|
.unwrap_or(&NonEmptyKeyValuePairs::Indef(vec![]))
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(racnt, _)| racnt.clone())
|
.map(|(racnt, _)| racnt.clone())
|
||||||
.collect::<Vec<RewardAccount>>();
|
.collect::<Vec<RewardAccount>>();
|
||||||
|
@ -269,215 +116,15 @@ fn get_script_purpose(
|
||||||
}
|
}
|
||||||
RedeemerTag::Cert => {
|
RedeemerTag::Cert => {
|
||||||
// sort by order given in the tx (just take it as it is basically)
|
// sort by order given in the tx (just take it as it is basically)
|
||||||
match dcert
|
match dcert.as_deref().unwrap_or(&vec![]).get(index as usize) {
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&MaybeIndefArray::Indef(vec![]))
|
|
||||||
.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 => todo!("get_script_purpose for {tag:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_tx_info_v1(
|
|
||||||
tx: &MintedTx,
|
|
||||||
utxos: &[ResolvedInput],
|
|
||||||
slot_config: &SlotConfig,
|
|
||||||
) -> Result<TxInfo, Error> {
|
|
||||||
let body = tx.transaction_body.clone();
|
|
||||||
|
|
||||||
if body.reference_inputs.is_some() {
|
|
||||||
return Err(Error::ScriptAndInputRefNotAllowed);
|
|
||||||
}
|
|
||||||
|
|
||||||
let inputs = get_tx_in_info_v1(&body.inputs, utxos)?;
|
|
||||||
|
|
||||||
let outputs = body
|
|
||||||
.outputs
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(|output| TxOut::V1(sort_tx_out_value(&output.into())))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let fee = Value::Coin(body.fee);
|
|
||||||
|
|
||||||
let mint = sort_mint(&body.mint.clone().unwrap_or(KeyValuePairs::Indef(vec![])));
|
|
||||||
|
|
||||||
let dcert = body.certificates.clone().unwrap_or_default();
|
|
||||||
|
|
||||||
let wdrl = body
|
|
||||||
.withdrawals
|
|
||||||
.clone()
|
|
||||||
.unwrap_or(KeyValuePairs::Indef(vec![]))
|
|
||||||
.deref()
|
|
||||||
.clone()
|
|
||||||
.into_iter()
|
|
||||||
.sorted()
|
|
||||||
.map(|(reward_account, coin)| (Address::from_bytes(&reward_account).unwrap(), coin))
|
|
||||||
.collect_vec();
|
|
||||||
|
|
||||||
let valid_range = slot_range_to_posix_time_range(
|
|
||||||
TimeRange {
|
|
||||||
lower_bound: body.validity_interval_start,
|
|
||||||
upper_bound: body.ttl,
|
|
||||||
},
|
|
||||||
slot_config,
|
|
||||||
);
|
|
||||||
|
|
||||||
let signatories = body
|
|
||||||
.required_signers
|
|
||||||
.clone()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.into_iter()
|
|
||||||
.sorted()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let data = tx
|
|
||||||
.transaction_witness_set
|
|
||||||
.plutus_data
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&vec![])
|
|
||||||
.iter()
|
|
||||||
.map(|d| (d.original_hash(), d.clone().unwrap()))
|
|
||||||
.sorted()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let id = tx.transaction_body.original_hash();
|
|
||||||
|
|
||||||
Ok(TxInfo::V1(TxInfoV1 {
|
|
||||||
inputs,
|
|
||||||
outputs,
|
|
||||||
fee,
|
|
||||||
mint: MintValue { mint_value: mint },
|
|
||||||
dcert,
|
|
||||||
wdrl,
|
|
||||||
valid_range,
|
|
||||||
signatories,
|
|
||||||
data,
|
|
||||||
id,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_tx_info_v2(
|
|
||||||
tx: &MintedTx,
|
|
||||||
utxos: &[ResolvedInput],
|
|
||||||
slot_config: &SlotConfig,
|
|
||||||
) -> Result<TxInfo, Error> {
|
|
||||||
let body = tx.transaction_body.clone();
|
|
||||||
|
|
||||||
let inputs = get_tx_in_info_v2(&body.inputs, utxos)?;
|
|
||||||
|
|
||||||
let reference_inputs =
|
|
||||||
get_tx_in_info_v2(&body.reference_inputs.clone().unwrap_or_default(), utxos)?;
|
|
||||||
|
|
||||||
let outputs = body
|
|
||||||
.outputs
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(|output| TxOut::V2(sort_tx_out_value(&output.into())))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let fee = Value::Coin(body.fee);
|
|
||||||
|
|
||||||
let mint = sort_mint(&body.mint.clone().unwrap_or(KeyValuePairs::Indef(vec![])));
|
|
||||||
|
|
||||||
let dcert = body.certificates.clone().unwrap_or_default();
|
|
||||||
|
|
||||||
let wdrl = KeyValuePairs::Indef(
|
|
||||||
body.withdrawals
|
|
||||||
.clone()
|
|
||||||
.unwrap_or(KeyValuePairs::Indef(vec![]))
|
|
||||||
.deref()
|
|
||||||
.clone()
|
|
||||||
.into_iter()
|
|
||||||
.sorted()
|
|
||||||
.map(|(reward_account, coin)| (Address::from_bytes(&reward_account).unwrap(), coin))
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let valid_range = slot_range_to_posix_time_range(
|
|
||||||
TimeRange {
|
|
||||||
lower_bound: body.validity_interval_start,
|
|
||||||
upper_bound: body.ttl,
|
|
||||||
},
|
|
||||||
slot_config,
|
|
||||||
);
|
|
||||||
|
|
||||||
let signatories = body
|
|
||||||
.required_signers
|
|
||||||
.clone()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.into_iter()
|
|
||||||
.sorted()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let redeemers = KeyValuePairs::Indef(
|
|
||||||
tx.transaction_witness_set
|
|
||||||
.redeemer
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&MaybeIndefArray::Indef(vec![]))
|
|
||||||
.iter()
|
|
||||||
.sorted_by(|a, b| -> Ordering {
|
|
||||||
if a.tag == b.tag {
|
|
||||||
a.index.cmp(&b.index)
|
|
||||||
} else {
|
|
||||||
match (&a.tag, &b.tag) {
|
|
||||||
(RedeemerTag::Spend, _) => Ordering::Greater,
|
|
||||||
(RedeemerTag::Mint, RedeemerTag::Spend) => Ordering::Less,
|
|
||||||
(RedeemerTag::Mint, _) => Ordering::Greater,
|
|
||||||
(RedeemerTag::Cert, RedeemerTag::Reward) => Ordering::Greater,
|
|
||||||
(RedeemerTag::Cert, _) => Ordering::Less,
|
|
||||||
(RedeemerTag::Reward, _) => Ordering::Less,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map(|r| {
|
|
||||||
(
|
|
||||||
get_script_purpose(
|
|
||||||
r,
|
|
||||||
&tx.transaction_body.inputs,
|
|
||||||
&tx.transaction_body.mint,
|
|
||||||
&tx.transaction_body.certificates,
|
|
||||||
&tx.transaction_body.withdrawals,
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
r.clone(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let data = KeyValuePairs::Indef(
|
|
||||||
tx.transaction_witness_set
|
|
||||||
.plutus_data
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&vec![])
|
|
||||||
.iter()
|
|
||||||
.map(|d| (d.original_hash(), d.clone().unwrap()))
|
|
||||||
.sorted()
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let id = tx.transaction_body.original_hash();
|
|
||||||
|
|
||||||
Ok(TxInfo::V2(TxInfoV2 {
|
|
||||||
inputs,
|
|
||||||
reference_inputs,
|
|
||||||
outputs,
|
|
||||||
fee,
|
|
||||||
mint: MintValue { mint_value: mint },
|
|
||||||
dcert,
|
|
||||||
wdrl,
|
|
||||||
valid_range,
|
|
||||||
signatories,
|
|
||||||
redeemers,
|
|
||||||
data,
|
|
||||||
id,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_execution_purpose(
|
fn get_execution_purpose(
|
||||||
utxos: &[ResolvedInput],
|
utxos: &[ResolvedInput],
|
||||||
script_purpose: &ScriptPurpose,
|
script_purpose: &ScriptPurpose,
|
||||||
|
@ -643,24 +290,35 @@ pub fn get_script_and_datum_lookup_table(
|
||||||
.transaction_witness_set
|
.transaction_witness_set
|
||||||
.plutus_data
|
.plutus_data
|
||||||
.clone()
|
.clone()
|
||||||
|
.map(|s| s.to_vec())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let scripts_native_witnesses = tx
|
let scripts_native_witnesses = tx
|
||||||
.transaction_witness_set
|
.transaction_witness_set
|
||||||
.native_script
|
.native_script
|
||||||
.clone()
|
.clone()
|
||||||
|
.map(|s| s.to_vec())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let scripts_v1_witnesses = tx
|
let scripts_v1_witnesses = tx
|
||||||
.transaction_witness_set
|
.transaction_witness_set
|
||||||
.plutus_v1_script
|
.plutus_v1_script
|
||||||
.clone()
|
.clone()
|
||||||
|
.map(|s| s.to_vec())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let scripts_v2_witnesses = tx
|
let scripts_v2_witnesses = tx
|
||||||
.transaction_witness_set
|
.transaction_witness_set
|
||||||
.plutus_v2_script
|
.plutus_v2_script
|
||||||
.clone()
|
.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();
|
.unwrap_or_default();
|
||||||
|
|
||||||
for plutus_data in plutus_data_witnesses.iter() {
|
for plutus_data in plutus_data_witnesses.iter() {
|
||||||
|
@ -682,6 +340,10 @@ pub fn get_script_and_datum_lookup_table(
|
||||||
scripts.insert(script.compute_hash(), ScriptVersion::V2(script.clone()));
|
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)
|
// discovery in utxos (script ref)
|
||||||
|
|
||||||
for utxo in utxos.iter() {
|
for utxo in utxos.iter() {
|
||||||
|
@ -699,6 +361,9 @@ pub fn get_script_and_datum_lookup_table(
|
||||||
PseudoScript::PlutusV2Script(v2) => {
|
PseudoScript::PlutusV2Script(v2) => {
|
||||||
scripts.insert(v2.compute_hash(), ScriptVersion::V2(v2.clone()));
|
scripts.insert(v2.compute_hash(), ScriptVersion::V2(v2.clone()));
|
||||||
}
|
}
|
||||||
|
PseudoScript::PlutusV3Script(v3) => {
|
||||||
|
scripts.insert(v3.compute_hash(), ScriptVersion::V3(v3.clone()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -708,6 +373,49 @@ pub fn get_script_and_datum_lookup_table(
|
||||||
DataLookupTable { datum, scripts }
|
DataLookupTable { datum, scripts }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mk_redeemer_with_datum(
|
||||||
|
cost_mdl_opt: Option<&CostModel>,
|
||||||
|
initial_budget: &ExBudget,
|
||||||
|
lang: &Language,
|
||||||
|
datum: PlutusData,
|
||||||
|
(redeemer, purpose): (&Redeemer, ScriptPurpose),
|
||||||
|
tx_info: TxInfo,
|
||||||
|
program: Program<NamedDeBruijn>,
|
||||||
|
) -> Result<Redeemer, Error> {
|
||||||
|
let script_context = ScriptContext { tx_info, purpose };
|
||||||
|
|
||||||
|
let program = program
|
||||||
|
.apply_data(datum)
|
||||||
|
.apply_data(redeemer.data.clone())
|
||||||
|
.apply_data(script_context.to_plutus_data());
|
||||||
|
|
||||||
|
let mut eval_result = if let Some(costs) = cost_mdl_opt {
|
||||||
|
program.eval_as(lang, costs, Some(initial_budget))
|
||||||
|
} else {
|
||||||
|
program.eval_version(ExBudget::default(), lang)
|
||||||
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn eval_redeemer(
|
pub fn eval_redeemer(
|
||||||
tx: &MintedTx,
|
tx: &MintedTx,
|
||||||
utxos: &[ResolvedInput],
|
utxos: &[ResolvedInput],
|
||||||
|
@ -726,114 +434,73 @@ pub fn eval_redeemer(
|
||||||
&tx.transaction_body.withdrawals,
|
&tx.transaction_body.withdrawals,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let program = |script: Bytes| {
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
Program::<FakeNamedDeBruijn>::from_cbor(&script, &mut buffer)
|
||||||
|
.map(Into::<Program<NamedDeBruijn>>::into)
|
||||||
|
};
|
||||||
|
|
||||||
let execution_purpose: ExecutionPurpose =
|
let execution_purpose: ExecutionPurpose =
|
||||||
get_execution_purpose(utxos, &purpose, lookup_table)?;
|
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) => mk_redeemer_with_datum(
|
||||||
let tx_info = get_tx_info_v1(tx, utxos, slot_config)?;
|
cost_mdls_opt
|
||||||
let script_context = ScriptContext { tx_info, purpose };
|
.map(|cost_mdls| {
|
||||||
|
cost_mdls
|
||||||
|
.plutus_v1
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(Error::CostModelNotFound(Language::PlutusV1))
|
||||||
|
})
|
||||||
|
.transpose()?,
|
||||||
|
initial_budget,
|
||||||
|
&Language::PlutusV1,
|
||||||
|
datum,
|
||||||
|
(redeemer, purpose),
|
||||||
|
TxInfoV1::from_transaction(tx, utxos, slot_config)?,
|
||||||
|
program(script.0)?,
|
||||||
|
),
|
||||||
|
|
||||||
let program: Program<NamedDeBruijn> = {
|
ScriptVersion::V2(script) => mk_redeemer_with_datum(
|
||||||
let mut buffer = Vec::new();
|
cost_mdls_opt
|
||||||
|
.map(|cost_mdls| {
|
||||||
|
cost_mdls
|
||||||
|
.plutus_v2
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(Error::CostModelNotFound(Language::PlutusV2))
|
||||||
|
})
|
||||||
|
.transpose()?,
|
||||||
|
initial_budget,
|
||||||
|
&Language::PlutusV2,
|
||||||
|
datum,
|
||||||
|
(redeemer, purpose),
|
||||||
|
TxInfoV2::from_transaction(tx, utxos, slot_config)?,
|
||||||
|
program(script.0)?,
|
||||||
|
),
|
||||||
|
|
||||||
let prog = Program::<FakeNamedDeBruijn>::from_cbor(&script.0, &mut buffer)?;
|
ScriptVersion::V3(script) => mk_redeemer_with_datum(
|
||||||
|
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)?,
|
||||||
|
),
|
||||||
|
|
||||||
prog.into()
|
|
||||||
};
|
|
||||||
|
|
||||||
let program = program
|
|
||||||
.apply_data(datum)
|
|
||||||
.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::V1CostModelNotFound);
|
|
||||||
};
|
|
||||||
|
|
||||||
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)),
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_redeemer = Redeemer {
|
|
||||||
tag: redeemer.tag.clone(),
|
|
||||||
index: redeemer.index,
|
|
||||||
data: redeemer.data.clone(),
|
|
||||||
ex_units: ExUnits {
|
|
||||||
mem: cost.mem as u32,
|
|
||||||
steps: cost.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 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::V2CostModelNotFound);
|
|
||||||
};
|
|
||||||
|
|
||||||
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.clone(),
|
|
||||||
index: redeemer.index,
|
|
||||||
data: redeemer.data.clone(),
|
|
||||||
ex_units: ExUnits {
|
|
||||||
mem: cost.mem as u32,
|
|
||||||
steps: cost.cpu as u64,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(new_redeemer)
|
|
||||||
}
|
|
||||||
ScriptVersion::Native(_) => Err(Error::NativeScriptPhaseTwo),
|
ScriptVersion::Native(_) => Err(Error::NativeScriptPhaseTwo),
|
||||||
},
|
},
|
||||||
ExecutionPurpose::NoDatum(script_version) => match script_version {
|
ExecutionPurpose::NoDatum(script_version) => match script_version {
|
||||||
ScriptVersion::V1(script) => {
|
ScriptVersion::V1(script) => {
|
||||||
let tx_info = get_tx_info_v1(tx, utxos, slot_config)?;
|
let tx_info = TxInfoV1::from_transaction(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> = {
|
||||||
|
@ -852,7 +519,7 @@ pub fn eval_redeemer(
|
||||||
let costs = if let Some(costs) = &cost_mdls.plutus_v1 {
|
let costs = if let Some(costs) = &cost_mdls.plutus_v1 {
|
||||||
costs
|
costs
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::V1CostModelNotFound);
|
return Err(Error::CostModelNotFound(Language::PlutusV1));
|
||||||
};
|
};
|
||||||
|
|
||||||
program.eval_as(&Language::PlutusV1, costs, Some(initial_budget))
|
program.eval_as(&Language::PlutusV1, costs, Some(initial_budget))
|
||||||
|
@ -869,11 +536,11 @@ pub fn eval_redeemer(
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_redeemer = Redeemer {
|
let new_redeemer = Redeemer {
|
||||||
tag: redeemer.tag.clone(),
|
tag: redeemer.tag,
|
||||||
index: redeemer.index,
|
index: redeemer.index,
|
||||||
data: redeemer.data.clone(),
|
data: redeemer.data.clone(),
|
||||||
ex_units: ExUnits {
|
ex_units: ExUnits {
|
||||||
mem: cost.mem as u32,
|
mem: cost.mem as u64,
|
||||||
steps: cost.cpu as u64,
|
steps: cost.cpu as u64,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -881,7 +548,7 @@ pub fn eval_redeemer(
|
||||||
Ok(new_redeemer)
|
Ok(new_redeemer)
|
||||||
}
|
}
|
||||||
ScriptVersion::V2(script) => {
|
ScriptVersion::V2(script) => {
|
||||||
let tx_info = get_tx_info_v2(tx, utxos, slot_config)?;
|
let tx_info = TxInfoV2::from_transaction(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> = {
|
||||||
|
@ -900,7 +567,7 @@ pub fn eval_redeemer(
|
||||||
let costs = if let Some(costs) = &cost_mdls.plutus_v2 {
|
let costs = if let Some(costs) = &cost_mdls.plutus_v2 {
|
||||||
costs
|
costs
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::V2CostModelNotFound);
|
return Err(Error::CostModelNotFound(Language::PlutusV2));
|
||||||
};
|
};
|
||||||
|
|
||||||
program.eval_as(&Language::PlutusV2, costs, Some(initial_budget))
|
program.eval_as(&Language::PlutusV2, costs, Some(initial_budget))
|
||||||
|
@ -917,17 +584,18 @@ pub fn eval_redeemer(
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_redeemer = Redeemer {
|
let new_redeemer = Redeemer {
|
||||||
tag: redeemer.tag.clone(),
|
tag: redeemer.tag,
|
||||||
index: redeemer.index,
|
index: redeemer.index,
|
||||||
data: redeemer.data.clone(),
|
data: redeemer.data.clone(),
|
||||||
ex_units: ExUnits {
|
ex_units: ExUnits {
|
||||||
mem: cost.mem as u32,
|
mem: cost.mem as u64,
|
||||||
steps: cost.cpu as u64,
|
steps: cost.cpu as u64,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(new_redeemer)
|
Ok(new_redeemer)
|
||||||
}
|
}
|
||||||
|
ScriptVersion::V3(_script) => todo!(),
|
||||||
ScriptVersion::Native(_) => Err(Error::NativeScriptPhaseTwo),
|
ScriptVersion::Native(_) => Err(Error::NativeScriptPhaseTwo),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,20 +3,14 @@ use super::{
|
||||||
eval::{DataLookupTable, ScriptVersion},
|
eval::{DataLookupTable, ScriptVersion},
|
||||||
script_context::{ResolvedInput, ScriptPurpose},
|
script_context::{ResolvedInput, ScriptPurpose},
|
||||||
};
|
};
|
||||||
|
use itertools::Itertools;
|
||||||
use pallas_addresses::{Address, ScriptHash, ShelleyPaymentPart, StakePayload};
|
use pallas_addresses::{Address, ScriptHash, ShelleyPaymentPart, StakePayload};
|
||||||
use pallas_codec::utils::{KeyValuePairs, MaybeIndefArray};
|
use pallas_primitives::conway::{
|
||||||
use pallas_primitives::babbage::{
|
Certificate, MintedTx, PolicyId, RedeemerTag, RedeemersKey, RewardAccount, StakeCredential,
|
||||||
Certificate, MintedTx, PolicyId, RedeemerTag, RewardAccount, StakeCredential, TransactionOutput,
|
TransactionOutput,
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
// TODO: include in pallas eventually?
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
struct RedeemerPtr {
|
|
||||||
tag: RedeemerTag,
|
|
||||||
index: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
type AlonzoScriptsNeeded = Vec<(ScriptPurpose, ScriptHash)>;
|
type AlonzoScriptsNeeded = Vec<(ScriptPurpose, ScriptHash)>;
|
||||||
|
|
||||||
// subset of phase-1 ledger checks related to scripts
|
// subset of phase-1 ledger checks related to scripts
|
||||||
|
@ -98,49 +92,55 @@ pub fn scripts_needed(
|
||||||
|
|
||||||
let mut reward = txb
|
let mut reward = txb
|
||||||
.withdrawals
|
.withdrawals
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.unwrap_or(&KeyValuePairs::Indef(vec![]))
|
.map(|w| {
|
||||||
.iter()
|
w.iter()
|
||||||
.filter_map(|(acnt, _)| {
|
.filter_map(|(acnt, _)| {
|
||||||
let address = Address::from_bytes(acnt).unwrap();
|
let address = Address::from_bytes(acnt).unwrap();
|
||||||
|
|
||||||
if let Address::Stake(a) = address {
|
if let Address::Stake(a) = address {
|
||||||
if let StakePayload::Script(h) = a.payload() {
|
if let StakePayload::Script(h) = a.payload() {
|
||||||
let cred = StakeCredential::Scripthash(*h);
|
let cred = StakeCredential::Scripthash(*h);
|
||||||
return Some((ScriptPurpose::Rewarding(cred), *h));
|
return Some((ScriptPurpose::Rewarding(cred), *h));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
|
})
|
||||||
|
.collect::<AlonzoScriptsNeeded>()
|
||||||
})
|
})
|
||||||
.collect::<AlonzoScriptsNeeded>();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let mut cert = txb
|
let mut cert = txb
|
||||||
.certificates
|
.certificates
|
||||||
.clone()
|
.as_deref()
|
||||||
.unwrap_or_default()
|
.map(|m| {
|
||||||
.iter()
|
m.iter()
|
||||||
.filter_map(|cert| {
|
.filter_map(|cert| {
|
||||||
// only Dereg and Deleg certs can require scripts
|
// only Dereg and Deleg certs can require scripts
|
||||||
match cert {
|
match cert {
|
||||||
Certificate::StakeDeregistration(StakeCredential::Scripthash(h)) => {
|
Certificate::StakeDeregistration(StakeCredential::Scripthash(h)) => {
|
||||||
Some((ScriptPurpose::Certifying(cert.clone()), *h))
|
Some((ScriptPurpose::Certifying(cert.clone()), *h))
|
||||||
}
|
}
|
||||||
Certificate::StakeDelegation(StakeCredential::Scripthash(h), _) => {
|
Certificate::StakeDelegation(StakeCredential::Scripthash(h), _) => {
|
||||||
Some((ScriptPurpose::Certifying(cert.clone()), *h))
|
Some((ScriptPurpose::Certifying(cert.clone()), *h))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.collect::<AlonzoScriptsNeeded>()
|
||||||
})
|
})
|
||||||
.collect::<AlonzoScriptsNeeded>();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let mut mint = txb
|
let mut mint = txb
|
||||||
.mint
|
.mint
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.unwrap_or(&KeyValuePairs::Indef(vec![]))
|
.map(|m| {
|
||||||
.iter()
|
m.iter()
|
||||||
.map(|(policy_id, _)| (ScriptPurpose::Minting(*policy_id), *policy_id))
|
.map(|(policy_id, _)| (ScriptPurpose::Minting(*policy_id), *policy_id))
|
||||||
.collect::<AlonzoScriptsNeeded>();
|
.collect::<AlonzoScriptsNeeded>()
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
needed.append(&mut spend);
|
needed.append(&mut spend);
|
||||||
needed.append(&mut reward);
|
needed.append(&mut reward);
|
||||||
|
@ -159,52 +159,50 @@ pub fn has_exact_set_of_redeemers(
|
||||||
let mut redeemers_needed = Vec::new();
|
let mut redeemers_needed = Vec::new();
|
||||||
|
|
||||||
for (script_purpose, script_hash) in needed {
|
for (script_purpose, script_hash) in needed {
|
||||||
let redeemer_ptr = build_redeemer_ptr(tx, script_purpose)?;
|
let redeemer_key = build_redeemer_key(tx, script_purpose)?;
|
||||||
let script = tx_scripts.get(script_hash);
|
let script = tx_scripts.get(script_hash);
|
||||||
|
|
||||||
if let (Some(ptr), Some(script)) = (redeemer_ptr, script) {
|
if let (Some(key), Some(script)) = (redeemer_key, script) {
|
||||||
match script {
|
match script {
|
||||||
ScriptVersion::V1(_) => {
|
ScriptVersion::V1(_) => {
|
||||||
redeemers_needed.push((ptr, script_purpose.clone(), *script_hash))
|
redeemers_needed.push((key, script_purpose.clone(), *script_hash))
|
||||||
}
|
}
|
||||||
ScriptVersion::V2(_) => {
|
ScriptVersion::V2(_) => {
|
||||||
redeemers_needed.push((ptr, script_purpose.clone(), *script_hash))
|
redeemers_needed.push((key, script_purpose.clone(), *script_hash))
|
||||||
|
}
|
||||||
|
ScriptVersion::V3(_) => {
|
||||||
|
redeemers_needed.push((key, script_purpose.clone(), *script_hash))
|
||||||
}
|
}
|
||||||
ScriptVersion::Native(_) => (),
|
ScriptVersion::Native(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let wits_redeemer_ptrs: Vec<RedeemerPtr> = tx
|
let wits_redeemer_keys: Vec<&RedeemersKey> = tx
|
||||||
.transaction_witness_set
|
.transaction_witness_set
|
||||||
.redeemer
|
.redeemer
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.unwrap_or(&MaybeIndefArray::Indef(vec![]))
|
.map(|m| m.iter().map(|(k, _)| k).collect())
|
||||||
.iter()
|
.unwrap_or_default();
|
||||||
.map(|r| RedeemerPtr {
|
|
||||||
tag: r.tag.clone(),
|
|
||||||
index: r.index,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let needed_redeemer_ptrs: Vec<RedeemerPtr> =
|
let needed_redeemer_keys: Vec<RedeemersKey> =
|
||||||
redeemers_needed.iter().map(|x| x.0.clone()).collect();
|
redeemers_needed.iter().map(|x| x.0.clone()).collect();
|
||||||
|
|
||||||
let missing: Vec<_> = redeemers_needed
|
let missing: Vec<_> = redeemers_needed
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|x| !wits_redeemer_ptrs.contains(&x.0))
|
.filter(|x| !wits_redeemer_keys.contains(&&x.0))
|
||||||
.map(|x| {
|
.map(|x| {
|
||||||
format!(
|
format!(
|
||||||
"[Missing (redeemer_ptr: {:?}, script_purpose: {:?}, script_hash: {})]",
|
"[Missing (redeemer_key: {:?}, script_purpose: {:?}, script_hash: {})]",
|
||||||
x.0, x.1, x.2,
|
x.0, x.1, x.2,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let extra: Vec<_> = wits_redeemer_ptrs
|
let extra: Vec<_> = wits_redeemer_keys
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|x| !needed_redeemer_ptrs.contains(x))
|
.filter(|x| !needed_redeemer_keys.contains(x))
|
||||||
.map(|x| format!("[Extraneous (redeemer_ptr: {x:?})]"))
|
.map(|x| format!("[Extraneous (redeemer_key: {x:?})]"))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if !missing.is_empty() || !extra.is_empty() {
|
if !missing.is_empty() || !extra.is_empty() {
|
||||||
|
@ -217,66 +215,62 @@ pub fn has_exact_set_of_redeemers(
|
||||||
/// builds a redeemer pointer (tag, index) from a script purpose by setting the tag
|
/// builds a redeemer pointer (tag, index) from a script purpose by setting the tag
|
||||||
/// according to the type of the script purpose, and the index according to the
|
/// according to the type of the script purpose, and the index according to the
|
||||||
/// placement of script purpose inside its container.
|
/// placement of script purpose inside its container.
|
||||||
fn build_redeemer_ptr(
|
fn build_redeemer_key(
|
||||||
tx: &MintedTx,
|
tx: &MintedTx,
|
||||||
script_purpose: &ScriptPurpose,
|
script_purpose: &ScriptPurpose,
|
||||||
) -> Result<Option<RedeemerPtr>, Error> {
|
) -> Result<Option<RedeemersKey>, Error> {
|
||||||
let tx_body = tx.transaction_body.clone();
|
let tx_body = tx.transaction_body.clone();
|
||||||
|
|
||||||
match script_purpose {
|
match script_purpose {
|
||||||
ScriptPurpose::Minting(hash) => {
|
ScriptPurpose::Minting(hash) => {
|
||||||
let mut policy_ids = tx_body
|
let policy_ids: Vec<&PolicyId> = tx_body
|
||||||
.mint
|
.mint
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.unwrap_or(&KeyValuePairs::Indef(vec![]))
|
.map(|m| m.iter().map(|(policy_id, _)| policy_id).sorted().collect())
|
||||||
.iter()
|
.unwrap_or_default();
|
||||||
.map(|(policy_id, _)| *policy_id)
|
|
||||||
.collect::<Vec<PolicyId>>();
|
|
||||||
|
|
||||||
policy_ids.sort();
|
let redeemer_key =
|
||||||
|
policy_ids
|
||||||
|
.iter()
|
||||||
|
.position(|x| x == &hash)
|
||||||
|
.map(|index| RedeemersKey {
|
||||||
|
tag: RedeemerTag::Mint,
|
||||||
|
index: index as u32,
|
||||||
|
});
|
||||||
|
|
||||||
let maybe_idx = policy_ids.iter().position(|x| x == hash);
|
Ok(redeemer_key)
|
||||||
|
|
||||||
match maybe_idx {
|
|
||||||
Some(idx) => Ok(Some(RedeemerPtr {
|
|
||||||
tag: RedeemerTag::Mint,
|
|
||||||
index: idx as u32,
|
|
||||||
})),
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptPurpose::Spending(txin) => {
|
ScriptPurpose::Spending(txin) => {
|
||||||
let mut inputs = tx_body.inputs.to_vec();
|
let redeemer_key = tx_body
|
||||||
inputs.sort_by(
|
.inputs
|
||||||
|i_a, i_b| match i_a.transaction_id.cmp(&i_b.transaction_id) {
|
|
||||||
std::cmp::Ordering::Less => std::cmp::Ordering::Less,
|
|
||||||
std::cmp::Ordering::Equal => i_a.index.cmp(&i_b.index),
|
|
||||||
std::cmp::Ordering::Greater => std::cmp::Ordering::Greater,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let maybe_idx = inputs.iter().position(|x| x == txin);
|
|
||||||
|
|
||||||
match maybe_idx {
|
|
||||||
Some(idx) => Ok(Some(RedeemerPtr {
|
|
||||||
tag: RedeemerTag::Spend,
|
|
||||||
index: idx as u32,
|
|
||||||
})),
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ScriptPurpose::Rewarding(racnt) => {
|
|
||||||
let mut reward_accounts = tx_body
|
|
||||||
.withdrawals
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&KeyValuePairs::Indef(vec![]))
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(acnt, _)| acnt.clone())
|
.sorted_by(
|
||||||
.collect::<Vec<RewardAccount>>();
|
|i_a, i_b| match i_a.transaction_id.cmp(&i_b.transaction_id) {
|
||||||
|
std::cmp::Ordering::Less => std::cmp::Ordering::Less,
|
||||||
|
std::cmp::Ordering::Equal => i_a.index.cmp(&i_b.index),
|
||||||
|
std::cmp::Ordering::Greater => std::cmp::Ordering::Greater,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.position(|x| x == txin)
|
||||||
|
.map(|index| RedeemersKey {
|
||||||
|
tag: RedeemerTag::Spend,
|
||||||
|
index: index as u32,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(redeemer_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptPurpose::Rewarding(racnt) => {
|
||||||
|
let mut reward_accounts: Vec<&RewardAccount> = tx_body
|
||||||
|
.withdrawals
|
||||||
|
.as_deref()
|
||||||
|
.map(|m| m.iter().map(|(acnt, _)| acnt).collect())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
reward_accounts.sort();
|
reward_accounts.sort();
|
||||||
|
|
||||||
let mut maybe_idx = None;
|
let mut redeemer_key = None;
|
||||||
|
|
||||||
for (idx, x) in reward_accounts.iter().enumerate() {
|
for (idx, x) in reward_accounts.iter().enumerate() {
|
||||||
let cred = match Address::from_bytes(x).unwrap() {
|
let cred = match Address::from_bytes(x).unwrap() {
|
||||||
|
@ -288,33 +282,28 @@ fn build_redeemer_ptr(
|
||||||
};
|
};
|
||||||
|
|
||||||
if cred == Some(racnt.to_owned()) {
|
if cred == Some(racnt.to_owned()) {
|
||||||
maybe_idx = Some(idx);
|
redeemer_key = Some(RedeemersKey {
|
||||||
|
tag: RedeemerTag::Reward,
|
||||||
|
index: idx as u32,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match maybe_idx {
|
Ok(redeemer_key)
|
||||||
Some(idx) => Ok(Some(RedeemerPtr {
|
|
||||||
tag: RedeemerTag::Reward,
|
|
||||||
index: idx as u32,
|
|
||||||
})),
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ScriptPurpose::Certifying(d) => {
|
|
||||||
let maybe_idx = tx_body
|
|
||||||
.certificates
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&MaybeIndefArray::Indef(vec![]))
|
|
||||||
.iter()
|
|
||||||
.position(|x| x == d);
|
|
||||||
|
|
||||||
match maybe_idx {
|
ScriptPurpose::Certifying(d) => {
|
||||||
Some(idx) => Ok(Some(RedeemerPtr {
|
let redeemer_key = tx_body
|
||||||
|
.certificates
|
||||||
|
.as_deref()
|
||||||
|
.map(|m| m.iter().position(|x| x == d))
|
||||||
|
.unwrap_or_default()
|
||||||
|
.map(|index| RedeemersKey {
|
||||||
tag: RedeemerTag::Cert,
|
tag: RedeemerTag::Cert,
|
||||||
index: idx as u32,
|
index: index as u32,
|
||||||
})),
|
});
|
||||||
None => Ok(None),
|
|
||||||
}
|
Ok(redeemer_key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,20 @@
|
||||||
use pallas_addresses::Address;
|
use super::{to_plutus_data::MintValue, Error};
|
||||||
use pallas_codec::utils::KeyValuePairs;
|
use itertools::Itertools;
|
||||||
|
use pallas_addresses::{Address, StakePayload};
|
||||||
|
use pallas_codec::utils::{KeyValuePairs, NonEmptyKeyValuePairs, NonEmptySet};
|
||||||
use pallas_crypto::hash::Hash;
|
use pallas_crypto::hash::Hash;
|
||||||
use pallas_primitives::babbage::{
|
use pallas_primitives::{
|
||||||
AddrKeyhash, Certificate, Coin, DatumHash, PlutusData, PolicyId, Redeemer, StakeCredential,
|
alonzo,
|
||||||
TransactionInput, TransactionOutput, Value,
|
conway::{
|
||||||
|
AddrKeyhash, Certificate, Coin, DatumHash, DatumOption, Mint, MintedTransactionBody,
|
||||||
|
MintedTransactionOutput, MintedTx, MintedWitnessSet, PlutusData, PolicyId,
|
||||||
|
PostAlonzoTransactionOutput, PseudoDatumOption, Redeemer, RedeemerTag, RedeemersKey,
|
||||||
|
RequiredSigners, RewardAccount, StakeCredential, TransactionInput, TransactionOutput,
|
||||||
|
Value,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
use pallas_traverse::OriginalHash;
|
||||||
use super::to_plutus_data::MintValue;
|
use std::{cmp::Ordering, ops::Deref};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct ResolvedInput {
|
pub struct ResolvedInput {
|
||||||
|
@ -47,6 +55,31 @@ pub struct TxInfoV1 {
|
||||||
pub id: Hash<32>,
|
pub id: Hash<32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TxInfoV1 {
|
||||||
|
pub fn from_transaction(
|
||||||
|
tx: &MintedTx,
|
||||||
|
utxos: &[ResolvedInput],
|
||||||
|
slot_config: &SlotConfig,
|
||||||
|
) -> Result<TxInfo, Error> {
|
||||||
|
if tx.transaction_body.reference_inputs.is_some() {
|
||||||
|
return Err(Error::ScriptAndInputRefNotAllowed);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(TxInfo::V1(TxInfoV1 {
|
||||||
|
inputs: get_tx_in_info_v1(&tx.transaction_body.inputs, utxos)?,
|
||||||
|
outputs: get_outputs_info(TxOut::V1, &tx.transaction_body.outputs[..]),
|
||||||
|
fee: get_fee_info(&tx.transaction_body.fee),
|
||||||
|
mint: get_mint_info(&tx.transaction_body.mint),
|
||||||
|
dcert: get_certificates_info(&tx.transaction_body.certificates),
|
||||||
|
wdrl: get_withdrawal_info(&tx.transaction_body.withdrawals),
|
||||||
|
valid_range: get_validity_range_info(&tx.transaction_body, slot_config),
|
||||||
|
signatories: get_signatories_info(&tx.transaction_body.required_signers),
|
||||||
|
data: get_data_info(&tx.transaction_witness_set),
|
||||||
|
id: tx.transaction_body.original_hash(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct TxInfoV2 {
|
pub struct TxInfoV2 {
|
||||||
pub inputs: Vec<TxInInfo>,
|
pub inputs: Vec<TxInInfo>,
|
||||||
|
@ -63,6 +96,59 @@ pub struct TxInfoV2 {
|
||||||
pub id: Hash<32>,
|
pub id: Hash<32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TxInfoV2 {
|
||||||
|
pub fn from_transaction(
|
||||||
|
tx: &MintedTx,
|
||||||
|
utxos: &[ResolvedInput],
|
||||||
|
slot_config: &SlotConfig,
|
||||||
|
) -> Result<TxInfo, Error> {
|
||||||
|
let inputs = get_tx_in_info_v2(&tx.transaction_body.inputs, utxos)?;
|
||||||
|
let dcert = get_certificates_info(&tx.transaction_body.certificates);
|
||||||
|
let wdrl = 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, &dcert, &wdrl),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let reference_inputs = tx
|
||||||
|
.transaction_body
|
||||||
|
.reference_inputs
|
||||||
|
.clone()
|
||||||
|
.map(|refs| get_tx_in_info_v2(&refs[..], utxos))
|
||||||
|
.transpose()?
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
Ok(TxInfo::V2(TxInfoV2 {
|
||||||
|
inputs,
|
||||||
|
reference_inputs,
|
||||||
|
outputs: get_outputs_info(TxOut::V2, &tx.transaction_body.outputs[..]),
|
||||||
|
fee: get_fee_info(&tx.transaction_body.fee),
|
||||||
|
mint,
|
||||||
|
dcert,
|
||||||
|
wdrl,
|
||||||
|
valid_range: get_validity_range_info(&tx.transaction_body, slot_config),
|
||||||
|
signatories: get_signatories_info(&tx.transaction_body.required_signers),
|
||||||
|
data: KeyValuePairs::from(get_data_info(&tx.transaction_witness_set)),
|
||||||
|
redeemers,
|
||||||
|
id: tx.transaction_body.original_hash(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TxInfoV3 {}
|
||||||
|
|
||||||
|
impl TxInfoV3 {
|
||||||
|
pub fn from_transaction(
|
||||||
|
_tx: &MintedTx,
|
||||||
|
_utxos: &[ResolvedInput],
|
||||||
|
_slot_config: &SlotConfig,
|
||||||
|
) -> Result<TxInfo, Error> {
|
||||||
|
todo!("TxInfoV3")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum TxInfo {
|
pub enum TxInfo {
|
||||||
V1(TxInfoV1),
|
V1(TxInfoV1),
|
||||||
|
@ -97,3 +183,353 @@ impl Default for SlotConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --------------------- Translations
|
||||||
|
|
||||||
|
pub fn get_tx_in_info_v1(
|
||||||
|
inputs: &[TransactionInput],
|
||||||
|
utxos: &[ResolvedInput],
|
||||||
|
) -> Result<Vec<TxInInfo>, Error> {
|
||||||
|
inputs
|
||||||
|
.iter()
|
||||||
|
.sorted()
|
||||||
|
.map(|input| {
|
||||||
|
let utxo = match utxos.iter().find(|utxo| utxo.input == *input) {
|
||||||
|
Some(resolved) => resolved,
|
||||||
|
None => return Err(Error::ResolvedInputNotFound(input.clone())),
|
||||||
|
};
|
||||||
|
let address = Address::from_bytes(match &utxo.output {
|
||||||
|
TransactionOutput::Legacy(output) => output.address.as_ref(),
|
||||||
|
TransactionOutput::PostAlonzo(output) => output.address.as_ref(),
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match address {
|
||||||
|
Address::Byron(_) => {
|
||||||
|
return Err(Error::ByronAddressNotAllowed);
|
||||||
|
}
|
||||||
|
Address::Stake(_) => {
|
||||||
|
return Err(Error::NoPaymentCredential);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
match &utxo.output {
|
||||||
|
TransactionOutput::Legacy(_) => {}
|
||||||
|
TransactionOutput::PostAlonzo(output) => {
|
||||||
|
if let Some(DatumOption::Data(_)) = output.datum_option {
|
||||||
|
return Err(Error::InlineDatumNotAllowed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if output.script_ref.is_some() {
|
||||||
|
return Err(Error::ScriptAndInputRefNotAllowed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(TxInInfo {
|
||||||
|
out_ref: utxo.input.clone(),
|
||||||
|
resolved: TxOut::V1(sort_tx_out_value(&utxo.output)),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_tx_in_info_v2(
|
||||||
|
inputs: &[TransactionInput],
|
||||||
|
utxos: &[ResolvedInput],
|
||||||
|
) -> Result<Vec<TxInInfo>, Error> {
|
||||||
|
inputs
|
||||||
|
.iter()
|
||||||
|
.sorted()
|
||||||
|
.map(|input| {
|
||||||
|
let utxo = match utxos.iter().find(|utxo| utxo.input == *input) {
|
||||||
|
Some(resolved) => resolved,
|
||||||
|
None => return Err(Error::ResolvedInputNotFound(input.clone())),
|
||||||
|
};
|
||||||
|
let address = Address::from_bytes(match &utxo.output {
|
||||||
|
TransactionOutput::Legacy(output) => output.address.as_ref(),
|
||||||
|
TransactionOutput::PostAlonzo(output) => output.address.as_ref(),
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match address {
|
||||||
|
Address::Byron(_) => {
|
||||||
|
return Err(Error::ByronAddressNotAllowed);
|
||||||
|
}
|
||||||
|
Address::Stake(_) => {
|
||||||
|
return Err(Error::NoPaymentCredential);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(TxInInfo {
|
||||||
|
out_ref: utxo.input.clone(),
|
||||||
|
resolved: TxOut::V2(sort_tx_out_value(&utxo.output)),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mint_info(mint: &Option<Mint>) -> MintValue {
|
||||||
|
MintValue {
|
||||||
|
mint_value: mint
|
||||||
|
.as_ref()
|
||||||
|
.map(sort_mint)
|
||||||
|
.unwrap_or(NonEmptyKeyValuePairs::Indef(vec![])),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_outputs_info(
|
||||||
|
to_tx_out: fn(TransactionOutput) -> TxOut,
|
||||||
|
outputs: &[MintedTransactionOutput],
|
||||||
|
) -> Vec<TxOut> {
|
||||||
|
outputs
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|output| to_tx_out(sort_tx_out_value(&output.into())))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_fee_info(fee: &Coin) -> Value {
|
||||||
|
Value::Coin(*fee)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_certificates_info(certificates: &Option<NonEmptySet<Certificate>>) -> Vec<Certificate> {
|
||||||
|
certificates.clone().map(|s| s.to_vec()).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_withdrawal_info(
|
||||||
|
withdrawals: &Option<NonEmptyKeyValuePairs<RewardAccount, Coin>>,
|
||||||
|
) -> Vec<(Address, Coin)> {
|
||||||
|
withdrawals
|
||||||
|
.clone()
|
||||||
|
.map(|w| {
|
||||||
|
w.into_iter()
|
||||||
|
.sorted()
|
||||||
|
.map(|(reward_account, coin)| (Address::from_bytes(&reward_account).unwrap(), coin))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_validity_range_info(body: &MintedTransactionBody, slot_config: &SlotConfig) -> TimeRange {
|
||||||
|
fn slot_to_begin_posix_time(slot: u64, sc: &SlotConfig) -> u64 {
|
||||||
|
let ms_after_begin = (slot - sc.zero_slot) * sc.slot_length as u64;
|
||||||
|
sc.zero_time + ms_after_begin
|
||||||
|
}
|
||||||
|
|
||||||
|
fn slot_range_to_posix_time_range(slot_range: TimeRange, sc: &SlotConfig) -> TimeRange {
|
||||||
|
TimeRange {
|
||||||
|
lower_bound: slot_range
|
||||||
|
.lower_bound
|
||||||
|
.map(|lower_bound| slot_to_begin_posix_time(lower_bound, sc)),
|
||||||
|
upper_bound: slot_range
|
||||||
|
.upper_bound
|
||||||
|
.map(|upper_bound| slot_to_begin_posix_time(upper_bound, sc)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
slot_range_to_posix_time_range(
|
||||||
|
TimeRange {
|
||||||
|
lower_bound: body.validity_interval_start,
|
||||||
|
upper_bound: body.ttl,
|
||||||
|
},
|
||||||
|
slot_config,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_signatories_info(signers: &Option<RequiredSigners>) -> Vec<AddrKeyhash> {
|
||||||
|
signers
|
||||||
|
.as_deref()
|
||||||
|
.map(|s| s.iter().cloned().sorted().collect())
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_data_info(witness_set: &MintedWitnessSet) -> Vec<(DatumHash, PlutusData)> {
|
||||||
|
witness_set
|
||||||
|
.plutus_data
|
||||||
|
.as_deref()
|
||||||
|
.map(|s| {
|
||||||
|
s.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|d| (d.original_hash(), d.clone().unwrap()))
|
||||||
|
.sorted()
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_redeemers_info<'a>(
|
||||||
|
witness_set: &'a MintedWitnessSet,
|
||||||
|
to_script_purpose: impl Fn(&'a RedeemersKey) -> Result<ScriptPurpose, Error>,
|
||||||
|
) -> Result<KeyValuePairs<ScriptPurpose, Redeemer>, Error> {
|
||||||
|
Ok(KeyValuePairs::from(
|
||||||
|
witness_set
|
||||||
|
.redeemer
|
||||||
|
.as_deref()
|
||||||
|
.map(|m| {
|
||||||
|
m.iter()
|
||||||
|
.sorted_by(|a, b| sort_redeemers(&a.0, &b.0))
|
||||||
|
.map(|(redeemer_key, redeemer_value)| {
|
||||||
|
let redeemer = Redeemer {
|
||||||
|
tag: redeemer_key.tag,
|
||||||
|
index: redeemer_key.index,
|
||||||
|
data: redeemer_value.data.clone(),
|
||||||
|
ex_units: redeemer_value.ex_units,
|
||||||
|
};
|
||||||
|
|
||||||
|
to_script_purpose(redeemer_key).map(|purpose| (purpose, redeemer))
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
})
|
||||||
|
.transpose()?
|
||||||
|
.unwrap_or_default(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn script_purpose_builder<'a>(
|
||||||
|
inputs: &'a [TxInInfo],
|
||||||
|
mint: &'a MintValue,
|
||||||
|
dcert: &'a [Certificate],
|
||||||
|
wdrl: &'a KeyValuePairs<Address, Coin>,
|
||||||
|
) -> impl Fn(&'a RedeemersKey) -> Result<ScriptPurpose, Error> {
|
||||||
|
move |redeemer: &'a RedeemersKey| {
|
||||||
|
let tag = redeemer.tag;
|
||||||
|
let index = redeemer.index as usize;
|
||||||
|
match tag {
|
||||||
|
RedeemerTag::Mint => mint
|
||||||
|
.mint_value
|
||||||
|
.get(index)
|
||||||
|
.map(|(policy_id, _)| ScriptPurpose::Minting(*policy_id)),
|
||||||
|
RedeemerTag::Spend => inputs
|
||||||
|
.get(index)
|
||||||
|
.cloned()
|
||||||
|
.map(|i| ScriptPurpose::Spending(i.out_ref)),
|
||||||
|
RedeemerTag::Cert => dcert.get(index).cloned().map(ScriptPurpose::Certifying),
|
||||||
|
RedeemerTag::Reward => wdrl
|
||||||
|
.get(index)
|
||||||
|
.cloned()
|
||||||
|
.map(|(address, _)| match address {
|
||||||
|
Address::Stake(stake_address) => match stake_address.payload() {
|
||||||
|
StakePayload::Script(script_hash) => Ok(ScriptPurpose::Rewarding(
|
||||||
|
StakeCredential::Scripthash(*script_hash),
|
||||||
|
)),
|
||||||
|
StakePayload::Stake(_) => Err(Error::ScriptKeyHash),
|
||||||
|
},
|
||||||
|
_ => Err(Error::BadWithdrawalAddress),
|
||||||
|
})
|
||||||
|
.transpose()?,
|
||||||
|
tag => todo!("get_script_purpose for {tag:?}"),
|
||||||
|
}
|
||||||
|
.ok_or(Error::ExtraneousRedeemer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_alonzo_value(value: &alonzo::Value) -> Value {
|
||||||
|
match value {
|
||||||
|
alonzo::Value::Coin(coin) => Value::Coin(*coin),
|
||||||
|
alonzo::Value::Multiasset(coin, assets) if assets.is_empty() => Value::Coin(*coin),
|
||||||
|
alonzo::Value::Multiasset(coin, assets) => Value::Multiasset(
|
||||||
|
*coin,
|
||||||
|
NonEmptyKeyValuePairs::try_from(
|
||||||
|
assets
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|(policy_id, tokens)| {
|
||||||
|
(
|
||||||
|
policy_id,
|
||||||
|
NonEmptyKeyValuePairs::try_from(
|
||||||
|
tokens
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|(asset_name, quantity)| {
|
||||||
|
(
|
||||||
|
asset_name,
|
||||||
|
quantity.try_into().expect("0 Ada in output value?"),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect_vec(),
|
||||||
|
)
|
||||||
|
.expect("empty tokens under a policy?"),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect_vec(),
|
||||||
|
)
|
||||||
|
.expect("assets cannot be empty due to pattern-guard"),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------- Sorting
|
||||||
|
|
||||||
|
fn sort_tx_out_value(tx_output: &TransactionOutput) -> TransactionOutput {
|
||||||
|
match tx_output {
|
||||||
|
TransactionOutput::Legacy(output) => {
|
||||||
|
let new_output = PostAlonzoTransactionOutput {
|
||||||
|
address: output.address.clone(),
|
||||||
|
value: sort_value(&from_alonzo_value(&output.amount)),
|
||||||
|
datum_option: output.datum_hash.map(PseudoDatumOption::Hash),
|
||||||
|
script_ref: None,
|
||||||
|
};
|
||||||
|
TransactionOutput::PostAlonzo(new_output)
|
||||||
|
}
|
||||||
|
TransactionOutput::PostAlonzo(output) => {
|
||||||
|
let mut new_output = output.clone();
|
||||||
|
new_output.value = sort_value(&output.value);
|
||||||
|
TransactionOutput::PostAlonzo(new_output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sort_mint(mint: &Mint) -> Mint {
|
||||||
|
let mut mint_vec = vec![];
|
||||||
|
|
||||||
|
for m in mint.deref().iter().sorted() {
|
||||||
|
mint_vec.push((
|
||||||
|
m.0,
|
||||||
|
NonEmptyKeyValuePairs::Indef(
|
||||||
|
m.1.deref().clone().into_iter().sorted().clone().collect(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
NonEmptyKeyValuePairs::Indef(mint_vec)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sort_value(value: &Value) -> Value {
|
||||||
|
match value {
|
||||||
|
Value::Coin(_) => value.clone(),
|
||||||
|
Value::Multiasset(coin, ma) => {
|
||||||
|
let mut ma_vec = vec![];
|
||||||
|
for m in ma.deref().iter().sorted() {
|
||||||
|
ma_vec.push((
|
||||||
|
m.0,
|
||||||
|
NonEmptyKeyValuePairs::Indef(
|
||||||
|
m.1.deref().clone().into_iter().sorted().clone().collect(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Value::Multiasset(*coin, NonEmptyKeyValuePairs::Indef(ma_vec))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sort_redeemers(a: &RedeemersKey, b: &RedeemersKey) -> Ordering {
|
||||||
|
fn redeemer_tag_as_usize(tag: &RedeemerTag) -> usize {
|
||||||
|
match tag {
|
||||||
|
RedeemerTag::Spend => 0,
|
||||||
|
RedeemerTag::Mint => 1,
|
||||||
|
RedeemerTag::Cert => 2,
|
||||||
|
RedeemerTag::Reward => 3,
|
||||||
|
RedeemerTag::Vote => 4,
|
||||||
|
RedeemerTag::Propose => 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.tag == b.tag {
|
||||||
|
a.index.cmp(&b.index)
|
||||||
|
} else {
|
||||||
|
redeemer_tag_as_usize(&a.tag).cmp(&redeemer_tag_as_usize(&b.tag))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
|
use super::{eval_phase_two, ResolvedInput, SlotConfig};
|
||||||
|
use crate::machine::cost_model::ExBudget;
|
||||||
use pallas_codec::utils::MaybeIndefArray;
|
use pallas_codec::utils::MaybeIndefArray;
|
||||||
use pallas_primitives::{
|
use pallas_primitives::{
|
||||||
babbage::{CostMdls, TransactionInput, TransactionOutput},
|
conway::{CostMdls, TransactionInput, TransactionOutput},
|
||||||
Fragment,
|
Fragment,
|
||||||
};
|
};
|
||||||
use pallas_traverse::{Era, MultiEraTx};
|
use pallas_traverse::{Era, MultiEraTx};
|
||||||
|
|
||||||
use crate::machine::cost_model::ExBudget;
|
|
||||||
|
|
||||||
use super::{eval_phase_two, ResolvedInput, SlotConfig};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_eval_0() {
|
fn test_eval_0() {
|
||||||
/*
|
/*
|
||||||
|
@ -227,6 +225,7 @@ fn test_eval_0() {
|
||||||
let cost_mdl = CostMdls {
|
let cost_mdl = CostMdls {
|
||||||
plutus_v1: None,
|
plutus_v1: None,
|
||||||
plutus_v2: Some(costs),
|
plutus_v2: Some(costs),
|
||||||
|
plutus_v3: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let initial_budget = ExBudget {
|
let initial_budget = ExBudget {
|
||||||
|
@ -234,11 +233,12 @@ fn test_eval_0() {
|
||||||
mem: 14000000,
|
mem: 14000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
let multi_era_tx = MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes)
|
let multi_era_tx = MultiEraTx::decode_for_era(Era::Conway, &tx_bytes)
|
||||||
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes))
|
||||||
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match multi_era_tx {
|
match multi_era_tx {
|
||||||
MultiEraTx::Babbage(tx) => {
|
MultiEraTx::Conway(tx) => {
|
||||||
let redeemers = eval_phase_two(
|
let redeemers = eval_phase_two(
|
||||||
&tx,
|
&tx,
|
||||||
&utxos,
|
&utxos,
|
||||||
|
@ -497,6 +497,7 @@ fn test_eval_1() {
|
||||||
let cost_mdl = CostMdls {
|
let cost_mdl = CostMdls {
|
||||||
plutus_v1: None,
|
plutus_v1: None,
|
||||||
plutus_v2: Some(costs),
|
plutus_v2: Some(costs),
|
||||||
|
plutus_v3: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let initial_budget = ExBudget {
|
let initial_budget = ExBudget {
|
||||||
|
@ -504,11 +505,13 @@ fn test_eval_1() {
|
||||||
mem: 14000000,
|
mem: 14000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
let multi_era_tx = MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes)
|
let multi_era_tx = MultiEraTx::decode_for_era(Era::Conway, &tx_bytes)
|
||||||
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes))
|
||||||
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
match multi_era_tx {
|
match multi_era_tx {
|
||||||
MultiEraTx::Babbage(tx) => {
|
MultiEraTx::Conway(tx) => {
|
||||||
let redeemers = eval_phase_two(
|
let redeemers = eval_phase_two(
|
||||||
&tx,
|
&tx,
|
||||||
&utxos,
|
&utxos,
|
||||||
|
@ -606,6 +609,7 @@ fn test_eval_2() {
|
||||||
let cost_mdl = CostMdls {
|
let cost_mdl = CostMdls {
|
||||||
plutus_v1: Some(costs),
|
plutus_v1: Some(costs),
|
||||||
plutus_v2: None,
|
plutus_v2: None,
|
||||||
|
plutus_v3: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let initial_budget = ExBudget {
|
let initial_budget = ExBudget {
|
||||||
|
@ -613,11 +617,12 @@ fn test_eval_2() {
|
||||||
mem: 14000000,
|
mem: 14000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
let multi_era_tx = MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes)
|
let multi_era_tx = MultiEraTx::decode_for_era(Era::Conway, &tx_bytes)
|
||||||
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes))
|
||||||
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match multi_era_tx {
|
match multi_era_tx {
|
||||||
MultiEraTx::Babbage(tx) => {
|
MultiEraTx::Conway(tx) => {
|
||||||
let redeemers = eval_phase_two(
|
let redeemers = eval_phase_two(
|
||||||
&tx,
|
&tx,
|
||||||
&utxos,
|
&utxos,
|
||||||
|
@ -874,6 +879,7 @@ fn test_eval_3() {
|
||||||
let cost_mdl = CostMdls {
|
let cost_mdl = CostMdls {
|
||||||
plutus_v1: None,
|
plutus_v1: None,
|
||||||
plutus_v2: Some(costs),
|
plutus_v2: Some(costs),
|
||||||
|
plutus_v3: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let initial_budget = ExBudget {
|
let initial_budget = ExBudget {
|
||||||
|
@ -881,11 +887,12 @@ fn test_eval_3() {
|
||||||
mem: 14000000,
|
mem: 14000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
let multi_era_tx = MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes)
|
let multi_era_tx = MultiEraTx::decode_for_era(Era::Conway, &tx_bytes)
|
||||||
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes))
|
||||||
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match multi_era_tx {
|
match multi_era_tx {
|
||||||
MultiEraTx::Babbage(tx) => {
|
MultiEraTx::Conway(tx) => {
|
||||||
let redeemers = eval_phase_two(
|
let redeemers = eval_phase_two(
|
||||||
&tx,
|
&tx,
|
||||||
&utxos,
|
&utxos,
|
||||||
|
@ -980,6 +987,7 @@ fn test_eval_4() {
|
||||||
let cost_mdl = CostMdls {
|
let cost_mdl = CostMdls {
|
||||||
plutus_v1: Some(costs),
|
plutus_v1: Some(costs),
|
||||||
plutus_v2: None,
|
plutus_v2: None,
|
||||||
|
plutus_v3: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let initial_budget = ExBudget {
|
let initial_budget = ExBudget {
|
||||||
|
@ -987,11 +995,12 @@ fn test_eval_4() {
|
||||||
mem: 14000000,
|
mem: 14000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
let multi_era_tx = MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes)
|
let multi_era_tx = MultiEraTx::decode_for_era(Era::Conway, &tx_bytes)
|
||||||
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes))
|
||||||
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match multi_era_tx {
|
match multi_era_tx {
|
||||||
MultiEraTx::Babbage(tx) => {
|
MultiEraTx::Conway(tx) => {
|
||||||
assert!(eval_phase_two(
|
assert!(eval_phase_two(
|
||||||
&tx,
|
&tx,
|
||||||
&utxos,
|
&utxos,
|
||||||
|
@ -1063,6 +1072,7 @@ fn test_eval_5() {
|
||||||
let cost_mdl = CostMdls {
|
let cost_mdl = CostMdls {
|
||||||
plutus_v1: Some(costs),
|
plutus_v1: Some(costs),
|
||||||
plutus_v2: None,
|
plutus_v2: None,
|
||||||
|
plutus_v3: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let initial_budget = ExBudget {
|
let initial_budget = ExBudget {
|
||||||
|
@ -1070,11 +1080,12 @@ fn test_eval_5() {
|
||||||
mem: 14000000,
|
mem: 14000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
let multi_era_tx = MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes)
|
let multi_era_tx = MultiEraTx::decode_for_era(Era::Conway, &tx_bytes)
|
||||||
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes))
|
||||||
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match multi_era_tx {
|
match multi_era_tx {
|
||||||
MultiEraTx::Babbage(tx) => {
|
MultiEraTx::Conway(tx) => {
|
||||||
let redeemers = eval_phase_two(
|
let redeemers = eval_phase_two(
|
||||||
&tx,
|
&tx,
|
||||||
&utxos,
|
&utxos,
|
||||||
|
@ -1171,6 +1182,7 @@ fn test_eval_6() {
|
||||||
let cost_mdl = CostMdls {
|
let cost_mdl = CostMdls {
|
||||||
plutus_v1: Some(costs),
|
plutus_v1: Some(costs),
|
||||||
plutus_v2: None,
|
plutus_v2: None,
|
||||||
|
plutus_v3: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let initial_budget = ExBudget {
|
let initial_budget = ExBudget {
|
||||||
|
@ -1178,11 +1190,12 @@ fn test_eval_6() {
|
||||||
mem: 14000000,
|
mem: 14000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
let multi_era_tx = MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes)
|
let multi_era_tx = MultiEraTx::decode_for_era(Era::Conway, &tx_bytes)
|
||||||
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes))
|
||||||
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match multi_era_tx {
|
match multi_era_tx {
|
||||||
MultiEraTx::Babbage(tx) => {
|
MultiEraTx::Conway(tx) => {
|
||||||
let redeemers = eval_phase_two(
|
let redeemers = eval_phase_two(
|
||||||
&tx,
|
&tx,
|
||||||
&utxos,
|
&utxos,
|
||||||
|
@ -1279,6 +1292,7 @@ fn test_eval_7() {
|
||||||
let cost_mdl = CostMdls {
|
let cost_mdl = CostMdls {
|
||||||
plutus_v1: Some(costs),
|
plutus_v1: Some(costs),
|
||||||
plutus_v2: None,
|
plutus_v2: None,
|
||||||
|
plutus_v3: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let initial_budget = ExBudget {
|
let initial_budget = ExBudget {
|
||||||
|
@ -1286,11 +1300,12 @@ fn test_eval_7() {
|
||||||
mem: 14000000,
|
mem: 14000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
let multi_era_tx = MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes)
|
let multi_era_tx = MultiEraTx::decode_for_era(Era::Conway, &tx_bytes)
|
||||||
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes))
|
||||||
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match multi_era_tx {
|
match multi_era_tx {
|
||||||
MultiEraTx::Babbage(tx) => {
|
MultiEraTx::Conway(tx) => {
|
||||||
let redeemers = eval_phase_two(
|
let redeemers = eval_phase_two(
|
||||||
&tx,
|
&tx,
|
||||||
&utxos,
|
&utxos,
|
||||||
|
@ -1538,6 +1553,7 @@ fn test_eval_8() {
|
||||||
let cost_mdl = CostMdls {
|
let cost_mdl = CostMdls {
|
||||||
plutus_v1: None,
|
plutus_v1: None,
|
||||||
plutus_v2: Some(costs),
|
plutus_v2: Some(costs),
|
||||||
|
plutus_v3: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let initial_budget = ExBudget {
|
let initial_budget = ExBudget {
|
||||||
|
@ -1545,11 +1561,12 @@ fn test_eval_8() {
|
||||||
mem: 14000000,
|
mem: 14000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
let multi_era_tx = MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes)
|
let multi_era_tx = MultiEraTx::decode_for_era(Era::Conway, &tx_bytes)
|
||||||
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes))
|
||||||
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match multi_era_tx {
|
match multi_era_tx {
|
||||||
MultiEraTx::Babbage(tx) => {
|
MultiEraTx::Conway(tx) => {
|
||||||
let redeemers = eval_phase_two(
|
let redeemers = eval_phase_two(
|
||||||
&tx,
|
&tx,
|
||||||
&utxos,
|
&utxos,
|
||||||
|
@ -1593,12 +1610,13 @@ fn test_eval_8() {
|
||||||
fn eval_missing_redeemer() {
|
fn eval_missing_redeemer() {
|
||||||
let tx_bytes = hex::decode("84a30082825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c5000825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c50010181825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02cf47c8021a00028d89a1068149480100002221200101f5f6").unwrap();
|
let tx_bytes = hex::decode("84a30082825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c5000825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c50010181825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02cf47c8021a00028d89a1068149480100002221200101f5f6").unwrap();
|
||||||
|
|
||||||
let multi_era_tx = MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes)
|
let multi_era_tx = MultiEraTx::decode_for_era(Era::Conway, &tx_bytes)
|
||||||
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes))
|
||||||
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let inputs = multi_era_tx
|
let inputs = multi_era_tx
|
||||||
.as_babbage()
|
.as_conway()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.transaction_body
|
.transaction_body
|
||||||
.inputs
|
.inputs
|
||||||
|
@ -1641,6 +1659,7 @@ fn eval_missing_redeemer() {
|
||||||
let cost_mdl = CostMdls {
|
let cost_mdl = CostMdls {
|
||||||
plutus_v1: Some(costs),
|
plutus_v1: Some(costs),
|
||||||
plutus_v2: None,
|
plutus_v2: None,
|
||||||
|
plutus_v3: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let initial_budget = ExBudget {
|
let initial_budget = ExBudget {
|
||||||
|
@ -1648,12 +1667,13 @@ fn eval_missing_redeemer() {
|
||||||
mem: 14000000,
|
mem: 14000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
let multi_era_tx = MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes)
|
let multi_era_tx = MultiEraTx::decode_for_era(Era::Conway, &tx_bytes)
|
||||||
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes))
|
||||||
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
match multi_era_tx {
|
match multi_era_tx {
|
||||||
MultiEraTx::Babbage(tx) => {
|
MultiEraTx::Conway(tx) => {
|
||||||
eval_phase_two(
|
eval_phase_two(
|
||||||
&tx,
|
&tx,
|
||||||
&utxos,
|
&utxos,
|
||||||
|
@ -1673,12 +1693,13 @@ fn eval_missing_redeemer() {
|
||||||
fn eval_extraneous_redeemer() {
|
fn eval_extraneous_redeemer() {
|
||||||
let tx_bytes = hex::decode("84a70082825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c5000825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c50010181825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02cf2b47021a0002aa0a0b5820fc54f302cff3a8a1cb374f5e4979e18a1d3627dcf4539637b03f5959eb8565bf0d81825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c500110825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02af51c2111a0003ff0fa40081825820065dd553fbe4e240a8f819bb9e333a7483de4a22b65c7fb6a95ce9450f84dff758402c26125a057a696079d08f2c8c9d2b8ccda9fe7cf7360c1a86712b85a91db82a3b80996b30ba6f4b2f969c93eb50694e0f6ea0bcf129080dcc07ecd9e605f00a049fd87980ff0582840000d879808219044c1a000382d48401001864821903e81903e8068149480100002221200101f5f6").unwrap();
|
let tx_bytes = hex::decode("84a70082825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c5000825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c50010181825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02cf2b47021a0002aa0a0b5820fc54f302cff3a8a1cb374f5e4979e18a1d3627dcf4539637b03f5959eb8565bf0d81825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c500110825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02af51c2111a0003ff0fa40081825820065dd553fbe4e240a8f819bb9e333a7483de4a22b65c7fb6a95ce9450f84dff758402c26125a057a696079d08f2c8c9d2b8ccda9fe7cf7360c1a86712b85a91db82a3b80996b30ba6f4b2f969c93eb50694e0f6ea0bcf129080dcc07ecd9e605f00a049fd87980ff0582840000d879808219044c1a000382d48401001864821903e81903e8068149480100002221200101f5f6").unwrap();
|
||||||
|
|
||||||
let multi_era_tx = MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes)
|
let multi_era_tx = MultiEraTx::decode_for_era(Era::Conway, &tx_bytes)
|
||||||
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes))
|
||||||
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let inputs = multi_era_tx
|
let inputs = multi_era_tx
|
||||||
.as_babbage()
|
.as_conway()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.transaction_body
|
.transaction_body
|
||||||
.inputs
|
.inputs
|
||||||
|
@ -1721,6 +1742,7 @@ fn eval_extraneous_redeemer() {
|
||||||
let cost_mdl = CostMdls {
|
let cost_mdl = CostMdls {
|
||||||
plutus_v1: Some(costs),
|
plutus_v1: Some(costs),
|
||||||
plutus_v2: None,
|
plutus_v2: None,
|
||||||
|
plutus_v3: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let initial_budget = ExBudget {
|
let initial_budget = ExBudget {
|
||||||
|
@ -1728,12 +1750,13 @@ fn eval_extraneous_redeemer() {
|
||||||
mem: 14000000,
|
mem: 14000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
let multi_era_tx = MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes)
|
let multi_era_tx = MultiEraTx::decode_for_era(Era::Conway, &tx_bytes)
|
||||||
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes))
|
||||||
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, &tx_bytes))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
match multi_era_tx {
|
match multi_era_tx {
|
||||||
MultiEraTx::Babbage(tx) => {
|
MultiEraTx::Conway(tx) => {
|
||||||
assert!(eval_phase_two(
|
assert!(eval_phase_two(
|
||||||
&tx,
|
&tx,
|
||||||
&utxos,
|
&utxos,
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
|
use super::script_context::{ScriptContext, ScriptPurpose, TimeRange, TxInInfo, TxInfo, TxOut};
|
||||||
|
use crate::machine::runtime::{convert_constr_to_tag, ANY_TAG};
|
||||||
use pallas_addresses::{Address, ShelleyDelegationPart, ShelleyPaymentPart, StakePayload};
|
use pallas_addresses::{Address, ShelleyDelegationPart, ShelleyPaymentPart, StakePayload};
|
||||||
use pallas_codec::utils::{AnyUInt, Bytes, Int, KeyValuePairs};
|
use pallas_codec::utils::{AnyUInt, Bytes, Int, KeyValuePairs};
|
||||||
use pallas_crypto::hash::Hash;
|
use pallas_crypto::hash::Hash;
|
||||||
use pallas_primitives::babbage::{AssetName, BigInt, Constr, Mint, PlutusData, ScriptRef};
|
use pallas_primitives::conway::{
|
||||||
use pallas_primitives::babbage::{
|
AssetName, BigInt, Certificate, Constr, DatumOption, Mint, PlutusData, PseudoScript, Redeemer,
|
||||||
Certificate, DatumOption, PseudoScript, Redeemer, StakeCredential, TransactionInput,
|
ScriptRef, StakeCredential, TransactionInput, TransactionOutput, Value,
|
||||||
TransactionOutput, Value,
|
|
||||||
};
|
};
|
||||||
use pallas_traverse::ComputeHash;
|
use pallas_traverse::ComputeHash;
|
||||||
|
|
||||||
use crate::machine::runtime::{convert_constr_to_tag, ANY_TAG};
|
|
||||||
|
|
||||||
use super::script_context::{ScriptContext, ScriptPurpose, TimeRange, TxInInfo, TxInfo, TxOut};
|
|
||||||
|
|
||||||
fn wrap_with_constr(index: u64, data: PlutusData) -> PlutusData {
|
fn wrap_with_constr(index: u64, data: PlutusData) -> PlutusData {
|
||||||
let converted = convert_constr_to_tag(index);
|
let converted = convert_constr_to_tag(index);
|
||||||
PlutusData::Constr(Constr {
|
PlutusData::Constr(Constr {
|
||||||
|
@ -235,7 +232,8 @@ impl ToPlutusData for Value {
|
||||||
for (policy_id, assets) in multiassets.iter() {
|
for (policy_id, assets) in multiassets.iter() {
|
||||||
let mut assets_vec = vec![];
|
let mut assets_vec = vec![];
|
||||||
for (asset, amount) in assets.iter() {
|
for (asset, amount) in assets.iter() {
|
||||||
assets_vec.push((asset.to_plutus_data(), amount.to_plutus_data()));
|
assets_vec
|
||||||
|
.push((asset.to_plutus_data(), u64::from(amount).to_plutus_data()));
|
||||||
}
|
}
|
||||||
data_vec.push((
|
data_vec.push((
|
||||||
policy_id.to_plutus_data(),
|
policy_id.to_plutus_data(),
|
||||||
|
@ -262,7 +260,7 @@ impl ToPlutusData for MintValue {
|
||||||
for (policy_id, assets) in self.mint_value.iter() {
|
for (policy_id, assets) in self.mint_value.iter() {
|
||||||
let mut assets_vec = vec![];
|
let mut assets_vec = vec![];
|
||||||
for (asset, amount) in assets.iter() {
|
for (asset, amount) in assets.iter() {
|
||||||
assets_vec.push((asset.to_plutus_data(), amount.to_plutus_data()));
|
assets_vec.push((asset.to_plutus_data(), i64::from(amount).to_plutus_data()));
|
||||||
}
|
}
|
||||||
data_vec.push((
|
data_vec.push((
|
||||||
policy_id.to_plutus_data(),
|
policy_id.to_plutus_data(),
|
||||||
|
@ -282,6 +280,7 @@ impl ToPlutusData for ScriptRef {
|
||||||
}
|
}
|
||||||
PseudoScript::PlutusV1Script(plutus_v1) => plutus_v1.compute_hash().to_plutus_data(),
|
PseudoScript::PlutusV1Script(plutus_v1) => plutus_v1.compute_hash().to_plutus_data(),
|
||||||
PseudoScript::PlutusV2Script(plutus_v2) => plutus_v2.compute_hash().to_plutus_data(),
|
PseudoScript::PlutusV2Script(plutus_v2) => plutus_v2.compute_hash().to_plutus_data(),
|
||||||
|
PseudoScript::PlutusV3Script(plutus_v3) => plutus_v3.compute_hash().to_plutus_data(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,16 +289,17 @@ impl ToPlutusData for TxOut {
|
||||||
fn to_plutus_data(&self) -> PlutusData {
|
fn to_plutus_data(&self) -> PlutusData {
|
||||||
match self {
|
match self {
|
||||||
TxOut::V1(output) => match output {
|
TxOut::V1(output) => match output {
|
||||||
TransactionOutput::Legacy(legacy_output) => wrap_multiple_with_constr(
|
// TransactionOutput::Legacy(legacy_output) => wrap_multiple_with_constr(
|
||||||
0,
|
// 0,
|
||||||
vec![
|
// vec![
|
||||||
Address::from_bytes(&legacy_output.address)
|
// Address::from_bytes(&legacy_output.address)
|
||||||
.unwrap()
|
// .unwrap()
|
||||||
.to_plutus_data(),
|
// .to_plutus_data(),
|
||||||
legacy_output.amount.to_plutus_data(),
|
// legacy_output.amount.to_plutus_data(),
|
||||||
legacy_output.datum_hash.to_plutus_data(),
|
// legacy_output.datum_hash.to_plutus_data(),
|
||||||
],
|
// ],
|
||||||
),
|
// ),
|
||||||
|
TransactionOutput::Legacy(..) => todo!("TransactionOutput::Legacy"),
|
||||||
TransactionOutput::PostAlonzo(post_alonzo_output) => wrap_multiple_with_constr(
|
TransactionOutput::PostAlonzo(post_alonzo_output) => wrap_multiple_with_constr(
|
||||||
0,
|
0,
|
||||||
vec![
|
vec![
|
||||||
|
@ -315,20 +315,21 @@ impl ToPlutusData for TxOut {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
TxOut::V2(output) => match output {
|
TxOut::V2(output) => match output {
|
||||||
TransactionOutput::Legacy(legacy_output) => wrap_multiple_with_constr(
|
// TransactionOutput::Legacy(legacy_output) => wrap_multiple_with_constr(
|
||||||
0,
|
// 0,
|
||||||
vec![
|
// vec![
|
||||||
Address::from_bytes(&legacy_output.address)
|
// Address::from_bytes(&legacy_output.address)
|
||||||
.unwrap()
|
// .unwrap()
|
||||||
.to_plutus_data(),
|
// .to_plutus_data(),
|
||||||
legacy_output.amount.to_plutus_data(),
|
// legacy_output.amount.to_plutus_data(),
|
||||||
match legacy_output.datum_hash {
|
// match legacy_output.datum_hash {
|
||||||
Some(hash) => wrap_with_constr(1, hash.to_plutus_data()),
|
// Some(hash) => wrap_with_constr(1, hash.to_plutus_data()),
|
||||||
_ => empty_constr(0),
|
// _ => empty_constr(0),
|
||||||
},
|
// },
|
||||||
None::<ScriptRef>.to_plutus_data(),
|
// None::<ScriptRef>.to_plutus_data(),
|
||||||
],
|
// ],
|
||||||
),
|
// ),
|
||||||
|
TransactionOutput::Legacy(..) => todo!("TransactionOutput::Legacy"),
|
||||||
TransactionOutput::PostAlonzo(post_alonzo_output) => wrap_multiple_with_constr(
|
TransactionOutput::PostAlonzo(post_alonzo_output) => wrap_multiple_with_constr(
|
||||||
0,
|
0,
|
||||||
vec![
|
vec![
|
||||||
|
@ -401,8 +402,18 @@ impl ToPlutusData for Certificate {
|
||||||
4,
|
4,
|
||||||
vec![pool_keyhash.to_plutus_data(), epoch.to_plutus_data()],
|
vec![pool_keyhash.to_plutus_data(), epoch.to_plutus_data()],
|
||||||
),
|
),
|
||||||
Certificate::GenesisKeyDelegation(_, _, _) => empty_constr(5),
|
_ => todo!("other certificates"), // Reg(StakeCredential, Coin),
|
||||||
Certificate::MoveInstantaneousRewardsCert(_) => empty_constr(6),
|
// UnReg(StakeCredential, Coin),
|
||||||
|
// VoteDeleg(StakeCredential, DRep),
|
||||||
|
// StakeVoteDeleg(StakeCredential, PoolKeyhash, DRep),
|
||||||
|
// StakeRegDeleg(StakeCredential, PoolKeyhash, Coin),
|
||||||
|
// VoteRegDeleg(StakeCredential, DRep, Coin),
|
||||||
|
// StakeVoteRegDeleg(StakeCredential, PoolKeyhash, DRep, Coin),
|
||||||
|
// AuthCommitteeHot(CommitteeColdCredential, CommitteeHotCredential),
|
||||||
|
// ResignCommitteeCold(CommitteeColdCredential, Nullable<Anchor>),
|
||||||
|
// RegDRepCert(DRepCredential, Coin, Nullable<Anchor>),
|
||||||
|
// UnRegDRepCert(DRepCredential, Coin),
|
||||||
|
// UpdateDRepCert(StakeCredential, Nullable<Anchor>),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue