From 795d9ee028b3adf1fc5728cafccc6ae7983c41d1 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sun, 3 Jul 2022 16:38:55 -0400 Subject: [PATCH] feat: clean up the machine a bit --- crates/uplc/src/ast.rs | 37 ++++++--- crates/uplc/src/interpreter.rs | 26 +----- crates/uplc/src/lib.rs | 1 + crates/uplc/src/machine.rs | 132 +++++++++++++++++++++++++++++++ crates/uplc/src/machine/error.rs | 9 +++ 5 files changed, 170 insertions(+), 35 deletions(-) create mode 100644 crates/uplc/src/machine.rs create mode 100644 crates/uplc/src/machine/error.rs diff --git a/crates/uplc/src/ast.rs b/crates/uplc/src/ast.rs index 8e40b22d..c4422134 100644 --- a/crates/uplc/src/ast.rs +++ b/crates/uplc/src/ast.rs @@ -14,6 +14,32 @@ pub struct Program { pub term: Term, } +impl Program +where + T: Clone, +{ + /// We use this to apply the validator to Datum, + /// then redeemer, then ScriptContext. If datum is + /// even necessary (i.e. minting policy). + /// + /// ```rust + /// program.apply(&datum) + /// .apply(&redeemer) + /// .apply(&script_context); + /// ``` + pub fn apply(&self, program: &Self) -> Self { + let applied_term = Term::Apply { + function: Box::new(self.term.clone()), + argument: Box::new(program.term.clone()), + }; + + Program { + version: self.version, + term: applied_term, + } + } +} + /// This represents a term in Untyped Plutus Core. /// We need a generic type for the different forms that a program may be in. /// Specifically, `Var` and `parameter_name` in `Lambda` can be a `Name`, @@ -374,14 +400,3 @@ impl From> for Term { converter.fake_named_debruijn_to_named_debruijn(value) } } - -pub fn apply_program(p1: Program, p2: Program) -> Program { - let applied_term = Term::Apply { - function: Box::new(p1.term), - argument: Box::new(p2.term), - }; - Program { - version: p1.version, - term: applied_term, - } -} diff --git a/crates/uplc/src/interpreter.rs b/crates/uplc/src/interpreter.rs index d9d97383..4ccfe9a2 100644 --- a/crates/uplc/src/interpreter.rs +++ b/crates/uplc/src/interpreter.rs @@ -16,6 +16,7 @@ enum StepKind { BForce = 5, BBuiltin = 6, } + impl TryFrom for StepKind { type Error = de::Error; @@ -41,6 +42,7 @@ enum ExBudgetCategory { BBuiltinApp(DefaultFunction), BStartup, } + /// Can be negative #[derive(Debug, Clone, PartialEq)] struct ExBudget { @@ -146,27 +148,3 @@ fn spend_accumulated_budget_cek(unbudgeted_steps: &mut Vec, current_budget: ); } } - -fn spend_budget_cek( - current_budget: &mut ExBudget, - category: ExBudgetCategory, - spend_budget: ExBudget, -) { - current_budget.mem -= spend_budget.mem; - current_budget.cpu -= spend_budget.cpu; - if current_budget.mem < 0 || current_budget.cpu < 0 { - panic!("Budget exhausted {:?}", current_budget); - } -} - -fn get_cost_by_step(step: StepKind) -> ExBudget { - match (step) { - StepKind::BConst => ExBudget { mem: 100, cpu: 100 }, - StepKind::BVar => ExBudget { mem: 100, cpu: 100 }, - StepKind::BLamAbs => ExBudget { mem: 100, cpu: 100 }, - StepKind::BApply => ExBudget { mem: 100, cpu: 100 }, - StepKind::BDelay => ExBudget { mem: 100, cpu: 100 }, - StepKind::BForce => ExBudget { mem: 100, cpu: 100 }, - StepKind::BBuiltin => ExBudget { mem: 100, cpu: 100 }, - } -} diff --git a/crates/uplc/src/lib.rs b/crates/uplc/src/lib.rs index f6512702..713d5565 100644 --- a/crates/uplc/src/lib.rs +++ b/crates/uplc/src/lib.rs @@ -3,6 +3,7 @@ pub mod builtins; mod debruijn; mod flat; pub mod interpreter; +pub mod machine; pub mod parser; mod pretty; pub mod program_builder; diff --git a/crates/uplc/src/machine.rs b/crates/uplc/src/machine.rs new file mode 100644 index 00000000..19bab916 --- /dev/null +++ b/crates/uplc/src/machine.rs @@ -0,0 +1,132 @@ +use crate::{ + ast::{Constant, DeBruijn, Term}, + builtins::DefaultFunction, +}; + +mod error; + +pub use error::Error; + +pub struct Machine { + costs: Costs, + ex_budget: ExBudget, + frames: Vec, + slippage: u32, + env: Vec, +} + +impl Machine { + pub fn new(costs: Costs, initial_budget: ExBudget, slippage: u32) -> Machine { + Machine { + costs, + ex_budget: initial_budget, + slippage, + frames: vec![], + env: vec![], + } + } + + pub fn run( + &mut self, + term: &Term, + ) -> Result<(Term, usize, Vec), Error> { + let startup_budget = self.costs.get(StepKind::StartUp); + + self.spend_budget(startup_budget)?; + + self.push_frame(Context::NoFrame); + + self.enter_compute(term) + } + + fn enter_compute(&mut self, term: &Term) {} + + fn spend_budget(&mut self, spend_budget: ExBudget) -> Result<(), Error> { + self.ex_budget.mem -= spend_budget.mem; + self.ex_budget.cpu -= spend_budget.cpu; + + if self.ex_budget.mem < 0 || self.ex_budget.cpu < 0 { + Err(Error::OutOfExError(self.ex_budget)) + } else { + Ok(()) + } + } + + fn push_frame(&mut self, frame: Context) { + self.frames.push(frame); + } +} + +enum Context { + FrameApplyFun(Term, Term), + FrameApplyArg(Vec, Term, Box), + FrameForce(Box), + NoFrame, +} + +enum Value { + Con(Constant), + Delay(Term, Vec), + Lambda(DeBruijn, Term, Vec), + Builtin( + DefaultFunction, + Term, + // Need to figure out run time stuff + // BuiltinRuntime (CekValue uni fun) + ), +} + +/// Can be negative +#[derive(Debug, Clone, PartialEq)] +struct ExBudget { + mem: i32, + cpu: i32, +} + +impl ExBudget { + pub fn occurence(&mut self, n: i32) { + self.mem *= n; + self.cpu *= n; + } +} + +enum StepKind { + Constant, + Var, + Lambda, + Apply, + Delay, + Force, + Builtin, + StartUp, +} + +/// 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? +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/error.rs b/crates/uplc/src/machine/error.rs new file mode 100644 index 00000000..83350b1b --- /dev/null +++ b/crates/uplc/src/machine/error.rs @@ -0,0 +1,9 @@ +use thiserror::Error; + +use super::ExBudget; + +#[derive(Error, Debug)] +pub enum Error { + #[error("Over budget mem: {} & cpu: {}", .0.mem, .0.cpu)] + OutOfExError(ExBudget), +}