diff --git a/crates/uplc/src/flat/decode/decoder.rs b/crates/uplc/src/flat/decode/decoder.rs index 21c11460..e61142e2 100644 --- a/crates/uplc/src/flat/decode/decoder.rs +++ b/crates/uplc/src/flat/decode/decoder.rs @@ -21,6 +21,16 @@ pub struct Ctx<'a> { } impl<'a> Ctx<'a> { + /// Returns true when constr/case terms are allowed. + /// + /// Both the protocol version (>= 9, i.e. Conway) and the program version + /// (>= 1.1.0) must permit them. + pub fn is_constr_case_available(&self) -> bool { + let protocol_ok = self.protocol_version.is_none_or(|pv| pv >= 9); + let version_ok = self.version.is_none_or(|v| v.is_constr_case_available()); + protocol_ok && version_ok + } + /// Returns true if the given builtin is NOT available under the current /// plutus_version / protocol_version combination. pub fn is_builtin_gated(&self, func: &DefaultFunction) -> bool { diff --git a/crates/uplc/src/flat/decode/mod.rs b/crates/uplc/src/flat/decode/mod.rs index 7f410e5e..e63d6a77 100644 --- a/crates/uplc/src/flat/decode/mod.rs +++ b/crates/uplc/src/flat/decode/mod.rs @@ -161,7 +161,7 @@ where } // Constr tag::CONSTR => { - if ctx.version.is_some_and(|v| v.is_less_than_1_1_0()) { + if !ctx.is_constr_case_available() { return Err(FlatDecodeError::TermNotAvailable(tag::CONSTR, "constr")); } @@ -175,7 +175,7 @@ where } // Case tag::CASE => { - if ctx.version.is_some_and(|v| v.is_less_than_1_1_0()) { + if !ctx.is_constr_case_available() { return Err(FlatDecodeError::TermNotAvailable(tag::CASE, "case")); } diff --git a/crates/uplc/src/lib.rs b/crates/uplc/src/lib.rs index 54bec029..ca6d6a53 100644 --- a/crates/uplc/src/lib.rs +++ b/crates/uplc/src/lib.rs @@ -19,6 +19,7 @@ mod tests { use pretty_assertions::assert_eq; use crate::binder::DeBruijn; + use crate::machine::{default_v3_cost_model, ExBudget, PlutusVersion}; use crate::program::Version; use super::arena::Arena; @@ -137,4 +138,142 @@ mod tests { assert_eq!(result.term.unwrap(), Term::integer_from(arena, 610)); } + + // --- eval_with_params protocol_version gating tests --- + + #[test] + fn eval_with_params_base_builtin_same_budget_across_protocol_versions() { + // add_integer is a base V3 builtin (positions 0-3 in the cost key list). + // Its costs should be identical regardless of protocol_version since they + // are always included in the base key section. + let arena = Arena::new(); + let costs = default_v3_cost_model(); + + let term = Term::add_integer(&arena) + .apply(&arena, Term::integer_from(&arena, 1)) + .apply(&arena, Term::integer_from(&arena, 3)); + let version = Version::plutus_v3(&arena); + let program = Program::::new(&arena, version, term); + + let r9 = program.eval_with_params( + &arena, + PlutusVersion::V3, + (9, 0), + &costs, + ExBudget::default(), + ); + let r10 = program.eval_with_params( + &arena, + PlutusVersion::V3, + (10, 0), + &costs, + ExBudget::default(), + ); + let r11 = program.eval_with_params( + &arena, + PlutusVersion::V3, + (11, 0), + &costs, + ExBudget::default(), + ); + + // All three should produce the correct result + assert_eq!(r9.term.unwrap(), Term::integer_from(&arena, 4)); + assert_eq!(r10.term.unwrap(), Term::integer_from(&arena, 4)); + assert_eq!(r11.term.unwrap(), Term::integer_from(&arena, 4)); + + // Base builtin budgets should be identical regardless of protocol version + assert_eq!(r9.info.consumed_budget, r10.info.consumed_budget); + assert_eq!(r10.info.consumed_budget, r11.info.consumed_budget); + } + + #[test] + fn eval_with_params_plomin_builtin_succeeds_at_protocol_v10() { + // ripemd_160 is a Plomin builtin. With protocol_version >= 10, + // PLOMIN_KEYS are included in the cost map so the real costs apply. + let arena = Arena::new(); + let costs = default_v3_cost_model(); + + let term = + Term::::ripemd_160(&arena).apply(&arena, Term::byte_string(&arena, b"test")); + let version = Version::plutus_v3(&arena); + let program = Program::::new(&arena, version, term); + + let result = program.eval_with_params( + &arena, + PlutusVersion::V3, + (10, 0), + &costs, + ExBudget::default(), + ); + + assert!( + result.term.is_ok(), + "post-Plomin ripemd_160 should succeed with real costs" + ); + } + + #[test] + fn eval_with_params_plomin_builtin_exceeds_budget_at_protocol_v9() { + // With protocol_version < 10, PLOMIN_KEYS are NOT included. The ripemd_160 + // cost keys are absent from the map, so the cost model falls back to the + // sentinel value (30_000_000_000) which exceeds the default budget. + let arena = Arena::new(); + let costs = default_v3_cost_model(); + + let term = + Term::::ripemd_160(&arena).apply(&arena, Term::byte_string(&arena, b"test")); + let version = Version::plutus_v3(&arena); + let program = Program::::new(&arena, version, term); + + let result = program.eval_with_params( + &arena, + PlutusVersion::V3, + (9, 0), + &costs, + ExBudget::default(), + ); + + assert!( + result.term.is_err(), + "pre-Plomin ripemd_160 should fail: sentinel costs exceed budget" + ); + } + + #[test] + fn eval_with_params_plomin_builtin_different_budget_by_protocol_version() { + // The same ripemd_160 program with protocol_version 10 vs 11 should both + // succeed, but protocol_version 11 also adds PV11_KEYS. Since the cost + // array only has 297 values (base + Plomin), PV11 keys get no values and + // fall back to sentinel. The ripemd_160 cost itself is the same in both + // cases because it's in PLOMIN_KEYS which are included at both PV 10 and 11. + let arena = Arena::new(); + let costs = default_v3_cost_model(); + + let term = + Term::::ripemd_160(&arena).apply(&arena, Term::byte_string(&arena, b"test")); + let version = Version::plutus_v3(&arena); + let program = Program::::new(&arena, version, term); + + let r10 = program.eval_with_params( + &arena, + PlutusVersion::V3, + (10, 0), + &costs, + ExBudget::default(), + ); + let r11 = program.eval_with_params( + &arena, + PlutusVersion::V3, + (11, 0), + &costs, + ExBudget::default(), + ); + + assert!(r10.term.is_ok()); + assert!(r11.term.is_ok()); + + // ripemd_160 cost should be identical at PV 10 and 11 (same PLOMIN keys) + assert_eq!(r10.info.consumed_budget, r11.info.consumed_budget); + } } diff --git a/crates/uplc/src/machine/cek.rs b/crates/uplc/src/machine/cek.rs index 8ce56622..aeade8c5 100644 --- a/crates/uplc/src/machine/cek.rs +++ b/crates/uplc/src/machine/cek.rs @@ -255,7 +255,7 @@ impl<'a, B: BuiltinCostModel> Machine<'a, B> { Err(MachineError::MissingCaseBranch(branches, value)) } } - Value::Con(constant) if self.version.is_at_least_1_1_0() => { + Value::Con(constant) if self.version.is_constr_case_available() => { let (tag, max_branches, fields) = self.constant_as_tag_fields(constant)?; if branches.len() > max_branches { diff --git a/crates/uplc/src/machine/cost_model/builtin_costs/builtin_costs_v1.rs b/crates/uplc/src/machine/cost_model/builtin_costs/builtin_costs_v1.rs index f8ae8203..6749526e 100644 --- a/crates/uplc/src/machine/cost_model/builtin_costs/builtin_costs_v1.rs +++ b/crates/uplc/src/machine/cost_model/builtin_costs/builtin_costs_v1.rs @@ -76,6 +76,14 @@ pub struct BuiltinCostsV1 { mk_pair_data: TwoArgumentsCosting, mk_nil_data: OneArgumentCosting, mk_nil_pair_data: OneArgumentCosting, + // bitwise + ripemd_160: OneArgumentCosting, + + exp_mod_integer: ThreeArgumentsCosting, + drop_list: TwoArgumentsCosting, + length_of_array: OneArgumentCosting, + list_to_array: TwoArgumentsCosting, + index_array: TwoArgumentsCosting, } impl Default for BuiltinCostsV1 { @@ -285,6 +293,30 @@ impl Default for BuiltinCostsV1 { OneArgumentCosting::constant_cost(32), OneArgumentCosting::constant_cost(7391), ), + ripemd_160: OneArgumentCosting::new( + OneArgumentCosting::constant_cost(3), + OneArgumentCosting::linear_cost(1964219, 24520), + ), + exp_mod_integer: ThreeArgumentsCosting::new( + ThreeArgumentsCosting::linear_in_z(0, 1), + ThreeArgumentsCosting::exp_mod_cost(607153, 231697, 53144), + ), + drop_list: TwoArgumentsCosting::new( + TwoArgumentsCosting::constant_cost(4), + TwoArgumentsCosting::linear_in_x(116711, 1957), + ), + length_of_array: OneArgumentCosting::new( + OneArgumentCosting::constant_cost(10), + OneArgumentCosting::constant_cost(198994), + ), + list_to_array: TwoArgumentsCosting::new( + TwoArgumentsCosting::linear_in_x(7, 1), + TwoArgumentsCosting::linear_in_x(307802, 8496), + ), + index_array: TwoArgumentsCosting::new( + TwoArgumentsCosting::constant_cost(32), + TwoArgumentsCosting::constant_cost(194922), + ), } } } @@ -630,6 +662,44 @@ impl BuiltinCostModel for BuiltinCostsV1 { cost_map["verify_ed25519_signature-cpu-arguments-slope"], ), ), + ripemd_160: OneArgumentCosting::new( + OneArgumentCosting::constant_cost(cost_map["ripemd_160-memory-arguments"]), + OneArgumentCosting::linear_cost( + cost_map["ripemd_160-cpu-arguments-intercept"], + cost_map["ripemd_160-cpu-arguments-slope"], + ), + ), + + exp_mod_integer: ThreeArgumentsCosting::new( + ThreeArgumentsCosting::linear_in_z(0, 1), + ThreeArgumentsCosting::exp_mod_cost(607153, 231697, 53144), + ), + + drop_list: TwoArgumentsCosting::new( + TwoArgumentsCosting::constant_cost(cost_map["drop_list-mem-arguments"]), + TwoArgumentsCosting::linear_in_x( + cost_map["drop_list-cpu-arguments-intercept"], + cost_map["drop_list-cpu-arguments-slope"], + ), + ), + length_of_array: OneArgumentCosting::new( + OneArgumentCosting::constant_cost(cost_map["length_of_array-mem-arguments"]), + OneArgumentCosting::constant_cost(cost_map["length_of_array-cpu-arguments"]), + ), + list_to_array: TwoArgumentsCosting::new( + TwoArgumentsCosting::linear_in_x( + cost_map["list_to_array-mem-arguments-intercept"], + cost_map["list_to_array-mem-arguments-slope"], + ), + TwoArgumentsCosting::linear_in_x( + cost_map["list_to_array-cpu-arguments-intercept"], + cost_map["list_to_array-cpu-arguments-slope"], + ), + ), + index_array: TwoArgumentsCosting::new( + TwoArgumentsCosting::constant_cost(cost_map["index_array-mem-arguments"]), + TwoArgumentsCosting::constant_cost(cost_map["index_array-cpu-arguments"]), + ), } } @@ -851,6 +921,30 @@ impl BuiltinCostModel for BuiltinCostsV1 { self.mk_nil_pair_data.mem.cost([args[0]]), self.mk_nil_pair_data.cpu.cost([args[0]]), )), + DefaultFunction::Ripemd_160 => Some(ExBudget::new( + self.ripemd_160.mem.cost([args[0]]), + self.ripemd_160.cpu.cost([args[0]]), + )), + DefaultFunction::ExpModInteger => Some(ExBudget::new( + self.exp_mod_integer.mem.cost([args[0], args[1], args[2]]), + self.exp_mod_integer.cpu.cost([args[0], args[1], args[2]]), + )), + DefaultFunction::DropList => Some(ExBudget::new( + self.drop_list.mem.cost([args[0], args[1]]), + self.drop_list.cpu.cost([args[0], args[1]]), + )), + DefaultFunction::LengthOfArray => Some(ExBudget::new( + self.length_of_array.mem.cost([args[0]]), + self.length_of_array.cpu.cost([args[0]]), + )), + DefaultFunction::ListToArray => Some(ExBudget::new( + self.list_to_array.mem.cost([args[0], args[1]]), + self.list_to_array.cpu.cost([args[0], args[1]]), + )), + DefaultFunction::IndexArray => Some(ExBudget::new( + self.index_array.mem.cost([args[0], args[1]]), + self.index_array.cpu.cost([args[0], args[1]]), + )), _ => None, } } diff --git a/crates/uplc/src/machine/cost_model/builtin_costs/builtin_costs_v2.rs b/crates/uplc/src/machine/cost_model/builtin_costs/builtin_costs_v2.rs index 75c3345a..28987c3d 100644 --- a/crates/uplc/src/machine/cost_model/builtin_costs/builtin_costs_v2.rs +++ b/crates/uplc/src/machine/cost_model/builtin_costs/builtin_costs_v2.rs @@ -79,6 +79,14 @@ pub struct BuiltinCostsV2 { mk_nil_data: OneArgumentCosting, mk_nil_pair_data: OneArgumentCosting, serialise_data: OneArgumentCosting, + // bitwise + ripemd_160: OneArgumentCosting, + + exp_mod_integer: ThreeArgumentsCosting, + drop_list: TwoArgumentsCosting, + length_of_array: OneArgumentCosting, + list_to_array: TwoArgumentsCosting, + index_array: TwoArgumentsCosting, } impl Default for BuiltinCostsV2 { @@ -300,6 +308,30 @@ impl Default for BuiltinCostsV2 { OneArgumentCosting::linear_cost(0, 2), OneArgumentCosting::linear_cost(955506, 213312), ), + ripemd_160: OneArgumentCosting::new( + OneArgumentCosting::constant_cost(3), + OneArgumentCosting::linear_cost(1964219, 24520), + ), + exp_mod_integer: ThreeArgumentsCosting::new( + ThreeArgumentsCosting::linear_in_z(0, 1), + ThreeArgumentsCosting::exp_mod_cost(607153, 231697, 53144), + ), + drop_list: TwoArgumentsCosting::new( + TwoArgumentsCosting::constant_cost(4), + TwoArgumentsCosting::linear_in_x(116711, 1957), + ), + length_of_array: OneArgumentCosting::new( + OneArgumentCosting::constant_cost(10), + OneArgumentCosting::constant_cost(198994), + ), + list_to_array: TwoArgumentsCosting::new( + TwoArgumentsCosting::linear_in_x(7, 1), + TwoArgumentsCosting::linear_in_x(307802, 8496), + ), + index_array: TwoArgumentsCosting::new( + TwoArgumentsCosting::constant_cost(32), + TwoArgumentsCosting::constant_cost(194922), + ), } } } @@ -671,6 +703,44 @@ impl BuiltinCostModel for BuiltinCostsV2 { cost_map["verify_schnorr_secp256k1_signature-cpu-arguments-slope"], ), ), + ripemd_160: OneArgumentCosting::new( + OneArgumentCosting::constant_cost(cost_map["ripemd_160-memory-arguments"]), + OneArgumentCosting::linear_cost( + cost_map["ripemd_160-cpu-arguments-intercept"], + cost_map["ripemd_160-cpu-arguments-slope"], + ), + ), + + exp_mod_integer: ThreeArgumentsCosting::new( + ThreeArgumentsCosting::linear_in_z(0, 1), + ThreeArgumentsCosting::exp_mod_cost(607153, 231697, 53144), + ), + + drop_list: TwoArgumentsCosting::new( + TwoArgumentsCosting::constant_cost(cost_map["drop_list-mem-arguments"]), + TwoArgumentsCosting::linear_in_x( + cost_map["drop_list-cpu-arguments-intercept"], + cost_map["drop_list-cpu-arguments-slope"], + ), + ), + length_of_array: OneArgumentCosting::new( + OneArgumentCosting::constant_cost(cost_map["length_of_array-mem-arguments"]), + OneArgumentCosting::constant_cost(cost_map["length_of_array-cpu-arguments"]), + ), + list_to_array: TwoArgumentsCosting::new( + TwoArgumentsCosting::linear_in_x( + cost_map["list_to_array-mem-arguments-intercept"], + cost_map["list_to_array-mem-arguments-slope"], + ), + TwoArgumentsCosting::linear_in_x( + cost_map["list_to_array-cpu-arguments-intercept"], + cost_map["list_to_array-cpu-arguments-slope"], + ), + ), + index_array: TwoArgumentsCosting::new( + TwoArgumentsCosting::constant_cost(cost_map["index_array-mem-arguments"]), + TwoArgumentsCosting::constant_cost(cost_map["index_array-cpu-arguments"]), + ), } } @@ -912,6 +982,30 @@ impl BuiltinCostModel for BuiltinCostsV2 { self.serialise_data.mem.cost([args[0]]), self.serialise_data.cpu.cost([args[0]]), )), + DefaultFunction::Ripemd_160 => Some(ExBudget::new( + self.ripemd_160.mem.cost([args[0]]), + self.ripemd_160.cpu.cost([args[0]]), + )), + DefaultFunction::ExpModInteger => Some(ExBudget::new( + self.exp_mod_integer.mem.cost([args[0], args[1], args[2]]), + self.exp_mod_integer.cpu.cost([args[0], args[1], args[2]]), + )), + DefaultFunction::DropList => Some(ExBudget::new( + self.drop_list.mem.cost([args[0], args[1]]), + self.drop_list.cpu.cost([args[0], args[1]]), + )), + DefaultFunction::LengthOfArray => Some(ExBudget::new( + self.length_of_array.mem.cost([args[0]]), + self.length_of_array.cpu.cost([args[0]]), + )), + DefaultFunction::ListToArray => Some(ExBudget::new( + self.list_to_array.mem.cost([args[0], args[1]]), + self.list_to_array.cpu.cost([args[0], args[1]]), + )), + DefaultFunction::IndexArray => Some(ExBudget::new( + self.index_array.mem.cost([args[0], args[1]]), + self.index_array.cpu.cost([args[0], args[1]]), + )), _ => None, } } diff --git a/crates/uplc/src/machine/cost_model/builtin_costs/builtin_costs_v3.rs b/crates/uplc/src/machine/cost_model/builtin_costs/builtin_costs_v3.rs index 29eb8358..b8a366a0 100644 --- a/crates/uplc/src/machine/cost_model/builtin_costs/builtin_costs_v3.rs +++ b/crates/uplc/src/machine/cost_model/builtin_costs/builtin_costs_v3.rs @@ -1066,7 +1066,7 @@ impl BuiltinCostModel for BuiltinCostsV3 { ), blake2b_224: OneArgumentCosting::new( - OneArgumentCosting::constant_cost(cost_map["blake2b_224-mem-arguments-slope"]), + OneArgumentCosting::constant_cost(cost_map["blake2b_224-mem-arguments"]), OneArgumentCosting::linear_cost( cost_map["blake2b_224-cpu-arguments-intercept"], cost_map["blake2b_224-cpu-arguments-slope"], diff --git a/crates/uplc/src/machine/cost_model/builtin_costs/mod.rs b/crates/uplc/src/machine/cost_model/builtin_costs/mod.rs index 55dc97e4..010aa35b 100644 --- a/crates/uplc/src/machine/cost_model/builtin_costs/mod.rs +++ b/crates/uplc/src/machine/cost_model/builtin_costs/mod.rs @@ -14,12 +14,15 @@ pub trait BuiltinCostModel { #[cfg(test)] mod tests { - use crate::machine::{ - cost_model::builtin_costs::{ - builtin_costs_v1::BuiltinCostsV1, builtin_costs_v2::BuiltinCostsV2, - builtin_costs_v3::BuiltinCostsV3, + use crate::{ + builtin::DefaultFunction, + machine::{ + cost_model::builtin_costs::{ + builtin_costs_v1::BuiltinCostsV1, builtin_costs_v2::BuiltinCostsV2, + builtin_costs_v3::BuiltinCostsV3, + }, + ExBudget, PlutusVersion, }, - PlutusVersion, }; use super::*; @@ -27,7 +30,10 @@ mod tests { #[test] fn assert_default_cost_model_v1() { - let costs = vec![ + // 166 V1 base values, then 110 zeros for extension positions 166-275 (not read by V1 + // initialize), then ripemd_160 at 276-278, zeros for exp_mod_integer at 279-283 + // (hardcoded), and drop_list/length_of_array/list_to_array/index_array at 284-294. + let mut costs: Vec = vec![ 100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, 8356, 4, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 100, 100, 16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, 178, 0, 1, 22151, 32, 91189, 769, @@ -40,8 +46,17 @@ mod tests { 1, 1, 81663, 32, 59498, 32, 20142, 32, 24588, 32, 20744, 32, 25933, 32, 24623, 32, 53384111, 14333, 10, ]; + assert_eq!(costs.len(), 166); + costs.extend(vec![0i64; 110]); // positions 166-275: extension keys not read by V1 + costs.extend([1964219, 24520, 3]); // 276-278: ripemd_160 cpu-intercept, cpu-slope, memory + costs.extend(vec![0i64; 5]); // 279-283: exp_mod_integer (hardcoded in initialize) + costs.extend([116711, 1957, 4]); // 284-286: drop_list cpu-intercept, cpu-slope, mem + costs.extend([198994, 10]); // 287-288: length_of_array cpu, mem + costs.extend([307802, 8496, 7, 1]); // 289-292: list_to_array cpu-intercept, cpu-slope, mem-intercept, mem-slope + costs.extend([194922, 32]); // 293-294: index_array cpu, mem + assert_eq!(costs.len(), 295); - let cost_model = CostMap::new(&PlutusVersion::V1, &costs); + let cost_model = CostMap::new(&PlutusVersion::V1, (11, 0), &costs); assert_eq!( BuiltinCostsV1::default(), @@ -51,7 +66,10 @@ mod tests { #[test] fn assert_default_cost_model_v2() { - let costs = vec![ + // 175 V2 base values, then 101 zeros for extension positions 175-275 (not read by V2 + // initialize), then ripemd_160 at 276-278, zeros for exp_mod_integer at 279-283 + // (hardcoded), and drop_list/length_of_array/list_to_array/index_array at 284-294. + let mut costs: Vec = vec![ 100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, 8356, 4, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 100, 100, 16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, 178, 0, 1, 22151, 32, 91189, 769, @@ -64,8 +82,17 @@ mod tests { 141992, 32, 100788, 420, 1, 1, 81663, 32, 59498, 32, 20142, 32, 24588, 32, 20744, 32, 25933, 32, 24623, 32, 43053543, 10, 53384111, 14333, 10, 43574283, 26308, 10, ]; + assert_eq!(costs.len(), 175); + costs.extend(vec![0i64; 101]); // positions 175-275: extension keys not read by V2 + costs.extend([1964219, 24520, 3]); // 276-278: ripemd_160 cpu-intercept, cpu-slope, memory + costs.extend(vec![0i64; 5]); // 279-283: exp_mod_integer (hardcoded in initialize) + costs.extend([116711, 1957, 4]); // 284-286: drop_list cpu-intercept, cpu-slope, mem + costs.extend([198994, 10]); // 287-288: length_of_array cpu, mem + costs.extend([307802, 8496, 7, 1]); // 289-292: list_to_array cpu-intercept, cpu-slope, mem-intercept, mem-slope + costs.extend([194922, 32]); // 293-294: index_array cpu, mem + assert_eq!(costs.len(), 295); - let cost_model = CostMap::new(&PlutusVersion::V2, &costs); + let cost_model = CostMap::new(&PlutusVersion::V2, (11, 0), &costs); assert_eq!( BuiltinCostsV2::default(), @@ -98,11 +125,130 @@ mod tests { 1964219, 24520, 3, ]; - let cost_model = CostMap::new(&PlutusVersion::V3, &costs); + let cost_model = CostMap::new(&PlutusVersion::V3, (10, 0), &costs); assert_eq!( BuiltinCostsV3::default(), BuiltinCostsV3::initialize(&cost_model) ); } + + // Pre-Plomin V1 (166 entries): ripemd_160 keys absent from the map, so initialize() + // falls back to the sentinel value (30_000_000_000) for all three ripemd_160 params. + #[test] + fn ripemd_160_cost_pre_plomin_v1() { + let costs = vec![ + 100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, 8356, 4, + 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 100, 100, + 16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, 178, 0, 1, 22151, 32, 91189, 769, + 4, 2, 85848, 228465, 122, 0, 1, 1, 1000, 42921, 4, 2, 24548, 29498, 38, 1, 898148, + 27279, 1, 51775, 558, 1, 39184, 1000, 60594, 1, 141895, 32, 83150, 32, 15299, 32, + 76049, 1, 13169, 4, 22100, 10, 28999, 74, 1, 28999, 74, 1, 43285, 552, 1, 44749, 541, + 1, 33852, 32, 68246, 32, 72362, 32, 7243, 32, 7391, 32, 11546, 32, 85848, 228465, 122, + 0, 1, 1, 90434, 519, 0, 1, 74433, 32, 85848, 228465, 122, 0, 1, 1, 85848, 228465, 122, + 0, 1, 1, 270652, 22588, 4, 1457325, 64566, 4, 20467, 1, 4, 0, 141992, 32, 100788, 420, + 1, 1, 81663, 32, 59498, 32, 20142, 32, 24588, 32, 20744, 32, 25933, 32, 24623, 32, + 53384111, 14333, 10, + ]; + assert_eq!(costs.len(), 166); + + let cost_model = CostMap::new(&PlutusVersion::V1, (9, 0), &costs); + let builtin_costs = BuiltinCostsV1::initialize(&cost_model); + + const SENTINEL: i64 = 30_000_000_000; + let budget = builtin_costs + .get_cost(DefaultFunction::Ripemd_160, &[32]) + .unwrap(); + // mem = constant_cost(SENTINEL), cpu = SENTINEL + SENTINEL * 32 + assert_eq!(budget, ExBudget::new(SENTINEL, SENTINEL + SENTINEL * 32)); + } + + // Post-Plomin V1 (279 entries): ripemd_160 at positions 276-278; costs parsed correctly. + #[test] + fn ripemd_160_cost_post_plomin_v1() { + let mut costs: Vec = vec![ + 100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, 8356, 4, + 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 100, 100, + 16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, 178, 0, 1, 22151, 32, 91189, 769, + 4, 2, 85848, 228465, 122, 0, 1, 1, 1000, 42921, 4, 2, 24548, 29498, 38, 1, 898148, + 27279, 1, 51775, 558, 1, 39184, 1000, 60594, 1, 141895, 32, 83150, 32, 15299, 32, + 76049, 1, 13169, 4, 22100, 10, 28999, 74, 1, 28999, 74, 1, 43285, 552, 1, 44749, 541, + 1, 33852, 32, 68246, 32, 72362, 32, 7243, 32, 7391, 32, 11546, 32, 85848, 228465, 122, + 0, 1, 1, 90434, 519, 0, 1, 74433, 32, 85848, 228465, 122, 0, 1, 1, 85848, 228465, 122, + 0, 1, 1, 270652, 22588, 4, 1457325, 64566, 4, 20467, 1, 4, 0, 141992, 32, 100788, 420, + 1, 1, 81663, 32, 59498, 32, 20142, 32, 24588, 32, 20744, 32, 25933, 32, 24623, 32, + 53384111, 14333, 10, + ]; + assert_eq!(costs.len(), 166); + costs.extend(vec![0i64; 110]); // positions 166-275 + costs.extend([1964219, 24520, 3]); // 276-278: ripemd_160 + assert_eq!(costs.len(), 279); + + let cost_model = CostMap::new(&PlutusVersion::V1, (10, 0), &costs); + let builtin_costs = BuiltinCostsV1::initialize(&cost_model); + + let budget = builtin_costs + .get_cost(DefaultFunction::Ripemd_160, &[32]) + .unwrap(); + // mem = constant_cost(3), cpu = 1964219 + 24520 * 32 + assert_eq!(budget, ExBudget::new(3, 1964219 + 24520 * 32)); + } + + // Pre-Plomin V2 (175 entries): ripemd_160 keys absent; falls back to sentinel. + #[test] + fn ripemd_160_cost_pre_plomin_v2() { + let costs = vec![ + 100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, 8356, 4, + 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 100, 100, + 16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, 178, 0, 1, 22151, 32, 91189, 769, + 4, 2, 85848, 228465, 122, 0, 1, 1, 1000, 42921, 4, 2, 24548, 29498, 38, 1, 898148, + 27279, 1, 51775, 558, 1, 39184, 1000, 60594, 1, 141895, 32, 83150, 32, 15299, 32, + 76049, 1, 13169, 4, 22100, 10, 28999, 74, 1, 28999, 74, 1, 43285, 552, 1, 44749, 541, + 1, 33852, 32, 68246, 32, 72362, 32, 7243, 32, 7391, 32, 11546, 32, 85848, 228465, 122, + 0, 1, 1, 90434, 519, 0, 1, 74433, 32, 85848, 228465, 122, 0, 1, 1, 85848, 228465, 122, + 0, 1, 1, 955506, 213312, 0, 2, 270652, 22588, 4, 1457325, 64566, 4, 20467, 1, 4, 0, + 141992, 32, 100788, 420, 1, 1, 81663, 32, 59498, 32, 20142, 32, 24588, 32, 20744, 32, + 25933, 32, 24623, 32, 43053543, 10, 53384111, 14333, 10, 43574283, 26308, 10, + ]; + assert_eq!(costs.len(), 175); + + let cost_model = CostMap::new(&PlutusVersion::V2, (9, 0), &costs); + let builtin_costs = BuiltinCostsV2::initialize(&cost_model); + + const SENTINEL: i64 = 30_000_000_000; + let budget = builtin_costs + .get_cost(DefaultFunction::Ripemd_160, &[32]) + .unwrap(); + assert_eq!(budget, ExBudget::new(SENTINEL, SENTINEL + SENTINEL * 32)); + } + + // Post-Plomin V2 (279 entries): ripemd_160 at positions 276-278; costs parsed correctly. + #[test] + fn ripemd_160_cost_post_plomin_v2() { + let mut costs: Vec = vec![ + 100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, 8356, 4, + 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 100, 100, + 16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, 178, 0, 1, 22151, 32, 91189, 769, + 4, 2, 85848, 228465, 122, 0, 1, 1, 1000, 42921, 4, 2, 24548, 29498, 38, 1, 898148, + 27279, 1, 51775, 558, 1, 39184, 1000, 60594, 1, 141895, 32, 83150, 32, 15299, 32, + 76049, 1, 13169, 4, 22100, 10, 28999, 74, 1, 28999, 74, 1, 43285, 552, 1, 44749, 541, + 1, 33852, 32, 68246, 32, 72362, 32, 7243, 32, 7391, 32, 11546, 32, 85848, 228465, 122, + 0, 1, 1, 90434, 519, 0, 1, 74433, 32, 85848, 228465, 122, 0, 1, 1, 85848, 228465, 122, + 0, 1, 1, 955506, 213312, 0, 2, 270652, 22588, 4, 1457325, 64566, 4, 20467, 1, 4, 0, + 141992, 32, 100788, 420, 1, 1, 81663, 32, 59498, 32, 20142, 32, 24588, 32, 20744, 32, + 25933, 32, 24623, 32, 43053543, 10, 53384111, 14333, 10, 43574283, 26308, 10, + ]; + assert_eq!(costs.len(), 175); + costs.extend(vec![0i64; 101]); // positions 175-275 + costs.extend([1964219, 24520, 3]); // 276-278: ripemd_160 + assert_eq!(costs.len(), 279); + + let cost_model = CostMap::new(&PlutusVersion::V2, (10, 0), &costs); + let builtin_costs = BuiltinCostsV2::initialize(&cost_model); + + let budget = builtin_costs + .get_cost(DefaultFunction::Ripemd_160, &[32]) + .unwrap(); + assert_eq!(budget, ExBudget::new(3, 1964219 + 24520 * 32)); + } } diff --git a/crates/uplc/src/machine/cost_model/cost_map.rs b/crates/uplc/src/machine/cost_model/cost_map.rs index 96a8e651..7a9d009c 100644 --- a/crates/uplc/src/machine/cost_model/cost_map.rs +++ b/crates/uplc/src/machine/cost_model/cost_map.rs @@ -7,356 +7,599 @@ pub struct CostMap { map: HashMap, } +// Shared extension sections used by all three versions. +const PLOMIN_KEYS: [&str; 46] = [ + "andByteString-cpu-arguments-intercept", + "andByteString-cpu-arguments-slope1", + "andByteString-cpu-arguments-slope2", + "andByteString-memory-arguments-intercept", + "andByteString-memory-arguments-slope", + "orByteString-cpu-arguments-intercept", + "orByteString-cpu-arguments-slope1", + "orByteString-cpu-arguments-slope2", + "orByteString-memory-arguments-intercept", + "orByteString-memory-arguments-slope", + "xorByteString-cpu-arguments-intercept", + "xorByteString-cpu-arguments-slope1", + "xorByteString-cpu-arguments-slope2", + "xorByteString-memory-arguments-intercept", + "xorByteString-memory-arguments-slope", + "complementByteString-cpu-arguments-intercept", + "complementByteString-cpu-arguments-slope", + "complementByteString-memory-arguments-intercept", + "complementByteString-memory-arguments-slope", + "readBit-cpu-arguments", + "readBit-memory-arguments", + "writeBits-cpu-arguments-intercept", + "writeBits-cpu-arguments-slope", + "writeBits-memory-arguments-intercept", + "writeBits-memory-arguments-slope", + "replicateByte-cpu-arguments-intercept", + "replicateByte-cpu-arguments-slope", + "replicateByte-memory-arguments-intercept", + "replicateByte-memory-arguments-slope", + "shiftByteString-cpu-arguments-intercept", + "shiftByteString-cpu-arguments-slope", + "shiftByteString-memory-arguments-intercept", + "shiftByteString-memory-arguments-slope", + "rotateByteString-cpu-arguments-intercept", + "rotateByteString-cpu-arguments-slope", + "rotateByteString-memory-arguments-intercept", + "rotateByteString-memory-arguments-slope", + "countSetBits-cpu-arguments-intercept", + "countSetBits-cpu-arguments-slope", + "countSetBits-memory-arguments", + "findFirstSetBit-cpu-arguments-intercept", + "findFirstSetBit-cpu-arguments-slope", + "findFirstSetBit-memory-arguments", + "ripemd_160-cpu-arguments-intercept", + "ripemd_160-cpu-arguments-slope", + "ripemd_160-memory-arguments", +]; + +const PV11_KEYS: [&str; 53] = [ + "exp_mod_integer-cpu-arguments-coefficient00", + "exp_mod_integer-cpu-arguments-coefficient11", + "exp_mod_integer-cpu-arguments-coefficient12", + "exp_mod_integer-mem-arguments-intercept", + "exp_mod_integer-mem-arguments-slope", + "drop_list-cpu-arguments-intercept", + "drop_list-cpu-arguments-slope", + "drop_list-mem-arguments", + "length_of_array-cpu-arguments", + "length_of_array-mem-arguments", + "list_to_array-cpu-arguments-intercept", + "list_to_array-cpu-arguments-slope", + "list_to_array-mem-arguments-intercept", + "list_to_array-mem-arguments-slope", + "index_array-cpu-arguments", + "index_array-mem-arguments", + "bls12_381_G1_multiScalarMul-cpu-arguments-intercept", + "bls12_381_G1_multiScalarMul-cpu-arguments-slope", + "bls12_381_G1_multiScalarMul-mem-arguments", + "bls12_381_G2_multiScalarMul-cpu-arguments-intercept", + "bls12_381_G2_multiScalarMul-cpu-arguments-slope", + "bls12_381_G2_multiScalarMul-mem-arguments", + "insert_coin-cpu-arguments-intercept", + "insert_coin-cpu-arguments-slope", + "insert_coin-mem-arguments-intercept", + "insert_coin-mem-arguments-slope", + "lookup_coin-cpu-arguments-intercept", + "lookup_coin-cpu-arguments-slope", + "lookup_coin-mem-arguments", + "union_value-cpu-arguments-c00", + "union_value-cpu-arguments-c10", + "union_value-cpu-arguments-c01", + "union_value-cpu-arguments-c11", + "union_value-mem-arguments-intercept", + "union_value-mem-arguments-slope", + "value_contains-cpu-arguments-constant", + "value_contains-cpu-arguments-model-arguments-intercept", + "value_contains-cpu-arguments-model-arguments-slope1", + "value_contains-cpu-arguments-model-arguments-slope2", + "value_contains-mem-arguments", + "value_data-cpu-arguments-intercept", + "value_data-cpu-arguments-slope", + "value_data-mem-arguments-intercept", + "value_data-mem-arguments-slope", + "un_value_data-cpu-arguments-c0", + "un_value_data-cpu-arguments-c1", + "un_value_data-cpu-arguments-c2", + "un_value_data-mem-arguments-intercept", + "un_value_data-mem-arguments-slope", + "scale_value-cpu-arguments-intercept", + "scale_value-cpu-arguments-slope", + "scale_value-mem-arguments-intercept", + "scale_value-mem-arguments-slope", +]; + +// BLS12 G1 and G2 sections shared across versions +const BLS12_G1_KEYS: [&str; 16] = [ + "bls12_381_G1_add-cpu-arguments", + "bls12_381_G1_add-mem-arguments", + "bls12_381_G1_compress-cpu-arguments", + "bls12_381_G1_compress-mem-arguments", + "bls12_381_G1_equal-cpu-arguments", + "bls12_381_G1_equal-mem-arguments", + "bls12_381_G1_hashToGroup-cpu-arguments-intercept", + "bls12_381_G1_hashToGroup-cpu-arguments-slope", + "bls12_381_G1_hashToGroup-mem-arguments", + "bls12_381_G1_neg-cpu-arguments", + "bls12_381_G1_neg-mem-arguments", + "bls12_381_G1_scalarMul-cpu-arguments-intercept", + "bls12_381_G1_scalarMul-cpu-arguments-slope", + "bls12_381_G1_scalarMul-mem-arguments", + "bls12_381_G1_uncompress-cpu-arguments", + "bls12_381_G1_uncompress-mem-arguments", +]; + +const BLS12_G2_KEYS: [&str; 16] = [ + "bls12_381_G2_add-cpu-arguments", + "bls12_381_G2_add-mem-arguments", + "bls12_381_G2_compress-cpu-arguments", + "bls12_381_G2_compress-mem-arguments", + "bls12_381_G2_equal-cpu-arguments", + "bls12_381_G2_equal-mem-arguments", + "bls12_381_G2_hashToGroup-cpu-arguments-intercept", + "bls12_381_G2_hashToGroup-cpu-arguments-slope", + "bls12_381_G2_hashToGroup-mem-arguments", + "bls12_381_G2_neg-cpu-arguments", + "bls12_381_G2_neg-mem-arguments", + "bls12_381_G2_scalarMul-cpu-arguments-intercept", + "bls12_381_G2_scalarMul-cpu-arguments-slope", + "bls12_381_G2_scalarMul-mem-arguments", + "bls12_381_G2_uncompress-cpu-arguments", + "bls12_381_G2_uncompress-mem-arguments", +]; + impl CostMap { - pub fn new(version: &PlutusVersion, values: &[i64]) -> Self { - let keys = match version { - PlutusVersion::V1 => vec![ - "add_integer-cpu-arguments-intercept", - "add_integer-cpu-arguments-slope", - "add_integer-mem-arguments-intercept", - "add_integer-mem-arguments-slope", - "append_byte_string-cpu-arguments-intercept", - "append_byte_string-cpu-arguments-slope", - "append_byte_string-mem-arguments-intercept", - "append_byte_string-mem-arguments-slope", - "append_string-cpu-arguments-intercept", - "append_string-cpu-arguments-slope", - "append_string-mem-arguments-intercept", - "append_string-mem-arguments-slope", - "b_data-cpu-arguments", - "b_data-mem-arguments", - "blake2b_256-cpu-arguments-intercept", - "blake2b_256-cpu-arguments-slope", - "blake2b_256-mem-arguments", - "cek_apply_cost-exBudgetCPU", - "cek_apply_cost-exBudgetmem", - "cek_builtin_cost-exBudgetCPU", - "cek_builtin_cost-exBudgetmem", - "cek_const_cost-exBudgetCPU", - "cek_const_cost-exBudgetmem", - "cek_delay_cost-exBudgetCPU", - "cek_delay_cost-exBudgetmem", - "cek_force_cost-exBudgetCPU", - "cek_force_cost-exBudgetmem", - "cek_lam_cost-exBudgetCPU", - "cek_lam_cost-exBudgetmem", - "cek_startup_cost-exBudgetCPU", - "cek_startup_cost-exBudgetmem", - "cek_var_cost-exBudgetCPU", - "cek_var_cost-exBudgetmem", - "choose_data-cpu-arguments", - "choose_data-mem-arguments", - "choose_list-cpu-arguments", - "choose_list-mem-arguments", - "choose_unit-cpu-arguments", - "choose_unit-mem-arguments", - "cons_byte_string-cpu-arguments-intercept", - "cons_byte_string-cpu-arguments-slope", - "cons_byte_string-mem-arguments-intercept", - "cons_byte_string-mem-arguments-slope", - "constr_data-cpu-arguments", - "constr_data-mem-arguments", - "decode_utf8-cpu-arguments-intercept", - "decode_utf8-cpu-arguments-slope", - "decode_utf8-mem-arguments-intercept", - "decode_utf8-mem-arguments-slope", - "divide_integer-cpu-arguments-constant", - "divide_integer-cpu-arguments-model-arguments-intercept", - "divide_integer-cpu-arguments-model-arguments-slope", - "divide_integer-mem-arguments-intercept", - "divide_integer-mem-arguments-minimum", - "divide_integer-mem-arguments-slope", - "encode_utf8-cpu-arguments-intercept", - "encode_utf8-cpu-arguments-slope", - "encode_utf8-mem-arguments-intercept", - "encode_utf8-mem-arguments-slope", - "equals_byte_string-cpu-arguments-constant", - "equals_byte_string-cpu-arguments-intercept", - "equals_byte_string-cpu-arguments-slope", - "equals_byte_string-mem-arguments", - "equals_data-cpu-arguments-intercept", - "equals_data-cpu-arguments-slope", - "equals_data-mem-arguments", - "equals_integer-cpu-arguments-intercept", - "equals_integer-cpu-arguments-slope", - "equals_integer-mem-arguments", - "equals_string-cpu-arguments-constant", - "equals_string-cpu-arguments-intercept", - "equals_string-cpu-arguments-slope", - "equals_string-mem-arguments", - "fst_pair-cpu-arguments", - "fst_pair-mem-arguments", - "head_list-cpu-arguments", - "head_list-mem-arguments", - "i_data-cpu-arguments", - "i_data-mem-arguments", - "if_then_else-cpu-arguments", - "if_then_else-mem-arguments", - "index_byte_string-cpu-arguments", - "index_byte_string-mem-arguments", - "length_of_byte_string-cpu-arguments", - "length_of_byte_string-mem-arguments", - "less_than_byte_string-cpu-arguments-intercept", - "less_than_byte_string-cpu-arguments-slope", - "less_than_byte_string-mem-arguments", - "less_than_equals_byte_string-cpu-arguments-intercept", - "less_than_equals_byte_string-cpu-arguments-slope", - "less_than_equals_byte_string-mem-arguments", - "less_than_equals_integer-cpu-arguments-intercept", - "less_than_equals_integer-cpu-arguments-slope", - "less_than_equals_integer-mem-arguments", - "less_than_integer-cpu-arguments-intercept", - "less_than_integer-cpu-arguments-slope", - "less_than_integer-mem-arguments", - "list_data-cpu-arguments", - "list_data-mem-arguments", - "map_data-cpu-arguments", - "map_data-mem-arguments", - "mk_cons-cpu-arguments", - "mk_cons-mem-arguments", - "mk_nil_data-cpu-arguments", - "mk_nil_data-mem-arguments", - "mk_nil_pair_data-cpu-arguments", - "mk_nil_pair_data-mem-arguments", - "mk_pair_data-cpu-arguments", - "mk_pair_data-mem-arguments", - "mod_integer-cpu-arguments-constant", - "mod_integer-cpu-arguments-model-arguments-intercept", - "mod_integer-cpu-arguments-model-arguments-slope", - "mod_integer-mem-arguments-intercept", - "mod_integer-mem-arguments-minimum", - "mod_integer-mem-arguments-slope", - "multiply_integer-cpu-arguments-intercept", - "multiply_integer-cpu-arguments-slope", - "multiply_integer-mem-arguments-intercept", - "multiply_integer-mem-arguments-slope", - "null_list-cpu-arguments", - "null_list-mem-arguments", - "quotient_integer-cpu-arguments-constant", - "quotient_integer-cpu-arguments-model-arguments-intercept", - "quotient_integer-cpu-arguments-model-arguments-slope", - "quotient_integer-mem-arguments-intercept", - "quotient_integer-mem-arguments-minimum", - "quotient_integer-mem-arguments-slope", - "remainder_integer-cpu-arguments-constant", - "remainder_integer-cpu-arguments-model-arguments-intercept", - "remainder_integer-cpu-arguments-model-arguments-slope", - "remainder_integer-mem-arguments-intercept", - "remainder_integer-mem-arguments-minimum", - "remainder_integer-mem-arguments-slope", - "sha2_256-cpu-arguments-intercept", - "sha2_256-cpu-arguments-slope", - "sha2_256-mem-arguments", - "sha3_256-cpu-arguments-intercept", - "sha3_256-cpu-arguments-slope", - "sha3_256-mem-arguments", - "slice_byte_string-cpu-arguments-intercept", - "slice_byte_string-cpu-arguments-slope", - "slice_byte_string-mem-arguments-intercept", - "slice_byte_string-mem-arguments-slope", - "snd_pair-cpu-arguments", - "snd_pair-mem-arguments", - "subtract_integer-cpu-arguments-intercept", - "subtract_integer-cpu-arguments-slope", - "subtract_integer-mem-arguments-intercept", - "subtract_integer-mem-arguments-slope", - "tail_list-cpu-arguments", - "tail_list-mem-arguments", - "trace-cpu-arguments", - "trace-mem-arguments", - "un_b_data-cpu-arguments", - "un_b_data-mem-arguments", - "un_constr_data-cpu-arguments", - "un_constr_data-mem-arguments", - "un_i_data-cpu-arguments", - "un_i_data-mem-arguments", - "un_list_data-cpu-arguments", - "un_list_data-mem-arguments", - "un_map_data-cpu-arguments", - "un_map_data-mem-arguments", - "verify_ed25519_signature-cpu-arguments-intercept", - "verify_ed25519_signature-cpu-arguments-slope", - "verify_ed25519_signature-mem-arguments", - ], - PlutusVersion::V2 => vec![ - "add_integer-cpu-arguments-intercept", - "add_integer-cpu-arguments-slope", - "add_integer-mem-arguments-intercept", - "add_integer-mem-arguments-slope", - "append_byte_string-cpu-arguments-intercept", - "append_byte_string-cpu-arguments-slope", - "append_byte_string-mem-arguments-intercept", - "append_byte_string-mem-arguments-slope", - "append_string-cpu-arguments-intercept", - "append_string-cpu-arguments-slope", - "append_string-mem-arguments-intercept", - "append_string-mem-arguments-slope", - "b_data-cpu-arguments", - "b_data-mem-arguments", - "blake2b_256-cpu-arguments-intercept", - "blake2b_256-cpu-arguments-slope", - "blake2b_256-mem-arguments", - "cek_apply_cost-exBudgetCPU", - "cek_apply_cost-exBudgetmem", - "cek_builtin_cost-exBudgetCPU", - "cek_builtin_cost-exBudgetmem", - "cek_const_cost-exBudgetCPU", - "cek_const_cost-exBudgetmem", - "cek_delay_cost-exBudgetCPU", - "cek_delay_cost-exBudgetmem", - "cek_force_cost-exBudgetCPU", - "cek_force_cost-exBudgetmem", - "cek_lam_cost-exBudgetCPU", - "cek_lam_cost-exBudgetmem", - "cek_startup_cost-exBudgetCPU", - "cek_startup_cost-exBudgetmem", - "cek_var_cost-exBudgetCPU", - "cek_var_cost-exBudgetmem", - "choose_data-cpu-arguments", - "choose_data-mem-arguments", - "choose_list-cpu-arguments", - "choose_list-mem-arguments", - "choose_unit-cpu-arguments", - "choose_unit-mem-arguments", - "cons_byte_string-cpu-arguments-intercept", - "cons_byte_string-cpu-arguments-slope", - "cons_byte_string-mem-arguments-intercept", - "cons_byte_string-mem-arguments-slope", - "constr_data-cpu-arguments", - "constr_data-mem-arguments", - "decode_utf8-cpu-arguments-intercept", - "decode_utf8-cpu-arguments-slope", - "decode_utf8-mem-arguments-intercept", - "decode_utf8-mem-arguments-slope", - "divide_integer-cpu-arguments-constant", - "divide_integer-cpu-arguments-model-arguments-intercept", - "divide_integer-cpu-arguments-model-arguments-slope", - "divide_integer-mem-arguments-intercept", - "divide_integer-mem-arguments-minimum", - "divide_integer-mem-arguments-slope", - "encode_utf8-cpu-arguments-intercept", - "encode_utf8-cpu-arguments-slope", - "encode_utf8-mem-arguments-intercept", - "encode_utf8-mem-arguments-slope", - "equals_byte_string-cpu-arguments-constant", - "equals_byte_string-cpu-arguments-intercept", - "equals_byte_string-cpu-arguments-slope", - "equals_byte_string-mem-arguments", - "equals_data-cpu-arguments-intercept", - "equals_data-cpu-arguments-slope", - "equals_data-mem-arguments", - "equals_integer-cpu-arguments-intercept", - "equals_integer-cpu-arguments-slope", - "equals_integer-mem-arguments", - "equals_string-cpu-arguments-constant", - "equals_string-cpu-arguments-intercept", - "equals_string-cpu-arguments-slope", - "equals_string-mem-arguments", - "fst_pair-cpu-arguments", - "fst_pair-mem-arguments", - "head_list-cpu-arguments", - "head_list-mem-arguments", - "i_data-cpu-arguments", - "i_data-mem-arguments", - "if_then_else-cpu-arguments", - "if_then_else-mem-arguments", - "index_byte_string-cpu-arguments", - "index_byte_string-mem-arguments", - "length_of_byte_string-cpu-arguments", - "length_of_byte_string-mem-arguments", - "less_than_byte_string-cpu-arguments-intercept", - "less_than_byte_string-cpu-arguments-slope", - "less_than_byte_string-mem-arguments", - "less_than_equals_byte_string-cpu-arguments-intercept", - "less_than_equals_byte_string-cpu-arguments-slope", - "less_than_equals_byte_string-mem-arguments", - "less_than_equals_integer-cpu-arguments-intercept", - "less_than_equals_integer-cpu-arguments-slope", - "less_than_equals_integer-mem-arguments", - "less_than_integer-cpu-arguments-intercept", - "less_than_integer-cpu-arguments-slope", - "less_than_integer-mem-arguments", - "list_data-cpu-arguments", - "list_data-mem-arguments", - "map_data-cpu-arguments", - "map_data-mem-arguments", - "mk_cons-cpu-arguments", - "mk_cons-mem-arguments", - "mk_nil_data-cpu-arguments", - "mk_nil_data-mem-arguments", - "mk_nil_pair_data-cpu-arguments", - "mk_nil_pair_data-mem-arguments", - "mk_pair_data-cpu-arguments", - "mk_pair_data-mem-arguments", - "mod_integer-cpu-arguments-constant", - "mod_integer-cpu-arguments-model-arguments-intercept", - "mod_integer-cpu-arguments-model-arguments-slope", - "mod_integer-mem-arguments-intercept", - "mod_integer-mem-arguments-minimum", - "mod_integer-mem-arguments-slope", - "multiply_integer-cpu-arguments-intercept", - "multiply_integer-cpu-arguments-slope", - "multiply_integer-mem-arguments-intercept", - "multiply_integer-mem-arguments-slope", - "null_list-cpu-arguments", - "null_list-mem-arguments", - "quotient_integer-cpu-arguments-constant", - "quotient_integer-cpu-arguments-model-arguments-intercept", - "quotient_integer-cpu-arguments-model-arguments-slope", - "quotient_integer-mem-arguments-intercept", - "quotient_integer-mem-arguments-minimum", - "quotient_integer-mem-arguments-slope", - "remainder_integer-cpu-arguments-constant", - "remainder_integer-cpu-arguments-model-arguments-intercept", - "remainder_integer-cpu-arguments-model-arguments-slope", - "remainder_integer-mem-arguments-intercept", - "remainder_integer-mem-arguments-minimum", - "remainder_integer-mem-arguments-slope", - "serialise_data-cpu-arguments-intercept", - "serialise_data-cpu-arguments-slope", - "serialise_data-mem-arguments-intercept", - "serialise_data-mem-arguments-slope", - "sha2_256-cpu-arguments-intercept", - "sha2_256-cpu-arguments-slope", - "sha2_256-mem-arguments", - "sha3_256-cpu-arguments-intercept", - "sha3_256-cpu-arguments-slope", - "sha3_256-mem-arguments", - "slice_byte_string-cpu-arguments-intercept", - "slice_byte_string-cpu-arguments-slope", - "slice_byte_string-mem-arguments-intercept", - "slice_byte_string-mem-arguments-slope", - "snd_pair-cpu-arguments", - "snd_pair-mem-arguments", - "subtract_integer-cpu-arguments-intercept", - "subtract_integer-cpu-arguments-slope", - "subtract_integer-mem-arguments-intercept", - "subtract_integer-mem-arguments-slope", - "tail_list-cpu-arguments", - "tail_list-mem-arguments", - "trace-cpu-arguments", - "trace-mem-arguments", - "un_b_data-cpu-arguments", - "un_b_data-mem-arguments", - "un_constr_data-cpu-arguments", - "un_constr_data-mem-arguments", - "un_i_data-cpu-arguments", - "un_i_data-mem-arguments", - "un_list_data-cpu-arguments", - "un_list_data-mem-arguments", - "un_map_data-cpu-arguments", - "un_map_data-mem-arguments", - "verify_ecdsa_secp256k1_signature-cpu-arguments", - "verify_ecdsa_secp256k1_signature-mem-arguments", - "verify_ed25519_signature-cpu-arguments-intercept", - "verify_ed25519_signature-cpu-arguments-slope", - "verify_ed25519_signature-mem-arguments", - "verify_schnorr_secp256k1_signature-cpu-arguments-intercept", - "verify_schnorr_secp256k1_signature-cpu-arguments-slope", - "verify_schnorr_secp256k1_signature-mem-arguments", - ], + pub fn new(version: &PlutusVersion, protocol_version: (u64, u64), values: &[i64]) -> Self { + let keys: Vec<&str> = match version { + PlutusVersion::V1 => { + let mut v: Vec<&str> = vec![ + // 0-165: V1 original builtins + "add_integer-cpu-arguments-intercept", + "add_integer-cpu-arguments-slope", + "add_integer-mem-arguments-intercept", + "add_integer-mem-arguments-slope", + "append_byte_string-cpu-arguments-intercept", + "append_byte_string-cpu-arguments-slope", + "append_byte_string-mem-arguments-intercept", + "append_byte_string-mem-arguments-slope", + "append_string-cpu-arguments-intercept", + "append_string-cpu-arguments-slope", + "append_string-mem-arguments-intercept", + "append_string-mem-arguments-slope", + "b_data-cpu-arguments", + "b_data-mem-arguments", + "blake2b_256-cpu-arguments-intercept", + "blake2b_256-cpu-arguments-slope", + "blake2b_256-mem-arguments", + "cek_apply_cost-exBudgetCPU", + "cek_apply_cost-exBudgetmem", + "cek_builtin_cost-exBudgetCPU", + "cek_builtin_cost-exBudgetmem", + "cek_const_cost-exBudgetCPU", + "cek_const_cost-exBudgetmem", + "cek_delay_cost-exBudgetCPU", + "cek_delay_cost-exBudgetmem", + "cek_force_cost-exBudgetCPU", + "cek_force_cost-exBudgetmem", + "cek_lam_cost-exBudgetCPU", + "cek_lam_cost-exBudgetmem", + "cek_startup_cost-exBudgetCPU", + "cek_startup_cost-exBudgetmem", + "cek_var_cost-exBudgetCPU", + "cek_var_cost-exBudgetmem", + "choose_data-cpu-arguments", + "choose_data-mem-arguments", + "choose_list-cpu-arguments", + "choose_list-mem-arguments", + "choose_unit-cpu-arguments", + "choose_unit-mem-arguments", + "cons_byte_string-cpu-arguments-intercept", + "cons_byte_string-cpu-arguments-slope", + "cons_byte_string-mem-arguments-intercept", + "cons_byte_string-mem-arguments-slope", + "constr_data-cpu-arguments", + "constr_data-mem-arguments", + "decode_utf8-cpu-arguments-intercept", + "decode_utf8-cpu-arguments-slope", + "decode_utf8-mem-arguments-intercept", + "decode_utf8-mem-arguments-slope", + "divide_integer-cpu-arguments-constant", + "divide_integer-cpu-arguments-model-arguments-intercept", + "divide_integer-cpu-arguments-model-arguments-slope", + "divide_integer-mem-arguments-intercept", + "divide_integer-mem-arguments-minimum", + "divide_integer-mem-arguments-slope", + "encode_utf8-cpu-arguments-intercept", + "encode_utf8-cpu-arguments-slope", + "encode_utf8-mem-arguments-intercept", + "encode_utf8-mem-arguments-slope", + "equals_byte_string-cpu-arguments-constant", + "equals_byte_string-cpu-arguments-intercept", + "equals_byte_string-cpu-arguments-slope", + "equals_byte_string-mem-arguments", + "equals_data-cpu-arguments-intercept", + "equals_data-cpu-arguments-slope", + "equals_data-mem-arguments", + "equals_integer-cpu-arguments-intercept", + "equals_integer-cpu-arguments-slope", + "equals_integer-mem-arguments", + "equals_string-cpu-arguments-constant", + "equals_string-cpu-arguments-intercept", + "equals_string-cpu-arguments-slope", + "equals_string-mem-arguments", + "fst_pair-cpu-arguments", + "fst_pair-mem-arguments", + "head_list-cpu-arguments", + "head_list-mem-arguments", + "i_data-cpu-arguments", + "i_data-mem-arguments", + "if_then_else-cpu-arguments", + "if_then_else-mem-arguments", + "index_byte_string-cpu-arguments", + "index_byte_string-mem-arguments", + "length_of_byte_string-cpu-arguments", + "length_of_byte_string-mem-arguments", + "less_than_byte_string-cpu-arguments-intercept", + "less_than_byte_string-cpu-arguments-slope", + "less_than_byte_string-mem-arguments", + "less_than_equals_byte_string-cpu-arguments-intercept", + "less_than_equals_byte_string-cpu-arguments-slope", + "less_than_equals_byte_string-mem-arguments", + "less_than_equals_integer-cpu-arguments-intercept", + "less_than_equals_integer-cpu-arguments-slope", + "less_than_equals_integer-mem-arguments", + "less_than_integer-cpu-arguments-intercept", + "less_than_integer-cpu-arguments-slope", + "less_than_integer-mem-arguments", + "list_data-cpu-arguments", + "list_data-mem-arguments", + "map_data-cpu-arguments", + "map_data-mem-arguments", + "mk_cons-cpu-arguments", + "mk_cons-mem-arguments", + "mk_nil_data-cpu-arguments", + "mk_nil_data-mem-arguments", + "mk_nil_pair_data-cpu-arguments", + "mk_nil_pair_data-mem-arguments", + "mk_pair_data-cpu-arguments", + "mk_pair_data-mem-arguments", + "mod_integer-cpu-arguments-constant", + "mod_integer-cpu-arguments-model-arguments-intercept", + "mod_integer-cpu-arguments-model-arguments-slope", + "mod_integer-mem-arguments-intercept", + "mod_integer-mem-arguments-minimum", + "mod_integer-mem-arguments-slope", + "multiply_integer-cpu-arguments-intercept", + "multiply_integer-cpu-arguments-slope", + "multiply_integer-mem-arguments-intercept", + "multiply_integer-mem-arguments-slope", + "null_list-cpu-arguments", + "null_list-mem-arguments", + "quotient_integer-cpu-arguments-constant", + "quotient_integer-cpu-arguments-model-arguments-intercept", + "quotient_integer-cpu-arguments-model-arguments-slope", + "quotient_integer-mem-arguments-intercept", + "quotient_integer-mem-arguments-minimum", + "quotient_integer-mem-arguments-slope", + "remainder_integer-cpu-arguments-constant", + "remainder_integer-cpu-arguments-model-arguments-intercept", + "remainder_integer-cpu-arguments-model-arguments-slope", + "remainder_integer-mem-arguments-intercept", + "remainder_integer-mem-arguments-minimum", + "remainder_integer-mem-arguments-slope", + "sha2_256-cpu-arguments-intercept", + "sha2_256-cpu-arguments-slope", + "sha2_256-mem-arguments", + "sha3_256-cpu-arguments-intercept", + "sha3_256-cpu-arguments-slope", + "sha3_256-mem-arguments", + "slice_byte_string-cpu-arguments-intercept", + "slice_byte_string-cpu-arguments-slope", + "slice_byte_string-mem-arguments-intercept", + "slice_byte_string-mem-arguments-slope", + "snd_pair-cpu-arguments", + "snd_pair-mem-arguments", + "subtract_integer-cpu-arguments-intercept", + "subtract_integer-cpu-arguments-slope", + "subtract_integer-mem-arguments-intercept", + "subtract_integer-mem-arguments-slope", + "tail_list-cpu-arguments", + "tail_list-mem-arguments", + "trace-cpu-arguments", + "trace-mem-arguments", + "un_b_data-cpu-arguments", + "un_b_data-mem-arguments", + "un_constr_data-cpu-arguments", + "un_constr_data-mem-arguments", + "un_i_data-cpu-arguments", + "un_i_data-mem-arguments", + "un_list_data-cpu-arguments", + "un_list_data-mem-arguments", + "un_map_data-cpu-arguments", + "un_map_data-mem-arguments", + "verify_ed25519_signature-cpu-arguments-intercept", + "verify_ed25519_signature-cpu-arguments-slope", + "verify_ed25519_signature-mem-arguments", + // 166-331: V1 extension (matches Haskell V1 ParamName enum order) + "serialise_data-cpu-arguments-intercept", + "serialise_data-cpu-arguments-slope", + "serialise_data-mem-arguments-intercept", + "serialise_data-mem-arguments-slope", + "verify_ecdsa_secp256k1_signature-cpu-arguments", + "verify_ecdsa_secp256k1_signature-mem-arguments", + "verify_schnorr_secp256k1_signature-cpu-arguments-intercept", + "verify_schnorr_secp256k1_signature-cpu-arguments-slope", + "verify_schnorr_secp256k1_signature-mem-arguments", + "cek_constr_cost-exBudgetCPU", + "cek_constr_cost-exBudgetmem", + "cek_case_cost-exBudgetCPU", + "cek_case_cost-exBudgetmem", + ]; + v.extend(BLS12_G1_KEYS); + v.extend(BLS12_G2_KEYS); + v.extend([ + "bls12_381_finalVerify-cpu-arguments", + "bls12_381_finalVerify-mem-arguments", + "bls12_381_millerLoop-cpu-arguments", + "bls12_381_millerLoop-mem-arguments", + "bls12_381_mulMlResult-cpu-arguments", + "bls12_381_mulMlResult-mem-arguments", + "keccak_256-cpu-arguments-intercept", + "keccak_256-cpu-arguments-slope", + "keccak_256-mem-arguments", + "blake2b_224-cpu-arguments-intercept", + "blake2b_224-cpu-arguments-slope", + "blake2b_224-mem-arguments", + "integerToByteString-cpu-arguments-c0", + "integerToByteString-cpu-arguments-c1", + "integerToByteString-cpu-arguments-c2", + "integerToByteString-mem-arguments-intercept", + "integerToByteString-mem-arguments-slope", + "byteStringToInteger-cpu-arguments-c0", + "byteStringToInteger-cpu-arguments-c1", + "byteStringToInteger-cpu-arguments-c2", + "byteStringToInteger-mem-arguments-intercept", + "byteStringToInteger-mem-arguments-slope", + ]); + if protocol_version.0 >= 10 { + v.extend(PLOMIN_KEYS); + } + if protocol_version.0 >= 11 { + v.extend(PV11_KEYS); + } + v + } + PlutusVersion::V2 => { + let mut v: Vec<&str> = vec![ + // 0-174: V2 original builtins + "add_integer-cpu-arguments-intercept", + "add_integer-cpu-arguments-slope", + "add_integer-mem-arguments-intercept", + "add_integer-mem-arguments-slope", + "append_byte_string-cpu-arguments-intercept", + "append_byte_string-cpu-arguments-slope", + "append_byte_string-mem-arguments-intercept", + "append_byte_string-mem-arguments-slope", + "append_string-cpu-arguments-intercept", + "append_string-cpu-arguments-slope", + "append_string-mem-arguments-intercept", + "append_string-mem-arguments-slope", + "b_data-cpu-arguments", + "b_data-mem-arguments", + "blake2b_256-cpu-arguments-intercept", + "blake2b_256-cpu-arguments-slope", + "blake2b_256-mem-arguments", + "cek_apply_cost-exBudgetCPU", + "cek_apply_cost-exBudgetmem", + "cek_builtin_cost-exBudgetCPU", + "cek_builtin_cost-exBudgetmem", + "cek_const_cost-exBudgetCPU", + "cek_const_cost-exBudgetmem", + "cek_delay_cost-exBudgetCPU", + "cek_delay_cost-exBudgetmem", + "cek_force_cost-exBudgetCPU", + "cek_force_cost-exBudgetmem", + "cek_lam_cost-exBudgetCPU", + "cek_lam_cost-exBudgetmem", + "cek_startup_cost-exBudgetCPU", + "cek_startup_cost-exBudgetmem", + "cek_var_cost-exBudgetCPU", + "cek_var_cost-exBudgetmem", + "choose_data-cpu-arguments", + "choose_data-mem-arguments", + "choose_list-cpu-arguments", + "choose_list-mem-arguments", + "choose_unit-cpu-arguments", + "choose_unit-mem-arguments", + "cons_byte_string-cpu-arguments-intercept", + "cons_byte_string-cpu-arguments-slope", + "cons_byte_string-mem-arguments-intercept", + "cons_byte_string-mem-arguments-slope", + "constr_data-cpu-arguments", + "constr_data-mem-arguments", + "decode_utf8-cpu-arguments-intercept", + "decode_utf8-cpu-arguments-slope", + "decode_utf8-mem-arguments-intercept", + "decode_utf8-mem-arguments-slope", + "divide_integer-cpu-arguments-constant", + "divide_integer-cpu-arguments-model-arguments-intercept", + "divide_integer-cpu-arguments-model-arguments-slope", + "divide_integer-mem-arguments-intercept", + "divide_integer-mem-arguments-minimum", + "divide_integer-mem-arguments-slope", + "encode_utf8-cpu-arguments-intercept", + "encode_utf8-cpu-arguments-slope", + "encode_utf8-mem-arguments-intercept", + "encode_utf8-mem-arguments-slope", + "equals_byte_string-cpu-arguments-constant", + "equals_byte_string-cpu-arguments-intercept", + "equals_byte_string-cpu-arguments-slope", + "equals_byte_string-mem-arguments", + "equals_data-cpu-arguments-intercept", + "equals_data-cpu-arguments-slope", + "equals_data-mem-arguments", + "equals_integer-cpu-arguments-intercept", + "equals_integer-cpu-arguments-slope", + "equals_integer-mem-arguments", + "equals_string-cpu-arguments-constant", + "equals_string-cpu-arguments-intercept", + "equals_string-cpu-arguments-slope", + "equals_string-mem-arguments", + "fst_pair-cpu-arguments", + "fst_pair-mem-arguments", + "head_list-cpu-arguments", + "head_list-mem-arguments", + "i_data-cpu-arguments", + "i_data-mem-arguments", + "if_then_else-cpu-arguments", + "if_then_else-mem-arguments", + "index_byte_string-cpu-arguments", + "index_byte_string-mem-arguments", + "length_of_byte_string-cpu-arguments", + "length_of_byte_string-mem-arguments", + "less_than_byte_string-cpu-arguments-intercept", + "less_than_byte_string-cpu-arguments-slope", + "less_than_byte_string-mem-arguments", + "less_than_equals_byte_string-cpu-arguments-intercept", + "less_than_equals_byte_string-cpu-arguments-slope", + "less_than_equals_byte_string-mem-arguments", + "less_than_equals_integer-cpu-arguments-intercept", + "less_than_equals_integer-cpu-arguments-slope", + "less_than_equals_integer-mem-arguments", + "less_than_integer-cpu-arguments-intercept", + "less_than_integer-cpu-arguments-slope", + "less_than_integer-mem-arguments", + "list_data-cpu-arguments", + "list_data-mem-arguments", + "map_data-cpu-arguments", + "map_data-mem-arguments", + "mk_cons-cpu-arguments", + "mk_cons-mem-arguments", + "mk_nil_data-cpu-arguments", + "mk_nil_data-mem-arguments", + "mk_nil_pair_data-cpu-arguments", + "mk_nil_pair_data-mem-arguments", + "mk_pair_data-cpu-arguments", + "mk_pair_data-mem-arguments", + "mod_integer-cpu-arguments-constant", + "mod_integer-cpu-arguments-model-arguments-intercept", + "mod_integer-cpu-arguments-model-arguments-slope", + "mod_integer-mem-arguments-intercept", + "mod_integer-mem-arguments-minimum", + "mod_integer-mem-arguments-slope", + "multiply_integer-cpu-arguments-intercept", + "multiply_integer-cpu-arguments-slope", + "multiply_integer-mem-arguments-intercept", + "multiply_integer-mem-arguments-slope", + "null_list-cpu-arguments", + "null_list-mem-arguments", + "quotient_integer-cpu-arguments-constant", + "quotient_integer-cpu-arguments-model-arguments-intercept", + "quotient_integer-cpu-arguments-model-arguments-slope", + "quotient_integer-mem-arguments-intercept", + "quotient_integer-mem-arguments-minimum", + "quotient_integer-mem-arguments-slope", + "remainder_integer-cpu-arguments-constant", + "remainder_integer-cpu-arguments-model-arguments-intercept", + "remainder_integer-cpu-arguments-model-arguments-slope", + "remainder_integer-mem-arguments-intercept", + "remainder_integer-mem-arguments-minimum", + "remainder_integer-mem-arguments-slope", + "serialise_data-cpu-arguments-intercept", + "serialise_data-cpu-arguments-slope", + "serialise_data-mem-arguments-intercept", + "serialise_data-mem-arguments-slope", + "sha2_256-cpu-arguments-intercept", + "sha2_256-cpu-arguments-slope", + "sha2_256-mem-arguments", + "sha3_256-cpu-arguments-intercept", + "sha3_256-cpu-arguments-slope", + "sha3_256-mem-arguments", + "slice_byte_string-cpu-arguments-intercept", + "slice_byte_string-cpu-arguments-slope", + "slice_byte_string-mem-arguments-intercept", + "slice_byte_string-mem-arguments-slope", + "snd_pair-cpu-arguments", + "snd_pair-mem-arguments", + "subtract_integer-cpu-arguments-intercept", + "subtract_integer-cpu-arguments-slope", + "subtract_integer-mem-arguments-intercept", + "subtract_integer-mem-arguments-slope", + "tail_list-cpu-arguments", + "tail_list-mem-arguments", + "trace-cpu-arguments", + "trace-mem-arguments", + "un_b_data-cpu-arguments", + "un_b_data-mem-arguments", + "un_constr_data-cpu-arguments", + "un_constr_data-mem-arguments", + "un_i_data-cpu-arguments", + "un_i_data-mem-arguments", + "un_list_data-cpu-arguments", + "un_list_data-mem-arguments", + "un_map_data-cpu-arguments", + "un_map_data-mem-arguments", + "verify_ecdsa_secp256k1_signature-cpu-arguments", + "verify_ecdsa_secp256k1_signature-mem-arguments", + "verify_ed25519_signature-cpu-arguments-intercept", + "verify_ed25519_signature-cpu-arguments-slope", + "verify_ed25519_signature-mem-arguments", + "verify_schnorr_secp256k1_signature-cpu-arguments-intercept", + "verify_schnorr_secp256k1_signature-cpu-arguments-slope", + "verify_schnorr_secp256k1_signature-mem-arguments", + // 175-331: V2 extension (matches Haskell V2 ParamName enum order) + // 175-184: integerToByteString + byteStringToInteger (enabled at Plomin) + "integerToByteString-cpu-arguments-c0", + "integerToByteString-cpu-arguments-c1", + "integerToByteString-cpu-arguments-c2", + "integerToByteString-mem-arguments-intercept", + "integerToByteString-mem-arguments-slope", + "byteStringToInteger-cpu-arguments-c0", + "byteStringToInteger-cpu-arguments-c1", + "byteStringToInteger-cpu-arguments-c2", + "byteStringToInteger-mem-arguments-intercept", + "byteStringToInteger-mem-arguments-slope", + // 185-188: cek_constr + cek_case + "cek_constr_cost-exBudgetCPU", + "cek_constr_cost-exBudgetmem", + "cek_case_cost-exBudgetCPU", + "cek_case_cost-exBudgetmem", + ]; + // 189-204: BLS12 G1, 205-220: BLS12 G2 + v.extend(BLS12_G1_KEYS); + v.extend(BLS12_G2_KEYS); + // 221-232: finalVerify/millerLoop/mulMlResult + keccak + blake2b_224 + v.extend([ + "bls12_381_finalVerify-cpu-arguments", + "bls12_381_finalVerify-mem-arguments", + "bls12_381_millerLoop-cpu-arguments", + "bls12_381_millerLoop-mem-arguments", + "bls12_381_mulMlResult-cpu-arguments", + "bls12_381_mulMlResult-mem-arguments", + "keccak_256-cpu-arguments-intercept", + "keccak_256-cpu-arguments-slope", + "keccak_256-mem-arguments", + "blake2b_224-cpu-arguments-intercept", + "blake2b_224-cpu-arguments-slope", + "blake2b_224-mem-arguments", + ]); + // 233-278: Plomin (bitwise + ripemd_160) + if protocol_version.0 >= 10 { + v.extend(PLOMIN_KEYS); + } + // 279-331: PV11 + if protocol_version.0 >= 11 { + v.extend(PV11_KEYS); + } + v + } PlutusVersion::V3 => { - let mut base_keys = vec![ + let mut v: Vec<&str> = vec![ + // 0-250: V3 original builtins "add_integer-cpu-arguments-intercept", "add_integer-cpu-arguments-slope", "add_integer-mem-arguments-intercept", @@ -554,38 +797,11 @@ impl CostMap { "cek_constr_cost-exBudgetmem", "cek_case_cost-exBudgetCPU", "cek_case_cost-exBudgetmem", - "bls12_381_G1_add-cpu-arguments", - "bls12_381_G1_add-mem-arguments", - "bls12_381_G1_compress-cpu-arguments", - "bls12_381_G1_compress-mem-arguments", - "bls12_381_G1_equal-cpu-arguments", - "bls12_381_G1_equal-mem-arguments", - "bls12_381_G1_hashToGroup-cpu-arguments-intercept", - "bls12_381_G1_hashToGroup-cpu-arguments-slope", - "bls12_381_G1_hashToGroup-mem-arguments", - "bls12_381_G1_neg-cpu-arguments", - "bls12_381_G1_neg-mem-arguments", - "bls12_381_G1_scalarMul-cpu-arguments-intercept", - "bls12_381_G1_scalarMul-cpu-arguments-slope", - "bls12_381_G1_scalarMul-mem-arguments", - "bls12_381_G1_uncompress-cpu-arguments", - "bls12_381_G1_uncompress-mem-arguments", - "bls12_381_G2_add-cpu-arguments", - "bls12_381_G2_add-mem-arguments", - "bls12_381_G2_compress-cpu-arguments", - "bls12_381_G2_compress-mem-arguments", - "bls12_381_G2_equal-cpu-arguments", - "bls12_381_G2_equal-mem-arguments", - "bls12_381_G2_hashToGroup-cpu-arguments-intercept", - "bls12_381_G2_hashToGroup-cpu-arguments-slope", - "bls12_381_G2_hashToGroup-mem-arguments", - "bls12_381_G2_neg-cpu-arguments", - "bls12_381_G2_neg-mem-arguments", - "bls12_381_G2_scalarMul-cpu-arguments-intercept", - "bls12_381_G2_scalarMul-cpu-arguments-slope", - "bls12_381_G2_scalarMul-mem-arguments", - "bls12_381_G2_uncompress-cpu-arguments", - "bls12_381_G2_uncompress-mem-arguments", + ]; + // BLS12 G1 and G2 + v.extend(BLS12_G1_KEYS); + v.extend(BLS12_G2_KEYS); + v.extend([ "bls12_381_finalVerify-cpu-arguments", "bls12_381_finalVerify-mem-arguments", "bls12_381_millerLoop-cpu-arguments", @@ -597,7 +813,7 @@ impl CostMap { "keccak_256-mem-arguments", "blake2b_224-cpu-arguments-intercept", "blake2b_224-cpu-arguments-slope", - "blake2b_224-mem-arguments-slope", + "blake2b_224-mem-arguments", "integerToByteString-cpu-arguments-c0", "integerToByteString-cpu-arguments-c1", "integerToByteString-cpu-arguments-c2", @@ -608,62 +824,16 @@ impl CostMap { "byteStringToInteger-cpu-arguments-c2", "byteStringToInteger-mem-arguments-intercept", "byteStringToInteger-mem-arguments-slope", - ]; - - // Newer parameter vectors may append extra entries after the known - // bitwise/ripemd V3 costs. As long as we have at least that prefix, - // keep mapping the known keys instead of silently dropping them. - if values.len() >= 297 { - base_keys.extend([ - "andByteString-cpu-arguments-intercept", - "andByteString-cpu-arguments-slope1", - "andByteString-cpu-arguments-slope2", - "andByteString-memory-arguments-intercept", - "andByteString-memory-arguments-slope", - "orByteString-cpu-arguments-intercept", - "orByteString-cpu-arguments-slope1", - "orByteString-cpu-arguments-slope2", - "orByteString-memory-arguments-intercept", - "orByteString-memory-arguments-slope", - "xorByteString-cpu-arguments-intercept", - "xorByteString-cpu-arguments-slope1", - "xorByteString-cpu-arguments-slope2", - "xorByteString-memory-arguments-intercept", - "xorByteString-memory-arguments-slope", - "complementByteString-cpu-arguments-intercept", - "complementByteString-cpu-arguments-slope", - "complementByteString-memory-arguments-intercept", - "complementByteString-memory-arguments-slope", - "readBit-cpu-arguments", - "readBit-memory-arguments", - "writeBits-cpu-arguments-intercept", - "writeBits-cpu-arguments-slope", - "writeBits-memory-arguments-intercept", - "writeBits-memory-arguments-slope", - "replicateByte-cpu-arguments-intercept", - "replicateByte-cpu-arguments-slope", - "replicateByte-memory-arguments-intercept", - "replicateByte-memory-arguments-slope", - "shiftByteString-cpu-arguments-intercept", - "shiftByteString-cpu-arguments-slope", - "shiftByteString-memory-arguments-intercept", - "shiftByteString-memory-arguments-slope", - "rotateByteString-cpu-arguments-intercept", - "rotateByteString-cpu-arguments-slope", - "rotateByteString-memory-arguments-intercept", - "rotateByteString-memory-arguments-slope", - "countSetBits-cpu-arguments-intercept", - "countSetBits-cpu-arguments-slope", - "countSetBits-memory-arguments", - "findFirstSetBit-cpu-arguments-intercept", - "findFirstSetBit-cpu-arguments-slope", - "findFirstSetBit-memory-arguments", - "ripemd_160-cpu-arguments-intercept", - "ripemd_160-cpu-arguments-slope", - "ripemd_160-memory-arguments", - ]); + ]); + // 251-296: Plomin (bitwise + ripemd_160) + if protocol_version.0 >= 10 { + v.extend(PLOMIN_KEYS); + } + // 297-349: PV11 + if protocol_version.0 >= 11 { + v.extend(PV11_KEYS); } - base_keys + v } }; diff --git a/crates/uplc/src/machine/cost_model/mod.rs b/crates/uplc/src/machine/cost_model/mod.rs index 5cf1482e..1241bac7 100644 --- a/crates/uplc/src/machine/cost_model/mod.rs +++ b/crates/uplc/src/machine/cost_model/mod.rs @@ -20,8 +20,12 @@ pub struct CostModel { } impl CostModel { - pub fn initialize_cost_model(version: &PlutusVersion, cost_model: &[i64]) -> CostModel { - let cost_map = cost_map::CostMap::new(version, cost_model); + pub fn initialize_cost_model( + version: &PlutusVersion, + protocol_version: (u64, u64), + cost_model: &[i64], + ) -> CostModel { + let cost_map = cost_map::CostMap::new(version, protocol_version, cost_model); Self { machine_startup: ExBudget { mem: cost_map["cek_startup_cost-exBudgetmem"], @@ -43,6 +47,32 @@ impl Default for CostModel { } } +/// Default V3 cost model values (251 base + 46 PLOMIN = 297 entries). +/// These match the Cardano mainnet default cost parameters for PlutusV3. +pub fn default_v3_cost_model() -> Vec { + vec![ + 100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, 8356, 4, 16000, + 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 100, 100, 16000, 100, + 94375, 32, 132994, 32, 61462, 4, 72010, 178, 0, 1, 22151, 32, 91189, 769, 4, 2, 85848, + 123203, 7305, -900, 1716, 960, 57, 85848, 0, 1, 1, 1000, 42921, 4, 2, 30623, 28755, 75, 1, + 898148, 27279, 1, 51775, 558, 1, 39184, 1000, 60594, 1, 141895, 32, 83150, 32, 15299, 32, + 76049, 1, 13169, 4, 22100, 10, 28999, 74, 1, 28999, 74, 1, 43285, 552, 1, 44749, 541, 1, + 33852, 32, 68246, 32, 72362, 32, 7243, 32, 7391, 32, 11546, 32, 85848, 123203, 7305, -900, + 1716, 960, 57, 85848, 0, 1, 90434, 519, 0, 1, 74433, 32, 85848, 123203, 7305, -900, 1716, + 960, 57, 85848, 0, 1, 1, 85848, 123203, 7305, -900, 1716, 960, 57, 85848, 0, 1, 955506, + 213312, 0, 2, 270652, 22588, 4, 1457325, 64566, 4, 20467, 1, 4, 0, 141992, 32, 100788, 420, + 1, 1, 81663, 32, 59498, 32, 20142, 32, 24588, 32, 20744, 32, 25933, 32, 24623, 32, + 43053543, 10, 53384111, 14333, 10, 43574283, 26308, 10, 16000, 100, 16000, 100, 962335, 18, + 2780678, 6, 442008, 1, 52538055, 3756, 18, 267929, 18, 76433006, 8868, 18, 52948122, 18, + 1995836, 36, 3227919, 12, 901022, 1, 166917843, 4307, 36, 284546, 36, 158221314, 26549, 36, + 74698472, 36, 333849714, 1, 254006273, 72, 2174038, 72, 2261318, 64571, 4, 207616, 8310, 4, + 1293828, 28716, 63, 0, 1, 1006041, 43623, 251, 0, 1, 100181, 726, 719, 0, 1, 100181, 726, + 719, 0, 1, 100181, 726, 719, 0, 1, 107878, 680, 0, 1, 95336, 1, 281145, 18848, 0, 1, + 180194, 159, 1, 1, 158519, 8942, 0, 1, 159378, 8813, 0, 1, 107490, 3298, 1, 106057, 655, 1, + 1964219, 24520, 3, + ] +} + #[repr(usize)] pub enum StepKind { Constant = 0, diff --git a/crates/uplc/src/machine/mod.rs b/crates/uplc/src/machine/mod.rs index 4e079e2a..49ce286a 100644 --- a/crates/uplc/src/machine/mod.rs +++ b/crates/uplc/src/machine/mod.rs @@ -11,6 +11,7 @@ mod state; mod value; pub use cek::*; +pub use cost_model::default_v3_cost_model; pub use cost_model::ex_budget::*; pub use cost_model::CostModel; pub use error::*; diff --git a/crates/uplc/src/program.rs b/crates/uplc/src/program.rs index 76256b82..f7c9f51d 100644 --- a/crates/uplc/src/program.rs +++ b/crates/uplc/src/program.rs @@ -99,25 +99,38 @@ where &'a self, arena: &'a Arena, plutus_version: PlutusVersion, + protocol_version: (u64, u64), cost_model: &[i64], initial_budget: ExBudget, ) -> EvalResult<'a, V> { match plutus_version { PlutusVersion::V1 => self.evaluate( arena, - CostModel::::initialize_cost_model(&plutus_version, cost_model), + CostModel::::initialize_cost_model( + &plutus_version, + protocol_version, + cost_model, + ), plutus_version, initial_budget, ), PlutusVersion::V2 => self.evaluate( arena, - CostModel::::initialize_cost_model(&plutus_version, cost_model), + CostModel::::initialize_cost_model( + &plutus_version, + protocol_version, + cost_model, + ), plutus_version, initial_budget, ), PlutusVersion::V3 => self.evaluate( arena, - CostModel::::initialize_cost_model(&plutus_version, cost_model), + CostModel::::initialize_cost_model( + &plutus_version, + protocol_version, + cost_model, + ), plutus_version, initial_budget, ), @@ -147,24 +160,8 @@ impl<'a> Version<'a> { Self::new(arena, 1, 1, 0) } - pub fn is_v1_0_0(&'a self) -> bool { - self.0 == &(1, 0, 0) - } - - pub fn is_v1_1_0(&'a self) -> bool { - self.0 == &(1, 1, 0) - } - - pub fn is_valid_version(&'a self) -> bool { - self.is_v1_0_0() || self.is_v1_1_0() - } - - pub fn is_less_than_1_1_0(&'a self) -> bool { - self.0 < &(1, 1, 0) - } - - pub fn is_at_least_1_1_0(&'a self) -> bool { - self.0 >= &(1, 1, 0) + pub fn is_constr_case_available(&'a self) -> bool { + self.0 .0 >= 1 && self.0 .1 >= 1 } pub fn major(&'a self) -> usize { diff --git a/crates/uplc/src/syn/mod.rs b/crates/uplc/src/syn/mod.rs index be40cd2d..f6504efb 100644 --- a/crates/uplc/src/syn/mod.rs +++ b/crates/uplc/src/syn/mod.rs @@ -17,8 +17,9 @@ use crate::{ pub fn parse_program<'a>( arena: &'a Arena, input: &'a str, + protocol_version: u32, ) -> ParseResult<&'a Program<'a, DeBruijn>, Rich<'a, char>> { - let mut initial_state = SimpleState(types::State::new(arena)); + let mut initial_state = SimpleState(types::State::new(arena, Some(protocol_version))); program::parser().parse_with_state(input, &mut initial_state) } @@ -27,7 +28,7 @@ pub fn parse_term<'a>( arena: &'a Arena, input: &'a str, ) -> ParseResult<&'a Term<'a, DeBruijn>, Rich<'a, char>> { - let mut initial_state = SimpleState(types::State::new(arena)); + let mut initial_state = SimpleState(types::State::new(arena, None)); term::parser().parse_with_state(input, &mut initial_state) } @@ -36,7 +37,7 @@ pub fn parse_constant<'a>( arena: &'a Arena, input: &'a str, ) -> ParseResult<&'a Constant<'a>, Rich<'a, char>> { - let mut initial_state = SimpleState(types::State::new(arena)); + let mut initial_state = SimpleState(types::State::new(arena, None)); constant::parser().parse_with_state(input, &mut initial_state) } @@ -45,7 +46,7 @@ pub fn parse_data<'a>( arena: &'a Arena, input: &'a str, ) -> ParseResult<&'a PlutusData<'a>, Rich<'a, char>> { - let mut initial_state = SimpleState(types::State::new(arena)); + let mut initial_state = SimpleState(types::State::new(arena, None)); data::parser().parse_with_state(input, &mut initial_state) } diff --git a/crates/uplc/src/syn/term.rs b/crates/uplc/src/syn/term.rs index 9bb62779..b5c1bf1d 100644 --- a/crates/uplc/src/syn/term.rs +++ b/crates/uplc/src/syn/term.rs @@ -140,10 +140,10 @@ pub fn parser<'a>() -> impl Parser<'a, &'a str, &'a Term<'a, DeBruijn>, Extra<'a Ok(t) => { let ret = Term::constr(state.arena, t, fields); - if state.is_less_than_1_1_0() { + if !state.is_constr_case_available() { emitter.emit(Rich::custom( e.span(), - "constr is not supported before 1.1.0", + "constr is not available for this protocol version", )); } @@ -174,8 +174,11 @@ pub fn parser<'a>() -> impl Parser<'a, &'a str, &'a Term<'a, DeBruijn>, Extra<'a let ret = Term::case(state.arena, tag, branches); - if state.is_less_than_1_1_0() { - emitter.emit(Rich::custom(e.span(), "case is not supported before 1.1.0")); + if !state.is_constr_case_available() { + emitter.emit(Rich::custom( + e.span(), + "case is not available for this protocol version", + )); } ret diff --git a/crates/uplc/src/syn/types.rs b/crates/uplc/src/syn/types.rs index dcb6467d..cc2fa9b0 100644 --- a/crates/uplc/src/syn/types.rs +++ b/crates/uplc/src/syn/types.rs @@ -6,14 +6,16 @@ pub struct State<'a> { pub arena: &'a Arena, pub env: Vec<&'a str>, pub version: Option>, + pub protocol_version: Option, } impl<'a> State<'a> { - pub fn new(arena: &'a Arena) -> Self { + pub fn new(arena: &'a Arena, protocol_version: Option) -> Self { Self { arena, env: Vec::new(), version: None, + protocol_version, } } @@ -21,10 +23,10 @@ impl<'a> State<'a> { self.version = Some(version); } - pub fn is_less_than_1_1_0(&self) -> bool { - self.version - .map(|v| v.is_less_than_1_1_0()) - .unwrap_or(false) + pub fn is_constr_case_available(&self) -> bool { + let protocol_ok = self.protocol_version.is_none_or(|pv| pv >= 9); + let version_ok = self.version.is_none_or(|v| v.is_constr_case_available()); + protocol_ok && version_ok } } diff --git a/crates/uplc/tests/conformance.rs b/crates/uplc/tests/conformance.rs index d9faa148..0bb04de0 100644 --- a/crates/uplc/tests/conformance.rs +++ b/crates/uplc/tests/conformance.rs @@ -1,16 +1,31 @@ -use amaru_uplc::{arena::Arena, machine::PlutusVersion, syn::parse_program}; +use amaru_uplc::{ + arena::Arena, + machine::{default_v3_cost_model, ExBudget, PlutusVersion}, + syn::parse_program, +}; + +const PLUTUS_VERSION: PlutusVersion = PlutusVersion::V3; +const PROTOCOL_VERSION: (u64, u64) = (11, 0); fn run_conformance(file_contents: &str, expected_output: &str, expected_budget: &str) { let arena = Arena::new(); + let costs = default_v3_cost_model(); - let Ok(program) = parse_program(&arena, file_contents).into_result() else { + let Ok(program) = parse_program(&arena, file_contents, PROTOCOL_VERSION.0 as u32).into_result() + else { pretty_assertions::assert_eq!("parse error", expected_output); pretty_assertions::assert_eq!("parse error", expected_budget); return; }; - let result = program.eval_version(&arena, PlutusVersion::V3); + let result = program.eval_with_params( + &arena, + PLUTUS_VERSION, + PROTOCOL_VERSION, + &costs, + ExBudget::default(), + ); let info = result.info; @@ -21,7 +36,7 @@ fn run_conformance(file_contents: &str, expected_output: &str, expected_budget: return; }; - let expected = parse_program(&arena, expected_output) + let expected = parse_program(&arena, expected_output, PROTOCOL_VERSION.0 as u32) .into_result() .unwrap(); diff --git a/crates/uplc/tests/conformance_flat.rs b/crates/uplc/tests/conformance_flat.rs index f7cd178c..9c65d3e2 100644 --- a/crates/uplc/tests/conformance_flat.rs +++ b/crates/uplc/tests/conformance_flat.rs @@ -2,7 +2,7 @@ use amaru_uplc::{ arena::Arena, binder::DeBruijn, flat, - machine::{ExBudget, PlutusVersion}, + machine::{default_v3_cost_model, ExBudget, PlutusVersion}, program::Program, }; use serde::Deserialize; @@ -11,7 +11,7 @@ use serde::Deserialize; // including the CIP-153 batch (ExpModInteger, DropList, LengthOfArray, // ListToArray, IndexArray). const PLUTUS_VERSION: PlutusVersion = PlutusVersion::V3; -const PROTOCOL_VERSION_MAJOR: u32 = 11; +const PROTOCOL_VERSION: (u64, u64) = (11, 0); #[derive(Debug, Deserialize)] struct Fixture { @@ -60,11 +60,13 @@ fn run_conformance(fixture_json: &str) { let arena = Arena::new(); + let costs = default_v3_cost_model(); + let program = match flat::decode_strict::( &arena, &input, PLUTUS_VERSION, - PROTOCOL_VERSION_MAJOR, + PROTOCOL_VERSION.0 as u32, ) { Ok(p) => p, Err(e) => { @@ -88,7 +90,13 @@ fn run_conformance(fixture_json: &str) { panic!("fixture pinned `decode` but decode succeeded; eval will run next"); } - let result = program.eval(&arena); + let result = program.eval_with_params( + &arena, + PLUTUS_VERSION, + PROTOCOL_VERSION, + &costs, + ExBudget::default(), + ); let term = match result.term { Ok(t) => t,