From 739f38beac28b9ad1c8da01ecc3e2e029a75a052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20M=C3=BCndler?= Date: Thu, 8 Sep 2022 22:10:12 +0200 Subject: [PATCH 01/77] UPLC data parsing support by CBOR notation (#37) * Implement constant data parsing support New notation: (con data #0000) Where #0000 is the CBOR hex representation of a PlutusDatum * Add pretty printing support for data * Format --- crates/uplc/src/parser.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/uplc/src/parser.rs b/crates/uplc/src/parser.rs index 5caee1ca..1bb6d5d7 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; @@ -53,6 +54,7 @@ peg::parser! { / constant_string() / constant_unit() / constant_bool() + / constant_data() ) _* ")" { Term::Constant(con) } @@ -110,6 +112,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() } } From 6d6f671f4f0e300c8525fd5dfc489833e6d25ea8 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 8 Sep 2022 18:20:52 -0400 Subject: [PATCH 02/77] eval with inputs (#56) --- add_integers.uplc | 5 ++--- crates/cli/src/args.rs | 4 +++- crates/cli/src/main.rs | 20 +++++++++++++++----- crates/uplc/src/ast.rs | 15 +++++++++++++++ crates/uplc/src/parser.rs | 15 ++++++++++++++- 5 files changed, 49 insertions(+), 10 deletions(-) diff --git a/add_integers.uplc b/add_integers.uplc index fa621147..0ca9825e 100644 --- a/add_integers.uplc +++ b/add_integers.uplc @@ -1,5 +1,4 @@ (program 1.0.0 - [(force (force (builtin fstPair))) (con (pair integer bytestring) (22, #1122aabb))] -) - + (lam y (lam x [ [ (builtin addInteger) x ] y ])) +) \ No newline at end of file diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs index fcbe306c..032c36b1 100644 --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -39,9 +39,11 @@ pub enum UplcCommand { }, /// Evaluate an Untyped Plutus Core program Eval { - input: PathBuf, + script: PathBuf, #[clap(short, long)] flat: bool, + /// Arguments to pass to the uplc program + args: Vec, }, } diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 4bc26704..322fca9a 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -82,21 +82,28 @@ fn main() -> anyhow::Result<()> { fs::write(&out_name, pretty)?; } } - 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 { @@ -121,7 +128,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/src/ast.rs b/crates/uplc/src/ast.rs index 97d84333..fd4ef74d 100644 --- a/crates/uplc/src/ast.rs +++ b/crates/uplc/src/ast.rs @@ -39,6 +39,21 @@ 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, + } + } } impl<'a, T> Display for Program diff --git a/crates/uplc/src/parser.rs b/crates/uplc/src/parser.rs index 1bb6d5d7..80d0ef08 100644 --- a/crates/uplc/src/parser.rs +++ b/crates/uplc/src/parser.rs @@ -25,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 @@ -37,7 +50,7 @@ peg::parser! { (major as usize, minor as usize, patch as usize) } - rule term() -> Term + pub rule term() -> Term = constant() / builtin() / var() From 4615132b059d281a003dc4b31fa639f5cabeafee Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 8 Sep 2022 18:22:46 -0400 Subject: [PATCH 03/77] Release 0.0.12 aiken@0.0.12 uplc@0.0.12 Generated by cargo-workspaces --- Cargo.lock | 4 ++-- crates/cli/Cargo.toml | 4 ++-- crates/uplc/Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c2c18b5c..58dc6f16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "aiken" -version = "0.0.11" +version = "0.0.12" dependencies = [ "anyhow", "clap", @@ -641,7 +641,7 @@ checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" [[package]] name = "uplc" -version = "0.0.11" +version = "0.0.12" dependencies = [ "cryptoxide", "flat-rs", diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 01feb483..699249d7 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.12" edition = "2021" repository = "https://github.com/txpipe/aiken" homepage = "https://github.com/txpipe/aiken" @@ -13,4 +13,4 @@ authors = ["Lucas Rosa ", "Kasey White "] [dependencies] anyhow = "1.0.57" clap = { version = "3.1.14", features = ["derive"] } -uplc = { path = '../uplc', version = "0.0.11" } +uplc = { path = '../uplc', version = "0.0.12" } diff --git a/crates/uplc/Cargo.toml b/crates/uplc/Cargo.toml index 2923d38b..3795fb11 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.12" edition = "2021" repository = "https://github.com/txpipe/aiken/crates/uplc" homepage = "https://github.com/txpipe/aiken" From 055ca5c66b9484b08e43e61454651a74de140d85 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sat, 3 Sep 2022 13:35:10 -0400 Subject: [PATCH 04/77] feat: add a new subcommand for tx simulation --- Cargo.lock | 87 +++++++++++++++++++++++++++++++++++++++--- crates/cli/Cargo.toml | 3 ++ crates/cli/src/args.rs | 14 +++++++ crates/cli/src/main.rs | 21 +++++++++- 4 files changed, 117 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 58dc6f16..4ab8bc0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,9 @@ version = "0.0.12" dependencies = [ "anyhow", "clap", + "hex", + "pallas-primitives 0.13.2", + "pallas-traverse", "uplc", ] @@ -46,6 +49,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" +[[package]] +name = "bech32" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9ff0bbfd639f15c74af777d81383cf53efb7c93613f6cab67c6c11e05bbf8b" + [[package]] name = "bech32" version = "0.9.0" @@ -300,6 +309,20 @@ version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435" +[[package]] +name = "pallas-addresses" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712ead72b4f40d09f1c8660c47340489862754609cc2d587f24874ce55e51492" +dependencies = [ + "base58", + "bech32 0.8.1", + "hex", + "pallas-codec 0.13.2", + "pallas-crypto 0.13.2", + "thiserror", +] + [[package]] name = "pallas-codec" version = "0.12.0" @@ -309,6 +332,15 @@ dependencies = [ "minicbor 0.17.1", ] +[[package]] +name = "pallas-codec" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1758a68b9b2332a24403cf484ea2dd05dd14cceeb6f7b63cb8d11137865fb4cf" +dependencies = [ + "minicbor 0.17.1", +] + [[package]] name = "pallas-crypto" version = "0.12.0" @@ -317,7 +349,20 @@ checksum = "051226367cd851895c73e3115d378b58495ade1ee60c3a154c0d0c30554565fa" dependencies = [ "cryptoxide", "hex", - "pallas-codec", + "pallas-codec 0.12.0", + "rand_core", + "thiserror", +] + +[[package]] +name = "pallas-crypto" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17a13efbe2609916de03c063d5bbef840616b02c658f3db5222785a18b63362" +dependencies = [ + "cryptoxide", + "hex", + "pallas-codec 0.13.2", "rand_core", "thiserror", ] @@ -329,15 +374,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97a0fcc7d5a7120bc2b2e203ec5e7f8088107c500c0eb665569d0e77a910d3c0" dependencies = [ "base58", - "bech32", + "bech32 0.9.0", "hex", "log", - "pallas-codec", - "pallas-crypto", + "pallas-codec 0.12.0", + "pallas-crypto 0.12.0", "serde", "serde_json", ] +[[package]] +name = "pallas-primitives" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24f8d900bac04b711d2c4b21e906680224efd02d312a87ce25e688595b91d1fc" +dependencies = [ + "base58", + "bech32 0.9.0", + "hex", + "log", + "pallas-codec 0.13.2", + "pallas-crypto 0.13.2", + "serde", + "serde_json", +] + +[[package]] +name = "pallas-traverse" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0549db2f8bbaf22d13fc35791dd50fb13a50a1290a342a98929c8ce6c422ed86" +dependencies = [ + "hex", + "pallas-addresses", + "pallas-codec 0.13.2", + "pallas-crypto 0.13.2", + "pallas-primitives 0.13.2", + "thiserror", +] + [[package]] name = "peg" version = "0.8.0" @@ -647,8 +722,8 @@ dependencies = [ "flat-rs", "hex", "minicbor 0.18.0", - "pallas-codec", - "pallas-primitives", + "pallas-codec 0.12.0", + "pallas-primitives 0.12.0", "peg", "pretty", "proptest", diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 699249d7..8d3e11a7 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -13,4 +13,7 @@ authors = ["Lucas Rosa ", "Kasey White "] [dependencies] anyhow = "1.0.57" clap = { version = "3.1.14", features = ["derive"] } +hex = "0.4.3" +pallas-primitives = "0.13.2" +pallas-traverse = "0.13.2" uplc = { path = '../uplc', version = "0.0.12" } diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs index 032c36b1..15f0436f 100644 --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -7,11 +7,25 @@ use clap::{Parser, Subcommand}; #[clap(version, about, long_about = None)] #[clap(propagate_version = true)] pub enum Args { + /// 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 { + input: PathBuf, + #[clap(short, long)] + cbor: bool, + }, +} + /// Commands for working with Untyped Plutus Core #[derive(Subcommand)] pub enum UplcCommand { diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 322fca9a..d54f035a 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,5 +1,6 @@ use std::{fmt::Write as _, fs}; +use pallas_traverse::{Era, MultiEraTx}; use uplc::{ ast::{DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term}, machine::cost_model::ExBudget, @@ -8,13 +9,29 @@ use uplc::{ mod args; -use args::{Args, UplcCommand}; +use args::{Args, TxCommand, UplcCommand}; fn main() -> anyhow::Result<()> { let args = Args::default(); match args { - Args::Uplc(uplc) => match uplc { + Args::Tx(tx_cmd) => match tx_cmd { + TxCommand::Simulate { input, cbor } => { + let tx_bytes = if cbor { + fs::read(input)? + } else { + let cbor_hex = fs::read_to_string(input)?; + + hex::decode(cbor_hex)? + }; + + let tx = MultiEraTx::decode(Era::Alonzo, &tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Byron, &tx_bytes))?; + + println!("{:?}", tx); + } + }, + Args::Uplc(uplc_cmd) => match uplc_cmd { UplcCommand::Flat { input, print, out } => { let code = std::fs::read_to_string(&input)?; From c9072deb09d28c40be3457481b12297951eee35a Mon Sep 17 00:00:00 2001 From: rvcas Date: Sat, 3 Sep 2022 14:22:10 -0400 Subject: [PATCH 05/77] feat: print some info --- crates/cli/src/main.rs | 20 ++++++++++++++++++-- thing.tx | 1 + 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 thing.tx diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index d54f035a..46792eeb 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -22,13 +22,29 @@ fn main() -> anyhow::Result<()> { } else { let cbor_hex = fs::read_to_string(input)?; - hex::decode(cbor_hex)? + hex::decode(cbor_hex.trim())? }; let tx = MultiEraTx::decode(Era::Alonzo, &tx_bytes) .or_else(|_| MultiEraTx::decode(Era::Byron, &tx_bytes))?; - println!("{:?}", tx); + println!("Simulating: {}", tx.hash()); + + println!("\nPlutus Data:"); + + println!("{:#?}", tx.witnesses().plutus_data()); + + println!("\nRedeemer:"); + + println!("{:#?}", tx.witnesses().redeemer()); + + println!("\nPlutus V1 Script:"); + + println!("{:#?}", tx.witnesses().plutus_v1_script()); + + println!("\nPlutus V2 Script:"); + + println!("{:#?}", tx.witnesses().plutus_v2_script()); } }, Args::Uplc(uplc_cmd) => match uplc_cmd { diff --git a/thing.tx b/thing.tx new file mode 100644 index 00000000..90a46f74 --- /dev/null +++ b/thing.tx @@ -0,0 +1 @@ +84A8008282582071B02D2309057CA589878C02EF9F89CA2A911F4282BEF459A44B035DEEE292F0008258207F825475CA1C61EB4520EB486D66727BBBFBD6E9EED68FCC92DC8BA5147F8C20000182825839018385FBF4C951B5FD7E8F0457BDE479C99F910F5B7006E0A4B7F3E714EC061767F7E1B3069F7D4AD6CED921F4FF21CF578C368B70E9993BCE821A00150BD0A1581CDEEBF749DD081B3AEA1C59EF2A1BE1038D61A0C7398DE15C310244BEA14C54455354544F4B454E31363101825839018385FBF4C951B5FD7E8F0457BDE479C99F910F5B7006E0A4B7F3E714EC061767F7E1B3069F7D4AD6CED921F4FF21CF578C368B70E9993BCE1A0042F4FB021A0005ED3C031A0431210D081A043112FD0B582000000000000000000000000000000000000000000000000000000000000000000D81825820FA794F486D1AA007961381BF05E2E435E71D4C361707ABA64854B59D50D3C498010E81581C8385FBF4C951B5FD7E8F0457BDE479C99F910F5B7006E0A4B7F3E714A40082825820000000008385FBF4C951B5FD7E8F0457BDE479C99F910F5B7006E0A4B7F3E714584000F899D39B17FD5D66C192C4B50D343E42F7235B30504C8AE7619F93C828DC6DCE4568DD69177C551828492D777A6727FD66C2FBCCBDA8C2AEED92032C99790A82582000000000FA130B1813EC48D7DAAD638CC95080A8D7FE68CBBAF15D87F57B73C6584000F899D39B17FD5D66C192C4B50D343E42F7235B30504C8AE7619F93C828DC6DCE4568DD69177C551828492D777A6727FD66C2FBCCBDA8C2AEED92032C99790A0381590FC1590FBE0100003233223232323322333222323233322232333222323332223322323322323332223232332233223232332232323332223322332233223322323232323232323232323232332232323232323232323322323232332232333322223232323232322232232325335305C33223530310012235303500222222222322235304400C23232325335306F333222533530723300300200110731074506E32353047001220023253353505A00113507649010350543800221002302E5002004107115335304A01313301B49101350033355029302F1200123535505E00122323355030335502D30331200123535506200122353550640012253353506F335503533550250490040062153353507033550363335550323039120012235355069002232233225335350770022130020011507800425335350763335502C0500040072153353080013304D0010031335503D5079300400215335308001330630010031335503D507933335502E0510053304900100300215078150773200135508601225335350680011506A2213535506E002225335308201330530020071003133506D33550710020013006003350720010022133026491023130003322333573466E200080041F41F8CC8CD54C0EC48004D40C00048004CD40C40952000335530321200123535506900122001001004133573892010231310007A133573892010231320007900233233553023120013503500135034001335038223355302C120012353550630012233550660023355302F12001235355066001223355069002333535502B0012330264800000488CC09C0080048CC09800520000013355302C1200123535506300122335506600233353550280012335530301200123535506700122335506A00235502F0010012233355502804A0020012335530301200123535506700122335506A00235502D001001333555023045002001505E33233553023120012253353506C3003002213350610010021001505F2353053001222533530763332001504100600313506F0021506E011320013333555028302F1200122353055002225335307333044002500B10031333355502C303312001235305A00122253353506E333550245042003001213333550265043004335530301200123535506700122335506A002333535502C0012001223535506B002223535506D0032233550703302C00400233553039120012353550700012233550730023335355035001200122330310020012001333555030052003001200133355502704900300100213333550255042003002001003001505B500113301B49101340033355029302F1200123530540012233043500A00250011533535058335530401200123320015051320013530460012235305100122253353506B0012321300100D3200135507F2253353506100113507D491022D310022135355067002225335307B3304C00200710011300600313507A49101370050011350744901013600221335530421200123320015053320013530480012235305300122253353506D0012321300100F32001355081012253353506300113507F491022D310022135355069002225335307D3304E00200710011300600313507C4910137005003133355301D12001225335306F335306A303E302D35304600222001207125335307033041001300401010721350764901013300133505A0020011001505900D3200135507622533535058001135074491022D31002213530470022253353072333200150710020071353063303000122335306F00223507B491022D310020011300600315335350520011306D4988854CD4D41500044008884C1C5263333573466E1D40112002203A23333573466E1D40152000203A23263530663357380B80CE0CA0C80C66666AE68CDC39AAB9D5002480008CC0C4C8C8C8C8C8C8C8C8C8C8C8CCCD5CD19B8735573AA01490001199999999981F99A828919191999AB9A3370E6AAE7540092000233045304B35742A00460986AE84D5D1280111931A983A99AB9C06B076074073135573CA00226EA8004D5D0A80519A8288241ABA1500935742A0106AE85401CD5D0A8031ABA1500535742A00866A0A2EB8D5D0A80199A82899AA82B3AE200135742A0046AE84D5D1280111931A983899AB9C06707207006F135744A00226AE8940044D5D1280089ABA25001135744A00226AE8940044D5D1280089ABA25001135573CA00226EA8004D5D0A80119191999AB9A3370E6AAE75400520022303A303E357426AAE7940088C98D4C1A0CD5CE02F03483383309BAA001357426AE8940088C98D4C194CD5CE02D833032031883289931A983219AB9C49010350543500065063135573CA00226EA80044D55CE9BAA001223370000400244A66A60AC00220B0266AE7000815C4488C88C008004C8004D5417C894CD4D41040045413C884D4D5411C008894CD4C16CCC02000801C4D41500044C01800C448888C8CD54C03C480048D4D5411800488CD54124008CCD4D5402C0048004880048004CCD554018014008004C8CD40054109410C488CC008CD5411C014010004444888CCD54C0104800540FCCD54C030480048D4D5410C00488CD54118008D5402C004CCD54C0104800488D4D54110008894CD4C160CCD54C06048004D4034CD403C894CD4C168008417040041648D4D5411C00488CC028008014018400C4CD410C01000D4100004CD54C030480048D4D5410C00488C8CD5411C00CC004014C8004D54184894CD4D410C0044D5402C00C884D4D54124008894CD4C174CC0300080204CD5404001C0044C01800C008C8004D5416888448894CD4D40FC0044008884CC014008CCD54C01C480040140100044484888C00C01044884888CC0080140104484888C00401044800448CD404888CCD4D401000C88008008004D4D40080048800448848CC00400C00848004C8004D541488844894CD4D40D8004540E0884CD40E4C010008CD54C018480040100044448888CCCD54011403C00C0040084488CD54008C8CD403C88CCD4D401800C88008008004D4D401000488004CD401005012800448848CC00400C008480044488C0080048D4C08C00488800CC8004D5412C88C8C94CD4C114CC0A14009200213300C300433530081200150010033004335300D12001500100310031332233706004002A002900209999AA9801890009919A80511199A8040018008011A802800A8039119B800014800800520003200135504A221122253353502F00113500600322133350090053004002333553007120010050040011235350050012200112353500400122002320013550472212253353041333573466E2400920000430421502D153353502B0011502D22133502E002335300612001337020089001000899A801111180198010009000891091980080180109000990009AA821911299A9A81300108009109A980A801111A982180111299A9A8160038A99A9A8160038804110B1109A980D801111A982480111299A9824199AB9A33720010004094092266A0660186601E01601A2A66A6090666AE68CDC8804001024825099A819803198078070028A99A982419809003800899A81980619807805806899A81980319807807002990009AA82111091299A9A8130008A814110A99A981F19804002240002006266A600C240026600E00890010009119B8100200122333573466E240080040E80E4488CC00CCC01CC018008C018004CC894CD4D40C0008854CD4D40C400884CD4C0B80088CD4C0BC0088CC034008004888100888CD4C0C401081008894CD4C104CCD5CD19B87006003043042153353041333573466E1C01400810C1084CC0380100044108410840EC54CD4D40C0004840EC40ECC014008C014004894CD4C0D8008400440DC88CCD5CD19B8700200103703623530240012200123530230012200222335302D0022335302E00223300500200120352335302E002203523300500200122333573466E3C0080040CC0C8C8004D540E08844894CD4D407000454078884CD407CC010008CD54C018480040100048848CC00400C0088004888888888848CCCCCCCCCC00402C02802402001C01801401000C00880048848CC00400C0088004848C004008800448800848800480048C8C8CCCD5CD19B8735573AA004900011981519191999AB9A3370E6AAE75400520002375C6AE84D55CF280111931A981899AB9C02703203002F137540026AE854008DD69ABA135744A004464C6A605C66AE700900BC0B40B04D55CF280089BAA00123232323333573466E1CD55CEA801A4000466600E602C6AE85400CCCD5403DD719AA807BAE75A6AE854008CD4071D71ABA135744A004464C6A605C66AE700900BC0B40B04D5D1280089AAB9E5001137540024442466600200800600440022464646666AE68CDC39AAB9D5002480008CC88CC024008004DD71ABA1500233500A232323333573466E1CD55CEA80124000466446601E004002602C6AE854008CCD5403DD719AA809919299A981419805A800A40022A00226A05C921022D33001375A00266AA01EEB88C94CD4C0A0CC02D400520001500113502E491022D32001375A0026AE84D5D1280111931A981719AB9C02402F02D02C135573CA00226EA8004D5D09ABA25002232635302A33573804005605205026AAE7940044DD500091199AB9A33712004002040042442466002006004400244246600200600440022464460046EB0004C8004D5408C88CCCD55CF80092804119A80398021ABA1002300335744004048224464460046EAC004C8004D5408C88C8CCCD55CF80112804919A80419AA80618031AAB9D5002300535573CA00460086AE8800C0944D5D0800889100109109119800802001890008891119191999AB9A3370E6AAE754009200023355008300635742A004600A6AE84D5D1280111931A981099AB9C01702202001F135573CA00226EA8004448848CC00400C0084480048C8C8CCCD5CD19B8735573AA004900011980318071ABA1500233500A2323232323333573466E1D40052002233300E375A6AE854010DD69ABA15003375A6AE84D5D1280191999AB9A3370EA004900011808180A9ABA135573CA00C464C6A604666AE700640900880840804D55CEA80189ABA25001135573CA00226EA8004D5D09ABA25002232635301C33573802403A03603426AAE7940044DD5000910919800801801100090911801001911091199800802802001900089119191999AB9A3370EA002900011A80418029ABA135573CA00646666AE68CDC3A801240044A010464C6A603066AE7003806405C0580544D55CEA80089BAA001121223002003112200112001232323333573466E1D4005200223006375C6AE84D55CF280191999AB9A3370EA0049000118041BAE357426AAE7940108C98D4C04CCD5CE00480A00900880809AAB9D50011375400242446004006424460020064002921035054310012253353003333573466E3CD4C01800888008D4C018004880080140104CCD5CD19B8735300600222001353006001220010050041004122002122001200122123300100300220012350024901013100123263530033357380020080049309000900088919180080091198019801001000810481D87982581C8385FBF4C951B5FD7E8F0457BDE479C99F910F5B7006E0A4B7F3E71482D87982D87982D87981581C70E60F3B5EA7153E0ACC7A803E4401D44B8ED1BAE1C7BAAAD1A62A72D87981D87981D87981581C1E78AAE7C90CC36D624F7B3BB6D86B52696DC84E490F343EBA89005FA140D8798200A1401B00000003AD610AC0D87982D87982D87981581C8385FBF4C951B5FD7E8F0457BDE479C99F910F5B7006E0A4B7F3E714D87981D87981D87981581CEC061767F7E1B3069F7D4AD6CED921F4FF21CF578C368B70E9993BCEA140D8798200A1401B000000B42F930EC00581840000D87980821A00D59F7F1B00000002540BE3FFF5F6 \ No newline at end of file From cf27c0c7553e2aa4b6d1121a4354334040d73855 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sat, 3 Sep 2022 17:10:51 -0400 Subject: [PATCH 06/77] feat: apply redeemers and datum to script --- Cargo.lock | 60 ++++++++--------------------------------- crates/cli/src/main.rs | 29 ++++++++++++++------ crates/uplc/Cargo.toml | 4 +-- crates/uplc/src/ast.rs | 12 +++++++++ crates/uplc/src/flat.rs | 17 +++++++++--- 5 files changed, 60 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4ab8bc0c..b3ec7045 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,7 +9,7 @@ dependencies = [ "anyhow", "clap", "hex", - "pallas-primitives 0.13.2", + "pallas-primitives", "pallas-traverse", "uplc", ] @@ -318,20 +318,11 @@ dependencies = [ "base58", "bech32 0.8.1", "hex", - "pallas-codec 0.13.2", - "pallas-crypto 0.13.2", + "pallas-codec", + "pallas-crypto", "thiserror", ] -[[package]] -name = "pallas-codec" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dce0ea17341c1a0e43e2bb4a637740198dcb09826879ce3ac5ae1c6f4398a5d" -dependencies = [ - "minicbor 0.17.1", -] - [[package]] name = "pallas-codec" version = "0.13.2" @@ -341,19 +332,6 @@ dependencies = [ "minicbor 0.17.1", ] -[[package]] -name = "pallas-crypto" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "051226367cd851895c73e3115d378b58495ade1ee60c3a154c0d0c30554565fa" -dependencies = [ - "cryptoxide", - "hex", - "pallas-codec 0.12.0", - "rand_core", - "thiserror", -] - [[package]] name = "pallas-crypto" version = "0.13.2" @@ -362,27 +340,11 @@ checksum = "e17a13efbe2609916de03c063d5bbef840616b02c658f3db5222785a18b63362" dependencies = [ "cryptoxide", "hex", - "pallas-codec 0.13.2", + "pallas-codec", "rand_core", "thiserror", ] -[[package]] -name = "pallas-primitives" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a0fcc7d5a7120bc2b2e203ec5e7f8088107c500c0eb665569d0e77a910d3c0" -dependencies = [ - "base58", - "bech32 0.9.0", - "hex", - "log", - "pallas-codec 0.12.0", - "pallas-crypto 0.12.0", - "serde", - "serde_json", -] - [[package]] name = "pallas-primitives" version = "0.13.2" @@ -393,8 +355,8 @@ dependencies = [ "bech32 0.9.0", "hex", "log", - "pallas-codec 0.13.2", - "pallas-crypto 0.13.2", + "pallas-codec", + "pallas-crypto", "serde", "serde_json", ] @@ -407,9 +369,9 @@ checksum = "0549db2f8bbaf22d13fc35791dd50fb13a50a1290a342a98929c8ce6c422ed86" dependencies = [ "hex", "pallas-addresses", - "pallas-codec 0.13.2", - "pallas-crypto 0.13.2", - "pallas-primitives 0.13.2", + "pallas-codec", + "pallas-crypto", + "pallas-primitives", "thiserror", ] @@ -722,8 +684,8 @@ dependencies = [ "flat-rs", "hex", "minicbor 0.18.0", - "pallas-codec 0.12.0", - "pallas-primitives 0.12.0", + "pallas-codec", + "pallas-primitives", "peg", "pretty", "proptest", diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 46792eeb..f53d53e5 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -2,7 +2,7 @@ use std::{fmt::Write as _, fs}; use pallas_traverse::{Era, MultiEraTx}; use uplc::{ - ast::{DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term}, + ast::{Constant, DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term}, machine::cost_model::ExBudget, parser, }; @@ -30,17 +30,30 @@ fn main() -> anyhow::Result<()> { println!("Simulating: {}", tx.hash()); - println!("\nPlutus Data:"); + let witnesses = tx.witnesses(); - println!("{:#?}", tx.witnesses().plutus_data()); + if let Some(((datums, redeemers), scripts)) = witnesses + .plutus_data() + .zip(witnesses.redeemer()) + .zip(witnesses.plutus_v1_script()) + { + for ((datum, redeemer), script) in + datums.iter().zip(redeemers.iter()).zip(scripts.iter()) + { + let program: Program = { + let mut buffer = Vec::new(); - println!("\nRedeemer:"); + let prog = + Program::::from_cbor(&script.0, &mut buffer)?; - println!("{:#?}", tx.witnesses().redeemer()); + prog.into() + }; - println!("\nPlutus V1 Script:"); - - println!("{:#?}", tx.witnesses().plutus_v1_script()); + program + .apply_data(datum.clone()) + .apply_data(redeemer.data.clone()); + } + } println!("\nPlutus V2 Script:"); diff --git a/crates/uplc/Cargo.toml b/crates/uplc/Cargo.toml index 3795fb11..336721c7 100644 --- a/crates/uplc/Cargo.toml +++ b/crates/uplc/Cargo.toml @@ -17,8 +17,8 @@ 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-codec = "0.13.2" +pallas-primitives = "0.13.2" peg = "0.8.0" pretty = "0.11.3" thiserror = "1.0.31" diff --git a/crates/uplc/src/ast.rs b/crates/uplc/src/ast.rs index fd4ef74d..55a38ae6 100644 --- a/crates/uplc/src/ast.rs +++ b/crates/uplc/src/ast.rs @@ -54,6 +54,18 @@ where 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 diff --git a/crates/uplc/src/flat.rs b/crates/uplc/src/flat.rs index 161751f8..efb5b7d1 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 = 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 = 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 From d5f8b749ae096f03907125588138eefa4a732bca Mon Sep 17 00:00:00 2001 From: Kasey White Date: Sat, 3 Sep 2022 18:48:51 -0400 Subject: [PATCH 07/77] add txininfo plutus data to main --- Cargo.lock | 26 ++++++-- crates/cli/Cargo.toml | 4 ++ crates/cli/src/args.rs | 22 +++++- crates/cli/src/main.rs | 148 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 192 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b3ec7045..d453dad5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,8 +9,12 @@ dependencies = [ "anyhow", "clap", "hex", + "pallas-addresses", + "pallas-codec", "pallas-primitives", "pallas-traverse", + "serde", + "serde_json", "uplc", ] @@ -577,15 +581,29 @@ checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "serde" -version = "1.0.142" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e590c437916fb6b221e1d00df6e3294f3fccd70ca7e92541c475d6ed6ef5fee2" +checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.144" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "serde_json" -version = "1.0.83" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" dependencies = [ "itoa", "ryu", diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 8d3e11a7..469ce34d 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -15,5 +15,9 @@ anyhow = "1.0.57" clap = { version = "3.1.14", features = ["derive"] } hex = "0.4.3" pallas-primitives = "0.13.2" +pallas-codec = "0.13.2" pallas-traverse = "0.13.2" +serde = { version = "1.0.144", features = ["derive"] } +serde_json = "1.0.85" uplc = { path = '../uplc', version = "0.0.12" } +pallas-addresses = "0.13.2" diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs index 15f0436f..00af0c52 100644 --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -1,6 +1,7 @@ -use std::path::PathBuf; +use std::{collections::HashMap, path::PathBuf}; use clap::{Parser, Subcommand}; +use serde::{de, Deserialize}; /// Cardano smart contract toolchain #[derive(Parser)] @@ -23,9 +24,28 @@ pub enum TxCommand { input: PathBuf, #[clap(short, long)] cbor: bool, + resolved_inputs: PathBuf, }, } +#[derive(Deserialize)] +pub struct ResolvedInput { + pub input: Input, + pub ouput: Output, +} + +#[derive(Deserialize)] +pub struct Input { + pub tx_hash: String, + pub index: u64, +} + +#[derive(Deserialize)] +pub struct Output { + pub address: String, + pub value: (u64, HashMap>), +} + /// Commands for working with Untyped Plutus Core #[derive(Subcommand)] pub enum UplcCommand { diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index f53d53e5..ce65e8b5 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,22 +1,35 @@ -use std::{fmt::Write as _, fs}; +use std::{ + fmt::Write as _, + fs::{self, File}, + io::BufReader, collections::HashMap, thread::LocalKey, +}; +use pallas_addresses::Address; +use pallas_codec::{minicbor::bytes::ByteVec, utils::{MaybeIndefArray, KeyValuePairs}}; +use pallas_primitives::babbage::{BigInt, Constr}; use pallas_traverse::{Era, MultiEraTx}; use uplc::{ ast::{Constant, DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term}, machine::cost_model::ExBudget, - parser, + parser, PlutusData, }; mod args; use args::{Args, TxCommand, UplcCommand}; +use crate::args::ResolvedInput; + fn main() -> anyhow::Result<()> { let args = Args::default(); match args { Args::Tx(tx_cmd) => match tx_cmd { - TxCommand::Simulate { input, cbor } => { + TxCommand::Simulate { + input, + cbor, + resolved_inputs, + } => { let tx_bytes = if cbor { fs::read(input)? } else { @@ -52,6 +65,135 @@ fn main() -> anyhow::Result<()> { program .apply_data(datum.clone()) .apply_data(redeemer.data.clone()); + + let file = File::open(&resolved_inputs)?; + let reader = BufReader::new(file); + let resolved_inputs: Vec = serde_json::from_reader(reader)?; + let tx_in_info: Vec = resolved_inputs + .iter() + .map(|resolved_input| { + let tx_out_ref = PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + PlutusData::BoundedBytes( + hex::decode(resolved_input.input.tx_hash.clone()) + .unwrap() + .into(), + ), + PlutusData::BigInt(BigInt::Int( + resolved_input.input.index.into(), + )), + ]), + }); + + let address = + Address::from_bech32(&resolved_input.ouput.address).unwrap(); + + let payment_tag = match address.typeid() % 2 { + 0 => 0, + 1 => 1, + _ => unreachable!(), + }; + let stake_tag = match address.typeid() { + 0 | 1 => Some(0), + 2 | 3 => Some(1), + _ => None, + }; + + let (payment_part, stake_part) = match address { + Address::Shelley(s) => { + (s.payment().to_vec(), s.delegation().to_vec()) + } + _ => unreachable!(), + }; + + let lovelace = resolved_input.ouput.value.0; + + let mut assets = resolved_input.ouput.value.1.clone(); + + assets.insert("".to_string(), vec![("".to_string(), lovelace)].into_iter().collect()); + + let tx_out = PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + // txOutAddress + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + // addressCredential + PlutusData::Constr(Constr { + tag: payment_tag, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + PlutusData::BoundedBytes( + payment_part.into(), + ), + ]), + }), + // addressStakingCredential + PlutusData::Constr(Constr { + tag: if stake_tag.is_some() { 0 } else { 1 }, + any_constructor: None, + fields: MaybeIndefArray::Indef( + if stake_tag.is_some() { + vec![ + // StakingCredential + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + // StakingHash + PlutusData::Constr(Constr { + tag: stake_tag.unwrap(), + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + PlutusData::BoundedBytes( + stake_part.into(), + ), + ]), + }), + ]), + }), + ] + + } else { + vec![] + }, + ), + }), + ]), + }), + + // txOutValue + PlutusData::Map(KeyValuePairs::Def( + assets.iter().map(|val| { + let currency_symbol = PlutusData::BoundedBytes(hex::decode(val.0).unwrap().into()); + let token_map = PlutusData::Map(KeyValuePairs::Def( + val.1.iter().map(|token| { + ( PlutusData::BoundedBytes(token.0.as_bytes().to_vec().into()), PlutusData::BigInt(BigInt::Int((*token.1).into()))) + }).collect() + + )); + (currency_symbol, token_map) + }).collect() + ) ), + + + ]), + }); + PlutusData::Constr(Constr{ + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + tx_out_ref, + tx_out + ]) + }) + }) + .collect(); } } From 564cd41626b978d219fe059e6984ce3af8251b3e Mon Sep 17 00:00:00 2001 From: rvcas Date: Sat, 3 Sep 2022 19:08:01 -0400 Subject: [PATCH 08/77] chore: clean up tx in info --- crates/cli/src/args.rs | 2 +- crates/cli/src/main.rs | 135 ++-------------------------------------- crates/cli/src/utils.rs | 127 +++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 131 deletions(-) create mode 100644 crates/cli/src/utils.rs diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs index 00af0c52..3f5c2ec1 100644 --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, path::PathBuf}; use clap::{Parser, Subcommand}; -use serde::{de, Deserialize}; +use serde::Deserialize; /// Cardano smart contract toolchain #[derive(Parser)] diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index ce65e8b5..5174f349 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,20 +1,18 @@ use std::{ fmt::Write as _, fs::{self, File}, - io::BufReader, collections::HashMap, thread::LocalKey, + io::BufReader, }; -use pallas_addresses::Address; -use pallas_codec::{minicbor::bytes::ByteVec, utils::{MaybeIndefArray, KeyValuePairs}}; -use pallas_primitives::babbage::{BigInt, Constr}; use pallas_traverse::{Era, MultiEraTx}; use uplc::{ - ast::{Constant, DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term}, + ast::{DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term}, machine::cost_model::ExBudget, - parser, PlutusData, + parser, }; mod args; +mod utils; use args::{Args, TxCommand, UplcCommand}; @@ -69,131 +67,8 @@ fn main() -> anyhow::Result<()> { let file = File::open(&resolved_inputs)?; let reader = BufReader::new(file); let resolved_inputs: Vec = serde_json::from_reader(reader)?; - let tx_in_info: Vec = resolved_inputs - .iter() - .map(|resolved_input| { - let tx_out_ref = PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - PlutusData::BoundedBytes( - hex::decode(resolved_input.input.tx_hash.clone()) - .unwrap() - .into(), - ), - PlutusData::BigInt(BigInt::Int( - resolved_input.input.index.into(), - )), - ]), - }); - let address = - Address::from_bech32(&resolved_input.ouput.address).unwrap(); - - let payment_tag = match address.typeid() % 2 { - 0 => 0, - 1 => 1, - _ => unreachable!(), - }; - let stake_tag = match address.typeid() { - 0 | 1 => Some(0), - 2 | 3 => Some(1), - _ => None, - }; - - let (payment_part, stake_part) = match address { - Address::Shelley(s) => { - (s.payment().to_vec(), s.delegation().to_vec()) - } - _ => unreachable!(), - }; - - let lovelace = resolved_input.ouput.value.0; - - let mut assets = resolved_input.ouput.value.1.clone(); - - assets.insert("".to_string(), vec![("".to_string(), lovelace)].into_iter().collect()); - - let tx_out = PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - // txOutAddress - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - // addressCredential - PlutusData::Constr(Constr { - tag: payment_tag, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - PlutusData::BoundedBytes( - payment_part.into(), - ), - ]), - }), - // addressStakingCredential - PlutusData::Constr(Constr { - tag: if stake_tag.is_some() { 0 } else { 1 }, - any_constructor: None, - fields: MaybeIndefArray::Indef( - if stake_tag.is_some() { - vec![ - // StakingCredential - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - // StakingHash - PlutusData::Constr(Constr { - tag: stake_tag.unwrap(), - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - PlutusData::BoundedBytes( - stake_part.into(), - ), - ]), - }), - ]), - }), - ] - - } else { - vec![] - }, - ), - }), - ]), - }), - - // txOutValue - PlutusData::Map(KeyValuePairs::Def( - assets.iter().map(|val| { - let currency_symbol = PlutusData::BoundedBytes(hex::decode(val.0).unwrap().into()); - let token_map = PlutusData::Map(KeyValuePairs::Def( - val.1.iter().map(|token| { - ( PlutusData::BoundedBytes(token.0.as_bytes().to_vec().into()), PlutusData::BigInt(BigInt::Int((*token.1).into()))) - }).collect() - - )); - (currency_symbol, token_map) - }).collect() - ) ), - - - ]), - }); - PlutusData::Constr(Constr{ - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - tx_out_ref, - tx_out - ]) - }) - }) - .collect(); + let tx_in_info = utils::get_tx_in_info(&resolved_inputs)?; } } diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs new file mode 100644 index 00000000..959016ba --- /dev/null +++ b/crates/cli/src/utils.rs @@ -0,0 +1,127 @@ +use pallas_addresses::Address; +use pallas_codec::utils::{KeyValuePairs, MaybeIndefArray}; +use pallas_primitives::babbage::{BigInt, Constr}; +use uplc::PlutusData; + +use crate::args::ResolvedInput; + +pub fn get_tx_in_info(resolved_inputs: &[ResolvedInput]) -> anyhow::Result> { + let mut tx_in_info = Vec::new(); + + for resolved_input in resolved_inputs { + let tx_out_ref = PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + PlutusData::BoundedBytes(hex::decode(resolved_input.input.tx_hash.clone())?.into()), + PlutusData::BigInt(BigInt::Int(resolved_input.input.index.into())), + ]), + }); + + let address = Address::from_bech32(&resolved_input.ouput.address)?; + + let payment_tag = match address.typeid() % 2 { + 0 => 0, + 1 => 1, + _ => unreachable!(), + }; + let stake_tag = match address.typeid() { + 0 | 1 => Some(0), + 2 | 3 => Some(1), + _ => None, + }; + + let (payment_part, stake_part) = match address { + Address::Shelley(s) => (s.payment().to_vec(), s.delegation().to_vec()), + _ => unreachable!(), + }; + + let lovelace = resolved_input.ouput.value.0; + + let mut assets = resolved_input.ouput.value.1.clone(); + + assets.insert( + "".to_string(), + vec![("".to_string(), lovelace)].into_iter().collect(), + ); + + let tx_out = PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + // txOutAddress + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + // addressCredential + PlutusData::Constr(Constr { + tag: payment_tag, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![PlutusData::BoundedBytes( + payment_part.into(), + )]), + }), + // addressStakingCredential + PlutusData::Constr(Constr { + tag: if stake_tag.is_some() { 0 } else { 1 }, + any_constructor: None, + fields: MaybeIndefArray::Indef(match stake_tag { + Some(stake_tag) => vec![ + // StakingCredential + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + // StakingHash + PlutusData::Constr(Constr { + tag: stake_tag, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + PlutusData::BoundedBytes(stake_part.into()), + ]), + }), + ]), + }), + ], + None => vec![], + }), + }), + ]), + }), + // txOutValue + PlutusData::Map(KeyValuePairs::Def( + assets + .iter() + .map(|val| { + let currency_symbol = + PlutusData::BoundedBytes(hex::decode(val.0).unwrap().into()); + let token_map = PlutusData::Map(KeyValuePairs::Def( + val.1 + .iter() + .map(|token| { + ( + PlutusData::BoundedBytes( + token.0.as_bytes().to_vec().into(), + ), + PlutusData::BigInt(BigInt::Int((*token.1).into())), + ) + }) + .collect(), + )); + (currency_symbol, token_map) + }) + .collect(), + )), + ]), + }); + + tx_in_info.push(PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![tx_out_ref, tx_out]), + })); + } + + Ok(tx_in_info) +} From d8cdeaf925fd8e3a3801e39816465bb59d4fcd5b Mon Sep 17 00:00:00 2001 From: Harper Date: Sun, 4 Sep 2022 15:16:11 +0100 Subject: [PATCH 09/77] ouput (#51) --- crates/cli/src/args.rs | 11 ++++++++++- crates/cli/src/utils.rs | 6 +++--- thing_resolved_txins.tx | 4 ++++ 3 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 thing_resolved_txins.tx diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs index 3f5c2ec1..5de21310 100644 --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -31,7 +31,7 @@ pub enum TxCommand { #[derive(Deserialize)] pub struct ResolvedInput { pub input: Input, - pub ouput: Output, + pub output: Output, } #[derive(Deserialize)] @@ -44,6 +44,15 @@ pub struct Input { pub struct Output { pub address: String, pub value: (u64, HashMap>), + pub datum: Option, + pub script: Option, +} + +#[derive(Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum OutputDatum { + DatumHash(String), + Datum(String), } /// Commands for working with Untyped Plutus Core diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index 959016ba..6abc5085 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -18,7 +18,7 @@ pub fn get_tx_in_info(resolved_inputs: &[ResolvedInput]) -> anyhow::Result 0, @@ -36,9 +36,9 @@ pub fn get_tx_in_info(resolved_inputs: &[ResolvedInput]) -> anyhow::Result unreachable!(), }; - let lovelace = resolved_input.ouput.value.0; + let lovelace = resolved_input.output.value.0; - let mut assets = resolved_input.ouput.value.1.clone(); + let mut assets = resolved_input.output.value.1.clone(); assets.insert( "".to_string(), diff --git a/thing_resolved_txins.tx b/thing_resolved_txins.tx new file mode 100644 index 00000000..38c497d9 --- /dev/null +++ b/thing_resolved_txins.tx @@ -0,0 +1,4 @@ +[ + {"input":{"tx_hash":"71b02d2309057ca589878c02ef9f89ca2a911f4282bef459a44b035deee292f0","index":0},"output":{"address":"addr1zxj47sy4qxlktqzmkrw8dahe46gtv8seakrshsqz26qnvzypw288a4x0xf8pxgcntelxmyclq83s0ykeehchz2wtspksr3q9nx","value":[1724100,{"deebf749dd081b3aea1c59ef2a1be1038d61a0c7398de15c310244be": {"54455354544f4b454e313631": 1}}],"datum":{"datum_hash": "d908988cd6197fb46e9711a8e84eda57e1b134b0e0fe11ea7e9cdc6e3d484189"}}}, + {"input":{"tx_hash":"ba68b9076c6c34666d5a554d7cd0fdffacc1c38d86cd37ff5c565d77a1ce2fd0","index":0},"output":{"address":"addr1qxpct7l5e9gmtlt73uz9000y08yelyg0tdcqdc9ykle7w98vqctk0alpkvrf7l226m8djg05lusu74uvx69hp6ve808qxc39wj","value":[5000000,{}]}} +] \ No newline at end of file From da739951245b8f3683ec40f25d3dee0184845e04 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sun, 4 Sep 2022 13:53:38 -0400 Subject: [PATCH 10/77] fix: resolved inputs should be a flag --- crates/cli/src/args.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs index 5de21310..e7cfc47c 100644 --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -24,6 +24,7 @@ pub enum TxCommand { input: PathBuf, #[clap(short, long)] cbor: bool, + #[clap(short, long)] resolved_inputs: PathBuf, }, } From bc983d694aeeaded9dc890d06dd51ca439db21b6 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sun, 4 Sep 2022 17:32:38 -0400 Subject: [PATCH 11/77] feat: some cbor hex flags for flat and unflat --- crates/cli/src/args.rs | 4 +++ crates/cli/src/main.rs | 77 ++++++++++++++++++++++++++++++------------ 2 files changed, 60 insertions(+), 21 deletions(-) diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs index e7cfc47c..d5645ede 100644 --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -66,6 +66,8 @@ pub enum UplcCommand { print: bool, #[clap(short, long)] out: Option, + #[clap(short, long)] + cbor_hex: bool, }, /// Decode flat bytes to textual Untyped Plutus Core Unflat { @@ -74,6 +76,8 @@ pub enum UplcCommand { print: bool, #[clap(short, long)] out: Option, + #[clap(short, long)] + cbor_hex: bool, }, /// Format an Untyped Plutus Core program Fmt { diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 5174f349..d0077832 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -78,37 +78,58 @@ fn main() -> anyhow::Result<()> { } }, Args::Uplc(uplc_cmd) => match uplc_cmd { - UplcCommand::Flat { input, print, out } => { + 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)?; + } } } UplcCommand::Fmt { input, print } => { @@ -124,10 +145,24 @@ fn main() -> anyhow::Result<()> { fs::write(&input, pretty)?; } } - UplcCommand::Unflat { input, print, out } => { - let bytes = std::fs::read(&input)?; + UplcCommand::Unflat { + input, + print, + out, + cbor_hex, + } => { + let program = if cbor_hex { + let cbor = std::fs::read_to_string(&input)?; - let program = Program::::from_flat(&bytes)?; + let mut cbor_buffer = Vec::new(); + let mut flat_buffer = Vec::new(); + + 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()?; From 60d7c52c268208614b95926088780241fc6f8086 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Fri, 9 Sep 2022 18:50:07 +0200 Subject: [PATCH 12/77] added some structs; added ToPlutusData --- Cargo.lock | 1 + crates/cli/Cargo.toml | 1 + crates/cli/src/utils.rs | 221 ++++++++++++++++++++++++++++------------ 3 files changed, 158 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d453dad5..923a49aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,7 @@ dependencies = [ "hex", "pallas-addresses", "pallas-codec", + "pallas-crypto", "pallas-primitives", "pallas-traverse", "serde", diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 469ce34d..f8439e17 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -17,6 +17,7 @@ hex = "0.4.3" pallas-primitives = "0.13.2" pallas-codec = "0.13.2" pallas-traverse = "0.13.2" +pallas-crypto = "0.13.2" serde = { version = "1.0.144", features = ["derive"] } serde_json = "1.0.85" uplc = { path = '../uplc', version = "0.0.12" } diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index 6abc5085..a532e0e2 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -1,6 +1,12 @@ -use pallas_addresses::Address; +use pallas_addresses::{Address, PaymentKeyHash}; use pallas_codec::utils::{KeyValuePairs, MaybeIndefArray}; -use pallas_primitives::babbage::{BigInt, Constr}; +use pallas_crypto::hash::Hash; +use pallas_primitives::babbage::{ + BigInt, Certificate, Constr, DatumHash, PolicyId, PostAlonzoTransactionOutput, Redeemer, + StakeCredential, TransactionInput, Value, Withdrawals, +}; +use pallas_traverse::OutputRef; +use std::str::FromStr; use uplc::PlutusData; use crate::args::ResolvedInput; @@ -9,33 +15,14 @@ pub fn get_tx_in_info(resolved_inputs: &[ResolvedInput]) -> anyhow::Result 0, - 1 => 1, - _ => unreachable!(), - }; - let stake_tag = match address.typeid() { - 0 | 1 => Some(0), - 2 | 3 => Some(1), - _ => None, - }; - - let (payment_part, stake_part) = match address { - Address::Shelley(s) => (s.payment().to_vec(), s.delegation().to_vec()), - _ => unreachable!(), - }; - let lovelace = resolved_input.output.value.0; let mut assets = resolved_input.output.value.1.clone(); @@ -50,45 +37,7 @@ pub fn get_tx_in_info(resolved_inputs: &[ResolvedInput]) -> anyhow::Result vec![ - // StakingCredential - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - // StakingHash - PlutusData::Constr(Constr { - tag: stake_tag, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - PlutusData::BoundedBytes(stake_part.into()), - ]), - }), - ]), - }), - ], - None => vec![], - }), - }), - ]), - }), + address.to_plutus_data(), // txOutValue PlutusData::Map(KeyValuePairs::Def( assets @@ -125,3 +74,145 @@ pub fn get_tx_in_info(resolved_inputs: &[ResolvedInput]) -> anyhow::Result posix time range + +type Slot = u64; +type PosixTime = u64; // in milliseconds + +type SlotRange = (Slot, Slot); +type PosixTimeRange = (PosixTime, PosixTime); + +struct SlotConfig { + slot_length: u64, + zero_time: PosixTime, +} + +fn slot_to_begin_posix_time(slot: Slot, sc: &SlotConfig) -> PosixTime { + let ms_after_begin = slot * sc.slot_length; + sc.zero_time + ms_after_begin +} + +fn slot_range_to_posix_time_range(slot_range: SlotRange, sc: &SlotConfig) -> PosixTimeRange { + ( + slot_to_begin_posix_time(slot_range.0, sc), + slot_to_begin_posix_time(slot_range.1, sc), + ) +} + +// --------------- + +pub trait ToPlutusData { + fn to_plutus_data(&self) -> PlutusData; +} + +impl ToPlutusData for Address { + fn to_plutus_data(&self) -> PlutusData { + //TOD: Byron address and reward address + + let payment_tag = match self.typeid() % 2 { + 0 => 0, + 1 => 1, + _ => unreachable!(), + }; + let stake_tag = match self.typeid() { + 0 | 1 => Some(0), + 2 | 3 => Some(1), + _ => None, + }; + + let (payment_part, stake_part) = match self { + Address::Shelley(s) => (s.payment().to_vec(), s.delegation().to_vec()), + _ => unreachable!(), + }; + + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + // addressCredential + PlutusData::Constr(Constr { + tag: payment_tag, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![PlutusData::BoundedBytes( + payment_part.into(), + )]), + }), + // addressStakingCredential + PlutusData::Constr(Constr { + tag: if stake_tag.is_some() { 0 } else { 1 }, + any_constructor: None, + fields: MaybeIndefArray::Indef(match stake_tag { + Some(stake_tag) => vec![ + // StakingCredential + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + // StakingHash + PlutusData::Constr(Constr { + tag: stake_tag, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + PlutusData::BoundedBytes(stake_part.into()), + ]), + }), + ]), + }), + ], + None => vec![], + }), + }), + ]), + }) + } +} + +impl ToPlutusData for TransactionInput { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + PlutusData::BoundedBytes(hex::decode(self.transaction_id.clone()).unwrap().into()), + PlutusData::BigInt(BigInt::Int(self.index.into())), + ]), + }) + } +} + +// impl ToPlutusData for LegacyTransactionOutput { +// fn to_plutus_data(&self) -> PlutusData {} +// } + +// impl ToPlutusData for PostAlonzoTransactionOutput { +// fn to_plutus_data(&self) -> PlutusData {} +// } + +pub struct TxInInfo { + out_ref: OutputRef, + resolved: PostAlonzoTransactionOutput, +} + +// Plutus V2 +pub enum ScriptPurpose { + Minting(PolicyId), + Spending(OutputRef), + Reward(StakeCredential), + Certifying(Certificate), +} + +pub struct TxInfo { + inputs: Vec, + reference_inputs: Vec, + outputs: Vec, + fee: Value, + mint: Value, + dcert: Vec, + wdrl: Withdrawals, + valid_range: PosixTimeRange, + signatories: Vec, + redeemers: KeyValuePairs, + data: KeyValuePairs, + id: Hash<32>, +} From 4e51826981f3e250d3f0aea733f889eb72ddd59a Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Sat, 10 Sep 2022 13:46:44 +0200 Subject: [PATCH 13/77] added more implementations for ToPlutusData --- crates/cli/src/utils.rs | 217 +++++++++++++++++++++++++++++++++++----- 1 file changed, 194 insertions(+), 23 deletions(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index a532e0e2..9a93eeb3 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -1,11 +1,17 @@ -use pallas_addresses::{Address, PaymentKeyHash}; -use pallas_codec::utils::{KeyValuePairs, MaybeIndefArray}; -use pallas_crypto::hash::Hash; -use pallas_primitives::babbage::{ - BigInt, Certificate, Constr, DatumHash, PolicyId, PostAlonzoTransactionOutput, Redeemer, - StakeCredential, TransactionInput, Value, Withdrawals, +use pallas_addresses::Address; +use pallas_codec::{ + minicbor::{bytes::ByteVec, data::Int}, + utils::{AnyUInt, KeyValuePairs, MaybeIndefArray}, +}; +use pallas_crypto::hash::Hash; +use pallas_primitives::{ + babbage::{ + AddrKeyhash, AssetName, BigInt, Certificate, Constr, DatumHash, DatumOption, PolicyId, + PostAlonzoTransactionOutput, Redeemer, RewardAccount, Script, ScriptRef, StakeCredential, + TransactionInput, TransactionOutput, Value, Withdrawals, + }, + ToHash, }; -use pallas_traverse::OutputRef; use std::str::FromStr; use uplc::PlutusData; @@ -174,30 +180,190 @@ impl ToPlutusData for TransactionInput { tag: 0, any_constructor: None, fields: MaybeIndefArray::Indef(vec![ - PlutusData::BoundedBytes(hex::decode(self.transaction_id.clone()).unwrap().into()), + self.transaction_id.to_plutus_data(), PlutusData::BigInt(BigInt::Int(self.index.into())), ]), }) } } -// impl ToPlutusData for LegacyTransactionOutput { -// fn to_plutus_data(&self) -> PlutusData {} -// } - -// impl ToPlutusData for PostAlonzoTransactionOutput { -// fn to_plutus_data(&self) -> PlutusData {} -// } - -pub struct TxInInfo { - out_ref: OutputRef, - resolved: PostAlonzoTransactionOutput, +impl ToPlutusData for Hash { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::BoundedBytes(self.to_vec().into()) + } +} + +impl ToPlutusData for ByteVec { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::BoundedBytes(self.clone()) + } +} + +impl ToPlutusData for Vec { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::Array(MaybeIndefArray::Indef( + self.iter().map(|p| p.to_plutus_data()).collect(), + )) + } +} + +impl ToPlutusData for Option { + fn to_plutus_data(&self) -> PlutusData { + match self { + None => PlutusData::Constr(Constr { + tag: 1, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![]), + }), + Some(data) => PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![data.to_plutus_data()]), + }), + } + } +} + +impl ToPlutusData for DatumOption { + fn to_plutus_data(&self) -> PlutusData { + match self { + // tag : 0 is NoOutputDatum. Determined after unwrapping Option which is wrapped around DatumOption + DatumOption::Hash(hash) => PlutusData::Constr(Constr { + tag: 1, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![hash.to_plutus_data()]), + }), + DatumOption::Data(data) => PlutusData::Constr(Constr { + tag: 2, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![data.0.clone()]), // does data need an extra wrapper constructor? + }), + } + } +} + +impl ToPlutusData for AnyUInt { + fn to_plutus_data(&self) -> PlutusData { + match self { + AnyUInt::U8(u8) => PlutusData::BigInt(BigInt::Int(Int::from(*u8))), + AnyUInt::U16(u16) => PlutusData::BigInt(BigInt::Int(Int::from(*u16))), + AnyUInt::U32(u32) => PlutusData::BigInt(BigInt::Int(Int::from(*u32))), + AnyUInt::U64(u64) => PlutusData::BigInt(BigInt::Int(Int::from(*u64))), + AnyUInt::MajorByte(u8) => PlutusData::BigInt(BigInt::Int(Int::from(*u8))), // is this correct? I don't know exactly what is does + } + } +} + +impl ToPlutusData for Value { + fn to_plutus_data(&self) -> PlutusData { + match self { + Value::Coin(coin) => PlutusData::Map(KeyValuePairs::Def(vec![( + PolicyId::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![( + PolicyId::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 ScriptRef { + fn to_plutus_data(&self) -> PlutusData { + match &self.0 { + Script::NativeScript(native_script) => native_script.to_hash().to_plutus_data(), + Script::PlutusV1Script(plutus_v1) => plutus_v1.to_hash().to_plutus_data(), + Script::PlutusV2Script(plutus_v2) => plutus_v2.to_hash().to_plutus_data(), + } + } +} + +impl ToPlutusData for TransactionOutput { + fn to_plutus_data(&self) -> PlutusData { + match self { + TransactionOutput::Legacy(legacy_output) => PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + Address::from_bytes(&legacy_output.address) + .unwrap() + .to_plutus_data(), + legacy_output.amount.to_plutus_data(), + None::.to_plutus_data(), + None::.to_plutus_data(), + ]), + }), + TransactionOutput::PostAlonzo(post_alonzo_output) => PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + Address::from_bytes(&post_alonzo_output.address) + .unwrap() + .to_plutus_data(), + post_alonzo_output.value.to_plutus_data(), + // DatumOption needs to be handled differently a bit. In Haskell it's NoOutputDatum | OutputDatumHash DatumHash | OutputDatum Datum + // So we unwrap first to check if it's someting. If it is then turn the unwrapped data to PlutusData, otherwise have None and turn that into PlutusData + if post_alonzo_output.datum_option.is_some() { + post_alonzo_output + .datum_option + .clone() + .unwrap() + .to_plutus_data() + } else { + None::.to_plutus_data() + }, + post_alonzo_output.script_ref.to_plutus_data(), + ]), + }), + } + } +} + +impl ToPlutusData for TxInInfo { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + self.out_ref.to_plutus_data(), + self.resolved.to_plutus_data(), + ]), + }) + } +} + +// Plutus V2 only for now + +pub struct TxInInfo { + out_ref: TransactionInput, + resolved: TransactionOutput, } -// Plutus V2 pub enum ScriptPurpose { Minting(PolicyId), - Spending(OutputRef), + Spending(TransactionInput), Reward(StakeCredential), Certifying(Certificate), } @@ -205,14 +371,19 @@ pub enum ScriptPurpose { pub struct TxInfo { inputs: Vec, reference_inputs: Vec, - outputs: Vec, + outputs: Vec, fee: Value, mint: Value, dcert: Vec, wdrl: Withdrawals, valid_range: PosixTimeRange, - signatories: Vec, + signatories: Vec, redeemers: KeyValuePairs, data: KeyValuePairs, id: Hash<32>, } + +pub struct ScriptContext { + tx_info: TxInfo, + purpose: ScriptPurpose, +} From c0c7bd58b3a61eace9919bb9140c32dbb1f53bd2 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Sat, 10 Sep 2022 15:55:19 +0200 Subject: [PATCH 14/77] added all necessary trait impl for PlutusV2 scriptContext --- crates/cli/src/utils.rs | 243 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 229 insertions(+), 14 deletions(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index 9a93eeb3..ceaf761c 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -6,13 +6,13 @@ use pallas_codec::{ use pallas_crypto::hash::Hash; use pallas_primitives::{ babbage::{ - AddrKeyhash, AssetName, BigInt, Certificate, Constr, DatumHash, DatumOption, PolicyId, - PostAlonzoTransactionOutput, Redeemer, RewardAccount, Script, ScriptRef, StakeCredential, - TransactionInput, TransactionOutput, Value, Withdrawals, + AddrKeyhash, AssetName, BigInt, Certificate, Constr, DatumHash, DatumOption, Mint, + PolicyId, Redeemer, RewardAccount, Script, ScriptRef, StakeCredential, TransactionInput, + TransactionOutput, Value, Withdrawals, }, ToHash, }; -use std::str::FromStr; +use std::{str::FromStr, vec}; use uplc::PlutusData; use crate::args::ResolvedInput; @@ -86,8 +86,10 @@ pub fn get_tx_in_info(resolved_inputs: &[ResolvedInput]) -> anyhow::Result { + lower_bound: A, + upper_bound: A, +} struct SlotConfig { slot_length: u64, @@ -99,11 +101,14 @@ fn slot_to_begin_posix_time(slot: Slot, sc: &SlotConfig) -> PosixTime { sc.zero_time + ms_after_begin } -fn slot_range_to_posix_time_range(slot_range: SlotRange, sc: &SlotConfig) -> PosixTimeRange { - ( - slot_to_begin_posix_time(slot_range.0, sc), - slot_to_begin_posix_time(slot_range.1, sc), - ) +fn slot_range_to_posix_time_range( + slot_range: TimeRange, + sc: &SlotConfig, +) -> TimeRange { + TimeRange { + lower_bound: slot_to_begin_posix_time(slot_range.lower_bound, sc), + upper_bound: slot_to_begin_posix_time(slot_range.upper_bound, sc), + } } // --------------- @@ -207,6 +212,16 @@ impl ToPlutusData for Vec { } } +impl ToPlutusData for KeyValuePairs { + 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 { @@ -254,6 +269,30 @@ impl ToPlutusData for AnyUInt { } } +impl ToPlutusData for Int { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::BigInt(BigInt::Int(self.clone())) + } +} + +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))) + } +} + impl ToPlutusData for Value { fn to_plutus_data(&self) -> PlutusData { match self { @@ -341,6 +380,119 @@ impl ToPlutusData for TransactionOutput { } } +impl ToPlutusData for StakeCredential { + fn to_plutus_data(&self) -> PlutusData { + match self { + StakeCredential::AddrKeyhash(addr_keyhas) => PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![addr_keyhas.to_plutus_data()]), + }), + StakeCredential::Scripthash(script_hash) => PlutusData::Constr(Constr { + tag: 1, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![script_hash.to_plutus_data()]), + }), + } + } +} + +impl ToPlutusData for Certificate { + fn to_plutus_data(&self) -> PlutusData { + match self { + Certificate::StakeRegistration(stake_credential) => PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![stake_credential.to_plutus_data()]), + }), + Certificate::StakeDeregistration(stake_credential) => PlutusData::Constr(Constr { + tag: 1, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![stake_credential.to_plutus_data()]), + }), + Certificate::StakeDelegation(stake_credential, pool_keyhash) => { + PlutusData::Constr(Constr { + tag: 2, + any_constructor: None, + fields: MaybeIndefArray::Indef(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: _, + } => PlutusData::Constr(Constr { + tag: 3, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + operator.to_plutus_data(), + vrf_keyhash.to_plutus_data(), + ]), + }), + Certificate::PoolRetirement(pool_keyhash, epoch) => PlutusData::Constr(Constr { + tag: 4, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + pool_keyhash.to_plutus_data(), + epoch.to_plutus_data(), + ]), + }), + Certificate::GenesisKeyDelegation(_, _, _) => PlutusData::Constr(Constr { + tag: 5, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![]), + }), + Certificate::MoveInstantaneousRewardsCert(_) => PlutusData::Constr(Constr { + tag: 6, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![]), + }), + } + } +} + +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 { + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![self.lower_bound.to_plutus_data()]), + }), + PlutusData::Constr(Constr { + tag: 1, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![self.upper_bound.to_plutus_data()]), + }), + ]), + }) + } +} + impl ToPlutusData for TxInInfo { fn to_plutus_data(&self) -> PlutusData { PlutusData::Constr(Constr { @@ -354,6 +506,69 @@ impl ToPlutusData for TxInInfo { } } +impl ToPlutusData for ScriptPurpose { + fn to_plutus_data(&self) -> PlutusData { + match self { + ScriptPurpose::Minting(policy_id) => PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![policy_id.to_plutus_data()]), + }), + ScriptPurpose::Spending(out_ref) => PlutusData::Constr(Constr { + tag: 1, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![out_ref.to_plutus_data()]), + }), + ScriptPurpose::Rewarding(stake_credential) => PlutusData::Constr(Constr { + tag: 3, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![stake_credential.to_plutus_data()]), + }), + ScriptPurpose::Certifying(dcert) => PlutusData::Constr(Constr { + tag: 4, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![dcert.to_plutus_data()]), + }), + } + } +} + +impl ToPlutusData for TxInfo { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + self.inputs.to_plutus_data(), + self.reference_inputs.to_plutus_data(), + self.outputs.to_plutus_data(), + self.fee.to_plutus_data(), + self.mint.to_plutus_data(), + self.dcert.to_plutus_data(), + self.wdrl.to_plutus_data(), + self.valid_range.to_plutus_data(), + self.signatories.to_plutus_data(), + self.redeemers.to_plutus_data(), + self.data.to_plutus_data(), + self.id.to_plutus_data(), + ]), + }) + } +} + +impl ToPlutusData for ScriptContext { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + self.tx_info.to_plutus_data(), + self.purpose.to_plutus_data(), + ]), + }) + } +} + // Plutus V2 only for now pub struct TxInInfo { @@ -364,7 +579,7 @@ pub struct TxInInfo { pub enum ScriptPurpose { Minting(PolicyId), Spending(TransactionInput), - Reward(StakeCredential), + Rewarding(StakeCredential), Certifying(Certificate), } @@ -373,10 +588,10 @@ pub struct TxInfo { reference_inputs: Vec, outputs: Vec, fee: Value, - mint: Value, + mint: Mint, dcert: Vec, wdrl: Withdrawals, - valid_range: PosixTimeRange, + valid_range: TimeRange, signatories: Vec, redeemers: KeyValuePairs, data: KeyValuePairs, From 8b894f73515164e556c51e794db350fbae982909 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Sat, 10 Sep 2022 17:09:50 +0200 Subject: [PATCH 15/77] added functions to get script context --- crates/cli/src/main.rs | 2 +- crates/cli/src/utils.rs | 119 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 111 insertions(+), 10 deletions(-) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index d0077832..2fbd9615 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -68,7 +68,7 @@ fn main() -> anyhow::Result<()> { let reader = BufReader::new(file); let resolved_inputs: Vec = serde_json::from_reader(reader)?; - let tx_in_info = utils::get_tx_in_info(&resolved_inputs)?; + let tx_in_info = utils::get_tx_in_info_old(&resolved_inputs)?; } } diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index ceaf761c..d336ecd1 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -7,8 +7,8 @@ use pallas_crypto::hash::Hash; use pallas_primitives::{ babbage::{ AddrKeyhash, AssetName, BigInt, Certificate, Constr, DatumHash, DatumOption, Mint, - PolicyId, Redeemer, RewardAccount, Script, ScriptRef, StakeCredential, TransactionInput, - TransactionOutput, Value, Withdrawals, + PolicyId, Redeemer, Script, ScriptRef, StakeCredential, TransactionInput, + TransactionOutput, Tx, Value, Withdrawals, }, ToHash, }; @@ -17,7 +17,7 @@ use uplc::PlutusData; use crate::args::ResolvedInput; -pub fn get_tx_in_info(resolved_inputs: &[ResolvedInput]) -> anyhow::Result> { +pub fn get_tx_in_info_old(resolved_inputs: &[ResolvedInput]) -> anyhow::Result> { let mut tx_in_info = Vec::new(); for resolved_input in resolved_inputs { @@ -204,7 +204,7 @@ impl ToPlutusData for ByteVec { } } -impl ToPlutusData for Vec { +impl ToPlutusData for MaybeIndefArray { fn to_plutus_data(&self) -> PlutusData { PlutusData::Array(MaybeIndefArray::Indef( self.iter().map(|p| p.to_plutus_data()).collect(), @@ -584,15 +584,15 @@ pub enum ScriptPurpose { } pub struct TxInfo { - inputs: Vec, - reference_inputs: Vec, - outputs: Vec, + inputs: MaybeIndefArray, + reference_inputs: MaybeIndefArray, + outputs: MaybeIndefArray, fee: Value, mint: Mint, - dcert: Vec, + dcert: MaybeIndefArray, wdrl: Withdrawals, valid_range: TimeRange, - signatories: Vec, + signatories: MaybeIndefArray, redeemers: KeyValuePairs, data: KeyValuePairs, id: Hash<32>, @@ -602,3 +602,104 @@ pub struct ScriptContext { tx_info: TxInfo, purpose: ScriptPurpose, } + +fn get_tx_in_info( + inputs: &MaybeIndefArray, + utxos: &Vec<(TransactionInput, TransactionOutput)>, +) -> anyhow::Result> { + Ok(MaybeIndefArray::Indef( + inputs + .iter() + .map(|input| { + let utxo = utxos.iter().find(|utxo| utxo.0 == *input); + match utxo { + Some(u) => TxInInfo { + out_ref: input.clone(), + resolved: u.1.clone(), + }, + None => panic!(), + } + }) + .collect(), + )) +} + +fn get_script_purpose(redeemer: &Redeemer) -> anyhow::Result { + todo!() +} + +fn get_tx_info( + tx: &Tx, + utxos: &Vec<(TransactionInput, TransactionOutput)>, + slot_config: &SlotConfig, +) -> anyhow::Result { + let body = tx.transaction_body.clone(); + + let inputs = get_tx_in_info(&body.inputs, &utxos)?; + let reference_inputs = get_tx_in_info( + &body + .reference_inputs + .unwrap_or(MaybeIndefArray::Indef(vec![])), + &utxos, + )?; + let outputs = body.outputs; + let fee = Value::Coin(AnyUInt::U64(body.fee)); + let mint = body.mint.unwrap_or(KeyValuePairs::Indef(vec![])); + let dcert = body.certificates.unwrap_or(MaybeIndefArray::Indef(vec![])); + let wdrl = body.withdrawals.unwrap_or(KeyValuePairs::Indef(vec![])); + let valid_range = slot_range_to_posix_time_range( + TimeRange { + lower_bound: 0, + upper_bound: 0, + }, + &slot_config, + ); // TODO + let signatories = body + .required_signers + .unwrap_or(MaybeIndefArray::Indef(vec![])); + let redeemers = KeyValuePairs::Indef( + tx.transaction_witness_set + .redeemer + .as_ref() + .unwrap_or(&MaybeIndefArray::Indef(vec![])) + .iter() + .map(|r| (get_script_purpose(&r).unwrap(), r.clone())) + .collect(), + ); + let data = KeyValuePairs::Indef( + tx.transaction_witness_set + .plutus_data + .as_ref() + .unwrap_or(&MaybeIndefArray::Indef(vec![])) + .iter() + .map(|d| (d.to_hash(), d.clone())) + .collect(), + ); + let id = tx.transaction_body.to_hash(); + + Ok(TxInfo { + inputs, + reference_inputs, + outputs, + fee, + mint, + dcert, + wdrl, + valid_range, + signatories, + redeemers, + data, + id, + }) +} + +fn get_script_context( + tx: &Tx, + utxos: &Vec<(TransactionInput, TransactionOutput)>, + slot_config: &SlotConfig, + redeemer: &Redeemer, +) -> anyhow::Result { + let tx_info = get_tx_info(tx, utxos, slot_config)?; + let purpose = get_script_purpose(redeemer)?; + Ok(ScriptContext { tx_info, purpose }) +} From 57816d4c092b882d961a27132eaa5461eafa1db3 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Sat, 10 Sep 2022 18:27:23 +0200 Subject: [PATCH 16/77] added get script purpose --- crates/cli/src/utils.rs | 87 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 79 insertions(+), 8 deletions(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index d336ecd1..68b58a98 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -1,4 +1,4 @@ -use pallas_addresses::Address; +use pallas_addresses::{Address, StakePayload}; use pallas_codec::{ minicbor::{bytes::ByteVec, data::Int}, utils::{AnyUInt, KeyValuePairs, MaybeIndefArray}, @@ -7,8 +7,8 @@ use pallas_crypto::hash::Hash; use pallas_primitives::{ babbage::{ AddrKeyhash, AssetName, BigInt, Certificate, Constr, DatumHash, DatumOption, Mint, - PolicyId, Redeemer, Script, ScriptRef, StakeCredential, TransactionInput, - TransactionOutput, Tx, Value, Withdrawals, + PolicyId, Redeemer, RedeemerTag, RewardAccount, Script, ScriptRef, StakeCredential, + TransactionInput, TransactionOutput, Tx, Value, Withdrawals, }, ToHash, }; @@ -624,8 +624,68 @@ fn get_tx_in_info( )) } -fn get_script_purpose(redeemer: &Redeemer) -> anyhow::Result { - todo!() +fn get_script_purpose( + redeemer: &Redeemer, + inputs: &MaybeIndefArray, + mint: &Mint, + dcert: &MaybeIndefArray, + wdrl: &Withdrawals, +) -> anyhow::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 + .iter() + .map(|(policy_id, _)| policy_id) + .collect::>() + .clone(); + policy_ids.sort(); + let policy_id = policy_ids[index as usize].clone(); + Ok(ScriptPurpose::Minting(policy_id)) + } + RedeemerTag::Spend => { + // sort lexical by tx_hash and index + let mut inputs = inputs + .iter() + .map(|input| input.out_ref.clone()) + .collect::>() + .clone(); + // inputs.sort(); // TODO!!!!!!!!!; + let input = inputs[index as usize].clone(); + Ok(ScriptPurpose::Spending(input)) + } + RedeemerTag::Reward => { + // sort lexical by reward account + let mut reward_accounts = wdrl + .iter() + .map(|(policy_id, _)| policy_id) + .collect::>() + .clone(); + reward_accounts.sort(); + let reward_account = reward_accounts[index as usize]; + let addresss = Address::from_bytes(&reward_account)?; + let credential = match addresss { + Address::Stake(stake_address) => match stake_address.payload() { + StakePayload::Stake(stake_keyhash) => { + StakeCredential::AddrKeyhash(stake_keyhash.clone()) + } + StakePayload::Script(script_hash) => { + StakeCredential::Scripthash(script_hash.clone()) + } + }, + _ => panic!(), + }; + Ok(ScriptPurpose::Rewarding(credential)) + } + RedeemerTag::Cert => { + // sort by order given in the tx (just take it as it is basically) + let cert = dcert[index as usize].clone(); + Ok(ScriptPurpose::Certifying(cert.clone())) + } + } } fn get_tx_info( @@ -653,7 +713,7 @@ fn get_tx_info( upper_bound: 0, }, &slot_config, - ); // TODO + ); // TODO!!!! let signatories = body .required_signers .unwrap_or(MaybeIndefArray::Indef(vec![])); @@ -663,7 +723,12 @@ fn get_tx_info( .as_ref() .unwrap_or(&MaybeIndefArray::Indef(vec![])) .iter() - .map(|r| (get_script_purpose(&r).unwrap(), r.clone())) + .map(|r| { + ( + get_script_purpose(&r, &inputs, &mint, &dcert, &wdrl).unwrap(), + r.clone(), + ) + }) .collect(), ); let data = KeyValuePairs::Indef( @@ -700,6 +765,12 @@ fn get_script_context( redeemer: &Redeemer, ) -> anyhow::Result { let tx_info = get_tx_info(tx, utxos, slot_config)?; - let purpose = get_script_purpose(redeemer)?; + let purpose = get_script_purpose( + redeemer, + &tx_info.inputs, + &tx_info.mint, + &tx_info.dcert, + &tx_info.wdrl, + )?; Ok(ScriptContext { tx_info, purpose }) } From 24397d4b5885d07ffbad19274d9dc2776f169cb5 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Sat, 10 Sep 2022 21:25:23 +0200 Subject: [PATCH 17/77] fixed time range --- crates/cli/src/utils.rs | 278 +++++++++++++++++++++++++++++++++------- 1 file changed, 229 insertions(+), 49 deletions(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index 68b58a98..e0030243 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -81,36 +81,6 @@ pub fn get_tx_in_info_old(resolved_inputs: &[ResolvedInput]) -> anyhow::Result posix time range - -type Slot = u64; -type PosixTime = u64; // in milliseconds - -struct TimeRange { - lower_bound: A, - upper_bound: A, -} - -struct SlotConfig { - slot_length: u64, - zero_time: PosixTime, -} - -fn slot_to_begin_posix_time(slot: Slot, sc: &SlotConfig) -> PosixTime { - let ms_after_begin = 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_to_begin_posix_time(slot_range.lower_bound, sc), - upper_bound: slot_to_begin_posix_time(slot_range.upper_bound, sc), - } -} - // --------------- pub trait ToPlutusData { @@ -472,24 +442,178 @@ impl ToPlutusData for PlutusData { } } -impl ToPlutusData for TimeRange { +impl ToPlutusData for TimeRange { fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + match &self { + TimeRange { + lower_bound: Some(lower_bound), + upper_bound: None, + } => { PlutusData::Constr(Constr { tag: 0, any_constructor: None, - fields: MaybeIndefArray::Indef(vec![self.lower_bound.to_plutus_data()]), - }), + fields: MaybeIndefArray::Indef(vec![ + // LowerBound + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + // Finite + PlutusData::Constr(Constr { + tag: 1, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + lower_bound.to_plutus_data() + ]), + }), + // Closure + true.to_plutus_data(), + ]), + }), //UpperBound + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + // PosInf + PlutusData::Constr(Constr { + tag: 2, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![]), + }), + // Closure + true.to_plutus_data(), + ]), + }), + ]), + }) + } + TimeRange { + lower_bound: None, + upper_bound: Some(upper_bound), + } => { PlutusData::Constr(Constr { - tag: 1, + tag: 0, any_constructor: None, - fields: MaybeIndefArray::Indef(vec![self.upper_bound.to_plutus_data()]), - }), - ]), - }) + fields: MaybeIndefArray::Indef(vec![ + // LowerBound + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + // NegInf + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![]), + }), + // Closure + true.to_plutus_data(), + ]), + }), //UpperBound + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + // Finite + PlutusData::Constr(Constr { + tag: 1, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + upper_bound.to_plutus_data() + ]), + }), + // Closure + true.to_plutus_data(), + ]), + }), + ]), + }) + } + TimeRange { + lower_bound: Some(lower_bound), + upper_bound: Some(upper_bound), + } => { + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + // LowerBound + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + // Finite + PlutusData::Constr(Constr { + tag: 1, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + lower_bound.to_plutus_data() + ]), + }), + // Closure + true.to_plutus_data(), + ]), + }), //UpperBound + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + // Finite + PlutusData::Constr(Constr { + tag: 1, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + upper_bound.to_plutus_data() + ]), + }), + // Closure + false.to_plutus_data(), + ]), + }), + ]), + }) + } + TimeRange { + lower_bound: None, + upper_bound: None, + } => { + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + // LowerBound + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + // NegInf + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![]), + }), + // Closure + true.to_plutus_data(), + ]), + }), //UpperBound + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + // PosInf + PlutusData::Constr(Constr { + tag: 2, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![]), + }), + // Closure + true.to_plutus_data(), + ]), + }), + ]), + }) + } + } } } @@ -520,12 +644,12 @@ impl ToPlutusData for ScriptPurpose { fields: MaybeIndefArray::Indef(vec![out_ref.to_plutus_data()]), }), ScriptPurpose::Rewarding(stake_credential) => PlutusData::Constr(Constr { - tag: 3, + tag: 2, any_constructor: None, fields: MaybeIndefArray::Indef(vec![stake_credential.to_plutus_data()]), }), ScriptPurpose::Certifying(dcert) => PlutusData::Constr(Constr { - tag: 4, + tag: 3, any_constructor: None, fields: MaybeIndefArray::Indef(vec![dcert.to_plutus_data()]), }), @@ -569,6 +693,23 @@ impl ToPlutusData for ScriptContext { } } +impl ToPlutusData for bool { + fn to_plutus_data(&self) -> PlutusData { + match self { + false => PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![]), + }), + true => PlutusData::Constr(Constr { + tag: 1, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![]), + }), + } + } +} + // Plutus V2 only for now pub struct TxInInfo { @@ -591,7 +732,7 @@ pub struct TxInfo { mint: Mint, dcert: MaybeIndefArray, wdrl: Withdrawals, - valid_range: TimeRange, + valid_range: TimeRange, signatories: MaybeIndefArray, redeemers: KeyValuePairs, data: KeyValuePairs, @@ -603,6 +744,36 @@ pub struct ScriptContext { purpose: ScriptPurpose, } +//---- Time conversion: slot range => posix time range + +struct TimeRange { + lower_bound: Option, + upper_bound: Option, +} + +struct SlotConfig { + slot_length: u64, + zero_time: u64, +} + +fn slot_to_begin_posix_time(slot: u64, sc: &SlotConfig) -> u64 { + let ms_after_begin = 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: match slot_range.lower_bound { + Some(lower_bound) => Some(slot_to_begin_posix_time(lower_bound, sc)), + None => None, + }, + upper_bound: match slot_range.upper_bound { + Some(upper_bound) => Some(slot_to_begin_posix_time(upper_bound, sc)), + None => None, + }, + } +} + fn get_tx_in_info( inputs: &MaybeIndefArray, utxos: &Vec<(TransactionInput, TransactionOutput)>, @@ -653,7 +824,14 @@ fn get_script_purpose( .map(|input| input.out_ref.clone()) .collect::>() .clone(); - // inputs.sort(); // TODO!!!!!!!!!; + // is this correct? Does this sort lexical from low to high? maybe get Ordering into pallas for TransactionInput? + 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 input = inputs[index as usize].clone(); Ok(ScriptPurpose::Spending(input)) } @@ -709,11 +887,11 @@ fn get_tx_info( let wdrl = body.withdrawals.unwrap_or(KeyValuePairs::Indef(vec![])); let valid_range = slot_range_to_posix_time_range( TimeRange { - lower_bound: 0, - upper_bound: 0, + lower_bound: body.validity_interval_start, + upper_bound: body.ttl, }, &slot_config, - ); // TODO!!!! + ); let signatories = body .required_signers .unwrap_or(MaybeIndefArray::Indef(vec![])); @@ -774,3 +952,5 @@ fn get_script_context( )?; Ok(ScriptContext { tx_info, purpose }) } + +// TODO: Maybe make ToPlutusData dependent on a Plutus Language so it works for V1 and V2? From 4060a253243a485e6c6f65406a8d9ac1054c2e75 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Sat, 10 Sep 2022 23:07:51 +0200 Subject: [PATCH 18/77] fixed bugs --- crates/cli/src/utils.rs | 141 +++++++++++++++++++++++----------------- 1 file changed, 82 insertions(+), 59 deletions(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index e0030243..3a971953 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -1,4 +1,4 @@ -use pallas_addresses::{Address, StakePayload}; +use pallas_addresses::{Address, ShelleyDelegationPart, ShelleyPaymentPart, StakePayload}; use pallas_codec::{ minicbor::{bytes::ByteVec, data::Int}, utils::{AnyUInt, KeyValuePairs, MaybeIndefArray}, @@ -22,7 +22,7 @@ pub fn get_tx_in_info_old(resolved_inputs: &[ResolvedInput]) -> anyhow::Result PlutusData { - //TOD: Byron address and reward address - - let payment_tag = match self.typeid() % 2 { - 0 => 0, - 1 => 1, - _ => unreachable!(), - }; - let stake_tag = match self.typeid() { - 0 | 1 => Some(0), - 2 | 3 => Some(1), - _ => None, - }; - - let (payment_part, stake_part) = match self { - Address::Shelley(s) => (s.payment().to_vec(), s.delegation().to_vec()), - _ => unreachable!(), - }; - - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - // addressCredential + match self { + Address::Byron(byron_address) => { PlutusData::Constr(Constr { - tag: payment_tag, + tag: 0, any_constructor: None, - fields: MaybeIndefArray::Indef(vec![PlutusData::BoundedBytes( - payment_part.into(), - )]), - }), - // addressStakingCredential - PlutusData::Constr(Constr { - tag: if stake_tag.is_some() { 0 } else { 1 }, - any_constructor: None, - fields: MaybeIndefArray::Indef(match stake_tag { - Some(stake_tag) => vec![ - // StakingCredential - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - // StakingHash - PlutusData::Constr(Constr { - tag: stake_tag, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - PlutusData::BoundedBytes(stake_part.into()), - ]), - }), - ]), - }), - ], - None => vec![], + fields: MaybeIndefArray::Indef(vec![ + //addressCredential + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![byron_address + .decode() + .unwrap() + .root + .to_plutus_data()]), + }), + //addressStakeCredential + None::.to_plutus_data(), + ]), + }) + } + 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) => PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![payment_keyhash.to_plutus_data()]), }), - }), - ]), - }) + ShelleyPaymentPart::Script(script_hash) => PlutusData::Constr(Constr { + tag: 1, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![script_hash.to_plutus_data()]), + }), + }; + + let stake_part_plutus_data = match stake_part { + ShelleyDelegationPart::Key(stake_keyhash) => { + Some(StakeCredential::AddrKeyhash(stake_keyhash.clone())).to_plutus_data() + } + ShelleyDelegationPart::Script(script_hash) => { + Some(StakeCredential::Scripthash(script_hash.clone())).to_plutus_data() + } + ShelleyDelegationPart::Pointer(pointer) => Some(PlutusData::Constr(Constr { + tag: 1, + any_constructor: None, + fields: MaybeIndefArray::Indef(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(), + }; + + PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + payment_part_plutus_data, + stake_part_plutus_data, + ]), + }) + } + Address::Stake(_) => unreachable!(), + } } } @@ -351,17 +363,28 @@ impl ToPlutusData for TransactionOutput { } 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) => PlutusData::Constr(Constr { tag: 0, any_constructor: None, - fields: MaybeIndefArray::Indef(vec![addr_keyhas.to_plutus_data()]), + fields: MaybeIndefArray::Indef(vec![PlutusData::Constr(Constr { + tag: 0, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![addr_keyhas.to_plutus_data()]), + })]), }), StakeCredential::Scripthash(script_hash) => PlutusData::Constr(Constr { - tag: 1, + tag: 0, any_constructor: None, - fields: MaybeIndefArray::Indef(vec![script_hash.to_plutus_data()]), + fields: MaybeIndefArray::Indef(vec![PlutusData::Constr(Constr { + tag: 1, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![script_hash.to_plutus_data()]), + })]), }), } } From ecd363e67daa3e884ff8eec338d3185d82ba33b5 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Sat, 10 Sep 2022 23:49:31 +0200 Subject: [PATCH 19/77] fixed bugs; preliminary work on eval --- crates/cli/src/utils.rs | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index 3a971953..4400ab44 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -6,12 +6,13 @@ use pallas_codec::{ use pallas_crypto::hash::Hash; use pallas_primitives::{ babbage::{ - AddrKeyhash, AssetName, BigInt, Certificate, Constr, DatumHash, DatumOption, Mint, - PolicyId, Redeemer, RedeemerTag, RewardAccount, Script, ScriptRef, StakeCredential, - TransactionInput, TransactionOutput, Tx, Value, Withdrawals, + AddrKeyhash, AssetName, BigInt, Certificate, Constr, CostModel, DatumHash, DatumOption, + Language, Mint, PolicyId, Redeemer, RedeemerTag, RewardAccount, Script, ScriptRef, + StakeCredential, TransactionInput, TransactionOutput, Tx, Value, Withdrawals, }, ToHash, }; +use pallas_traverse::{Era, MultiEraTx}; use std::{str::FromStr, vec}; use uplc::PlutusData; @@ -144,7 +145,7 @@ impl ToPlutusData for Address { ]), })) .to_plutus_data(), - ShelleyDelegationPart::Null => None::
.to_plutus_data(), + ShelleyDelegationPart::Null => None::.to_plutus_data(), }; PlutusData::Constr(Constr { @@ -977,3 +978,29 @@ fn get_script_context( } // TODO: Maybe make ToPlutusData dependent on a Plutus Language so it works for V1 and V2? + +// fn eval_single_redeemer( +// redeemer: &Redeemer, +// tx: &Tx, +// utxos: &Vec<(TransactionInput, TransactionOutput)>, +// cost_model: &CostModel, +// slot_config: &SlotConfig, +// language: &Language, +// ) -> anyhow::Result { +// } + +// fn eval_tx( +// tx_bytes: &Vec, +// utxos: &Vec<(Vec, Vec)>, +// cost_model: &Vec, +// zero_time: u64, +// slot_length: u64, +// ) -> anyhow::Result { +// let multi_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) +// .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) +// .or_else(|_| MultiEraTx::decode(Era::Byron, &tx_bytes))?; + +// let tx = multi_tx.as_babbage().unwrap(); + +// Ok(true) +// } From f0d17897ab3f31694866aa01db4ec4bb3d9657b1 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Sun, 11 Sep 2022 10:07:17 +0200 Subject: [PATCH 20/77] fixed bug in tx_in_info --- crates/cli/src/utils.rs | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index 4400ab44..e1042f8c 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -736,11 +736,13 @@ impl ToPlutusData for bool { // Plutus V2 only for now +#[derive(Debug, PartialEq, Clone)] pub struct TxInInfo { out_ref: TransactionInput, resolved: TransactionOutput, } +#[derive(Debug, PartialEq, Clone)] pub enum ScriptPurpose { Minting(PolicyId), Spending(TransactionInput), @@ -748,6 +750,7 @@ pub enum ScriptPurpose { Certifying(Certificate), } +#[derive(Debug, PartialEq, Clone)] pub struct TxInfo { inputs: MaybeIndefArray, reference_inputs: MaybeIndefArray, @@ -763,13 +766,14 @@ pub struct TxInfo { id: Hash<32>, } +#[derive(Debug, PartialEq, Clone)] pub struct ScriptContext { tx_info: TxInfo, purpose: ScriptPurpose, } //---- Time conversion: slot range => posix time range - +#[derive(Debug, PartialEq, Clone)] struct TimeRange { lower_bound: Option, upper_bound: Option, @@ -800,22 +804,14 @@ fn slot_range_to_posix_time_range(slot_range: TimeRange, sc: &SlotConfig) -> Tim fn get_tx_in_info( inputs: &MaybeIndefArray, - utxos: &Vec<(TransactionInput, TransactionOutput)>, + utxos: &MaybeIndefArray, ) -> anyhow::Result> { Ok(MaybeIndefArray::Indef( - inputs + utxos .iter() - .map(|input| { - let utxo = utxos.iter().find(|utxo| utxo.0 == *input); - match utxo { - Some(u) => TxInInfo { - out_ref: input.clone(), - resolved: u.1.clone(), - }, - None => panic!(), - } - }) - .collect(), + .filter(|utxo| inputs.contains(&utxo.out_ref)) + .map(|utxo| utxo.clone()) + .collect::>(), )) } @@ -892,7 +888,7 @@ fn get_script_purpose( fn get_tx_info( tx: &Tx, - utxos: &Vec<(TransactionInput, TransactionOutput)>, + utxos: &MaybeIndefArray, slot_config: &SlotConfig, ) -> anyhow::Result { let body = tx.transaction_body.clone(); @@ -962,7 +958,7 @@ fn get_tx_info( fn get_script_context( tx: &Tx, - utxos: &Vec<(TransactionInput, TransactionOutput)>, + utxos: &MaybeIndefArray, slot_config: &SlotConfig, redeemer: &Redeemer, ) -> anyhow::Result { From ec63bd236576b438e7a6dbbc4c56ec60fab92c31 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Sun, 11 Sep 2022 12:48:18 +0200 Subject: [PATCH 21/77] fixed bugs in conversion to plutus data; little refactoring --- crates/cli/src/utils.rs | 226 +++++++++++++--------------------------- 1 file changed, 70 insertions(+), 156 deletions(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index e1042f8c..05b42c5c 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -84,6 +84,22 @@ pub fn get_tx_in_info_old(resolved_inputs: &[ResolvedInput]) -> anyhow::Result PlutusData { + PlutusData::Constr(Constr { + tag, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![data]), + }) +} + +fn empty_constr(tag: u64) -> PlutusData { + PlutusData::Constr(Constr { + tag, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![]), + }) +} + pub trait ToPlutusData { fn to_plutus_data(&self) -> PlutusData; } @@ -97,15 +113,7 @@ impl ToPlutusData for Address { any_constructor: None, fields: MaybeIndefArray::Indef(vec![ //addressCredential - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![byron_address - .decode() - .unwrap() - .root - .to_plutus_data()]), - }), + wrap_with_constr(0, byron_address.decode().unwrap().root.to_plutus_data()), //addressStakeCredential None::.to_plutus_data(), ]), @@ -116,16 +124,12 @@ impl ToPlutusData for Address { let stake_part = shelley_address.delegation(); let payment_part_plutus_data = match payment_part { - ShelleyPaymentPart::Key(payment_keyhash) => PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![payment_keyhash.to_plutus_data()]), - }), - ShelleyPaymentPart::Script(script_hash) => PlutusData::Constr(Constr { - tag: 1, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![script_hash.to_plutus_data()]), - }), + 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 { @@ -168,7 +172,7 @@ impl ToPlutusData for TransactionInput { tag: 0, any_constructor: None, fields: MaybeIndefArray::Indef(vec![ - self.transaction_id.to_plutus_data(), + wrap_with_constr(0, self.transaction_id.to_plutus_data()), PlutusData::BigInt(BigInt::Int(self.index.into())), ]), }) @@ -213,29 +217,21 @@ impl ToPlutusData for Option { any_constructor: None, fields: MaybeIndefArray::Indef(vec![]), }), - Some(data) => PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![data.to_plutus_data()]), - }), + Some(data) => wrap_with_constr(0, data.to_plutus_data()), } } } -impl ToPlutusData for DatumOption { +// 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 { - // tag : 0 is NoOutputDatum. Determined after unwrapping Option which is wrapped around DatumOption - DatumOption::Hash(hash) => PlutusData::Constr(Constr { - tag: 1, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![hash.to_plutus_data()]), - }), - DatumOption::Data(data) => PlutusData::Constr(Constr { - tag: 2, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![data.0.clone()]), // does data need an extra wrapper constructor? - }), + 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()), + }, } } } @@ -347,15 +343,7 @@ impl ToPlutusData for TransactionOutput { post_alonzo_output.value.to_plutus_data(), // DatumOption needs to be handled differently a bit. In Haskell it's NoOutputDatum | OutputDatumHash DatumHash | OutputDatum Datum // So we unwrap first to check if it's someting. If it is then turn the unwrapped data to PlutusData, otherwise have None and turn that into PlutusData - if post_alonzo_output.datum_option.is_some() { - post_alonzo_output - .datum_option - .clone() - .unwrap() - .to_plutus_data() - } else { - None::.to_plutus_data() - }, + post_alonzo_output.datum_option.to_plutus_data(), post_alonzo_output.script_ref.to_plutus_data(), ]), }), @@ -369,24 +357,12 @@ impl ToPlutusData for StakeCredential { // So a StakeCredential for a Pointer address needs to be converted separately fn to_plutus_data(&self) -> PlutusData { match self { - StakeCredential::AddrKeyhash(addr_keyhas) => PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![addr_keyhas.to_plutus_data()]), - })]), - }), - StakeCredential::Scripthash(script_hash) => PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![PlutusData::Constr(Constr { - tag: 1, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![script_hash.to_plutus_data()]), - })]), - }), + 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())) + } } } } @@ -483,13 +459,7 @@ impl ToPlutusData for TimeRange { any_constructor: None, fields: MaybeIndefArray::Indef(vec![ // Finite - PlutusData::Constr(Constr { - tag: 1, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - lower_bound.to_plutus_data() - ]), - }), + wrap_with_constr(1, lower_bound.to_plutus_data()), // Closure true.to_plutus_data(), ]), @@ -499,11 +469,7 @@ impl ToPlutusData for TimeRange { any_constructor: None, fields: MaybeIndefArray::Indef(vec![ // PosInf - PlutusData::Constr(Constr { - tag: 2, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![]), - }), + empty_constr(2), // Closure true.to_plutus_data(), ]), @@ -525,11 +491,7 @@ impl ToPlutusData for TimeRange { any_constructor: None, fields: MaybeIndefArray::Indef(vec![ // NegInf - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![]), - }), + empty_constr(0), // Closure true.to_plutus_data(), ]), @@ -539,13 +501,7 @@ impl ToPlutusData for TimeRange { any_constructor: None, fields: MaybeIndefArray::Indef(vec![ // Finite - PlutusData::Constr(Constr { - tag: 1, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - upper_bound.to_plutus_data() - ]), - }), + wrap_with_constr(1, upper_bound.to_plutus_data()), // Closure true.to_plutus_data(), ]), @@ -567,13 +523,7 @@ impl ToPlutusData for TimeRange { any_constructor: None, fields: MaybeIndefArray::Indef(vec![ // Finite - PlutusData::Constr(Constr { - tag: 1, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - lower_bound.to_plutus_data() - ]), - }), + wrap_with_constr(1, lower_bound.to_plutus_data()), // Closure true.to_plutus_data(), ]), @@ -583,13 +533,7 @@ impl ToPlutusData for TimeRange { any_constructor: None, fields: MaybeIndefArray::Indef(vec![ // Finite - PlutusData::Constr(Constr { - tag: 1, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - upper_bound.to_plutus_data() - ]), - }), + wrap_with_constr(1, upper_bound.to_plutus_data()), // Closure false.to_plutus_data(), ]), @@ -611,11 +555,7 @@ impl ToPlutusData for TimeRange { any_constructor: None, fields: MaybeIndefArray::Indef(vec![ // NegInf - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![]), - }), + empty_constr(0), // Closure true.to_plutus_data(), ]), @@ -625,11 +565,7 @@ impl ToPlutusData for TimeRange { any_constructor: None, fields: MaybeIndefArray::Indef(vec![ // PosInf - PlutusData::Constr(Constr { - tag: 2, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![]), - }), + empty_constr(2), // Closure true.to_plutus_data(), ]), @@ -657,26 +593,12 @@ impl ToPlutusData for TxInInfo { impl ToPlutusData for ScriptPurpose { fn to_plutus_data(&self) -> PlutusData { match self { - ScriptPurpose::Minting(policy_id) => PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![policy_id.to_plutus_data()]), - }), - ScriptPurpose::Spending(out_ref) => PlutusData::Constr(Constr { - tag: 1, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![out_ref.to_plutus_data()]), - }), - ScriptPurpose::Rewarding(stake_credential) => PlutusData::Constr(Constr { - tag: 2, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![stake_credential.to_plutus_data()]), - }), - ScriptPurpose::Certifying(dcert) => PlutusData::Constr(Constr { - tag: 3, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![dcert.to_plutus_data()]), - }), + 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()), } } } @@ -720,16 +642,8 @@ impl ToPlutusData for ScriptContext { impl ToPlutusData for bool { fn to_plutus_data(&self) -> PlutusData { match self { - false => PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![]), - }), - true => PlutusData::Constr(Constr { - tag: 1, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![]), - }), + false => empty_constr(0), + true => empty_constr(1), } } } @@ -881,7 +795,7 @@ fn get_script_purpose( RedeemerTag::Cert => { // sort by order given in the tx (just take it as it is basically) let cert = dcert[index as usize].clone(); - Ok(ScriptPurpose::Certifying(cert.clone())) + Ok(ScriptPurpose::Certifying(cert)) } } } @@ -985,18 +899,18 @@ fn get_script_context( // ) -> anyhow::Result { // } -// fn eval_tx( -// tx_bytes: &Vec, -// utxos: &Vec<(Vec, Vec)>, -// cost_model: &Vec, -// zero_time: u64, -// slot_length: u64, -// ) -> anyhow::Result { -// let multi_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) -// .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) -// .or_else(|_| MultiEraTx::decode(Era::Byron, &tx_bytes))?; +fn eval_tx( + tx_bytes: &Vec, + utxos: &Vec<(Vec, Vec)>, + cost_model: &Vec, + zero_time: u64, + slot_length: u64, +) -> anyhow::Result { + let multi_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) + .or_else(|_| MultiEraTx::decode(Era::Byron, &tx_bytes))?; -// let tx = multi_tx.as_babbage().unwrap(); + let tx = multi_tx.as_babbage().unwrap(); -// Ok(true) -// } + Ok(true) +} From 33c2a25fa3b0a391efddc551d21b94f6260036fc Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Sun, 11 Sep 2022 12:58:05 +0200 Subject: [PATCH 22/77] bit more refactoring --- crates/cli/src/utils.rs | 36 +++++++++--------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index 05b42c5c..037451ba 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -212,11 +212,7 @@ impl ToPlutusData for KeyValuePairs { impl ToPlutusData for Option { fn to_plutus_data(&self) -> PlutusData { match self { - None => PlutusData::Constr(Constr { - tag: 1, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![]), - }), + None => empty_constr(1), Some(data) => wrap_with_constr(0, data.to_plutus_data()), } } @@ -341,8 +337,6 @@ impl ToPlutusData for TransactionOutput { .unwrap() .to_plutus_data(), post_alonzo_output.value.to_plutus_data(), - // DatumOption needs to be handled differently a bit. In Haskell it's NoOutputDatum | OutputDatumHash DatumHash | OutputDatum Datum - // So we unwrap first to check if it's someting. If it is then turn the unwrapped data to PlutusData, otherwise have None and turn that into PlutusData post_alonzo_output.datum_option.to_plutus_data(), post_alonzo_output.script_ref.to_plutus_data(), ]), @@ -370,16 +364,12 @@ impl ToPlutusData for StakeCredential { impl ToPlutusData for Certificate { fn to_plutus_data(&self) -> PlutusData { match self { - Certificate::StakeRegistration(stake_credential) => PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![stake_credential.to_plutus_data()]), - }), - Certificate::StakeDeregistration(stake_credential) => PlutusData::Constr(Constr { - tag: 1, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![stake_credential.to_plutus_data()]), - }), + 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) => { PlutusData::Constr(Constr { tag: 2, @@ -416,16 +406,8 @@ impl ToPlutusData for Certificate { epoch.to_plutus_data(), ]), }), - Certificate::GenesisKeyDelegation(_, _, _) => PlutusData::Constr(Constr { - tag: 5, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![]), - }), - Certificate::MoveInstantaneousRewardsCert(_) => PlutusData::Constr(Constr { - tag: 6, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![]), - }), + Certificate::GenesisKeyDelegation(_, _, _) => empty_constr(5), + Certificate::MoveInstantaneousRewardsCert(_) => empty_constr(6), } } } From 080985b7c6624f7f99b192cebfacbe56b4783147 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Sun, 11 Sep 2022 13:29:10 +0200 Subject: [PATCH 23/77] more bug fixes --- crates/cli/src/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index 037451ba..8b798fcb 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -602,7 +602,7 @@ impl ToPlutusData for TxInfo { self.signatories.to_plutus_data(), self.redeemers.to_plutus_data(), self.data.to_plutus_data(), - self.id.to_plutus_data(), + wrap_with_constr(0, self.id.to_plutus_data()), ]), }) } From b1b9d3a5d40c6d7fbc39023948d6bf24f979937a Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Sun, 11 Sep 2022 14:16:53 +0200 Subject: [PATCH 24/77] preliminary work on execution --- crates/cli/src/utils.rs | 133 +++++++++++++++++++++++++--------------- 1 file changed, 85 insertions(+), 48 deletions(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index 8b798fcb..29426ad3 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -698,6 +698,12 @@ fn slot_range_to_posix_time_range(slot_range: TimeRange, sc: &SlotConfig) -> Tim } } +#[derive(Debug, PartialEq, Clone)] +enum ExecutionPurpose { + WithDatum(Language, PlutusData), // Spending + NoDatum(Language), // Minting, Wdrl, DCert +} + fn get_tx_in_info( inputs: &MaybeIndefArray, utxos: &MaybeIndefArray, @@ -713,10 +719,10 @@ fn get_tx_in_info( fn get_script_purpose( redeemer: &Redeemer, - inputs: &MaybeIndefArray, - mint: &Mint, - dcert: &MaybeIndefArray, - wdrl: &Withdrawals, + inputs: &MaybeIndefArray, + mint: &Option, + dcert: &Option>, + wdrl: &Option, ) -> anyhow::Result { // sorting according to specs section 4.1: https://hydra.iohk.io/build/18583827/download/1/alonzo-changes.pdf let tag = redeemer.tag.clone(); @@ -725,10 +731,11 @@ fn get_script_purpose( RedeemerTag::Mint => { // sort lexical by policy id let mut policy_ids = mint + .as_ref() + .unwrap() .iter() - .map(|(policy_id, _)| policy_id) - .collect::>() - .clone(); + .map(|(policy_id, _)| policy_id.clone()) + .collect::>(); policy_ids.sort(); let policy_id = policy_ids[index as usize].clone(); Ok(ScriptPurpose::Minting(policy_id)) @@ -737,9 +744,8 @@ fn get_script_purpose( // sort lexical by tx_hash and index let mut inputs = inputs .iter() - .map(|input| input.out_ref.clone()) - .collect::>() - .clone(); + .map(|input| input.clone()) + .collect::>(); // is this correct? Does this sort lexical from low to high? maybe get Ordering into pallas for TransactionInput? inputs.sort_by( |i_a, i_b| match i_a.transaction_id.cmp(&i_b.transaction_id) { @@ -754,12 +760,13 @@ fn get_script_purpose( RedeemerTag::Reward => { // sort lexical by reward account let mut reward_accounts = wdrl + .as_ref() + .unwrap() .iter() - .map(|(policy_id, _)| policy_id) - .collect::>() - .clone(); + .map(|(policy_id, _)| policy_id.clone()) + .collect::>(); reward_accounts.sort(); - let reward_account = reward_accounts[index as usize]; + let reward_account = reward_accounts[index as usize].clone(); let addresss = Address::from_bytes(&reward_account)?; let credential = match addresss { Address::Stake(stake_address) => match stake_address.payload() { @@ -776,7 +783,7 @@ fn get_script_purpose( } RedeemerTag::Cert => { // sort by order given in the tx (just take it as it is basically) - let cert = dcert[index as usize].clone(); + let cert = dcert.as_ref().unwrap()[index as usize].clone(); Ok(ScriptPurpose::Certifying(cert)) } } @@ -819,7 +826,14 @@ fn get_tx_info( .iter() .map(|r| { ( - get_script_purpose(&r, &inputs, &mint, &dcert, &wdrl).unwrap(), + get_script_purpose( + &r, + &tx.transaction_body.inputs, + &tx.transaction_body.mint, + &tx.transaction_body.certificates, + &tx.transaction_body.withdrawals, + ) + .unwrap(), r.clone(), ) }) @@ -852,47 +866,70 @@ fn get_tx_info( }) } -fn get_script_context( +fn get_execution_purpose( + tx: &Tx, + utxos: &MaybeIndefArray, + script_purpose: &ScriptPurpose, +) -> ExecutionPurpose { + todo!() +} + +fn eval_redeemer( tx: &Tx, utxos: &MaybeIndefArray, slot_config: &SlotConfig, redeemer: &Redeemer, -) -> anyhow::Result { - let tx_info = get_tx_info(tx, utxos, slot_config)?; +) -> anyhow::Result { let purpose = get_script_purpose( redeemer, - &tx_info.inputs, - &tx_info.mint, - &tx_info.dcert, - &tx_info.wdrl, + &tx.transaction_body.inputs, + &tx.transaction_body.mint, + &tx.transaction_body.certificates, + &tx.transaction_body.withdrawals, )?; - Ok(ScriptContext { tx_info, purpose }) + + let execution_purpose: ExecutionPurpose = get_execution_purpose(&tx, &utxos, &purpose); + + match execution_purpose { + ExecutionPurpose::WithDatum(language, datum) => match language { + Language::PlutusV1 => todo!(), + Language::PlutusV2 => { + let tx_info = get_tx_info(tx, utxos, slot_config)?; + let script_context = ScriptContext { tx_info, purpose }; + + // TODO: eval programm + + Ok(redeemer.clone()) + } + }, + ExecutionPurpose::NoDatum(language) => match language { + Language::PlutusV1 => todo!(), + Language::PlutusV2 => { + let tx_info = get_tx_info(tx, utxos, slot_config)?; + let script_context = ScriptContext { tx_info, purpose }; + + // TODO: eval programm + + Ok(redeemer.clone()) + } + }, + } } // TODO: Maybe make ToPlutusData dependent on a Plutus Language so it works for V1 and V2? -// fn eval_single_redeemer( -// redeemer: &Redeemer, -// tx: &Tx, -// utxos: &Vec<(TransactionInput, TransactionOutput)>, -// cost_model: &CostModel, -// slot_config: &SlotConfig, -// language: &Language, -// ) -> anyhow::Result { +// fn eval_tx( +// tx_bytes: &Vec, +// utxos: &Vec<(Vec, Vec)>, +// cost_model: &Vec, +// zero_time: u64, +// slot_length: u64, +// ) -> anyhow::Result { +// let multi_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) +// .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) +// .or_else(|_| MultiEraTx::decode(Era::Byron, &tx_bytes))?; + +// let tx = multi_tx.as_babbage().unwrap(); + +// Ok(true) // } - -fn eval_tx( - tx_bytes: &Vec, - utxos: &Vec<(Vec, Vec)>, - cost_model: &Vec, - zero_time: u64, - slot_length: u64, -) -> anyhow::Result { - let multi_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) - .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) - .or_else(|_| MultiEraTx::decode(Era::Byron, &tx_bytes))?; - - let tx = multi_tx.as_babbage().unwrap(); - - Ok(true) -} From 76d326b9accdb0fd2c9c5ae5aab5bfa79ce7d487 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Sun, 11 Sep 2022 17:46:07 +0200 Subject: [PATCH 25/77] completed execution part --- crates/cli/src/utils.rs | 304 +++++++++++++++++++++++++++++++++++----- 1 file changed, 268 insertions(+), 36 deletions(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index 29426ad3..994cf7e8 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -1,4 +1,6 @@ -use pallas_addresses::{Address, ShelleyDelegationPart, ShelleyPaymentPart, StakePayload}; +use pallas_addresses::{ + Address, ScriptHash, ShelleyDelegationPart, ShelleyPaymentPart, StakePayload, +}; use pallas_codec::{ minicbor::{bytes::ByteVec, data::Int}, utils::{AnyUInt, KeyValuePairs, MaybeIndefArray}, @@ -7,14 +9,23 @@ use pallas_crypto::hash::Hash; use pallas_primitives::{ babbage::{ AddrKeyhash, AssetName, BigInt, Certificate, Constr, CostModel, DatumHash, DatumOption, - Language, Mint, PolicyId, Redeemer, RedeemerTag, RewardAccount, Script, ScriptRef, - StakeCredential, TransactionInput, TransactionOutput, Tx, Value, Withdrawals, + ExUnits, Language, Mint, PlutusV1Script, PlutusV2Script, PolicyId, Redeemer, RedeemerTag, + RewardAccount, Script, ScriptRef, StakeCredential, TransactionInput, TransactionOutput, Tx, + Value, Withdrawals, }, ToHash, }; use pallas_traverse::{Era, MultiEraTx}; -use std::{str::FromStr, vec}; -use uplc::PlutusData; +use std::{ + collections::HashMap, + convert::{TryFrom, TryInto}, + str::FromStr, + vec, +}; +use uplc::{ + ast::{FakeNamedDeBruijn, NamedDeBruijn, Program}, + PlutusData, +}; use crate::args::ResolvedInput; @@ -698,10 +709,21 @@ fn slot_range_to_posix_time_range(slot_range: TimeRange, sc: &SlotConfig) -> Tim } } +#[derive(Debug, PartialEq, Clone)] +enum ScriptVersion { + PlutusV1(PlutusV1Script), + PlutusV2(PlutusV2Script), +} + #[derive(Debug, PartialEq, Clone)] enum ExecutionPurpose { - WithDatum(Language, PlutusData), // Spending - NoDatum(Language), // Minting, Wdrl, DCert + WithDatum(ScriptVersion, PlutusData), // Spending + NoDatum(ScriptVersion), // Minting, Wdrl, DCert +} + +struct DataLookupTable { + datum: HashMap, + scripts: HashMap, } fn get_tx_in_info( @@ -867,11 +889,161 @@ fn get_tx_info( } fn get_execution_purpose( - tx: &Tx, utxos: &MaybeIndefArray, script_purpose: &ScriptPurpose, + lookup_table: &DataLookupTable, ) -> ExecutionPurpose { - todo!() + 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 = lookup_table.scripts.get(&hash).unwrap(); + ExecutionPurpose::NoDatum(script.clone()) + } + ScriptPurpose::Spending(out_ref) => { + let utxo = utxos.iter().find(|utxo| utxo.out_ref == *out_ref).unwrap(); + match &utxo.resolved { + TransactionOutput::Legacy(output) => { + let address = Address::from_bytes(&output.address).unwrap(); + match address { + Address::Shelley(shelley_address) => { + let script = lookup_table + .scripts + .get(&shelley_address.payment().as_hash()) + .unwrap(); + + let datum = + lookup_table.datum.get(&output.datum_hash.unwrap()).unwrap(); + ExecutionPurpose::WithDatum(script.clone(), datum.clone()) + } + _ => unreachable!(), + } + } + TransactionOutput::PostAlonzo(output) => { + let address = Address::from_bytes(&output.address).unwrap(); + match address { + Address::Shelley(shelley_address) => { + let script = lookup_table + .scripts + .get(&shelley_address.payment().as_hash()) + .unwrap(); + + let datum = match &output.datum_option { + Some(DatumOption::Hash(hash)) => { + lookup_table.datum.get(&hash).unwrap().clone() + } + Some(DatumOption::Data(data)) => data.0.clone(), + _ => unreachable!(), + }; + + ExecutionPurpose::WithDatum(script.clone(), datum) + } + _ => unreachable!(), + } + } + } + } + ScriptPurpose::Rewarding(stake_credential) => { + let script_hash = match stake_credential { + StakeCredential::Scripthash(hash) => hash.clone(), + _ => unreachable!(), + }; + let script = lookup_table.scripts.get(&script_hash).unwrap(); + ExecutionPurpose::NoDatum(script.clone()) + } + ScriptPurpose::Certifying(cert) => match cert { + // StakeRegistration doesn't require a witness from a stake key/script. So I assume it doesn't need to be handled in Plutus either? + + // Certificate::StakeRegistration(stake_credential) => { + // let script_hash = match stake_credential { + // StakeCredential::Scripthash(hash) => hash.clone(), + // _ => unreachable!(), + // }; + // let script = lookup_table.scripts.get(&script_hash).unwrap(); + // ExecutionPurpose::NoDatum(script.clone()) + // } + Certificate::StakeDeregistration(stake_credential) => { + let script_hash = match stake_credential { + StakeCredential::Scripthash(hash) => hash.clone(), + _ => unreachable!(), + }; + let script = lookup_table.scripts.get(&script_hash).unwrap(); + ExecutionPurpose::NoDatum(script.clone()) + } + Certificate::StakeDelegation(stake_credential, _) => { + let script_hash = match stake_credential { + StakeCredential::Scripthash(hash) => hash.clone(), + _ => unreachable!(), + }; + let script = lookup_table.scripts.get(&script_hash).unwrap(); + ExecutionPurpose::NoDatum(script.clone()) + } + _ => unreachable!(), + }, + } +} + +fn get_script_and_datum_lookup_table( + tx: &Tx, + utxos: &MaybeIndefArray, +) -> 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(MaybeIndefArray::Indef(vec![])); + + let scripts_v1_witnesses = tx + .transaction_witness_set + .plutus_v1_script + .clone() + .unwrap_or(MaybeIndefArray::Indef(vec![])); + + let scripts_v2_witnesses = tx + .transaction_witness_set + .plutus_v2_script + .clone() + .unwrap_or(MaybeIndefArray::Indef(vec![])); + + for plutus_data in plutus_data_witnesses.iter() { + datum.insert(plutus_data.to_hash(), plutus_data.clone()); + } + + for script in scripts_v1_witnesses.iter() { + scripts.insert(script.to_hash(), ScriptVersion::PlutusV1(script.clone())); + } + + for script in scripts_v2_witnesses.iter() { + scripts.insert(script.to_hash(), ScriptVersion::PlutusV2(script.clone())); + } + + // discovery in utxos (script ref) + + for utxo in utxos.iter() { + match &utxo.resolved { + TransactionOutput::Legacy(_) => {} + TransactionOutput::PostAlonzo(output) => match &output.script_ref { + Some(script) => match &script.0 { + Script::PlutusV1Script(v1) => { + scripts.insert(v1.to_hash(), ScriptVersion::PlutusV1(v1.clone())); + } + Script::PlutusV2Script(v2) => { + scripts.insert(v2.to_hash(), ScriptVersion::PlutusV2(v2.clone())); + } + _ => {} + }, + _ => {} + }, + } + } + + DataLookupTable { datum, scripts } } fn eval_redeemer( @@ -879,6 +1051,7 @@ fn eval_redeemer( utxos: &MaybeIndefArray, slot_config: &SlotConfig, redeemer: &Redeemer, + lookup_table: &DataLookupTable, ) -> anyhow::Result { let purpose = get_script_purpose( redeemer, @@ -888,48 +1061,107 @@ fn eval_redeemer( &tx.transaction_body.withdrawals, )?; - let execution_purpose: ExecutionPurpose = get_execution_purpose(&tx, &utxos, &purpose); + let execution_purpose: ExecutionPurpose = get_execution_purpose(utxos, &purpose, lookup_table); match execution_purpose { - ExecutionPurpose::WithDatum(language, datum) => match language { - Language::PlutusV1 => todo!(), - Language::PlutusV2 => { + ExecutionPurpose::WithDatum(script_version, datum) => match script_version { + ScriptVersion::PlutusV1(script) => todo!(), + ScriptVersion::PlutusV2(script) => { let tx_info = get_tx_info(tx, utxos, slot_config)?; let script_context = ScriptContext { tx_info, purpose }; - // TODO: eval programm + let program: Program = { + let mut buffer = Vec::new(); - Ok(redeemer.clone()) + let prog = Program::::from_cbor(&script.0, &mut buffer)?; + + prog.into() + }; + + let result = program + .apply_data(datum.clone()) + .apply_data(redeemer.data.clone()) + .apply_data(script_context.to_plutus_data()) + .eval(); + + result.0.unwrap(); + + let new_redeemer = Redeemer { + tag: redeemer.tag.clone(), + index: redeemer.index, + data: redeemer.data.clone(), + ex_units: ExUnits { + mem: result.1.mem as u32, + steps: result.1.cpu as u64, + }, + }; + + Ok(new_redeemer) } }, - ExecutionPurpose::NoDatum(language) => match language { - Language::PlutusV1 => todo!(), - Language::PlutusV2 => { + ExecutionPurpose::NoDatum(script_version) => match script_version { + ScriptVersion::PlutusV1(script) => todo!(), + ScriptVersion::PlutusV2(script) => { let tx_info = get_tx_info(tx, utxos, slot_config)?; let script_context = ScriptContext { tx_info, purpose }; - // TODO: eval programm + let program: Program = { + let mut buffer = Vec::new(); - Ok(redeemer.clone()) + let prog = Program::::from_cbor(&script.0, &mut buffer)?; + + prog.into() + }; + + let result = program + .apply_data(redeemer.data.clone()) + .apply_data(script_context.to_plutus_data()) + .eval(); + + result.0.unwrap(); + + let new_redeemer = Redeemer { + tag: redeemer.tag.clone(), + index: redeemer.index, + data: redeemer.data.clone(), + ex_units: ExUnits { + mem: result.1.mem as u32, + steps: result.1.cpu as u64, + }, + }; + + Ok(new_redeemer) } }, } } +fn eval_tx( + tx: &Tx, + utxos: &MaybeIndefArray, + //TODO: costMdls + slot_config: &SlotConfig, +) -> anyhow::Result> { + let redeemers = tx.transaction_witness_set.redeemer.as_ref(); + + let lookup_table = get_script_and_datum_lookup_table(tx, utxos); + + match redeemers { + Some(rs) => { + let mut collected_redeemers = vec![]; + for redeemer in rs.iter() { + collected_redeemers.push(eval_redeemer( + tx, + utxos, + slot_config, + &redeemer, + &lookup_table, + )?) + } + Ok(MaybeIndefArray::Indef(collected_redeemers)) + } + None => Ok(MaybeIndefArray::Indef(vec![])), + } +} + // TODO: Maybe make ToPlutusData dependent on a Plutus Language so it works for V1 and V2? - -// fn eval_tx( -// tx_bytes: &Vec, -// utxos: &Vec<(Vec, Vec)>, -// cost_model: &Vec, -// zero_time: u64, -// slot_length: u64, -// ) -> anyhow::Result { -// let multi_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) -// .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) -// .or_else(|_| MultiEraTx::decode(Era::Byron, &tx_bytes))?; - -// let tx = multi_tx.as_babbage().unwrap(); - -// Ok(true) -// } From fa6520e58597c054ff102b8c6fb6834b4292fa9c Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Mon, 12 Sep 2022 00:10:02 +0200 Subject: [PATCH 26/77] added full evaluation; still fails because hashes mismatch regarding pallas --- Cargo.lock | 6 +-- crates/cli/src/utils.rs | 111 ++++++++++++++++++++++++++++++++-------- 2 files changed, 92 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 923a49aa..30f2f9f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,9 +62,9 @@ checksum = "cf9ff0bbfd639f15c74af777d81383cf53efb7c93613f6cab67c6c11e05bbf8b" [[package]] name = "bech32" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5738be7561b0eeb501ef1d5c5db3f24e01ceb55fededd9b00039aada34966ad" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" [[package]] name = "bit-set" @@ -357,7 +357,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24f8d900bac04b711d2c4b21e906680224efd02d312a87ce25e688595b91d1fc" dependencies = [ "base58", - "bech32 0.9.0", + "bech32 0.9.1", "hex", "log", "pallas-codec", diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index 994cf7e8..de1cf659 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -5,13 +5,13 @@ use pallas_codec::{ minicbor::{bytes::ByteVec, data::Int}, utils::{AnyUInt, KeyValuePairs, MaybeIndefArray}, }; -use pallas_crypto::hash::Hash; +use pallas_crypto::hash::{Hash, Hasher}; use pallas_primitives::{ babbage::{ AddrKeyhash, AssetName, BigInt, Certificate, Constr, CostModel, DatumHash, DatumOption, - ExUnits, Language, Mint, PlutusV1Script, PlutusV2Script, PolicyId, Redeemer, RedeemerTag, - RewardAccount, Script, ScriptRef, StakeCredential, TransactionInput, TransactionOutput, Tx, - Value, Withdrawals, + ExUnits, Language, Mint, MintedTx, PlutusV1Script, PlutusV2Script, PolicyId, Redeemer, + RedeemerTag, RewardAccount, Script, ScriptRef, StakeCredential, TransactionInput, + TransactionOutput, Tx, Value, Withdrawals, }, ToHash, }; @@ -812,7 +812,7 @@ fn get_script_purpose( } fn get_tx_info( - tx: &Tx, + tx: &MintedTx, utxos: &MaybeIndefArray, slot_config: &SlotConfig, ) -> anyhow::Result { @@ -822,14 +822,21 @@ fn get_tx_info( let reference_inputs = get_tx_in_info( &body .reference_inputs + .clone() .unwrap_or(MaybeIndefArray::Indef(vec![])), &utxos, )?; - let outputs = body.outputs; + let outputs = body.outputs.clone(); let fee = Value::Coin(AnyUInt::U64(body.fee)); - let mint = body.mint.unwrap_or(KeyValuePairs::Indef(vec![])); - let dcert = body.certificates.unwrap_or(MaybeIndefArray::Indef(vec![])); - let wdrl = body.withdrawals.unwrap_or(KeyValuePairs::Indef(vec![])); + let mint = body.mint.clone().unwrap_or(KeyValuePairs::Indef(vec![])); + let dcert = body + .certificates + .clone() + .unwrap_or(MaybeIndefArray::Indef(vec![])); + 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, @@ -839,6 +846,7 @@ fn get_tx_info( ); let signatories = body .required_signers + .clone() .unwrap_or(MaybeIndefArray::Indef(vec![])); let redeemers = KeyValuePairs::Indef( tx.transaction_witness_set @@ -913,6 +921,18 @@ fn get_execution_purpose( .get(&shelley_address.payment().as_hash()) .unwrap(); + // let arr: [u8; 28] = hex::decode( + // "8f1d2219046d3f8ff710f0976160b0de191321ef76f7655e9644b59a", + // ) + // .unwrap() + // .to_vec() + // .try_into() + // .unwrap(); + + // let hash = Hash::from(arr); + + // let script = lookup_table.scripts.get(&hash).unwrap(); + let datum = lookup_table.datum.get(&output.datum_hash.unwrap()).unwrap(); ExecutionPurpose::WithDatum(script.clone(), datum.clone()) @@ -954,15 +974,6 @@ fn get_execution_purpose( } ScriptPurpose::Certifying(cert) => match cert { // StakeRegistration doesn't require a witness from a stake key/script. So I assume it doesn't need to be handled in Plutus either? - - // Certificate::StakeRegistration(stake_credential) => { - // let script_hash = match stake_credential { - // StakeCredential::Scripthash(hash) => hash.clone(), - // _ => unreachable!(), - // }; - // let script = lookup_table.scripts.get(&script_hash).unwrap(); - // ExecutionPurpose::NoDatum(script.clone()) - // } Certificate::StakeDeregistration(stake_credential) => { let script_hash = match stake_credential { StakeCredential::Scripthash(hash) => hash.clone(), @@ -985,7 +996,7 @@ fn get_execution_purpose( } fn get_script_and_datum_lookup_table( - tx: &Tx, + tx: &MintedTx, utxos: &MaybeIndefArray, ) -> DataLookupTable { let mut datum = HashMap::new(); @@ -1047,7 +1058,7 @@ fn get_script_and_datum_lookup_table( } fn eval_redeemer( - tx: &Tx, + tx: &MintedTx, utxos: &MaybeIndefArray, slot_config: &SlotConfig, redeemer: &Redeemer, @@ -1137,7 +1148,7 @@ fn eval_redeemer( } fn eval_tx( - tx: &Tx, + tx: &MintedTx, utxos: &MaybeIndefArray, //TODO: costMdls slot_config: &SlotConfig, @@ -1164,4 +1175,60 @@ fn eval_tx( } } -// TODO: Maybe make ToPlutusData dependent on a Plutus Language so it works for V1 and V2? +#[cfg(test)] +mod tests { + use pallas_codec::utils::MaybeIndefArray; + use pallas_primitives::{ + babbage::{ + LegacyTransactionOutput, TransactionBody, TransactionInput, TransactionOutput, Tx, + }, + Fragment, + }; + use pallas_traverse::{Era, MultiEraTx}; + + use super::{eval_tx, SlotConfig, TxInInfo}; + + #[test] + fn test_eval() { + // Plutus Core (Haskell): (1100, 230100) Ex Units + // What does the script look like: + // validate :: () -> () -> () -> Bool + // validate = True + + let tx_bytes = hex::decode("84a70082825820ee9ae4e9102e5e8a725ca5e0bd8ee1f4e9d7d8a86195cb180334c15a6265a3ad00825820975c17a4fed0051be622328efa548e206657d2b65a19224bf6ff8132571e6a5002018182581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a00acd8c6021a0002a11a0b582010f5361f3d433170f55a62bc6c3b423b00c65dea95204c985b865b995f0b921f0d818258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d011082581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af10c70d111a0003f1a7a4008182582031ae74f8058527afb305d7495b10a99422d9337fc199e1f28044f2c477a0f94658402e38452ee99c0a080e5804156a7be45ef83b0eb361486a9f8eedeb07e499ccd0865f56620db297f831ab60b93490ee1426e0b7d42417ecb0b793c9f915c58c01049fd87980ff0581840001d879808219044c1a000382d4068149480100002221200101f5f6").unwrap(); + + let raw_inputs = hex::decode("86825820ee9ae4e9102e5e8a725ca5e0bd8ee1f4e9d7d8a86195cb180334c15a6265a3ad00825820d64cb7f6e13b0f9539d5505d368210034b8a3a2df8086382bd66dfb3a6e6a78601825820b16778c9cf065d9efeefe37ec269b4fc5107ecdbd0dd6bf3274b224165c2edd9008258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d01825820b810e77e706ccaebf7284f6c3d41e2a1eb4af5fabae452618f4175ad1b2aaded02825820975c17a4fed0051be622328efa548e206657d2b65a19224bf6ff8132571e6a5002").unwrap(); + let raw_outputs = hex::decode("8683581d703a888d65f16790950a72daee1f63aa05add6d268434107cfa5b677121a0016e3605820923918e403bf43c34b4ef6b48eb2ee04babed17320d8d1b9ff9ad086e86f44eca400581d703a888d65f16790950a72daee1f63aa05add6d268434107cfa5b67712011a0010b4540282005820923918e403bf43c34b4ef6b48eb2ee04babed17320d8d1b9ff9ad086e86f44ec03d8184c82024948010000222120010182581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f67235821a000f8548a1581c15be994a64bdb79dde7fe080d8e7ff81b33a9e4860e9ee0d857a8e85a144576177610182581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af14b8b482581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a0098968082581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a00989680").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)| TxInInfo { + out_ref: input.clone(), + resolved: output.clone(), + }) + .collect(), + ); + + let slot_config = SlotConfig { + zero_time: 1660003200000, // Preview network + slot_length: 1000, + }; + + 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_tx(&tx, &utxos, &slot_config).unwrap(); + + println!("{:?}", redeemers.len()); + } + _ => unreachable!(), + }; + } +} From eb1466d269e3682c9d333056557798342814c470 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Mon, 12 Sep 2022 00:10:58 +0200 Subject: [PATCH 27/77] fixed budget calc --- crates/cli/src/utils.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index de1cf659..ea480bdf 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -24,6 +24,7 @@ use std::{ }; use uplc::{ ast::{FakeNamedDeBruijn, NamedDeBruijn, Program}, + machine::cost_model::ExBudget, PlutusData, }; @@ -1102,8 +1103,8 @@ fn eval_redeemer( index: redeemer.index, data: redeemer.data.clone(), ex_units: ExUnits { - mem: result.1.mem as u32, - steps: result.1.cpu as u64, + mem: (ExBudget::default().mem - result.1.mem) as u32, + steps: (ExBudget::default().cpu - result.1.cpu) as u64, }, }; From 61bf55e1519cba1b00399099ff7353d12210bc41 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Mon, 12 Sep 2022 15:20:56 +0200 Subject: [PATCH 28/77] added example --- crates/cli/src/utils.rs | 83 +++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index ea480bdf..d79fd7f3 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -13,7 +13,7 @@ use pallas_primitives::{ RedeemerTag, RewardAccount, Script, ScriptRef, StakeCredential, TransactionInput, TransactionOutput, Tx, Value, Withdrawals, }, - ToHash, + Fragment, ToHash, }; use pallas_traverse::{Era, MultiEraTx}; use std::{ @@ -922,20 +922,12 @@ fn get_execution_purpose( .get(&shelley_address.payment().as_hash()) .unwrap(); - // let arr: [u8; 28] = hex::decode( - // "8f1d2219046d3f8ff710f0976160b0de191321ef76f7655e9644b59a", - // ) - // .unwrap() - // .to_vec() - // .try_into() - // .unwrap(); - - // let hash = Hash::from(arr); - - // let script = lookup_table.scripts.get(&hash).unwrap(); - let datum = lookup_table.datum.get(&output.datum_hash.unwrap()).unwrap(); + + let a = hex::encode(datum.encode_fragment().unwrap()); + + println!("{:?}", a); ExecutionPurpose::WithDatum(script.clone(), datum.clone()) } _ => unreachable!(), @@ -1028,11 +1020,23 @@ fn get_script_and_datum_lookup_table( } for script in scripts_v1_witnesses.iter() { - scripts.insert(script.to_hash(), ScriptVersion::PlutusV1(script.clone())); + // scripts.insert(script.to_hash(), ScriptVersion::PlutusV1(script.clone())); // TODO: fix hashing bug in pallas + + let mut prefixed_script: Vec = vec![0x01]; + prefixed_script.extend(script.0.iter()); + + let hash = Hasher::<224>::hash(&prefixed_script); + scripts.insert(hash, ScriptVersion::PlutusV1(script.clone())); } for script in scripts_v2_witnesses.iter() { - scripts.insert(script.to_hash(), ScriptVersion::PlutusV2(script.clone())); + // scripts.insert(script.to_hash(), ScriptVersion::PlutusV2(script.clone())); // TODO: fix hashing bug in pallas + + let mut prefixed_script: Vec = vec![0x02]; + prefixed_script.extend(script.0.iter()); + + let hash = Hasher::<224>::hash(&prefixed_script); + scripts.insert(hash, ScriptVersion::PlutusV2(script.clone())); } // discovery in utxos (script ref) @@ -1043,10 +1047,21 @@ fn get_script_and_datum_lookup_table( TransactionOutput::PostAlonzo(output) => match &output.script_ref { Some(script) => match &script.0 { Script::PlutusV1Script(v1) => { - scripts.insert(v1.to_hash(), ScriptVersion::PlutusV1(v1.clone())); + // scripts.insert(v1.to_hash(), ScriptVersion::PlutusV1(v1.clone())); // TODO: fix hashing bug in pallas + let mut prefixed_script: Vec = vec![0x01]; + prefixed_script.extend(v1.0.iter()); + + let hash = Hasher::<224>::hash(&prefixed_script); + scripts.insert(hash, ScriptVersion::PlutusV1(v1.clone())); } Script::PlutusV2Script(v2) => { - scripts.insert(v2.to_hash(), ScriptVersion::PlutusV2(v2.clone())); + // scripts.insert(v2.to_hash(), ScriptVersion::PlutusV2(v2.clone())); // TODO: fix hashing bug in pallas + + let mut prefixed_script: Vec = vec![0x02]; + prefixed_script.extend(v2.0.iter()); + + let hash = Hasher::<224>::hash(&prefixed_script); + scripts.insert(hash, ScriptVersion::PlutusV2(v2.clone())); } _ => {} }, @@ -1130,15 +1145,25 @@ fn eval_redeemer( .apply_data(script_context.to_plutus_data()) .eval(); - result.0.unwrap(); + match result.0 { + Ok(_) => {} + Err(err) => { + println!("ERROR: {:?}", err.to_string()) + } + } + + println!("MEM: {:?}", ExBudget::default().mem - result.1.mem); + println!("STEP: {:?}", ExBudget::default().cpu - result.1.cpu); + + // result.0.unwrap(); let new_redeemer = Redeemer { tag: redeemer.tag.clone(), index: redeemer.index, data: redeemer.data.clone(), ex_units: ExUnits { - mem: result.1.mem as u32, - steps: result.1.cpu as u64, + mem: (ExBudget::default().mem - result.1.mem) as u32, + steps: (ExBudget::default().cpu - result.1.cpu) as u64, }, }; @@ -1180,26 +1205,20 @@ fn eval_tx( mod tests { use pallas_codec::utils::MaybeIndefArray; use pallas_primitives::{ - babbage::{ - LegacyTransactionOutput, TransactionBody, TransactionInput, TransactionOutput, Tx, - }, - Fragment, + babbage::{TransactionInput, TransactionOutput}, + Fragment, ToHash, }; use pallas_traverse::{Era, MultiEraTx}; + use uplc::PlutusData; use super::{eval_tx, SlotConfig, TxInInfo}; #[test] fn test_eval() { - // Plutus Core (Haskell): (1100, 230100) Ex Units - // What does the script look like: - // validate :: () -> () -> () -> Bool - // validate = True + let tx_bytes = hex::decode("84a80081825820975c17a4fed0051be622328efa548e206657d2b65a19224bf6ff8132571e6a5002018282581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f67235821a000f41f0a1581cc4f241450001af08f3ddbaf9335db79883cbcd81071b8e3508de3055a1400a82581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a0084192f021a00053b6109a1581cc4f241450001af08f3ddbaf9335db79883cbcd81071b8e3508de3055a1400a0b5820b4f96b0acec8beff2adededa8ba317bcac92174f0f65ccefe569b9a6aac7375a0d818258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d011082581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af0cdfa2111a0007d912a3008182582031ae74f8058527afb305d7495b10a99422d9337fc199e1f28044f2c477a0f9465840b8b97b7c3b4e19ecfc2fcd9884ee53a35887ee6e4d36901b9ecbac3fe032d7e8a4358305afa573a86396e378255651ed03501906e9def450e588d4bb36f42a050581840100d87980821a000b68081a0cf3a5bf06815909b25909af010000323322323232323232323232323232323232323232332232323232323232323233223232223232533533223233025323233355300f1200135028502623500122333553012120013502b50292350012233350012330314800000488cc0c80080048cc0c400520000013355300e1200123500122335501c0023335001233553012120012350012233550200023550140010012233355500f0150020012335530121200123500122335502000235501300100133355500a01000200130105002300f5001533532350012222222222220045001102a2216135001220023333573466e1cd55ce9baa0044800080808c98c8080cd5ce01081000f1999ab9a3370e6aae7540092000233221233001003002323232323232323232323232323333573466e1cd55cea8062400046666666666664444444444442466666666666600201a01801601401201000e00c00a00800600466a03803a6ae854030cd4070074d5d0a80599a80e00f1aba1500a3335502075ca03e6ae854024ccd54081d7280f9aba1500833501c02835742a00e666aa040052eb4d5d0a8031919191999ab9a3370e6aae75400920002332212330010030023232323333573466e1cd55cea8012400046644246600200600466a066eb4d5d0a801181a1aba135744a004464c6406c66ae700dc0d80d04d55cf280089baa00135742a0046464646666ae68cdc39aab9d5002480008cc8848cc00400c008cd40cdd69aba150023034357426ae8940088c98c80d8cd5ce01b81b01a09aab9e5001137540026ae84d5d1280111931901919ab9c033032030135573ca00226ea8004d5d0a80299a80e3ae35742a008666aa04004a40026ae85400cccd54081d710009aba150023027357426ae8940088c98c80b8cd5ce01781701609aba25001135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba150023017357426ae8940088c98c8080cd5ce01081000f080f89931900f99ab9c4901035054350001f135573ca00226ea8004444888ccd54c010480054040cd54c01c480048d400488cd54054008d54024004ccd54c0104800488d4008894cd4ccd54c03048004c8cd409c88ccd400c88008008004d40048800448cc004894cd400840b040040a48d400488cc028008014018400c4cd405001000d4044004cd54c01c480048d400488c8cd5405800cc004014c8004d540a4894cd40044d5402800c884d4008894cd4cc03000802044888cc0080280104c01800c008c8004d5408888448894cd40044008884cc014008ccd54c01c480040140100044484888c00c0104484888c004010c8004d5407c8844894cd400454038884cd403cc010008cd54c01848004010004c8004d5407888448894cd40044d400c88004884ccd401488008c010008ccd54c01c4800401401000488ccd5cd19b8f00200101e01d2350012222222222220091232230023758002640026aa038446666aae7c004940288cd4024c010d5d080118019aba2002015232323333573466e1cd55cea80124000466442466002006004601a6ae854008c014d5d09aba2500223263201533573802c02a02626aae7940044dd50009191919191999ab9a3370e6aae75401120002333322221233330010050040030023232323333573466e1cd55cea80124000466442466002006004602c6ae854008cd4040054d5d09aba2500223263201a33573803603403026aae7940044dd50009aba150043335500875ca00e6ae85400cc8c8c8cccd5cd19b875001480108c84888c008010d5d09aab9e500323333573466e1d4009200223212223001004375c6ae84d55cf280211999ab9a3370ea00690001091100191931900e19ab9c01d01c01a019018135573aa00226ea8004d5d0a80119a8063ae357426ae8940088c98c8058cd5ce00b80b00a09aba25001135744a00226aae7940044dd5000899aa800bae75a224464460046eac004c8004d5406488c8cccd55cf80112804119a80399aa80498031aab9d5002300535573ca00460086ae8800c04c4d5d08008891001091091198008020018891091980080180109119191999ab9a3370ea0029000119091180100198029aba135573ca00646666ae68cdc3a801240044244002464c6402066ae700440400380344d55cea80089baa001232323333573466e1d400520062321222230040053007357426aae79400c8cccd5cd19b875002480108c848888c008014c024d5d09aab9e500423333573466e1d400d20022321222230010053007357426aae7940148cccd5cd19b875004480008c848888c00c014dd71aba135573ca00c464c6402066ae7004404003803403002c4d55cea80089baa001232323333573466e1cd55cea80124000466442466002006004600a6ae854008dd69aba135744a004464c6401866ae700340300284d55cf280089baa0012323333573466e1cd55cea800a400046eb8d5d09aab9e500223263200a33573801601401026ea80048c8c8c8c8c8cccd5cd19b8750014803084888888800c8cccd5cd19b875002480288488888880108cccd5cd19b875003480208cc8848888888cc004024020dd71aba15005375a6ae84d5d1280291999ab9a3370ea00890031199109111111198010048041bae35742a00e6eb8d5d09aba2500723333573466e1d40152004233221222222233006009008300c35742a0126eb8d5d09aba2500923333573466e1d40192002232122222223007008300d357426aae79402c8cccd5cd19b875007480008c848888888c014020c038d5d09aab9e500c23263201333573802802602202001e01c01a01801626aae7540104d55cf280189aab9e5002135573ca00226ea80048c8c8c8c8cccd5cd19b875001480088ccc888488ccc00401401000cdd69aba15004375a6ae85400cdd69aba135744a00646666ae68cdc3a80124000464244600400660106ae84d55cf280311931900619ab9c00d00c00a009135573aa00626ae8940044d55cf280089baa001232323333573466e1d400520022321223001003375c6ae84d55cf280191999ab9a3370ea004900011909118010019bae357426aae7940108c98c8024cd5ce00500480380309aab9d50011375400224464646666ae68cdc3a800a40084244400246666ae68cdc3a8012400446424446006008600c6ae84d55cf280211999ab9a3370ea00690001091100111931900519ab9c00b00a008007006135573aa00226ea80048c8cccd5cd19b8750014800880348cccd5cd19b8750024800080348c98c8018cd5ce00380300200189aab9d37540029309000a4810350543100112330010020072253350021001100612335002223335003220020020013500122001122123300100300222333573466e1c00800401000c488008488004448c8c00400488cc00cc008008005f5f6").unwrap(); - let tx_bytes = hex::decode("84a70082825820ee9ae4e9102e5e8a725ca5e0bd8ee1f4e9d7d8a86195cb180334c15a6265a3ad00825820975c17a4fed0051be622328efa548e206657d2b65a19224bf6ff8132571e6a5002018182581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a00acd8c6021a0002a11a0b582010f5361f3d433170f55a62bc6c3b423b00c65dea95204c985b865b995f0b921f0d818258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d011082581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af10c70d111a0003f1a7a4008182582031ae74f8058527afb305d7495b10a99422d9337fc199e1f28044f2c477a0f94658402e38452ee99c0a080e5804156a7be45ef83b0eb361486a9f8eedeb07e499ccd0865f56620db297f831ab60b93490ee1426e0b7d42417ecb0b793c9f915c58c01049fd87980ff0581840001d879808219044c1a000382d4068149480100002221200101f5f6").unwrap(); - - let raw_inputs = hex::decode("86825820ee9ae4e9102e5e8a725ca5e0bd8ee1f4e9d7d8a86195cb180334c15a6265a3ad00825820d64cb7f6e13b0f9539d5505d368210034b8a3a2df8086382bd66dfb3a6e6a78601825820b16778c9cf065d9efeefe37ec269b4fc5107ecdbd0dd6bf3274b224165c2edd9008258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d01825820b810e77e706ccaebf7284f6c3d41e2a1eb4af5fabae452618f4175ad1b2aaded02825820975c17a4fed0051be622328efa548e206657d2b65a19224bf6ff8132571e6a5002").unwrap(); - let raw_outputs = hex::decode("8683581d703a888d65f16790950a72daee1f63aa05add6d268434107cfa5b677121a0016e3605820923918e403bf43c34b4ef6b48eb2ee04babed17320d8d1b9ff9ad086e86f44eca400581d703a888d65f16790950a72daee1f63aa05add6d268434107cfa5b67712011a0010b4540282005820923918e403bf43c34b4ef6b48eb2ee04babed17320d8d1b9ff9ad086e86f44ec03d8184c82024948010000222120010182581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f67235821a000f8548a1581c15be994a64bdb79dde7fe080d8e7ff81b33a9e4860e9ee0d857a8e85a144576177610182581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af14b8b482581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a0098968082581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a00989680").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(); From d9b34973c759a5718b7e793df34a353455f34d5e Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Mon, 12 Sep 2022 17:17:11 +0200 Subject: [PATCH 29/77] fixed constructor tags --- crates/cli/src/utils.rs | 350 ++++++++++++++++++++++------------------ 1 file changed, 193 insertions(+), 157 deletions(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index d79fd7f3..1eb6b716 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -96,22 +96,35 @@ pub fn get_tx_in_info_old(resolved_inputs: &[ResolvedInput]) -> anyhow::Result PlutusData { +fn wrap_with_constr(index: u64, data: PlutusData) -> PlutusData { PlutusData::Constr(Constr { - tag, + tag: constr_index(index), any_constructor: None, fields: MaybeIndefArray::Indef(vec![data]), }) } -fn empty_constr(tag: u64) -> PlutusData { +fn wrap_multiple_with_constr(index: u64, data: Vec) -> PlutusData { PlutusData::Constr(Constr { - tag, + tag: constr_index(index), + any_constructor: None, + fields: MaybeIndefArray::Indef(data), + }) +} + +fn empty_constr(index: u64) -> PlutusData { + PlutusData::Constr(Constr { + tag: constr_index(index), any_constructor: None, fields: MaybeIndefArray::Indef(vec![]), }) } +/// Translate constructor index to cbor tag +fn constr_index(index: u64) -> u64 { + 121 + index +} + pub trait ToPlutusData { fn to_plutus_data(&self) -> PlutusData; } @@ -120,16 +133,15 @@ impl ToPlutusData for Address { fn to_plutus_data(&self) -> PlutusData { match self { Address::Byron(byron_address) => { - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + wrap_multiple_with_constr( + 0, + vec![ //addressCredential wrap_with_constr(0, byron_address.decode().unwrap().root.to_plutus_data()), //addressStakeCredential None::.to_plutus_data(), - ]), - }) + ], + ) } Address::Shelley(shelley_address) => { let payment_part = shelley_address.payment(); @@ -151,27 +163,19 @@ impl ToPlutusData for Address { ShelleyDelegationPart::Script(script_hash) => { Some(StakeCredential::Scripthash(script_hash.clone())).to_plutus_data() } - ShelleyDelegationPart::Pointer(pointer) => Some(PlutusData::Constr(Constr { - tag: 1, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + 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(), }; - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - payment_part_plutus_data, - stake_part_plutus_data, - ]), - }) + wrap_multiple_with_constr(0, vec![payment_part_plutus_data, stake_part_plutus_data]) } Address::Stake(_) => unreachable!(), } @@ -180,14 +184,13 @@ impl ToPlutusData for Address { impl ToPlutusData for TransactionInput { fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + wrap_multiple_with_constr( + 0, + vec![ wrap_with_constr(0, self.transaction_id.to_plutus_data()), PlutusData::BigInt(BigInt::Int(self.index.into())), - ]), - }) + ], + ) } } @@ -329,30 +332,28 @@ impl ToPlutusData for ScriptRef { impl ToPlutusData for TransactionOutput { fn to_plutus_data(&self) -> PlutusData { match self { - TransactionOutput::Legacy(legacy_output) => PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + 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(), None::.to_plutus_data(), None::.to_plutus_data(), - ]), - }), - TransactionOutput::PostAlonzo(post_alonzo_output) => PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + ], + ), + 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(), - ]), - }), + ], + ), } } } @@ -383,14 +384,13 @@ impl ToPlutusData for Certificate { wrap_with_constr(1, stake_credential.to_plutus_data()) } Certificate::StakeDelegation(stake_credential, pool_keyhash) => { - PlutusData::Constr(Constr { - tag: 2, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + wrap_multiple_with_constr( + 2, + vec![ stake_credential.to_plutus_data(), pool_keyhash.to_plutus_data(), - ]), - }) + ], + ) } Certificate::PoolRegistration { operator, @@ -402,22 +402,14 @@ impl ToPlutusData for Certificate { pool_owners: _, relays: _, pool_metadata: _, - } => PlutusData::Constr(Constr { - tag: 3, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - operator.to_plutus_data(), - vrf_keyhash.to_plutus_data(), - ]), - }), - Certificate::PoolRetirement(pool_keyhash, epoch) => PlutusData::Constr(Constr { - tag: 4, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - pool_keyhash.to_plutus_data(), - epoch.to_plutus_data(), - ]), - }), + } => 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), } @@ -443,129 +435,120 @@ impl ToPlutusData for TimeRange { lower_bound: Some(lower_bound), upper_bound: None, } => { - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + wrap_multiple_with_constr( + 0, + vec![ // LowerBound - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + wrap_multiple_with_constr( + 0, + vec![ // Finite wrap_with_constr(1, lower_bound.to_plutus_data()), // Closure true.to_plutus_data(), - ]), - }), //UpperBound - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + ], + ), //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), } => { - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + wrap_multiple_with_constr( + 0, + vec![ // LowerBound - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + wrap_multiple_with_constr( + 0, + vec![ // NegInf empty_constr(0), // Closure true.to_plutus_data(), - ]), - }), //UpperBound - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + ], + ), + //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), } => { - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + wrap_multiple_with_constr( + 0, + vec![ // LowerBound - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + wrap_multiple_with_constr( + 0, + vec![ // Finite wrap_with_constr(1, lower_bound.to_plutus_data()), // Closure true.to_plutus_data(), - ]), - }), //UpperBound - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + ], + ), + //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, } => { - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + wrap_multiple_with_constr( + 0, + vec![ // LowerBound - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + wrap_multiple_with_constr( + 0, + vec![ // NegInf empty_constr(0), // Closure true.to_plutus_data(), - ]), - }), //UpperBound - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + ], + ), + //UpperBound + wrap_multiple_with_constr( + 0, + vec![ // PosInf empty_constr(2), // Closure true.to_plutus_data(), - ]), - }), - ]), - }) + ], + ), + ], + ) } } } @@ -573,14 +556,13 @@ impl ToPlutusData for TimeRange { impl ToPlutusData for TxInInfo { fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + wrap_multiple_with_constr( + 0, + vec![ self.out_ref.to_plutus_data(), self.resolved.to_plutus_data(), - ]), - }) + ], + ) } } @@ -599,10 +581,9 @@ impl ToPlutusData for ScriptPurpose { impl ToPlutusData for TxInfo { fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + wrap_multiple_with_constr( + 0, + vec![ self.inputs.to_plutus_data(), self.reference_inputs.to_plutus_data(), self.outputs.to_plutus_data(), @@ -615,21 +596,17 @@ impl ToPlutusData for TxInfo { self.redeemers.to_plutus_data(), self.data.to_plutus_data(), wrap_with_constr(0, self.id.to_plutus_data()), - ]), - }) + ], + ) } } impl ToPlutusData for ScriptContext { fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ - self.tx_info.to_plutus_data(), - self.purpose.to_plutus_data(), - ]), - }) + wrap_multiple_with_constr( + 0, + vec![self.tx_info.to_plutus_data(), self.purpose.to_plutus_data()], + ) } } @@ -1146,7 +1123,9 @@ fn eval_redeemer( .eval(); match result.0 { - Ok(_) => {} + Ok(_) => { + println!("SUCCESS") + } Err(err) => { println!("ERROR: {:?}", err.to_string()) } @@ -1215,6 +1194,15 @@ mod tests { #[test] fn test_eval() { + /* + {-# 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(); @@ -1251,4 +1239,52 @@ mod tests { _ => unreachable!(), }; } + + #[test] + fn test_eval_1() { + /* + {-# 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)| TxInInfo { + out_ref: input.clone(), + resolved: output.clone(), + }) + .collect(), + ); + + let slot_config = SlotConfig { + zero_time: 1660003200000, // Preview network + slot_length: 1000, + }; + + 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_tx(&tx, &utxos, &slot_config).unwrap(); + + println!("{:?}", redeemers.len()); + } + _ => unreachable!(), + }; + } } From 6e4ff1e282f66597ff7d6ccc9f36b4ad2c51776e Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Tue, 13 Sep 2022 11:41:42 +0200 Subject: [PATCH 30/77] added plutusV1 --- crates/cli/src/args.rs | 2 +- crates/cli/src/main.rs | 5 +- crates/cli/src/utils.rs | 443 ++++++++++++++++++++++++++++++---------- 3 files changed, 339 insertions(+), 111 deletions(-) diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs index d5645ede..15ffa8fb 100644 --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -30,7 +30,7 @@ pub enum TxCommand { } #[derive(Deserialize)] -pub struct ResolvedInput { +pub struct ResolvedInputOld { pub input: Input, pub output: Output, } diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 2fbd9615..c86ff068 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -16,7 +16,7 @@ mod utils; use args::{Args, TxCommand, UplcCommand}; -use crate::args::ResolvedInput; +use crate::args::ResolvedInputOld; fn main() -> anyhow::Result<()> { let args = Args::default(); @@ -66,7 +66,8 @@ fn main() -> anyhow::Result<()> { let file = File::open(&resolved_inputs)?; let reader = BufReader::new(file); - let resolved_inputs: Vec = serde_json::from_reader(reader)?; + let resolved_inputs: Vec = + serde_json::from_reader(reader)?; let tx_in_info = utils::get_tx_in_info_old(&resolved_inputs)?; } diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index 1eb6b716..d6d6289a 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -8,9 +8,9 @@ use pallas_codec::{ use pallas_crypto::hash::{Hash, Hasher}; use pallas_primitives::{ babbage::{ - AddrKeyhash, AssetName, BigInt, Certificate, Constr, CostModel, DatumHash, DatumOption, - ExUnits, Language, Mint, MintedTx, PlutusV1Script, PlutusV2Script, PolicyId, Redeemer, - RedeemerTag, RewardAccount, Script, ScriptRef, StakeCredential, TransactionInput, + AddrKeyhash, AssetName, BigInt, Certificate, Coin, Constr, CostModel, DatumHash, + DatumOption, ExUnits, Language, Mint, MintedTx, PlutusV1Script, PlutusV2Script, PolicyId, + Redeemer, RedeemerTag, RewardAccount, Script, ScriptRef, StakeCredential, TransactionInput, TransactionOutput, Tx, Value, Withdrawals, }, Fragment, ToHash, @@ -19,6 +19,7 @@ use pallas_traverse::{Era, MultiEraTx}; use std::{ collections::HashMap, convert::{TryFrom, TryInto}, + ops::Deref, str::FromStr, vec, }; @@ -28,9 +29,9 @@ use uplc::{ PlutusData, }; -use crate::args::ResolvedInput; +use crate::args::ResolvedInputOld; -pub fn get_tx_in_info_old(resolved_inputs: &[ResolvedInput]) -> anyhow::Result> { +pub fn get_tx_in_info_old(resolved_inputs: &[ResolvedInputOld]) -> anyhow::Result> { let mut tx_in_info = Vec::new(); for resolved_input in resolved_inputs { @@ -120,7 +121,7 @@ fn empty_constr(index: u64) -> PlutusData { }) } -/// Translate constructor index to cbor tag +/// Translate constructor index to cbor tag. fn constr_index(index: u64) -> u64 { 121 + index } @@ -132,17 +133,6 @@ pub trait ToPlutusData { impl ToPlutusData for Address { fn to_plutus_data(&self) -> PlutusData { match self { - Address::Byron(byron_address) => { - wrap_multiple_with_constr( - 0, - vec![ - //addressCredential - wrap_with_constr(0, byron_address.decode().unwrap().root.to_plutus_data()), - //addressStakeCredential - None::.to_plutus_data(), - ], - ) - } Address::Shelley(shelley_address) => { let payment_part = shelley_address.payment(); let stake_part = shelley_address.delegation(); @@ -177,7 +167,7 @@ impl ToPlutusData for Address { wrap_multiple_with_constr(0, vec![payment_part_plutus_data, stake_part_plutus_data]) } - Address::Stake(_) => unreachable!(), + _ => unreachable!(), } } } @@ -206,6 +196,12 @@ impl ToPlutusData for ByteVec { } } +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 MaybeIndefArray { fn to_plutus_data(&self) -> PlutusData { PlutusData::Array(MaybeIndefArray::Indef( @@ -329,31 +325,61 @@ impl ToPlutusData for ScriptRef { } } -impl ToPlutusData for TransactionOutput { +impl ToPlutusData for TxOut { fn to_plutus_data(&self) -> PlutusData { match self { - 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(), - None::.to_plutus_data(), - 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(), - ], - ), + 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(), + ], + ), + }, } } } @@ -581,23 +607,40 @@ impl ToPlutusData for ScriptPurpose { impl ToPlutusData for TxInfo { fn to_plutus_data(&self) -> PlutusData { - wrap_multiple_with_constr( - 0, - vec![ - self.inputs.to_plutus_data(), - self.reference_inputs.to_plutus_data(), - self.outputs.to_plutus_data(), - self.fee.to_plutus_data(), - self.mint.to_plutus_data(), - self.dcert.to_plutus_data(), - self.wdrl.to_plutus_data(), - self.valid_range.to_plutus_data(), - self.signatories.to_plutus_data(), - self.redeemers.to_plutus_data(), - self.data.to_plutus_data(), - wrap_with_constr(0, self.id.to_plutus_data()), - ], - ) + 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()), + ], + ), + } } } @@ -619,12 +662,21 @@ impl ToPlutusData for bool { } } -// Plutus V2 only for now +#[derive(Debug, PartialEq, Clone)] +pub struct ResolvedInput { + input: TransactionInput, + output: TransactionOutput, +} #[derive(Debug, PartialEq, Clone)] pub struct TxInInfo { out_ref: TransactionInput, - resolved: TransactionOutput, + resolved: TxOut, +} +#[derive(Debug, PartialEq, Clone)] +pub enum TxOut { + V1(TransactionOutput), + V2(TransactionOutput), } #[derive(Debug, PartialEq, Clone)] @@ -636,10 +688,24 @@ pub enum ScriptPurpose { } #[derive(Debug, PartialEq, Clone)] -pub struct TxInfo { +pub struct TxInfoV1 { + inputs: MaybeIndefArray, + outputs: MaybeIndefArray, + fee: Value, + mint: Mint, + dcert: MaybeIndefArray, + wdrl: MaybeIndefArray<(RewardAccount, Coin)>, + valid_range: TimeRange, + signatories: MaybeIndefArray, + data: MaybeIndefArray<(DatumHash, PlutusData)>, + id: Hash<32>, +} + +#[derive(Debug, PartialEq, Clone)] +pub struct TxInfoV2 { inputs: MaybeIndefArray, reference_inputs: MaybeIndefArray, - outputs: MaybeIndefArray, + outputs: MaybeIndefArray, fee: Value, mint: Mint, dcert: MaybeIndefArray, @@ -651,6 +717,12 @@ pub struct TxInfo { id: Hash<32>, } +#[derive(Debug, PartialEq, Clone)] +pub enum TxInfo { + V1(TxInfoV1), + V2(TxInfoV2), +} + #[derive(Debug, PartialEq, Clone)] pub struct ScriptContext { tx_info: TxInfo, @@ -689,8 +761,8 @@ fn slot_range_to_posix_time_range(slot_range: TimeRange, sc: &SlotConfig) -> Tim #[derive(Debug, PartialEq, Clone)] enum ScriptVersion { - PlutusV1(PlutusV1Script), - PlutusV2(PlutusV2Script), + V1(PlutusV1Script), + V2(PlutusV2Script), } #[derive(Debug, PartialEq, Clone)] @@ -704,15 +776,34 @@ struct DataLookupTable { scripts: HashMap, } -fn get_tx_in_info( +fn get_tx_in_info_v1( inputs: &MaybeIndefArray, - utxos: &MaybeIndefArray, + utxos: &MaybeIndefArray, ) -> anyhow::Result> { Ok(MaybeIndefArray::Indef( utxos .iter() - .filter(|utxo| inputs.contains(&utxo.out_ref)) - .map(|utxo| utxo.clone()) + .filter(|utxo| inputs.contains(&utxo.input)) + .map(|utxo| TxInInfo { + out_ref: utxo.input.clone(), + resolved: TxOut::V1(utxo.output.clone()), + }) + .collect::>(), + )) +} + +fn get_tx_in_info_v2( + inputs: &MaybeIndefArray, + utxos: &MaybeIndefArray, +) -> anyhow::Result> { + Ok(MaybeIndefArray::Indef( + utxos + .iter() + .filter(|utxo| inputs.contains(&utxo.input)) + .map(|utxo| TxInInfo { + out_ref: utxo.input.clone(), + resolved: TxOut::V2(utxo.output.clone()), + }) .collect::>(), )) } @@ -770,14 +861,14 @@ fn get_script_purpose( let addresss = Address::from_bytes(&reward_account)?; let credential = match addresss { Address::Stake(stake_address) => match stake_address.payload() { - StakePayload::Stake(stake_keyhash) => { - StakeCredential::AddrKeyhash(stake_keyhash.clone()) - } StakePayload::Script(script_hash) => { StakeCredential::Scripthash(script_hash.clone()) } + StakePayload::Stake(_) => { + unreachable!(); + } }, - _ => panic!(), + _ => unreachable!(), }; Ok(ScriptPurpose::Rewarding(credential)) } @@ -789,22 +880,94 @@ fn get_script_purpose( } } -fn get_tx_info( +fn get_tx_info_v1( tx: &MintedTx, - utxos: &MaybeIndefArray, + utxos: &MaybeIndefArray, slot_config: &SlotConfig, ) -> anyhow::Result { + // Check if outputs do not contain ref scripts or inline datums or byron addresses in outputs + let body = tx.transaction_body.clone(); - let inputs = get_tx_in_info(&body.inputs, &utxos)?; - let reference_inputs = get_tx_in_info( + let inputs = get_tx_in_info_v1(&body.inputs, &utxos)?; + let outputs = MaybeIndefArray::Indef( + body.outputs + .iter() + .map(|output| TxOut::V1(output.clone())) + .collect(), + ); + let fee = Value::Coin(AnyUInt::U64(body.fee)); + let mint = body.mint.clone().unwrap_or(KeyValuePairs::Indef(vec![])); + let dcert = body + .certificates + .clone() + .unwrap_or(MaybeIndefArray::Indef(vec![])); + let wdrl = MaybeIndefArray::Indef( + 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(MaybeIndefArray::Indef(vec![])); + let data = MaybeIndefArray::Indef( + tx.transaction_witness_set + .plutus_data + .as_ref() + .unwrap_or(&MaybeIndefArray::Indef(vec![])) + .iter() + .map(|d| (d.to_hash(), d.clone())) + .collect(), + ); + let id = tx.transaction_body.to_hash(); + + Ok(TxInfo::V1(TxInfoV1 { + inputs, + outputs, + fee, + mint, + dcert, + wdrl, + valid_range, + signatories, + data, + id, + })) +} + +fn get_tx_info_v2( + tx: &MintedTx, + utxos: &MaybeIndefArray, + slot_config: &SlotConfig, +) -> anyhow::Result { + //TODO: Check if no byron addresses in outputs + + 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(MaybeIndefArray::Indef(vec![])), &utxos, )?; - let outputs = body.outputs.clone(); + let outputs = MaybeIndefArray::Indef( + body.outputs + .iter() + .map(|output| TxOut::V2(output.clone())) + .collect(), + ); let fee = Value::Coin(AnyUInt::U64(body.fee)); let mint = body.mint.clone().unwrap_or(KeyValuePairs::Indef(vec![])); let dcert = body @@ -858,7 +1021,7 @@ fn get_tx_info( ); let id = tx.transaction_body.to_hash(); - Ok(TxInfo { + Ok(TxInfo::V2(TxInfoV2 { inputs, reference_inputs, outputs, @@ -871,11 +1034,11 @@ fn get_tx_info( redeemers, data, id, - }) + })) } fn get_execution_purpose( - utxos: &MaybeIndefArray, + utxos: &MaybeIndefArray, script_purpose: &ScriptPurpose, lookup_table: &DataLookupTable, ) -> ExecutionPurpose { @@ -888,8 +1051,8 @@ fn get_execution_purpose( ExecutionPurpose::NoDatum(script.clone()) } ScriptPurpose::Spending(out_ref) => { - let utxo = utxos.iter().find(|utxo| utxo.out_ref == *out_ref).unwrap(); - match &utxo.resolved { + let utxo = utxos.iter().find(|utxo| utxo.input == *out_ref).unwrap(); + match &utxo.output { TransactionOutput::Legacy(output) => { let address = Address::from_bytes(&output.address).unwrap(); match address { @@ -902,9 +1065,6 @@ fn get_execution_purpose( let datum = lookup_table.datum.get(&output.datum_hash.unwrap()).unwrap(); - let a = hex::encode(datum.encode_fragment().unwrap()); - - println!("{:?}", a); ExecutionPurpose::WithDatum(script.clone(), datum.clone()) } _ => unreachable!(), @@ -967,7 +1127,7 @@ fn get_execution_purpose( fn get_script_and_datum_lookup_table( tx: &MintedTx, - utxos: &MaybeIndefArray, + utxos: &MaybeIndefArray, ) -> DataLookupTable { let mut datum = HashMap::new(); let mut scripts = HashMap::new(); @@ -1003,7 +1163,7 @@ fn get_script_and_datum_lookup_table( prefixed_script.extend(script.0.iter()); let hash = Hasher::<224>::hash(&prefixed_script); - scripts.insert(hash, ScriptVersion::PlutusV1(script.clone())); + scripts.insert(hash, ScriptVersion::V1(script.clone())); } for script in scripts_v2_witnesses.iter() { @@ -1013,13 +1173,13 @@ fn get_script_and_datum_lookup_table( prefixed_script.extend(script.0.iter()); let hash = Hasher::<224>::hash(&prefixed_script); - scripts.insert(hash, ScriptVersion::PlutusV2(script.clone())); + scripts.insert(hash, ScriptVersion::V2(script.clone())); } // discovery in utxos (script ref) for utxo in utxos.iter() { - match &utxo.resolved { + match &utxo.output { TransactionOutput::Legacy(_) => {} TransactionOutput::PostAlonzo(output) => match &output.script_ref { Some(script) => match &script.0 { @@ -1029,7 +1189,7 @@ fn get_script_and_datum_lookup_table( prefixed_script.extend(v1.0.iter()); let hash = Hasher::<224>::hash(&prefixed_script); - scripts.insert(hash, ScriptVersion::PlutusV1(v1.clone())); + scripts.insert(hash, ScriptVersion::V1(v1.clone())); } Script::PlutusV2Script(v2) => { // scripts.insert(v2.to_hash(), ScriptVersion::PlutusV2(v2.clone())); // TODO: fix hashing bug in pallas @@ -1038,7 +1198,7 @@ fn get_script_and_datum_lookup_table( prefixed_script.extend(v2.0.iter()); let hash = Hasher::<224>::hash(&prefixed_script); - scripts.insert(hash, ScriptVersion::PlutusV2(v2.clone())); + scripts.insert(hash, ScriptVersion::V2(v2.clone())); } _ => {} }, @@ -1052,7 +1212,7 @@ fn get_script_and_datum_lookup_table( fn eval_redeemer( tx: &MintedTx, - utxos: &MaybeIndefArray, + utxos: &MaybeIndefArray, slot_config: &SlotConfig, redeemer: &Redeemer, lookup_table: &DataLookupTable, @@ -1069,9 +1229,40 @@ fn eval_redeemer( match execution_purpose { ExecutionPurpose::WithDatum(script_version, datum) => match script_version { - ScriptVersion::PlutusV1(script) => todo!(), - ScriptVersion::PlutusV2(script) => { - let tx_info = get_tx_info(tx, utxos, slot_config)?; + 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 result = program + .apply_data(datum.clone()) + .apply_data(redeemer.data.clone()) + .apply_data(script_context.to_plutus_data()) + .eval(); + + result.0.unwrap(); + + let new_redeemer = Redeemer { + tag: redeemer.tag.clone(), + index: redeemer.index, + data: redeemer.data.clone(), + ex_units: ExUnits { + mem: (ExBudget::default().mem - result.1.mem) as u32, + steps: (ExBudget::default().cpu - result.1.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 = { @@ -1104,9 +1295,39 @@ fn eval_redeemer( } }, ExecutionPurpose::NoDatum(script_version) => match script_version { - ScriptVersion::PlutusV1(script) => todo!(), - ScriptVersion::PlutusV2(script) => { - let tx_info = get_tx_info(tx, utxos, slot_config)?; + 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 result = program + .apply_data(redeemer.data.clone()) + .apply_data(script_context.to_plutus_data()) + .eval(); + + result.0.unwrap(); + + let new_redeemer = Redeemer { + tag: redeemer.tag.clone(), + index: redeemer.index, + data: redeemer.data.clone(), + ex_units: ExUnits { + mem: (ExBudget::default().mem - result.1.mem) as u32, + steps: (ExBudget::default().cpu - result.1.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 = { @@ -1154,7 +1375,7 @@ fn eval_redeemer( fn eval_tx( tx: &MintedTx, - utxos: &MaybeIndefArray, + utxos: &MaybeIndefArray, //TODO: costMdls slot_config: &SlotConfig, ) -> anyhow::Result> { @@ -1190,11 +1411,14 @@ mod tests { use pallas_traverse::{Era, MultiEraTx}; use uplc::PlutusData; - use super::{eval_tx, SlotConfig, TxInInfo}; + use super::{eval_tx, ResolvedInput, SlotConfig, TxInInfo}; #[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) @@ -1211,13 +1435,13 @@ mod tests { let inputs = MaybeIndefArray::::decode_fragment(&raw_inputs).unwrap(); let outputs = MaybeIndefArray::::decode_fragment(&raw_outputs).unwrap(); - let utxos: MaybeIndefArray = MaybeIndefArray::Indef( + let utxos: MaybeIndefArray = MaybeIndefArray::Indef( inputs .iter() .zip(outputs.iter()) - .map(|(input, output)| TxInInfo { - out_ref: input.clone(), - resolved: output.clone(), + .map(|(input, output)| ResolvedInput { + input: input.clone(), + output: output.clone(), }) .collect(), ); @@ -1243,6 +1467,9 @@ mod tests { #[test] fn test_eval_1() { /* + + PlutusV2 + {-# INLINEABLE mintTestValidator #-} mintTestValidator :: () -> Api.ScriptContext -> Bool mintTestValidator _ ctx = Api.txInfoFee txInfo == Api.txInfoFee txInfo @@ -1259,13 +1486,13 @@ mod tests { let inputs = MaybeIndefArray::::decode_fragment(&raw_inputs).unwrap(); let outputs = MaybeIndefArray::::decode_fragment(&raw_outputs).unwrap(); - let utxos: MaybeIndefArray = MaybeIndefArray::Indef( + let utxos: MaybeIndefArray = MaybeIndefArray::Indef( inputs .iter() .zip(outputs.iter()) - .map(|(input, output)| TxInInfo { - out_ref: input.clone(), - resolved: output.clone(), + .map(|(input, output)| ResolvedInput { + input: input.clone(), + output: output.clone(), }) .collect(), ); From be8daa00dd42d532943cf4b62f999351ed1dc37f Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Tue, 13 Sep 2022 12:47:45 +0200 Subject: [PATCH 31/77] added plutusV1 test --- crates/cli/src/utils.rs | 51 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index d6d6289a..0d024496 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -1514,4 +1514,55 @@ mod tests { _ => unreachable!(), }; } + + #[test] + fn test_eval_2() { + /* + + PlutusV1 + + {-# 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 + slot_length: 1000, + }; + + 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_tx(&tx, &utxos, &slot_config).unwrap(); + + println!("{:?}", redeemers.len()); + } + _ => unreachable!(), + }; + } } From a408491e41f28cdbddb062341d6c7430bb9d8356 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Tue, 13 Sep 2022 17:42:18 +0200 Subject: [PATCH 32/77] added error messages --- crates/cli/src/utils.rs | 125 ++++++++++++++++++++++++++++++---------- 1 file changed, 94 insertions(+), 31 deletions(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index 0d024496..a1914f0c 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -736,7 +736,7 @@ struct TimeRange { upper_bound: Option, } -struct SlotConfig { +pub struct SlotConfig { slot_length: u64, zero_time: u64, } @@ -780,32 +780,83 @@ fn get_tx_in_info_v1( inputs: &MaybeIndefArray, utxos: &MaybeIndefArray, ) -> anyhow::Result> { - Ok(MaybeIndefArray::Indef( - utxos - .iter() - .filter(|utxo| inputs.contains(&utxo.input)) - .map(|utxo| TxInInfo { + let result = inputs + .iter() + .map(|input| { + let utxo = match utxos.iter().find(|utxo| utxo.input == *input) { + Some(u) => u, + None => unreachable!("Resolved input not found."), + }; + 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(_) => unreachable!("Byron addresses not supported in Plutus."), + Address::Stake(_) => { + unreachable!("This is impossible. A stake address cannot lock a UTxO.") + } + _ => {} + } + + match &utxo.output { + TransactionOutput::Legacy(_) => {} + TransactionOutput::PostAlonzo(output) => { + match output.datum_option { + Some(DatumOption::Data(_)) => { + unreachable!("Inline datum not allowed in PlutusV1.") + } + _ => {} + }; + if output.script_ref.is_some() { + unreachable!("Reference scripts not allowed in PlutusV1.") + } + } + } + + TxInInfo { out_ref: utxo.input.clone(), resolved: TxOut::V1(utxo.output.clone()), - }) - .collect::>(), - )) + } + }) + .collect::>(); + Ok(MaybeIndefArray::Indef(result)) } fn get_tx_in_info_v2( inputs: &MaybeIndefArray, utxos: &MaybeIndefArray, ) -> anyhow::Result> { - Ok(MaybeIndefArray::Indef( - utxos - .iter() - .filter(|utxo| inputs.contains(&utxo.input)) - .map(|utxo| TxInInfo { + let result = inputs + .iter() + .map(|input| { + let utxo = match utxos.iter().find(|utxo| utxo.input == *input) { + Some(u) => u, + None => unreachable!("Resolved input not found."), + }; + 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(_) => unreachable!("Byron addresses not supported in Plutus."), + Address::Stake(_) => { + unreachable!("This is impossible. A stake address cannot lock a UTxO.") + } + _ => {} + } + + TxInInfo { out_ref: utxo.input.clone(), resolved: TxOut::V2(utxo.output.clone()), - }) - .collect::>(), - )) + } + }) + .collect::>(); + Ok(MaybeIndefArray::Indef(result)) } fn get_script_purpose( @@ -865,10 +916,14 @@ fn get_script_purpose( StakeCredential::Scripthash(script_hash.clone()) } StakePayload::Stake(_) => { - unreachable!(); + unreachable!( + "This is impossible. A key hash cannot be the hash of a script." + ); } }, - _ => unreachable!(), + _ => unreachable!( + "This is impossible. Only shelley reward addresses can be a part of withdrawals." + ), }; Ok(ScriptPurpose::Rewarding(credential)) } @@ -885,10 +940,12 @@ fn get_tx_info_v1( utxos: &MaybeIndefArray, slot_config: &SlotConfig, ) -> anyhow::Result { - // Check if outputs do not contain ref scripts or inline datums or byron addresses in outputs - let body = tx.transaction_body.clone(); + if body.reference_inputs.is_some() { + unreachable!("Reference inputs not allowed in PlutusV1.") + } + let inputs = get_tx_in_info_v1(&body.inputs, &utxos)?; let outputs = MaybeIndefArray::Indef( body.outputs @@ -950,8 +1007,6 @@ fn get_tx_info_v2( utxos: &MaybeIndefArray, slot_config: &SlotConfig, ) -> anyhow::Result { - //TODO: Check if no byron addresses in outputs - let body = tx.transaction_body.clone(); let inputs = get_tx_in_info_v2(&body.inputs, &utxos)?; @@ -1067,7 +1122,9 @@ fn get_execution_purpose( ExecutionPurpose::WithDatum(script.clone(), datum.clone()) } - _ => unreachable!(), + _ => unreachable!( + "This is impossible. Only shelley addresses can contain a script hash." + ), } } TransactionOutput::PostAlonzo(output) => { @@ -1084,12 +1141,14 @@ fn get_execution_purpose( lookup_table.datum.get(&hash).unwrap().clone() } Some(DatumOption::Data(data)) => data.0.clone(), - _ => unreachable!(), + _ => unreachable!( "This is impossible. The script UTxO needs to contain an inline datum or datum hash in order to spend it."), }; ExecutionPurpose::WithDatum(script.clone(), datum) } - _ => unreachable!(), + _ => unreachable!( + "This is impossible. Only shelley addresses can contain a script hash." + ), } } } @@ -1097,7 +1156,7 @@ fn get_execution_purpose( ScriptPurpose::Rewarding(stake_credential) => { let script_hash = match stake_credential { StakeCredential::Scripthash(hash) => hash.clone(), - _ => unreachable!(), + _ => unreachable!("This is impossible. A key hash cannot be the hash of a script."), }; let script = lookup_table.scripts.get(&script_hash).unwrap(); ExecutionPurpose::NoDatum(script.clone()) @@ -1107,7 +1166,9 @@ fn get_execution_purpose( Certificate::StakeDeregistration(stake_credential) => { let script_hash = match stake_credential { StakeCredential::Scripthash(hash) => hash.clone(), - _ => unreachable!(), + _ => unreachable!( + "This is impossible. A key hash cannot be the hash of a script." + ), }; let script = lookup_table.scripts.get(&script_hash).unwrap(); ExecutionPurpose::NoDatum(script.clone()) @@ -1115,12 +1176,14 @@ fn get_execution_purpose( Certificate::StakeDelegation(stake_credential, _) => { let script_hash = match stake_credential { StakeCredential::Scripthash(hash) => hash.clone(), - _ => unreachable!(), + _ => unreachable!( + "This is impossible. A key hash cannot be the hash of a script." + ), }; let script = lookup_table.scripts.get(&script_hash).unwrap(); ExecutionPurpose::NoDatum(script.clone()) } - _ => unreachable!(), + _ => unreachable!("This is impossible. Only stake deregistration and stake delegation are valid a script purposes."), }, } } @@ -1519,7 +1582,7 @@ mod tests { fn test_eval_2() { /* - PlutusV1 + Plutus V1 {-# INLINEABLE mintTestValidator #-} mintTestValidator :: () -> Api.ScriptContext -> Bool From 829d959fa3e74293838f8fe03ca5e9fe5bcc15cc Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Tue, 13 Sep 2022 18:16:20 +0200 Subject: [PATCH 33/77] more error statements --- crates/cli/src/utils.rs | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index a1914f0c..32af05dc 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -874,13 +874,15 @@ fn get_script_purpose( // sort lexical by policy id let mut policy_ids = mint .as_ref() - .unwrap() + .unwrap_or(&KeyValuePairs::Indef(vec![])) .iter() .map(|(policy_id, _)| policy_id.clone()) .collect::>(); policy_ids.sort(); - let policy_id = policy_ids[index as usize].clone(); - Ok(ScriptPurpose::Minting(policy_id)) + match policy_ids.get(index as usize) { + Some(policy_id) => Ok(ScriptPurpose::Minting(policy_id.clone())), + None => unreachable!("Script purpose not found for redeemer."), + } } RedeemerTag::Spend => { // sort lexical by tx_hash and index @@ -896,19 +898,24 @@ fn get_script_purpose( std::cmp::Ordering::Greater => std::cmp::Ordering::Greater, }, ); - let input = inputs[index as usize].clone(); - Ok(ScriptPurpose::Spending(input)) + match inputs.get(index as usize) { + Some(input) => Ok(ScriptPurpose::Spending(input.clone())), + None => unreachable!("Script purpose not found for redeemer."), + } } RedeemerTag::Reward => { // sort lexical by reward account let mut reward_accounts = wdrl .as_ref() - .unwrap() + .unwrap_or(&KeyValuePairs::Indef(vec![])) .iter() .map(|(policy_id, _)| policy_id.clone()) .collect::>(); reward_accounts.sort(); - let reward_account = reward_accounts[index as usize].clone(); + let reward_account = match reward_accounts.get(index as usize) { + Some(ra) => ra.clone(), + None => unreachable!("Script purpose not found for redeemer."), + }; let addresss = Address::from_bytes(&reward_account)?; let credential = match addresss { Address::Stake(stake_address) => match stake_address.payload() { @@ -929,8 +936,14 @@ fn get_script_purpose( } RedeemerTag::Cert => { // sort by order given in the tx (just take it as it is basically) - let cert = dcert.as_ref().unwrap()[index as usize].clone(); - Ok(ScriptPurpose::Certifying(cert)) + match dcert + .as_ref() + .unwrap_or(&MaybeIndefArray::Indef(vec![])) + .get(index as usize) + { + Some(cert) => Ok(ScriptPurpose::Certifying(cert.clone())), + None => unreachable!("Script purpose not found for redeemer."), + } } } } From 2bd46f90bbf849cc2285fdb8e38bd26a80a36978 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Tue, 13 Sep 2022 19:17:44 +0200 Subject: [PATCH 34/77] error statemens after program --- crates/cli/src/utils.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index 32af05dc..a3a663d9 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -1323,7 +1323,10 @@ fn eval_redeemer( .apply_data(script_context.to_plutus_data()) .eval(); - result.0.unwrap(); + match result.0 { + Ok(_) => {} + Err(err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message + } let new_redeemer = Redeemer { tag: redeemer.tag.clone(), @@ -1355,7 +1358,10 @@ fn eval_redeemer( .apply_data(script_context.to_plutus_data()) .eval(); - result.0.unwrap(); + match result.0 { + Ok(_) => {} + Err(err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message + } let new_redeemer = Redeemer { tag: redeemer.tag.clone(), @@ -1388,7 +1394,10 @@ fn eval_redeemer( .apply_data(script_context.to_plutus_data()) .eval(); - result.0.unwrap(); + match result.0 { + Ok(_) => {} + Err(err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message + } let new_redeemer = Redeemer { tag: redeemer.tag.clone(), @@ -1420,19 +1429,10 @@ fn eval_redeemer( .eval(); match result.0 { - Ok(_) => { - println!("SUCCESS") - } - Err(err) => { - println!("ERROR: {:?}", err.to_string()) - } + Ok(_) => {} + Err(err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message } - println!("MEM: {:?}", ExBudget::default().mem - result.1.mem); - println!("STEP: {:?}", ExBudget::default().cpu - result.1.cpu); - - // result.0.unwrap(); - let new_redeemer = Redeemer { tag: redeemer.tag.clone(), index: redeemer.index, From fc92c40c3ca2de59b2ee63e189cb38ead03089c6 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Tue, 13 Sep 2022 19:20:18 +0200 Subject: [PATCH 35/77] fixed typo --- crates/cli/src/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index a3a663d9..2cbc14f6 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -1196,7 +1196,7 @@ fn get_execution_purpose( let script = lookup_table.scripts.get(&script_hash).unwrap(); ExecutionPurpose::NoDatum(script.clone()) } - _ => unreachable!("This is impossible. Only stake deregistration and stake delegation are valid a script purposes."), + _ => unreachable!("This is impossible. Only stake deregistration and stake delegation are valid script purposes."), }, } } From 806d98e5fc7dc15d368eb69fbbb315694e78a072 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Tue, 13 Sep 2022 23:51:23 +0200 Subject: [PATCH 36/77] added error messages to execution purpose --- crates/cli/src/utils.rs | 65 +++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 18 deletions(-) diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index 2cbc14f6..fecff157 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -1115,7 +1115,10 @@ fn get_execution_purpose( let policy_id_array: [u8; 28] = policy_id.to_vec().try_into().unwrap(); let hash = Hash::from(policy_id_array); - let script = lookup_table.scripts.get(&hash).unwrap(); + let script = match lookup_table.scripts.get(&hash) { + Some(s) => s.clone(), + None => unreachable!("Missing required scripts.") + }; ExecutionPurpose::NoDatum(script.clone()) } ScriptPurpose::Spending(out_ref) => { @@ -1125,13 +1128,20 @@ fn get_execution_purpose( let address = Address::from_bytes(&output.address).unwrap(); match address { Address::Shelley(shelley_address) => { - let script = lookup_table - .scripts - .get(&shelley_address.payment().as_hash()) - .unwrap(); - let datum = - lookup_table.datum.get(&output.datum_hash.unwrap()).unwrap(); + let script = match lookup_table + .scripts + .get(&shelley_address.payment().as_hash()) { + Some(s) => s.clone(), + None => unreachable!("Missing required scripts.") + }; + + + + let datum = match lookup_table.datum.get(&output.datum_hash.unwrap_or_else(|| unreachable!("Missing datum hash in input."))) { + Some(d) => d.clone(), + None => unreachable!("Missing datum in witness set.") + }; ExecutionPurpose::WithDatum(script.clone(), datum.clone()) } @@ -1144,17 +1154,21 @@ fn get_execution_purpose( let address = Address::from_bytes(&output.address).unwrap(); match address { Address::Shelley(shelley_address) => { - let script = lookup_table - .scripts - .get(&shelley_address.payment().as_hash()) - .unwrap(); + + let script = match lookup_table + .scripts + .get(&shelley_address.payment().as_hash()) { + Some(s) => s.clone(), + None => unreachable!("Missing required scripts.") + }; + let datum = match &output.datum_option { Some(DatumOption::Hash(hash)) => { lookup_table.datum.get(&hash).unwrap().clone() } Some(DatumOption::Data(data)) => data.0.clone(), - _ => unreachable!( "This is impossible. The script UTxO needs to contain an inline datum or datum hash in order to spend it."), + _ => unreachable!( "Missing datum hash or inline datum in input."), }; ExecutionPurpose::WithDatum(script.clone(), datum) @@ -1171,8 +1185,13 @@ fn get_execution_purpose( StakeCredential::Scripthash(hash) => hash.clone(), _ => unreachable!("This is impossible. A key hash cannot be the hash of a script."), }; - let script = lookup_table.scripts.get(&script_hash).unwrap(); - ExecutionPurpose::NoDatum(script.clone()) + + let script = match lookup_table.scripts.get(&script_hash) { + Some(s) => s.clone(), + None => unreachable!("Missing required scripts.") + }; + + ExecutionPurpose::NoDatum(script) } ScriptPurpose::Certifying(cert) => match cert { // StakeRegistration doesn't require a witness from a stake key/script. So I assume it doesn't need to be handled in Plutus either? @@ -1183,8 +1202,13 @@ fn get_execution_purpose( "This is impossible. A key hash cannot be the hash of a script." ), }; - let script = lookup_table.scripts.get(&script_hash).unwrap(); - ExecutionPurpose::NoDatum(script.clone()) + + let script = match lookup_table.scripts.get(&script_hash) { + Some(s) => s.clone(), + None => unreachable!("Missing required scripts.") + }; + + ExecutionPurpose::NoDatum(script) } Certificate::StakeDelegation(stake_credential, _) => { let script_hash = match stake_credential { @@ -1193,8 +1217,13 @@ fn get_execution_purpose( "This is impossible. A key hash cannot be the hash of a script." ), }; - let script = lookup_table.scripts.get(&script_hash).unwrap(); - ExecutionPurpose::NoDatum(script.clone()) + + let script = match lookup_table.scripts.get(&script_hash) { + Some(s) => s.clone(), + None => unreachable!("Missing required scripts.") + }; + + ExecutionPurpose::NoDatum(script) } _ => unreachable!("This is impossible. Only stake deregistration and stake delegation are valid script purposes."), }, From 336af376e124789064c184c2b6a42dce499b2fda Mon Sep 17 00:00:00 2001 From: rvcas Date: Wed, 14 Sep 2022 12:26:35 -0400 Subject: [PATCH 37/77] chore: bump pallas version --- Cargo.lock | 61 ++++++++++-------------------- crates/cli/Cargo.toml | 10 ++--- crates/uplc/Cargo.toml | 4 +- crates/uplc/src/machine.rs | 16 ++------ crates/uplc/src/machine/runtime.rs | 38 +++++++++---------- 5 files changed, 48 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30f2f9f2..d90e4dee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,12 +54,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" -[[package]] -name = "bech32" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9ff0bbfd639f15c74af777d81383cf53efb7c93613f6cab67c6c11e05bbf8b" - [[package]] name = "bech32" version = "0.9.1" @@ -258,34 +252,14 @@ dependencies = [ "cfg-if", ] -[[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]] @@ -316,12 +290,12 @@ checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435" [[package]] name = "pallas-addresses" -version = "0.13.2" +version = "0.14.0-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712ead72b4f40d09f1c8660c47340489862754609cc2d587f24874ce55e51492" +checksum = "5caad3c874e1b6235c3a27a7e25497289392eabbd977e006863caf8ef3652e57" dependencies = [ "base58", - "bech32 0.8.1", + "bech32", "hex", "pallas-codec", "pallas-crypto", @@ -330,34 +304,37 @@ dependencies = [ [[package]] name = "pallas-codec" -version = "0.13.2" +version = "0.14.0-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1758a68b9b2332a24403cf484ea2dd05dd14cceeb6f7b63cb8d11137865fb4cf" +checksum = "8ac4f8895300b80ef2e624373f6421a9cb81523508bc48400f15db7876ec52ea" dependencies = [ - "minicbor 0.17.1", + "hex", + "minicbor", + "serde", ] [[package]] name = "pallas-crypto" -version = "0.13.2" +version = "0.14.0-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17a13efbe2609916de03c063d5bbef840616b02c658f3db5222785a18b63362" +checksum = "2c0a5b31037a97528e10feca6d208a21022f1c6d7837017915a43fae57be99dc" dependencies = [ "cryptoxide", "hex", "pallas-codec", "rand_core", + "serde", "thiserror", ] [[package]] name = "pallas-primitives" -version = "0.13.2" +version = "0.14.0-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f8d900bac04b711d2c4b21e906680224efd02d312a87ce25e688595b91d1fc" +checksum = "a8d2f217857eeccd37ad9fad3cdfc8288d8d2bc41e5c93e7f5f6b3c688873068" dependencies = [ "base58", - "bech32 0.9.1", + "bech32", "hex", "log", "pallas-codec", @@ -368,9 +345,9 @@ dependencies = [ [[package]] name = "pallas-traverse" -version = "0.13.2" +version = "0.14.0-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0549db2f8bbaf22d13fc35791dd50fb13a50a1290a342a98929c8ce6c422ed86" +checksum = "d4d32519a431ecc6c02507608cee4b75e06518a1cfdfcdd1b13cb7d0b07f638a" dependencies = [ "hex", "pallas-addresses", @@ -702,7 +679,7 @@ dependencies = [ "cryptoxide", "flat-rs", "hex", - "minicbor 0.18.0", + "minicbor", "pallas-codec", "pallas-primitives", "peg", diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index f8439e17..78c74c0e 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -14,11 +14,11 @@ authors = ["Lucas Rosa ", "Kasey White "] anyhow = "1.0.57" clap = { version = "3.1.14", features = ["derive"] } hex = "0.4.3" -pallas-primitives = "0.13.2" -pallas-codec = "0.13.2" -pallas-traverse = "0.13.2" -pallas-crypto = "0.13.2" +pallas-addresses = "0.14.0-alpha.2" +pallas-codec = "0.14.0-alpha.2" +pallas-crypto = "0.14.0-alpha.2" +pallas-primitives = "0.14.0-alpha.2" +pallas-traverse = "0.14.0-alpha.2" serde = { version = "1.0.144", features = ["derive"] } serde_json = "1.0.85" uplc = { path = '../uplc', version = "0.0.12" } -pallas-addresses = "0.13.2" diff --git a/crates/uplc/Cargo.toml b/crates/uplc/Cargo.toml index 336721c7..e2a35ab6 100644 --- a/crates/uplc/Cargo.toml +++ b/crates/uplc/Cargo.toml @@ -17,8 +17,8 @@ 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.13.2" -pallas-primitives = "0.13.2" +pallas-codec = "0.14.0-alpha.2" +pallas-primitives = "0.14.0-alpha.2" peg = "0.8.0" pretty = "0.11.3" thiserror = "1.0.31" diff --git a/crates/uplc/src/machine.rs b/crates/uplc/src/machine.rs index 0394d737..e3076b7d 100644 --- a/crates/uplc/src/machine.rs +++ b/crates/uplc/src/machine.rs @@ -488,9 +488,9 @@ 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| { - acc.push_back(&d.0); - acc.push_back(&d.1); + new_stack = m.iter().fold(VecDeque::new(), |mut acc, d| { + acc.push_back(d.0); + acc.push_back(d.1); acc }); // Append old stack to the back of the new stack @@ -499,7 +499,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 +517,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/runtime.rs b/crates/uplc/src/machine/runtime.rs index afac6f39..95851e5c 100644 --- a/crates/uplc/src/machine/runtime.rs +++ b/crates/uplc/src/machine/runtime.rs @@ -1,6 +1,5 @@ -use std::ops::Deref; +use std::{collections::BTreeMap, ops::Deref}; -use pallas_codec::utils::{KeyValuePairs, MaybeIndefArray}; use pallas_primitives::babbage::{BigInt, Constr, PlutusData}; use crate::{ @@ -664,8 +663,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 +685,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 +693,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 = BTreeMap::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.insert(key, value); + } _ => unreachable!(), } } _ => unreachable!(), - }) - .collect(); - Ok(Value::Con(Constant::Data(PlutusData::Map( - KeyValuePairs::Def(data_list), - )))) + } + } + + Ok(Value::Con(Constant::Data(PlutusData::Map(map)))) } _ => unreachable!(), }, @@ -722,9 +722,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 +778,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 +792,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 { From 348ed3b719954b2164a5a5d5cc7ea06ff98da71f Mon Sep 17 00:00:00 2001 From: rvcas Date: Wed, 14 Sep 2022 21:59:16 -0400 Subject: [PATCH 38/77] fix: changes from pallas bump Co-authored-by: Kasey White --- Cargo.lock | 16 +-- crates/cli/Cargo.toml | 10 +- crates/cli/src/utils.rs | 175 ++++++++++++++--------------- crates/uplc/Cargo.toml | 5 +- crates/uplc/src/flat.rs | 4 +- crates/uplc/src/machine.rs | 4 +- crates/uplc/src/machine/runtime.rs | 8 +- 7 files changed, 102 insertions(+), 120 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d90e4dee..0f9282db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -291,8 +291,7 @@ checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435" [[package]] name = "pallas-addresses" version = "0.14.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5caad3c874e1b6235c3a27a7e25497289392eabbd977e006863caf8ef3652e57" +source = "git+https://github.com/txpipe/pallas#ac25b48797572925ba48ccb0a8f4603410613a75" dependencies = [ "base58", "bech32", @@ -305,8 +304,7 @@ dependencies = [ [[package]] name = "pallas-codec" version = "0.14.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ac4f8895300b80ef2e624373f6421a9cb81523508bc48400f15db7876ec52ea" +source = "git+https://github.com/txpipe/pallas#ac25b48797572925ba48ccb0a8f4603410613a75" dependencies = [ "hex", "minicbor", @@ -316,8 +314,7 @@ dependencies = [ [[package]] name = "pallas-crypto" version = "0.14.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c0a5b31037a97528e10feca6d208a21022f1c6d7837017915a43fae57be99dc" +source = "git+https://github.com/txpipe/pallas#ac25b48797572925ba48ccb0a8f4603410613a75" dependencies = [ "cryptoxide", "hex", @@ -330,8 +327,7 @@ dependencies = [ [[package]] name = "pallas-primitives" version = "0.14.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d2f217857eeccd37ad9fad3cdfc8288d8d2bc41e5c93e7f5f6b3c688873068" +source = "git+https://github.com/txpipe/pallas#ac25b48797572925ba48ccb0a8f4603410613a75" dependencies = [ "base58", "bech32", @@ -346,8 +342,7 @@ dependencies = [ [[package]] name = "pallas-traverse" version = "0.14.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d32519a431ecc6c02507608cee4b75e06518a1cfdfcdd1b13cb7d0b07f638a" +source = "git+https://github.com/txpipe/pallas#ac25b48797572925ba48ccb0a8f4603410613a75" dependencies = [ "hex", "pallas-addresses", @@ -679,7 +674,6 @@ dependencies = [ "cryptoxide", "flat-rs", "hex", - "minicbor", "pallas-codec", "pallas-primitives", "peg", diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 78c74c0e..c9a0a452 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -14,11 +14,11 @@ authors = ["Lucas Rosa ", "Kasey White "] anyhow = "1.0.57" clap = { version = "3.1.14", features = ["derive"] } hex = "0.4.3" -pallas-addresses = "0.14.0-alpha.2" -pallas-codec = "0.14.0-alpha.2" -pallas-crypto = "0.14.0-alpha.2" -pallas-primitives = "0.14.0-alpha.2" -pallas-traverse = "0.14.0-alpha.2" +pallas-addresses = { git = "https://github.com/txpipe/pallas" } +pallas-codec = { git = "https://github.com/txpipe/pallas" } +pallas-crypto = { git = "https://github.com/txpipe/pallas" } +pallas-primitives = { git = "https://github.com/txpipe/pallas" } +pallas-traverse = { git = "https://github.com/txpipe/pallas" } serde = { version = "1.0.144", features = ["derive"] } serde_json = "1.0.85" uplc = { path = '../uplc', version = "0.0.12" } diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index fecff157..7da3b1b4 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -2,8 +2,8 @@ use pallas_addresses::{ Address, ScriptHash, ShelleyDelegationPart, ShelleyPaymentPart, StakePayload, }; use pallas_codec::{ - minicbor::{bytes::ByteVec, data::Int}, - utils::{AnyUInt, KeyValuePairs, MaybeIndefArray}, + minicbor::{bytes::ByteVec}, + utils::{AnyUInt, KeyValuePairs, MaybeIndefArray, Bytes, Int}, }; use pallas_crypto::hash::{Hash, Hasher}; use pallas_primitives::{ @@ -13,9 +13,9 @@ use pallas_primitives::{ Redeemer, RedeemerTag, RewardAccount, Script, ScriptRef, StakeCredential, TransactionInput, TransactionOutput, Tx, Value, Withdrawals, }, - Fragment, ToHash, + Fragment, }; -use pallas_traverse::{Era, MultiEraTx}; +use pallas_traverse::{Era, MultiEraTx, OriginalHash, ComputeHash}; use std::{ collections::HashMap, convert::{TryFrom, TryInto}, @@ -55,7 +55,7 @@ pub fn get_tx_in_info_old(resolved_inputs: &[ResolvedInputOld]) -> anyhow::Resul let tx_out = PlutusData::Constr(Constr { tag: 0, any_constructor: None, - fields: MaybeIndefArray::Indef(vec![ + fields: vec![ // txOutAddress address.to_plutus_data(), // txOutValue @@ -73,7 +73,7 @@ pub fn get_tx_in_info_old(resolved_inputs: &[ResolvedInputOld]) -> anyhow::Resul PlutusData::BoundedBytes( token.0.as_bytes().to_vec().into(), ), - PlutusData::BigInt(BigInt::Int((*token.1).into())), + PlutusData::BigInt(BigInt::Int((*token.1 as i64).into())), ) }) .collect(), @@ -82,13 +82,13 @@ pub fn get_tx_in_info_old(resolved_inputs: &[ResolvedInputOld]) -> anyhow::Resul }) .collect(), )), - ]), + ], }); tx_in_info.push(PlutusData::Constr(Constr { tag: 0, any_constructor: None, - fields: MaybeIndefArray::Indef(vec![tx_out_ref, tx_out]), + fields: vec![tx_out_ref, tx_out], })); } @@ -101,7 +101,7 @@ fn wrap_with_constr(index: u64, data: PlutusData) -> PlutusData { PlutusData::Constr(Constr { tag: constr_index(index), any_constructor: None, - fields: MaybeIndefArray::Indef(vec![data]), + fields: vec![data], }) } @@ -109,7 +109,7 @@ fn wrap_multiple_with_constr(index: u64, data: Vec) -> PlutusData { PlutusData::Constr(Constr { tag: constr_index(index), any_constructor: None, - fields: MaybeIndefArray::Indef(data), + fields: data, }) } @@ -117,7 +117,7 @@ fn empty_constr(index: u64) -> PlutusData { PlutusData::Constr(Constr { tag: constr_index(index), any_constructor: None, - fields: MaybeIndefArray::Indef(vec![]), + fields: vec![], }) } @@ -178,7 +178,7 @@ impl ToPlutusData for TransactionInput { 0, vec![ wrap_with_constr(0, self.transaction_id.to_plutus_data()), - PlutusData::BigInt(BigInt::Int(self.index.into())), + PlutusData::BigInt(BigInt::Int((self.index as i64).into())), ], ) } @@ -190,7 +190,7 @@ impl ToPlutusData for Hash { } } -impl ToPlutusData for ByteVec { +impl ToPlutusData for Bytes { fn to_plutus_data(&self) -> PlutusData { PlutusData::BoundedBytes(self.clone()) } @@ -202,15 +202,15 @@ impl ToPlutusData for (K, V) { } } -impl ToPlutusData for MaybeIndefArray { +impl ToPlutusData for Vec where A: ToPlutusData { fn to_plutus_data(&self) -> PlutusData { - PlutusData::Array(MaybeIndefArray::Indef( + PlutusData::Array( self.iter().map(|p| p.to_plutus_data()).collect(), - )) + ) } } -impl ToPlutusData for KeyValuePairs { +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() { @@ -246,11 +246,11 @@ impl ToPlutusData for Option { impl ToPlutusData for AnyUInt { fn to_plutus_data(&self) -> PlutusData { match self { - AnyUInt::U8(u8) => PlutusData::BigInt(BigInt::Int(Int::from(*u8))), - AnyUInt::U16(u16) => PlutusData::BigInt(BigInt::Int(Int::from(*u16))), - AnyUInt::U32(u32) => PlutusData::BigInt(BigInt::Int(Int::from(*u32))), - AnyUInt::U64(u64) => PlutusData::BigInt(BigInt::Int(Int::from(*u64))), - AnyUInt::MajorByte(u8) => PlutusData::BigInt(BigInt::Int(Int::from(*u8))), // is this correct? I don't know exactly what is does + 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 } } } @@ -275,7 +275,7 @@ impl ToPlutusData for i64 { impl ToPlutusData for u64 { fn to_plutus_data(&self) -> PlutusData { - PlutusData::BigInt(BigInt::Int(Int::from(*self))) + PlutusData::BigInt(BigInt::Int(Int::from(*self as i64))) } } @@ -283,7 +283,7 @@ impl ToPlutusData for Value { fn to_plutus_data(&self) -> PlutusData { match self { Value::Coin(coin) => PlutusData::Map(KeyValuePairs::Def(vec![( - PolicyId::from(vec![]).to_plutus_data(), + PolicyId::from([0; 28]).to_plutus_data(), PlutusData::Map(KeyValuePairs::Def(vec![( AssetName::from(vec![]).to_plutus_data(), coin.to_plutus_data(), @@ -291,7 +291,7 @@ impl ToPlutusData for Value { )])), Value::Multiasset(coin, multiassets) => { let mut data_vec: Vec<(PlutusData, PlutusData)> = vec![( - PolicyId::from(vec![]).to_plutus_data(), + PolicyId::from([0; 28]).to_plutus_data(), PlutusData::Map(KeyValuePairs::Def(vec![( AssetName::from(vec![]).to_plutus_data(), coin.to_plutus_data(), @@ -318,9 +318,9 @@ impl ToPlutusData for Value { impl ToPlutusData for ScriptRef { fn to_plutus_data(&self) -> PlutusData { match &self.0 { - Script::NativeScript(native_script) => native_script.to_hash().to_plutus_data(), - Script::PlutusV1Script(plutus_v1) => plutus_v1.to_hash().to_plutus_data(), - Script::PlutusV2Script(plutus_v2) => plutus_v2.to_hash().to_plutus_data(), + 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(), } } } @@ -689,29 +689,29 @@ pub enum ScriptPurpose { #[derive(Debug, PartialEq, Clone)] pub struct TxInfoV1 { - inputs: MaybeIndefArray, - outputs: MaybeIndefArray, + inputs: Vec, + outputs: Vec, fee: Value, mint: Mint, - dcert: MaybeIndefArray, - wdrl: MaybeIndefArray<(RewardAccount, Coin)>, + dcert: Vec, + wdrl: Vec<(RewardAccount, Coin)>, valid_range: TimeRange, - signatories: MaybeIndefArray, - data: MaybeIndefArray<(DatumHash, PlutusData)>, + signatories: Vec, + data: Vec<(DatumHash, PlutusData)>, id: Hash<32>, } #[derive(Debug, PartialEq, Clone)] pub struct TxInfoV2 { - inputs: MaybeIndefArray, - reference_inputs: MaybeIndefArray, - outputs: MaybeIndefArray, + inputs: Vec, + reference_inputs: Vec, + outputs: Vec, fee: Value, mint: Mint, - dcert: MaybeIndefArray, + dcert: Vec, wdrl: Withdrawals, valid_range: TimeRange, - signatories: MaybeIndefArray, + signatories: Vec, redeemers: KeyValuePairs, data: KeyValuePairs, id: Hash<32>, @@ -777,9 +777,9 @@ struct DataLookupTable { } fn get_tx_in_info_v1( - inputs: &MaybeIndefArray, - utxos: &MaybeIndefArray, -) -> anyhow::Result> { + inputs: &[TransactionInput], + utxos: &[ResolvedInput], +) -> anyhow::Result> { let result = inputs .iter() .map(|input| { @@ -822,13 +822,13 @@ fn get_tx_in_info_v1( } }) .collect::>(); - Ok(MaybeIndefArray::Indef(result)) + Ok(result) } fn get_tx_in_info_v2( - inputs: &MaybeIndefArray, - utxos: &MaybeIndefArray, -) -> anyhow::Result> { + inputs: &[TransactionInput], + utxos: &[ResolvedInput], +) -> anyhow::Result> { let result = inputs .iter() .map(|input| { @@ -856,14 +856,14 @@ fn get_tx_in_info_v2( } }) .collect::>(); - Ok(MaybeIndefArray::Indef(result)) + Ok(result) } fn get_script_purpose( redeemer: &Redeemer, - inputs: &MaybeIndefArray, + inputs: &[TransactionInput], mint: &Option, - dcert: &Option>, + dcert: &Option>, wdrl: &Option, ) -> anyhow::Result { // sorting according to specs section 4.1: https://hydra.iohk.io/build/18583827/download/1/alonzo-changes.pdf @@ -877,7 +877,7 @@ fn get_script_purpose( .unwrap_or(&KeyValuePairs::Indef(vec![])) .iter() .map(|(policy_id, _)| policy_id.clone()) - .collect::>(); + .collect::>(); policy_ids.sort(); match policy_ids.get(index as usize) { Some(policy_id) => Ok(ScriptPurpose::Minting(policy_id.clone())), @@ -960,25 +960,26 @@ fn get_tx_info_v1( } let inputs = get_tx_in_info_v1(&body.inputs, &utxos)?; - let outputs = MaybeIndefArray::Indef( + + let outputs = body.outputs .iter() .map(|output| TxOut::V1(output.clone())) - .collect(), - ); - let fee = Value::Coin(AnyUInt::U64(body.fee)); + .collect(); + + let fee = Value::Coin(body.fee); let mint = body.mint.clone().unwrap_or(KeyValuePairs::Indef(vec![])); let dcert = body .certificates .clone() - .unwrap_or(MaybeIndefArray::Indef(vec![])); - let wdrl = MaybeIndefArray::Indef( + .unwrap_or(vec![]); + let wdrl = body.withdrawals .clone() .unwrap_or(KeyValuePairs::Indef(vec![])) .deref() - .clone(), - ); + .clone(); + let valid_range = slot_range_to_posix_time_range( TimeRange { lower_bound: body.validity_interval_start, @@ -989,17 +990,18 @@ fn get_tx_info_v1( let signatories = body .required_signers .clone() - .unwrap_or(MaybeIndefArray::Indef(vec![])); - let data = MaybeIndefArray::Indef( + .unwrap_or(vec![]); + + let data = tx.transaction_witness_set .plutus_data .as_ref() - .unwrap_or(&MaybeIndefArray::Indef(vec![])) + .unwrap_or(&vec![]) .iter() - .map(|d| (d.to_hash(), d.clone())) - .collect(), - ); - let id = tx.transaction_body.to_hash(); + .map(|d| (d.original_hash(), d.unwrap())) + .collect(); + + let id = tx.transaction_body.compute_hash(); Ok(TxInfo::V1(TxInfoV1 { inputs, @@ -1027,21 +1029,20 @@ fn get_tx_info_v2( &body .reference_inputs .clone() - .unwrap_or(MaybeIndefArray::Indef(vec![])), + .unwrap_or(vec![]), &utxos, )?; - let outputs = MaybeIndefArray::Indef( + let outputs = body.outputs .iter() .map(|output| TxOut::V2(output.clone())) - .collect(), - ); - let fee = Value::Coin(AnyUInt::U64(body.fee)); + .collect(); + let fee = Value::Coin(body.fee); let mint = body.mint.clone().unwrap_or(KeyValuePairs::Indef(vec![])); let dcert = body .certificates .clone() - .unwrap_or(MaybeIndefArray::Indef(vec![])); + .unwrap_or(vec![]); let wdrl = body .withdrawals .clone() @@ -1056,7 +1057,7 @@ fn get_tx_info_v2( let signatories = body .required_signers .clone() - .unwrap_or(MaybeIndefArray::Indef(vec![])); + .unwrap_or(vec![]); let redeemers = KeyValuePairs::Indef( tx.transaction_witness_set .redeemer @@ -1082,12 +1083,12 @@ fn get_tx_info_v2( tx.transaction_witness_set .plutus_data .as_ref() - .unwrap_or(&MaybeIndefArray::Indef(vec![])) + .unwrap_or(&vec![]) .iter() - .map(|d| (d.to_hash(), d.clone())) + .map(|d| (d.original_hash(), d.unwrap())) .collect(), ); - let id = tx.transaction_body.to_hash(); + let id = tx.transaction_body.compute_hash(); Ok(TxInfo::V2(TxInfoV2 { inputs, @@ -1243,42 +1244,30 @@ fn get_script_and_datum_lookup_table( .transaction_witness_set .plutus_data .clone() - .unwrap_or(MaybeIndefArray::Indef(vec![])); + .unwrap_or(vec![]); let scripts_v1_witnesses = tx .transaction_witness_set .plutus_v1_script .clone() - .unwrap_or(MaybeIndefArray::Indef(vec![])); + .unwrap_or(vec![]); let scripts_v2_witnesses = tx .transaction_witness_set .plutus_v2_script .clone() - .unwrap_or(MaybeIndefArray::Indef(vec![])); + .unwrap_or(vec![]); for plutus_data in plutus_data_witnesses.iter() { - datum.insert(plutus_data.to_hash(), plutus_data.clone()); + datum.insert(plutus_data.original_hash(), plutus_data.clone()); } for script in scripts_v1_witnesses.iter() { - // scripts.insert(script.to_hash(), ScriptVersion::PlutusV1(script.clone())); // TODO: fix hashing bug in pallas - - let mut prefixed_script: Vec = vec![0x01]; - prefixed_script.extend(script.0.iter()); - - let hash = Hasher::<224>::hash(&prefixed_script); - scripts.insert(hash, ScriptVersion::V1(script.clone())); + scripts.insert(script.compute_hash(), ScriptVersion::V1(script.clone())); // TODO: fix hashing bug in pallas } for script in scripts_v2_witnesses.iter() { - // scripts.insert(script.to_hash(), ScriptVersion::PlutusV2(script.clone())); // TODO: fix hashing bug in pallas - - let mut prefixed_script: Vec = vec![0x02]; - prefixed_script.extend(script.0.iter()); - - let hash = Hasher::<224>::hash(&prefixed_script); - scripts.insert(hash, ScriptVersion::V2(script.clone())); + scripts.insert(script.compute_hash(), ScriptVersion::V2(script.clone())); // TODO: fix hashing bug in pallas } // discovery in utxos (script ref) diff --git a/crates/uplc/Cargo.toml b/crates/uplc/Cargo.toml index e2a35ab6..5ecce951 100644 --- a/crates/uplc/Cargo.toml +++ b/crates/uplc/Cargo.toml @@ -16,9 +16,8 @@ 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.14.0-alpha.2" -pallas-primitives = "0.14.0-alpha.2" +pallas-codec = { git = "https://github.com/txpipe/pallas" } +pallas-primitives = { git = "https://github.com/txpipe/pallas" } peg = "0.8.0" pretty = "0.11.3" thiserror = "1.0.31" diff --git a/crates/uplc/src/flat.rs b/crates/uplc/src/flat.rs index efb5b7d1..dd2f800b 100644 --- a/crates/uplc/src/flat.rs +++ b/crates/uplc/src/flat.rs @@ -31,7 +31,7 @@ where T: Binder<'b> + Debug, { pub fn from_cbor(bytes: &'b [u8], buffer: &'b mut Vec) -> Result { - let mut cbor_decoder = minicbor::Decoder::new(bytes); + let mut cbor_decoder = pallas_codec::minicbor::Decoder::new(bytes); let flat_bytes = cbor_decoder .bytes() @@ -63,7 +63,7 @@ where let mut bytes = Vec::new(); - let mut cbor_encoder = minicbor::Encoder::new(&mut bytes); + let mut cbor_encoder = pallas_codec::minicbor::Encoder::new(&mut bytes); cbor_encoder .bytes(&flat_bytes) diff --git a/crates/uplc/src/machine.rs b/crates/uplc/src/machine.rs index e3076b7d..07f5c346 100644 --- a/crates/uplc/src/machine.rs +++ b/crates/uplc/src/machine.rs @@ -489,8 +489,8 @@ impl Value { let mut new_stack: VecDeque<&PlutusData>; // create new stack with of items from the list of pairs of data new_stack = m.iter().fold(VecDeque::new(), |mut acc, d| { - acc.push_back(d.0); - acc.push_back(d.1); + acc.push_back(&d.0); + acc.push_back(&d.1); acc }); // Append old stack to the back of the new stack diff --git a/crates/uplc/src/machine/runtime.rs b/crates/uplc/src/machine/runtime.rs index 95851e5c..c722dc9d 100644 --- a/crates/uplc/src/machine/runtime.rs +++ b/crates/uplc/src/machine/runtime.rs @@ -1,4 +1,4 @@ -use std::{collections::BTreeMap, ops::Deref}; +use std::ops::Deref; use pallas_primitives::babbage::{BigInt, Constr, PlutusData}; @@ -693,14 +693,14 @@ impl DefaultFunction { }, DefaultFunction::MapData => match &args[0] { Value::Con(Constant::ProtoList(_, list)) => { - let mut map = BTreeMap::new(); + 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)) => { - map.insert(key, value); + map.push((key, value)); } _ => unreachable!(), } @@ -709,7 +709,7 @@ impl DefaultFunction { } } - Ok(Value::Con(Constant::Data(PlutusData::Map(map)))) + Ok(Value::Con(Constant::Data(PlutusData::Map(map.into())))) } _ => unreachable!(), }, From 26deb6df108b12696d7180e33062311d03ba3c93 Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 15 Sep 2022 11:27:33 -0400 Subject: [PATCH 39/77] fix: it compiles again --- crates/cli/src/main.rs | 40 +---- crates/cli/src/utils.rs | 371 ++++++++++++++-------------------------- 2 files changed, 129 insertions(+), 282 deletions(-) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index c86ff068..027814aa 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -36,46 +36,10 @@ fn main() -> anyhow::Result<()> { hex::decode(cbor_hex.trim())? }; - let tx = MultiEraTx::decode(Era::Alonzo, &tx_bytes) - .or_else(|_| MultiEraTx::decode(Era::Byron, &tx_bytes))?; + let tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes))?; println!("Simulating: {}", tx.hash()); - - let witnesses = tx.witnesses(); - - if let Some(((datums, redeemers), scripts)) = witnesses - .plutus_data() - .zip(witnesses.redeemer()) - .zip(witnesses.plutus_v1_script()) - { - for ((datum, redeemer), script) in - datums.iter().zip(redeemers.iter()).zip(scripts.iter()) - { - let program: Program = { - let mut buffer = Vec::new(); - - let prog = - Program::::from_cbor(&script.0, &mut buffer)?; - - prog.into() - }; - - program - .apply_data(datum.clone()) - .apply_data(redeemer.data.clone()); - - let file = File::open(&resolved_inputs)?; - let reader = BufReader::new(file); - let resolved_inputs: Vec = - serde_json::from_reader(reader)?; - - let tx_in_info = utils::get_tx_in_info_old(&resolved_inputs)?; - } - } - - println!("\nPlutus V2 Script:"); - - println!("{:#?}", tx.witnesses().plutus_v2_script()); } }, Args::Uplc(uplc_cmd) => match uplc_cmd { diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index 7da3b1b4..9227f028 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -1,102 +1,21 @@ use pallas_addresses::{ Address, ScriptHash, ShelleyDelegationPart, ShelleyPaymentPart, StakePayload, }; -use pallas_codec::{ - minicbor::{bytes::ByteVec}, - utils::{AnyUInt, KeyValuePairs, MaybeIndefArray, Bytes, Int}, -}; -use pallas_crypto::hash::{Hash, Hasher}; -use pallas_primitives::{ - babbage::{ - AddrKeyhash, AssetName, BigInt, Certificate, Coin, Constr, CostModel, DatumHash, - DatumOption, ExUnits, Language, Mint, MintedTx, PlutusV1Script, PlutusV2Script, PolicyId, - Redeemer, RedeemerTag, RewardAccount, Script, ScriptRef, StakeCredential, TransactionInput, - TransactionOutput, Tx, Value, Withdrawals, - }, - Fragment, -}; -use pallas_traverse::{Era, MultiEraTx, OriginalHash, ComputeHash}; -use std::{ - collections::HashMap, - convert::{TryFrom, TryInto}, - ops::Deref, - str::FromStr, - vec, +use pallas_codec::utils::{AnyUInt, Bytes, Int, KeyValuePairs, MaybeIndefArray}; +use pallas_crypto::hash::Hash; +use pallas_primitives::babbage::{ + AddrKeyhash, AssetName, BigInt, Certificate, Coin, Constr, DatumHash, DatumOption, ExUnits, + Mint, MintedTx, PlutusV1Script, PlutusV2Script, PolicyId, Redeemer, RedeemerTag, RewardAccount, + Script, ScriptRef, StakeCredential, TransactionInput, TransactionOutput, Value, Withdrawals, }; +use pallas_traverse::{ComputeHash, OriginalHash}; +use std::{collections::HashMap, convert::TryInto, ops::Deref, vec}; use uplc::{ ast::{FakeNamedDeBruijn, NamedDeBruijn, Program}, machine::cost_model::ExBudget, PlutusData, }; -use crate::args::ResolvedInputOld; - -pub fn get_tx_in_info_old(resolved_inputs: &[ResolvedInputOld]) -> anyhow::Result> { - let mut tx_in_info = Vec::new(); - - for resolved_input in resolved_inputs { - let tx_out_ref = TransactionInput { - transaction_id: Hash::from_str(resolved_input.input.tx_hash.as_str())?, - index: resolved_input.input.index, - } - .to_plutus_data(); - - let address = Address::from_bech32(&resolved_input.output.address)?; - - let lovelace = resolved_input.output.value.0; - - let mut assets = resolved_input.output.value.1.clone(); - - assets.insert( - "".to_string(), - vec![("".to_string(), lovelace)].into_iter().collect(), - ); - - let tx_out = PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: vec![ - // txOutAddress - address.to_plutus_data(), - // txOutValue - PlutusData::Map(KeyValuePairs::Def( - assets - .iter() - .map(|val| { - let currency_symbol = - PlutusData::BoundedBytes(hex::decode(val.0).unwrap().into()); - let token_map = PlutusData::Map(KeyValuePairs::Def( - val.1 - .iter() - .map(|token| { - ( - PlutusData::BoundedBytes( - token.0.as_bytes().to_vec().into(), - ), - PlutusData::BigInt(BigInt::Int((*token.1 as i64).into())), - ) - }) - .collect(), - )); - (currency_symbol, token_map) - }) - .collect(), - )), - ], - }); - - tx_in_info.push(PlutusData::Constr(Constr { - tag: 0, - any_constructor: None, - fields: vec![tx_out_ref, tx_out], - })); - } - - Ok(tx_in_info) -} - -// --------------- - fn wrap_with_constr(index: u64, data: PlutusData) -> PlutusData { PlutusData::Constr(Constr { tag: constr_index(index), @@ -148,10 +67,10 @@ impl ToPlutusData for Address { let stake_part_plutus_data = match stake_part { ShelleyDelegationPart::Key(stake_keyhash) => { - Some(StakeCredential::AddrKeyhash(stake_keyhash.clone())).to_plutus_data() + Some(StakeCredential::AddrKeyhash(*stake_keyhash)).to_plutus_data() } ShelleyDelegationPart::Script(script_hash) => { - Some(StakeCredential::Scripthash(script_hash.clone())).to_plutus_data() + Some(StakeCredential::Scripthash(*script_hash)).to_plutus_data() } ShelleyDelegationPart::Pointer(pointer) => Some(wrap_multiple_with_constr( 1, @@ -202,15 +121,20 @@ impl ToPlutusData for (K, V) { } } -impl ToPlutusData for Vec where A: ToPlutusData { +impl ToPlutusData for Vec +where + A: ToPlutusData, +{ fn to_plutus_data(&self) -> PlutusData { - PlutusData::Array( - self.iter().map(|p| p.to_plutus_data()).collect(), - ) + PlutusData::Array(self.iter().map(|p| p.to_plutus_data()).collect()) } } -impl ToPlutusData for KeyValuePairs where K: ToPlutusData + Clone, V: ToPlutusData + Clone { +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() { @@ -257,7 +181,7 @@ impl ToPlutusData for AnyUInt { impl ToPlutusData for Int { fn to_plutus_data(&self) -> PlutusData { - PlutusData::BigInt(BigInt::Int(self.clone())) + PlutusData::BigInt(BigInt::Int(*self)) } } @@ -679,7 +603,7 @@ pub enum TxOut { V2(TransactionOutput), } -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum ScriptPurpose { Minting(PolicyId), Spending(TransactionInput), @@ -748,14 +672,12 @@ fn slot_to_begin_posix_time(slot: u64, sc: &SlotConfig) -> u64 { fn slot_range_to_posix_time_range(slot_range: TimeRange, sc: &SlotConfig) -> TimeRange { TimeRange { - lower_bound: match slot_range.lower_bound { - Some(lower_bound) => Some(slot_to_begin_posix_time(lower_bound, sc)), - None => None, - }, - upper_bound: match slot_range.upper_bound { - Some(upper_bound) => Some(slot_to_begin_posix_time(upper_bound, sc)), - None => None, - }, + 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)), } } @@ -771,12 +693,12 @@ enum ExecutionPurpose { NoDatum(ScriptVersion), // Minting, Wdrl, DCert } -struct DataLookupTable { +pub struct DataLookupTable { datum: HashMap, scripts: HashMap, } -fn get_tx_in_info_v1( +pub fn get_tx_in_info_v1( inputs: &[TransactionInput], utxos: &[ResolvedInput], ) -> anyhow::Result> { @@ -804,12 +726,10 @@ fn get_tx_in_info_v1( match &utxo.output { TransactionOutput::Legacy(_) => {} TransactionOutput::PostAlonzo(output) => { - match output.datum_option { - Some(DatumOption::Data(_)) => { - unreachable!("Inline datum not allowed in PlutusV1.") - } - _ => {} - }; + if let Some(DatumOption::Data(_)) = output.datum_option { + unreachable!("Inline datum not allowed in PlutusV1.") + } + if output.script_ref.is_some() { unreachable!("Reference scripts not allowed in PlutusV1.") } @@ -876,20 +796,17 @@ fn get_script_purpose( .as_ref() .unwrap_or(&KeyValuePairs::Indef(vec![])) .iter() - .map(|(policy_id, _)| policy_id.clone()) + .map(|(policy_id, _)| *policy_id) .collect::>(); policy_ids.sort(); match policy_ids.get(index as usize) { - Some(policy_id) => Ok(ScriptPurpose::Minting(policy_id.clone())), + Some(policy_id) => Ok(ScriptPurpose::Minting(*policy_id)), None => unreachable!("Script purpose not found for redeemer."), } } RedeemerTag::Spend => { // sort lexical by tx_hash and index - let mut inputs = inputs - .iter() - .map(|input| input.clone()) - .collect::>(); + let mut inputs = inputs.to_vec(); // is this correct? Does this sort lexical from low to high? maybe get Ordering into pallas for TransactionInput? inputs.sort_by( |i_a, i_b| match i_a.transaction_id.cmp(&i_b.transaction_id) { @@ -920,7 +837,7 @@ fn get_script_purpose( let credential = match addresss { Address::Stake(stake_address) => match stake_address.payload() { StakePayload::Script(script_hash) => { - StakeCredential::Scripthash(script_hash.clone()) + StakeCredential::Scripthash(*script_hash) } StakePayload::Stake(_) => { unreachable!( @@ -950,7 +867,7 @@ fn get_script_purpose( fn get_tx_info_v1( tx: &MintedTx, - utxos: &MaybeIndefArray, + utxos: &[ResolvedInput], slot_config: &SlotConfig, ) -> anyhow::Result { let body = tx.transaction_body.clone(); @@ -961,45 +878,39 @@ fn get_tx_info_v1( let inputs = get_tx_in_info_v1(&body.inputs, &utxos)?; - let outputs = - body.outputs - .iter() - .map(|output| TxOut::V1(output.clone())) - .collect(); + 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 + let dcert = body.certificates.clone().unwrap_or_default(); + let wdrl = body + .withdrawals .clone() - .unwrap_or(vec![]); - let wdrl = - body.withdrawals - .clone() - .unwrap_or(KeyValuePairs::Indef(vec![])) - .deref() - .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, + slot_config, ); - let signatories = body - .required_signers - .clone() - .unwrap_or(vec![]); + 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.unwrap())) - .collect(); + 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(); @@ -1019,30 +930,22 @@ fn get_tx_info_v1( fn get_tx_info_v2( tx: &MintedTx, - utxos: &MaybeIndefArray, + utxos: &[ResolvedInput], slot_config: &SlotConfig, ) -> anyhow::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(vec![]), - &utxos, - )?; - let outputs = - body.outputs - .iter() - .map(|output| TxOut::V2(output.clone())) - .collect(); + 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(vec![]); + let dcert = body.certificates.clone().unwrap_or_default(); let wdrl = body .withdrawals .clone() @@ -1052,12 +955,9 @@ fn get_tx_info_v2( lower_bound: body.validity_interval_start, upper_bound: body.ttl, }, - &slot_config, + slot_config, ); - let signatories = body - .required_signers - .clone() - .unwrap_or(vec![]); + let signatories = body.required_signers.clone().unwrap_or_default(); let redeemers = KeyValuePairs::Indef( tx.transaction_witness_set .redeemer @@ -1067,7 +967,7 @@ fn get_tx_info_v2( .map(|r| { ( get_script_purpose( - &r, + r, &tx.transaction_body.inputs, &tx.transaction_body.mint, &tx.transaction_body.certificates, @@ -1085,7 +985,7 @@ fn get_tx_info_v2( .as_ref() .unwrap_or(&vec![]) .iter() - .map(|d| (d.original_hash(), d.unwrap())) + .map(|d| (d.original_hash(), d.clone().unwrap())) .collect(), ); let id = tx.transaction_body.compute_hash(); @@ -1107,7 +1007,7 @@ fn get_tx_info_v2( } fn get_execution_purpose( - utxos: &MaybeIndefArray, + utxos: &[ResolvedInput], script_purpose: &ScriptPurpose, lookup_table: &DataLookupTable, ) -> ExecutionPurpose { @@ -1120,7 +1020,7 @@ fn get_execution_purpose( Some(s) => s.clone(), None => unreachable!("Missing required scripts.") }; - ExecutionPurpose::NoDatum(script.clone()) + ExecutionPurpose::NoDatum(script) } ScriptPurpose::Spending(out_ref) => { let utxo = utxos.iter().find(|utxo| utxo.input == *out_ref).unwrap(); @@ -1129,22 +1029,19 @@ fn get_execution_purpose( let address = Address::from_bytes(&output.address).unwrap(); match address { Address::Shelley(shelley_address) => { - let script = match lookup_table .scripts - .get(&shelley_address.payment().as_hash()) { + .get(shelley_address.payment().as_hash()) { Some(s) => s.clone(), None => unreachable!("Missing required scripts.") }; - - let datum = match lookup_table.datum.get(&output.datum_hash.unwrap_or_else(|| unreachable!("Missing datum hash in input."))) { Some(d) => d.clone(), None => unreachable!("Missing datum in witness set.") }; - ExecutionPurpose::WithDatum(script.clone(), datum.clone()) + ExecutionPurpose::WithDatum(script, datum) } _ => unreachable!( "This is impossible. Only shelley addresses can contain a script hash." @@ -1158,7 +1055,7 @@ fn get_execution_purpose( let script = match lookup_table .scripts - .get(&shelley_address.payment().as_hash()) { + .get(shelley_address.payment().as_hash()) { Some(s) => s.clone(), None => unreachable!("Missing required scripts.") }; @@ -1166,13 +1063,13 @@ fn get_execution_purpose( let datum = match &output.datum_option { Some(DatumOption::Hash(hash)) => { - lookup_table.datum.get(&hash).unwrap().clone() + lookup_table.datum.get(hash).unwrap().clone() } Some(DatumOption::Data(data)) => data.0.clone(), _ => unreachable!( "Missing datum hash or inline datum in input."), }; - ExecutionPurpose::WithDatum(script.clone(), datum) + ExecutionPurpose::WithDatum(script, datum) } _ => unreachable!( "This is impossible. Only shelley addresses can contain a script hash." @@ -1183,7 +1080,7 @@ fn get_execution_purpose( } ScriptPurpose::Rewarding(stake_credential) => { let script_hash = match stake_credential { - StakeCredential::Scripthash(hash) => hash.clone(), + StakeCredential::Scripthash(hash) => *hash, _ => unreachable!("This is impossible. A key hash cannot be the hash of a script."), }; @@ -1198,7 +1095,7 @@ fn get_execution_purpose( // StakeRegistration doesn't require a witness from a stake key/script. So I assume it doesn't need to be handled in Plutus either? Certificate::StakeDeregistration(stake_credential) => { let script_hash = match stake_credential { - StakeCredential::Scripthash(hash) => hash.clone(), + StakeCredential::Scripthash(hash) => *hash, _ => unreachable!( "This is impossible. A key hash cannot be the hash of a script." ), @@ -1213,7 +1110,7 @@ fn get_execution_purpose( } Certificate::StakeDelegation(stake_credential, _) => { let script_hash = match stake_credential { - StakeCredential::Scripthash(hash) => hash.clone(), + StakeCredential::Scripthash(hash) => *hash, _ => unreachable!( "This is impossible. A key hash cannot be the hash of a script." ), @@ -1231,10 +1128,7 @@ fn get_execution_purpose( } } -fn get_script_and_datum_lookup_table( - tx: &MintedTx, - utxos: &MaybeIndefArray, -) -> DataLookupTable { +fn get_script_and_datum_lookup_table(tx: &MintedTx, utxos: &[ResolvedInput]) -> DataLookupTable { let mut datum = HashMap::new(); let mut scripts = HashMap::new(); @@ -1244,30 +1138,32 @@ fn get_script_and_datum_lookup_table( .transaction_witness_set .plutus_data .clone() - .unwrap_or(vec![]); + .unwrap_or_default(); let scripts_v1_witnesses = tx .transaction_witness_set .plutus_v1_script .clone() - .unwrap_or(vec![]); + .unwrap_or_default(); let scripts_v2_witnesses = tx .transaction_witness_set .plutus_v2_script .clone() - .unwrap_or(vec![]); + .unwrap_or_default(); for plutus_data in plutus_data_witnesses.iter() { - datum.insert(plutus_data.original_hash(), plutus_data.clone()); + datum.insert(plutus_data.original_hash(), plutus_data.clone().unwrap()); } for script in scripts_v1_witnesses.iter() { - scripts.insert(script.compute_hash(), ScriptVersion::V1(script.clone())); // TODO: fix hashing bug in pallas + scripts.insert(script.compute_hash(), ScriptVersion::V1(script.clone())); + // TODO: fix hashing bug in pallas } for script in scripts_v2_witnesses.iter() { - scripts.insert(script.compute_hash(), ScriptVersion::V2(script.clone())); // TODO: fix hashing bug in pallas + scripts.insert(script.compute_hash(), ScriptVersion::V2(script.clone())); + // TODO: fix hashing bug in pallas } // discovery in utxos (script ref) @@ -1275,38 +1171,28 @@ fn get_script_and_datum_lookup_table( for utxo in utxos.iter() { match &utxo.output { TransactionOutput::Legacy(_) => {} - TransactionOutput::PostAlonzo(output) => match &output.script_ref { - Some(script) => match &script.0 { - Script::PlutusV1Script(v1) => { - // scripts.insert(v1.to_hash(), ScriptVersion::PlutusV1(v1.clone())); // TODO: fix hashing bug in pallas - let mut prefixed_script: Vec = vec![0x01]; - prefixed_script.extend(v1.0.iter()); - - let hash = Hasher::<224>::hash(&prefixed_script); - scripts.insert(hash, ScriptVersion::V1(v1.clone())); + TransactionOutput::PostAlonzo(output) => { + if let Some(script) = &output.script_ref { + match &script.0 { + Script::PlutusV1Script(v1) => { + scripts.insert(v1.compute_hash(), ScriptVersion::V1(v1.clone())); + } + Script::PlutusV2Script(v2) => { + scripts.insert(v2.compute_hash(), ScriptVersion::V2(v2.clone())); + } + _ => {} } - Script::PlutusV2Script(v2) => { - // scripts.insert(v2.to_hash(), ScriptVersion::PlutusV2(v2.clone())); // TODO: fix hashing bug in pallas - - let mut prefixed_script: Vec = vec![0x02]; - prefixed_script.extend(v2.0.iter()); - - let hash = Hasher::<224>::hash(&prefixed_script); - scripts.insert(hash, ScriptVersion::V2(v2.clone())); - } - _ => {} - }, - _ => {} - }, + } + } } } DataLookupTable { datum, scripts } } -fn eval_redeemer( +pub fn eval_redeemer( tx: &MintedTx, - utxos: &MaybeIndefArray, + utxos: &[ResolvedInput], slot_config: &SlotConfig, redeemer: &Redeemer, lookup_table: &DataLookupTable, @@ -1336,7 +1222,7 @@ fn eval_redeemer( }; let result = program - .apply_data(datum.clone()) + .apply_data(datum) .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()) .eval(); @@ -1371,7 +1257,7 @@ fn eval_redeemer( }; let result = program - .apply_data(datum.clone()) + .apply_data(datum) .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()) .eval(); @@ -1467,12 +1353,12 @@ fn eval_redeemer( } } -fn eval_tx( +pub fn eval_tx( tx: &MintedTx, - utxos: &MaybeIndefArray, + utxos: &[ResolvedInput], //TODO: costMdls slot_config: &SlotConfig, -) -> anyhow::Result> { +) -> anyhow::Result> { let redeemers = tx.transaction_witness_set.redeemer.as_ref(); let lookup_table = get_script_and_datum_lookup_table(tx, utxos); @@ -1489,9 +1375,9 @@ fn eval_tx( &lookup_table, )?) } - Ok(MaybeIndefArray::Indef(collected_redeemers)) + Ok(collected_redeemers) } - None => Ok(MaybeIndefArray::Indef(vec![])), + None => Ok(vec![]), } } @@ -1500,12 +1386,11 @@ mod tests { use pallas_codec::utils::MaybeIndefArray; use pallas_primitives::{ babbage::{TransactionInput, TransactionOutput}, - Fragment, ToHash, + Fragment, }; use pallas_traverse::{Era, MultiEraTx}; - use uplc::PlutusData; - use super::{eval_tx, ResolvedInput, SlotConfig, TxInInfo}; + use super::{eval_tx, ResolvedInput, SlotConfig}; #[test] fn test_eval() { @@ -1526,19 +1411,17 @@ mod tests { 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 inputs = Vec::::decode_fragment(&raw_inputs).unwrap(); + let outputs = Vec::::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 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 @@ -1552,7 +1435,7 @@ mod tests { MultiEraTx::Babbage(tx) => { let redeemers = eval_tx(&tx, &utxos, &slot_config).unwrap(); - println!("{:?}", redeemers.len()); + assert_eq!(redeemers.len(), 1) } _ => unreachable!(), }; From ddf3cdb6ec521d570667c18b65b60dc5c29cf37a Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 15 Sep 2022 11:29:08 -0400 Subject: [PATCH 40/77] chore: use alpha 3 instead of main --- Cargo.lock | 25 +++++++++++++++---------- crates/cli/Cargo.toml | 10 +++++----- crates/uplc/Cargo.toml | 4 ++-- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f9282db..f05144b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -290,8 +290,9 @@ checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435" [[package]] name = "pallas-addresses" -version = "0.14.0-alpha.2" -source = "git+https://github.com/txpipe/pallas#ac25b48797572925ba48ccb0a8f4603410613a75" +version = "0.14.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92140c1ffe3d3b71ad41c3879a506e34feb6ea42e5d75e02285c1483dd4a28b6" dependencies = [ "base58", "bech32", @@ -303,8 +304,9 @@ dependencies = [ [[package]] name = "pallas-codec" -version = "0.14.0-alpha.2" -source = "git+https://github.com/txpipe/pallas#ac25b48797572925ba48ccb0a8f4603410613a75" +version = "0.14.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "749de2b1953b806a9f3e2b7d6cfee48a474c709b160eb143e34ab62a9a8eac97" dependencies = [ "hex", "minicbor", @@ -313,8 +315,9 @@ dependencies = [ [[package]] name = "pallas-crypto" -version = "0.14.0-alpha.2" -source = "git+https://github.com/txpipe/pallas#ac25b48797572925ba48ccb0a8f4603410613a75" +version = "0.14.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cb57716e86f5c131ee8da655361dd8b9ec83a23c92c2a2ad3fb75352936fd5d" dependencies = [ "cryptoxide", "hex", @@ -326,8 +329,9 @@ dependencies = [ [[package]] name = "pallas-primitives" -version = "0.14.0-alpha.2" -source = "git+https://github.com/txpipe/pallas#ac25b48797572925ba48ccb0a8f4603410613a75" +version = "0.14.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d0711ac752a723c39c02204bf70de9173a83f2de29a2356256dd3b85ce7fd8a" dependencies = [ "base58", "bech32", @@ -341,8 +345,9 @@ dependencies = [ [[package]] name = "pallas-traverse" -version = "0.14.0-alpha.2" -source = "git+https://github.com/txpipe/pallas#ac25b48797572925ba48ccb0a8f4603410613a75" +version = "0.14.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aa8f851594c1e23f95ff533824fa8cff2c012d99fbcdcf82d1006237411d582" dependencies = [ "hex", "pallas-addresses", diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index c9a0a452..1590c381 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -14,11 +14,11 @@ authors = ["Lucas Rosa ", "Kasey White "] anyhow = "1.0.57" clap = { version = "3.1.14", features = ["derive"] } hex = "0.4.3" -pallas-addresses = { git = "https://github.com/txpipe/pallas" } -pallas-codec = { git = "https://github.com/txpipe/pallas" } -pallas-crypto = { git = "https://github.com/txpipe/pallas" } -pallas-primitives = { git = "https://github.com/txpipe/pallas" } -pallas-traverse = { git = "https://github.com/txpipe/pallas" } +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.12" } diff --git a/crates/uplc/Cargo.toml b/crates/uplc/Cargo.toml index 5ecce951..d2df1399 100644 --- a/crates/uplc/Cargo.toml +++ b/crates/uplc/Cargo.toml @@ -16,8 +16,8 @@ exclude = ["test_data/*"] cryptoxide = "0.4.2" flat-rs = { path = "../flat", version = "0.0.10" } hex = "0.4.3" -pallas-codec = { git = "https://github.com/txpipe/pallas" } -pallas-primitives = { git = "https://github.com/txpipe/pallas" } +pallas-codec = "0.14.0-alpha.3" +pallas-primitives = "0.14.0-alpha.3" peg = "0.8.0" pretty = "0.11.3" thiserror = "1.0.31" From 3f27bd9f13fca63a5eee62d3b4ce7d1dd9e81960 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Fri, 16 Sep 2022 04:00:29 -0400 Subject: [PATCH 41/77] move utils to uplc and break up --- Cargo.lock | 5 + crates/cli/src/main.rs | 10 +- crates/cli/src/utils.rs | 1545 ----------------- crates/uplc/Cargo.toml | 5 + crates/uplc/src/lib.rs | 1 + crates/uplc/src/transaction_eval.rs | 198 +++ .../src/transaction_eval/script_context.rs | 85 + .../src/transaction_eval/to_plutus_data.rs | 583 +++++++ crates/uplc/src/transaction_eval/utils.rs | 715 ++++++++ 9 files changed, 1596 insertions(+), 1551 deletions(-) delete mode 100644 crates/cli/src/utils.rs create mode 100644 crates/uplc/src/transaction_eval.rs create mode 100644 crates/uplc/src/transaction_eval/script_context.rs create mode 100644 crates/uplc/src/transaction_eval/to_plutus_data.rs create mode 100644 crates/uplc/src/transaction_eval/utils.rs diff --git a/Cargo.lock b/Cargo.lock index f05144b5..e123a658 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -676,14 +676,19 @@ checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" name = "uplc" version = "0.0.12" dependencies = [ + "anyhow", "cryptoxide", "flat-rs", "hex", + "pallas-addresses", "pallas-codec", + "pallas-crypto", "pallas-primitives", + "pallas-traverse", "peg", "pretty", "proptest", + "serde_json", "thiserror", ] diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 027814aa..604c1fd8 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,7 +1,6 @@ use std::{ fmt::Write as _, - fs::{self, File}, - io::BufReader, + fs::{self}, }; use pallas_traverse::{Era, MultiEraTx}; @@ -12,12 +11,9 @@ use uplc::{ }; mod args; -mod utils; use args::{Args, TxCommand, UplcCommand}; -use crate::args::ResolvedInputOld; - fn main() -> anyhow::Result<()> { let args = Args::default(); @@ -26,7 +22,7 @@ fn main() -> anyhow::Result<()> { TxCommand::Simulate { input, cbor, - resolved_inputs, + resolved_inputs: _, } => { let tx_bytes = if cbor { fs::read(input)? @@ -40,6 +36,8 @@ fn main() -> anyhow::Result<()> { .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes))?; println!("Simulating: {}", tx.hash()); + + } }, Args::Uplc(uplc_cmd) => match uplc_cmd { diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs deleted file mode 100644 index 9227f028..00000000 --- a/crates/cli/src/utils.rs +++ /dev/null @@ -1,1545 +0,0 @@ -use pallas_addresses::{ - Address, ScriptHash, ShelleyDelegationPart, ShelleyPaymentPart, StakePayload, -}; -use pallas_codec::utils::{AnyUInt, Bytes, Int, KeyValuePairs, MaybeIndefArray}; -use pallas_crypto::hash::Hash; -use pallas_primitives::babbage::{ - AddrKeyhash, AssetName, BigInt, Certificate, Coin, Constr, DatumHash, DatumOption, ExUnits, - Mint, MintedTx, PlutusV1Script, PlutusV2Script, PolicyId, Redeemer, RedeemerTag, RewardAccount, - Script, ScriptRef, StakeCredential, TransactionInput, TransactionOutput, Value, Withdrawals, -}; -use pallas_traverse::{ComputeHash, OriginalHash}; -use std::{collections::HashMap, convert::TryInto, ops::Deref, vec}; -use uplc::{ - ast::{FakeNamedDeBruijn, NamedDeBruijn, Program}, - machine::cost_model::ExBudget, - PlutusData, -}; - -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; -} - -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![( - PolicyId::from([0; 28]).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![( - PolicyId::from([0; 28]).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 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), - } - } -} - -#[derive(Debug, PartialEq, Clone)] -pub struct ResolvedInput { - input: TransactionInput, - output: TransactionOutput, -} - -#[derive(Debug, PartialEq, Clone)] -pub struct TxInInfo { - out_ref: TransactionInput, - 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 { - inputs: Vec, - outputs: Vec, - fee: Value, - mint: Mint, - dcert: Vec, - wdrl: Vec<(RewardAccount, Coin)>, - valid_range: TimeRange, - signatories: Vec, - data: Vec<(DatumHash, PlutusData)>, - id: Hash<32>, -} - -#[derive(Debug, PartialEq, Clone)] -pub struct TxInfoV2 { - inputs: Vec, - reference_inputs: Vec, - outputs: Vec, - fee: Value, - mint: Mint, - dcert: Vec, - wdrl: Withdrawals, - valid_range: TimeRange, - signatories: Vec, - redeemers: KeyValuePairs, - data: KeyValuePairs, - id: Hash<32>, -} - -#[derive(Debug, PartialEq, Clone)] -pub enum TxInfo { - V1(TxInfoV1), - V2(TxInfoV2), -} - -#[derive(Debug, PartialEq, Clone)] -pub struct ScriptContext { - tx_info: TxInfo, - purpose: ScriptPurpose, -} - -//---- Time conversion: slot range => posix time range -#[derive(Debug, PartialEq, Clone)] -struct TimeRange { - lower_bound: Option, - upper_bound: Option, -} - -pub struct SlotConfig { - slot_length: u64, - zero_time: u64, -} - -fn slot_to_begin_posix_time(slot: u64, sc: &SlotConfig) -> u64 { - let ms_after_begin = 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)] -enum ScriptVersion { - 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, -} - -pub fn get_tx_in_info_v1( - inputs: &[TransactionInput], - utxos: &[ResolvedInput], -) -> anyhow::Result> { - let result = inputs - .iter() - .map(|input| { - let utxo = match utxos.iter().find(|utxo| utxo.input == *input) { - Some(u) => u, - None => unreachable!("Resolved input not found."), - }; - 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(_) => unreachable!("Byron addresses not supported in Plutus."), - Address::Stake(_) => { - unreachable!("This is impossible. A stake address cannot lock a UTxO.") - } - _ => {} - } - - match &utxo.output { - TransactionOutput::Legacy(_) => {} - TransactionOutput::PostAlonzo(output) => { - if let Some(DatumOption::Data(_)) = output.datum_option { - unreachable!("Inline datum not allowed in PlutusV1.") - } - - if output.script_ref.is_some() { - unreachable!("Reference scripts not allowed in PlutusV1.") - } - } - } - - TxInInfo { - out_ref: utxo.input.clone(), - resolved: TxOut::V1(utxo.output.clone()), - } - }) - .collect::>(); - Ok(result) -} - -fn get_tx_in_info_v2( - inputs: &[TransactionInput], - utxos: &[ResolvedInput], -) -> anyhow::Result> { - let result = inputs - .iter() - .map(|input| { - let utxo = match utxos.iter().find(|utxo| utxo.input == *input) { - Some(u) => u, - None => unreachable!("Resolved input not found."), - }; - 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(_) => unreachable!("Byron addresses not supported in Plutus."), - Address::Stake(_) => { - unreachable!("This is impossible. A stake address cannot lock a UTxO.") - } - _ => {} - } - - TxInInfo { - out_ref: utxo.input.clone(), - resolved: TxOut::V2(utxo.output.clone()), - } - }) - .collect::>(); - Ok(result) -} - -fn get_script_purpose( - redeemer: &Redeemer, - inputs: &[TransactionInput], - mint: &Option, - dcert: &Option>, - wdrl: &Option, -) -> anyhow::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 => unreachable!("Script purpose not found for redeemer."), - } - } - RedeemerTag::Spend => { - // sort lexical by tx_hash and index - let mut inputs = inputs.to_vec(); - // is this correct? Does this sort lexical from low to high? maybe get Ordering into pallas for TransactionInput? - 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, - }, - ); - match inputs.get(index as usize) { - Some(input) => Ok(ScriptPurpose::Spending(input.clone())), - None => unreachable!("Script purpose not found for redeemer."), - } - } - RedeemerTag::Reward => { - // sort lexical by reward account - let mut reward_accounts = wdrl - .as_ref() - .unwrap_or(&KeyValuePairs::Indef(vec![])) - .iter() - .map(|(policy_id, _)| policy_id.clone()) - .collect::>(); - reward_accounts.sort(); - let reward_account = match reward_accounts.get(index as usize) { - Some(ra) => ra.clone(), - None => unreachable!("Script purpose not found for redeemer."), - }; - let addresss = Address::from_bytes(&reward_account)?; - let credential = match addresss { - Address::Stake(stake_address) => match stake_address.payload() { - StakePayload::Script(script_hash) => { - StakeCredential::Scripthash(*script_hash) - } - StakePayload::Stake(_) => { - unreachable!( - "This is impossible. A key hash cannot be the hash of a script." - ); - } - }, - _ => unreachable!( - "This is impossible. Only shelley reward addresses can be a part of withdrawals." - ), - }; - 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 => unreachable!("Script purpose not found for redeemer."), - } - } - } -} - -fn get_tx_info_v1( - tx: &MintedTx, - utxos: &[ResolvedInput], - slot_config: &SlotConfig, -) -> anyhow::Result { - let body = tx.transaction_body.clone(); - - if body.reference_inputs.is_some() { - unreachable!("Reference inputs not allowed in PlutusV1.") - } - - 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, - dcert, - wdrl, - valid_range, - signatories, - data, - id, - })) -} - -fn get_tx_info_v2( - tx: &MintedTx, - utxos: &[ResolvedInput], - slot_config: &SlotConfig, -) -> anyhow::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, - dcert, - wdrl, - valid_range, - signatories, - redeemers, - data, - id, - })) -} - -fn get_execution_purpose( - utxos: &[ResolvedInput], - script_purpose: &ScriptPurpose, - lookup_table: &DataLookupTable, -) -> ExecutionPurpose { - 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 => unreachable!("Missing required scripts.") - }; - ExecutionPurpose::NoDatum(script) - } - ScriptPurpose::Spending(out_ref) => { - let utxo = utxos.iter().find(|utxo| utxo.input == *out_ref).unwrap(); - match &utxo.output { - TransactionOutput::Legacy(output) => { - let address = Address::from_bytes(&output.address).unwrap(); - match address { - Address::Shelley(shelley_address) => { - let script = match lookup_table - .scripts - .get(shelley_address.payment().as_hash()) { - Some(s) => s.clone(), - None => unreachable!("Missing required scripts.") - }; - - let datum = match lookup_table.datum.get(&output.datum_hash.unwrap_or_else(|| unreachable!("Missing datum hash in input."))) { - Some(d) => d.clone(), - None => unreachable!("Missing datum in witness set.") - }; - - ExecutionPurpose::WithDatum(script, datum) - } - _ => unreachable!( - "This is impossible. Only shelley addresses can contain a script hash." - ), - } - } - TransactionOutput::PostAlonzo(output) => { - let address = Address::from_bytes(&output.address).unwrap(); - match address { - Address::Shelley(shelley_address) => { - - let script = match lookup_table - .scripts - .get(shelley_address.payment().as_hash()) { - Some(s) => s.clone(), - None => unreachable!("Missing required scripts.") - }; - - - let datum = match &output.datum_option { - Some(DatumOption::Hash(hash)) => { - lookup_table.datum.get(hash).unwrap().clone() - } - Some(DatumOption::Data(data)) => data.0.clone(), - _ => unreachable!( "Missing datum hash or inline datum in input."), - }; - - ExecutionPurpose::WithDatum(script, datum) - } - _ => unreachable!( - "This is impossible. Only shelley addresses can contain a script hash." - ), - } - } - } - } - ScriptPurpose::Rewarding(stake_credential) => { - let script_hash = match stake_credential { - StakeCredential::Scripthash(hash) => *hash, - _ => unreachable!("This is impossible. A key hash cannot be the hash of a script."), - }; - - let script = match lookup_table.scripts.get(&script_hash) { - Some(s) => s.clone(), - None => unreachable!("Missing required scripts.") - }; - - ExecutionPurpose::NoDatum(script) - } - ScriptPurpose::Certifying(cert) => match cert { - // StakeRegistration doesn't require a witness from a stake key/script. So I assume it doesn't need to be handled in Plutus either? - Certificate::StakeDeregistration(stake_credential) => { - let script_hash = match stake_credential { - StakeCredential::Scripthash(hash) => *hash, - _ => unreachable!( - "This is impossible. A key hash cannot be the hash of a script." - ), - }; - - let script = match lookup_table.scripts.get(&script_hash) { - Some(s) => s.clone(), - None => unreachable!("Missing required scripts.") - }; - - ExecutionPurpose::NoDatum(script) - } - Certificate::StakeDelegation(stake_credential, _) => { - let script_hash = match stake_credential { - StakeCredential::Scripthash(hash) => *hash, - _ => unreachable!( - "This is impossible. A key hash cannot be the hash of a script." - ), - }; - - let script = match lookup_table.scripts.get(&script_hash) { - Some(s) => s.clone(), - None => unreachable!("Missing required scripts.") - }; - - ExecutionPurpose::NoDatum(script) - } - _ => unreachable!("This is impossible. Only stake deregistration and stake delegation are valid script purposes."), - }, - } -} - -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_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_v1_witnesses.iter() { - scripts.insert(script.compute_hash(), ScriptVersion::V1(script.clone())); - // TODO: fix hashing bug in pallas - } - - for script in scripts_v2_witnesses.iter() { - scripts.insert(script.compute_hash(), ScriptVersion::V2(script.clone())); - // TODO: fix hashing bug in pallas - } - - // 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::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, -) -> anyhow::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 result = program - .apply_data(datum) - .apply_data(redeemer.data.clone()) - .apply_data(script_context.to_plutus_data()) - .eval(); - - match result.0 { - Ok(_) => {} - Err(err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message - } - - let new_redeemer = Redeemer { - tag: redeemer.tag.clone(), - index: redeemer.index, - data: redeemer.data.clone(), - ex_units: ExUnits { - mem: (ExBudget::default().mem - result.1.mem) as u32, - steps: (ExBudget::default().cpu - result.1.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 result = program - .apply_data(datum) - .apply_data(redeemer.data.clone()) - .apply_data(script_context.to_plutus_data()) - .eval(); - - match result.0 { - Ok(_) => {} - Err(err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message - } - - let new_redeemer = Redeemer { - tag: redeemer.tag.clone(), - index: redeemer.index, - data: redeemer.data.clone(), - ex_units: ExUnits { - mem: (ExBudget::default().mem - result.1.mem) as u32, - steps: (ExBudget::default().cpu - result.1.cpu) as u64, - }, - }; - - Ok(new_redeemer) - } - }, - 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 result = program - .apply_data(redeemer.data.clone()) - .apply_data(script_context.to_plutus_data()) - .eval(); - - match result.0 { - Ok(_) => {} - Err(err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message - } - - let new_redeemer = Redeemer { - tag: redeemer.tag.clone(), - index: redeemer.index, - data: redeemer.data.clone(), - ex_units: ExUnits { - mem: (ExBudget::default().mem - result.1.mem) as u32, - steps: (ExBudget::default().cpu - result.1.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 result = program - .apply_data(redeemer.data.clone()) - .apply_data(script_context.to_plutus_data()) - .eval(); - - match result.0 { - Ok(_) => {} - Err(err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message - } - - let new_redeemer = Redeemer { - tag: redeemer.tag.clone(), - index: redeemer.index, - data: redeemer.data.clone(), - ex_units: ExUnits { - mem: (ExBudget::default().mem - result.1.mem) as u32, - steps: (ExBudget::default().cpu - result.1.cpu) as u64, - }, - }; - - Ok(new_redeemer) - } - }, - } -} - -pub fn eval_tx( - tx: &MintedTx, - utxos: &[ResolvedInput], - //TODO: costMdls - slot_config: &SlotConfig, -) -> anyhow::Result> { - let redeemers = tx.transaction_witness_set.redeemer.as_ref(); - - let lookup_table = get_script_and_datum_lookup_table(tx, utxos); - - match redeemers { - Some(rs) => { - let mut collected_redeemers = vec![]; - for redeemer in rs.iter() { - collected_redeemers.push(eval_redeemer( - tx, - utxos, - slot_config, - &redeemer, - &lookup_table, - )?) - } - Ok(collected_redeemers) - } - None => Ok(vec![]), - } -} - -#[cfg(test)] -mod tests { - use pallas_codec::utils::MaybeIndefArray; - use pallas_primitives::{ - babbage::{TransactionInput, TransactionOutput}, - Fragment, - }; - use pallas_traverse::{Era, MultiEraTx}; - - use super::{eval_tx, 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 - slot_length: 1000, - }; - - 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_tx(&tx, &utxos, &slot_config).unwrap(); - - assert_eq!(redeemers.len(), 1) - } - _ => unreachable!(), - }; - } - - #[test] - fn test_eval_1() { - /* - - 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 - slot_length: 1000, - }; - - 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_tx(&tx, &utxos, &slot_config).unwrap(); - - println!("{:?}", redeemers.len()); - } - _ => unreachable!(), - }; - } - - #[test] - fn test_eval_2() { - /* - - 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 - slot_length: 1000, - }; - - 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_tx(&tx, &utxos, &slot_config).unwrap(); - - println!("{:?}", redeemers.len()); - } - _ => unreachable!(), - }; - } -} diff --git a/crates/uplc/Cargo.toml b/crates/uplc/Cargo.toml index d2df1399..4ebe5993 100644 --- a/crates/uplc/Cargo.toml +++ b/crates/uplc/Cargo.toml @@ -16,11 +16,16 @@ exclude = ["test_data/*"] cryptoxide = "0.4.2" flat-rs = { path = "../flat", version = "0.0.10" } 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" peg = "0.8.0" pretty = "0.11.3" thiserror = "1.0.31" +anyhow = "1.0.57" +serde_json = "1.0.85" [dev-dependencies] hex = "0.4.3" diff --git a/crates/uplc/src/lib.rs b/crates/uplc/src/lib.rs index 64ee0971..5c14dd07 100644 --- a/crates/uplc/src/lib.rs +++ b/crates/uplc/src/lib.rs @@ -6,6 +6,7 @@ pub mod machine; pub mod parser; mod pretty; pub mod program_builder; +pub mod transaction_eval; pub use pallas_primitives::alonzo::PlutusData; pub type Error = Box; diff --git a/crates/uplc/src/transaction_eval.rs b/crates/uplc/src/transaction_eval.rs new file mode 100644 index 00000000..941a48c6 --- /dev/null +++ b/crates/uplc/src/transaction_eval.rs @@ -0,0 +1,198 @@ +use pallas_primitives::babbage::{MintedTx, Redeemer}; + +use self::script_context::{ResolvedInput, SlotConfig}; + +mod script_context; +mod to_plutus_data; +mod utils; + +pub fn eval_tx( + tx: &MintedTx, + utxos: &[ResolvedInput], + //TODO: costMdls + slot_config: &SlotConfig, +) -> anyhow::Result> { + let redeemers = tx.transaction_witness_set.redeemer.as_ref(); + + let lookup_table = utils::get_script_and_datum_lookup_table(tx, utxos); + + match redeemers { + Some(rs) => { + let mut collected_redeemers = vec![]; + for redeemer in rs.iter() { + collected_redeemers.push(utils::eval_redeemer( + tx, + utxos, + slot_config, + redeemer, + &lookup_table, + )?) + } + Ok(collected_redeemers) + } + None => Ok(vec![]), + } +} + +#[cfg(test)] +mod tests { + use pallas_codec::utils::MaybeIndefArray; + use pallas_primitives::{ + babbage::{TransactionInput, TransactionOutput}, + Fragment, + }; + use pallas_traverse::{Era, MultiEraTx}; + + use super::{eval_tx, 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 + slot_length: 1000, + }; + + 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_tx(&tx, &utxos, &slot_config).unwrap(); + + assert_eq!(redeemers.len(), 1) + } + _ => unreachable!(), + }; + } + + #[test] + fn test_eval_1() { + /* + + 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 + slot_length: 1000, + }; + + 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_tx(&tx, &utxos, &slot_config).unwrap(); + + println!("{:?}", redeemers.len()); + } + _ => unreachable!(), + }; + } + + #[test] + fn test_eval_2() { + /* + + 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 + slot_length: 1000, + }; + + 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_tx(&tx, &utxos, &slot_config).unwrap(); + + println!("{:?}", redeemers.len()); + } + _ => unreachable!(), + }; + } +} diff --git a/crates/uplc/src/transaction_eval/script_context.rs b/crates/uplc/src/transaction_eval/script_context.rs new file mode 100644 index 00000000..1f8eb9d8 --- /dev/null +++ b/crates/uplc/src/transaction_eval/script_context.rs @@ -0,0 +1,85 @@ +use pallas_codec::utils::KeyValuePairs; +use pallas_crypto::hash::Hash; +use pallas_primitives::babbage::{ + AddrKeyhash, Certificate, Coin, DatumHash, Mint, PlutusData, PolicyId, Redeemer, RewardAccount, + StakeCredential, TransactionInput, TransactionOutput, Value, Withdrawals, +}; + +#[derive(Debug, PartialEq, Clone)] +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: Mint, + 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: Mint, + 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, Clone)] +pub struct TimeRange { + pub lower_bound: Option, + pub upper_bound: Option, +} + +pub struct SlotConfig { + pub slot_length: u64, + pub zero_time: u64, +} diff --git a/crates/uplc/src/transaction_eval/to_plutus_data.rs b/crates/uplc/src/transaction_eval/to_plutus_data.rs new file mode 100644 index 00000000..570e2d16 --- /dev/null +++ b/crates/uplc/src/transaction_eval/to_plutus_data.rs @@ -0,0 +1,583 @@ +use pallas_addresses::{Address, ShelleyDelegationPart, ShelleyPaymentPart}; +use pallas_primitives::babbage::{AssetName, BigInt, Constr, PlutusData, ScriptRef}; + +use pallas_codec::utils::{AnyUInt, Bytes, Int, KeyValuePairs}; +use pallas_crypto::hash::Hash; +use pallas_primitives::babbage::{ + Certificate, DatumOption, PolicyId, Redeemer, Script, StakeCredential, TransactionInput, + TransactionOutput, Value, +}; +use pallas_traverse::ComputeHash; +use std::vec; + +use super::script_context::{TxOut, TimeRange, TxInInfo, ScriptPurpose, TxInfo, ScriptContext}; + +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; +} + +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![( + PolicyId::from([0; 28]).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![( + PolicyId::from([0; 28]).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 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), + } + } +} diff --git a/crates/uplc/src/transaction_eval/utils.rs b/crates/uplc/src/transaction_eval/utils.rs new file mode 100644 index 00000000..554e8832 --- /dev/null +++ b/crates/uplc/src/transaction_eval/utils.rs @@ -0,0 +1,715 @@ +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, DatumHash, DatumOption, ExUnits, Mint, MintedTx, + 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::ToPlutusData, +}; + +fn slot_to_begin_posix_time(slot: u64, sc: &SlotConfig) -> u64 { + let ms_after_begin = 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)] +enum ScriptVersion { + 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, +} + +pub fn get_tx_in_info_v1( + inputs: &[TransactionInput], + utxos: &[ResolvedInput], +) -> anyhow::Result> { + let result = inputs + .iter() + .map(|input| { + let utxo = match utxos.iter().find(|utxo| utxo.input == *input) { + Some(u) => u, + None => unreachable!("Resolved input not found."), + }; + 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(_) => unreachable!("Byron addresses not supported in Plutus."), + Address::Stake(_) => { + unreachable!("This is impossible. A stake address cannot lock a UTxO.") + } + _ => {} + } + + match &utxo.output { + TransactionOutput::Legacy(_) => {} + TransactionOutput::PostAlonzo(output) => { + if let Some(DatumOption::Data(_)) = output.datum_option { + unreachable!("Inline datum not allowed in PlutusV1.") + } + + if output.script_ref.is_some() { + unreachable!("Reference scripts not allowed in PlutusV1.") + } + } + } + + TxInInfo { + out_ref: utxo.input.clone(), + resolved: TxOut::V1(utxo.output.clone()), + } + }) + .collect::>(); + Ok(result) +} + +fn get_tx_in_info_v2( + inputs: &[TransactionInput], + utxos: &[ResolvedInput], +) -> anyhow::Result> { + let result = inputs + .iter() + .map(|input| { + let utxo = match utxos.iter().find(|utxo| utxo.input == *input) { + Some(u) => u, + None => unreachable!("Resolved input not found."), + }; + 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(_) => unreachable!("Byron addresses not supported in Plutus."), + Address::Stake(_) => { + unreachable!("This is impossible. A stake address cannot lock a UTxO.") + } + _ => {} + } + + TxInInfo { + out_ref: utxo.input.clone(), + resolved: TxOut::V2(utxo.output.clone()), + } + }) + .collect::>(); + Ok(result) +} + +fn get_script_purpose( + redeemer: &Redeemer, + inputs: &[TransactionInput], + mint: &Option, + dcert: &Option>, + wdrl: &Option, +) -> anyhow::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 => unreachable!("Script purpose not found for redeemer."), + } + } + RedeemerTag::Spend => { + // sort lexical by tx_hash and index + let mut inputs = inputs.to_vec(); + // is this correct? Does this sort lexical from low to high? maybe get Ordering into pallas for TransactionInput? + 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, + }, + ); + match inputs.get(index as usize) { + Some(input) => Ok(ScriptPurpose::Spending(input.clone())), + None => unreachable!("Script purpose not found for redeemer."), + } + } + RedeemerTag::Reward => { + // sort lexical by reward account + let mut reward_accounts = wdrl + .as_ref() + .unwrap_or(&KeyValuePairs::Indef(vec![])) + .iter() + .map(|(policy_id, _)| policy_id.clone()) + .collect::>(); + reward_accounts.sort(); + let reward_account = match reward_accounts.get(index as usize) { + Some(ra) => ra.clone(), + None => unreachable!("Script purpose not found for redeemer."), + }; + let addresss = Address::from_bytes(&reward_account)?; + let credential = match addresss { + Address::Stake(stake_address) => match stake_address.payload() { + StakePayload::Script(script_hash) => { + StakeCredential::Scripthash(*script_hash) + } + StakePayload::Stake(_) => { + unreachable!( + "This is impossible. A key hash cannot be the hash of a script." + ); + } + }, + _ => unreachable!( + "This is impossible. Only shelley reward addresses can be a part of withdrawals." + ), + }; + 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 => unreachable!("Script purpose not found for redeemer."), + } + } + } +} + +fn get_tx_info_v1( + tx: &MintedTx, + utxos: &[ResolvedInput], + slot_config: &SlotConfig, +) -> anyhow::Result { + let body = tx.transaction_body.clone(); + + if body.reference_inputs.is_some() { + unreachable!("Reference inputs not allowed in PlutusV1.") + } + + 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, + dcert, + wdrl, + valid_range, + signatories, + data, + id, + })) +} + +fn get_tx_info_v2( + tx: &MintedTx, + utxos: &[ResolvedInput], + slot_config: &SlotConfig, +) -> anyhow::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, + dcert, + wdrl, + valid_range, + signatories, + redeemers, + data, + id, + })) +} + +fn get_execution_purpose( + utxos: &[ResolvedInput], + script_purpose: &ScriptPurpose, + lookup_table: &DataLookupTable, +) -> ExecutionPurpose { + 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 => unreachable!("Missing required scripts.") + }; + ExecutionPurpose::NoDatum(script) + } + ScriptPurpose::Spending(out_ref) => { + let utxo = utxos.iter().find(|utxo| utxo.input == *out_ref).unwrap(); + match &utxo.output { + TransactionOutput::Legacy(output) => { + let address = Address::from_bytes(&output.address).unwrap(); + match address { + Address::Shelley(shelley_address) => { + let script = match lookup_table + .scripts + .get(shelley_address.payment().as_hash()) { + Some(s) => s.clone(), + None => unreachable!("Missing required scripts.") + }; + + let datum = match lookup_table.datum.get(&output.datum_hash.unwrap_or_else(|| unreachable!("Missing datum hash in input."))) { + Some(d) => d.clone(), + None => unreachable!("Missing datum in witness set.") + }; + + ExecutionPurpose::WithDatum(script, datum) + } + _ => unreachable!( + "This is impossible. Only shelley addresses can contain a script hash." + ), + } + } + TransactionOutput::PostAlonzo(output) => { + let address = Address::from_bytes(&output.address).unwrap(); + match address { + Address::Shelley(shelley_address) => { + + let script = match lookup_table + .scripts + .get(shelley_address.payment().as_hash()) { + Some(s) => s.clone(), + None => unreachable!("Missing required scripts.") + }; + + + let datum = match &output.datum_option { + Some(DatumOption::Hash(hash)) => { + lookup_table.datum.get(hash).unwrap().clone() + } + Some(DatumOption::Data(data)) => data.0.clone(), + _ => unreachable!( "Missing datum hash or inline datum in input."), + }; + + ExecutionPurpose::WithDatum(script, datum) + } + _ => unreachable!( + "This is impossible. Only shelley addresses can contain a script hash." + ), + } + } + } + } + ScriptPurpose::Rewarding(stake_credential) => { + let script_hash = match stake_credential { + StakeCredential::Scripthash(hash) => *hash, + _ => unreachable!("This is impossible. A key hash cannot be the hash of a script."), + }; + + let script = match lookup_table.scripts.get(&script_hash) { + Some(s) => s.clone(), + None => unreachable!("Missing required scripts.") + }; + + ExecutionPurpose::NoDatum(script) + } + ScriptPurpose::Certifying(cert) => match cert { + // StakeRegistration doesn't require a witness from a stake key/script. So I assume it doesn't need to be handled in Plutus either? + Certificate::StakeDeregistration(stake_credential) => { + let script_hash = match stake_credential { + StakeCredential::Scripthash(hash) => *hash, + _ => unreachable!( + "This is impossible. A key hash cannot be the hash of a script." + ), + }; + + let script = match lookup_table.scripts.get(&script_hash) { + Some(s) => s.clone(), + None => unreachable!("Missing required scripts.") + }; + + ExecutionPurpose::NoDatum(script) + } + Certificate::StakeDelegation(stake_credential, _) => { + let script_hash = match stake_credential { + StakeCredential::Scripthash(hash) => *hash, + _ => unreachable!( + "This is impossible. A key hash cannot be the hash of a script." + ), + }; + + let script = match lookup_table.scripts.get(&script_hash) { + Some(s) => s.clone(), + None => unreachable!("Missing required scripts.") + }; + + ExecutionPurpose::NoDatum(script) + } + _ => unreachable!("This is impossible. Only stake deregistration and stake delegation are valid script purposes."), + }, + } +} + +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_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_v1_witnesses.iter() { + scripts.insert(script.compute_hash(), ScriptVersion::V1(script.clone())); + // TODO: fix hashing bug in pallas + } + + for script in scripts_v2_witnesses.iter() { + scripts.insert(script.compute_hash(), ScriptVersion::V2(script.clone())); + // TODO: fix hashing bug in pallas + } + + // 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::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, +) -> anyhow::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 result = program + .apply_data(datum) + .apply_data(redeemer.data.clone()) + .apply_data(script_context.to_plutus_data()) + .eval(); + + match result.0 { + Ok(_) => {} + Err(_err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message + } + + let new_redeemer = Redeemer { + tag: redeemer.tag.clone(), + index: redeemer.index, + data: redeemer.data.clone(), + ex_units: ExUnits { + mem: (ExBudget::default().mem - result.1.mem) as u32, + steps: (ExBudget::default().cpu - result.1.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 result = program + .apply_data(datum) + .apply_data(redeemer.data.clone()) + .apply_data(script_context.to_plutus_data()) + .eval(); + + match result.0 { + Ok(_) => {} + Err(_err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message + } + + let new_redeemer = Redeemer { + tag: redeemer.tag.clone(), + index: redeemer.index, + data: redeemer.data.clone(), + ex_units: ExUnits { + mem: (ExBudget::default().mem - result.1.mem) as u32, + steps: (ExBudget::default().cpu - result.1.cpu) as u64, + }, + }; + + Ok(new_redeemer) + } + }, + 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 result = program + .apply_data(redeemer.data.clone()) + .apply_data(script_context.to_plutus_data()) + .eval(); + + match result.0 { + Ok(_) => {} + Err(_err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message + } + + let new_redeemer = Redeemer { + tag: redeemer.tag.clone(), + index: redeemer.index, + data: redeemer.data.clone(), + ex_units: ExUnits { + mem: (ExBudget::default().mem - result.1.mem) as u32, + steps: (ExBudget::default().cpu - result.1.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 result = program + .apply_data(redeemer.data.clone()) + .apply_data(script_context.to_plutus_data()) + .eval(); + + match result.0 { + Ok(_) => {} + Err(_err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message + } + + let new_redeemer = Redeemer { + tag: redeemer.tag.clone(), + index: redeemer.index, + data: redeemer.data.clone(), + ex_units: ExUnits { + mem: (ExBudget::default().mem - result.1.mem) as u32, + steps: (ExBudget::default().cpu - result.1.cpu) as u64, + }, + }; + + Ok(new_redeemer) + } + }, + } +} + From 02a8a34fe8f2169f808efe98ea99c35a7b1993b2 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Fri, 16 Sep 2022 04:35:20 -0400 Subject: [PATCH 42/77] expose tx simulation with simulate cli command --- Cargo.lock | 1 + crates/cli/src/args.rs | 34 +++---------------- crates/cli/src/main.rs | 22 ++++++++++-- crates/uplc/Cargo.toml | 1 + crates/uplc/src/transaction_eval.rs | 8 ++--- .../transaction_eval/{utils.rs => eval.rs} | 7 ++-- .../src/transaction_eval/script_context.rs | 12 ++++++- 7 files changed, 44 insertions(+), 41 deletions(-) rename crates/uplc/src/transaction_eval/{utils.rs => eval.rs} (99%) diff --git a/Cargo.lock b/Cargo.lock index e123a658..bc815bba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -688,6 +688,7 @@ dependencies = [ "peg", "pretty", "proptest", + "serde", "serde_json", "thiserror", ] diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs index 15ffa8fb..c861c25b 100644 --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -1,7 +1,6 @@ -use std::{collections::HashMap, path::PathBuf}; +use std::path::PathBuf; use clap::{Parser, Subcommand}; -use serde::Deserialize; /// Cardano smart contract toolchain #[derive(Parser)] @@ -26,36 +25,13 @@ pub enum TxCommand { cbor: bool, #[clap(short, long)] resolved_inputs: PathBuf, + #[clap(short, long)] + slot_length: u64, + #[clap(short, long)] + zero_time: u64, }, } -#[derive(Deserialize)] -pub struct ResolvedInputOld { - pub input: Input, - pub output: Output, -} - -#[derive(Deserialize)] -pub struct Input { - pub tx_hash: String, - pub index: u64, -} - -#[derive(Deserialize)] -pub struct Output { - pub address: String, - pub value: (u64, HashMap>), - pub datum: Option, - pub script: Option, -} - -#[derive(Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum OutputDatum { - DatumHash(String), - Datum(String), -} - /// Commands for working with Untyped Plutus Core #[derive(Subcommand)] pub enum UplcCommand { diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 604c1fd8..54d4cb3c 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,6 +1,7 @@ use std::{ fmt::Write as _, - fs::{self}, + fs::{self, File}, + io::BufReader, }; use pallas_traverse::{Era, MultiEraTx}; @@ -8,6 +9,8 @@ use uplc::{ ast::{DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term}, machine::cost_model::ExBudget, parser, + transaction_eval::eval_tx, + transaction_eval::script_context::{ResolvedInput, SlotConfig}, }; mod args; @@ -22,7 +25,9 @@ fn main() -> anyhow::Result<()> { TxCommand::Simulate { input, cbor, - resolved_inputs: _, + resolved_inputs, + slot_length, + zero_time, } => { let tx_bytes = if cbor { fs::read(input)? @@ -37,7 +42,18 @@ fn main() -> anyhow::Result<()> { println!("Simulating: {}", tx.hash()); - + if let Some(tx_babbage) = tx.as_babbage() { + let file = File::open(&resolved_inputs)?; + let reader = BufReader::new(file); + let resolved_inputs: Vec = serde_json::from_reader(reader)?; + + let slot_config = SlotConfig { + zero_time, + slot_length, + }; + + eval_tx(tx_babbage, &resolved_inputs, &slot_config)?; + } } }, Args::Uplc(uplc_cmd) => match uplc_cmd { diff --git a/crates/uplc/Cargo.toml b/crates/uplc/Cargo.toml index 4ebe5993..56e4b6ef 100644 --- a/crates/uplc/Cargo.toml +++ b/crates/uplc/Cargo.toml @@ -25,6 +25,7 @@ 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] diff --git a/crates/uplc/src/transaction_eval.rs b/crates/uplc/src/transaction_eval.rs index 941a48c6..87cb3056 100644 --- a/crates/uplc/src/transaction_eval.rs +++ b/crates/uplc/src/transaction_eval.rs @@ -2,9 +2,9 @@ use pallas_primitives::babbage::{MintedTx, Redeemer}; use self::script_context::{ResolvedInput, SlotConfig}; -mod script_context; +mod eval; +pub mod script_context; mod to_plutus_data; -mod utils; pub fn eval_tx( tx: &MintedTx, @@ -14,13 +14,13 @@ pub fn eval_tx( ) -> anyhow::Result> { let redeemers = tx.transaction_witness_set.redeemer.as_ref(); - let lookup_table = utils::get_script_and_datum_lookup_table(tx, utxos); + let lookup_table = eval::get_script_and_datum_lookup_table(tx, utxos); match redeemers { Some(rs) => { let mut collected_redeemers = vec![]; for redeemer in rs.iter() { - collected_redeemers.push(utils::eval_redeemer( + collected_redeemers.push(eval::eval_redeemer( tx, utxos, slot_config, diff --git a/crates/uplc/src/transaction_eval/utils.rs b/crates/uplc/src/transaction_eval/eval.rs similarity index 99% rename from crates/uplc/src/transaction_eval/utils.rs rename to crates/uplc/src/transaction_eval/eval.rs index 554e8832..7fc37e73 100644 --- a/crates/uplc/src/transaction_eval/utils.rs +++ b/crates/uplc/src/transaction_eval/eval.rs @@ -7,9 +7,9 @@ use pallas_addresses::{Address, ScriptHash, StakePayload}; use pallas_codec::utils::{KeyValuePairs, MaybeIndefArray}; use pallas_crypto::hash::Hash; use pallas_primitives::babbage::{ - Certificate, DatumHash, DatumOption, ExUnits, Mint, MintedTx, - PlutusV1Script, PlutusV2Script, PolicyId, Redeemer, RedeemerTag, RewardAccount, Script, - StakeCredential, TransactionInput, TransactionOutput, Value, Withdrawals, + Certificate, DatumHash, DatumOption, ExUnits, Mint, MintedTx, 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}; @@ -712,4 +712,3 @@ pub fn eval_redeemer( }, } } - diff --git a/crates/uplc/src/transaction_eval/script_context.rs b/crates/uplc/src/transaction_eval/script_context.rs index 1f8eb9d8..4e883464 100644 --- a/crates/uplc/src/transaction_eval/script_context.rs +++ b/crates/uplc/src/transaction_eval/script_context.rs @@ -4,8 +4,9 @@ use pallas_primitives::babbage::{ AddrKeyhash, Certificate, Coin, DatumHash, Mint, PlutusData, PolicyId, Redeemer, RewardAccount, StakeCredential, TransactionInput, TransactionOutput, Value, Withdrawals, }; +use serde::Deserialize; -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Deserialize)] pub struct ResolvedInput { pub input: TransactionInput, pub output: TransactionOutput, @@ -83,3 +84,12 @@ pub struct SlotConfig { pub slot_length: u64, pub zero_time: u64, } + +impl Default for SlotConfig { + fn default() -> Self { + Self { + slot_length: 1000, + zero_time: 1596491091, + } + } +} From d426f4922f1d9bdd8c0e24e34321af619f9a4a60 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Fri, 16 Sep 2022 17:13:42 +0200 Subject: [PATCH 43/77] added eval_tx_raw --- crates/uplc/src/transaction_eval.rs | 51 ++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/crates/uplc/src/transaction_eval.rs b/crates/uplc/src/transaction_eval.rs index 87cb3056..6ea0d6b2 100644 --- a/crates/uplc/src/transaction_eval.rs +++ b/crates/uplc/src/transaction_eval.rs @@ -1,4 +1,8 @@ -use pallas_primitives::babbage::{MintedTx, Redeemer}; +use pallas_primitives::{ + babbage::{CostMdls, MintedTx, Redeemer, TransactionInput, TransactionOutput}, + Fragment, +}; +use pallas_traverse::{Era, MultiEraTx}; use self::script_context::{ResolvedInput, SlotConfig}; @@ -34,6 +38,51 @@ pub fn eval_tx( } } +pub fn eval_tx_raw( + tx_bytes: &Vec, + utxos_bytes: &Vec<(Vec, Vec)>, + cost_mdls_bytes: &Vec, + slot_config: (u64, u64), +) -> Result>, ()> { + let multi_era_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) + .or_else(|_| Err(()))?; // TODO: proper error message + + let cost_mdls = CostMdls::decode_fragment(&cost_mdls_bytes).or_else(|_| Err(()))?; // TODO: proper error message + + let utxos: Vec = utxos_bytes + .iter() + .map(|(input, output)| ResolvedInput { + input: TransactionInput::decode_fragment(input).unwrap(), + output: TransactionOutput::decode_fragment(input).unwrap(), + }) + .collect(); + + let sc = SlotConfig { + zero_time: slot_config.0, + slot_length: slot_config.1, + }; + + match multi_era_tx { + MultiEraTx::Babbage(tx) => match eval_tx(&tx, &utxos, &sc) { + Ok(redeemers) => Ok(redeemers + .iter() + .map(|r| r.encode_fragment().unwrap()) + .collect()), + Err(_) => Err(()), + }, + // 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. + _ => Err(()), + } +} + #[cfg(test)] mod tests { use pallas_codec::utils::MaybeIndefArray; From 472cea6c41463b4ce872dd6845590257f9664353 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Sat, 17 Sep 2022 21:12:48 -0400 Subject: [PATCH 44/77] parameratize cost model --- crates/cli/src/main.rs | 2 +- crates/uplc/src/ast.rs | 32 +- crates/uplc/src/machine.rs | 17 +- crates/uplc/src/machine/cost_model.rs | 1650 +++++++++++++++++++++- crates/uplc/src/machine/runtime.rs | 8 +- crates/uplc/src/transaction_eval.rs | 475 ++++++- crates/uplc/src/transaction_eval/eval.rs | 10 +- 7 files changed, 2143 insertions(+), 51 deletions(-) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 54d4cb3c..63c0c6c3 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -52,7 +52,7 @@ fn main() -> anyhow::Result<()> { slot_length, }; - eval_tx(tx_babbage, &resolved_inputs, &slot_config)?; + // eval_tx(tx_babbage, &resolved_inputs, &slot_config)?; } } }, diff --git a/crates/uplc/src/ast.rs b/crates/uplc/src/ast.rs index 55a38ae6..b8fb7ab3 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, }, }; @@ -488,7 +488,33 @@ 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) + } + + pub fn eval_with_params( + &self, + version: &Language, + costs: &[i64], + ) -> ( + Result, crate::machine::Error>, + ExBudget, + Vec, + ) { + let mut machine = Machine::new( + version.clone(), + initialize_cost_model(version, costs), + ExBudget::default(), + 200, //slippage + ); let term = machine.run(&self.term); diff --git a/crates/uplc/src/machine.rs b/crates/uplc/src/machine.rs index 07f5c346..e03e12e1 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) diff --git a/crates/uplc/src/machine/cost_model.rs b/crates/uplc/src/machine/cost_model.rs index 78ddcf42..1aa095d9 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 { @@ -364,14 +379,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 +545,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 +949,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-exBudgetCPU") + .unwrap_or(&30000000000), + cpu: *cost_map + .get("cek_startup_cost-exBudgetmem") + .unwrap_or(&30000000000), + }, + var: ExBudget { + mem: *cost_map + .get("cek_var_cost-exBudgetCPU") + .unwrap_or(&30000000000), + cpu: *cost_map + .get("cek_var_cost-exBudgetmem") + .unwrap_or(&30000000000), + }, + constant: ExBudget { + mem: *cost_map + .get("cek_const_cost-exBudgetCPU") + .unwrap_or(&30000000000), + cpu: *cost_map + .get("cek_const_cost-exBudgetmem") + .unwrap_or(&30000000000), + }, + lambda: ExBudget { + mem: *cost_map + .get("cek_lam_cost-exBudgetCPU") + .unwrap_or(&30000000000), + cpu: *cost_map + .get("cek_lam_cost-exBudgetmem") + .unwrap_or(&30000000000), + }, + delay: ExBudget { + mem: *cost_map + .get("cek_delay_cost-exBudgetCPU") + .unwrap_or(&30000000000), + cpu: *cost_map + .get("cek_delay_cost-exBudgetmem") + .unwrap_or(&30000000000), + }, + force: ExBudget { + mem: *cost_map + .get("cek_force_cost-exBudgetCPU") + .unwrap_or(&30000000000), + cpu: *cost_map + .get("cek_force_cost-exBudgetmem") + .unwrap_or(&30000000000), + }, + apply: ExBudget { + mem: *cost_map + .get("cek_apply_cost-exBudgetCPU") + .unwrap_or(&30000000000), + cpu: *cost_map + .get("cek_apply_cost-exBudgetmem") + .unwrap_or(&30000000000), + }, + builtin: ExBudget { + mem: *cost_map + .get("cek_builtin_cost-exBudgetCPU") + .unwrap_or(&30000000000), + cpu: *cost_map + .get("cek_builtin_cost-exBudgetmem") + .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/runtime.rs b/crates/uplc/src/machine/runtime.rs index c722dc9d..fa1614b2 100644 --- a/crates/uplc/src/machine/runtime.rs +++ b/crates/uplc/src/machine/runtime.rs @@ -63,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) } } diff --git a/crates/uplc/src/transaction_eval.rs b/crates/uplc/src/transaction_eval.rs index 6ea0d6b2..33fc9e11 100644 --- a/crates/uplc/src/transaction_eval.rs +++ b/crates/uplc/src/transaction_eval.rs @@ -1,9 +1,11 @@ use pallas_primitives::{ - babbage::{CostMdls, MintedTx, Redeemer, TransactionInput, TransactionOutput}, + babbage::{CostMdls, Language, MintedTx, Redeemer, TransactionInput, TransactionOutput}, Fragment, }; use pallas_traverse::{Era, MultiEraTx}; +use crate::Error; + use self::script_context::{ResolvedInput, SlotConfig}; mod eval; @@ -13,46 +15,63 @@ mod to_plutus_data; pub fn eval_tx( tx: &MintedTx, utxos: &[ResolvedInput], - //TODO: costMdls + cost_mdls: &CostMdls, + version: &Language, slot_config: &SlotConfig, ) -> anyhow::Result> { let redeemers = tx.transaction_witness_set.redeemer.as_ref(); let lookup_table = eval::get_script_and_datum_lookup_table(tx, utxos); - match redeemers { - Some(rs) => { - let mut collected_redeemers = vec![]; - for redeemer in rs.iter() { - collected_redeemers.push(eval::eval_redeemer( - tx, - utxos, - slot_config, - redeemer, - &lookup_table, - )?) + let costs_maybe = match version { + Language::PlutusV1 => cost_mdls.plutus_v1.as_ref(), + Language::PlutusV2 => cost_mdls.plutus_v2.as_ref(), + }; + + if let Some(costs) = costs_maybe { + match redeemers { + Some(rs) => { + let mut collected_redeemers = vec![]; + for redeemer in rs.iter() { + collected_redeemers.push(eval::eval_redeemer( + tx, + utxos, + slot_config, + redeemer, + &lookup_table, + version, + costs, + )?) + } + Ok(collected_redeemers) } - Ok(collected_redeemers) + None => Ok(vec![]), } - None => Ok(vec![]), + } else { + Err(anyhow::Error::msg(format!( + "Missing cost model for version: {:?}", + version + ))) } } pub fn eval_tx_raw( - tx_bytes: &Vec, - utxos_bytes: &Vec<(Vec, Vec)>, - cost_mdls_bytes: &Vec, + tx_bytes: &[u8], + utxos_bytes: &[(Vec, Vec)], + cost_mdls_bytes: &[u8], + version_bytes: u32, slot_config: (u64, u64), -) -> Result>, ()> { - let multi_era_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) - .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) - .or_else(|_| Err(()))?; // TODO: proper error message +) -> Result>, Error> { + let multi_era_tx = MultiEraTx::decode(Era::Babbage, tx_bytes) + .or_else(|_| MultiEraTx::decode(Era::Alonzo, tx_bytes)) + .map_err(|_| Error::from("Wrong era. Please use Babbage or Alonzo"))?; // TODO: proper error message - let cost_mdls = CostMdls::decode_fragment(&cost_mdls_bytes).or_else(|_| Err(()))?; // TODO: proper error message + let cost_mdls = CostMdls::decode_fragment(cost_mdls_bytes) + .map_err(|_| Error::from("Unable to decode cost models"))?; // TODO: proper error message let utxos: Vec = utxos_bytes .iter() - .map(|(input, output)| ResolvedInput { + .map(|(input, _output)| ResolvedInput { input: TransactionInput::decode_fragment(input).unwrap(), output: TransactionOutput::decode_fragment(input).unwrap(), }) @@ -63,13 +82,19 @@ pub fn eval_tx_raw( slot_length: slot_config.1, }; + let version = match version_bytes { + 1 => Language::PlutusV1, + 2 => Language::PlutusV2, + _ => unreachable!(), + }; + match multi_era_tx { - MultiEraTx::Babbage(tx) => match eval_tx(&tx, &utxos, &sc) { + MultiEraTx::Babbage(tx) => match eval_tx(&tx, &utxos, &cost_mdls, &version, &sc) { Ok(redeemers) => Ok(redeemers .iter() .map(|r| r.encode_fragment().unwrap()) .collect()), - Err(_) => Err(()), + Err(_) => Err(Error::from("Can't eval without redeemers")), }, // MultiEraTx::AlonzoCompatible(tx, _) => match eval_tx(&tx, &utxos, &sc) { // Ok(redeemers) => Ok(redeemers @@ -79,7 +104,7 @@ pub fn eval_tx_raw( // Err(_) => Err(()), // }, // TODO: I probably did a mistake here with using MintedTx which is only compatible with Babbage tx. - _ => Err(()), + _ => Err(Error::from("Wrong era. Please use babbage")), } } @@ -87,7 +112,7 @@ pub fn eval_tx_raw( mod tests { use pallas_codec::utils::MaybeIndefArray; use pallas_primitives::{ - babbage::{TransactionInput, TransactionOutput}, + babbage::{CostMdls, Language, TransactionInput, TransactionOutput}, Fragment, }; use pallas_traverse::{Era, MultiEraTx}; @@ -130,12 +155,196 @@ mod tests { 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 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_tx(&tx, &utxos, &slot_config).unwrap(); + let redeemers = + eval_tx(&tx, &utxos, &cost_mdl, &Language::PlutusV2, &slot_config).unwrap(); assert_eq!(redeemers.len(), 1) } @@ -181,12 +390,196 @@ mod tests { 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 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_tx(&tx, &utxos, &slot_config).unwrap(); + let redeemers = + eval_tx(&tx, &utxos, &cost_mdl, &Language::PlutusV2, &slot_config).unwrap(); println!("{:?}", redeemers.len()); } @@ -232,12 +625,32 @@ mod tests { 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 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_tx(&tx, &utxos, &slot_config).unwrap(); + let redeemers = + eval_tx(&tx, &utxos, &cost_mdl, &Language::PlutusV1, &slot_config).unwrap(); println!("{:?}", redeemers.len()); } diff --git a/crates/uplc/src/transaction_eval/eval.rs b/crates/uplc/src/transaction_eval/eval.rs index 7fc37e73..867db658 100644 --- a/crates/uplc/src/transaction_eval/eval.rs +++ b/crates/uplc/src/transaction_eval/eval.rs @@ -7,9 +7,9 @@ use pallas_addresses::{Address, ScriptHash, StakePayload}; use pallas_codec::utils::{KeyValuePairs, MaybeIndefArray}; use pallas_crypto::hash::Hash; use pallas_primitives::babbage::{ - Certificate, DatumHash, DatumOption, ExUnits, Mint, MintedTx, PlutusV1Script, PlutusV2Script, - PolicyId, Redeemer, RedeemerTag, RewardAccount, Script, StakeCredential, TransactionInput, - TransactionOutput, Value, Withdrawals, + Certificate, DatumHash, DatumOption, ExUnits, Language, Mint, MintedTx, 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}; @@ -556,6 +556,8 @@ pub fn eval_redeemer( slot_config: &SlotConfig, redeemer: &Redeemer, lookup_table: &DataLookupTable, + version: &Language, + costs: &[i64], ) -> anyhow::Result { let purpose = get_script_purpose( redeemer, @@ -585,7 +587,7 @@ pub fn eval_redeemer( .apply_data(datum) .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()) - .eval(); + .eval_with_params(version, costs); match result.0 { Ok(_) => {} From 6619a0e431f4152ed65cafe909d0cf797a760d20 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Sun, 18 Sep 2022 09:37:14 +0200 Subject: [PATCH 45/77] moved cost_mdls language determination into eval_redeemer --- crates/uplc/src/transaction_eval.rs | 64 ++++++++---------------- crates/uplc/src/transaction_eval/eval.rs | 41 +++++++++++---- 2 files changed, 52 insertions(+), 53 deletions(-) diff --git a/crates/uplc/src/transaction_eval.rs b/crates/uplc/src/transaction_eval.rs index 33fc9e11..78fcad4a 100644 --- a/crates/uplc/src/transaction_eval.rs +++ b/crates/uplc/src/transaction_eval.rs @@ -1,5 +1,5 @@ use pallas_primitives::{ - babbage::{CostMdls, Language, MintedTx, Redeemer, TransactionInput, TransactionOutput}, + babbage::{CostMdls, MintedTx, Redeemer, TransactionInput, TransactionOutput}, Fragment, }; use pallas_traverse::{Era, MultiEraTx}; @@ -16,42 +16,28 @@ pub fn eval_tx( tx: &MintedTx, utxos: &[ResolvedInput], cost_mdls: &CostMdls, - version: &Language, slot_config: &SlotConfig, ) -> anyhow::Result> { let redeemers = tx.transaction_witness_set.redeemer.as_ref(); let lookup_table = eval::get_script_and_datum_lookup_table(tx, utxos); - let costs_maybe = match version { - Language::PlutusV1 => cost_mdls.plutus_v1.as_ref(), - Language::PlutusV2 => cost_mdls.plutus_v2.as_ref(), - }; - - if let Some(costs) = costs_maybe { - match redeemers { - Some(rs) => { - let mut collected_redeemers = vec![]; - for redeemer in rs.iter() { - collected_redeemers.push(eval::eval_redeemer( - tx, - utxos, - slot_config, - redeemer, - &lookup_table, - version, - costs, - )?) - } - Ok(collected_redeemers) + match redeemers { + Some(rs) => { + let mut collected_redeemers = vec![]; + for redeemer in rs.iter() { + collected_redeemers.push(eval::eval_redeemer( + tx, + utxos, + slot_config, + redeemer, + &lookup_table, + cost_mdls, + )?) } - None => Ok(vec![]), + Ok(collected_redeemers) } - } else { - Err(anyhow::Error::msg(format!( - "Missing cost model for version: {:?}", - version - ))) + None => Ok(vec![]), } } @@ -59,7 +45,6 @@ pub fn eval_tx_raw( tx_bytes: &[u8], utxos_bytes: &[(Vec, Vec)], cost_mdls_bytes: &[u8], - version_bytes: u32, slot_config: (u64, u64), ) -> Result>, Error> { let multi_era_tx = MultiEraTx::decode(Era::Babbage, tx_bytes) @@ -82,14 +67,8 @@ pub fn eval_tx_raw( slot_length: slot_config.1, }; - let version = match version_bytes { - 1 => Language::PlutusV1, - 2 => Language::PlutusV2, - _ => unreachable!(), - }; - match multi_era_tx { - MultiEraTx::Babbage(tx) => match eval_tx(&tx, &utxos, &cost_mdls, &version, &sc) { + MultiEraTx::Babbage(tx) => match eval_tx(&tx, &utxos, &cost_mdls, &sc) { Ok(redeemers) => Ok(redeemers .iter() .map(|r| r.encode_fragment().unwrap()) @@ -112,7 +91,7 @@ pub fn eval_tx_raw( mod tests { use pallas_codec::utils::MaybeIndefArray; use pallas_primitives::{ - babbage::{CostMdls, Language, TransactionInput, TransactionOutput}, + babbage::{CostMdls, TransactionInput, TransactionOutput}, Fragment, }; use pallas_traverse::{Era, MultiEraTx}; @@ -343,8 +322,7 @@ mod tests { .unwrap(); match multi_era_tx { MultiEraTx::Babbage(tx) => { - let redeemers = - eval_tx(&tx, &utxos, &cost_mdl, &Language::PlutusV2, &slot_config).unwrap(); + let redeemers = eval_tx(&tx, &utxos, &cost_mdl, &slot_config).unwrap(); assert_eq!(redeemers.len(), 1) } @@ -578,8 +556,7 @@ mod tests { .unwrap(); match multi_era_tx { MultiEraTx::Babbage(tx) => { - let redeemers = - eval_tx(&tx, &utxos, &cost_mdl, &Language::PlutusV2, &slot_config).unwrap(); + let redeemers = eval_tx(&tx, &utxos, &cost_mdl, &slot_config).unwrap(); println!("{:?}", redeemers.len()); } @@ -649,8 +626,7 @@ mod tests { .unwrap(); match multi_era_tx { MultiEraTx::Babbage(tx) => { - let redeemers = - eval_tx(&tx, &utxos, &cost_mdl, &Language::PlutusV1, &slot_config).unwrap(); + let redeemers = eval_tx(&tx, &utxos, &cost_mdl, &slot_config).unwrap(); println!("{:?}", redeemers.len()); } diff --git a/crates/uplc/src/transaction_eval/eval.rs b/crates/uplc/src/transaction_eval/eval.rs index 867db658..84d9d19e 100644 --- a/crates/uplc/src/transaction_eval/eval.rs +++ b/crates/uplc/src/transaction_eval/eval.rs @@ -7,9 +7,9 @@ use pallas_addresses::{Address, ScriptHash, StakePayload}; use pallas_codec::utils::{KeyValuePairs, MaybeIndefArray}; use pallas_crypto::hash::Hash; use pallas_primitives::babbage::{ - Certificate, DatumHash, DatumOption, ExUnits, Language, Mint, MintedTx, PlutusV1Script, - PlutusV2Script, PolicyId, Redeemer, RedeemerTag, RewardAccount, Script, StakeCredential, - TransactionInput, TransactionOutput, Value, Withdrawals, + Certificate, CostMdls, DatumHash, DatumOption, ExUnits, Language, Mint, MintedTx, + 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}; @@ -556,8 +556,7 @@ pub fn eval_redeemer( slot_config: &SlotConfig, redeemer: &Redeemer, lookup_table: &DataLookupTable, - version: &Language, - costs: &[i64], + cost_mdls: &CostMdls, ) -> anyhow::Result { let purpose = get_script_purpose( redeemer, @@ -583,11 +582,17 @@ pub fn eval_redeemer( prog.into() }; + let costs = if let Some(costs) = &cost_mdls.plutus_v1 { + costs + } else { + return Err(anyhow::Error::msg("PlutusV1 cost model not found.")); + }; + let result = program .apply_data(datum) .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()) - .eval_with_params(version, costs); + .eval_with_params(&Language::PlutusV1, &costs); match result.0 { Ok(_) => {} @@ -618,11 +623,17 @@ pub fn eval_redeemer( prog.into() }; + let costs = if let Some(costs) = &cost_mdls.plutus_v2 { + costs + } else { + return Err(anyhow::Error::msg("PlutusV2 cost model not found.")); + }; + let result = program .apply_data(datum) .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()) - .eval(); + .eval_with_params(&Language::PlutusV2, &costs); match result.0 { Ok(_) => {} @@ -655,10 +666,16 @@ pub fn eval_redeemer( prog.into() }; + let costs = if let Some(costs) = &cost_mdls.plutus_v1 { + costs + } else { + return Err(anyhow::Error::msg("PlutusV1 cost model not found.")); + }; + let result = program .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()) - .eval(); + .eval_with_params(&Language::PlutusV1, &costs); match result.0 { Ok(_) => {} @@ -689,10 +706,16 @@ pub fn eval_redeemer( prog.into() }; + let costs = if let Some(costs) = &cost_mdls.plutus_v2 { + costs + } else { + return Err(anyhow::Error::msg("PlutusV2 cost model not found.")); + }; + let result = program .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()) - .eval(); + .eval_with_params(&Language::PlutusV2, &costs); match result.0 { Ok(_) => {} From dac25b6f41026a41d506d2acc3abf3aaad7839e0 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Sun, 18 Sep 2022 04:15:30 -0400 Subject: [PATCH 46/77] fix cpu mem mix up on steps --- crates/uplc/src/machine/cost_model.rs | 32 +++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/crates/uplc/src/machine/cost_model.rs b/crates/uplc/src/machine/cost_model.rs index 1aa095d9..f7efadfb 100644 --- a/crates/uplc/src/machine/cost_model.rs +++ b/crates/uplc/src/machine/cost_model.rs @@ -1709,66 +1709,66 @@ pub fn initialize_cost_model(version: &Language, costs: &[i64]) -> CostModel { machine_costs: MachineCosts { startup: ExBudget { mem: *cost_map - .get("cek_startup_cost-exBudgetCPU") + .get("cek_startup_cost-exBudgetmem") .unwrap_or(&30000000000), cpu: *cost_map - .get("cek_startup_cost-exBudgetmem") + .get("cek_startup_cost-exBudgetCPU") .unwrap_or(&30000000000), }, var: ExBudget { mem: *cost_map - .get("cek_var_cost-exBudgetCPU") + .get("cek_var_cost-exBudgetmem") .unwrap_or(&30000000000), cpu: *cost_map - .get("cek_var_cost-exBudgetmem") + .get("cek_var_cost-exBudgetCPU") .unwrap_or(&30000000000), }, constant: ExBudget { mem: *cost_map - .get("cek_const_cost-exBudgetCPU") + .get("cek_const_cost-exBudgetmem") .unwrap_or(&30000000000), cpu: *cost_map - .get("cek_const_cost-exBudgetmem") + .get("cek_const_cost-exBudgetCPU") .unwrap_or(&30000000000), }, lambda: ExBudget { mem: *cost_map - .get("cek_lam_cost-exBudgetCPU") + .get("cek_lam_cost-exBudgetmem") .unwrap_or(&30000000000), cpu: *cost_map - .get("cek_lam_cost-exBudgetmem") + .get("cek_lam_cost-exBudgetCPU") .unwrap_or(&30000000000), }, delay: ExBudget { mem: *cost_map - .get("cek_delay_cost-exBudgetCPU") + .get("cek_delay_cost-exBudgetmem") .unwrap_or(&30000000000), cpu: *cost_map - .get("cek_delay_cost-exBudgetmem") + .get("cek_delay_cost-exBudgetCPU") .unwrap_or(&30000000000), }, force: ExBudget { mem: *cost_map - .get("cek_force_cost-exBudgetCPU") + .get("cek_force_cost-exBudgetmem") .unwrap_or(&30000000000), cpu: *cost_map - .get("cek_force_cost-exBudgetmem") + .get("cek_force_cost-exBudgetCPU") .unwrap_or(&30000000000), }, apply: ExBudget { mem: *cost_map - .get("cek_apply_cost-exBudgetCPU") + .get("cek_apply_cost-exBudgetmem") .unwrap_or(&30000000000), cpu: *cost_map - .get("cek_apply_cost-exBudgetmem") + .get("cek_apply_cost-exBudgetCPU") .unwrap_or(&30000000000), }, builtin: ExBudget { mem: *cost_map - .get("cek_builtin_cost-exBudgetCPU") + .get("cek_builtin_cost-exBudgetmem") .unwrap_or(&30000000000), cpu: *cost_map - .get("cek_builtin_cost-exBudgetmem") + .get("cek_builtin_cost-exBudgetCPU") .unwrap_or(&30000000000), }, }, From 68fc0f643e39a7b6b6214c499f1a4c9d10c73262 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sun, 18 Sep 2022 15:16:41 -0400 Subject: [PATCH 47/77] feat: add some v1 methods so that cost_mdls can be optional --- crates/cli/src/main.rs | 2 +- crates/uplc/src/ast.rs | 17 +- crates/uplc/src/machine/cost_model.rs | 420 ++++++++++++++++++ crates/uplc/src/transaction_eval.rs | 10 +- crates/uplc/src/transaction_eval/eval.rs | 94 ++-- .../src/transaction_eval/script_context.rs | 2 +- 6 files changed, 500 insertions(+), 45 deletions(-) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 63c0c6c3..eac3e103 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -52,7 +52,7 @@ fn main() -> anyhow::Result<()> { slot_length, }; - // eval_tx(tx_babbage, &resolved_inputs, &slot_config)?; + eval_tx(tx_babbage, &resolved_inputs, None, &slot_config)?; } } }, diff --git a/crates/uplc/src/ast.rs b/crates/uplc/src/ast.rs index b8fb7ab3..38ad2a8d 100644 --- a/crates/uplc/src/ast.rs +++ b/crates/uplc/src/ast.rs @@ -500,7 +500,22 @@ impl Program { (term, machine.ex_budget, machine.logs) } - pub fn eval_with_params( + /// 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], diff --git a/crates/uplc/src/machine/cost_model.rs b/crates/uplc/src/machine/cost_model.rs index f7efadfb..4e6304fb 100644 --- a/crates/uplc/src/machine/cost_model.rs +++ b/crates/uplc/src/machine/cost_model.rs @@ -29,6 +29,14 @@ impl ExBudget { self.mem *= n; self.cpu *= n; } + + // TODO: actually fill in the v1 numbers + pub fn v1() -> Self { + ExBudget { + mem: 14000000, + cpu: 10000000000, + } + } } impl Default for ExBudget { @@ -46,6 +54,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 { @@ -74,9 +91,45 @@ impl MachineCosts { StepKind::StartUp => self.startup, } } + + // TODO: actually fill in the v1 numbers + 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 }, @@ -179,7 +232,374 @@ pub struct BuiltinCosts { pub serialise_data: CostingFun, } +impl BuiltinCosts { + // TODO: actually fill in the v1 numbers + 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: 0, + slope: 2, + }), + cpu: OneArgument::LinearCost(LinearSize { + intercept: 1159724, + slope: 392670, + }), + }, + } + } +} + impl Default for BuiltinCosts { + /// Default is V2 fn default() -> Self { Self { add_integer: CostingFun { diff --git a/crates/uplc/src/transaction_eval.rs b/crates/uplc/src/transaction_eval.rs index 78fcad4a..2c723eb9 100644 --- a/crates/uplc/src/transaction_eval.rs +++ b/crates/uplc/src/transaction_eval.rs @@ -15,7 +15,7 @@ mod to_plutus_data; pub fn eval_tx( tx: &MintedTx, utxos: &[ResolvedInput], - cost_mdls: &CostMdls, + cost_mdls: Option<&CostMdls>, slot_config: &SlotConfig, ) -> anyhow::Result> { let redeemers = tx.transaction_witness_set.redeemer.as_ref(); @@ -68,7 +68,7 @@ pub fn eval_tx_raw( }; match multi_era_tx { - MultiEraTx::Babbage(tx) => match eval_tx(&tx, &utxos, &cost_mdls, &sc) { + MultiEraTx::Babbage(tx) => match eval_tx(&tx, &utxos, Some(&cost_mdls), &sc) { Ok(redeemers) => Ok(redeemers .iter() .map(|r| r.encode_fragment().unwrap()) @@ -322,7 +322,7 @@ mod tests { .unwrap(); match multi_era_tx { MultiEraTx::Babbage(tx) => { - let redeemers = eval_tx(&tx, &utxos, &cost_mdl, &slot_config).unwrap(); + let redeemers = eval_tx(&tx, &utxos, Some(&cost_mdl), &slot_config).unwrap(); assert_eq!(redeemers.len(), 1) } @@ -556,7 +556,7 @@ mod tests { .unwrap(); match multi_era_tx { MultiEraTx::Babbage(tx) => { - let redeemers = eval_tx(&tx, &utxos, &cost_mdl, &slot_config).unwrap(); + let redeemers = eval_tx(&tx, &utxos, Some(&cost_mdl), &slot_config).unwrap(); println!("{:?}", redeemers.len()); } @@ -626,7 +626,7 @@ mod tests { .unwrap(); match multi_era_tx { MultiEraTx::Babbage(tx) => { - let redeemers = eval_tx(&tx, &utxos, &cost_mdl, &slot_config).unwrap(); + let redeemers = eval_tx(&tx, &utxos, Some(&cost_mdl), &slot_config).unwrap(); println!("{:?}", redeemers.len()); } diff --git a/crates/uplc/src/transaction_eval/eval.rs b/crates/uplc/src/transaction_eval/eval.rs index 84d9d19e..3ae1c88e 100644 --- a/crates/uplc/src/transaction_eval/eval.rs +++ b/crates/uplc/src/transaction_eval/eval.rs @@ -556,7 +556,7 @@ pub fn eval_redeemer( slot_config: &SlotConfig, redeemer: &Redeemer, lookup_table: &DataLookupTable, - cost_mdls: &CostMdls, + cost_mdls_opt: Option<&CostMdls>, ) -> anyhow::Result { let purpose = get_script_purpose( redeemer, @@ -582,17 +582,22 @@ pub fn eval_redeemer( prog.into() }; - let costs = if let Some(costs) = &cost_mdls.plutus_v1 { - costs - } else { - return Err(anyhow::Error::msg("PlutusV1 cost model not found.")); - }; - - let result = program + let program = program .apply_data(datum) .apply_data(redeemer.data.clone()) - .apply_data(script_context.to_plutus_data()) - .eval_with_params(&Language::PlutusV1, &costs); + .apply_data(script_context.to_plutus_data()); + + let result = if let Some(cost_mdls) = cost_mdls_opt { + let costs = if let Some(costs) = &cost_mdls.plutus_v1 { + costs + } else { + return Err(anyhow::Error::msg("PlutusV1 cost model not found.")); + }; + + program.eval_as(&Language::PlutusV1, costs) + } else { + program.eval_v1() + }; match result.0 { Ok(_) => {} @@ -623,17 +628,22 @@ pub fn eval_redeemer( prog.into() }; - let costs = if let Some(costs) = &cost_mdls.plutus_v2 { - costs - } else { - return Err(anyhow::Error::msg("PlutusV2 cost model not found.")); - }; - - let result = program + let program = program .apply_data(datum) .apply_data(redeemer.data.clone()) - .apply_data(script_context.to_plutus_data()) - .eval_with_params(&Language::PlutusV2, &costs); + .apply_data(script_context.to_plutus_data()); + + let result = if let Some(cost_mdls) = cost_mdls_opt { + let costs = if let Some(costs) = &cost_mdls.plutus_v1 { + costs + } else { + return Err(anyhow::Error::msg("PlutusV1 cost model not found.")); + }; + + program.eval_as(&Language::PlutusV2, costs) + } else { + program.eval() + }; match result.0 { Ok(_) => {} @@ -666,16 +676,21 @@ pub fn eval_redeemer( prog.into() }; - let costs = if let Some(costs) = &cost_mdls.plutus_v1 { - costs - } else { - return Err(anyhow::Error::msg("PlutusV1 cost model not found.")); - }; - - let result = program + let program = program .apply_data(redeemer.data.clone()) - .apply_data(script_context.to_plutus_data()) - .eval_with_params(&Language::PlutusV1, &costs); + .apply_data(script_context.to_plutus_data()); + + let result = if let Some(cost_mdls) = cost_mdls_opt { + let costs = if let Some(costs) = &cost_mdls.plutus_v1 { + costs + } else { + return Err(anyhow::Error::msg("PlutusV1 cost model not found.")); + }; + + program.eval_as(&Language::PlutusV1, costs) + } else { + program.eval_v1() + }; match result.0 { Ok(_) => {} @@ -706,16 +721,21 @@ pub fn eval_redeemer( prog.into() }; - let costs = if let Some(costs) = &cost_mdls.plutus_v2 { - costs - } else { - return Err(anyhow::Error::msg("PlutusV2 cost model not found.")); - }; - - let result = program + let program = program .apply_data(redeemer.data.clone()) - .apply_data(script_context.to_plutus_data()) - .eval_with_params(&Language::PlutusV2, &costs); + .apply_data(script_context.to_plutus_data()); + + let result = if let Some(cost_mdls) = cost_mdls_opt { + let costs = if let Some(costs) = &cost_mdls.plutus_v1 { + costs + } else { + return Err(anyhow::Error::msg("PlutusV1 cost model not found.")); + }; + + program.eval_as(&Language::PlutusV2, costs) + } else { + program.eval() + }; match result.0 { Ok(_) => {} diff --git a/crates/uplc/src/transaction_eval/script_context.rs b/crates/uplc/src/transaction_eval/script_context.rs index 4e883464..2ae643b9 100644 --- a/crates/uplc/src/transaction_eval/script_context.rs +++ b/crates/uplc/src/transaction_eval/script_context.rs @@ -74,7 +74,7 @@ pub struct ScriptContext { } //---- Time conversion: slot range => posix time range -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct TimeRange { pub lower_bound: Option, pub upper_bound: Option, From 9e280f9cb547c677554f2cbad1869f286cb66118 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sun, 18 Sep 2022 15:35:10 -0400 Subject: [PATCH 48/77] feat: rename transaction eval and add error enum --- crates/cli/src/main.rs | 8 ++++--- crates/uplc/src/lib.rs | 6 ++--- crates/uplc/src/machine/error.rs | 4 +--- .../uplc/src/{transaction_eval.rs => tx.rs} | 22 +++++++++---------- crates/uplc/src/tx/error.rs | 7 ++++++ .../uplc/src/{transaction_eval => tx}/eval.rs | 13 ++++++----- .../script_context.rs | 0 .../to_plutus_data.rs | 0 8 files changed, 34 insertions(+), 26 deletions(-) rename crates/uplc/src/{transaction_eval.rs => tx.rs} (98%) create mode 100644 crates/uplc/src/tx/error.rs rename crates/uplc/src/{transaction_eval => tx}/eval.rs (99%) rename crates/uplc/src/{transaction_eval => tx}/script_context.rs (100%) rename crates/uplc/src/{transaction_eval => tx}/to_plutus_data.rs (100%) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index eac3e103..58189423 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -9,8 +9,10 @@ use uplc::{ ast::{DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term}, machine::cost_model::ExBudget, parser, - transaction_eval::eval_tx, - transaction_eval::script_context::{ResolvedInput, SlotConfig}, + tx::{ + self, + script_context::{ResolvedInput, SlotConfig}, + }, }; mod args; @@ -52,7 +54,7 @@ fn main() -> anyhow::Result<()> { slot_length, }; - eval_tx(tx_babbage, &resolved_inputs, None, &slot_config)?; + tx::eval(tx_babbage, &resolved_inputs, None, &slot_config)?; } } }, diff --git a/crates/uplc/src/lib.rs b/crates/uplc/src/lib.rs index 5c14dd07..55b107e7 100644 --- a/crates/uplc/src/lib.rs +++ b/crates/uplc/src/lib.rs @@ -6,11 +6,11 @@ pub mod machine; pub mod parser; mod pretty; pub mod program_builder; -pub mod transaction_eval; +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/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/transaction_eval.rs b/crates/uplc/src/tx.rs similarity index 98% rename from crates/uplc/src/transaction_eval.rs rename to crates/uplc/src/tx.rs index 2c723eb9..9e4b6126 100644 --- a/crates/uplc/src/transaction_eval.rs +++ b/crates/uplc/src/tx.rs @@ -4,20 +4,20 @@ use pallas_primitives::{ }; use pallas_traverse::{Era, MultiEraTx}; -use crate::Error; - -use self::script_context::{ResolvedInput, SlotConfig}; +use error::Error; +use script_context::{ResolvedInput, SlotConfig}; +mod error; mod eval; pub mod script_context; mod to_plutus_data; -pub fn eval_tx( +pub fn eval( tx: &MintedTx, utxos: &[ResolvedInput], cost_mdls: Option<&CostMdls>, slot_config: &SlotConfig, -) -> anyhow::Result> { +) -> Result, Error> { let redeemers = tx.transaction_witness_set.redeemer.as_ref(); let lookup_table = eval::get_script_and_datum_lookup_table(tx, utxos); @@ -41,7 +41,7 @@ pub fn eval_tx( } } -pub fn eval_tx_raw( +pub fn eval_raw( tx_bytes: &[u8], utxos_bytes: &[(Vec, Vec)], cost_mdls_bytes: &[u8], @@ -68,7 +68,7 @@ pub fn eval_tx_raw( }; match multi_era_tx { - MultiEraTx::Babbage(tx) => match eval_tx(&tx, &utxos, Some(&cost_mdls), &sc) { + MultiEraTx::Babbage(tx) => match eval(&tx, &utxos, Some(&cost_mdls), &sc) { Ok(redeemers) => Ok(redeemers .iter() .map(|r| r.encode_fragment().unwrap()) @@ -96,7 +96,7 @@ mod tests { }; use pallas_traverse::{Era, MultiEraTx}; - use super::{eval_tx, ResolvedInput, SlotConfig}; + use super::{eval, ResolvedInput, SlotConfig}; #[test] fn test_eval() { @@ -322,7 +322,7 @@ mod tests { .unwrap(); match multi_era_tx { MultiEraTx::Babbage(tx) => { - let redeemers = eval_tx(&tx, &utxos, Some(&cost_mdl), &slot_config).unwrap(); + let redeemers = eval(&tx, &utxos, Some(&cost_mdl), &slot_config).unwrap(); assert_eq!(redeemers.len(), 1) } @@ -556,7 +556,7 @@ mod tests { .unwrap(); match multi_era_tx { MultiEraTx::Babbage(tx) => { - let redeemers = eval_tx(&tx, &utxos, Some(&cost_mdl), &slot_config).unwrap(); + let redeemers = eval(&tx, &utxos, Some(&cost_mdl), &slot_config).unwrap(); println!("{:?}", redeemers.len()); } @@ -626,7 +626,7 @@ mod tests { .unwrap(); match multi_era_tx { MultiEraTx::Babbage(tx) => { - let redeemers = eval_tx(&tx, &utxos, Some(&cost_mdl), &slot_config).unwrap(); + let redeemers = eval(&tx, &utxos, Some(&cost_mdl), &slot_config).unwrap(); println!("{:?}", redeemers.len()); } diff --git a/crates/uplc/src/tx/error.rs b/crates/uplc/src/tx/error.rs new file mode 100644 index 00000000..2e7b003e --- /dev/null +++ b/crates/uplc/src/tx/error.rs @@ -0,0 +1,7 @@ +use crate::machine; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Runtime error")] + Machine(#[from] machine::Error), +} diff --git a/crates/uplc/src/transaction_eval/eval.rs b/crates/uplc/src/tx/eval.rs similarity index 99% rename from crates/uplc/src/transaction_eval/eval.rs rename to crates/uplc/src/tx/eval.rs index 3ae1c88e..ab3f8595 100644 --- a/crates/uplc/src/transaction_eval/eval.rs +++ b/crates/uplc/src/tx/eval.rs @@ -20,6 +20,7 @@ use super::{ TxInfoV1, TxInfoV2, TxOut, }, to_plutus_data::ToPlutusData, + Error, }; fn slot_to_begin_posix_time(slot: u64, sc: &SlotConfig) -> u64 { @@ -58,7 +59,7 @@ pub struct DataLookupTable { pub fn get_tx_in_info_v1( inputs: &[TransactionInput], utxos: &[ResolvedInput], -) -> anyhow::Result> { +) -> Result, Error> { let result = inputs .iter() .map(|input| { @@ -105,7 +106,7 @@ pub fn get_tx_in_info_v1( fn get_tx_in_info_v2( inputs: &[TransactionInput], utxos: &[ResolvedInput], -) -> anyhow::Result> { +) -> Result, Error> { let result = inputs .iter() .map(|input| { @@ -142,7 +143,7 @@ fn get_script_purpose( mint: &Option, dcert: &Option>, wdrl: &Option, -) -> anyhow::Result { +) -> 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; @@ -226,7 +227,7 @@ fn get_tx_info_v1( tx: &MintedTx, utxos: &[ResolvedInput], slot_config: &SlotConfig, -) -> anyhow::Result { +) -> Result { let body = tx.transaction_body.clone(); if body.reference_inputs.is_some() { @@ -289,7 +290,7 @@ fn get_tx_info_v2( tx: &MintedTx, utxos: &[ResolvedInput], slot_config: &SlotConfig, -) -> anyhow::Result { +) -> Result { let body = tx.transaction_body.clone(); let inputs = get_tx_in_info_v2(&body.inputs, utxos)?; @@ -557,7 +558,7 @@ pub fn eval_redeemer( redeemer: &Redeemer, lookup_table: &DataLookupTable, cost_mdls_opt: Option<&CostMdls>, -) -> anyhow::Result { +) -> Result { let purpose = get_script_purpose( redeemer, &tx.transaction_body.inputs, diff --git a/crates/uplc/src/transaction_eval/script_context.rs b/crates/uplc/src/tx/script_context.rs similarity index 100% rename from crates/uplc/src/transaction_eval/script_context.rs rename to crates/uplc/src/tx/script_context.rs diff --git a/crates/uplc/src/transaction_eval/to_plutus_data.rs b/crates/uplc/src/tx/to_plutus_data.rs similarity index 100% rename from crates/uplc/src/transaction_eval/to_plutus_data.rs rename to crates/uplc/src/tx/to_plutus_data.rs From 6e901de2f092f2e9a1b119afa84533b91fb8d5a4 Mon Sep 17 00:00:00 2001 From: Harper Date: Mon, 19 Sep 2022 04:31:30 +0100 Subject: [PATCH 49/77] feat: implement script-related ledger checks for Tx Simulate (#57) * feat: functions for extraneous/missing redeemers checks * chore: typos * feat: implement function to check for missing/extraneous scripts * feat: check for missing/extraneous redeemers and scripts in eval_tx * chore: add tests for missing/extraneous redeemers * chore: remove duplicate file --- crates/uplc/src/tx.rs | 129 ++++++++++++- crates/uplc/src/tx/eval.rs | 36 +++- crates/uplc/src/tx/phase_one.rs | 325 ++++++++++++++++++++++++++++++++ 3 files changed, 480 insertions(+), 10 deletions(-) create mode 100644 crates/uplc/src/tx/phase_one.rs diff --git a/crates/uplc/src/tx.rs b/crates/uplc/src/tx.rs index 9e4b6126..833f924b 100644 --- a/crates/uplc/src/tx.rs +++ b/crates/uplc/src/tx.rs @@ -4,13 +4,15 @@ use pallas_primitives::{ }; use pallas_traverse::{Era, MultiEraTx}; -use error::Error; -use script_context::{ResolvedInput, SlotConfig}; +use crate::Error; + +use self::{script_context::{ResolvedInput, SlotConfig}, phase_one::eval_phase_one}; mod error; mod eval; pub mod script_context; mod to_plutus_data; +mod phase_one; pub fn eval( tx: &MintedTx, @@ -22,6 +24,9 @@ pub fn eval( let lookup_table = eval::get_script_and_datum_lookup_table(tx, utxos); + // 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![]; @@ -633,4 +638,124 @@ mod tests { _ => 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 + 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 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(&tx, &utxos, Some(&cost_mdl), &slot_config).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 + 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 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(&tx, &utxos, Some(&cost_mdl), &slot_config).unwrap(); + } + _ => unreachable!(), + }; + } } diff --git a/crates/uplc/src/tx/eval.rs b/crates/uplc/src/tx/eval.rs index ab3f8595..e3ec011a 100644 --- a/crates/uplc/src/tx/eval.rs +++ b/crates/uplc/src/tx/eval.rs @@ -9,7 +9,7 @@ use pallas_crypto::hash::Hash; use pallas_primitives::babbage::{ Certificate, CostMdls, DatumHash, DatumOption, ExUnits, Language, Mint, MintedTx, PlutusV1Script, PlutusV2Script, PolicyId, Redeemer, RedeemerTag, RewardAccount, Script, - StakeCredential, TransactionInput, TransactionOutput, Value, Withdrawals, + StakeCredential, TransactionInput, TransactionOutput, Value, Withdrawals, NativeScript, }; use pallas_traverse::{ComputeHash, OriginalHash}; use std::{collections::HashMap, convert::TryInto, ops::Deref, vec}; @@ -40,7 +40,8 @@ fn slot_range_to_posix_time_range(slot_range: TimeRange, sc: &SlotConfig) -> Tim } #[derive(Debug, PartialEq, Clone)] -enum ScriptVersion { +pub enum ScriptVersion { + Native(NativeScript), V1(PlutusV1Script), V2(PlutusV2Script), } @@ -56,6 +57,12 @@ pub struct DataLookupTable { scripts: HashMap, } +impl DataLookupTable { + pub fn scripts(&self) -> HashMap { + self.scripts.clone() + } +} + pub fn get_tx_in_info_v1( inputs: &[TransactionInput], utxos: &[ResolvedInput], @@ -184,15 +191,15 @@ fn get_script_purpose( .as_ref() .unwrap_or(&KeyValuePairs::Indef(vec![])) .iter() - .map(|(policy_id, _)| policy_id.clone()) + .map(|(racnt, _)| racnt.clone()) .collect::>(); reward_accounts.sort(); let reward_account = match reward_accounts.get(index as usize) { Some(ra) => ra.clone(), None => unreachable!("Script purpose not found for redeemer."), }; - let addresss = Address::from_bytes(&reward_account)?; - let credential = match addresss { + 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) @@ -501,6 +508,12 @@ pub fn get_script_and_datum_lookup_table( .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 @@ -517,14 +530,17 @@ pub fn get_script_and_datum_lookup_table( 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())); + // TODO: implement `original_hash` for native scripts in pallas + } + for script in scripts_v1_witnesses.iter() { scripts.insert(script.compute_hash(), ScriptVersion::V1(script.clone())); - // TODO: fix hashing bug in pallas } for script in scripts_v2_witnesses.iter() { scripts.insert(script.compute_hash(), ScriptVersion::V2(script.clone())); - // TODO: fix hashing bug in pallas } // discovery in utxos (script ref) @@ -535,13 +551,15 @@ pub fn get_script_and_datum_lookup_table( 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())); } - _ => {} } } } @@ -663,6 +681,7 @@ pub fn eval_redeemer( Ok(new_redeemer) } + ScriptVersion::Native(_) => unreachable!("Native script can't be executed in phase-two.") }, ExecutionPurpose::NoDatum(script_version) => match script_version { ScriptVersion::V1(script) => { @@ -755,6 +774,7 @@ pub fn eval_redeemer( Ok(new_redeemer) } + ScriptVersion::Native(_) => unreachable!("Native script can't be executed in phase-two.") }, } } diff --git a/crates/uplc/src/tx/phase_one.rs b/crates/uplc/src/tx/phase_one.rs new file mode 100644 index 00000000..f6bb93b6 --- /dev/null +++ b/crates/uplc/src/tx/phase_one.rs @@ -0,0 +1,325 @@ +use std::collections::HashMap; + +use pallas_addresses::{ScriptHash, Address, ShelleyPaymentPart, StakePayload}; +use pallas_codec::utils::{KeyValuePairs, MaybeIndefArray}; +use pallas_primitives::babbage::{MintedTx, TransactionOutput, StakeCredential, Certificate, RedeemerTag, RewardAccount, PolicyId}; + +use super::{script_context::{ScriptPurpose, ResolvedInput}, eval::{ScriptVersion, DataLookupTable}}; + +// 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, +) -> anyhow::Result<()> { + 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, +) -> anyhow::Result<()> { + let received_hashes = txscripts + .keys() + .map(|x| *x) + .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.len() > 0 || extra.len() > 0 { + 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], +) -> AlonzoScriptsNeeded { + let mut needed = Vec::new(); + + let txb = tx.transaction_body.clone(); + + let mut spend = txb.inputs + .iter() + .map(|input| { + let utxo = match utxos.iter().find(|utxo| utxo.input == *input) { + Some(u) => u, + None => panic!("Resolved input not found."), + }; + let address = Address::from_bytes(match &utxo.output { + TransactionOutput::Legacy(output) => output.address.as_ref(), + TransactionOutput::PostAlonzo(output) => output.address.as_ref(), + }) + .unwrap(); + + if let Address::Shelley(a) = address { + if let ShelleyPaymentPart::Script(h) = a.payment() { + return Some((ScriptPurpose::Spending(input.clone()), *h)) + } + } + + None + }) + .flatten() + .collect::(); + + let mut reward = txb.withdrawals + .as_ref() + .unwrap_or(&KeyValuePairs::Indef(vec![])) + .iter() + .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 + }) + .flatten() + .collect::(); + + let mut cert = txb.certificates + .clone() + .unwrap_or_default() + .iter() + .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 + } + }) + .flatten() + .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); + + needed +} + +/// hasExactSetOfRedeemers in Ledger Spec, but we pass `txscripts` directly +pub fn has_exact_set_of_redeemers( + tx: &MintedTx, + needed: &AlonzoScriptsNeeded, + txscripts: HashMap, +) -> anyhow::Result<()> { + let redeemers_needed = needed + .iter() + .map(|(sp, sh)| { + let rp = rdptr(tx, sp); + let script = txscripts.get(&sh); + + match (rp, script) { + (Some(ptr), Some(script)) => match script { + ScriptVersion::V1(_) => Some((ptr, sp.clone(), *sh)), + ScriptVersion::V2(_) => Some((ptr, sp.clone(), *sh)), + ScriptVersion::Native(_) => None, + }, + _ => None + } + }) + .flatten() + .collect::>(); + + let wits_rdptrs = 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_rdptrs = redeemers_needed + .iter() + .map(|x| x.0.clone()) + .collect::>(); + + let missing: Vec<_> = redeemers_needed + .into_iter() + .filter(|x| !wits_rdptrs.contains(&x.0)) + .map(|x| format!( + "[Missing (rp: {:?}, sp: {:?}, sh: {})]", + x.0, + x.1, + x.2.to_string(), + )) + .collect(); + + let extra: Vec<_> = wits_rdptrs + .into_iter() + .filter(|x| !needed_rdptrs.contains(x)) + .map(|x| format!( + "[Extraneous (rp: {:?})]", + x + )) + .collect(); + + if missing.len() > 0 || extra.len() > 0 { + let missing_errors = missing.join(" "); + let extra_errors = extra.join(" "); + + unreachable!("Mismatch in required redeemers: {} {}", missing_errors, extra_errors); + } + + 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 rdptr( + tx: &MintedTx, + sp: &ScriptPurpose, +) -> Option { + let txb = tx.transaction_body.clone(); + + match sp { + ScriptPurpose::Minting(hash) => { + let mut policy_ids = txb.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) => Some(RedeemerPtr { tag: RedeemerTag::Mint, index: idx as u32 }), + None => None, + } + } + ScriptPurpose::Spending(txin) => { + let mut inputs = txb.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) => Some(RedeemerPtr { tag: RedeemerTag::Spend, index: idx as u32 }), + None => None, + } + }, + ScriptPurpose::Rewarding(racnt) => { + let mut reward_accounts = txb.withdrawals + .as_ref() + .unwrap_or(&KeyValuePairs::Indef(vec![])) + .iter() + .map(|(acnt, _)| acnt.clone()) + .collect::>(); + + reward_accounts.sort(); + + let maybe_idx = reward_accounts.iter().position(|x| { + let cred = match Address::from_bytes(x).unwrap() { + Address::Stake(a) => match a.payload() { + StakePayload::Script(sh) => { + StakeCredential::Scripthash(*sh) + } + StakePayload::Stake(_) => { + unreachable!( + "This is impossible. A key hash cannot be the hash of a script." + ); + } + }, + _ => unreachable!( + "This is impossible. Only shelley reward addresses can be a part of withdrawals." + ), + }; + + cred == *racnt + }); + + match maybe_idx { + Some(idx) => Some(RedeemerPtr { tag: RedeemerTag::Reward, index: idx as u32 }), + None => None, + } + }, + ScriptPurpose::Certifying(d) => { + let maybe_idx = txb.certificates + .as_ref() + .unwrap_or(&MaybeIndefArray::Indef(vec![])) + .iter() + .position(|x| x == d); + + match maybe_idx { + Some(idx) => Some(RedeemerPtr{ tag: RedeemerTag::Cert, index: idx as u32 }), + None => None + } + }, + } +} From 9bab3187b125030e882042005652a384964e3baa Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 19 Sep 2022 00:39:52 -0400 Subject: [PATCH 50/77] feat: more errors --- crates/uplc/src/tx.rs | 751 ++------------------------- crates/uplc/src/tx/error.rs | 21 +- crates/uplc/src/tx/phase_one.rs | 264 +++++----- crates/uplc/src/tx/tests.rs | 676 ++++++++++++++++++++++++ crates/uplc/src/tx/to_plutus_data.rs | 6 +- 5 files changed, 884 insertions(+), 834 deletions(-) create mode 100644 crates/uplc/src/tx/tests.rs diff --git a/crates/uplc/src/tx.rs b/crates/uplc/src/tx.rs index 833f924b..cd583c77 100644 --- a/crates/uplc/src/tx.rs +++ b/crates/uplc/src/tx.rs @@ -4,68 +4,85 @@ use pallas_primitives::{ }; use pallas_traverse::{Era, MultiEraTx}; -use crate::Error; +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 self::{script_context::{ResolvedInput, SlotConfig}, phase_one::eval_phase_one}; - -mod error; +pub mod error; mod eval; -pub mod script_context; -mod to_plutus_data; mod phase_one; +pub mod script_context; +#[cfg(test)] +mod tests; +mod to_plutus_data; -pub fn eval( +/// 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>, slot_config: &SlotConfig, + run_phase_one: bool, ) -> Result, Error> { let redeemers = tx.transaction_witness_set.redeemer.as_ref(); - let lookup_table = eval::get_script_and_datum_lookup_table(tx, utxos); + let lookup_table = get_script_and_datum_lookup_table(tx, utxos); - // subset of phase 1 check on redeemers and scripts - eval_phase_one(tx, utxos, &lookup_table)?; + 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() { - collected_redeemers.push(eval::eval_redeemer( + let redeemer = eval::eval_redeemer( tx, utxos, slot_config, redeemer, &lookup_table, cost_mdls, - )?) + )?; + + collected_redeemers.push(redeemer) } + Ok(collected_redeemers) } None => Ok(vec![]), } } -pub fn eval_raw( +/// This function is the same as [`eval_phase_two`] +/// but the inputs are raw bytes. +pub fn eval_phase_two_raw( tx_bytes: &[u8], utxos_bytes: &[(Vec, Vec)], cost_mdls_bytes: &[u8], slot_config: (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)) - .map_err(|_| Error::from("Wrong era. Please use Babbage or Alonzo"))?; // TODO: proper error message + .or_else(|_| MultiEraTx::decode(Era::Alonzo, tx_bytes))?; - let cost_mdls = CostMdls::decode_fragment(cost_mdls_bytes) - .map_err(|_| Error::from("Unable to decode cost models"))?; // TODO: proper error message + let cost_mdls = CostMdls::decode_fragment(cost_mdls_bytes)?; - let utxos: Vec = utxos_bytes - .iter() - .map(|(input, _output)| ResolvedInput { - input: TransactionInput::decode_fragment(input).unwrap(), - output: TransactionOutput::decode_fragment(input).unwrap(), - }) - .collect(); + let mut utxos = Vec::new(); + + for (input, _output) in utxos_bytes { + utxos.push(ResolvedInput { + input: TransactionInput::decode_fragment(input)?, + output: TransactionOutput::decode_fragment(input)?, + }); + } let sc = SlotConfig { zero_time: slot_config.0, @@ -73,13 +90,15 @@ pub fn eval_raw( }; match multi_era_tx { - MultiEraTx::Babbage(tx) => match eval(&tx, &utxos, Some(&cost_mdls), &sc) { - Ok(redeemers) => Ok(redeemers - .iter() - .map(|r| r.encode_fragment().unwrap()) - .collect()), - Err(_) => Err(Error::from("Can't eval without redeemers")), - }, + MultiEraTx::Babbage(tx) => { + match eval_phase_two(&tx, &utxos, Some(&cost_mdls), &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() @@ -88,674 +107,6 @@ pub fn eval_raw( // Err(_) => Err(()), // }, // TODO: I probably did a mistake here with using MintedTx which is only compatible with Babbage tx. - _ => Err(Error::from("Wrong era. Please use babbage")), - } -} - -#[cfg(test)] -mod tests { - use pallas_codec::utils::MaybeIndefArray; - use pallas_primitives::{ - babbage::{CostMdls, TransactionInput, TransactionOutput}, - Fragment, - }; - use pallas_traverse::{Era, MultiEraTx}; - - use super::{eval, 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 - 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 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(&tx, &utxos, Some(&cost_mdl), &slot_config).unwrap(); - - assert_eq!(redeemers.len(), 1) - } - _ => unreachable!(), - }; - } - - #[test] - fn test_eval_1() { - /* - - 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 - 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 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(&tx, &utxos, Some(&cost_mdl), &slot_config).unwrap(); - - println!("{:?}", redeemers.len()); - } - _ => unreachable!(), - }; - } - - #[test] - fn test_eval_2() { - /* - - 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 - 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 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(&tx, &utxos, Some(&cost_mdl), &slot_config).unwrap(); - - println!("{:?}", redeemers.len()); - } - _ => 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 - 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 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(&tx, &utxos, Some(&cost_mdl), &slot_config).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 - 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 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(&tx, &utxos, Some(&cost_mdl), &slot_config).unwrap(); - } - _ => unreachable!(), - }; + _ => todo!("Wrong era. Please use babbage"), } } diff --git a/crates/uplc/src/tx/error.rs b/crates/uplc/src/tx/error.rs index 2e7b003e..804405a9 100644 --- a/crates/uplc/src/tx/error.rs +++ b/crates/uplc/src/tx/error.rs @@ -2,6 +2,25 @@ use crate::machine; #[derive(thiserror::Error, Debug)] pub enum Error { - #[error("Runtime error")] + #[error("{0}")] + Address(#[from] pallas_addresses::Error), + #[error("Only shelley reward addresses can be a part of withdrawals")] + BadWithdrawalAddress, + #[error("{0}")] + FragmentDecode(#[from] pallas_primitives::Error), + #[error("{0}")] Machine(#[from] machine::Error), + #[error("Can't eval without redeemers")] + NoRedeemers, + #[error("Mismatch in required redeemers: {} {}", .missing.join(" "), .extra.join(" "))] + RequiredRedeemersMismatch { + missing: Vec, + extra: Vec, + }, + #[error("Resolved Input not found")] + ResolvedInputNotFound, + #[error("A key hash cannot be the hash of a script")] + ScriptKeyHash, + #[error("Wrong era, Please use Babbage or Alonzo: {0}")] + WrongEra(#[from] pallas_codec::minicbor::decode::Error), } diff --git a/crates/uplc/src/tx/phase_one.rs b/crates/uplc/src/tx/phase_one.rs index f6bb93b6..ef2df2e6 100644 --- a/crates/uplc/src/tx/phase_one.rs +++ b/crates/uplc/src/tx/phase_one.rs @@ -1,10 +1,16 @@ use std::collections::HashMap; -use pallas_addresses::{ScriptHash, Address, ShelleyPaymentPart, StakePayload}; +use pallas_addresses::{Address, ScriptHash, ShelleyPaymentPart, StakePayload}; use pallas_codec::utils::{KeyValuePairs, MaybeIndefArray}; -use pallas_primitives::babbage::{MintedTx, TransactionOutput, StakeCredential, Certificate, RedeemerTag, RewardAccount, PolicyId}; +use pallas_primitives::babbage::{ + Certificate, MintedTx, PolicyId, RedeemerTag, RewardAccount, StakeCredential, TransactionOutput, +}; -use super::{script_context::{ScriptPurpose, ResolvedInput}, eval::{ScriptVersion, DataLookupTable}}; +use super::{ + error::Error, + eval::{DataLookupTable, ScriptVersion}, + script_context::{ResolvedInput, ScriptPurpose}, +}; // TODO: include in pallas eventually? #[derive(Debug, PartialEq, Clone)] @@ -20,8 +26,8 @@ pub fn eval_phase_one( tx: &MintedTx, utxos: &[ResolvedInput], lookup_table: &DataLookupTable, -) -> anyhow::Result<()> { - let scripts_needed = scripts_needed(tx, utxos); +) -> Result<(), Error> { + let scripts_needed = scripts_needed(tx, utxos)?; validate_missing_scripts(&scripts_needed, lookup_table.scripts())?; @@ -33,41 +39,32 @@ pub fn eval_phase_one( pub fn validate_missing_scripts( needed: &AlonzoScriptsNeeded, txscripts: HashMap, -) -> anyhow::Result<()> { - let received_hashes = txscripts - .keys() - .map(|x| *x) - .collect::>(); +) -> Result<(), Error> { + let received_hashes = txscripts.keys().map(|x| *x).collect::>(); - let needed_hashes = needed - .iter() - .map(|x| x.1) - .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 - )) + .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 - )) + .map(|x| format!("[Extraneous (sh: {:?})]", x)) .collect(); if missing.len() > 0 || extra.len() > 0 { let missing_errors = missing.join(" "); let extra_errors = extra.join(" "); - unreachable!("Mismatch in required scripts: {} {}", missing_errors, extra_errors); + unreachable!( + "Mismatch in required scripts: {} {}", + missing_errors, extra_errors + ); } Ok(()) @@ -76,36 +73,33 @@ pub fn validate_missing_scripts( pub fn scripts_needed( tx: &MintedTx, utxos: &[ResolvedInput], -) -> AlonzoScriptsNeeded { +) -> Result { let mut needed = Vec::new(); let txb = tx.transaction_body.clone(); - let mut spend = txb.inputs - .iter() - .map(|input| { - let utxo = match utxos.iter().find(|utxo| utxo.input == *input) { - Some(u) => u, - None => panic!("Resolved input not found."), - }; - let address = Address::from_bytes(match &utxo.output { - TransactionOutput::Legacy(output) => output.address.as_ref(), - TransactionOutput::PostAlonzo(output) => output.address.as_ref(), - }) - .unwrap(); + let mut spend = Vec::new(); - if let Address::Shelley(a) = address { - if let ShelleyPaymentPart::Script(h) = a.payment() { - return Some((ScriptPurpose::Spending(input.clone()), *h)) - } + for input in txb.inputs { + 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)); } + } + } - None - }) - .flatten() - .collect::(); - - let mut reward = txb.withdrawals + let mut reward = txb + .withdrawals .as_ref() .unwrap_or(&KeyValuePairs::Indef(vec![])) .iter() @@ -115,7 +109,7 @@ pub fn scripts_needed( if let Address::Stake(a) = address { if let StakePayload::Script(h) = a.payload() { let cred = StakeCredential::Scripthash(*h); - return Some((ScriptPurpose::Rewarding(cred), *h)) + return Some((ScriptPurpose::Rewarding(cred), *h)); } } @@ -124,7 +118,8 @@ pub fn scripts_needed( .flatten() .collect::(); - let mut cert = txb.certificates + let mut cert = txb + .certificates .clone() .unwrap_or_default() .iter() @@ -133,23 +128,22 @@ pub fn scripts_needed( 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 + } + _ => None, } }) .flatten() .collect::(); - let mut mint = txb.mint + let mut mint = txb + .mint .as_ref() .unwrap_or(&KeyValuePairs::Indef(vec![])) .iter() - .map(|(policy_id, _)| { - (ScriptPurpose::Minting(*policy_id), *policy_id) - }) + .map(|(policy_id, _)| (ScriptPurpose::Minting(*policy_id), *policy_id)) .collect::(); needed.append(&mut spend); @@ -157,91 +151,91 @@ pub fn scripts_needed( needed.append(&mut cert); needed.append(&mut mint); - needed + Ok(needed) } /// hasExactSetOfRedeemers in Ledger Spec, but we pass `txscripts` directly pub fn has_exact_set_of_redeemers( tx: &MintedTx, needed: &AlonzoScriptsNeeded, - txscripts: HashMap, -) -> anyhow::Result<()> { - let redeemers_needed = needed - .iter() - .map(|(sp, sh)| { - let rp = rdptr(tx, sp); - let script = txscripts.get(&sh); + tx_scripts: HashMap, +) -> Result<(), Error> { + let mut redeemers_needed = Vec::new(); - match (rp, script) { - (Some(ptr), Some(script)) => match script { - ScriptVersion::V1(_) => Some((ptr, sp.clone(), *sh)), - ScriptVersion::V2(_) => Some((ptr, sp.clone(), *sh)), - ScriptVersion::Native(_) => None, - }, - _ => None + 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(_) => (), } - }) - .flatten() - .collect::>(); + } + } - let wits_rdptrs = tx + 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 } + .map(|r| RedeemerPtr { + tag: r.tag.clone(), + index: r.index, }) - .collect::>(); + .collect(); - let needed_rdptrs = redeemers_needed - .iter() - .map(|x| x.0.clone()) - .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_rdptrs.contains(&x.0)) - .map(|x| format!( - "[Missing (rp: {:?}, sp: {:?}, sh: {})]", - x.0, - x.1, - x.2.to_string(), - )) + .filter(|x| !wits_redeemer_ptrs.contains(&x.0)) + .map(|x| { + format!( + "[Missing (redeemer_ptr: {:?}, script_purpose: {:?}, script_hash: {})]", + x.0, + x.1, + x.2.to_string(), + ) + }) .collect(); - let extra: Vec<_> = wits_rdptrs + let extra: Vec<_> = wits_redeemer_ptrs .into_iter() - .filter(|x| !needed_rdptrs.contains(x)) - .map(|x| format!( - "[Extraneous (rp: {:?})]", - x - )) + .filter(|x| !needed_redeemer_ptrs.contains(x)) + .map(|x| format!("[Extraneous (redeemer_ptr: {:?})]", x)) .collect(); if missing.len() > 0 || extra.len() > 0 { let missing_errors = missing.join(" "); let extra_errors = extra.join(" "); - unreachable!("Mismatch in required redeemers: {} {}", missing_errors, extra_errors); + Err(Error::RequiredRedeemersMismatch { missing, extra }) + } else { + Ok(()) } - - 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 rdptr( +fn build_redeemer_ptr( tx: &MintedTx, - sp: &ScriptPurpose, -) -> Option { - let txb = tx.transaction_body.clone(); + script_purpose: &ScriptPurpose, +) -> Result, Error> { + let tx_body = tx.transaction_body.clone(); - match sp { + match script_purpose { ScriptPurpose::Minting(hash) => { - let mut policy_ids = txb.mint + let mut policy_ids = tx_body + .mint .as_ref() .unwrap_or(&KeyValuePairs::Indef(vec![])) .iter() @@ -253,12 +247,15 @@ fn rdptr( let maybe_idx = policy_ids.iter().position(|x| x == hash); match maybe_idx { - Some(idx) => Some(RedeemerPtr { tag: RedeemerTag::Mint, index: idx as u32 }), - None => None, + Some(idx) => Ok(Some(RedeemerPtr { + tag: RedeemerTag::Mint, + index: idx as u32, + })), + None => Ok(None), } } ScriptPurpose::Spending(txin) => { - let mut inputs = txb.inputs.to_vec(); + 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, @@ -270,12 +267,16 @@ fn rdptr( let maybe_idx = inputs.iter().position(|x| x == txin); match maybe_idx { - Some(idx) => Some(RedeemerPtr { tag: RedeemerTag::Spend, index: idx as u32 }), - None => None, + Some(idx) => Ok(Some(RedeemerPtr { + tag: RedeemerTag::Spend, + index: idx as u32, + })), + None => Ok(None), } - }, + } ScriptPurpose::Rewarding(racnt) => { - let mut reward_accounts = txb.withdrawals + let mut reward_accounts = tx_body + .withdrawals .as_ref() .unwrap_or(&KeyValuePairs::Indef(vec![])) .iter() @@ -284,42 +285,47 @@ fn rdptr( reward_accounts.sort(); - let maybe_idx = reward_accounts.iter().position(|x| { + 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::Script(sh) => StakeCredential::Scripthash(*sh), StakePayload::Stake(_) => { - unreachable!( - "This is impossible. A key hash cannot be the hash of a script." - ); + return Err(Error::ScriptKeyHash); } }, - _ => unreachable!( - "This is impossible. Only shelley reward addresses can be a part of withdrawals." - ), + _ => return Err(Error::BadWithdrawalAddress), }; - cred == *racnt - }); + if cred == *racnt { + maybe_idx = Some(idx); + } + } match maybe_idx { - Some(idx) => Some(RedeemerPtr { tag: RedeemerTag::Reward, index: idx as u32 }), - None => None, + Some(idx) => Ok(Some(RedeemerPtr { + tag: RedeemerTag::Reward, + index: idx as u32, + })), + None => Ok(None), } - }, + } ScriptPurpose::Certifying(d) => { - let maybe_idx = txb.certificates + let maybe_idx = tx_body + .certificates .as_ref() .unwrap_or(&MaybeIndefArray::Indef(vec![])) .iter() .position(|x| x == d); match maybe_idx { - Some(idx) => Some(RedeemerPtr{ tag: RedeemerTag::Cert, index: idx as u32 }), - None => None + Some(idx) => Ok(Some(RedeemerPtr { + tag: RedeemerTag::Cert, + index: idx as u32, + })), + None => Ok(None), } - }, + } } } diff --git a/crates/uplc/src/tx/tests.rs b/crates/uplc/src/tx/tests.rs new file mode 100644 index 00000000..c8d9cef5 --- /dev/null +++ b/crates/uplc/src/tx/tests.rs @@ -0,0 +1,676 @@ +use pallas_codec::utils::MaybeIndefArray; +use pallas_primitives::{ + babbage::{CostMdls, TransactionInput, TransactionOutput}, + Fragment, +}; +use pallas_traverse::{Era, MultiEraTx}; + +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 + 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 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), &slot_config, false).unwrap(); + + assert_eq!(redeemers.len(), 1) + } + _ => unreachable!(), + }; +} + +#[test] +fn test_eval_1() { + /* + + 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 + 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 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), &slot_config, false).unwrap(); + + println!("{:?}", redeemers.len()); + } + _ => unreachable!(), + }; +} + +#[test] +fn test_eval_2() { + /* + + 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 + 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 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), &slot_config, false).unwrap(); + + println!("{:?}", redeemers.len()); + } + _ => 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 + 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 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), &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 + 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 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), &slot_config, false).unwrap(); + } + _ => unreachable!(), + }; +} diff --git a/crates/uplc/src/tx/to_plutus_data.rs b/crates/uplc/src/tx/to_plutus_data.rs index 570e2d16..7747509e 100644 --- a/crates/uplc/src/tx/to_plutus_data.rs +++ b/crates/uplc/src/tx/to_plutus_data.rs @@ -1,16 +1,14 @@ use pallas_addresses::{Address, ShelleyDelegationPart, ShelleyPaymentPart}; -use pallas_primitives::babbage::{AssetName, BigInt, Constr, PlutusData, ScriptRef}; - use pallas_codec::utils::{AnyUInt, Bytes, Int, KeyValuePairs}; use pallas_crypto::hash::Hash; +use pallas_primitives::babbage::{AssetName, BigInt, Constr, PlutusData, ScriptRef}; use pallas_primitives::babbage::{ Certificate, DatumOption, PolicyId, Redeemer, Script, StakeCredential, TransactionInput, TransactionOutput, Value, }; use pallas_traverse::ComputeHash; -use std::vec; -use super::script_context::{TxOut, TimeRange, TxInInfo, ScriptPurpose, TxInfo, ScriptContext}; +use super::script_context::{ScriptContext, ScriptPurpose, TimeRange, TxInInfo, TxInfo, TxOut}; fn wrap_with_constr(index: u64, data: PlutusData) -> PlutusData { PlutusData::Constr(Constr { From 6c34c9be19d1837e01ca274798eb33b6fe9168ac Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 19 Sep 2022 01:05:33 -0400 Subject: [PATCH 51/77] feat: even more errors work --- crates/cli/src/main.rs | 2 +- crates/uplc/src/tx/error.rs | 8 ++++ crates/uplc/src/tx/eval.rs | 76 ++++++++++++++++----------------- crates/uplc/src/tx/phase_one.rs | 19 +++------ 4 files changed, 54 insertions(+), 51 deletions(-) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 58189423..859f2adf 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -54,7 +54,7 @@ fn main() -> anyhow::Result<()> { slot_length, }; - tx::eval(tx_babbage, &resolved_inputs, None, &slot_config)?; + tx::eval_phase_two(tx_babbage, &resolved_inputs, None, &slot_config, true)?; } } }, diff --git a/crates/uplc/src/tx/error.rs b/crates/uplc/src/tx/error.rs index 804405a9..fbdc243b 100644 --- a/crates/uplc/src/tx/error.rs +++ b/crates/uplc/src/tx/error.rs @@ -7,9 +7,13 @@ pub enum 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("{0}")] Machine(#[from] machine::Error), + #[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(" "))] @@ -21,6 +25,10 @@ pub enum Error { 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), } diff --git a/crates/uplc/src/tx/eval.rs b/crates/uplc/src/tx/eval.rs index e3ec011a..7b355e77 100644 --- a/crates/uplc/src/tx/eval.rs +++ b/crates/uplc/src/tx/eval.rs @@ -7,9 +7,9 @@ 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, + Certificate, CostMdls, DatumHash, DatumOption, ExUnits, Language, Mint, MintedTx, NativeScript, PlutusV1Script, PlutusV2Script, PolicyId, Redeemer, RedeemerTag, RewardAccount, Script, - StakeCredential, TransactionInput, TransactionOutput, Value, Withdrawals, NativeScript, + StakeCredential, TransactionInput, TransactionOutput, Value, Withdrawals, }; use pallas_traverse::{ComputeHash, OriginalHash}; use std::{collections::HashMap, convert::TryInto, ops::Deref, vec}; @@ -606,11 +606,11 @@ pub fn eval_redeemer( .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()); - let result = if let Some(cost_mdls) = cost_mdls_opt { + let (result, budget, _) = if let Some(cost_mdls) = cost_mdls_opt { let costs = if let Some(costs) = &cost_mdls.plutus_v1 { costs } else { - return Err(anyhow::Error::msg("PlutusV1 cost model not found.")); + return Err(Error::V1CostModelNotFound); }; program.eval_as(&Language::PlutusV1, costs) @@ -618,18 +618,18 @@ pub fn eval_redeemer( program.eval_v1() }; - match result.0 { - Ok(_) => {} - Err(_err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message - } + // TODO: do we want the logs in the error? + result?; + + let initial_budget = ExBudget::default(); let new_redeemer = Redeemer { tag: redeemer.tag.clone(), index: redeemer.index, data: redeemer.data.clone(), ex_units: ExUnits { - mem: (ExBudget::default().mem - result.1.mem) as u32, - steps: (ExBudget::default().cpu - result.1.cpu) as u64, + mem: (initial_budget.mem - budget.mem) as u32, + steps: (initial_budget.cpu - budget.cpu) as u64, }, }; @@ -652,11 +652,11 @@ pub fn eval_redeemer( .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()); - let result = if let Some(cost_mdls) = cost_mdls_opt { - let costs = if let Some(costs) = &cost_mdls.plutus_v1 { + let (result, budget, _) = if let Some(cost_mdls) = cost_mdls_opt { + let costs = if let Some(costs) = &cost_mdls.plutus_v2 { costs } else { - return Err(anyhow::Error::msg("PlutusV1 cost model not found.")); + return Err(Error::V2CostModelNotFound); }; program.eval_as(&Language::PlutusV2, costs) @@ -664,24 +664,24 @@ pub fn eval_redeemer( program.eval() }; - match result.0 { - Ok(_) => {} - Err(_err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message - } + // TODO: do we want the logs in the error? + result?; + + let initial_budget = ExBudget::default(); let new_redeemer = Redeemer { tag: redeemer.tag.clone(), index: redeemer.index, data: redeemer.data.clone(), ex_units: ExUnits { - mem: (ExBudget::default().mem - result.1.mem) as u32, - steps: (ExBudget::default().cpu - result.1.cpu) as u64, + mem: (initial_budget.mem - budget.mem) as u32, + steps: (initial_budget.cpu - budget.cpu) as u64, }, }; Ok(new_redeemer) } - ScriptVersion::Native(_) => unreachable!("Native script can't be executed in phase-two.") + ScriptVersion::Native(_) => Err(Error::NativeScriptPhaseTwo), }, ExecutionPurpose::NoDatum(script_version) => match script_version { ScriptVersion::V1(script) => { @@ -700,11 +700,11 @@ pub fn eval_redeemer( .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()); - let result = if let Some(cost_mdls) = cost_mdls_opt { + let (result, budget, _) = if let Some(cost_mdls) = cost_mdls_opt { let costs = if let Some(costs) = &cost_mdls.plutus_v1 { costs } else { - return Err(anyhow::Error::msg("PlutusV1 cost model not found.")); + return Err(Error::V1CostModelNotFound); }; program.eval_as(&Language::PlutusV1, costs) @@ -712,18 +712,18 @@ pub fn eval_redeemer( program.eval_v1() }; - match result.0 { - Ok(_) => {} - Err(_err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message - } + // TODO: do we want the logs in the error? + result?; + + let initial_budget = ExBudget::default(); let new_redeemer = Redeemer { tag: redeemer.tag.clone(), index: redeemer.index, data: redeemer.data.clone(), ex_units: ExUnits { - mem: (ExBudget::default().mem - result.1.mem) as u32, - steps: (ExBudget::default().cpu - result.1.cpu) as u64, + mem: (initial_budget.mem - budget.mem) as u32, + steps: (initial_budget.cpu - budget.cpu) as u64, }, }; @@ -745,11 +745,11 @@ pub fn eval_redeemer( .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()); - let result = if let Some(cost_mdls) = cost_mdls_opt { - let costs = if let Some(costs) = &cost_mdls.plutus_v1 { + let (result, budget, _) = if let Some(cost_mdls) = cost_mdls_opt { + let costs = if let Some(costs) = &cost_mdls.plutus_v2 { costs } else { - return Err(anyhow::Error::msg("PlutusV1 cost model not found.")); + return Err(Error::V2CostModelNotFound); }; program.eval_as(&Language::PlutusV2, costs) @@ -757,24 +757,24 @@ pub fn eval_redeemer( program.eval() }; - match result.0 { - Ok(_) => {} - Err(_err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message - } + // TODO: do we want the logs in the error? + result?; + + let initial_budget = ExBudget::default(); let new_redeemer = Redeemer { tag: redeemer.tag.clone(), index: redeemer.index, data: redeemer.data.clone(), ex_units: ExUnits { - mem: (ExBudget::default().mem - result.1.mem) as u32, - steps: (ExBudget::default().cpu - result.1.cpu) as u64, + mem: (initial_budget.mem - budget.mem) as u32, + steps: (initial_budget.cpu - budget.cpu) as u64, }, }; Ok(new_redeemer) } - ScriptVersion::Native(_) => unreachable!("Native script can't be executed in phase-two.") + ScriptVersion::Native(_) => Err(Error::NativeScriptPhaseTwo), }, } } diff --git a/crates/uplc/src/tx/phase_one.rs b/crates/uplc/src/tx/phase_one.rs index ef2df2e6..8afa7269 100644 --- a/crates/uplc/src/tx/phase_one.rs +++ b/crates/uplc/src/tx/phase_one.rs @@ -40,7 +40,7 @@ pub fn validate_missing_scripts( needed: &AlonzoScriptsNeeded, txscripts: HashMap, ) -> Result<(), Error> { - let received_hashes = txscripts.keys().map(|x| *x).collect::>(); + let received_hashes = txscripts.keys().copied().collect::>(); let needed_hashes = needed.iter().map(|x| x.1).collect::>(); @@ -57,7 +57,7 @@ pub fn validate_missing_scripts( .map(|x| format!("[Extraneous (sh: {:?})]", x)) .collect(); - if missing.len() > 0 || extra.len() > 0 { + if !missing.is_empty() || !extra.is_empty() { let missing_errors = missing.join(" "); let extra_errors = extra.join(" "); @@ -80,8 +80,8 @@ pub fn scripts_needed( let mut spend = Vec::new(); - for input in txb.inputs { - let utxo = match utxos.iter().find(|utxo| utxo.input == input) { + for input in txb.inputs.iter() { + let utxo = match utxos.iter().find(|utxo| utxo.input == *input) { Some(u) => u, None => return Err(Error::ResolvedInputNotFound), }; @@ -164,7 +164,7 @@ pub fn has_exact_set_of_redeemers( for (script_purpose, script_hash) in needed { let redeemer_ptr = build_redeemer_ptr(tx, script_purpose)?; - let script = tx_scripts.get(&script_hash); + let script = tx_scripts.get(script_hash); if let (Some(ptr), Some(script)) = (redeemer_ptr, script) { match script { @@ -200,9 +200,7 @@ pub fn has_exact_set_of_redeemers( .map(|x| { format!( "[Missing (redeemer_ptr: {:?}, script_purpose: {:?}, script_hash: {})]", - x.0, - x.1, - x.2.to_string(), + x.0, x.1, x.2, ) }) .collect(); @@ -213,10 +211,7 @@ pub fn has_exact_set_of_redeemers( .map(|x| format!("[Extraneous (redeemer_ptr: {:?})]", x)) .collect(); - if missing.len() > 0 || extra.len() > 0 { - let missing_errors = missing.join(" "); - let extra_errors = extra.join(" "); - + if !missing.is_empty() || !extra.is_empty() { Err(Error::RequiredRedeemersMismatch { missing, extra }) } else { Ok(()) From d5f398bc5f99aabbae97f83c958b07c393586881 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Mon, 19 Sep 2022 10:50:25 +0200 Subject: [PATCH 52/77] Fixed output decoding for ResolvedInput --- crates/uplc/src/tx.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/uplc/src/tx.rs b/crates/uplc/src/tx.rs index cd583c77..5c5ece26 100644 --- a/crates/uplc/src/tx.rs +++ b/crates/uplc/src/tx.rs @@ -77,10 +77,10 @@ pub fn eval_phase_two_raw( let mut utxos = Vec::new(); - for (input, _output) in utxos_bytes { + for (input, output) in utxos_bytes { utxos.push(ResolvedInput { input: TransactionInput::decode_fragment(input)?, - output: TransactionOutput::decode_fragment(input)?, + output: TransactionOutput::decode_fragment(output)?, }); } From 83bac598defe75d1a014ac56c7e6105f6f20338b Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Mon, 19 Sep 2022 10:58:45 +0200 Subject: [PATCH 53/77] removed deprecated comment --- crates/uplc/src/tx/eval.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/uplc/src/tx/eval.rs b/crates/uplc/src/tx/eval.rs index 7b355e77..01e13262 100644 --- a/crates/uplc/src/tx/eval.rs +++ b/crates/uplc/src/tx/eval.rs @@ -532,7 +532,6 @@ pub fn get_script_and_datum_lookup_table( for script in scripts_native_witnesses.iter() { scripts.insert(script.compute_hash(), ScriptVersion::Native(script.clone())); - // TODO: implement `original_hash` for native scripts in pallas } for script in scripts_v1_witnesses.iter() { From 1148863a43571462a18402fe9553edefe875a79f Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Mon, 19 Sep 2022 12:02:41 +0200 Subject: [PATCH 54/77] Allow to set initial budget --- crates/uplc/src/ast.rs | 8 +++- crates/uplc/src/tx.rs | 20 +++++++++- crates/uplc/src/tx/eval.rs | 9 +++-- crates/uplc/src/tx/tests.rs | 80 +++++++++++++++++++++++++++++++++---- 4 files changed, 103 insertions(+), 14 deletions(-) diff --git a/crates/uplc/src/ast.rs b/crates/uplc/src/ast.rs index 38ad2a8d..f5a9e66b 100644 --- a/crates/uplc/src/ast.rs +++ b/crates/uplc/src/ast.rs @@ -519,15 +519,21 @@ impl Program { &self, version: &Language, costs: &[i64], + initial_budget: Option<&ExBudget>, ) -> ( Result, crate::machine::Error>, ExBudget, Vec, ) { + let budget = match initial_budget { + Some(b) => b.clone(), + None => ExBudget::default(), + }; + let mut machine = Machine::new( version.clone(), initialize_cost_model(version, costs), - ExBudget::default(), + budget, 200, //slippage ); diff --git a/crates/uplc/src/tx.rs b/crates/uplc/src/tx.rs index 5c5ece26..35c8de7e 100644 --- a/crates/uplc/src/tx.rs +++ b/crates/uplc/src/tx.rs @@ -9,6 +9,8 @@ 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; @@ -26,6 +28,7 @@ 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> { @@ -50,6 +53,7 @@ pub fn eval_phase_two( redeemer, &lookup_table, cost_mdls, + initial_budget, )?; collected_redeemers.push(redeemer) @@ -63,10 +67,12 @@ pub fn eval_phase_two( /// This function is the same as [`eval_phase_two`] /// but the inputs are raw bytes. +/// initial_budget expects (cpu, mem). 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), run_phase_one: bool, ) -> Result>, Error> { @@ -75,6 +81,11 @@ pub fn eval_phase_two_raw( 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 { @@ -91,7 +102,14 @@ pub fn eval_phase_two_raw( match multi_era_tx { MultiEraTx::Babbage(tx) => { - match eval_phase_two(&tx, &utxos, Some(&cost_mdls), &sc, run_phase_one) { + 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()) diff --git a/crates/uplc/src/tx/eval.rs b/crates/uplc/src/tx/eval.rs index 01e13262..dccd6ae5 100644 --- a/crates/uplc/src/tx/eval.rs +++ b/crates/uplc/src/tx/eval.rs @@ -575,6 +575,7 @@ pub fn eval_redeemer( redeemer: &Redeemer, lookup_table: &DataLookupTable, cost_mdls_opt: Option<&CostMdls>, + initial_budget: Option<&ExBudget>, ) -> Result { let purpose = get_script_purpose( redeemer, @@ -612,7 +613,7 @@ pub fn eval_redeemer( return Err(Error::V1CostModelNotFound); }; - program.eval_as(&Language::PlutusV1, costs) + program.eval_as(&Language::PlutusV1, costs, initial_budget) } else { program.eval_v1() }; @@ -658,7 +659,7 @@ pub fn eval_redeemer( return Err(Error::V2CostModelNotFound); }; - program.eval_as(&Language::PlutusV2, costs) + program.eval_as(&Language::PlutusV2, costs, initial_budget) } else { program.eval() }; @@ -706,7 +707,7 @@ pub fn eval_redeemer( return Err(Error::V1CostModelNotFound); }; - program.eval_as(&Language::PlutusV1, costs) + program.eval_as(&Language::PlutusV1, costs, initial_budget) } else { program.eval_v1() }; @@ -751,7 +752,7 @@ pub fn eval_redeemer( return Err(Error::V2CostModelNotFound); }; - program.eval_as(&Language::PlutusV2, costs) + program.eval_as(&Language::PlutusV2, costs, initial_budget) } else { program.eval() }; diff --git a/crates/uplc/src/tx/tests.rs b/crates/uplc/src/tx/tests.rs index c8d9cef5..1fa5deef 100644 --- a/crates/uplc/src/tx/tests.rs +++ b/crates/uplc/src/tx/tests.rs @@ -5,6 +5,8 @@ use pallas_primitives::{ }; use pallas_traverse::{Era, MultiEraTx}; +use crate::machine::cost_model::ExBudget; + use super::{eval_phase_two, ResolvedInput, SlotConfig}; #[test] @@ -226,13 +228,25 @@ fn test_eval() { 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), &slot_config, false).unwrap(); + let redeemers = eval_phase_two( + &tx, + &utxos, + Some(&cost_mdl), + Some(&initial_budget), + &slot_config, + false, + ) + .unwrap(); assert_eq!(redeemers.len(), 1) } @@ -461,13 +475,25 @@ fn test_eval_1() { 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), &slot_config, false).unwrap(); + let redeemers = eval_phase_two( + &tx, + &utxos, + Some(&cost_mdl), + Some(&initial_budget), + &slot_config, + false, + ) + .unwrap(); println!("{:?}", redeemers.len()); } @@ -531,13 +557,25 @@ fn test_eval_2() { 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), &slot_config, false).unwrap(); + let redeemers = eval_phase_two( + &tx, + &utxos, + Some(&cost_mdl), + Some(&initial_budget), + &slot_config, + false, + ) + .unwrap(); println!("{:?}", redeemers.len()); } @@ -598,13 +636,26 @@ fn eval_missing_redeemer() { 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), &slot_config, false).unwrap(); + eval_phase_two( + &tx, + &utxos, + Some(&cost_mdl), + Some(&initial_budget), + &slot_config, + false, + ) + .unwrap(); } _ => unreachable!(), }; @@ -663,13 +714,26 @@ fn eval_extraneous_redeemer() { 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), &slot_config, false).unwrap(); + eval_phase_two( + &tx, + &utxos, + Some(&cost_mdl), + Some(&initial_budget), + &slot_config, + false, + ) + .unwrap(); } _ => unreachable!(), }; From 4a6496db3fb9ac2cbb5e8f4deed8d6c208bd6740 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Mon, 19 Sep 2022 13:38:56 +0200 Subject: [PATCH 55/77] Replaced unreachable statements with Error --- crates/uplc/src/tx/error.rs | 18 ++++ crates/uplc/src/tx/eval.rs | 207 +++++++++++++++++++++--------------- 2 files changed, 141 insertions(+), 84 deletions(-) diff --git a/crates/uplc/src/tx/error.rs b/crates/uplc/src/tx/error.rs index fbdc243b..0c09fa1c 100644 --- a/crates/uplc/src/tx/error.rs +++ b/crates/uplc/src/tx/error.rs @@ -21,6 +21,8 @@ pub enum Error { missing: Vec, extra: Vec, }, + #[error("Extranous redeemer found: Tag {:?}, Index {}", tag, index)] + ExtranousRedeemer { tag: String, index: u32 }, #[error("Resolved Input not found")] ResolvedInputNotFound, #[error("A key hash cannot be the hash of a script")] @@ -31,4 +33,20 @@ pub enum Error { 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 index dccd6ae5..8439038f 100644 --- a/crates/uplc/src/tx/eval.rs +++ b/crates/uplc/src/tx/eval.rs @@ -67,12 +67,12 @@ pub fn get_tx_in_info_v1( inputs: &[TransactionInput], utxos: &[ResolvedInput], ) -> Result, Error> { - let result = inputs + inputs .iter() .map(|input| { let utxo = match utxos.iter().find(|utxo| utxo.input == *input) { Some(u) => u, - None => unreachable!("Resolved input not found."), + None => return Err(Error::ResolvedInputNotFound), }; let address = Address::from_bytes(match &utxo.output { TransactionOutput::Legacy(output) => output.address.as_ref(), @@ -81,45 +81,46 @@ pub fn get_tx_in_info_v1( .unwrap(); match address { - Address::Byron(_) => unreachable!("Byron addresses not supported in Plutus."), + Address::Byron(_) => { + return Err(Error::ByronAddressNotAllowed); + } Address::Stake(_) => { - unreachable!("This is impossible. A stake address cannot lock a UTxO.") + return Err(Error::NoPaymentCredential); } _ => {} - } + }; match &utxo.output { TransactionOutput::Legacy(_) => {} TransactionOutput::PostAlonzo(output) => { if let Some(DatumOption::Data(_)) = output.datum_option { - unreachable!("Inline datum not allowed in PlutusV1.") + return Err(Error::InlineDatumNotAllowed); } if output.script_ref.is_some() { - unreachable!("Reference scripts not allowed in PlutusV1.") + return Err(Error::ScriptAndInputRefNotAllowed); } } } - TxInInfo { + Ok(TxInInfo { out_ref: utxo.input.clone(), resolved: TxOut::V1(utxo.output.clone()), - } + }) }) - .collect::>(); - Ok(result) + .collect() } fn get_tx_in_info_v2( inputs: &[TransactionInput], utxos: &[ResolvedInput], ) -> Result, Error> { - let result = inputs + inputs .iter() .map(|input| { let utxo = match utxos.iter().find(|utxo| utxo.input == *input) { Some(u) => u, - None => unreachable!("Resolved input not found."), + None => return Err(Error::ResolvedInputNotFound), }; let address = Address::from_bytes(match &utxo.output { TransactionOutput::Legacy(output) => output.address.as_ref(), @@ -128,20 +129,21 @@ fn get_tx_in_info_v2( .unwrap(); match address { - Address::Byron(_) => unreachable!("Byron addresses not supported in Plutus."), + Address::Byron(_) => { + return Err(Error::ByronAddressNotAllowed); + } Address::Stake(_) => { - unreachable!("This is impossible. A stake address cannot lock a UTxO.") + return Err(Error::NoPaymentCredential); } _ => {} - } + }; - TxInInfo { + Ok(TxInInfo { out_ref: utxo.input.clone(), resolved: TxOut::V2(utxo.output.clone()), - } + }) }) - .collect::>(); - Ok(result) + .collect() } fn get_script_purpose( @@ -166,23 +168,26 @@ fn get_script_purpose( policy_ids.sort(); match policy_ids.get(index as usize) { Some(policy_id) => Ok(ScriptPurpose::Minting(*policy_id)), - None => unreachable!("Script purpose not found for redeemer."), + None => { + return Err(Error::ExtranousRedeemer { + tag: "Mint".to_string(), + index, + }) + } } } RedeemerTag::Spend => { // sort lexical by tx_hash and index let mut inputs = inputs.to_vec(); - // is this correct? Does this sort lexical from low to high? maybe get Ordering into pallas for TransactionInput? - 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, - }, - ); + inputs.sort(); match inputs.get(index as usize) { Some(input) => Ok(ScriptPurpose::Spending(input.clone())), - None => unreachable!("Script purpose not found for redeemer."), + None => { + return Err(Error::ExtranousRedeemer { + tag: "Spend".to_string(), + index, + }) + } } } RedeemerTag::Reward => { @@ -196,23 +201,22 @@ fn get_script_purpose( reward_accounts.sort(); let reward_account = match reward_accounts.get(index as usize) { Some(ra) => ra.clone(), - None => unreachable!("Script purpose not found for redeemer."), + None => { + return Err(Error::ExtranousRedeemer { + 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::Script(script_hash) => StakeCredential::Scripthash(*script_hash), StakePayload::Stake(_) => { - unreachable!( - "This is impossible. A key hash cannot be the hash of a script." - ); + return Err(Error::ScriptKeyHash); } }, - _ => unreachable!( - "This is impossible. Only shelley reward addresses can be a part of withdrawals." - ), + _ => return Err(Error::BadWithdrawalAddress), }; Ok(ScriptPurpose::Rewarding(credential)) } @@ -224,7 +228,12 @@ fn get_script_purpose( .get(index as usize) { Some(cert) => Ok(ScriptPurpose::Certifying(cert.clone())), - None => unreachable!("Script purpose not found for redeemer."), + None => { + return Err(Error::ExtranousRedeemer { + tag: "Cert".to_string(), + index, + }) + } } } } @@ -238,7 +247,7 @@ fn get_tx_info_v1( let body = tx.transaction_body.clone(); if body.reference_inputs.is_some() { - unreachable!("Reference inputs not allowed in PlutusV1.") + return Err(Error::ScriptAndInputRefNotAllowed); } let inputs = get_tx_in_info_v1(&body.inputs, utxos)?; @@ -375,7 +384,7 @@ fn get_execution_purpose( utxos: &[ResolvedInput], script_purpose: &ScriptPurpose, lookup_table: &DataLookupTable, -) -> ExecutionPurpose { +) -> Result { match script_purpose { ScriptPurpose::Minting(policy_id) => { let policy_id_array: [u8; 28] = policy_id.to_vec().try_into().unwrap(); @@ -383,62 +392,85 @@ fn get_execution_purpose( let script = match lookup_table.scripts.get(&hash) { Some(s) => s.clone(), - None => unreachable!("Missing required scripts.") + None => { + return Err(Error::MissingRequiredScript { + hash: hash.to_string(), + }) + } }; - ExecutionPurpose::NoDatum(script) + Ok(ExecutionPurpose::NoDatum(script)) } ScriptPurpose::Spending(out_ref) => { - let utxo = utxos.iter().find(|utxo| utxo.input == *out_ref).unwrap(); + 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 script = match lookup_table - .scripts - .get(shelley_address.payment().as_hash()) { + let hash = shelley_address.payment().as_hash(); + let script = match lookup_table.scripts.get(hash) { Some(s) => s.clone(), - None => unreachable!("Missing required scripts.") + None => { + return Err(Error::MissingRequiredScript { + hash: hash.to_string(), + }) + } }; - let datum = match lookup_table.datum.get(&output.datum_hash.unwrap_or_else(|| unreachable!("Missing datum hash in input."))) { + 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 => unreachable!("Missing datum in witness set.") + None => { + return Err(Error::MissingRequiredDatum { + hash: datum_hash.to_string(), + }) + } }; - ExecutionPurpose::WithDatum(script, datum) + Ok(ExecutionPurpose::WithDatum(script, datum)) } - _ => unreachable!( - "This is impossible. Only shelley addresses can contain a script hash." - ), + _ => return Err(Error::ScriptKeyHash), } } TransactionOutput::PostAlonzo(output) => { let address = Address::from_bytes(&output.address).unwrap(); match address { Address::Shelley(shelley_address) => { - - let script = match lookup_table - .scripts - .get(shelley_address.payment().as_hash()) { + let hash = shelley_address.payment().as_hash(); + let script = match lookup_table.scripts.get(hash) { Some(s) => s.clone(), - None => unreachable!("Missing required scripts.") + None => { + return Err(Error::MissingRequiredScript { + hash: hash.to_string(), + }) + } }; - let datum = match &output.datum_option { Some(DatumOption::Hash(hash)) => { - lookup_table.datum.get(hash).unwrap().clone() + 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(), - _ => unreachable!( "Missing datum hash or inline datum in input."), + _ => return Err(Error::MissingRequiredInlineDatumOrHash), }; - ExecutionPurpose::WithDatum(script, datum) + Ok(ExecutionPurpose::WithDatum(script, datum)) } - _ => unreachable!( - "This is impossible. Only shelley addresses can contain a script hash." - ), + _ => return Err(Error::ScriptKeyHash), } } } @@ -446,49 +478,56 @@ fn get_execution_purpose( ScriptPurpose::Rewarding(stake_credential) => { let script_hash = match stake_credential { StakeCredential::Scripthash(hash) => *hash, - _ => unreachable!("This is impossible. A key hash cannot be the hash of a script."), + _ => return Err(Error::ScriptKeyHash), }; let script = match lookup_table.scripts.get(&script_hash) { Some(s) => s.clone(), - None => unreachable!("Missing required scripts.") + None => { + return Err(Error::MissingRequiredScript { + hash: script_hash.to_string(), + }) + } }; - ExecutionPurpose::NoDatum(script) + Ok(ExecutionPurpose::NoDatum(script)) } ScriptPurpose::Certifying(cert) => match cert { - // StakeRegistration doesn't require a witness from a stake key/script. So I assume it doesn't need to be handled in Plutus either? Certificate::StakeDeregistration(stake_credential) => { let script_hash = match stake_credential { StakeCredential::Scripthash(hash) => *hash, - _ => unreachable!( - "This is impossible. A key hash cannot be the hash of a script." - ), + _ => return Err(Error::ScriptKeyHash), }; let script = match lookup_table.scripts.get(&script_hash) { Some(s) => s.clone(), - None => unreachable!("Missing required scripts.") + None => { + return Err(Error::MissingRequiredScript { + hash: script_hash.to_string(), + }) + } }; - ExecutionPurpose::NoDatum(script) + Ok(ExecutionPurpose::NoDatum(script)) } Certificate::StakeDelegation(stake_credential, _) => { let script_hash = match stake_credential { StakeCredential::Scripthash(hash) => *hash, - _ => unreachable!( - "This is impossible. A key hash cannot be the hash of a script." - ), + _ => return Err(Error::ScriptKeyHash), }; let script = match lookup_table.scripts.get(&script_hash) { Some(s) => s.clone(), - None => unreachable!("Missing required scripts.") + None => { + return Err(Error::MissingRequiredScript { + hash: script_hash.to_string(), + }) + } }; - ExecutionPurpose::NoDatum(script) + Ok(ExecutionPurpose::NoDatum(script)) } - _ => unreachable!("This is impossible. Only stake deregistration and stake delegation are valid script purposes."), + _ => return Err(Error::OnlyStakeDeregAndDelegAllowed), }, } } @@ -585,7 +624,7 @@ pub fn eval_redeemer( &tx.transaction_body.withdrawals, )?; - let execution_purpose: ExecutionPurpose = get_execution_purpose(utxos, &purpose, lookup_table); + let execution_purpose: ExecutionPurpose = get_execution_purpose(utxos, &purpose, lookup_table)?; match execution_purpose { ExecutionPurpose::WithDatum(script_version, datum) => match script_version { From ea735428dd5f0d7f9fd741892fbe5658e16385e0 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Mon, 19 Sep 2022 13:48:05 +0200 Subject: [PATCH 56/77] fixed typos --- crates/uplc/src/tx/error.rs | 4 ++-- crates/uplc/src/tx/eval.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/uplc/src/tx/error.rs b/crates/uplc/src/tx/error.rs index 0c09fa1c..fe563144 100644 --- a/crates/uplc/src/tx/error.rs +++ b/crates/uplc/src/tx/error.rs @@ -21,8 +21,8 @@ pub enum Error { missing: Vec, extra: Vec, }, - #[error("Extranous redeemer found: Tag {:?}, Index {}", tag, index)] - ExtranousRedeemer { tag: String, index: u32 }, + #[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")] diff --git a/crates/uplc/src/tx/eval.rs b/crates/uplc/src/tx/eval.rs index 8439038f..63ebffd8 100644 --- a/crates/uplc/src/tx/eval.rs +++ b/crates/uplc/src/tx/eval.rs @@ -71,7 +71,7 @@ pub fn get_tx_in_info_v1( .iter() .map(|input| { let utxo = match utxos.iter().find(|utxo| utxo.input == *input) { - Some(u) => u, + Some(resolved) => resolved, None => return Err(Error::ResolvedInputNotFound), }; let address = Address::from_bytes(match &utxo.output { @@ -119,7 +119,7 @@ fn get_tx_in_info_v2( .iter() .map(|input| { let utxo = match utxos.iter().find(|utxo| utxo.input == *input) { - Some(u) => u, + Some(resolved) => resolved, None => return Err(Error::ResolvedInputNotFound), }; let address = Address::from_bytes(match &utxo.output { @@ -169,7 +169,7 @@ fn get_script_purpose( match policy_ids.get(index as usize) { Some(policy_id) => Ok(ScriptPurpose::Minting(*policy_id)), None => { - return Err(Error::ExtranousRedeemer { + return Err(Error::ExtraneousRedeemer { tag: "Mint".to_string(), index, }) @@ -183,7 +183,7 @@ fn get_script_purpose( match inputs.get(index as usize) { Some(input) => Ok(ScriptPurpose::Spending(input.clone())), None => { - return Err(Error::ExtranousRedeemer { + return Err(Error::ExtraneousRedeemer { tag: "Spend".to_string(), index, }) @@ -202,7 +202,7 @@ fn get_script_purpose( let reward_account = match reward_accounts.get(index as usize) { Some(ra) => ra.clone(), None => { - return Err(Error::ExtranousRedeemer { + return Err(Error::ExtraneousRedeemer { tag: "Reward".to_string(), index, }) @@ -229,7 +229,7 @@ fn get_script_purpose( { Some(cert) => Ok(ScriptPurpose::Certifying(cert.clone())), None => { - return Err(Error::ExtranousRedeemer { + return Err(Error::ExtraneousRedeemer { tag: "Cert".to_string(), index, }) From 30ed8d915024ba42aab0d784e6dce5b17dc7d397 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Mon, 19 Sep 2022 14:53:44 +0200 Subject: [PATCH 57/77] fixed initial_budget --- crates/uplc/src/tx/eval.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/crates/uplc/src/tx/eval.rs b/crates/uplc/src/tx/eval.rs index 63ebffd8..54c76eb2 100644 --- a/crates/uplc/src/tx/eval.rs +++ b/crates/uplc/src/tx/eval.rs @@ -660,7 +660,10 @@ pub fn eval_redeemer( // TODO: do we want the logs in the error? result?; - let initial_budget = ExBudget::default(); + let initial_budget = match initial_budget { + Some(b) => b.clone(), + None => ExBudget::default(), + }; let new_redeemer = Redeemer { tag: redeemer.tag.clone(), @@ -706,7 +709,10 @@ pub fn eval_redeemer( // TODO: do we want the logs in the error? result?; - let initial_budget = ExBudget::default(); + let initial_budget = match initial_budget { + Some(b) => b.clone(), + None => ExBudget::default(), + }; let new_redeemer = Redeemer { tag: redeemer.tag.clone(), @@ -754,7 +760,10 @@ pub fn eval_redeemer( // TODO: do we want the logs in the error? result?; - let initial_budget = ExBudget::default(); + let initial_budget = match initial_budget { + Some(b) => b.clone(), + None => ExBudget::default(), + }; let new_redeemer = Redeemer { tag: redeemer.tag.clone(), @@ -799,7 +808,10 @@ pub fn eval_redeemer( // TODO: do we want the logs in the error? result?; - let initial_budget = ExBudget::default(); + let initial_budget = match initial_budget { + Some(b) => b.clone(), + None => ExBudget::default(), + }; let new_redeemer = Redeemer { tag: redeemer.tag.clone(), From f10e3836ada5ed0fb0eaf3bf471fb3838166d43e Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 19 Sep 2022 11:04:21 -0400 Subject: [PATCH 58/77] feat: new error for bad term --- crates/cli/src/main.rs | 22 ++++++++- crates/uplc/src/ast.rs | 8 +++- crates/uplc/src/tx/error.rs | 11 +++-- crates/uplc/src/tx/eval.rs | 92 ++++++++++++++++++++++--------------- 4 files changed, 91 insertions(+), 42 deletions(-) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 859f2adf..39e4cc51 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -54,7 +54,27 @@ fn main() -> anyhow::Result<()> { slot_length, }; - tx::eval_phase_two(tx_babbage, &resolved_inputs, None, &slot_config, true)?; + let result = tx::eval_phase_two( + tx_babbage, + &resolved_inputs, + None, + None, + &slot_config, + true, + ); + + match result { + Ok(redeemers) => { + println!("\nResult\n------\n\n"); + + for redeemer in redeemers { + println!("{:#?}", redeemer) + } + } + Err(err) => { + eprintln!("\nError\n-----\n\n{}\n", err); + } + } } } }, diff --git a/crates/uplc/src/ast.rs b/crates/uplc/src/ast.rs index f5a9e66b..dbfcc2c8 100644 --- a/crates/uplc/src/ast.rs +++ b/crates/uplc/src/ast.rs @@ -526,7 +526,7 @@ impl Program { Vec, ) { let budget = match initial_budget { - Some(b) => b.clone(), + Some(b) => *b, None => ExBudget::default(), }; @@ -556,3 +556,9 @@ impl Program { program.eval() } } + +impl Term { + pub fn is_valid_script_result(&self) -> bool { + matches!(self, Term::Constant(Constant::Unit)) + } +} diff --git a/crates/uplc/src/tx/error.rs b/crates/uplc/src/tx/error.rs index fe563144..1d4befa2 100644 --- a/crates/uplc/src/tx/error.rs +++ b/crates/uplc/src/tx/error.rs @@ -1,17 +1,22 @@ -use crate::machine; +use crate::{ + ast::{NamedDeBruijn, Term}, + machine::{self, cost_model::ExBudget}, +}; #[derive(thiserror::Error, Debug)] pub enum Error { #[error("{0}")] Address(#[from] pallas_addresses::Error), + #[error("{}\n\n{:#?}\n\n{}", .0, .1, .2.join("\n"))] + BadTerm(Term, ExBudget, Vec), #[error("Only shelley reward addresses can be a part of withdrawals")] BadWithdrawalAddress, #[error("{0}")] FlatDecode(#[from] flat_rs::de::Error), #[error("{0}")] FragmentDecode(#[from] pallas_primitives::Error), - #[error("{0}")] - Machine(#[from] machine::Error), + #[error("{}\n\n{:#?}\n\n{}", .0, .1, .2.join("\n"))] + Machine(machine::Error, ExBudget, Vec), #[error("Native script can't be executed in phase-two")] NativeScriptPhaseTwo, #[error("Can't eval without redeemers")] diff --git a/crates/uplc/src/tx/eval.rs b/crates/uplc/src/tx/eval.rs index 54c76eb2..4dad060f 100644 --- a/crates/uplc/src/tx/eval.rs +++ b/crates/uplc/src/tx/eval.rs @@ -168,12 +168,10 @@ fn get_script_purpose( policy_ids.sort(); match policy_ids.get(index as usize) { Some(policy_id) => Ok(ScriptPurpose::Minting(*policy_id)), - None => { - return Err(Error::ExtraneousRedeemer { - tag: "Mint".to_string(), - index, - }) - } + None => Err(Error::ExtraneousRedeemer { + tag: "Mint".to_string(), + index, + }), } } RedeemerTag::Spend => { @@ -182,12 +180,10 @@ fn get_script_purpose( inputs.sort(); match inputs.get(index as usize) { Some(input) => Ok(ScriptPurpose::Spending(input.clone())), - None => { - return Err(Error::ExtraneousRedeemer { - tag: "Spend".to_string(), - index, - }) - } + None => Err(Error::ExtraneousRedeemer { + tag: "Spend".to_string(), + index, + }), } } RedeemerTag::Reward => { @@ -228,12 +224,10 @@ fn get_script_purpose( .get(index as usize) { Some(cert) => Ok(ScriptPurpose::Certifying(cert.clone())), - None => { - return Err(Error::ExtraneousRedeemer { - tag: "Cert".to_string(), - index, - }) - } + None => Err(Error::ExtraneousRedeemer { + tag: "Cert".to_string(), + index, + }), } } } @@ -436,7 +430,7 @@ fn get_execution_purpose( Ok(ExecutionPurpose::WithDatum(script, datum)) } - _ => return Err(Error::ScriptKeyHash), + _ => Err(Error::ScriptKeyHash), } } TransactionOutput::PostAlonzo(output) => { @@ -470,7 +464,7 @@ fn get_execution_purpose( Ok(ExecutionPurpose::WithDatum(script, datum)) } - _ => return Err(Error::ScriptKeyHash), + _ => Err(Error::ScriptKeyHash), } } } @@ -527,7 +521,7 @@ fn get_execution_purpose( Ok(ExecutionPurpose::NoDatum(script)) } - _ => return Err(Error::OnlyStakeDeregAndDelegAllowed), + _ => Err(Error::OnlyStakeDeregAndDelegAllowed), }, } } @@ -645,7 +639,7 @@ pub fn eval_redeemer( .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()); - let (result, budget, _) = if let Some(cost_mdls) = cost_mdls_opt { + let (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt { let costs = if let Some(costs) = &cost_mdls.plutus_v1 { costs } else { @@ -657,11 +651,17 @@ pub fn eval_redeemer( program.eval_v1() }; - // TODO: do we want the logs in the error? - result?; + match result { + Ok(term) => { + if !term.is_valid_script_result() { + return Err(Error::BadTerm(term, budget, logs)); + } + } + Err(err) => return Err(Error::Machine(err, budget, logs)), + } let initial_budget = match initial_budget { - Some(b) => b.clone(), + Some(b) => *b, None => ExBudget::default(), }; @@ -694,7 +694,7 @@ pub fn eval_redeemer( .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()); - let (result, budget, _) = if let Some(cost_mdls) = cost_mdls_opt { + let (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt { let costs = if let Some(costs) = &cost_mdls.plutus_v2 { costs } else { @@ -706,11 +706,17 @@ pub fn eval_redeemer( program.eval() }; - // TODO: do we want the logs in the error? - result?; + match result { + Ok(term) => { + if !term.is_valid_script_result() { + return Err(Error::BadTerm(term, budget, logs)); + } + } + Err(err) => return Err(Error::Machine(err, budget, logs)), + } let initial_budget = match initial_budget { - Some(b) => b.clone(), + Some(b) => *b, None => ExBudget::default(), }; @@ -745,7 +751,7 @@ pub fn eval_redeemer( .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()); - let (result, budget, _) = if let Some(cost_mdls) = cost_mdls_opt { + let (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt { let costs = if let Some(costs) = &cost_mdls.plutus_v1 { costs } else { @@ -757,11 +763,17 @@ pub fn eval_redeemer( program.eval_v1() }; - // TODO: do we want the logs in the error? - result?; + match result { + Ok(term) => { + if !term.is_valid_script_result() { + return Err(Error::BadTerm(term, budget, logs)); + } + } + Err(err) => return Err(Error::Machine(err, budget, logs)), + } let initial_budget = match initial_budget { - Some(b) => b.clone(), + Some(b) => *b, None => ExBudget::default(), }; @@ -793,7 +805,7 @@ pub fn eval_redeemer( .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()); - let (result, budget, _) = if let Some(cost_mdls) = cost_mdls_opt { + let (result, budget, logs) = if let Some(cost_mdls) = cost_mdls_opt { let costs = if let Some(costs) = &cost_mdls.plutus_v2 { costs } else { @@ -805,11 +817,17 @@ pub fn eval_redeemer( program.eval() }; - // TODO: do we want the logs in the error? - result?; + match result { + Ok(term) => { + if !term.is_valid_script_result() { + return Err(Error::BadTerm(term, budget, logs)); + } + } + Err(err) => return Err(Error::Machine(err, budget, logs)), + } let initial_budget = match initial_budget { - Some(b) => b.clone(), + Some(b) => *b, None => ExBudget::default(), }; From 0ba8787b9742ded7c8ed13f19ea0978e8c137eb0 Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 19 Sep 2022 11:11:52 -0400 Subject: [PATCH 59/77] chore: clippy --- crates/uplc/src/tx/phase_one.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/uplc/src/tx/phase_one.rs b/crates/uplc/src/tx/phase_one.rs index 8afa7269..0361f4fd 100644 --- a/crates/uplc/src/tx/phase_one.rs +++ b/crates/uplc/src/tx/phase_one.rs @@ -103,7 +103,7 @@ pub fn scripts_needed( .as_ref() .unwrap_or(&KeyValuePairs::Indef(vec![])) .iter() - .map(|(acnt, _)| { + .filter_map(|(acnt, _)| { let address = Address::from_bytes(acnt).unwrap(); if let Address::Stake(a) = address { @@ -115,7 +115,6 @@ pub fn scripts_needed( None }) - .flatten() .collect::(); let mut cert = txb @@ -123,7 +122,7 @@ pub fn scripts_needed( .clone() .unwrap_or_default() .iter() - .map(|cert| { + .filter_map(|cert| { // only Dereg and Deleg certs can require scripts match cert { Certificate::StakeDeregistration(StakeCredential::Scripthash(h)) => { @@ -135,7 +134,6 @@ pub fn scripts_needed( _ => None, } }) - .flatten() .collect::(); let mut mint = txb From f12540d8e23e01634419679bd4df88334ae2ff0a Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Mon, 19 Sep 2022 17:17:15 +0200 Subject: [PATCH 60/77] fixed missing script test --- crates/uplc/src/tx/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/uplc/src/tx/tests.rs b/crates/uplc/src/tx/tests.rs index 1fa5deef..fbe0be8d 100644 --- a/crates/uplc/src/tx/tests.rs +++ b/crates/uplc/src/tx/tests.rs @@ -725,7 +725,7 @@ fn eval_extraneous_redeemer() { match multi_era_tx { MultiEraTx::Babbage(tx) => { - eval_phase_two( + assert!(eval_phase_two( &tx, &utxos, Some(&cost_mdl), @@ -733,7 +733,7 @@ fn eval_extraneous_redeemer() { &slot_config, false, ) - .unwrap(); + .is_err()); } _ => unreachable!(), }; From 21d71e94b336a3d59bc2b0af8ca7fda0c2df5666 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Mon, 19 Sep 2022 18:19:56 +0200 Subject: [PATCH 61/77] added more tests --- crates/uplc/src/tx/tests.rs | 406 ++++++++++++++++++++++++++++++++++++ 1 file changed, 406 insertions(+) diff --git a/crates/uplc/src/tx/tests.rs b/crates/uplc/src/tx/tests.rs index fbe0be8d..7007d295 100644 --- a/crates/uplc/src/tx/tests.rs +++ b/crates/uplc/src/tx/tests.rs @@ -583,6 +583,412 @@ fn test_eval_2() { }; } +#[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 + 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(); + + println!("{:?}", redeemers.len()); + } + _ => unreachable!(), + }; +} + +#[test] +fn test_eval_4() { + /* + + Plutus V1 + + Helios script: + + func main() Bool { + true + } + + */ + + let tx_bytes = hex::decode("84a80081825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c50010182825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a00111958a1581cdae5a9bdfbdd28831245f80ba10e4717f2975e5d684a3551f3e4fb99a1400a825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a029f7b29021a0002bc5009a1581cdae5a9bdfbdd28831245f80ba10e4717f2975e5d684a3551f3e4fb99a1400a0b58205013dbe72526511f63b0c4a235fbbc5d09d11d42f310113aaab1a28e01e0bde60d81825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c500110825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02af3659111a00041a78a30081825820065dd553fbe4e240a8f819bb9e333a7483de4a22b65c7fb6a95ce9450f84dff758401679b607eabef3dbbc9ac0abb03afb3a979ea32243bb5b99e299290d709bb4a6c2aa528c447c2db610a103cc9c0e7c018caa4fc8322d8ec217620e6d4bc2ef0b03815453010000322233335734600693124c4c931251010581840100d87980821909611a00094d78f5f6").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 + 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(); + + println!("{:?}", redeemers.len()); + } + _ => unreachable!(), + }; +} + +#[test] +fn test_eval_5() { + /* + + 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 + 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(); + + println!("{:?}", redeemers.len()); + } + _ => unreachable!(), + }; +} + #[test] fn eval_missing_redeemer() { let tx_bytes = hex::decode("84a30082825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c5000825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c50010181825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02cf47c8021a00028d89a1068149480100002221200101f5f6").unwrap(); From 6ccd0aa2fb20b838444cbf974f72633665df8774 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Mon, 19 Sep 2022 14:59:49 -0400 Subject: [PATCH 62/77] adding better debug for wrong term tag in uplc --- crates/uplc/src/flat.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/uplc/src/flat.rs b/crates/uplc/src/flat.rs index dd2f800b..dd766547 100644 --- a/crates/uplc/src/flat.rs +++ b/crates/uplc/src/flat.rs @@ -190,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() ))), } } From caa88dab126a932a4eb8023ab472148743f8268b Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 19 Sep 2022 16:00:25 -0400 Subject: [PATCH 63/77] fix: valid condition for a script --- crates/uplc/src/ast.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/uplc/src/ast.rs b/crates/uplc/src/ast.rs index dbfcc2c8..f1c5b81d 100644 --- a/crates/uplc/src/ast.rs +++ b/crates/uplc/src/ast.rs @@ -559,6 +559,6 @@ impl Program { impl Term { pub fn is_valid_script_result(&self) -> bool { - matches!(self, Term::Constant(Constant::Unit)) + !matches!(self, Term::Error) } } From 3e10fcbfbb8a11206439dfac4ff91841991cb22d Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Mon, 19 Sep 2022 22:15:31 +0200 Subject: [PATCH 64/77] changed example 4 to check 'False' --- crates/uplc/src/tx/tests.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/uplc/src/tx/tests.rs b/crates/uplc/src/tx/tests.rs index 7007d295..1f152388 100644 --- a/crates/uplc/src/tx/tests.rs +++ b/crates/uplc/src/tx/tests.rs @@ -836,12 +836,12 @@ fn test_eval_4() { Helios script: func main() Bool { - true + false } */ - let tx_bytes = hex::decode("84a80081825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c50010182825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a00111958a1581cdae5a9bdfbdd28831245f80ba10e4717f2975e5d684a3551f3e4fb99a1400a825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a029f7b29021a0002bc5009a1581cdae5a9bdfbdd28831245f80ba10e4717f2975e5d684a3551f3e4fb99a1400a0b58205013dbe72526511f63b0c4a235fbbc5d09d11d42f310113aaab1a28e01e0bde60d81825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c500110825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02af3659111a00041a78a30081825820065dd553fbe4e240a8f819bb9e333a7483de4a22b65c7fb6a95ce9450f84dff758401679b607eabef3dbbc9ac0abb03afb3a979ea32243bb5b99e299290d709bb4a6c2aa528c447c2db610a103cc9c0e7c018caa4fc8322d8ec217620e6d4bc2ef0b03815453010000322233335734600693124c4c931251010581840100d87980821909611a00094d78f5f6").unwrap(); + let tx_bytes = hex::decode("84A80081825820275B5DA338C8B899035081EB34BFA950B634911A5DD3271B3AD6CF4C2BBA0C50010182825839000AF00CC47500BB64CFFFB783E8C42F746B4E8B8A70EDE9C08C7113ACF3BDE34D1041F5A2076EF9AA6CF4539AB1A96ED462A0300ACBDB65D5821A00111958A1581C1E8BCA1FA1D937F408AFE2FD4DBF343AB7A09CF07984071ED95B3C92A1400A825839000AF00CC47500BB64CFFFB783E8C42F746B4E8B8A70EDE9C08C7113ACF3BDE34D1041F5A2076EF9AA6CF4539AB1A96ED462A0300ACBDB65D51A029F7B29021A0002BC5009A1581C1E8BCA1FA1D937F408AFE2FD4DBF343AB7A09CF07984071ED95B3C92A1400A0B58205013DBE72526511F63B0C4A235FBBC5D09D11D42F310113AAAB1A28E01E0BDE60D81825820275B5DA338C8B899035081EB34BFA950B634911A5DD3271B3AD6CF4C2BBA0C500110825839000AF00CC47500BB64CFFFB783E8C42F746B4E8B8A70EDE9C08C7113ACF3BDE34D1041F5A2076EF9AA6CF4539AB1A96ED462A0300ACBDB65D51A02AF3659111A00041A78A30081825820065DD553FBE4E240A8F819BB9E333A7483DE4A22B65C7FB6A95CE9450F84DFF758401679B607EABEF3DBBC9AC0ABB03AFB3A979EA32243BB5B99E299290D709BB4A6C2AA528C447C2DB610A103CC9C0E7C018CAA4FC8322D8EC217620E6D4BC2EF0B03815453010000322233335734600693124C4C931250010581840100D87980821909611A00094D78F5F6").unwrap(); let raw_inputs = hex::decode("8682582075a419179618ca358554fc47aeb33b6c93d12ba8f752495a4e5ef6ea0a1a099a03825820b810e77e706ccaebf7284f6c3d41e2a1eb4af5fabae452618f4175ad1b2aaded03825820975c17a4fed0051be622328efa548e206657d2b65a19224bf6ff8132571e6a50038258207453531a00f98db47c8c2b05e5c38f2c40a0be4f91d42d835bc3bc998b612a8e00825820452b2fc0d170323f86ad1e5b761dcae912774c42c1b1af4de2905a094f2f541403825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c5001").unwrap(); let raw_outputs = hex::decode("86825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a00412c6aad581c01dd79d464e2446231d662c9422445c4cf709b691baceb8b040a34d4a14d28323232294d6174726978363701581c11638d4d600d32b2849c93314e7e6bc656fded924f30514749e1eb3ea64c28323232294d617472697830014c28323232294d617472697831014d28323232294d61747269783133014d28323232294d61747269783330014d28323232294d61747269783534014d28323232294d6174726978383601581c1e60ac8228a21f6e3685c73970584aa54504a0476c4b66f3ef5c4dd2a14c28323232294d61747269783001581c25da1064122988292665c14259ea26cb4dd96d7f04535125fea248ffa14c28323232294d61747269783001581c46214283f4b5cc5d66836a4fe743e121190f1e5b91448a1b52f1b7bfa14d28323232294d6174726978313801581c4788a484721270845917e0986ab55b51922a46b514eb7a1f871e917ca14d28323232294d6174726978323101581c6ed9951ddcd79c98bc50142ba033890815330d4de1cb4c96870a234ca24c28323232294d617472697830014c28323232294d61747269783101581c6f7fd77c85b9856bdb1cfac1afa90c65d92c3c5e2fcca4a993e7fb52a14d28323232294d6174726978323401581ca07afd05db7f0ccb144052935be97b48593e5c8435f9eb859191de81a34c28323232294d617472697830014c28323232294d617472697831014d28323232294d6174726978323101581ca5ca38805c14270ec4c3c1c2446b28a95324054fac98066c5e82a016a14d28323232294d6174726978313901581ca65e6e94d1a260dbc6c4d9319b45585fa54b83742a33a2c599df56b9a2494265727279436f616c014c426572727954616e67656c6f01581cb3e2625ebd6bd613ce904db9fedb0565eec0671054d30d08bc5edadda44c28323232294d617472697835014d28323232294d61747269783237014d28323232294d61747269783430014d28323232294d6174726978343701581ce3ef435a5910f74d890b2a7cb0d1f7288efc22c75823d57acdab9f52a14d28323232294d6174726978363101825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a0011f436a1581c11638d4d600d32b2849c93314e7e6bc656fded924f30514749e1eb3ea14d28323232294d6174726978363801825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a0011f436a1581c11638d4d600d32b2849c93314e7e6bc656fded924f30514749e1eb3ea14d28323232294d6174726978343101825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a0012378ea1581c2c04f7a15aec58b2bec5dab3d201f3e3898370b98d2f01d4ac8bc270a14d28323232294d6174726978323801825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a0011f436a1581c11638d4d600d32b2849c93314e7e6bc656fded924f30514749e1eb3ea14d28323232294d6174726978323101825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02b350d1").unwrap(); @@ -893,7 +893,7 @@ fn test_eval_4() { .unwrap(); match multi_era_tx { MultiEraTx::Babbage(tx) => { - let redeemers = eval_phase_two( + assert!(eval_phase_two( &tx, &utxos, Some(&cost_mdl), @@ -901,9 +901,7 @@ fn test_eval_4() { &slot_config, false, ) - .unwrap(); - - println!("{:?}", redeemers.len()); + .is_err()); } _ => unreachable!(), }; From 169fa055725ec5bd92d18742cf0038b18444ffc1 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Mon, 19 Sep 2022 22:32:56 +0200 Subject: [PATCH 65/77] added ex units from haskell plc --- crates/uplc/src/tx/eval.rs | 6 ++++++ crates/uplc/src/tx/tests.rs | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/crates/uplc/src/tx/eval.rs b/crates/uplc/src/tx/eval.rs index 4dad060f..ceaa25af 100644 --- a/crates/uplc/src/tx/eval.rs +++ b/crates/uplc/src/tx/eval.rs @@ -831,6 +831,12 @@ pub fn eval_redeemer( None => ExBudget::default(), }; + println!( + "({},{})", + initial_budget.mem - budget.mem, + initial_budget.cpu - budget.cpu + ); + let new_redeemer = Redeemer { tag: redeemer.tag.clone(), index: redeemer.index, diff --git a/crates/uplc/src/tx/tests.rs b/crates/uplc/src/tx/tests.rs index 1f152388..a7a39266 100644 --- a/crates/uplc/src/tx/tests.rs +++ b/crates/uplc/src/tx/tests.rs @@ -258,6 +258,9 @@ fn test_eval() { fn test_eval_1() { /* + Haskell PLC: (738928, 215316271) + Aiken PLC: (721484, 210171376) + PlutusV2 {-# INLINEABLE mintTestValidator #-} @@ -505,6 +508,9 @@ fn test_eval_1() { fn test_eval_2() { /* + Haskell PLC: (655782, 188449458) + Aiken PLC: (638338, 183304563) + Plutus V1 {-# INLINEABLE mintTestValidator #-} @@ -911,6 +917,9 @@ fn test_eval_4() { fn test_eval_5() { /* + Haskell PLC: (114126, 40098159) + Aiken PLC: (114126,40211433) + Plutus V1 Helios script: From 2572ed6bbd5c2e5772878fa59923ea81778b7ef1 Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 19 Sep 2022 16:40:08 -0400 Subject: [PATCH 66/77] fix: remove BadTerm --- crates/uplc/src/tx/error.rs | 7 +------ crates/uplc/src/tx/eval.rs | 24 ++++-------------------- 2 files changed, 5 insertions(+), 26 deletions(-) diff --git a/crates/uplc/src/tx/error.rs b/crates/uplc/src/tx/error.rs index 1d4befa2..b20b68f3 100644 --- a/crates/uplc/src/tx/error.rs +++ b/crates/uplc/src/tx/error.rs @@ -1,14 +1,9 @@ -use crate::{ - ast::{NamedDeBruijn, Term}, - machine::{self, cost_model::ExBudget}, -}; +use crate::machine::{self, cost_model::ExBudget}; #[derive(thiserror::Error, Debug)] pub enum Error { #[error("{0}")] Address(#[from] pallas_addresses::Error), - #[error("{}\n\n{:#?}\n\n{}", .0, .1, .2.join("\n"))] - BadTerm(Term, ExBudget, Vec), #[error("Only shelley reward addresses can be a part of withdrawals")] BadWithdrawalAddress, #[error("{0}")] diff --git a/crates/uplc/src/tx/eval.rs b/crates/uplc/src/tx/eval.rs index ceaa25af..c1f8dc0c 100644 --- a/crates/uplc/src/tx/eval.rs +++ b/crates/uplc/src/tx/eval.rs @@ -652,11 +652,7 @@ pub fn eval_redeemer( }; match result { - Ok(term) => { - if !term.is_valid_script_result() { - return Err(Error::BadTerm(term, budget, logs)); - } - } + Ok(_) => (), Err(err) => return Err(Error::Machine(err, budget, logs)), } @@ -707,11 +703,7 @@ pub fn eval_redeemer( }; match result { - Ok(term) => { - if !term.is_valid_script_result() { - return Err(Error::BadTerm(term, budget, logs)); - } - } + Ok(_) => (), Err(err) => return Err(Error::Machine(err, budget, logs)), } @@ -764,11 +756,7 @@ pub fn eval_redeemer( }; match result { - Ok(term) => { - if !term.is_valid_script_result() { - return Err(Error::BadTerm(term, budget, logs)); - } - } + Ok(_) => (), Err(err) => return Err(Error::Machine(err, budget, logs)), } @@ -818,11 +806,7 @@ pub fn eval_redeemer( }; match result { - Ok(term) => { - if !term.is_valid_script_result() { - return Err(Error::BadTerm(term, budget, logs)); - } - } + Ok(_) => (), Err(err) => return Err(Error::Machine(err, budget, logs)), } From 9b6d4e20c76ace7537cae5c16b6358db53235889 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Thu, 22 Sep 2022 03:13:30 -0400 Subject: [PATCH 67/77] check exunits for eval tests --- crates/uplc/src/tx/tests.rs | 76 +++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/crates/uplc/src/tx/tests.rs b/crates/uplc/src/tx/tests.rs index a7a39266..f456dbca 100644 --- a/crates/uplc/src/tx/tests.rs +++ b/crates/uplc/src/tx/tests.rs @@ -499,6 +499,25 @@ fn test_eval_1() { .unwrap(); println!("{:?}", redeemers.len()); + + 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: 210171376, + mem: 721484 + } + ); } _ => unreachable!(), }; @@ -584,6 +603,25 @@ fn test_eval_2() { .unwrap(); println!("{:?}", redeemers.len()); + + 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: 183304563, + mem: 638338 + } + ); } _ => unreachable!(), }; @@ -828,6 +866,25 @@ fn test_eval_3() { .unwrap(); println!("{:?}", redeemers.len()); + + 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: 177894084, + mem: 566628 + } + ); } _ => unreachable!(), }; @@ -991,6 +1048,25 @@ fn test_eval_5() { .unwrap(); println!("{:?}", redeemers.len()); + + 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: 40211433, + mem: 114126 + } + ); } _ => unreachable!(), }; From c45643bb016c7b12ec43a041bd4d8bd8c66bc3b1 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Fri, 23 Sep 2022 04:09:28 -0400 Subject: [PATCH 68/77] empty bytestring should return 0 not 1 --- crates/uplc/src/machine.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/uplc/src/machine.rs b/crates/uplc/src/machine.rs index e03e12e1..f8a4fa87 100644 --- a/crates/uplc/src/machine.rs +++ b/crates/uplc/src/machine.rs @@ -457,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() { + 0 + } else { + ((b.len() as i64 - 1) / 8) + 1 + } + } Constant::String(s) => s.chars().count() as i64, Constant::Unit => 1, Constant::Bool(_) => 1, From 3bb5826b918d75f1b8bde25bb6eddba3e9e5e0c9 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Fri, 23 Sep 2022 18:34:24 -0400 Subject: [PATCH 69/77] change how mint gets converted to plutus data --- crates/uplc/src/tx/eval.rs | 8 ++++---- crates/uplc/src/tx/script_context.rs | 6 ++++-- crates/uplc/src/tx/to_plutus_data.rs | 26 +++++++++++++++++++++++++- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/crates/uplc/src/tx/eval.rs b/crates/uplc/src/tx/eval.rs index c1f8dc0c..9767a986 100644 --- a/crates/uplc/src/tx/eval.rs +++ b/crates/uplc/src/tx/eval.rs @@ -1,5 +1,5 @@ use crate::{ - ast::{FakeNamedDeBruijn, NamedDeBruijn, Program}, + ast::{DeBruijn, FakeNamedDeBruijn, NamedDeBruijn, Program}, machine::cost_model::ExBudget, PlutusData, }; @@ -19,7 +19,7 @@ use super::{ ResolvedInput, ScriptContext, ScriptPurpose, SlotConfig, TimeRange, TxInInfo, TxInfo, TxInfoV1, TxInfoV2, TxOut, }, - to_plutus_data::ToPlutusData, + to_plutus_data::{MintValue, ToPlutusData}, Error, }; @@ -286,7 +286,7 @@ fn get_tx_info_v1( inputs, outputs, fee, - mint, + mint: MintValue { mint_value: mint }, dcert, wdrl, valid_range, @@ -363,7 +363,7 @@ fn get_tx_info_v2( reference_inputs, outputs, fee, - mint, + mint: MintValue { mint_value: mint }, dcert, wdrl, valid_range, diff --git a/crates/uplc/src/tx/script_context.rs b/crates/uplc/src/tx/script_context.rs index 2ae643b9..f865d241 100644 --- a/crates/uplc/src/tx/script_context.rs +++ b/crates/uplc/src/tx/script_context.rs @@ -6,6 +6,8 @@ use pallas_primitives::babbage::{ }; use serde::Deserialize; +use super::to_plutus_data::MintValue; + #[derive(Debug, PartialEq, Clone, Deserialize)] pub struct ResolvedInput { pub input: TransactionInput, @@ -36,7 +38,7 @@ pub struct TxInfoV1 { pub inputs: Vec, pub outputs: Vec, pub fee: Value, - pub mint: Mint, + pub mint: MintValue, pub dcert: Vec, pub wdrl: Vec<(RewardAccount, Coin)>, pub valid_range: TimeRange, @@ -51,7 +53,7 @@ pub struct TxInfoV2 { pub reference_inputs: Vec, pub outputs: Vec, pub fee: Value, - pub mint: Mint, + pub mint: MintValue, pub dcert: Vec, pub wdrl: Withdrawals, pub valid_range: TimeRange, diff --git a/crates/uplc/src/tx/to_plutus_data.rs b/crates/uplc/src/tx/to_plutus_data.rs index 7747509e..305dfedc 100644 --- a/crates/uplc/src/tx/to_plutus_data.rs +++ b/crates/uplc/src/tx/to_plutus_data.rs @@ -1,7 +1,7 @@ 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, PlutusData, ScriptRef}; +use pallas_primitives::babbage::{AssetName, BigInt, Constr, Mint, PlutusData, ScriptRef}; use pallas_primitives::babbage::{ Certificate, DatumOption, PolicyId, Redeemer, Script, StakeCredential, TransactionInput, TransactionOutput, Value, @@ -43,6 +43,11 @@ pub trait ToPlutusData { fn to_plutus_data(&self) -> PlutusData; } +#[derive(Debug, PartialEq, Clone)] +pub struct MintValue { + pub mint_value: Mint, +} + impl ToPlutusData for Address { fn to_plutus_data(&self) -> PlutusData { match self { @@ -233,6 +238,25 @@ impl ToPlutusData for Value { } } +impl ToPlutusData for MintValue { + fn to_plutus_data(&self) -> PlutusData { + let mut data_vec: Vec<(PlutusData, PlutusData)> = vec![]; + + 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 { From 99a27f6a3dc4f85f1429b35adc2b3d4ed2de0748 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Sat, 24 Sep 2022 00:43:46 +0200 Subject: [PATCH 70/77] new tests --- crates/uplc/src/tx/eval.rs | 7 +- crates/uplc/src/tx/tests.rs | 206 ++++++++++++++++++++++++++++++++++++ 2 files changed, 212 insertions(+), 1 deletion(-) diff --git a/crates/uplc/src/tx/eval.rs b/crates/uplc/src/tx/eval.rs index c1f8dc0c..f7021efe 100644 --- a/crates/uplc/src/tx/eval.rs +++ b/crates/uplc/src/tx/eval.rs @@ -1,5 +1,5 @@ use crate::{ - ast::{FakeNamedDeBruijn, NamedDeBruijn, Program}, + ast::{DeBruijn, FakeNamedDeBruijn, NamedDeBruijn, Program}, machine::cost_model::ExBudget, PlutusData, }; @@ -739,10 +739,15 @@ pub fn eval_redeemer( prog.into() }; + let program_flat: Program = program.clone().into(); + println!("{}", hex::encode(program_flat.to_cbor().unwrap())); + let program = program .apply_data(redeemer.data.clone()) .apply_data(script_context.to_plutus_data()); + println!("{:#?}", 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 diff --git a/crates/uplc/src/tx/tests.rs b/crates/uplc/src/tx/tests.rs index f456dbca..0fea12d7 100644 --- a/crates/uplc/src/tx/tests.rs +++ b/crates/uplc/src/tx/tests.rs @@ -1072,6 +1072,212 @@ fn test_eval_5() { }; } +#[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 + 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(); + + println!("{:?}", redeemers.len()); + + 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: 40211433, + mem: 114126 + } + ); + } + _ => 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("84a80082825820f375a19211ee8ce75b8306a2604e2a0e8c50dfae7a103dd3688f980bec5e29b200825820c5bc1437117c200f325467dcede3c09fbfca32f9fe399bd40c778ab2fa832f43010182825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d5821a00111958a1581c366b596f230f1f7002b5a7a63c3fc6be4ef4e3ab81aae50824f78f52a1400a825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a0060019e021a0003d41009a1581c366b596f230f1f7002b5a7a63c3fc6be4ef4e3ab81aae50824f78f52a1400a0b58204d87ff063c986ace7ca9fb756623f2125e7270c6934724f4cef6ae181d59cf650d81825820f375a19211ee8ce75b8306a2604e2a0e8c50dfae7a103dd3688f980bec5e29b20110825839010af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a054a2fa2111a0005be18a30081825820065dd553fbe4e240a8f819bb9e333a7483de4a22b65c7fb6a95ce9450f84dff75840bb436c43e52cf56c75c551c65c25b10e6ef864ded07742bc2bca8ee20c29bad00d2016e265d15b614fa486b912443e3206a2ca19cca5d9a7c0ff08124a821f0e038159045a590457010000332232323232323222323232325335001100c1326320083357389201035054350000c3353235001220013553353333333574800846666ae68cdc39aab9d5004480008cccd55cfa8021280791999aab9f500425010233335573e6ae89401494cd54cd4c8c8c8c8c8c8c8c8c8c8c8c8ccccccd5d200611999ab9a3370e6aae7540312000233335573ea0184a04046666aae7d4030940848cccd55cfa8061281111999aab9f500c25023233335573ea0184a04846666aae7d4030940948cccd55cfa8061281311999aab9f500c25027233335573ea0184a05046666aae7d4030940a48cccd55cf9aba2500d2533553355335533553355335533533355025028335502502802735742a028426a05866644444444424666666666600201401201001600e00c00a0080060046ae854060d5d0a80b9aba150161502a213502c300135742a0282a054426a05860026ae85404c540a884d40b0c004d5d0a8090a815109a81618009aba150111502a213502c300135742a0202a054426a05860026ae85403c540a8940a80a009c09809409008c08808408007c0789407c060940789407894078940780704d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aab9e5001137540026ae85401c84d404c848cc00400c00854044854cd4c8c8c8c8ccccccd5d200211999ab9a3370ea004900111999aab9f500425019233335573e6ae89401494cd4c8c8c8c8ccccccd5d200211999ab9a3370e6aae7540112000233335573ea0084a04246666aae7d4010940888cccd55cf9aba25005253353232323333333574800646666ae68cdc39aab9d5003480008cccd55cfa8019281491999aab9f35744a0084a66a60506ae85401484d40b0004540a8940a80a009c940a00849409c9409c9409c9409c0944d55cf280089baa00135742a00e42a66a60426ae85401c84d409848cc00400c008540905408c9408c08408007c940800649407c9407c9407c9407c0744d5d1280089aab9e5001137540026ae85401884d4070488c00400c540689406806005c8cccd5cd19b875003480008cccd55cfa8029280d11999aab9f35744a00c4a66a60326ae85401c84d4074488c00800c5406c9406c064060940640480449405c9405c9405c9405c0544d55cea80109aab9e5001137540026ae85401c84d4050c00800454048540449404403c0380349403801c9403494034940349403402c8448c0040085880048488008984d5d1280089aab9e5001137540029308911911999999aba4001550052533530033756004426a0180022a014aa00aaa00aaa00a010640026460020024464646666aae7c00c8d403448800894cd4c01cd55cea80110a99a98039aab9e500321533530053574400c426a02024466002246600200c00a0062a01c2a01a2a01801426ae84008c00800844940148ccccccd5d200092802928029280291a8031bad002250050032333333357480024a0084a0084a0084a00846a00a6eb80080084800448488c00800c44880050581840100d87980821a0003b9f01a0675391df5f6").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 + 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(); + + println!("{:?}", redeemers.len()); + + 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: 40211433, + mem: 114126 + } + ); + } + _ => unreachable!(), + }; +} + #[test] fn eval_missing_redeemer() { let tx_bytes = hex::decode("84a30082825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c5000825820275b5da338c8b899035081eb34bfa950b634911a5dd3271b3ad6cf4c2bba0c50010181825839000af00cc47500bb64cfffb783e8c42f746b4e8b8a70ede9c08c7113acf3bde34d1041f5a2076ef9aa6cf4539ab1a96ed462a0300acbdb65d51a02cf47c8021a00028d89a1068149480100002221200101f5f6").unwrap(); From ca2d8f0a1fefe107ed742ace2f7c9870bff37162 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Sat, 24 Sep 2022 15:47:51 -0400 Subject: [PATCH 71/77] ada policy is now empty and mintValue includes 0 ada --- crates/uplc/src/tx/eval.rs | 2 +- crates/uplc/src/tx/script_context.rs | 2 +- crates/uplc/src/tx/to_plutus_data.rs | 14 ++++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/crates/uplc/src/tx/eval.rs b/crates/uplc/src/tx/eval.rs index 9767a986..253dc538 100644 --- a/crates/uplc/src/tx/eval.rs +++ b/crates/uplc/src/tx/eval.rs @@ -1,5 +1,5 @@ use crate::{ - ast::{DeBruijn, FakeNamedDeBruijn, NamedDeBruijn, Program}, + ast::{FakeNamedDeBruijn, NamedDeBruijn, Program}, machine::cost_model::ExBudget, PlutusData, }; diff --git a/crates/uplc/src/tx/script_context.rs b/crates/uplc/src/tx/script_context.rs index f865d241..f762e441 100644 --- a/crates/uplc/src/tx/script_context.rs +++ b/crates/uplc/src/tx/script_context.rs @@ -1,7 +1,7 @@ use pallas_codec::utils::KeyValuePairs; use pallas_crypto::hash::Hash; use pallas_primitives::babbage::{ - AddrKeyhash, Certificate, Coin, DatumHash, Mint, PlutusData, PolicyId, Redeemer, RewardAccount, + AddrKeyhash, Certificate, Coin, DatumHash, PlutusData, PolicyId, Redeemer, RewardAccount, StakeCredential, TransactionInput, TransactionOutput, Value, Withdrawals, }; use serde::Deserialize; diff --git a/crates/uplc/src/tx/to_plutus_data.rs b/crates/uplc/src/tx/to_plutus_data.rs index 305dfedc..a68b504f 100644 --- a/crates/uplc/src/tx/to_plutus_data.rs +++ b/crates/uplc/src/tx/to_plutus_data.rs @@ -3,7 +3,7 @@ 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, PolicyId, Redeemer, Script, StakeCredential, TransactionInput, + Certificate, DatumOption, Redeemer, Script, StakeCredential, TransactionInput, TransactionOutput, Value, }; use pallas_traverse::ComputeHash; @@ -206,7 +206,7 @@ impl ToPlutusData for Value { fn to_plutus_data(&self) -> PlutusData { match self { Value::Coin(coin) => PlutusData::Map(KeyValuePairs::Def(vec![( - PolicyId::from([0; 28]).to_plutus_data(), + Bytes::from(vec![]).to_plutus_data(), PlutusData::Map(KeyValuePairs::Def(vec![( AssetName::from(vec![]).to_plutus_data(), coin.to_plutus_data(), @@ -214,7 +214,7 @@ impl ToPlutusData for Value { )])), Value::Multiasset(coin, multiassets) => { let mut data_vec: Vec<(PlutusData, PlutusData)> = vec![( - PolicyId::from([0; 28]).to_plutus_data(), + Bytes::from(vec![]).to_plutus_data(), PlutusData::Map(KeyValuePairs::Def(vec![( AssetName::from(vec![]).to_plutus_data(), coin.to_plutus_data(), @@ -240,7 +240,13 @@ impl ToPlutusData for Value { impl ToPlutusData for MintValue { fn to_plutus_data(&self) -> PlutusData { - let mut data_vec: Vec<(PlutusData, PlutusData)> = vec![]; + 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![]; From 35d09c642b704ec919de5a598796900447b585c1 Mon Sep 17 00:00:00 2001 From: alessandrokonrad Date: Sat, 24 Sep 2022 23:59:58 +0200 Subject: [PATCH 72/77] fixed time conversion --- crates/cli/src/args.rs | 2 ++ crates/cli/src/main.rs | 2 ++ crates/uplc/src/tx.rs | 6 ++++-- crates/uplc/src/tx/eval.rs | 2 +- crates/uplc/src/tx/script_context.rs | 4 +++- crates/uplc/src/tx/tests.rs | 11 +++++++++++ 6 files changed, 23 insertions(+), 4 deletions(-) diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs index c861c25b..cd886739 100644 --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -29,6 +29,8 @@ pub enum TxCommand { slot_length: u64, #[clap(short, long)] zero_time: u64, + #[clap(short, long)] + zero_slot: u64, }, } diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 39e4cc51..23915c87 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -30,6 +30,7 @@ fn main() -> anyhow::Result<()> { resolved_inputs, slot_length, zero_time, + zero_slot, } => { let tx_bytes = if cbor { fs::read(input)? @@ -51,6 +52,7 @@ fn main() -> anyhow::Result<()> { let slot_config = SlotConfig { zero_time, + zero_slot, slot_length, }; diff --git a/crates/uplc/src/tx.rs b/crates/uplc/src/tx.rs index 35c8de7e..8996efd0 100644 --- a/crates/uplc/src/tx.rs +++ b/crates/uplc/src/tx.rs @@ -68,12 +68,13 @@ pub fn eval_phase_two( /// 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), + slot_config: (u64, u64, u64), run_phase_one: bool, ) -> Result>, Error> { let multi_era_tx = MultiEraTx::decode(Era::Babbage, tx_bytes) @@ -97,7 +98,8 @@ pub fn eval_phase_two_raw( let sc = SlotConfig { zero_time: slot_config.0, - slot_length: slot_config.1, + zero_slot: slot_config.1, + slot_length: slot_config.2, }; match multi_era_tx { diff --git a/crates/uplc/src/tx/eval.rs b/crates/uplc/src/tx/eval.rs index 9d7750bb..19212bd6 100644 --- a/crates/uplc/src/tx/eval.rs +++ b/crates/uplc/src/tx/eval.rs @@ -24,7 +24,7 @@ use super::{ }; fn slot_to_begin_posix_time(slot: u64, sc: &SlotConfig) -> u64 { - let ms_after_begin = slot * sc.slot_length; + let ms_after_begin = (slot - sc.zero_slot) * sc.slot_length; sc.zero_time + ms_after_begin } diff --git a/crates/uplc/src/tx/script_context.rs b/crates/uplc/src/tx/script_context.rs index f762e441..65497dec 100644 --- a/crates/uplc/src/tx/script_context.rs +++ b/crates/uplc/src/tx/script_context.rs @@ -84,6 +84,7 @@ pub struct TimeRange { pub struct SlotConfig { pub slot_length: u64, + pub zero_slot: u64, pub zero_time: u64, } @@ -91,7 +92,8 @@ impl Default for SlotConfig { fn default() -> Self { Self { slot_length: 1000, - zero_time: 1596491091, + zero_slot: 4492800, + zero_time: 1596059091000, } } } diff --git a/crates/uplc/src/tx/tests.rs b/crates/uplc/src/tx/tests.rs index 9fd84405..d37e1c48 100644 --- a/crates/uplc/src/tx/tests.rs +++ b/crates/uplc/src/tx/tests.rs @@ -42,6 +42,7 @@ fn test_eval() { let slot_config = SlotConfig { zero_time: 1660003200000, // Preview network + zero_slot: 0, slot_length: 1000, }; @@ -292,6 +293,7 @@ fn test_eval_1() { let slot_config = SlotConfig { zero_time: 1660003200000, // Preview network + zero_slot: 0, slot_length: 1000, }; @@ -561,6 +563,7 @@ fn test_eval_2() { let slot_config = SlotConfig { zero_time: 1660003200000, // Preview network + zero_slot: 0, slot_length: 1000, }; @@ -659,6 +662,7 @@ fn test_eval_3() { let slot_config = SlotConfig { zero_time: 1660003200000, // Preview network + zero_slot: 0, slot_length: 1000, }; @@ -925,6 +929,7 @@ fn test_eval_4() { let slot_config = SlotConfig { zero_time: 1660003200000, // Preview network + zero_slot: 0, slot_length: 1000, }; @@ -1006,6 +1011,7 @@ fn test_eval_5() { let slot_config = SlotConfig { zero_time: 1660003200000, // Preview network + zero_slot: 0, slot_length: 1000, }; @@ -1108,6 +1114,7 @@ fn test_eval_6() { let slot_config = SlotConfig { zero_time: 1596059091000, // Mainnet network + zero_slot: 4492800, slot_length: 1000, }; @@ -1211,6 +1218,7 @@ fn test_eval_7() { let slot_config = SlotConfig { zero_time: 1596059091000, // Mainnet network + zero_slot: 4492800, slot_length: 1000, }; @@ -1301,6 +1309,7 @@ fn test_eval_8() { let slot_config = SlotConfig { zero_time: 1596059091000, // Mainnet network + zero_slot: 4492800, slot_length: 1000, }; @@ -1564,6 +1573,7 @@ fn eval_missing_redeemer() { let slot_config = SlotConfig { zero_time: 1660003200000, // Preview network + zero_slot: 0, slot_length: 1000, }; @@ -1642,6 +1652,7 @@ fn eval_extraneous_redeemer() { let slot_config = SlotConfig { zero_time: 1660003200000, // Preview network + zero_slot: 0, slot_length: 1000, }; From 3cb24a1d0011379ee738e17bdbc34b8df9e1e208 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Sat, 24 Sep 2022 18:19:18 -0400 Subject: [PATCH 73/77] update test mem and cpu assertions for eval tx tests --- crates/uplc/src/tx/tests.rs | 61 ++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/crates/uplc/src/tx/tests.rs b/crates/uplc/src/tx/tests.rs index d37e1c48..1b56e242 100644 --- a/crates/uplc/src/tx/tests.rs +++ b/crates/uplc/src/tx/tests.rs @@ -249,7 +249,26 @@ fn test_eval() { ) .unwrap(); - assert_eq!(redeemers.len(), 1) + 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!(), }; @@ -500,7 +519,7 @@ fn test_eval_1() { ) .unwrap(); - println!("{:?}", redeemers.len()); + assert_eq!(redeemers.len(), 1); let total_budget_used = redeemers @@ -516,8 +535,8 @@ fn test_eval_1() { total_budget_used, // Numbers came uplc evaluate ExBudget { - cpu: 210171376, - mem: 721484 + cpu: 215316271, + mem: 738928 } ); } @@ -605,7 +624,7 @@ fn test_eval_2() { ) .unwrap(); - println!("{:?}", redeemers.len()); + assert_eq!(redeemers.len(), 1); let total_budget_used = redeemers @@ -621,8 +640,8 @@ fn test_eval_2() { total_budget_used, // Numbers came uplc evaluate ExBudget { - cpu: 183304563, - mem: 638338 + cpu: 188449458, + mem: 655782 } ); } @@ -869,7 +888,7 @@ fn test_eval_3() { ) .unwrap(); - println!("{:?}", redeemers.len()); + assert_eq!(redeemers.len(), 1); let total_budget_used = redeemers @@ -885,8 +904,8 @@ fn test_eval_3() { total_budget_used, // Numbers came uplc evaluate ExBudget { - cpu: 177894084, - mem: 566628 + cpu: 182855351, + mem: 583272 } ); } @@ -1053,7 +1072,7 @@ fn test_eval_5() { ) .unwrap(); - println!("{:?}", redeemers.len()); + assert_eq!(redeemers.len(), 1); let total_budget_used = redeemers @@ -1069,7 +1088,7 @@ fn test_eval_5() { total_budget_used, // Numbers came uplc evaluate ExBudget { - cpu: 40211433, + cpu: 40098159, mem: 114126 } ); @@ -1157,7 +1176,7 @@ fn test_eval_6() { ) .unwrap(); - println!("{:?}", redeemers.len()); + assert_eq!(redeemers.len(), 1); let total_budget_used = redeemers @@ -1173,8 +1192,8 @@ fn test_eval_6() { total_budget_used, // Numbers came uplc evaluate ExBudget { - cpu: 40211433, - mem: 114126 + cpu: 6231248, + mem: 4002 } ); } @@ -1261,7 +1280,7 @@ fn test_eval_7() { ) .unwrap(); - println!("{:?}", redeemers.len()); + assert_eq!(redeemers.len(), 1); let total_budget_used = redeemers @@ -1277,8 +1296,8 @@ fn test_eval_7() { total_budget_used, // Numbers came uplc evaluate ExBudget { - cpu: 40211433, - mem: 114126 + cpu: 1221507148, + mem: 2954794 } ); } @@ -1516,7 +1535,7 @@ fn test_eval_8() { ) .unwrap(); - println!("{:?}", redeemers.len()); + assert_eq!(redeemers.len(), 1); let total_budget_used = redeemers @@ -1532,8 +1551,8 @@ fn test_eval_8() { total_budget_used, // Numbers came uplc evaluate ExBudget { - cpu: 40211433, - mem: 114126 + cpu: 711173018, + mem: 2691678 } ); } From 8620332b75a90f421db450fa1c59c558f5ed4723 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sat, 24 Sep 2022 19:40:07 -0400 Subject: [PATCH 74/77] feat: move input from json to helper method --- crates/cli/src/args.rs | 9 +++++++-- crates/cli/src/main.rs | 10 ++-------- crates/uplc/src/tx/eval.rs | 2 +- crates/uplc/src/tx/script_context.rs | 22 ++++++++++++++++++++++ crates/uplc/src/tx/to_plutus_data.rs | 2 +- 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs index cd886739..6bc689e1 100644 --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -20,16 +20,21 @@ pub enum Args { pub enum TxCommand { /// Simulate a transaction by evaluating it's script Simulate { + /// A file containing cbor hex input: PathBuf, + + /// Toggle whether input is raw cbor or a hex string #[clap(short, long)] cbor: bool, + + /// Json file containing resolved inputs #[clap(short, long)] resolved_inputs: PathBuf, #[clap(short, long)] slot_length: u64, - #[clap(short, long)] + #[clap(long)] zero_time: u64, - #[clap(short, long)] + #[clap(long)] zero_slot: u64, }, } diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 23915c87..2b089a1c 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,8 +1,4 @@ -use std::{ - fmt::Write as _, - fs::{self, File}, - io::BufReader, -}; +use std::{fmt::Write as _, fs}; use pallas_traverse::{Era, MultiEraTx}; use uplc::{ @@ -46,9 +42,7 @@ fn main() -> anyhow::Result<()> { println!("Simulating: {}", tx.hash()); if let Some(tx_babbage) = tx.as_babbage() { - let file = File::open(&resolved_inputs)?; - let reader = BufReader::new(file); - let resolved_inputs: Vec = serde_json::from_reader(reader)?; + let resolved_inputs = ResolvedInput::from_json(&resolved_inputs)?; let slot_config = SlotConfig { zero_time, diff --git a/crates/uplc/src/tx/eval.rs b/crates/uplc/src/tx/eval.rs index 68894a6d..56027428 100644 --- a/crates/uplc/src/tx/eval.rs +++ b/crates/uplc/src/tx/eval.rs @@ -1,5 +1,5 @@ use crate::{ - ast::{DeBruijn, FakeNamedDeBruijn, NamedDeBruijn, Program}, + ast::{FakeNamedDeBruijn, NamedDeBruijn, Program}, machine::cost_model::ExBudget, PlutusData, }; diff --git a/crates/uplc/src/tx/script_context.rs b/crates/uplc/src/tx/script_context.rs index 65497dec..8fa08875 100644 --- a/crates/uplc/src/tx/script_context.rs +++ b/crates/uplc/src/tx/script_context.rs @@ -1,3 +1,5 @@ +use std::{fs::File, io::BufReader, path::PathBuf}; + use pallas_codec::utils::KeyValuePairs; use pallas_crypto::hash::Hash; use pallas_primitives::babbage::{ @@ -14,6 +16,26 @@ pub struct ResolvedInput { pub output: TransactionOutput, } +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("{0}")] + File(#[from] std::io::Error), + #[error("{0}")] + Serde(#[from] serde_json::error::Error), +} + +impl ResolvedInput { + pub fn from_json(file: &PathBuf) -> Result, Error> { + let file = File::open(file)?; + + let reader = BufReader::new(file); + + let resolved_inputs: Vec = serde_json::from_reader(reader)?; + + Ok(resolved_inputs) + } +} + #[derive(Debug, PartialEq, Clone)] pub struct TxInInfo { pub out_ref: TransactionInput, diff --git a/crates/uplc/src/tx/to_plutus_data.rs b/crates/uplc/src/tx/to_plutus_data.rs index a68b504f..3c6423aa 100644 --- a/crates/uplc/src/tx/to_plutus_data.rs +++ b/crates/uplc/src/tx/to_plutus_data.rs @@ -43,7 +43,7 @@ pub trait ToPlutusData { fn to_plutus_data(&self) -> PlutusData; } -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct MintValue { pub mint_value: Mint, } From 4166e27fd7717c9f3c2ed29afd14b656731672d0 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sat, 24 Sep 2022 19:52:40 -0400 Subject: [PATCH 75/77] chore: v1 comments --- crates/uplc/src/machine/cost_model.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/uplc/src/machine/cost_model.rs b/crates/uplc/src/machine/cost_model.rs index 4e6304fb..98be17e7 100644 --- a/crates/uplc/src/machine/cost_model.rs +++ b/crates/uplc/src/machine/cost_model.rs @@ -30,7 +30,6 @@ impl ExBudget { self.cpu *= n; } - // TODO: actually fill in the v1 numbers pub fn v1() -> Self { ExBudget { mem: 14000000, @@ -92,7 +91,6 @@ impl MachineCosts { } } - // TODO: actually fill in the v1 numbers pub fn v1() -> Self { Self { startup: ExBudget { mem: 100, cpu: 100 }, @@ -233,7 +231,6 @@ pub struct BuiltinCosts { } impl BuiltinCosts { - // TODO: actually fill in the v1 numbers pub fn v1() -> Self { Self { add_integer: CostingFun { @@ -586,7 +583,7 @@ impl BuiltinCosts { }, serialise_data: CostingFun { mem: OneArgument::LinearCost(LinearSize { - intercept: 0, + intercept: 30000000000, slope: 2, }), cpu: OneArgument::LinearCost(LinearSize { From 08596588a71f812e48d5e7c820b419566ff4920e Mon Sep 17 00:00:00 2001 From: rvcas Date: Sat, 24 Sep 2022 20:23:51 -0400 Subject: [PATCH 76/77] feat: output total budget spent from cli --- add_integers.uplc | 4 --- crates/cli/src/args.rs | 22 ++++++++---- crates/cli/src/main.rs | 52 ++++++++++++++++++++++------ crates/uplc/src/tx/script_context.rs | 22 ------------ thing.tx | 1 - thing_resolved_txins.tx | 4 --- 6 files changed, 57 insertions(+), 48 deletions(-) delete mode 100644 add_integers.uplc delete mode 100644 thing.tx delete mode 100644 thing_resolved_txins.tx diff --git a/add_integers.uplc b/add_integers.uplc deleted file mode 100644 index 0ca9825e..00000000 --- a/add_integers.uplc +++ /dev/null @@ -1,4 +0,0 @@ -(program - 1.0.0 - (lam y (lam x [ [ (builtin addInteger) x ] y ])) -) \ No newline at end of file diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs index 6bc689e1..5fbbbdcf 100644 --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -20,21 +20,29 @@ pub enum Args { pub enum TxCommand { /// Simulate a transaction by evaluating it's script Simulate { - /// A file containing cbor hex + /// 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, - /// Json file containing resolved inputs - #[clap(short, long)] - resolved_inputs: PathBuf, - #[clap(short, long)] + /// 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, - #[clap(long)] + + /// Time of shelley hardfork + #[clap(long, default_value_t = 1596059091000)] zero_time: u64, - #[clap(long)] + + /// Slot number at the start of the shelley hardfork + #[clap(long, default_value_t = 4492800)] zero_slot: u64, }, } diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 2b089a1c..cdbcacb7 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,5 +1,9 @@ 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}, @@ -23,27 +27,48 @@ fn main() -> anyhow::Result<()> { TxCommand::Simulate { input, cbor, - resolved_inputs, + raw_inputs, + raw_outputs, slot_length, zero_time, zero_slot, } => { - let tx_bytes = if cbor { - fs::read(input)? + 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(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 resolved_inputs = ResolvedInput::from_json(&resolved_inputs)?; - let slot_config = SlotConfig { zero_time, zero_slot, @@ -61,11 +86,18 @@ fn main() -> anyhow::Result<()> { match result { Ok(redeemers) => { - println!("\nResult\n------\n\n"); + println!("\nTotal Budget Used\n-----------------\n"); - for redeemer in redeemers { - println!("{:#?}", redeemer) - } + 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); diff --git a/crates/uplc/src/tx/script_context.rs b/crates/uplc/src/tx/script_context.rs index 8fa08875..65497dec 100644 --- a/crates/uplc/src/tx/script_context.rs +++ b/crates/uplc/src/tx/script_context.rs @@ -1,5 +1,3 @@ -use std::{fs::File, io::BufReader, path::PathBuf}; - use pallas_codec::utils::KeyValuePairs; use pallas_crypto::hash::Hash; use pallas_primitives::babbage::{ @@ -16,26 +14,6 @@ pub struct ResolvedInput { pub output: TransactionOutput, } -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("{0}")] - File(#[from] std::io::Error), - #[error("{0}")] - Serde(#[from] serde_json::error::Error), -} - -impl ResolvedInput { - pub fn from_json(file: &PathBuf) -> Result, Error> { - let file = File::open(file)?; - - let reader = BufReader::new(file); - - let resolved_inputs: Vec = serde_json::from_reader(reader)?; - - Ok(resolved_inputs) - } -} - #[derive(Debug, PartialEq, Clone)] pub struct TxInInfo { pub out_ref: TransactionInput, diff --git a/thing.tx b/thing.tx deleted file mode 100644 index 90a46f74..00000000 --- a/thing.tx +++ /dev/null @@ -1 +0,0 @@ -84A8008282582071B02D2309057CA589878C02EF9F89CA2A911F4282BEF459A44B035DEEE292F0008258207F825475CA1C61EB4520EB486D66727BBBFBD6E9EED68FCC92DC8BA5147F8C20000182825839018385FBF4C951B5FD7E8F0457BDE479C99F910F5B7006E0A4B7F3E714EC061767F7E1B3069F7D4AD6CED921F4FF21CF578C368B70E9993BCE821A00150BD0A1581CDEEBF749DD081B3AEA1C59EF2A1BE1038D61A0C7398DE15C310244BEA14C54455354544F4B454E31363101825839018385FBF4C951B5FD7E8F0457BDE479C99F910F5B7006E0A4B7F3E714EC061767F7E1B3069F7D4AD6CED921F4FF21CF578C368B70E9993BCE1A0042F4FB021A0005ED3C031A0431210D081A043112FD0B582000000000000000000000000000000000000000000000000000000000000000000D81825820FA794F486D1AA007961381BF05E2E435E71D4C361707ABA64854B59D50D3C498010E81581C8385FBF4C951B5FD7E8F0457BDE479C99F910F5B7006E0A4B7F3E714A40082825820000000008385FBF4C951B5FD7E8F0457BDE479C99F910F5B7006E0A4B7F3E714584000F899D39B17FD5D66C192C4B50D343E42F7235B30504C8AE7619F93C828DC6DCE4568DD69177C551828492D777A6727FD66C2FBCCBDA8C2AEED92032C99790A82582000000000FA130B1813EC48D7DAAD638CC95080A8D7FE68CBBAF15D87F57B73C6584000F899D39B17FD5D66C192C4B50D343E42F7235B30504C8AE7619F93C828DC6DCE4568DD69177C551828492D777A6727FD66C2FBCCBDA8C2AEED92032C99790A0381590FC1590FBE0100003233223232323322333222323233322232333222323332223322323322323332223232332233223232332232323332223322332233223322323232323232323232323232332232323232323232323322323232332232333322223232323232322232232325335305C33223530310012235303500222222222322235304400C23232325335306F333222533530723300300200110731074506E32353047001220023253353505A00113507649010350543800221002302E5002004107115335304A01313301B49101350033355029302F1200123535505E00122323355030335502D30331200123535506200122353550640012253353506F335503533550250490040062153353507033550363335550323039120012235355069002232233225335350770022130020011507800425335350763335502C0500040072153353080013304D0010031335503D5079300400215335308001330630010031335503D507933335502E0510053304900100300215078150773200135508601225335350680011506A2213535506E002225335308201330530020071003133506D33550710020013006003350720010022133026491023130003322333573466E200080041F41F8CC8CD54C0EC48004D40C00048004CD40C40952000335530321200123535506900122001001004133573892010231310007A133573892010231320007900233233553023120013503500135034001335038223355302C120012353550630012233550660023355302F12001235355066001223355069002333535502B0012330264800000488CC09C0080048CC09800520000013355302C1200123535506300122335506600233353550280012335530301200123535506700122335506A00235502F0010012233355502804A0020012335530301200123535506700122335506A00235502D001001333555023045002001505E33233553023120012253353506C3003002213350610010021001505F2353053001222533530763332001504100600313506F0021506E011320013333555028302F1200122353055002225335307333044002500B10031333355502C303312001235305A00122253353506E333550245042003001213333550265043004335530301200123535506700122335506A002333535502C0012001223535506B002223535506D0032233550703302C00400233553039120012353550700012233550730023335355035001200122330310020012001333555030052003001200133355502704900300100213333550255042003002001003001505B500113301B49101340033355029302F1200123530540012233043500A00250011533535058335530401200123320015051320013530460012235305100122253353506B0012321300100D3200135507F2253353506100113507D491022D310022135355067002225335307B3304C00200710011300600313507A49101370050011350744901013600221335530421200123320015053320013530480012235305300122253353506D0012321300100F32001355081012253353506300113507F491022D310022135355069002225335307D3304E00200710011300600313507C4910137005003133355301D12001225335306F335306A303E302D35304600222001207125335307033041001300401010721350764901013300133505A0020011001505900D3200135507622533535058001135074491022D31002213530470022253353072333200150710020071353063303000122335306F00223507B491022D310020011300600315335350520011306D4988854CD4D41500044008884C1C5263333573466E1D40112002203A23333573466E1D40152000203A23263530663357380B80CE0CA0C80C66666AE68CDC39AAB9D5002480008CC0C4C8C8C8C8C8C8C8C8C8C8C8CCCD5CD19B8735573AA01490001199999999981F99A828919191999AB9A3370E6AAE7540092000233045304B35742A00460986AE84D5D1280111931A983A99AB9C06B076074073135573CA00226EA8004D5D0A80519A8288241ABA1500935742A0106AE85401CD5D0A8031ABA1500535742A00866A0A2EB8D5D0A80199A82899AA82B3AE200135742A0046AE84D5D1280111931A983899AB9C06707207006F135744A00226AE8940044D5D1280089ABA25001135744A00226AE8940044D5D1280089ABA25001135573CA00226EA8004D5D0A80119191999AB9A3370E6AAE75400520022303A303E357426AAE7940088C98D4C1A0CD5CE02F03483383309BAA001357426AE8940088C98D4C194CD5CE02D833032031883289931A983219AB9C49010350543500065063135573CA00226EA80044D55CE9BAA001223370000400244A66A60AC00220B0266AE7000815C4488C88C008004C8004D5417C894CD4D41040045413C884D4D5411C008894CD4C16CCC02000801C4D41500044C01800C448888C8CD54C03C480048D4D5411800488CD54124008CCD4D5402C0048004880048004CCD554018014008004C8CD40054109410C488CC008CD5411C014010004444888CCD54C0104800540FCCD54C030480048D4D5410C00488CD54118008D5402C004CCD54C0104800488D4D54110008894CD4C160CCD54C06048004D4034CD403C894CD4C168008417040041648D4D5411C00488CC028008014018400C4CD410C01000D4100004CD54C030480048D4D5410C00488C8CD5411C00CC004014C8004D54184894CD4D410C0044D5402C00C884D4D54124008894CD4C174CC0300080204CD5404001C0044C01800C008C8004D5416888448894CD4D40FC0044008884CC014008CCD54C01C480040140100044484888C00C01044884888CC0080140104484888C00401044800448CD404888CCD4D401000C88008008004D4D40080048800448848CC00400C00848004C8004D541488844894CD4D40D8004540E0884CD40E4C010008CD54C018480040100044448888CCCD54011403C00C0040084488CD54008C8CD403C88CCD4D401800C88008008004D4D401000488004CD401005012800448848CC00400C008480044488C0080048D4C08C00488800CC8004D5412C88C8C94CD4C114CC0A14009200213300C300433530081200150010033004335300D12001500100310031332233706004002A002900209999AA9801890009919A80511199A8040018008011A802800A8039119B800014800800520003200135504A221122253353502F00113500600322133350090053004002333553007120010050040011235350050012200112353500400122002320013550472212253353041333573466E2400920000430421502D153353502B0011502D22133502E002335300612001337020089001000899A801111180198010009000891091980080180109000990009AA821911299A9A81300108009109A980A801111A982180111299A9A8160038A99A9A8160038804110B1109A980D801111A982480111299A9824199AB9A33720010004094092266A0660186601E01601A2A66A6090666AE68CDC8804001024825099A819803198078070028A99A982419809003800899A81980619807805806899A81980319807807002990009AA82111091299A9A8130008A814110A99A981F19804002240002006266A600C240026600E00890010009119B8100200122333573466E240080040E80E4488CC00CCC01CC018008C018004CC894CD4D40C0008854CD4D40C400884CD4C0B80088CD4C0BC0088CC034008004888100888CD4C0C401081008894CD4C104CCD5CD19B87006003043042153353041333573466E1C01400810C1084CC0380100044108410840EC54CD4D40C0004840EC40ECC014008C014004894CD4C0D8008400440DC88CCD5CD19B8700200103703623530240012200123530230012200222335302D0022335302E00223300500200120352335302E002203523300500200122333573466E3C0080040CC0C8C8004D540E08844894CD4D407000454078884CD407CC010008CD54C018480040100048848CC00400C0088004888888888848CCCCCCCCCC00402C02802402001C01801401000C00880048848CC00400C0088004848C004008800448800848800480048C8C8CCCD5CD19B8735573AA004900011981519191999AB9A3370E6AAE75400520002375C6AE84D55CF280111931A981899AB9C02703203002F137540026AE854008DD69ABA135744A004464C6A605C66AE700900BC0B40B04D55CF280089BAA00123232323333573466E1CD55CEA801A4000466600E602C6AE85400CCCD5403DD719AA807BAE75A6AE854008CD4071D71ABA135744A004464C6A605C66AE700900BC0B40B04D5D1280089AAB9E5001137540024442466600200800600440022464646666AE68CDC39AAB9D5002480008CC88CC024008004DD71ABA1500233500A232323333573466E1CD55CEA80124000466446601E004002602C6AE854008CCD5403DD719AA809919299A981419805A800A40022A00226A05C921022D33001375A00266AA01EEB88C94CD4C0A0CC02D400520001500113502E491022D32001375A0026AE84D5D1280111931A981719AB9C02402F02D02C135573CA00226EA8004D5D09ABA25002232635302A33573804005605205026AAE7940044DD500091199AB9A33712004002040042442466002006004400244246600200600440022464460046EB0004C8004D5408C88CCCD55CF80092804119A80398021ABA1002300335744004048224464460046EAC004C8004D5408C88C8CCCD55CF80112804919A80419AA80618031AAB9D5002300535573CA00460086AE8800C0944D5D0800889100109109119800802001890008891119191999AB9A3370E6AAE754009200023355008300635742A004600A6AE84D5D1280111931A981099AB9C01702202001F135573CA00226EA8004448848CC00400C0084480048C8C8CCCD5CD19B8735573AA004900011980318071ABA1500233500A2323232323333573466E1D40052002233300E375A6AE854010DD69ABA15003375A6AE84D5D1280191999AB9A3370EA004900011808180A9ABA135573CA00C464C6A604666AE700640900880840804D55CEA80189ABA25001135573CA00226EA8004D5D09ABA25002232635301C33573802403A03603426AAE7940044DD5000910919800801801100090911801001911091199800802802001900089119191999AB9A3370EA002900011A80418029ABA135573CA00646666AE68CDC3A801240044A010464C6A603066AE7003806405C0580544D55CEA80089BAA001121223002003112200112001232323333573466E1D4005200223006375C6AE84D55CF280191999AB9A3370EA0049000118041BAE357426AAE7940108C98D4C04CCD5CE00480A00900880809AAB9D50011375400242446004006424460020064002921035054310012253353003333573466E3CD4C01800888008D4C018004880080140104CCD5CD19B8735300600222001353006001220010050041004122002122001200122123300100300220012350024901013100123263530033357380020080049309000900088919180080091198019801001000810481D87982581C8385FBF4C951B5FD7E8F0457BDE479C99F910F5B7006E0A4B7F3E71482D87982D87982D87981581C70E60F3B5EA7153E0ACC7A803E4401D44B8ED1BAE1C7BAAAD1A62A72D87981D87981D87981581C1E78AAE7C90CC36D624F7B3BB6D86B52696DC84E490F343EBA89005FA140D8798200A1401B00000003AD610AC0D87982D87982D87981581C8385FBF4C951B5FD7E8F0457BDE479C99F910F5B7006E0A4B7F3E714D87981D87981D87981581CEC061767F7E1B3069F7D4AD6CED921F4FF21CF578C368B70E9993BCEA140D8798200A1401B000000B42F930EC00581840000D87980821A00D59F7F1B00000002540BE3FFF5F6 \ No newline at end of file diff --git a/thing_resolved_txins.tx b/thing_resolved_txins.tx deleted file mode 100644 index 38c497d9..00000000 --- a/thing_resolved_txins.tx +++ /dev/null @@ -1,4 +0,0 @@ -[ - {"input":{"tx_hash":"71b02d2309057ca589878c02ef9f89ca2a911f4282bef459a44b035deee292f0","index":0},"output":{"address":"addr1zxj47sy4qxlktqzmkrw8dahe46gtv8seakrshsqz26qnvzypw288a4x0xf8pxgcntelxmyclq83s0ykeehchz2wtspksr3q9nx","value":[1724100,{"deebf749dd081b3aea1c59ef2a1be1038d61a0c7398de15c310244be": {"54455354544f4b454e313631": 1}}],"datum":{"datum_hash": "d908988cd6197fb46e9711a8e84eda57e1b134b0e0fe11ea7e9cdc6e3d484189"}}}, - {"input":{"tx_hash":"ba68b9076c6c34666d5a554d7cd0fdffacc1c38d86cd37ff5c565d77a1ce2fd0","index":0},"output":{"address":"addr1qxpct7l5e9gmtlt73uz9000y08yelyg0tdcqdc9ykle7w98vqctk0alpkvrf7l226m8djg05lusu74uvx69hp6ve808qxc39wj","value":[5000000,{}]}} -] \ No newline at end of file From 25790287b709e175d7c9310392ad39a35848506b Mon Sep 17 00:00:00 2001 From: rvcas Date: Sat, 24 Sep 2022 20:35:36 -0400 Subject: [PATCH 77/77] Release 0.0.13 aiken@0.0.13 uplc@0.0.13 Generated by cargo-workspaces --- Cargo.lock | 4 ++-- crates/cli/Cargo.toml | 4 ++-- crates/uplc/Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bc815bba..32cf5b85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "aiken" -version = "0.0.12" +version = "0.0.13" dependencies = [ "anyhow", "clap", @@ -674,7 +674,7 @@ checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" [[package]] name = "uplc" -version = "0.0.12" +version = "0.0.13" dependencies = [ "anyhow", "cryptoxide", diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 1590c381..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.12" +version = "0.0.13" edition = "2021" repository = "https://github.com/txpipe/aiken" homepage = "https://github.com/txpipe/aiken" @@ -21,4 +21,4 @@ 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.12" } +uplc = { path = '../uplc', version = "0.0.13" } diff --git a/crates/uplc/Cargo.toml b/crates/uplc/Cargo.toml index 56e4b6ef..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.12" +version = "0.0.13" edition = "2021" repository = "https://github.com/txpipe/aiken/crates/uplc" homepage = "https://github.com/txpipe/aiken"