diff --git a/Cargo.lock b/Cargo.lock index e361b432..037dba60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,10 +24,18 @@ dependencies = [ [[package]] name = "aiken" -version = "0.0.11" +version = "0.0.13" dependencies = [ "anyhow", "clap", + "hex", + "pallas-addresses", + "pallas-codec", + "pallas-crypto", + "pallas-primitives", + "pallas-traverse", + "serde", + "serde_json", "uplc", ] @@ -43,9 +51,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.64" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9a8f622bcf6ff3df478e9deba3e03e4e04b300f8e6a139e192c05fa3490afc7" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" [[package]] name = "arrayvec" @@ -126,9 +134,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.20" +version = "3.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" dependencies = [ "atty", "bitflags", @@ -311,15 +319,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.132" +version = "0.2.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" +checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" [[package]] name = "lock_api" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f80bf5aacaf25cbfc8210d1cfb718f2bf3b11c4c54e5afe36c236853a8ec390" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ "autocfg", "scopeguard", @@ -357,34 +365,14 @@ dependencies = [ "syn", ] -[[package]] -name = "minicbor" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e575910763b21a0db7df5e142907fe944bff84d1dfc78e2ba92e7f3bdfd36b" -dependencies = [ - "half", - "minicbor-derive 0.11.0", -] - [[package]] name = "minicbor" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a20020e8e2d1881d8736f64011bb5ff99f1db9947ce3089706945c8915695cb" dependencies = [ - "minicbor-derive 0.12.0", -] - -[[package]] -name = "minicbor-derive" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a86c5f04def8fb7735ae918bb589af82f985526f4c62e0249544b668b2f456" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "half", + "minicbor-derive", ] [[package]] @@ -409,9 +397,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" [[package]] name = "os_str_bytes" @@ -420,32 +408,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" [[package]] -name = "pallas-codec" -version = "0.12.0" +name = "pallas-addresses" +version = "0.14.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dce0ea17341c1a0e43e2bb4a637740198dcb09826879ce3ac5ae1c6f4398a5d" +checksum = "1188a2434037b74129f8d209a37a1d09721b900e6e378255db1a91abc37199bc" dependencies = [ - "minicbor 0.17.1", + "base58", + "bech32", + "hex", + "pallas-codec", + "pallas-crypto", + "thiserror", +] + +[[package]] +name = "pallas-codec" +version = "0.14.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c035a772aa84e858e53b7c98e6036eaa216d8a699bb9c826787722bde13d05" +dependencies = [ + "hex", + "minicbor", + "serde", ] [[package]] name = "pallas-crypto" -version = "0.12.0" +version = "0.14.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "051226367cd851895c73e3115d378b58495ade1ee60c3a154c0d0c30554565fa" +checksum = "2841f9225dcd6a78c6f386d4d5e76bcdbecd7b4489455b2d2485b105bf4c0499" dependencies = [ "cryptoxide", "hex", "pallas-codec", "rand_core", + "serde", "thiserror", ] [[package]] name = "pallas-primitives" -version = "0.12.0" +version = "0.14.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a0fcc7d5a7120bc2b2e203ec5e7f8088107c500c0eb665569d0e77a910d3c0" +checksum = "3e51824547f7a1e1a6574ecec4bf8557f3819f435132873c0bae97acba81cbb1" dependencies = [ "base58", "bech32", @@ -457,6 +462,20 @@ dependencies = [ "serde_json", ] +[[package]] +name = "pallas-traverse" +version = "0.14.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3291d1ae31cd803b9142fb32e1fcb0a58fc1d65b3bbe1d527d14b322db6eb019" +dependencies = [ + "hex", + "pallas-addresses", + "pallas-codec", + "pallas-crypto", + "pallas-primitives", + "thiserror", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -557,9 +576,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58" dependencies = [ "unicode-ident", ] @@ -628,9 +647,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] @@ -694,9 +713,23 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.144" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "serde_json" @@ -723,9 +756,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.99" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e" dependencies = [ "proc-macro2", "quote", @@ -757,24 +790,24 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" [[package]] name = "thiserror" -version = "1.0.34" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1b05ca9d106ba7d2e31a9dab4a64e7be2cce415321966ea3132c49a656e252" +checksum = "0a99cb8c4b9a8ef0e7907cd3b617cc8dc04d571c4e73c8ae403d80ac160bb122" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.34" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8f2591983642de85c921015f3f070c665a197ed69e417af436115e3a1407487" +checksum = "3a891860d3c8d66fec8e73ddb3765f90082374dbaaa833407b904a94f1a7eb43" dependencies = [ "proc-macro2", "quote", @@ -798,35 +831,40 @@ checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae" [[package]] name = "unicode-ident" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" [[package]] name = "unicode-segmentation" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "uplc" -version = "0.0.11" +version = "0.0.13" dependencies = [ + "anyhow", "cryptoxide", "flat-rs", "hex", - "minicbor 0.18.0", + "pallas-addresses", "pallas-codec", + "pallas-crypto", "pallas-primitives", + "pallas-traverse", "peg", "pretty", "proptest", + "serde", + "serde_json", "thiserror", ] diff --git a/add_integers.uplc b/add_integers.uplc deleted file mode 100644 index fa621147..00000000 --- a/add_integers.uplc +++ /dev/null @@ -1,5 +0,0 @@ -(program - 1.0.0 - [(force (force (builtin fstPair))) (con (pair integer bytestring) (22, #1122aabb))] -) - diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 01feb483..6208b13a 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "aiken" description = "Cardano smart contract language and toolchain" -version = "0.0.11" +version = "0.0.13" edition = "2021" repository = "https://github.com/txpipe/aiken" homepage = "https://github.com/txpipe/aiken" @@ -13,4 +13,12 @@ authors = ["Lucas Rosa ", "Kasey White "] [dependencies] anyhow = "1.0.57" clap = { version = "3.1.14", features = ["derive"] } -uplc = { path = '../uplc', version = "0.0.11" } +hex = "0.4.3" +pallas-addresses = "0.14.0-alpha.3" +pallas-codec = "0.14.0-alpha.3" +pallas-crypto = "0.14.0-alpha.3" +pallas-primitives = "0.14.0-alpha.3" +pallas-traverse = "0.14.0-alpha.3" +serde = { version = "1.0.144", features = ["derive"] } +serde_json = "1.0.85" +uplc = { path = '../uplc', version = "0.0.13" } diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs index 8e60cbf7..ef4c92ac 100644 --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -16,22 +16,58 @@ pub enum Args { /// Project name name: PathBuf, }, + /// A subcommand for working with transactions + #[clap(subcommand)] + Tx(TxCommand), /// A subcommand for working with Untyped Plutus Core #[clap(subcommand)] Uplc(UplcCommand), } +/// Commands for working with transactions +#[derive(Subcommand)] +pub enum TxCommand { + /// Simulate a transaction by evaluating it's script + Simulate { + /// A file containing cbor hex for a transaction + input: PathBuf, + + /// Toggle whether input is raw cbor or a hex string + #[clap(short, long)] + cbor: bool, + + /// A file containing cbor hex for the raw inputs + raw_inputs: PathBuf, + + /// A file containing cbor hex for the raw outputs + raw_outputs: PathBuf, + + /// Time between each slot + #[clap(short, long, default_value_t = 1000)] + slot_length: u64, + + /// Time of shelley hardfork + #[clap(long, default_value_t = 1596059091000)] + zero_time: u64, + + /// Slot number at the start of the shelley hardfork + #[clap(long, default_value_t = 4492800)] + zero_slot: u64, + }, +} + /// Commands for working with Untyped Plutus Core #[derive(Subcommand)] pub enum UplcCommand { /// Evaluate an Untyped Plutus Core program Eval { - /// Handle input as flat bytes + script: PathBuf, + #[clap(short, long)] flat: bool, - /// File to load and evaluate - input: PathBuf, + /// Arguments to pass to the uplc program + args: Vec, }, /// Encode textual Untyped Plutus Core to flat bytes Flat { diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index bd973694..78a7b92b 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,14 +1,23 @@ use std::{fmt::Write as _, fs}; +use pallas_primitives::{ + babbage::{TransactionInput, TransactionOutput}, + Fragment, +}; +use pallas_traverse::{Era, MultiEraTx}; use uplc::{ ast::{DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term}, machine::cost_model::ExBudget, parser, + tx::{ + self, + script_context::{ResolvedInput, SlotConfig}, + }, }; mod args; -use args::{Args, UplcCommand}; +use args::{Args, TxCommand, UplcCommand}; fn main() -> anyhow::Result<()> { let args = Args::default(); @@ -39,38 +48,142 @@ fn main() -> anyhow::Result<()> { } } - Args::Uplc(uplc) => match uplc { - UplcCommand::Flat { input, print, out } => { + Args::Tx(tx_cmd) => match tx_cmd { + TxCommand::Simulate { + input, + cbor, + raw_inputs, + raw_outputs, + slot_length, + zero_time, + zero_slot, + } => { + let (tx_bytes, inputs_bytes, outputs_bytes) = if cbor { + ( + fs::read(input)?, + fs::read(raw_inputs)?, + fs::read(raw_outputs)?, + ) + } else { + let cbor_hex = fs::read_to_string(input)?; + let inputs_hex = fs::read_to_string(raw_inputs)?; + let outputs_hex = fs::read_to_string(raw_outputs)?; + + ( + hex::decode(cbor_hex.trim())?, + hex::decode(inputs_hex.trim())?, + hex::decode(outputs_hex.trim())?, + ) + }; + + let tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes))?; + + let inputs = Vec::::decode_fragment(&inputs_bytes).unwrap(); + let outputs = Vec::::decode_fragment(&outputs_bytes).unwrap(); + + let resolved_inputs: Vec = inputs + .iter() + .zip(outputs.iter()) + .map(|(input, output)| ResolvedInput { + input: input.clone(), + output: output.clone(), + }) + .collect(); + + println!("Simulating: {}", tx.hash()); + + if let Some(tx_babbage) = tx.as_babbage() { + let slot_config = SlotConfig { + zero_time, + zero_slot, + slot_length, + }; + + let result = tx::eval_phase_two( + tx_babbage, + &resolved_inputs, + None, + None, + &slot_config, + true, + ); + + match result { + Ok(redeemers) => { + println!("\nTotal Budget Used\n-----------------\n"); + + let total_budget_used = redeemers.iter().fold( + ExBudget { mem: 0, cpu: 0 }, + |accum, curr| ExBudget { + mem: accum.mem + curr.ex_units.mem as i64, + cpu: accum.cpu + curr.ex_units.steps as i64, + }, + ); + + println!("mem: {}", total_budget_used.mem); + println!("cpu: {}", total_budget_used.cpu); + } + Err(err) => { + eprintln!("\nError\n-----\n\n{}\n", err); + } + } + } + } + }, + Args::Uplc(uplc_cmd) => match uplc_cmd { + UplcCommand::Flat { + input, + print, + out, + cbor_hex, + } => { let code = std::fs::read_to_string(&input)?; let program = parser::program(&code)?; let program = Program::::try_from(program)?; - let bytes = program.to_flat()?; + if cbor_hex { + let bytes = program.to_flat()?; - if print { - let mut output = String::new(); + if print { + let mut output = String::new(); - for (i, byte) in bytes.iter().enumerate() { - let _ = write!(output, "{:08b}", byte); + for (i, byte) in bytes.iter().enumerate() { + let _ = write!(output, "{:08b}", byte); - if (i + 1) % 4 == 0 { - output.push('\n'); - } else { - output.push(' '); + if (i + 1) % 4 == 0 { + output.push('\n'); + } else { + output.push(' '); + } } - } - println!("{}", output); - } else { - let out_name = if let Some(out) = out { - out + println!("{}", output); } else { - format!("{}.flat", input.file_stem().unwrap().to_str().unwrap()) - }; + let out_name = if let Some(out) = out { + out + } else { + format!("{}.flat", input.file_stem().unwrap().to_str().unwrap()) + }; - fs::write(&out_name, &bytes)?; + fs::write(&out_name, &bytes)?; + } + } else { + let cbor = program.to_hex()?; + + if print { + println!("{}", &cbor); + } else { + let out_name = if let Some(out) = out { + out + } else { + format!("{}.cbor", input.file_stem().unwrap().to_str().unwrap()) + }; + + fs::write(&out_name, &cbor)?; + } } } @@ -87,11 +200,24 @@ fn main() -> anyhow::Result<()> { fs::write(&input, pretty)?; } } + UplcCommand::Unflat { + input, + print, + out, + cbor_hex, + } => { + let program = if cbor_hex { + let cbor = std::fs::read_to_string(&input)?; - UplcCommand::Unflat { input, print, out } => { - let bytes = std::fs::read(&input)?; + let mut cbor_buffer = Vec::new(); + let mut flat_buffer = Vec::new(); - let program = Program::::from_flat(&bytes)?; + Program::::from_hex(cbor.trim(), &mut cbor_buffer, &mut flat_buffer)? + } else { + let bytes = std::fs::read(&input)?; + + Program::::from_flat(&bytes)? + }; let program: Program = program.try_into()?; @@ -110,21 +236,27 @@ fn main() -> anyhow::Result<()> { } } - UplcCommand::Eval { input, flat } => { - let program = if flat { - let bytes = std::fs::read(&input)?; + UplcCommand::Eval { script, flat, args } => { + let mut program = if flat { + let bytes = std::fs::read(&script)?; let prog = Program::::from_flat(&bytes)?; prog.into() } else { - let code = std::fs::read_to_string(&input)?; + let code = std::fs::read_to_string(&script)?; let prog = parser::program(&code)?; Program::::try_from(prog)? }; + for arg in args { + let term: Term = parser::term(&arg)?.try_into()?; + + program = program.apply_term(&term); + } + let (term, cost, logs) = program.eval(); match term { @@ -149,7 +281,10 @@ fn main() -> anyhow::Result<()> { "\nBudget\n------\ncpu: {}\nmemory: {}\n", cost.cpu, cost.mem ); - println!("\nLogs\n----\n{}", logs.join("\n")) + + if !logs.is_empty() { + println!("\nLogs\n----\n{}", logs.join("\n")) + } } }, } diff --git a/crates/uplc/Cargo.toml b/crates/uplc/Cargo.toml index 2923d38b..a3a4131a 100644 --- a/crates/uplc/Cargo.toml +++ b/crates/uplc/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "uplc" description = "Utilities for working with Untyped Plutus Core" -version = "0.0.11" +version = "0.0.13" edition = "2021" repository = "https://github.com/txpipe/aiken/crates/uplc" homepage = "https://github.com/txpipe/aiken" @@ -16,12 +16,17 @@ exclude = ["test_data/*"] cryptoxide = "0.4.2" flat-rs = { path = "../flat", version = "0.0.10" } hex = "0.4.3" -minicbor = { version = "0.18.0", features = ["std"] } -pallas-codec = "0.12.0" -pallas-primitives = "0.12.0" +pallas-addresses = "0.14.0-alpha.3" +pallas-codec = "0.14.0-alpha.3" +pallas-crypto = "0.14.0-alpha.3" +pallas-primitives = "0.14.0-alpha.3" +pallas-traverse = "0.14.0-alpha.3" peg = "0.8.0" pretty = "0.11.3" thiserror = "1.0.31" +anyhow = "1.0.57" +serde = { version = "1.0.144", features = ["derive"] } +serde_json = "1.0.85" [dev-dependencies] hex = "0.4.3" diff --git a/crates/uplc/src/ast.rs b/crates/uplc/src/ast.rs index 97d84333..f1c5b81d 100644 --- a/crates/uplc/src/ast.rs +++ b/crates/uplc/src/ast.rs @@ -1,13 +1,13 @@ use std::{fmt::Display, rc::Rc}; -use pallas_primitives::alonzo::PlutusData; +use pallas_primitives::{alonzo::PlutusData, babbage::Language}; use crate::{ builtins::DefaultFunction, debruijn::{self, Converter}, flat::Binder, machine::{ - cost_model::{CostModel, ExBudget}, + cost_model::{initialize_cost_model, CostModel, ExBudget}, Machine, }, }; @@ -39,6 +39,33 @@ where term: applied_term, } } + + /// We use this to apply the validator to Datum, + /// then redeemer, then ScriptContext. If datum is + /// even necessary (i.e. minting policy). + pub fn apply_term(&self, term: &Term) -> Self { + let applied_term = Term::Apply { + function: Rc::new(self.term.clone()), + argument: Rc::new(term.clone()), + }; + + Program { + version: self.version, + term: applied_term, + } + } + + pub fn apply_data(&self, plutus_data: PlutusData) -> Self { + let applied_term = Term::Apply { + function: Rc::new(self.term.clone()), + argument: Rc::new(Term::Constant(Constant::Data(plutus_data))), + }; + + Program { + version: self.version, + term: applied_term, + } + } } impl<'a, T> Display for Program @@ -461,7 +488,54 @@ impl Program { ExBudget, Vec, ) { - let mut machine = Machine::new(CostModel::default(), ExBudget::default(), 200); + let mut machine = Machine::new( + Language::PlutusV2, + CostModel::default(), + ExBudget::default(), + 200, + ); + + let term = machine.run(&self.term); + + (term, machine.ex_budget, machine.logs) + } + + /// Evaluate a Program as PlutusV1 + pub fn eval_v1( + &self, + ) -> ( + Result, crate::machine::Error>, + ExBudget, + Vec, + ) { + let mut machine = Machine::new(Language::PlutusV1, CostModel::v1(), ExBudget::v1(), 200); + + let term = machine.run(&self.term); + + (term, machine.ex_budget, machine.logs) + } + + pub fn eval_as( + &self, + version: &Language, + costs: &[i64], + initial_budget: Option<&ExBudget>, + ) -> ( + Result, crate::machine::Error>, + ExBudget, + Vec, + ) { + let budget = match initial_budget { + Some(b) => *b, + None => ExBudget::default(), + }; + + let mut machine = Machine::new( + version.clone(), + initialize_cost_model(version, costs), + budget, + 200, //slippage + ); let term = machine.run(&self.term); @@ -482,3 +556,9 @@ impl Program { program.eval() } } + +impl Term { + pub fn is_valid_script_result(&self) -> bool { + !matches!(self, Term::Error) + } +} diff --git a/crates/uplc/src/flat.rs b/crates/uplc/src/flat.rs index 161751f8..dd766547 100644 --- a/crates/uplc/src/flat.rs +++ b/crates/uplc/src/flat.rs @@ -31,8 +31,11 @@ where T: Binder<'b> + Debug, { pub fn from_cbor(bytes: &'b [u8], buffer: &'b mut Vec) -> Result { - let flat_bytes: Vec = - minicbor::decode(bytes).map_err(|err| de::Error::Message(err.to_string()))?; + let mut cbor_decoder = pallas_codec::minicbor::Decoder::new(bytes); + + let flat_bytes = cbor_decoder + .bytes() + .map_err(|err| de::Error::Message(err.to_string()))?; buffer.extend(flat_bytes); @@ -58,7 +61,15 @@ where pub fn to_cbor(&self) -> Result, en::Error> { let flat_bytes = self.flat()?; - minicbor::to_vec(&flat_bytes).map_err(|err| en::Error::Message(err.to_string())) + let mut bytes = Vec::new(); + + let mut cbor_encoder = pallas_codec::minicbor::Encoder::new(&mut bytes); + + cbor_encoder + .bytes(&flat_bytes) + .map_err(|err| en::Error::Message(err.to_string()))?; + + Ok(bytes) } // convenient so that people don't need to depend on the flat crate @@ -179,8 +190,10 @@ where 6 => Ok(Term::Error), 7 => Ok(Term::Builtin(DefaultFunction::decode(d)?)), x => Err(de::Error::Message(format!( - "Unknown term constructor tag: {}", - x + "Unknown term constructor tag: {} and buffer position is {} and buffer length is {}", + x, + d.buffer.len() - d.pos, + d.buffer.len() ))), } } diff --git a/crates/uplc/src/lib.rs b/crates/uplc/src/lib.rs index 64ee0971..55b107e7 100644 --- a/crates/uplc/src/lib.rs +++ b/crates/uplc/src/lib.rs @@ -6,10 +6,11 @@ pub mod machine; pub mod parser; mod pretty; pub mod program_builder; +pub mod tx; pub use pallas_primitives::alonzo::PlutusData; -pub type Error = Box; -use pallas_primitives::Fragment; + +use pallas_primitives::{Error, Fragment}; pub fn plutus_data(bytes: &[u8]) -> Result { PlutusData::decode_fragment(bytes) diff --git a/crates/uplc/src/machine.rs b/crates/uplc/src/machine.rs index 0394d737..8740699a 100644 --- a/crates/uplc/src/machine.rs +++ b/crates/uplc/src/machine.rs @@ -11,7 +11,7 @@ mod runtime; use cost_model::{ExBudget, StepKind}; pub use error::Error; -use pallas_primitives::babbage::{BigInt, PlutusData}; +use pallas_primitives::babbage::{BigInt, Language, PlutusData}; use self::{cost_model::CostModel, runtime::BuiltinRuntime}; @@ -39,10 +39,16 @@ pub struct Machine { unbudgeted_steps: [u32; 8], pub logs: Vec, stack: Vec, + version: Language, } impl Machine { - pub fn new(costs: CostModel, initial_budget: ExBudget, slippage: u32) -> Machine { + pub fn new( + version: Language, + costs: CostModel, + initial_budget: ExBudget, + slippage: u32, + ) -> Machine { Machine { costs, ex_budget: initial_budget, @@ -50,6 +56,7 @@ impl Machine { unbudgeted_steps: [0; 8], logs: vec![], stack: vec![], + version, } } @@ -347,8 +354,10 @@ impl Machine { runtime: BuiltinRuntime, ) -> Result { if runtime.is_ready() { - let cost = runtime.to_ex_budget(&self.costs.builtin_costs); - + let cost = match self.version { + Language::PlutusV1 => runtime.to_ex_budget_v1(&self.costs.builtin_costs), + Language::PlutusV2 => runtime.to_ex_budget_v2(&self.costs.builtin_costs), + }; self.spend_budget(cost)?; runtime.call(&mut self.logs) @@ -448,7 +457,13 @@ impl Value { ((i.abs() as f64).log2().floor() as i64 / 64) + 1 } } - Constant::ByteString(b) => (((b.len() as i64 - 1) / 8) + 1), + Constant::ByteString(b) => { + if b.is_empty() { + 1 + } else { + ((b.len() as i64 - 1) / 8) + 1 + } + } Constant::String(s) => s.chars().count() as i64, Constant::Unit => 1, Constant::Bool(_) => 1, @@ -488,7 +503,7 @@ impl Value { PlutusData::Map(m) => { let mut new_stack: VecDeque<&PlutusData>; // create new stack with of items from the list of pairs of data - new_stack = m.deref().iter().fold(VecDeque::new(), |mut acc, d| { + new_stack = m.iter().fold(VecDeque::new(), |mut acc, d| { acc.push_back(&d.0); acc.push_back(&d.1); acc @@ -499,7 +514,7 @@ impl Value { } PlutusData::BigInt(i) => { if let BigInt::Int(g) = i { - let numb: i64 = (*g).try_into().unwrap(); + let numb: i128 = (*g).try_into().unwrap(); total += Value::Con(Constant::Integer(numb as isize)).to_ex_mem(); } else { unreachable!() @@ -517,14 +532,6 @@ impl Value { new_stack.append(&mut stack); stack = new_stack; } - PlutusData::ArrayIndef(a) => { - // create new stack with of items from the list of data - let mut new_stack: VecDeque<&PlutusData> = - VecDeque::from_iter(a.deref().iter()); - // Append old stack to the back of the new stack - new_stack.append(&mut stack); - stack = new_stack; - } } } total diff --git a/crates/uplc/src/machine/cost_model.rs b/crates/uplc/src/machine/cost_model.rs index 78ddcf42..98be17e7 100644 --- a/crates/uplc/src/machine/cost_model.rs +++ b/crates/uplc/src/machine/cost_model.rs @@ -1,7 +1,22 @@ +use std::collections::HashMap; + +use pallas_primitives::babbage::Language; + use crate::builtins::DefaultFunction; use super::Value; +macro_rules! hashmap { + // map-like + ($($k:expr => $v:expr),* $(,)?) => {{ + core::convert::From::from([$(($k, $v),)*]) + }}; + // set-like + ($($v:expr),* $(,)?) => {{ + core::convert::From::from([$($v,)*]) + }}; +} + /// Can be negative #[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct ExBudget { @@ -14,6 +29,13 @@ impl ExBudget { self.mem *= n; self.cpu *= n; } + + pub fn v1() -> Self { + ExBudget { + mem: 14000000, + cpu: 10000000000, + } + } } impl Default for ExBudget { @@ -31,6 +53,15 @@ pub struct CostModel { pub builtin_costs: BuiltinCosts, } +impl CostModel { + pub fn v1() -> Self { + Self { + machine_costs: MachineCosts::v1(), + builtin_costs: BuiltinCosts::v1(), + } + } +} + /// There's no entry for Error since we'll be exiting anyway; also, what would /// happen if calling 'Error' caused the budget to be exceeded? pub struct MachineCosts { @@ -59,9 +90,44 @@ impl MachineCosts { StepKind::StartUp => self.startup, } } + + pub fn v1() -> Self { + Self { + startup: ExBudget { mem: 100, cpu: 100 }, + var: ExBudget { + mem: 100, + cpu: 23000, + }, + constant: ExBudget { + mem: 100, + cpu: 23000, + }, + lambda: ExBudget { + mem: 100, + cpu: 23000, + }, + delay: ExBudget { + mem: 100, + cpu: 23000, + }, + force: ExBudget { + mem: 100, + cpu: 23000, + }, + apply: ExBudget { + mem: 100, + cpu: 23000, + }, + builtin: ExBudget { + mem: 100, + cpu: 23000, + }, + } + } } impl Default for MachineCosts { + /// Default is V2 fn default() -> Self { Self { startup: ExBudget { mem: 100, cpu: 100 }, @@ -164,7 +230,373 @@ pub struct BuiltinCosts { pub serialise_data: CostingFun, } +impl BuiltinCosts { + pub fn v1() -> Self { + Self { + add_integer: CostingFun { + mem: TwoArguments::MaxSize(MaxSize { + intercept: 1, + slope: 1, + }), + cpu: TwoArguments::MaxSize(MaxSize { + intercept: 205665, + slope: 812, + }), + }, + subtract_integer: CostingFun { + mem: TwoArguments::MaxSize(MaxSize { + intercept: 1, + slope: 1, + }), + cpu: TwoArguments::MaxSize(MaxSize { + intercept: 205665, + slope: 812, + }), + }, + multiply_integer: CostingFun { + mem: TwoArguments::AddedSizes(AddedSizes { + intercept: 0, + slope: 1, + }), + + cpu: TwoArguments::AddedSizes(AddedSizes { + intercept: 69522, + slope: 11687, + }), + }, + divide_integer: CostingFun { + mem: TwoArguments::SubtractedSizes(SubtractedSizes { + intercept: 0, + slope: 1, + minimum: 1, + }), + cpu: TwoArguments::ConstAboveDiagonal(ConstantOrTwoArguments { + constant: 196500, + model: Box::new(TwoArguments::MultipliedSizes(MultipliedSizes { + intercept: 453240, + slope: 220, + })), + }), + }, + quotient_integer: CostingFun { + mem: TwoArguments::SubtractedSizes(SubtractedSizes { + intercept: 0, + slope: 1, + minimum: 1, + }), + cpu: TwoArguments::ConstAboveDiagonal(ConstantOrTwoArguments { + constant: 196500, + model: Box::new(TwoArguments::MultipliedSizes(MultipliedSizes { + intercept: 453240, + slope: 220, + })), + }), + }, + remainder_integer: CostingFun { + mem: TwoArguments::SubtractedSizes(SubtractedSizes { + intercept: 0, + slope: 1, + minimum: 1, + }), + cpu: TwoArguments::ConstAboveDiagonal(ConstantOrTwoArguments { + constant: 196500, + model: Box::new(TwoArguments::MultipliedSizes(MultipliedSizes { + intercept: 453240, + slope: 220, + })), + }), + }, + mod_integer: CostingFun { + mem: TwoArguments::SubtractedSizes(SubtractedSizes { + intercept: 0, + slope: 1, + minimum: 1, + }), + cpu: TwoArguments::ConstAboveDiagonal(ConstantOrTwoArguments { + constant: 196500, + model: Box::new(TwoArguments::MultipliedSizes(MultipliedSizes { + intercept: 453240, + slope: 220, + })), + }), + }, + equals_integer: CostingFun { + mem: TwoArguments::ConstantCost(1), + cpu: TwoArguments::MinSize(MinSize { + intercept: 208512, + slope: 421, + }), + }, + less_than_integer: CostingFun { + mem: TwoArguments::ConstantCost(1), + cpu: TwoArguments::MinSize(MinSize { + intercept: 208896, + slope: 511, + }), + }, + less_than_equals_integer: CostingFun { + mem: TwoArguments::ConstantCost(1), + cpu: TwoArguments::MinSize(MinSize { + intercept: 204924, + slope: 473, + }), + }, + append_byte_string: CostingFun { + mem: TwoArguments::AddedSizes(AddedSizes { + intercept: 0, + slope: 1, + }), + cpu: TwoArguments::AddedSizes(AddedSizes { + intercept: 1000, + slope: 571, + }), + }, + cons_byte_string: CostingFun { + mem: TwoArguments::AddedSizes(AddedSizes { + intercept: 0, + slope: 1, + }), + cpu: TwoArguments::LinearInY(LinearSize { + intercept: 221973, + slope: 511, + }), + }, + slice_byte_string: CostingFun { + mem: ThreeArguments::LinearInZ(LinearSize { + intercept: 4, + slope: 0, + }), + cpu: ThreeArguments::LinearInZ(LinearSize { + intercept: 265318, + slope: 0, + }), + }, + length_of_byte_string: CostingFun { + mem: OneArgument::ConstantCost(10), + cpu: OneArgument::ConstantCost(1000), + }, + index_byte_string: CostingFun { + mem: TwoArguments::ConstantCost(4), + cpu: TwoArguments::ConstantCost(57667), + }, + equals_byte_string: CostingFun { + mem: TwoArguments::ConstantCost(1), + cpu: TwoArguments::LinearOnDiagonal(ConstantOrLinear { + constant: 245000, + intercept: 216773, + slope: 62, + }), + }, + less_than_byte_string: CostingFun { + mem: TwoArguments::ConstantCost(1), + cpu: TwoArguments::MinSize(MinSize { + intercept: 197145, + slope: 156, + }), + }, + less_than_equals_byte_string: CostingFun { + mem: TwoArguments::ConstantCost(1), + cpu: TwoArguments::MinSize(MinSize { + intercept: 197145, + slope: 156, + }), + }, + sha2_256: CostingFun { + mem: OneArgument::ConstantCost(4), + cpu: OneArgument::LinearCost(LinearSize { + intercept: 806990, + slope: 30482, + }), + }, + sha3_256: CostingFun { + mem: OneArgument::ConstantCost(4), + cpu: OneArgument::LinearCost(LinearSize { + intercept: 1927926, + slope: 82523, + }), + }, + blake2b_256: CostingFun { + mem: OneArgument::ConstantCost(4), + cpu: OneArgument::LinearCost(LinearSize { + intercept: 117366, + slope: 10475, + }), + }, + verify_ed25519_signature: CostingFun { + mem: ThreeArguments::ConstantCost(10), + cpu: ThreeArguments::LinearInZ(LinearSize { + intercept: 57996947, + slope: 18975, + }), + }, + verify_ecdsa_secp256k1_signature: CostingFun { + mem: ThreeArguments::ConstantCost(20000000000), + cpu: ThreeArguments::ConstantCost(20000000000), + }, + verify_schnorr_secp256k1_signature: CostingFun { + mem: ThreeArguments::ConstantCost(20000000000), + cpu: ThreeArguments::LinearInY(LinearSize { + intercept: 20000000000, + slope: 0, + }), + }, + append_string: CostingFun { + mem: TwoArguments::AddedSizes(AddedSizes { + intercept: 4, + slope: 1, + }), + cpu: TwoArguments::AddedSizes(AddedSizes { + intercept: 1000, + slope: 24177, + }), + }, + equals_string: CostingFun { + mem: TwoArguments::ConstantCost(1), + cpu: TwoArguments::LinearOnDiagonal(ConstantOrLinear { + constant: 187000, + intercept: 1000, + slope: 52998, + }), + }, + encode_utf8: CostingFun { + mem: OneArgument::LinearCost(LinearSize { + intercept: 4, + slope: 2, + }), + cpu: OneArgument::LinearCost(LinearSize { + intercept: 1000, + slope: 28662, + }), + }, + decode_utf8: CostingFun { + mem: OneArgument::LinearCost(LinearSize { + intercept: 4, + slope: 2, + }), + cpu: OneArgument::LinearCost(LinearSize { + intercept: 497525, + slope: 14068, + }), + }, + if_then_else: CostingFun { + mem: ThreeArguments::ConstantCost(1), + cpu: ThreeArguments::ConstantCost(80556), + }, + choose_unit: CostingFun { + mem: TwoArguments::ConstantCost(4), + cpu: TwoArguments::ConstantCost(46417), + }, + trace: CostingFun { + mem: TwoArguments::ConstantCost(32), + cpu: TwoArguments::ConstantCost(212342), + }, + fst_pair: CostingFun { + mem: OneArgument::ConstantCost(32), + cpu: OneArgument::ConstantCost(80436), + }, + snd_pair: CostingFun { + mem: OneArgument::ConstantCost(32), + cpu: OneArgument::ConstantCost(85931), + }, + choose_list: CostingFun { + mem: ThreeArguments::ConstantCost(32), + cpu: ThreeArguments::ConstantCost(175354), + }, + mk_cons: CostingFun { + mem: TwoArguments::ConstantCost(32), + cpu: TwoArguments::ConstantCost(65493), + }, + head_list: CostingFun { + mem: OneArgument::ConstantCost(32), + cpu: OneArgument::ConstantCost(43249), + }, + tail_list: CostingFun { + mem: OneArgument::ConstantCost(32), + cpu: OneArgument::ConstantCost(41182), + }, + null_list: CostingFun { + mem: OneArgument::ConstantCost(32), + cpu: OneArgument::ConstantCost(60091), + }, + choose_data: CostingFun { + mem: SixArguments::ConstantCost(32), + cpu: SixArguments::ConstantCost(19537), + }, + constr_data: CostingFun { + mem: TwoArguments::ConstantCost(32), + cpu: TwoArguments::ConstantCost(89141), + }, + map_data: CostingFun { + mem: OneArgument::ConstantCost(32), + cpu: OneArgument::ConstantCost(64832), + }, + list_data: CostingFun { + mem: OneArgument::ConstantCost(32), + cpu: OneArgument::ConstantCost(52467), + }, + i_data: CostingFun { + mem: OneArgument::ConstantCost(32), + cpu: OneArgument::ConstantCost(1000), + }, + b_data: CostingFun { + mem: OneArgument::ConstantCost(32), + cpu: OneArgument::ConstantCost(1000), + }, + un_constr_data: CostingFun { + mem: OneArgument::ConstantCost(32), + cpu: OneArgument::ConstantCost(32696), + }, + un_map_data: CostingFun { + mem: OneArgument::ConstantCost(32), + cpu: OneArgument::ConstantCost(38314), + }, + un_list_data: CostingFun { + mem: OneArgument::ConstantCost(32), + cpu: OneArgument::ConstantCost(32247), + }, + un_i_data: CostingFun { + mem: OneArgument::ConstantCost(32), + cpu: OneArgument::ConstantCost(43357), + }, + un_b_data: CostingFun { + mem: OneArgument::ConstantCost(32), + cpu: OneArgument::ConstantCost(31220), + }, + equals_data: CostingFun { + mem: TwoArguments::ConstantCost(1), + cpu: TwoArguments::MinSize(MinSize { + intercept: 1060367, + slope: 12586, + }), + }, + mk_pair_data: CostingFun { + mem: TwoArguments::ConstantCost(32), + cpu: TwoArguments::ConstantCost(76511), + }, + mk_nil_data: CostingFun { + mem: OneArgument::ConstantCost(32), + cpu: OneArgument::ConstantCost(22558), + }, + mk_nil_pair_data: CostingFun { + mem: OneArgument::ConstantCost(32), + cpu: OneArgument::ConstantCost(16563), + }, + serialise_data: CostingFun { + mem: OneArgument::LinearCost(LinearSize { + intercept: 30000000000, + slope: 2, + }), + cpu: OneArgument::LinearCost(LinearSize { + intercept: 1159724, + slope: 392670, + }), + }, + } + } +} + impl Default for BuiltinCosts { + /// Default is V2 fn default() -> Self { Self { add_integer: CostingFun { @@ -364,14 +796,14 @@ impl Default for BuiltinCosts { }), }, verify_ecdsa_secp256k1_signature: CostingFun { - mem: ThreeArguments::ConstantCost(10), - cpu: ThreeArguments::ConstantCost(35190005), + mem: ThreeArguments::ConstantCost(20000000000), + cpu: ThreeArguments::ConstantCost(20000000000), }, verify_schnorr_secp256k1_signature: CostingFun { - mem: ThreeArguments::ConstantCost(10), + mem: ThreeArguments::ConstantCost(20000000000), cpu: ThreeArguments::LinearInY(LinearSize { - intercept: 39121781, - slope: 32260, + intercept: 20000000000, + slope: 0, }), }, append_string: CostingFun { @@ -530,7 +962,7 @@ impl Default for BuiltinCosts { } impl BuiltinCosts { - pub fn to_ex_budget(&self, fun: DefaultFunction, args: &[Value]) -> ExBudget { + pub fn to_ex_budget_v2(&self, fun: DefaultFunction, args: &[Value]) -> ExBudget { match fun { DefaultFunction::AddInteger => ExBudget { mem: self @@ -934,6 +1366,1629 @@ impl BuiltinCosts { }, } } + + pub fn to_ex_budget_v1(&self, fun: DefaultFunction, args: &[Value]) -> ExBudget { + match fun { + DefaultFunction::AddInteger => ExBudget { + mem: self + .add_integer + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .add_integer + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::SubtractInteger => ExBudget { + mem: self + .subtract_integer + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .subtract_integer + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::MultiplyInteger => ExBudget { + mem: self + .multiply_integer + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .multiply_integer + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::DivideInteger => ExBudget { + mem: self + .divide_integer + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .divide_integer + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::QuotientInteger => ExBudget { + mem: self + .quotient_integer + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .quotient_integer + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::RemainderInteger => ExBudget { + mem: self + .remainder_integer + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .remainder_integer + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::ModInteger => ExBudget { + mem: self + .mod_integer + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .mod_integer + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::EqualsInteger => ExBudget { + mem: self + .equals_integer + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .equals_integer + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::LessThanInteger => ExBudget { + mem: self + .less_than_integer + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .less_than_integer + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::LessThanEqualsInteger => ExBudget { + mem: self + .less_than_equals_integer + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .less_than_equals_integer + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::AppendByteString => ExBudget { + mem: self + .append_byte_string + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .append_byte_string + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::ConsByteString => ExBudget { + mem: self + .cons_byte_string + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .cons_byte_string + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::SliceByteString => ExBudget { + mem: self.slice_byte_string.mem.cost( + args[0].to_ex_mem(), + args[1].to_ex_mem(), + args[2].to_ex_mem(), + ), + cpu: self.slice_byte_string.cpu.cost( + args[0].to_ex_mem(), + args[1].to_ex_mem(), + args[2].to_ex_mem(), + ), + }, + DefaultFunction::LengthOfByteString => ExBudget { + mem: self.length_of_byte_string.mem.cost(args[0].to_ex_mem()), + cpu: self.length_of_byte_string.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::IndexByteString => ExBudget { + mem: self + .index_byte_string + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .index_byte_string + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::EqualsByteString => ExBudget { + mem: self + .equals_byte_string + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .equals_byte_string + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::LessThanByteString => ExBudget { + mem: self + .less_than_byte_string + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .less_than_byte_string + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::LessThanEqualsByteString => ExBudget { + mem: self + .less_than_equals_byte_string + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .less_than_equals_byte_string + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::Sha2_256 => ExBudget { + mem: self.sha2_256.mem.cost(args[0].to_ex_mem()), + cpu: self.sha2_256.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::Sha3_256 => ExBudget { + mem: self.sha3_256.mem.cost(args[0].to_ex_mem()), + cpu: self.sha3_256.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::Blake2b_256 => ExBudget { + mem: self.blake2b_256.mem.cost(args[0].to_ex_mem()), + cpu: self.blake2b_256.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::VerifyEd25519Signature => ExBudget { + mem: self.verify_ed25519_signature.mem.cost( + args[0].to_ex_mem(), + args[1].to_ex_mem(), + args[2].to_ex_mem(), + ), + cpu: self.verify_ed25519_signature.cpu.cost( + args[0].to_ex_mem(), + args[1].to_ex_mem(), + args[2].to_ex_mem(), + ), + }, + DefaultFunction::VerifyEcdsaSecp256k1Signature => unreachable!(), + DefaultFunction::VerifySchnorrSecp256k1Signature => unreachable!(), + DefaultFunction::AppendString => ExBudget { + mem: self + .append_string + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .append_string + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::EqualsString => ExBudget { + mem: self + .equals_string + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .equals_string + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::EncodeUtf8 => ExBudget { + mem: self.encode_utf8.mem.cost(args[0].to_ex_mem()), + cpu: self.encode_utf8.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::DecodeUtf8 => ExBudget { + mem: self.decode_utf8.mem.cost(args[0].to_ex_mem()), + cpu: self.decode_utf8.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::IfThenElse => ExBudget { + mem: self.if_then_else.mem.cost( + args[0].to_ex_mem(), + args[1].to_ex_mem(), + args[2].to_ex_mem(), + ), + cpu: self.if_then_else.cpu.cost( + args[0].to_ex_mem(), + args[1].to_ex_mem(), + args[2].to_ex_mem(), + ), + }, + DefaultFunction::ChooseUnit => ExBudget { + mem: self + .choose_unit + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .choose_unit + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::Trace => ExBudget { + mem: self + .trace + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .trace + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::FstPair => ExBudget { + mem: self.fst_pair.mem.cost(args[0].to_ex_mem()), + cpu: self.fst_pair.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::SndPair => ExBudget { + mem: self.snd_pair.mem.cost(args[0].to_ex_mem()), + cpu: self.snd_pair.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::ChooseList => ExBudget { + mem: self.choose_list.mem.cost( + args[0].to_ex_mem(), + args[1].to_ex_mem(), + args[2].to_ex_mem(), + ), + cpu: self.choose_list.cpu.cost( + args[0].to_ex_mem(), + args[1].to_ex_mem(), + args[2].to_ex_mem(), + ), + }, + DefaultFunction::MkCons => ExBudget { + mem: self + .mk_cons + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .mk_cons + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::HeadList => ExBudget { + mem: self.head_list.mem.cost(args[0].to_ex_mem()), + cpu: self.head_list.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::TailList => ExBudget { + mem: self.tail_list.mem.cost(args[0].to_ex_mem()), + cpu: self.tail_list.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::NullList => ExBudget { + mem: self.null_list.mem.cost(args[0].to_ex_mem()), + cpu: self.null_list.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::ChooseData => ExBudget { + mem: self.choose_data.mem.cost( + args[0].to_ex_mem(), + args[1].to_ex_mem(), + args[2].to_ex_mem(), + args[3].to_ex_mem(), + args[4].to_ex_mem(), + args[5].to_ex_mem(), + ), + cpu: self.choose_data.cpu.cost( + args[0].to_ex_mem(), + args[1].to_ex_mem(), + args[2].to_ex_mem(), + args[3].to_ex_mem(), + args[4].to_ex_mem(), + args[5].to_ex_mem(), + ), + }, + DefaultFunction::ConstrData => ExBudget { + mem: self + .constr_data + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .constr_data + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::MapData => ExBudget { + mem: self.map_data.mem.cost(args[0].to_ex_mem()), + cpu: self.map_data.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::ListData => ExBudget { + mem: self.list_data.mem.cost(args[0].to_ex_mem()), + cpu: self.list_data.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::IData => ExBudget { + mem: self.i_data.mem.cost(args[0].to_ex_mem()), + cpu: self.i_data.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::BData => ExBudget { + mem: self.b_data.mem.cost(args[0].to_ex_mem()), + cpu: self.b_data.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::UnConstrData => ExBudget { + mem: self.un_constr_data.mem.cost(args[0].to_ex_mem()), + cpu: self.un_constr_data.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::UnMapData => ExBudget { + mem: self.un_map_data.mem.cost(args[0].to_ex_mem()), + cpu: self.un_map_data.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::UnListData => ExBudget { + mem: self.un_list_data.mem.cost(args[0].to_ex_mem()), + cpu: self.un_list_data.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::UnIData => ExBudget { + mem: self.un_i_data.mem.cost(args[0].to_ex_mem()), + cpu: self.un_i_data.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::UnBData => ExBudget { + mem: self.un_b_data.mem.cost(args[0].to_ex_mem()), + cpu: self.un_b_data.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::EqualsData => ExBudget { + mem: self + .equals_data + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .equals_data + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::SerialiseData => unreachable!(), + DefaultFunction::MkPairData => ExBudget { + mem: self + .mk_pair_data + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .mk_pair_data + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::MkNilData => ExBudget { + mem: self.mk_nil_data.mem.cost(args[0].to_ex_mem()), + cpu: self.mk_nil_data.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::MkNilPairData => ExBudget { + mem: self.mk_nil_pair_data.mem.cost(args[0].to_ex_mem()), + cpu: self.mk_nil_pair_data.cpu.cost(args[0].to_ex_mem()), + }, + } + } +} + +pub fn initialize_cost_model(version: &Language, costs: &[i64]) -> CostModel { + let cost_map: HashMap<&str, i64> = match version { + Language::PlutusV1 => { + hashmap! { + "add_integer-cpu-arguments-intercept" => costs[0], + "add_integer-cpu-arguments-slope" => costs[1], + "add_integer-mem-arguments-intercept" => costs[2], + "add_integer-mem-arguments-slope" => costs[3], + "append_byte_string-cpu-arguments-intercept" => costs[4], + "append_byte_string-cpu-arguments-slope" => costs[5], + "append_byte_string-mem-arguments-intercept" => costs[6], + "append_byte_string-mem-arguments-slope" => costs[7], + "append_string-cpu-arguments-intercept" => costs[8], + "append_string-cpu-arguments-slope" => costs[9], + "append_string-mem-arguments-intercept" => costs[10], + "append_string-mem-arguments-slope" => costs[11], + "b_data-cpu-arguments" => costs[12], + "b_data-mem-arguments" => costs[13], + "blake2b_256-cpu-arguments-intercept" => costs[14], + "blake2b_256-cpu-arguments-slope" => costs[15], + "blake2b_256-mem-arguments" => costs[16], + "cek_apply_cost-exBudgetCPU" => costs[17], + "cek_apply_cost-exBudgetmem" => costs[18], + "cek_builtin_cost-exBudgetCPU" => costs[19], + "cek_builtin_cost-exBudgetmem" => costs[20], + "cek_const_cost-exBudgetCPU" => costs[21], + "cek_const_cost-exBudgetmem" => costs[22], + "cek_delay_cost-exBudgetCPU" => costs[23], + "cek_delay_cost-exBudgetmem" => costs[24], + "cek_force_cost-exBudgetCPU" => costs[25], + "cek_force_cost-exBudgetmem" => costs[26], + "cek_lam_cost-exBudgetCPU" => costs[27], + "cek_lam_cost-exBudgetmem" => costs[28], + "cek_startup_cost-exBudgetCPU" => costs[29], + "cek_startup_cost-exBudgetmem" => costs[30], + "cek_var_cost-exBudgetCPU" => costs[31], + "cek_var_cost-exBudgetmem" => costs[32], + "choose_data-cpu-arguments" => costs[33], + "choose_data-mem-arguments" => costs[34], + "choose_list-cpu-arguments" => costs[35], + "choose_list-mem-arguments" => costs[36], + "choose_unit-cpu-arguments" => costs[37], + "choose_unit-mem-arguments" => costs[38], + "cons_byte_string-cpu-arguments-intercept" => costs[39], + "cons_byte_string-cpu-arguments-slope" => costs[40], + "cons_byte_string-mem-arguments-intercept" => costs[41], + "cons_byte_string-mem-arguments-slope" => costs[42], + "constr_data-cpu-arguments" => costs[43], + "constr_data-mem-arguments" => costs[44], + "decode_utf8-cpu-arguments-intercept" => costs[45], + "decode_utf8-cpu-arguments-slope" => costs[46], + "decode_utf8-mem-arguments-intercept" => costs[47], + "decode_utf8-mem-arguments-slope" => costs[48], + "divide_integer-cpu-arguments-constant" => costs[49], + "divide_integer-cpu-arguments-model-arguments-intercept" => costs[50], + "divide_integer-cpu-arguments-model-arguments-slope" => costs[51], + "divide_integer-mem-arguments-intercept" => costs[52], + "divide_integer-mem-arguments-minimum" => costs[53], + "divide_integer-mem-arguments-slope" => costs[54], + "encode_utf8-cpu-arguments-intercept" => costs[55], + "encode_utf8-cpu-arguments-slope" => costs[56], + "encode_utf8-mem-arguments-intercept" => costs[57], + "encode_utf8-mem-arguments-slope" => costs[58], + "equals_byte_string-cpu-arguments-constant" => costs[59], + "equals_byte_string-cpu-arguments-intercept" => costs[60], + "equals_byte_string-cpu-arguments-slope" => costs[61], + "equals_byte_string-mem-arguments" => costs[62], + "equals_data-cpu-arguments-intercept" => costs[63], + "equals_data-cpu-arguments-slope" => costs[64], + "equals_data-mem-arguments" => costs[65], + "equals_integer-cpu-arguments-intercept" => costs[66], + "equals_integer-cpu-arguments-slope" => costs[67], + "equals_integer-mem-arguments" => costs[68], + "equals_string-cpu-arguments-constant" => costs[69], + "equals_string-cpu-arguments-intercept" => costs[70], + "equals_string-cpu-arguments-slope" => costs[71], + "equals_string-mem-arguments" => costs[72], + "fst_pair-cpu-arguments" => costs[73], + "fst_pair-mem-arguments" => costs[74], + "head_list-cpu-arguments" => costs[75], + "head_list-mem-arguments" => costs[76], + "i_data-cpu-arguments" => costs[77], + "i_data-mem-arguments" => costs[78], + "if_then_else-cpu-arguments" => costs[79], + "if_then_else-mem-arguments" => costs[80], + "index_byte_string-cpu-arguments" => costs[81], + "index_byte_string-mem-arguments" => costs[82], + "length_of_byte_string-cpu-arguments" => costs[83], + "length_of_byte_string-mem-arguments" => costs[84], + "less_than_byte_string-cpu-arguments-intercept" => costs[85], + "less_than_byte_string-cpu-arguments-slope" => costs[86], + "less_than_byte_string-mem-arguments" => costs[87], + "less_than_equals_byte_string-cpu-arguments-intercept" => costs[88], + "less_than_equals_byte_string-cpu-arguments-slope" => costs[89], + "less_than_equals_byte_string-mem-arguments" => costs[90], + "less_than_equals_integer-cpu-arguments-intercept" => costs[91], + "less_than_equals_integer-cpu-arguments-slope" => costs[92], + "less_than_equals_integer-mem-arguments" => costs[93], + "less_than_integer-cpu-arguments-intercept" => costs[94], + "less_than_integer-cpu-arguments-slope" => costs[95], + "less_than_integer-mem-arguments" => costs[96], + "list_data-cpu-arguments" => costs[97], + "list_data-mem-arguments" => costs[98], + "map_data-cpu-arguments" => costs[99], + "map_data-mem-arguments" => costs[100], + "mk_cons-cpu-arguments" => costs[101], + "mk_cons-mem-arguments" => costs[102], + "mk_nil_data-cpu-arguments" => costs[103], + "mk_nil_data-mem-arguments" => costs[104], + "mk_nil_pair_data-cpu-arguments" => costs[105], + "mk_nil_pair_data-mem-arguments" => costs[106], + "mk_pair_data-cpu-arguments" => costs[107], + "mk_pair_data-mem-arguments" => costs[108], + "mod_integer-cpu-arguments-constant" => costs[109], + "mod_integer-cpu-arguments-model-arguments-intercept" => costs[110], + "mod_integer-cpu-arguments-model-arguments-slope" => costs[111], + "mod_integer-mem-arguments-intercept" => costs[112], + "mod_integer-mem-arguments-minimum" => costs[113], + "mod_integer-mem-arguments-slope" => costs[114], + "multiply_integer-cpu-arguments-intercept" => costs[115], + "multiply_integer-cpu-arguments-slope" => costs[116], + "multiply_integer-mem-arguments-intercept" => costs[117], + "multiply_integer-mem-arguments-slope" => costs[118], + "null_list-cpu-arguments" => costs[119], + "null_list-mem-arguments" => costs[120], + "quotient_integer-cpu-arguments-constant" => costs[121], + "quotient_integer-cpu-arguments-model-arguments-intercept" => costs[122], + "quotient_integer-cpu-arguments-model-arguments-slope" => costs[123], + "quotient_integer-mem-arguments-intercept" => costs[124], + "quotient_integer-mem-arguments-minimum" => costs[125], + "quotient_integer-mem-arguments-slope" => costs[126], + "remainder_integer-cpu-arguments-constant" => costs[127], + "remainder_integer-cpu-arguments-model-arguments-intercept" => costs[128], + "remainder_integer-cpu-arguments-model-arguments-slope" => costs[129], + "remainder_integer-mem-arguments-intercept" => costs[130], + "remainder_integer-mem-arguments-minimum" => costs[131], + "remainder_integer-mem-arguments-slope" => costs[132], + "sha2_256-cpu-arguments-intercept" => costs[133], + "sha2_256-cpu-arguments-slope" => costs[134], + "sha2_256-mem-arguments" => costs[135], + "sha3_256-cpu-arguments-intercept" => costs[136], + "sha3_256-cpu-arguments-slope" => costs[137], + "sha3_256-mem-arguments" => costs[138], + "slice_byte_string-cpu-arguments-intercept" => costs[139], + "slice_byte_string-cpu-arguments-slope" => costs[140], + "slice_byte_string-mem-arguments-intercept" => costs[141], + "slice_byte_string-mem-arguments-slope" => costs[142], + "snd_pair-cpu-arguments" => costs[143], + "snd_pair-mem-arguments" => costs[144], + "subtract_integer-cpu-arguments-intercept" => costs[145], + "subtract_integer-cpu-arguments-slope" => costs[146], + "subtract_integer-mem-arguments-intercept" => costs[147], + "subtract_integer-mem-arguments-slope" => costs[148], + "tail_list-cpu-arguments" => costs[149], + "tail_list-mem-arguments" => costs[150], + "trace-cpu-arguments" => costs[151], + "trace-mem-arguments" => costs[152], + "un_b_data-cpu-arguments" => costs[153], + "un_b_data-mem-arguments" => costs[154], + "un_constr_data-cpu-arguments" => costs[155], + "un_constr_data-mem-arguments" => costs[156], + "un_i_data-cpu-arguments" => costs[157], + "un_i_data-mem-arguments" => costs[158], + "un_list_data-cpu-arguments" => costs[159], + "un_list_data-mem-arguments" => costs[160], + "un_map_data-cpu-arguments" => costs[161], + "un_map_data-mem-arguments" => costs[162], + "verify_ed25519_signature-cpu-arguments-intercept" => costs[163], + "verify_ed25519_signature-cpu-arguments-slope" => costs[164], + "verify_ed25519_signature-mem-arguments" => costs[165] + } + } + Language::PlutusV2 => { + hashmap! { + "add_integer-cpu-arguments-intercept"=> costs[0], + "add_integer-cpu-arguments-slope"=> costs[1], + "add_integer-mem-arguments-intercept"=> costs[2], + "add_integer-mem-arguments-slope"=> costs[3], + "append_byte_string-cpu-arguments-intercept"=> costs[4], + "append_byte_string-cpu-arguments-slope"=> costs[5], + "append_byte_string-mem-arguments-intercept"=> costs[6], + "append_byte_string-mem-arguments-slope"=> costs[7], + "append_string-cpu-arguments-intercept"=> costs[8], + "append_string-cpu-arguments-slope"=> costs[9], + "append_string-mem-arguments-intercept"=> costs[10], + "append_string-mem-arguments-slope"=> costs[11], + "b_data-cpu-arguments"=> costs[12], + "b_data-mem-arguments"=> costs[13], + "blake2b_256-cpu-arguments-intercept"=> costs[14], + "blake2b_256-cpu-arguments-slope"=> costs[15], + "blake2b_256-mem-arguments"=> costs[16], + "cek_apply_cost-exBudgetCPU"=> costs[17], + "cek_apply_cost-exBudgetmem"=> costs[18], + "cek_builtin_cost-exBudgetCPU"=> costs[19], + "cek_builtin_cost-exBudgetmem"=> costs[20], + "cek_const_cost-exBudgetCPU"=> costs[21], + "cek_const_cost-exBudgetmem"=> costs[22], + "cek_delay_cost-exBudgetCPU"=> costs[23], + "cek_delay_cost-exBudgetmem"=> costs[24], + "cek_force_cost-exBudgetCPU"=> costs[25], + "cek_force_cost-exBudgetmem"=> costs[26], + "cek_lam_cost-exBudgetCPU"=> costs[27], + "cek_lam_cost-exBudgetmem"=> costs[28], + "cek_startup_cost-exBudgetCPU"=> costs[29], + "cek_startup_cost-exBudgetmem"=> costs[30], + "cek_var_cost-exBudgetCPU"=> costs[31], + "cek_var_cost-exBudgetmem"=> costs[32], + "choose_data-cpu-arguments"=> costs[33], + "choose_data-mem-arguments"=> costs[34], + "choose_list-cpu-arguments"=> costs[35], + "choose_list-mem-arguments"=> costs[36], + "choose_unit-cpu-arguments"=> costs[37], + "choose_unit-mem-arguments"=> costs[38], + "cons_byte_string-cpu-arguments-intercept"=> costs[39], + "cons_byte_string-cpu-arguments-slope"=> costs[40], + "cons_byte_string-mem-arguments-intercept"=> costs[41], + "cons_byte_string-mem-arguments-slope"=> costs[42], + "constr_data-cpu-arguments"=> costs[43], + "constr_data-mem-arguments"=> costs[44], + "decode_utf8-cpu-arguments-intercept"=> costs[45], + "decode_utf8-cpu-arguments-slope"=> costs[46], + "decode_utf8-mem-arguments-intercept"=> costs[47], + "decode_utf8-mem-arguments-slope"=> costs[48], + "divide_integer-cpu-arguments-constant"=> costs[49], + "divide_integer-cpu-arguments-model-arguments-intercept"=> costs[50], + "divide_integer-cpu-arguments-model-arguments-slope"=> costs[51], + "divide_integer-mem-arguments-intercept"=> costs[52], + "divide_integer-mem-arguments-minimum"=> costs[53], + "divide_integer-mem-arguments-slope"=> costs[54], + "encode_utf8-cpu-arguments-intercept"=> costs[55], + "encode_utf8-cpu-arguments-slope"=> costs[56], + "encode_utf8-mem-arguments-intercept"=> costs[57], + "encode_utf8-mem-arguments-slope"=> costs[58], + "equals_byte_string-cpu-arguments-constant"=> costs[59], + "equals_byte_string-cpu-arguments-intercept"=> costs[60], + "equals_byte_string-cpu-arguments-slope"=> costs[61], + "equals_byte_string-mem-arguments"=> costs[62], + "equals_data-cpu-arguments-intercept"=> costs[63], + "equals_data-cpu-arguments-slope"=> costs[64], + "equals_data-mem-arguments"=> costs[65], + "equals_integer-cpu-arguments-intercept"=> costs[66], + "equals_integer-cpu-arguments-slope"=> costs[67], + "equals_integer-mem-arguments"=> costs[68], + "equals_string-cpu-arguments-constant"=> costs[69], + "equals_string-cpu-arguments-intercept"=> costs[70], + "equals_string-cpu-arguments-slope"=> costs[71], + "equals_string-mem-arguments"=> costs[72], + "fst_pair-cpu-arguments"=> costs[73], + "fst_pair-mem-arguments"=> costs[74], + "head_list-cpu-arguments"=> costs[75], + "head_list-mem-arguments"=> costs[76], + "i_data-cpu-arguments"=> costs[77], + "i_data-mem-arguments"=> costs[78], + "if_then_else-cpu-arguments"=> costs[79], + "if_then_else-mem-arguments"=> costs[80], + "index_byte_string-cpu-arguments"=> costs[81], + "index_byte_string-mem-arguments"=> costs[82], + "length_of_byte_string-cpu-arguments"=> costs[83], + "length_of_byte_string-mem-arguments"=> costs[84], + "less_than_byte_string-cpu-arguments-intercept"=> costs[85], + "less_than_byte_string-cpu-arguments-slope"=> costs[86], + "less_than_byte_string-mem-arguments"=> costs[87], + "less_than_equals_byte_string-cpu-arguments-intercept"=> costs[88], + "less_than_equals_byte_string-cpu-arguments-slope"=> costs[89], + "less_than_equals_byte_string-mem-arguments"=> costs[90], + "less_than_equals_integer-cpu-arguments-intercept"=> costs[91], + "less_than_equals_integer-cpu-arguments-slope"=> costs[92], + "less_than_equals_integer-mem-arguments"=> costs[93], + "less_than_integer-cpu-arguments-intercept"=> costs[94], + "less_than_integer-cpu-arguments-slope"=> costs[95], + "less_than_integer-mem-arguments"=> costs[96], + "list_data-cpu-arguments"=> costs[97], + "list_data-mem-arguments"=> costs[98], + "map_data-cpu-arguments"=> costs[99], + "map_data-mem-arguments"=> costs[100], + "mk_cons-cpu-arguments"=> costs[101], + "mk_cons-mem-arguments"=> costs[102], + "mk_nil_data-cpu-arguments"=> costs[103], + "mk_nil_data-mem-arguments"=> costs[104], + "mk_nil_pair_data-cpu-arguments"=> costs[105], + "mk_nil_pair_data-mem-arguments"=> costs[106], + "mk_pair_data-cpu-arguments"=> costs[107], + "mk_pair_data-mem-arguments"=> costs[108], + "mod_integer-cpu-arguments-constant"=> costs[109], + "mod_integer-cpu-arguments-model-arguments-intercept"=> costs[110], + "mod_integer-cpu-arguments-model-arguments-slope"=> costs[111], + "mod_integer-mem-arguments-intercept"=> costs[112], + "mod_integer-mem-arguments-minimum"=> costs[113], + "mod_integer-mem-arguments-slope"=> costs[114], + "multiply_integer-cpu-arguments-intercept"=> costs[115], + "multiply_integer-cpu-arguments-slope"=> costs[116], + "multiply_integer-mem-arguments-intercept"=> costs[117], + "multiply_integer-mem-arguments-slope"=> costs[118], + "null_list-cpu-arguments"=> costs[119], + "null_list-mem-arguments"=> costs[120], + "quotient_integer-cpu-arguments-constant"=> costs[121], + "quotient_integer-cpu-arguments-model-arguments-intercept"=> costs[122], + "quotient_integer-cpu-arguments-model-arguments-slope"=> costs[123], + "quotient_integer-mem-arguments-intercept"=> costs[124], + "quotient_integer-mem-arguments-minimum"=> costs[125], + "quotient_integer-mem-arguments-slope"=> costs[126], + "remainder_integer-cpu-arguments-constant"=> costs[127], + "remainder_integer-cpu-arguments-model-arguments-intercept"=> costs[128], + "remainder_integer-cpu-arguments-model-arguments-slope"=> costs[129], + "remainder_integer-mem-arguments-intercept"=> costs[130], + "remainder_integer-mem-arguments-minimum"=> costs[131], + "remainder_integer-mem-arguments-slope"=> costs[132], + "serialise_data-cpu-arguments-intercept"=> costs[133], + "serialise_data-cpu-arguments-slope"=> costs[134], + "serialise_data-mem-arguments-intercept"=> costs[135], + "serialise_data-mem-arguments-slope"=> costs[136], + "sha2_256-cpu-arguments-intercept"=> costs[137], + "sha2_256-cpu-arguments-slope"=> costs[138], + "sha2_256-mem-arguments"=> costs[139], + "sha3_256-cpu-arguments-intercept"=> costs[140], + "sha3_256-cpu-arguments-slope"=> costs[141], + "sha3_256-mem-arguments"=> costs[142], + "slice_byte_string-cpu-arguments-intercept"=> costs[143], + "slice_byte_string-cpu-arguments-slope"=> costs[144], + "slice_byte_string-mem-arguments-intercept"=> costs[145], + "slice_byte_string-mem-arguments-slope"=> costs[146], + "snd_pair-cpu-arguments"=> costs[147], + "snd_pair-mem-arguments"=> costs[148], + "subtract_integer-cpu-arguments-intercept"=> costs[149], + "subtract_integer-cpu-arguments-slope"=> costs[150], + "subtract_integer-mem-arguments-intercept"=> costs[151], + "subtract_integer-mem-arguments-slope"=> costs[152], + "tail_list-cpu-arguments"=> costs[153], + "tail_list-mem-arguments"=> costs[154], + "trace-cpu-arguments"=> costs[155], + "trace-mem-arguments"=> costs[156], + "un_b_data-cpu-arguments"=> costs[157], + "un_b_data-mem-arguments"=> costs[158], + "un_constr_data-cpu-arguments"=> costs[159], + "un_constr_data-mem-arguments"=> costs[160], + "un_i_data-cpu-arguments"=> costs[161], + "un_i_data-mem-arguments"=> costs[162], + "un_list_data-cpu-arguments"=> costs[163], + "un_list_data-mem-arguments"=> costs[164], + "un_map_data-cpu-arguments"=> costs[165], + "un_map_data-mem-arguments"=> costs[166], + "verify_ecdsa_secp256k1_signature-cpu-arguments"=> costs[167], + "verify_ecdsa_secp256k1_signature-mem-arguments"=> costs[168], + "verify_ed25519_signature-cpu-arguments-intercept"=> costs[169], + "verify_ed25519_signature-cpu-arguments-slope"=> costs[170], + "verify_ed25519_signature-mem-arguments"=> costs[171], + "verify_schnorr_secp256k1_signature-cpu-arguments-intercept"=> costs[172], + "verify_schnorr_secp256k1_signature-cpu-arguments-slope"=> costs[173], + "verify_schnorr_secp256k1_signature-mem-arguments"=> costs[174] + } + } + }; + CostModel { + machine_costs: MachineCosts { + startup: ExBudget { + mem: *cost_map + .get("cek_startup_cost-exBudgetmem") + .unwrap_or(&30000000000), + cpu: *cost_map + .get("cek_startup_cost-exBudgetCPU") + .unwrap_or(&30000000000), + }, + var: ExBudget { + mem: *cost_map + .get("cek_var_cost-exBudgetmem") + .unwrap_or(&30000000000), + cpu: *cost_map + .get("cek_var_cost-exBudgetCPU") + .unwrap_or(&30000000000), + }, + constant: ExBudget { + mem: *cost_map + .get("cek_const_cost-exBudgetmem") + .unwrap_or(&30000000000), + cpu: *cost_map + .get("cek_const_cost-exBudgetCPU") + .unwrap_or(&30000000000), + }, + lambda: ExBudget { + mem: *cost_map + .get("cek_lam_cost-exBudgetmem") + .unwrap_or(&30000000000), + cpu: *cost_map + .get("cek_lam_cost-exBudgetCPU") + .unwrap_or(&30000000000), + }, + delay: ExBudget { + mem: *cost_map + .get("cek_delay_cost-exBudgetmem") + .unwrap_or(&30000000000), + cpu: *cost_map + .get("cek_delay_cost-exBudgetCPU") + .unwrap_or(&30000000000), + }, + force: ExBudget { + mem: *cost_map + .get("cek_force_cost-exBudgetmem") + .unwrap_or(&30000000000), + cpu: *cost_map + .get("cek_force_cost-exBudgetCPU") + .unwrap_or(&30000000000), + }, + apply: ExBudget { + mem: *cost_map + .get("cek_apply_cost-exBudgetmem") + .unwrap_or(&30000000000), + cpu: *cost_map + .get("cek_apply_cost-exBudgetCPU") + .unwrap_or(&30000000000), + }, + builtin: ExBudget { + mem: *cost_map + .get("cek_builtin_cost-exBudgetmem") + .unwrap_or(&30000000000), + cpu: *cost_map + .get("cek_builtin_cost-exBudgetCPU") + .unwrap_or(&30000000000), + }, + }, + builtin_costs: BuiltinCosts { + add_integer: CostingFun { + mem: TwoArguments::MaxSize(MaxSize { + intercept: *cost_map + .get("add_integer-mem-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("add_integer-mem-arguments-slope") + .unwrap_or(&30000000000), + }), + cpu: TwoArguments::MaxSize(MaxSize { + intercept: *cost_map + .get("add_integer-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("add_integer-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + subtract_integer: CostingFun { + mem: TwoArguments::MaxSize(MaxSize { + intercept: *cost_map + .get("subtract_integer-mem-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("subtract_integer-mem-arguments-slope") + .unwrap_or(&30000000000), + }), + cpu: TwoArguments::MaxSize(MaxSize { + intercept: *cost_map + .get("subtract_integer-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("subtract_integer-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + multiply_integer: CostingFun { + mem: TwoArguments::AddedSizes(AddedSizes { + intercept: *cost_map + .get("multiply_integer-mem-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("multiply_integer-mem-arguments-slope") + .unwrap_or(&30000000000), + }), + cpu: TwoArguments::AddedSizes(AddedSizes { + intercept: *cost_map + .get("multiply_integer-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("multiply_integer-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + divide_integer: CostingFun { + mem: TwoArguments::SubtractedSizes(SubtractedSizes { + intercept: *cost_map + .get("divide_integer-mem-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("divide_integer-mem-arguments-slope") + .unwrap_or(&30000000000), + minimum: *cost_map + .get("divide_integer-mem-arguments-minimum") + .unwrap_or(&30000000000), + }), + cpu: TwoArguments::ConstAboveDiagonal(ConstantOrTwoArguments { + constant: *cost_map + .get("divide_integer-cpu-arguments-constant") + .unwrap_or(&30000000000), + model: Box::new(TwoArguments::MultipliedSizes(MultipliedSizes { + intercept: *cost_map + .get("divide_integer-cpu-arguments-model-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("divide_integer-cpu-arguments-model-arguments-slope") + .unwrap_or(&30000000000), + })), + }), + }, + quotient_integer: CostingFun { + mem: TwoArguments::SubtractedSizes(SubtractedSizes { + intercept: *cost_map + .get("quotient_integer-mem-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("quotient_integer-mem-arguments-slope") + .unwrap_or(&30000000000), + minimum: *cost_map + .get("quotient_integer-mem-arguments-minimum") + .unwrap_or(&30000000000), + }), + cpu: TwoArguments::ConstAboveDiagonal(ConstantOrTwoArguments { + constant: *cost_map + .get("quotient_integer-cpu-arguments-constant") + .unwrap_or(&30000000000), + model: Box::new(TwoArguments::MultipliedSizes(MultipliedSizes { + intercept: *cost_map + .get("quotient_integer-cpu-arguments-model-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("quotient_integer-cpu-arguments-model-arguments-slope") + .unwrap_or(&30000000000), + })), + }), + }, + remainder_integer: CostingFun { + mem: TwoArguments::SubtractedSizes(SubtractedSizes { + intercept: *cost_map + .get("remainder_integer-mem-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("remainder_integer-mem-arguments-slope") + .unwrap_or(&30000000000), + minimum: *cost_map + .get("remainder_integer-mem-arguments-minimum") + .unwrap_or(&30000000000), + }), + cpu: TwoArguments::ConstAboveDiagonal(ConstantOrTwoArguments { + constant: *cost_map + .get("remainder_integer-cpu-arguments-constant") + .unwrap_or(&30000000000), + model: Box::new(TwoArguments::MultipliedSizes(MultipliedSizes { + intercept: *cost_map + .get("remainder_integer-cpu-arguments-model-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("remainder_integer-cpu-arguments-model-arguments-slope") + .unwrap_or(&30000000000), + })), + }), + }, + mod_integer: CostingFun { + mem: TwoArguments::SubtractedSizes(SubtractedSizes { + intercept: *cost_map + .get("mod_integer-mem-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("mod_integer-mem-arguments-slope") + .unwrap_or(&30000000000), + minimum: *cost_map + .get("mod_integer-mem-arguments-minimum") + .unwrap_or(&30000000000), + }), + cpu: TwoArguments::ConstAboveDiagonal(ConstantOrTwoArguments { + constant: *cost_map + .get("mod_integer-cpu-arguments-constant") + .unwrap_or(&30000000000), + model: Box::new(TwoArguments::MultipliedSizes(MultipliedSizes { + intercept: *cost_map + .get("mod_integer-cpu-arguments-model-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("mod_integer-cpu-arguments-model-arguments-slope") + .unwrap_or(&30000000000), + })), + }), + }, + equals_integer: CostingFun { + mem: TwoArguments::ConstantCost( + *cost_map + .get("equals_integer-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: TwoArguments::MinSize(MinSize { + intercept: *cost_map + .get("equals_integer-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("equals_integer-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + less_than_integer: CostingFun { + mem: TwoArguments::ConstantCost( + *cost_map + .get("less_than_integer-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: TwoArguments::MinSize(MinSize { + intercept: *cost_map + .get("less_than_integer-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("less_than_integer-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + less_than_equals_integer: CostingFun { + mem: TwoArguments::ConstantCost( + *cost_map + .get("less_than_equals_integer-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: TwoArguments::MinSize(MinSize { + intercept: *cost_map + .get("less_than_equals_integer-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("less_than_equals_integer-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + append_byte_string: CostingFun { + mem: TwoArguments::AddedSizes(AddedSizes { + intercept: *cost_map + .get("append_byte_string-mem-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("append_byte_string-mem-arguments-slope") + .unwrap_or(&30000000000), + }), + cpu: TwoArguments::AddedSizes(AddedSizes { + intercept: *cost_map + .get("append_byte_string-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("append_byte_string-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + cons_byte_string: CostingFun { + mem: TwoArguments::AddedSizes(AddedSizes { + intercept: *cost_map + .get("cons_byte_string-mem-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("cons_byte_string-mem-arguments-slope") + .unwrap_or(&30000000000), + }), + cpu: TwoArguments::LinearInY(LinearSize { + intercept: *cost_map + .get("cons_byte_string-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("cons_byte_string-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + slice_byte_string: CostingFun { + mem: ThreeArguments::LinearInZ(LinearSize { + intercept: *cost_map + .get("slice_byte_string-mem-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("slice_byte_string-mem-arguments-slope") + .unwrap_or(&30000000000), + }), + cpu: ThreeArguments::LinearInZ(LinearSize { + intercept: *cost_map + .get("slice_byte_string-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("slice_byte_string-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + length_of_byte_string: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map + .get("length_of_byte_string-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: OneArgument::ConstantCost( + *cost_map + .get("length_of_byte_string-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + index_byte_string: CostingFun { + mem: TwoArguments::ConstantCost( + *cost_map + .get("index_byte_string-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: TwoArguments::ConstantCost( + *cost_map + .get("index_byte_string-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + equals_byte_string: CostingFun { + mem: TwoArguments::ConstantCost( + *cost_map + .get("equals_byte_string-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: TwoArguments::LinearOnDiagonal(ConstantOrLinear { + constant: *cost_map + .get("equals_byte_string-cpu-arguments-constant") + .unwrap_or(&30000000000), + intercept: *cost_map + .get("equals_byte_string-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("equals_byte_string-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + less_than_byte_string: CostingFun { + mem: TwoArguments::ConstantCost( + *cost_map + .get("less_than_byte_string-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: TwoArguments::MinSize(MinSize { + intercept: *cost_map + .get("less_than_byte_string-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("less_than_byte_string-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + less_than_equals_byte_string: CostingFun { + mem: TwoArguments::ConstantCost( + *cost_map + .get("less_than_equals_byte_string-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: TwoArguments::MinSize(MinSize { + intercept: *cost_map + .get("less_than_equals_byte_string-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("less_than_equals_byte_string-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + sha2_256: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map + .get("sha2_256-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: OneArgument::LinearCost(LinearSize { + intercept: *cost_map + .get("sha2_256-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("sha2_256-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + sha3_256: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map + .get("sha3_256-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: OneArgument::LinearCost(LinearSize { + intercept: *cost_map + .get("sha3_256-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("sha3_256-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + blake2b_256: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map + .get("blake2b_256-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: OneArgument::LinearCost(LinearSize { + intercept: *cost_map + .get("blake2b_256-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("blake2b_256-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + verify_ed25519_signature: CostingFun { + mem: ThreeArguments::ConstantCost( + *cost_map + .get("verify_ed25519_signature-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: ThreeArguments::LinearInZ(LinearSize { + intercept: *cost_map + .get("verify_ed25519_signature-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("verify_ed25519_signature-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + verify_ecdsa_secp256k1_signature: CostingFun { + mem: ThreeArguments::ConstantCost( + *cost_map + .get("verify_ecdsa_secp256k1_signature-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: ThreeArguments::ConstantCost( + *cost_map + .get("verify_ecdsa_secp256k1_signature-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + verify_schnorr_secp256k1_signature: CostingFun { + mem: ThreeArguments::ConstantCost( + *cost_map + .get("verify_schnorr_secp256k1_signature-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: ThreeArguments::LinearInY(LinearSize { + intercept: *cost_map + .get("verify_schnorr_secp256k1_signature-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("verify_schnorr_secp256k1_signature-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + append_string: CostingFun { + mem: TwoArguments::AddedSizes(AddedSizes { + intercept: *cost_map + .get("append_string-mem-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("append_string-mem-arguments-slope") + .unwrap_or(&30000000000), + }), + cpu: TwoArguments::AddedSizes(AddedSizes { + intercept: *cost_map + .get("append_string-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("append_string-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + equals_string: CostingFun { + mem: TwoArguments::ConstantCost( + *cost_map + .get("equals_string-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: TwoArguments::LinearOnDiagonal(ConstantOrLinear { + constant: *cost_map + .get("equals_string-cpu-arguments-constant") + .unwrap_or(&30000000000), + intercept: *cost_map + .get("equals_string-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("equals_string-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + encode_utf8: CostingFun { + mem: OneArgument::LinearCost(LinearSize { + intercept: *cost_map + .get("encode_utf8-mem-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("encode_utf8-mem-arguments-slope") + .unwrap_or(&30000000000), + }), + cpu: OneArgument::LinearCost(LinearSize { + intercept: *cost_map + .get("encode_utf8-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("encode_utf8-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + decode_utf8: CostingFun { + mem: OneArgument::LinearCost(LinearSize { + intercept: *cost_map + .get("decode_utf8-mem-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("decode_utf8-mem-arguments-slope") + .unwrap_or(&30000000000), + }), + cpu: OneArgument::LinearCost(LinearSize { + intercept: *cost_map + .get("decode_utf8-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("decode_utf8-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + if_then_else: CostingFun { + mem: ThreeArguments::ConstantCost( + *cost_map + .get("if_then_else-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: ThreeArguments::ConstantCost( + *cost_map + .get("if_then_else-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + choose_unit: CostingFun { + mem: TwoArguments::ConstantCost( + *cost_map + .get("choose_unit-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: TwoArguments::ConstantCost( + *cost_map + .get("choose_unit-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + trace: CostingFun { + mem: TwoArguments::ConstantCost( + *cost_map.get("trace-mem-arguments").unwrap_or(&30000000000), + ), + cpu: TwoArguments::ConstantCost( + *cost_map.get("trace-cpu-arguments").unwrap_or(&30000000000), + ), + }, + fst_pair: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map + .get("fst_pair-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: OneArgument::ConstantCost( + *cost_map + .get("fst_pair-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + snd_pair: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map + .get("snd_pair-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: OneArgument::ConstantCost( + *cost_map + .get("snd_pair-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + choose_list: CostingFun { + mem: ThreeArguments::ConstantCost( + *cost_map + .get("choose_list-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: ThreeArguments::ConstantCost( + *cost_map + .get("choose_list-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + mk_cons: CostingFun { + mem: TwoArguments::ConstantCost( + *cost_map + .get("mk_cons-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: TwoArguments::ConstantCost( + *cost_map + .get("mk_cons-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + head_list: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map + .get("head_list-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: OneArgument::ConstantCost( + *cost_map + .get("head_list-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + tail_list: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map + .get("tail_list-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: OneArgument::ConstantCost( + *cost_map + .get("tail_list-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + null_list: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map + .get("null_list-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: OneArgument::ConstantCost( + *cost_map + .get("null_list-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + choose_data: CostingFun { + mem: SixArguments::ConstantCost( + *cost_map + .get("choose_data-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: SixArguments::ConstantCost( + *cost_map + .get("choose_data-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + constr_data: CostingFun { + mem: TwoArguments::ConstantCost( + *cost_map + .get("constr_data-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: TwoArguments::ConstantCost( + *cost_map + .get("constr_data-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + map_data: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map + .get("map_data-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: OneArgument::ConstantCost( + *cost_map + .get("map_data-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + list_data: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map + .get("list_data-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: OneArgument::ConstantCost( + *cost_map + .get("list_data-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + i_data: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map.get("i_data-mem-arguments").unwrap_or(&30000000000), + ), + cpu: OneArgument::ConstantCost( + *cost_map.get("i_data-cpu-arguments").unwrap_or(&30000000000), + ), + }, + b_data: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map.get("b_data-mem-arguments").unwrap_or(&30000000000), + ), + cpu: OneArgument::ConstantCost( + *cost_map.get("b_data-cpu-arguments").unwrap_or(&30000000000), + ), + }, + un_constr_data: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map + .get("un_constr_data-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: OneArgument::ConstantCost( + *cost_map + .get("un_constr_data-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + un_map_data: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map + .get("un_map_data-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: OneArgument::ConstantCost( + *cost_map + .get("un_map_data-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + un_list_data: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map + .get("un_list_data-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: OneArgument::ConstantCost( + *cost_map + .get("un_list_data-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + un_i_data: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map + .get("un_i_data-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: OneArgument::ConstantCost( + *cost_map + .get("un_i_data-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + un_b_data: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map + .get("un_b_data-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: OneArgument::ConstantCost( + *cost_map + .get("un_b_data-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + equals_data: CostingFun { + mem: TwoArguments::ConstantCost( + *cost_map + .get("equals_data-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: TwoArguments::MinSize(MinSize { + intercept: *cost_map + .get("equals_data-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("equals_data-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + mk_pair_data: CostingFun { + mem: TwoArguments::ConstantCost( + *cost_map + .get("mk_pair_data-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: TwoArguments::ConstantCost( + *cost_map + .get("mk_pair_data-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + mk_nil_data: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map + .get("mk_nil_data-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: OneArgument::ConstantCost( + *cost_map + .get("mk_nil_data-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + mk_nil_pair_data: CostingFun { + mem: OneArgument::ConstantCost( + *cost_map + .get("mk_nil_pair_data-mem-arguments") + .unwrap_or(&30000000000), + ), + cpu: OneArgument::ConstantCost( + *cost_map + .get("mk_nil_pair_data-cpu-arguments") + .unwrap_or(&30000000000), + ), + }, + serialise_data: CostingFun { + mem: OneArgument::LinearCost(LinearSize { + intercept: *cost_map + .get("serialise_data-mem-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("serialise_data-mem-arguments-slope") + .unwrap_or(&30000000000), + }), + cpu: OneArgument::LinearCost(LinearSize { + intercept: *cost_map + .get("serialise_data-cpu-arguments-intercept") + .unwrap_or(&30000000000), + slope: *cost_map + .get("serialise_data-cpu-arguments-slope") + .unwrap_or(&30000000000), + }), + }, + }, + } } pub struct CostingFun { diff --git a/crates/uplc/src/machine/error.rs b/crates/uplc/src/machine/error.rs index 93bc1b57..9be3823a 100644 --- a/crates/uplc/src/machine/error.rs +++ b/crates/uplc/src/machine/error.rs @@ -1,12 +1,10 @@ use std::string::FromUtf8Error; -use thiserror::Error; - use crate::ast::{NamedDeBruijn, Term, Type}; use super::{ExBudget, Value}; -#[derive(Error, Debug)] +#[derive(thiserror::Error, Debug)] pub enum Error { #[error("Over budget mem: {} & cpu: {}", .0.mem, .0.cpu)] OutOfExError(ExBudget), diff --git a/crates/uplc/src/machine/runtime.rs b/crates/uplc/src/machine/runtime.rs index afac6f39..fa1614b2 100644 --- a/crates/uplc/src/machine/runtime.rs +++ b/crates/uplc/src/machine/runtime.rs @@ -1,6 +1,5 @@ use std::ops::Deref; -use pallas_codec::utils::{KeyValuePairs, MaybeIndefArray}; use pallas_primitives::babbage::{BigInt, Constr, PlutusData}; use crate::{ @@ -64,8 +63,12 @@ impl BuiltinRuntime { Ok(()) } - pub fn to_ex_budget(&self, costs: &BuiltinCosts) -> ExBudget { - costs.to_ex_budget(self.fun, &self.args) + pub fn to_ex_budget_v2(&self, costs: &BuiltinCosts) -> ExBudget { + costs.to_ex_budget_v2(self.fun, &self.args) + } + + pub fn to_ex_budget_v1(&self, costs: &BuiltinCosts) -> ExBudget { + costs.to_ex_budget_v1(self.fun, &self.args) } } @@ -664,8 +667,7 @@ impl DefaultFunction { DefaultFunction::ChooseData => match &args[0] { Value::Con(Constant::Data(PlutusData::Constr(_))) => Ok(args[1].clone()), Value::Con(Constant::Data(PlutusData::Map(_))) => Ok(args[2].clone()), - Value::Con(Constant::Data(PlutusData::Array(_))) - | Value::Con(Constant::Data(PlutusData::ArrayIndef(_))) => Ok(args[3].clone()), + Value::Con(Constant::Data(PlutusData::Array(_))) => Ok(args[3].clone()), Value::Con(Constant::Data(PlutusData::BigInt(_))) => Ok(args[4].clone()), Value::Con(Constant::Data(PlutusData::BoundedBytes(_))) => Ok(args[5].clone()), _ => unreachable!(), @@ -687,7 +689,7 @@ impl DefaultFunction { // TODO: handle other types of constructor tags tag: convert_constr_to_tag(*i as u64), any_constructor: None, - fields: MaybeIndefArray::Indef(data_list), + fields: data_list, }); Ok(Value::Con(Constant::Data(constr_data))) } @@ -695,21 +697,23 @@ impl DefaultFunction { }, DefaultFunction::MapData => match &args[0] { Value::Con(Constant::ProtoList(_, list)) => { - let data_list: Vec<(PlutusData, PlutusData)> = list - .iter() - .map(|item| match item { + let mut map = Vec::new(); + + for item in list { + match item { Constant::ProtoPair(Type::Data, Type::Data, left, right) => { match (*left.clone(), *right.clone()) { - (Constant::Data(key), Constant::Data(value)) => (key, value), + (Constant::Data(key), Constant::Data(value)) => { + map.push((key, value)); + } _ => unreachable!(), } } _ => unreachable!(), - }) - .collect(); - Ok(Value::Con(Constant::Data(PlutusData::Map( - KeyValuePairs::Def(data_list), - )))) + } + } + + Ok(Value::Con(Constant::Data(PlutusData::Map(map.into())))) } _ => unreachable!(), }, @@ -722,9 +726,8 @@ impl DefaultFunction { _ => unreachable!(), }) .collect(); - Ok(Value::Con(Constant::Data(PlutusData::ArrayIndef( - MaybeIndefArray::Indef(data_list), - )))) + + Ok(Value::Con(Constant::Data(PlutusData::Array(data_list)))) } _ => unreachable!(), }, @@ -779,8 +782,7 @@ impl DefaultFunction { _ => unreachable!(), }, DefaultFunction::UnListData => match &args[0] { - Value::Con(Constant::Data(PlutusData::Array(l))) - | Value::Con(Constant::Data(PlutusData::ArrayIndef(l))) => { + Value::Con(Constant::Data(PlutusData::Array(l))) => { Ok(Value::Con(Constant::ProtoList( Type::Data, l.deref() @@ -794,7 +796,7 @@ impl DefaultFunction { DefaultFunction::UnIData => match &args[0] { Value::Con(Constant::Data(PlutusData::BigInt(b))) => { if let BigInt::Int(i) = b { - let x: i64 = (*i).try_into().unwrap(); + let x: i128 = (*i).try_into().unwrap(); Ok(Value::Con(Constant::Integer(x as isize))) } else { diff --git a/crates/uplc/src/parser.rs b/crates/uplc/src/parser.rs index 5caee1ca..80d0ef08 100644 --- a/crates/uplc/src/parser.rs +++ b/crates/uplc/src/parser.rs @@ -6,6 +6,7 @@ use crate::{ }; use interner::Interner; +use pallas_primitives::{alonzo::PlutusData, Fragment}; use peg::{error::ParseError, str::LineCol}; mod interner; @@ -24,6 +25,19 @@ pub fn program(src: &str) -> Result, ParseError> { Ok(program) } +pub fn term(src: &str) -> Result, ParseError> { + // initialize the string interner to get unique name + let mut interner = Interner::new(); + + // run the generated parser + let mut term = uplc::term(src)?; + + // assign proper unique ids in place + interner.term(&mut term); + + Ok(term) +} + peg::parser! { grammar uplc() for str { pub rule program() -> Program @@ -36,7 +50,7 @@ peg::parser! { (major as usize, minor as usize, patch as usize) } - rule term() -> Term + pub rule term() -> Term = constant() / builtin() / var() @@ -53,6 +67,7 @@ peg::parser! { / constant_string() / constant_unit() / constant_bool() + / constant_data() ) _* ")" { Term::Constant(con) } @@ -110,6 +125,15 @@ peg::parser! { rule number() -> isize = n:$("-"* ['0'..='9']+) {? n.parse().or(Err("isize")) } + rule constant_data() -> Constant + = "data" _+ "#" i:ident()* { + Constant::Data( + PlutusData::decode_fragment( + hex::decode(String::from_iter(i)).unwrap().as_slice() + ).unwrap() + ) + } + rule name() -> Name = text:ident() { Name { text, unique: 0.into() } } diff --git a/crates/uplc/src/tx.rs b/crates/uplc/src/tx.rs new file mode 100644 index 00000000..8996efd0 --- /dev/null +++ b/crates/uplc/src/tx.rs @@ -0,0 +1,132 @@ +use pallas_primitives::{ + babbage::{CostMdls, MintedTx, Redeemer, TransactionInput, TransactionOutput}, + Fragment, +}; +use pallas_traverse::{Era, MultiEraTx}; + +use error::Error; +pub use eval::get_script_and_datum_lookup_table; +pub use phase_one::eval_phase_one; +use script_context::{ResolvedInput, SlotConfig}; + +use crate::machine::cost_model::ExBudget; + +pub mod error; +mod eval; +mod phase_one; +pub mod script_context; +#[cfg(test)] +mod tests; +mod to_plutus_data; + +/// Evaluate the scripts in a transaction using +/// the UPLC Cek Machine. This function collects +/// redeemers with ExUnits calculated from the evaluation. +/// You may optionally run a subset of phase one checks on +/// redeemers and scripts. +pub fn eval_phase_two( + tx: &MintedTx, + utxos: &[ResolvedInput], + cost_mdls: Option<&CostMdls>, + initial_budget: Option<&ExBudget>, + slot_config: &SlotConfig, + run_phase_one: bool, +) -> Result, Error> { + let redeemers = tx.transaction_witness_set.redeemer.as_ref(); + + let lookup_table = get_script_and_datum_lookup_table(tx, utxos); + + if run_phase_one { + // subset of phase 1 check on redeemers and scripts + eval_phase_one(tx, utxos, &lookup_table)?; + } + + match redeemers { + Some(rs) => { + let mut collected_redeemers = vec![]; + + for redeemer in rs.iter() { + let redeemer = eval::eval_redeemer( + tx, + utxos, + slot_config, + redeemer, + &lookup_table, + cost_mdls, + initial_budget, + )?; + + collected_redeemers.push(redeemer) + } + + Ok(collected_redeemers) + } + None => Ok(vec![]), + } +} + +/// This function is the same as [`eval_phase_two`] +/// but the inputs are raw bytes. +/// initial_budget expects (cpu, mem). +/// slot_config (zero_time, zero_slot, slot_length) +pub fn eval_phase_two_raw( + tx_bytes: &[u8], + utxos_bytes: &[(Vec, Vec)], + cost_mdls_bytes: &[u8], + initial_budget: (u64, u64), + slot_config: (u64, u64, u64), + run_phase_one: bool, +) -> Result>, Error> { + let multi_era_tx = MultiEraTx::decode(Era::Babbage, tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Alonzo, tx_bytes))?; + + let cost_mdls = CostMdls::decode_fragment(cost_mdls_bytes)?; + + let budget = ExBudget { + cpu: initial_budget.0 as i64, + mem: initial_budget.1 as i64, + }; + + let mut utxos = Vec::new(); + + for (input, output) in utxos_bytes { + utxos.push(ResolvedInput { + input: TransactionInput::decode_fragment(input)?, + output: TransactionOutput::decode_fragment(output)?, + }); + } + + let sc = SlotConfig { + zero_time: slot_config.0, + zero_slot: slot_config.1, + slot_length: slot_config.2, + }; + + match multi_era_tx { + MultiEraTx::Babbage(tx) => { + match eval_phase_two( + &tx, + &utxos, + Some(&cost_mdls), + Some(&budget), + &sc, + run_phase_one, + ) { + Ok(redeemers) => Ok(redeemers + .iter() + .map(|r| r.encode_fragment().unwrap()) + .collect()), + Err(_) => Err(Error::NoRedeemers), + } + } + // MultiEraTx::AlonzoCompatible(tx, _) => match eval_tx(&tx, &utxos, &sc) { + // 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"), + } +} diff --git a/crates/uplc/src/tx/error.rs b/crates/uplc/src/tx/error.rs new file mode 100644 index 00000000..b20b68f3 --- /dev/null +++ b/crates/uplc/src/tx/error.rs @@ -0,0 +1,52 @@ +use crate::machine::{self, cost_model::ExBudget}; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("{0}")] + Address(#[from] pallas_addresses::Error), + #[error("Only shelley reward addresses can be a part of withdrawals")] + BadWithdrawalAddress, + #[error("{0}")] + FlatDecode(#[from] flat_rs::de::Error), + #[error("{0}")] + FragmentDecode(#[from] pallas_primitives::Error), + #[error("{}\n\n{:#?}\n\n{}", .0, .1, .2.join("\n"))] + Machine(machine::Error, ExBudget, Vec), + #[error("Native script can't be executed in phase-two")] + NativeScriptPhaseTwo, + #[error("Can't eval without redeemers")] + NoRedeemers, + #[error("Mismatch in required redeemers: {} {}", .missing.join(" "), .extra.join(" "))] + RequiredRedeemersMismatch { + missing: Vec, + extra: Vec, + }, + #[error("Extraneous redeemer found: Tag {:?}, Index {}", tag, index)] + ExtraneousRedeemer { tag: String, index: u32 }, + #[error("Resolved Input not found")] + ResolvedInputNotFound, + #[error("A key hash cannot be the hash of a script")] + ScriptKeyHash, + #[error("PlutusV1 cost model not found.")] + V1CostModelNotFound, + #[error("PlutusV2 cost model not found.")] + V2CostModelNotFound, + #[error("Wrong era, Please use Babbage or Alonzo: {0}")] + WrongEra(#[from] pallas_codec::minicbor::decode::Error), + #[error("Byron address not allowed in Plutus.")] + ByronAddressNotAllowed, + #[error("Inline datum not allowed in PlutusV1.")] + InlineDatumNotAllowed, + #[error("Script and input reference not allowed in PlutusV1.")] + ScriptAndInputRefNotAllowed, + #[error("Address doesn't contain a payment credential.")] + NoPaymentCredential, + #[error("Missing required datum in witness set. Datum hash: {}", hash)] + MissingRequiredDatum { hash: String }, + #[error("Missing required script. Script hash: {}", hash)] + MissingRequiredScript { hash: String }, + #[error("Missing required inline datum or datum hash in script input.")] + MissingRequiredInlineDatumOrHash, + #[error("Only stake deregistration and delegation are valid certificate script purposes.")] + OnlyStakeDeregAndDelegAllowed, +} diff --git a/crates/uplc/src/tx/eval.rs b/crates/uplc/src/tx/eval.rs new file mode 100644 index 00000000..56027428 --- /dev/null +++ b/crates/uplc/src/tx/eval.rs @@ -0,0 +1,833 @@ +use crate::{ + ast::{FakeNamedDeBruijn, NamedDeBruijn, Program}, + machine::cost_model::ExBudget, + PlutusData, +}; +use pallas_addresses::{Address, ScriptHash, StakePayload}; +use pallas_codec::utils::{KeyValuePairs, MaybeIndefArray}; +use pallas_crypto::hash::Hash; +use pallas_primitives::babbage::{ + Certificate, CostMdls, DatumHash, DatumOption, ExUnits, Language, Mint, MintedTx, NativeScript, + PlutusV1Script, PlutusV2Script, PolicyId, Redeemer, RedeemerTag, RewardAccount, Script, + StakeCredential, TransactionInput, TransactionOutput, Value, Withdrawals, +}; +use pallas_traverse::{ComputeHash, OriginalHash}; +use std::{collections::HashMap, convert::TryInto, ops::Deref, vec}; + +use super::{ + script_context::{ + ResolvedInput, ScriptContext, ScriptPurpose, SlotConfig, TimeRange, TxInInfo, TxInfo, + TxInfoV1, TxInfoV2, TxOut, + }, + to_plutus_data::{MintValue, ToPlutusData}, + Error, +}; + +fn slot_to_begin_posix_time(slot: u64, sc: &SlotConfig) -> u64 { + let ms_after_begin = (slot - sc.zero_slot) * sc.slot_length; + 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)), + } +} + +#[derive(Debug, PartialEq, Clone)] +pub enum ScriptVersion { + Native(NativeScript), + V1(PlutusV1Script), + V2(PlutusV2Script), +} + +#[derive(Debug, PartialEq, Clone)] +enum ExecutionPurpose { + WithDatum(ScriptVersion, PlutusData), // Spending + NoDatum(ScriptVersion), // Minting, Wdrl, DCert +} + +pub struct DataLookupTable { + datum: HashMap, + scripts: HashMap, +} + +impl DataLookupTable { + pub fn scripts(&self) -> HashMap { + self.scripts.clone() + } +} + +pub fn get_tx_in_info_v1( + inputs: &[TransactionInput], + utxos: &[ResolvedInput], +) -> Result, Error> { + inputs + .iter() + .map(|input| { + let utxo = match utxos.iter().find(|utxo| utxo.input == *input) { + Some(resolved) => resolved, + None => return Err(Error::ResolvedInputNotFound), + }; + 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(utxo.output.clone()), + }) + }) + .collect() +} + +fn get_tx_in_info_v2( + inputs: &[TransactionInput], + utxos: &[ResolvedInput], +) -> Result, Error> { + inputs + .iter() + .map(|input| { + let utxo = match utxos.iter().find(|utxo| utxo.input == *input) { + Some(resolved) => resolved, + None => return Err(Error::ResolvedInputNotFound), + }; + 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(utxo.output.clone()), + }) + }) + .collect() +} + +fn get_script_purpose( + redeemer: &Redeemer, + inputs: &[TransactionInput], + mint: &Option, + dcert: &Option>, + wdrl: &Option, +) -> Result { + // sorting according to specs section 4.1: https://hydra.iohk.io/build/18583827/download/1/alonzo-changes.pdf + let tag = redeemer.tag.clone(); + let index = redeemer.index; + match tag { + RedeemerTag::Mint => { + // sort lexical by policy id + let mut policy_ids = mint + .as_ref() + .unwrap_or(&KeyValuePairs::Indef(vec![])) + .iter() + .map(|(policy_id, _)| *policy_id) + .collect::>(); + policy_ids.sort(); + match policy_ids.get(index as usize) { + Some(policy_id) => Ok(ScriptPurpose::Minting(*policy_id)), + None => Err(Error::ExtraneousRedeemer { + tag: "Mint".to_string(), + index, + }), + } + } + RedeemerTag::Spend => { + // sort lexical by tx_hash and index + let mut inputs = inputs.to_vec(); + inputs.sort(); + match inputs.get(index as usize) { + Some(input) => Ok(ScriptPurpose::Spending(input.clone())), + None => Err(Error::ExtraneousRedeemer { + tag: "Spend".to_string(), + index, + }), + } + } + RedeemerTag::Reward => { + // sort lexical by reward account + let mut reward_accounts = wdrl + .as_ref() + .unwrap_or(&KeyValuePairs::Indef(vec![])) + .iter() + .map(|(racnt, _)| racnt.clone()) + .collect::>(); + reward_accounts.sort(); + let reward_account = match reward_accounts.get(index as usize) { + Some(ra) => ra.clone(), + None => { + return Err(Error::ExtraneousRedeemer { + tag: "Reward".to_string(), + index, + }) + } + }; + let address = Address::from_bytes(&reward_account)?; + let credential = match address { + Address::Stake(stake_address) => match stake_address.payload() { + StakePayload::Script(script_hash) => StakeCredential::Scripthash(*script_hash), + StakePayload::Stake(_) => { + return Err(Error::ScriptKeyHash); + } + }, + _ => return Err(Error::BadWithdrawalAddress), + }; + Ok(ScriptPurpose::Rewarding(credential)) + } + RedeemerTag::Cert => { + // sort by order given in the tx (just take it as it is basically) + match dcert + .as_ref() + .unwrap_or(&MaybeIndefArray::Indef(vec![])) + .get(index as usize) + { + Some(cert) => Ok(ScriptPurpose::Certifying(cert.clone())), + None => Err(Error::ExtraneousRedeemer { + tag: "Cert".to_string(), + index, + }), + } + } + } +} + +fn get_tx_info_v1( + tx: &MintedTx, + utxos: &[ResolvedInput], + slot_config: &SlotConfig, +) -> Result { + 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() + .map(|output| TxOut::V1(output.clone())) + .collect(); + + let fee = Value::Coin(body.fee); + let 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(); + + 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(); + + let data = tx + .transaction_witness_set + .plutus_data + .as_ref() + .unwrap_or(&vec![]) + .iter() + .map(|d| (d.original_hash(), d.clone().unwrap())) + .collect(); + + let id = tx.transaction_body.compute_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 { + 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() + .map(|output| TxOut::V2(output.clone())) + .collect(); + let fee = Value::Coin(body.fee); + let 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![])); + 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(); + let redeemers = KeyValuePairs::Indef( + tx.transaction_witness_set + .redeemer + .as_ref() + .unwrap_or(&MaybeIndefArray::Indef(vec![])) + .iter() + .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())) + .collect(), + ); + let id = tx.transaction_body.compute_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( + utxos: &[ResolvedInput], + script_purpose: &ScriptPurpose, + lookup_table: &DataLookupTable, +) -> Result { + match script_purpose { + ScriptPurpose::Minting(policy_id) => { + let policy_id_array: [u8; 28] = policy_id.to_vec().try_into().unwrap(); + let hash = Hash::from(policy_id_array); + + let script = match lookup_table.scripts.get(&hash) { + Some(s) => s.clone(), + None => { + return Err(Error::MissingRequiredScript { + hash: hash.to_string(), + }) + } + }; + Ok(ExecutionPurpose::NoDatum(script)) + } + ScriptPurpose::Spending(out_ref) => { + let utxo = match utxos.iter().find(|utxo| utxo.input == *out_ref) { + Some(resolved) => resolved, + None => return Err(Error::ResolvedInputNotFound), + }; + match &utxo.output { + TransactionOutput::Legacy(output) => { + let address = Address::from_bytes(&output.address).unwrap(); + match address { + Address::Shelley(shelley_address) => { + let hash = shelley_address.payment().as_hash(); + let script = match lookup_table.scripts.get(hash) { + Some(s) => s.clone(), + None => { + return Err(Error::MissingRequiredScript { + hash: hash.to_string(), + }) + } + }; + + let datum_hash = match &output.datum_hash { + Some(hash) => hash, + None => return Err(Error::MissingRequiredInlineDatumOrHash), + }; + + let datum = match lookup_table.datum.get(datum_hash) { + Some(d) => d.clone(), + None => { + return Err(Error::MissingRequiredDatum { + hash: datum_hash.to_string(), + }) + } + }; + + Ok(ExecutionPurpose::WithDatum(script, datum)) + } + _ => Err(Error::ScriptKeyHash), + } + } + TransactionOutput::PostAlonzo(output) => { + let address = Address::from_bytes(&output.address).unwrap(); + match address { + Address::Shelley(shelley_address) => { + let hash = shelley_address.payment().as_hash(); + let script = match lookup_table.scripts.get(hash) { + Some(s) => s.clone(), + None => { + return Err(Error::MissingRequiredScript { + hash: hash.to_string(), + }) + } + }; + + let datum = match &output.datum_option { + Some(DatumOption::Hash(hash)) => { + match lookup_table.datum.get(hash) { + Some(d) => d.clone(), + None => { + return Err(Error::MissingRequiredDatum { + hash: hash.to_string(), + }) + } + } + } + Some(DatumOption::Data(data)) => data.0.clone(), + _ => return Err(Error::MissingRequiredInlineDatumOrHash), + }; + + Ok(ExecutionPurpose::WithDatum(script, datum)) + } + _ => Err(Error::ScriptKeyHash), + } + } + } + } + ScriptPurpose::Rewarding(stake_credential) => { + let script_hash = match stake_credential { + StakeCredential::Scripthash(hash) => *hash, + _ => return Err(Error::ScriptKeyHash), + }; + + let script = match lookup_table.scripts.get(&script_hash) { + Some(s) => s.clone(), + None => { + return Err(Error::MissingRequiredScript { + hash: script_hash.to_string(), + }) + } + }; + + Ok(ExecutionPurpose::NoDatum(script)) + } + ScriptPurpose::Certifying(cert) => match cert { + Certificate::StakeDeregistration(stake_credential) => { + let script_hash = match stake_credential { + StakeCredential::Scripthash(hash) => *hash, + _ => return Err(Error::ScriptKeyHash), + }; + + let script = match lookup_table.scripts.get(&script_hash) { + Some(s) => s.clone(), + None => { + return Err(Error::MissingRequiredScript { + hash: script_hash.to_string(), + }) + } + }; + + Ok(ExecutionPurpose::NoDatum(script)) + } + Certificate::StakeDelegation(stake_credential, _) => { + let script_hash = match stake_credential { + StakeCredential::Scripthash(hash) => *hash, + _ => return Err(Error::ScriptKeyHash), + }; + + let script = match lookup_table.scripts.get(&script_hash) { + Some(s) => s.clone(), + None => { + return Err(Error::MissingRequiredScript { + hash: script_hash.to_string(), + }) + } + }; + + Ok(ExecutionPurpose::NoDatum(script)) + } + _ => Err(Error::OnlyStakeDeregAndDelegAllowed), + }, + } +} + +pub fn get_script_and_datum_lookup_table( + tx: &MintedTx, + utxos: &[ResolvedInput], +) -> DataLookupTable { + let mut datum = HashMap::new(); + let mut scripts = HashMap::new(); + + // discovery in witness set + + let plutus_data_witnesses = tx + .transaction_witness_set + .plutus_data + .clone() + .unwrap_or_default(); + + let scripts_native_witnesses = tx + .transaction_witness_set + .native_script + .clone() + .unwrap_or_default(); + + let scripts_v1_witnesses = tx + .transaction_witness_set + .plutus_v1_script + .clone() + .unwrap_or_default(); + + let scripts_v2_witnesses = tx + .transaction_witness_set + .plutus_v2_script + .clone() + .unwrap_or_default(); + + for plutus_data in plutus_data_witnesses.iter() { + datum.insert(plutus_data.original_hash(), plutus_data.clone().unwrap()); + } + + for script in scripts_native_witnesses.iter() { + scripts.insert(script.compute_hash(), ScriptVersion::Native(script.clone())); + } + + for script in scripts_v1_witnesses.iter() { + scripts.insert(script.compute_hash(), ScriptVersion::V1(script.clone())); + } + + for script in scripts_v2_witnesses.iter() { + scripts.insert(script.compute_hash(), ScriptVersion::V2(script.clone())); + } + + // discovery in utxos (script ref) + + for utxo in utxos.iter() { + match &utxo.output { + TransactionOutput::Legacy(_) => {} + TransactionOutput::PostAlonzo(output) => { + if let Some(script) = &output.script_ref { + match &script.0 { + Script::NativeScript(ns) => { + scripts.insert(ns.compute_hash(), ScriptVersion::Native(ns.clone())); + } + Script::PlutusV1Script(v1) => { + scripts.insert(v1.compute_hash(), ScriptVersion::V1(v1.clone())); + } + Script::PlutusV2Script(v2) => { + scripts.insert(v2.compute_hash(), ScriptVersion::V2(v2.clone())); + } + } + } + } + } + } + + DataLookupTable { datum, scripts } +} + +pub fn eval_redeemer( + tx: &MintedTx, + utxos: &[ResolvedInput], + slot_config: &SlotConfig, + redeemer: &Redeemer, + lookup_table: &DataLookupTable, + cost_mdls_opt: Option<&CostMdls>, + initial_budget: Option<&ExBudget>, +) -> Result { + let purpose = get_script_purpose( + redeemer, + &tx.transaction_body.inputs, + &tx.transaction_body.mint, + &tx.transaction_body.certificates, + &tx.transaction_body.withdrawals, + )?; + + let execution_purpose: ExecutionPurpose = get_execution_purpose(utxos, &purpose, lookup_table)?; + + match execution_purpose { + ExecutionPurpose::WithDatum(script_version, datum) => match script_version { + ScriptVersion::V1(script) => { + let tx_info = get_tx_info_v1(tx, utxos, slot_config)?; + let script_context = ScriptContext { tx_info, purpose }; + + let program: Program = { + let mut buffer = Vec::new(); + + let prog = Program::::from_cbor(&script.0, &mut buffer)?; + + prog.into() + }; + + let program = program + .apply_data(datum) + .apply_data(redeemer.data.clone()) + .apply_data(script_context.to_plutus_data()); + + let (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt { + let costs = if let Some(costs) = &cost_mdls.plutus_v1 { + costs + } else { + return Err(Error::V1CostModelNotFound); + }; + + program.eval_as(&Language::PlutusV1, costs, initial_budget) + } else { + program.eval_v1() + }; + + match result { + Ok(_) => (), + Err(err) => return Err(Error::Machine(err, budget, logs)), + } + + let initial_budget = match initial_budget { + Some(b) => *b, + None => ExBudget::default(), + }; + + let new_redeemer = Redeemer { + tag: redeemer.tag.clone(), + index: redeemer.index, + data: redeemer.data.clone(), + ex_units: ExUnits { + mem: (initial_budget.mem - budget.mem) as u32, + steps: (initial_budget.cpu - budget.cpu) as u64, + }, + }; + + Ok(new_redeemer) + } + ScriptVersion::V2(script) => { + let tx_info = get_tx_info_v2(tx, utxos, slot_config)?; + let script_context = ScriptContext { tx_info, purpose }; + + let program: Program = { + let mut buffer = Vec::new(); + + let prog = Program::::from_cbor(&script.0, &mut buffer)?; + + prog.into() + }; + + let program = program + .apply_data(datum) + .apply_data(redeemer.data.clone()) + .apply_data(script_context.to_plutus_data()); + + let (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt { + let costs = if let Some(costs) = &cost_mdls.plutus_v2 { + costs + } else { + return Err(Error::V2CostModelNotFound); + }; + + program.eval_as(&Language::PlutusV2, costs, initial_budget) + } else { + program.eval() + }; + + match result { + Ok(_) => (), + Err(err) => return Err(Error::Machine(err, budget, logs)), + } + + let initial_budget = match initial_budget { + Some(b) => *b, + None => ExBudget::default(), + }; + + let new_redeemer = Redeemer { + tag: redeemer.tag.clone(), + index: redeemer.index, + data: redeemer.data.clone(), + ex_units: ExUnits { + mem: (initial_budget.mem - budget.mem) as u32, + steps: (initial_budget.cpu - budget.cpu) as u64, + }, + }; + + Ok(new_redeemer) + } + ScriptVersion::Native(_) => Err(Error::NativeScriptPhaseTwo), + }, + ExecutionPurpose::NoDatum(script_version) => match script_version { + ScriptVersion::V1(script) => { + let tx_info = get_tx_info_v1(tx, utxos, slot_config)?; + let script_context = ScriptContext { tx_info, purpose }; + + let program: Program = { + let mut buffer = Vec::new(); + + let prog = Program::::from_cbor(&script.0, &mut buffer)?; + + prog.into() + }; + + let program = program + .apply_data(redeemer.data.clone()) + .apply_data(script_context.to_plutus_data()); + + let (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt { + let costs = if let Some(costs) = &cost_mdls.plutus_v1 { + costs + } else { + return Err(Error::V1CostModelNotFound); + }; + + program.eval_as(&Language::PlutusV1, costs, initial_budget) + } else { + program.eval_v1() + }; + + match result { + Ok(_) => (), + Err(err) => return Err(Error::Machine(err, budget, logs)), + } + + let initial_budget = match initial_budget { + Some(b) => *b, + None => ExBudget::default(), + }; + + let new_redeemer = Redeemer { + tag: redeemer.tag.clone(), + index: redeemer.index, + data: redeemer.data.clone(), + ex_units: ExUnits { + mem: (initial_budget.mem - budget.mem) as u32, + steps: (initial_budget.cpu - budget.cpu) as u64, + }, + }; + + Ok(new_redeemer) + } + ScriptVersion::V2(script) => { + let tx_info = get_tx_info_v2(tx, utxos, slot_config)?; + let script_context = ScriptContext { tx_info, purpose }; + + let program: Program = { + let mut buffer = Vec::new(); + + let prog = Program::::from_cbor(&script.0, &mut buffer)?; + + prog.into() + }; + + let program = program + .apply_data(redeemer.data.clone()) + .apply_data(script_context.to_plutus_data()); + + let (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt { + let costs = if let Some(costs) = &cost_mdls.plutus_v2 { + costs + } else { + return Err(Error::V2CostModelNotFound); + }; + + program.eval_as(&Language::PlutusV2, costs, initial_budget) + } else { + program.eval() + }; + + match result { + Ok(_) => (), + Err(err) => return Err(Error::Machine(err, budget, logs)), + } + + let initial_budget = match initial_budget { + Some(b) => *b, + None => ExBudget::default(), + }; + + let new_redeemer = Redeemer { + tag: redeemer.tag.clone(), + index: redeemer.index, + data: redeemer.data.clone(), + ex_units: ExUnits { + mem: (initial_budget.mem - budget.mem) as u32, + steps: (initial_budget.cpu - budget.cpu) as u64, + }, + }; + + Ok(new_redeemer) + } + ScriptVersion::Native(_) => Err(Error::NativeScriptPhaseTwo), + }, + } +} diff --git a/crates/uplc/src/tx/phase_one.rs b/crates/uplc/src/tx/phase_one.rs new file mode 100644 index 00000000..0361f4fd --- /dev/null +++ b/crates/uplc/src/tx/phase_one.rs @@ -0,0 +1,324 @@ +use std::collections::HashMap; + +use pallas_addresses::{Address, ScriptHash, ShelleyPaymentPart, StakePayload}; +use pallas_codec::utils::{KeyValuePairs, MaybeIndefArray}; +use pallas_primitives::babbage::{ + Certificate, MintedTx, PolicyId, RedeemerTag, RewardAccount, StakeCredential, TransactionOutput, +}; + +use super::{ + error::Error, + eval::{DataLookupTable, ScriptVersion}, + script_context::{ResolvedInput, ScriptPurpose}, +}; + +// TODO: include in pallas eventually? +#[derive(Debug, PartialEq, Clone)] +struct RedeemerPtr { + tag: RedeemerTag, + index: u32, +} + +type AlonzoScriptsNeeded = Vec<(ScriptPurpose, ScriptHash)>; + +// subset of phase-1 ledger checks related to scripts +pub fn eval_phase_one( + tx: &MintedTx, + utxos: &[ResolvedInput], + lookup_table: &DataLookupTable, +) -> Result<(), Error> { + let scripts_needed = scripts_needed(tx, utxos)?; + + validate_missing_scripts(&scripts_needed, lookup_table.scripts())?; + + has_exact_set_of_redeemers(tx, &scripts_needed, lookup_table.scripts())?; + + Ok(()) +} + +pub fn validate_missing_scripts( + needed: &AlonzoScriptsNeeded, + txscripts: HashMap, +) -> Result<(), Error> { + let received_hashes = txscripts.keys().copied().collect::>(); + + let needed_hashes = needed.iter().map(|x| x.1).collect::>(); + + let missing: Vec<_> = needed_hashes + .clone() + .into_iter() + .filter(|x| !received_hashes.contains(x)) + .map(|x| format!("[Missing (sh: {})]", x)) + .collect(); + + let extra: Vec<_> = received_hashes + .into_iter() + .filter(|x| !needed_hashes.contains(x)) + .map(|x| format!("[Extraneous (sh: {:?})]", x)) + .collect(); + + if !missing.is_empty() || !extra.is_empty() { + let missing_errors = missing.join(" "); + let extra_errors = extra.join(" "); + + unreachable!( + "Mismatch in required scripts: {} {}", + missing_errors, extra_errors + ); + } + + Ok(()) +} + +pub fn scripts_needed( + tx: &MintedTx, + utxos: &[ResolvedInput], +) -> Result { + let mut needed = Vec::new(); + + let txb = tx.transaction_body.clone(); + + let mut spend = Vec::new(); + + for input in txb.inputs.iter() { + let utxo = match utxos.iter().find(|utxo| utxo.input == *input) { + Some(u) => u, + None => return Err(Error::ResolvedInputNotFound), + }; + + let address = Address::from_bytes(match &utxo.output { + TransactionOutput::Legacy(output) => output.address.as_ref(), + TransactionOutput::PostAlonzo(output) => output.address.as_ref(), + })?; + + if let Address::Shelley(a) = address { + if let ShelleyPaymentPart::Script(h) = a.payment() { + spend.push((ScriptPurpose::Spending(input.clone()), *h)); + } + } + } + + let mut reward = txb + .withdrawals + .as_ref() + .unwrap_or(&KeyValuePairs::Indef(vec![])) + .iter() + .filter_map(|(acnt, _)| { + let address = Address::from_bytes(acnt).unwrap(); + + if let Address::Stake(a) = address { + if let StakePayload::Script(h) = a.payload() { + let cred = StakeCredential::Scripthash(*h); + return Some((ScriptPurpose::Rewarding(cred), *h)); + } + } + + None + }) + .collect::(); + + let mut cert = txb + .certificates + .clone() + .unwrap_or_default() + .iter() + .filter_map(|cert| { + // only Dereg and Deleg certs can require scripts + match cert { + Certificate::StakeDeregistration(StakeCredential::Scripthash(h)) => { + Some((ScriptPurpose::Certifying(cert.clone()), *h)) + } + Certificate::StakeDelegation(StakeCredential::Scripthash(h), _) => { + Some((ScriptPurpose::Certifying(cert.clone()), *h)) + } + _ => None, + } + }) + .collect::(); + + let mut mint = txb + .mint + .as_ref() + .unwrap_or(&KeyValuePairs::Indef(vec![])) + .iter() + .map(|(policy_id, _)| (ScriptPurpose::Minting(*policy_id), *policy_id)) + .collect::(); + + needed.append(&mut spend); + needed.append(&mut reward); + needed.append(&mut cert); + needed.append(&mut mint); + + Ok(needed) +} + +/// hasExactSetOfRedeemers in Ledger Spec, but we pass `txscripts` directly +pub fn has_exact_set_of_redeemers( + tx: &MintedTx, + needed: &AlonzoScriptsNeeded, + tx_scripts: HashMap, +) -> Result<(), Error> { + let mut redeemers_needed = Vec::new(); + + for (script_purpose, script_hash) in needed { + let redeemer_ptr = build_redeemer_ptr(tx, script_purpose)?; + let script = tx_scripts.get(script_hash); + + if let (Some(ptr), Some(script)) = (redeemer_ptr, script) { + match script { + ScriptVersion::V1(_) => { + redeemers_needed.push((ptr, script_purpose.clone(), *script_hash)) + } + ScriptVersion::V2(_) => { + redeemers_needed.push((ptr, script_purpose.clone(), *script_hash)) + } + ScriptVersion::Native(_) => (), + } + } + } + + let wits_redeemer_ptrs: Vec = tx + .transaction_witness_set + .redeemer + .as_ref() + .unwrap_or(&MaybeIndefArray::Indef(vec![])) + .iter() + .map(|r| RedeemerPtr { + tag: r.tag.clone(), + index: r.index, + }) + .collect(); + + let needed_redeemer_ptrs: Vec = + redeemers_needed.iter().map(|x| x.0.clone()).collect(); + + let missing: Vec<_> = redeemers_needed + .into_iter() + .filter(|x| !wits_redeemer_ptrs.contains(&x.0)) + .map(|x| { + format!( + "[Missing (redeemer_ptr: {:?}, script_purpose: {:?}, script_hash: {})]", + x.0, x.1, x.2, + ) + }) + .collect(); + + let extra: Vec<_> = wits_redeemer_ptrs + .into_iter() + .filter(|x| !needed_redeemer_ptrs.contains(x)) + .map(|x| format!("[Extraneous (redeemer_ptr: {:?})]", x)) + .collect(); + + if !missing.is_empty() || !extra.is_empty() { + Err(Error::RequiredRedeemersMismatch { missing, extra }) + } else { + Ok(()) + } +} + +/// 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 +/// placement of script purpose inside its container. +fn build_redeemer_ptr( + tx: &MintedTx, + script_purpose: &ScriptPurpose, +) -> Result, Error> { + let tx_body = tx.transaction_body.clone(); + + match script_purpose { + ScriptPurpose::Minting(hash) => { + let mut policy_ids = tx_body + .mint + .as_ref() + .unwrap_or(&KeyValuePairs::Indef(vec![])) + .iter() + .map(|(policy_id, _)| *policy_id) + .collect::>(); + + policy_ids.sort(); + + let maybe_idx = policy_ids.iter().position(|x| x == hash); + + match maybe_idx { + Some(idx) => Ok(Some(RedeemerPtr { + tag: RedeemerTag::Mint, + index: idx as u32, + })), + None => Ok(None), + } + } + ScriptPurpose::Spending(txin) => { + let mut inputs = tx_body.inputs.to_vec(); + inputs.sort_by( + |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() + .map(|(acnt, _)| acnt.clone()) + .collect::>(); + + reward_accounts.sort(); + + let mut maybe_idx = None; + + for (idx, x) in reward_accounts.iter().enumerate() { + let cred = match Address::from_bytes(x).unwrap() { + Address::Stake(a) => match a.payload() { + StakePayload::Script(sh) => StakeCredential::Scripthash(*sh), + StakePayload::Stake(_) => { + return Err(Error::ScriptKeyHash); + } + }, + _ => return Err(Error::BadWithdrawalAddress), + }; + + if cred == *racnt { + maybe_idx = Some(idx); + } + } + + match maybe_idx { + 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 { + Some(idx) => Ok(Some(RedeemerPtr { + tag: RedeemerTag::Cert, + index: idx as u32, + })), + None => Ok(None), + } + } + } +} diff --git a/crates/uplc/src/tx/script_context.rs b/crates/uplc/src/tx/script_context.rs new file mode 100644 index 00000000..65497dec --- /dev/null +++ b/crates/uplc/src/tx/script_context.rs @@ -0,0 +1,99 @@ +use pallas_codec::utils::KeyValuePairs; +use pallas_crypto::hash::Hash; +use pallas_primitives::babbage::{ + AddrKeyhash, Certificate, Coin, DatumHash, PlutusData, PolicyId, Redeemer, RewardAccount, + StakeCredential, TransactionInput, TransactionOutput, Value, Withdrawals, +}; +use serde::Deserialize; + +use super::to_plutus_data::MintValue; + +#[derive(Debug, PartialEq, Clone, Deserialize)] +pub struct ResolvedInput { + pub input: TransactionInput, + pub output: TransactionOutput, +} + +#[derive(Debug, PartialEq, Clone)] +pub struct TxInInfo { + pub out_ref: TransactionInput, + pub resolved: TxOut, +} +#[derive(Debug, PartialEq, Clone)] +pub enum TxOut { + V1(TransactionOutput), + V2(TransactionOutput), +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum ScriptPurpose { + Minting(PolicyId), + Spending(TransactionInput), + Rewarding(StakeCredential), + Certifying(Certificate), +} + +#[derive(Debug, PartialEq, Clone)] +pub struct TxInfoV1 { + pub inputs: Vec, + pub outputs: Vec, + pub fee: Value, + pub mint: MintValue, + pub dcert: Vec, + pub wdrl: Vec<(RewardAccount, Coin)>, + pub valid_range: TimeRange, + pub signatories: Vec, + pub data: Vec<(DatumHash, PlutusData)>, + pub id: Hash<32>, +} + +#[derive(Debug, PartialEq, Clone)] +pub struct TxInfoV2 { + pub inputs: Vec, + pub reference_inputs: Vec, + pub outputs: Vec, + pub fee: Value, + pub mint: MintValue, + pub dcert: Vec, + pub wdrl: Withdrawals, + pub valid_range: TimeRange, + pub signatories: Vec, + pub redeemers: KeyValuePairs, + pub data: KeyValuePairs, + pub id: Hash<32>, +} + +#[derive(Debug, PartialEq, Clone)] +pub enum TxInfo { + V1(TxInfoV1), + V2(TxInfoV2), +} + +#[derive(Debug, PartialEq, Clone)] +pub struct ScriptContext { + pub tx_info: TxInfo, + pub purpose: ScriptPurpose, +} + +//---- Time conversion: slot range => posix time range +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct TimeRange { + pub lower_bound: Option, + pub upper_bound: Option, +} + +pub struct SlotConfig { + pub slot_length: u64, + pub zero_slot: u64, + pub zero_time: u64, +} + +impl Default for SlotConfig { + fn default() -> Self { + Self { + slot_length: 1000, + zero_slot: 4492800, + zero_time: 1596059091000, + } + } +} diff --git a/crates/uplc/src/tx/tests.rs b/crates/uplc/src/tx/tests.rs new file mode 100644 index 00000000..1b56e242 --- /dev/null +++ b/crates/uplc/src/tx/tests.rs @@ -0,0 +1,1719 @@ +use pallas_codec::utils::MaybeIndefArray; +use pallas_primitives::{ + babbage::{CostMdls, TransactionInput, TransactionOutput}, + Fragment, +}; +use pallas_traverse::{Era, MultiEraTx}; + +use crate::machine::cost_model::ExBudget; + +use super::{eval_phase_two, ResolvedInput, SlotConfig}; + +#[test] +fn test_eval() { + /* + + PlutusV2 + + {-# INLINEABLE mintTestValidator #-} + mintTestValidator :: () -> Api.ScriptContext -> Bool + mintTestValidator _ ctx = Api.txInfoFee txInfo == Api.txInfoFee txInfo && (case Api.txInfoSignatories txInfo of [] -> True) + + where + txInfo :: Api.TxInfo + txInfo = Api.scriptContextTxInfo ctx */ + + let tx_bytes = hex::decode("84a80081825820975c17a4fed0051be622328efa548e206657d2b65a19224bf6ff8132571e6a5002018282581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f67235821a000f41f0a1581cc4f241450001af08f3ddbaf9335db79883cbcd81071b8e3508de3055a1400a82581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a0084192f021a00053b6109a1581cc4f241450001af08f3ddbaf9335db79883cbcd81071b8e3508de3055a1400a0b5820b4f96b0acec8beff2adededa8ba317bcac92174f0f65ccefe569b9a6aac7375a0d818258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d011082581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af0cdfa2111a0007d912a3008182582031ae74f8058527afb305d7495b10a99422d9337fc199e1f28044f2c477a0f9465840b8b97b7c3b4e19ecfc2fcd9884ee53a35887ee6e4d36901b9ecbac3fe032d7e8a4358305afa573a86396e378255651ed03501906e9def450e588d4bb36f42a050581840100d87980821a000b68081a0cf3a5bf06815909b25909af010000323322323232323232323232323232323232323232332232323232323232323233223232223232533533223233025323233355300f1200135028502623500122333553012120013502b50292350012233350012330314800000488cc0c80080048cc0c400520000013355300e1200123500122335501c0023335001233553012120012350012233550200023550140010012233355500f0150020012335530121200123500122335502000235501300100133355500a01000200130105002300f5001533532350012222222222220045001102a2216135001220023333573466e1cd55ce9baa0044800080808c98c8080cd5ce01081000f1999ab9a3370e6aae7540092000233221233001003002323232323232323232323232323333573466e1cd55cea8062400046666666666664444444444442466666666666600201a01801601401201000e00c00a00800600466a03803a6ae854030cd4070074d5d0a80599a80e00f1aba1500a3335502075ca03e6ae854024ccd54081d7280f9aba1500833501c02835742a00e666aa040052eb4d5d0a8031919191999ab9a3370e6aae75400920002332212330010030023232323333573466e1cd55cea8012400046644246600200600466a066eb4d5d0a801181a1aba135744a004464c6406c66ae700dc0d80d04d55cf280089baa00135742a0046464646666ae68cdc39aab9d5002480008cc8848cc00400c008cd40cdd69aba150023034357426ae8940088c98c80d8cd5ce01b81b01a09aab9e5001137540026ae84d5d1280111931901919ab9c033032030135573ca00226ea8004d5d0a80299a80e3ae35742a008666aa04004a40026ae85400cccd54081d710009aba150023027357426ae8940088c98c80b8cd5ce01781701609aba25001135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba150023017357426ae8940088c98c8080cd5ce01081000f080f89931900f99ab9c4901035054350001f135573ca00226ea8004444888ccd54c010480054040cd54c01c480048d400488cd54054008d54024004ccd54c0104800488d4008894cd4ccd54c03048004c8cd409c88ccd400c88008008004d40048800448cc004894cd400840b040040a48d400488cc028008014018400c4cd405001000d4044004cd54c01c480048d400488c8cd5405800cc004014c8004d540a4894cd40044d5402800c884d4008894cd4cc03000802044888cc0080280104c01800c008c8004d5408888448894cd40044008884cc014008ccd54c01c480040140100044484888c00c0104484888c004010c8004d5407c8844894cd400454038884cd403cc010008cd54c01848004010004c8004d5407888448894cd40044d400c88004884ccd401488008c010008ccd54c01c4800401401000488ccd5cd19b8f00200101e01d2350012222222222220091232230023758002640026aa038446666aae7c004940288cd4024c010d5d080118019aba2002015232323333573466e1cd55cea80124000466442466002006004601a6ae854008c014d5d09aba2500223263201533573802c02a02626aae7940044dd50009191919191999ab9a3370e6aae75401120002333322221233330010050040030023232323333573466e1cd55cea80124000466442466002006004602c6ae854008cd4040054d5d09aba2500223263201a33573803603403026aae7940044dd50009aba150043335500875ca00e6ae85400cc8c8c8cccd5cd19b875001480108c84888c008010d5d09aab9e500323333573466e1d4009200223212223001004375c6ae84d55cf280211999ab9a3370ea00690001091100191931900e19ab9c01d01c01a019018135573aa00226ea8004d5d0a80119a8063ae357426ae8940088c98c8058cd5ce00b80b00a09aba25001135744a00226aae7940044dd5000899aa800bae75a224464460046eac004c8004d5406488c8cccd55cf80112804119a80399aa80498031aab9d5002300535573ca00460086ae8800c04c4d5d08008891001091091198008020018891091980080180109119191999ab9a3370ea0029000119091180100198029aba135573ca00646666ae68cdc3a801240044244002464c6402066ae700440400380344d55cea80089baa001232323333573466e1d400520062321222230040053007357426aae79400c8cccd5cd19b875002480108c848888c008014c024d5d09aab9e500423333573466e1d400d20022321222230010053007357426aae7940148cccd5cd19b875004480008c848888c00c014dd71aba135573ca00c464c6402066ae7004404003803403002c4d55cea80089baa001232323333573466e1cd55cea80124000466442466002006004600a6ae854008dd69aba135744a004464c6401866ae700340300284d55cf280089baa0012323333573466e1cd55cea800a400046eb8d5d09aab9e500223263200a33573801601401026ea80048c8c8c8c8c8cccd5cd19b8750014803084888888800c8cccd5cd19b875002480288488888880108cccd5cd19b875003480208cc8848888888cc004024020dd71aba15005375a6ae84d5d1280291999ab9a3370ea00890031199109111111198010048041bae35742a00e6eb8d5d09aba2500723333573466e1d40152004233221222222233006009008300c35742a0126eb8d5d09aba2500923333573466e1d40192002232122222223007008300d357426aae79402c8cccd5cd19b875007480008c848888888c014020c038d5d09aab9e500c23263201333573802802602202001e01c01a01801626aae7540104d55cf280189aab9e5002135573ca00226ea80048c8c8c8c8cccd5cd19b875001480088ccc888488ccc00401401000cdd69aba15004375a6ae85400cdd69aba135744a00646666ae68cdc3a80124000464244600400660106ae84d55cf280311931900619ab9c00d00c00a009135573aa00626ae8940044d55cf280089baa001232323333573466e1d400520022321223001003375c6ae84d55cf280191999ab9a3370ea004900011909118010019bae357426aae7940108c98c8024cd5ce00500480380309aab9d50011375400224464646666ae68cdc3a800a40084244400246666ae68cdc3a8012400446424446006008600c6ae84d55cf280211999ab9a3370ea00690001091100111931900519ab9c00b00a008007006135573aa00226ea80048c8cccd5cd19b8750014800880348cccd5cd19b8750024800080348c98c8018cd5ce00380300200189aab9d37540029309000a4810350543100112330010020072253350021001100612335002223335003220020020013500122001122123300100300222333573466e1c00800401000c488008488004448c8c00400488cc00cc008008005f5f6").unwrap(); + + let raw_inputs = hex::decode("84825820b16778c9cf065d9efeefe37ec269b4fc5107ecdbd0dd6bf3274b224165c2edd9008258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d01825820975c17a4fed0051be622328efa548e206657d2b65a19224bf6ff8132571e6a500282582018f86700660fc88d0370a8f95ea58f75507e6b27a18a17925ad3b1777eb0d77600").unwrap(); + let raw_outputs = hex::decode("8482581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f67235821a000f8548a1581c15be994a64bdb79dde7fe080d8e7ff81b33a9e4860e9ee0d857a8e85a144576177610182581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af14b8b482581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a0098968082581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a00acd8c6").unwrap(); + + let inputs = Vec::::decode_fragment(&raw_inputs).unwrap(); + let outputs = Vec::::decode_fragment(&raw_outputs).unwrap(); + + let utxos: Vec = inputs + .iter() + .zip(outputs.iter()) + .map(|(input, output)| ResolvedInput { + input: input.clone(), + output: output.clone(), + }) + .collect(); + + let slot_config = SlotConfig { + zero_time: 1660003200000, // Preview network + zero_slot: 0, + slot_length: 1000, + }; + + let costs: Vec = vec![ + 205665, + 812, + 1, + 1, + 1000, + 571, + 0, + 1, + 1000, + 24177, + 4, + 1, + 1000, + 32, + 117366, + 10475, + 4, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 100, + 100, + 23000, + 100, + 19537, + 32, + 175354, + 32, + 46417, + 4, + 221973, + 511, + 0, + 1, + 89141, + 32, + 497525, + 14068, + 4, + 2, + 196500, + 453240, + 220, + 0, + 1, + 1, + 1000, + 28662, + 4, + 2, + 245000, + 216773, + 62, + 1, + 1060367, + 12586, + 1, + 208512, + 421, + 1, + 187000, + 1000, + 52998, + 1, + 80436, + 32, + 43249, + 32, + 1000, + 32, + 80556, + 1, + 57667, + 4, + 1000, + 10, + 197145, + 156, + 1, + 197145, + 156, + 1, + 204924, + 473, + 1, + 208896, + 511, + 1, + 52467, + 32, + 64832, + 32, + 65493, + 32, + 22558, + 32, + 16563, + 32, + 76511, + 32, + 196500, + 453240, + 220, + 0, + 1, + 1, + 69522, + 11687, + 0, + 1, + 60091, + 32, + 196500, + 453240, + 220, + 0, + 1, + 1, + 196500, + 453240, + 220, + 0, + 1, + 1, + 1159724, + 392670, + 0, + 2, + 806990, + 30482, + 4, + 1927926, + 82523, + 4, + 265318, + 0, + 4, + 0, + 85931, + 32, + 205665, + 812, + 1, + 1, + 41182, + 32, + 212342, + 32, + 31220, + 32, + 32696, + 32, + 43357, + 32, + 32247, + 32, + 38314, + 32, + 20000000000, + 20000000000, + 9462713, + 1021, + 10, + 20000000000, + 0, + 20000000000, + ]; + + let cost_mdl = CostMdls { + plutus_v1: None, + plutus_v2: Some(costs), + }; + + let initial_budget = ExBudget { + cpu: 10000000000, + mem: 14000000, + }; + + let multi_era_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) + .unwrap(); + match multi_era_tx { + MultiEraTx::Babbage(tx) => { + let redeemers = eval_phase_two( + &tx, + &utxos, + Some(&cost_mdl), + Some(&initial_budget), + &slot_config, + false, + ) + .unwrap(); + + assert_eq!(redeemers.len(), 1); + + let total_budget_used = + redeemers + .iter() + .fold(ExBudget { mem: 0, cpu: 0 }, |accum, curr| ExBudget { + mem: accum.mem + curr.ex_units.mem as i64, + cpu: accum.cpu + curr.ex_units.steps as i64, + }); + + println!("{:?}", total_budget_used); + + assert_eq!( + total_budget_used, + // Numbers came uplc evaluate + ExBudget { + cpu: 217294271, + mem: 747528 + } + ); + } + _ => unreachable!(), + }; +} + +#[test] +fn test_eval_1() { + /* + + Haskell PLC: (738928, 215316271) + Aiken PLC: (721484, 210171376) + + PlutusV2 + + {-# INLINEABLE mintTestValidator #-} + mintTestValidator :: () -> Api.ScriptContext -> Bool + mintTestValidator _ ctx = Api.txInfoFee txInfo == Api.txInfoFee txInfo + + where + txInfo :: Api.TxInfo + txInfo = Api.scriptContextTxInfo ctx */ + + let tx_bytes = hex::decode("84a800818258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d01018282581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f67235821a000f41f0a1581c88c9dfd60601e22509d58b904c2730fe2bdef6a52a41a6f376b0ba94a1400a82581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af004152021a0005357209a1581c88c9dfd60601e22509d58b904c2730fe2bdef6a52a41a6f376b0ba94a1400a0b5820ff1a62ad8cb2d73ffb8687471e2c99b48bf3b067966a7ea9285f95adcee708a20d818258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d011082581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af0ce889111a0007d02ba3008182582031ae74f8058527afb305d7495b10a99422d9337fc199e1f28044f2c477a0f9465840b3dde25e3a2b825d3f120955211e722cc4a7c65fa67697076f2725a2ed0adec0d4bfc742934fe7c29d475bb0630aed1b1cdcf5fac9e06d84976455a661b0dc080581840100d87980821a000b46701a0cd5772f068159099a59099701000032332232323232323232323232323232323232323322323232323232323232332232322232325335332232323233355300f1200135027502623500122333553012120013502a50292350012233350012330304800000488cc0c40080048cc0c000520000013355300e1200123500122335501c0023335001233553012120012350012233550200023550140010012233355500f0150020012335530121200123500122335502000235501300100133355500a01000200130105002300f5001135001220023333573466e1cd55ce9baa0044800080808c98c8080cd5ce01081000f1999ab9a3370e6aae7540092000233221233001003002323232323232323232323232323333573466e1cd55cea8062400046666666666664444444444442466666666666600201a01801601401201000e00c00a00800600466a03803a6ae854030cd4070074d5d0a80599a80e00f1aba1500a3335502075ca03e6ae854024ccd54081d7280f9aba1500833501c02835742a00e666aa040052eb4d5d0a8031919191999ab9a3370e6aae75400920002332212330010030023232323333573466e1cd55cea8012400046644246600200600466a066eb4d5d0a801181a1aba135744a004464c6406c66ae700dc0d80d04d55cf280089baa00135742a0046464646666ae68cdc39aab9d5002480008cc8848cc00400c008cd40cdd69aba150023034357426ae8940088c98c80d8cd5ce01b81b01a09aab9e5001137540026ae84d5d1280111931901919ab9c033032030135573ca00226ea8004d5d0a80299a80e3ae35742a008666aa04004a40026ae85400cccd54081d710009aba150023027357426ae8940088c98c80b8cd5ce01781701609aba25001135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba150023017357426ae8940088c98c8080cd5ce01081000f080f89931900f99ab9c491035054350001f135573ca00226ea8004444888ccd54c010480054040cd54c01c480048d400488cd54054008d54024004ccd54c0104800488d4008894cd4ccd54c03048004c8cd409888ccd400c88008008004d40048800448cc004894cd400840ac40040a08d400488cc028008014018400c4cd405001000d4044004cd54c01c480048d400488c8cd5405800cc004014c8004d540a0894cd40044d5402800c884d4008894cd4cc03000802044888cc0080280104c01800c008c8004d5408488448894cd40044008884cc014008ccd54c01c480040140100044484888c00c0104484888c004010c8004d540788844894cd400454038884cd403cc010008cd54c01848004010004c8004d5407488448894cd40044d400c88004884ccd401488008c010008ccd54c01c4800401401000488ccd5cd19b8f00200101d01c2350012222222222220091232230023758002640026aa036446666aae7c004940288cd4024c010d5d080118019aba2002015232323333573466e1cd55cea80124000466442466002006004601a6ae854008c014d5d09aba2500223263201533573802c02a02626aae7940044dd50009191919191999ab9a3370e6aae75401120002333322221233330010050040030023232323333573466e1cd55cea80124000466442466002006004602c6ae854008cd4040054d5d09aba2500223263201a33573803603403026aae7940044dd50009aba150043335500875ca00e6ae85400cc8c8c8cccd5cd19b875001480108c84888c008010d5d09aab9e500323333573466e1d4009200223212223001004375c6ae84d55cf280211999ab9a3370ea00690001091100191931900e19ab9c01d01c01a019018135573aa00226ea8004d5d0a80119a8063ae357426ae8940088c98c8058cd5ce00b80b00a09aba25001135744a00226aae7940044dd5000899aa800bae75a224464460046eac004c8004d5406088c8cccd55cf80112804119a80399aa80498031aab9d5002300535573ca00460086ae8800c04c4d5d08008891001091091198008020018891091980080180109119191999ab9a3370ea0029000119091180100198029aba135573ca00646666ae68cdc3a801240044244002464c6402066ae700440400380344d55cea80089baa001232323333573466e1d400520062321222230040053007357426aae79400c8cccd5cd19b875002480108c848888c008014c024d5d09aab9e500423333573466e1d400d20022321222230010053007357426aae7940148cccd5cd19b875004480008c848888c00c014dd71aba135573ca00c464c6402066ae7004404003803403002c4d55cea80089baa001232323333573466e1cd55cea80124000466442466002006004600a6ae854008dd69aba135744a004464c6401866ae700340300284d55cf280089baa0012323333573466e1cd55cea800a400046eb8d5d09aab9e500223263200a33573801601401026ea80048c8c8c8c8c8cccd5cd19b8750014803084888888800c8cccd5cd19b875002480288488888880108cccd5cd19b875003480208cc8848888888cc004024020dd71aba15005375a6ae84d5d1280291999ab9a3370ea00890031199109111111198010048041bae35742a00e6eb8d5d09aba2500723333573466e1d40152004233221222222233006009008300c35742a0126eb8d5d09aba2500923333573466e1d40192002232122222223007008300d357426aae79402c8cccd5cd19b875007480008c848888888c014020c038d5d09aab9e500c23263201333573802802602202001e01c01a01801626aae7540104d55cf280189aab9e5002135573ca00226ea80048c8c8c8c8cccd5cd19b875001480088ccc888488ccc00401401000cdd69aba15004375a6ae85400cdd69aba135744a00646666ae68cdc3a80124000464244600400660106ae84d55cf280311931900619ab9c00d00c00a009135573aa00626ae8940044d55cf280089baa001232323333573466e1d400520022321223001003375c6ae84d55cf280191999ab9a3370ea004900011909118010019bae357426aae7940108c98c8024cd5ce00500480380309aab9d50011375400224464646666ae68cdc3a800a40084244400246666ae68cdc3a8012400446424446006008600c6ae84d55cf280211999ab9a3370ea00690001091100111931900519ab9c00b00a008007006135573aa00226ea80048c8cccd5cd19b8750014800880308cccd5cd19b8750024800080308c98c8018cd5ce00380300200189aab9d37540029309000a4810350543100112330012253350021001100700612335002223335003220020020013500122001122123300100300222333573466e1c00800401000c488008488004448c8c00400488cc00cc0080080041f5f6").unwrap(); + + let raw_inputs = hex::decode("84825820b16778c9cf065d9efeefe37ec269b4fc5107ecdbd0dd6bf3274b224165c2edd9008258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d01825820975c17a4fed0051be622328efa548e206657d2b65a19224bf6ff8132571e6a500282582018f86700660fc88d0370a8f95ea58f75507e6b27a18a17925ad3b1777eb0d77600").unwrap(); + let raw_outputs = hex::decode("8482581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f67235821a000f8548a1581c15be994a64bdb79dde7fe080d8e7ff81b33a9e4860e9ee0d857a8e85a144576177610182581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af14b8b482581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a0098968082581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a00acd8c6").unwrap(); + + let inputs = MaybeIndefArray::::decode_fragment(&raw_inputs).unwrap(); + let outputs = MaybeIndefArray::::decode_fragment(&raw_outputs).unwrap(); + + let utxos: MaybeIndefArray = MaybeIndefArray::Indef( + inputs + .iter() + .zip(outputs.iter()) + .map(|(input, output)| ResolvedInput { + input: input.clone(), + output: output.clone(), + }) + .collect(), + ); + + let slot_config = SlotConfig { + zero_time: 1660003200000, // Preview network + zero_slot: 0, + slot_length: 1000, + }; + + let costs: Vec = vec![ + 205665, + 812, + 1, + 1, + 1000, + 571, + 0, + 1, + 1000, + 24177, + 4, + 1, + 1000, + 32, + 117366, + 10475, + 4, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 100, + 100, + 23000, + 100, + 19537, + 32, + 175354, + 32, + 46417, + 4, + 221973, + 511, + 0, + 1, + 89141, + 32, + 497525, + 14068, + 4, + 2, + 196500, + 453240, + 220, + 0, + 1, + 1, + 1000, + 28662, + 4, + 2, + 245000, + 216773, + 62, + 1, + 1060367, + 12586, + 1, + 208512, + 421, + 1, + 187000, + 1000, + 52998, + 1, + 80436, + 32, + 43249, + 32, + 1000, + 32, + 80556, + 1, + 57667, + 4, + 1000, + 10, + 197145, + 156, + 1, + 197145, + 156, + 1, + 204924, + 473, + 1, + 208896, + 511, + 1, + 52467, + 32, + 64832, + 32, + 65493, + 32, + 22558, + 32, + 16563, + 32, + 76511, + 32, + 196500, + 453240, + 220, + 0, + 1, + 1, + 69522, + 11687, + 0, + 1, + 60091, + 32, + 196500, + 453240, + 220, + 0, + 1, + 1, + 196500, + 453240, + 220, + 0, + 1, + 1, + 1159724, + 392670, + 0, + 2, + 806990, + 30482, + 4, + 1927926, + 82523, + 4, + 265318, + 0, + 4, + 0, + 85931, + 32, + 205665, + 812, + 1, + 1, + 41182, + 32, + 212342, + 32, + 31220, + 32, + 32696, + 32, + 43357, + 32, + 32247, + 32, + 38314, + 32, + 20000000000, + 20000000000, + 9462713, + 1021, + 10, + 20000000000, + 0, + 20000000000, + ]; + + let cost_mdl = CostMdls { + plutus_v1: None, + plutus_v2: Some(costs), + }; + + let initial_budget = ExBudget { + cpu: 10000000000, + mem: 14000000, + }; + + let multi_era_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) + .unwrap(); + match multi_era_tx { + MultiEraTx::Babbage(tx) => { + let redeemers = eval_phase_two( + &tx, + &utxos, + Some(&cost_mdl), + Some(&initial_budget), + &slot_config, + false, + ) + .unwrap(); + + assert_eq!(redeemers.len(), 1); + + let total_budget_used = + redeemers + .iter() + .fold(ExBudget { mem: 0, cpu: 0 }, |accum, curr| ExBudget { + mem: accum.mem + curr.ex_units.mem as i64, + cpu: accum.cpu + curr.ex_units.steps as i64, + }); + + println!("{:?}", total_budget_used); + + assert_eq!( + total_budget_used, + // Numbers came uplc evaluate + ExBudget { + cpu: 215316271, + mem: 738928 + } + ); + } + _ => unreachable!(), + }; +} + +#[test] +fn test_eval_2() { + /* + + Haskell PLC: (655782, 188449458) + Aiken PLC: (638338, 183304563) + + Plutus V1 + + {-# INLINEABLE mintTestValidator #-} + mintTestValidator :: () -> Api.ScriptContext -> Bool + mintTestValidator _ ctx = Api.txInfoFee txInfo == Api.txInfoFee txInfo + + where + txInfo :: Api.TxInfo + txInfo = Api.scriptContextTxInfo ctx */ + + let tx_bytes = hex::decode("84a800818258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d01018282581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f67235821a000f41f0a1581ccba7bc9e83499376b6ad49304157778dba7c14bd748e4fd31792a930a1400a82581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af006ac1021a00050c0309a1581ccba7bc9e83499376b6ad49304157778dba7c14bd748e4fd31792a930a1400a0b5820eb3b868ec2b33dffaf5d5481703ed00870333812b96e0f75ae89fd150b4744300d818258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d011082581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af0d26af111a00079205a3008182582031ae74f8058527afb305d7495b10a99422d9337fc199e1f28044f2c477a0f94658405a2dd70be89483bd6291a018c6ca91328dad37e092fdeab2eea14685004878da5f1e5962f35d771498bf54e79be3dcf922ea93b46a1356960dcf0bfd80a91b0b038159094259093f010000323322323232323232323232323232323232323233223232323232323232332232322232325335332232323233355300f12001350265025235001223335530121200135029502823500122333500123302f4800000488cc0c00080048cc0bc00520000013355300e120012350012233550250023335001233553012120012350012233550290023550140010012233355500f0150020012335530121200123500122335502900235501300100133355500a01000200130105002300f5001135001220023333573466e1cd55ce9baa00448000807c8c98c8078cd5ce01000f80e1999ab9a3370e6aae754009200023322123300100300232323232323232323232323333573466e1cd55cea8052400046666666666444444444424666666666600201601401201000e00c00a00800600466a034464646666ae68cdc39aab9d5002480008cc8848cc00400c008c094d5d0a801180f9aba135744a004464c6405c66ae700c00bc0b04d55cf280089baa00135742a01466a0340366ae854024ccd54075d7280e1aba150083335501d75ca0386ae85401ccd4068094d5d0a80319a80d19aa8140133ad35742a00a6464646666ae68cdc39aab9d5002480008cc8848cc00400c008c8c8c8cccd5cd19b8735573aa004900011991091980080180119a815bad35742a00460586ae84d5d1280111931901919ab9c034033030135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae754009200023322123300100300233502b75a6ae854008c0b0d5d09aba2500223263203233573806806606026aae7940044dd50009aba135744a004464c6405c66ae700c00bc0b04d55cf280089baa00135742a00866a034eb8d5d0a80199a80d19aa8143ae200135742a00460446ae84d5d1280111931901519ab9c02c02b028135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea0029003119091111802002980e9aba135573ca00646666ae68cdc3a8012400846424444600400a603e6ae84d55cf280211999ab9a3370ea0069001119091111800802980d9aba135573ca00a46666ae68cdc3a8022400046424444600600a6eb8d5d09aab9e500623263202533573804e04c04604404204026aae7540044dd50009aba135744a004464c6403c66ae7008007c07040784c98c8074cd5ce249035054350001e135573ca00226ea8004444888ccd54c01048005403ccd54c01c480048d400488cd54078008d54024004ccd54c0104800488d4008894cd4ccd54c03048004c8cd409488ccd400c88008008004d40048800448cc004894cd400840a8400409c8d400488cc028008014018400c4cd404c01000d4040004cd54c01c480048d400488c8cd5407c00cc004014c8004d5409c894cd40044d5402800c884d4008894cd4cc03000802044888cc0080280104c01800c008c8004d5408088448894cd40044008884cc014008ccd54c01c480040140100044484888c00c0104484888c004010c8004d540748844894cd400454034884cd4038c010008cd54c01848004010004c8004d5407088448894cd40044d400c88004884ccd401488008c010008ccd54c01c4800401401000488ccd5cd19b8f00200101c01b23500122222222220081232230023758002640026aa034446666aae7c004940248cd4020c010d5d080118019aba200201423232323333573466e1cd55cea801a40004666444246660020080060046464646666ae68cdc39aab9d5002480008cc8848cc00400c008c054d5d0a80119a80700a1aba135744a004464c6403066ae700680640584d55cf280089baa00135742a006666aa00eeb94018d5d0a80119a8053ae357426ae8940088c98c8050cd5ce00b00a80909aba25001135573ca00226ea80044cd54005d73ad112232230023756002640026aa03044646666aae7c008940208cd401ccd5404cc018d55cea80118029aab9e500230043574400602626ae840044488008488488cc00401000c488c8c8cccd5cd19b875001480008c8488c00800cc014d5d09aab9e500323333573466e1d40092002212200123263201033573802402201c01a26aae7540044dd5000919191999ab9a3370e6aae7540092000233221233001003002300535742a0046eb4d5d09aba2500223263200d33573801e01c01626aae7940044dd50009191999ab9a3370e6aae75400520002375c6ae84d55cf280111931900599ab9c00d00c0091375400224464646666ae68cdc3a800a40084244400246666ae68cdc3a8012400446424446006008600c6ae84d55cf280211999ab9a3370ea00690001091100111931900719ab9c01000f00c00b00a135573aa00226ea80048c8cccd5cd19b8750014800880448cccd5cd19b8750024800080448c98c8028cd5ce00600580400389aab9d3754002464646464646666ae68cdc3a800a401842444444400646666ae68cdc3a8012401442444444400846666ae68cdc3a801a40104664424444444660020120106eb8d5d0a8029bad357426ae8940148cccd5cd19b875004480188cc8848888888cc008024020dd71aba15007375c6ae84d5d1280391999ab9a3370ea00a900211991091111111980300480418061aba15009375c6ae84d5d1280491999ab9a3370ea00c900111909111111180380418069aba135573ca01646666ae68cdc3a803a400046424444444600a010601c6ae84d55cf280611931900919ab9c01401301000f00e00d00c00b00a135573aa00826aae79400c4d55cf280109aab9e5001137540024646464646666ae68cdc3a800a4004466644424466600200a0080066eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d4009200023212230020033008357426aae7940188c98c802ccd5ce00680600480409aab9d5003135744a00226aae7940044dd5000919191999ab9a3370ea002900111909118008019bae357426aae79400c8cccd5cd19b875002480008c8488c00800cdd71aba135573ca008464c6401066ae700280240180144d55cea80089baa0011122232323333573466e1cd55cea80124000466aa010600c6ae854008c014d5d09aba2500223263200833573801401200c26aae7940044dd5000a4c22442466002006004240029210350543100112330012253350021001100700612335002223335003220020020013500122001122123300100300222333573466e1c00800401000c488008488004448c8c00400488cc00cc00800800410581840100d87980821a000a01a61a0b3b82b2f5f6").unwrap(); + + let raw_inputs = hex::decode("84825820b16778c9cf065d9efeefe37ec269b4fc5107ecdbd0dd6bf3274b224165c2edd9008258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d01825820975c17a4fed0051be622328efa548e206657d2b65a19224bf6ff8132571e6a500282582018f86700660fc88d0370a8f95ea58f75507e6b27a18a17925ad3b1777eb0d77600").unwrap(); + let raw_outputs = hex::decode("8482581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f67235821a000f8548a1581c15be994a64bdb79dde7fe080d8e7ff81b33a9e4860e9ee0d857a8e85a144576177610182581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af14b8b482581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a0098968082581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a00acd8c6").unwrap(); + + let inputs = MaybeIndefArray::::decode_fragment(&raw_inputs).unwrap(); + let outputs = MaybeIndefArray::::decode_fragment(&raw_outputs).unwrap(); + + let utxos: MaybeIndefArray = MaybeIndefArray::Indef( + inputs + .iter() + .zip(outputs.iter()) + .map(|(input, output)| ResolvedInput { + input: input.clone(), + output: output.clone(), + }) + .collect(), + ); + + let slot_config = SlotConfig { + zero_time: 1660003200000, // Preview network + zero_slot: 0, + slot_length: 1000, + }; + + let costs: Vec = vec![ + 205665, 812, 1, 1, 1000, 571, 0, 1, 1000, 24177, 4, 1, 1000, 32, 117366, 10475, 4, 23000, + 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 100, 100, 23000, 100, + 19537, 32, 175354, 32, 46417, 4, 221973, 511, 0, 1, 89141, 32, 497525, 14068, 4, 2, 196500, + 453240, 220, 0, 1, 1, 1000, 28662, 4, 2, 245000, 216773, 62, 1, 1060367, 12586, 1, 208512, + 421, 1, 187000, 1000, 52998, 1, 80436, 32, 43249, 32, 1000, 32, 80556, 1, 57667, 4, 1000, + 10, 197145, 156, 1, 197145, 156, 1, 204924, 473, 1, 208896, 511, 1, 52467, 32, 64832, 32, + 65493, 32, 22558, 32, 16563, 32, 76511, 32, 196500, 453240, 220, 0, 1, 1, 69522, 11687, 0, + 1, 60091, 32, 196500, 453240, 220, 0, 1, 1, 196500, 453240, 220, 0, 1, 1, 806990, 30482, 4, + 1927926, 82523, 4, 265318, 0, 4, 0, 85931, 32, 205665, 812, 1, 1, 41182, 32, 212342, 32, + 31220, 32, 32696, 32, 43357, 32, 32247, 32, 38314, 32, 9462713, 1021, 10, + ]; + + let cost_mdl = CostMdls { + plutus_v1: Some(costs), + plutus_v2: None, + }; + + let initial_budget = ExBudget { + cpu: 10000000000, + mem: 14000000, + }; + + let multi_era_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) + .unwrap(); + match multi_era_tx { + MultiEraTx::Babbage(tx) => { + let redeemers = eval_phase_two( + &tx, + &utxos, + Some(&cost_mdl), + Some(&initial_budget), + &slot_config, + false, + ) + .unwrap(); + + assert_eq!(redeemers.len(), 1); + + let total_budget_used = + redeemers + .iter() + .fold(ExBudget { mem: 0, cpu: 0 }, |accum, curr| ExBudget { + mem: accum.mem + curr.ex_units.mem as i64, + cpu: accum.cpu + curr.ex_units.steps as i64, + }); + + println!("{:?}", total_budget_used); + + assert_eq!( + total_budget_used, + // Numbers came uplc evaluate + ExBudget { + cpu: 188449458, + mem: 655782 + } + ); + } + _ => unreachable!(), + }; +} + +#[test] +fn test_eval_3() { + /* + + Plutus V2 + + {-# INLINEABLE mintTestValidator #-} + mintTestValidator :: () -> Api.ScriptContext -> Bool + mintTestValidator _ ctx = True + */ + + let tx_bytes = hex::decode("84a80081825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c50010182825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a00111958a1581c74184f56338d719f7888d9719ff0c8a9d9550c0f46165e09831cdba9a1400a825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a029d7d51021a0004ba2809a1581c74184f56338d719f7888d9719ff0c8a9d9550c0f46165e09831cdba9a1400a0b5820901a846f82e9b756b94686bc56109ca62e7724cbb99091b0b971bcb1c8ce98530d81825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c500110825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02ac3995111a0007173ca30081825820065dd553fbe4e240a8f819bb9e333a7483de4a22b65c7fb6a95ce9450f84dff7584063f5dd2edfd43a5c0fc066d0230e5a5ba4b201bc75b22f94bfa5344c0ff9cd08c0018b5cc0f0e34cd2ab4e8c59954834330ba63a1b875c0cb3b5fd1ac68db4060581840100d87980821a0008e6681a0ae626b7068159078259077f0100003232323232323232323232323232332232323232322232325335332201a3333573466e1cd55ce9baa00448000805c8c98c805ccd5ce00c00b80a9999ab9a3370e6aae7540092000233221233001003002323232323232323232323232323333573466e1cd55cea8062400046666666666664444444444442466666666666600201a01801601401201000e00c00a00800600466a02802a6ae854030cd4050054d5d0a80599a80a00b1aba1500a3335501875ca02e6ae854024ccd54061d7280b9aba1500833501401f35742a00e666aa030040eb4d5d0a8031919191999ab9a3370e6aae75400920002332212330010030023232323333573466e1cd55cea8012400046644246600200600466a054eb4d5d0a80118159aba135744a004464c6405a66ae700b80b40ac4d55cf280089baa00135742a0046464646666ae68cdc39aab9d5002480008cc8848cc00400c008cd40a9d69aba15002302b357426ae8940088c98c80b4cd5ce01701681589aab9e5001137540026ae84d5d1280111931901499ab9c02a029027135573ca00226ea8004d5d0a80299a80a3ae35742a008666aa03003840026ae85400cccd54061d710009aba15002301e357426ae8940088c98c8094cd5ce01301281189aba25001135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba15002300e357426ae8940088c98c805ccd5ce00c00b80a880b09931900b19ab9c4910350543500016135573ca00226ea800448c88c008dd6000990009aa80a911999aab9f0012500a233500930043574200460066ae880080508c8c8cccd5cd19b8735573aa004900011991091980080180118061aba150023005357426ae8940088c98c8050cd5ce00a80a00909aab9e5001137540024646464646666ae68cdc39aab9d5004480008cccc888848cccc00401401000c008c8c8c8cccd5cd19b8735573aa0049000119910919800801801180a9aba1500233500f014357426ae8940088c98c8064cd5ce00d00c80b89aab9e5001137540026ae854010ccd54021d728039aba150033232323333573466e1d4005200423212223002004357426aae79400c8cccd5cd19b875002480088c84888c004010dd71aba135573ca00846666ae68cdc3a801a400042444006464c6403666ae7007006c06406005c4d55cea80089baa00135742a00466a016eb8d5d09aba2500223263201533573802c02a02626ae8940044d5d1280089aab9e500113754002266aa002eb9d6889119118011bab00132001355012223233335573e0044a010466a00e66442466002006004600c6aae754008c014d55cf280118021aba200301213574200222440042442446600200800624464646666ae68cdc3a800a40004642446004006600a6ae84d55cf280191999ab9a3370ea0049001109100091931900819ab9c01101000e00d135573aa00226ea80048c8c8cccd5cd19b875001480188c848888c010014c01cd5d09aab9e500323333573466e1d400920042321222230020053009357426aae7940108cccd5cd19b875003480088c848888c004014c01cd5d09aab9e500523333573466e1d40112000232122223003005375c6ae84d55cf280311931900819ab9c01101000e00d00c00b135573aa00226ea80048c8c8cccd5cd19b8735573aa004900011991091980080180118029aba15002375a6ae84d5d1280111931900619ab9c00d00c00a135573ca00226ea80048c8cccd5cd19b8735573aa002900011bae357426aae7940088c98c8028cd5ce00580500409baa001232323232323333573466e1d4005200c21222222200323333573466e1d4009200a21222222200423333573466e1d400d2008233221222222233001009008375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c4664424444444660040120106eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc8848888888cc018024020c030d5d0a8049bae357426ae8940248cccd5cd19b875006480088c848888888c01c020c034d5d09aab9e500b23333573466e1d401d2000232122222223005008300e357426aae7940308c98c804ccd5ce00a00980880800780700680600589aab9d5004135573ca00626aae7940084d55cf280089baa0012323232323333573466e1d400520022333222122333001005004003375a6ae854010dd69aba15003375a6ae84d5d1280191999ab9a3370ea0049000119091180100198041aba135573ca00c464c6401866ae700340300280244d55cea80189aba25001135573ca00226ea80048c8c8cccd5cd19b875001480088c8488c00400cdd71aba135573ca00646666ae68cdc3a8012400046424460040066eb8d5d09aab9e500423263200933573801401200e00c26aae7540044dd500089119191999ab9a3370ea00290021091100091999ab9a3370ea00490011190911180180218031aba135573ca00846666ae68cdc3a801a400042444004464c6401466ae7002c02802001c0184d55cea80089baa0012323333573466e1d40052002200723333573466e1d40092000212200123263200633573800e00c00800626aae74dd5000a4c2400292010350543100122002112323001001223300330020020011f5f6").unwrap(); + + let raw_inputs = hex::decode("8682582075a419179618ca358554fc47aeb33b6c93d12ba8f752495a4e5ef6ea0a1a099a03825820b810e77e706ccaebf7284f6c3d41e2a1eb4af5fabae452618f4175ad1b2aaded03825820975c17a4fed0051be622328efa548e206657d2b65a19224bf6ff8132571e6a50038258207453531a00f98db47c8c2b05e5c38f2c40a0be4f91d42d835bc3bc998b612a8e00825820452b2fc0d170323f86ad1e5b761dcae912774c42c1b1af4de2905a094f2f541403825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c5001").unwrap(); + let raw_outputs = hex::decode("86825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a00412c6aad581c01dd79d464e2446231d662c9422445c4cf709b691baceb8b040a34d4a14d28323232294d6174726978363701581c11638d4d600d32b2849c93314e7e6bc656fded924f30514749e1eb3ea64c28323232294d617472697830014c28323232294d617472697831014d28323232294d61747269783133014d28323232294d61747269783330014d28323232294d61747269783534014d28323232294d6174726978383601581c1e60ac8228a21f6e3685c73970584aa54504a0476c4b66f3ef5c4dd2a14c28323232294d61747269783001581c25da1064122988292665c14259ea26cb4dd96d7f04535125fea248ffa14c28323232294d61747269783001581c46214283f4b5cc5d66836a4fe743e121190f1e5b91448a1b52f1b7bfa14d28323232294d6174726978313801581c4788a484721270845917e0986ab55b51922a46b514eb7a1f871e917ca14d28323232294d6174726978323101581c6ed9951ddcd79c98bc50142ba033890815330d4de1cb4c96870a234ca24c28323232294d617472697830014c28323232294d61747269783101581c6f7fd77c85b9856bdb1cfac1afa90c65d92c3c5e2fcca4a993e7fb52a14d28323232294d6174726978323401581ca07afd05db7f0ccb144052935be97b48593e5c8435f9eb859191de81a34c28323232294d617472697830014c28323232294d617472697831014d28323232294d6174726978323101581ca5ca38805c14270ec4c3c1c2446b28a95324054fac98066c5e82a016a14d28323232294d6174726978313901581ca65e6e94d1a260dbc6c4d9319b45585fa54b83742a33a2c599df56b9a2494265727279436f616c014c426572727954616e67656c6f01581cb3e2625ebd6bd613ce904db9fedb0565eec0671054d30d08bc5edadda44c28323232294d617472697835014d28323232294d61747269783237014d28323232294d61747269783430014d28323232294d6174726978343701581ce3ef435a5910f74d890b2a7cb0d1f7288efc22c75823d57acdab9f52a14d28323232294d6174726978363101825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a0011f436a1581c11638d4d600d32b2849c93314e7e6bc656fded924f30514749e1eb3ea14d28323232294d6174726978363801825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a0011f436a1581c11638d4d600d32b2849c93314e7e6bc656fded924f30514749e1eb3ea14d28323232294d6174726978343101825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a0012378ea1581c2c04f7a15aec58b2bec5dab3d201f3e3898370b98d2f01d4ac8bc270a14d28323232294d6174726978323801825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a0011f436a1581c11638d4d600d32b2849c93314e7e6bc656fded924f30514749e1eb3ea14d28323232294d6174726978323101825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02b350d1").unwrap(); + + let inputs = MaybeIndefArray::::decode_fragment(&raw_inputs).unwrap(); + let outputs = MaybeIndefArray::::decode_fragment(&raw_outputs).unwrap(); + + let utxos: MaybeIndefArray = MaybeIndefArray::Indef( + inputs + .iter() + .zip(outputs.iter()) + .map(|(input, output)| ResolvedInput { + input: input.clone(), + output: output.clone(), + }) + .collect(), + ); + + let slot_config = SlotConfig { + zero_time: 1660003200000, // Preview network + zero_slot: 0, + slot_length: 1000, + }; + + let costs: Vec = vec![ + 205665, + 812, + 1, + 1, + 1000, + 571, + 0, + 1, + 1000, + 24177, + 4, + 1, + 1000, + 32, + 117366, + 10475, + 4, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 100, + 100, + 23000, + 100, + 19537, + 32, + 175354, + 32, + 46417, + 4, + 221973, + 511, + 0, + 1, + 89141, + 32, + 497525, + 14068, + 4, + 2, + 196500, + 453240, + 220, + 0, + 1, + 1, + 1000, + 28662, + 4, + 2, + 245000, + 216773, + 62, + 1, + 1060367, + 12586, + 1, + 208512, + 421, + 1, + 187000, + 1000, + 52998, + 1, + 80436, + 32, + 43249, + 32, + 1000, + 32, + 80556, + 1, + 57667, + 4, + 1000, + 10, + 197145, + 156, + 1, + 197145, + 156, + 1, + 204924, + 473, + 1, + 208896, + 511, + 1, + 52467, + 32, + 64832, + 32, + 65493, + 32, + 22558, + 32, + 16563, + 32, + 76511, + 32, + 196500, + 453240, + 220, + 0, + 1, + 1, + 69522, + 11687, + 0, + 1, + 60091, + 32, + 196500, + 453240, + 220, + 0, + 1, + 1, + 196500, + 453240, + 220, + 0, + 1, + 1, + 1159724, + 392670, + 0, + 2, + 806990, + 30482, + 4, + 1927926, + 82523, + 4, + 265318, + 0, + 4, + 0, + 85931, + 32, + 205665, + 812, + 1, + 1, + 41182, + 32, + 212342, + 32, + 31220, + 32, + 32696, + 32, + 43357, + 32, + 32247, + 32, + 38314, + 32, + 20000000000, + 20000000000, + 9462713, + 1021, + 10, + 20000000000, + 0, + 20000000000, + ]; + + let cost_mdl = CostMdls { + plutus_v1: None, + plutus_v2: Some(costs), + }; + + let initial_budget = ExBudget { + cpu: 10000000000, + mem: 14000000, + }; + + let multi_era_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) + .unwrap(); + match multi_era_tx { + MultiEraTx::Babbage(tx) => { + let redeemers = eval_phase_two( + &tx, + &utxos, + Some(&cost_mdl), + Some(&initial_budget), + &slot_config, + false, + ) + .unwrap(); + + assert_eq!(redeemers.len(), 1); + + let total_budget_used = + redeemers + .iter() + .fold(ExBudget { mem: 0, cpu: 0 }, |accum, curr| ExBudget { + mem: accum.mem + curr.ex_units.mem as i64, + cpu: accum.cpu + curr.ex_units.steps as i64, + }); + + println!("{:?}", total_budget_used); + + assert_eq!( + total_budget_used, + // Numbers came uplc evaluate + ExBudget { + cpu: 182855351, + mem: 583272 + } + ); + } + _ => unreachable!(), + }; +} + +#[test] +fn test_eval_4() { + /* + + Plutus V1 + + Helios script: + + func main() Bool { + false + } + + */ + + let tx_bytes = hex::decode("84A80081825820275B5DA338C8B899035081EB34BFA950B634911A5DD3271B3AD6CF4C2BBA0C50010182825839000AF00CC47500BB64CFFFB783E8C42F746B4E8B8A70EDE9C08C7113ACF3BDE34D1041F5A2076EF9AA6CF4539AB1A96ED462A0300ACBDB65D5821A00111958A1581C1E8BCA1FA1D937F408AFE2FD4DBF343AB7A09CF07984071ED95B3C92A1400A825839000AF00CC47500BB64CFFFB783E8C42F746B4E8B8A70EDE9C08C7113ACF3BDE34D1041F5A2076EF9AA6CF4539AB1A96ED462A0300ACBDB65D51A029F7B29021A0002BC5009A1581C1E8BCA1FA1D937F408AFE2FD4DBF343AB7A09CF07984071ED95B3C92A1400A0B58205013DBE72526511F63B0C4A235FBBC5D09D11D42F310113AAAB1A28E01E0BDE60D81825820275B5DA338C8B899035081EB34BFA950B634911A5DD3271B3AD6CF4C2BBA0C500110825839000AF00CC47500BB64CFFFB783E8C42F746B4E8B8A70EDE9C08C7113ACF3BDE34D1041F5A2076EF9AA6CF4539AB1A96ED462A0300ACBDB65D51A02AF3659111A00041A78A30081825820065DD553FBE4E240A8F819BB9E333A7483DE4A22B65C7FB6A95CE9450F84DFF758401679B607EABEF3DBBC9AC0ABB03AFB3A979EA32243BB5B99E299290D709BB4A6C2AA528C447C2DB610A103CC9C0E7C018CAA4FC8322D8EC217620E6D4BC2EF0B03815453010000322233335734600693124C4C931250010581840100D87980821909611A00094D78F5F6").unwrap(); + + let raw_inputs = hex::decode("8682582075a419179618ca358554fc47aeb33b6c93d12ba8f752495a4e5ef6ea0a1a099a03825820b810e77e706ccaebf7284f6c3d41e2a1eb4af5fabae452618f4175ad1b2aaded03825820975c17a4fed0051be622328efa548e206657d2b65a19224bf6ff8132571e6a50038258207453531a00f98db47c8c2b05e5c38f2c40a0be4f91d42d835bc3bc998b612a8e00825820452b2fc0d170323f86ad1e5b761dcae912774c42c1b1af4de2905a094f2f541403825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c5001").unwrap(); + let raw_outputs = hex::decode("86825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a00412c6aad581c01dd79d464e2446231d662c9422445c4cf709b691baceb8b040a34d4a14d28323232294d6174726978363701581c11638d4d600d32b2849c93314e7e6bc656fded924f30514749e1eb3ea64c28323232294d617472697830014c28323232294d617472697831014d28323232294d61747269783133014d28323232294d61747269783330014d28323232294d61747269783534014d28323232294d6174726978383601581c1e60ac8228a21f6e3685c73970584aa54504a0476c4b66f3ef5c4dd2a14c28323232294d61747269783001581c25da1064122988292665c14259ea26cb4dd96d7f04535125fea248ffa14c28323232294d61747269783001581c46214283f4b5cc5d66836a4fe743e121190f1e5b91448a1b52f1b7bfa14d28323232294d6174726978313801581c4788a484721270845917e0986ab55b51922a46b514eb7a1f871e917ca14d28323232294d6174726978323101581c6ed9951ddcd79c98bc50142ba033890815330d4de1cb4c96870a234ca24c28323232294d617472697830014c28323232294d61747269783101581c6f7fd77c85b9856bdb1cfac1afa90c65d92c3c5e2fcca4a993e7fb52a14d28323232294d6174726978323401581ca07afd05db7f0ccb144052935be97b48593e5c8435f9eb859191de81a34c28323232294d617472697830014c28323232294d617472697831014d28323232294d6174726978323101581ca5ca38805c14270ec4c3c1c2446b28a95324054fac98066c5e82a016a14d28323232294d6174726978313901581ca65e6e94d1a260dbc6c4d9319b45585fa54b83742a33a2c599df56b9a2494265727279436f616c014c426572727954616e67656c6f01581cb3e2625ebd6bd613ce904db9fedb0565eec0671054d30d08bc5edadda44c28323232294d617472697835014d28323232294d61747269783237014d28323232294d61747269783430014d28323232294d6174726978343701581ce3ef435a5910f74d890b2a7cb0d1f7288efc22c75823d57acdab9f52a14d28323232294d6174726978363101825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a0011f436a1581c11638d4d600d32b2849c93314e7e6bc656fded924f30514749e1eb3ea14d28323232294d6174726978363801825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a0011f436a1581c11638d4d600d32b2849c93314e7e6bc656fded924f30514749e1eb3ea14d28323232294d6174726978343101825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a0012378ea1581c2c04f7a15aec58b2bec5dab3d201f3e3898370b98d2f01d4ac8bc270a14d28323232294d6174726978323801825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a0011f436a1581c11638d4d600d32b2849c93314e7e6bc656fded924f30514749e1eb3ea14d28323232294d6174726978323101825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02b350d1").unwrap(); + + let inputs = MaybeIndefArray::::decode_fragment(&raw_inputs).unwrap(); + let outputs = MaybeIndefArray::::decode_fragment(&raw_outputs).unwrap(); + + let utxos: MaybeIndefArray = MaybeIndefArray::Indef( + inputs + .iter() + .zip(outputs.iter()) + .map(|(input, output)| ResolvedInput { + input: input.clone(), + output: output.clone(), + }) + .collect(), + ); + + let slot_config = SlotConfig { + zero_time: 1660003200000, // Preview network + zero_slot: 0, + slot_length: 1000, + }; + + let costs: Vec = vec![ + 205665, 812, 1, 1, 1000, 571, 0, 1, 1000, 24177, 4, 1, 1000, 32, 117366, 10475, 4, 23000, + 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 100, 100, 23000, 100, + 19537, 32, 175354, 32, 46417, 4, 221973, 511, 0, 1, 89141, 32, 497525, 14068, 4, 2, 196500, + 453240, 220, 0, 1, 1, 1000, 28662, 4, 2, 245000, 216773, 62, 1, 1060367, 12586, 1, 208512, + 421, 1, 187000, 1000, 52998, 1, 80436, 32, 43249, 32, 1000, 32, 80556, 1, 57667, 4, 1000, + 10, 197145, 156, 1, 197145, 156, 1, 204924, 473, 1, 208896, 511, 1, 52467, 32, 64832, 32, + 65493, 32, 22558, 32, 16563, 32, 76511, 32, 196500, 453240, 220, 0, 1, 1, 69522, 11687, 0, + 1, 60091, 32, 196500, 453240, 220, 0, 1, 1, 196500, 453240, 220, 0, 1, 1, 806990, 30482, 4, + 1927926, 82523, 4, 265318, 0, 4, 0, 85931, 32, 205665, 812, 1, 1, 41182, 32, 212342, 32, + 31220, 32, 32696, 32, 43357, 32, 32247, 32, 38314, 32, 9462713, 1021, 10, + ]; + + let cost_mdl = CostMdls { + plutus_v1: Some(costs), + plutus_v2: None, + }; + + let initial_budget = ExBudget { + cpu: 10000000000, + mem: 14000000, + }; + + let multi_era_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) + .unwrap(); + match multi_era_tx { + MultiEraTx::Babbage(tx) => { + assert!(eval_phase_two( + &tx, + &utxos, + Some(&cost_mdl), + Some(&initial_budget), + &slot_config, + false, + ) + .is_err()); + } + _ => unreachable!(), + }; +} + +#[test] +fn test_eval_5() { + /* + + Haskell PLC: (114126, 40098159) + Aiken PLC: (114126,40211433) + + Plutus V1 + + Helios script: + + func main(ctx: ScriptContext) -> Bool {ctx.tx.fee == ctx.tx.fee} + + */ + + let tx_bytes = hex::decode("84a80081825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c50010182825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a00111958a1581cd1f4c65937e3c25d4f229b12b79a68f22e500c312b4c2e6977ffe443a1400a825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a029ef827021a00033f5209a1581cd1f4c65937e3c25d4f229b12b79a68f22e500c312b4c2e6977ffe443a1400a0b5820bdbcad5d5d3667646455252dcc5a255ce7162a9792be84ea48635b4645b962480d81825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c500110825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02ae71d6111a0004defba30081825820065dd553fbe4e240a8f819bb9e333a7483de4a22b65c7fb6a95ce9450f84dff7584060b43793ced32fb8128ad53e0710f7f4191410548a0d6bd80454b6988fbb0ae58e5f0c942b8bbf4cbfe79aeae36156690a289096cff034c31c13d083a096420803815902375902340100003232323232323232323232323232323232323232223333573460060024931324c466012600860040026008600400200246ae84c0180040048d5d0980100091aba23002001235744600400246aae78dd5000911998019119b87002001002001222332232330010013300b00300222333357346ae8c0049289191999ab9a300d33300b0093300a0060013300a00500124a046600a00a6ae880112635742004931bab0023756002446466002002006446666ae68d5d180091bb24988cccd5cd19baf35573a6ae840080108dd59aab9e357420064660080086ae8800d26498888c8cc004004cc01c00c00888cccd5cd1aba300124a24646666ae68c024cc01ccc020018004cc0200140049281198028029aba2004498d5d080124c44646660020020060044446666ae68d5d180112400046666ae68cdd79aab9d3574200600446eb4d55cf1aba10042333005005357440080069324c4666ae6800528251223232323300a003001333001001002003222333357346ae8c004802c8c8cccd5cd1980500300091998030030029aba2004233574000466600c00c00a6ae880112635573a6ae840092630030022323300100100222333357346ae8c004801c8cd5d01aab9d35742004660060066ae8800926223300300223375e00200444646660020020060044446666ae68d5d1801125023333573460046ae8400c92891998028029aba20040034992637629311191998008008010019111999ab9a357460024006466ae80d5d08011998020020019aba200249810581840100d87980821a0001bdce1a0263d96ff5f6").unwrap(); + + let raw_inputs = hex::decode("8682582075a419179618ca358554fc47aeb33b6c93d12ba8f752495a4e5ef6ea0a1a099a03825820b810e77e706ccaebf7284f6c3d41e2a1eb4af5fabae452618f4175ad1b2aaded03825820975c17a4fed0051be622328efa548e206657d2b65a19224bf6ff8132571e6a50038258207453531a00f98db47c8c2b05e5c38f2c40a0be4f91d42d835bc3bc998b612a8e00825820452b2fc0d170323f86ad1e5b761dcae912774c42c1b1af4de2905a094f2f541403825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c5001").unwrap(); + let raw_outputs = hex::decode("86825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a00412c6aad581c01dd79d464e2446231d662c9422445c4cf709b691baceb8b040a34d4a14d28323232294d6174726978363701581c11638d4d600d32b2849c93314e7e6bc656fded924f30514749e1eb3ea64c28323232294d617472697830014c28323232294d617472697831014d28323232294d61747269783133014d28323232294d61747269783330014d28323232294d61747269783534014d28323232294d6174726978383601581c1e60ac8228a21f6e3685c73970584aa54504a0476c4b66f3ef5c4dd2a14c28323232294d61747269783001581c25da1064122988292665c14259ea26cb4dd96d7f04535125fea248ffa14c28323232294d61747269783001581c46214283f4b5cc5d66836a4fe743e121190f1e5b91448a1b52f1b7bfa14d28323232294d6174726978313801581c4788a484721270845917e0986ab55b51922a46b514eb7a1f871e917ca14d28323232294d6174726978323101581c6ed9951ddcd79c98bc50142ba033890815330d4de1cb4c96870a234ca24c28323232294d617472697830014c28323232294d61747269783101581c6f7fd77c85b9856bdb1cfac1afa90c65d92c3c5e2fcca4a993e7fb52a14d28323232294d6174726978323401581ca07afd05db7f0ccb144052935be97b48593e5c8435f9eb859191de81a34c28323232294d617472697830014c28323232294d617472697831014d28323232294d6174726978323101581ca5ca38805c14270ec4c3c1c2446b28a95324054fac98066c5e82a016a14d28323232294d6174726978313901581ca65e6e94d1a260dbc6c4d9319b45585fa54b83742a33a2c599df56b9a2494265727279436f616c014c426572727954616e67656c6f01581cb3e2625ebd6bd613ce904db9fedb0565eec0671054d30d08bc5edadda44c28323232294d617472697835014d28323232294d61747269783237014d28323232294d61747269783430014d28323232294d6174726978343701581ce3ef435a5910f74d890b2a7cb0d1f7288efc22c75823d57acdab9f52a14d28323232294d6174726978363101825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a0011f436a1581c11638d4d600d32b2849c93314e7e6bc656fded924f30514749e1eb3ea14d28323232294d6174726978363801825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a0011f436a1581c11638d4d600d32b2849c93314e7e6bc656fded924f30514749e1eb3ea14d28323232294d6174726978343101825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a0012378ea1581c2c04f7a15aec58b2bec5dab3d201f3e3898370b98d2f01d4ac8bc270a14d28323232294d6174726978323801825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a0011f436a1581c11638d4d600d32b2849c93314e7e6bc656fded924f30514749e1eb3ea14d28323232294d6174726978323101825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02b350d1").unwrap(); + + let inputs = MaybeIndefArray::::decode_fragment(&raw_inputs).unwrap(); + let outputs = MaybeIndefArray::::decode_fragment(&raw_outputs).unwrap(); + + let utxos: MaybeIndefArray = MaybeIndefArray::Indef( + inputs + .iter() + .zip(outputs.iter()) + .map(|(input, output)| ResolvedInput { + input: input.clone(), + output: output.clone(), + }) + .collect(), + ); + + let slot_config = SlotConfig { + zero_time: 1660003200000, // Preview network + zero_slot: 0, + slot_length: 1000, + }; + + let costs: Vec = vec![ + 205665, 812, 1, 1, 1000, 571, 0, 1, 1000, 24177, 4, 1, 1000, 32, 117366, 10475, 4, 23000, + 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 100, 100, 23000, 100, + 19537, 32, 175354, 32, 46417, 4, 221973, 511, 0, 1, 89141, 32, 497525, 14068, 4, 2, 196500, + 453240, 220, 0, 1, 1, 1000, 28662, 4, 2, 245000, 216773, 62, 1, 1060367, 12586, 1, 208512, + 421, 1, 187000, 1000, 52998, 1, 80436, 32, 43249, 32, 1000, 32, 80556, 1, 57667, 4, 1000, + 10, 197145, 156, 1, 197145, 156, 1, 204924, 473, 1, 208896, 511, 1, 52467, 32, 64832, 32, + 65493, 32, 22558, 32, 16563, 32, 76511, 32, 196500, 453240, 220, 0, 1, 1, 69522, 11687, 0, + 1, 60091, 32, 196500, 453240, 220, 0, 1, 1, 196500, 453240, 220, 0, 1, 1, 806990, 30482, 4, + 1927926, 82523, 4, 265318, 0, 4, 0, 85931, 32, 205665, 812, 1, 1, 41182, 32, 212342, 32, + 31220, 32, 32696, 32, 43357, 32, 32247, 32, 38314, 32, 9462713, 1021, 10, + ]; + + let cost_mdl = CostMdls { + plutus_v1: Some(costs), + plutus_v2: None, + }; + + let initial_budget = ExBudget { + cpu: 10000000000, + mem: 14000000, + }; + + let multi_era_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) + .unwrap(); + match multi_era_tx { + MultiEraTx::Babbage(tx) => { + let redeemers = eval_phase_two( + &tx, + &utxos, + Some(&cost_mdl), + Some(&initial_budget), + &slot_config, + false, + ) + .unwrap(); + + assert_eq!(redeemers.len(), 1); + + let total_budget_used = + redeemers + .iter() + .fold(ExBudget { mem: 0, cpu: 0 }, |accum, curr| ExBudget { + mem: accum.mem + curr.ex_units.mem as i64, + cpu: accum.cpu + curr.ex_units.steps as i64, + }); + + println!("{:?}", total_budget_used); + + assert_eq!( + total_budget_used, + // Numbers came uplc evaluate + ExBudget { + cpu: 40098159, + mem: 114126 + } + ); + } + _ => unreachable!(), + }; +} + +#[test] +fn test_eval_6() { + /* + + Haskell PLC: (_, _) + Aiken PLC: (_,_) + + Plutus V1 + + Helios script: + + func main(ctx: ScriptContext) -> Bool {ctx.tx.fee == ctx.tx.fee} + + */ + + let tx_bytes = hex::decode("84a80082825820f375a19211ee8ce75b8306a2604e2a0e8c50dfae7a103dd3688f980bec5e29b200825820a0bfcb30c19402a5bc79ecd33ddea4d67d8d6cb2ebe694c5ac2f14abc47092f4000182825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a00111958a1581c652cc39999ceb1ddbdd788c3206d70536d84d36f42198296736355efa1400a825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a0047acf6021a0002c73209a1581c652cc39999ceb1ddbdd788c3206d70536d84d36f42198296736355efa1400a0b58201ca9076f41fe63a349f05c6346c732fa7f42744e43e0d54f546af0ffc463057f0d81825820f375a19211ee8ce75b8306a2604e2a0e8c50dfae7a103dd3688f980bec5e29b20110825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a054bc2ef111a00042acba30081825820065dd553fbe4e240a8f819bb9e333a7483de4a22b65c7fb6a95ce9450f84dff758402220dcecba177cabf70414676a5b9af9e0d6dec5ce3a7335caef5e4843a2f045995f6e8debbe019d21dcfc8749d780f0cd75c717cfab17ba275702c3aae37309038158235821010000323232223333573460060024931324c4660040020020024466ebc00800410581840100d8798082190fa21a005f14d0f5f6").unwrap(); + + let raw_inputs = hex::decode("86825820a0bfcb30c19402a5bc79ecd33ddea4d67d8d6cb2ebe694c5ac2f14abc47092f400825820a0bfcb30c19402a5bc79ecd33ddea4d67d8d6cb2ebe694c5ac2f14abc47092f401825820cd3589fae0ae4ad33dbc8a8d99b4e99b4a78c2b0473040aab34da8e44fba9fa300825820f375a19211ee8ce75b8306a2604e2a0e8c50dfae7a103dd3688f980bec5e29b200825820f375a19211ee8ce75b8306a2604e2a0e8c50dfae7a103dd3688f980bec5e29b201825820c5bc1437117c200f325467dcede3c09fbfca32f9fe399bd40c778ab2fa832f4301").unwrap(); + let raw_outputs = hex::decode("86825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a000f4240825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a005da7c4ac581c1e852216c006c55490cd85b6e0ba523a37c2be3526f479e61cca41eba2444e616d6902454e616d693201581c21514b6c51f0aaa0c9c337329ce93c52fad208cfd0b4ccd51074d9dcab4d50494e4b44494e4f4331494439014d50494e4b44494e4f4332494439014d50494e4b44494e4f4333494439014d50494e4b44494e4f4334494439014d50494e4b44494e4f4335494439014d50494e4b44494e4f4336494439014d50494e4b44494e4f4337494439014d50494e4b44494e4f4338494439014d50494e4b44494e4f4339494439014e50494e4b44494e4f433130494439014e50494e4b44494e4f43313149443901581c321668648736a09a40bc3ef5fbd05590530be624e39af84fab3bc6bba84d435033503050726f6d6f333431014f4b696e67447572616e747574323531015053616d757257686950726f6d6f313933015143727970746f6d626f50726f6d6f3134360154536b756c6c62616c6c4c6f676f50726f6d6f35320155536b756c6c62616c6c4c6f676f50726f6d6f33313301581b425345647761726473536c616d6d696e48616e647330303034313201581c42697a61727265537461727344726561646c696f6e526f736533323401581c9ec93fd47d1e43ec5bb5dad70af92b6498b92d20d4325f9027ac4743a1574a6f736570684d6972616e64615754503030353754323601581ca7dc26584358f5ca8d99d7aa646a7b658120296a94588e1cefad8940a157546573744e616d69467269656e645368696e794a50454701581cbc5a0f0f3f1bfa92286410388c54655687bb518f47e12b88c0d0728ca14b566563746f724669656c6401581cc4c00fbd8fa227442a5e7cdecde33b24588494d05a2c50fda8938c6da1444b49445a19c346581cd068fe47123ec4c86460eeb74c7d7765c67d2df295a3ac86d664ed45a156506c757475734669727374436c61737350686f746f3101581cd3b65744dd067fd7103cc6a4019cc9cd5f8627b78174c05dc67a9ad6a1544c696665496e4c6f636b646f776e53314d31503201581cd973d8df645da318bb331dbe4af8eb0270079470225d955c6183445ca15148617070794269727468646179416c657301581cdf9974c2192744f3b6dac92990769fe26c36b0ac8bcc623fb457a45da14d575450303035466c796572323401581cee47c1521b55a006c345f739f084f5c205aeb295e3e42fa66b9d1dd2a151446965666e6643727970746f6e6965726505825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a000f4240825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a004c4b40825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a054fedba825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a0028a3c6").unwrap(); + + let inputs = MaybeIndefArray::::decode_fragment(&raw_inputs).unwrap(); + let outputs = MaybeIndefArray::::decode_fragment(&raw_outputs).unwrap(); + + let utxos: MaybeIndefArray = MaybeIndefArray::Indef( + inputs + .iter() + .zip(outputs.iter()) + .map(|(input, output)| ResolvedInput { + input: input.clone(), + output: output.clone(), + }) + .collect(), + ); + + let slot_config = SlotConfig { + zero_time: 1596059091000, // Mainnet network + zero_slot: 4492800, + slot_length: 1000, + }; + + let costs: Vec = vec![ + 197209, 0, 1, 1, 396231, 621, 0, 1, 150000, 1000, 0, 1, 150000, 32, 2477736, 29175, 4, + 29773, 100, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 100, 100, 29773, + 100, 150000, 32, 150000, 32, 150000, 32, 150000, 1000, 0, 1, 150000, 32, 150000, 1000, 0, + 8, 148000, 425507, 118, 0, 1, 1, 150000, 1000, 0, 8, 150000, 112536, 247, 1, 150000, 10000, + 1, 136542, 1326, 1, 1000, 150000, 1000, 1, 150000, 32, 150000, 32, 150000, 32, 1, 1, + 150000, 1, 150000, 4, 103599, 248, 1, 103599, 248, 1, 145276, 1366, 1, 179690, 497, 1, + 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 148000, 425507, + 118, 0, 1, 1, 61516, 11218, 0, 1, 150000, 32, 148000, 425507, 118, 0, 1, 1, 148000, 425507, + 118, 0, 1, 1, 2477736, 29175, 4, 0, 82363, 4, 150000, 5000, 0, 1, 150000, 32, 197209, 0, 1, + 1, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, + 3345831, 1, 1, + ]; + + let cost_mdl = CostMdls { + plutus_v1: Some(costs), + plutus_v2: None, + }; + + let initial_budget = ExBudget { + cpu: 10000000000, + mem: 14000000, + }; + + let multi_era_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) + .unwrap(); + match multi_era_tx { + MultiEraTx::Babbage(tx) => { + let redeemers = eval_phase_two( + &tx, + &utxos, + Some(&cost_mdl), + Some(&initial_budget), + &slot_config, + false, + ) + .unwrap(); + + assert_eq!(redeemers.len(), 1); + + let total_budget_used = + redeemers + .iter() + .fold(ExBudget { mem: 0, cpu: 0 }, |accum, curr| ExBudget { + mem: accum.mem + curr.ex_units.mem as i64, + cpu: accum.cpu + curr.ex_units.steps as i64, + }); + + println!("{:?}", total_budget_used); + + assert_eq!( + total_budget_used, + // Numbers came uplc evaluate + ExBudget { + cpu: 6231248, + mem: 4002 + } + ); + } + _ => unreachable!(), + }; +} + +#[test] +fn test_eval_7() { + /* + + Haskell PLC: (_, _) + Aiken PLC: (_,_) + + Plutus V1 + + Helios script: + + func main(ctx: ScriptContext) -> Bool {ctx.tx.fee == ctx.tx.fee} + + */ + + let tx_bytes = hex::decode("84a80082825820a0bfcb30c19402a5bc79ecd33ddea4d67d8d6cb2ebe694c5ac2f14abc47092f400825820f375a19211ee8ce75b8306a2604e2a0e8c50dfae7a103dd3688f980bec5e29b2000182825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a00113b04a1581c9bed3f1e167c09eb82273ab0b51b383d31c3b0dbf3a468ca4bc23d02a14231310c825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a0040eb3c021a0009674009a1581c9bed3f1e167c09eb82273ab0b51b383d31c3b0dbf3a468ca4bc23d02a14231310c0b5820f7a477a807b46ecb36f2552c1ac4730394e8d24e572c65afde772771d688114a0d8182582017a796d7541fc547bca875a82c7bd35e45b821cb69bebd21db774bcde16fffdc0110825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a052fe707111a000e1ae0a30081825820065dd553fbe4e240a8f819bb9e333a7483de4a22b65c7fb6a95ce9450f84dff7584090a0c710cbd45fdfca8b789e9062bc7d8fbfb6eb44a2a348db3b8daaddb6f6198d6ece3a36f808d118a7b16ac17747ee6e0790a4b5234187aa5f3c11631ba50a0381590fc3590fc001000032332232332232323232323232323232323232332232323232323232323232323232332232322232323232325335001102713263202633573892010350543500027323233355300e1200135012501123500122333553011120013501550142350012233350012330324800000488cc0cc0080048cc0c800520000013355300d1200123500122335502a00233350012335530111200123500122335502e0023550130010012233355500e0140020012335530111200123500122335502e00235501200100133355500900f0020013013300f50023012300e500113553353333333574800846666ae68cdc39aab9d5004480008cccd55cfa8021281491999aab9f50042502a233335573e6ae89401494cd54cd4c8c8c8c8c8c8c8c8c8c8c8c8ccccccd5d200611999ab9a3370e6aae7540312000233335573ea0184a07446666aae7d4030940ec8cccd55cfa8061281e11999aab9f500c2503d233335573ea0184a07c46666aae7d4030940fc8cccd55cfa8061282011999aab9f500c25041233335573ea0184a08446666aae7d40309410c8cccd55cf9aba2500d2533553355335533553355335533553355335533533503023232323333333574800846666ae68cdc39aab9d5004480008cccd55cfa8021282591999aab9f50042504c233335573e6ae89401494cd4c10cd5d0a80390a99a981d9aba15007213505012330010030021504e1504d2504d04b04a0492504a04625049250492504925049047135744a00226aae7940044dd50009aba15017213504622222222212333333333300100b00a0090080070060050040030021504421533533503103235742a02e426a08e60040022a08a2a08842a66a666aa06807ca0666ae85405884d411cc0080045411454110854cd4ccd540d00f940ccd5d0a80a909a82398010008a8228a82210a99a99a81891919191919191999999aba400723333573466e1d4009200c233335573ea00e46a0a0244444440064a09e09a46666ae68cdc3a801a401446666aae7d40208d414448888888010941401388cccd5cd19b875004480208cccd55cfa8049282891999aab9f500725052233335573e6ae89402094cd4c130d5d0a80610a99a98259aba1500a2135056122222223300100900815054150532505305105004f23333573466e1d40152006233335573ea0144a0a446666aae7d40249414c8cccd55cf9aba2500a25335304d35742a01a42a66a609c6ae85403084d415c48888888cc0080240205415454150941501481441408cccd5cd19b875006480108cccd55cfa8059282991999aab9f500b25054233335573e6ae89403094cd4c124d5d0a80710a99a98279aba1500e2135058122222223300600900815056150552505505305205123333573466e1d401d2002233335573ea0184a0a846666aae7cd5d128069299a98249aba1500e2135057122222223007008150552505505305223333573466e1d40212000233335573ea01a4a0aa46666aae7cd5d128071299a98251aba1500f213505812222222300500815056250560540532505405004f04e04d04c04b04a2504d2504d2504d2504d04b135573aa00a26ae89400c4d5d1280109aba25001135573ca00226ea8004d5d0a80a109a82398010008a8228a82210a99a99a81899aa82081c81e1aba15013213504730020011504515044215335323232323333333574800846666ae68cdc39aab9d5004480008cccd55cfa8021282611999aab9f50042504d233335573e6ae89401494cd4c8c8c8c8ccccccd5d200211999ab9a3370e6aae7540112000233335573ea0084a0aa46666aae7d4010941588cccd55cf9aba250052533533505104e35742a00e42a66a60a66ae85401c84d416848cc00400c008541605415c9415c15415014c941501409414c9414c9414c9414c1444d5d1280089aab9e5001137540026ae85401c854cd4c8c8c8c8ccccccd5d200211999ab9a3370e6aae7540112000233335573ea0084a0ac46666aae7d40109415c8cccd55cf9aba250052533533505204f35742a00e42a66a60a86ae85401c84d416c48cc00400c00854164541609416015815415094154144941509415094150941501484d5d1280089aab9e5001137540026ae85401c84d414448cc00400c0085413c541389413813012c1289412c11c941289412894128941281204d5d1280089aab9e5001137540026ae85404884d411cc0080045411454110854cd4cd40c40f8d5d0a808909a82398010008a8228a82210a99a99a81899aa82081f28231aba15010213504730020011504515044215335303d35742a01e426a08e60040022a08a2a0884a08808408208007e07c07a0780760740720704a07206a4a0704a0704a0704a07006c26ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d55cf280089baa00135742a00e426a05a424660020060042a05642a66a646464646666666ae900108cccd5cd19b875002480088cccd55cfa8021281991999aab9f35744a00a4a66a60546ae85401884d40d8488c00400c540d0940d00c80c48cccd5cd19b875003480008cccd55cfa8029281a11999aab9f35744a00c4a66a605c6ae85401c84d40dc488c00800c540d4940d40cc0c8940cc0bc0b8940c4940c4940c4940c40bc4d55cea80109aab9e5001137540026ae85401c84d40b8c008004540b0540ac940ac0a40a009c940a00909409c9409c9409c9409c0948448c0040085880044d5d1280089aab9e500113754002222444666aa600824002a02866aa600e2400246a0024466aa0480046aa012002666aa600824002446a00444a66a666aa6018240026466a02444666a006440040040026a00244002246600244a66a004205c200205646a002446601400400a00c2006266a030008006a02a00266aa600e2400246a002446466aa04a006600200a640026aa05644a66a00226aa0140064426a00444a66a6601800401022444660040140082600c006004640026aa0484422444a66a00220044426600a004666aa600e2400200a0080022242444600600822424446002008640026aa042442244a66a0022a02444266a026600800466aa600c24002008002640026aa0404422444a66a00226a00644002442666a00a440046008004666aa600e2400200a00800244666ae68cdc780100081000f91a800910010891980091299a8010800880f00e8919a80111199a801910010010009a80091000891091980080180111a8009111111111004091911999999aba400125018250182300337580044a0304a03002c640026aa034446666aae7c0048d4065402894cd4c010d5d080110a99a98021aba2003213501b33500b00200115019150180162323232323333333574800a46666ae68cdc39aab9d5005480008cccd55cfa8029280d91999aab9f50052501c233335573ea00a4a03a46666aae7cd5d128031299a991919191999999aba400423333573466e1cd55cea8022400046666aae7d4010940948cccd55cfa8021281311999aab9f35744a00a4a66a60386ae85401c854cd4cd406c070d5d0a803909a81509198008018010a8140a81392813812812011928120101281192811928119281181089aba25001135573ca00226ea8004d5d0a80490a99a999aa80700c28069aba1500921533533501301935742a012426a044246660020080060042a0402a03e2a03c4a03c0380360340324a03402c4a0324a0324a0324a03202e26ae8940044d5d1280089aab9e500113754002266aa00201601222446446666666ae900055401494cd4c00cdd5801109a80c0008a80b2a802aa802aa80280a190009aa80c11191999aab9f002235018500925335300635573aa00442a66a600c6aae79400c854cd4c018d5d1002909a80d99a80599aa80c0018010008a80c8a80c0a80b80a89aba100111250111122002122122330010040031223232323333333574800846666ae68cdc3a8012400046666aae7d4010940548cccd55cf9aba2500525335300935742a00c426a0306a0300022a02c4a02c02802646666ae68cdc3a801a400446666aae7d40148d405d405894058050940540440409404c9404c9404c9404c0444d55cea80109aab9e500113754002464646464646666666ae900188cccd5cd19b875002480088cccd55cfa8031280a91999aab9f500625016233335573ea00c4a02e46666aae7cd5d128039299a98079aba1500a215335301035742a01442a66a60226ae85402884d4070488ccc00401401000c5406854064540609406005805405004c8cccd5cd19b875003480008cccd55cfa8039280b11999aab9f35744a0104a66a60186ae85402484d4064488c00800c5405c9405c054050940540440409404c9404c9404c9404c0444d55cea80209aba25001135744a00226aae7940044dd500091919191999999aba400423333573466e1d40092002233335573ea0084a02446666aae7cd5d128029299a98061aba1500621350151223001003150132501301101023333573466e1d400d2000233335573ea00a4a02646666aae7cd5d128031299a98069aba150072135016122300200315014250140120112501200e00d2501025010250102501000e135573aa00426aae7940044dd500091919191999999aba400423333573466e1cd55cea8022400046666aae7d4010940448cccd55cfa8021280911999aab9f35744a00a4a66a60166ae85401c854cd4c02cd5d0a803909a80b09198008018010a80a0a80992809808808007928080061280792807928079280780689aba25001135573ca00226ea80048ccccccd5d200092805928059280591a8061bad0022500b009232323333333574800646666ae68cdc39aab9d5003480008cccd55cfa8019280711999aab9f35744a0084a66a60106ae85401484d40440045403c9403c03403094034024940309403094030940300284d55cf280089baa0012333333357480024a0124a0124a0124a01246a0146eb800801c488c8c8c8ccccccd5d200211999ab9a3370ea004900211999aab9f500423500f12220012500e00c23333573466e1d400d2002233335573ea00a4a01e46666aae7cd5d128031299a98051aba15007213501212223003004150102501000e00d23333573466e1d40112000233335573ea00c46a02224440044a02001c4a01e0160140124a0184a0184a0184a01801426aae7540084d55cf280089baa00123232323333333574800846666ae68cdc3a8012400446666aae7d40108d4034040940300288cccd5cd19b875003480008cccd55cfa80291a80700812806805928060040039280512805128051280500409aab9d5002135573ca00226ea800444888c8c8c8ccccccd5d200211999ab9a3370e6aae7540112000233335573ea0084a01a46666aae7d4010940388cccd55cf9aba2500525335300b35742a00e42a66a60166ae85401c84d4048cd5403c008004540405403c9403c03403002c940300209402c9402c9402c9402c0244d5d1280089aab9e500113754002930891091980080180109000890911801001889100091199ab9a3370e0040020080062440042440022246460020024466006600400400210581840100d87980821a002d162a1a48ceb84cf5f6").unwrap(); + + let raw_inputs = hex::decode("8782582017a796d7541fc547bca875a82c7bd35e45b821cb69bebd21db774bcde16fffdc00825820a0bfcb30c19402a5bc79ecd33ddea4d67d8d6cb2ebe694c5ac2f14abc47092f400825820a0bfcb30c19402a5bc79ecd33ddea4d67d8d6cb2ebe694c5ac2f14abc47092f401825820cd3589fae0ae4ad33dbc8a8d99b4e99b4a78c2b0473040aab34da8e44fba9fa300825820f375a19211ee8ce75b8306a2604e2a0e8c50dfae7a103dd3688f980bec5e29b200825820c5bc1437117c200f325467dcede3c09fbfca32f9fe399bd40c778ab2fa832f430182582017a796d7541fc547bca875a82c7bd35e45b821cb69bebd21db774bcde16fffdc01").unwrap(); + let raw_outputs = hex::decode("8783581d71ef703237c089955b1f87e4a5bf1c706151b45bdfcf5a957ea20bef321a000f52c6582058260815eb79c0e8168502dbe5c6e73ac0fa37b57fe2b0582f765778c9c63550825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a000f4240825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a005da7c4ac581c1e852216c006c55490cd85b6e0ba523a37c2be3526f479e61cca41eba2444e616d6902454e616d693201581c21514b6c51f0aaa0c9c337329ce93c52fad208cfd0b4ccd51074d9dcab4d50494e4b44494e4f4331494439014d50494e4b44494e4f4332494439014d50494e4b44494e4f4333494439014d50494e4b44494e4f4334494439014d50494e4b44494e4f4335494439014d50494e4b44494e4f4336494439014d50494e4b44494e4f4337494439014d50494e4b44494e4f4338494439014d50494e4b44494e4f4339494439014e50494e4b44494e4f433130494439014e50494e4b44494e4f43313149443901581c321668648736a09a40bc3ef5fbd05590530be624e39af84fab3bc6bba84d435033503050726f6d6f333431014f4b696e67447572616e747574323531015053616d757257686950726f6d6f313933015143727970746f6d626f50726f6d6f3134360154536b756c6c62616c6c4c6f676f50726f6d6f35320155536b756c6c62616c6c4c6f676f50726f6d6f33313301581b425345647761726473536c616d6d696e48616e647330303034313201581c42697a61727265537461727344726561646c696f6e526f736533323401581c9ec93fd47d1e43ec5bb5dad70af92b6498b92d20d4325f9027ac4743a1574a6f736570684d6972616e64615754503030353754323601581ca7dc26584358f5ca8d99d7aa646a7b658120296a94588e1cefad8940a157546573744e616d69467269656e645368696e794a50454701581cbc5a0f0f3f1bfa92286410388c54655687bb518f47e12b88c0d0728ca14b566563746f724669656c6401581cc4c00fbd8fa227442a5e7cdecde33b24588494d05a2c50fda8938c6da1444b49445a19c346581cd068fe47123ec4c86460eeb74c7d7765c67d2df295a3ac86d664ed45a156506c757475734669727374436c61737350686f746f3101581cd3b65744dd067fd7103cc6a4019cc9cd5f8627b78174c05dc67a9ad6a1544c696665496e4c6f636b646f776e53314d31503201581cd973d8df645da318bb331dbe4af8eb0270079470225d955c6183445ca15148617070794269727468646179416c657301581cdf9974c2192744f3b6dac92990769fe26c36b0ac8bcc623fb457a45da14d575450303035466c796572323401581cee47c1521b55a006c345f739f084f5c205aeb295e3e42fa66b9d1dd2a151446965666e6643727970746f6e6965726505825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a000f4240825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a004c4b40825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a0028a3c6825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a053e01e7").unwrap(); + + let inputs = MaybeIndefArray::::decode_fragment(&raw_inputs).unwrap(); + let outputs = MaybeIndefArray::::decode_fragment(&raw_outputs).unwrap(); + + let utxos: MaybeIndefArray = MaybeIndefArray::Indef( + inputs + .iter() + .zip(outputs.iter()) + .map(|(input, output)| ResolvedInput { + input: input.clone(), + output: output.clone(), + }) + .collect(), + ); + + let slot_config = SlotConfig { + zero_time: 1596059091000, // Mainnet network + zero_slot: 4492800, + slot_length: 1000, + }; + + let costs: Vec = vec![ + 197209, 0, 1, 1, 396231, 621, 0, 1, 150000, 1000, 0, 1, 150000, 32, 2477736, 29175, 4, + 29773, 100, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 100, 100, 29773, + 100, 150000, 32, 150000, 32, 150000, 32, 150000, 1000, 0, 1, 150000, 32, 150000, 1000, 0, + 8, 148000, 425507, 118, 0, 1, 1, 150000, 1000, 0, 8, 150000, 112536, 247, 1, 150000, 10000, + 1, 136542, 1326, 1, 1000, 150000, 1000, 1, 150000, 32, 150000, 32, 150000, 32, 1, 1, + 150000, 1, 150000, 4, 103599, 248, 1, 103599, 248, 1, 145276, 1366, 1, 179690, 497, 1, + 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 148000, 425507, + 118, 0, 1, 1, 61516, 11218, 0, 1, 150000, 32, 148000, 425507, 118, 0, 1, 1, 148000, 425507, + 118, 0, 1, 1, 2477736, 29175, 4, 0, 82363, 4, 150000, 5000, 0, 1, 150000, 32, 197209, 0, 1, + 1, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, + 3345831, 1, 1, + ]; + + let cost_mdl = CostMdls { + plutus_v1: Some(costs), + plutus_v2: None, + }; + + let initial_budget = ExBudget { + cpu: 10000000000, + mem: 14000000, + }; + + let multi_era_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) + .unwrap(); + match multi_era_tx { + MultiEraTx::Babbage(tx) => { + let redeemers = eval_phase_two( + &tx, + &utxos, + Some(&cost_mdl), + Some(&initial_budget), + &slot_config, + false, + ) + .unwrap(); + + assert_eq!(redeemers.len(), 1); + + let total_budget_used = + redeemers + .iter() + .fold(ExBudget { mem: 0, cpu: 0 }, |accum, curr| ExBudget { + mem: accum.mem + curr.ex_units.mem as i64, + cpu: accum.cpu + curr.ex_units.steps as i64, + }); + + println!("{:?}", total_budget_used); + + assert_eq!( + total_budget_used, + // Numbers came uplc evaluate + ExBudget { + cpu: 1221507148, + mem: 2954794 + } + ); + } + _ => unreachable!(), + }; +} + +#[test] +fn test_eval_8() { + let tx_bytes = hex::decode("84a800838258206eba69fbd940408c9ba007cb6059282be7afc95682e68a6160110239287c393c00825820ffcb865d43f1f1734de4c547584a1fe757ea58c58df29b18a5a5a148f92db37700825820aa1643f6a487d81273fa18df738b9d39a0930fd2b6562ba713169d33731e42c6010182825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a00113b04a1581c4bc8e3d235d4d3ecf3d963293b4e775cb793c29e96fd4b0103b2e570a14231310c825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51b0000000246f999e7021a00085b3f09a1581c4bc8e3d235d4d3ecf3d963293b4e775cb793c29e96fd4b0103b2e570a14231310c0b5820fbfc34e93ecf7c829127cb623f88ee5a41e553356cce7f16c6fda22dd7bd2c640d81825820aa1643f6a487d81273fa18df738b9d39a0930fd2b6562ba713169d33731e42c60110825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51b0000000246e822cb111a000c88dfa30081825820065dd553fbe4e240a8f819bb9e333a7483de4a22b65c7fb6a95ce9450f84dff75840510d58d69433006c8a7880b704c703810cdda4554b1b3bde9df0eb2e74247c88f0f2e1639a5f8d92b347651e147cc501a91f2bd93c73573f4c9f529d20212c0c0581840100d87980821a0029125e1a2a63a39a0681590e1d590e1a010000323322323232323232323232332232323233223232323232323232323232323232323232323232323232323222323253353322350012235003225335350042222222222223500e2222222222225335333530361200103701800c15335333530361200103701700b15335333530361200103801600a1533533303904b0150091533533303904b014008153353335303612001048013007153353335303612001223500222350032253353304f0040021330510030011052012006153353501122350022235009223235003223253335008215333500421533533057002001150031058105710571533350032105715001105615333500321057105615001153350061533500115003105515335001105515003135005223500322533350042153335003215335330570020011330410050031058105710571533350022105713303f003001105615333500221057105613303f00300115335333530361200104a010004153353335303612001223500222350032253353304c00400213303a003001105200f00315335333530361200122350022235003225335333573466e3c01000814c1484cc0e800c00441480380084cc128034004413041304130413041304130413041304130413041304cc0b800c00440d0cccd5cd19b8735573a6ea80112000201523263201533573802c02a0266666ae68cdc39aab9d5002480008cc8848cc00400c008c8c8c8c8c8c8c8c8c8c8c8c8c8cccd5cd19b8735573aa018900011999999999999111111111110919999999999980080680600580500480400380300280200180119a80a00a9aba1500c33501401535742a01666a02802c6ae854028ccd54061d7280b9aba150093335501875ca02e6ae854020cd4050074d5d0a803999aa80c00f3ad35742a00c6464646666ae68cdc39aab9d5002480008cc8848cc00400c008c8c8c8cccd5cd19b8735573aa004900011991091980080180119a8143ad35742a00460526ae84d5d1280111931901599ab9c02c02b029135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae754009200023322123300100300233502875a6ae854008c0a4d5d09aba2500223263202b33573805805605226aae7940044dd50009aba135744a004464c6404e66ae700a009c0944d55cf280089baa00135742a00a66a028eb8d5d0a802199aa80c00d10009aba150033335501875c40026ae854008c070d5d09aba2500223263202333573804804604226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aab9e5001137540026ae854008c030d5d09aba2500223263201533573802c02a0262028264c6402866ae712410350543500014135573ca00226ea800448c88c008dd6000990009aa816111999aab9f00125023233502230043574200460066ae880080488c8c8cccd5cd19b8735573aa004900011991091980080180118051aba150023005357426ae8940088c98c8048cd5ce00980900809aab9e5001137540024646464646666ae68cdc39aab9d5004480008cccc888848cccc00401401000c008c8c8c8cccd5cd19b8735573aa004900011991091980080180118099aba1500233500d012357426ae8940088c98c805ccd5ce00c00b80a89aab9e5001137540026ae854010ccd54021d728039aba150033232323333573466e1d4005200423212223002004357426aae79400c8cccd5cd19b875002480088c84888c004010dd71aba135573ca00846666ae68cdc3a801a400042444006464c6403266ae7006806405c0580544d55cea80089baa00135742a00466a012eb8d5d09aba2500223263201333573802802602226ae8940044d5d1280089aab9e500113754002266aa002eb9d6889119118011bab00132001355029223233335573e0044a042466a04066aa03a600c6aae754008c014d55cf280118021aba200301013574200224464646666ae68cdc3a800a40004642446004006600a6ae84d55cf280191999ab9a3370ea0049001109100091931900819ab9c01101000e00d135573aa00226ea80048c8c8cccd5cd19b875001480188c848888c010014c01cd5d09aab9e500323333573466e1d400920042321222230020053009357426aae7940108cccd5cd19b875003480088c848888c004014c01cd5d09aab9e500523333573466e1d40112000232122223003005375c6ae84d55cf280311931900819ab9c01101000e00d00c00b135573aa00226ea80048c8c8cccd5cd19b8735573aa004900011991091980080180118029aba15002375a6ae84d5d1280111931900619ab9c00d00c00a135573ca00226ea80048c8cccd5cd19b8735573aa002900011bae357426aae7940088c98c8028cd5ce00580500409baa001232323232323333573466e1d4005200c21222222200323333573466e1d4009200a21222222200423333573466e1d400d2008233221222222233001009008375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c4664424444444660040120106eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc8848888888cc018024020c030d5d0a8049bae357426ae8940248cccd5cd19b875006480088c848888888c01c020c034d5d09aab9e500b23333573466e1d401d2000232122222223005008300e357426aae7940308c98c804ccd5ce00a00980880800780700680600589aab9d5004135573ca00626aae7940084d55cf280089baa0012323232323333573466e1d400520022333222122333001005004003375a6ae854010dd69aba15003375a6ae84d5d1280191999ab9a3370ea0049000119091180100198041aba135573ca00c464c6401866ae700340300280244d55cea80189aba25001135573ca00226ea80048c8c8cccd5cd19b875001480088c8488c00400cdd71aba135573ca00646666ae68cdc3a8012400046424460040066eb8d5d09aab9e500423263200933573801401200e00c26aae7540044dd500089119191999ab9a3370ea00290021091100091999ab9a3370ea00490011190911180180218031aba135573ca00846666ae68cdc3a801a400042444004464c6401466ae7002c02802001c0184d55cea80089baa0012323333573466e1d40052002202023333573466e1d40092000202023263200633573800e00c00800626aae74dd5000a4c24002921035054310022333573466ebc00800406c068894cd4008400454cd400440644068c8004d540608848894cd400854cd400440708840748854cd400c40748854cd4cc01c0100084ccd4c0244800401c00c004407c88d400888d400c894cd4cc0580100084cc01c00c004406c88d40088888d401088d401c8888c8d401488c94cd4cd40288cd401094cd4ccd5cd19b8f00200102702615003102620262335004202625335333573466e3c00800409c0985400c409854cd4ccc04408c03401c54ccd403054ccd4018540108409484094854ccd401c4094854cd4ccd5cd19baf002001027026150061026210262153335007102521026215335333573466e3c00800409c0985401840984090409054cd4020854cd400884cc088008004409054cd400484090409054cd401c854cd400884cc080008004408454cd4004840844084888ccd54c02848004d403940348d400488ccd54c03448004d404540408d400488ccd40048cc0292000001223300b00200123300a00148000004cd54c024480048d400488cd5402c008ccd40048cd54c034480048d400488cd5403c008d5403800400488ccd5540240640080048cd54c034480048d400488cd5403c008d54034004004ccd554010050008004444888ccd54c010480054028cd54c020480048d400488cd54028008d54024004ccd54c0104800488d4008894cd4ccd54c03448004c8cd404c88ccd400c88008008004d40048800448cc004894cd4008407440040688d400488cc028008014018400c4cd403801000d402c004cd54c020480048d400488c8cd5402c00cc004014c8004d54068894cd40044d5402800c884d4008894cd4cc03000802044888cc0080280104c01800c008c8004d5404c88448894cd40044008884cc014008ccd54c01c480040140100044484888c00c0104484888c004010448848cc00400c008c8004d5403c8844894cd40045401c884cd4020c010008cd54c01848004010004c8004d5403888448894cd40044d400c88004884ccd401488008c010008ccd54c01c480040140100044488008488488cc00401000c448cc004894cd40084004403002c48cd400888ccd400c88008008004d40048800448848cc00400c00888cccd40088cccd40088cc0180080048028802880288cccd400880288cc020008004802880288cccd4008802880288cc01c00800480288cccd40088028802880288cc014008004894cd4ccd5cd19b8f350022200235001220020080071333573466e1cd400888004d40048800402001c401c894ccccccd4008854ccccccd400884cc01400800488402484020401c401c8840248840248854ccccccd400c840248854cd4cc01c0100084cc02000c00440288402440204020884028884028854ccccccd40088402088402484cc014008004401c401c88402488402454ccccccd40048401c8840208401c401c401888402088402054ccccccd40048401c8840208401c4018401c8840208840208854ccccccd400c8402488402884024402040208854cd4ccd5cd19b8f00400200b00a133008003001100a22100a2215333333350032100922100a210091008100822100a2215335333573466e3c01000802c0284cc02400c004402888cd40088cd40088cd40088cd40088cc01c00800480248cd400880248cc01c008004888024888cd401080248894cd4ccd5cd19b8700600300c00b15335333573466e1c01400803002c4cc028010004402c402c88ccd5cd19b8f00200100500422333573466e1c00800401000c488008488004448c8c00400488cc00cc008008005f5f6").unwrap(); + + let raw_inputs = hex::decode("84825820ffcb865d43f1f1734de4c547584a1fe757ea58c58df29b18a5a5a148f92db377008258206eba69fbd940408c9ba007cb6059282be7afc95682e68a6160110239287c393c00825820aa1643f6a487d81273fa18df738b9d39a0930fd2b6562ba713169d33731e42c600825820aa1643f6a487d81273fa18df738b9d39a0930fd2b6562ba713169d33731e42c601").unwrap(); + let raw_outputs = hex::decode("84825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a000f4240825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a000f4240825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a000f4240825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51b0000000246f4abaa").unwrap(); + + let inputs = MaybeIndefArray::::decode_fragment(&raw_inputs).unwrap(); + let outputs = MaybeIndefArray::::decode_fragment(&raw_outputs).unwrap(); + + let utxos: MaybeIndefArray = MaybeIndefArray::Indef( + inputs + .iter() + .zip(outputs.iter()) + .map(|(input, output)| ResolvedInput { + input: input.clone(), + output: output.clone(), + }) + .collect(), + ); + + let slot_config = SlotConfig { + zero_time: 1596059091000, // Mainnet network + zero_slot: 4492800, + slot_length: 1000, + }; + + let costs: Vec = vec![ + 205665, + 812, + 1, + 1, + 1000, + 571, + 0, + 1, + 1000, + 24177, + 4, + 1, + 1000, + 32, + 117366, + 10475, + 4, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 100, + 100, + 23000, + 100, + 19537, + 32, + 175354, + 32, + 46417, + 4, + 221973, + 511, + 0, + 1, + 89141, + 32, + 497525, + 14068, + 4, + 2, + 196500, + 453240, + 220, + 0, + 1, + 1, + 1000, + 28662, + 4, + 2, + 245000, + 216773, + 62, + 1, + 1060367, + 12586, + 1, + 208512, + 421, + 1, + 187000, + 1000, + 52998, + 1, + 80436, + 32, + 43249, + 32, + 1000, + 32, + 80556, + 1, + 57667, + 4, + 1000, + 10, + 197145, + 156, + 1, + 197145, + 156, + 1, + 204924, + 473, + 1, + 208896, + 511, + 1, + 52467, + 32, + 64832, + 32, + 65493, + 32, + 22558, + 32, + 16563, + 32, + 76511, + 32, + 196500, + 453240, + 220, + 0, + 1, + 1, + 69522, + 11687, + 0, + 1, + 60091, + 32, + 196500, + 453240, + 220, + 0, + 1, + 1, + 196500, + 453240, + 220, + 0, + 1, + 1, + 1159724, + 392670, + 0, + 2, + 806990, + 30482, + 4, + 1927926, + 82523, + 4, + 265318, + 0, + 4, + 0, + 85931, + 32, + 205665, + 812, + 1, + 1, + 41182, + 32, + 212342, + 32, + 31220, + 32, + 32696, + 32, + 43357, + 32, + 32247, + 32, + 38314, + 32, + 20000000000, + 20000000000, + 9462713, + 1021, + 10, + 20000000000, + 0, + 20000000000, + ]; + + let cost_mdl = CostMdls { + plutus_v1: None, + plutus_v2: Some(costs), + }; + + let initial_budget = ExBudget { + cpu: 10000000000, + mem: 14000000, + }; + + let multi_era_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) + .unwrap(); + match multi_era_tx { + MultiEraTx::Babbage(tx) => { + let redeemers = eval_phase_two( + &tx, + &utxos, + Some(&cost_mdl), + Some(&initial_budget), + &slot_config, + false, + ) + .unwrap(); + + assert_eq!(redeemers.len(), 1); + + let total_budget_used = + redeemers + .iter() + .fold(ExBudget { mem: 0, cpu: 0 }, |accum, curr| ExBudget { + mem: accum.mem + curr.ex_units.mem as i64, + cpu: accum.cpu + curr.ex_units.steps as i64, + }); + + println!("{:?}", total_budget_used); + + assert_eq!( + total_budget_used, + // Numbers came uplc evaluate + ExBudget { + cpu: 711173018, + mem: 2691678 + } + ); + } + _ => unreachable!(), + }; +} + +#[test] +fn eval_missing_redeemer() { + let tx_bytes = hex::decode("84a30082825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c5000825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c50010181825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02cf47c8021a00028d89a1068149480100002221200101f5f6").unwrap(); + + let multi_era_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) + .unwrap(); + + let inputs = multi_era_tx + .as_babbage() + .unwrap() + .transaction_body + .inputs + .clone(); + + let raw_outputs = hex::decode("82825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02b3603082581d703a888d65f16790950a72daee1f63aa05add6d268434107cfa5b677121a001e8480").unwrap(); + + let outputs = MaybeIndefArray::::decode_fragment(&raw_outputs).unwrap(); + + let utxos: MaybeIndefArray = MaybeIndefArray::Indef( + inputs + .iter() + .zip(outputs.iter()) + .map(|(input, output)| ResolvedInput { + input: input.clone(), + output: output.clone(), + }) + .collect(), + ); + + let slot_config = SlotConfig { + zero_time: 1660003200000, // Preview network + zero_slot: 0, + slot_length: 1000, + }; + + let costs: Vec = vec![ + 205665, 812, 1, 1, 1000, 571, 0, 1, 1000, 24177, 4, 1, 1000, 32, 117366, 10475, 4, 23000, + 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 100, 100, 23000, 100, + 19537, 32, 175354, 32, 46417, 4, 221973, 511, 0, 1, 89141, 32, 497525, 14068, 4, 2, 196500, + 453240, 220, 0, 1, 1, 1000, 28662, 4, 2, 245000, 216773, 62, 1, 1060367, 12586, 1, 208512, + 421, 1, 187000, 1000, 52998, 1, 80436, 32, 43249, 32, 1000, 32, 80556, 1, 57667, 4, 1000, + 10, 197145, 156, 1, 197145, 156, 1, 204924, 473, 1, 208896, 511, 1, 52467, 32, 64832, 32, + 65493, 32, 22558, 32, 16563, 32, 76511, 32, 196500, 453240, 220, 0, 1, 1, 69522, 11687, 0, + 1, 60091, 32, 196500, 453240, 220, 0, 1, 1, 196500, 453240, 220, 0, 1, 1, 806990, 30482, 4, + 1927926, 82523, 4, 265318, 0, 4, 0, 85931, 32, 205665, 812, 1, 1, 41182, 32, 212342, 32, + 31220, 32, 32696, 32, 43357, 32, 32247, 32, 38314, 32, 9462713, 1021, 10, + ]; + + let cost_mdl = CostMdls { + plutus_v1: Some(costs), + plutus_v2: None, + }; + + let initial_budget = ExBudget { + cpu: 10000000000, + mem: 14000000, + }; + + let multi_era_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) + .unwrap(); + + match multi_era_tx { + MultiEraTx::Babbage(tx) => { + eval_phase_two( + &tx, + &utxos, + Some(&cost_mdl), + Some(&initial_budget), + &slot_config, + false, + ) + .unwrap(); + } + _ => unreachable!(), + }; +} + +#[test] +fn eval_extraneous_redeemer() { + let tx_bytes = hex::decode("84a70082825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c5000825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c50010181825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02cf2b47021a0002aa0a0b5820fc54f302cff3a8a1cb374f5e4979e18a1d3627dcf4539637b03f5959eb8565bf0d81825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c500110825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02af51c2111a0003ff0fa40081825820065dd553fbe4e240a8f819bb9e333a7483de4a22b65c7fb6a95ce9450f84dff758402c26125a057a696079d08f2c8c9d2b8ccda9fe7cf7360c1a86712b85a91db82a3b80996b30ba6f4b2f969c93eb50694e0f6ea0bcf129080dcc07ecd9e605f00a049fd87980ff0582840000d879808219044c1a000382d48401001864821903e81903e8068149480100002221200101f5f6").unwrap(); + + let multi_era_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) + .unwrap(); + + let inputs = multi_era_tx + .as_babbage() + .unwrap() + .transaction_body + .inputs + .clone(); + + let raw_outputs = hex::decode("82825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02b3603082581d703a888d65f16790950a72daee1f63aa05add6d268434107cfa5b677121a001e8480").unwrap(); + + let outputs = MaybeIndefArray::::decode_fragment(&raw_outputs).unwrap(); + + let utxos: MaybeIndefArray = MaybeIndefArray::Indef( + inputs + .iter() + .zip(outputs.iter()) + .map(|(input, output)| ResolvedInput { + input: input.clone(), + output: output.clone(), + }) + .collect(), + ); + + let slot_config = SlotConfig { + zero_time: 1660003200000, // Preview network + zero_slot: 0, + slot_length: 1000, + }; + + let costs: Vec = vec![ + 205665, 812, 1, 1, 1000, 571, 0, 1, 1000, 24177, 4, 1, 1000, 32, 117366, 10475, 4, 23000, + 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 100, 100, 23000, 100, + 19537, 32, 175354, 32, 46417, 4, 221973, 511, 0, 1, 89141, 32, 497525, 14068, 4, 2, 196500, + 453240, 220, 0, 1, 1, 1000, 28662, 4, 2, 245000, 216773, 62, 1, 1060367, 12586, 1, 208512, + 421, 1, 187000, 1000, 52998, 1, 80436, 32, 43249, 32, 1000, 32, 80556, 1, 57667, 4, 1000, + 10, 197145, 156, 1, 197145, 156, 1, 204924, 473, 1, 208896, 511, 1, 52467, 32, 64832, 32, + 65493, 32, 22558, 32, 16563, 32, 76511, 32, 196500, 453240, 220, 0, 1, 1, 69522, 11687, 0, + 1, 60091, 32, 196500, 453240, 220, 0, 1, 1, 196500, 453240, 220, 0, 1, 1, 806990, 30482, 4, + 1927926, 82523, 4, 265318, 0, 4, 0, 85931, 32, 205665, 812, 1, 1, 41182, 32, 212342, 32, + 31220, 32, 32696, 32, 43357, 32, 32247, 32, 38314, 32, 9462713, 1021, 10, + ]; + + let cost_mdl = CostMdls { + plutus_v1: Some(costs), + plutus_v2: None, + }; + + let initial_budget = ExBudget { + cpu: 10000000000, + mem: 14000000, + }; + + let multi_era_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) + .unwrap(); + + match multi_era_tx { + MultiEraTx::Babbage(tx) => { + assert!(eval_phase_two( + &tx, + &utxos, + Some(&cost_mdl), + Some(&initial_budget), + &slot_config, + false, + ) + .is_err()); + } + _ => unreachable!(), + }; +} diff --git a/crates/uplc/src/tx/to_plutus_data.rs b/crates/uplc/src/tx/to_plutus_data.rs new file mode 100644 index 00000000..3c6423aa --- /dev/null +++ b/crates/uplc/src/tx/to_plutus_data.rs @@ -0,0 +1,611 @@ +use pallas_addresses::{Address, ShelleyDelegationPart, ShelleyPaymentPart}; +use pallas_codec::utils::{AnyUInt, Bytes, Int, KeyValuePairs}; +use pallas_crypto::hash::Hash; +use pallas_primitives::babbage::{AssetName, BigInt, Constr, Mint, PlutusData, ScriptRef}; +use pallas_primitives::babbage::{ + Certificate, DatumOption, Redeemer, Script, StakeCredential, TransactionInput, + TransactionOutput, Value, +}; +use pallas_traverse::ComputeHash; + +use super::script_context::{ScriptContext, ScriptPurpose, TimeRange, TxInInfo, TxInfo, TxOut}; + +fn wrap_with_constr(index: u64, data: PlutusData) -> PlutusData { + PlutusData::Constr(Constr { + tag: constr_index(index), + any_constructor: None, + fields: vec![data], + }) +} + +fn wrap_multiple_with_constr(index: u64, data: Vec) -> PlutusData { + PlutusData::Constr(Constr { + tag: constr_index(index), + any_constructor: None, + fields: data, + }) +} + +fn empty_constr(index: u64) -> PlutusData { + PlutusData::Constr(Constr { + tag: constr_index(index), + any_constructor: None, + fields: vec![], + }) +} + +/// Translate constructor index to cbor tag. +fn constr_index(index: u64) -> u64 { + 121 + index +} + +pub trait ToPlutusData { + fn to_plutus_data(&self) -> PlutusData; +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct MintValue { + pub mint_value: Mint, +} + +impl ToPlutusData for Address { + fn to_plutus_data(&self) -> PlutusData { + match self { + Address::Shelley(shelley_address) => { + let payment_part = shelley_address.payment(); + let stake_part = shelley_address.delegation(); + + let payment_part_plutus_data = match payment_part { + ShelleyPaymentPart::Key(payment_keyhash) => { + wrap_with_constr(0, payment_keyhash.to_plutus_data()) + } + ShelleyPaymentPart::Script(script_hash) => { + wrap_with_constr(1, script_hash.to_plutus_data()) + } + }; + + let stake_part_plutus_data = match stake_part { + ShelleyDelegationPart::Key(stake_keyhash) => { + Some(StakeCredential::AddrKeyhash(*stake_keyhash)).to_plutus_data() + } + ShelleyDelegationPart::Script(script_hash) => { + Some(StakeCredential::Scripthash(*script_hash)).to_plutus_data() + } + ShelleyDelegationPart::Pointer(pointer) => Some(wrap_multiple_with_constr( + 1, + vec![ + pointer.slot().to_plutus_data(), + pointer.tx_idx().to_plutus_data(), + pointer.cert_idx().to_plutus_data(), + ], + )) + .to_plutus_data(), + ShelleyDelegationPart::Null => None::.to_plutus_data(), + }; + + wrap_multiple_with_constr(0, vec![payment_part_plutus_data, stake_part_plutus_data]) + } + _ => unreachable!(), + } + } +} + +impl ToPlutusData for TransactionInput { + fn to_plutus_data(&self) -> PlutusData { + wrap_multiple_with_constr( + 0, + vec![ + wrap_with_constr(0, self.transaction_id.to_plutus_data()), + PlutusData::BigInt(BigInt::Int((self.index as i64).into())), + ], + ) + } +} + +impl ToPlutusData for Hash { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::BoundedBytes(self.to_vec().into()) + } +} + +impl ToPlutusData for Bytes { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::BoundedBytes(self.clone()) + } +} + +impl ToPlutusData for (K, V) { + fn to_plutus_data(&self) -> PlutusData { + wrap_multiple_with_constr(0, vec![self.0.to_plutus_data(), self.1.to_plutus_data()]) + } +} + +impl ToPlutusData for Vec +where + A: ToPlutusData, +{ + fn to_plutus_data(&self) -> PlutusData { + PlutusData::Array(self.iter().map(|p| p.to_plutus_data()).collect()) + } +} + +impl ToPlutusData for KeyValuePairs +where + K: ToPlutusData + Clone, + V: ToPlutusData + Clone, +{ + fn to_plutus_data(&self) -> PlutusData { + let mut data_vec: Vec<(PlutusData, PlutusData)> = vec![]; + for (key, value) in self.iter() { + data_vec.push((key.to_plutus_data(), value.to_plutus_data())) + } + PlutusData::Map(KeyValuePairs::Def(data_vec)) + } +} + +impl ToPlutusData for Option { + fn to_plutus_data(&self) -> PlutusData { + match self { + None => empty_constr(1), + Some(data) => wrap_with_constr(0, data.to_plutus_data()), + } + } +} + +// Does this here surely overwrite Option from above for DatumOption? +impl ToPlutusData for Option { + // NoOutputDatum = 0 | OutputDatumHash = 1 | OutputDatum = 2 + fn to_plutus_data(&self) -> PlutusData { + match self { + None => empty_constr(0), + Some(option) => match option { + DatumOption::Hash(hash) => wrap_with_constr(1, hash.to_plutus_data()), + DatumOption::Data(data) => wrap_with_constr(2, data.0.clone()), + }, + } + } +} + +impl ToPlutusData for AnyUInt { + fn to_plutus_data(&self) -> PlutusData { + match self { + AnyUInt::U8(n) => PlutusData::BigInt(BigInt::Int(Int::from(*n as i64))), + AnyUInt::U16(n) => PlutusData::BigInt(BigInt::Int(Int::from(*n as i64))), + AnyUInt::U32(n) => PlutusData::BigInt(BigInt::Int(Int::from(*n as i64))), + AnyUInt::U64(n) => PlutusData::BigInt(BigInt::Int(Int::from(*n as i64))), + AnyUInt::MajorByte(n) => PlutusData::BigInt(BigInt::Int(Int::from(*n as i64))), // is this correct? I don't know exactly what is does + } + } +} + +impl ToPlutusData for Int { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::BigInt(BigInt::Int(*self)) + } +} + +impl ToPlutusData for BigInt { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::BigInt(self.clone()) + } +} + +impl ToPlutusData for i64 { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::BigInt(BigInt::Int(Int::from(*self))) + } +} + +impl ToPlutusData for u64 { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::BigInt(BigInt::Int(Int::from(*self as i64))) + } +} + +impl ToPlutusData for Value { + fn to_plutus_data(&self) -> PlutusData { + match self { + Value::Coin(coin) => PlutusData::Map(KeyValuePairs::Def(vec![( + Bytes::from(vec![]).to_plutus_data(), + PlutusData::Map(KeyValuePairs::Def(vec![( + AssetName::from(vec![]).to_plutus_data(), + coin.to_plutus_data(), + )])), + )])), + Value::Multiasset(coin, multiassets) => { + let mut data_vec: Vec<(PlutusData, PlutusData)> = vec![( + Bytes::from(vec![]).to_plutus_data(), + PlutusData::Map(KeyValuePairs::Def(vec![( + AssetName::from(vec![]).to_plutus_data(), + coin.to_plutus_data(), + )])), + )]; + + for (policy_id, assets) in multiassets.iter() { + let mut assets_vec = vec![]; + for (asset, amount) in assets.iter() { + assets_vec.push((asset.to_plutus_data(), amount.to_plutus_data())); + } + data_vec.push(( + policy_id.to_plutus_data(), + PlutusData::Map(KeyValuePairs::Def(assets_vec)), + )); + } + + PlutusData::Map(KeyValuePairs::Def(data_vec)) + } + } + } +} + +impl ToPlutusData for MintValue { + fn to_plutus_data(&self) -> PlutusData { + let mut data_vec: Vec<(PlutusData, PlutusData)> = vec![( + Bytes::from(vec![]).to_plutus_data(), + PlutusData::Map(KeyValuePairs::Def(vec![( + AssetName::from(vec![]).to_plutus_data(), + 0_i64.to_plutus_data(), + )])), + )]; + + for (policy_id, assets) in self.mint_value.iter() { + let mut assets_vec = vec![]; + for (asset, amount) in assets.iter() { + assets_vec.push((asset.to_plutus_data(), amount.to_plutus_data())); + } + data_vec.push(( + policy_id.to_plutus_data(), + PlutusData::Map(KeyValuePairs::Def(assets_vec)), + )); + } + + PlutusData::Map(KeyValuePairs::Def(data_vec)) + } +} + +impl ToPlutusData for ScriptRef { + fn to_plutus_data(&self) -> PlutusData { + match &self.0 { + Script::NativeScript(native_script) => native_script.compute_hash().to_plutus_data(), + Script::PlutusV1Script(plutus_v1) => plutus_v1.compute_hash().to_plutus_data(), + Script::PlutusV2Script(plutus_v2) => plutus_v2.compute_hash().to_plutus_data(), + } + } +} + +impl ToPlutusData for TxOut { + fn to_plutus_data(&self) -> PlutusData { + match self { + TxOut::V1(output) => match output { + TransactionOutput::Legacy(legacy_output) => wrap_multiple_with_constr( + 0, + vec![ + Address::from_bytes(&legacy_output.address) + .unwrap() + .to_plutus_data(), + legacy_output.amount.to_plutus_data(), + legacy_output.datum_hash.to_plutus_data(), + ], + ), + TransactionOutput::PostAlonzo(post_alonzo_output) => wrap_multiple_with_constr( + 0, + vec![ + Address::from_bytes(&post_alonzo_output.address) + .unwrap() + .to_plutus_data(), + post_alonzo_output.value.to_plutus_data(), + match post_alonzo_output.datum_option { + Some(DatumOption::Hash(hash)) => Some(hash).to_plutus_data(), + _ => None::.to_plutus_data(), + }, + ], + ), + }, + TxOut::V2(output) => match output { + TransactionOutput::Legacy(legacy_output) => wrap_multiple_with_constr( + 0, + vec![ + Address::from_bytes(&legacy_output.address) + .unwrap() + .to_plutus_data(), + legacy_output.amount.to_plutus_data(), + match legacy_output.datum_hash { + Some(hash) => wrap_with_constr(1, hash.to_plutus_data()), + _ => empty_constr(0), + }, + None::.to_plutus_data(), + ], + ), + TransactionOutput::PostAlonzo(post_alonzo_output) => wrap_multiple_with_constr( + 0, + vec![ + Address::from_bytes(&post_alonzo_output.address) + .unwrap() + .to_plutus_data(), + post_alonzo_output.value.to_plutus_data(), + post_alonzo_output.datum_option.to_plutus_data(), + post_alonzo_output.script_ref.to_plutus_data(), + ], + ), + }, + } + } +} + +impl ToPlutusData for StakeCredential { + // Stake Credential needs to be wrapped inside another Constr, because we could have either a StakingHash or a StakingPtr + // The current implementation of StakeCredential doesn't capture the credential of a Pointer address. + // So a StakeCredential for a Pointer address needs to be converted separately + fn to_plutus_data(&self) -> PlutusData { + match self { + StakeCredential::AddrKeyhash(addr_keyhas) => { + wrap_with_constr(0, wrap_with_constr(0, addr_keyhas.to_plutus_data())) + } + StakeCredential::Scripthash(script_hash) => { + wrap_with_constr(0, wrap_with_constr(1, script_hash.to_plutus_data())) + } + } + } +} + +impl ToPlutusData for Certificate { + fn to_plutus_data(&self) -> PlutusData { + match self { + Certificate::StakeRegistration(stake_credential) => { + wrap_with_constr(0, stake_credential.to_plutus_data()) + } + Certificate::StakeDeregistration(stake_credential) => { + wrap_with_constr(1, stake_credential.to_plutus_data()) + } + Certificate::StakeDelegation(stake_credential, pool_keyhash) => { + wrap_multiple_with_constr( + 2, + vec![ + stake_credential.to_plutus_data(), + pool_keyhash.to_plutus_data(), + ], + ) + } + Certificate::PoolRegistration { + operator, + vrf_keyhash, + pledge: _, + cost: _, + margin: _, + reward_account: _, + pool_owners: _, + relays: _, + pool_metadata: _, + } => wrap_multiple_with_constr( + 3, + vec![operator.to_plutus_data(), vrf_keyhash.to_plutus_data()], + ), + Certificate::PoolRetirement(pool_keyhash, epoch) => wrap_multiple_with_constr( + 4, + vec![pool_keyhash.to_plutus_data(), epoch.to_plutus_data()], + ), + Certificate::GenesisKeyDelegation(_, _, _) => empty_constr(5), + Certificate::MoveInstantaneousRewardsCert(_) => empty_constr(6), + } + } +} + +impl ToPlutusData for Redeemer { + fn to_plutus_data(&self) -> PlutusData { + self.data.clone() + } +} + +impl ToPlutusData for PlutusData { + fn to_plutus_data(&self) -> PlutusData { + self.clone() + } +} + +impl ToPlutusData for TimeRange { + fn to_plutus_data(&self) -> PlutusData { + match &self { + TimeRange { + lower_bound: Some(lower_bound), + upper_bound: None, + } => { + wrap_multiple_with_constr( + 0, + vec![ + // LowerBound + wrap_multiple_with_constr( + 0, + vec![ + // Finite + wrap_with_constr(1, lower_bound.to_plutus_data()), + // Closure + true.to_plutus_data(), + ], + ), //UpperBound + wrap_multiple_with_constr( + 0, + vec![ + // PosInf + empty_constr(2), + // Closure + true.to_plutus_data(), + ], + ), + ], + ) + } + TimeRange { + lower_bound: None, + upper_bound: Some(upper_bound), + } => { + wrap_multiple_with_constr( + 0, + vec![ + // LowerBound + wrap_multiple_with_constr( + 0, + vec![ + // NegInf + empty_constr(0), + // Closure + true.to_plutus_data(), + ], + ), + //UpperBound + wrap_multiple_with_constr( + 0, + vec![ + // Finite + wrap_with_constr(1, upper_bound.to_plutus_data()), + // Closure + true.to_plutus_data(), + ], + ), + ], + ) + } + TimeRange { + lower_bound: Some(lower_bound), + upper_bound: Some(upper_bound), + } => { + wrap_multiple_with_constr( + 0, + vec![ + // LowerBound + wrap_multiple_with_constr( + 0, + vec![ + // Finite + wrap_with_constr(1, lower_bound.to_plutus_data()), + // Closure + true.to_plutus_data(), + ], + ), + //UpperBound + wrap_multiple_with_constr( + 0, + vec![ + // Finite + wrap_with_constr(1, upper_bound.to_plutus_data()), + // Closure + false.to_plutus_data(), + ], + ), + ], + ) + } + TimeRange { + lower_bound: None, + upper_bound: None, + } => { + wrap_multiple_with_constr( + 0, + vec![ + // LowerBound + wrap_multiple_with_constr( + 0, + vec![ + // NegInf + empty_constr(0), + // Closure + true.to_plutus_data(), + ], + ), + //UpperBound + wrap_multiple_with_constr( + 0, + vec![ + // PosInf + empty_constr(2), + // Closure + true.to_plutus_data(), + ], + ), + ], + ) + } + } + } +} + +impl ToPlutusData for TxInInfo { + fn to_plutus_data(&self) -> PlutusData { + wrap_multiple_with_constr( + 0, + vec![ + self.out_ref.to_plutus_data(), + self.resolved.to_plutus_data(), + ], + ) + } +} + +impl ToPlutusData for ScriptPurpose { + fn to_plutus_data(&self) -> PlutusData { + match self { + ScriptPurpose::Minting(policy_id) => wrap_with_constr(0, policy_id.to_plutus_data()), + ScriptPurpose::Spending(out_ref) => wrap_with_constr(1, out_ref.to_plutus_data()), + ScriptPurpose::Rewarding(stake_credential) => { + wrap_with_constr(2, stake_credential.to_plutus_data()) + } + ScriptPurpose::Certifying(dcert) => wrap_with_constr(3, dcert.to_plutus_data()), + } + } +} + +impl ToPlutusData for TxInfo { + fn to_plutus_data(&self) -> PlutusData { + match self { + TxInfo::V1(tx_info) => wrap_multiple_with_constr( + 0, + vec![ + tx_info.inputs.to_plutus_data(), + tx_info.outputs.to_plutus_data(), + tx_info.fee.to_plutus_data(), + tx_info.mint.to_plutus_data(), + tx_info.dcert.to_plutus_data(), + tx_info.wdrl.to_plutus_data(), + tx_info.valid_range.to_plutus_data(), + tx_info.signatories.to_plutus_data(), + tx_info.data.to_plutus_data(), + wrap_with_constr(0, tx_info.id.to_plutus_data()), + ], + ), + TxInfo::V2(tx_info) => wrap_multiple_with_constr( + 0, + vec![ + tx_info.inputs.to_plutus_data(), + tx_info.reference_inputs.to_plutus_data(), + tx_info.outputs.to_plutus_data(), + tx_info.fee.to_plutus_data(), + tx_info.mint.to_plutus_data(), + tx_info.dcert.to_plutus_data(), + tx_info.wdrl.to_plutus_data(), + tx_info.valid_range.to_plutus_data(), + tx_info.signatories.to_plutus_data(), + tx_info.redeemers.to_plutus_data(), + tx_info.data.to_plutus_data(), + wrap_with_constr(0, tx_info.id.to_plutus_data()), + ], + ), + } + } +} + +impl ToPlutusData for ScriptContext { + fn to_plutus_data(&self) -> PlutusData { + wrap_multiple_with_constr( + 0, + vec![self.tx_info.to_plutus_data(), self.purpose.to_plutus_data()], + ) + } +} + +impl ToPlutusData for bool { + fn to_plutus_data(&self) -> PlutusData { + match self { + false => empty_constr(0), + true => empty_constr(1), + } + } +}