From 045dc8fed8279b27622713eb281e3f253c6764a4 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Thu, 7 Jul 2022 23:16:51 -0400 Subject: [PATCH] add builtin cost model Co-authored-by: rvcas --- crates/uplc/src/ast.rs | 7 +- crates/uplc/src/machine.rs | 90 ++--------- crates/uplc/src/machine/cost_model.rs | 223 ++++++++++++++++++++++++++ 3 files changed, 239 insertions(+), 81 deletions(-) create mode 100644 crates/uplc/src/machine/cost_model.rs diff --git a/crates/uplc/src/ast.rs b/crates/uplc/src/ast.rs index bc06cb32..b2f18355 100644 --- a/crates/uplc/src/ast.rs +++ b/crates/uplc/src/ast.rs @@ -3,7 +3,10 @@ use std::fmt::Display; use crate::{ builtins::DefaultFunction, debruijn::{self, Converter}, - machine::{Costs, ExBudget, Machine}, + machine::{ + cost_model::{ExBudget, MachineCosts}, + Machine, + }, }; /// This represents a program in Untyped Plutus Core. @@ -404,7 +407,7 @@ impl From> for Term { impl Program { pub fn eval(&self) -> Result, crate::machine::Error> { - let mut machine = Machine::new(Costs::default(), ExBudget::default(), 200); + let mut machine = Machine::new(MachineCosts::default(), ExBudget::default(), 200); let (term, _, _) = machine.run(&self.term)?; diff --git a/crates/uplc/src/machine.rs b/crates/uplc/src/machine.rs index dae5435e..f6a10c44 100644 --- a/crates/uplc/src/machine.rs +++ b/crates/uplc/src/machine.rs @@ -3,12 +3,14 @@ use crate::{ builtins::DefaultFunction, }; +pub mod cost_model; mod error; +use cost_model::{ExBudget, MachineCosts, StepKind}; pub use error::Error; pub struct Machine { - costs: Costs, + costs: MachineCosts, ex_budget: ExBudget, frames: Vec, slippage: u32, @@ -17,7 +19,7 @@ pub struct Machine { } impl Machine { - pub fn new(costs: Costs, initial_budget: ExBudget, slippage: u32) -> Machine { + pub fn new(costs: MachineCosts, initial_budget: ExBudget, slippage: u32) -> Machine { Machine { costs, ex_budget: initial_budget, @@ -86,7 +88,10 @@ impl Machine { self.compute(body) } Term::Error => Err(Error::EvaluationFailure), - Term::Builtin(_) => todo!(), + Term::Builtin(_bn) => { + self.step_and_maybe_spend(StepKind::Builtin)?; + todo!() + } } } @@ -237,10 +242,12 @@ impl Machine { unspent_step_budget.occurences(self.unbudgeted_steps[i] as i32); self.spend_budget(unspent_step_budget)?; - + self.unbudgeted_steps[i] = 0; } + self.unbudgeted_steps[7] = 0; + Ok(()) } @@ -279,78 +286,3 @@ pub enum Value { // BuiltinRuntime (CekValue uni fun) ), } - -/// Can be negative -#[derive(Debug, Clone, PartialEq, Copy, Default)] -pub struct ExBudget { - mem: i32, - cpu: i32, -} - -impl ExBudget { - pub fn occurences(&mut self, n: i32) { - self.mem *= n; - self.cpu *= n; - } -} - -#[repr(u8)] -pub enum StepKind { - Constant = 0, - Var = 1, - Lambda = 2, - Apply = 3, - Delay = 4, - Force = 5, - Builtin = 6, - // DO NOT USE THIS IN `step_and_maybe_spend` - StartUp = 7, -} - -impl TryFrom for StepKind { - type Error = error::Error; - - fn try_from(value: u8) -> Result { - match value { - 0 => Ok(StepKind::Constant), - 1 => Ok(StepKind::Var), - 2 => Ok(StepKind::Lambda), - 3 => Ok(StepKind::Apply), - 4 => Ok(StepKind::Delay), - 5 => Ok(StepKind::Force), - 6 => Ok(StepKind::Builtin), - v => Err(error::Error::InvalidStepKind(v)), - } - } -} - -/// 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? -#[derive(Default)] -pub struct Costs { - startup: ExBudget, - var: ExBudget, - constant: ExBudget, - lambda: ExBudget, - delay: ExBudget, - force: ExBudget, - apply: ExBudget, - /// Just the cost of evaluating a Builtin node, not the builtin itself. - builtin: ExBudget, -} - -impl Costs { - /// Get the cost of a step - pub fn get(&self, step: StepKind) -> ExBudget { - match step { - StepKind::Constant => self.constant, - StepKind::Var => self.var, - StepKind::Lambda => self.lambda, - StepKind::Apply => self.apply, - StepKind::Delay => self.delay, - StepKind::Force => self.force, - StepKind::Builtin => self.builtin, - StepKind::StartUp => self.startup, - } - } -} diff --git a/crates/uplc/src/machine/cost_model.rs b/crates/uplc/src/machine/cost_model.rs new file mode 100644 index 00000000..1fb5ec94 --- /dev/null +++ b/crates/uplc/src/machine/cost_model.rs @@ -0,0 +1,223 @@ +/// Can be negative +#[derive(Debug, Clone, PartialEq, Copy, Default)] +pub struct ExBudget { + pub mem: i32, + pub cpu: i32, +} + +impl ExBudget { + pub fn occurences(&mut self, n: i32) { + self.mem *= n; + self.cpu *= n; + } +} + +pub struct CostModel { + pub machine_costs: MachineCosts, +} + +/// 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? +#[derive(Default)] +pub struct MachineCosts { + startup: ExBudget, + var: ExBudget, + constant: ExBudget, + lambda: ExBudget, + delay: ExBudget, + force: ExBudget, + apply: ExBudget, + /// Just the cost of evaluating a Builtin node, not the builtin itself. + builtin: ExBudget, +} + +impl MachineCosts { + /// Get the cost of a step + pub fn get(&self, step: StepKind) -> ExBudget { + match step { + StepKind::Constant => self.constant, + StepKind::Var => self.var, + StepKind::Lambda => self.lambda, + StepKind::Apply => self.apply, + StepKind::Delay => self.delay, + StepKind::Force => self.force, + StepKind::Builtin => self.builtin, + StepKind::StartUp => self.startup, + } + } +} + +pub struct BuiltinCosts { + pub add_integer: CostingFun, + pub subtract_integer: CostingFun, + pub multiply_integer: CostingFun, + pub divide_integer: CostingFun, + pub quotient_integer: CostingFun, + pub remainder_integer: CostingFun, + pub mod_integer: CostingFun, + pub equals_integer: CostingFun, + pub less_than_integer: CostingFun, + pub less_than_equals_integer: CostingFun, + // Bytestrings + pub append_byte_string: CostingFun, + pub cons_byte_string: CostingFun, + pub slice_byte_string: CostingFun, + pub length_of_byte_string: CostingFun, + pub index_byte_string: CostingFun, + pub equals_byte_string: CostingFun, + pub less_than_byte_string: CostingFun, + pub less_than_equals_byte_string: CostingFun, + // Cryptography and hashes + pub sha2_256: CostingFun, + pub sha3_256: CostingFun, + pub blake2b_256: CostingFun, + pub verify_ed25519_signature: CostingFun, + pub verify_ecdsa_secp256k1_signature: CostingFun, + pub verify_schnorr_secp256k1_signature: CostingFun, + // Strings + pub append_string: CostingFun, + pub equals_string: CostingFun, + pub encode_utf8: CostingFun, + pub decode_utf8: CostingFun, + // Bool + pub if_then_else: CostingFun, + // Unit + pub choose_unit: CostingFun, + // Tracing + pub trace: CostingFun, + // Pairs + pub fst_pair: CostingFun, + pub snd_pair: CostingFun, + // Lists + pub choose_list: CostingFun, + pub mk_cons: CostingFun, + pub head_list: CostingFun, + pub tail_list: CostingFun, + pub null_list: CostingFun, + // Data + pub choose_data: CostingFun, + pub constr_data: CostingFun, + pub map_data: CostingFun, + pub list_data: CostingFun, + pub i_data: CostingFun, + pub b_data: CostingFun, + pub un_constr_data: CostingFun, + pub un_map_data: CostingFun, + pub un_list_data: CostingFun, + pub un_i_data: CostingFun, + pub un_b_data: CostingFun, + pub equals_data: CostingFun, + // Misc constructors + pub mk_pair_data: CostingFun, + pub mk_nil_data: CostingFun, + pub mk_nil_pair_data: CostingFun, + pub serialise_data: CostingFun, +} + +pub struct CostingFun { + pub memory: T, + pub cpu: T, +} + +pub enum OneArgument { + ConstantCost(isize), + LinearCost(LinearSize), +} + +pub enum TwoArguments { + ConstantCost(isize), + LinearInX(LinearSize), + LinearInY(LinearSize), + AddedSizes(AddedSizes), + SubtractedSizes(SubtractedSizes), + MultipliedSizes(MultipliedSizes), + MinSize(MinSize), + MaxSize(MaxSize), + LinearOnDiagonal(ConstantOrLinear), + ConstAboveDiagonal(ConstantOrTwoArguments), + ConstBelowDiagonal(ConstantOrTwoArguments), +} + +pub enum ThreeArguments { + ConstantCost(isize), + AddedSizes(AddedSizes), + LinearInX(LinearSize), + LinearInY(LinearSize), + LinearInZ(LinearSize), +} + +pub enum SixArguments { + ConstantCost(isize), +} + +pub struct LinearSize { + pub intercept: isize, + pub slope: isize, +} + +pub struct AddedSizes { + pub intercept: isize, + pub slope: isize, +} + +pub struct SubtractedSizes { + pub intercept: isize, + pub slope: isize, + pub minimum: isize, +} + +pub struct MultipliedSizes { + pub intercept: isize, + pub slope: isize, +} + +pub struct MinSize { + pub intercept: isize, + pub slope: isize, +} + +pub struct MaxSize { + pub intercept: isize, + pub slope: isize, +} + +pub struct ConstantOrLinear { + pub constant: isize, + pub intercept: isize, + pub slope: isize, +} + +pub struct ConstantOrTwoArguments { + pub constant: isize, + pub model: Box, +} + +#[repr(u8)] +pub enum StepKind { + Constant = 0, + Var = 1, + Lambda = 2, + Apply = 3, + Delay = 4, + Force = 5, + Builtin = 6, + // DO NOT USE THIS IN `step_and_maybe_spend` + StartUp = 7, +} + +impl TryFrom for StepKind { + type Error = super::error::Error; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(StepKind::Constant), + 1 => Ok(StepKind::Var), + 2 => Ok(StepKind::Lambda), + 3 => Ok(StepKind::Apply), + 4 => Ok(StepKind::Delay), + 5 => Ok(StepKind::Force), + 6 => Ok(StepKind::Builtin), + v => Err(super::error::Error::InvalidStepKind(v)), + } + } +}