From 3c71368e9076ed5c5914d5cf6c943bd97c0d8646 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Fri, 24 Mar 2023 17:44:04 +0000 Subject: [PATCH 001/125] cargo.toml --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f0fb3d90e..f85acaec1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] -members = ["acir_field", "acir", "acvm", "stdlib"] +members = ["acir_field", "acir", "acvm", "stdlib", "brillig_bytecode"] [workspace.package] authors = ["The Noir Team "] @@ -19,4 +19,4 @@ num-traits = "0.2" blake2 = "0.9.1" -serde = { version = "1.0.136", features = ["derive"] } \ No newline at end of file +serde = { version = "1.0.136", features = ["derive"] } From 7ce1431ed6a7c357abc2d28d69e818eb1f87ffe4 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Fri, 24 Mar 2023 17:44:32 +0000 Subject: [PATCH 002/125] add initial code for non deterministic vm --- brillig_bytecode/Cargo.toml | 14 +++ brillig_bytecode/src/lib.rs | 152 ++++++++++++++++++++++++++++++ brillig_bytecode/src/opcodes.rs | 75 +++++++++++++++ brillig_bytecode/src/registers.rs | 38 ++++++++ brillig_bytecode/src/value.rs | 75 +++++++++++++++ 5 files changed, 354 insertions(+) create mode 100644 brillig_bytecode/Cargo.toml create mode 100644 brillig_bytecode/src/lib.rs create mode 100644 brillig_bytecode/src/opcodes.rs create mode 100644 brillig_bytecode/src/registers.rs create mode 100644 brillig_bytecode/src/value.rs diff --git a/brillig_bytecode/Cargo.toml b/brillig_bytecode/Cargo.toml new file mode 100644 index 000000000..929ca522b --- /dev/null +++ b/brillig_bytecode/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "brillig_bytecode" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +acir.workspace = true + + +[features] +bn254 = ["acir/bn254"] +bls12_381 = ["acir/bls12_381"] diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs new file mode 100644 index 000000000..a69ce2c25 --- /dev/null +++ b/brillig_bytecode/src/lib.rs @@ -0,0 +1,152 @@ +// ACVM is capable of running brillig-bytecode +// This bytecode is ran in the traditional sense +// and allows one to do non-determinism. +// This is a generalization over the fixed directives +// that we have in ACVM. + +mod opcodes; +mod registers; +mod value; + +pub use opcodes::{BinaryOp, Opcode, UnaryOp}; +pub use registers::{RegisterIndex, Registers}; +pub use value::Value; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum VMStatus { + Halted, + InProgress, +} + +pub struct VM { + registers: Registers, + program_counter: usize, + bytecode: Vec, + status: VMStatus, +} + +impl VM { + pub fn new(inputs: Registers, bytecode: Vec) -> VM { + Self { registers: inputs, program_counter: 0, bytecode, status: VMStatus::InProgress } + } + + /// Loop over the bytecode and update the program counter + pub fn process_opcodes(mut self) -> Registers { + while self.process_opcode() != VMStatus::Halted {} + self.finish() + } + // Process a single opcode and modify the program counter + pub fn process_opcode(&mut self) -> VMStatus { + let opcode = &self.bytecode[self.program_counter]; + match opcode { + Opcode::BinaryOp { op, lhs, rhs, result } => { + self.process_binary_op(*op, *lhs, *rhs, *result); + self.increment_program_counter() + } + Opcode::UnaryOp { op, input, result } => { + self.process_unary_op(*op, *input, *result); + self.increment_program_counter() + } + Opcode::JMP { destination } => self.set_program_counter(destination.inner()), + Opcode::JMPIF { condition, destination } => { + // Check if condition is true + // We use 0 to mean false and any other value to mean true + + let condition_value = self.registers.get(*condition); + if !condition_value.is_zero() { + return self.set_program_counter(destination.inner()); + } + self.status + } + Opcode::Call => todo!(), + Opcode::Intrinsics => todo!(), + Opcode::Oracle => todo!(), + Opcode::Store => todo!(), + Opcode::Load => todo!(), + } + } + + /// Increments the program counter by 1. + fn increment_program_counter(&mut self) -> VMStatus { + self.set_program_counter(self.program_counter + 1) + } + + /// Increments the program counter by `value`. + /// If the program counter no longer points to an opcode + /// in the bytecode, then the VMStatus reports halted. + fn set_program_counter(&mut self, value: usize) -> VMStatus { + assert!(self.program_counter < self.bytecode.len()); + self.program_counter = value; + if self.program_counter >= self.bytecode.len() { + self.status = VMStatus::Halted; + } + return self.status; + } + + /// Process a binary operation. + /// This method will not modify the program counter. + fn process_binary_op( + &mut self, + op: BinaryOp, + lhs: RegisterIndex, + rhs: RegisterIndex, + result: RegisterIndex, + ) { + let lhs_value = self.registers.get(lhs); + let rhs_value = self.registers.get(rhs); + + let result_value = op.function()(lhs_value, rhs_value); + + self.registers.set(result, result_value) + } + + /// Process a unary operation. + /// This method will not modify the program counter. + fn process_unary_op(&mut self, op: UnaryOp, input: RegisterIndex, result: RegisterIndex) { + let input_value = self.registers.get(input); + + let result_value = op.function()(input_value); + + self.registers.set(result, result_value) + } + /// Returns the state of the registers. + /// This consumes ownership of the VM and is conventionally + /// called when all of the bytecode has been processed. + fn finish(self) -> Registers { + self.registers + } +} + +#[test] +fn add_single_step_smoke() { + // Load values into registers and initialize the registers that + // will be used during bytecode processing + let input_registers = + Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(0u128)]); + + // Add opcode to add the value in register `0` and `1` + // and place the output in register `2` + let opcode = Opcode::BinaryOp { + op: BinaryOp::Add, + lhs: RegisterIndex(0), + rhs: RegisterIndex(1), + result: RegisterIndex(2), + }; + + // Start VM + let mut vm = VM::new(input_registers, vec![opcode]); + + // Process a single VM opcode + // + // After processing a single opcode, we should have + // the vm status as halted since there is only one opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::Halted); + + // The register at index `2` should have the value of 3 since we had an + // add opcode + let registers = vm.finish(); + let output_value = registers.get(RegisterIndex(2)); + + assert_eq!(output_value, Value::from(3u128)) +} diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs new file mode 100644 index 000000000..151dc2570 --- /dev/null +++ b/brillig_bytecode/src/opcodes.rs @@ -0,0 +1,75 @@ +use crate::{value::Value, RegisterIndex}; + +#[derive(Debug, Clone, Copy)] +pub enum Opcode { + /// Takes the values in registers `lhs` and `rhs` + /// Performs the specified binary operation + /// and stores the value in the `result` register. + BinaryOp { + op: BinaryOp, + lhs: RegisterIndex, + rhs: RegisterIndex, + result: RegisterIndex, + }, + /// Takes the value in register `input` + /// Performs the specified unary operation + /// and stores the value in `result` register. + UnaryOp { + op: UnaryOp, + input: RegisterIndex, + result: RegisterIndex, + }, + /// Sets the program counter to the value located at `destination` + /// If the value at condition is non-zero + JMPIF { + condition: RegisterIndex, + destination: RegisterIndex, + }, + /// Sets the program counter to the value located at `destination` + JMP { + destination: RegisterIndex, + }, + // TODO:This is used to call functions and setup things like + // TODO execution contexts. + Call, + // TODO:These are special functions like sha256 + Intrinsics, + // TODO:This will be used to get data from an outside source + Oracle, + // TODO: This will be used to store a value at a particular index + Store, + // TODO: This will be used to explicitly load a value at a particular index + Load, +} + +#[derive(Debug, Clone, Copy)] +pub enum UnaryOp { + Inv, +} + +impl UnaryOp { + pub fn function(&self) -> fn(Value) -> Value { + match self { + UnaryOp::Inv => |a: Value| a.inverse(), + } + } +} + +#[derive(Debug, Clone, Copy)] +pub enum BinaryOp { + Add, + Sub, + Mul, + Div, +} + +impl BinaryOp { + pub fn function(&self) -> fn(Value, Value) -> Value { + match self { + BinaryOp::Add => |a: Value, b: Value| a + b, + BinaryOp::Sub => |a: Value, b: Value| a - b, + BinaryOp::Mul => |a: Value, b: Value| a * b, + BinaryOp::Div => |a: Value, b: Value| a / b, + } + } +} diff --git a/brillig_bytecode/src/registers.rs b/brillig_bytecode/src/registers.rs new file mode 100644 index 000000000..8563235fb --- /dev/null +++ b/brillig_bytecode/src/registers.rs @@ -0,0 +1,38 @@ +use crate::Value; + +/// Registers will store field element values during the +/// duration of the execution of the bytecode. +pub struct Registers { + inner: Vec, +} + +#[derive(Debug, Clone, Copy)] +pub struct RegisterIndex(pub usize); + +impl RegisterIndex { + pub fn inner(self) -> usize { + self.0 + } +} + +impl Registers { + /// Contiguously load the register with `values` + pub fn load(values: Vec) -> Registers { + Self { inner: values } + } + /// Gets the values at register with address `index` + pub fn get(&self, index: RegisterIndex) -> Value { + self.inner[index.inner()] + } + /// Sets the value at register with address `index` to `value` + pub fn set(&mut self, index: RegisterIndex, value: Value) { + self.inner[index.inner()] = value + } + /// Returns all of the values in the register + /// This should be done at the end of the VM + /// run and will be useful for mapping the values + /// to witness indices + pub fn values(self) -> Vec { + self.inner + } +} diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs new file mode 100644 index 000000000..fbe825cf5 --- /dev/null +++ b/brillig_bytecode/src/value.rs @@ -0,0 +1,75 @@ +use acir::FieldElement; +use std::ops::{Add, Div, Mul, Neg, Sub}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +/// Values that we can store in registers +pub enum Value { + Field(FieldElement), +} + +impl Value { + /// Returns true if the Value represents `zero` + pub fn is_zero(&self) -> bool { + match self { + Value::Field(val) => *val == FieldElement::zero(), + } + } + /// Performs the multiplicative inverse of `Value` + pub fn inverse(&self) -> Value { + match self { + Value::Field(val) => Value::Field(val.inverse()), + } + } +} + +impl From for Value { + fn from(value: u128) -> Self { + Value::Field(FieldElement::from(value)) + } +} + +impl Add for Value { + type Output = Value; + + fn add(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Value::Field(lhs + rhs), + } + } +} +impl Sub for Value { + type Output = Value; + + fn sub(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Value::Field(lhs - rhs), + } + } +} +impl Mul for Value { + type Output = Value; + + fn mul(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Value::Field(lhs * rhs), + } + } +} +impl Div for Value { + type Output = Value; + + fn div(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Value::Field(lhs / rhs), + } + } +} +impl Neg for Value { + type Output = Value; + + fn neg(self) -> Self::Output { + match self { + Value::Field(input) => Value::Field(-input), + } + } +} From 5218be79562fea3eded23c33b9f870b84bf42806 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Fri, 24 Mar 2023 17:52:32 +0000 Subject: [PATCH 003/125] fix clippy --- brillig_bytecode/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index a69ce2c25..13ec3b947 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -80,7 +80,7 @@ impl VM { if self.program_counter >= self.bytecode.len() { self.status = VMStatus::Halted; } - return self.status; + self.status } /// Process a binary operation. From 87c28d11676edbc5c9bf6a7ce8c9523b2c7a5f2a Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Mon, 27 Mar 2023 17:48:17 +0100 Subject: [PATCH 004/125] remove unary op -- can be simulated with binary op --- brillig_bytecode/src/lib.rs | 6 +----- brillig_bytecode/src/opcodes.rs | 28 +++++----------------------- 2 files changed, 6 insertions(+), 28 deletions(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 13ec3b947..b8ad3d844 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -8,7 +8,7 @@ mod opcodes; mod registers; mod value; -pub use opcodes::{BinaryOp, Opcode, UnaryOp}; +pub use opcodes::{BinaryOp, Opcode}; pub use registers::{RegisterIndex, Registers}; pub use value::Value; @@ -43,10 +43,6 @@ impl VM { self.process_binary_op(*op, *lhs, *rhs, *result); self.increment_program_counter() } - Opcode::UnaryOp { op, input, result } => { - self.process_unary_op(*op, *input, *result); - self.increment_program_counter() - } Opcode::JMP { destination } => self.set_program_counter(destination.inner()), Opcode::JMPIF { condition, destination } => { // Check if condition is true diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 151dc2570..b7d4c65cf 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -11,14 +11,6 @@ pub enum Opcode { rhs: RegisterIndex, result: RegisterIndex, }, - /// Takes the value in register `input` - /// Performs the specified unary operation - /// and stores the value in `result` register. - UnaryOp { - op: UnaryOp, - input: RegisterIndex, - result: RegisterIndex, - }, /// Sets the program counter to the value located at `destination` /// If the value at condition is non-zero JMPIF { @@ -37,22 +29,12 @@ pub enum Opcode { // TODO:This will be used to get data from an outside source Oracle, // TODO: This will be used to store a value at a particular index - Store, + RegisterStore, // TODO: This will be used to explicitly load a value at a particular index - Load, -} - -#[derive(Debug, Clone, Copy)] -pub enum UnaryOp { - Inv, -} - -impl UnaryOp { - pub fn function(&self) -> fn(Value) -> Value { - match self { - UnaryOp::Inv => |a: Value| a.inverse(), - } - } + RegisterLoad, + // + ArrayStore, + ArrayLoad, } #[derive(Debug, Clone, Copy)] From 7dfa3441adf2721ab01dc25afba42def7044d5ea Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Mon, 27 Mar 2023 21:51:05 +0100 Subject: [PATCH 005/125] refactor API: - use ValueKind - refactor opcodes needed --- brillig_bytecode/src/lib.rs | 21 +++++---------- brillig_bytecode/src/opcodes.rs | 42 ++++++++++++++++++++++------- brillig_bytecode/src/value.rs | 48 ++++++++++++++++----------------- 3 files changed, 63 insertions(+), 48 deletions(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index b8ad3d844..36c37b3be 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -10,6 +10,7 @@ mod value; pub use opcodes::{BinaryOp, Opcode}; pub use registers::{RegisterIndex, Registers}; +use value::Typ; pub use value::Value; #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -39,8 +40,8 @@ impl VM { pub fn process_opcode(&mut self) -> VMStatus { let opcode = &self.bytecode[self.program_counter]; match opcode { - Opcode::BinaryOp { op, lhs, rhs, result } => { - self.process_binary_op(*op, *lhs, *rhs, *result); + Opcode::BinaryOp { op, lhs, rhs, result, result_type } => { + self.process_binary_op(*op, *lhs, *rhs, *result, *result_type); self.increment_program_counter() } Opcode::JMP { destination } => self.set_program_counter(destination.inner()), @@ -56,9 +57,8 @@ impl VM { } Opcode::Call => todo!(), Opcode::Intrinsics => todo!(), - Opcode::Oracle => todo!(), - Opcode::Store => todo!(), - Opcode::Load => todo!(), + Opcode::Oracle { inputs, destination } => todo!(), + Opcode::Mov { destination, source } => todo!(), } } @@ -87,6 +87,7 @@ impl VM { lhs: RegisterIndex, rhs: RegisterIndex, result: RegisterIndex, + result_type: Typ, ) { let lhs_value = self.registers.get(lhs); let rhs_value = self.registers.get(rhs); @@ -96,15 +97,6 @@ impl VM { self.registers.set(result, result_value) } - /// Process a unary operation. - /// This method will not modify the program counter. - fn process_unary_op(&mut self, op: UnaryOp, input: RegisterIndex, result: RegisterIndex) { - let input_value = self.registers.get(input); - - let result_value = op.function()(input_value); - - self.registers.set(result, result_value) - } /// Returns the state of the registers. /// This consumes ownership of the VM and is conventionally /// called when all of the bytecode has been processed. @@ -127,6 +119,7 @@ fn add_single_step_smoke() { lhs: RegisterIndex(0), rhs: RegisterIndex(1), result: RegisterIndex(2), + result_type: Typ::Field, }; // Start VM diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index b7d4c65cf..0688a4574 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -1,11 +1,30 @@ -use crate::{value::Value, RegisterIndex}; +use acir::FieldElement; + +use crate::{ + value::{Typ, Value}, + RegisterIndex, +}; #[derive(Debug, Clone, Copy)] +pub struct ArrayIndex { + pointer: usize, + index: usize, +} + +#[derive(Debug, Clone, Copy)] +pub enum RegisterMemIndex { + Register(RegisterIndex), + Memory(ArrayIndex), + Value(FieldElement), +} + +#[derive(Debug, Clone)] pub enum Opcode { /// Takes the values in registers `lhs` and `rhs` /// Performs the specified binary operation /// and stores the value in the `result` register. BinaryOp { + result_type: Typ, op: BinaryOp, lhs: RegisterIndex, rhs: RegisterIndex, @@ -27,14 +46,14 @@ pub enum Opcode { // TODO:These are special functions like sha256 Intrinsics, // TODO:This will be used to get data from an outside source - Oracle, - // TODO: This will be used to store a value at a particular index - RegisterStore, - // TODO: This will be used to explicitly load a value at a particular index - RegisterLoad, - // - ArrayStore, - ArrayLoad, + Oracle { + inputs: Vec, + destination: Vec, + }, + Mov { + destination: RegisterMemIndex, + source: RegisterMemIndex, + }, } #[derive(Debug, Clone, Copy)] @@ -43,8 +62,12 @@ pub enum BinaryOp { Sub, Mul, Div, + Cmp(Comparison), } +#[derive(Debug, Clone, Copy)] +pub enum Comparison {} + impl BinaryOp { pub fn function(&self) -> fn(Value, Value) -> Value { match self { @@ -52,6 +75,7 @@ impl BinaryOp { BinaryOp::Sub => |a: Value, b: Value| a - b, BinaryOp::Mul => |a: Value, b: Value| a * b, BinaryOp::Div => |a: Value, b: Value| a / b, + BinaryOp::Cmp(_) => todo!(), } } } diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs index fbe825cf5..99c5e7bfb 100644 --- a/brillig_bytecode/src/value.rs +++ b/brillig_bytecode/src/value.rs @@ -2,29 +2,37 @@ use acir::FieldElement; use std::ops::{Add, Div, Mul, Neg, Sub}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] -/// Values that we can store in registers -pub enum Value { - Field(FieldElement), +pub enum Typ { + Field, + Unsigned { bit_size: u32 }, + Signed { bit_size: u32 }, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Value { + typ: Typ, + inner: FieldElement, } impl Value { /// Returns true if the Value represents `zero` pub fn is_zero(&self) -> bool { - match self { - Value::Field(val) => *val == FieldElement::zero(), - } + self.inner.is_zero() } /// Performs the multiplicative inverse of `Value` pub fn inverse(&self) -> Value { - match self { - Value::Field(val) => Value::Field(val.inverse()), - } + let value = match self.typ { + Typ::Field => self.inner.inverse(), + Typ::Unsigned { bit_size } => todo!("TODO"), + Typ::Signed { bit_size } => todo!("TODO"), + }; + Value { typ: self.typ, inner: value } } } impl From for Value { fn from(value: u128) -> Self { - Value::Field(FieldElement::from(value)) + Value { typ: Typ::Field, inner: FieldElement::from(value) } } } @@ -32,44 +40,34 @@ impl Add for Value { type Output = Value; fn add(self, rhs: Self) -> Self::Output { - match (self, rhs) { - (Value::Field(lhs), Value::Field(rhs)) => Value::Field(lhs + rhs), - } + Value { typ: self.typ, inner: self.inner + rhs.inner } } } impl Sub for Value { type Output = Value; fn sub(self, rhs: Self) -> Self::Output { - match (self, rhs) { - (Value::Field(lhs), Value::Field(rhs)) => Value::Field(lhs - rhs), - } + Value { typ: self.typ, inner: self.inner - rhs.inner } } } impl Mul for Value { type Output = Value; fn mul(self, rhs: Self) -> Self::Output { - match (self, rhs) { - (Value::Field(lhs), Value::Field(rhs)) => Value::Field(lhs * rhs), - } + Value { typ: self.typ, inner: self.inner * rhs.inner } } } impl Div for Value { type Output = Value; fn div(self, rhs: Self) -> Self::Output { - match (self, rhs) { - (Value::Field(lhs), Value::Field(rhs)) => Value::Field(lhs / rhs), - } + Value { typ: self.typ, inner: self.inner / rhs.inner } } } impl Neg for Value { type Output = Value; fn neg(self) -> Self::Output { - match self { - Value::Field(input) => Value::Field(-input), - } + Value { typ: self.typ, inner: -self.inner } } } From e93ae96bb55a962dc066caf667a0963483ef68bc Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Mon, 27 Mar 2023 22:29:47 +0100 Subject: [PATCH 006/125] re-export brillig bytecode --- acvm/Cargo.toml | 1 + acvm/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/acvm/Cargo.toml b/acvm/Cargo.toml index d89e39667..74ab94c73 100644 --- a/acvm/Cargo.toml +++ b/acvm/Cargo.toml @@ -16,6 +16,7 @@ blake2.workspace = true acir.workspace = true stdlib.workspace = true +brillig_bytecode = { version = "0.1.0", path = "../brillig_bytecode" } sha2 = "0.9.3" crc32fast = "1.3.2" diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index e25d96939..7d7ab36bf 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -24,7 +24,7 @@ use thiserror::Error; // re-export acir pub use acir; pub use acir::FieldElement; - +pub use brillig_bytecode; // This enum represents the different cases in which an // opcode can be unsolvable. // The most common being that one of its input has not been From a8f553e0afc83fcf331adbfa5ea3ae398b72e54e Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Tue, 28 Mar 2023 11:57:00 +0100 Subject: [PATCH 007/125] add brillig --- acir/Cargo.toml | 1 + acir/src/circuit/directives.rs | 31 ++++++++++++++++++++++++++++++- acir/src/circuit/opcodes.rs | 5 +++++ acir/src/lib.rs | 1 + 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/acir/Cargo.toml b/acir/Cargo.toml index 1ea1e3fce..2d60b7395 100644 --- a/acir/Cargo.toml +++ b/acir/Cargo.toml @@ -12,6 +12,7 @@ rust-version.workspace = true [dependencies] acir_field.workspace = true serde.workspace = true +brillig_bytecode = { version = "0.1.0", path = "../brillig_bytecode" } rmp-serde = "1.1.0" flate2 = "1.0.24" diff --git a/acir/src/circuit/directives.rs b/acir/src/circuit/directives.rs index 1ff637a8d..4c8870627 100644 --- a/acir/src/circuit/directives.rs +++ b/acir/src/circuit/directives.rs @@ -4,6 +4,7 @@ use crate::{ native_types::{Expression, Witness}, serialization::{read_n, read_u16, read_u32, write_bytes, write_u16, write_u32}, }; +use flate2::{bufread::DeflateEncoder, Compression}; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -33,6 +34,12 @@ pub enum Directive { radix: u32, }, + Brillig { + inputs: Vec, + outputs: Vec, + bytecode: Vec, + }, + // Sort directive, using a sorting network // This directive is used to generate the values of the control bits for the sorting network such that its outputs are properly sorted according to sort_by PermutationSort { @@ -52,6 +59,7 @@ impl Directive { Directive::ToLeRadix { .. } => "to_le_radix", Directive::PermutationSort { .. } => "permutation_sort", Directive::Log { .. } => "log", + Directive::Brillig { .. } => "brillig", } } fn to_u16(&self) -> u16 { @@ -61,6 +69,7 @@ impl Directive { Directive::ToLeRadix { .. } => 2, Directive::Log { .. } => 3, Directive::PermutationSort { .. } => 4, + Directive::Brillig { .. } => 5, } } @@ -120,6 +129,25 @@ impl Directive { } } }, + Directive::Brillig { inputs, outputs, bytecode } => { + let inputs_len = inputs.len() as u32; + write_u32(&mut writer, inputs_len)?; + for input in inputs { + input.write(&mut writer)? + } + + let outputs_len = outputs.len() as u32; + write_u32(&mut writer, outputs_len)?; + for output in outputs { + write_u32(&mut writer, output.witness_index())?; + } + + let buf = rmp_serde::to_vec(&bytecode).unwrap(); + let mut deflater = DeflateEncoder::new(buf.as_slice(), Compression::best()); + let mut buf_c = Vec::new(); + deflater.read_to_end(&mut buf_c).unwrap(); + write_bytes(&mut writer, &buf_c)?; + } }; Ok(()) @@ -234,8 +262,9 @@ fn serialization_roundtrip() { b: vec![Witness(1u32), Witness(2u32), Witness(3u32), Witness(4u32)], radix: 4, }; + let log = Directive::Log(LogInfo::FinalizedOutput("foo".to_owned())); - let directives = vec![invert, quotient_none, quotient_predicate, to_le_radix]; + let directives = vec![invert, quotient_none, quotient_predicate, to_le_radix, log]; for directive in directives { let (dir, got_dir) = read_write(directive); diff --git a/acir/src/circuit/opcodes.rs b/acir/src/circuit/opcodes.rs index 12ee35c55..3ebd25c17 100644 --- a/acir/src/circuit/opcodes.rs +++ b/acir/src/circuit/opcodes.rs @@ -379,6 +379,11 @@ impl std::fmt::Display for Opcode { witnesses.last().unwrap().witness_index() ), }, + Opcode::Directive(Directive::Brillig { inputs, outputs, bytecode }) => { + writeln!(f, "inputs: {:?}", inputs)?; + writeln!(f, "outputs: {:?}", outputs)?; + writeln!(f, "{:?}", bytecode) + } Opcode::Block(block) => { write!(f, "BLOCK ")?; write!(f, "(id: {}, len: {}) ", block.id.0, block.trace.len()) diff --git a/acir/src/lib.rs b/acir/src/lib.rs index 2f5654fa1..c8f024860 100644 --- a/acir/src/lib.rs +++ b/acir/src/lib.rs @@ -9,4 +9,5 @@ mod serialization; pub use acir_field; pub use acir_field::FieldElement; +pub use brillig_bytecode; pub use circuit::black_box_functions::BlackBoxFunc; From 0c32dcb1186e73dea932b7ad7bb1797b7522b554 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Tue, 28 Mar 2023 11:57:20 +0100 Subject: [PATCH 008/125] brillig is now re-exported from acir --- acvm/Cargo.toml | 1 - acvm/src/lib.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/acvm/Cargo.toml b/acvm/Cargo.toml index 74ab94c73..d89e39667 100644 --- a/acvm/Cargo.toml +++ b/acvm/Cargo.toml @@ -16,7 +16,6 @@ blake2.workspace = true acir.workspace = true stdlib.workspace = true -brillig_bytecode = { version = "0.1.0", path = "../brillig_bytecode" } sha2 = "0.9.3" crc32fast = "1.3.2" diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 7d7ab36bf..e7c41963d 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -24,7 +24,6 @@ use thiserror::Error; // re-export acir pub use acir; pub use acir::FieldElement; -pub use brillig_bytecode; // This enum represents the different cases in which an // opcode can be unsolvable. // The most common being that one of its input has not been From 022951970c23c6ef6cb9200e74cc9148a3653da8 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Tue, 28 Mar 2023 11:57:36 +0100 Subject: [PATCH 009/125] use acir_field instead of acir --- brillig_bytecode/Cargo.toml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/brillig_bytecode/Cargo.toml b/brillig_bytecode/Cargo.toml index 929ca522b..f64752a51 100644 --- a/brillig_bytecode/Cargo.toml +++ b/brillig_bytecode/Cargo.toml @@ -6,9 +6,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -acir.workspace = true +acir_field.workspace = true +serde.workspace = true [features] -bn254 = ["acir/bn254"] -bls12_381 = ["acir/bls12_381"] +bn254 = ["acir_field/bn254"] +bls12_381 = ["acir_field/bls12_381"] From 12b7f510ee1643ad38d7e2c320f9371e52ea5625 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Tue, 28 Mar 2023 11:58:09 +0100 Subject: [PATCH 010/125] wip --- brillig_bytecode/src/builder.rs | 5 +++++ brillig_bytecode/src/lib.rs | 2 ++ brillig_bytecode/src/memory.rs | 12 ++++++++++++ brillig_bytecode/src/opcodes.rs | 25 +++++++++++-------------- brillig_bytecode/src/registers.rs | 4 ++-- brillig_bytecode/src/value.rs | 11 ++++++++--- 6 files changed, 40 insertions(+), 19 deletions(-) create mode 100644 brillig_bytecode/src/builder.rs create mode 100644 brillig_bytecode/src/memory.rs diff --git a/brillig_bytecode/src/builder.rs b/brillig_bytecode/src/builder.rs new file mode 100644 index 000000000..aed029307 --- /dev/null +++ b/brillig_bytecode/src/builder.rs @@ -0,0 +1,5 @@ +use crate::Opcode; + +pub struct Builder { + opcodes : Vec +} \ No newline at end of file diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 36c37b3be..8741ac3e4 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -4,9 +4,11 @@ // This is a generalization over the fixed directives // that we have in ACVM. +mod memory; mod opcodes; mod registers; mod value; +mod builder; pub use opcodes::{BinaryOp, Opcode}; pub use registers::{RegisterIndex, Registers}; diff --git a/brillig_bytecode/src/memory.rs b/brillig_bytecode/src/memory.rs new file mode 100644 index 000000000..78d216404 --- /dev/null +++ b/brillig_bytecode/src/memory.rs @@ -0,0 +1,12 @@ +use serde::{Deserialize, Serialize}; +/// Memory in the VM is used for storing arrays +/// +/// ArrayIndex will be used to reference an Array element. +/// The pointer is needed to find it's location in memory, +/// and the index is used to offset from that point to find +/// the exact element. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub struct ArrayIndex { + pointer: usize, + index: usize, +} diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 0688a4574..a01e0d2d0 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -1,24 +1,21 @@ -use acir::FieldElement; - use crate::{ + memory::ArrayIndex, value::{Typ, Value}, RegisterIndex, }; +use acir_field::FieldElement; +use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, Copy)] -pub struct ArrayIndex { - pointer: usize, - index: usize, -} - -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum RegisterMemIndex { Register(RegisterIndex), + Constant(FieldElement), Memory(ArrayIndex), - Value(FieldElement), } -#[derive(Debug, Clone)] +pub type Label = usize; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum Opcode { /// Takes the values in registers `lhs` and `rhs` /// Performs the specified binary operation @@ -36,7 +33,7 @@ pub enum Opcode { condition: RegisterIndex, destination: RegisterIndex, }, - /// Sets the program counter to the value located at `destination` + /// Sets the program counter to the label. JMP { destination: RegisterIndex, }, @@ -56,7 +53,7 @@ pub enum Opcode { }, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum BinaryOp { Add, Sub, @@ -65,7 +62,7 @@ pub enum BinaryOp { Cmp(Comparison), } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum Comparison {} impl BinaryOp { diff --git a/brillig_bytecode/src/registers.rs b/brillig_bytecode/src/registers.rs index 8563235fb..0f241dd54 100644 --- a/brillig_bytecode/src/registers.rs +++ b/brillig_bytecode/src/registers.rs @@ -1,12 +1,12 @@ use crate::Value; - +use serde::{Deserialize, Serialize}; /// Registers will store field element values during the /// duration of the execution of the bytecode. pub struct Registers { inner: Vec, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub struct RegisterIndex(pub usize); impl RegisterIndex { diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs index 99c5e7bfb..7c5209c62 100644 --- a/brillig_bytecode/src/value.rs +++ b/brillig_bytecode/src/value.rs @@ -1,13 +1,16 @@ -use acir::FieldElement; +use acir_field::FieldElement; +use serde::{Deserialize, Serialize}; use std::ops::{Add, Div, Mul, Neg, Sub}; -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +/// Types of values allowed in the VM +#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum Typ { Field, Unsigned { bit_size: u32 }, Signed { bit_size: u32 }, } +/// Value represents a Value in the VM #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Value { typ: Typ, @@ -23,7 +26,9 @@ impl Value { pub fn inverse(&self) -> Value { let value = match self.typ { Typ::Field => self.inner.inverse(), - Typ::Unsigned { bit_size } => todo!("TODO"), + Typ::Unsigned { bit_size } => { + todo!("TODO") + } Typ::Signed { bit_size } => todo!("TODO"), }; Value { typ: self.typ, inner: value } From 327fe7594a9aefce3d9e18bfde1d6826fbb437a1 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Tue, 28 Mar 2023 12:00:16 +0100 Subject: [PATCH 011/125] add directive todo --- acvm/src/pwg/directives.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/acvm/src/pwg/directives.rs b/acvm/src/pwg/directives.rs index 2feb2b231..c5d3af978 100644 --- a/acvm/src/pwg/directives.rs +++ b/acvm/src/pwg/directives.rs @@ -150,6 +150,7 @@ pub fn solve_directives( Ok(()) } + Directive::Brillig { inputs, outputs, bytecode } => todo!("Maxim"), } } From 5cb9f3aaacf87305f5266e440e834845d6eeb705 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Tue, 28 Mar 2023 14:45:51 +0100 Subject: [PATCH 012/125] feat: Add serialization for brillig bytecode (#158) * add serialization for brillig * cargo fmt --- acir/src/circuit/directives.rs | 41 +++++++++++++++++++++++++++------ brillig_bytecode/src/builder.rs | 4 ++-- brillig_bytecode/src/lib.rs | 2 +- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/acir/src/circuit/directives.rs b/acir/src/circuit/directives.rs index d1997b39d..f6d06530f 100644 --- a/acir/src/circuit/directives.rs +++ b/acir/src/circuit/directives.rs @@ -4,7 +4,6 @@ use crate::{ native_types::{Expression, Witness}, serialization::{read_bytes, read_n, read_u16, read_u32, write_bytes, write_u16, write_u32}, }; -use flate2::{bufread::DeflateEncoder, Compression}; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -146,11 +145,10 @@ impl Directive { write_u32(&mut writer, output.witness_index())?; } - let buf = rmp_serde::to_vec(&bytecode).unwrap(); - let mut deflater = DeflateEncoder::new(buf.as_slice(), Compression::best()); - let mut buf_c = Vec::new(); - deflater.read_to_end(&mut buf_c).unwrap(); - write_bytes(&mut writer, &buf_c)?; + // TODO: We use rmp_serde as its easier than doing it manually + let buffer = rmp_serde::to_vec(&bytecode).unwrap(); + write_u32(&mut writer, buffer.len() as u32)?; + write_bytes(&mut writer, &buffer)?; } }; @@ -240,6 +238,27 @@ impl Directive { }; Ok(Directive::Log(log_info)) } + 5 => { + let inputs_len = read_u32(&mut reader)?; + let mut inputs = Vec::with_capacity(inputs_len as usize); + for _ in 0..inputs_len { + inputs.push(Expression::read(&mut reader)?); + } + + let outputs_len = read_u32(&mut reader)?; + let mut outputs = Vec::with_capacity(outputs_len as usize); + for _ in 0..outputs_len { + outputs.push(Witness(read_u32(&mut reader)?)); + } + + let buffer_len = read_u32(&mut reader)?; + let mut buffer = vec![0u8; buffer_len as usize]; + reader.read_exact(&mut buffer)?; + let opcodes: Vec = + rmp_serde::from_slice(&buffer).unwrap(); + + Ok(Directive::Brillig { inputs, outputs, bytecode: opcodes }) + } _ => Err(std::io::ErrorKind::InvalidData.into()), } @@ -297,7 +316,6 @@ fn serialization_roundtrip() { b: vec![Witness(1u32), Witness(2u32), Witness(3u32), Witness(4u32)], radix: 4, }; - let log = Directive::Log(LogInfo::FinalizedOutput("foo".to_owned())); let permutation_sort = Directive::PermutationSort { inputs: vec![vec![Expression::default()], vec![Expression::default()]], @@ -310,6 +328,14 @@ fn serialization_roundtrip() { let log_witnesses = Directive::Log(LogInfo::WitnessOutput(vec![Witness(1u32), Witness(2u32), Witness(3u32)])); + let brillig = Directive::Brillig { + inputs: vec![Expression::default()], + outputs: vec![Witness(2), Witness(3)], + bytecode: vec![brillig_bytecode::Opcode::JMP { + destination: brillig_bytecode::RegisterIndex(10), + }], + }; + let directives = vec![ invert, quotient_none, @@ -318,6 +344,7 @@ fn serialization_roundtrip() { log_string, log_witnesses, permutation_sort, + brillig, ]; for directive in directives { diff --git a/brillig_bytecode/src/builder.rs b/brillig_bytecode/src/builder.rs index aed029307..56c16bf70 100644 --- a/brillig_bytecode/src/builder.rs +++ b/brillig_bytecode/src/builder.rs @@ -1,5 +1,5 @@ use crate::Opcode; pub struct Builder { - opcodes : Vec -} \ No newline at end of file + opcodes: Vec, +} diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 8741ac3e4..e4cc52b1a 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -4,11 +4,11 @@ // This is a generalization over the fixed directives // that we have in ACVM. +mod builder; mod memory; mod opcodes; mod registers; mod value; -mod builder; pub use opcodes::{BinaryOp, Opcode}; pub use registers::{RegisterIndex, Registers}; From ca4fe1a9375f0129b1f2b0e955cc18e4ba5a57b1 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 28 Mar 2023 14:50:54 +0100 Subject: [PATCH 013/125] feat: Solve Brillig Directive (#159) * solve the brillig directive * move brilling_bytecode out of acvm * go back to correct process_opcodes * cargo fmt * switch to using insert_witness method --- acvm/src/pwg/directives.rs | 23 ++++++++++++++++++++++- brillig_bytecode/src/registers.rs | 2 +- brillig_bytecode/src/value.rs | 8 +++++++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/acvm/src/pwg/directives.rs b/acvm/src/pwg/directives.rs index c5d3af978..8d93845cd 100644 --- a/acvm/src/pwg/directives.rs +++ b/acvm/src/pwg/directives.rs @@ -150,7 +150,28 @@ pub fn solve_directives( Ok(()) } - Directive::Brillig { inputs, outputs, bytecode } => todo!("Maxim"), + Directive::Brillig { inputs, outputs, bytecode } => { + let mut input_register_values: Vec = Vec::new(); + for expr in inputs { + let expr_value = get_value(expr, initial_witness)?; + input_register_values.push(expr_value.into()) + } + let input_registers = + acir::brillig_bytecode::Registers { inner: input_register_values }; + + let vm = acir::brillig_bytecode::VM::new(input_registers, bytecode.clone()); + + let output_registers = vm.process_opcodes(); + + let output_register_values: Vec = + output_registers.inner.into_iter().map(|v| v.inner).collect::>(); + + for (witness, value) in outputs.into_iter().zip(output_register_values) { + insert_witness(*witness, value, initial_witness)?; + } + + Ok(()) + } } } diff --git a/brillig_bytecode/src/registers.rs b/brillig_bytecode/src/registers.rs index 0f241dd54..72299eba5 100644 --- a/brillig_bytecode/src/registers.rs +++ b/brillig_bytecode/src/registers.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; /// Registers will store field element values during the /// duration of the execution of the bytecode. pub struct Registers { - inner: Vec, + pub inner: Vec, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs index 7c5209c62..fc8b65498 100644 --- a/brillig_bytecode/src/value.rs +++ b/brillig_bytecode/src/value.rs @@ -14,7 +14,7 @@ pub enum Typ { #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Value { typ: Typ, - inner: FieldElement, + pub inner: FieldElement, } impl Value { @@ -41,6 +41,12 @@ impl From for Value { } } +impl From for Value { + fn from(value: FieldElement) -> Self { + Value { typ: Typ::Field, inner: value } + } +} + impl Add for Value { type Output = Value; From 5f2ebf1fc2ef1552ec52704d95b6248d322cc1da Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Tue, 28 Mar 2023 14:52:30 +0100 Subject: [PATCH 014/125] RegisterIndex -> RegisterMemIndex --- brillig_bytecode/src/opcodes.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index a01e0d2d0..3d5e55381 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -23,19 +23,19 @@ pub enum Opcode { BinaryOp { result_type: Typ, op: BinaryOp, - lhs: RegisterIndex, - rhs: RegisterIndex, + lhs: RegisterMemIndex, + rhs: RegisterMemIndex, result: RegisterIndex, }, /// Sets the program counter to the value located at `destination` /// If the value at condition is non-zero JMPIF { - condition: RegisterIndex, - destination: RegisterIndex, + condition: RegisterMemIndex, + destination: Label, }, /// Sets the program counter to the label. JMP { - destination: RegisterIndex, + destination: Label, }, // TODO:This is used to call functions and setup things like // TODO execution contexts. @@ -44,7 +44,7 @@ pub enum Opcode { Intrinsics, // TODO:This will be used to get data from an outside source Oracle { - inputs: Vec, + inputs: Vec, destination: Vec, }, Mov { From 92a8f77cc33f43b4563d05c60098297c1dc26fe8 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Tue, 28 Mar 2023 14:57:12 +0100 Subject: [PATCH 015/125] - use `Label` - RegisterIndex -> RegisterMemIndex --- brillig_bytecode/src/lib.rs | 23 ++++++++++++----------- brillig_bytecode/src/registers.rs | 10 +++++++--- brillig_bytecode/src/value.rs | 2 +- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index e4cc52b1a..73bc04239 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -10,9 +10,10 @@ mod opcodes; mod registers; mod value; +use opcodes::RegisterMemIndex; pub use opcodes::{BinaryOp, Opcode}; pub use registers::{RegisterIndex, Registers}; -use value::Typ; +pub use value::Typ; pub use value::Value; #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -46,15 +47,15 @@ impl VM { self.process_binary_op(*op, *lhs, *rhs, *result, *result_type); self.increment_program_counter() } - Opcode::JMP { destination } => self.set_program_counter(destination.inner()), + Opcode::JMP { destination } => todo!(), Opcode::JMPIF { condition, destination } => { // Check if condition is true // We use 0 to mean false and any other value to mean true - let condition_value = self.registers.get(*condition); - if !condition_value.is_zero() { - return self.set_program_counter(destination.inner()); - } + // let condition_value = self.registers.get(*condition); + // if !condition_value.is_zero() { + // return self.set_program_counter(destination.inner()); + // } self.status } Opcode::Call => todo!(), @@ -86,8 +87,8 @@ impl VM { fn process_binary_op( &mut self, op: BinaryOp, - lhs: RegisterIndex, - rhs: RegisterIndex, + lhs: RegisterMemIndex, + rhs: RegisterMemIndex, result: RegisterIndex, result_type: Typ, ) { @@ -118,8 +119,8 @@ fn add_single_step_smoke() { // and place the output in register `2` let opcode = Opcode::BinaryOp { op: BinaryOp::Add, - lhs: RegisterIndex(0), - rhs: RegisterIndex(1), + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(1)), result: RegisterIndex(2), result_type: Typ::Field, }; @@ -137,7 +138,7 @@ fn add_single_step_smoke() { // The register at index `2` should have the value of 3 since we had an // add opcode let registers = vm.finish(); - let output_value = registers.get(RegisterIndex(2)); + let output_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); assert_eq!(output_value, Value::from(3u128)) } diff --git a/brillig_bytecode/src/registers.rs b/brillig_bytecode/src/registers.rs index 72299eba5..4552ffbb7 100644 --- a/brillig_bytecode/src/registers.rs +++ b/brillig_bytecode/src/registers.rs @@ -1,4 +1,4 @@ -use crate::Value; +use crate::{opcodes::RegisterMemIndex, Typ, Value}; use serde::{Deserialize, Serialize}; /// Registers will store field element values during the /// duration of the execution of the bytecode. @@ -21,8 +21,12 @@ impl Registers { Self { inner: values } } /// Gets the values at register with address `index` - pub fn get(&self, index: RegisterIndex) -> Value { - self.inner[index.inner()] + pub fn get(&self, index: RegisterMemIndex) -> Value { + match index { + RegisterMemIndex::Register(register) => self.inner[register.inner()], + RegisterMemIndex::Constant(constant) => Value { typ: Typ::Field, inner: constant }, + RegisterMemIndex::Memory(_) => todo!("we will implement memory later"), + } } /// Sets the value at register with address `index` to `value` pub fn set(&mut self, index: RegisterIndex, value: Value) { diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs index fc8b65498..edd87d92f 100644 --- a/brillig_bytecode/src/value.rs +++ b/brillig_bytecode/src/value.rs @@ -13,7 +13,7 @@ pub enum Typ { /// Value represents a Value in the VM #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Value { - typ: Typ, + pub typ: Typ, pub inner: FieldElement, } From b57bf11db94b930c3bbae7d354c82590eeab1d83 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Tue, 28 Mar 2023 14:58:37 +0100 Subject: [PATCH 016/125] export RegisterMemIndex --- brillig_bytecode/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 73bc04239..85004d018 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -10,7 +10,7 @@ mod opcodes; mod registers; mod value; -use opcodes::RegisterMemIndex; +pub use opcodes::RegisterMemIndex; pub use opcodes::{BinaryOp, Opcode}; pub use registers::{RegisterIndex, Registers}; pub use value::Typ; From 12a53df5952331be9e07af35c54a9e9446ea3827 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Tue, 28 Mar 2023 15:01:17 +0100 Subject: [PATCH 017/125] conversion func for registerIndex --- brillig_bytecode/src/opcodes.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 3d5e55381..3d3573d54 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -13,6 +13,15 @@ pub enum RegisterMemIndex { Memory(ArrayIndex), } +impl RegisterMemIndex { + pub fn to_register_index(self) -> Option { + match self { + RegisterMemIndex::Register(register) => Some(register), + RegisterMemIndex::Constant(_) | RegisterMemIndex::Memory(_) => None, + } + } +} + pub type Label = usize; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] From 824ccc491c892c266a755dcb30862f990807c894 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Tue, 28 Mar 2023 15:08:56 +0100 Subject: [PATCH 018/125] add jmpifnot --- brillig_bytecode/src/lib.rs | 1 + brillig_bytecode/src/opcodes.rs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 85004d018..d6f485ae2 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -62,6 +62,7 @@ impl VM { Opcode::Intrinsics => todo!(), Opcode::Oracle { inputs, destination } => todo!(), Opcode::Mov { destination, source } => todo!(), + Opcode::JMPIFNOT { condition, destination } => todo!(), } } diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 3d3573d54..90aae2054 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -36,6 +36,10 @@ pub enum Opcode { rhs: RegisterMemIndex, result: RegisterIndex, }, + JMPIFNOT { + condition: RegisterMemIndex, + destination: Label, + }, /// Sets the program counter to the value located at `destination` /// If the value at condition is non-zero JMPIF { From 0b7bd3d93f82a21d2e820f947614ccef9e907060 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 28 Mar 2023 17:05:58 +0100 Subject: [PATCH 019/125] feat: Add trap opcode (#161) * trap opcode in Opcode enum * process trap opcode --- brillig_bytecode/src/lib.rs | 2 ++ brillig_bytecode/src/opcodes.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index d6f485ae2..23b9f2a0e 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -20,6 +20,7 @@ pub use value::Value; pub enum VMStatus { Halted, InProgress, + Failure, } pub struct VM { @@ -62,6 +63,7 @@ impl VM { Opcode::Intrinsics => todo!(), Opcode::Oracle { inputs, destination } => todo!(), Opcode::Mov { destination, source } => todo!(), + Opcode::Trap => VMStatus::Failure, Opcode::JMPIFNOT { condition, destination } => todo!(), } } diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 90aae2054..8c9fc8e52 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -64,6 +64,8 @@ pub enum Opcode { destination: RegisterMemIndex, source: RegisterMemIndex, }, + /// Used if execution fails during evaluation + Trap } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] From cb70b7ddf38cee6d0276f24a35a18f4dc39dcc02 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 28 Mar 2023 18:17:00 +0100 Subject: [PATCH 020/125] feat: Brillig JMP and JMPIF (#162) * brillig jmp process_opcode * making jump test * starting jmp opcode test * test brillig jumps * debug cleanup * remove comments from Value in vm --- acir/src/circuit/directives.rs | 4 +-- brillig_bytecode/src/lib.rs | 55 +++++++++++++++++++++++++++---- brillig_bytecode/src/opcodes.rs | 8 +++-- brillig_bytecode/src/registers.rs | 2 ++ brillig_bytecode/src/value.rs | 10 ++++++ 5 files changed, 67 insertions(+), 12 deletions(-) diff --git a/acir/src/circuit/directives.rs b/acir/src/circuit/directives.rs index f6d06530f..df902bbb2 100644 --- a/acir/src/circuit/directives.rs +++ b/acir/src/circuit/directives.rs @@ -331,9 +331,7 @@ fn serialization_roundtrip() { let brillig = Directive::Brillig { inputs: vec![Expression::default()], outputs: vec![Witness(2), Witness(3)], - bytecode: vec![brillig_bytecode::Opcode::JMP { - destination: brillig_bytecode::RegisterIndex(10), - }], + bytecode: vec![brillig_bytecode::Opcode::JMP { destination: 10 }], }; let directives = vec![ diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 23b9f2a0e..977e76450 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -11,7 +11,7 @@ mod registers; mod value; pub use opcodes::RegisterMemIndex; -pub use opcodes::{BinaryOp, Opcode}; +pub use opcodes::{BinaryOp, Opcode, Comparison}; pub use registers::{RegisterIndex, Registers}; pub use value::Typ; pub use value::Value; @@ -23,6 +23,7 @@ pub enum VMStatus { Failure, } +#[derive(Debug, PartialEq, Eq, Clone)] pub struct VM { registers: Registers, program_counter: usize, @@ -48,15 +49,14 @@ impl VM { self.process_binary_op(*op, *lhs, *rhs, *result, *result_type); self.increment_program_counter() } - Opcode::JMP { destination } => todo!(), + Opcode::JMP { destination } => self.set_program_counter(*destination), Opcode::JMPIF { condition, destination } => { // Check if condition is true // We use 0 to mean false and any other value to mean true - - // let condition_value = self.registers.get(*condition); - // if !condition_value.is_zero() { - // return self.set_program_counter(destination.inner()); - // } + let condition_value = self.registers.get(*condition); + if !condition_value.is_zero() { + return self.set_program_counter(*destination); + } self.status } Opcode::Call => todo!(), @@ -145,3 +145,44 @@ fn add_single_step_smoke() { assert_eq!(output_value, Value::from(3u128)) } + +#[test] +fn test_jmpif_opcode() { + let input_registers = Registers::load(vec![ + Value::from(2u128), + Value::from(2u128), + Value::from(0u128), + Value::from(5u128), + Value::from(6u128), + Value::from(10u128), + ]); + + let equal_cmp_opcode = Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Equal), + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(1)), + result: RegisterIndex(2), + }; + + let jump_opcode = Opcode::JMP { destination: 2 }; + + let jump_if_opcode = + Opcode::JMPIF { condition: RegisterMemIndex::Register(RegisterIndex(2)), destination: 3 }; + + let mut vm = VM::new(input_registers, vec![equal_cmp_opcode, jump_opcode, jump_if_opcode]); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(output_cmp_value, Value::from(true)); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::Halted); + + vm.finish(); +} diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 8c9fc8e52..7d711887b 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -78,7 +78,10 @@ pub enum BinaryOp { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub enum Comparison {} +pub enum Comparison { + NotEqual, + Equal, +} impl BinaryOp { pub fn function(&self) -> fn(Value, Value) -> Value { @@ -87,7 +90,8 @@ impl BinaryOp { BinaryOp::Sub => |a: Value, b: Value| a - b, BinaryOp::Mul => |a: Value, b: Value| a * b, BinaryOp::Div => |a: Value, b: Value| a / b, - BinaryOp::Cmp(_) => todo!(), + // TODO: only support equal and not equal, need less than, greater than, etc. + BinaryOp::Cmp(_) => |a: Value, b: Value| (a == b).into(), } } } diff --git a/brillig_bytecode/src/registers.rs b/brillig_bytecode/src/registers.rs index 4552ffbb7..874547139 100644 --- a/brillig_bytecode/src/registers.rs +++ b/brillig_bytecode/src/registers.rs @@ -1,7 +1,9 @@ use crate::{opcodes::RegisterMemIndex, Typ, Value}; use serde::{Deserialize, Serialize}; + /// Registers will store field element values during the /// duration of the execution of the bytecode. +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Registers { pub inner: Vec, } diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs index edd87d92f..84484dd86 100644 --- a/brillig_bytecode/src/value.rs +++ b/brillig_bytecode/src/value.rs @@ -47,6 +47,16 @@ impl From for Value { } } +impl From for Value { + fn from(value: bool) -> Self { + if value { + Value { typ: Typ::Unsigned { bit_size: 1 }, inner: FieldElement::one() } + } else { + Value { typ: Typ::Unsigned { bit_size: 1 }, inner: FieldElement::zero() } + } + } +} + impl Add for Value { type Output = Value; From 59eb20a7b7390f7f4457388483529b3463a1e734 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 28 Mar 2023 18:47:58 +0100 Subject: [PATCH 021/125] fix to stop VM on Trap opcode (#163) --- brillig_bytecode/src/lib.rs | 2 +- brillig_bytecode/src/opcodes.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 977e76450..3eb8978d5 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -38,7 +38,7 @@ impl VM { /// Loop over the bytecode and update the program counter pub fn process_opcodes(mut self) -> Registers { - while self.process_opcode() != VMStatus::Halted {} + while !matches!(self.process_opcode(), VMStatus::Halted | VMStatus::Failure) {} self.finish() } // Process a single opcode and modify the program counter diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 7d711887b..e8e054401 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -65,7 +65,7 @@ pub enum Opcode { source: RegisterMemIndex, }, /// Used if execution fails during evaluation - Trap + Trap, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] From 3a197aa548214d0002eb31e8049f6ec7194dde13 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 28 Mar 2023 19:57:55 +0100 Subject: [PATCH 022/125] feat: JMPIFNOT (#165) * jmp if not opcode and test * jmp if not opode and test * fix jmpifnot test * carog fmt * remove unnecessary registers in jmpif test * cargo fmt --- brillig_bytecode/src/lib.rs | 76 ++++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 3eb8978d5..67ff22a64 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -11,7 +11,7 @@ mod registers; mod value; pub use opcodes::RegisterMemIndex; -pub use opcodes::{BinaryOp, Opcode, Comparison}; +pub use opcodes::{BinaryOp, Comparison, Opcode}; pub use registers::{RegisterIndex, Registers}; pub use value::Typ; pub use value::Value; @@ -59,12 +59,18 @@ impl VM { } self.status } + Opcode::JMPIFNOT { condition, destination } => { + let condition_value = self.registers.get(*condition); + if condition_value.is_zero() { + return self.set_program_counter(*destination); + } + self.status + } Opcode::Call => todo!(), Opcode::Intrinsics => todo!(), Opcode::Oracle { inputs, destination } => todo!(), Opcode::Mov { destination, source } => todo!(), Opcode::Trap => VMStatus::Failure, - Opcode::JMPIFNOT { condition, destination } => todo!(), } } @@ -148,14 +154,8 @@ fn add_single_step_smoke() { #[test] fn test_jmpif_opcode() { - let input_registers = Registers::load(vec![ - Value::from(2u128), - Value::from(2u128), - Value::from(0u128), - Value::from(5u128), - Value::from(6u128), - Value::from(10u128), - ]); + let input_registers = + Registers::load(vec![Value::from(2u128), Value::from(2u128), Value::from(0u128)]); let equal_cmp_opcode = Opcode::BinaryOp { result_type: Typ::Field, @@ -186,3 +186,59 @@ fn test_jmpif_opcode() { vm.finish(); } + +#[test] +fn test_jmpifnot_opcode() { + let input_registers = + Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(0u128)]); + + let trap_opcode = Opcode::Trap; + + let not_equal_cmp_opcode = Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Equal), + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(1)), + result: RegisterIndex(2), + }; + + let jump_opcode = Opcode::JMP { destination: 2 }; + + let jump_if_not_opcode = Opcode::JMPIFNOT { + condition: RegisterMemIndex::Register(RegisterIndex(2)), + destination: 1, + }; + + let add_opcode = Opcode::BinaryOp { + op: BinaryOp::Add, + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(1)), + result: RegisterIndex(2), + result_type: Typ::Field, + }; + + let mut vm = VM::new( + input_registers, + vec![jump_opcode, trap_opcode, not_equal_cmp_opcode, jump_if_not_opcode, add_opcode], + ); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(output_cmp_value, Value::from(false)); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::Failure); + + // The register at index `2` should have not changed as we jumped over the add opcode + let registers = vm.finish(); + let output_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(output_value, Value::from(false)); +} From 01d21c6dcaf4367a83df01d30eb368ba29b904cd Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 28 Mar 2023 21:58:28 +0100 Subject: [PATCH 023/125] feat: Mov Opcode in Brillig (#166) * MOV opcode in brillig and a basic test * cargo fmt * fixed move to not only move source into dest, not a full swap * cargo fmt --- brillig_bytecode/src/lib.rs | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 67ff22a64..1c3267cef 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -69,7 +69,18 @@ impl VM { Opcode::Call => todo!(), Opcode::Intrinsics => todo!(), Opcode::Oracle { inputs, destination } => todo!(), - Opcode::Mov { destination, source } => todo!(), + Opcode::Mov { destination, source } => { + let source_value = self.registers.get(*source); + + match destination { + RegisterMemIndex::Register(dest_index) => { + self.registers.set(*dest_index, source_value) + } + _ => return VMStatus::Failure, // TODO: add variants to VMStatus::Failure for more informed failures + } + + self.increment_program_counter() + } Opcode::Trap => VMStatus::Failure, } } @@ -242,3 +253,27 @@ fn test_jmpifnot_opcode() { let output_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); assert_eq!(output_value, Value::from(false)); } + +#[test] +fn test_mov_opcode() { + let input_registers = + Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(3u128)]); + + let mov_opcode = Opcode::Mov { + destination: RegisterMemIndex::Register(RegisterIndex(2)), + source: RegisterMemIndex::Register(RegisterIndex(0)), + }; + + let mut vm = VM::new(input_registers, vec![mov_opcode]); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::Halted); + + let registers = vm.finish(); + + let destination_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(destination_value, Value::from(1u128)); + + let source_value = registers.get(RegisterMemIndex::Register(RegisterIndex(0))); + assert_eq!(source_value, Value::from(1u128)); +} From dcae6b3da8d952fe4f5335a37bdc0d96a5bc372b Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 28 Mar 2023 22:02:29 +0100 Subject: [PATCH 024/125] feat: Comparison Binary Ops (#167) * add lt and lte comparisons to BinaryOp + tests * briefly delete test * cargo fmt * temp remove test --- brillig_bytecode/src/lib.rs | 78 ++++++++++++++++++++++++++++++++- brillig_bytecode/src/opcodes.rs | 12 +++-- brillig_bytecode/src/value.rs | 4 +- 3 files changed, 86 insertions(+), 8 deletions(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 1c3267cef..70f5b6673 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -170,7 +170,7 @@ fn test_jmpif_opcode() { let equal_cmp_opcode = Opcode::BinaryOp { result_type: Typ::Field, - op: BinaryOp::Cmp(Comparison::Equal), + op: BinaryOp::Cmp(Comparison::Eq), lhs: RegisterMemIndex::Register(RegisterIndex(0)), rhs: RegisterMemIndex::Register(RegisterIndex(1)), result: RegisterIndex(2), @@ -207,7 +207,7 @@ fn test_jmpifnot_opcode() { let not_equal_cmp_opcode = Opcode::BinaryOp { result_type: Typ::Field, - op: BinaryOp::Cmp(Comparison::Equal), + op: BinaryOp::Cmp(Comparison::Eq), lhs: RegisterMemIndex::Register(RegisterIndex(0)), rhs: RegisterMemIndex::Register(RegisterIndex(1)), result: RegisterIndex(2), @@ -277,3 +277,77 @@ fn test_mov_opcode() { let source_value = registers.get(RegisterMemIndex::Register(RegisterIndex(0))); assert_eq!(source_value, Value::from(1u128)); } + +#[test] +fn test_cmp_binary_ops() { + let input_registers = Registers::load(vec![ + Value::from(2u128), + Value::from(2u128), + Value::from(0u128), + Value::from(5u128), + Value::from(6u128), + ]); + + let equal_opcode = Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Eq), + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(1)), + result: RegisterIndex(2), + }; + + let not_equal_opcode = Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Eq), + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(3)), + result: RegisterIndex(2), + }; + + let less_than_opcode = Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Lt), + lhs: RegisterMemIndex::Register(RegisterIndex(3)), + rhs: RegisterMemIndex::Register(RegisterIndex(4)), + result: RegisterIndex(2), + }; + + let less_than_equal_opcode = Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Lte), + lhs: RegisterMemIndex::Register(RegisterIndex(3)), + rhs: RegisterMemIndex::Register(RegisterIndex(4)), + result: RegisterIndex(2), + }; + + let mut vm = VM::new( + input_registers, + vec![equal_opcode, not_equal_opcode, less_than_opcode, less_than_equal_opcode], + ); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let output_eq_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(output_eq_value, Value::from(true)); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let output_neq_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(output_neq_value, Value::from(false)); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let lt_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(lt_value, Value::from(true)); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::Halted); + + let lte_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(lte_value, Value::from(true)); + + vm.finish(); +} diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index e8e054401..fca46f4ee 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -79,8 +79,9 @@ pub enum BinaryOp { #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum Comparison { - NotEqual, - Equal, + Eq, //(==) equal + Lt, //(<) field less + Lte, //(<=) field less or equal } impl BinaryOp { @@ -90,8 +91,11 @@ impl BinaryOp { BinaryOp::Sub => |a: Value, b: Value| a - b, BinaryOp::Mul => |a: Value, b: Value| a * b, BinaryOp::Div => |a: Value, b: Value| a / b, - // TODO: only support equal and not equal, need less than, greater than, etc. - BinaryOp::Cmp(_) => |a: Value, b: Value| (a == b).into(), + BinaryOp::Cmp(comparison) => match comparison { + Comparison::Eq => |a: Value, b: Value| (a == b).into(), + Comparison::Lt => |a: Value, b: Value| (a.inner < b.inner).into(), + Comparison::Lte => |a: Value, b: Value| (a.inner <= b.inner).into(), + }, } } } diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs index 84484dd86..d5bd3d9fb 100644 --- a/brillig_bytecode/src/value.rs +++ b/brillig_bytecode/src/value.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use std::ops::{Add, Div, Mul, Neg, Sub}; /// Types of values allowed in the VM -#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)] pub enum Typ { Field, Unsigned { bit_size: u32 }, @@ -11,7 +11,7 @@ pub enum Typ { } /// Value represents a Value in the VM -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Value { pub typ: Typ, pub inner: FieldElement, From d1e9c59e93187954d31f5b684cc2189ec5916766 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 29 Mar 2023 13:36:39 +0100 Subject: [PATCH 025/125] move Brillig from being a directive to an Opcode (#169) --- acir/src/circuit/directives.rs | 54 ----------------- acir/src/circuit/opcodes.rs | 70 ++++++++++++++++++++-- acvm/src/compiler/transformers/fallback.rs | 3 +- acvm/src/lib.rs | 8 ++- acvm/src/pwg.rs | 2 + acvm/src/pwg/brillig.rs | 36 +++++++++++ acvm/src/pwg/directives.rs | 22 ------- brillig_bytecode/Cargo.toml | 1 - 8 files changed, 112 insertions(+), 84 deletions(-) create mode 100644 acvm/src/pwg/brillig.rs diff --git a/acir/src/circuit/directives.rs b/acir/src/circuit/directives.rs index df902bbb2..e7bcb3542 100644 --- a/acir/src/circuit/directives.rs +++ b/acir/src/circuit/directives.rs @@ -33,12 +33,6 @@ pub enum Directive { radix: u32, }, - Brillig { - inputs: Vec, - outputs: Vec, - bytecode: Vec, - }, - // Sort directive, using a sorting network // This directive is used to generate the values of the control bits for the sorting network such that its outputs are properly sorted according to sort_by PermutationSort { @@ -58,7 +52,6 @@ impl Directive { Directive::ToLeRadix { .. } => "to_le_radix", Directive::PermutationSort { .. } => "permutation_sort", Directive::Log { .. } => "log", - Directive::Brillig { .. } => "brillig", } } fn to_u16(&self) -> u16 { @@ -68,7 +61,6 @@ impl Directive { Directive::ToLeRadix { .. } => 2, Directive::PermutationSort { .. } => 3, Directive::Log { .. } => 4, - Directive::Brillig { .. } => 5, } } @@ -132,24 +124,6 @@ impl Directive { } } } - Directive::Brillig { inputs, outputs, bytecode } => { - let inputs_len = inputs.len() as u32; - write_u32(&mut writer, inputs_len)?; - for input in inputs { - input.write(&mut writer)? - } - - let outputs_len = outputs.len() as u32; - write_u32(&mut writer, outputs_len)?; - for output in outputs { - write_u32(&mut writer, output.witness_index())?; - } - - // TODO: We use rmp_serde as its easier than doing it manually - let buffer = rmp_serde::to_vec(&bytecode).unwrap(); - write_u32(&mut writer, buffer.len() as u32)?; - write_bytes(&mut writer, &buffer)?; - } }; Ok(()) @@ -238,27 +212,6 @@ impl Directive { }; Ok(Directive::Log(log_info)) } - 5 => { - let inputs_len = read_u32(&mut reader)?; - let mut inputs = Vec::with_capacity(inputs_len as usize); - for _ in 0..inputs_len { - inputs.push(Expression::read(&mut reader)?); - } - - let outputs_len = read_u32(&mut reader)?; - let mut outputs = Vec::with_capacity(outputs_len as usize); - for _ in 0..outputs_len { - outputs.push(Witness(read_u32(&mut reader)?)); - } - - let buffer_len = read_u32(&mut reader)?; - let mut buffer = vec![0u8; buffer_len as usize]; - reader.read_exact(&mut buffer)?; - let opcodes: Vec = - rmp_serde::from_slice(&buffer).unwrap(); - - Ok(Directive::Brillig { inputs, outputs, bytecode: opcodes }) - } _ => Err(std::io::ErrorKind::InvalidData.into()), } @@ -328,12 +281,6 @@ fn serialization_roundtrip() { let log_witnesses = Directive::Log(LogInfo::WitnessOutput(vec![Witness(1u32), Witness(2u32), Witness(3u32)])); - let brillig = Directive::Brillig { - inputs: vec![Expression::default()], - outputs: vec![Witness(2), Witness(3)], - bytecode: vec![brillig_bytecode::Opcode::JMP { destination: 10 }], - }; - let directives = vec![ invert, quotient_none, @@ -342,7 +289,6 @@ fn serialization_roundtrip() { log_string, log_witnesses, permutation_sort, - brillig, ]; for directive in directives { diff --git a/acir/src/circuit/opcodes.rs b/acir/src/circuit/opcodes.rs index 3ebd25c17..c13ec4cb1 100644 --- a/acir/src/circuit/opcodes.rs +++ b/acir/src/circuit/opcodes.rs @@ -9,6 +9,57 @@ use crate::BlackBoxFunc; use acir_field::FieldElement; use serde::{Deserialize, Serialize}; +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] +pub struct Brillig { + pub inputs: Vec, + pub outputs: Vec, + pub bytecode: Vec, +} + +impl Brillig { + pub fn write(&self, mut writer: W) -> std::io::Result<()> { + let inputs_len = self.inputs.len() as u32; + write_u32(&mut writer, inputs_len)?; + for input in &self.inputs { + input.write(&mut writer)? + } + + let outputs_len = self.outputs.len() as u32; + write_u32(&mut writer, outputs_len)?; + for output in &self.outputs { + write_u32(&mut writer, output.witness_index())?; + } + + // TODO: We use rmp_serde as its easier than doing it manually + let buffer = rmp_serde::to_vec(&self.bytecode).unwrap(); + write_u32(&mut writer, buffer.len() as u32)?; + write_bytes(&mut writer, &buffer)?; + + Ok(()) + } + + pub fn read(mut reader: R) -> std::io::Result { + let inputs_len = read_u32(&mut reader)?; + let mut inputs = Vec::with_capacity(inputs_len as usize); + for _ in 0..inputs_len { + inputs.push(Expression::read(&mut reader)?); + } + + let outputs_len = read_u32(&mut reader)?; + let mut outputs = Vec::with_capacity(outputs_len as usize); + for _ in 0..outputs_len { + outputs.push(Witness(read_u32(&mut reader)?)); + } + + let buffer_len = read_u32(&mut reader)?; + let mut buffer = vec![0u8; buffer_len as usize]; + reader.read_exact(&mut buffer)?; + let opcodes: Vec = rmp_serde::from_slice(&buffer).unwrap(); + + Ok(Brillig { inputs, outputs, bytecode: opcodes }) + } +} + #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Copy, Default)] pub struct BlockId(pub u32); @@ -215,6 +266,7 @@ pub enum Opcode { /// RAM is required for Aztec Backend as dynamic memory implementation in Barrentenberg requires an intialisation phase and can only handle constant values for operations. RAM(MemoryBlock), Oracle(OracleData), + Brillig(Brillig), } impl Opcode { @@ -229,6 +281,7 @@ impl Opcode { Opcode::RAM(_) => "ram", Opcode::ROM(_) => "rom", Opcode::Oracle(data) => &data.name, + Opcode::Brillig(_) => "brillig", } } @@ -243,6 +296,7 @@ impl Opcode { Opcode::ROM(_) => 4, Opcode::RAM(_) => 5, Opcode::Oracle { .. } => 6, + Opcode::Brillig { .. } => 7, } } @@ -268,6 +322,7 @@ impl Opcode { mem_block.write(writer) } Opcode::Oracle(data) => data.write(writer), + Opcode::Brillig(brillig) => brillig.write(writer), } } pub fn read(mut reader: R) -> std::io::Result { @@ -305,6 +360,10 @@ impl Opcode { let data = OracleData::read(reader)?; Ok(Opcode::Oracle(data)) } + 7 => { + let brillig = Brillig::read(reader)?; + Ok(Opcode::Brillig(brillig)) + } _ => Err(std::io::ErrorKind::InvalidData.into()), } } @@ -379,11 +438,6 @@ impl std::fmt::Display for Opcode { witnesses.last().unwrap().witness_index() ), }, - Opcode::Directive(Directive::Brillig { inputs, outputs, bytecode }) => { - writeln!(f, "inputs: {:?}", inputs)?; - writeln!(f, "outputs: {:?}", outputs)?; - writeln!(f, "{:?}", bytecode) - } Opcode::Block(block) => { write!(f, "BLOCK ")?; write!(f, "(id: {}, len: {}) ", block.id.0, block.trace.len()) @@ -400,6 +454,12 @@ impl std::fmt::Display for Opcode { write!(f, "ORACLE: ")?; write!(f, "{data}") } + Opcode::Brillig(brillig) => { + write!(f, "BRILLIG: ")?; + writeln!(f, "inputs: {:?}", brillig.inputs)?; + writeln!(f, "outputs: {:?}", brillig.outputs)?; + writeln!(f, "{:?}", brillig.bytecode) + } } } } diff --git a/acvm/src/compiler/transformers/fallback.rs b/acvm/src/compiler/transformers/fallback.rs index 33c8c031e..a7ffbff8b 100644 --- a/acvm/src/compiler/transformers/fallback.rs +++ b/acvm/src/compiler/transformers/fallback.rs @@ -27,7 +27,8 @@ impl FallbackTransformer { | Opcode::Block(_) | Opcode::ROM(_) | Opcode::RAM(_) - | Opcode::Oracle { .. } => { + | Opcode::Oracle { .. } + | Opcode::Brillig(_) => { // directive, arithmetic expression or block are handled by acvm // The oracle opcode is assumed to be supported. acir_supported_opcodes.push(opcode); diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index e5c851068..bbd2ecfc2 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -7,7 +7,7 @@ pub mod compiler; pub mod pwg; -use crate::pwg::{arithmetic::ArithmeticSolver, oracle::OracleSolver}; +use crate::pwg::{arithmetic::ArithmeticSolver, brillig::BrilligSolver, oracle::OracleSolver}; use acir::{ circuit::{ directives::Directive, @@ -109,6 +109,12 @@ pub trait PartialWitnessGenerator { solved_oracle_data = Some(data_clone); Ok(result) } + Opcode::Brillig(brillig) => { + let mut brillig_clone = brillig.clone(); + let result = BrilligSolver::solve(initial_witness, &mut brillig_clone)?; + // TODO: add oracle logic + Ok(result) + } }; match resolution { Ok(OpcodeResolution::Solved) => { diff --git a/acvm/src/pwg.rs b/acvm/src/pwg.rs index bf7bbdab5..fbd5f0a02 100644 --- a/acvm/src/pwg.rs +++ b/acvm/src/pwg.rs @@ -11,6 +11,8 @@ use self::arithmetic::ArithmeticSolver; // arithmetic pub mod arithmetic; +// Brillig bytecode +pub mod brillig; // Directives pub mod directives; // black box functions diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs new file mode 100644 index 000000000..7e4248a99 --- /dev/null +++ b/acvm/src/pwg/brillig.rs @@ -0,0 +1,36 @@ +use std::collections::BTreeMap; + +use acir::{circuit::opcodes::Brillig, native_types::Witness, FieldElement}; + +use crate::{OpcodeNotSolvable, OpcodeResolution, OpcodeResolutionError}; + +use super::{directives::insert_witness, get_value}; + +pub struct BrilligSolver; + +impl BrilligSolver { + pub fn solve( + initial_witness: &mut BTreeMap, + brillig: &mut Brillig, + ) -> Result { + let mut input_register_values: Vec = Vec::new(); + for expr in &brillig.inputs { + let expr_value = get_value(expr, initial_witness)?; + input_register_values.push(expr_value.into()) + } + let input_registers = acir::brillig_bytecode::Registers { inner: input_register_values }; + + let vm = acir::brillig_bytecode::VM::new(input_registers, brillig.bytecode.clone()); + + let output_registers = vm.process_opcodes(); + + let output_register_values: Vec = + output_registers.inner.into_iter().map(|v| v.inner).collect::>(); + + for (witness, value) in brillig.outputs.iter().zip(output_register_values) { + insert_witness(*witness, value, initial_witness)?; + } + + Ok(OpcodeResolution::Solved) + } +} diff --git a/acvm/src/pwg/directives.rs b/acvm/src/pwg/directives.rs index 8d93845cd..2feb2b231 100644 --- a/acvm/src/pwg/directives.rs +++ b/acvm/src/pwg/directives.rs @@ -148,28 +148,6 @@ pub fn solve_directives( println!("{output_witnesses_string}"); - Ok(()) - } - Directive::Brillig { inputs, outputs, bytecode } => { - let mut input_register_values: Vec = Vec::new(); - for expr in inputs { - let expr_value = get_value(expr, initial_witness)?; - input_register_values.push(expr_value.into()) - } - let input_registers = - acir::brillig_bytecode::Registers { inner: input_register_values }; - - let vm = acir::brillig_bytecode::VM::new(input_registers, bytecode.clone()); - - let output_registers = vm.process_opcodes(); - - let output_register_values: Vec = - output_registers.inner.into_iter().map(|v| v.inner).collect::>(); - - for (witness, value) in outputs.into_iter().zip(output_register_values) { - insert_witness(*witness, value, initial_witness)?; - } - Ok(()) } } diff --git a/brillig_bytecode/Cargo.toml b/brillig_bytecode/Cargo.toml index f64752a51..54746490d 100644 --- a/brillig_bytecode/Cargo.toml +++ b/brillig_bytecode/Cargo.toml @@ -9,7 +9,6 @@ edition = "2021" acir_field.workspace = true serde.workspace = true - [features] bn254 = ["acir_field/bn254"] bls12_381 = ["acir_field/bls12_381"] From 2ca6648e2c5d9f9d5f177daeac55ed3c59ee4395 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Wed, 29 Mar 2023 17:01:22 +0100 Subject: [PATCH 026/125] bootstrap register allocation --- brillig_bytecode/src/lib.rs | 33 ++++++++++++++++++++++++++----- brillig_bytecode/src/registers.rs | 6 ++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 70f5b6673..3368ea68f 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -10,6 +10,7 @@ mod opcodes; mod registers; mod value; +use acir_field::FieldElement; pub use opcodes::RegisterMemIndex; pub use opcodes::{BinaryOp, Comparison, Opcode}; pub use registers::{RegisterIndex, Registers}; @@ -32,8 +33,27 @@ pub struct VM { } impl VM { - pub fn new(inputs: Registers, bytecode: Vec) -> VM { - Self { registers: inputs, program_counter: 0, bytecode, status: VMStatus::InProgress } + pub fn new( + mut inputs: Registers, + bytecode: Vec, + register_alloc_indices: Option>, + ) -> VM { + if let Some(indices) = register_alloc_indices { + let mut registers_modified = + Registers::load(vec![Value { typ: Typ::Field, inner: FieldElement::from(0u128) }]); + + for i in 0..indices.len() { + let register_index = indices[i]; + let register_value = inputs.get(RegisterMemIndex::Register(RegisterIndex(i))); + registers_modified.set(RegisterIndex(register_index as usize), register_value) + } + + inputs = registers_modified; + } + + let vm = + Self { registers: inputs, program_counter: 0, bytecode, status: VMStatus::InProgress }; + vm } /// Loop over the bytecode and update the program counter @@ -146,7 +166,7 @@ fn add_single_step_smoke() { }; // Start VM - let mut vm = VM::new(input_registers, vec![opcode]); + let mut vm = VM::new(input_registers, vec![opcode], None); // Process a single VM opcode // @@ -181,7 +201,8 @@ fn test_jmpif_opcode() { let jump_if_opcode = Opcode::JMPIF { condition: RegisterMemIndex::Register(RegisterIndex(2)), destination: 3 }; - let mut vm = VM::new(input_registers, vec![equal_cmp_opcode, jump_opcode, jump_if_opcode]); + let mut vm = + VM::new(input_registers, vec![equal_cmp_opcode, jump_opcode, jump_if_opcode], None); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -231,6 +252,7 @@ fn test_jmpifnot_opcode() { let mut vm = VM::new( input_registers, vec![jump_opcode, trap_opcode, not_equal_cmp_opcode, jump_if_not_opcode, add_opcode], + None, ); let status = vm.process_opcode(); @@ -264,7 +286,7 @@ fn test_mov_opcode() { source: RegisterMemIndex::Register(RegisterIndex(0)), }; - let mut vm = VM::new(input_registers, vec![mov_opcode]); + let mut vm = VM::new(input_registers, vec![mov_opcode], None); let status = vm.process_opcode(); assert_eq!(status, VMStatus::Halted); @@ -323,6 +345,7 @@ fn test_cmp_binary_ops() { let mut vm = VM::new( input_registers, vec![equal_opcode, not_equal_opcode, less_than_opcode, less_than_equal_opcode], + None, ); let status = vm.process_opcode(); diff --git a/brillig_bytecode/src/registers.rs b/brillig_bytecode/src/registers.rs index 874547139..cf074943c 100644 --- a/brillig_bytecode/src/registers.rs +++ b/brillig_bytecode/src/registers.rs @@ -1,4 +1,5 @@ use crate::{opcodes::RegisterMemIndex, Typ, Value}; +use acir_field::FieldElement; use serde::{Deserialize, Serialize}; /// Registers will store field element values during the @@ -32,6 +33,11 @@ impl Registers { } /// Sets the value at register with address `index` to `value` pub fn set(&mut self, index: RegisterIndex, value: Value) { + if index.inner() >= self.inner.len() { + let diff = index.inner() - self.inner.len() + 1; + self.inner + .extend(vec![Value { typ: Typ::Field, inner: FieldElement::from(0u128) }; diff]) + } self.inner[index.inner()] = value } /// Returns all of the values in the register From 404721ef17d29f27a1cc399d5e9086e541038c39 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Wed, 29 Mar 2023 17:26:12 +0100 Subject: [PATCH 027/125] add bootstrap hack --- brillig_bytecode/src/lib.rs | 28 +++++++++++++--------------- brillig_bytecode/src/opcodes.rs | 4 ++++ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 3368ea68f..c69a1d95f 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -33,24 +33,22 @@ pub struct VM { } impl VM { - pub fn new( - mut inputs: Registers, - bytecode: Vec, - register_alloc_indices: Option>, - ) -> VM { - if let Some(indices) = register_alloc_indices { + pub fn new(mut inputs: Registers, mut bytecode: Vec) -> VM { + let last_opcode = bytecode.last().expect("need at least one opcode"); + + if let Opcode::Bootstrap { register_allocation_indices } = last_opcode { let mut registers_modified = Registers::load(vec![Value { typ: Typ::Field, inner: FieldElement::from(0u128) }]); - for i in 0..indices.len() { - let register_index = indices[i]; + for i in 0..register_allocation_indices.len() { + let register_index = register_allocation_indices[i]; let register_value = inputs.get(RegisterMemIndex::Register(RegisterIndex(i))); registers_modified.set(RegisterIndex(register_index as usize), register_value) } + bytecode.pop(); inputs = registers_modified; } - let vm = Self { registers: inputs, program_counter: 0, bytecode, status: VMStatus::InProgress }; vm @@ -102,6 +100,9 @@ impl VM { self.increment_program_counter() } Opcode::Trap => VMStatus::Failure, + Opcode::Bootstrap { .. } => unreachable!( + "should only be at end of opcodes and popped off when initializing the vm" + ), } } @@ -166,7 +167,7 @@ fn add_single_step_smoke() { }; // Start VM - let mut vm = VM::new(input_registers, vec![opcode], None); + let mut vm = VM::new(input_registers, vec![opcode]); // Process a single VM opcode // @@ -201,8 +202,7 @@ fn test_jmpif_opcode() { let jump_if_opcode = Opcode::JMPIF { condition: RegisterMemIndex::Register(RegisterIndex(2)), destination: 3 }; - let mut vm = - VM::new(input_registers, vec![equal_cmp_opcode, jump_opcode, jump_if_opcode], None); + let mut vm = VM::new(input_registers, vec![equal_cmp_opcode, jump_opcode, jump_if_opcode]); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -252,7 +252,6 @@ fn test_jmpifnot_opcode() { let mut vm = VM::new( input_registers, vec![jump_opcode, trap_opcode, not_equal_cmp_opcode, jump_if_not_opcode, add_opcode], - None, ); let status = vm.process_opcode(); @@ -286,7 +285,7 @@ fn test_mov_opcode() { source: RegisterMemIndex::Register(RegisterIndex(0)), }; - let mut vm = VM::new(input_registers, vec![mov_opcode], None); + let mut vm = VM::new(input_registers, vec![mov_opcode]); let status = vm.process_opcode(); assert_eq!(status, VMStatus::Halted); @@ -345,7 +344,6 @@ fn test_cmp_binary_ops() { let mut vm = VM::new( input_registers, vec![equal_opcode, not_equal_opcode, less_than_opcode, less_than_equal_opcode], - None, ); let status = vm.process_opcode(); diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index fca46f4ee..f1e31b125 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -66,6 +66,10 @@ pub enum Opcode { }, /// Used if execution fails during evaluation Trap, + /// Hack + Bootstrap { + register_allocation_indices: Vec, + }, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] From d3bdeeca0c75a199e5f324f9cd2ffc89f63c563d Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Wed, 29 Mar 2023 18:24:05 +0100 Subject: [PATCH 028/125] increment program counter --- brillig_bytecode/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index c69a1d95f..8da18099d 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -75,14 +75,14 @@ impl VM { if !condition_value.is_zero() { return self.set_program_counter(*destination); } - self.status + self.increment_program_counter() } Opcode::JMPIFNOT { condition, destination } => { let condition_value = self.registers.get(*condition); if condition_value.is_zero() { return self.set_program_counter(*destination); } - self.status + self.increment_program_counter() } Opcode::Call => todo!(), Opcode::Intrinsics => todo!(), From a9b00f4f13fcc4024ab74c3968908f1684072776 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 29 Mar 2023 18:27:40 +0100 Subject: [PATCH 029/125] feat: Brillig oracle opcode (#170) * Brillig OracleWait and test for it * cleanup and add stalled brillig data to unresolved_opcodes * wip --------- Co-authored-by: Kevaundray Wedderburn --- acvm/src/lib.rs | 160 +++++++++++++++++++++++++++++--- acvm/src/pwg/brillig.rs | 62 +++++++++++-- acvm/src/pwg/logic.rs | 2 +- brillig_bytecode/src/lib.rs | 36 +++++-- brillig_bytecode/src/opcodes.rs | 36 ++++++- 5 files changed, 260 insertions(+), 36 deletions(-) diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index bbd2ecfc2..dc6b55f95 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -9,9 +9,10 @@ pub mod pwg; use crate::pwg::{arithmetic::ArithmeticSolver, brillig::BrilligSolver, oracle::OracleSolver}; use acir::{ + brillig_bytecode, circuit::{ directives::Directive, - opcodes::{BlackBoxFuncCall, OracleData}, + opcodes::{BlackBoxFuncCall, Brillig, OracleData}, Circuit, Opcode, }, native_types::{Expression, Witness}, @@ -49,7 +50,7 @@ pub enum OpcodeResolutionError { #[error("could not satisfy all constraints")] UnsatisfiedConstrain, #[error("unexpected opcode, expected {0}, but got {1}")] - UnexpectedOpcode(&'static str, BlackBoxFunc), + UnexpectedOpcode(&'static str, &'static str), #[error("expected {0} inputs for function {1}, but got {2}")] IncorrectNumFunctionArguments(usize, BlackBoxFunc, usize), } @@ -62,6 +63,8 @@ pub enum OpcodeResolution { Stalled(OpcodeNotSolvable), /// The opcode is not solvable but could resolved some witness InProgress, + /// The brillig oracle opcode is not solved but could be resolved given some values + InProgessBrillig(brillig_bytecode::OracleData), } pub trait Backend: SmartContract + ProofSystemCompiler + PartialWitnessGenerator {} @@ -75,15 +78,17 @@ pub trait PartialWitnessGenerator { initial_witness: &mut BTreeMap, blocks: &mut Blocks, mut opcode_to_solve: Vec, - ) -> Result<(Vec, Vec), OpcodeResolutionError> { + ) -> Result { let mut unresolved_opcodes: Vec = Vec::new(); let mut unresolved_oracles: Vec = Vec::new(); + let mut unresolved_brillig_oracles: Vec = Vec::new(); while !opcode_to_solve.is_empty() || !unresolved_oracles.is_empty() { unresolved_opcodes.clear(); let mut stalled = true; let mut opcode_not_solvable = None; for opcode in &opcode_to_solve { let mut solved_oracle_data = None; + let mut solved_brillig_data = None; let resolution = match opcode { Opcode::Arithmetic(expr) => ArithmeticSolver::solve(initial_witness, expr), Opcode::BlackBoxFuncCall(bb_func) => { @@ -112,7 +117,7 @@ pub trait PartialWitnessGenerator { Opcode::Brillig(brillig) => { let mut brillig_clone = brillig.clone(); let result = BrilligSolver::solve(initial_witness, &mut brillig_clone)?; - // TODO: add oracle logic + solved_brillig_data = Some(brillig_clone); Ok(result) } }; @@ -129,6 +134,11 @@ pub trait PartialWitnessGenerator { unresolved_opcodes.push(opcode.clone()); } } + Ok(OpcodeResolution::InProgessBrillig(oracle_data)) => { + stalled = false; + // InProgressBrillig Oracles must be externally re-solved + unresolved_brillig_oracles.push(oracle_data); + } Ok(OpcodeResolution::Stalled(not_solvable)) => { if opcode_not_solvable.is_none() { // we keep track of the first unsolvable opcode @@ -141,6 +151,10 @@ pub trait PartialWitnessGenerator { Some(oracle_data) => Opcode::Oracle(oracle_data), None => opcode.clone(), }); + unresolved_opcodes.push(match solved_brillig_data { + Some(brillig) => Opcode::Brillig(brillig), + None => opcode.clone(), + }) } Err(OpcodeResolutionError::OpcodeNotSolvable(_)) => { unreachable!("ICE - Result should have been converted to GateResolution") @@ -149,8 +163,12 @@ pub trait PartialWitnessGenerator { } } // We have oracles that must be externally resolved - if !unresolved_oracles.is_empty() { - return Ok((unresolved_opcodes, unresolved_oracles)); + if !unresolved_oracles.is_empty() | !unresolved_brillig_oracles.is_empty() { + return Ok(UnresolvedData { + unresolved_opcodes, + unresolved_oracles, + unresolved_brillig_oracles, + }); } // We are stalled because of an opcode being bad if stalled && !unresolved_opcodes.is_empty() { @@ -161,7 +179,11 @@ pub trait PartialWitnessGenerator { } std::mem::swap(&mut opcode_to_solve, &mut unresolved_opcodes); } - Ok((Vec::new(), Vec::new())) + Ok(UnresolvedData { + unresolved_opcodes: Vec::new(), + unresolved_oracles: Vec::new(), + unresolved_brillig_oracles: Vec::new(), + }) } fn solve_black_box_function_call( @@ -198,6 +220,12 @@ pub trait PartialWitnessGenerator { } } +pub struct UnresolvedData { + pub unresolved_opcodes: Vec, + pub unresolved_oracles: Vec, + pub unresolved_brillig_oracles: Vec, +} + pub trait SmartContract { // TODO: Allow a backend to support multiple smart contract platforms @@ -320,9 +348,11 @@ mod test { use std::collections::BTreeMap; use acir::{ + brillig_bytecode, + brillig_bytecode::{RegisterIndex, RegisterMemIndex}, circuit::{ directives::Directive, - opcodes::{BlackBoxFuncCall, OracleData}, + opcodes::{BlackBoxFuncCall, Brillig, OracleData}, Opcode, }, native_types::{Expression, Witness}, @@ -331,6 +361,7 @@ mod test { use crate::{ pwg::block::Blocks, OpcodeResolution, OpcodeResolutionError, PartialWitnessGenerator, + UnresolvedData, }; struct StubbedPwg; @@ -395,10 +426,10 @@ mod test { (Witness(2), FieldElement::from(3u128)), ]); let mut blocks = Blocks::default(); - let (unsolved_opcodes, mut unresolved_oracles) = pwg + let UnresolvedData { unresolved_opcodes, mut unresolved_oracles, .. } = pwg .solve(&mut witness_assignments, &mut blocks, opcodes) .expect("should stall on oracle"); - assert!(unsolved_opcodes.is_empty(), "oracle should be removed"); + assert!(unresolved_opcodes.is_empty(), "oracle should be removed"); assert_eq!(unresolved_oracles.len(), 1, "should have an oracle request"); let mut oracle_data = unresolved_oracles.remove(0); assert_eq!(oracle_data.input_values.len(), 1, "Should have solved a single input"); @@ -406,11 +437,112 @@ mod test { // Filling data request and continue solving oracle_data.output_values = vec![oracle_data.input_values.last().unwrap().inverse()]; let mut next_opcodes_for_solving = vec![Opcode::Oracle(oracle_data)]; - next_opcodes_for_solving.extend_from_slice(&unsolved_opcodes[..]); - let (unsolved_opcodes, unresolved_oracles) = pwg + next_opcodes_for_solving.extend_from_slice(&unresolved_opcodes[..]); + let UnresolvedData { unresolved_opcodes, .. } = pwg .solve(&mut witness_assignments, &mut blocks, next_opcodes_for_solving) - .expect("should be solvable"); - assert!(unsolved_opcodes.is_empty(), "should be fully solved"); + .expect("should not stall on oracle"); + assert!(unresolved_opcodes.is_empty(), "should be fully solved"); assert!(unresolved_oracles.is_empty(), "should have no unresolved oracles"); } + + #[test] + fn inversion_brillig_oracle_equivalence() { + // Opcodes below describe the following: + // fn main(x : Field, y : pub Field) { + // let z = x + y; + // constrain 1/z == Oracle("inverse", x + y); + // } + let fe_0 = FieldElement::zero(); + let fe_1 = FieldElement::one(); + let w_x = Witness(1); + let w_y = Witness(2); + let w_oracle = Witness(3); + let w_z = Witness(4); + let w_z_inverse = Witness(5); + let w_x_plus_y = Witness(6); + + let brillig_bytecode = brillig_bytecode::Opcode::Oracle(brillig_bytecode::OracleData { + name: "invert".into(), + inputs: vec![RegisterMemIndex::Register(RegisterIndex(0))], + input_values: vec![], + outputs: vec![RegisterIndex(1)], + output_values: vec![], + }); + + let brillig_opcode = Opcode::Brillig(Brillig { + inputs: vec![ + Expression { + mul_terms: vec![], + linear_combinations: vec![(fe_1, w_x), (fe_1, w_y)], + q_c: fe_0, + }, + Expression::default(), + ], + outputs: vec![w_x_plus_y, w_oracle], + bytecode: vec![brillig_bytecode], + }); + + let opcodes = vec![ + brillig_opcode, + Opcode::Arithmetic(Expression { + mul_terms: vec![], + linear_combinations: vec![(fe_1, w_x), (fe_1, w_y), (-fe_1, w_z)], + q_c: fe_0, + }), + Opcode::Directive(Directive::Invert { x: w_z, result: w_z_inverse }), + Opcode::Arithmetic(Expression { + mul_terms: vec![(fe_1, w_z, w_z_inverse)], + linear_combinations: vec![], + q_c: -fe_1, + }), + Opcode::Arithmetic(Expression { + mul_terms: vec![], + linear_combinations: vec![(-fe_1, w_oracle), (fe_1, w_z_inverse)], + q_c: fe_0, + }), + ]; + + let pwg = StubbedPwg; + + let mut witness_assignments = BTreeMap::from([ + (Witness(1), FieldElement::from(2u128)), + (Witness(2), FieldElement::from(3u128)), + (Witness(6), FieldElement::from(5u128)), + ]); + let mut blocks = Blocks::default(); + let UnresolvedData { unresolved_opcodes, mut unresolved_brillig_oracles, .. } = pwg + .solve(&mut witness_assignments, &mut blocks, opcodes) + .expect("should stall on oracle"); + + assert!(unresolved_opcodes.is_empty(), "opcode should be removed"); + assert_eq!(unresolved_brillig_oracles.len(), 1, "should have a brillig oracle request"); + + let mut oracle_data = unresolved_brillig_oracles.remove(0); + assert_eq!(oracle_data.inputs.len(), 1, "Should have solved a single input"); + + // Filling data request and continue solving + oracle_data.output_values = vec![oracle_data.input_values.last().unwrap().inverse()]; + let brillig_bytecode = brillig_bytecode::Opcode::Oracle(oracle_data); + + let mut next_opcodes_for_solving = vec![Opcode::Brillig(Brillig { + inputs: vec![ + Expression { + mul_terms: vec![], + linear_combinations: vec![(fe_1, w_x), (fe_1, w_y)], + q_c: fe_0, + }, + Expression::default(), + ], + outputs: vec![w_x_plus_y, w_oracle], + bytecode: vec![brillig_bytecode], + })]; + + next_opcodes_for_solving.extend_from_slice(&unresolved_opcodes[..]); + let UnresolvedData { unresolved_opcodes, unresolved_brillig_oracles, .. } = pwg + .solve(&mut witness_assignments, &mut blocks, next_opcodes_for_solving) + .expect("should not stall on oracle"); + + assert!(unresolved_opcodes.is_empty(), "should be fully solved"); + assert!(unresolved_brillig_oracles.is_empty(), "should have no unresolved oracles"); + } } diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index 7e4248a99..02696413b 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -1,8 +1,15 @@ use std::collections::BTreeMap; -use acir::{circuit::opcodes::Brillig, native_types::Witness, FieldElement}; +use acir::{ + brillig_bytecode::{Opcode, RegisterMemIndex, Registers, VMStatus, Value, VM}, + circuit::opcodes::Brillig, + native_types::Witness, + FieldElement, +}; -use crate::{OpcodeNotSolvable, OpcodeResolution, OpcodeResolutionError}; +use crate::{ + pwg::arithmetic::ArithmeticSolver, OpcodeNotSolvable, OpcodeResolution, OpcodeResolutionError, +}; use super::{directives::insert_witness, get_value}; @@ -13,16 +20,55 @@ impl BrilligSolver { initial_witness: &mut BTreeMap, brillig: &mut Brillig, ) -> Result { - let mut input_register_values: Vec = Vec::new(); + // Set input values + let mut input_register_values: Vec = Vec::new(); for expr in &brillig.inputs { - let expr_value = get_value(expr, initial_witness)?; - input_register_values.push(expr_value.into()) + // Break from setting the inputs values if unable to solve the arithmetic expression inputs + let solve = ArithmeticSolver::evaluate(expr, initial_witness); + if let Some(value) = solve.to_const() { + input_register_values.push(value.into()) + } else { + break; + } } - let input_registers = acir::brillig_bytecode::Registers { inner: input_register_values }; - let vm = acir::brillig_bytecode::VM::new(input_registers, brillig.bytecode.clone()); + if input_register_values.len() != brillig.inputs.len() { + return Ok(OpcodeResolution::Stalled(OpcodeNotSolvable::ExpressionHasTooManyUnknowns( + brillig + .inputs + .last() + .expect("Infallible: cannot reach this point if no inputs") + .clone(), + ))); + } + + let input_registers = Registers { inner: input_register_values }; + let vm = VM::new(input_registers, brillig.bytecode.clone()); + + let (output_registers, status) = vm.clone().process_opcodes(); - let output_registers = vm.process_opcodes(); + if status == VMStatus::OracleWait { + let pc = vm.program_counter(); + let current_opcode = &brillig.bytecode[pc]; + let mut data = match current_opcode.clone() { + Opcode::Oracle(data) => data, + _ => { + return Err(OpcodeResolutionError::UnexpectedOpcode( + "brillig oracle", + current_opcode.name(), + )) + } + }; + let input_values = data + .clone() + .inputs + .into_iter() + .map(|register_mem_index| output_registers.get(register_mem_index).inner) + .collect::>(); + data.input_values = input_values; + + return Ok(OpcodeResolution::InProgessBrillig(data.clone())); + } let output_register_values: Vec = output_registers.inner.into_iter().map(|v| v.inner).collect::>(); diff --git a/acvm/src/pwg/logic.rs b/acvm/src/pwg/logic.rs index ba030901c..230fd27b9 100644 --- a/acvm/src/pwg/logic.rs +++ b/acvm/src/pwg/logic.rs @@ -10,7 +10,7 @@ pub fn solve_logic_opcode( match func_call.name { BlackBoxFunc::AND => LogicSolver::solve_and_gate(initial_witness, func_call), BlackBoxFunc::XOR => LogicSolver::solve_xor_gate(initial_witness, func_call), - _ => Err(OpcodeResolutionError::UnexpectedOpcode("logic opcode", func_call.name)), + _ => Err(OpcodeResolutionError::UnexpectedOpcode("logic opcode", func_call.name.name())), } } diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 8da18099d..b47a6be89 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -12,7 +12,7 @@ mod value; use acir_field::FieldElement; pub use opcodes::RegisterMemIndex; -pub use opcodes::{BinaryOp, Comparison, Opcode}; +pub use opcodes::{BinaryOp, Comparison, Opcode, OracleData}; pub use registers::{RegisterIndex, Registers}; pub use value::Typ; pub use value::Value; @@ -22,6 +22,7 @@ pub enum VMStatus { Halted, InProgress, Failure, + OracleWait, } #[derive(Debug, PartialEq, Eq, Clone)] @@ -55,8 +56,11 @@ impl VM { } /// Loop over the bytecode and update the program counter - pub fn process_opcodes(mut self) -> Registers { - while !matches!(self.process_opcode(), VMStatus::Halted | VMStatus::Failure) {} + pub fn process_opcodes(mut self) -> (Registers, VMStatus) { + while !matches!( + self.process_opcode(), + VMStatus::Halted | VMStatus::Failure | VMStatus::OracleWait + ) {} self.finish() } // Process a single opcode and modify the program counter @@ -86,7 +90,17 @@ impl VM { } Opcode::Call => todo!(), Opcode::Intrinsics => todo!(), - Opcode::Oracle { inputs, destination } => todo!(), + Opcode::Oracle(data) => { + if data.outputs.len() == data.output_values.len() { + for (index, value) in data.outputs.iter().zip(data.output_values.iter()) { + self.registers.set(*index, (*value).into()) + } + } else { + self.status = VMStatus::OracleWait; + return VMStatus::OracleWait; + } + self.increment_program_counter() + } Opcode::Mov { destination, source } => { let source_value = self.registers.get(*source); @@ -106,6 +120,10 @@ impl VM { } } + pub fn program_counter(self) -> usize { + self.program_counter + } + /// Increments the program counter by 1. fn increment_program_counter(&mut self) -> VMStatus { self.set_program_counter(self.program_counter + 1) @@ -144,8 +162,8 @@ impl VM { /// Returns the state of the registers. /// This consumes ownership of the VM and is conventionally /// called when all of the bytecode has been processed. - fn finish(self) -> Registers { - self.registers + fn finish(self) -> (Registers, VMStatus) { + (self.registers, self.status) } } @@ -178,7 +196,7 @@ fn add_single_step_smoke() { // The register at index `2` should have the value of 3 since we had an // add opcode - let registers = vm.finish(); + let (registers, _) = vm.finish(); let output_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); assert_eq!(output_value, Value::from(3u128)) @@ -270,7 +288,7 @@ fn test_jmpifnot_opcode() { assert_eq!(status, VMStatus::Failure); // The register at index `2` should have not changed as we jumped over the add opcode - let registers = vm.finish(); + let (registers, status) = vm.finish(); let output_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); assert_eq!(output_value, Value::from(false)); } @@ -290,7 +308,7 @@ fn test_mov_opcode() { let status = vm.process_opcode(); assert_eq!(status, VMStatus::Halted); - let registers = vm.finish(); + let (registers, status) = vm.finish(); let destination_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); assert_eq!(destination_value, Value::from(1u128)); diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index f1e31b125..9dc98e9f3 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -56,10 +56,7 @@ pub enum Opcode { // TODO:These are special functions like sha256 Intrinsics, // TODO:This will be used to get data from an outside source - Oracle { - inputs: Vec, - destination: Vec, - }, + Oracle(OracleData), Mov { destination: RegisterMemIndex, source: RegisterMemIndex, @@ -72,6 +69,37 @@ pub enum Opcode { }, } +impl Opcode { + pub fn name(&self) -> &'static str { + match self { + Opcode::BinaryOp { .. } => "binary_op", + Opcode::JMPIFNOT { .. } => "jmpifnot", + Opcode::JMPIF { .. } => "jmpif", + Opcode::JMP { .. } => "jmp", + Opcode::Call => "call", + Opcode::Intrinsics => "intrinsics", + Opcode::Oracle(_) => "oracle", + Opcode::Mov { .. } => "mov", + Opcode::Trap => "trap", + Opcode::Bootstrap { .. } => "bootstrap", + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct OracleData { + /// Name of the oracle + pub name: String, + /// Inputs + pub inputs: Vec, + /// Input values + pub input_values: Vec, + /// Output witness + pub outputs: Vec, + /// Output values - they are computed by the (external) oracle once the inputs are known + pub output_values: Vec, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum BinaryOp { Add, From b2f3ff2c2e118a02f1612abe531283769fb0e6dc Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 30 Mar 2023 14:32:45 +0100 Subject: [PATCH 030/125] feat: Reduce brillig bytecodes sent to VM after oracle wait (#173) * VM returns a program counter to enable the caller to restart execution at where the oracle was waiting * fix inputs for brillig oracle * remove comment --- acvm/src/lib.rs | 66 ++++++++++++++++++++++++++----------- acvm/src/pwg/brillig.rs | 21 ++++++++---- brillig_bytecode/src/lib.rs | 12 +++---- 3 files changed, 68 insertions(+), 31 deletions(-) diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index dc6b55f95..8bfac533d 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -18,7 +18,7 @@ use acir::{ native_types::{Expression, Witness}, BlackBoxFunc, }; -use pwg::block::Blocks; +use pwg::{block::Blocks, brillig}; use std::collections::BTreeMap; use thiserror::Error; @@ -33,7 +33,7 @@ pub use acir::FieldElement; // TODO: ExpressionHasTooManyUnknowns is specific for arithmetic expressions // TODO: we could have a error enum for arithmetic failure cases in that module // TODO that can be converted into an OpcodeNotSolvable or OpcodeResolutionError enum -#[derive(PartialEq, Eq, Debug, Error)] +#[derive(PartialEq, Eq, Debug, Error, Clone)] pub enum OpcodeNotSolvable { #[error("missing assignment for witness index {0}")] MissingAssignment(u32), @@ -41,7 +41,7 @@ pub enum OpcodeNotSolvable { ExpressionHasTooManyUnknowns(Expression), } -#[derive(PartialEq, Eq, Debug, Error)] +#[derive(PartialEq, Eq, Debug, Error, Clone)] pub enum OpcodeResolutionError { #[error("cannot solve opcode: {0}")] OpcodeNotSolvable(#[from] OpcodeNotSolvable), @@ -55,7 +55,7 @@ pub enum OpcodeResolutionError { IncorrectNumFunctionArguments(usize, BlackBoxFunc, usize), } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum OpcodeResolution { /// The opcode is resolved Solved, @@ -64,7 +64,7 @@ pub enum OpcodeResolution { /// The opcode is not solvable but could resolved some witness InProgress, /// The brillig oracle opcode is not solved but could be resolved given some values - InProgessBrillig(brillig_bytecode::OracleData), + InProgressBrillig(brillig::OracleWaitInfo), } pub trait Backend: SmartContract + ProofSystemCompiler + PartialWitnessGenerator {} @@ -81,7 +81,7 @@ pub trait PartialWitnessGenerator { ) -> Result { let mut unresolved_opcodes: Vec = Vec::new(); let mut unresolved_oracles: Vec = Vec::new(); - let mut unresolved_brillig_oracles: Vec = Vec::new(); + let mut unresolved_brillig_oracles: Vec = Vec::new(); while !opcode_to_solve.is_empty() || !unresolved_oracles.is_empty() { unresolved_opcodes.clear(); let mut stalled = true; @@ -121,6 +121,7 @@ pub trait PartialWitnessGenerator { Ok(result) } }; + match resolution { Ok(OpcodeResolution::Solved) => { stalled = false; @@ -134,10 +135,10 @@ pub trait PartialWitnessGenerator { unresolved_opcodes.push(opcode.clone()); } } - Ok(OpcodeResolution::InProgessBrillig(oracle_data)) => { + Ok(OpcodeResolution::InProgressBrillig(oracle_wait_info)) => { stalled = false; // InProgressBrillig Oracles must be externally re-solved - unresolved_brillig_oracles.push(oracle_data); + unresolved_brillig_oracles.push(oracle_wait_info); } Ok(OpcodeResolution::Stalled(not_solvable)) => { if opcode_not_solvable.is_none() { @@ -223,7 +224,7 @@ pub trait PartialWitnessGenerator { pub struct UnresolvedData { pub unresolved_opcodes: Vec, pub unresolved_oracles: Vec, - pub unresolved_brillig_oracles: Vec, + pub unresolved_brillig_oracles: Vec, } pub trait SmartContract { @@ -349,7 +350,7 @@ mod test { use acir::{ brillig_bytecode, - brillig_bytecode::{RegisterIndex, RegisterMemIndex}, + brillig_bytecode::{BinaryOp, Comparison, RegisterIndex, RegisterMemIndex, Typ}, circuit::{ directives::Directive, opcodes::{BlackBoxFuncCall, Brillig, OracleData}, @@ -460,8 +461,26 @@ mod test { let w_z = Witness(4); let w_z_inverse = Witness(5); let w_x_plus_y = Witness(6); - - let brillig_bytecode = brillig_bytecode::Opcode::Oracle(brillig_bytecode::OracleData { + let w_equal_res = Witness(7); + let w_lt_res = Witness(8); + + let equal_opcode = brillig_bytecode::Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Eq), + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(1)), + result: RegisterIndex(2), + }; + + let less_than_opcode = brillig_bytecode::Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Lt), + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(1)), + result: RegisterIndex(3), + }; + + let invert_oracle = brillig_bytecode::Opcode::Oracle(brillig_bytecode::OracleData { name: "invert".into(), inputs: vec![RegisterMemIndex::Register(RegisterIndex(0))], input_values: vec![], @@ -469,6 +488,8 @@ mod test { output_values: vec![], }); + let mut brillig_bytecode = vec![equal_opcode, less_than_opcode, invert_oracle]; + let brillig_opcode = Opcode::Brillig(Brillig { inputs: vec![ Expression { @@ -478,8 +499,8 @@ mod test { }, Expression::default(), ], - outputs: vec![w_x_plus_y, w_oracle], - bytecode: vec![brillig_bytecode], + outputs: vec![w_x_plus_y, w_oracle, w_equal_res, w_lt_res], + bytecode: brillig_bytecode.clone(), }); let opcodes = vec![ @@ -507,7 +528,6 @@ mod test { let mut witness_assignments = BTreeMap::from([ (Witness(1), FieldElement::from(2u128)), (Witness(2), FieldElement::from(3u128)), - (Witness(6), FieldElement::from(5u128)), ]); let mut blocks = Blocks::default(); let UnresolvedData { unresolved_opcodes, mut unresolved_brillig_oracles, .. } = pwg @@ -517,12 +537,16 @@ mod test { assert!(unresolved_opcodes.is_empty(), "opcode should be removed"); assert_eq!(unresolved_brillig_oracles.len(), 1, "should have a brillig oracle request"); - let mut oracle_data = unresolved_brillig_oracles.remove(0); + let oracle_wait_info = unresolved_brillig_oracles.remove(0); + let mut oracle_data = oracle_wait_info.data; assert_eq!(oracle_data.inputs.len(), 1, "Should have solved a single input"); // Filling data request and continue solving oracle_data.output_values = vec![oracle_data.input_values.last().unwrap().inverse()]; - let brillig_bytecode = brillig_bytecode::Opcode::Oracle(oracle_data); + let invert_oracle = brillig_bytecode::Opcode::Oracle(oracle_data); + brillig_bytecode[oracle_wait_info.program_counter] = invert_oracle; + // Update the bytecode to only start where we were stopped the previous VM process + let new_brillig_bytecode = brillig_bytecode[oracle_wait_info.program_counter..].to_vec(); let mut next_opcodes_for_solving = vec![Opcode::Brillig(Brillig { inputs: vec![ @@ -532,9 +556,13 @@ mod test { q_c: fe_0, }, Expression::default(), + // These are the brillig binary op results + // We include the register values here so that they are part of the witness + Expression::default(), + Expression::default(), ], - outputs: vec![w_x_plus_y, w_oracle], - bytecode: vec![brillig_bytecode], + outputs: vec![w_x_plus_y, w_oracle, w_equal_res, w_lt_res], + bytecode: new_brillig_bytecode, })]; next_opcodes_for_solving.extend_from_slice(&unresolved_opcodes[..]); diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index 02696413b..4b9c339eb 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use acir::{ - brillig_bytecode::{Opcode, RegisterMemIndex, Registers, VMStatus, Value, VM}, + brillig_bytecode::{Opcode, OracleData, RegisterMemIndex, Registers, VMStatus, Value, VM}, circuit::opcodes::Brillig, native_types::Witness, FieldElement, @@ -11,7 +11,7 @@ use crate::{ pwg::arithmetic::ArithmeticSolver, OpcodeNotSolvable, OpcodeResolution, OpcodeResolutionError, }; -use super::{directives::insert_witness, get_value}; +use super::directives::insert_witness; pub struct BrilligSolver; @@ -24,6 +24,7 @@ impl BrilligSolver { let mut input_register_values: Vec = Vec::new(); for expr in &brillig.inputs { // Break from setting the inputs values if unable to solve the arithmetic expression inputs + // TODO: switch this to `get_value` and map the err let solve = ArithmeticSolver::evaluate(expr, initial_witness); if let Some(value) = solve.to_const() { input_register_values.push(value.into()) @@ -45,10 +46,9 @@ impl BrilligSolver { let input_registers = Registers { inner: input_register_values }; let vm = VM::new(input_registers, brillig.bytecode.clone()); - let (output_registers, status) = vm.clone().process_opcodes(); + let (output_registers, status, pc) = vm.process_opcodes(); if status == VMStatus::OracleWait { - let pc = vm.program_counter(); let current_opcode = &brillig.bytecode[pc]; let mut data = match current_opcode.clone() { Opcode::Oracle(data) => data, @@ -67,11 +67,14 @@ impl BrilligSolver { .collect::>(); data.input_values = input_values; - return Ok(OpcodeResolution::InProgessBrillig(data.clone())); + return Ok(OpcodeResolution::InProgressBrillig(OracleWaitInfo { + data: data.clone(), + program_counter: pc, + })); } let output_register_values: Vec = - output_registers.inner.into_iter().map(|v| v.inner).collect::>(); + output_registers.clone().inner.into_iter().map(|v| v.inner).collect::>(); for (witness, value) in brillig.outputs.iter().zip(output_register_values) { insert_witness(*witness, value, initial_witness)?; @@ -80,3 +83,9 @@ impl BrilligSolver { Ok(OpcodeResolution::Solved) } } + +#[derive(Debug, PartialEq, Clone)] +pub struct OracleWaitInfo { + pub data: OracleData, + pub program_counter: usize, +} diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index b47a6be89..b097586ae 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -56,7 +56,7 @@ impl VM { } /// Loop over the bytecode and update the program counter - pub fn process_opcodes(mut self) -> (Registers, VMStatus) { + pub fn process_opcodes(mut self) -> (Registers, VMStatus, usize) { while !matches!( self.process_opcode(), VMStatus::Halted | VMStatus::Failure | VMStatus::OracleWait @@ -162,8 +162,8 @@ impl VM { /// Returns the state of the registers. /// This consumes ownership of the VM and is conventionally /// called when all of the bytecode has been processed. - fn finish(self) -> (Registers, VMStatus) { - (self.registers, self.status) + fn finish(self) -> (Registers, VMStatus, usize) { + (self.registers, self.status, self.program_counter) } } @@ -196,7 +196,7 @@ fn add_single_step_smoke() { // The register at index `2` should have the value of 3 since we had an // add opcode - let (registers, _) = vm.finish(); + let (registers, _, _) = vm.finish(); let output_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); assert_eq!(output_value, Value::from(3u128)) @@ -288,7 +288,7 @@ fn test_jmpifnot_opcode() { assert_eq!(status, VMStatus::Failure); // The register at index `2` should have not changed as we jumped over the add opcode - let (registers, status) = vm.finish(); + let (registers, status, _) = vm.finish(); let output_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); assert_eq!(output_value, Value::from(false)); } @@ -308,7 +308,7 @@ fn test_mov_opcode() { let status = vm.process_opcode(); assert_eq!(status, VMStatus::Halted); - let (registers, status) = vm.finish(); + let (registers, status, _) = vm.finish(); let destination_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); assert_eq!(destination_value, Value::from(1u128)); From 620fff06c706fcc747c8e210ea1bf927dde24d51 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 30 Mar 2023 15:46:11 +0100 Subject: [PATCH 031/125] feat: Brillig Stop opcode (#174) * stop opcode * stop opcode --- brillig_bytecode/src/lib.rs | 1 + brillig_bytecode/src/opcodes.rs | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index b097586ae..48b22fef1 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -117,6 +117,7 @@ impl VM { Opcode::Bootstrap { .. } => unreachable!( "should only be at end of opcodes and popped off when initializing the vm" ), + Opcode::Stop => VMStatus::Halted, } } diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 9dc98e9f3..2f9669bda 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -55,7 +55,7 @@ pub enum Opcode { Call, // TODO:These are special functions like sha256 Intrinsics, - // TODO:This will be used to get data from an outside source + /// Used to get data from an outside source Oracle(OracleData), Mov { destination: RegisterMemIndex, @@ -67,6 +67,8 @@ pub enum Opcode { Bootstrap { register_allocation_indices: Vec, }, + /// Stop execution + Stop, } impl Opcode { @@ -82,6 +84,7 @@ impl Opcode { Opcode::Mov { .. } => "mov", Opcode::Trap => "trap", Opcode::Bootstrap { .. } => "bootstrap", + Opcode::Stop => "stop", } } } From 99f3cebe9e2be0cba984a182b5d2422ccc9f2c31 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 30 Mar 2023 15:56:52 +0100 Subject: [PATCH 032/125] feat: Brillig Call Opcode (#175) * brillig call opcode * jump from register * add expect instead of unwrap for register into label --- brillig_bytecode/src/lib.rs | 9 ++++++++- brillig_bytecode/src/opcodes.rs | 6 ++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 48b22fef1..5db84f412 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -88,7 +88,14 @@ impl VM { } self.increment_program_counter() } - Opcode::Call => todo!(), + Opcode::Call { destination } => { + let register = self.registers.get(*destination); + let label = usize::try_from( + register.inner.try_to_u64().expect("register does not fit into u64"), + ) + .expect("register does not fit into usize"); + self.set_program_counter(label) + } Opcode::Intrinsics => todo!(), Opcode::Oracle(data) => { if data.outputs.len() == data.output_values.len() { diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 2f9669bda..00f0e5ac7 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -52,7 +52,9 @@ pub enum Opcode { }, // TODO:This is used to call functions and setup things like // TODO execution contexts. - Call, + Call { + destination: RegisterMemIndex, + }, // TODO:These are special functions like sha256 Intrinsics, /// Used to get data from an outside source @@ -78,7 +80,7 @@ impl Opcode { Opcode::JMPIFNOT { .. } => "jmpifnot", Opcode::JMPIF { .. } => "jmpif", Opcode::JMP { .. } => "jmp", - Opcode::Call => "call", + Opcode::Call { .. } => "call", Opcode::Intrinsics => "intrinsics", Opcode::Oracle(_) => "oracle", Opcode::Mov { .. } => "mov", From 45d4ca492730aff3e3a97a42ea4812048f609d35 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 30 Mar 2023 17:59:24 +0100 Subject: [PATCH 033/125] feat: Brillig Oracle Predicate (#176) * brillig oracle predicate * switch predicate from brillig oracle to brillig itself * fix brillig predicate write --- acir/src/circuit/opcodes.rs | 18 ++++++- acvm/src/lib.rs | 98 +++++++++++++++++++++++++++++++++++-- acvm/src/pwg/brillig.rs | 27 +++++++++- 3 files changed, 137 insertions(+), 6 deletions(-) diff --git a/acir/src/circuit/opcodes.rs b/acir/src/circuit/opcodes.rs index c13ec4cb1..201b7ddb2 100644 --- a/acir/src/circuit/opcodes.rs +++ b/acir/src/circuit/opcodes.rs @@ -14,6 +14,8 @@ pub struct Brillig { pub inputs: Vec, pub outputs: Vec, pub bytecode: Vec, + /// Predicate of the Brillig execution - indicates if it should be skipped + pub predicate: Option, } impl Brillig { @@ -35,6 +37,13 @@ impl Brillig { write_u32(&mut writer, buffer.len() as u32)?; write_bytes(&mut writer, &buffer)?; + let predicate_is_some = vec![self.predicate.is_some() as u8]; + write_bytes(&mut writer, &predicate_is_some)?; + + if let Some(pred) = &self.predicate { + pred.write(&mut writer)?; + } + Ok(()) } @@ -56,7 +65,14 @@ impl Brillig { reader.read_exact(&mut buffer)?; let opcodes: Vec = rmp_serde::from_slice(&buffer).unwrap(); - Ok(Brillig { inputs, outputs, bytecode: opcodes }) + // Read byte to figure out if there is a predicate + let predicate_is_some = read_n::<1, _>(&mut reader)?[0] != 0; + let predicate = match predicate_is_some { + true => Some(Expression::read(&mut reader)?), + false => None, + }; + + Ok(Brillig { inputs, outputs, bytecode: opcodes, predicate }) } } diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 8bfac533d..e1c0301a2 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -9,10 +9,9 @@ pub mod pwg; use crate::pwg::{arithmetic::ArithmeticSolver, brillig::BrilligSolver, oracle::OracleSolver}; use acir::{ - brillig_bytecode, circuit::{ directives::Directive, - opcodes::{BlackBoxFuncCall, Brillig, OracleData}, + opcodes::{BlackBoxFuncCall, OracleData}, Circuit, Opcode, }, native_types::{Expression, Witness}, @@ -501,6 +500,7 @@ mod test { ], outputs: vec![w_x_plus_y, w_oracle, w_equal_res, w_lt_res], bytecode: brillig_bytecode.clone(), + predicate: None, }); let opcodes = vec![ @@ -556,13 +556,14 @@ mod test { q_c: fe_0, }, Expression::default(), - // These are the brillig binary op results + // These are the Brillig binary op results // We include the register values here so that they are part of the witness Expression::default(), Expression::default(), ], outputs: vec![w_x_plus_y, w_oracle, w_equal_res, w_lt_res], bytecode: new_brillig_bytecode, + predicate: None, })]; next_opcodes_for_solving.extend_from_slice(&unresolved_opcodes[..]); @@ -573,4 +574,95 @@ mod test { assert!(unresolved_opcodes.is_empty(), "should be fully solved"); assert!(unresolved_brillig_oracles.is_empty(), "should have no unresolved oracles"); } + + #[test] + fn brillig_oracle_predicate() { + // Opcodes below describe the following: + // fn main(x : Field, y : pub Field, cond: bool) { + // let z = x + y; + // let z_inverse = 1/z + // if cond { + // constrain z_inverse == Oracle("inverse", x + y); + // } + // } + let fe_0 = FieldElement::zero(); + let fe_1 = FieldElement::one(); + let w_x = Witness(1); + let w_y = Witness(2); + let w_oracle = Witness(3); + let w_z = Witness(4); + let w_z_inverse = Witness(5); + let w_x_plus_y = Witness(6); + let w_equal_res = Witness(7); + let w_lt_res = Witness(8); + + let equal_opcode = brillig_bytecode::Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Eq), + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(1)), + result: RegisterIndex(2), + }; + + let less_than_opcode = brillig_bytecode::Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Lt), + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(1)), + result: RegisterIndex(3), + }; + + let invert_oracle = brillig_bytecode::Opcode::Oracle(brillig_bytecode::OracleData { + name: "invert".into(), + inputs: vec![RegisterMemIndex::Register(RegisterIndex(0))], + input_values: vec![], + outputs: vec![RegisterIndex(1)], + output_values: vec![], + }); + + let brillig_bytecode = vec![equal_opcode, less_than_opcode, invert_oracle]; + + let brillig_opcode = Opcode::Brillig(Brillig { + inputs: vec![ + Expression { + mul_terms: vec![], + linear_combinations: vec![(fe_1, w_x), (fe_1, w_y)], + q_c: fe_0, + }, + Expression::default(), + ], + outputs: vec![w_x_plus_y, w_oracle, w_equal_res, w_lt_res], + bytecode: brillig_bytecode.clone(), + predicate: Some(Expression::default()), + }); + + let opcodes = vec![ + brillig_opcode, + Opcode::Arithmetic(Expression { + mul_terms: vec![], + linear_combinations: vec![(fe_1, w_x), (fe_1, w_y), (-fe_1, w_z)], + q_c: fe_0, + }), + Opcode::Directive(Directive::Invert { x: w_z, result: w_z_inverse }), + Opcode::Arithmetic(Expression { + mul_terms: vec![(fe_1, w_z, w_z_inverse)], + linear_combinations: vec![], + q_c: -fe_1, + }), + ]; + + let pwg = StubbedPwg; + + let mut witness_assignments = BTreeMap::from([ + (Witness(1), FieldElement::from(2u128)), + (Witness(2), FieldElement::from(3u128)), + ]); + let mut blocks = Blocks::default(); + let UnresolvedData { unresolved_opcodes, mut unresolved_brillig_oracles, .. } = pwg + .solve(&mut witness_assignments, &mut blocks, opcodes) + .expect("should stall on oracle"); + + assert!(unresolved_opcodes.is_empty(), "opcode should be removed"); + assert_eq!(unresolved_brillig_oracles.len(), 0, "should have a brillig oracle request"); + } } diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index 4b9c339eb..b8bc1ecd4 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use acir::{ - brillig_bytecode::{Opcode, OracleData, RegisterMemIndex, Registers, VMStatus, Value, VM}, + brillig_bytecode::{Opcode, OracleData, Registers, VMStatus, Value, VM}, circuit::opcodes::Brillig, native_types::Witness, FieldElement, @@ -11,7 +11,7 @@ use crate::{ pwg::arithmetic::ArithmeticSolver, OpcodeNotSolvable, OpcodeResolution, OpcodeResolutionError, }; -use super::directives::insert_witness; +use super::{directives::insert_witness, get_value}; pub struct BrilligSolver; @@ -20,6 +20,28 @@ impl BrilligSolver { initial_witness: &mut BTreeMap, brillig: &mut Brillig, ) -> Result { + // If the predicate is `None`, then we simply return the value 1 + // If the predicate is `Some` but we cannot find a value, then we return stalled + let pred_value = match &brillig.predicate { + Some(pred) => get_value(pred, initial_witness), + None => Ok(FieldElement::one()), + }; + let pred_value = match pred_value { + Ok(pred_value) => pred_value, + Err(OpcodeResolutionError::OpcodeNotSolvable(unsolved)) => { + return Ok(OpcodeResolution::Stalled(unsolved)) + } + Err(err) => return Err(err), + }; + + // A zero predicate indicates the oracle should be skipped, and its ouputs zeroed. + if pred_value.is_zero() { + for output_witness in &brillig.outputs { + insert_witness(*output_witness, FieldElement::zero(), initial_witness)?; + } + return Ok(OpcodeResolution::Solved); + } + // Set input values let mut input_register_values: Vec = Vec::new(); for expr in &brillig.inputs { @@ -59,6 +81,7 @@ impl BrilligSolver { )) } }; + let input_values = data .clone() .inputs From 4d771302167a0a9bd6affee69833c8614e796122 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 11 Apr 2023 16:37:21 +0100 Subject: [PATCH 034/125] feat: Brillig Memory (#178) * initial brillig vm memory * some more fixup with handling array outputs from vm * load opcode test * load and store tests * remove arrayid value type * move to BTreeMap for array heap * rename brillig inputs and outputs --- acir/src/circuit/opcodes.rs | 88 ++++++++++- acvm/src/lib.rs | 45 ++++-- acvm/src/pwg/brillig.rs | 105 +++++++++---- brillig_bytecode/src/lib.rs | 240 ++++++++++++++++++++++++++++-- brillig_bytecode/src/opcodes.rs | 12 ++ brillig_bytecode/src/registers.rs | 26 ++++ brillig_bytecode/src/value.rs | 6 + 7 files changed, 458 insertions(+), 64 deletions(-) diff --git a/acir/src/circuit/opcodes.rs b/acir/src/circuit/opcodes.rs index 201b7ddb2..233ff3922 100644 --- a/acir/src/circuit/opcodes.rs +++ b/acir/src/circuit/opcodes.rs @@ -9,10 +9,40 @@ use crate::BlackBoxFunc; use acir_field::FieldElement; use serde::{Deserialize, Serialize}; +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] +pub enum BrilligInputs { + Simple(Expression), + Array(u32, Vec), +} + +impl BrilligInputs { + fn to_u16(&self) -> u16 { + match self { + BrilligInputs::Simple(_) => 0, + BrilligInputs::Array { .. } => 1, + } + } +} + +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] +pub enum BrilligOutputs { + Simple(Witness), + Array(Vec), +} + +impl BrilligOutputs { + fn to_u16(&self) -> u16 { + match self { + BrilligOutputs::Simple(_) => 0, + BrilligOutputs::Array(_) => 1, + } + } +} + #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] pub struct Brillig { - pub inputs: Vec, - pub outputs: Vec, + pub inputs: Vec, + pub outputs: Vec, pub bytecode: Vec, /// Predicate of the Brillig execution - indicates if it should be skipped pub predicate: Option, @@ -23,13 +53,34 @@ impl Brillig { let inputs_len = self.inputs.len() as u32; write_u32(&mut writer, inputs_len)?; for input in &self.inputs { - input.write(&mut writer)? + write_u16(&mut writer, input.to_u16())?; + match input { + BrilligInputs::Simple(expr) => expr.write(&mut writer)?, + BrilligInputs::Array(id, expr_arr) => { + write_u32(&mut writer, *id)?; + write_u32(&mut writer, expr_arr.len() as u32)?; + for expr in expr_arr { + expr.write(&mut writer)?; + } + } + } } let outputs_len = self.outputs.len() as u32; write_u32(&mut writer, outputs_len)?; for output in &self.outputs { - write_u32(&mut writer, output.witness_index())?; + write_u16(&mut writer, output.to_u16())?; + match output { + BrilligOutputs::Simple(witness) => { + write_u32(&mut writer, witness.witness_index())?; + } + BrilligOutputs::Array(witness_arr) => { + write_u32(&mut writer, witness_arr.len() as u32)?; + for w in witness_arr { + write_u32(&mut writer, w.witness_index())?; + } + } + } } // TODO: We use rmp_serde as its easier than doing it manually @@ -51,13 +102,38 @@ impl Brillig { let inputs_len = read_u32(&mut reader)?; let mut inputs = Vec::with_capacity(inputs_len as usize); for _ in 0..inputs_len { - inputs.push(Expression::read(&mut reader)?); + let input_type = read_u16(&mut reader)?; + match input_type { + 0 => inputs.push(BrilligInputs::Simple(Expression::read(&mut reader)?)), + 1 => { + let arr_id = read_u32(&mut reader)?; + let arr_len = read_u32(&mut reader)?; + let mut arr_inputs = Vec::with_capacity(arr_len as usize); + for _ in 0..arr_len { + arr_inputs.push(Expression::read(&mut reader)?) + } + inputs.push(BrilligInputs::Array(arr_id, arr_inputs)) + } + _ => return Err(std::io::ErrorKind::InvalidData.into()), + } } let outputs_len = read_u32(&mut reader)?; let mut outputs = Vec::with_capacity(outputs_len as usize); for _ in 0..outputs_len { - outputs.push(Witness(read_u32(&mut reader)?)); + let output_type = read_u16(&mut reader)?; + match output_type { + 0 => outputs.push(BrilligOutputs::Simple(Witness(read_u32(&mut reader)?))), + 1 => { + let witness_arr_len = read_u32(&mut reader)?; + let mut witness_arr = Vec::with_capacity(witness_arr_len as usize); + for _ in 0..witness_arr_len { + witness_arr.push(Witness(read_u32(&mut reader)?)) + } + outputs.push(BrilligOutputs::Array(witness_arr)) + } + _ => return Err(std::io::ErrorKind::InvalidData.into()), + } } let buffer_len = read_u32(&mut reader)?; diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index e1c0301a2..441878930 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -352,7 +352,7 @@ mod test { brillig_bytecode::{BinaryOp, Comparison, RegisterIndex, RegisterMemIndex, Typ}, circuit::{ directives::Directive, - opcodes::{BlackBoxFuncCall, Brillig, OracleData}, + opcodes::{BlackBoxFuncCall, Brillig, BrilligInputs, BrilligOutputs, OracleData}, Opcode, }, native_types::{Expression, Witness}, @@ -491,14 +491,19 @@ mod test { let brillig_opcode = Opcode::Brillig(Brillig { inputs: vec![ - Expression { + BrilligInputs::Simple(Expression { mul_terms: vec![], linear_combinations: vec![(fe_1, w_x), (fe_1, w_y)], q_c: fe_0, - }, - Expression::default(), + }), + BrilligInputs::Simple(Expression::default()), + ], + outputs: vec![ + BrilligOutputs::Simple(w_x_plus_y), + BrilligOutputs::Simple(w_oracle), + BrilligOutputs::Simple(w_equal_res), + BrilligOutputs::Simple(w_lt_res), ], - outputs: vec![w_x_plus_y, w_oracle, w_equal_res, w_lt_res], bytecode: brillig_bytecode.clone(), predicate: None, }); @@ -550,18 +555,23 @@ mod test { let mut next_opcodes_for_solving = vec![Opcode::Brillig(Brillig { inputs: vec![ - Expression { + BrilligInputs::Simple(Expression { mul_terms: vec![], linear_combinations: vec![(fe_1, w_x), (fe_1, w_y)], q_c: fe_0, - }, - Expression::default(), + }), + BrilligInputs::Simple(Expression::default()), // These are the Brillig binary op results // We include the register values here so that they are part of the witness - Expression::default(), - Expression::default(), + BrilligInputs::Simple(Expression::default()), + BrilligInputs::Simple(Expression::default()), + ], + outputs: vec![ + BrilligOutputs::Simple(w_x_plus_y), + BrilligOutputs::Simple(w_oracle), + BrilligOutputs::Simple(w_equal_res), + BrilligOutputs::Simple(w_lt_res), ], - outputs: vec![w_x_plus_y, w_oracle, w_equal_res, w_lt_res], bytecode: new_brillig_bytecode, predicate: None, })]; @@ -624,14 +634,19 @@ mod test { let brillig_opcode = Opcode::Brillig(Brillig { inputs: vec![ - Expression { + BrilligInputs::Simple(Expression { mul_terms: vec![], linear_combinations: vec![(fe_1, w_x), (fe_1, w_y)], q_c: fe_0, - }, - Expression::default(), + }), + BrilligInputs::Simple(Expression::default()), + ], + outputs: vec![ + BrilligOutputs::Simple(w_x_plus_y), + BrilligOutputs::Simple(w_oracle), + BrilligOutputs::Simple(w_equal_res), + BrilligOutputs::Simple(w_lt_res), ], - outputs: vec![w_x_plus_y, w_oracle, w_equal_res, w_lt_res], bytecode: brillig_bytecode.clone(), predicate: Some(Expression::default()), }); diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index b8bc1ecd4..ac1ba48c0 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -1,8 +1,10 @@ use std::collections::BTreeMap; use acir::{ - brillig_bytecode::{Opcode, OracleData, Registers, VMStatus, Value, VM}, - circuit::opcodes::Brillig, + brillig_bytecode::{ + ArrayHeap, Opcode, OracleData, Registers, Typ, VMOutputState, VMStatus, Value, VM, + }, + circuit::opcodes::{Brillig, BrilligInputs, BrilligOutputs}, native_types::Witness, FieldElement, }; @@ -36,42 +38,84 @@ impl BrilligSolver { // A zero predicate indicates the oracle should be skipped, and its ouputs zeroed. if pred_value.is_zero() { - for output_witness in &brillig.outputs { - insert_witness(*output_witness, FieldElement::zero(), initial_witness)?; + for output in &brillig.outputs { + match output { + BrilligOutputs::Simple(witness) => { + insert_witness(*witness, FieldElement::zero(), initial_witness)? + } + BrilligOutputs::Array(witness_arr) => { + for w in witness_arr { + insert_witness(*w, FieldElement::zero(), initial_witness)? + } + } + } } return Ok(OpcodeResolution::Solved); } // Set input values let mut input_register_values: Vec = Vec::new(); - for expr in &brillig.inputs { - // Break from setting the inputs values if unable to solve the arithmetic expression inputs - // TODO: switch this to `get_value` and map the err - let solve = ArithmeticSolver::evaluate(expr, initial_witness); - if let Some(value) = solve.to_const() { - input_register_values.push(value.into()) - } else { - break; + let mut input_memory: BTreeMap = BTreeMap::new(); + for input in &brillig.inputs { + match input { + BrilligInputs::Simple(expr) => { + // TODO: switch this to `get_value` and map the err + let solve = ArithmeticSolver::evaluate(expr, initial_witness); + if let Some(value) = solve.to_const() { + input_register_values.push(value.into()) + } else { + break; + } + } + BrilligInputs::Array(id, expr_arr) => { + let id_as_value: Value = Value { + typ: Typ::Unsigned { bit_size: 32 }, + inner: FieldElement::from(*id as u128), + }; + // Push value of the array id as a register + input_register_values.push(id_as_value.into()); + + let mut continue_eval = true; + let mut array_heap: BTreeMap = BTreeMap::new(); + for (i, expr) in expr_arr.into_iter().enumerate() { + let solve = ArithmeticSolver::evaluate(expr, initial_witness); + if let Some(value) = solve.to_const() { + array_heap.insert(i, value.into()); + } else { + continue_eval = false; + break; + } + } + input_memory.insert(id_as_value, ArrayHeap { memory_map: array_heap }); + + if !continue_eval { + break; + } + } } } if input_register_values.len() != brillig.inputs.len() { + let jabber_input = + brillig.inputs.last().expect("Infallible: cannot reach this point if no inputs"); + let expr = match jabber_input { + BrilligInputs::Simple(expr) => expr, + BrilligInputs::Array(_, expr_arr) => { + expr_arr.last().expect("Infallible: cannot reach this point if no inputs") + } + }; return Ok(OpcodeResolution::Stalled(OpcodeNotSolvable::ExpressionHasTooManyUnknowns( - brillig - .inputs - .last() - .expect("Infallible: cannot reach this point if no inputs") - .clone(), + expr.clone(), ))); } let input_registers = Registers { inner: input_register_values }; - let vm = VM::new(input_registers, brillig.bytecode.clone()); + let vm = VM::new(input_registers, input_memory, brillig.bytecode.clone()); - let (output_registers, status, pc) = vm.process_opcodes(); + let VMOutputState { registers, program_counter, status, memory } = vm.process_opcodes(); if status == VMStatus::OracleWait { - let current_opcode = &brillig.bytecode[pc]; + let current_opcode = &brillig.bytecode[program_counter]; let mut data = match current_opcode.clone() { Opcode::Oracle(data) => data, _ => { @@ -86,21 +130,28 @@ impl BrilligSolver { .clone() .inputs .into_iter() - .map(|register_mem_index| output_registers.get(register_mem_index).inner) + .map(|register_mem_index| registers.get(register_mem_index).inner) .collect::>(); data.input_values = input_values; return Ok(OpcodeResolution::InProgressBrillig(OracleWaitInfo { data: data.clone(), - program_counter: pc, + program_counter, })); } - let output_register_values: Vec = - output_registers.clone().inner.into_iter().map(|v| v.inner).collect::>(); - - for (witness, value) in brillig.outputs.iter().zip(output_register_values) { - insert_witness(*witness, value, initial_witness)?; + for (output, register_value) in brillig.outputs.iter().zip(registers) { + match output { + BrilligOutputs::Simple(witness) => { + insert_witness(*witness, register_value.inner, initial_witness)?; + } + BrilligOutputs::Array(witness_arr) => { + let array = memory[®ister_value].memory_map.values(); + for (witness, value) in witness_arr.iter().zip(array) { + insert_witness(*witness, value.inner, initial_witness)?; + } + } + } } Ok(OpcodeResolution::Solved) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 5db84f412..03e1a9cff 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -10,6 +10,9 @@ mod opcodes; mod registers; mod value; +use std::array; +use std::collections::BTreeMap; + use acir_field::FieldElement; pub use opcodes::RegisterMemIndex; pub use opcodes::{BinaryOp, Comparison, Opcode, OracleData}; @@ -25,19 +28,31 @@ pub enum VMStatus { OracleWait, } +#[derive(Default, Debug, PartialEq, Eq, Clone)] +pub struct ArrayHeap { + // maps memory address to Value + pub memory_map: BTreeMap, +} + #[derive(Debug, PartialEq, Eq, Clone)] pub struct VM { registers: Registers, program_counter: usize, bytecode: Vec, status: VMStatus, + memory: BTreeMap, } impl VM { - pub fn new(mut inputs: Registers, mut bytecode: Vec) -> VM { + pub fn new( + mut inputs: Registers, + memory: BTreeMap, + mut bytecode: Vec, + ) -> VM { let last_opcode = bytecode.last().expect("need at least one opcode"); if let Opcode::Bootstrap { register_allocation_indices } = last_opcode { + // TODO: might have to handle arrays in bootstrap to be correct let mut registers_modified = Registers::load(vec![Value { typ: Typ::Field, inner: FieldElement::from(0u128) }]); @@ -50,13 +65,18 @@ impl VM { bytecode.pop(); inputs = registers_modified; } - let vm = - Self { registers: inputs, program_counter: 0, bytecode, status: VMStatus::InProgress }; + let vm = Self { + registers: inputs, + program_counter: 0, + bytecode, + status: VMStatus::InProgress, + memory, + }; vm } /// Loop over the bytecode and update the program counter - pub fn process_opcodes(mut self) -> (Registers, VMStatus, usize) { + pub fn process_opcodes(mut self) -> VMOutputState { while !matches!( self.process_opcode(), VMStatus::Halted | VMStatus::Failure | VMStatus::OracleWait @@ -125,6 +145,24 @@ impl VM { "should only be at end of opcodes and popped off when initializing the vm" ), Opcode::Stop => VMStatus::Halted, + Opcode::Load { destination, array_id_reg, index } => { + let array_id = self.registers.get(*array_id_reg); + let array = &self.memory[&array_id]; + match destination { + RegisterMemIndex::Register(dest_index) => { + self.registers.set(*dest_index, array.memory_map[index]); + } + _ => return VMStatus::Failure, // TODO: add variants to VMStatus::Failure for more informed failures + } + self.increment_program_counter() + } + Opcode::Store { source, array_id_reg, index } => { + let source_value = self.registers.get(*source); + let array_id = self.registers.get(*array_id_reg); + let heap = &mut self.memory.entry(array_id).or_default().memory_map; + heap.insert(*index, source_value); + self.increment_program_counter() + } } } @@ -170,11 +208,23 @@ impl VM { /// Returns the state of the registers. /// This consumes ownership of the VM and is conventionally /// called when all of the bytecode has been processed. - fn finish(self) -> (Registers, VMStatus, usize) { - (self.registers, self.status, self.program_counter) + fn finish(self) -> VMOutputState { + VMOutputState { + registers: self.registers, + program_counter: self.program_counter, + status: self.status, + memory: self.memory, + } } } +pub struct VMOutputState { + pub registers: Registers, + pub program_counter: usize, + pub status: VMStatus, + pub memory: BTreeMap, +} + #[test] fn add_single_step_smoke() { // Load values into registers and initialize the registers that @@ -193,7 +243,7 @@ fn add_single_step_smoke() { }; // Start VM - let mut vm = VM::new(input_registers, vec![opcode]); + let mut vm = VM::new(input_registers, BTreeMap::new(), vec![opcode]); // Process a single VM opcode // @@ -204,14 +254,14 @@ fn add_single_step_smoke() { // The register at index `2` should have the value of 3 since we had an // add opcode - let (registers, _, _) = vm.finish(); + let VMOutputState { registers, .. } = vm.finish(); let output_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); assert_eq!(output_value, Value::from(3u128)) } #[test] -fn test_jmpif_opcode() { +fn jmpif_opcode() { let input_registers = Registers::load(vec![Value::from(2u128), Value::from(2u128), Value::from(0u128)]); @@ -228,7 +278,11 @@ fn test_jmpif_opcode() { let jump_if_opcode = Opcode::JMPIF { condition: RegisterMemIndex::Register(RegisterIndex(2)), destination: 3 }; - let mut vm = VM::new(input_registers, vec![equal_cmp_opcode, jump_opcode, jump_if_opcode]); + let mut vm = VM::new( + input_registers, + BTreeMap::new(), + vec![equal_cmp_opcode, jump_opcode, jump_if_opcode], + ); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -246,7 +300,7 @@ fn test_jmpif_opcode() { } #[test] -fn test_jmpifnot_opcode() { +fn jmpifnot_opcode() { let input_registers = Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(0u128)]); @@ -277,6 +331,7 @@ fn test_jmpifnot_opcode() { let mut vm = VM::new( input_registers, + BTreeMap::new(), vec![jump_opcode, trap_opcode, not_equal_cmp_opcode, jump_if_not_opcode, add_opcode], ); @@ -296,13 +351,13 @@ fn test_jmpifnot_opcode() { assert_eq!(status, VMStatus::Failure); // The register at index `2` should have not changed as we jumped over the add opcode - let (registers, status, _) = vm.finish(); + let VMOutputState { registers, .. } = vm.finish(); let output_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); assert_eq!(output_value, Value::from(false)); } #[test] -fn test_mov_opcode() { +fn mov_opcode() { let input_registers = Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(3u128)]); @@ -311,12 +366,12 @@ fn test_mov_opcode() { source: RegisterMemIndex::Register(RegisterIndex(0)), }; - let mut vm = VM::new(input_registers, vec![mov_opcode]); + let mut vm = VM::new(input_registers, BTreeMap::new(), vec![mov_opcode]); let status = vm.process_opcode(); assert_eq!(status, VMStatus::Halted); - let (registers, status, _) = vm.finish(); + let VMOutputState { registers, .. } = vm.finish(); let destination_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); assert_eq!(destination_value, Value::from(1u128)); @@ -326,7 +381,7 @@ fn test_mov_opcode() { } #[test] -fn test_cmp_binary_ops() { +fn cmp_binary_ops() { let input_registers = Registers::load(vec![ Value::from(2u128), Value::from(2u128), @@ -369,6 +424,7 @@ fn test_cmp_binary_ops() { let mut vm = VM::new( input_registers, + BTreeMap::new(), vec![equal_opcode, not_equal_opcode, less_than_opcode, less_than_equal_opcode], ); @@ -398,3 +454,155 @@ fn test_cmp_binary_ops() { vm.finish(); } + +#[test] +fn load_opcode() { + let input_registers = Registers::load(vec![ + Value::from(2u128), + Value::from(2u128), + Value::from(0u128), + Value::from(5u128), + Value::from(0u128), + Value::from(6u128), + Value::from(0u128), + ]); + + let equal_cmp_opcode = Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Eq), + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(1)), + result: RegisterIndex(2), + }; + + let jump_opcode = Opcode::JMP { destination: 3 }; + + let jump_if_opcode = + Opcode::JMPIF { condition: RegisterMemIndex::Register(RegisterIndex(2)), destination: 10 }; + + let load_opcode = Opcode::Load { + destination: RegisterMemIndex::Register(RegisterIndex(4)), + array_id_reg: RegisterMemIndex::Register(RegisterIndex(3)), + index: 1, + }; + + let mem_equal_opcode = Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Eq), + lhs: RegisterMemIndex::Register(RegisterIndex(4)), + rhs: RegisterMemIndex::Register(RegisterIndex(5)), + result: RegisterIndex(6), + }; + + let mut initial_memory = BTreeMap::new(); + let initial_heap = ArrayHeap { + memory_map: BTreeMap::from([(0 as usize, Value::from(5u128)), (1, Value::from(6u128))]), + }; + initial_memory.insert(Value::from(5u128), initial_heap); + + let mut vm = VM::new( + input_registers, + initial_memory, + vec![equal_cmp_opcode, load_opcode, jump_opcode, mem_equal_opcode, jump_if_opcode], + ); + + // equal_cmp_opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(output_cmp_value, Value::from(true)); + + // load_opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(4))); + assert_eq!(output_cmp_value, Value::from(6u128)); + + // jump_opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + // mem_equal_opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(6))); + assert_eq!(output_cmp_value, Value::from(true)); + + // jump_if_opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::Halted); + + vm.finish(); +} + +#[test] +fn store_opcode() { + let input_registers = Registers::load(vec![ + Value::from(2u128), + Value::from(2u128), + Value::from(0u128), + Value::from(5u128), + Value::from(0u128), + Value::from(6u128), + Value::from(0u128), + ]); + + let equal_cmp_opcode = Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Eq), + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(1)), + result: RegisterIndex(2), + }; + + let jump_opcode = Opcode::JMP { destination: 3 }; + + let jump_if_opcode = + Opcode::JMPIF { condition: RegisterMemIndex::Register(RegisterIndex(2)), destination: 10 }; + + let store_opcode = Opcode::Store { + source: RegisterMemIndex::Register(RegisterIndex(2)), + array_id_reg: RegisterMemIndex::Register(RegisterIndex(3)), + index: 3, + }; + + let mut initial_memory = BTreeMap::new(); + let initial_heap = ArrayHeap { + memory_map: BTreeMap::from([(0 as usize, Value::from(5u128)), (1, Value::from(6u128))]), + }; + initial_memory.insert(Value::from(5u128), initial_heap); + + let mut vm = VM::new( + input_registers, + initial_memory, + vec![equal_cmp_opcode, store_opcode, jump_opcode, jump_if_opcode], + ); + + // equal_cmp_opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(output_cmp_value, Value::from(true)); + + // store_opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let mem_array = vm.memory[&Value::from(5u128)].clone(); + assert_eq!(mem_array.memory_map[&3], Value::from(true)); + + // jump_opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + dbg!(status); + + // jump_if_opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::Halted); + + vm.finish(); +} diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 00f0e5ac7..49df96134 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -63,6 +63,16 @@ pub enum Opcode { destination: RegisterMemIndex, source: RegisterMemIndex, }, + Load { + destination: RegisterMemIndex, + array_id_reg: RegisterMemIndex, + index: usize, + }, + Store { + source: RegisterMemIndex, + array_id_reg: RegisterMemIndex, + index: usize, + }, /// Used if execution fails during evaluation Trap, /// Hack @@ -84,6 +94,8 @@ impl Opcode { Opcode::Intrinsics => "intrinsics", Opcode::Oracle(_) => "oracle", Opcode::Mov { .. } => "mov", + Opcode::Load { .. } => "load", + Opcode::Store { .. } => "store", Opcode::Trap => "trap", Opcode::Bootstrap { .. } => "bootstrap", Opcode::Stop => "stop", diff --git a/brillig_bytecode/src/registers.rs b/brillig_bytecode/src/registers.rs index cf074943c..a885ded12 100644 --- a/brillig_bytecode/src/registers.rs +++ b/brillig_bytecode/src/registers.rs @@ -9,6 +9,32 @@ pub struct Registers { pub inner: Vec, } +impl IntoIterator for Registers { + type Item = Value; + type IntoIter = RegistersIntoIterator; + + fn into_iter(self) -> Self::IntoIter { + RegistersIntoIterator { registers: self, index: 0 } + } +} +pub struct RegistersIntoIterator { + registers: Registers, + index: usize, +} + +impl Iterator for RegistersIntoIterator { + type Item = Value; + + fn next(&mut self) -> Option { + if self.index >= self.registers.inner.len() { + return None; + } + + self.index += 1; + Some(self.registers.inner[self.index - 1]) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub struct RegisterIndex(pub usize); diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs index d5bd3d9fb..d66313876 100644 --- a/brillig_bytecode/src/value.rs +++ b/brillig_bytecode/src/value.rs @@ -47,6 +47,12 @@ impl From for Value { } } +impl From for Value { + fn from(value: u32) -> Self { + Value { typ: Typ::Field, inner: FieldElement::from(value as i128) } + } +} + impl From for Value { fn from(value: bool) -> Self { if value { From 608201f3f283b4be99cddd84eaf6d790a291763e Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 11 Apr 2023 17:36:28 +0100 Subject: [PATCH 035/125] switch store load index field to RegisterMemIndex (#193) --- brillig_bytecode/src/lib.rs | 21 ++++++++++++++++----- brillig_bytecode/src/opcodes.rs | 4 ++-- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 03e1a9cff..2056b02ee 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -150,7 +150,12 @@ impl VM { let array = &self.memory[&array_id]; match destination { RegisterMemIndex::Register(dest_index) => { - self.registers.set(*dest_index, array.memory_map[index]); + let index_value = self.registers.get(*index); + let index_usize = usize::try_from( + index_value.inner.try_to_u64().expect("register does not fit into u64"), + ) + .expect("register does not fit into usize"); + self.registers.set(*dest_index, array.memory_map[&index_usize]); } _ => return VMStatus::Failure, // TODO: add variants to VMStatus::Failure for more informed failures } @@ -160,7 +165,14 @@ impl VM { let source_value = self.registers.get(*source); let array_id = self.registers.get(*array_id_reg); let heap = &mut self.memory.entry(array_id).or_default().memory_map; - heap.insert(*index, source_value); + + let index_value = self.registers.get(*index); + let index_usize = usize::try_from( + index_value.inner.try_to_u64().expect("register does not fit into u64"), + ) + .expect("register does not fit into usize"); + heap.insert(index_usize, source_value); + self.increment_program_counter() } } @@ -483,7 +495,7 @@ fn load_opcode() { let load_opcode = Opcode::Load { destination: RegisterMemIndex::Register(RegisterIndex(4)), array_id_reg: RegisterMemIndex::Register(RegisterIndex(3)), - index: 1, + index: RegisterMemIndex::Register(RegisterIndex(2)), }; let mem_equal_opcode = Opcode::BinaryOp { @@ -566,7 +578,7 @@ fn store_opcode() { let store_opcode = Opcode::Store { source: RegisterMemIndex::Register(RegisterIndex(2)), array_id_reg: RegisterMemIndex::Register(RegisterIndex(3)), - index: 3, + index: RegisterMemIndex::Constant(FieldElement::from(3_u128)), }; let mut initial_memory = BTreeMap::new(); @@ -598,7 +610,6 @@ fn store_opcode() { // jump_opcode let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - dbg!(status); // jump_if_opcode let status = vm.process_opcode(); diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 49df96134..14f420473 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -66,12 +66,12 @@ pub enum Opcode { Load { destination: RegisterMemIndex, array_id_reg: RegisterMemIndex, - index: usize, + index: RegisterMemIndex, }, Store { source: RegisterMemIndex, array_id_reg: RegisterMemIndex, - index: usize, + index: RegisterMemIndex, }, /// Used if execution fails during evaluation Trap, From 7daf334a17d810761524ab4ae9e31e9ab446a12a Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 12 Apr 2023 14:15:04 -0400 Subject: [PATCH 036/125] return whole unresolved brillig opcode instead of just the oracle for the caller to access --- acvm/src/lib.rs | 53 +++++++++++++++++++------------------------------ 1 file changed, 20 insertions(+), 33 deletions(-) diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 441878930..c25a95ad0 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -138,6 +138,7 @@ pub trait PartialWitnessGenerator { stalled = false; // InProgressBrillig Oracles must be externally re-solved unresolved_brillig_oracles.push(oracle_wait_info); + unresolved_opcodes.push(opcode.clone()); } Ok(OpcodeResolution::Stalled(not_solvable)) => { if opcode_not_solvable.is_none() { @@ -487,7 +488,7 @@ mod test { output_values: vec![], }); - let mut brillig_bytecode = vec![equal_opcode, less_than_opcode, invert_oracle]; + let brillig_bytecode = vec![equal_opcode, less_than_opcode, invert_oracle]; let brillig_opcode = Opcode::Brillig(Brillig { inputs: vec![ @@ -504,7 +505,7 @@ mod test { BrilligOutputs::Simple(w_equal_res), BrilligOutputs::Simple(w_lt_res), ], - bytecode: brillig_bytecode.clone(), + bytecode: brillig_bytecode, predicate: None, }); @@ -535,48 +536,34 @@ mod test { (Witness(2), FieldElement::from(3u128)), ]); let mut blocks = Blocks::default(); - let UnresolvedData { unresolved_opcodes, mut unresolved_brillig_oracles, .. } = pwg + let UnresolvedData { mut unresolved_opcodes, mut unresolved_brillig_oracles, .. } = pwg .solve(&mut witness_assignments, &mut blocks, opcodes) .expect("should stall on oracle"); - assert!(unresolved_opcodes.is_empty(), "opcode should be removed"); + assert_eq!(unresolved_opcodes.len(), 1, "should have an unresolved brillig opcode"); assert_eq!(unresolved_brillig_oracles.len(), 1, "should have a brillig oracle request"); let oracle_wait_info = unresolved_brillig_oracles.remove(0); let mut oracle_data = oracle_wait_info.data; assert_eq!(oracle_data.inputs.len(), 1, "Should have solved a single input"); - // Filling data request and continue solving + // Fill data request and continue solving oracle_data.output_values = vec![oracle_data.input_values.last().unwrap().inverse()]; let invert_oracle = brillig_bytecode::Opcode::Oracle(oracle_data); - brillig_bytecode[oracle_wait_info.program_counter] = invert_oracle; - // Update the bytecode to only start where we were stopped the previous VM process - let new_brillig_bytecode = brillig_bytecode[oracle_wait_info.program_counter..].to_vec(); - let mut next_opcodes_for_solving = vec![Opcode::Brillig(Brillig { - inputs: vec![ - BrilligInputs::Simple(Expression { - mul_terms: vec![], - linear_combinations: vec![(fe_1, w_x), (fe_1, w_y)], - q_c: fe_0, - }), - BrilligInputs::Simple(Expression::default()), - // These are the Brillig binary op results - // We include the register values here so that they are part of the witness - BrilligInputs::Simple(Expression::default()), - BrilligInputs::Simple(Expression::default()), - ], - outputs: vec![ - BrilligOutputs::Simple(w_x_plus_y), - BrilligOutputs::Simple(w_oracle), - BrilligOutputs::Simple(w_equal_res), - BrilligOutputs::Simple(w_lt_res), - ], - bytecode: new_brillig_bytecode, - predicate: None, - })]; + // Alter Brillig oracle opcode + let mut brillig_opcode = unresolved_opcodes.remove(0); + brillig_opcode = match brillig_opcode.clone() { + Opcode::Brillig(mut brillig) => { + brillig.bytecode[oracle_wait_info.program_counter] = invert_oracle; + Opcode::Brillig(brillig) + } + _ => unreachable!("should have extracted a brillig opcode"), + }; + let mut next_opcodes_for_solving = vec![brillig_opcode]; next_opcodes_for_solving.extend_from_slice(&unresolved_opcodes[..]); + let UnresolvedData { unresolved_opcodes, unresolved_brillig_oracles, .. } = pwg .solve(&mut witness_assignments, &mut blocks, next_opcodes_for_solving) .expect("should not stall on oracle"); @@ -647,7 +634,7 @@ mod test { BrilligOutputs::Simple(w_equal_res), BrilligOutputs::Simple(w_lt_res), ], - bytecode: brillig_bytecode.clone(), + bytecode: brillig_bytecode, predicate: Some(Expression::default()), }); @@ -673,11 +660,11 @@ mod test { (Witness(2), FieldElement::from(3u128)), ]); let mut blocks = Blocks::default(); - let UnresolvedData { unresolved_opcodes, mut unresolved_brillig_oracles, .. } = pwg + let UnresolvedData { unresolved_opcodes, unresolved_brillig_oracles, .. } = pwg .solve(&mut witness_assignments, &mut blocks, opcodes) .expect("should stall on oracle"); assert!(unresolved_opcodes.is_empty(), "opcode should be removed"); - assert_eq!(unresolved_brillig_oracles.len(), 0, "should have a brillig oracle request"); + assert!(unresolved_brillig_oracles.is_empty(), "should have no unresolved oracles"); } } From 1f37025243cd2c07c7d532da47e6bc7abd2a1cfd Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 13 Apr 2023 09:24:28 +0100 Subject: [PATCH 037/125] feat: Brillig oracle array outputs (#195) * handle oracle array outputs * switch to single output register * simplify memory opcode creation in oracle arr test --- acvm/src/lib.rs | 4 +-- brillig_bytecode/src/lib.rs | 60 ++++++++++++++++++++++++++++++--- brillig_bytecode/src/opcodes.rs | 6 ++-- 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 441878930..251e9f048 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -483,7 +483,7 @@ mod test { name: "invert".into(), inputs: vec![RegisterMemIndex::Register(RegisterIndex(0))], input_values: vec![], - outputs: vec![RegisterIndex(1)], + output: RegisterIndex(1), output_values: vec![], }); @@ -626,7 +626,7 @@ mod test { name: "invert".into(), inputs: vec![RegisterMemIndex::Register(RegisterIndex(0))], input_values: vec![], - outputs: vec![RegisterIndex(1)], + output: RegisterIndex(1), output_values: vec![], }); diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 2056b02ee..1aca5a1dd 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -10,7 +10,6 @@ mod opcodes; mod registers; mod value; -use std::array; use std::collections::BTreeMap; use acir_field::FieldElement; @@ -118,9 +117,13 @@ impl VM { } Opcode::Intrinsics => todo!(), Opcode::Oracle(data) => { - if data.outputs.len() == data.output_values.len() { - for (index, value) in data.outputs.iter().zip(data.output_values.iter()) { - self.registers.set(*index, (*value).into()) + if data.output_values.len() == 1 { + self.registers.set(data.output, data.output_values[0].into()); + } else if data.output_values.len() > 1 { + let register = self.registers.get(RegisterMemIndex::Register(data.output)); + let heap = &mut self.memory.entry(register).or_default().memory_map; + for (i, value) in data.output_values.iter().enumerate() { + heap.insert(i, (*value).into()); } } else { self.status = VMStatus::OracleWait; @@ -617,3 +620,52 @@ fn store_opcode() { vm.finish(); } + +#[test] +fn oracle_array_output() { + let input_registers = Registers::load(vec![ + Value::from(2u128), + Value::from(2u128), + Value::from(0u128), + Value::from(5u128), + Value::from(0u128), + Value::from(6u128), + Value::from(0u128), + ]); + + let mut oracle_data = OracleData { + name: "get_notes".to_owned(), + inputs: vec![RegisterMemIndex::Register(RegisterIndex(0))], + input_values: vec![], + output: RegisterIndex(3), + output_values: vec![], + }; + + let oracle_opcode = Opcode::Oracle(oracle_data.clone()); + + let initial_memory = BTreeMap::new(); + + let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode]); + + let output_state = vm.process_opcodes(); + assert_eq!(output_state.status, VMStatus::OracleWait); + + let input_values = oracle_data + .clone() + .inputs + .into_iter() + .map(|register_mem_index| output_state.registers.get(register_mem_index).inner) + .collect::>(); + + oracle_data.input_values = input_values; + oracle_data.output_values = vec![FieldElement::from(10_u128), FieldElement::from(2_u128)]; + let updated_oracle_opcode = Opcode::Oracle(oracle_data); + + let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode]); + let output_state = vm.process_opcodes(); + assert_eq!(output_state.status, VMStatus::Halted); + + let mem_array = output_state.memory[&Value::from(5u128)].clone(); + assert_eq!(mem_array.memory_map[&0], Value::from(10_u128)); + assert_eq!(mem_array.memory_map[&1], Value::from(2_u128)); +} diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 14f420473..02d7df19e 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -107,12 +107,12 @@ impl Opcode { pub struct OracleData { /// Name of the oracle pub name: String, - /// Inputs + /// Input registers pub inputs: Vec, /// Input values pub input_values: Vec, - /// Output witness - pub outputs: Vec, + /// Output register + pub output: RegisterIndex, /// Output values - they are computed by the (external) oracle once the inputs are known pub output_values: Vec, } From 7a1336e4278325928bf6a18e37d6594ef53c5876 Mon Sep 17 00:00:00 2001 From: Joss Date: Thu, 13 Apr 2023 10:44:23 +0100 Subject: [PATCH 038/125] chore(acvm): couple unresolved brillig and brillig oracle to make resolving less brittle --- acvm/src/lib.rs | 53 +++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index c25a95ad0..559f9183c 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -11,7 +11,7 @@ use crate::pwg::{arithmetic::ArithmeticSolver, brillig::BrilligSolver, oracle::O use acir::{ circuit::{ directives::Directive, - opcodes::{BlackBoxFuncCall, OracleData}, + opcodes::{BlackBoxFuncCall, Brillig, OracleData}, Circuit, Opcode, }, native_types::{Expression, Witness}, @@ -80,7 +80,7 @@ pub trait PartialWitnessGenerator { ) -> Result { let mut unresolved_opcodes: Vec = Vec::new(); let mut unresolved_oracles: Vec = Vec::new(); - let mut unresolved_brillig_oracles: Vec = Vec::new(); + let mut unresolved_brilligs: Vec = Vec::new(); while !opcode_to_solve.is_empty() || !unresolved_oracles.is_empty() { unresolved_opcodes.clear(); let mut stalled = true; @@ -137,8 +137,11 @@ pub trait PartialWitnessGenerator { Ok(OpcodeResolution::InProgressBrillig(oracle_wait_info)) => { stalled = false; // InProgressBrillig Oracles must be externally re-solved - unresolved_brillig_oracles.push(oracle_wait_info); - unresolved_opcodes.push(opcode.clone()); + let brillig = match opcode { + Opcode::Brillig(brillig) => brillig.clone(), + _ => unreachable!("Brillig resolution for non brillig opcode"), + }; + unresolved_brilligs.push(UnresolvedBrillig { brillig, oracle_wait_info }) } Ok(OpcodeResolution::Stalled(not_solvable)) => { if opcode_not_solvable.is_none() { @@ -164,11 +167,11 @@ pub trait PartialWitnessGenerator { } } // We have oracles that must be externally resolved - if !unresolved_oracles.is_empty() | !unresolved_brillig_oracles.is_empty() { + if !unresolved_oracles.is_empty() | !unresolved_brilligs.is_empty() { return Ok(UnresolvedData { unresolved_opcodes, unresolved_oracles, - unresolved_brillig_oracles, + unresolved_brilligs, }); } // We are stalled because of an opcode being bad @@ -183,7 +186,7 @@ pub trait PartialWitnessGenerator { Ok(UnresolvedData { unresolved_opcodes: Vec::new(), unresolved_oracles: Vec::new(), - unresolved_brillig_oracles: Vec::new(), + unresolved_brilligs: Vec::new(), }) } @@ -221,10 +224,15 @@ pub trait PartialWitnessGenerator { } } +pub struct UnresolvedBrillig { + pub brillig: Brillig, + pub oracle_wait_info: brillig::OracleWaitInfo, +} + pub struct UnresolvedData { pub unresolved_opcodes: Vec, pub unresolved_oracles: Vec, - pub unresolved_brillig_oracles: Vec, + pub unresolved_brilligs: Vec, } pub trait SmartContract { @@ -362,7 +370,7 @@ mod test { use crate::{ pwg::block::Blocks, OpcodeResolution, OpcodeResolutionError, PartialWitnessGenerator, - UnresolvedData, + UnresolvedBrillig, UnresolvedData, }; struct StubbedPwg; @@ -536,14 +544,14 @@ mod test { (Witness(2), FieldElement::from(3u128)), ]); let mut blocks = Blocks::default(); - let UnresolvedData { mut unresolved_opcodes, mut unresolved_brillig_oracles, .. } = pwg + let UnresolvedData { unresolved_opcodes, mut unresolved_brilligs, .. } = pwg .solve(&mut witness_assignments, &mut blocks, opcodes) .expect("should stall on oracle"); - assert_eq!(unresolved_opcodes.len(), 1, "should have an unresolved brillig opcode"); - assert_eq!(unresolved_brillig_oracles.len(), 1, "should have a brillig oracle request"); + assert_eq!(unresolved_opcodes.len(), 0, "brillig should have been removed"); + assert_eq!(unresolved_brilligs.len(), 1, "should have a brillig oracle request"); - let oracle_wait_info = unresolved_brillig_oracles.remove(0); + let UnresolvedBrillig { oracle_wait_info, mut brillig } = unresolved_brilligs.remove(0); let mut oracle_data = oracle_wait_info.data; assert_eq!(oracle_data.inputs.len(), 1, "Should have solved a single input"); @@ -552,24 +560,17 @@ mod test { let invert_oracle = brillig_bytecode::Opcode::Oracle(oracle_data); // Alter Brillig oracle opcode - let mut brillig_opcode = unresolved_opcodes.remove(0); - brillig_opcode = match brillig_opcode.clone() { - Opcode::Brillig(mut brillig) => { - brillig.bytecode[oracle_wait_info.program_counter] = invert_oracle; - Opcode::Brillig(brillig) - } - _ => unreachable!("should have extracted a brillig opcode"), - }; + brillig.bytecode[oracle_wait_info.program_counter] = invert_oracle; - let mut next_opcodes_for_solving = vec![brillig_opcode]; + let mut next_opcodes_for_solving = vec![Opcode::Brillig(brillig)]; next_opcodes_for_solving.extend_from_slice(&unresolved_opcodes[..]); - let UnresolvedData { unresolved_opcodes, unresolved_brillig_oracles, .. } = pwg + let UnresolvedData { unresolved_opcodes, unresolved_brilligs, .. } = pwg .solve(&mut witness_assignments, &mut blocks, next_opcodes_for_solving) .expect("should not stall on oracle"); assert!(unresolved_opcodes.is_empty(), "should be fully solved"); - assert!(unresolved_brillig_oracles.is_empty(), "should have no unresolved oracles"); + assert!(unresolved_brilligs.is_empty(), "should have no unresolved oracles"); } #[test] @@ -660,11 +661,11 @@ mod test { (Witness(2), FieldElement::from(3u128)), ]); let mut blocks = Blocks::default(); - let UnresolvedData { unresolved_opcodes, unresolved_brillig_oracles, .. } = pwg + let UnresolvedData { unresolved_opcodes, unresolved_brilligs, .. } = pwg .solve(&mut witness_assignments, &mut blocks, opcodes) .expect("should stall on oracle"); assert!(unresolved_opcodes.is_empty(), "opcode should be removed"); - assert!(unresolved_brillig_oracles.is_empty(), "should have no unresolved oracles"); + assert!(unresolved_brilligs.is_empty(), "should have no unresolved oracles"); } } From 27cdaddf02bb23bd592ebf19bf029a1546e0da8f Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 17 Apr 2023 17:19:05 +0100 Subject: [PATCH 039/125] add OracleInput struct and handle them in brillig solver (#199) --- acvm/src/lib.rs | 18 +++++-- acvm/src/pwg/brillig.rs | 20 ++++--- brillig_bytecode/src/lib.rs | 94 ++++++++++++++++++++++++++++++--- brillig_bytecode/src/opcodes.rs | 8 ++- 4 files changed, 122 insertions(+), 18 deletions(-) diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 69234b392..5a0f49f76 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -358,7 +358,9 @@ mod test { use acir::{ brillig_bytecode, - brillig_bytecode::{BinaryOp, Comparison, RegisterIndex, RegisterMemIndex, Typ}, + brillig_bytecode::{ + BinaryOp, Comparison, OracleInput, RegisterIndex, RegisterMemIndex, Typ, + }, circuit::{ directives::Directive, opcodes::{BlackBoxFuncCall, Brillig, BrilligInputs, BrilligOutputs, OracleData}, @@ -488,9 +490,14 @@ mod test { result: RegisterIndex(3), }; + let invert_oracle_input = OracleInput { + register_mem_index: RegisterMemIndex::Register(RegisterIndex(0)), + length: 0, + }; + let invert_oracle = brillig_bytecode::Opcode::Oracle(brillig_bytecode::OracleData { name: "invert".into(), - inputs: vec![RegisterMemIndex::Register(RegisterIndex(0))], + inputs: vec![invert_oracle_input], input_values: vec![], output: RegisterIndex(1), output_values: vec![], @@ -610,9 +617,14 @@ mod test { result: RegisterIndex(3), }; + let invert_oracle_input = OracleInput { + register_mem_index: RegisterMemIndex::Register(RegisterIndex(0)), + length: 0, + }; + let invert_oracle = brillig_bytecode::Opcode::Oracle(brillig_bytecode::OracleData { name: "invert".into(), - inputs: vec![RegisterMemIndex::Register(RegisterIndex(0))], + inputs: vec![invert_oracle_input], input_values: vec![], output: RegisterIndex(1), output_values: vec![], diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index ac1ba48c0..f37fe16d7 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -126,12 +126,20 @@ impl BrilligSolver { } }; - let input_values = data - .clone() - .inputs - .into_iter() - .map(|register_mem_index| registers.get(register_mem_index).inner) - .collect::>(); + let mut input_values = Vec::new(); + for oracle_input in data.clone().inputs { + if oracle_input.length == 0 { + let x = registers.get(oracle_input.register_mem_index).inner; + input_values.push(x); + } else { + let array_id = registers.get(oracle_input.register_mem_index); + let array = memory[&array_id].clone(); + let heap_fields = + array.memory_map.into_values().map(|value| value.inner).collect::>(); + input_values.extend(heap_fields); + } + } + data.input_values = input_values; return Ok(OpcodeResolution::InProgressBrillig(OracleWaitInfo { diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 1aca5a1dd..fefebd12b 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -14,7 +14,7 @@ use std::collections::BTreeMap; use acir_field::FieldElement; pub use opcodes::RegisterMemIndex; -pub use opcodes::{BinaryOp, Comparison, Opcode, OracleData}; +pub use opcodes::{BinaryOp, Comparison, Opcode, OracleData, OracleInput}; pub use registers::{RegisterIndex, Registers}; pub use value::Typ; pub use value::Value; @@ -623,6 +623,8 @@ fn store_opcode() { #[test] fn oracle_array_output() { + use crate::opcodes::OracleInput; + let input_registers = Registers::load(vec![ Value::from(2u128), Value::from(2u128), @@ -633,9 +635,12 @@ fn oracle_array_output() { Value::from(0u128), ]); + let oracle_input = + OracleInput { register_mem_index: RegisterMemIndex::Register(RegisterIndex(0)), length: 0 }; + let mut oracle_data = OracleData { name: "get_notes".to_owned(), - inputs: vec![RegisterMemIndex::Register(RegisterIndex(0))], + inputs: vec![oracle_input], input_values: vec![], output: RegisterIndex(3), output_values: vec![], @@ -650,12 +655,19 @@ fn oracle_array_output() { let output_state = vm.process_opcodes(); assert_eq!(output_state.status, VMStatus::OracleWait); - let input_values = oracle_data - .clone() - .inputs - .into_iter() - .map(|register_mem_index| output_state.registers.get(register_mem_index).inner) - .collect::>(); + let mut input_values = Vec::new(); + for oracle_input in oracle_data.clone().inputs { + if oracle_input.length == 0 { + let x = output_state.registers.get(oracle_input.register_mem_index).inner; + input_values.push(x); + } else { + let array_id = output_state.registers.get(oracle_input.register_mem_index); + let array = output_state.memory[&array_id].clone(); + let heap_fields = + array.memory_map.into_values().map(|value| value.inner).collect::>(); + input_values.extend(heap_fields); + } + } oracle_data.input_values = input_values; oracle_data.output_values = vec![FieldElement::from(10_u128), FieldElement::from(2_u128)]; @@ -669,3 +681,69 @@ fn oracle_array_output() { assert_eq!(mem_array.memory_map[&0], Value::from(10_u128)); assert_eq!(mem_array.memory_map[&1], Value::from(2_u128)); } + +#[test] +fn oracle_array_input() { + use crate::opcodes::OracleInput; + + let input_registers = Registers::load(vec![ + Value::from(2u128), + Value::from(2u128), + Value::from(0u128), + Value::from(5u128), + Value::from(0u128), + Value::from(6u128), + Value::from(0u128), + ]); + + let oracle_input = + OracleInput { register_mem_index: RegisterMemIndex::Register(RegisterIndex(3)), length: 2 }; + + let mut oracle_data = OracleData { + name: "call_private_function_oracle".to_owned(), + inputs: vec![oracle_input.clone()], + input_values: vec![], + output: RegisterIndex(6), + output_values: vec![], + }; + + let oracle_opcode = Opcode::Oracle(oracle_data.clone()); + + let mut initial_memory = BTreeMap::new(); + let initial_heap = ArrayHeap { + memory_map: BTreeMap::from([(0 as usize, Value::from(5u128)), (1, Value::from(6u128))]), + }; + initial_memory.insert(Value::from(5u128), initial_heap); + + let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode]); + + let output_state = vm.process_opcodes(); + assert_eq!(output_state.status, VMStatus::OracleWait); + + let mut input_values = Vec::new(); + for oracle_input in oracle_data.clone().inputs { + if oracle_input.length == 0 { + let x = output_state.registers.get(oracle_input.register_mem_index).inner; + input_values.push(x); + } else { + let array_id = output_state.registers.get(oracle_input.register_mem_index); + let array = output_state.memory[&array_id].clone(); + let heap_fields = + array.memory_map.into_values().map(|value| value.inner).collect::>(); + input_values.extend(heap_fields); + } + } + assert_eq!(input_values.len(), oracle_input.length); + + oracle_data.input_values = input_values; + oracle_data.output_values = vec![FieldElement::from(5_u128)]; + let updated_oracle_opcode = Opcode::Oracle(oracle_data); + + let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode]); + let output_state = vm.process_opcodes(); + assert_eq!(output_state.status, VMStatus::Halted); + + let mem_array = output_state.memory[&Value::from(5u128)].clone(); + assert_eq!(mem_array.memory_map[&0], Value::from(5_u128)); + assert_eq!(mem_array.memory_map[&1], Value::from(6_u128)); +} diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 02d7df19e..5bee76a4d 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -108,7 +108,7 @@ pub struct OracleData { /// Name of the oracle pub name: String, /// Input registers - pub inputs: Vec, + pub inputs: Vec, /// Input values pub input_values: Vec, /// Output register @@ -117,6 +117,12 @@ pub struct OracleData { pub output_values: Vec, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct OracleInput { + pub register_mem_index: RegisterMemIndex, + pub length: usize, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum BinaryOp { Add, From 7ba98fcdab10c182a8d90d4e91af9de736fa1b36 Mon Sep 17 00:00:00 2001 From: jfecher Date: Mon, 17 Apr 2023 13:59:14 -0500 Subject: [PATCH 040/125] fix: Change OracleInput to an enum (#200) * Fix OracleInput representation * Fix tests --- acvm/src/lib.rs | 12 +- acvm/src/pwg/brillig.rs | 29 +- brillig_bytecode/src/lib.rs | 963 ++++++++++++++++---------------- brillig_bytecode/src/memory.rs | 1 + brillig_bytecode/src/opcodes.rs | 6 +- brillig_bytecode/src/value.rs | 4 +- 6 files changed, 506 insertions(+), 509 deletions(-) diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 5a0f49f76..1096779ef 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -490,10 +490,8 @@ mod test { result: RegisterIndex(3), }; - let invert_oracle_input = OracleInput { - register_mem_index: RegisterMemIndex::Register(RegisterIndex(0)), - length: 0, - }; + let invert_oracle_input = + OracleInput::RegisterMemIndex(RegisterMemIndex::Register(RegisterIndex(0))); let invert_oracle = brillig_bytecode::Opcode::Oracle(brillig_bytecode::OracleData { name: "invert".into(), @@ -617,10 +615,8 @@ mod test { result: RegisterIndex(3), }; - let invert_oracle_input = OracleInput { - register_mem_index: RegisterMemIndex::Register(RegisterIndex(0)), - length: 0, - }; + let invert_oracle_input = + OracleInput::RegisterMemIndex(RegisterMemIndex::Register(RegisterIndex(0))); let invert_oracle = brillig_bytecode::Opcode::Oracle(brillig_bytecode::OracleData { name: "invert".into(), diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index f37fe16d7..135e5e10d 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -1,9 +1,7 @@ use std::collections::BTreeMap; use acir::{ - brillig_bytecode::{ - ArrayHeap, Opcode, OracleData, Registers, Typ, VMOutputState, VMStatus, Value, VM, - }, + brillig_bytecode::{ArrayHeap, Opcode, OracleData, Registers, Typ, VMStatus, Value, VM}, circuit::opcodes::{Brillig, BrilligInputs, BrilligOutputs}, native_types::Witness, FieldElement, @@ -112,9 +110,11 @@ impl BrilligSolver { let input_registers = Registers { inner: input_register_values }; let vm = VM::new(input_registers, input_memory, brillig.bytecode.clone()); - let VMOutputState { registers, program_counter, status, memory } = vm.process_opcodes(); + let vm_output = vm.process_opcodes(); + + if vm_output.status == VMStatus::OracleWait { + let program_counter = vm_output.program_counter; - if status == VMStatus::OracleWait { let current_opcode = &brillig.bytecode[program_counter]; let mut data = match current_opcode.clone() { Opcode::Oracle(data) => data, @@ -126,20 +126,7 @@ impl BrilligSolver { } }; - let mut input_values = Vec::new(); - for oracle_input in data.clone().inputs { - if oracle_input.length == 0 { - let x = registers.get(oracle_input.register_mem_index).inner; - input_values.push(x); - } else { - let array_id = registers.get(oracle_input.register_mem_index); - let array = memory[&array_id].clone(); - let heap_fields = - array.memory_map.into_values().map(|value| value.inner).collect::>(); - input_values.extend(heap_fields); - } - } - + let input_values = vm_output.map_input_values(&data); data.input_values = input_values; return Ok(OpcodeResolution::InProgressBrillig(OracleWaitInfo { @@ -148,13 +135,13 @@ impl BrilligSolver { })); } - for (output, register_value) in brillig.outputs.iter().zip(registers) { + for (output, register_value) in brillig.outputs.iter().zip(vm_output.registers) { match output { BrilligOutputs::Simple(witness) => { insert_witness(*witness, register_value.inner, initial_witness)?; } BrilligOutputs::Array(witness_arr) => { - let array = memory[®ister_value].memory_map.values(); + let array = vm_output.memory[®ister_value].memory_map.values(); for (witness, value) in witness_arr.iter().zip(array) { insert_witness(*witness, value.inner, initial_witness)?; } diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index fefebd12b..3c1882826 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -210,7 +210,7 @@ impl VM { lhs: RegisterMemIndex, rhs: RegisterMemIndex, result: RegisterIndex, - result_type: Typ, + _result_type: Typ, ) { let lhs_value = self.registers.get(lhs); let rhs_value = self.registers.get(rhs); @@ -240,510 +240,523 @@ pub struct VMOutputState { pub memory: BTreeMap, } -#[test] -fn add_single_step_smoke() { - // Load values into registers and initialize the registers that - // will be used during bytecode processing - let input_registers = - Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(0u128)]); - - // Add opcode to add the value in register `0` and `1` - // and place the output in register `2` - let opcode = Opcode::BinaryOp { - op: BinaryOp::Add, - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(1)), - result: RegisterIndex(2), - result_type: Typ::Field, - }; - - // Start VM - let mut vm = VM::new(input_registers, BTreeMap::new(), vec![opcode]); - - // Process a single VM opcode - // - // After processing a single opcode, we should have - // the vm status as halted since there is only one opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Halted); - - // The register at index `2` should have the value of 3 since we had an - // add opcode - let VMOutputState { registers, .. } = vm.finish(); - let output_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); - - assert_eq!(output_value, Value::from(3u128)) +impl VMOutputState { + pub fn map_input_values(&self, oracle_data: &OracleData) -> Vec { + let mut input_values = vec![]; + for oracle_input in &oracle_data.inputs { + match oracle_input { + OracleInput::RegisterMemIndex(register_index) => { + let register = self.registers.get(*register_index); + input_values.push(register.inner); + } + OracleInput::Array { start, length } => { + let array_id = self.registers.get(*start); + let array = &self.memory[&array_id]; + let heap_fields = array.memory_map.values().map(|value| value.inner.clone()); + + assert_eq!(heap_fields.len(), *length); + input_values.extend(heap_fields); + } + } + } + input_values + } } -#[test] -fn jmpif_opcode() { - let input_registers = - Registers::load(vec![Value::from(2u128), Value::from(2u128), Value::from(0u128)]); +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn add_single_step_smoke() { + // Load values into registers and initialize the registers that + // will be used during bytecode processing + let input_registers = + Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(0u128)]); + + // Add opcode to add the value in register `0` and `1` + // and place the output in register `2` + let opcode = Opcode::BinaryOp { + op: BinaryOp::Add, + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(1)), + result: RegisterIndex(2), + result_type: Typ::Field, + }; - let equal_cmp_opcode = Opcode::BinaryOp { - result_type: Typ::Field, - op: BinaryOp::Cmp(Comparison::Eq), - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(1)), - result: RegisterIndex(2), - }; + // Start VM + let mut vm = VM::new(input_registers, BTreeMap::new(), vec![opcode]); - let jump_opcode = Opcode::JMP { destination: 2 }; + // Process a single VM opcode + // + // After processing a single opcode, we should have + // the vm status as halted since there is only one opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::Halted); - let jump_if_opcode = - Opcode::JMPIF { condition: RegisterMemIndex::Register(RegisterIndex(2)), destination: 3 }; + // The register at index `2` should have the value of 3 since we had an + // add opcode + let VMOutputState { registers, .. } = vm.finish(); + let output_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); - let mut vm = VM::new( - input_registers, - BTreeMap::new(), - vec![equal_cmp_opcode, jump_opcode, jump_if_opcode], - ); + assert_eq!(output_value, Value::from(3u128)) + } - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); + #[test] + fn jmpif_opcode() { + let input_registers = + Registers::load(vec![Value::from(2u128), Value::from(2u128), Value::from(0u128)]); + + let equal_cmp_opcode = Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Eq), + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(1)), + result: RegisterIndex(2), + }; - let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); - assert_eq!(output_cmp_value, Value::from(true)); + let jump_opcode = Opcode::JMP { destination: 2 }; - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); + let jump_if_opcode = Opcode::JMPIF { + condition: RegisterMemIndex::Register(RegisterIndex(2)), + destination: 3, + }; - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Halted); + let mut vm = VM::new( + input_registers, + BTreeMap::new(), + vec![equal_cmp_opcode, jump_opcode, jump_if_opcode], + ); - vm.finish(); -} + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); -#[test] -fn jmpifnot_opcode() { - let input_registers = - Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(0u128)]); - - let trap_opcode = Opcode::Trap; - - let not_equal_cmp_opcode = Opcode::BinaryOp { - result_type: Typ::Field, - op: BinaryOp::Cmp(Comparison::Eq), - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(1)), - result: RegisterIndex(2), - }; - - let jump_opcode = Opcode::JMP { destination: 2 }; - - let jump_if_not_opcode = Opcode::JMPIFNOT { - condition: RegisterMemIndex::Register(RegisterIndex(2)), - destination: 1, - }; - - let add_opcode = Opcode::BinaryOp { - op: BinaryOp::Add, - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(1)), - result: RegisterIndex(2), - result_type: Typ::Field, - }; - - let mut vm = VM::new( - input_registers, - BTreeMap::new(), - vec![jump_opcode, trap_opcode, not_equal_cmp_opcode, jump_if_not_opcode, add_opcode], - ); - - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); - assert_eq!(output_cmp_value, Value::from(false)); - - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Failure); - - // The register at index `2` should have not changed as we jumped over the add opcode - let VMOutputState { registers, .. } = vm.finish(); - let output_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); - assert_eq!(output_value, Value::from(false)); -} + let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(output_cmp_value, Value::from(true)); -#[test] -fn mov_opcode() { - let input_registers = - Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(3u128)]); + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); - let mov_opcode = Opcode::Mov { - destination: RegisterMemIndex::Register(RegisterIndex(2)), - source: RegisterMemIndex::Register(RegisterIndex(0)), - }; + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::Halted); - let mut vm = VM::new(input_registers, BTreeMap::new(), vec![mov_opcode]); + vm.finish(); + } - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Halted); + #[test] + fn jmpifnot_opcode() { + let input_registers = + Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(0u128)]); - let VMOutputState { registers, .. } = vm.finish(); + let trap_opcode = Opcode::Trap; - let destination_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); - assert_eq!(destination_value, Value::from(1u128)); + let not_equal_cmp_opcode = Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Eq), + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(1)), + result: RegisterIndex(2), + }; - let source_value = registers.get(RegisterMemIndex::Register(RegisterIndex(0))); - assert_eq!(source_value, Value::from(1u128)); -} + let jump_opcode = Opcode::JMP { destination: 2 }; -#[test] -fn cmp_binary_ops() { - let input_registers = Registers::load(vec![ - Value::from(2u128), - Value::from(2u128), - Value::from(0u128), - Value::from(5u128), - Value::from(6u128), - ]); - - let equal_opcode = Opcode::BinaryOp { - result_type: Typ::Field, - op: BinaryOp::Cmp(Comparison::Eq), - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(1)), - result: RegisterIndex(2), - }; - - let not_equal_opcode = Opcode::BinaryOp { - result_type: Typ::Field, - op: BinaryOp::Cmp(Comparison::Eq), - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(3)), - result: RegisterIndex(2), - }; - - let less_than_opcode = Opcode::BinaryOp { - result_type: Typ::Field, - op: BinaryOp::Cmp(Comparison::Lt), - lhs: RegisterMemIndex::Register(RegisterIndex(3)), - rhs: RegisterMemIndex::Register(RegisterIndex(4)), - result: RegisterIndex(2), - }; - - let less_than_equal_opcode = Opcode::BinaryOp { - result_type: Typ::Field, - op: BinaryOp::Cmp(Comparison::Lte), - lhs: RegisterMemIndex::Register(RegisterIndex(3)), - rhs: RegisterMemIndex::Register(RegisterIndex(4)), - result: RegisterIndex(2), - }; - - let mut vm = VM::new( - input_registers, - BTreeMap::new(), - vec![equal_opcode, not_equal_opcode, less_than_opcode, less_than_equal_opcode], - ); - - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - let output_eq_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); - assert_eq!(output_eq_value, Value::from(true)); - - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - let output_neq_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); - assert_eq!(output_neq_value, Value::from(false)); - - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - let lt_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); - assert_eq!(lt_value, Value::from(true)); - - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Halted); - - let lte_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); - assert_eq!(lte_value, Value::from(true)); - - vm.finish(); -} + let jump_if_not_opcode = Opcode::JMPIFNOT { + condition: RegisterMemIndex::Register(RegisterIndex(2)), + destination: 1, + }; -#[test] -fn load_opcode() { - let input_registers = Registers::load(vec![ - Value::from(2u128), - Value::from(2u128), - Value::from(0u128), - Value::from(5u128), - Value::from(0u128), - Value::from(6u128), - Value::from(0u128), - ]); - - let equal_cmp_opcode = Opcode::BinaryOp { - result_type: Typ::Field, - op: BinaryOp::Cmp(Comparison::Eq), - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(1)), - result: RegisterIndex(2), - }; - - let jump_opcode = Opcode::JMP { destination: 3 }; - - let jump_if_opcode = - Opcode::JMPIF { condition: RegisterMemIndex::Register(RegisterIndex(2)), destination: 10 }; - - let load_opcode = Opcode::Load { - destination: RegisterMemIndex::Register(RegisterIndex(4)), - array_id_reg: RegisterMemIndex::Register(RegisterIndex(3)), - index: RegisterMemIndex::Register(RegisterIndex(2)), - }; - - let mem_equal_opcode = Opcode::BinaryOp { - result_type: Typ::Field, - op: BinaryOp::Cmp(Comparison::Eq), - lhs: RegisterMemIndex::Register(RegisterIndex(4)), - rhs: RegisterMemIndex::Register(RegisterIndex(5)), - result: RegisterIndex(6), - }; - - let mut initial_memory = BTreeMap::new(); - let initial_heap = ArrayHeap { - memory_map: BTreeMap::from([(0 as usize, Value::from(5u128)), (1, Value::from(6u128))]), - }; - initial_memory.insert(Value::from(5u128), initial_heap); - - let mut vm = VM::new( - input_registers, - initial_memory, - vec![equal_cmp_opcode, load_opcode, jump_opcode, mem_equal_opcode, jump_if_opcode], - ); - - // equal_cmp_opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); - assert_eq!(output_cmp_value, Value::from(true)); - - // load_opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(4))); - assert_eq!(output_cmp_value, Value::from(6u128)); - - // jump_opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - // mem_equal_opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(6))); - assert_eq!(output_cmp_value, Value::from(true)); - - // jump_if_opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Halted); - - vm.finish(); -} + let add_opcode = Opcode::BinaryOp { + op: BinaryOp::Add, + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(1)), + result: RegisterIndex(2), + result_type: Typ::Field, + }; -#[test] -fn store_opcode() { - let input_registers = Registers::load(vec![ - Value::from(2u128), - Value::from(2u128), - Value::from(0u128), - Value::from(5u128), - Value::from(0u128), - Value::from(6u128), - Value::from(0u128), - ]); - - let equal_cmp_opcode = Opcode::BinaryOp { - result_type: Typ::Field, - op: BinaryOp::Cmp(Comparison::Eq), - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(1)), - result: RegisterIndex(2), - }; - - let jump_opcode = Opcode::JMP { destination: 3 }; - - let jump_if_opcode = - Opcode::JMPIF { condition: RegisterMemIndex::Register(RegisterIndex(2)), destination: 10 }; - - let store_opcode = Opcode::Store { - source: RegisterMemIndex::Register(RegisterIndex(2)), - array_id_reg: RegisterMemIndex::Register(RegisterIndex(3)), - index: RegisterMemIndex::Constant(FieldElement::from(3_u128)), - }; - - let mut initial_memory = BTreeMap::new(); - let initial_heap = ArrayHeap { - memory_map: BTreeMap::from([(0 as usize, Value::from(5u128)), (1, Value::from(6u128))]), - }; - initial_memory.insert(Value::from(5u128), initial_heap); - - let mut vm = VM::new( - input_registers, - initial_memory, - vec![equal_cmp_opcode, store_opcode, jump_opcode, jump_if_opcode], - ); - - // equal_cmp_opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); - assert_eq!(output_cmp_value, Value::from(true)); - - // store_opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - let mem_array = vm.memory[&Value::from(5u128)].clone(); - assert_eq!(mem_array.memory_map[&3], Value::from(true)); - - // jump_opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - // jump_if_opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Halted); - - vm.finish(); -} + let mut vm = VM::new( + input_registers, + BTreeMap::new(), + vec![jump_opcode, trap_opcode, not_equal_cmp_opcode, jump_if_not_opcode, add_opcode], + ); -#[test] -fn oracle_array_output() { - use crate::opcodes::OracleInput; - - let input_registers = Registers::load(vec![ - Value::from(2u128), - Value::from(2u128), - Value::from(0u128), - Value::from(5u128), - Value::from(0u128), - Value::from(6u128), - Value::from(0u128), - ]); - - let oracle_input = - OracleInput { register_mem_index: RegisterMemIndex::Register(RegisterIndex(0)), length: 0 }; - - let mut oracle_data = OracleData { - name: "get_notes".to_owned(), - inputs: vec![oracle_input], - input_values: vec![], - output: RegisterIndex(3), - output_values: vec![], - }; - - let oracle_opcode = Opcode::Oracle(oracle_data.clone()); - - let initial_memory = BTreeMap::new(); - - let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode]); - - let output_state = vm.process_opcodes(); - assert_eq!(output_state.status, VMStatus::OracleWait); - - let mut input_values = Vec::new(); - for oracle_input in oracle_data.clone().inputs { - if oracle_input.length == 0 { - let x = output_state.registers.get(oracle_input.register_mem_index).inner; - input_values.push(x); - } else { - let array_id = output_state.registers.get(oracle_input.register_mem_index); - let array = output_state.memory[&array_id].clone(); - let heap_fields = - array.memory_map.into_values().map(|value| value.inner).collect::>(); - input_values.extend(heap_fields); - } + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(output_cmp_value, Value::from(false)); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::Failure); + + // The register at index `2` should have not changed as we jumped over the add opcode + let VMOutputState { registers, .. } = vm.finish(); + let output_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(output_value, Value::from(false)); } - oracle_data.input_values = input_values; - oracle_data.output_values = vec![FieldElement::from(10_u128), FieldElement::from(2_u128)]; - let updated_oracle_opcode = Opcode::Oracle(oracle_data); + #[test] + fn mov_opcode() { + let input_registers = + Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(3u128)]); - let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode]); - let output_state = vm.process_opcodes(); - assert_eq!(output_state.status, VMStatus::Halted); + let mov_opcode = Opcode::Mov { + destination: RegisterMemIndex::Register(RegisterIndex(2)), + source: RegisterMemIndex::Register(RegisterIndex(0)), + }; - let mem_array = output_state.memory[&Value::from(5u128)].clone(); - assert_eq!(mem_array.memory_map[&0], Value::from(10_u128)); - assert_eq!(mem_array.memory_map[&1], Value::from(2_u128)); -} + let mut vm = VM::new(input_registers, BTreeMap::new(), vec![mov_opcode]); -#[test] -fn oracle_array_input() { - use crate::opcodes::OracleInput; - - let input_registers = Registers::load(vec![ - Value::from(2u128), - Value::from(2u128), - Value::from(0u128), - Value::from(5u128), - Value::from(0u128), - Value::from(6u128), - Value::from(0u128), - ]); - - let oracle_input = - OracleInput { register_mem_index: RegisterMemIndex::Register(RegisterIndex(3)), length: 2 }; - - let mut oracle_data = OracleData { - name: "call_private_function_oracle".to_owned(), - inputs: vec![oracle_input.clone()], - input_values: vec![], - output: RegisterIndex(6), - output_values: vec![], - }; - - let oracle_opcode = Opcode::Oracle(oracle_data.clone()); - - let mut initial_memory = BTreeMap::new(); - let initial_heap = ArrayHeap { - memory_map: BTreeMap::from([(0 as usize, Value::from(5u128)), (1, Value::from(6u128))]), - }; - initial_memory.insert(Value::from(5u128), initial_heap); - - let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode]); - - let output_state = vm.process_opcodes(); - assert_eq!(output_state.status, VMStatus::OracleWait); - - let mut input_values = Vec::new(); - for oracle_input in oracle_data.clone().inputs { - if oracle_input.length == 0 { - let x = output_state.registers.get(oracle_input.register_mem_index).inner; - input_values.push(x); - } else { - let array_id = output_state.registers.get(oracle_input.register_mem_index); - let array = output_state.memory[&array_id].clone(); - let heap_fields = - array.memory_map.into_values().map(|value| value.inner).collect::>(); - input_values.extend(heap_fields); - } + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::Halted); + + let VMOutputState { registers, .. } = vm.finish(); + + let destination_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(destination_value, Value::from(1u128)); + + let source_value = registers.get(RegisterMemIndex::Register(RegisterIndex(0))); + assert_eq!(source_value, Value::from(1u128)); + } + + #[test] + fn cmp_binary_ops() { + let input_registers = Registers::load(vec![ + Value::from(2u128), + Value::from(2u128), + Value::from(0u128), + Value::from(5u128), + Value::from(6u128), + ]); + + let equal_opcode = Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Eq), + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(1)), + result: RegisterIndex(2), + }; + + let not_equal_opcode = Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Eq), + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(3)), + result: RegisterIndex(2), + }; + + let less_than_opcode = Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Lt), + lhs: RegisterMemIndex::Register(RegisterIndex(3)), + rhs: RegisterMemIndex::Register(RegisterIndex(4)), + result: RegisterIndex(2), + }; + + let less_than_equal_opcode = Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Lte), + lhs: RegisterMemIndex::Register(RegisterIndex(3)), + rhs: RegisterMemIndex::Register(RegisterIndex(4)), + result: RegisterIndex(2), + }; + + let mut vm = VM::new( + input_registers, + BTreeMap::new(), + vec![equal_opcode, not_equal_opcode, less_than_opcode, less_than_equal_opcode], + ); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let output_eq_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(output_eq_value, Value::from(true)); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let output_neq_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(output_neq_value, Value::from(false)); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let lt_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(lt_value, Value::from(true)); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::Halted); + + let lte_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(lte_value, Value::from(true)); + + vm.finish(); } - assert_eq!(input_values.len(), oracle_input.length); - oracle_data.input_values = input_values; - oracle_data.output_values = vec![FieldElement::from(5_u128)]; - let updated_oracle_opcode = Opcode::Oracle(oracle_data); + #[test] + fn load_opcode() { + let input_registers = Registers::load(vec![ + Value::from(2u128), + Value::from(2u128), + Value::from(0u128), + Value::from(5u128), + Value::from(0u128), + Value::from(6u128), + Value::from(0u128), + ]); + + let equal_cmp_opcode = Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Eq), + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(1)), + result: RegisterIndex(2), + }; + + let jump_opcode = Opcode::JMP { destination: 3 }; - let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode]); - let output_state = vm.process_opcodes(); - assert_eq!(output_state.status, VMStatus::Halted); + let jump_if_opcode = Opcode::JMPIF { + condition: RegisterMemIndex::Register(RegisterIndex(2)), + destination: 10, + }; - let mem_array = output_state.memory[&Value::from(5u128)].clone(); - assert_eq!(mem_array.memory_map[&0], Value::from(5_u128)); - assert_eq!(mem_array.memory_map[&1], Value::from(6_u128)); + let load_opcode = Opcode::Load { + destination: RegisterMemIndex::Register(RegisterIndex(4)), + array_id_reg: RegisterMemIndex::Register(RegisterIndex(3)), + index: RegisterMemIndex::Register(RegisterIndex(2)), + }; + + let mem_equal_opcode = Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Eq), + lhs: RegisterMemIndex::Register(RegisterIndex(4)), + rhs: RegisterMemIndex::Register(RegisterIndex(5)), + result: RegisterIndex(6), + }; + + let mut initial_memory = BTreeMap::new(); + let initial_heap = ArrayHeap { + memory_map: BTreeMap::from([(0 as usize, Value::from(5u128)), (1, Value::from(6u128))]), + }; + initial_memory.insert(Value::from(5u128), initial_heap); + + let mut vm = VM::new( + input_registers, + initial_memory, + vec![equal_cmp_opcode, load_opcode, jump_opcode, mem_equal_opcode, jump_if_opcode], + ); + + // equal_cmp_opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(output_cmp_value, Value::from(true)); + + // load_opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(4))); + assert_eq!(output_cmp_value, Value::from(6u128)); + + // jump_opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + // mem_equal_opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(6))); + assert_eq!(output_cmp_value, Value::from(true)); + + // jump_if_opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::Halted); + + vm.finish(); + } + + #[test] + fn store_opcode() { + let input_registers = Registers::load(vec![ + Value::from(2u128), + Value::from(2u128), + Value::from(0u128), + Value::from(5u128), + Value::from(0u128), + Value::from(6u128), + Value::from(0u128), + ]); + + let equal_cmp_opcode = Opcode::BinaryOp { + result_type: Typ::Field, + op: BinaryOp::Cmp(Comparison::Eq), + lhs: RegisterMemIndex::Register(RegisterIndex(0)), + rhs: RegisterMemIndex::Register(RegisterIndex(1)), + result: RegisterIndex(2), + }; + + let jump_opcode = Opcode::JMP { destination: 3 }; + + let jump_if_opcode = Opcode::JMPIF { + condition: RegisterMemIndex::Register(RegisterIndex(2)), + destination: 10, + }; + + let store_opcode = Opcode::Store { + source: RegisterMemIndex::Register(RegisterIndex(2)), + array_id_reg: RegisterMemIndex::Register(RegisterIndex(3)), + index: RegisterMemIndex::Constant(FieldElement::from(3_u128)), + }; + + let mut initial_memory = BTreeMap::new(); + let initial_heap = ArrayHeap { + memory_map: BTreeMap::from([(0 as usize, Value::from(5u128)), (1, Value::from(6u128))]), + }; + initial_memory.insert(Value::from(5u128), initial_heap); + + let mut vm = VM::new( + input_registers, + initial_memory, + vec![equal_cmp_opcode, store_opcode, jump_opcode, jump_if_opcode], + ); + + // equal_cmp_opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + assert_eq!(output_cmp_value, Value::from(true)); + + // store_opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let mem_array = vm.memory[&Value::from(5u128)].clone(); + assert_eq!(mem_array.memory_map[&3], Value::from(true)); + + // jump_opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + // jump_if_opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::Halted); + + vm.finish(); + } + + #[test] + fn oracle_array_output() { + use crate::opcodes::OracleInput; + + let input_registers = Registers::load(vec![ + Value::from(2u128), + Value::from(2u128), + Value::from(0u128), + Value::from(5u128), + Value::from(0u128), + Value::from(6u128), + Value::from(0u128), + ]); + + let oracle_input = + OracleInput::RegisterMemIndex(RegisterMemIndex::Register(RegisterIndex(0))); + + let mut oracle_data = OracleData { + name: "get_notes".to_owned(), + inputs: vec![oracle_input], + input_values: vec![], + output: RegisterIndex(3), + output_values: vec![], + }; + + let oracle_opcode = Opcode::Oracle(oracle_data.clone()); + + let initial_memory = BTreeMap::new(); + + let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode]); + + let output_state = vm.process_opcodes(); + assert_eq!(output_state.status, VMStatus::OracleWait); + + let input_values = output_state.map_input_values(&oracle_data); + + oracle_data.input_values = input_values; + oracle_data.output_values = vec![FieldElement::from(10_u128), FieldElement::from(2_u128)]; + let updated_oracle_opcode = Opcode::Oracle(oracle_data); + + let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode]); + let output_state = vm.process_opcodes(); + assert_eq!(output_state.status, VMStatus::Halted); + + let mem_array = output_state.memory[&Value::from(5u128)].clone(); + assert_eq!(mem_array.memory_map[&0], Value::from(10_u128)); + assert_eq!(mem_array.memory_map[&1], Value::from(2_u128)); + } + + #[test] + fn oracle_array_input() { + use crate::opcodes::OracleInput; + + let input_registers = Registers::load(vec![ + Value::from(2u128), + Value::from(2u128), + Value::from(0u128), + Value::from(5u128), + Value::from(0u128), + Value::from(6u128), + Value::from(0u128), + ]); + + let expected_length = 2; + let oracle_input = OracleInput::Array { + start: RegisterMemIndex::Register(RegisterIndex(3)), + length: expected_length, + }; + + let mut oracle_data = OracleData { + name: "call_private_function_oracle".to_owned(), + inputs: vec![oracle_input.clone()], + input_values: vec![], + output: RegisterIndex(6), + output_values: vec![], + }; + + let oracle_opcode = Opcode::Oracle(oracle_data.clone()); + + let mut initial_memory = BTreeMap::new(); + let initial_heap = ArrayHeap { + memory_map: BTreeMap::from([(0 as usize, Value::from(5u128)), (1, Value::from(6u128))]), + }; + initial_memory.insert(Value::from(5u128), initial_heap); + + let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode]); + + let output_state = vm.process_opcodes(); + assert_eq!(output_state.status, VMStatus::OracleWait); + + let input_values = output_state.map_input_values(&oracle_data); + assert_eq!(input_values.len(), expected_length); + + oracle_data.input_values = input_values; + oracle_data.output_values = vec![FieldElement::from(5_u128)]; + let updated_oracle_opcode = Opcode::Oracle(oracle_data); + + let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode]); + let output_state = vm.process_opcodes(); + assert_eq!(output_state.status, VMStatus::Halted); + + let mem_array = output_state.memory[&Value::from(5u128)].clone(); + assert_eq!(mem_array.memory_map[&0], Value::from(5_u128)); + assert_eq!(mem_array.memory_map[&1], Value::from(6_u128)); + } } diff --git a/brillig_bytecode/src/memory.rs b/brillig_bytecode/src/memory.rs index 78d216404..24b218dac 100644 --- a/brillig_bytecode/src/memory.rs +++ b/brillig_bytecode/src/memory.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Serialize}; + /// Memory in the VM is used for storing arrays /// /// ArrayIndex will be used to reference an Array element. diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 5bee76a4d..f206f2f6d 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -118,9 +118,9 @@ pub struct OracleData { } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct OracleInput { - pub register_mem_index: RegisterMemIndex, - pub length: usize, +pub enum OracleInput { + RegisterMemIndex(RegisterMemIndex), + Array { start: RegisterMemIndex, length: usize }, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs index d66313876..a59e8b02b 100644 --- a/brillig_bytecode/src/value.rs +++ b/brillig_bytecode/src/value.rs @@ -26,10 +26,10 @@ impl Value { pub fn inverse(&self) -> Value { let value = match self.typ { Typ::Field => self.inner.inverse(), - Typ::Unsigned { bit_size } => { + Typ::Unsigned { bit_size: _ } => { todo!("TODO") } - Typ::Signed { bit_size } => todo!("TODO"), + Typ::Signed { bit_size: _ } => todo!("TODO"), }; Value { typ: self.typ, inner: value } } From 69a0798d87856872f99f8ec634b75e86f05103fb Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 18 Apr 2023 14:35:02 +0100 Subject: [PATCH 041/125] feat!: Use OracleOutput enum in OracleData (#201) * make an OracleOutput enum similar to OracleInput * delete unnecessary comments --- acvm/src/lib.rs | 8 +++--- brillig_bytecode/src/lib.rs | 44 ++++++++++++++++++++++++--------- brillig_bytecode/src/opcodes.rs | 8 +++++- 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 1096779ef..5052d7667 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -359,7 +359,7 @@ mod test { use acir::{ brillig_bytecode, brillig_bytecode::{ - BinaryOp, Comparison, OracleInput, RegisterIndex, RegisterMemIndex, Typ, + BinaryOp, Comparison, OracleInput, OracleOutput, RegisterIndex, RegisterMemIndex, Typ, }, circuit::{ directives::Directive, @@ -492,12 +492,13 @@ mod test { let invert_oracle_input = OracleInput::RegisterMemIndex(RegisterMemIndex::Register(RegisterIndex(0))); + let invert_oracle_output = OracleOutput::RegisterIndex(RegisterIndex(1)); let invert_oracle = brillig_bytecode::Opcode::Oracle(brillig_bytecode::OracleData { name: "invert".into(), inputs: vec![invert_oracle_input], input_values: vec![], - output: RegisterIndex(1), + outputs: vec![invert_oracle_output], output_values: vec![], }); @@ -617,12 +618,13 @@ mod test { let invert_oracle_input = OracleInput::RegisterMemIndex(RegisterMemIndex::Register(RegisterIndex(0))); + let invert_oracle_output = OracleOutput::RegisterIndex(RegisterIndex(1)); let invert_oracle = brillig_bytecode::Opcode::Oracle(brillig_bytecode::OracleData { name: "invert".into(), inputs: vec![invert_oracle_input], input_values: vec![], - output: RegisterIndex(1), + outputs: vec![invert_oracle_output], output_values: vec![], }); diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 3c1882826..882678368 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -14,7 +14,7 @@ use std::collections::BTreeMap; use acir_field::FieldElement; pub use opcodes::RegisterMemIndex; -pub use opcodes::{BinaryOp, Comparison, Opcode, OracleData, OracleInput}; +pub use opcodes::{BinaryOp, Comparison, Opcode, OracleData, OracleInput, OracleOutput}; pub use registers::{RegisterIndex, Registers}; pub use value::Typ; pub use value::Value; @@ -117,18 +117,38 @@ impl VM { } Opcode::Intrinsics => todo!(), Opcode::Oracle(data) => { - if data.output_values.len() == 1 { - self.registers.set(data.output, data.output_values[0].into()); - } else if data.output_values.len() > 1 { - let register = self.registers.get(RegisterMemIndex::Register(data.output)); - let heap = &mut self.memory.entry(register).or_default().memory_map; - for (i, value) in data.output_values.iter().enumerate() { - heap.insert(i, (*value).into()); + let mut num_output_values = 0; + for oracle_output in data.clone().outputs { + match oracle_output { + OracleOutput::RegisterIndex(_) => num_output_values += 1, + OracleOutput::Array { length, .. } => num_output_values += length, } - } else { + } + if num_output_values != data.output_values.len() { self.status = VMStatus::OracleWait; return VMStatus::OracleWait; + } else { + let mut current_value_index = 0; + for oracle_output in data.clone().outputs { + match oracle_output { + OracleOutput::RegisterIndex(index) => { + self.registers + .set(index, data.output_values[current_value_index].into()); + current_value_index += 1 + } + OracleOutput::Array { start, length } => { + let array_id = + self.registers.get(RegisterMemIndex::Register(start)); + let heap = &mut self.memory.entry(array_id).or_default().memory_map; + for (i, value) in data.output_values.iter().enumerate() { + heap.insert(i, (*value).into()); + } + current_value_index += length + } + } + } } + self.increment_program_counter() } Opcode::Mov { destination, source } => { @@ -670,12 +690,13 @@ mod tests { let oracle_input = OracleInput::RegisterMemIndex(RegisterMemIndex::Register(RegisterIndex(0))); + let oracle_output = OracleOutput::Array { start: RegisterIndex(3), length: 2 }; let mut oracle_data = OracleData { name: "get_notes".to_owned(), inputs: vec![oracle_input], input_values: vec![], - output: RegisterIndex(3), + outputs: vec![oracle_output], output_values: vec![], }; @@ -722,12 +743,13 @@ mod tests { start: RegisterMemIndex::Register(RegisterIndex(3)), length: expected_length, }; + let oracle_output = OracleOutput::RegisterIndex(RegisterIndex(6)); let mut oracle_data = OracleData { name: "call_private_function_oracle".to_owned(), inputs: vec![oracle_input.clone()], input_values: vec![], - output: RegisterIndex(6), + outputs: vec![oracle_output], output_values: vec![], }; diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index f206f2f6d..b704f85c3 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -112,7 +112,7 @@ pub struct OracleData { /// Input values pub input_values: Vec, /// Output register - pub output: RegisterIndex, + pub outputs: Vec, /// Output values - they are computed by the (external) oracle once the inputs are known pub output_values: Vec, } @@ -123,6 +123,12 @@ pub enum OracleInput { Array { start: RegisterMemIndex, length: usize }, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum OracleOutput { + RegisterIndex(RegisterIndex), + Array { start: RegisterIndex, length: usize }, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum BinaryOp { Add, From 1547d447ee3020bccc89f39c01ea714588e8c080 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 18 Apr 2023 15:11:41 +0100 Subject: [PATCH 042/125] update array start to registermemindex as it can be a constant (#205) --- brillig_bytecode/src/lib.rs | 6 +++--- brillig_bytecode/src/opcodes.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 882678368..4962c2f75 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -137,8 +137,7 @@ impl VM { current_value_index += 1 } OracleOutput::Array { start, length } => { - let array_id = - self.registers.get(RegisterMemIndex::Register(start)); + let array_id = self.registers.get(start); let heap = &mut self.memory.entry(array_id).or_default().memory_map; for (i, value) in data.output_values.iter().enumerate() { heap.insert(i, (*value).into()); @@ -690,7 +689,8 @@ mod tests { let oracle_input = OracleInput::RegisterMemIndex(RegisterMemIndex::Register(RegisterIndex(0))); - let oracle_output = OracleOutput::Array { start: RegisterIndex(3), length: 2 }; + let oracle_output = + OracleOutput::Array { start: RegisterMemIndex::Register(RegisterIndex(3)), length: 2 }; let mut oracle_data = OracleData { name: "get_notes".to_owned(), diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index b704f85c3..36b7d631c 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -126,7 +126,7 @@ pub enum OracleInput { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum OracleOutput { RegisterIndex(RegisterIndex), - Array { start: RegisterIndex, length: usize }, + Array { start: RegisterMemIndex, length: usize }, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] From ea5b5db4cbe46538b1cbc28769e681fc0c1d32fb Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 18 Apr 2023 15:37:53 +0100 Subject: [PATCH 043/125] handle unresolved brillig data when stalled correctly (#208) --- acvm/src/lib.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 5052d7667..83b4ee2a4 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -151,14 +151,13 @@ pub trait PartialWitnessGenerator { // We push those opcodes not solvable to the back as // it could be because the opcodes are out of order, i.e. this assignment // relies on a later opcodes' results - unresolved_opcodes.push(match solved_oracle_data { - Some(oracle_data) => Opcode::Oracle(oracle_data), - None => opcode.clone(), - }); - unresolved_opcodes.push(match solved_brillig_data { - Some(brillig) => Opcode::Brillig(brillig), - None => opcode.clone(), - }) + if let Some(oracle_data) = solved_oracle_data { + unresolved_opcodes.push(Opcode::Oracle(oracle_data)); + } else if let Some(brillig) = solved_brillig_data { + unresolved_opcodes.push(Opcode::Brillig(brillig)); + } else { + unresolved_opcodes.push(opcode.clone()); + } } Err(OpcodeResolutionError::OpcodeNotSolvable(_)) => { unreachable!("ICE - Result should have been converted to GateResolution") From c2e3d031435a792a2820618eba18083d4c428166 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 19 Apr 2023 13:45:07 +0100 Subject: [PATCH 044/125] feat!: Handle result type of Binary Ops in Brillig (#202) * initial work for wrapping addition with unsigned int * new evaluate method for bin ops that checks the res_type and wraps ops appropriately * handle signed and unsigned division for binary ops --- brillig_bytecode/Cargo.toml | 1 + brillig_bytecode/src/lib.rs | 5 +- brillig_bytecode/src/opcodes.rs | 87 ++++++++++++++++++++++++++++++--- 3 files changed, 83 insertions(+), 10 deletions(-) diff --git a/brillig_bytecode/Cargo.toml b/brillig_bytecode/Cargo.toml index 54746490d..3b1c9f94f 100644 --- a/brillig_bytecode/Cargo.toml +++ b/brillig_bytecode/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] acir_field.workspace = true serde.workspace = true +num-bigint = "0.4" [features] bn254 = ["acir_field/bn254"] diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 4962c2f75..70a77a82f 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -13,6 +13,7 @@ mod value; use std::collections::BTreeMap; use acir_field::FieldElement; +use num_bigint::{BigInt, Sign}; pub use opcodes::RegisterMemIndex; pub use opcodes::{BinaryOp, Comparison, Opcode, OracleData, OracleInput, OracleOutput}; pub use registers::{RegisterIndex, Registers}; @@ -229,12 +230,12 @@ impl VM { lhs: RegisterMemIndex, rhs: RegisterMemIndex, result: RegisterIndex, - _result_type: Typ, + result_type: Typ, ) { let lhs_value = self.registers.get(lhs); let rhs_value = self.registers.get(rhs); - let result_value = op.function()(lhs_value, rhs_value); + let result_value = op.evaluate(lhs_value, rhs_value, result_type); self.registers.set(result, result_value) } diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 36b7d631c..343ebd338 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -1,3 +1,5 @@ +use std::ops::{Add, Mul, Sub}; + use crate::{ memory::ArrayIndex, value::{Typ, Value}, @@ -146,17 +148,86 @@ pub enum Comparison { } impl BinaryOp { - pub fn function(&self) -> fn(Value, Value) -> Value { + pub fn evaluate(&self, a: Value, b: Value, res_type: Typ) -> Value { match self { - BinaryOp::Add => |a: Value, b: Value| a + b, - BinaryOp::Sub => |a: Value, b: Value| a - b, - BinaryOp::Mul => |a: Value, b: Value| a * b, - BinaryOp::Div => |a: Value, b: Value| a / b, + BinaryOp::Add => { + let res_inner = self.wrapping(a.inner, b.inner, res_type, u128::add, Add::add); + Value { typ: res_type, inner: res_inner } + } + BinaryOp::Sub => { + let res_inner = + self.wrapping(a.inner, b.inner, res_type, u128::wrapping_sub, Sub::sub); + Value { typ: res_type, inner: res_inner } + } + BinaryOp::Mul => { + let res_inner = self.wrapping(a.inner, b.inner, res_type, u128::mul, Mul::mul); + Value { typ: res_type, inner: res_inner } + } + BinaryOp::Div => match res_type { + Typ::Field => a / b, + Typ::Unsigned { bit_size } => { + let lhs = a.inner.to_u128() % (1_u128 << bit_size); + let rhs = b.inner.to_u128() % (1_u128 << bit_size); + Value { typ: res_type, inner: FieldElement::from(lhs / rhs) } + } + Typ::Signed { bit_size } => { + let a = field_to_signed(a.inner, bit_size); + let b = field_to_signed(b.inner, bit_size); + let res_inner = signed_to_field(a / b, bit_size); + Value { typ: res_type, inner: res_inner } + } + }, BinaryOp::Cmp(comparison) => match comparison { - Comparison::Eq => |a: Value, b: Value| (a == b).into(), - Comparison::Lt => |a: Value, b: Value| (a.inner < b.inner).into(), - Comparison::Lte => |a: Value, b: Value| (a.inner <= b.inner).into(), + Comparison::Eq => (a == b).into(), + Comparison::Lt => (a.inner < b.inner).into(), + Comparison::Lte => (a.inner <= b.inner).into(), }, } } + + /// Perform the given numeric operation and modulo the result by the max value for the given bit count + /// if the res_type is not a FieldElement. + fn wrapping( + &self, + lhs: FieldElement, + rhs: FieldElement, + res_type: Typ, + u128_op: impl FnOnce(u128, u128) -> u128, + field_op: impl FnOnce(FieldElement, FieldElement) -> FieldElement, + ) -> FieldElement { + match res_type { + Typ::Field => field_op(lhs, rhs), + Typ::Unsigned { bit_size } | Typ::Signed { bit_size } => { + let type_modulo = 1_u128 << bit_size; + let lhs = lhs.to_u128() % type_modulo; + let rhs = rhs.to_u128() % type_modulo; + let mut x = u128_op(lhs, rhs); + x %= type_modulo; + FieldElement::from(x) + } + } + } +} + +fn field_to_signed(f: FieldElement, n: u32) -> i128 { + assert!(n < 127); + let a = f.to_u128(); + let pow_2 = 2_u128.pow(n); + if a < pow_2 { + a as i128 + } else { + (a - 2 * pow_2) as i128 + } +} + +fn signed_to_field(a: i128, n: u32) -> FieldElement { + if n >= 126 { + panic!("ICE: cannot convert signed {n} bit size into field"); + } + if a >= 0 { + FieldElement::from(a) + } else { + let b = (a + 2_i128.pow(n + 1)) as u128; + FieldElement::from(b) + } } From 836a1b142395141647ee96ef4e6aaecbef111aab Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 20 Apr 2023 14:14:13 +0100 Subject: [PATCH 045/125] initial call stack opcodes and their impl in VM (#215) --- brillig_bytecode/src/lib.rs | 24 +++++++++++++++++------- brillig_bytecode/src/opcodes.rs | 10 +++++++--- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 70a77a82f..1f82d8528 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -41,6 +41,7 @@ pub struct VM { bytecode: Vec, status: VMStatus, memory: BTreeMap, + call_stack: Vec, } impl VM { @@ -71,6 +72,7 @@ impl VM { bytecode, status: VMStatus::InProgress, memory, + call_stack: Vec::new(), }; vm } @@ -108,13 +110,16 @@ impl VM { } self.increment_program_counter() } - Opcode::Call { destination } => { - let register = self.registers.get(*destination); - let label = usize::try_from( - register.inner.try_to_u64().expect("register does not fit into u64"), - ) - .expect("register does not fit into usize"); - self.set_program_counter(label) + Opcode::CallBack => { + if let Some(register) = self.call_stack.pop() { + let label = usize::try_from( + register.inner.try_to_u64().expect("register does not fit into u64"), + ) + .expect("register does not fit into usize"); + self.set_program_counter(label) + } else { + return VMStatus::Halted; + } } Opcode::Intrinsics => todo!(), Opcode::Oracle(data) => { @@ -198,6 +203,11 @@ impl VM { self.increment_program_counter() } + Opcode::PushStack { source } => { + let register = self.registers.get(*source); + self.call_stack.push(register); + self.increment_program_counter() + } } } diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 343ebd338..d41f6b9d0 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -54,9 +54,7 @@ pub enum Opcode { }, // TODO:This is used to call functions and setup things like // TODO execution contexts. - Call { - destination: RegisterMemIndex, - }, + CallBack, // TODO:These are special functions like sha256 Intrinsics, /// Used to get data from an outside source @@ -75,6 +73,12 @@ pub enum Opcode { array_id_reg: RegisterMemIndex, index: RegisterMemIndex, }, + PushStack { + source: RegisterMemIndex, + }, + // PopStack { + + // } /// Used if execution fails during evaluation Trap, /// Hack From 77857ebec10a138e621dea06a466ce9995109712 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 20 Apr 2023 16:22:40 +0100 Subject: [PATCH 046/125] fix reference to old Opcode::Call (#217) --- brillig_bytecode/src/opcodes.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index d41f6b9d0..22b7aeb99 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -52,6 +52,9 @@ pub enum Opcode { JMP { destination: Label, }, + PushStack { + source: RegisterMemIndex, + }, // TODO:This is used to call functions and setup things like // TODO execution contexts. CallBack, @@ -73,12 +76,6 @@ pub enum Opcode { array_id_reg: RegisterMemIndex, index: RegisterMemIndex, }, - PushStack { - source: RegisterMemIndex, - }, - // PopStack { - - // } /// Used if execution fails during evaluation Trap, /// Hack @@ -96,7 +93,8 @@ impl Opcode { Opcode::JMPIFNOT { .. } => "jmpifnot", Opcode::JMPIF { .. } => "jmpif", Opcode::JMP { .. } => "jmp", - Opcode::Call { .. } => "call", + Opcode::PushStack { .. } => "pushstack", + Opcode::CallBack => "callback", Opcode::Intrinsics => "intrinsics", Opcode::Oracle(_) => "oracle", Opcode::Mov { .. } => "mov", From b23bcc1db67833ccf3c0466e0b72a60ce65eee66 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 21 Apr 2023 16:10:00 +0100 Subject: [PATCH 047/125] change id_as_value typ for brillig inputs (#222) --- acvm/src/pwg/brillig.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index 135e5e10d..cd0dda94c 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -67,7 +67,7 @@ impl BrilligSolver { } BrilligInputs::Array(id, expr_arr) => { let id_as_value: Value = Value { - typ: Typ::Unsigned { bit_size: 32 }, + typ: Typ::Field, inner: FieldElement::from(*id as u128), }; // Push value of the array id as a register From b8345c15b9d7186453052fd81bb4653f7c4c725b Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 21 Apr 2023 18:48:26 +0100 Subject: [PATCH 048/125] bitwise operators, shifts, and cleanup for BinaryOp.evaluate (#224) --- acvm/src/pwg/brillig.rs | 6 ++-- brillig_bytecode/src/lib.rs | 18 +++++----- brillig_bytecode/src/opcodes.rs | 59 +++++++++++++++++++++------------ 3 files changed, 49 insertions(+), 34 deletions(-) diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index cd0dda94c..b4c050693 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -66,10 +66,8 @@ impl BrilligSolver { } } BrilligInputs::Array(id, expr_arr) => { - let id_as_value: Value = Value { - typ: Typ::Field, - inner: FieldElement::from(*id as u128), - }; + let id_as_value: Value = + Value { typ: Typ::Field, inner: FieldElement::from(*id as u128) }; // Push value of the array id as a register input_register_values.push(id_as_value.into()); diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 1f82d8528..76c41eeff 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -338,7 +338,7 @@ mod tests { Registers::load(vec![Value::from(2u128), Value::from(2u128), Value::from(0u128)]); let equal_cmp_opcode = Opcode::BinaryOp { - result_type: Typ::Field, + result_type: Typ::Unsigned { bit_size: 1 }, op: BinaryOp::Cmp(Comparison::Eq), lhs: RegisterMemIndex::Register(RegisterIndex(0)), rhs: RegisterMemIndex::Register(RegisterIndex(1)), @@ -381,7 +381,7 @@ mod tests { let trap_opcode = Opcode::Trap; let not_equal_cmp_opcode = Opcode::BinaryOp { - result_type: Typ::Field, + result_type: Typ::Unsigned { bit_size: 1 }, op: BinaryOp::Cmp(Comparison::Eq), lhs: RegisterMemIndex::Register(RegisterIndex(0)), rhs: RegisterMemIndex::Register(RegisterIndex(1)), @@ -465,7 +465,7 @@ mod tests { ]); let equal_opcode = Opcode::BinaryOp { - result_type: Typ::Field, + result_type: Typ::Unsigned { bit_size: 1 }, op: BinaryOp::Cmp(Comparison::Eq), lhs: RegisterMemIndex::Register(RegisterIndex(0)), rhs: RegisterMemIndex::Register(RegisterIndex(1)), @@ -473,7 +473,7 @@ mod tests { }; let not_equal_opcode = Opcode::BinaryOp { - result_type: Typ::Field, + result_type: Typ::Unsigned { bit_size: 1 }, op: BinaryOp::Cmp(Comparison::Eq), lhs: RegisterMemIndex::Register(RegisterIndex(0)), rhs: RegisterMemIndex::Register(RegisterIndex(3)), @@ -481,7 +481,7 @@ mod tests { }; let less_than_opcode = Opcode::BinaryOp { - result_type: Typ::Field, + result_type: Typ::Unsigned { bit_size: 1 }, op: BinaryOp::Cmp(Comparison::Lt), lhs: RegisterMemIndex::Register(RegisterIndex(3)), rhs: RegisterMemIndex::Register(RegisterIndex(4)), @@ -489,7 +489,7 @@ mod tests { }; let less_than_equal_opcode = Opcode::BinaryOp { - result_type: Typ::Field, + result_type: Typ::Unsigned { bit_size: 1 }, op: BinaryOp::Cmp(Comparison::Lte), lhs: RegisterMemIndex::Register(RegisterIndex(3)), rhs: RegisterMemIndex::Register(RegisterIndex(4)), @@ -542,7 +542,7 @@ mod tests { ]); let equal_cmp_opcode = Opcode::BinaryOp { - result_type: Typ::Field, + result_type: Typ::Unsigned { bit_size: 1 }, op: BinaryOp::Cmp(Comparison::Eq), lhs: RegisterMemIndex::Register(RegisterIndex(0)), rhs: RegisterMemIndex::Register(RegisterIndex(1)), @@ -563,7 +563,7 @@ mod tests { }; let mem_equal_opcode = Opcode::BinaryOp { - result_type: Typ::Field, + result_type: Typ::Unsigned { bit_size: 1 }, op: BinaryOp::Cmp(Comparison::Eq), lhs: RegisterMemIndex::Register(RegisterIndex(4)), rhs: RegisterMemIndex::Register(RegisterIndex(5)), @@ -627,7 +627,7 @@ mod tests { ]); let equal_cmp_opcode = Opcode::BinaryOp { - result_type: Typ::Field, + result_type: Typ::Unsigned { bit_size: 1 }, op: BinaryOp::Cmp(Comparison::Eq), lhs: RegisterMemIndex::Register(RegisterIndex(0)), rhs: RegisterMemIndex::Register(RegisterIndex(1)), diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 22b7aeb99..1ebda6a74 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -1,4 +1,4 @@ -use std::ops::{Add, Mul, Sub}; +use std::ops::{Add, BitAnd, BitOr, BitXor, Mul, Shl, Shr, Sub}; use crate::{ memory::ArrayIndex, @@ -140,6 +140,11 @@ pub enum BinaryOp { Mul, Div, Cmp(Comparison), + And, // (&) Bitwise AND + Or, // (|) Bitwise OR + Xor, // (^) Bitwise XOR + Shl, // (<<) Shift left + Shr, // (>>) Shift right } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] @@ -151,40 +156,48 @@ pub enum Comparison { impl BinaryOp { pub fn evaluate(&self, a: Value, b: Value, res_type: Typ) -> Value { - match self { - BinaryOp::Add => { - let res_inner = self.wrapping(a.inner, b.inner, res_type, u128::add, Add::add); - Value { typ: res_type, inner: res_inner } - } + let res_inner = match self { + BinaryOp::Add => self.wrapping(a.inner, b.inner, res_type, u128::add, Add::add), BinaryOp::Sub => { - let res_inner = - self.wrapping(a.inner, b.inner, res_type, u128::wrapping_sub, Sub::sub); - Value { typ: res_type, inner: res_inner } - } - BinaryOp::Mul => { - let res_inner = self.wrapping(a.inner, b.inner, res_type, u128::mul, Mul::mul); - Value { typ: res_type, inner: res_inner } + self.wrapping(a.inner, b.inner, res_type, u128::wrapping_sub, Sub::sub) } + BinaryOp::Mul => self.wrapping(a.inner, b.inner, res_type, u128::mul, Mul::mul), BinaryOp::Div => match res_type { - Typ::Field => a / b, + Typ::Field => a.inner / b.inner, Typ::Unsigned { bit_size } => { let lhs = a.inner.to_u128() % (1_u128 << bit_size); let rhs = b.inner.to_u128() % (1_u128 << bit_size); - Value { typ: res_type, inner: FieldElement::from(lhs / rhs) } + FieldElement::from(lhs / rhs) } Typ::Signed { bit_size } => { let a = field_to_signed(a.inner, bit_size); let b = field_to_signed(b.inner, bit_size); - let res_inner = signed_to_field(a / b, bit_size); - Value { typ: res_type, inner: res_inner } + signed_to_field(a / b, bit_size) } }, BinaryOp::Cmp(comparison) => match comparison { - Comparison::Eq => (a == b).into(), - Comparison::Lt => (a.inner < b.inner).into(), - Comparison::Lte => (a.inner <= b.inner).into(), + Comparison::Eq => ((a == b) as u128).into(), + Comparison::Lt => ((a.inner < b.inner) as u128).into(), + Comparison::Lte => ((a.inner <= b.inner) as u128).into(), }, - } + BinaryOp::And => { + self.wrapping(a.inner, b.inner, res_type, u128::bitand, field_op_not_allowed) + } + BinaryOp::Or => { + self.wrapping(a.inner, b.inner, res_type, u128::bitor, field_op_not_allowed) + } + BinaryOp::Xor => { + self.wrapping(a.inner, b.inner, res_type, u128::bitxor, field_op_not_allowed) + } + BinaryOp::Shl => { + self.wrapping(a.inner, b.inner, res_type, u128::shl, field_op_not_allowed) + } + BinaryOp::Shr => { + self.wrapping(a.inner, b.inner, res_type, u128::shr, field_op_not_allowed) + } + }; + + Value { typ: res_type, inner: res_inner } } /// Perform the given numeric operation and modulo the result by the max value for the given bit count @@ -233,3 +246,7 @@ fn signed_to_field(a: i128, n: u32) -> FieldElement { FieldElement::from(b) } } + +fn field_op_not_allowed(_lhs: FieldElement, _rhs: FieldElement) -> FieldElement { + unreachable!("operation not allowed for FieldElement"); +} From 61eefe50b375ac8088050df0041dd2ee3cb7555c Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 24 Apr 2023 15:44:14 +0100 Subject: [PATCH 049/125] move insertion of array input to brillig only if entire array is solved (#226) --- acvm/src/pwg/brillig.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index b4c050693..508e648f4 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -66,11 +66,7 @@ impl BrilligSolver { } } BrilligInputs::Array(id, expr_arr) => { - let id_as_value: Value = - Value { typ: Typ::Field, inner: FieldElement::from(*id as u128) }; - // Push value of the array id as a register - input_register_values.push(id_as_value.into()); - + // Attempt to fetch all array input values let mut continue_eval = true; let mut array_heap: BTreeMap = BTreeMap::new(); for (i, expr) in expr_arr.into_iter().enumerate() { @@ -82,19 +78,26 @@ impl BrilligSolver { break; } } - input_memory.insert(id_as_value, ArrayHeap { memory_map: array_heap }); + // If an array value is missing exit the input solver and do not insert the array id as an input register if !continue_eval { break; } + + let id_as_value: Value = + Value { typ: Typ::Field, inner: FieldElement::from(*id as u128) }; + // Push value of the array id as a register + input_register_values.push(id_as_value.into()); + + input_memory.insert(id_as_value, ArrayHeap { memory_map: array_heap }); } } } if input_register_values.len() != brillig.inputs.len() { - let jabber_input = + let brillig_input = brillig.inputs.last().expect("Infallible: cannot reach this point if no inputs"); - let expr = match jabber_input { + let expr = match brillig_input { BrilligInputs::Simple(expr) => expr, BrilligInputs::Array(_, expr_arr) => { expr_arr.last().expect("Infallible: cannot reach this point if no inputs") From 3142c0e85af90d8dd20a5681c19339b8a7bdc9ee Mon Sep 17 00:00:00 2001 From: guipublic Date: Tue, 25 Apr 2023 08:38:55 +0000 Subject: [PATCH 050/125] small fix for comparison --- brillig_bytecode/src/opcodes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 1ebda6a74..9cf6417df 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -176,7 +176,7 @@ impl BinaryOp { } }, BinaryOp::Cmp(comparison) => match comparison { - Comparison::Eq => ((a == b) as u128).into(), + Comparison::Eq => ((a.inner == b.inner) as u128).into(), Comparison::Lt => ((a.inner < b.inner) as u128).into(), Comparison::Lte => ((a.inner <= b.inner) as u128).into(), }, From dd86dc58ec143cdd4e6d1d46f86dd4a249a127c7 Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Tue, 25 Apr 2023 16:08:54 +0200 Subject: [PATCH 051/125] Return failure on trap (#229) --- acvm/src/lib.rs | 4 +- acvm/src/pwg/brillig.rs | 74 ++++++++++++++++++++----------------- brillig_bytecode/src/lib.rs | 27 ++++++++++---- 3 files changed, 62 insertions(+), 43 deletions(-) diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 83b4ee2a4..6c3c53ce3 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -115,9 +115,9 @@ pub trait PartialWitnessGenerator { } Opcode::Brillig(brillig) => { let mut brillig_clone = brillig.clone(); - let result = BrilligSolver::solve(initial_witness, &mut brillig_clone)?; + let result = BrilligSolver::solve(initial_witness, &mut brillig_clone); solved_brillig_data = Some(brillig_clone); - Ok(result) + result } }; diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index 508e648f4..d1eb40ee1 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -112,45 +112,51 @@ impl BrilligSolver { let vm = VM::new(input_registers, input_memory, brillig.bytecode.clone()); let vm_output = vm.process_opcodes(); - - if vm_output.status == VMStatus::OracleWait { - let program_counter = vm_output.program_counter; - - let current_opcode = &brillig.bytecode[program_counter]; - let mut data = match current_opcode.clone() { - Opcode::Oracle(data) => data, - _ => { - return Err(OpcodeResolutionError::UnexpectedOpcode( - "brillig oracle", - current_opcode.name(), - )) + + let result = match vm_output.status { + VMStatus::Halted => { + for (output, register_value) in brillig.outputs.iter().zip(vm_output.registers) { + match output { + BrilligOutputs::Simple(witness) => { + insert_witness(*witness, register_value.inner, initial_witness)?; + } + BrilligOutputs::Array(witness_arr) => { + let array = vm_output.memory[®ister_value].memory_map.values(); + for (witness, value) in witness_arr.iter().zip(array) { + insert_witness(*witness, value.inner, initial_witness)?; + } + } + } } - }; - - let input_values = vm_output.map_input_values(&data); - data.input_values = input_values; + OpcodeResolution::Solved + } + VMStatus::InProgress => unreachable!("Brillig VM has not completed execution"), + VMStatus::Failure => return Err(OpcodeResolutionError::UnsatisfiedConstrain), + VMStatus::OracleWait => { + let program_counter = vm_output.program_counter; + + let current_opcode = &brillig.bytecode[program_counter]; + let mut data = match current_opcode.clone() { + Opcode::Oracle(data) => data, + _ => { + return Err(OpcodeResolutionError::UnexpectedOpcode( + "brillig oracle", + current_opcode.name(), + )) + } + }; - return Ok(OpcodeResolution::InProgressBrillig(OracleWaitInfo { - data: data.clone(), - program_counter, - })); - } + let input_values = vm_output.map_input_values(&data); + data.input_values = input_values; - for (output, register_value) in brillig.outputs.iter().zip(vm_output.registers) { - match output { - BrilligOutputs::Simple(witness) => { - insert_witness(*witness, register_value.inner, initial_witness)?; - } - BrilligOutputs::Array(witness_arr) => { - let array = vm_output.memory[®ister_value].memory_map.values(); - for (witness, value) in witness_arr.iter().zip(array) { - insert_witness(*witness, value.inner, initial_witness)?; - } - } + OpcodeResolution::InProgressBrillig(OracleWaitInfo { + data: data.clone(), + program_counter, + }) } - } + }; - Ok(OpcodeResolution::Solved) + Ok(result) } } diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 76c41eeff..b028bf677 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -77,6 +77,20 @@ impl VM { vm } + fn status(&mut self, status: VMStatus) -> VMStatus { + self.status = status; + status + } + fn halt(&mut self) -> VMStatus { + self.status(VMStatus::Halted) + } + fn wait(&mut self) -> VMStatus { + self.status(VMStatus::OracleWait) + } + fn fail(&mut self) -> VMStatus { + self.status(VMStatus::Failure) + } + /// Loop over the bytecode and update the program counter pub fn process_opcodes(mut self) -> VMOutputState { while !matches!( @@ -118,7 +132,7 @@ impl VM { .expect("register does not fit into usize"); self.set_program_counter(label) } else { - return VMStatus::Halted; + return self.halt(); } } Opcode::Intrinsics => todo!(), @@ -131,8 +145,7 @@ impl VM { } } if num_output_values != data.output_values.len() { - self.status = VMStatus::OracleWait; - return VMStatus::OracleWait; + return self.wait(); } else { let mut current_value_index = 0; for oracle_output in data.clone().outputs { @@ -163,16 +176,16 @@ impl VM { RegisterMemIndex::Register(dest_index) => { self.registers.set(*dest_index, source_value) } - _ => return VMStatus::Failure, // TODO: add variants to VMStatus::Failure for more informed failures + _ => return self.fail(), // TODO: add variants to VMStatus::Failure for more informed failures } self.increment_program_counter() } - Opcode::Trap => VMStatus::Failure, + Opcode::Trap => self.fail(), Opcode::Bootstrap { .. } => unreachable!( "should only be at end of opcodes and popped off when initializing the vm" ), - Opcode::Stop => VMStatus::Halted, + Opcode::Stop => self.halt(), Opcode::Load { destination, array_id_reg, index } => { let array_id = self.registers.get(*array_id_reg); let array = &self.memory[&array_id]; @@ -185,7 +198,7 @@ impl VM { .expect("register does not fit into usize"); self.registers.set(*dest_index, array.memory_map[&index_usize]); } - _ => return VMStatus::Failure, // TODO: add variants to VMStatus::Failure for more informed failures + _ => return self.fail(), // TODO: add variants to VMStatus::Failure for more informed failures } self.increment_program_counter() } From ad9c33f4caa48659c4a3d21a72d5d76dc523f480 Mon Sep 17 00:00:00 2001 From: ludamad Date: Thu, 4 May 2023 18:25:07 -0400 Subject: [PATCH 052/125] feat(brillig): experiments --- acvm/src/lib.rs | 38 ++--- acvm/src/pwg/brillig.rs | 2 +- brillig_bytecode/src/lib.rs | 258 ++++++++++++++++-------------- brillig_bytecode/src/opcodes.rs | 189 +++++++++++----------- brillig_bytecode/src/registers.rs | 10 +- brillig_bytecode/src/value.rs | 33 ++-- 6 files changed, 267 insertions(+), 263 deletions(-) diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 6c3c53ce3..8d8b6da8c 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -358,7 +358,7 @@ mod test { use acir::{ brillig_bytecode, brillig_bytecode::{ - BinaryOp, Comparison, OracleInput, OracleOutput, RegisterIndex, RegisterMemIndex, Typ, + Comparison, OracleInput, OracleOutput, RegisterIndex, RegisterMemIndex, Typ, BinaryOp, }, circuit::{ directives::Directive, @@ -473,24 +473,21 @@ mod test { let w_equal_res = Witness(7); let w_lt_res = Witness(8); - let equal_opcode = brillig_bytecode::Opcode::BinaryOp { - result_type: Typ::Field, - op: BinaryOp::Cmp(Comparison::Eq), - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(1)), + let equal_opcode = brillig_bytecode::Opcode::BinaryFieldOp { + op: brillig_bytecode::BinaryOp::Cmp(Comparison::Eq), + lhs: RegisterIndex(0), + rhs: RegisterIndex(1), result: RegisterIndex(2), }; - let less_than_opcode = brillig_bytecode::Opcode::BinaryOp { - result_type: Typ::Field, + let less_than_opcode = brillig_bytecode::Opcode::BinaryFieldOp { op: BinaryOp::Cmp(Comparison::Lt), - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(1)), + lhs: RegisterIndex(0), + rhs: RegisterIndex(1), result: RegisterIndex(3), }; - let invert_oracle_input = - OracleInput::RegisterMemIndex(RegisterMemIndex::Register(RegisterIndex(0))); + let invert_oracle_input = OracleInput::RegisterIndex(RegisterIndex(0)); let invert_oracle_output = OracleOutput::RegisterIndex(RegisterIndex(1)); let invert_oracle = brillig_bytecode::Opcode::Oracle(brillig_bytecode::OracleData { @@ -599,24 +596,21 @@ mod test { let w_equal_res = Witness(7); let w_lt_res = Witness(8); - let equal_opcode = brillig_bytecode::Opcode::BinaryOp { - result_type: Typ::Field, + let equal_opcode = brillig_bytecode::Opcode::BinaryFieldOp { op: BinaryOp::Cmp(Comparison::Eq), - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(1)), + lhs: RegisterIndex(0), + rhs: RegisterIndex(1), result: RegisterIndex(2), }; - let less_than_opcode = brillig_bytecode::Opcode::BinaryOp { - result_type: Typ::Field, + let less_than_opcode = brillig_bytecode::Opcode::BinaryFieldOp { op: BinaryOp::Cmp(Comparison::Lt), - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(1)), + lhs: RegisterIndex(0), + rhs: RegisterIndex(1), result: RegisterIndex(3), }; - let invert_oracle_input = - OracleInput::RegisterMemIndex(RegisterMemIndex::Register(RegisterIndex(0))); + let invert_oracle_input = OracleInput::RegisterIndex(RegisterIndex(0)); let invert_oracle_output = OracleOutput::RegisterIndex(RegisterIndex(1)); let invert_oracle = brillig_bytecode::Opcode::Oracle(brillig_bytecode::OracleData { diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index d1eb40ee1..254933cf6 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -85,7 +85,7 @@ impl BrilligSolver { } let id_as_value: Value = - Value { typ: Typ::Field, inner: FieldElement::from(*id as u128) }; + Value { inner: FieldElement::from(*id as u128) }; // Push value of the array id as a register input_register_values.push(id_as_value.into()); diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index b028bf677..959525f42 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -14,8 +14,9 @@ use std::collections::BTreeMap; use acir_field::FieldElement; use num_bigint::{BigInt, Sign}; +pub use opcodes::BinaryOp; pub use opcodes::RegisterMemIndex; -pub use opcodes::{BinaryOp, Comparison, Opcode, OracleData, OracleInput, OracleOutput}; +pub use opcodes::{Comparison, Opcode, OracleData, OracleInput, OracleOutput}; pub use registers::{RegisterIndex, Registers}; pub use value::Typ; pub use value::Value; @@ -55,11 +56,11 @@ impl VM { if let Opcode::Bootstrap { register_allocation_indices } = last_opcode { // TODO: might have to handle arrays in bootstrap to be correct let mut registers_modified = - Registers::load(vec![Value { typ: Typ::Field, inner: FieldElement::from(0u128) }]); + Registers::load(vec![Value { inner: FieldElement::from(0u128) }]); for i in 0..register_allocation_indices.len() { let register_index = register_allocation_indices[i]; - let register_value = inputs.get(RegisterMemIndex::Register(RegisterIndex(i))); + let register_value = inputs.get(RegisterIndex(i)); registers_modified.set(RegisterIndex(register_index as usize), register_value) } @@ -103,12 +104,16 @@ impl VM { pub fn process_opcode(&mut self) -> VMStatus { let opcode = &self.bytecode[self.program_counter]; match opcode { - Opcode::BinaryOp { op, lhs, rhs, result, result_type } => { - self.process_binary_op(*op, *lhs, *rhs, *result, *result_type); + Opcode::BinaryFieldOp { op, lhs, rhs, result } => { + self.process_binary_field_op(*op, *lhs, *rhs, *result); self.increment_program_counter() } - Opcode::JMP { destination } => self.set_program_counter(*destination), - Opcode::JMPIF { condition, destination } => { + Opcode::BinaryIntOp { op, bit_size, lhs, rhs, result } => { + self.process_binary_int_op(*op, *bit_size, *lhs, *rhs, *result); + self.increment_program_counter() + } + Opcode::Jump { destination } => self.set_program_counter(*destination), + Opcode::JumpIf { condition, destination } => { // Check if condition is true // We use 0 to mean false and any other value to mean true let condition_value = self.registers.get(*condition); @@ -117,14 +122,14 @@ impl VM { } self.increment_program_counter() } - Opcode::JMPIFNOT { condition, destination } => { + Opcode::JumpIfNot { condition, destination } => { let condition_value = self.registers.get(*condition); if condition_value.is_zero() { return self.set_program_counter(*destination); } self.increment_program_counter() } - Opcode::CallBack => { + Opcode::Call => { if let Some(register) = self.call_stack.pop() { let label = usize::try_from( register.inner.try_to_u64().expect("register does not fit into u64"), @@ -171,14 +176,7 @@ impl VM { } Opcode::Mov { destination, source } => { let source_value = self.registers.get(*source); - - match destination { - RegisterMemIndex::Register(dest_index) => { - self.registers.set(*dest_index, source_value) - } - _ => return self.fail(), // TODO: add variants to VMStatus::Failure for more informed failures - } - + self.registers.set(*destination, source_value); self.increment_program_counter() } Opcode::Trap => self.fail(), @@ -189,17 +187,11 @@ impl VM { Opcode::Load { destination, array_id_reg, index } => { let array_id = self.registers.get(*array_id_reg); let array = &self.memory[&array_id]; - match destination { - RegisterMemIndex::Register(dest_index) => { - let index_value = self.registers.get(*index); - let index_usize = usize::try_from( - index_value.inner.try_to_u64().expect("register does not fit into u64"), - ) - .expect("register does not fit into usize"); - self.registers.set(*dest_index, array.memory_map[&index_usize]); - } - _ => return self.fail(), // TODO: add variants to VMStatus::Failure for more informed failures - } + let index_value = self.registers.get(*index); + let index_usize = usize::try_from( + index_value.inner.try_to_u64().expect("register does not fit into u64"), + ).expect("register does not fit into usize"); + self.registers.set(*destination, array.memory_map[&index_usize]); self.increment_program_counter() } Opcode::Store { source, array_id_reg, index } => { @@ -221,6 +213,10 @@ impl VM { self.call_stack.push(register); self.increment_program_counter() } + Opcode::LoadConst { destination, constant } => { + self.registers.set(*destination, Value { inner: *constant }); + self.increment_program_counter() + } } } @@ -247,20 +243,37 @@ impl VM { /// Process a binary operation. /// This method will not modify the program counter. - fn process_binary_op( + fn process_binary_int_op( + &mut self, + op: BinaryOp, + bit_size: u32, + lhs: RegisterIndex, + rhs: RegisterIndex, + result: RegisterIndex, + ) { + let lhs_value = self.registers.get(lhs); + let rhs_value = self.registers.get(rhs); + + let result_value = op.evaluate_int(lhs_value.to_u128(), rhs_value.to_u128(), bit_size); + + self.registers.set(result, result_value.into()); + } + + /// Process a binary operation. + /// This method will not modify the program counter. + fn process_binary_field_op( &mut self, op: BinaryOp, - lhs: RegisterMemIndex, - rhs: RegisterMemIndex, + lhs: RegisterIndex, + rhs: RegisterIndex, result: RegisterIndex, - result_type: Typ, ) { let lhs_value = self.registers.get(lhs); let rhs_value = self.registers.get(rhs); - let result_value = op.evaluate(lhs_value, rhs_value, result_type); + let result_value = op.evaluate_field(lhs_value.inner, rhs_value.inner); - self.registers.set(result, result_value) + self.registers.set(result, result_value.into()) } /// Returns the state of the registers. @@ -288,7 +301,7 @@ impl VMOutputState { let mut input_values = vec![]; for oracle_input in &oracle_data.inputs { match oracle_input { - OracleInput::RegisterMemIndex(register_index) => { + OracleInput::RegisterIndex(register_index) => { let register = self.registers.get(*register_index); input_values.push(register.inner); } @@ -319,12 +332,12 @@ mod tests { // Add opcode to add the value in register `0` and `1` // and place the output in register `2` - let opcode = Opcode::BinaryOp { + let opcode = Opcode::BinaryIntOp { op: BinaryOp::Add, - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(1)), + bit_size: 2, + lhs: RegisterIndex(0), + rhs: RegisterIndex(1), result: RegisterIndex(2), - result_type: Typ::Field, }; // Start VM @@ -340,7 +353,7 @@ mod tests { // The register at index `2` should have the value of 3 since we had an // add opcode let VMOutputState { registers, .. } = vm.finish(); - let output_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + let output_value = registers.get(RegisterIndex(2)); assert_eq!(output_value, Value::from(3u128)) } @@ -350,18 +363,18 @@ mod tests { let input_registers = Registers::load(vec![Value::from(2u128), Value::from(2u128), Value::from(0u128)]); - let equal_cmp_opcode = Opcode::BinaryOp { - result_type: Typ::Unsigned { bit_size: 1 }, + let equal_cmp_opcode = Opcode::BinaryIntOp { op: BinaryOp::Cmp(Comparison::Eq), - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(1)), + bit_size: 1, + lhs: RegisterIndex(0), + rhs: RegisterIndex(1), result: RegisterIndex(2), }; - let jump_opcode = Opcode::JMP { destination: 2 }; + let jump_opcode = Opcode::Jump { destination: 2 }; - let jump_if_opcode = Opcode::JMPIF { - condition: RegisterMemIndex::Register(RegisterIndex(2)), + let jump_if_opcode = Opcode::JumpIf { + condition: RegisterIndex(2), destination: 3, }; @@ -374,7 +387,7 @@ mod tests { let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + let output_cmp_value = vm.registers.get(RegisterIndex(2)); assert_eq!(output_cmp_value, Value::from(true)); let status = vm.process_opcode(); @@ -393,27 +406,25 @@ mod tests { let trap_opcode = Opcode::Trap; - let not_equal_cmp_opcode = Opcode::BinaryOp { - result_type: Typ::Unsigned { bit_size: 1 }, + let not_equal_cmp_opcode = Opcode::BinaryFieldOp { op: BinaryOp::Cmp(Comparison::Eq), - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(1)), + lhs: RegisterIndex(0), + rhs: RegisterIndex(1), result: RegisterIndex(2), }; - let jump_opcode = Opcode::JMP { destination: 2 }; + let jump_opcode = Opcode::Jump { destination: 2 }; - let jump_if_not_opcode = Opcode::JMPIFNOT { - condition: RegisterMemIndex::Register(RegisterIndex(2)), + let jump_if_not_opcode = Opcode::JumpIfNot { + condition: RegisterIndex(2), destination: 1, }; - let add_opcode = Opcode::BinaryOp { + let add_opcode = Opcode::BinaryFieldOp { op: BinaryOp::Add, - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(1)), + lhs: RegisterIndex(0), + rhs: RegisterIndex(1), result: RegisterIndex(2), - result_type: Typ::Field, }; let mut vm = VM::new( @@ -428,7 +439,7 @@ mod tests { let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + let output_cmp_value = vm.registers.get(RegisterIndex(2)); assert_eq!(output_cmp_value, Value::from(false)); let status = vm.process_opcode(); @@ -439,7 +450,7 @@ mod tests { // The register at index `2` should have not changed as we jumped over the add opcode let VMOutputState { registers, .. } = vm.finish(); - let output_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + let output_value = registers.get(RegisterIndex(2)); assert_eq!(output_value, Value::from(false)); } @@ -449,8 +460,8 @@ mod tests { Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(3u128)]); let mov_opcode = Opcode::Mov { - destination: RegisterMemIndex::Register(RegisterIndex(2)), - source: RegisterMemIndex::Register(RegisterIndex(0)), + destination: RegisterIndex(2), + source: RegisterIndex(0), }; let mut vm = VM::new(input_registers, BTreeMap::new(), vec![mov_opcode]); @@ -460,10 +471,10 @@ mod tests { let VMOutputState { registers, .. } = vm.finish(); - let destination_value = registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + let destination_value = registers.get(RegisterIndex(2)); assert_eq!(destination_value, Value::from(1u128)); - let source_value = registers.get(RegisterMemIndex::Register(RegisterIndex(0))); + let source_value = registers.get(RegisterIndex(0)); assert_eq!(source_value, Value::from(1u128)); } @@ -477,35 +488,35 @@ mod tests { Value::from(6u128), ]); - let equal_opcode = Opcode::BinaryOp { - result_type: Typ::Unsigned { bit_size: 1 }, + let equal_opcode = Opcode::BinaryIntOp { + bit_size: 1, op: BinaryOp::Cmp(Comparison::Eq), - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(1)), + lhs: RegisterIndex(0), + rhs: RegisterIndex(1), result: RegisterIndex(2), }; - let not_equal_opcode = Opcode::BinaryOp { - result_type: Typ::Unsigned { bit_size: 1 }, + let not_equal_opcode = Opcode::BinaryIntOp { + bit_size: 1, op: BinaryOp::Cmp(Comparison::Eq), - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(3)), + lhs: RegisterIndex(0), + rhs: RegisterIndex(3), result: RegisterIndex(2), }; - let less_than_opcode = Opcode::BinaryOp { - result_type: Typ::Unsigned { bit_size: 1 }, + let less_than_opcode = Opcode::BinaryIntOp { + bit_size: 1, op: BinaryOp::Cmp(Comparison::Lt), - lhs: RegisterMemIndex::Register(RegisterIndex(3)), - rhs: RegisterMemIndex::Register(RegisterIndex(4)), + lhs: RegisterIndex(3), + rhs: RegisterIndex(4), result: RegisterIndex(2), }; - let less_than_equal_opcode = Opcode::BinaryOp { - result_type: Typ::Unsigned { bit_size: 1 }, + let less_than_equal_opcode = Opcode::BinaryIntOp { + bit_size: 1, op: BinaryOp::Cmp(Comparison::Lte), - lhs: RegisterMemIndex::Register(RegisterIndex(3)), - rhs: RegisterMemIndex::Register(RegisterIndex(4)), + lhs: RegisterIndex(3), + rhs: RegisterIndex(4), result: RegisterIndex(2), }; @@ -518,25 +529,25 @@ mod tests { let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_eq_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + let output_eq_value = vm.registers.get(RegisterIndex(2)); assert_eq!(output_eq_value, Value::from(true)); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_neq_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + let output_neq_value = vm.registers.get(RegisterIndex(2)); assert_eq!(output_neq_value, Value::from(false)); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let lt_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + let lt_value = vm.registers.get(RegisterIndex(2)); assert_eq!(lt_value, Value::from(true)); let status = vm.process_opcode(); assert_eq!(status, VMStatus::Halted); - let lte_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + let lte_value = vm.registers.get(RegisterIndex(2)); assert_eq!(lte_value, Value::from(true)); vm.finish(); @@ -554,32 +565,32 @@ mod tests { Value::from(0u128), ]); - let equal_cmp_opcode = Opcode::BinaryOp { - result_type: Typ::Unsigned { bit_size: 1 }, + let equal_cmp_opcode = Opcode::BinaryIntOp { + bit_size: 1, op: BinaryOp::Cmp(Comparison::Eq), - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(1)), + lhs: RegisterIndex(0), + rhs: RegisterIndex(1), result: RegisterIndex(2), }; - let jump_opcode = Opcode::JMP { destination: 3 }; + let jump_opcode = Opcode::Jump { destination: 3 }; - let jump_if_opcode = Opcode::JMPIF { - condition: RegisterMemIndex::Register(RegisterIndex(2)), + let jump_if_opcode = Opcode::JumpIf { + condition: RegisterIndex(2), destination: 10, }; let load_opcode = Opcode::Load { - destination: RegisterMemIndex::Register(RegisterIndex(4)), - array_id_reg: RegisterMemIndex::Register(RegisterIndex(3)), - index: RegisterMemIndex::Register(RegisterIndex(2)), + destination: RegisterIndex(4), + array_id_reg: RegisterIndex(3), + index: RegisterIndex(2), }; - let mem_equal_opcode = Opcode::BinaryOp { - result_type: Typ::Unsigned { bit_size: 1 }, + let mem_equal_opcode = Opcode::BinaryIntOp { + bit_size: 1, op: BinaryOp::Cmp(Comparison::Eq), - lhs: RegisterMemIndex::Register(RegisterIndex(4)), - rhs: RegisterMemIndex::Register(RegisterIndex(5)), + lhs: RegisterIndex(4), + rhs: RegisterIndex(5), result: RegisterIndex(6), }; @@ -599,14 +610,14 @@ mod tests { let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + let output_cmp_value = vm.registers.get(RegisterIndex(2)); assert_eq!(output_cmp_value, Value::from(true)); // load_opcode let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(4))); + let output_cmp_value = vm.registers.get(RegisterIndex(4)); assert_eq!(output_cmp_value, Value::from(6u128)); // jump_opcode @@ -617,7 +628,7 @@ mod tests { let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(6))); + let output_cmp_value = vm.registers.get(RegisterIndex(6)); assert_eq!(output_cmp_value, Value::from(true)); // jump_if_opcode @@ -637,27 +648,33 @@ mod tests { Value::from(0u128), Value::from(6u128), Value::from(0u128), + Value::from(0u128), ]); - let equal_cmp_opcode = Opcode::BinaryOp { - result_type: Typ::Unsigned { bit_size: 1 }, + let equal_cmp_opcode = Opcode::BinaryIntOp { + bit_size: 1, op: BinaryOp::Cmp(Comparison::Eq), - lhs: RegisterMemIndex::Register(RegisterIndex(0)), - rhs: RegisterMemIndex::Register(RegisterIndex(1)), + lhs: RegisterIndex(0), + rhs: RegisterIndex(1), result: RegisterIndex(2), }; - let jump_opcode = Opcode::JMP { destination: 3 }; + let jump_opcode = Opcode::Jump { destination: 4 }; - let jump_if_opcode = Opcode::JMPIF { - condition: RegisterMemIndex::Register(RegisterIndex(2)), - destination: 10, + let jump_if_opcode = Opcode::JumpIf { + condition: RegisterIndex(2), + destination: 11, + }; + + let load_const_opcode = Opcode::LoadConst { + destination: RegisterIndex(7), + constant: 3_u128.into() }; let store_opcode = Opcode::Store { - source: RegisterMemIndex::Register(RegisterIndex(2)), - array_id_reg: RegisterMemIndex::Register(RegisterIndex(3)), - index: RegisterMemIndex::Constant(FieldElement::from(3_u128)), + source: RegisterIndex(2), + array_id_reg: RegisterIndex(3), + index: RegisterIndex(7), }; let mut initial_memory = BTreeMap::new(); @@ -669,16 +686,20 @@ mod tests { let mut vm = VM::new( input_registers, initial_memory, - vec![equal_cmp_opcode, store_opcode, jump_opcode, jump_if_opcode], + vec![equal_cmp_opcode, load_const_opcode, store_opcode, jump_opcode, jump_if_opcode], ); // equal_cmp_opcode let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_cmp_value = vm.registers.get(RegisterMemIndex::Register(RegisterIndex(2))); + let output_cmp_value = vm.registers.get(RegisterIndex(2)); assert_eq!(output_cmp_value, Value::from(true)); + // load_const_opcode + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + // store_opcode let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -711,10 +732,9 @@ mod tests { Value::from(0u128), ]); - let oracle_input = - OracleInput::RegisterMemIndex(RegisterMemIndex::Register(RegisterIndex(0))); + let oracle_input = OracleInput::RegisterIndex(RegisterIndex(0)); let oracle_output = - OracleOutput::Array { start: RegisterMemIndex::Register(RegisterIndex(3)), length: 2 }; + OracleOutput::Array { start: RegisterIndex(3), length: 2 }; let mut oracle_data = OracleData { name: "get_notes".to_owned(), @@ -764,7 +784,7 @@ mod tests { let expected_length = 2; let oracle_input = OracleInput::Array { - start: RegisterMemIndex::Register(RegisterIndex(3)), + start: RegisterIndex(3), length: expected_length, }; let oracle_output = OracleOutput::RegisterIndex(RegisterIndex(6)); diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 9cf6417df..ea657ea73 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -28,53 +28,66 @@ pub type Label = usize; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum Opcode { - /// Takes the values in registers `lhs` and `rhs` + /// Takes the fields in registers `lhs` and `rhs` /// Performs the specified binary operation /// and stores the value in the `result` register. - BinaryOp { - result_type: Typ, + BinaryFieldOp { op: BinaryOp, - lhs: RegisterMemIndex, - rhs: RegisterMemIndex, + lhs: RegisterIndex, + rhs: RegisterIndex, result: RegisterIndex, }, - JMPIFNOT { - condition: RegisterMemIndex, + /// Takes the bit_size size integers in registers `lhs` and `rhs` + /// Performs the specified binary operation + /// and stores the value in the `result` register. + BinaryIntOp { + op: BinaryOp, + bit_size: u32, + lhs: RegisterIndex, + rhs: RegisterIndex, + result: RegisterIndex, + }, + JumpIfNot { + condition: RegisterIndex, destination: Label, }, /// Sets the program counter to the value located at `destination` /// If the value at condition is non-zero - JMPIF { - condition: RegisterMemIndex, + JumpIf { + condition: RegisterIndex, destination: Label, }, /// Sets the program counter to the label. - JMP { + Jump { destination: Label, }, PushStack { - source: RegisterMemIndex, + source: RegisterIndex, }, // TODO:This is used to call functions and setup things like // TODO execution contexts. - CallBack, + Call, // TODO:These are special functions like sha256 Intrinsics, /// Used to get data from an outside source Oracle(OracleData), Mov { - destination: RegisterMemIndex, - source: RegisterMemIndex, + destination: RegisterIndex, + source: RegisterIndex, }, Load { - destination: RegisterMemIndex, - array_id_reg: RegisterMemIndex, - index: RegisterMemIndex, + destination: RegisterIndex, + array_id_reg: RegisterIndex, + index: RegisterIndex, + }, + LoadConst { + destination: RegisterIndex, + constant: FieldElement, }, Store { - source: RegisterMemIndex, - array_id_reg: RegisterMemIndex, - index: RegisterMemIndex, + source: RegisterIndex, + array_id_reg: RegisterIndex, + index: RegisterIndex, }, /// Used if execution fails during evaluation Trap, @@ -89,12 +102,13 @@ pub enum Opcode { impl Opcode { pub fn name(&self) -> &'static str { match self { - Opcode::BinaryOp { .. } => "binary_op", - Opcode::JMPIFNOT { .. } => "jmpifnot", - Opcode::JMPIF { .. } => "jmpif", - Opcode::JMP { .. } => "jmp", + Opcode::BinaryFieldOp { .. } => "binary_field_op", + Opcode::BinaryIntOp { .. } => "binary_int_op", + Opcode::JumpIfNot { .. } => "jmpifnot", + Opcode::JumpIf { .. } => "jmpif", + Opcode::Jump { .. } => "jmp", Opcode::PushStack { .. } => "pushstack", - Opcode::CallBack => "callback", + Opcode::Call => "callback", Opcode::Intrinsics => "intrinsics", Opcode::Oracle(_) => "oracle", Opcode::Mov { .. } => "mov", @@ -103,6 +117,7 @@ impl Opcode { Opcode::Trap => "trap", Opcode::Bootstrap { .. } => "bootstrap", Opcode::Stop => "stop", + Opcode::LoadConst { .. } => "loadconst", } } } @@ -115,7 +130,7 @@ pub struct OracleData { pub inputs: Vec, /// Input values pub input_values: Vec, - /// Output register + /// Output registers pub outputs: Vec, /// Output values - they are computed by the (external) oracle once the inputs are known pub output_values: Vec, @@ -123,22 +138,25 @@ pub struct OracleData { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum OracleInput { - RegisterMemIndex(RegisterMemIndex), - Array { start: RegisterMemIndex, length: usize }, + RegisterIndex(RegisterIndex), + Array { start: RegisterIndex, length: usize }, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum OracleOutput { RegisterIndex(RegisterIndex), - Array { start: RegisterMemIndex, length: usize }, + Array { start: RegisterIndex, length: usize }, } + +// Binary fixed-length integer expressions #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum BinaryOp { Add, Sub, Mul, - Div, + SignedDiv, + UnsignedDiv, Cmp(Comparison), And, // (&) Bitwise AND Or, // (|) Bitwise OR @@ -155,78 +173,68 @@ pub enum Comparison { } impl BinaryOp { - pub fn evaluate(&self, a: Value, b: Value, res_type: Typ) -> Value { - let res_inner = match self { - BinaryOp::Add => self.wrapping(a.inner, b.inner, res_type, u128::add, Add::add), - BinaryOp::Sub => { - self.wrapping(a.inner, b.inner, res_type, u128::wrapping_sub, Sub::sub) - } - BinaryOp::Mul => self.wrapping(a.inner, b.inner, res_type, u128::mul, Mul::mul), - BinaryOp::Div => match res_type { - Typ::Field => a.inner / b.inner, - Typ::Unsigned { bit_size } => { - let lhs = a.inner.to_u128() % (1_u128 << bit_size); - let rhs = b.inner.to_u128() % (1_u128 << bit_size); - FieldElement::from(lhs / rhs) - } - Typ::Signed { bit_size } => { - let a = field_to_signed(a.inner, bit_size); - let b = field_to_signed(b.inner, bit_size); - signed_to_field(a / b, bit_size) - } + /// Evaluate a binary operation on two FieldElements and return the result as a FieldElement. + pub fn evaluate_field(&self, a: FieldElement, b: FieldElement) -> FieldElement { + match self { + // Perform addition, subtraction, multiplication, and division based on the BinaryOp variant. + BinaryOp::Add => a + b, + BinaryOp::Sub => a - b, + BinaryOp::Mul => a * b, + BinaryOp::SignedDiv | BinaryOp::UnsignedDiv => a / b, + // Perform a comparison between a and b based on the Comparison variant. + BinaryOp::Cmp(comparison) => match comparison { + Comparison::Eq => ((a == b) as u128).into(), + Comparison::Lt => ((a < b) as u128).into(), + Comparison::Lte => ((a <= b) as u128).into(), }, + // These operations are not allowed for FieldElement, so they are unreachable. + BinaryOp::And => unreachable!("operation not allowed for FieldElement"), + BinaryOp::Or => unreachable!("operation not allowed for FieldElement"), + BinaryOp::Xor => unreachable!("operation not allowed for FieldElement"), + BinaryOp::Shl => unreachable!("operation not allowed for FieldElement"), + BinaryOp::Shr => unreachable!("operation not allowed for FieldElement"), + } + } + /// Evaluate a binary operation on two unsigned integers (u128) with a given bit size and return the result as a u128. + pub fn evaluate_int(&self, a: u128, b: u128, bit_size: u32) -> u128 { + let bit_modulo = 1_u128 << bit_size; + match self { + // Perform addition, subtraction, and multiplication, applying a modulo operation to keep the result within the bit size. + BinaryOp::Add => (a + b) % bit_modulo, + BinaryOp::Sub => (a - b) % bit_modulo, + BinaryOp::Mul => (a * b) % bit_modulo, + // Perform unsigned division using the modulo operation on a and b. + BinaryOp::UnsignedDiv => (a % bit_modulo) / (b % bit_modulo), + // Perform signed division by first converting a and b to signed integers and then back to unsigned after the operation. + BinaryOp::SignedDiv => to_unsigned(to_signed(a, bit_size) / to_signed(b, bit_size), bit_size), + // Perform a comparison between a and b based on the Comparison variant. BinaryOp::Cmp(comparison) => match comparison { - Comparison::Eq => ((a.inner == b.inner) as u128).into(), - Comparison::Lt => ((a.inner < b.inner) as u128).into(), - Comparison::Lte => ((a.inner <= b.inner) as u128).into(), + Comparison::Eq => ((a == b) as u128).into(), + Comparison::Lt => ((a < b) as u128).into(), + Comparison::Lte => ((a <= b) as u128).into(), }, + // Perform bitwise AND, OR, XOR, left shift, and right shift operations, applying a modulo operation to keep the result within the bit size. BinaryOp::And => { - self.wrapping(a.inner, b.inner, res_type, u128::bitand, field_op_not_allowed) + (a & b) % bit_modulo } BinaryOp::Or => { - self.wrapping(a.inner, b.inner, res_type, u128::bitor, field_op_not_allowed) + (a | b) % bit_modulo } BinaryOp::Xor => { - self.wrapping(a.inner, b.inner, res_type, u128::bitxor, field_op_not_allowed) + (a ^ b) % bit_modulo } BinaryOp::Shl => { - self.wrapping(a.inner, b.inner, res_type, u128::shl, field_op_not_allowed) + (a << b) % bit_modulo } BinaryOp::Shr => { - self.wrapping(a.inner, b.inner, res_type, u128::shr, field_op_not_allowed) - } - }; - - Value { typ: res_type, inner: res_inner } - } - - /// Perform the given numeric operation and modulo the result by the max value for the given bit count - /// if the res_type is not a FieldElement. - fn wrapping( - &self, - lhs: FieldElement, - rhs: FieldElement, - res_type: Typ, - u128_op: impl FnOnce(u128, u128) -> u128, - field_op: impl FnOnce(FieldElement, FieldElement) -> FieldElement, - ) -> FieldElement { - match res_type { - Typ::Field => field_op(lhs, rhs), - Typ::Unsigned { bit_size } | Typ::Signed { bit_size } => { - let type_modulo = 1_u128 << bit_size; - let lhs = lhs.to_u128() % type_modulo; - let rhs = rhs.to_u128() % type_modulo; - let mut x = u128_op(lhs, rhs); - x %= type_modulo; - FieldElement::from(x) + (a >> b) % bit_modulo } } } } -fn field_to_signed(f: FieldElement, n: u32) -> i128 { +fn to_signed(a: u128, n: u32) -> i128 { assert!(n < 127); - let a = f.to_u128(); let pow_2 = 2_u128.pow(n); if a < pow_2 { a as i128 @@ -235,18 +243,13 @@ fn field_to_signed(f: FieldElement, n: u32) -> i128 { } } -fn signed_to_field(a: i128, n: u32) -> FieldElement { +fn to_unsigned(a: i128, n: u32) -> u128 { if n >= 126 { panic!("ICE: cannot convert signed {n} bit size into field"); } if a >= 0 { - FieldElement::from(a) + a as u128 } else { - let b = (a + 2_i128.pow(n + 1)) as u128; - FieldElement::from(b) + (a + 2_i128.pow(n + 1)) as u128 } -} - -fn field_op_not_allowed(_lhs: FieldElement, _rhs: FieldElement) -> FieldElement { - unreachable!("operation not allowed for FieldElement"); -} +} \ No newline at end of file diff --git a/brillig_bytecode/src/registers.rs b/brillig_bytecode/src/registers.rs index a885ded12..250af5fc4 100644 --- a/brillig_bytecode/src/registers.rs +++ b/brillig_bytecode/src/registers.rs @@ -50,19 +50,15 @@ impl Registers { Self { inner: values } } /// Gets the values at register with address `index` - pub fn get(&self, index: RegisterMemIndex) -> Value { - match index { - RegisterMemIndex::Register(register) => self.inner[register.inner()], - RegisterMemIndex::Constant(constant) => Value { typ: Typ::Field, inner: constant }, - RegisterMemIndex::Memory(_) => todo!("we will implement memory later"), - } + pub fn get(&self, register: RegisterIndex) -> Value { + self.inner[register.inner()] } /// Sets the value at register with address `index` to `value` pub fn set(&mut self, index: RegisterIndex, value: Value) { if index.inner() >= self.inner.len() { let diff = index.inner() - self.inner.len() + 1; self.inner - .extend(vec![Value { typ: Typ::Field, inner: FieldElement::from(0u128) }; diff]) + .extend(vec![Value {inner: FieldElement::from(0u128) }; diff]) } self.inner[index.inner()] = value } diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs index a59e8b02b..1e8f6b140 100644 --- a/brillig_bytecode/src/value.rs +++ b/brillig_bytecode/src/value.rs @@ -13,7 +13,6 @@ pub enum Typ { /// Value represents a Value in the VM #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Value { - pub typ: Typ, pub inner: FieldElement, } @@ -22,43 +21,35 @@ impl Value { pub fn is_zero(&self) -> bool { self.inner.is_zero() } - /// Performs the multiplicative inverse of `Value` - pub fn inverse(&self) -> Value { - let value = match self.typ { - Typ::Field => self.inner.inverse(), - Typ::Unsigned { bit_size: _ } => { - todo!("TODO") - } - Typ::Signed { bit_size: _ } => todo!("TODO"), - }; - Value { typ: self.typ, inner: value } + pub fn to_u128(&self) -> u128 { + self.inner.to_u128() } } impl From for Value { fn from(value: u128) -> Self { - Value { typ: Typ::Field, inner: FieldElement::from(value) } + Value { inner: FieldElement::from(value) } } } impl From for Value { fn from(value: FieldElement) -> Self { - Value { typ: Typ::Field, inner: value } + Value { inner: value } } } impl From for Value { fn from(value: u32) -> Self { - Value { typ: Typ::Field, inner: FieldElement::from(value as i128) } + Value { inner: FieldElement::from(value as i128) } } } impl From for Value { fn from(value: bool) -> Self { if value { - Value { typ: Typ::Unsigned { bit_size: 1 }, inner: FieldElement::one() } + Value { inner: FieldElement::one() } } else { - Value { typ: Typ::Unsigned { bit_size: 1 }, inner: FieldElement::zero() } + Value { inner: FieldElement::zero() } } } } @@ -67,34 +58,34 @@ impl Add for Value { type Output = Value; fn add(self, rhs: Self) -> Self::Output { - Value { typ: self.typ, inner: self.inner + rhs.inner } + Value { inner: self.inner + rhs.inner } } } impl Sub for Value { type Output = Value; fn sub(self, rhs: Self) -> Self::Output { - Value { typ: self.typ, inner: self.inner - rhs.inner } + Value { inner: self.inner - rhs.inner } } } impl Mul for Value { type Output = Value; fn mul(self, rhs: Self) -> Self::Output { - Value { typ: self.typ, inner: self.inner * rhs.inner } + Value { inner: self.inner * rhs.inner } } } impl Div for Value { type Output = Value; fn div(self, rhs: Self) -> Self::Output { - Value { typ: self.typ, inner: self.inner / rhs.inner } + Value { inner: self.inner / rhs.inner } } } impl Neg for Value { type Output = Value; fn neg(self) -> Self::Output { - Value { typ: self.typ, inner: -self.inner } + Value { inner: -self.inner } } } From 65c077573ce7cd9e4fc64d7886faead26249c964 Mon Sep 17 00:00:00 2001 From: ludamad Date: Mon, 8 May 2023 16:40:55 -0400 Subject: [PATCH 053/125] Low level pointer machine --- acvm/src/lib.rs | 10 +- acvm/src/pwg/brillig.rs | 19 +- brillig_bytecode/src/builder.rs | 5 - brillig_bytecode/src/lib.rs | 771 +++++++++++++++--------------- brillig_bytecode/src/opcodes.rs | 134 +++--- brillig_bytecode/src/registers.rs | 2 +- brillig_bytecode/src/value.rs | 15 +- 7 files changed, 472 insertions(+), 484 deletions(-) delete mode 100644 brillig_bytecode/src/builder.rs diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 8d8b6da8c..34b60345d 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -358,7 +358,7 @@ mod test { use acir::{ brillig_bytecode, brillig_bytecode::{ - Comparison, OracleInput, OracleOutput, RegisterIndex, RegisterMemIndex, Typ, BinaryOp, + Comparison, OracleInput, OracleOutput, RegisterIndex, BinaryFieldOp, }, circuit::{ directives::Directive, @@ -474,14 +474,14 @@ mod test { let w_lt_res = Witness(8); let equal_opcode = brillig_bytecode::Opcode::BinaryFieldOp { - op: brillig_bytecode::BinaryOp::Cmp(Comparison::Eq), + op: brillig_bytecode::BinaryFieldOp::Cmp(Comparison::Eq), lhs: RegisterIndex(0), rhs: RegisterIndex(1), result: RegisterIndex(2), }; let less_than_opcode = brillig_bytecode::Opcode::BinaryFieldOp { - op: BinaryOp::Cmp(Comparison::Lt), + op: brillig_bytecode::BinaryFieldOp::Cmp(Comparison::Lt), lhs: RegisterIndex(0), rhs: RegisterIndex(1), result: RegisterIndex(3), @@ -597,14 +597,14 @@ mod test { let w_lt_res = Witness(8); let equal_opcode = brillig_bytecode::Opcode::BinaryFieldOp { - op: BinaryOp::Cmp(Comparison::Eq), + op: BinaryFieldOp::Cmp(Comparison::Eq), lhs: RegisterIndex(0), rhs: RegisterIndex(1), result: RegisterIndex(2), }; let less_than_opcode = brillig_bytecode::Opcode::BinaryFieldOp { - op: BinaryOp::Cmp(Comparison::Lt), + op: BinaryFieldOp::Cmp(Comparison::Lt), lhs: RegisterIndex(0), rhs: RegisterIndex(1), result: RegisterIndex(3), diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index 254933cf6..871689aff 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use acir::{ - brillig_bytecode::{ArrayHeap, Opcode, OracleData, Registers, Typ, VMStatus, Value, VM}, + brillig_bytecode::{Opcode, OracleData, Registers, VMStatus, Value, VM}, circuit::opcodes::{Brillig, BrilligInputs, BrilligOutputs}, native_types::Witness, FieldElement, @@ -34,7 +34,7 @@ impl BrilligSolver { Err(err) => return Err(err), }; - // A zero predicate indicates the oracle should be skipped, and its ouputs zeroed. + // A zero predicate indicates the oracle should be skipped, and its outputs zeroed. if pred_value.is_zero() { for output in &brillig.outputs { match output { @@ -53,7 +53,7 @@ impl BrilligSolver { // Set input values let mut input_register_values: Vec = Vec::new(); - let mut input_memory: BTreeMap = BTreeMap::new(); + let mut input_memory: Vec = Vec::new(); for input in &brillig.inputs { match input { BrilligInputs::Simple(expr) => { @@ -68,11 +68,10 @@ impl BrilligSolver { BrilligInputs::Array(id, expr_arr) => { // Attempt to fetch all array input values let mut continue_eval = true; - let mut array_heap: BTreeMap = BTreeMap::new(); - for (i, expr) in expr_arr.into_iter().enumerate() { + for expr in expr_arr.into_iter() { let solve = ArithmeticSolver::evaluate(expr, initial_witness); if let Some(value) = solve.to_const() { - array_heap.insert(i, value.into()); + input_memory.push(value.into()); } else { continue_eval = false; break; @@ -88,8 +87,6 @@ impl BrilligSolver { Value { inner: FieldElement::from(*id as u128) }; // Push value of the array id as a register input_register_values.push(id_as_value.into()); - - input_memory.insert(id_as_value, ArrayHeap { memory_map: array_heap }); } } } @@ -109,7 +106,7 @@ impl BrilligSolver { } let input_registers = Registers { inner: input_register_values }; - let vm = VM::new(input_registers, input_memory, brillig.bytecode.clone()); + let vm = VM::new(input_registers, input_memory, brillig.bytecode.clone(), None); let vm_output = vm.process_opcodes(); @@ -121,8 +118,8 @@ impl BrilligSolver { insert_witness(*witness, register_value.inner, initial_witness)?; } BrilligOutputs::Array(witness_arr) => { - let array = vm_output.memory[®ister_value].memory_map.values(); - for (witness, value) in witness_arr.iter().zip(array) { + for (i, witness) in witness_arr.iter().enumerate() { + let value = &vm_output.memory[register_value.to_usize() + i]; insert_witness(*witness, value.inner, initial_witness)?; } } diff --git a/brillig_bytecode/src/builder.rs b/brillig_bytecode/src/builder.rs deleted file mode 100644 index 56c16bf70..000000000 --- a/brillig_bytecode/src/builder.rs +++ /dev/null @@ -1,5 +0,0 @@ -use crate::Opcode; - -pub struct Builder { - opcodes: Vec, -} diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 959525f42..4b8651e78 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -4,18 +4,14 @@ // This is a generalization over the fixed directives // that we have in ACVM. -mod builder; mod memory; mod opcodes; mod registers; mod value; -use std::collections::BTreeMap; use acir_field::FieldElement; -use num_bigint::{BigInt, Sign}; -pub use opcodes::BinaryOp; -pub use opcodes::RegisterMemIndex; +pub use opcodes::{BinaryFieldOp, BinaryIntOp}; pub use opcodes::{Comparison, Opcode, OracleData, OracleInput, OracleOutput}; pub use registers::{RegisterIndex, Registers}; pub use value::Typ; @@ -30,9 +26,8 @@ pub enum VMStatus { } #[derive(Default, Debug, PartialEq, Eq, Clone)] -pub struct ArrayHeap { - // maps memory address to Value - pub memory_map: BTreeMap, +pub struct HeapArray { + pub array: Vec, } #[derive(Debug, PartialEq, Eq, Clone)] @@ -41,30 +36,29 @@ pub struct VM { program_counter: usize, bytecode: Vec, status: VMStatus, - memory: BTreeMap, + memory: Vec, call_stack: Vec, } impl VM { pub fn new( mut inputs: Registers, - memory: BTreeMap, - mut bytecode: Vec, + memory: Vec, + bytecode: Vec, + register_allocation_indices: Option> ) -> VM { - let last_opcode = bytecode.last().expect("need at least one opcode"); - - if let Opcode::Bootstrap { register_allocation_indices } = last_opcode { + if let Some(register_allocation_indices) = register_allocation_indices { // TODO: might have to handle arrays in bootstrap to be correct + // TODO(AD): simplify this all to be done before calling VM.new() let mut registers_modified = - Registers::load(vec![Value { inner: FieldElement::from(0u128) }]); - + Registers::load(vec![Value::from(0u128)]); + for i in 0..register_allocation_indices.len() { let register_index = register_allocation_indices[i]; let register_value = inputs.get(RegisterIndex(i)); registers_modified.set(RegisterIndex(register_index as usize), register_value) } - - bytecode.pop(); + inputs = registers_modified; } let vm = Self { @@ -112,24 +106,24 @@ impl VM { self.process_binary_int_op(*op, *bit_size, *lhs, *rhs, *result); self.increment_program_counter() } - Opcode::Jump { destination } => self.set_program_counter(*destination), - Opcode::JumpIf { condition, destination } => { + Opcode::Jump { location: destination } => self.set_program_counter(*destination), + Opcode::JumpIf { condition, location: destination } => { // Check if condition is true // We use 0 to mean false and any other value to mean true - let condition_value = self.registers.get(*condition); + let condition_value = self.get(condition); if !condition_value.is_zero() { return self.set_program_counter(*destination); } self.increment_program_counter() } - Opcode::JumpIfNot { condition, destination } => { - let condition_value = self.registers.get(*condition); + Opcode::JumpIfNot { condition, location: destination } => { + let condition_value = self.get(condition); if condition_value.is_zero() { return self.set_program_counter(*destination); } self.increment_program_counter() } - Opcode::Call => { + Opcode::Return => { if let Some(register) = self.call_stack.pop() { let label = usize::try_from( register.inner.try_to_u64().expect("register does not fit into u64"), @@ -146,7 +140,6 @@ impl VM { for oracle_output in data.clone().outputs { match oracle_output { OracleOutput::RegisterIndex(_) => num_output_values += 1, - OracleOutput::Array { length, .. } => num_output_values += length, } } if num_output_values != data.output_values.len() { @@ -160,66 +153,56 @@ impl VM { .set(index, data.output_values[current_value_index].into()); current_value_index += 1 } - OracleOutput::Array { start, length } => { - let array_id = self.registers.get(start); - let heap = &mut self.memory.entry(array_id).or_default().memory_map; - for (i, value) in data.output_values.iter().enumerate() { - heap.insert(i, (*value).into()); - } - current_value_index += length - } } } } self.increment_program_counter() } - Opcode::Mov { destination, source } => { - let source_value = self.registers.get(*source); - self.registers.set(*destination, source_value); + Opcode::Mov { destination_register, source_register } => { + let source_value = self.get(source_register); + self.registers.set(*destination_register, source_value); self.increment_program_counter() } Opcode::Trap => self.fail(), - Opcode::Bootstrap { .. } => unreachable!( - "should only be at end of opcodes and popped off when initializing the vm" - ), Opcode::Stop => self.halt(), - Opcode::Load { destination, array_id_reg, index } => { - let array_id = self.registers.get(*array_id_reg); - let array = &self.memory[&array_id]; - let index_value = self.registers.get(*index); - let index_usize = usize::try_from( - index_value.inner.try_to_u64().expect("register does not fit into u64"), + Opcode::Load { destination_register, source_pointer } => { + // Convert our source_pointer to a usize + let source = self.get(source_pointer); + let source_usize = usize::try_from( + source.inner.try_to_u64().expect("register does not fit into u64"), ).expect("register does not fit into usize"); - self.registers.set(*destination, array.memory_map[&index_usize]); + // Use our usize source index to lookup the value in memory + let value = &self.memory[source_usize]; + self.registers.set(*destination_register, *value); self.increment_program_counter() } - Opcode::Store { source, array_id_reg, index } => { - let source_value = self.registers.get(*source); - let array_id = self.registers.get(*array_id_reg); - let heap = &mut self.memory.entry(array_id).or_default().memory_map; - - let index_value = self.registers.get(*index); - let index_usize = usize::try_from( - index_value.inner.try_to_u64().expect("register does not fit into u64"), - ) - .expect("register does not fit into usize"); - heap.insert(index_usize, source_value); - + Opcode::Store { destination_pointer, source_register } => { + // Convert our destination_pointer to a usize + let destination = self.get(destination_pointer); + let destination_usize = usize::try_from( + destination.inner.try_to_u64().expect("register does not fit into u64"), + ).expect("register does not fit into usize"); + // Use our usize destination index to set the value in memory + self.memory[destination_usize] = self.get(source_register); self.increment_program_counter() } - Opcode::PushStack { source } => { - let register = self.registers.get(*source); - self.call_stack.push(register); - self.increment_program_counter() + Opcode::Call { location } => { + // Push a return location + self.call_stack.push(Value::from(self.program_counter + 1)); + self.set_program_counter(*location) } - Opcode::LoadConst { destination, constant } => { - self.registers.set(*destination, Value { inner: *constant }); + Opcode::Const { destination, value } => { + self.registers.set(*destination, Value::from(*value)); self.increment_program_counter() } } } + /// Get the value of a register + fn get(&self, register: &RegisterIndex) -> Value { + self.registers.get(*register) + } pub fn program_counter(self) -> usize { self.program_counter } @@ -245,7 +228,7 @@ impl VM { /// This method will not modify the program counter. fn process_binary_int_op( &mut self, - op: BinaryOp, + op: BinaryIntOp, bit_size: u32, lhs: RegisterIndex, rhs: RegisterIndex, @@ -263,7 +246,7 @@ impl VM { /// This method will not modify the program counter. fn process_binary_field_op( &mut self, - op: BinaryOp, + op: BinaryFieldOp, lhs: RegisterIndex, rhs: RegisterIndex, result: RegisterIndex, @@ -293,7 +276,7 @@ pub struct VMOutputState { pub registers: Registers, pub program_counter: usize, pub status: VMStatus, - pub memory: BTreeMap, + pub memory: Vec, } impl VMOutputState { @@ -305,14 +288,6 @@ impl VMOutputState { let register = self.registers.get(*register_index); input_values.push(register.inner); } - OracleInput::Array { start, length } => { - let array_id = self.registers.get(*start); - let array = &self.memory[&array_id]; - let heap_fields = array.memory_map.values().map(|value| value.inner.clone()); - - assert_eq!(heap_fields.len(), *length); - input_values.extend(heap_fields); - } } } input_values @@ -322,6 +297,29 @@ impl VMOutputState { #[cfg(test)] mod tests { use super::*; + /// Test helper + struct TestCompiler { + registers: Vec, + opcodes: Vec + } + + impl TestCompiler { + fn make(&mut self, opcode: Opcode) { + self.opcodes.push(opcode); + } + fn register(&mut self, value: Value) -> RegisterIndex { + self.registers.push(value); + RegisterIndex(self.registers.len() - 1) + } + // fn make_const(&mut self, value: Value) -> usize { + // self.registers.push(Value::from(0u128)); + // self.opcodes.push(Opcode::Const { + // destination: RegisterIndex(self.registers.len() - 1), + // value: value.inner, + // }); + // self.registers.len() - 1 + // } + } #[test] fn add_single_step_smoke() { @@ -333,7 +331,7 @@ mod tests { // Add opcode to add the value in register `0` and `1` // and place the output in register `2` let opcode = Opcode::BinaryIntOp { - op: BinaryOp::Add, + op: BinaryIntOp::Add, bit_size: 2, lhs: RegisterIndex(0), rhs: RegisterIndex(1), @@ -341,7 +339,7 @@ mod tests { }; // Start VM - let mut vm = VM::new(input_registers, BTreeMap::new(), vec![opcode]); + let mut vm = VM::new(input_registers, vec![], vec![opcode], None); // Process a single VM opcode // @@ -360,28 +358,29 @@ mod tests { #[test] fn jmpif_opcode() { - let input_registers = - Registers::load(vec![Value::from(2u128), Value::from(2u128), Value::from(0u128)]); - + let mut compiler = TestCompiler { + registers: vec![], + opcodes: vec![], + }; let equal_cmp_opcode = Opcode::BinaryIntOp { - op: BinaryOp::Cmp(Comparison::Eq), + op: BinaryIntOp::Cmp(Comparison::Eq), bit_size: 1, - lhs: RegisterIndex(0), - rhs: RegisterIndex(1), - result: RegisterIndex(2), + lhs: compiler.register(Value::from(2u128)), + rhs: compiler.register(Value::from(2u128)), + result: compiler.register(Value::from(0u128)), }; - - let jump_opcode = Opcode::Jump { destination: 2 }; - - let jump_if_opcode = Opcode::JumpIf { + compiler.make(equal_cmp_opcode); + compiler.make(Opcode::Jump { location: 2 }); + compiler.make(Opcode::JumpIf { condition: RegisterIndex(2), - destination: 3, - }; + location: 3, + }); let mut vm = VM::new( - input_registers, - BTreeMap::new(), - vec![equal_cmp_opcode, jump_opcode, jump_if_opcode], + Registers {inner: compiler.registers}, + vec![], + compiler.opcodes, + None ); let status = vm.process_opcode(); @@ -407,21 +406,21 @@ mod tests { let trap_opcode = Opcode::Trap; let not_equal_cmp_opcode = Opcode::BinaryFieldOp { - op: BinaryOp::Cmp(Comparison::Eq), + op: BinaryFieldOp::Cmp(Comparison::Eq), lhs: RegisterIndex(0), rhs: RegisterIndex(1), result: RegisterIndex(2), }; - let jump_opcode = Opcode::Jump { destination: 2 }; + let jump_opcode = Opcode::Jump { location: 2 }; let jump_if_not_opcode = Opcode::JumpIfNot { condition: RegisterIndex(2), - destination: 1, + location: 1, }; let add_opcode = Opcode::BinaryFieldOp { - op: BinaryOp::Add, + op: BinaryFieldOp::Add, lhs: RegisterIndex(0), rhs: RegisterIndex(1), result: RegisterIndex(2), @@ -429,8 +428,9 @@ mod tests { let mut vm = VM::new( input_registers, - BTreeMap::new(), + vec![], vec![jump_opcode, trap_opcode, not_equal_cmp_opcode, jump_if_not_opcode, add_opcode], + None ); let status = vm.process_opcode(); @@ -460,11 +460,11 @@ mod tests { Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(3u128)]); let mov_opcode = Opcode::Mov { - destination: RegisterIndex(2), - source: RegisterIndex(0), + destination_register: RegisterIndex(2), + source_register: RegisterIndex(0), }; - let mut vm = VM::new(input_registers, BTreeMap::new(), vec![mov_opcode]); + let mut vm = VM::new(input_registers, vec![], vec![mov_opcode], None); let status = vm.process_opcode(); assert_eq!(status, VMStatus::Halted); @@ -490,7 +490,7 @@ mod tests { let equal_opcode = Opcode::BinaryIntOp { bit_size: 1, - op: BinaryOp::Cmp(Comparison::Eq), + op: BinaryIntOp::Cmp(Comparison::Eq), lhs: RegisterIndex(0), rhs: RegisterIndex(1), result: RegisterIndex(2), @@ -498,7 +498,7 @@ mod tests { let not_equal_opcode = Opcode::BinaryIntOp { bit_size: 1, - op: BinaryOp::Cmp(Comparison::Eq), + op: BinaryIntOp::Cmp(Comparison::Eq), lhs: RegisterIndex(0), rhs: RegisterIndex(3), result: RegisterIndex(2), @@ -506,7 +506,7 @@ mod tests { let less_than_opcode = Opcode::BinaryIntOp { bit_size: 1, - op: BinaryOp::Cmp(Comparison::Lt), + op: BinaryIntOp::Cmp(Comparison::Lt), lhs: RegisterIndex(3), rhs: RegisterIndex(4), result: RegisterIndex(2), @@ -514,7 +514,7 @@ mod tests { let less_than_equal_opcode = Opcode::BinaryIntOp { bit_size: 1, - op: BinaryOp::Cmp(Comparison::Lte), + op: BinaryIntOp::Cmp(Comparison::Lte), lhs: RegisterIndex(3), rhs: RegisterIndex(4), result: RegisterIndex(2), @@ -522,8 +522,9 @@ mod tests { let mut vm = VM::new( input_registers, - BTreeMap::new(), + vec![], vec![equal_opcode, not_equal_opcode, less_than_opcode, less_than_equal_opcode], + None, ); let status = vm.process_opcode(); @@ -553,276 +554,290 @@ mod tests { vm.finish(); } - #[test] - fn load_opcode() { - let input_registers = Registers::load(vec![ - Value::from(2u128), - Value::from(2u128), - Value::from(0u128), - Value::from(5u128), - Value::from(0u128), - Value::from(6u128), - Value::from(0u128), - ]); - - let equal_cmp_opcode = Opcode::BinaryIntOp { - bit_size: 1, - op: BinaryOp::Cmp(Comparison::Eq), - lhs: RegisterIndex(0), - rhs: RegisterIndex(1), - result: RegisterIndex(2), - }; - - let jump_opcode = Opcode::Jump { destination: 3 }; - - let jump_if_opcode = Opcode::JumpIf { - condition: RegisterIndex(2), - destination: 10, - }; - - let load_opcode = Opcode::Load { - destination: RegisterIndex(4), - array_id_reg: RegisterIndex(3), - index: RegisterIndex(2), - }; - - let mem_equal_opcode = Opcode::BinaryIntOp { - bit_size: 1, - op: BinaryOp::Cmp(Comparison::Eq), - lhs: RegisterIndex(4), - rhs: RegisterIndex(5), - result: RegisterIndex(6), - }; - - let mut initial_memory = BTreeMap::new(); - let initial_heap = ArrayHeap { - memory_map: BTreeMap::from([(0 as usize, Value::from(5u128)), (1, Value::from(6u128))]), - }; - initial_memory.insert(Value::from(5u128), initial_heap); - - let mut vm = VM::new( - input_registers, - initial_memory, - vec![equal_cmp_opcode, load_opcode, jump_opcode, mem_equal_opcode, jump_if_opcode], - ); - - // equal_cmp_opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - let output_cmp_value = vm.registers.get(RegisterIndex(2)); - assert_eq!(output_cmp_value, Value::from(true)); - - // load_opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - let output_cmp_value = vm.registers.get(RegisterIndex(4)); - assert_eq!(output_cmp_value, Value::from(6u128)); - - // jump_opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - // mem_equal_opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - let output_cmp_value = vm.registers.get(RegisterIndex(6)); - assert_eq!(output_cmp_value, Value::from(true)); - - // jump_if_opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Halted); - - vm.finish(); - } - - #[test] - fn store_opcode() { - let input_registers = Registers::load(vec![ - Value::from(2u128), - Value::from(2u128), - Value::from(0u128), - Value::from(5u128), - Value::from(0u128), - Value::from(6u128), - Value::from(0u128), - Value::from(0u128), - ]); - - let equal_cmp_opcode = Opcode::BinaryIntOp { - bit_size: 1, - op: BinaryOp::Cmp(Comparison::Eq), - lhs: RegisterIndex(0), - rhs: RegisterIndex(1), - result: RegisterIndex(2), - }; - - let jump_opcode = Opcode::Jump { destination: 4 }; - - let jump_if_opcode = Opcode::JumpIf { - condition: RegisterIndex(2), - destination: 11, - }; - - let load_const_opcode = Opcode::LoadConst { - destination: RegisterIndex(7), - constant: 3_u128.into() - }; - - let store_opcode = Opcode::Store { - source: RegisterIndex(2), - array_id_reg: RegisterIndex(3), - index: RegisterIndex(7), - }; - - let mut initial_memory = BTreeMap::new(); - let initial_heap = ArrayHeap { - memory_map: BTreeMap::from([(0 as usize, Value::from(5u128)), (1, Value::from(6u128))]), - }; - initial_memory.insert(Value::from(5u128), initial_heap); - - let mut vm = VM::new( - input_registers, - initial_memory, - vec![equal_cmp_opcode, load_const_opcode, store_opcode, jump_opcode, jump_if_opcode], - ); - - // equal_cmp_opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - let output_cmp_value = vm.registers.get(RegisterIndex(2)); - assert_eq!(output_cmp_value, Value::from(true)); - - // load_const_opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - // store_opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - let mem_array = vm.memory[&Value::from(5u128)].clone(); - assert_eq!(mem_array.memory_map[&3], Value::from(true)); - - // jump_opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::InProgress); - - // jump_if_opcode - let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Halted); - - vm.finish(); - } - - #[test] - fn oracle_array_output() { - use crate::opcodes::OracleInput; - - let input_registers = Registers::load(vec![ - Value::from(2u128), - Value::from(2u128), - Value::from(0u128), - Value::from(5u128), - Value::from(0u128), - Value::from(6u128), - Value::from(0u128), - ]); - - let oracle_input = OracleInput::RegisterIndex(RegisterIndex(0)); - let oracle_output = - OracleOutput::Array { start: RegisterIndex(3), length: 2 }; - - let mut oracle_data = OracleData { - name: "get_notes".to_owned(), - inputs: vec![oracle_input], - input_values: vec![], - outputs: vec![oracle_output], - output_values: vec![], - }; - - let oracle_opcode = Opcode::Oracle(oracle_data.clone()); - - let initial_memory = BTreeMap::new(); - - let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode]); - - let output_state = vm.process_opcodes(); - assert_eq!(output_state.status, VMStatus::OracleWait); - - let input_values = output_state.map_input_values(&oracle_data); - - oracle_data.input_values = input_values; - oracle_data.output_values = vec![FieldElement::from(10_u128), FieldElement::from(2_u128)]; - let updated_oracle_opcode = Opcode::Oracle(oracle_data); - - let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode]); - let output_state = vm.process_opcodes(); - assert_eq!(output_state.status, VMStatus::Halted); - - let mem_array = output_state.memory[&Value::from(5u128)].clone(); - assert_eq!(mem_array.memory_map[&0], Value::from(10_u128)); - assert_eq!(mem_array.memory_map[&1], Value::from(2_u128)); - } - - #[test] - fn oracle_array_input() { - use crate::opcodes::OracleInput; - - let input_registers = Registers::load(vec![ - Value::from(2u128), - Value::from(2u128), - Value::from(0u128), - Value::from(5u128), - Value::from(0u128), - Value::from(6u128), - Value::from(0u128), - ]); - - let expected_length = 2; - let oracle_input = OracleInput::Array { - start: RegisterIndex(3), - length: expected_length, - }; - let oracle_output = OracleOutput::RegisterIndex(RegisterIndex(6)); - - let mut oracle_data = OracleData { - name: "call_private_function_oracle".to_owned(), - inputs: vec![oracle_input.clone()], - input_values: vec![], - outputs: vec![oracle_output], - output_values: vec![], - }; - - let oracle_opcode = Opcode::Oracle(oracle_data.clone()); - - let mut initial_memory = BTreeMap::new(); - let initial_heap = ArrayHeap { - memory_map: BTreeMap::from([(0 as usize, Value::from(5u128)), (1, Value::from(6u128))]), - }; - initial_memory.insert(Value::from(5u128), initial_heap); - - let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode]); - - let output_state = vm.process_opcodes(); - assert_eq!(output_state.status, VMStatus::OracleWait); - - let input_values = output_state.map_input_values(&oracle_data); - assert_eq!(input_values.len(), expected_length); - - oracle_data.input_values = input_values; - oracle_data.output_values = vec![FieldElement::from(5_u128)]; - let updated_oracle_opcode = Opcode::Oracle(oracle_data); - - let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode]); - let output_state = vm.process_opcodes(); - assert_eq!(output_state.status, VMStatus::Halted); - - let mem_array = output_state.memory[&Value::from(5u128)].clone(); - assert_eq!(mem_array.memory_map[&0], Value::from(5_u128)); - assert_eq!(mem_array.memory_map[&1], Value::from(6_u128)); - } + // #[test] + // fn load_opcode() { + // let input_registers = Registers::load(vec![ + // Value::from(2u128), + // Value::from(2u128), + // Value::from(0u128), + // Value::from(5u128), + // Value::from(0u128), + // Value::from(6u128), + // Value::from(0u128), + // ]); + + // let equal_cmp_opcode = Opcode::BinaryIntOp { + // bit_size: 1, + // op: BinaryIntOp::Cmp(Comparison::Eq), + // lhs: RegisterIndex(0), + // rhs: RegisterIndex(1), + // result: RegisterIndex(2), + // }; + + // let jump_opcode = Opcode::Jump { location: 3 }; + + // let jump_if_opcode = Opcode::JumpIf { + // condition: RegisterIndex(2), + // location: 10, + // }; + + // let load_opcode = Opcode::Load { + // destination_register: RegisterIndex(4), + // source_array: RegisterIndex(3), + // source_index: RegisterIndex(2), + // }; + + // let mem_equal_opcode = Opcode::BinaryIntOp { + // bit_size: 1, + // op: BinaryIntOp::Cmp(Comparison::Eq), + // lhs: RegisterIndex(4), + // rhs: RegisterIndex(5), + // result: RegisterIndex(6), + // }; + + // let mut initial_memory = vec![]; + // let initial_heap = HeapArray { + // array: vec![Value::from(5), Value::from(6)] + // }; + // initial_memory.insert(Value::from(5), initial_heap); + + // let mut vm = VM::new( + // input_registers, + // initial_memory, + // vec![equal_cmp_opcode, load_opcode, jump_opcode, mem_equal_opcode, jump_if_opcode], + // None, + // ); + + // // equal_cmp_opcode + // let status = vm.process_opcode(); + // assert_eq!(status, VMStatus::InProgress); + + // let output_cmp_value = vm.registers.get(RegisterIndex(2)); + // assert_eq!(output_cmp_value, Value::from(true)); + + // // load_opcode + // let status = vm.process_opcode(); + // assert_eq!(status, VMStatus::InProgress); + + // let output_cmp_value = vm.registers.get(RegisterIndex(4)); + // assert_eq!(output_cmp_value, Value::from(6u128)); + + // // jump_opcode + // let status = vm.process_opcode(); + // assert_eq!(status, VMStatus::InProgress); + + // // mem_equal_opcode + // let status = vm.process_opcode(); + // assert_eq!(status, VMStatus::InProgress); + + // let output_cmp_value = vm.registers.get(RegisterIndex(6)); + // assert_eq!(output_cmp_value, Value::from(true)); + + // // jump_if_opcode + // let status = vm.process_opcode(); + // assert_eq!(status, VMStatus::Halted); + + // vm.finish(); + // } + + // #[test] + // fn store_opcode() { + // let mut compiler = TestCompiler { + // registers: vec![], + // opcodes: vec![], + // }; + // let input_registers = Registers::load(vec![ + // Value::from(2), + // Value::from(2), + // Value::from(0), + // Value::from(5), + // Value::from(0), + // Value::from(6), + // Value::from(0), + // Value::from(0), + // Value::from(0), + // ]); + + // let equal_cmp_opcode = Opcode::BinaryIntOp { + // bit_size: 1, + // op: BinaryIntOp::Cmp(Comparison::Eq), + // lhs: RegisterIndex(0), + // rhs: RegisterIndex(1), + // result: RegisterIndex(2), + // }; + + // let jump_opcode = Opcode::Jump { location: 4 }; + + // let jump_if_opcode = Opcode::JumpIf { + // condition: RegisterIndex(2), + // location: 11, + // }; + + // let load_const_opcode = Opcode::Const { + // destination: RegisterIndex(8), + // value: 1_u128.into() + // }; + // let load_const_opcode = Opcode::Const { + // destination: RegisterIndex(7), + // value: 3_u128.into() + // }; + // Opcode::Allocate { + // array_pointer: RegisterIndex(3), + // array_size: RegisterIndex(8) + // }; + // let store_opcode = Opcode::Store { + // source: RegisterIndex(2), + // destination_array: RegisterIndex(3), + // destination_index: RegisterIndex(7), + // }; + + // let mut initial_memory = vec![]; + // let initial_heap = HeapArray { + // array: vec![Value::from(5), Value::from(6)], + // }; + // initial_memory.insert(Value::from(5), initial_heap); + + // let mut vm = VM::new( + // input_registers, + // initial_memory, + // vec![equal_cmp_opcode, load_const_opcode, store_opcode, jump_opcode, jump_if_opcode], + // None, + // ); + + // // equal_cmp_opcode + // let status = vm.process_opcode(); + // assert_eq!(status, VMStatus::InProgress); + + // let output_cmp_value = vm.registers.get(RegisterIndex(2)); + // assert_eq!(output_cmp_value, Value::from(true)); + + // // load_const_opcode + // let status = vm.process_opcode(); + // assert_eq!(status, VMStatus::InProgress); + + // // store_opcode + // let status = vm.process_opcode(); + // assert_eq!(status, VMStatus::InProgress); + + // let mem_array = vm.memory[&Value::from(5)].clone(); + // assert_eq!(mem_array.array[3], Value::from(true)); + + // // jump_opcode + // let status = vm.process_opcode(); + // assert_eq!(status, VMStatus::InProgress); + + // // jump_if_opcode + // let status = vm.process_opcode(); + // assert_eq!(status, VMStatus::Halted); + + // vm.finish(); + // } + + // #[test] + // fn oracle_array_output() { + // use crate::opcodes::OracleInput; + + // let input_registers = Registers::load(vec![ + // Value::from(2), + // Value::from(2), + // Value::from(0), + // Value::from(5), + // Value::from(0), + // Value::from(6), + // Value::from(0), + // ]); + + // let oracle_input = OracleInput::RegisterIndex(RegisterIndex(0)); + // let oracle_output = + // OracleOutput::Array { start: RegisterIndex(3), length: 2 }; + + // let mut oracle_data = OracleData { + // name: "get_notes".to_owned(), + // inputs: vec![oracle_input], + // input_values: vec![], + // outputs: vec![oracle_output], + // output_values: vec![], + // }; + + // let oracle_opcode = Opcode::Oracle(oracle_data.clone()); + + // let initial_memory = vec![]; + + // let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode], None); + + // let output_state = vm.process_opcodes(); + // assert_eq!(output_state.status, VMStatus::OracleWait); + + // let input_values = output_state.map_input_values(&oracle_data); + + // oracle_data.input_values = input_values; + // oracle_data.output_values = vec![FieldElement::from(10_u128), FieldElement::from(2_u128)]; + // let updated_oracle_opcode = Opcode::Oracle(oracle_data); + + // let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode], None,); + // let output_state = vm.process_opcodes(); + // assert_eq!(output_state.status, VMStatus::Halted); + + // let mem_array = output_state.memory[&Value::from(5u128)].clone(); + // assert_eq!(mem_array.array[0], Value::from(10_u128)); + // assert_eq!(mem_array.array[1], Value::from(2_u128)); + // } + + // #[test] + // fn oracle_array_input() { + // use crate::opcodes::OracleInput; + + // let input_registers = Registers::load(vec![ + // Value::from(2), + // Value::from(2), + // Value::from(0), + // Value::from(5), + // Value::from(0), + // Value::from(6), + // Value::from(0), + // ]); + + // let expected_length = 2; + // let oracle_input = OracleInput::Array { + // start: RegisterIndex(3), + // length: expected_length, + // }; + // let oracle_output = OracleOutput::RegisterIndex(RegisterIndex(6)); + + // let mut oracle_data = OracleData { + // name: "call_private_function_oracle".to_owned(), + // inputs: vec![oracle_input.clone()], + // input_values: vec![], + // outputs: vec![oracle_output], + // output_values: vec![], + // }; + + // let oracle_opcode = Opcode::Oracle(oracle_data.clone()); + + // let mut initial_memory = vec![]; + // let initial_heap = HeapArray { + // array: vec![Value::from(5), Value::from(6)], + // }; + // initial_memory.insert(Value::from(5), initial_heap); + + // let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode], None,); + + // let output_state = vm.process_opcodes(); + // assert_eq!(output_state.status, VMStatus::OracleWait); + + // let input_values = output_state.map_input_values(&oracle_data); + // assert_eq!(input_values.len(), expected_length); + + // oracle_data.input_values = input_values; + // oracle_data.output_values = vec![FieldElement::from(5u128)]; + // let updated_oracle_opcode = Opcode::Oracle(oracle_data); + + // let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode], None,); + // let output_state = vm.process_opcodes(); + // assert_eq!(output_state.status, VMStatus::Halted); + + // let mem_array = output_state.memory[&Value::from(5)].clone(); + // assert_eq!(mem_array.array[0], Value::from(5)); + // assert_eq!(mem_array.array[1], Value::from(6)); + // } } diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index ea657ea73..c7e8fa8c2 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -1,29 +1,9 @@ -use std::ops::{Add, BitAnd, BitOr, BitXor, Mul, Shl, Shr, Sub}; - use crate::{ - memory::ArrayIndex, - value::{Typ, Value}, RegisterIndex, }; use acir_field::FieldElement; use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub enum RegisterMemIndex { - Register(RegisterIndex), - Constant(FieldElement), - Memory(ArrayIndex), -} - -impl RegisterMemIndex { - pub fn to_register_index(self) -> Option { - match self { - RegisterMemIndex::Register(register) => Some(register), - RegisterMemIndex::Constant(_) | RegisterMemIndex::Memory(_) => None, - } - } -} - pub type Label = usize; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -32,7 +12,7 @@ pub enum Opcode { /// Performs the specified binary operation /// and stores the value in the `result` register. BinaryFieldOp { - op: BinaryOp, + op: BinaryFieldOp, lhs: RegisterIndex, rhs: RegisterIndex, result: RegisterIndex, @@ -41,7 +21,7 @@ pub enum Opcode { /// Performs the specified binary operation /// and stores the value in the `result` register. BinaryIntOp { - op: BinaryOp, + op: BinaryIntOp, bit_size: u32, lhs: RegisterIndex, rhs: RegisterIndex, @@ -49,52 +29,46 @@ pub enum Opcode { }, JumpIfNot { condition: RegisterIndex, - destination: Label, + location: Label, }, /// Sets the program counter to the value located at `destination` /// If the value at condition is non-zero JumpIf { condition: RegisterIndex, - destination: Label, + location: Label, }, /// Sets the program counter to the label. Jump { - destination: Label, + location: Label, + }, + // We don't support dynamic jumps or calls + // See https://github.com/ethereum/aleth/issues/3404 for reasoning + Call { + location: Label, }, - PushStack { - source: RegisterIndex, + Const { + destination: RegisterIndex, + value: FieldElement, }, - // TODO:This is used to call functions and setup things like - // TODO execution contexts. - Call, + Return, // TODO:These are special functions like sha256 Intrinsics, /// Used to get data from an outside source Oracle(OracleData), Mov { - destination: RegisterIndex, - source: RegisterIndex, + destination_register: RegisterIndex, + source_register: RegisterIndex, }, Load { - destination: RegisterIndex, - array_id_reg: RegisterIndex, - index: RegisterIndex, - }, - LoadConst { - destination: RegisterIndex, - constant: FieldElement, + destination_register: RegisterIndex, + source_pointer: RegisterIndex, }, Store { - source: RegisterIndex, - array_id_reg: RegisterIndex, - index: RegisterIndex, + destination_pointer: RegisterIndex, + source_register: RegisterIndex, }, /// Used if execution fails during evaluation Trap, - /// Hack - Bootstrap { - register_allocation_indices: Vec, - }, /// Stop execution Stop, } @@ -104,20 +78,19 @@ impl Opcode { match self { Opcode::BinaryFieldOp { .. } => "binary_field_op", Opcode::BinaryIntOp { .. } => "binary_int_op", - Opcode::JumpIfNot { .. } => "jmpifnot", - Opcode::JumpIf { .. } => "jmpif", + Opcode::JumpIfNot { .. } => "jmp_if_not", + Opcode::JumpIf { .. } => "jmp_if", Opcode::Jump { .. } => "jmp", - Opcode::PushStack { .. } => "pushstack", - Opcode::Call => "callback", + Opcode::Call { .. } => "call", + Opcode::Const { .. } => "const", + Opcode::Return => "return", Opcode::Intrinsics => "intrinsics", Opcode::Oracle(_) => "oracle", Opcode::Mov { .. } => "mov", Opcode::Load { .. } => "load", Opcode::Store { .. } => "store", Opcode::Trap => "trap", - Opcode::Bootstrap { .. } => "bootstrap", Opcode::Stop => "stop", - Opcode::LoadConst { .. } => "loadconst", } } } @@ -139,19 +112,27 @@ pub struct OracleData { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum OracleInput { RegisterIndex(RegisterIndex), - Array { start: RegisterIndex, length: usize }, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum OracleOutput { RegisterIndex(RegisterIndex), - Array { start: RegisterIndex, length: usize }, } // Binary fixed-length integer expressions #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub enum BinaryOp { +pub enum BinaryFieldOp { + Add, + Sub, + Mul, + Div, + Cmp(Comparison), +} + +// Binary fixed-length integer expressions +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum BinaryIntOp { Add, Sub, Mul, @@ -172,61 +153,58 @@ pub enum Comparison { Lte, //(<=) field less or equal } -impl BinaryOp { +impl BinaryFieldOp { /// Evaluate a binary operation on two FieldElements and return the result as a FieldElement. pub fn evaluate_field(&self, a: FieldElement, b: FieldElement) -> FieldElement { match self { // Perform addition, subtraction, multiplication, and division based on the BinaryOp variant. - BinaryOp::Add => a + b, - BinaryOp::Sub => a - b, - BinaryOp::Mul => a * b, - BinaryOp::SignedDiv | BinaryOp::UnsignedDiv => a / b, + BinaryFieldOp::Add => a + b, + BinaryFieldOp::Sub => a - b, + BinaryFieldOp::Mul => a * b, + BinaryFieldOp::Div => a / b, // Perform a comparison between a and b based on the Comparison variant. - BinaryOp::Cmp(comparison) => match comparison { + BinaryFieldOp::Cmp(comparison) => match comparison { Comparison::Eq => ((a == b) as u128).into(), Comparison::Lt => ((a < b) as u128).into(), Comparison::Lte => ((a <= b) as u128).into(), }, - // These operations are not allowed for FieldElement, so they are unreachable. - BinaryOp::And => unreachable!("operation not allowed for FieldElement"), - BinaryOp::Or => unreachable!("operation not allowed for FieldElement"), - BinaryOp::Xor => unreachable!("operation not allowed for FieldElement"), - BinaryOp::Shl => unreachable!("operation not allowed for FieldElement"), - BinaryOp::Shr => unreachable!("operation not allowed for FieldElement"), } } +} + +impl BinaryIntOp { /// Evaluate a binary operation on two unsigned integers (u128) with a given bit size and return the result as a u128. pub fn evaluate_int(&self, a: u128, b: u128, bit_size: u32) -> u128 { let bit_modulo = 1_u128 << bit_size; match self { // Perform addition, subtraction, and multiplication, applying a modulo operation to keep the result within the bit size. - BinaryOp::Add => (a + b) % bit_modulo, - BinaryOp::Sub => (a - b) % bit_modulo, - BinaryOp::Mul => (a * b) % bit_modulo, + BinaryIntOp::Add => (a + b) % bit_modulo, + BinaryIntOp::Sub => (a - b) % bit_modulo, + BinaryIntOp::Mul => (a * b) % bit_modulo, // Perform unsigned division using the modulo operation on a and b. - BinaryOp::UnsignedDiv => (a % bit_modulo) / (b % bit_modulo), + BinaryIntOp::UnsignedDiv => (a % bit_modulo) / (b % bit_modulo), // Perform signed division by first converting a and b to signed integers and then back to unsigned after the operation. - BinaryOp::SignedDiv => to_unsigned(to_signed(a, bit_size) / to_signed(b, bit_size), bit_size), + BinaryIntOp::SignedDiv => to_unsigned(to_signed(a, bit_size) / to_signed(b, bit_size), bit_size), // Perform a comparison between a and b based on the Comparison variant. - BinaryOp::Cmp(comparison) => match comparison { + BinaryIntOp::Cmp(comparison) => match comparison { Comparison::Eq => ((a == b) as u128).into(), Comparison::Lt => ((a < b) as u128).into(), Comparison::Lte => ((a <= b) as u128).into(), }, // Perform bitwise AND, OR, XOR, left shift, and right shift operations, applying a modulo operation to keep the result within the bit size. - BinaryOp::And => { + BinaryIntOp::And => { (a & b) % bit_modulo } - BinaryOp::Or => { + BinaryIntOp::Or => { (a | b) % bit_modulo } - BinaryOp::Xor => { + BinaryIntOp::Xor => { (a ^ b) % bit_modulo } - BinaryOp::Shl => { + BinaryIntOp::Shl => { (a << b) % bit_modulo } - BinaryOp::Shr => { + BinaryIntOp::Shr => { (a >> b) % bit_modulo } } diff --git a/brillig_bytecode/src/registers.rs b/brillig_bytecode/src/registers.rs index 250af5fc4..153c835be 100644 --- a/brillig_bytecode/src/registers.rs +++ b/brillig_bytecode/src/registers.rs @@ -1,4 +1,4 @@ -use crate::{opcodes::RegisterMemIndex, Typ, Value}; +use crate::{Value}; use acir_field::FieldElement; use serde::{Deserialize, Serialize}; diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs index 1e8f6b140..584e02c9d 100644 --- a/brillig_bytecode/src/value.rs +++ b/brillig_bytecode/src/value.rs @@ -24,6 +24,15 @@ impl Value { pub fn to_u128(&self) -> u128 { self.inner.to_u128() } + pub fn to_usize(&self) -> usize { + self.inner.to_u128() as usize + } +} + +impl From for Value { + fn from(value: usize) -> Self { + Value { inner: FieldElement::from(value as u128) } + } } impl From for Value { @@ -38,12 +47,6 @@ impl From for Value { } } -impl From for Value { - fn from(value: u32) -> Self { - Value { inner: FieldElement::from(value as i128) } - } -} - impl From for Value { fn from(value: bool) -> Self { if value { From f7c5013027e6f9627b73da00047faa4edbd672ee Mon Sep 17 00:00:00 2001 From: ludamad Date: Tue, 9 May 2023 16:20:09 -0400 Subject: [PATCH 054/125] chore: additional testing --- acvm/src/lib.rs | 8 +- brillig_bytecode/src/lib.rs | 653 +++++++++++++++----------------- brillig_bytecode/src/opcodes.rs | 12 +- 3 files changed, 322 insertions(+), 351 deletions(-) diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 34b60345d..246044f19 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -477,14 +477,14 @@ mod test { op: brillig_bytecode::BinaryFieldOp::Cmp(Comparison::Eq), lhs: RegisterIndex(0), rhs: RegisterIndex(1), - result: RegisterIndex(2), + destination: RegisterIndex(2), }; let less_than_opcode = brillig_bytecode::Opcode::BinaryFieldOp { op: brillig_bytecode::BinaryFieldOp::Cmp(Comparison::Lt), lhs: RegisterIndex(0), rhs: RegisterIndex(1), - result: RegisterIndex(3), + destination: RegisterIndex(3), }; let invert_oracle_input = OracleInput::RegisterIndex(RegisterIndex(0)); @@ -600,14 +600,14 @@ mod test { op: BinaryFieldOp::Cmp(Comparison::Eq), lhs: RegisterIndex(0), rhs: RegisterIndex(1), - result: RegisterIndex(2), + destination: RegisterIndex(2), }; let less_than_opcode = brillig_bytecode::Opcode::BinaryFieldOp { op: BinaryFieldOp::Cmp(Comparison::Lt), lhs: RegisterIndex(0), rhs: RegisterIndex(1), - result: RegisterIndex(3), + destination: RegisterIndex(3), }; let invert_oracle_input = OracleInput::RegisterIndex(RegisterIndex(0)); diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 4b8651e78..0787393ed 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -98,11 +98,11 @@ impl VM { pub fn process_opcode(&mut self) -> VMStatus { let opcode = &self.bytecode[self.program_counter]; match opcode { - Opcode::BinaryFieldOp { op, lhs, rhs, result } => { + Opcode::BinaryFieldOp { op, lhs, rhs, destination: result } => { self.process_binary_field_op(*op, *lhs, *rhs, *result); self.increment_program_counter() } - Opcode::BinaryIntOp { op, bit_size, lhs, rhs, result } => { + Opcode::BinaryIntOp { op, bit_size, lhs, rhs, destination: result } => { self.process_binary_int_op(*op, *bit_size, *lhs, *rhs, *result); self.increment_program_counter() } @@ -159,14 +159,14 @@ impl VM { self.increment_program_counter() } - Opcode::Mov { destination_register, source_register } => { + Opcode::Mov { destination: destination_register, source: source_register } => { let source_value = self.get(source_register); self.registers.set(*destination_register, source_value); self.increment_program_counter() } Opcode::Trap => self.fail(), Opcode::Stop => self.halt(), - Opcode::Load { destination_register, source_pointer } => { + Opcode::Load { destination: destination_register, source_pointer } => { // Convert our source_pointer to a usize let source = self.get(source_pointer); let source_usize = usize::try_from( @@ -177,7 +177,7 @@ impl VM { self.registers.set(*destination_register, *value); self.increment_program_counter() } - Opcode::Store { destination_pointer, source_register } => { + Opcode::Store { destination_pointer, source: source_register } => { // Convert our destination_pointer to a usize let destination = self.get(destination_pointer); let destination_usize = usize::try_from( @@ -297,29 +297,6 @@ impl VMOutputState { #[cfg(test)] mod tests { use super::*; - /// Test helper - struct TestCompiler { - registers: Vec, - opcodes: Vec - } - - impl TestCompiler { - fn make(&mut self, opcode: Opcode) { - self.opcodes.push(opcode); - } - fn register(&mut self, value: Value) -> RegisterIndex { - self.registers.push(value); - RegisterIndex(self.registers.len() - 1) - } - // fn make_const(&mut self, value: Value) -> usize { - // self.registers.push(Value::from(0u128)); - // self.opcodes.push(Opcode::Const { - // destination: RegisterIndex(self.registers.len() - 1), - // value: value.inner, - // }); - // self.registers.len() - 1 - // } - } #[test] fn add_single_step_smoke() { @@ -335,7 +312,7 @@ mod tests { bit_size: 2, lhs: RegisterIndex(0), rhs: RegisterIndex(1), - result: RegisterIndex(2), + destination: RegisterIndex(2), }; // Start VM @@ -358,43 +335,57 @@ mod tests { #[test] fn jmpif_opcode() { - let mut compiler = TestCompiler { - registers: vec![], - opcodes: vec![], + let mut registers = vec![]; + let mut opcodes = vec![]; + + let lhs = { + registers.push(Value::from(2u128)); + RegisterIndex(registers.len() - 1) + }; + + let rhs = { + registers.push(Value::from(2u128)); + RegisterIndex(registers.len() - 1) }; + + let destination = { + registers.push(Value::from(0u128)); + RegisterIndex(registers.len() - 1) + }; + let equal_cmp_opcode = Opcode::BinaryIntOp { op: BinaryIntOp::Cmp(Comparison::Eq), bit_size: 1, - lhs: compiler.register(Value::from(2u128)), - rhs: compiler.register(Value::from(2u128)), - result: compiler.register(Value::from(0u128)), + lhs, + rhs, + destination, }; - compiler.make(equal_cmp_opcode); - compiler.make(Opcode::Jump { location: 2 }); - compiler.make(Opcode::JumpIf { + opcodes.push(equal_cmp_opcode); + opcodes.push(Opcode::Jump { location: 2 }); + opcodes.push(Opcode::JumpIf { condition: RegisterIndex(2), location: 3, }); - + let mut vm = VM::new( - Registers {inner: compiler.registers}, + Registers { inner: registers }, vec![], - compiler.opcodes, - None + opcodes, + None, ); - + let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - + let output_cmp_value = vm.registers.get(RegisterIndex(2)); assert_eq!(output_cmp_value, Value::from(true)); - + let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - + let status = vm.process_opcode(); assert_eq!(status, VMStatus::Halted); - + vm.finish(); } @@ -409,7 +400,7 @@ mod tests { op: BinaryFieldOp::Cmp(Comparison::Eq), lhs: RegisterIndex(0), rhs: RegisterIndex(1), - result: RegisterIndex(2), + destination: RegisterIndex(2), }; let jump_opcode = Opcode::Jump { location: 2 }; @@ -423,7 +414,7 @@ mod tests { op: BinaryFieldOp::Add, lhs: RegisterIndex(0), rhs: RegisterIndex(1), - result: RegisterIndex(2), + destination: RegisterIndex(2), }; let mut vm = VM::new( @@ -460,8 +451,8 @@ mod tests { Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(3u128)]); let mov_opcode = Opcode::Mov { - destination_register: RegisterIndex(2), - source_register: RegisterIndex(0), + destination: RegisterIndex(2), + source: RegisterIndex(0), }; let mut vm = VM::new(input_registers, vec![], vec![mov_opcode], None); @@ -493,7 +484,7 @@ mod tests { op: BinaryIntOp::Cmp(Comparison::Eq), lhs: RegisterIndex(0), rhs: RegisterIndex(1), - result: RegisterIndex(2), + destination: RegisterIndex(2), }; let not_equal_opcode = Opcode::BinaryIntOp { @@ -501,7 +492,7 @@ mod tests { op: BinaryIntOp::Cmp(Comparison::Eq), lhs: RegisterIndex(0), rhs: RegisterIndex(3), - result: RegisterIndex(2), + destination: RegisterIndex(2), }; let less_than_opcode = Opcode::BinaryIntOp { @@ -509,7 +500,7 @@ mod tests { op: BinaryIntOp::Cmp(Comparison::Lt), lhs: RegisterIndex(3), rhs: RegisterIndex(4), - result: RegisterIndex(2), + destination: RegisterIndex(2), }; let less_than_equal_opcode = Opcode::BinaryIntOp { @@ -517,7 +508,7 @@ mod tests { op: BinaryIntOp::Cmp(Comparison::Lte), lhs: RegisterIndex(3), rhs: RegisterIndex(4), - result: RegisterIndex(2), + destination: RegisterIndex(2), }; let mut vm = VM::new( @@ -553,291 +544,271 @@ mod tests { vm.finish(); } + #[test] + fn store_opcode() { + /// Brillig code for the following: + /// let mut i = 0; + /// let len = memory.len(); + /// while i < len { + /// memory[i] = i as Value; + /// i += 1; + /// } + fn brillig_write_memory(memory: Vec) -> Vec { + let r_i = RegisterIndex(0); + let r_len = RegisterIndex(1); + let r_tmp = RegisterIndex(2); + let start = [ + // i = 0 + Opcode::Const { + destination: r_i, + value: 0u128.into(), + }, + // len = memory.len() (approximation) + Opcode::Const { + destination: r_len, + value: FieldElement::from(memory.len() as u128), + }, + ]; + let loop_body = [ + // *i = i + Opcode::Store { + destination_pointer: r_i, + source: r_i, + }, + // tmp = 1 + Opcode::Const { + destination: r_tmp, + value: 1u128.into(), + }, + // i = i + 1 (tmp) + Opcode::BinaryIntOp { + destination: r_i, + lhs: r_i, + op: BinaryIntOp::Add, + rhs: r_tmp, + bit_size: 32, + }, + // tmp = i < len + Opcode::BinaryIntOp { + destination: r_tmp, + lhs: r_i, + op: BinaryIntOp::Cmp(Comparison::Lt), + rhs: r_len, + bit_size: 32, + }, + // if tmp != 0 goto loop_body + Opcode::JumpIf { + condition: r_tmp, + location: start.len() + }, + ]; + let vm = brillig_execute(memory, [&start[..], &loop_body[..]].concat()); + vm.memory.clone() + } + + let memory = brillig_write_memory(vec![Value::from(0u128); 5]); + let expected = vec![ + Value::from(0u128), + Value::from(1u128), + Value::from(2u128), + Value::from(3u128), + Value::from(4u128), + ]; + assert_eq!(memory, expected); + + let memory = brillig_write_memory(vec![Value::from(0u128); 1024]); + let expected: Vec = (0..1024).map(|i| Value::from(i as u128)).collect(); + assert_eq!(memory, expected); + } + + #[test] + fn load_opcode() { + /// Brillig code for the following: + /// let mut sum = 0; + /// let mut i = 0; + /// let len = memory.len(); + /// while i < len { + /// sum += memory[i]; + /// i += 1; + /// } + fn brillig_sum_memory(memory: Vec) -> Value { + let r_i = RegisterIndex(0); + let r_len = RegisterIndex(1); + let r_sum = RegisterIndex(2); + let r_tmp = RegisterIndex(3); + let start = [ + // sum = 0 + Opcode::Const { + destination: r_sum, + value: 0u128.into(), + }, + // i = 0 + Opcode::Const { + destination: r_i, + value: 0u128.into(), + }, + // len = array.len() (approximation) + Opcode::Const { + destination: r_len, + value: FieldElement::from(memory.len() as u128), + }, + ]; + let loop_body = [ + // tmp = *i + Opcode::Load { + destination: r_tmp, + source_pointer: r_i + }, + // sum = sum + tmp + Opcode::BinaryIntOp { + destination: r_sum, + lhs: r_sum, + op: BinaryIntOp::Add, + rhs: r_tmp, + bit_size: 32, + }, + // tmp = 1 + Opcode::Const { + destination: r_tmp, + value: 1u128.into(), + }, + // i = i + 1 (tmp) + Opcode::BinaryIntOp { + destination: r_i, + lhs: r_i, + op: BinaryIntOp::Add, + rhs: r_tmp, + bit_size: 32, + }, + // tmp = i < len + Opcode::BinaryIntOp { + destination: r_tmp, + lhs: r_i, + op: BinaryIntOp::Cmp(Comparison::Lt), + rhs: r_len, + bit_size: 32, + }, + // if tmp != 0 goto loop_body + Opcode::JumpIf { + condition: r_tmp, + location: start.len() + }, + ]; + let vm = brillig_execute(memory, [&start[..], &loop_body[..]].concat()); + vm.registers.get(r_sum) + } - // #[test] - // fn load_opcode() { - // let input_registers = Registers::load(vec![ - // Value::from(2u128), - // Value::from(2u128), - // Value::from(0u128), - // Value::from(5u128), - // Value::from(0u128), - // Value::from(6u128), - // Value::from(0u128), - // ]); - - // let equal_cmp_opcode = Opcode::BinaryIntOp { - // bit_size: 1, - // op: BinaryIntOp::Cmp(Comparison::Eq), - // lhs: RegisterIndex(0), - // rhs: RegisterIndex(1), - // result: RegisterIndex(2), - // }; - - // let jump_opcode = Opcode::Jump { location: 3 }; - - // let jump_if_opcode = Opcode::JumpIf { - // condition: RegisterIndex(2), - // location: 10, - // }; - - // let load_opcode = Opcode::Load { - // destination_register: RegisterIndex(4), - // source_array: RegisterIndex(3), - // source_index: RegisterIndex(2), - // }; - - // let mem_equal_opcode = Opcode::BinaryIntOp { - // bit_size: 1, - // op: BinaryIntOp::Cmp(Comparison::Eq), - // lhs: RegisterIndex(4), - // rhs: RegisterIndex(5), - // result: RegisterIndex(6), - // }; - - // let mut initial_memory = vec![]; - // let initial_heap = HeapArray { - // array: vec![Value::from(5), Value::from(6)] - // }; - // initial_memory.insert(Value::from(5), initial_heap); - - // let mut vm = VM::new( - // input_registers, - // initial_memory, - // vec![equal_cmp_opcode, load_opcode, jump_opcode, mem_equal_opcode, jump_if_opcode], - // None, - // ); - - // // equal_cmp_opcode - // let status = vm.process_opcode(); - // assert_eq!(status, VMStatus::InProgress); - - // let output_cmp_value = vm.registers.get(RegisterIndex(2)); - // assert_eq!(output_cmp_value, Value::from(true)); - - // // load_opcode - // let status = vm.process_opcode(); - // assert_eq!(status, VMStatus::InProgress); - - // let output_cmp_value = vm.registers.get(RegisterIndex(4)); - // assert_eq!(output_cmp_value, Value::from(6u128)); - - // // jump_opcode - // let status = vm.process_opcode(); - // assert_eq!(status, VMStatus::InProgress); - - // // mem_equal_opcode - // let status = vm.process_opcode(); - // assert_eq!(status, VMStatus::InProgress); - - // let output_cmp_value = vm.registers.get(RegisterIndex(6)); - // assert_eq!(output_cmp_value, Value::from(true)); - - // // jump_if_opcode - // let status = vm.process_opcode(); - // assert_eq!(status, VMStatus::Halted); - - // vm.finish(); - // } - - // #[test] - // fn store_opcode() { - // let mut compiler = TestCompiler { - // registers: vec![], - // opcodes: vec![], - // }; - // let input_registers = Registers::load(vec![ - // Value::from(2), - // Value::from(2), - // Value::from(0), - // Value::from(5), - // Value::from(0), - // Value::from(6), - // Value::from(0), - // Value::from(0), - // Value::from(0), - // ]); - - // let equal_cmp_opcode = Opcode::BinaryIntOp { - // bit_size: 1, - // op: BinaryIntOp::Cmp(Comparison::Eq), - // lhs: RegisterIndex(0), - // rhs: RegisterIndex(1), - // result: RegisterIndex(2), - // }; - - // let jump_opcode = Opcode::Jump { location: 4 }; - - // let jump_if_opcode = Opcode::JumpIf { - // condition: RegisterIndex(2), - // location: 11, - // }; - - // let load_const_opcode = Opcode::Const { - // destination: RegisterIndex(8), - // value: 1_u128.into() - // }; - // let load_const_opcode = Opcode::Const { - // destination: RegisterIndex(7), - // value: 3_u128.into() - // }; - // Opcode::Allocate { - // array_pointer: RegisterIndex(3), - // array_size: RegisterIndex(8) - // }; - // let store_opcode = Opcode::Store { - // source: RegisterIndex(2), - // destination_array: RegisterIndex(3), - // destination_index: RegisterIndex(7), - // }; - - // let mut initial_memory = vec![]; - // let initial_heap = HeapArray { - // array: vec![Value::from(5), Value::from(6)], - // }; - // initial_memory.insert(Value::from(5), initial_heap); - - // let mut vm = VM::new( - // input_registers, - // initial_memory, - // vec![equal_cmp_opcode, load_const_opcode, store_opcode, jump_opcode, jump_if_opcode], - // None, - // ); - - // // equal_cmp_opcode - // let status = vm.process_opcode(); - // assert_eq!(status, VMStatus::InProgress); - - // let output_cmp_value = vm.registers.get(RegisterIndex(2)); - // assert_eq!(output_cmp_value, Value::from(true)); - - // // load_const_opcode - // let status = vm.process_opcode(); - // assert_eq!(status, VMStatus::InProgress); - - // // store_opcode - // let status = vm.process_opcode(); - // assert_eq!(status, VMStatus::InProgress); - - // let mem_array = vm.memory[&Value::from(5)].clone(); - // assert_eq!(mem_array.array[3], Value::from(true)); - - // // jump_opcode - // let status = vm.process_opcode(); - // assert_eq!(status, VMStatus::InProgress); - - // // jump_if_opcode - // let status = vm.process_opcode(); - // assert_eq!(status, VMStatus::Halted); - - // vm.finish(); - // } - - // #[test] - // fn oracle_array_output() { - // use crate::opcodes::OracleInput; - - // let input_registers = Registers::load(vec![ - // Value::from(2), - // Value::from(2), - // Value::from(0), - // Value::from(5), - // Value::from(0), - // Value::from(6), - // Value::from(0), - // ]); - - // let oracle_input = OracleInput::RegisterIndex(RegisterIndex(0)); - // let oracle_output = - // OracleOutput::Array { start: RegisterIndex(3), length: 2 }; - - // let mut oracle_data = OracleData { - // name: "get_notes".to_owned(), - // inputs: vec![oracle_input], - // input_values: vec![], - // outputs: vec![oracle_output], - // output_values: vec![], - // }; - - // let oracle_opcode = Opcode::Oracle(oracle_data.clone()); - - // let initial_memory = vec![]; - - // let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode], None); - - // let output_state = vm.process_opcodes(); - // assert_eq!(output_state.status, VMStatus::OracleWait); - - // let input_values = output_state.map_input_values(&oracle_data); - - // oracle_data.input_values = input_values; - // oracle_data.output_values = vec![FieldElement::from(10_u128), FieldElement::from(2_u128)]; - // let updated_oracle_opcode = Opcode::Oracle(oracle_data); - - // let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode], None,); - // let output_state = vm.process_opcodes(); - // assert_eq!(output_state.status, VMStatus::Halted); - - // let mem_array = output_state.memory[&Value::from(5u128)].clone(); - // assert_eq!(mem_array.array[0], Value::from(10_u128)); - // assert_eq!(mem_array.array[1], Value::from(2_u128)); - // } - - // #[test] - // fn oracle_array_input() { - // use crate::opcodes::OracleInput; - - // let input_registers = Registers::load(vec![ - // Value::from(2), - // Value::from(2), - // Value::from(0), - // Value::from(5), - // Value::from(0), - // Value::from(6), - // Value::from(0), - // ]); - - // let expected_length = 2; - // let oracle_input = OracleInput::Array { - // start: RegisterIndex(3), - // length: expected_length, - // }; - // let oracle_output = OracleOutput::RegisterIndex(RegisterIndex(6)); - - // let mut oracle_data = OracleData { - // name: "call_private_function_oracle".to_owned(), - // inputs: vec![oracle_input.clone()], - // input_values: vec![], - // outputs: vec![oracle_output], - // output_values: vec![], - // }; - - // let oracle_opcode = Opcode::Oracle(oracle_data.clone()); - - // let mut initial_memory = vec![]; - // let initial_heap = HeapArray { - // array: vec![Value::from(5), Value::from(6)], - // }; - // initial_memory.insert(Value::from(5), initial_heap); - - // let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode], None,); - - // let output_state = vm.process_opcodes(); - // assert_eq!(output_state.status, VMStatus::OracleWait); - - // let input_values = output_state.map_input_values(&oracle_data); - // assert_eq!(input_values.len(), expected_length); - - // oracle_data.input_values = input_values; - // oracle_data.output_values = vec![FieldElement::from(5u128)]; - // let updated_oracle_opcode = Opcode::Oracle(oracle_data); - - // let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode], None,); - // let output_state = vm.process_opcodes(); - // assert_eq!(output_state.status, VMStatus::Halted); - - // let mem_array = output_state.memory[&Value::from(5)].clone(); - // assert_eq!(mem_array.array[0], Value::from(5)); - // assert_eq!(mem_array.array[1], Value::from(6)); - // } + assert_eq!(brillig_sum_memory(vec![ + Value::from(1u128), + Value::from(2u128), + Value::from(3u128), + Value::from(4u128), + Value::from(5u128), + ]), Value::from(15u128)); + assert_eq!(brillig_sum_memory(vec![Value::from(1u128); 1024]), Value::from(1024u128)); + } + + #[test] + fn call_and_return_opcodes() { + /// Brillig code for the following recursive function: + /// fn recursive_write(i: u128, len: u128) { + /// if len <= i { + /// return; + /// } + /// memory[i as usize] = i as Value; + /// recursive_write(memory, i + 1, len); + /// } + /// Note we represent a 100% in-register optimized form in brillig + fn brillig_recursive_write_memory(memory: Vec) -> Vec { + let r_i = RegisterIndex(0); + let r_len = RegisterIndex(1); + let r_tmp = RegisterIndex(2); + + let start = [ + // i = 0 + Opcode::Const { + destination: r_i, + value: 0u128.into(), + }, + // len = memory.len() (approximation) + Opcode::Const { + destination: r_len, + value: FieldElement::from(memory.len() as u128), + }, + ]; + + let recursive_fn = [ + // tmp = len <= i + Opcode::BinaryIntOp { + destination: r_tmp, + lhs: r_len, + op: BinaryIntOp::Cmp(Comparison::Lte), + rhs: r_i, + bit_size: 32, + }, + // if !tmp, goto end + Opcode::JumpIf { + condition: r_tmp, + location: start.len() + 6, // 7 ops in recursive_fn, go to 'Return' + }, + // *i = i + Opcode::Store { + destination_pointer: r_i, + source: r_i, + }, + // tmp = 1 + Opcode::Const { + destination: r_tmp, + value: 1u128.into(), + }, + // i = i + 1 (tmp) + Opcode::BinaryIntOp { + destination: r_i, + lhs: r_i, + op: BinaryIntOp::Add, + rhs: r_tmp, + bit_size: 32, + }, + // call recursive_fn + Opcode::Jump { + location: start.len(), + }, + Opcode::Return {} + ]; + + let vm = brillig_execute(memory, [&start[..], &recursive_fn[..]].concat()); + vm.memory.clone() + } + + let memory = brillig_recursive_write_memory(vec![Value::from(0u128); 5]); + let expected = vec![ + Value::from(0u128), + Value::from(1u128), + Value::from(2u128), + Value::from(3u128), + Value::from(4u128), + ]; + assert_eq!(memory, expected); + + let memory = brillig_recursive_write_memory(vec![Value::from(0u128); 1024]); + let expected: Vec = (0..1024).map(|i| Value::from(i as u128)).collect(); + assert_eq!(memory, expected); + } + + /// Helper to execute brillig code + fn brillig_execute(memory: Vec, opcodes: Vec) -> VM { + let mut vm = VM::new( + Registers { inner: vec![Value::from(0u128); 16]}, + memory, + opcodes, + None, + ); + loop { + let status = vm.process_opcode(); + if status == VMStatus::Halted { + break; + } + } + vm + } } diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index c7e8fa8c2..ce8774ecd 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -12,20 +12,20 @@ pub enum Opcode { /// Performs the specified binary operation /// and stores the value in the `result` register. BinaryFieldOp { + destination: RegisterIndex, op: BinaryFieldOp, lhs: RegisterIndex, rhs: RegisterIndex, - result: RegisterIndex, }, /// Takes the bit_size size integers in registers `lhs` and `rhs` /// Performs the specified binary operation /// and stores the value in the `result` register. BinaryIntOp { + destination: RegisterIndex, op: BinaryIntOp, bit_size: u32, lhs: RegisterIndex, rhs: RegisterIndex, - result: RegisterIndex, }, JumpIfNot { condition: RegisterIndex, @@ -56,16 +56,16 @@ pub enum Opcode { /// Used to get data from an outside source Oracle(OracleData), Mov { - destination_register: RegisterIndex, - source_register: RegisterIndex, + destination: RegisterIndex, + source: RegisterIndex, }, Load { - destination_register: RegisterIndex, + destination: RegisterIndex, source_pointer: RegisterIndex, }, Store { destination_pointer: RegisterIndex, - source_register: RegisterIndex, + source: RegisterIndex, }, /// Used if execution fails during evaluation Trap, From a52e2e83e6e87578b430872d6d644972478eb8f6 Mon Sep 17 00:00:00 2001 From: ludamad Date: Wed, 10 May 2023 10:15:10 -0400 Subject: [PATCH 055/125] chore: additional testing --- acvm/src/pwg/brillig.rs | 2 +- brillig_bytecode/src/lib.rs | 107 ++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index 871689aff..3c415c248 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -86,7 +86,7 @@ impl BrilligSolver { let id_as_value: Value = Value { inner: FieldElement::from(*id as u128) }; // Push value of the array id as a register - input_register_values.push(id_as_value.into()); + input_register_values.push(id_as_value); } } } diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 0787393ed..f5f28ad66 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -811,4 +811,111 @@ mod tests { } vm } + #[test] + fn oracle_array_output() { + use crate::opcodes::OracleInput; + + let input_registers = Registers::load(vec![ + Value::from(2), + Value::from(2), + Value::from(0), + Value::from(5), + Value::from(0), + Value::from(6), + Value::from(0), + ]); + + let oracle_input = OracleInput::RegisterIndex(RegisterIndex(0)); + let oracle_output = + OracleOutput::Array { start: RegisterIndex(3), length: 2 }; + + let mut oracle_data = OracleData { + name: "get_notes".to_owned(), + inputs: vec![oracle_input], + input_values: vec![], + outputs: vec![oracle_output], + output_values: vec![], + }; + + let oracle_opcode = Opcode::Oracle(oracle_data.clone()); + + let initial_memory = vec![]; + + let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode], None); + + let output_state = vm.process_opcodes(); + assert_eq!(output_state.status, VMStatus::OracleWait); + + let input_values = output_state.map_input_values(&oracle_data); + + oracle_data.input_values = input_values; + oracle_data.output_values = vec![FieldElement::from(10_u128), FieldElement::from(2_u128)]; + let updated_oracle_opcode = Opcode::Oracle(oracle_data); + + let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode], None,); + let output_state = vm.process_opcodes(); + assert_eq!(output_state.status, VMStatus::Halted); + + let mem_array = output_state.memory[&Value::from(5u128)].clone(); + assert_eq!(mem_array.array[0], Value::from(10_u128)); + assert_eq!(mem_array.array[1], Value::from(2_u128)); + } + + #[test] + fn oracle_array_input() { + use crate::opcodes::OracleInput; + + let input_registers = Registers::load(vec![ + Value::from(2), + Value::from(2), + Value::from(0), + Value::from(5), + Value::from(0), + Value::from(6), + Value::from(0), + ]); + + let expected_length = 2; + let oracle_input = OracleInput::Array { + start: RegisterIndex(3), + length: expected_length, + }; + let oracle_output = OracleOutput::RegisterIndex(RegisterIndex(6)); + + let mut oracle_data = OracleData { + name: "call_private_function_oracle".to_owned(), + inputs: vec![oracle_input.clone()], + input_values: vec![], + outputs: vec![oracle_output], + output_values: vec![], + }; + + let oracle_opcode = Opcode::Oracle(oracle_data.clone()); + + let mut initial_memory = vec![]; + let initial_heap = HeapArray { + array: vec![Value::from(5), Value::from(6)], + }; + initial_memory.insert(Value::from(5), initial_heap); + + let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode], None,); + + let output_state = vm.process_opcodes(); + assert_eq!(output_state.status, VMStatus::OracleWait); + + let input_values = output_state.map_input_values(&oracle_data); + assert_eq!(input_values.len(), expected_length); + + oracle_data.input_values = input_values; + oracle_data.output_values = vec![FieldElement::from(5u128)]; + let updated_oracle_opcode = Opcode::Oracle(oracle_data); + + let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode], None,); + let output_state = vm.process_opcodes(); + assert_eq!(output_state.status, VMStatus::Halted); + + let mem_array = output_state.memory[&Value::from(5)].clone(); + assert_eq!(mem_array.array[0], Value::from(5)); + assert_eq!(mem_array.array[1], Value::from(6)); + } } From 2eafb9443e493cd82f71a79e5f20d4759f5e4393 Mon Sep 17 00:00:00 2001 From: ludamad Date: Wed, 10 May 2023 10:20:31 -0400 Subject: [PATCH 056/125] comment non-working oracle tests --- brillig_bytecode/src/lib.rs | 216 ++++++++++++++++++------------------ 1 file changed, 108 insertions(+), 108 deletions(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index f5f28ad66..9167b7f88 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -770,7 +770,7 @@ mod tests { bit_size: 32, }, // call recursive_fn - Opcode::Jump { + Opcode::Call { location: start.len(), }, Opcode::Return {} @@ -811,111 +811,111 @@ mod tests { } vm } - #[test] - fn oracle_array_output() { - use crate::opcodes::OracleInput; - - let input_registers = Registers::load(vec![ - Value::from(2), - Value::from(2), - Value::from(0), - Value::from(5), - Value::from(0), - Value::from(6), - Value::from(0), - ]); - - let oracle_input = OracleInput::RegisterIndex(RegisterIndex(0)); - let oracle_output = - OracleOutput::Array { start: RegisterIndex(3), length: 2 }; - - let mut oracle_data = OracleData { - name: "get_notes".to_owned(), - inputs: vec![oracle_input], - input_values: vec![], - outputs: vec![oracle_output], - output_values: vec![], - }; - - let oracle_opcode = Opcode::Oracle(oracle_data.clone()); - - let initial_memory = vec![]; - - let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode], None); - - let output_state = vm.process_opcodes(); - assert_eq!(output_state.status, VMStatus::OracleWait); - - let input_values = output_state.map_input_values(&oracle_data); - - oracle_data.input_values = input_values; - oracle_data.output_values = vec![FieldElement::from(10_u128), FieldElement::from(2_u128)]; - let updated_oracle_opcode = Opcode::Oracle(oracle_data); - - let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode], None,); - let output_state = vm.process_opcodes(); - assert_eq!(output_state.status, VMStatus::Halted); - - let mem_array = output_state.memory[&Value::from(5u128)].clone(); - assert_eq!(mem_array.array[0], Value::from(10_u128)); - assert_eq!(mem_array.array[1], Value::from(2_u128)); - } - - #[test] - fn oracle_array_input() { - use crate::opcodes::OracleInput; - - let input_registers = Registers::load(vec![ - Value::from(2), - Value::from(2), - Value::from(0), - Value::from(5), - Value::from(0), - Value::from(6), - Value::from(0), - ]); - - let expected_length = 2; - let oracle_input = OracleInput::Array { - start: RegisterIndex(3), - length: expected_length, - }; - let oracle_output = OracleOutput::RegisterIndex(RegisterIndex(6)); - - let mut oracle_data = OracleData { - name: "call_private_function_oracle".to_owned(), - inputs: vec![oracle_input.clone()], - input_values: vec![], - outputs: vec![oracle_output], - output_values: vec![], - }; - - let oracle_opcode = Opcode::Oracle(oracle_data.clone()); - - let mut initial_memory = vec![]; - let initial_heap = HeapArray { - array: vec![Value::from(5), Value::from(6)], - }; - initial_memory.insert(Value::from(5), initial_heap); - - let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode], None,); - - let output_state = vm.process_opcodes(); - assert_eq!(output_state.status, VMStatus::OracleWait); - - let input_values = output_state.map_input_values(&oracle_data); - assert_eq!(input_values.len(), expected_length); - - oracle_data.input_values = input_values; - oracle_data.output_values = vec![FieldElement::from(5u128)]; - let updated_oracle_opcode = Opcode::Oracle(oracle_data); - - let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode], None,); - let output_state = vm.process_opcodes(); - assert_eq!(output_state.status, VMStatus::Halted); - - let mem_array = output_state.memory[&Value::from(5)].clone(); - assert_eq!(mem_array.array[0], Value::from(5)); - assert_eq!(mem_array.array[1], Value::from(6)); - } + // #[test] + // fn oracle_array_output() { + // use crate::opcodes::OracleInput; + + // let input_registers = Registers::load(vec![ + // Value::from(2), + // Value::from(2), + // Value::from(0), + // Value::from(5), + // Value::from(0), + // Value::from(6), + // Value::from(0), + // ]); + + // let oracle_input = OracleInput::RegisterIndex(RegisterIndex(0)); + // let oracle_output = + // OracleOutput::Array { start: RegisterIndex(3), length: 2 }; + + // let mut oracle_data = OracleData { + // name: "get_notes".to_owned(), + // inputs: vec![oracle_input], + // input_values: vec![], + // outputs: vec![oracle_output], + // output_values: vec![], + // }; + + // let oracle_opcode = Opcode::Oracle(oracle_data.clone()); + + // let initial_memory = vec![]; + + // let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode], None); + + // let output_state = vm.process_opcodes(); + // assert_eq!(output_state.status, VMStatus::OracleWait); + + // let input_values = output_state.map_input_values(&oracle_data); + + // oracle_data.input_values = input_values; + // oracle_data.output_values = vec![FieldElement::from(10_u128), FieldElement::from(2_u128)]; + // let updated_oracle_opcode = Opcode::Oracle(oracle_data); + + // let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode], None,); + // let output_state = vm.process_opcodes(); + // assert_eq!(output_state.status, VMStatus::Halted); + + // let mem_array = output_state.memory[&Value::from(5u128)].clone(); + // assert_eq!(mem_array.array[0], Value::from(10_u128)); + // assert_eq!(mem_array.array[1], Value::from(2_u128)); + // } + + // #[test] + // fn oracle_array_input() { + // use crate::opcodes::OracleInput; + + // let input_registers = Registers::load(vec![ + // Value::from(2), + // Value::from(2), + // Value::from(0), + // Value::from(5), + // Value::from(0), + // Value::from(6), + // Value::from(0), + // ]); + + // let expected_length = 2; + // let oracle_input = OracleInput::Array { + // start: RegisterIndex(3), + // length: expected_length, + // }; + // let oracle_output = OracleOutput::RegisterIndex(RegisterIndex(6)); + + // let mut oracle_data = OracleData { + // name: "call_private_function_oracle".to_owned(), + // inputs: vec![oracle_input.clone()], + // input_values: vec![], + // outputs: vec![oracle_output], + // output_values: vec![], + // }; + + // let oracle_opcode = Opcode::Oracle(oracle_data.clone()); + + // let mut initial_memory = vec![]; + // let initial_heap = HeapArray { + // array: vec![Value::from(5), Value::from(6)], + // }; + // initial_memory.insert(Value::from(5), initial_heap); + + // let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode], None,); + + // let output_state = vm.process_opcodes(); + // assert_eq!(output_state.status, VMStatus::OracleWait); + + // let input_values = output_state.map_input_values(&oracle_data); + // assert_eq!(input_values.len(), expected_length); + + // oracle_data.input_values = input_values; + // oracle_data.output_values = vec![FieldElement::from(5u128)]; + // let updated_oracle_opcode = Opcode::Oracle(oracle_data); + + // let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode], None,); + // let output_state = vm.process_opcodes(); + // assert_eq!(output_state.status, VMStatus::Halted); + + // let mem_array = output_state.memory[&Value::from(5)].clone(); + // assert_eq!(mem_array.array[0], Value::from(5)); + // assert_eq!(mem_array.array[1], Value::from(6)); + // } } From 2753229b4c43b1b16536ba6045b63155c1b39dfc Mon Sep 17 00:00:00 2001 From: ludamad Date: Wed, 10 May 2023 12:16:06 -0400 Subject: [PATCH 057/125] Clippy and test fixes --- acvm/src/pwg/brillig.rs | 6 +- brillig_bytecode/src/lib.rs | 277 ++++++++++++++++---------------- brillig_bytecode/src/opcodes.rs | 7 +- 3 files changed, 145 insertions(+), 145 deletions(-) diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index 3c415c248..9d458aec2 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -68,7 +68,7 @@ impl BrilligSolver { BrilligInputs::Array(id, expr_arr) => { // Attempt to fetch all array input values let mut continue_eval = true; - for expr in expr_arr.into_iter() { + for expr in expr_arr.iter() { let solve = ArithmeticSolver::evaluate(expr, initial_witness); if let Some(value) = solve.to_const() { input_memory.push(value.into()); @@ -111,7 +111,7 @@ impl BrilligSolver { let vm_output = vm.process_opcodes(); let result = match vm_output.status { - VMStatus::Halted => { + VMStatus::Finished => { for (output, register_value) in brillig.outputs.iter().zip(vm_output.registers) { match output { BrilligOutputs::Simple(witness) => { @@ -147,7 +147,7 @@ impl BrilligSolver { data.input_values = input_values; OpcodeResolution::InProgressBrillig(OracleWaitInfo { - data: data.clone(), + data, program_counter, }) } diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 9167b7f88..6505802cd 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -19,7 +19,7 @@ pub use value::Value; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum VMStatus { - Halted, + Finished, InProgress, Failure, OracleWait, @@ -53,46 +53,48 @@ impl VM { let mut registers_modified = Registers::load(vec![Value::from(0u128)]); - for i in 0..register_allocation_indices.len() { - let register_index = register_allocation_indices[i]; + for (i, register_index) in register_allocation_indices.iter().enumerate() { let register_value = inputs.get(RegisterIndex(i)); - registers_modified.set(RegisterIndex(register_index as usize), register_value) + registers_modified.set(RegisterIndex(*register_index as usize), register_value) } inputs = registers_modified; } - let vm = Self { + + Self { registers: inputs, program_counter: 0, bytecode, status: VMStatus::InProgress, memory, call_stack: Vec::new(), - }; - vm + } } fn status(&mut self, status: VMStatus) -> VMStatus { self.status = status; status } - fn halt(&mut self) -> VMStatus { - self.status(VMStatus::Halted) + fn finish(&mut self) -> VMStatus { + self.status(VMStatus::Finished) } fn wait(&mut self) -> VMStatus { self.status(VMStatus::OracleWait) } - fn fail(&mut self) -> VMStatus { - self.status(VMStatus::Failure) + fn fail(&mut self, error_msg: &str) -> VMStatus { + self.status(VMStatus::Failure); + // TODO(AD): Proper error handling + println!("Brillig error: {}", error_msg); + VMStatus::Failure } /// Loop over the bytecode and update the program counter pub fn process_opcodes(mut self) -> VMOutputState { while !matches!( self.process_opcode(), - VMStatus::Halted | VMStatus::Failure | VMStatus::OracleWait + VMStatus::Finished | VMStatus::Failure | VMStatus::OracleWait ) {} - self.finish() + self.output_final_state() } // Process a single opcode and modify the program counter pub fn process_opcode(&mut self) -> VMStatus { @@ -131,7 +133,7 @@ impl VM { .expect("register does not fit into usize"); self.set_program_counter(label) } else { - return self.halt(); + self.fail("return opcode hit, but callstack already empty") } } Opcode::Intrinsics => todo!(), @@ -164,8 +166,8 @@ impl VM { self.registers.set(*destination_register, source_value); self.increment_program_counter() } - Opcode::Trap => self.fail(), - Opcode::Stop => self.halt(), + Opcode::Trap => self.fail("explicit trap hit in brillig"), + Opcode::Stop => self.finish(), Opcode::Load { destination: destination_register, source_pointer } => { // Convert our source_pointer to a usize let source = self.get(source_pointer); @@ -219,7 +221,7 @@ impl VM { assert!(self.program_counter < self.bytecode.len()); self.program_counter = value; if self.program_counter >= self.bytecode.len() { - self.status = VMStatus::Halted; + self.status = VMStatus::Finished; } self.status } @@ -262,7 +264,7 @@ impl VM { /// Returns the state of the registers. /// This consumes ownership of the VM and is conventionally /// called when all of the bytecode has been processed. - fn finish(self) -> VMOutputState { + fn output_final_state(self) -> VMOutputState { VMOutputState { registers: self.registers, program_counter: self.program_counter, @@ -272,6 +274,7 @@ impl VM { } } + pub struct VMOutputState { pub registers: Registers, pub program_counter: usize, @@ -321,13 +324,13 @@ mod tests { // Process a single VM opcode // // After processing a single opcode, we should have - // the vm status as halted since there is only one opcode + // the vm status as finished since there is only one opcode let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Halted); + assert_eq!(status, VMStatus::Finished); // The register at index `2` should have the value of 3 since we had an // add opcode - let VMOutputState { registers, .. } = vm.finish(); + let VMOutputState { registers, .. } = vm.output_final_state(); let output_value = registers.get(RegisterIndex(2)); assert_eq!(output_value, Value::from(3u128)) @@ -384,9 +387,9 @@ mod tests { assert_eq!(status, VMStatus::InProgress); let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Halted); + assert_eq!(status, VMStatus::Finished); - vm.finish(); + vm.output_final_state(); } #[test] @@ -440,7 +443,7 @@ mod tests { assert_eq!(status, VMStatus::Failure); // The register at index `2` should have not changed as we jumped over the add opcode - let VMOutputState { registers, .. } = vm.finish(); + let VMOutputState { registers, .. } = vm.output_final_state(); let output_value = registers.get(RegisterIndex(2)); assert_eq!(output_value, Value::from(false)); } @@ -458,9 +461,9 @@ mod tests { let mut vm = VM::new(input_registers, vec![], vec![mov_opcode], None); let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Halted); + assert_eq!(status, VMStatus::Finished); - let VMOutputState { registers, .. } = vm.finish(); + let VMOutputState { registers, .. } = vm.output_final_state(); let destination_value = registers.get(RegisterIndex(2)); assert_eq!(destination_value, Value::from(1u128)); @@ -537,12 +540,12 @@ mod tests { assert_eq!(lt_value, Value::from(true)); let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Halted); + assert_eq!(status, VMStatus::Finished); let lte_value = vm.registers.get(RegisterIndex(2)); assert_eq!(lte_value, Value::from(true)); - vm.finish(); + vm.output_final_state(); } #[test] fn store_opcode() { @@ -603,7 +606,7 @@ mod tests { }, ]; let vm = brillig_execute(memory, [&start[..], &loop_body[..]].concat()); - vm.memory.clone() + vm.memory } let memory = brillig_write_memory(vec![Value::from(0u128); 5]); @@ -735,6 +738,14 @@ mod tests { destination: r_len, value: FieldElement::from(memory.len() as u128), }, + // call recursive_fn + Opcode::Call { + location: 4, // Call after 'start' + }, + // end program by jumping to end + Opcode::Jump { + location: 100, + }, ]; let recursive_fn = [ @@ -777,7 +788,7 @@ mod tests { ]; let vm = brillig_execute(memory, [&start[..], &recursive_fn[..]].concat()); - vm.memory.clone() + vm.memory } let memory = brillig_recursive_write_memory(vec![Value::from(0u128); 5]); @@ -795,127 +806,115 @@ mod tests { assert_eq!(memory, expected); } + fn empty_registers() -> Registers { + Registers::load(vec![Value::from(0u128); 16]) + } /// Helper to execute brillig code fn brillig_execute(memory: Vec, opcodes: Vec) -> VM { let mut vm = VM::new( - Registers { inner: vec![Value::from(0u128); 16]}, + empty_registers(), memory, opcodes, None, ); loop { let status = vm.process_opcode(); - if status == VMStatus::Halted { + if status == VMStatus::Finished || status == VMStatus::OracleWait { break; } + assert_eq!(status, VMStatus::InProgress) } + assert_eq!(vm.call_stack, vec![]); vm } - // #[test] - // fn oracle_array_output() { - // use crate::opcodes::OracleInput; - - // let input_registers = Registers::load(vec![ - // Value::from(2), - // Value::from(2), - // Value::from(0), - // Value::from(5), - // Value::from(0), - // Value::from(6), - // Value::from(0), - // ]); - - // let oracle_input = OracleInput::RegisterIndex(RegisterIndex(0)); - // let oracle_output = - // OracleOutput::Array { start: RegisterIndex(3), length: 2 }; - - // let mut oracle_data = OracleData { - // name: "get_notes".to_owned(), - // inputs: vec![oracle_input], - // input_values: vec![], - // outputs: vec![oracle_output], - // output_values: vec![], - // }; - - // let oracle_opcode = Opcode::Oracle(oracle_data.clone()); - - // let initial_memory = vec![]; - - // let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode], None); - - // let output_state = vm.process_opcodes(); - // assert_eq!(output_state.status, VMStatus::OracleWait); - - // let input_values = output_state.map_input_values(&oracle_data); - - // oracle_data.input_values = input_values; - // oracle_data.output_values = vec![FieldElement::from(10_u128), FieldElement::from(2_u128)]; - // let updated_oracle_opcode = Opcode::Oracle(oracle_data); - - // let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode], None,); - // let output_state = vm.process_opcodes(); - // assert_eq!(output_state.status, VMStatus::Halted); - - // let mem_array = output_state.memory[&Value::from(5u128)].clone(); - // assert_eq!(mem_array.array[0], Value::from(10_u128)); - // assert_eq!(mem_array.array[1], Value::from(2_u128)); - // } - - // #[test] - // fn oracle_array_input() { - // use crate::opcodes::OracleInput; - - // let input_registers = Registers::load(vec![ - // Value::from(2), - // Value::from(2), - // Value::from(0), - // Value::from(5), - // Value::from(0), - // Value::from(6), - // Value::from(0), - // ]); - - // let expected_length = 2; - // let oracle_input = OracleInput::Array { - // start: RegisterIndex(3), - // length: expected_length, - // }; - // let oracle_output = OracleOutput::RegisterIndex(RegisterIndex(6)); - - // let mut oracle_data = OracleData { - // name: "call_private_function_oracle".to_owned(), - // inputs: vec![oracle_input.clone()], - // input_values: vec![], - // outputs: vec![oracle_output], - // output_values: vec![], - // }; - - // let oracle_opcode = Opcode::Oracle(oracle_data.clone()); - - // let mut initial_memory = vec![]; - // let initial_heap = HeapArray { - // array: vec![Value::from(5), Value::from(6)], - // }; - // initial_memory.insert(Value::from(5), initial_heap); - - // let vm = VM::new(input_registers.clone(), initial_memory, vec![oracle_opcode], None,); - - // let output_state = vm.process_opcodes(); - // assert_eq!(output_state.status, VMStatus::OracleWait); - - // let input_values = output_state.map_input_values(&oracle_data); - // assert_eq!(input_values.len(), expected_length); - - // oracle_data.input_values = input_values; - // oracle_data.output_values = vec![FieldElement::from(5u128)]; - // let updated_oracle_opcode = Opcode::Oracle(oracle_data); - - // let vm = VM::new(input_registers, output_state.memory, vec![updated_oracle_opcode], None,); - // let output_state = vm.process_opcodes(); - // assert_eq!(output_state.status, VMStatus::Halted); - - // let mem_array = output_state.memory[&Value::from(5)].clone(); - // assert_eq!(mem_array.array[0], Value::from(5)); - // assert_eq!(mem_array.array[1], Value::from(6)); - // } + + #[test] + fn oracle_output() { + use crate::opcodes::OracleInput; + + let oracle_inputs = vec![OracleInput::RegisterIndex(RegisterIndex(0))]; + let oracle_outputs = vec![ + OracleOutput::RegisterIndex(RegisterIndex(3)), + OracleOutput::RegisterIndex(RegisterIndex(4)), + ]; + + let mut oracle_data = OracleData { + name: "get_notes".to_owned(), + inputs: oracle_inputs, + input_values: vec![], + outputs: oracle_outputs, + output_values: vec![], + }; + + let oracle_opcode = Opcode::Oracle(oracle_data.clone()); + + let vm = VM::new(empty_registers(), vec![], vec![oracle_opcode], None); + + let output_state = vm.process_opcodes(); + assert_eq!(output_state.status, VMStatus::OracleWait); + + let input_values = output_state.map_input_values(&oracle_data); + + oracle_data.input_values = input_values; + oracle_data.output_values = vec![FieldElement::from(10_u128), FieldElement::from(2_u128)]; + let updated_oracle_opcode = Opcode::Oracle(oracle_data); + + let vm = VM::new(empty_registers(), output_state.memory, vec![updated_oracle_opcode], None,); + let output_state = vm.process_opcodes(); + assert_eq!(output_state.status, VMStatus::Finished); + + assert_eq!(output_state.registers.get(RegisterIndex(3)), Value::from(10_u128)); + assert_eq!(output_state.registers.get(RegisterIndex(4)), Value::from(2_u128)); + } + + #[test] + fn oracle_input() { + use crate::opcodes::OracleInput; + + let oracle_outputs = vec![ + OracleOutput::RegisterIndex(RegisterIndex(1)), + OracleOutput::RegisterIndex(RegisterIndex(2)), + ]; + let oracle_inputs = vec![OracleInput::RegisterIndex(RegisterIndex(0))]; + + let mut oracle_data = OracleData { + name: "call_private_function_oracle".to_owned(), + inputs: oracle_inputs, + input_values: vec![], + outputs: oracle_outputs, + output_values: vec![], + }; + + let program = vec![ + Opcode::Const { + destination: RegisterIndex(1), + value: 0u128.into(), + }, + Opcode::Const { + destination: RegisterIndex(2), + value: 0u128.into(), + }, + Opcode::Oracle(oracle_data.clone()) + ]; + let vm = VM::new(empty_registers(), vec![], program.clone(), None,); + + let output_state = vm.process_opcodes(); + assert_eq!(output_state.status, VMStatus::OracleWait); + + let input_values = output_state.map_input_values(&oracle_data); + assert_eq!(input_values.len(), oracle_data.inputs.len()); + + // Update oracle_data + oracle_data.input_values = input_values; + oracle_data.output_values = vec![FieldElement::from(5u128), FieldElement::from(6u128)]; + let mut updated_program = program; + updated_program[2] = Opcode::Oracle(oracle_data); + + let vm = VM::new(empty_registers(), vec![], updated_program, None,); + let output_state = vm.process_opcodes(); + assert_eq!(output_state.status, VMStatus::Finished); + + assert_eq!(output_state.registers.get(RegisterIndex(1)), Value::from(5u128)); + assert_eq!(output_state.registers.get(RegisterIndex(2)), Value::from(6u128)); + } } diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index ce8774ecd..0507b169d 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -187,9 +187,9 @@ impl BinaryIntOp { BinaryIntOp::SignedDiv => to_unsigned(to_signed(a, bit_size) / to_signed(b, bit_size), bit_size), // Perform a comparison between a and b based on the Comparison variant. BinaryIntOp::Cmp(comparison) => match comparison { - Comparison::Eq => ((a == b) as u128).into(), - Comparison::Lt => ((a < b) as u128).into(), - Comparison::Lte => ((a <= b) as u128).into(), + Comparison::Eq => (a == b) as u128, + Comparison::Lt => (a < b) as u128, + Comparison::Lte => (a <= b) as u128, }, // Perform bitwise AND, OR, XOR, left shift, and right shift operations, applying a modulo operation to keep the result within the bit size. BinaryIntOp::And => { @@ -223,6 +223,7 @@ fn to_signed(a: u128, n: u32) -> i128 { fn to_unsigned(a: i128, n: u32) -> u128 { if n >= 126 { + // TODO(AD): clean this up a bit - this is only converted to a field later, error there? panic!("ICE: cannot convert signed {n} bit size into field"); } if a >= 0 { From 2fab89a816ac2aed5f6d9276e028defb9925501f Mon Sep 17 00:00:00 2001 From: ludamad Date: Tue, 16 May 2023 23:34:35 -0400 Subject: [PATCH 058/125] Rework Brillig oracles, rename ForeignCall --- acir/src/circuit/opcodes.rs | 115 +---------- acvm/src/lib.rs | 273 ++++++++++++------------- acvm/src/pwg/brillig.rs | 62 +++--- brillig_bytecode/src/lib.rs | 346 ++++++++++++++++---------------- brillig_bytecode/src/memory.rs | 13 -- brillig_bytecode/src/opcodes.rs | 48 ++--- brillig_bytecode/src/value.rs | 6 +- 7 files changed, 369 insertions(+), 494 deletions(-) delete mode 100644 brillig_bytecode/src/memory.rs diff --git a/acir/src/circuit/opcodes.rs b/acir/src/circuit/opcodes.rs index 233ff3922..09a9f5511 100644 --- a/acir/src/circuit/opcodes.rs +++ b/acir/src/circuit/opcodes.rs @@ -7,21 +7,13 @@ use crate::serialization::{ }; use crate::BlackBoxFunc; use acir_field::FieldElement; +use brillig_bytecode::ForeignCallResult; use serde::{Deserialize, Serialize}; #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] pub enum BrilligInputs { Simple(Expression), - Array(u32, Vec), -} - -impl BrilligInputs { - fn to_u16(&self) -> u16 { - match self { - BrilligInputs::Simple(_) => 0, - BrilligInputs::Array { .. } => 1, - } - } + Array(Vec), } #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] @@ -30,19 +22,12 @@ pub enum BrilligOutputs { Array(Vec), } -impl BrilligOutputs { - fn to_u16(&self) -> u16 { - match self { - BrilligOutputs::Simple(_) => 0, - BrilligOutputs::Array(_) => 1, - } - } -} - #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] pub struct Brillig { pub inputs: Vec, pub outputs: Vec, + // results of oracles/functions external to brillig like a database read + pub foreign_call_results: Vec, pub bytecode: Vec, /// Predicate of the Brillig execution - indicates if it should be skipped pub predicate: Option, @@ -50,105 +35,19 @@ pub struct Brillig { impl Brillig { pub fn write(&self, mut writer: W) -> std::io::Result<()> { - let inputs_len = self.inputs.len() as u32; - write_u32(&mut writer, inputs_len)?; - for input in &self.inputs { - write_u16(&mut writer, input.to_u16())?; - match input { - BrilligInputs::Simple(expr) => expr.write(&mut writer)?, - BrilligInputs::Array(id, expr_arr) => { - write_u32(&mut writer, *id)?; - write_u32(&mut writer, expr_arr.len() as u32)?; - for expr in expr_arr { - expr.write(&mut writer)?; - } - } - } - } - - let outputs_len = self.outputs.len() as u32; - write_u32(&mut writer, outputs_len)?; - for output in &self.outputs { - write_u16(&mut writer, output.to_u16())?; - match output { - BrilligOutputs::Simple(witness) => { - write_u32(&mut writer, witness.witness_index())?; - } - BrilligOutputs::Array(witness_arr) => { - write_u32(&mut writer, witness_arr.len() as u32)?; - for w in witness_arr { - write_u32(&mut writer, w.witness_index())?; - } - } - } - } - // TODO: We use rmp_serde as its easier than doing it manually - let buffer = rmp_serde::to_vec(&self.bytecode).unwrap(); + let buffer = rmp_serde::to_vec(self).unwrap(); write_u32(&mut writer, buffer.len() as u32)?; write_bytes(&mut writer, &buffer)?; - - let predicate_is_some = vec![self.predicate.is_some() as u8]; - write_bytes(&mut writer, &predicate_is_some)?; - - if let Some(pred) = &self.predicate { - pred.write(&mut writer)?; - } - Ok(()) } pub fn read(mut reader: R) -> std::io::Result { - let inputs_len = read_u32(&mut reader)?; - let mut inputs = Vec::with_capacity(inputs_len as usize); - for _ in 0..inputs_len { - let input_type = read_u16(&mut reader)?; - match input_type { - 0 => inputs.push(BrilligInputs::Simple(Expression::read(&mut reader)?)), - 1 => { - let arr_id = read_u32(&mut reader)?; - let arr_len = read_u32(&mut reader)?; - let mut arr_inputs = Vec::with_capacity(arr_len as usize); - for _ in 0..arr_len { - arr_inputs.push(Expression::read(&mut reader)?) - } - inputs.push(BrilligInputs::Array(arr_id, arr_inputs)) - } - _ => return Err(std::io::ErrorKind::InvalidData.into()), - } - } - - let outputs_len = read_u32(&mut reader)?; - let mut outputs = Vec::with_capacity(outputs_len as usize); - for _ in 0..outputs_len { - let output_type = read_u16(&mut reader)?; - match output_type { - 0 => outputs.push(BrilligOutputs::Simple(Witness(read_u32(&mut reader)?))), - 1 => { - let witness_arr_len = read_u32(&mut reader)?; - let mut witness_arr = Vec::with_capacity(witness_arr_len as usize); - for _ in 0..witness_arr_len { - witness_arr.push(Witness(read_u32(&mut reader)?)) - } - outputs.push(BrilligOutputs::Array(witness_arr)) - } - _ => return Err(std::io::ErrorKind::InvalidData.into()), - } - } - let buffer_len = read_u32(&mut reader)?; let mut buffer = vec![0u8; buffer_len as usize]; reader.read_exact(&mut buffer)?; - let opcodes: Vec = rmp_serde::from_slice(&buffer).unwrap(); - - // Read byte to figure out if there is a predicate - let predicate_is_some = read_n::<1, _>(&mut reader)?[0] != 0; - let predicate = match predicate_is_some { - true => Some(Expression::read(&mut reader)?), - false => None, - }; - - Ok(Brillig { inputs, outputs, bytecode: opcodes, predicate }) + let decoded_brillig: Self = rmp_serde::from_slice(&buffer).unwrap(); + Ok(decoded_brillig) } } diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 246044f19..688afc189 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -63,7 +63,7 @@ pub enum OpcodeResolution { /// The opcode is not solvable but could resolved some witness InProgress, /// The brillig oracle opcode is not solved but could be resolved given some values - InProgressBrillig(brillig::OracleWaitInfo), + InProgressBrillig(brillig::ForeignCallWaitInfo), } pub trait Backend: SmartContract + ProofSystemCompiler + PartialWitnessGenerator {} @@ -141,7 +141,7 @@ pub trait PartialWitnessGenerator { Opcode::Brillig(brillig) => brillig.clone(), _ => unreachable!("Brillig resolution for non brillig opcode"), }; - unresolved_brilligs.push(UnresolvedBrillig { brillig, oracle_wait_info }) + unresolved_brilligs.push(UnresolvedBrillig { brillig, foreign_call_wait_info: oracle_wait_info }) } Ok(OpcodeResolution::Stalled(not_solvable)) => { if opcode_not_solvable.is_none() { @@ -225,7 +225,8 @@ pub trait PartialWitnessGenerator { pub struct UnresolvedBrillig { pub brillig: Brillig, - pub oracle_wait_info: brillig::OracleWaitInfo, + // information for if there is a pending foreign call/oracle + pub foreign_call_wait_info: brillig::ForeignCallWaitInfo, } pub struct UnresolvedData { @@ -358,7 +359,7 @@ mod test { use acir::{ brillig_bytecode, brillig_bytecode::{ - Comparison, OracleInput, OracleOutput, RegisterIndex, BinaryFieldOp, + Comparison, RegisterIndex, BinaryFieldOp, RegisterValueOrArray, ForeignCallResult, Value, }, circuit::{ directives::Directive, @@ -473,34 +474,7 @@ mod test { let w_equal_res = Witness(7); let w_lt_res = Witness(8); - let equal_opcode = brillig_bytecode::Opcode::BinaryFieldOp { - op: brillig_bytecode::BinaryFieldOp::Cmp(Comparison::Eq), - lhs: RegisterIndex(0), - rhs: RegisterIndex(1), - destination: RegisterIndex(2), - }; - - let less_than_opcode = brillig_bytecode::Opcode::BinaryFieldOp { - op: brillig_bytecode::BinaryFieldOp::Cmp(Comparison::Lt), - lhs: RegisterIndex(0), - rhs: RegisterIndex(1), - destination: RegisterIndex(3), - }; - - let invert_oracle_input = OracleInput::RegisterIndex(RegisterIndex(0)); - let invert_oracle_output = OracleOutput::RegisterIndex(RegisterIndex(1)); - - let invert_oracle = brillig_bytecode::Opcode::Oracle(brillig_bytecode::OracleData { - name: "invert".into(), - inputs: vec![invert_oracle_input], - input_values: vec![], - outputs: vec![invert_oracle_output], - output_values: vec![], - }); - - let brillig_bytecode = vec![equal_opcode, less_than_opcode, invert_oracle]; - - let brillig_opcode = Opcode::Brillig(Brillig { + let brillig_data = Brillig { inputs: vec![ BrilligInputs::Simple(Expression { mul_terms: vec![], @@ -515,12 +489,33 @@ mod test { BrilligOutputs::Simple(w_equal_res), BrilligOutputs::Simple(w_lt_res), ], - bytecode: brillig_bytecode, + // stack of foreign call/oracle resolutions, starts empty + foreign_call_results: vec![], + bytecode: vec![ + brillig_bytecode::Opcode::BinaryFieldOp { + destination: RegisterIndex(2), + lhs: RegisterIndex(0), + op: brillig_bytecode::BinaryFieldOp::Cmp(Comparison::Eq), + rhs: RegisterIndex(1), + }, + brillig_bytecode::Opcode::BinaryFieldOp { + destination: RegisterIndex(3), + lhs: RegisterIndex(0), + op: brillig_bytecode::BinaryFieldOp::Cmp(Comparison::Lt), + rhs: RegisterIndex(1), + }, + // Oracles are named 'foreign calls' in brillig + brillig_bytecode::Opcode::ForeignCall { + function: "invert".into(), + destination: RegisterValueOrArray::RegisterIndex(RegisterIndex(0)), + input: RegisterValueOrArray::RegisterIndex(RegisterIndex(1)), + } + ], predicate: None, - }); + }; let opcodes = vec![ - brillig_opcode, + Opcode::Brillig(brillig_data), Opcode::Arithmetic(Expression { mul_terms: vec![], linear_combinations: vec![(fe_1, w_x), (fe_1, w_y), (-fe_1, w_z)], @@ -546,6 +541,7 @@ mod test { (Witness(2), FieldElement::from(3u128)), ]); let mut blocks = Blocks::default(); + // use the partial witness generation solver with our acir program let UnresolvedData { unresolved_opcodes, mut unresolved_brilligs, .. } = pwg .solve(&mut witness_assignments, &mut blocks, opcodes) .expect("should stall on oracle"); @@ -553,23 +549,18 @@ mod test { assert_eq!(unresolved_opcodes.len(), 0, "brillig should have been removed"); assert_eq!(unresolved_brilligs.len(), 1, "should have a brillig oracle request"); - let UnresolvedBrillig { oracle_wait_info, mut brillig } = unresolved_brilligs.remove(0); - let mut oracle_data = oracle_wait_info.data; - assert_eq!(oracle_data.inputs.len(), 1, "Should have solved a single input"); - - // Fill data request and continue solving - oracle_data.output_values = vec![oracle_data.input_values.last().unwrap().inverse()]; - let invert_oracle = brillig_bytecode::Opcode::Oracle(oracle_data); - + let UnresolvedBrillig { foreign_call_wait_info, mut brillig } = unresolved_brilligs.remove(0); + assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); // Alter Brillig oracle opcode - brillig.bytecode[oracle_wait_info.program_counter] = invert_oracle; - + brillig.foreign_call_results.push(ForeignCallResult { + values: vec![Value::from(foreign_call_wait_info.inputs[0].inner.inverse())] + }); let mut next_opcodes_for_solving = vec![Opcode::Brillig(brillig)]; next_opcodes_for_solving.extend_from_slice(&unresolved_opcodes[..]); - + // After filling data request, continue solving let UnresolvedData { unresolved_opcodes, unresolved_brilligs, .. } = pwg .solve(&mut witness_assignments, &mut blocks, next_opcodes_for_solving) - .expect("should not stall on oracle"); + .expect("should not stall"); assert!(unresolved_opcodes.is_empty(), "should be fully solved"); assert!(unresolved_brilligs.is_empty(), "should have no unresolved oracles"); @@ -577,98 +568,100 @@ mod test { #[test] fn brillig_oracle_predicate() { - // Opcodes below describe the following: - // fn main(x : Field, y : pub Field, cond: bool) { - // let z = x + y; - // let z_inverse = 1/z - // if cond { - // constrain z_inverse == Oracle("inverse", x + y); - // } - // } - let fe_0 = FieldElement::zero(); - let fe_1 = FieldElement::one(); - let w_x = Witness(1); - let w_y = Witness(2); - let w_oracle = Witness(3); - let w_z = Witness(4); - let w_z_inverse = Witness(5); - let w_x_plus_y = Witness(6); - let w_equal_res = Witness(7); - let w_lt_res = Witness(8); - - let equal_opcode = brillig_bytecode::Opcode::BinaryFieldOp { - op: BinaryFieldOp::Cmp(Comparison::Eq), - lhs: RegisterIndex(0), - rhs: RegisterIndex(1), - destination: RegisterIndex(2), - }; - - let less_than_opcode = brillig_bytecode::Opcode::BinaryFieldOp { - op: BinaryFieldOp::Cmp(Comparison::Lt), - lhs: RegisterIndex(0), - rhs: RegisterIndex(1), - destination: RegisterIndex(3), - }; - - let invert_oracle_input = OracleInput::RegisterIndex(RegisterIndex(0)); - let invert_oracle_output = OracleOutput::RegisterIndex(RegisterIndex(1)); - - let invert_oracle = brillig_bytecode::Opcode::Oracle(brillig_bytecode::OracleData { - name: "invert".into(), - inputs: vec![invert_oracle_input], - input_values: vec![], - outputs: vec![invert_oracle_output], - output_values: vec![], - }); - - let brillig_bytecode = vec![equal_opcode, less_than_opcode, invert_oracle]; - - let brillig_opcode = Opcode::Brillig(Brillig { - inputs: vec![ - BrilligInputs::Simple(Expression { - mul_terms: vec![], - linear_combinations: vec![(fe_1, w_x), (fe_1, w_y)], - q_c: fe_0, - }), - BrilligInputs::Simple(Expression::default()), - ], - outputs: vec![ - BrilligOutputs::Simple(w_x_plus_y), - BrilligOutputs::Simple(w_oracle), - BrilligOutputs::Simple(w_equal_res), - BrilligOutputs::Simple(w_lt_res), - ], - bytecode: brillig_bytecode, - predicate: Some(Expression::default()), - }); - - let opcodes = vec![ - brillig_opcode, - Opcode::Arithmetic(Expression { - mul_terms: vec![], - linear_combinations: vec![(fe_1, w_x), (fe_1, w_y), (-fe_1, w_z)], - q_c: fe_0, - }), - Opcode::Directive(Directive::Invert { x: w_z, result: w_z_inverse }), - Opcode::Arithmetic(Expression { - mul_terms: vec![(fe_1, w_z, w_z_inverse)], - linear_combinations: vec![], - q_c: -fe_1, - }), - ]; - - let pwg = StubbedPwg; - - let mut witness_assignments = BTreeMap::from([ - (Witness(1), FieldElement::from(2u128)), - (Witness(2), FieldElement::from(3u128)), - ]); - let mut blocks = Blocks::default(); - let UnresolvedData { unresolved_opcodes, unresolved_brilligs, .. } = pwg - .solve(&mut witness_assignments, &mut blocks, opcodes) - .expect("should stall on oracle"); - - assert!(unresolved_opcodes.is_empty(), "opcode should be removed"); - assert!(unresolved_brilligs.is_empty(), "should have no unresolved oracles"); + // // Opcodes below describe the following: + // // fn main(x : Field, y : pub Field, cond: bool) { + // // let z = x + y; + // // let z_inverse = 1/z + // // if cond { + // // constrain z_inverse == Oracle("inverse", x + y); + // // } + // // } + // let fe_0 = FieldElement::zero(); + // let fe_1 = FieldElement::one(); + // let w_x = Witness(1); + // let w_y = Witness(2); + // let w_oracle = Witness(3); + // let w_z = Witness(4); + // let w_z_inverse = Witness(5); + // let w_x_plus_y = Witness(6); + // let w_equal_res = Witness(7); + // let w_lt_res = Witness(8); + + // let equal_opcode = brillig_bytecode::Opcode::BinaryFieldOp { + // op: BinaryFieldOp::Cmp(Comparison::Eq), + // lhs: RegisterIndex(0), + // rhs: RegisterIndex(1), + // destination: RegisterIndex(2), + // }; + + // let less_than_opcode = brillig_bytecode::Opcode::BinaryFieldOp { + // op: BinaryFieldOp::Cmp(Comparison::Lt), + // lhs: RegisterIndex(0), + // rhs: RegisterIndex(1), + // destination: RegisterIndex(3), + // }; + + // let invert_oracle_input = OracleInput::RegisterIndex(RegisterIndex(0)); + // let invert_oracle_output = OracleOutput::RegisterIndex(RegisterIndex(1)); + + // let invert_oracle = brillig_bytecode::Opcode::Oracle(brillig_bytecode::OracleData { + // name: "invert".into(), + // inputs: vec![invert_oracle_input], + // input_values: vec![], + // outputs: vec![invert_oracle_output], + // output_values: vec![], + // }); + + // let brillig_bytecode = vec![equal_opcode, less_than_opcode, invert_oracle]; + + // let brillig_opcode = Opcode::Brillig(Brillig { + // inputs: vec![ + // BrilligInputs::Simple(Expression { + // mul_terms: vec![], + // linear_combinations: vec![(fe_1, w_x), (fe_1, w_y)], + // q_c: fe_0, + // }), + // BrilligInputs::Simple(Expression::default()), + // ], + // outputs: vec![ + // BrilligOutputs::Simple(w_x_plus_y), + // BrilligOutputs::Simple(w_oracle), + // BrilligOutputs::Simple(w_equal_res), + // BrilligOutputs::Simple(w_lt_res), + // ], + // bytecode: brillig_bytecode, + // predicate: Some(Expression::default()), + // // oracle results + // foreign_call_results: vec![] + // }); + + // let opcodes = vec![ + // brillig_opcode, + // Opcode::Arithmetic(Expression { + // mul_terms: vec![], + // linear_combinations: vec![(fe_1, w_x), (fe_1, w_y), (-fe_1, w_z)], + // q_c: fe_0, + // }), + // Opcode::Directive(Directive::Invert { x: w_z, result: w_z_inverse }), + // Opcode::Arithmetic(Expression { + // mul_terms: vec![(fe_1, w_z, w_z_inverse)], + // linear_combinations: vec![], + // q_c: -fe_1, + // }), + // ]; + + // let pwg = StubbedPwg; + + // let mut witness_assignments = BTreeMap::from([ + // (Witness(1), FieldElement::from(2u128)), + // (Witness(2), FieldElement::from(3u128)), + // ]); + // let mut blocks = Blocks::default(); + // let UnresolvedData { unresolved_opcodes, unresolved_brilligs, .. } = pwg + // .solve(&mut witness_assignments, &mut blocks, opcodes) + // .expect("should stall on oracle"); + + // assert!(unresolved_opcodes.is_empty(), "opcode should be removed"); + // assert!(unresolved_brilligs.is_empty(), "should have no unresolved oracles"); } } diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index 9d458aec2..afdba24d1 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use acir::{ - brillig_bytecode::{Opcode, OracleData, Registers, VMStatus, Value, VM}, + brillig_bytecode::{Registers, VMStatus, Value, VM, RegisterIndex}, circuit::opcodes::{Brillig, BrilligInputs, BrilligOutputs}, native_types::Witness, FieldElement, @@ -13,6 +13,8 @@ use crate::{ use super::{directives::insert_witness, get_value}; +const MIN_BRILLIG_REGISTERS: usize = 16; + pub struct BrilligSolver; impl BrilligSolver { @@ -65,9 +67,10 @@ impl BrilligSolver { break; } } - BrilligInputs::Array(id, expr_arr) => { + BrilligInputs::Array(expr_arr) => { // Attempt to fetch all array input values let mut continue_eval = true; + let memory_pointer = input_memory.len(); for expr in expr_arr.iter() { let solve = ArithmeticSolver::evaluate(expr, initial_witness); if let Some(value) = solve.to_const() { @@ -83,10 +86,8 @@ impl BrilligSolver { break; } - let id_as_value: Value = - Value { inner: FieldElement::from(*id as u128) }; - // Push value of the array id as a register - input_register_values.push(id_as_value); + // Push value of the array pointer as a register + input_register_values.push(Value::from(memory_pointer)); } } } @@ -96,7 +97,7 @@ impl BrilligSolver { brillig.inputs.last().expect("Infallible: cannot reach this point if no inputs"); let expr = match brillig_input { BrilligInputs::Simple(expr) => expr, - BrilligInputs::Array(_, expr_arr) => { + BrilligInputs::Array(expr_arr) => { expr_arr.last().expect("Infallible: cannot reach this point if no inputs") } }; @@ -105,21 +106,28 @@ impl BrilligSolver { ))); } + // Ensure at least MIN_BRILLIG_REGISTERS registers are available + while input_register_values.len() < MIN_BRILLIG_REGISTERS { + input_register_values.push(Value::from(0u128)); + } + let input_registers = Registers { inner: input_register_values }; - let vm = VM::new(input_registers, input_memory, brillig.bytecode.clone(), None); + let mut vm = VM::new(input_registers, input_memory, brillig.bytecode.clone(), brillig.foreign_call_results.clone(), None); - let vm_output = vm.process_opcodes(); + let vm_status = vm.process_opcodes(); - let result = match vm_output.status { + let result = match vm_status { VMStatus::Finished => { - for (output, register_value) in brillig.outputs.iter().zip(vm_output.registers) { + for (i, output) in brillig.outputs.iter().enumerate() { + let register_value = vm.get_registers().get(RegisterIndex(i)); match output { BrilligOutputs::Simple(witness) => { insert_witness(*witness, register_value.inner, initial_witness)?; } BrilligOutputs::Array(witness_arr) => { + // Treat the register value as a pointer to memory for (i, witness) in witness_arr.iter().enumerate() { - let value = &vm_output.memory[register_value.to_usize() + i]; + let value = &vm.get_memory()[register_value.to_usize() + i]; insert_witness(*witness, value.inner, initial_witness)?; } } @@ -129,26 +137,10 @@ impl BrilligSolver { } VMStatus::InProgress => unreachable!("Brillig VM has not completed execution"), VMStatus::Failure => return Err(OpcodeResolutionError::UnsatisfiedConstrain), - VMStatus::OracleWait => { - let program_counter = vm_output.program_counter; - - let current_opcode = &brillig.bytecode[program_counter]; - let mut data = match current_opcode.clone() { - Opcode::Oracle(data) => data, - _ => { - return Err(OpcodeResolutionError::UnexpectedOpcode( - "brillig oracle", - current_opcode.name(), - )) - } - }; - - let input_values = vm_output.map_input_values(&data); - data.input_values = input_values; - - OpcodeResolution::InProgressBrillig(OracleWaitInfo { - data, - program_counter, + VMStatus::ForeignCallWait { function, inputs } => { + OpcodeResolution::InProgressBrillig(ForeignCallWaitInfo { + function, + inputs }) } }; @@ -158,7 +150,7 @@ impl BrilligSolver { } #[derive(Debug, PartialEq, Clone)] -pub struct OracleWaitInfo { - pub data: OracleData, - pub program_counter: usize, +pub struct ForeignCallWaitInfo { + pub function: String, + pub inputs: Vec, } diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 6505802cd..b6e9933c0 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -4,36 +4,43 @@ // This is a generalization over the fixed directives // that we have in ACVM. -mod memory; mod opcodes; mod registers; mod value; -use acir_field::FieldElement; -pub use opcodes::{BinaryFieldOp, BinaryIntOp}; -pub use opcodes::{Comparison, Opcode, OracleData, OracleInput, OracleOutput}; +pub use opcodes::{BinaryFieldOp, BinaryIntOp, RegisterValueOrArray}; +pub use opcodes::{Comparison, Opcode}; pub use registers::{RegisterIndex, Registers}; +use serde::{Serialize, Deserialize}; pub use value::Typ; pub use value::Value; -#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum VMStatus { Finished, InProgress, Failure, - OracleWait, + ForeignCallWait { + // Interpreted by simulator context + function: String, + // Input values + inputs: Vec, + }, } -#[derive(Default, Debug, PartialEq, Eq, Clone)] -pub struct HeapArray { - pub array: Vec, +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)] +pub struct ForeignCallResult { + // resolved foreign call values + pub values: Vec } #[derive(Debug, PartialEq, Eq, Clone)] pub struct VM { registers: Registers, program_counter: usize, + foreign_call_counter: usize, + foreign_call_results: Vec, bytecode: Vec, status: VMStatus, memory: Vec, @@ -45,10 +52,10 @@ impl VM { mut inputs: Registers, memory: Vec, bytecode: Vec, + foreign_call_results: Vec, register_allocation_indices: Option> ) -> VM { if let Some(register_allocation_indices) = register_allocation_indices { - // TODO: might have to handle arrays in bootstrap to be correct // TODO(AD): simplify this all to be done before calling VM.new() let mut registers_modified = Registers::load(vec![Value::from(0u128)]); @@ -57,13 +64,15 @@ impl VM { let register_value = inputs.get(RegisterIndex(i)); registers_modified.set(RegisterIndex(*register_index as usize), register_value) } - + inputs = registers_modified; } Self { registers: inputs, program_counter: 0, + foreign_call_counter: 0, + foreign_call_results, bytecode, status: VMStatus::InProgress, memory, @@ -72,14 +81,18 @@ impl VM { } fn status(&mut self, status: VMStatus) -> VMStatus { - self.status = status; + self.status = status.clone(); status } fn finish(&mut self) -> VMStatus { self.status(VMStatus::Finished) } - fn wait(&mut self) -> VMStatus { - self.status(VMStatus::OracleWait) + /// Waits for a foreign call/oracle + fn wait_for_foreign_call(&mut self, function: String, inputs: Vec) -> VMStatus { + self.status(VMStatus::ForeignCallWait { + function, + inputs + }) } fn fail(&mut self, error_msg: &str) -> VMStatus { self.status(VMStatus::Failure); @@ -89,12 +102,18 @@ impl VM { } /// Loop over the bytecode and update the program counter - pub fn process_opcodes(mut self) -> VMOutputState { + pub fn process_opcodes(&mut self) -> VMStatus { while !matches!( self.process_opcode(), - VMStatus::Finished | VMStatus::Failure | VMStatus::OracleWait + VMStatus::Finished | VMStatus::Failure | VMStatus::ForeignCallWait {..} ) {} - self.output_final_state() + self.status.clone() + } + pub fn get_registers(&self) -> &Registers { + &self.registers + } + pub fn get_memory(&self) -> &Vec { + &self.memory } // Process a single opcode and modify the program counter pub fn process_opcode(&mut self) -> VMStatus { @@ -112,14 +131,14 @@ impl VM { Opcode::JumpIf { condition, location: destination } => { // Check if condition is true // We use 0 to mean false and any other value to mean true - let condition_value = self.get(condition); + let condition_value = self.registers.get(*condition); if !condition_value.is_zero() { return self.set_program_counter(*destination); } self.increment_program_counter() } Opcode::JumpIfNot { condition, location: destination } => { - let condition_value = self.get(condition); + let condition_value = self.registers.get(*condition); if condition_value.is_zero() { return self.set_program_counter(*destination); } @@ -136,33 +155,31 @@ impl VM { self.fail("return opcode hit, but callstack already empty") } } - Opcode::Intrinsics => todo!(), - Opcode::Oracle(data) => { - let mut num_output_values = 0; - for oracle_output in data.clone().outputs { - match oracle_output { - OracleOutput::RegisterIndex(_) => num_output_values += 1, - } + Opcode::ForeignCall {function, destination, input} => { + if self.foreign_call_counter >= self.foreign_call_results.len() { + let resolved_inputs = self.resolve_foreign_call_input(*input); + return self.wait_for_foreign_call(function.clone(), resolved_inputs); } - if num_output_values != data.output_values.len() { - return self.wait(); - } else { - let mut current_value_index = 0; - for oracle_output in data.clone().outputs { - match oracle_output { - OracleOutput::RegisterIndex(index) => { - self.registers - .set(index, data.output_values[current_value_index].into()); - current_value_index += 1 - } + + let ForeignCallResult { values } = &self.foreign_call_results[self.foreign_call_counter]; + match destination { + RegisterValueOrArray::RegisterIndex(index) => { + assert_eq!(values.len(), 1, "Function result size does not match brillig bytecode"); + self.registers.set(*index, values[0]) + }, + RegisterValueOrArray::HeapArray(index, size) => { + let destination_value = self.registers.get(*index); + assert_eq!(values.len(), *size, "Function result size does not match brillig bytecode"); + for (i, value) in values.iter().enumerate() { + self.memory[destination_value.to_usize() + i] = *value; } } } - + self.foreign_call_counter += 1; self.increment_program_counter() } Opcode::Mov { destination: destination_register, source: source_register } => { - let source_value = self.get(source_register); + let source_value = self.registers.get(*source_register); self.registers.set(*destination_register, source_value); self.increment_program_counter() } @@ -170,23 +187,17 @@ impl VM { Opcode::Stop => self.finish(), Opcode::Load { destination: destination_register, source_pointer } => { // Convert our source_pointer to a usize - let source = self.get(source_pointer); - let source_usize = usize::try_from( - source.inner.try_to_u64().expect("register does not fit into u64"), - ).expect("register does not fit into usize"); + let source = self.registers.get(*source_pointer); // Use our usize source index to lookup the value in memory - let value = &self.memory[source_usize]; + let value = &self.memory[source.to_usize()]; self.registers.set(*destination_register, *value); self.increment_program_counter() } Opcode::Store { destination_pointer, source: source_register } => { // Convert our destination_pointer to a usize - let destination = self.get(destination_pointer); - let destination_usize = usize::try_from( - destination.inner.try_to_u64().expect("register does not fit into u64"), - ).expect("register does not fit into usize"); + let destination = self.registers.get(*destination_pointer); // Use our usize destination index to set the value in memory - self.memory[destination_usize] = self.get(source_register); + self.memory[destination.to_usize()] = self.registers.get(*source_register); self.increment_program_counter() } Opcode::Call { location } => { @@ -195,16 +206,12 @@ impl VM { self.set_program_counter(*location) } Opcode::Const { destination, value } => { - self.registers.set(*destination, Value::from(*value)); + self.registers.set(*destination, *value); self.increment_program_counter() } } } - /// Get the value of a register - fn get(&self, register: &RegisterIndex) -> Value { - self.registers.get(*register) - } pub fn program_counter(self) -> usize { self.program_counter } @@ -223,7 +230,7 @@ impl VM { if self.program_counter >= self.bytecode.len() { self.status = VMStatus::Finished; } - self.status + self.status.clone() } /// Process a binary operation. @@ -244,6 +251,16 @@ impl VM { self.registers.set(result, result_value.into()); } + fn resolve_foreign_call_input(&self, input: RegisterValueOrArray) -> Vec { + match input { + RegisterValueOrArray::RegisterIndex(index) => vec![self.registers.get(index)], + RegisterValueOrArray::HeapArray(index, size) => { + let start = self.registers.get(index); + self.memory[start.to_usize()..(start.to_usize() + size)].to_vec() + } + } + } + /// Process a binary operation. /// This method will not modify the program counter. fn process_binary_field_op( @@ -260,43 +277,17 @@ impl VM { self.registers.set(result, result_value.into()) } - - /// Returns the state of the registers. - /// This consumes ownership of the VM and is conventionally - /// called when all of the bytecode has been processed. - fn output_final_state(self) -> VMOutputState { - VMOutputState { - registers: self.registers, - program_counter: self.program_counter, - status: self.status, - memory: self.memory, - } - } } pub struct VMOutputState { pub registers: Registers, pub program_counter: usize, + pub foreign_call_results: Vec, pub status: VMStatus, pub memory: Vec, } -impl VMOutputState { - pub fn map_input_values(&self, oracle_data: &OracleData) -> Vec { - let mut input_values = vec![]; - for oracle_input in &oracle_data.inputs { - match oracle_input { - OracleInput::RegisterIndex(register_index) => { - let register = self.registers.get(*register_index); - input_values.push(register.inner); - } - } - } - input_values - } -} - #[cfg(test)] mod tests { use super::*; @@ -319,7 +310,7 @@ mod tests { }; // Start VM - let mut vm = VM::new(input_registers, vec![], vec![opcode], None); + let mut vm = VM::new(input_registers, vec![], vec![opcode], vec![], None); // Process a single VM opcode // @@ -330,7 +321,7 @@ mod tests { // The register at index `2` should have the value of 3 since we had an // add opcode - let VMOutputState { registers, .. } = vm.output_final_state(); + let VM { registers, .. } = vm; let output_value = registers.get(RegisterIndex(2)); assert_eq!(output_value, Value::from(3u128)) @@ -374,6 +365,7 @@ mod tests { Registers { inner: registers }, vec![], opcodes, + vec![], None, ); @@ -388,8 +380,6 @@ mod tests { let status = vm.process_opcode(); assert_eq!(status, VMStatus::Finished); - - vm.output_final_state(); } #[test] @@ -424,6 +414,7 @@ mod tests { input_registers, vec![], vec![jump_opcode, trap_opcode, not_equal_cmp_opcode, jump_if_not_opcode, add_opcode], + vec![], None ); @@ -443,7 +434,7 @@ mod tests { assert_eq!(status, VMStatus::Failure); // The register at index `2` should have not changed as we jumped over the add opcode - let VMOutputState { registers, .. } = vm.output_final_state(); + let VM { registers, .. } = vm; let output_value = registers.get(RegisterIndex(2)); assert_eq!(output_value, Value::from(false)); } @@ -458,12 +449,12 @@ mod tests { source: RegisterIndex(0), }; - let mut vm = VM::new(input_registers, vec![], vec![mov_opcode], None); + let mut vm = VM::new(input_registers, vec![], vec![mov_opcode], vec![], None); let status = vm.process_opcode(); assert_eq!(status, VMStatus::Finished); - let VMOutputState { registers, .. } = vm.output_final_state(); + let VM { registers, .. } = vm; let destination_value = registers.get(RegisterIndex(2)); assert_eq!(destination_value, Value::from(1u128)); @@ -518,6 +509,7 @@ mod tests { input_registers, vec![], vec![equal_opcode, not_equal_opcode, less_than_opcode, less_than_equal_opcode], + vec![], None, ); @@ -544,8 +536,6 @@ mod tests { let lte_value = vm.registers.get(RegisterIndex(2)); assert_eq!(lte_value, Value::from(true)); - - vm.output_final_state(); } #[test] fn store_opcode() { @@ -569,7 +559,7 @@ mod tests { // len = memory.len() (approximation) Opcode::Const { destination: r_len, - value: FieldElement::from(memory.len() as u128), + value: Value::from(memory.len() as u128), }, ]; let loop_body = [ @@ -605,7 +595,7 @@ mod tests { location: start.len() }, ]; - let vm = brillig_execute(memory, [&start[..], &loop_body[..]].concat()); + let vm = brillig_execute_and_get_vm(memory, [&start[..], &loop_body[..]].concat()); vm.memory } @@ -653,7 +643,7 @@ mod tests { // len = array.len() (approximation) Opcode::Const { destination: r_len, - value: FieldElement::from(memory.len() as u128), + value: Value::from(memory.len() as u128), }, ]; let loop_body = [ @@ -697,7 +687,7 @@ mod tests { location: start.len() }, ]; - let vm = brillig_execute(memory, [&start[..], &loop_body[..]].concat()); + let vm = brillig_execute_and_get_vm(memory, [&start[..], &loop_body[..]].concat()); vm.registers.get(r_sum) } @@ -736,7 +726,7 @@ mod tests { // len = memory.len() (approximation) Opcode::Const { destination: r_len, - value: FieldElement::from(memory.len() as u128), + value: Value::from(memory.len() as u128), }, // call recursive_fn Opcode::Call { @@ -787,7 +777,7 @@ mod tests { Opcode::Return {} ]; - let vm = brillig_execute(memory, [&start[..], &recursive_fn[..]].concat()); + let vm = brillig_execute_and_get_vm(memory, [&start[..], &recursive_fn[..]].concat()); vm.memory } @@ -810,111 +800,129 @@ mod tests { Registers::load(vec![Value::from(0u128); 16]) } /// Helper to execute brillig code - fn brillig_execute(memory: Vec, opcodes: Vec) -> VM { + fn brillig_execute_and_get_vm(memory: Vec, opcodes: Vec) -> VM { let mut vm = VM::new( empty_registers(), memory, opcodes, + vec![], None, ); + brillig_execute(&mut vm); + assert_eq!(vm.call_stack, vec![]); + vm + } + + fn brillig_execute(vm: &mut VM) { loop { let status = vm.process_opcode(); - if status == VMStatus::Finished || status == VMStatus::OracleWait { + if matches!(status, VMStatus::Finished | VMStatus::ForeignCallWait {..}) { break; } assert_eq!(status, VMStatus::InProgress) } - assert_eq!(vm.call_stack, vec![]); - vm } #[test] - fn oracle_output() { - use crate::opcodes::OracleInput; - - let oracle_inputs = vec![OracleInput::RegisterIndex(RegisterIndex(0))]; - let oracle_outputs = vec![ - OracleOutput::RegisterIndex(RegisterIndex(3)), - OracleOutput::RegisterIndex(RegisterIndex(4)), + fn foreign_call_opcode_register_result() { + let r_input = RegisterIndex(0); + let r_result = RegisterIndex(1); + + let double_program = vec![ + // Load input register with value 5 + Opcode::Const { + destination: r_input, + value: Value::from(5u128), + }, + // Call foreign function "double" with the input register + Opcode::ForeignCall { + function: "double".into(), + destination: RegisterValueOrArray::RegisterIndex(r_result), + input: RegisterValueOrArray::RegisterIndex(r_input), + }, ]; + + let mut vm = brillig_execute_and_get_vm(vec![], double_program); - let mut oracle_data = OracleData { - name: "get_notes".to_owned(), - inputs: oracle_inputs, - input_values: vec![], - outputs: oracle_outputs, - output_values: vec![], - }; - - let oracle_opcode = Opcode::Oracle(oracle_data.clone()); - - let vm = VM::new(empty_registers(), vec![], vec![oracle_opcode], None); - - let output_state = vm.process_opcodes(); - assert_eq!(output_state.status, VMStatus::OracleWait); - - let input_values = output_state.map_input_values(&oracle_data); - - oracle_data.input_values = input_values; - oracle_data.output_values = vec![FieldElement::from(10_u128), FieldElement::from(2_u128)]; - let updated_oracle_opcode = Opcode::Oracle(oracle_data); + // Check that VM is waiting + assert_eq!(vm.status, VMStatus::ForeignCallWait { function: "double".into(), inputs: vec![Value::from(5u128)] }); - let vm = VM::new(empty_registers(), output_state.memory, vec![updated_oracle_opcode], None,); - let output_state = vm.process_opcodes(); - assert_eq!(output_state.status, VMStatus::Finished); + // Push result we're waiting for + vm.foreign_call_results.push(ForeignCallResult { + values: vec![Value::from(10u128)], // Result of doubling 5u128 + }); - assert_eq!(output_state.registers.get(RegisterIndex(3)), Value::from(10_u128)); - assert_eq!(output_state.registers.get(RegisterIndex(4)), Value::from(2_u128)); + // Resume VM + brillig_execute(&mut vm); + + // Check that VM finished once resumed + assert_eq!(vm.status, VMStatus::Finished); + + // Check result register + let result_value = vm.registers.get(r_result); + assert_eq!(result_value, Value::from(10u128)); + + // Ensure the foreign call counter has been incremented + assert_eq!(vm.foreign_call_counter, 1); } - #[test] - fn oracle_input() { - use crate::opcodes::OracleInput; + fn foreign_call_opcode_memory_result() { + let r_input = RegisterIndex(0); + let r_output = RegisterIndex(1); + + // Define a simple 2x2 matrix in memory + let initial_matrix = vec![ + Value::from(1u128), Value::from(2u128), + Value::from(3u128), Value::from(4u128) + ]; - let oracle_outputs = vec![ - OracleOutput::RegisterIndex(RegisterIndex(1)), - OracleOutput::RegisterIndex(RegisterIndex(2)), + // Transpose of the matrix (but arbitrary for this test, the 'correct value') + let expected_result = vec![ + Value::from(1u128), Value::from(3u128), + Value::from(2u128), Value::from(4u128) ]; - let oracle_inputs = vec![OracleInput::RegisterIndex(RegisterIndex(0))]; - - let mut oracle_data = OracleData { - name: "call_private_function_oracle".to_owned(), - inputs: oracle_inputs, - input_values: vec![], - outputs: oracle_outputs, - output_values: vec![], - }; - let program = vec![ + let invert_program = vec![ + // input = 0 Opcode::Const { - destination: RegisterIndex(1), - value: 0u128.into(), + destination: r_input, + value: Value::from(0u128), }, + // output = 0 Opcode::Const { - destination: RegisterIndex(2), - value: 0u128.into(), + destination: r_output, + value: Value::from(0u128), + }, + // *output = matrix_2x2_transpose(*input) + Opcode::ForeignCall { + function: "matrix_2x2_transpose".into(), + destination: RegisterValueOrArray::HeapArray(r_output, initial_matrix.len()), + input: RegisterValueOrArray::HeapArray(r_input, initial_matrix.len()) }, - Opcode::Oracle(oracle_data.clone()) ]; - let vm = VM::new(empty_registers(), vec![], program.clone(), None,); - let output_state = vm.process_opcodes(); - assert_eq!(output_state.status, VMStatus::OracleWait); + let mut vm = brillig_execute_and_get_vm(initial_matrix.clone(), invert_program); - let input_values = output_state.map_input_values(&oracle_data); - assert_eq!(input_values.len(), oracle_data.inputs.len()); + // Check that VM is waiting + assert_eq!(vm.status, VMStatus::ForeignCallWait { function: "matrix_2x2_transpose".into(), inputs: initial_matrix }); - // Update oracle_data - oracle_data.input_values = input_values; - oracle_data.output_values = vec![FieldElement::from(5u128), FieldElement::from(6u128)]; - let mut updated_program = program; - updated_program[2] = Opcode::Oracle(oracle_data); + // Push result we're waiting for + vm.foreign_call_results.push(ForeignCallResult { + values: expected_result.clone(), + }); - let vm = VM::new(empty_registers(), vec![], updated_program, None,); - let output_state = vm.process_opcodes(); - assert_eq!(output_state.status, VMStatus::Finished); + // Resume VM + brillig_execute(&mut vm); - assert_eq!(output_state.registers.get(RegisterIndex(1)), Value::from(5u128)); - assert_eq!(output_state.registers.get(RegisterIndex(2)), Value::from(6u128)); + // Check that VM finished once resumed + assert_eq!(vm.status, VMStatus::Finished); + + // Check result in memory + let result_values = vm.memory[0..4].to_vec(); + assert_eq!(result_values, expected_result); + + // Ensure the foreign call counter has been incremented + assert_eq!(vm.foreign_call_counter, 1); } + } diff --git a/brillig_bytecode/src/memory.rs b/brillig_bytecode/src/memory.rs deleted file mode 100644 index 24b218dac..000000000 --- a/brillig_bytecode/src/memory.rs +++ /dev/null @@ -1,13 +0,0 @@ -use serde::{Deserialize, Serialize}; - -/// Memory in the VM is used for storing arrays -/// -/// ArrayIndex will be used to reference an Array element. -/// The pointer is needed to find it's location in memory, -/// and the index is used to offset from that point to find -/// the exact element. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub struct ArrayIndex { - pointer: usize, - index: usize, -} diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 0507b169d..9a7869981 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -1,11 +1,17 @@ use crate::{ - RegisterIndex, + RegisterIndex, Value, }; use acir_field::FieldElement; use serde::{Deserialize, Serialize}; pub type Label = usize; +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] +pub enum RegisterValueOrArray { + RegisterIndex(RegisterIndex), + HeapArray(RegisterIndex, usize) +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum Opcode { /// Takes the fields in registers `lhs` and `rhs` @@ -48,13 +54,21 @@ pub enum Opcode { }, Const { destination: RegisterIndex, - value: FieldElement, + value: Value, }, Return, - // TODO:These are special functions like sha256 - Intrinsics, - /// Used to get data from an outside source - Oracle(OracleData), + /// Used to get data from an outside source. + /// Also referred to as an Oracle. However, we don't use that name as + /// this is intended for things like state tree reads, and shouldn't be confused + /// with e.g. blockchain price oracles. + ForeignCall { + // Interpreted by simulator context + function: String, + // Destination register (may be a memory pointer). + destination: RegisterValueOrArray, + // Input register (may be a memory pointer). + input: RegisterValueOrArray, + }, Mov { destination: RegisterIndex, source: RegisterIndex, @@ -84,8 +98,7 @@ impl Opcode { Opcode::Call { .. } => "call", Opcode::Const { .. } => "const", Opcode::Return => "return", - Opcode::Intrinsics => "intrinsics", - Opcode::Oracle(_) => "oracle", + Opcode::ForeignCall { .. } => "foreign_call", Opcode::Mov { .. } => "mov", Opcode::Load { .. } => "load", Opcode::Store { .. } => "store", @@ -95,30 +108,11 @@ impl Opcode { } } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct OracleData { - /// Name of the oracle - pub name: String, - /// Input registers - pub inputs: Vec, - /// Input values - pub input_values: Vec, - /// Output registers - pub outputs: Vec, - /// Output values - they are computed by the (external) oracle once the inputs are known - pub output_values: Vec, -} - #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum OracleInput { RegisterIndex(RegisterIndex), } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub enum OracleOutput { - RegisterIndex(RegisterIndex), -} - // Binary fixed-length integer expressions #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs index 584e02c9d..e7a9ab3c9 100644 --- a/brillig_bytecode/src/value.rs +++ b/brillig_bytecode/src/value.rs @@ -11,7 +11,7 @@ pub enum Typ { } /// Value represents a Value in the VM -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Value { pub inner: FieldElement, } @@ -25,7 +25,9 @@ impl Value { self.inner.to_u128() } pub fn to_usize(&self) -> usize { - self.inner.to_u128() as usize + usize::try_from( + self.inner.try_to_u64().expect("register does not fit into u64"), + ).expect("register does not fit into usize") } } From f5b6e2fedba02cc2b4bbe28942afd3f4aee7cf2b Mon Sep 17 00:00:00 2001 From: ludamad Date: Wed, 17 May 2023 14:57:17 -0400 Subject: [PATCH 059/125] Fix: test reorder issue --- acvm/src/lib.rs | 194 ++++++++++++++++++++++++------------------------ 1 file changed, 95 insertions(+), 99 deletions(-) diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 688afc189..b47d53e3a 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -497,18 +497,18 @@ mod test { lhs: RegisterIndex(0), op: brillig_bytecode::BinaryFieldOp::Cmp(Comparison::Eq), rhs: RegisterIndex(1), - }, + }, brillig_bytecode::Opcode::BinaryFieldOp { destination: RegisterIndex(3), lhs: RegisterIndex(0), op: brillig_bytecode::BinaryFieldOp::Cmp(Comparison::Lt), rhs: RegisterIndex(1), - }, + }, // Oracles are named 'foreign calls' in brillig brillig_bytecode::Opcode::ForeignCall { function: "invert".into(), - destination: RegisterValueOrArray::RegisterIndex(RegisterIndex(0)), - input: RegisterValueOrArray::RegisterIndex(RegisterIndex(1)), + destination: RegisterValueOrArray::RegisterIndex(RegisterIndex(1)), + input: RegisterValueOrArray::RegisterIndex(RegisterIndex(0)), } ], predicate: None, @@ -568,100 +568,96 @@ mod test { #[test] fn brillig_oracle_predicate() { - // // Opcodes below describe the following: - // // fn main(x : Field, y : pub Field, cond: bool) { - // // let z = x + y; - // // let z_inverse = 1/z - // // if cond { - // // constrain z_inverse == Oracle("inverse", x + y); - // // } - // // } - // let fe_0 = FieldElement::zero(); - // let fe_1 = FieldElement::one(); - // let w_x = Witness(1); - // let w_y = Witness(2); - // let w_oracle = Witness(3); - // let w_z = Witness(4); - // let w_z_inverse = Witness(5); - // let w_x_plus_y = Witness(6); - // let w_equal_res = Witness(7); - // let w_lt_res = Witness(8); - - // let equal_opcode = brillig_bytecode::Opcode::BinaryFieldOp { - // op: BinaryFieldOp::Cmp(Comparison::Eq), - // lhs: RegisterIndex(0), - // rhs: RegisterIndex(1), - // destination: RegisterIndex(2), - // }; - - // let less_than_opcode = brillig_bytecode::Opcode::BinaryFieldOp { - // op: BinaryFieldOp::Cmp(Comparison::Lt), - // lhs: RegisterIndex(0), - // rhs: RegisterIndex(1), - // destination: RegisterIndex(3), - // }; - - // let invert_oracle_input = OracleInput::RegisterIndex(RegisterIndex(0)); - // let invert_oracle_output = OracleOutput::RegisterIndex(RegisterIndex(1)); - - // let invert_oracle = brillig_bytecode::Opcode::Oracle(brillig_bytecode::OracleData { - // name: "invert".into(), - // inputs: vec![invert_oracle_input], - // input_values: vec![], - // outputs: vec![invert_oracle_output], - // output_values: vec![], - // }); - - // let brillig_bytecode = vec![equal_opcode, less_than_opcode, invert_oracle]; - - // let brillig_opcode = Opcode::Brillig(Brillig { - // inputs: vec![ - // BrilligInputs::Simple(Expression { - // mul_terms: vec![], - // linear_combinations: vec![(fe_1, w_x), (fe_1, w_y)], - // q_c: fe_0, - // }), - // BrilligInputs::Simple(Expression::default()), - // ], - // outputs: vec![ - // BrilligOutputs::Simple(w_x_plus_y), - // BrilligOutputs::Simple(w_oracle), - // BrilligOutputs::Simple(w_equal_res), - // BrilligOutputs::Simple(w_lt_res), - // ], - // bytecode: brillig_bytecode, - // predicate: Some(Expression::default()), - // // oracle results - // foreign_call_results: vec![] - // }); - - // let opcodes = vec![ - // brillig_opcode, - // Opcode::Arithmetic(Expression { - // mul_terms: vec![], - // linear_combinations: vec![(fe_1, w_x), (fe_1, w_y), (-fe_1, w_z)], - // q_c: fe_0, - // }), - // Opcode::Directive(Directive::Invert { x: w_z, result: w_z_inverse }), - // Opcode::Arithmetic(Expression { - // mul_terms: vec![(fe_1, w_z, w_z_inverse)], - // linear_combinations: vec![], - // q_c: -fe_1, - // }), - // ]; - - // let pwg = StubbedPwg; - - // let mut witness_assignments = BTreeMap::from([ - // (Witness(1), FieldElement::from(2u128)), - // (Witness(2), FieldElement::from(3u128)), - // ]); - // let mut blocks = Blocks::default(); - // let UnresolvedData { unresolved_opcodes, unresolved_brilligs, .. } = pwg - // .solve(&mut witness_assignments, &mut blocks, opcodes) - // .expect("should stall on oracle"); - - // assert!(unresolved_opcodes.is_empty(), "opcode should be removed"); - // assert!(unresolved_brilligs.is_empty(), "should have no unresolved oracles"); + // Opcodes below describe the following: + // fn main(x : Field, y : pub Field, cond: bool) { + // let z = x + y; + // let z_inverse = 1/z + // if cond { + // constrain z_inverse == Oracle("inverse", x + y); + // } + // } + let fe_0 = FieldElement::zero(); + let fe_1 = FieldElement::one(); + let w_x = Witness(1); + let w_y = Witness(2); + let w_oracle = Witness(3); + let w_z = Witness(4); + let w_z_inverse = Witness(5); + let w_x_plus_y = Witness(6); + let w_equal_res = Witness(7); + let w_lt_res = Witness(8); + + let equal_opcode = brillig_bytecode::Opcode::BinaryFieldOp { + op: BinaryFieldOp::Cmp(Comparison::Eq), + lhs: RegisterIndex(0), + rhs: RegisterIndex(1), + destination: RegisterIndex(2), + }; + + let less_than_opcode = brillig_bytecode::Opcode::BinaryFieldOp { + op: BinaryFieldOp::Cmp(Comparison::Lt), + lhs: RegisterIndex(0), + rhs: RegisterIndex(1), + destination: RegisterIndex(3), + }; + + let brillig_opcode = Opcode::Brillig(Brillig { + inputs: vec![ + BrilligInputs::Simple(Expression { + mul_terms: vec![], + linear_combinations: vec![(fe_1, w_x), (fe_1, w_y)], + q_c: fe_0, + }), + BrilligInputs::Simple(Expression::default()), + ], + outputs: vec![ + BrilligOutputs::Simple(w_x_plus_y), + BrilligOutputs::Simple(w_oracle), + BrilligOutputs::Simple(w_equal_res), + BrilligOutputs::Simple(w_lt_res), + ], + bytecode: vec![ + equal_opcode, + less_than_opcode, + // Oracles are named 'foreign calls' in brillig + brillig_bytecode::Opcode::ForeignCall { + function: "invert".into(), + destination: RegisterValueOrArray::RegisterIndex(RegisterIndex(1)), + input: RegisterValueOrArray::RegisterIndex(RegisterIndex(0)), + } + ], + predicate: Some(Expression::default()), + // oracle results + foreign_call_results: vec![] + }); + + let opcodes = vec![ + brillig_opcode, + Opcode::Arithmetic(Expression { + mul_terms: vec![], + linear_combinations: vec![(fe_1, w_x), (fe_1, w_y), (-fe_1, w_z)], + q_c: fe_0, + }), + Opcode::Directive(Directive::Invert { x: w_z, result: w_z_inverse }), + Opcode::Arithmetic(Expression { + mul_terms: vec![(fe_1, w_z, w_z_inverse)], + linear_combinations: vec![], + q_c: -fe_1, + }), + ]; + + let pwg = StubbedPwg; + + let mut witness_assignments = BTreeMap::from([ + (Witness(1), FieldElement::from(2u128)), + (Witness(2), FieldElement::from(3u128)), + ]); + let mut blocks = Blocks::default(); + let UnresolvedData { unresolved_opcodes, unresolved_brilligs, .. } = pwg + .solve(&mut witness_assignments, &mut blocks, opcodes) + .expect("should stall on oracle"); + + assert!(unresolved_opcodes.is_empty(), "opcode should be removed"); + assert!(unresolved_brilligs.is_empty(), "should have no unresolved oracles"); } } From a5ea7cbeba896470ce9a429ab7ce6a589c548fbd Mon Sep 17 00:00:00 2001 From: ludamad Date: Wed, 17 May 2023 14:59:05 -0400 Subject: [PATCH 060/125] cargo format --- acvm/src/lib.rs | 23 ++- acvm/src/pwg/brillig.rs | 17 +- brillig_bytecode/src/lib.rs | 282 ++++++++++++------------------ brillig_bytecode/src/opcodes.rs | 41 ++--- brillig_bytecode/src/registers.rs | 5 +- brillig_bytecode/src/value.rs | 5 +- 6 files changed, 150 insertions(+), 223 deletions(-) diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index b47d53e3a..83f1ad0ce 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -141,7 +141,10 @@ pub trait PartialWitnessGenerator { Opcode::Brillig(brillig) => brillig.clone(), _ => unreachable!("Brillig resolution for non brillig opcode"), }; - unresolved_brilligs.push(UnresolvedBrillig { brillig, foreign_call_wait_info: oracle_wait_info }) + unresolved_brilligs.push(UnresolvedBrillig { + brillig, + foreign_call_wait_info: oracle_wait_info, + }) } Ok(OpcodeResolution::Stalled(not_solvable)) => { if opcode_not_solvable.is_none() { @@ -359,7 +362,8 @@ mod test { use acir::{ brillig_bytecode, brillig_bytecode::{ - Comparison, RegisterIndex, BinaryFieldOp, RegisterValueOrArray, ForeignCallResult, Value, + BinaryFieldOp, Comparison, ForeignCallResult, RegisterIndex, RegisterValueOrArray, + Value, }, circuit::{ directives::Directive, @@ -509,7 +513,7 @@ mod test { function: "invert".into(), destination: RegisterValueOrArray::RegisterIndex(RegisterIndex(1)), input: RegisterValueOrArray::RegisterIndex(RegisterIndex(0)), - } + }, ], predicate: None, }; @@ -549,11 +553,12 @@ mod test { assert_eq!(unresolved_opcodes.len(), 0, "brillig should have been removed"); assert_eq!(unresolved_brilligs.len(), 1, "should have a brillig oracle request"); - let UnresolvedBrillig { foreign_call_wait_info, mut brillig } = unresolved_brilligs.remove(0); + let UnresolvedBrillig { foreign_call_wait_info, mut brillig } = + unresolved_brilligs.remove(0); assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); // Alter Brillig oracle opcode brillig.foreign_call_results.push(ForeignCallResult { - values: vec![Value::from(foreign_call_wait_info.inputs[0].inner.inverse())] + values: vec![Value::from(foreign_call_wait_info.inputs[0].inner.inverse())], }); let mut next_opcodes_for_solving = vec![Opcode::Brillig(brillig)]; next_opcodes_for_solving.extend_from_slice(&unresolved_opcodes[..]); @@ -617,18 +622,18 @@ mod test { BrilligOutputs::Simple(w_lt_res), ], bytecode: vec![ - equal_opcode, - less_than_opcode, + equal_opcode, + less_than_opcode, // Oracles are named 'foreign calls' in brillig brillig_bytecode::Opcode::ForeignCall { function: "invert".into(), destination: RegisterValueOrArray::RegisterIndex(RegisterIndex(1)), input: RegisterValueOrArray::RegisterIndex(RegisterIndex(0)), - } + }, ], predicate: Some(Expression::default()), // oracle results - foreign_call_results: vec![] + foreign_call_results: vec![], }); let opcodes = vec![ diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index afdba24d1..495cbeaf1 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use acir::{ - brillig_bytecode::{Registers, VMStatus, Value, VM, RegisterIndex}, + brillig_bytecode::{RegisterIndex, Registers, VMStatus, Value, VM}, circuit::opcodes::{Brillig, BrilligInputs, BrilligOutputs}, native_types::Witness, FieldElement, @@ -112,10 +112,16 @@ impl BrilligSolver { } let input_registers = Registers { inner: input_register_values }; - let mut vm = VM::new(input_registers, input_memory, brillig.bytecode.clone(), brillig.foreign_call_results.clone(), None); + let mut vm = VM::new( + input_registers, + input_memory, + brillig.bytecode.clone(), + brillig.foreign_call_results.clone(), + None, + ); let vm_status = vm.process_opcodes(); - + let result = match vm_status { VMStatus::Finished => { for (i, output) in brillig.outputs.iter().enumerate() { @@ -138,10 +144,7 @@ impl BrilligSolver { VMStatus::InProgress => unreachable!("Brillig VM has not completed execution"), VMStatus::Failure => return Err(OpcodeResolutionError::UnsatisfiedConstrain), VMStatus::ForeignCallWait { function, inputs } => { - OpcodeResolution::InProgressBrillig(ForeignCallWaitInfo { - function, - inputs - }) + OpcodeResolution::InProgressBrillig(ForeignCallWaitInfo { function, inputs }) } }; diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index b6e9933c0..328e1b1a4 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -8,11 +8,10 @@ mod opcodes; mod registers; mod value; - pub use opcodes::{BinaryFieldOp, BinaryIntOp, RegisterValueOrArray}; pub use opcodes::{Comparison, Opcode}; pub use registers::{RegisterIndex, Registers}; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; pub use value::Typ; pub use value::Value; @@ -32,7 +31,7 @@ pub enum VMStatus { #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)] pub struct ForeignCallResult { // resolved foreign call values - pub values: Vec + pub values: Vec, } #[derive(Debug, PartialEq, Eq, Clone)] @@ -53,13 +52,12 @@ impl VM { memory: Vec, bytecode: Vec, foreign_call_results: Vec, - register_allocation_indices: Option> + register_allocation_indices: Option>, ) -> VM { if let Some(register_allocation_indices) = register_allocation_indices { // TODO(AD): simplify this all to be done before calling VM.new() - let mut registers_modified = - Registers::load(vec![Value::from(0u128)]); - + let mut registers_modified = Registers::load(vec![Value::from(0u128)]); + for (i, register_index) in register_allocation_indices.iter().enumerate() { let register_value = inputs.get(RegisterIndex(i)); registers_modified.set(RegisterIndex(*register_index as usize), register_value) @@ -67,7 +65,7 @@ impl VM { inputs = registers_modified; } - + Self { registers: inputs, program_counter: 0, @@ -89,10 +87,7 @@ impl VM { } /// Waits for a foreign call/oracle fn wait_for_foreign_call(&mut self, function: String, inputs: Vec) -> VMStatus { - self.status(VMStatus::ForeignCallWait { - function, - inputs - }) + self.status(VMStatus::ForeignCallWait { function, inputs }) } fn fail(&mut self, error_msg: &str) -> VMStatus { self.status(VMStatus::Failure); @@ -105,7 +100,7 @@ impl VM { pub fn process_opcodes(&mut self) -> VMStatus { while !matches!( self.process_opcode(), - VMStatus::Finished | VMStatus::Failure | VMStatus::ForeignCallWait {..} + VMStatus::Finished | VMStatus::Failure | VMStatus::ForeignCallWait { .. } ) {} self.status.clone() } @@ -155,21 +150,30 @@ impl VM { self.fail("return opcode hit, but callstack already empty") } } - Opcode::ForeignCall {function, destination, input} => { + Opcode::ForeignCall { function, destination, input } => { if self.foreign_call_counter >= self.foreign_call_results.len() { - let resolved_inputs = self.resolve_foreign_call_input(*input); - return self.wait_for_foreign_call(function.clone(), resolved_inputs); + let resolved_inputs = self.resolve_foreign_call_input(*input); + return self.wait_for_foreign_call(function.clone(), resolved_inputs); } - let ForeignCallResult { values } = &self.foreign_call_results[self.foreign_call_counter]; + let ForeignCallResult { values } = + &self.foreign_call_results[self.foreign_call_counter]; match destination { RegisterValueOrArray::RegisterIndex(index) => { - assert_eq!(values.len(), 1, "Function result size does not match brillig bytecode"); + assert_eq!( + values.len(), + 1, + "Function result size does not match brillig bytecode" + ); self.registers.set(*index, values[0]) - }, + } RegisterValueOrArray::HeapArray(index, size) => { let destination_value = self.registers.get(*index); - assert_eq!(values.len(), *size, "Function result size does not match brillig bytecode"); + assert_eq!( + values.len(), + *size, + "Function result size does not match brillig bytecode" + ); for (i, value) in values.iter().enumerate() { self.memory[destination_value.to_usize() + i] = *value; } @@ -279,7 +283,6 @@ impl VM { } } - pub struct VMOutputState { pub registers: Registers, pub program_counter: usize, @@ -331,22 +334,22 @@ mod tests { fn jmpif_opcode() { let mut registers = vec![]; let mut opcodes = vec![]; - + let lhs = { registers.push(Value::from(2u128)); RegisterIndex(registers.len() - 1) }; - + let rhs = { registers.push(Value::from(2u128)); RegisterIndex(registers.len() - 1) }; - + let destination = { registers.push(Value::from(0u128)); RegisterIndex(registers.len() - 1) }; - + let equal_cmp_opcode = Opcode::BinaryIntOp { op: BinaryIntOp::Cmp(Comparison::Eq), bit_size: 1, @@ -356,28 +359,19 @@ mod tests { }; opcodes.push(equal_cmp_opcode); opcodes.push(Opcode::Jump { location: 2 }); - opcodes.push(Opcode::JumpIf { - condition: RegisterIndex(2), - location: 3, - }); - - let mut vm = VM::new( - Registers { inner: registers }, - vec![], - opcodes, - vec![], - None, - ); - + opcodes.push(Opcode::JumpIf { condition: RegisterIndex(2), location: 3 }); + + let mut vm = VM::new(Registers { inner: registers }, vec![], opcodes, vec![], None); + let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - + let output_cmp_value = vm.registers.get(RegisterIndex(2)); assert_eq!(output_cmp_value, Value::from(true)); - + let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - + let status = vm.process_opcode(); assert_eq!(status, VMStatus::Finished); } @@ -398,10 +392,7 @@ mod tests { let jump_opcode = Opcode::Jump { location: 2 }; - let jump_if_not_opcode = Opcode::JumpIfNot { - condition: RegisterIndex(2), - location: 1, - }; + let jump_if_not_opcode = Opcode::JumpIfNot { condition: RegisterIndex(2), location: 1 }; let add_opcode = Opcode::BinaryFieldOp { op: BinaryFieldOp::Add, @@ -415,7 +406,7 @@ mod tests { vec![], vec![jump_opcode, trap_opcode, not_equal_cmp_opcode, jump_if_not_opcode, add_opcode], vec![], - None + None, ); let status = vm.process_opcode(); @@ -444,10 +435,7 @@ mod tests { let input_registers = Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(3u128)]); - let mov_opcode = Opcode::Mov { - destination: RegisterIndex(2), - source: RegisterIndex(0), - }; + let mov_opcode = Opcode::Mov { destination: RegisterIndex(2), source: RegisterIndex(0) }; let mut vm = VM::new(input_registers, vec![], vec![mov_opcode], vec![], None); @@ -552,27 +540,15 @@ mod tests { let r_tmp = RegisterIndex(2); let start = [ // i = 0 - Opcode::Const { - destination: r_i, - value: 0u128.into(), - }, + Opcode::Const { destination: r_i, value: 0u128.into() }, // len = memory.len() (approximation) - Opcode::Const { - destination: r_len, - value: Value::from(memory.len() as u128), - }, + Opcode::Const { destination: r_len, value: Value::from(memory.len() as u128) }, ]; let loop_body = [ // *i = i - Opcode::Store { - destination_pointer: r_i, - source: r_i, - }, + Opcode::Store { destination_pointer: r_i, source: r_i }, // tmp = 1 - Opcode::Const { - destination: r_tmp, - value: 1u128.into(), - }, + Opcode::Const { destination: r_tmp, value: 1u128.into() }, // i = i + 1 (tmp) Opcode::BinaryIntOp { destination: r_i, @@ -585,20 +561,17 @@ mod tests { Opcode::BinaryIntOp { destination: r_tmp, lhs: r_i, - op: BinaryIntOp::Cmp(Comparison::Lt), + op: BinaryIntOp::Cmp(Comparison::Lt), rhs: r_len, bit_size: 32, }, // if tmp != 0 goto loop_body - Opcode::JumpIf { - condition: r_tmp, - location: start.len() - }, + Opcode::JumpIf { condition: r_tmp, location: start.len() }, ]; let vm = brillig_execute_and_get_vm(memory, [&start[..], &loop_body[..]].concat()); vm.memory } - + let memory = brillig_write_memory(vec![Value::from(0u128); 5]); let expected = vec![ Value::from(0u128), @@ -608,7 +581,7 @@ mod tests { Value::from(4u128), ]; assert_eq!(memory, expected); - + let memory = brillig_write_memory(vec![Value::from(0u128); 1024]); let expected: Vec = (0..1024).map(|i| Value::from(i as u128)).collect(); assert_eq!(memory, expected); @@ -631,27 +604,15 @@ mod tests { let r_tmp = RegisterIndex(3); let start = [ // sum = 0 - Opcode::Const { - destination: r_sum, - value: 0u128.into(), - }, + Opcode::Const { destination: r_sum, value: 0u128.into() }, // i = 0 - Opcode::Const { - destination: r_i, - value: 0u128.into(), - }, + Opcode::Const { destination: r_i, value: 0u128.into() }, // len = array.len() (approximation) - Opcode::Const { - destination: r_len, - value: Value::from(memory.len() as u128), - }, + Opcode::Const { destination: r_len, value: Value::from(memory.len() as u128) }, ]; let loop_body = [ // tmp = *i - Opcode::Load { - destination: r_tmp, - source_pointer: r_i - }, + Opcode::Load { destination: r_tmp, source_pointer: r_i }, // sum = sum + tmp Opcode::BinaryIntOp { destination: r_sum, @@ -661,10 +622,7 @@ mod tests { bit_size: 32, }, // tmp = 1 - Opcode::Const { - destination: r_tmp, - value: 1u128.into(), - }, + Opcode::Const { destination: r_tmp, value: 1u128.into() }, // i = i + 1 (tmp) Opcode::BinaryIntOp { destination: r_i, @@ -677,27 +635,27 @@ mod tests { Opcode::BinaryIntOp { destination: r_tmp, lhs: r_i, - op: BinaryIntOp::Cmp(Comparison::Lt), + op: BinaryIntOp::Cmp(Comparison::Lt), rhs: r_len, bit_size: 32, }, // if tmp != 0 goto loop_body - Opcode::JumpIf { - condition: r_tmp, - location: start.len() - }, + Opcode::JumpIf { condition: r_tmp, location: start.len() }, ]; let vm = brillig_execute_and_get_vm(memory, [&start[..], &loop_body[..]].concat()); vm.registers.get(r_sum) } - assert_eq!(brillig_sum_memory(vec![ - Value::from(1u128), - Value::from(2u128), - Value::from(3u128), - Value::from(4u128), - Value::from(5u128), - ]), Value::from(15u128)); + assert_eq!( + brillig_sum_memory(vec![ + Value::from(1u128), + Value::from(2u128), + Value::from(3u128), + Value::from(4u128), + Value::from(5u128), + ]), + Value::from(15u128) + ); assert_eq!(brillig_sum_memory(vec![Value::from(1u128); 1024]), Value::from(1024u128)); } @@ -716,28 +674,20 @@ mod tests { let r_i = RegisterIndex(0); let r_len = RegisterIndex(1); let r_tmp = RegisterIndex(2); - + let start = [ // i = 0 - Opcode::Const { - destination: r_i, - value: 0u128.into(), - }, + Opcode::Const { destination: r_i, value: 0u128.into() }, // len = memory.len() (approximation) - Opcode::Const { - destination: r_len, - value: Value::from(memory.len() as u128), - }, + Opcode::Const { destination: r_len, value: Value::from(memory.len() as u128) }, // call recursive_fn Opcode::Call { location: 4, // Call after 'start' }, // end program by jumping to end - Opcode::Jump { - location: 100, - }, + Opcode::Jump { location: 100 }, ]; - + let recursive_fn = [ // tmp = len <= i Opcode::BinaryIntOp { @@ -753,15 +703,9 @@ mod tests { location: start.len() + 6, // 7 ops in recursive_fn, go to 'Return' }, // *i = i - Opcode::Store { - destination_pointer: r_i, - source: r_i, - }, + Opcode::Store { destination_pointer: r_i, source: r_i }, // tmp = 1 - Opcode::Const { - destination: r_tmp, - value: 1u128.into(), - }, + Opcode::Const { destination: r_tmp, value: 1u128.into() }, // i = i + 1 (tmp) Opcode::BinaryIntOp { destination: r_i, @@ -771,16 +715,14 @@ mod tests { bit_size: 32, }, // call recursive_fn - Opcode::Call { - location: start.len(), - }, - Opcode::Return {} + Opcode::Call { location: start.len() }, + Opcode::Return {}, ]; - + let vm = brillig_execute_and_get_vm(memory, [&start[..], &recursive_fn[..]].concat()); vm.memory } - + let memory = brillig_recursive_write_memory(vec![Value::from(0u128); 5]); let expected = vec![ Value::from(0u128), @@ -790,24 +732,18 @@ mod tests { Value::from(4u128), ]; assert_eq!(memory, expected); - + let memory = brillig_recursive_write_memory(vec![Value::from(0u128); 1024]); let expected: Vec = (0..1024).map(|i| Value::from(i as u128)).collect(); assert_eq!(memory, expected); } - + fn empty_registers() -> Registers { Registers::load(vec![Value::from(0u128); 16]) } /// Helper to execute brillig code fn brillig_execute_and_get_vm(memory: Vec, opcodes: Vec) -> VM { - let mut vm = VM::new( - empty_registers(), - memory, - opcodes, - vec![], - None, - ); + let mut vm = VM::new(empty_registers(), memory, opcodes, vec![], None); brillig_execute(&mut vm); assert_eq!(vm.call_stack, vec![]); vm @@ -816,7 +752,7 @@ mod tests { fn brillig_execute(vm: &mut VM) { loop { let status = vm.process_opcode(); - if matches!(status, VMStatus::Finished | VMStatus::ForeignCallWait {..}) { + if matches!(status, VMStatus::Finished | VMStatus::ForeignCallWait { .. }) { break; } assert_eq!(status, VMStatus::InProgress) @@ -827,13 +763,10 @@ mod tests { fn foreign_call_opcode_register_result() { let r_input = RegisterIndex(0); let r_result = RegisterIndex(1); - + let double_program = vec![ // Load input register with value 5 - Opcode::Const { - destination: r_input, - value: Value::from(5u128), - }, + Opcode::Const { destination: r_input, value: Value::from(5u128) }, // Call foreign function "double" with the input register Opcode::ForeignCall { function: "double".into(), @@ -841,11 +774,17 @@ mod tests { input: RegisterValueOrArray::RegisterIndex(r_input), }, ]; - + let mut vm = brillig_execute_and_get_vm(vec![], double_program); // Check that VM is waiting - assert_eq!(vm.status, VMStatus::ForeignCallWait { function: "double".into(), inputs: vec![Value::from(5u128)] }); + assert_eq!( + vm.status, + VMStatus::ForeignCallWait { + function: "double".into(), + inputs: vec![Value::from(5u128)] + } + ); // Push result we're waiting for vm.foreign_call_results.push(ForeignCallResult { @@ -854,14 +793,14 @@ mod tests { // Resume VM brillig_execute(&mut vm); - + // Check that VM finished once resumed assert_eq!(vm.status, VMStatus::Finished); - + // Check result register let result_value = vm.registers.get(r_result); assert_eq!(result_value, Value::from(10u128)); - + // Ensure the foreign call counter has been incremented assert_eq!(vm.foreign_call_counter, 1); } @@ -869,47 +808,41 @@ mod tests { fn foreign_call_opcode_memory_result() { let r_input = RegisterIndex(0); let r_output = RegisterIndex(1); - + // Define a simple 2x2 matrix in memory - let initial_matrix = vec![ - Value::from(1u128), Value::from(2u128), - Value::from(3u128), Value::from(4u128) - ]; + let initial_matrix = + vec![Value::from(1u128), Value::from(2u128), Value::from(3u128), Value::from(4u128)]; // Transpose of the matrix (but arbitrary for this test, the 'correct value') - let expected_result = vec![ - Value::from(1u128), Value::from(3u128), - Value::from(2u128), Value::from(4u128) - ]; + let expected_result = + vec![Value::from(1u128), Value::from(3u128), Value::from(2u128), Value::from(4u128)]; let invert_program = vec![ // input = 0 - Opcode::Const { - destination: r_input, - value: Value::from(0u128), - }, + Opcode::Const { destination: r_input, value: Value::from(0u128) }, // output = 0 - Opcode::Const { - destination: r_output, - value: Value::from(0u128), - }, + Opcode::Const { destination: r_output, value: Value::from(0u128) }, // *output = matrix_2x2_transpose(*input) Opcode::ForeignCall { function: "matrix_2x2_transpose".into(), destination: RegisterValueOrArray::HeapArray(r_output, initial_matrix.len()), - input: RegisterValueOrArray::HeapArray(r_input, initial_matrix.len()) + input: RegisterValueOrArray::HeapArray(r_input, initial_matrix.len()), }, ]; let mut vm = brillig_execute_and_get_vm(initial_matrix.clone(), invert_program); // Check that VM is waiting - assert_eq!(vm.status, VMStatus::ForeignCallWait { function: "matrix_2x2_transpose".into(), inputs: initial_matrix }); + assert_eq!( + vm.status, + VMStatus::ForeignCallWait { + function: "matrix_2x2_transpose".into(), + inputs: initial_matrix + } + ); // Push result we're waiting for - vm.foreign_call_results.push(ForeignCallResult { - values: expected_result.clone(), - }); + vm.foreign_call_results.push(ForeignCallResult { values: expected_result.clone() }); // Resume VM brillig_execute(&mut vm); @@ -924,5 +857,4 @@ mod tests { // Ensure the foreign call counter has been incremented assert_eq!(vm.foreign_call_counter, 1); } - } diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 9a7869981..21b66325a 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -1,6 +1,4 @@ -use crate::{ - RegisterIndex, Value, -}; +use crate::{RegisterIndex, Value}; use acir_field::FieldElement; use serde::{Deserialize, Serialize}; @@ -9,7 +7,7 @@ pub type Label = usize; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] pub enum RegisterValueOrArray { RegisterIndex(RegisterIndex), - HeapArray(RegisterIndex, usize) + HeapArray(RegisterIndex, usize), } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -54,12 +52,12 @@ pub enum Opcode { }, Const { destination: RegisterIndex, - value: Value, + value: Value, }, Return, /// Used to get data from an outside source. /// Also referred to as an Oracle. However, we don't use that name as - /// this is intended for things like state tree reads, and shouldn't be confused + /// this is intended for things like state tree reads, and shouldn't be confused /// with e.g. blockchain price oracles. ForeignCall { // Interpreted by simulator context @@ -113,7 +111,6 @@ pub enum OracleInput { RegisterIndex(RegisterIndex), } - // Binary fixed-length integer expressions #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum BinaryFieldOp { @@ -156,8 +153,8 @@ impl BinaryFieldOp { BinaryFieldOp::Sub => a - b, BinaryFieldOp::Mul => a * b, BinaryFieldOp::Div => a / b, - // Perform a comparison between a and b based on the Comparison variant. - BinaryFieldOp::Cmp(comparison) => match comparison { + // Perform a comparison between a and b based on the Comparison variant. + BinaryFieldOp::Cmp(comparison) => match comparison { Comparison::Eq => ((a == b) as u128).into(), Comparison::Lt => ((a < b) as u128).into(), Comparison::Lte => ((a <= b) as u128).into(), @@ -178,7 +175,9 @@ impl BinaryIntOp { // Perform unsigned division using the modulo operation on a and b. BinaryIntOp::UnsignedDiv => (a % bit_modulo) / (b % bit_modulo), // Perform signed division by first converting a and b to signed integers and then back to unsigned after the operation. - BinaryIntOp::SignedDiv => to_unsigned(to_signed(a, bit_size) / to_signed(b, bit_size), bit_size), + BinaryIntOp::SignedDiv => { + to_unsigned(to_signed(a, bit_size) / to_signed(b, bit_size), bit_size) + } // Perform a comparison between a and b based on the Comparison variant. BinaryIntOp::Cmp(comparison) => match comparison { Comparison::Eq => (a == b) as u128, @@ -186,21 +185,11 @@ impl BinaryIntOp { Comparison::Lte => (a <= b) as u128, }, // Perform bitwise AND, OR, XOR, left shift, and right shift operations, applying a modulo operation to keep the result within the bit size. - BinaryIntOp::And => { - (a & b) % bit_modulo - } - BinaryIntOp::Or => { - (a | b) % bit_modulo - } - BinaryIntOp::Xor => { - (a ^ b) % bit_modulo - } - BinaryIntOp::Shl => { - (a << b) % bit_modulo - } - BinaryIntOp::Shr => { - (a >> b) % bit_modulo - } + BinaryIntOp::And => (a & b) % bit_modulo, + BinaryIntOp::Or => (a | b) % bit_modulo, + BinaryIntOp::Xor => (a ^ b) % bit_modulo, + BinaryIntOp::Shl => (a << b) % bit_modulo, + BinaryIntOp::Shr => (a >> b) % bit_modulo, } } } @@ -225,4 +214,4 @@ fn to_unsigned(a: i128, n: u32) -> u128 { } else { (a + 2_i128.pow(n + 1)) as u128 } -} \ No newline at end of file +} diff --git a/brillig_bytecode/src/registers.rs b/brillig_bytecode/src/registers.rs index 153c835be..417b7863e 100644 --- a/brillig_bytecode/src/registers.rs +++ b/brillig_bytecode/src/registers.rs @@ -1,4 +1,4 @@ -use crate::{Value}; +use crate::Value; use acir_field::FieldElement; use serde::{Deserialize, Serialize}; @@ -57,8 +57,7 @@ impl Registers { pub fn set(&mut self, index: RegisterIndex, value: Value) { if index.inner() >= self.inner.len() { let diff = index.inner() - self.inner.len() + 1; - self.inner - .extend(vec![Value {inner: FieldElement::from(0u128) }; diff]) + self.inner.extend(vec![Value { inner: FieldElement::from(0u128) }; diff]) } self.inner[index.inner()] = value } diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs index e7a9ab3c9..7e27afaf2 100644 --- a/brillig_bytecode/src/value.rs +++ b/brillig_bytecode/src/value.rs @@ -25,9 +25,8 @@ impl Value { self.inner.to_u128() } pub fn to_usize(&self) -> usize { - usize::try_from( - self.inner.try_to_u64().expect("register does not fit into u64"), - ).expect("register does not fit into usize") + usize::try_from(self.inner.try_to_u64().expect("register does not fit into u64")) + .expect("register does not fit into usize") } } From 59f0e8d766969e3b8ff5c3941816e3cf046d6144 Mon Sep 17 00:00:00 2001 From: ludamad Date: Wed, 17 May 2023 15:25:27 -0400 Subject: [PATCH 061/125] chore: align tests --- acvm/src/lib.rs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 83f1ad0ce..90ab12c90 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -478,6 +478,20 @@ mod test { let w_equal_res = Witness(7); let w_lt_res = Witness(8); + let equal_opcode = brillig_bytecode::Opcode::BinaryFieldOp { + op: BinaryFieldOp::Cmp(Comparison::Eq), + lhs: RegisterIndex(0), + rhs: RegisterIndex(1), + destination: RegisterIndex(2), + }; + + let less_than_opcode = brillig_bytecode::Opcode::BinaryFieldOp { + op: BinaryFieldOp::Cmp(Comparison::Lt), + lhs: RegisterIndex(0), + rhs: RegisterIndex(1), + destination: RegisterIndex(3), + }; + let brillig_data = Brillig { inputs: vec![ BrilligInputs::Simple(Expression { @@ -496,18 +510,8 @@ mod test { // stack of foreign call/oracle resolutions, starts empty foreign_call_results: vec![], bytecode: vec![ - brillig_bytecode::Opcode::BinaryFieldOp { - destination: RegisterIndex(2), - lhs: RegisterIndex(0), - op: brillig_bytecode::BinaryFieldOp::Cmp(Comparison::Eq), - rhs: RegisterIndex(1), - }, - brillig_bytecode::Opcode::BinaryFieldOp { - destination: RegisterIndex(3), - lhs: RegisterIndex(0), - op: brillig_bytecode::BinaryFieldOp::Cmp(Comparison::Lt), - rhs: RegisterIndex(1), - }, + equal_opcode, + less_than_opcode, // Oracles are named 'foreign calls' in brillig brillig_bytecode::Opcode::ForeignCall { function: "invert".into(), From 6ab600ccd3250e96701567cabcdeb11a9c594820 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 18 May 2023 16:51:12 -0400 Subject: [PATCH 062/125] added double oracle in one brillig gen --- acvm/src/lib.rs | 159 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 90ab12c90..32158250c 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -226,6 +226,7 @@ pub trait PartialWitnessGenerator { } } +#[derive(Debug, PartialEq, Clone)] pub struct UnresolvedBrillig { pub brillig: Brillig, // information for if there is a pending foreign call/oracle @@ -575,6 +576,164 @@ mod test { assert!(unresolved_brilligs.is_empty(), "should have no unresolved oracles"); } + #[test] + fn double_inversion_brillig_oracle() { + // Opcodes below describe the following: + // fn main(x : Field, y : pub Field) { + // let z = x + y; + // constrain 1/z == Oracle("inverse", x + y); + // } + let fe_0 = FieldElement::zero(); + let fe_1 = FieldElement::one(); + let w_x = Witness(1); + let w_y = Witness(2); + let w_oracle = Witness(3); + let w_z = Witness(4); + let w_z_inverse = Witness(5); + let w_x_plus_y = Witness(6); + let w_equal_res = Witness(7); + let w_lt_res = Witness(8); + + let w_i = Witness(9); + let w_j = Witness(10); + let w_ij_oracle = Witness(11); + let w_i_plus_j = Witness(12); + + let equal_opcode = brillig_bytecode::Opcode::BinaryFieldOp { + op: BinaryFieldOp::Cmp(Comparison::Eq), + lhs: RegisterIndex(0), + rhs: RegisterIndex(1), + destination: RegisterIndex(4), + }; + + let less_than_opcode = brillig_bytecode::Opcode::BinaryFieldOp { + op: BinaryFieldOp::Cmp(Comparison::Lt), + lhs: RegisterIndex(0), + rhs: RegisterIndex(1), + destination: RegisterIndex(5), + }; + + let brillig_data = Brillig { + inputs: vec![ + BrilligInputs::Simple(Expression { + mul_terms: vec![], + linear_combinations: vec![(fe_1, w_x), (fe_1, w_y)], + q_c: fe_0, + }), + BrilligInputs::Simple(Expression::default()), + BrilligInputs::Simple(Expression { + mul_terms: vec![], + linear_combinations: vec![(fe_1, w_i), (fe_1, w_j)], + q_c: fe_0, + }), + ], + outputs: vec![ + BrilligOutputs::Simple(w_x_plus_y), + BrilligOutputs::Simple(w_oracle), + BrilligOutputs::Simple(w_i_plus_j), + BrilligOutputs::Simple(w_ij_oracle), + BrilligOutputs::Simple(w_equal_res), + BrilligOutputs::Simple(w_lt_res), + ], + // stack of foreign call/oracle resolutions, starts empty + foreign_call_results: vec![], + bytecode: vec![ + equal_opcode, + less_than_opcode, + // Oracles are named 'foreign calls' in brillig + brillig_bytecode::Opcode::ForeignCall { + function: "invert".into(), + destination: RegisterValueOrArray::RegisterIndex(RegisterIndex(1)), + input: RegisterValueOrArray::RegisterIndex(RegisterIndex(0)), + }, + brillig_bytecode::Opcode::ForeignCall { + function: "invert".into(), + destination: RegisterValueOrArray::RegisterIndex(RegisterIndex(3)), + input: RegisterValueOrArray::RegisterIndex(RegisterIndex(2)), + }, + ], + predicate: None, + }; + + let opcodes = vec![ + Opcode::Brillig(brillig_data), + Opcode::Arithmetic(Expression { + mul_terms: vec![], + linear_combinations: vec![(fe_1, w_x), (fe_1, w_y), (-fe_1, w_z)], + q_c: fe_0, + }), + Opcode::Directive(Directive::Invert { x: w_z, result: w_z_inverse }), + Opcode::Arithmetic(Expression { + mul_terms: vec![(fe_1, w_z, w_z_inverse)], + linear_combinations: vec![], + q_c: -fe_1, + }), + Opcode::Arithmetic(Expression { + mul_terms: vec![], + linear_combinations: vec![(-fe_1, w_oracle), (fe_1, w_z_inverse)], + q_c: fe_0, + }), + ]; + + let pwg = StubbedPwg; + + let mut witness_assignments = BTreeMap::from([ + (Witness(1), FieldElement::from(2u128)), + (Witness(2), FieldElement::from(3u128)), + (Witness(9), FieldElement::from(5u128)), + (Witness(10), FieldElement::from(10u128)), + ]); + let mut blocks = Blocks::default(); + // use the partial witness generation solver with our acir program + let UnresolvedData { unresolved_opcodes, mut unresolved_brilligs, .. } = pwg + .solve(&mut witness_assignments, &mut blocks, opcodes) + .expect("should stall on oracle"); + + assert_eq!(unresolved_opcodes.len(), 0, "brillig should have been removed"); + assert_eq!(unresolved_brilligs.len(), 1, "should have a brillig oracle request"); + + let UnresolvedBrillig { foreign_call_wait_info, mut brillig } = + unresolved_brilligs.remove(0); + assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); + + let x_plus_y_inverse = foreign_call_wait_info.inputs[0].inner.inverse(); + // Alter Brillig oracle opcode + brillig + .foreign_call_results + .push(ForeignCallResult { values: vec![Value::from(x_plus_y_inverse)] }); + + let mut next_opcodes_for_solving = vec![Opcode::Brillig(brillig)]; + next_opcodes_for_solving.extend_from_slice(&unresolved_opcodes[..]); + // After filling data request, continue solving + let UnresolvedData { unresolved_opcodes, mut unresolved_brilligs, .. } = pwg + .solve(&mut witness_assignments, &mut blocks, next_opcodes_for_solving) + .expect("should not stall"); + + assert!(unresolved_opcodes.is_empty(), "should be fully solved"); + assert_eq!(unresolved_brilligs.len(), 1, "should have no unresolved oracles"); + + let UnresolvedBrillig { foreign_call_wait_info, mut brillig } = + unresolved_brilligs.remove(0); + assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); + + let i_plus_j_inverse = foreign_call_wait_info.inputs[0].inner.inverse(); + assert_ne!(x_plus_y_inverse, i_plus_j_inverse); + // Alter Brillig oracle opcode + brillig + .foreign_call_results + .push(ForeignCallResult { values: vec![Value::from(i_plus_j_inverse)] }); + let mut next_opcodes_for_solving = vec![Opcode::Brillig(brillig)]; + next_opcodes_for_solving.extend_from_slice(&unresolved_opcodes[..]); + + // After filling data request, continue solving + let UnresolvedData { unresolved_opcodes, unresolved_brilligs, .. } = pwg + .solve(&mut witness_assignments, &mut blocks, next_opcodes_for_solving) + .expect("should not stall"); + + assert!(unresolved_opcodes.is_empty(), "should be fully solved"); + assert!(unresolved_brilligs.is_empty(), "should have no unresolved oracles"); + } + #[test] fn brillig_oracle_predicate() { // Opcodes below describe the following: From 9987625262e396e2ae025bb462941ed46551300c Mon Sep 17 00:00:00 2001 From: ludamad Date: Thu, 18 May 2023 17:08:39 -0400 Subject: [PATCH 063/125] Remove MIN_REGISTER_VALUES --- acvm/src/pwg/brillig.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index 495cbeaf1..42765a017 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -13,8 +13,6 @@ use crate::{ use super::{directives::insert_witness, get_value}; -const MIN_BRILLIG_REGISTERS: usize = 16; - pub struct BrilligSolver; impl BrilligSolver { @@ -106,11 +104,6 @@ impl BrilligSolver { ))); } - // Ensure at least MIN_BRILLIG_REGISTERS registers are available - while input_register_values.len() < MIN_BRILLIG_REGISTERS { - input_register_values.push(Value::from(0u128)); - } - let input_registers = Registers { inner: input_register_values }; let mut vm = VM::new( input_registers, From 96f6949708d5c3a586820a9e36b3a99af4292c4d Mon Sep 17 00:00:00 2001 From: ludamad Date: Thu, 18 May 2023 17:18:11 -0400 Subject: [PATCH 064/125] Remove register_allocation_indices hack --- acvm/src/pwg/brillig.rs | 3 +-- brillig_bytecode/src/lib.rs | 31 ++++++------------------------- 2 files changed, 7 insertions(+), 27 deletions(-) diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index 42765a017..edfc34bdf 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -109,8 +109,7 @@ impl BrilligSolver { input_registers, input_memory, brillig.bytecode.clone(), - brillig.foreign_call_results.clone(), - None, + brillig.foreign_call_results.clone() ); let vm_status = vm.process_opcodes(); diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 328e1b1a4..db6987582 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -48,24 +48,11 @@ pub struct VM { impl VM { pub fn new( - mut inputs: Registers, + inputs: Registers, memory: Vec, bytecode: Vec, foreign_call_results: Vec, - register_allocation_indices: Option>, ) -> VM { - if let Some(register_allocation_indices) = register_allocation_indices { - // TODO(AD): simplify this all to be done before calling VM.new() - let mut registers_modified = Registers::load(vec![Value::from(0u128)]); - - for (i, register_index) in register_allocation_indices.iter().enumerate() { - let register_value = inputs.get(RegisterIndex(i)); - registers_modified.set(RegisterIndex(*register_index as usize), register_value) - } - - inputs = registers_modified; - } - Self { registers: inputs, program_counter: 0, @@ -141,11 +128,7 @@ impl VM { } Opcode::Return => { if let Some(register) = self.call_stack.pop() { - let label = usize::try_from( - register.inner.try_to_u64().expect("register does not fit into u64"), - ) - .expect("register does not fit into usize"); - self.set_program_counter(label) + self.set_program_counter(register.to_usize()) } else { self.fail("return opcode hit, but callstack already empty") } @@ -313,7 +296,7 @@ mod tests { }; // Start VM - let mut vm = VM::new(input_registers, vec![], vec![opcode], vec![], None); + let mut vm = VM::new(input_registers, vec![], vec![opcode], vec![]); // Process a single VM opcode // @@ -361,7 +344,7 @@ mod tests { opcodes.push(Opcode::Jump { location: 2 }); opcodes.push(Opcode::JumpIf { condition: RegisterIndex(2), location: 3 }); - let mut vm = VM::new(Registers { inner: registers }, vec![], opcodes, vec![], None); + let mut vm = VM::new(Registers { inner: registers }, vec![], opcodes, vec![]); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -406,7 +389,6 @@ mod tests { vec![], vec![jump_opcode, trap_opcode, not_equal_cmp_opcode, jump_if_not_opcode, add_opcode], vec![], - None, ); let status = vm.process_opcode(); @@ -437,7 +419,7 @@ mod tests { let mov_opcode = Opcode::Mov { destination: RegisterIndex(2), source: RegisterIndex(0) }; - let mut vm = VM::new(input_registers, vec![], vec![mov_opcode], vec![], None); + let mut vm = VM::new(input_registers, vec![], vec![mov_opcode], vec![]); let status = vm.process_opcode(); assert_eq!(status, VMStatus::Finished); @@ -498,7 +480,6 @@ mod tests { vec![], vec![equal_opcode, not_equal_opcode, less_than_opcode, less_than_equal_opcode], vec![], - None, ); let status = vm.process_opcode(); @@ -743,7 +724,7 @@ mod tests { } /// Helper to execute brillig code fn brillig_execute_and_get_vm(memory: Vec, opcodes: Vec) -> VM { - let mut vm = VM::new(empty_registers(), memory, opcodes, vec![], None); + let mut vm = VM::new(empty_registers(), memory, opcodes, vec![]); brillig_execute(&mut vm); assert_eq!(vm.call_stack, vec![]); vm From 58d4fd85138bdc4a67eeafa702b4f09dcabb28f0 Mon Sep 17 00:00:00 2001 From: ludamad Date: Thu, 18 May 2023 17:19:25 -0400 Subject: [PATCH 065/125] cargo fmt --- acvm/src/pwg/brillig.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index edfc34bdf..61f0f7635 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -109,7 +109,7 @@ impl BrilligSolver { input_registers, input_memory, brillig.bytecode.clone(), - brillig.foreign_call_results.clone() + brillig.foreign_call_results.clone(), ); let vm_status = vm.process_opcodes(); From 2a54a1a901763b1b54aae644e108814bc6cfff16 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 19 May 2023 15:34:07 -0400 Subject: [PATCH 066/125] cargo fmt and cargo clippy missing usage of solved_brillig_data --- acvm/src/pwg.rs | 61 +++++++++++++++++++++++++++-------------- acvm/src/pwg/brillig.rs | 3 +- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/acvm/src/pwg.rs b/acvm/src/pwg.rs index c2c8eb0b1..f626dbd3f 100644 --- a/acvm/src/pwg.rs +++ b/acvm/src/pwg.rs @@ -2,13 +2,14 @@ use crate::{Language, PartialWitnessGenerator}; use acir::{ - circuit::opcodes::{BlackBoxFuncCall, Opcode, OracleData, Brillig}, + circuit::opcodes::{BlackBoxFuncCall, Brillig, Opcode, OracleData}, native_types::{Expression, Witness, WitnessMap}, BlackBoxFunc, FieldElement, }; use self::{ - arithmetic::ArithmeticSolver, block::Blocks, directives::solve_directives, oracle::OracleSolver, brillig::BrilligSolver, + arithmetic::ArithmeticSolver, block::Blocks, brillig::BrilligSolver, + directives::solve_directives, oracle::OracleSolver, }; use thiserror::Error; @@ -38,7 +39,11 @@ pub enum PartialWitnessGeneratorStatus { /// /// The caller must resolve these opcodes externally and insert the results into the intermediate witness. /// Once this is done, the `PartialWitnessGenerator` can be restarted to solve the remaining opcodes. - RequiresOracleData { required_oracle_data: Vec, unsolved_opcodes: Vec, unresolved_brilligs: Vec, }, + RequiresOracleData { + required_oracle_data: Vec, + unsolved_opcodes: Vec, + unresolved_brilligs: Vec, + }, } #[derive(Debug, PartialEq)] @@ -154,10 +159,13 @@ pub fn solve( // We push those opcodes not solvable to the back as // it could be because the opcodes are out of order, i.e. this assignment // relies on a later opcodes' results - unresolved_opcodes.push(match solved_oracle_data { - Some(oracle_data) => Opcode::Oracle(oracle_data), - None => opcode.clone(), - }); + if let Some(oracle_data) = solved_oracle_data { + unresolved_opcodes.push(Opcode::Oracle(oracle_data)); + } else if let Some(brillig) = solved_brillig_data { + unresolved_opcodes.push(Opcode::Brillig(brillig)); + } else { + unresolved_opcodes.push(opcode.clone()); + } } Err(OpcodeResolutionError::OpcodeNotSolvable(_)) => { unreachable!("ICE - Result should have been converted to GateResolution") @@ -170,7 +178,7 @@ pub fn solve( return Ok(PartialWitnessGeneratorStatus::RequiresOracleData { required_oracle_data: unresolved_oracles, unsolved_opcodes: unresolved_opcodes, - unresolved_brilligs + unresolved_brilligs, }); } // We are stalled because of an opcode being bad @@ -283,17 +291,23 @@ mod test { use std::collections::BTreeMap; use acir::{ + brillig_bytecode::{ + self, BinaryFieldOp, Comparison, ForeignCallResult, RegisterIndex, + RegisterValueOrArray, Value, + }, circuit::{ directives::Directive, - opcodes::{FunctionInput, OracleData, Brillig, BrilligInputs, BrilligOutputs}, + opcodes::{Brillig, BrilligInputs, BrilligOutputs, FunctionInput, OracleData}, Opcode, }, native_types::{Expression, Witness, WitnessMap}, - FieldElement, brillig_bytecode::{self, BinaryFieldOp, Comparison, RegisterIndex, RegisterValueOrArray, ForeignCallResult, Value}, + FieldElement, }; use crate::{ - pwg::{self, block::Blocks, OpcodeResolution, PartialWitnessGeneratorStatus, UnresolvedBrillig}, + pwg::{ + self, block::Blocks, OpcodeResolution, PartialWitnessGeneratorStatus, UnresolvedBrillig, + }, OpcodeResolutionError, PartialWitnessGenerator, }; @@ -413,7 +427,6 @@ mod test { assert_eq!(solver_status, PartialWitnessGeneratorStatus::Solved, "should be fully solved"); } - #[test] fn inversion_brillig_oracle_equivalence() { // Opcodes below describe the following: @@ -501,7 +514,8 @@ mod test { let mut witness_assignments = BTreeMap::from([ (Witness(1), FieldElement::from(2u128)), (Witness(2), FieldElement::from(3u128)), - ]).into(); + ]) + .into(); let mut blocks = Blocks::default(); // use the partial witness generation solver with our acir program let solver_status = pwg::solve(&backend, &mut witness_assignments, &mut blocks, opcodes) @@ -523,8 +537,9 @@ mod test { let mut next_opcodes_for_solving = vec![Opcode::Brillig(brillig)]; next_opcodes_for_solving.extend_from_slice(&unsolved_opcodes[..]); // After filling data request, continue solving - let solver_status = pwg::solve(&backend, &mut witness_assignments, &mut blocks, next_opcodes_for_solving) - .expect("should not stall on oracle"); + let solver_status = + pwg::solve(&backend, &mut witness_assignments, &mut blocks, next_opcodes_for_solving) + .expect("should not stall on oracle"); assert_eq!(solver_status, PartialWitnessGeneratorStatus::Solved, "should be fully solved"); } @@ -634,7 +649,8 @@ mod test { (Witness(2), FieldElement::from(3u128)), (Witness(9), FieldElement::from(5u128)), (Witness(10), FieldElement::from(10u128)), - ]).into(); + ]) + .into(); let mut blocks = Blocks::default(); // use the partial witness generation solver with our acir program let solver_status = pwg::solve(&backend, &mut witness_assignments, &mut blocks, opcodes) @@ -659,8 +675,9 @@ mod test { let mut next_opcodes_for_solving = vec![Opcode::Brillig(brillig)]; next_opcodes_for_solving.extend_from_slice(&unsolved_opcodes[..]); // After filling data request, continue solving - let solver_status = pwg::solve(&backend, &mut witness_assignments, &mut blocks, next_opcodes_for_solving) - .expect("should stall on oracle"); + let solver_status = + pwg::solve(&backend, &mut witness_assignments, &mut blocks, next_opcodes_for_solving) + .expect("should stall on oracle"); let PartialWitnessGeneratorStatus::RequiresOracleData { unsolved_opcodes, mut unresolved_brilligs, .. } = solver_status else { panic!("Should require oracle data") }; @@ -682,8 +699,9 @@ mod test { next_opcodes_for_solving.extend_from_slice(&unsolved_opcodes[..]); // After filling data request, continue solving - let solver_status = pwg::solve(&backend, &mut witness_assignments, &mut blocks, next_opcodes_for_solving) - .expect("should not stall on oracle"); + let solver_status = + pwg::solve(&backend, &mut witness_assignments, &mut blocks, next_opcodes_for_solving) + .expect("should not stall on oracle"); assert_eq!(solver_status, PartialWitnessGeneratorStatus::Solved, "should be fully solved"); } @@ -772,7 +790,8 @@ mod test { let mut witness_assignments = BTreeMap::from([ (Witness(1), FieldElement::from(2u128)), (Witness(2), FieldElement::from(3u128)), - ]).into(); + ]) + .into(); let mut blocks = Blocks::default(); let solver_status = pwg::solve(&backend, &mut witness_assignments, &mut blocks, opcodes) .expect("should not stall on oracle"); diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index 187e12b80..6e316cd6a 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -6,7 +6,8 @@ use acir::{ }; use crate::{ - pwg::{arithmetic::ArithmeticSolver, OpcodeNotSolvable}, OpcodeResolution, OpcodeResolutionError, + pwg::{arithmetic::ArithmeticSolver, OpcodeNotSolvable}, + OpcodeResolution, OpcodeResolutionError, }; use super::{get_value, insert_value}; From 73aceeec89e6ad626f960f93ddd29283c83acb1b Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Fri, 19 May 2023 22:41:38 +0100 Subject: [PATCH 067/125] Add Brillig to cspell --- cspell.json | 1 + 1 file changed, 1 insertion(+) diff --git a/cspell.json b/cspell.json index 0d932419f..8ed13d2e6 100644 --- a/cspell.json +++ b/cspell.json @@ -10,6 +10,7 @@ "Axyz", "arithmetization", "bivariate", + "Brillig", "canonicalize", "coeff", "consts", From 300ac3eb6e0066e845f9aa826688b074b88cd2d3 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Fri, 19 May 2023 22:42:40 +0100 Subject: [PATCH 068/125] Update acir/src/circuit/opcodes.rs --- acir/src/circuit/opcodes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acir/src/circuit/opcodes.rs b/acir/src/circuit/opcodes.rs index f738832f1..2b3f6351d 100644 --- a/acir/src/circuit/opcodes.rs +++ b/acir/src/circuit/opcodes.rs @@ -51,7 +51,7 @@ pub enum BrilligOutputs { pub struct Brillig { pub inputs: Vec, pub outputs: Vec, - // results of oracles/functions external to brillig like a database read + /// Results of oracles/functions external to brillig like a database read pub foreign_call_results: Vec, pub bytecode: Vec, /// Predicate of the Brillig execution - indicates if it should be skipped From 2926363df8064f2df3784211f8825d55e9598a18 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Fri, 19 May 2023 22:43:57 +0100 Subject: [PATCH 069/125] Update acvm/src/compiler/transformers/fallback.rs --- acvm/src/compiler/transformers/fallback.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acvm/src/compiler/transformers/fallback.rs b/acvm/src/compiler/transformers/fallback.rs index e8a41d77e..bcd3d7a23 100644 --- a/acvm/src/compiler/transformers/fallback.rs +++ b/acvm/src/compiler/transformers/fallback.rs @@ -54,7 +54,7 @@ impl FallbackTransformer { acir_supported_opcodes.extend(opcodes_fallback); } } - Opcode::Brillig(_) => todo!(), + Opcode::Brillig(_) => unreachable!("Brillig is not required by the backend and so there is nothing to support"), } } } From f3e1317ecbf4156844541da23cf4f13edd9959fb Mon Sep 17 00:00:00 2001 From: kevaundray Date: Fri, 19 May 2023 22:45:30 +0100 Subject: [PATCH 070/125] Update acvm/src/pwg.rs --- acvm/src/pwg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acvm/src/pwg.rs b/acvm/src/pwg.rs index f626dbd3f..efbf8e356 100644 --- a/acvm/src/pwg.rs +++ b/acvm/src/pwg.rs @@ -249,7 +249,7 @@ fn insert_value( #[derive(Debug, PartialEq, Clone)] pub struct UnresolvedBrillig { pub brillig: Brillig, - // information for if there is a pending foreign call/oracle + // Information for, if there is a pending foreign call/oracle pub foreign_call_wait_info: brillig::ForeignCallWaitInfo, } From 61d136d095ebe048f886a793254dd2016cec9184 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Fri, 19 May 2023 22:54:34 +0100 Subject: [PATCH 071/125] Update brillig_bytecode/src/opcodes.rs --- brillig_bytecode/src/opcodes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 21b66325a..cf8e0aa5f 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -121,7 +121,7 @@ pub enum BinaryFieldOp { Cmp(Comparison), } -// Binary fixed-length integer expressions +/// Binary fixed-length integer expressions #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum BinaryIntOp { Add, From 49cc50af61be5880a454c84dbdbb98d4c6d235ef Mon Sep 17 00:00:00 2001 From: kevaundray Date: Fri, 19 May 2023 22:54:41 +0100 Subject: [PATCH 072/125] Update brillig_bytecode/src/lib.rs --- brillig_bytecode/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index db6987582..b8d2649ad 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -30,7 +30,7 @@ pub enum VMStatus { #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)] pub struct ForeignCallResult { - // resolved foreign call values + /// Resolved foreign call values pub values: Vec, } From ee15a7400a7b08e4288e3056580040caa25bc611 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Fri, 19 May 2023 22:54:52 +0100 Subject: [PATCH 073/125] Update brillig_bytecode/src/opcodes.rs --- brillig_bytecode/src/opcodes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index cf8e0aa5f..ed7b83aed 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -111,7 +111,7 @@ pub enum OracleInput { RegisterIndex(RegisterIndex), } -// Binary fixed-length integer expressions +/// Binary fixed-length integer expressions #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum BinaryFieldOp { Add, From 67396aa8e8655fa8fd4da769bb607478205ddb62 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Fri, 19 May 2023 22:59:13 +0100 Subject: [PATCH 074/125] cargo fmt --- acvm/src/compiler/transformers/fallback.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/acvm/src/compiler/transformers/fallback.rs b/acvm/src/compiler/transformers/fallback.rs index bcd3d7a23..a98e08b01 100644 --- a/acvm/src/compiler/transformers/fallback.rs +++ b/acvm/src/compiler/transformers/fallback.rs @@ -54,7 +54,9 @@ impl FallbackTransformer { acir_supported_opcodes.extend(opcodes_fallback); } } - Opcode::Brillig(_) => unreachable!("Brillig is not required by the backend and so there is nothing to support"), + Opcode::Brillig(_) => unreachable!( + "Brillig is not required by the backend and so there is nothing to support" + ), } } } From ee0b8c319f158e2adc651240c2704853de2bc1e3 Mon Sep 17 00:00:00 2001 From: Tom French Date: Mon, 22 May 2023 17:48:02 +0100 Subject: [PATCH 075/125] chore: comment updates --- brillig_bytecode/src/lib.rs | 10 +++++----- brillig_bytecode/src/opcodes.rs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index b8d2649ad..866dbe5a2 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -1,8 +1,8 @@ -// ACVM is capable of running brillig-bytecode -// This bytecode is ran in the traditional sense -// and allows one to do non-determinism. -// This is a generalization over the fixed directives -// that we have in ACVM. +//! ACVM is capable of running brillig-bytecode +//! This bytecode is run in the traditional sense +//! and allows one to do non-determinism. +//! This is a generalization over the fixed directives +//! that we have in ACVM. mod opcodes; mod registers; diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index ed7b83aed..867779ae7 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -21,7 +21,7 @@ pub enum Opcode { lhs: RegisterIndex, rhs: RegisterIndex, }, - /// Takes the bit_size size integers in registers `lhs` and `rhs` + /// Takes the `bit_size` size integers in registers `lhs` and `rhs` /// Performs the specified binary operation /// and stores the value in the `result` register. BinaryIntOp { @@ -36,7 +36,7 @@ pub enum Opcode { location: Label, }, /// Sets the program counter to the value located at `destination` - /// If the value at condition is non-zero + /// If the value at `condition` is non-zero JumpIf { condition: RegisterIndex, location: Label, From 25de758f00db12be9e7b81c25741224a9c588d60 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 19:22:49 +0100 Subject: [PATCH 076/125] Update brillig_bytecode/src/lib.rs Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- brillig_bytecode/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 866dbe5a2..3f9df718e 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -69,13 +69,16 @@ impl VM { self.status = status.clone(); status } + fn finish(&mut self) -> VMStatus { self.status(VMStatus::Finished) } + /// Waits for a foreign call/oracle fn wait_for_foreign_call(&mut self, function: String, inputs: Vec) -> VMStatus { self.status(VMStatus::ForeignCallWait { function, inputs }) } + fn fail(&mut self, error_msg: &str) -> VMStatus { self.status(VMStatus::Failure); // TODO(AD): Proper error handling @@ -91,12 +94,15 @@ impl VM { ) {} self.status.clone() } + pub fn get_registers(&self) -> &Registers { &self.registers } + pub fn get_memory(&self) -> &Vec { &self.memory } + // Process a single opcode and modify the program counter pub fn process_opcode(&mut self) -> VMStatus { let opcode = &self.bytecode[self.program_counter]; From 3896add5d57c4b0cbc60cd76dde0db3f3f8a2bb2 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Mon, 22 May 2023 19:50:02 +0100 Subject: [PATCH 077/125] rename `Simple` to `Single` and add a comment for Inputs and Outputs --- acir/src/circuit/opcodes.rs | 6 +++++- acvm/src/pwg.rs | 14 +++++++------- acvm/src/pwg/brillig.rs | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/acir/src/circuit/opcodes.rs b/acir/src/circuit/opcodes.rs index 2b3f6351d..f8fe24980 100644 --- a/acir/src/circuit/opcodes.rs +++ b/acir/src/circuit/opcodes.rs @@ -35,12 +35,16 @@ pub enum Opcode { Brillig(Brillig), } +/// Inputs for the Brillig VM. These are the initial inputs +/// that the Brillig VM will use to start. #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] pub enum BrilligInputs { - Simple(Expression), + Single(Expression), Array(Vec), } +/// Outputs for the Brillig VM. Once the VM has completed +/// execution, this will the object that is returned. #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] pub enum BrilligOutputs { Simple(Witness), diff --git a/acvm/src/pwg.rs b/acvm/src/pwg.rs index efbf8e356..151b87b3f 100644 --- a/acvm/src/pwg.rs +++ b/acvm/src/pwg.rs @@ -461,12 +461,12 @@ mod test { let brillig_data = Brillig { inputs: vec![ - BrilligInputs::Simple(Expression { + BrilligInputs::Single(Expression { mul_terms: vec![], linear_combinations: vec![(fe_1, w_x), (fe_1, w_y)], q_c: fe_0, }), - BrilligInputs::Simple(Expression::default()), + BrilligInputs::Single(Expression::default()), ], outputs: vec![ BrilligOutputs::Simple(w_x_plus_y), @@ -582,13 +582,13 @@ mod test { let brillig_data = Brillig { inputs: vec![ - BrilligInputs::Simple(Expression { + BrilligInputs::Single(Expression { mul_terms: vec![], linear_combinations: vec![(fe_1, w_x), (fe_1, w_y)], q_c: fe_0, }), - BrilligInputs::Simple(Expression::default()), - BrilligInputs::Simple(Expression { + BrilligInputs::Single(Expression::default()), + BrilligInputs::Single(Expression { mul_terms: vec![], linear_combinations: vec![(fe_1, w_i), (fe_1, w_j)], q_c: fe_0, @@ -742,12 +742,12 @@ mod test { let brillig_opcode = Opcode::Brillig(Brillig { inputs: vec![ - BrilligInputs::Simple(Expression { + BrilligInputs::Single(Expression { mul_terms: vec![], linear_combinations: vec![(fe_1, w_x), (fe_1, w_y)], q_c: fe_0, }), - BrilligInputs::Simple(Expression::default()), + BrilligInputs::Single(Expression::default()), ], outputs: vec![ BrilligOutputs::Simple(w_x_plus_y), diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index 6e316cd6a..7be7b7b3c 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -55,7 +55,7 @@ impl BrilligSolver { let mut input_memory: Vec = Vec::new(); for input in &brillig.inputs { match input { - BrilligInputs::Simple(expr) => { + BrilligInputs::Single(expr) => { // TODO: switch this to `get_value` and map the err let solve = ArithmeticSolver::evaluate(expr, initial_witness); if let Some(value) = solve.to_const() { @@ -93,7 +93,7 @@ impl BrilligSolver { let brillig_input = brillig.inputs.last().expect("Infallible: cannot reach this point if no inputs"); let expr = match brillig_input { - BrilligInputs::Simple(expr) => expr, + BrilligInputs::Single(expr) => expr, BrilligInputs::Array(expr_arr) => { expr_arr.last().expect("Infallible: cannot reach this point if no inputs") } From 0fd44d75f3892e2b8a1f375597145b9038e232ae Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 19:53:14 +0100 Subject: [PATCH 078/125] Update acvm/src/pwg.rs --- acvm/src/pwg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acvm/src/pwg.rs b/acvm/src/pwg.rs index 151b87b3f..291cc612b 100644 --- a/acvm/src/pwg.rs +++ b/acvm/src/pwg.rs @@ -432,7 +432,7 @@ mod test { // Opcodes below describe the following: // fn main(x : Field, y : pub Field) { // let z = x + y; - // constrain 1/z == Oracle("inverse", x + y); + // assert( 1/z == Oracle("inverse", x + y) ); // } let fe_0 = FieldElement::zero(); let fe_1 = FieldElement::one(); From 8ad2713d5848f88620704f849e487f9400080bb5 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:29:48 +0100 Subject: [PATCH 079/125] Update brillig_bytecode/src/opcodes.rs --- brillig_bytecode/src/opcodes.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 867779ae7..99e9b6b4f 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -60,7 +60,8 @@ pub enum Opcode { /// this is intended for things like state tree reads, and shouldn't be confused /// with e.g. blockchain price oracles. ForeignCall { - // Interpreted by simulator context + /// Interpreted by caller context, ie this will have different meanings depending on + /// who the caller is. function: String, // Destination register (may be a memory pointer). destination: RegisterValueOrArray, From 8e1335c1d7b16261a130feaf3ae2e3b9b2d289fb Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:29:58 +0100 Subject: [PATCH 080/125] Update brillig_bytecode/src/opcodes.rs --- brillig_bytecode/src/opcodes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 99e9b6b4f..4f6eec56d 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -63,7 +63,7 @@ pub enum Opcode { /// Interpreted by caller context, ie this will have different meanings depending on /// who the caller is. function: String, - // Destination register (may be a memory pointer). + /// Destination register (may be a memory pointer). destination: RegisterValueOrArray, // Input register (may be a memory pointer). input: RegisterValueOrArray, From c28c7fd1802c44a8d530add1e75339050611e1bc Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:30:06 +0100 Subject: [PATCH 081/125] Update brillig_bytecode/src/opcodes.rs --- brillig_bytecode/src/opcodes.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 4f6eec56d..3d94d9a72 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -131,11 +131,16 @@ pub enum BinaryIntOp { SignedDiv, UnsignedDiv, Cmp(Comparison), - And, // (&) Bitwise AND - Or, // (|) Bitwise OR - Xor, // (^) Bitwise XOR - Shl, // (<<) Shift left - Shr, // (>>) Shift right + /// (&) Bitwise AND + And, + /// (|) Bitwise OR + Or, + /// (^) Bitwise XOR + Xor, + /// (<<) Shift left + Shl, + /// (>>) Shift right + Shr, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] From e2d2ae1729ef63d38c655dde8c4d778c7eaa5940 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:30:16 +0100 Subject: [PATCH 082/125] Update brillig_bytecode/src/opcodes.rs --- brillig_bytecode/src/opcodes.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 3d94d9a72..13348d641 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -145,9 +145,12 @@ pub enum BinaryIntOp { #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum Comparison { - Eq, //(==) equal - Lt, //(<) field less - Lte, //(<=) field less or equal + /// (==) equal + Eq, + /// (<) Field less than + Lt, + /// (<=) field less or equal + Lte, } impl BinaryFieldOp { From 6e26e238d60d1a6a5ac5f164982d8e60efec6922 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:30:25 +0100 Subject: [PATCH 083/125] Update brillig_bytecode/src/opcodes.rs --- brillig_bytecode/src/opcodes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 13348d641..bf056df22 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -65,7 +65,7 @@ pub enum Opcode { function: String, /// Destination register (may be a memory pointer). destination: RegisterValueOrArray, - // Input register (may be a memory pointer). + /// Input register (may be a memory pointer). input: RegisterValueOrArray, }, Mov { From 7783b5a49a1292576fad3cd810b9f50b194d0f0f Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:30:35 +0100 Subject: [PATCH 084/125] Update brillig_bytecode/src/value.rs --- brillig_bytecode/src/value.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs index 7e27afaf2..91916e26d 100644 --- a/brillig_bytecode/src/value.rs +++ b/brillig_bytecode/src/value.rs @@ -10,7 +10,7 @@ pub enum Typ { Signed { bit_size: u32 }, } -/// Value represents a Value in the VM +/// Value represents the base descriptor for a value in the VM. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Value { pub inner: FieldElement, From ea164bc070bc02f1393c480badaa82872d7e977f Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:30:48 +0100 Subject: [PATCH 085/125] Update brillig_bytecode/src/lib.rs --- brillig_bytecode/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 3f9df718e..92993d0f2 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -21,9 +21,9 @@ pub enum VMStatus { InProgress, Failure, ForeignCallWait { - // Interpreted by simulator context + /// Interpreted by simulator context function: String, - // Input values + /// Input values inputs: Vec, }, } From 2e3506ebe973a894633e04cdeb26535005382960 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:30:57 +0100 Subject: [PATCH 086/125] Update brillig_bytecode/src/lib.rs --- brillig_bytecode/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 92993d0f2..96fa037f0 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -78,7 +78,9 @@ impl VM { fn wait_for_foreign_call(&mut self, function: String, inputs: Vec) -> VMStatus { self.status(VMStatus::ForeignCallWait { function, inputs }) } - +/// Sets the current status of the VM to `fail`. +/// Indicating that the VM encoutered a `Trap` Opcode +/// or an invalid state. fn fail(&mut self, error_msg: &str) -> VMStatus { self.status(VMStatus::Failure); // TODO(AD): Proper error handling From c90042836c2df1ca93c69f8d4f406bec259eefe1 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:31:06 +0100 Subject: [PATCH 087/125] Update brillig_bytecode/src/lib.rs --- brillig_bytecode/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 96fa037f0..e60534a72 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -206,7 +206,7 @@ impl VM { } } } - +/// Returns the current value of the program counter. pub fn program_counter(self) -> usize { self.program_counter } From 8c619f8dabb24bfa0207244ed7e04ee8d3df8653 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:31:15 +0100 Subject: [PATCH 088/125] Update brillig_bytecode/src/lib.rs --- brillig_bytecode/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index e60534a72..6a685e619 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -96,7 +96,7 @@ impl VM { ) {} self.status.clone() } - +/// Returns all of the registers in the VM. pub fn get_registers(&self) -> &Registers { &self.registers } From d7a685ccb26b3c830e1f60291ed16280ba849351 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:31:22 +0100 Subject: [PATCH 089/125] Update brillig_bytecode/src/value.rs --- brillig_bytecode/src/value.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs index 91916e26d..912f95ac5 100644 --- a/brillig_bytecode/src/value.rs +++ b/brillig_bytecode/src/value.rs @@ -24,6 +24,9 @@ impl Value { pub fn to_u128(&self) -> u128 { self.inner.to_u128() } + /// Converts `Value` into a u64 and then casts it into a usize. + /// Panics: If `Value` cannot fit into a u64 or `Value` does + //// not fit into a usize. pub fn to_usize(&self) -> usize { usize::try_from(self.inner.try_to_u64().expect("register does not fit into u64")) .expect("register does not fit into usize") From 6960c2e52a71a0a7ec1727307e99c668d8eb3df9 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:31:43 +0100 Subject: [PATCH 090/125] Update brillig_bytecode/src/registers.rs --- brillig_bytecode/src/registers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/src/registers.rs b/brillig_bytecode/src/registers.rs index 417b7863e..09009290d 100644 --- a/brillig_bytecode/src/registers.rs +++ b/brillig_bytecode/src/registers.rs @@ -34,7 +34,7 @@ impl Iterator for RegistersIntoIterator { Some(self.registers.inner[self.index - 1]) } } - +RegisterIndex refers to the index of a register in the VM. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub struct RegisterIndex(pub usize); From 32495c4f5e6d9d333fa6746c503c10d5eb9dda9c Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:31:53 +0100 Subject: [PATCH 091/125] Update brillig_bytecode/src/value.rs --- brillig_bytecode/src/value.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs index 912f95ac5..bcab9c30a 100644 --- a/brillig_bytecode/src/value.rs +++ b/brillig_bytecode/src/value.rs @@ -21,6 +21,8 @@ impl Value { pub fn is_zero(&self) -> bool { self.inner.is_zero() } + /// Converts `Value` into a u128. + // TODO: Check what happens if `Value` cannot fit into a u128 pub fn to_u128(&self) -> u128 { self.inner.to_u128() } From 8ec304ae1cc913c3fd9a07c026865d5f9e22ac4a Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:32:01 +0100 Subject: [PATCH 092/125] Update brillig_bytecode/src/opcodes.rs --- brillig_bytecode/src/opcodes.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index bf056df22..f4e070201 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -107,10 +107,6 @@ impl Opcode { } } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub enum OracleInput { - RegisterIndex(RegisterIndex), -} /// Binary fixed-length integer expressions #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] From 26eddd9421a96f3f86e1fefa25c99826e5f6a000 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:32:10 +0100 Subject: [PATCH 093/125] Update brillig_bytecode/src/opcodes.rs --- brillig_bytecode/src/opcodes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index f4e070201..1ff68728c 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -45,8 +45,8 @@ pub enum Opcode { Jump { location: Label, }, - // We don't support dynamic jumps or calls - // See https://github.com/ethereum/aleth/issues/3404 for reasoning + /// We don't support dynamic jumps or calls + /// See https://github.com/ethereum/aleth/issues/3404 for reasoning Call { location: Label, }, From 0f9f92acd9c1e4a23093694f22f5f6a608cebb57 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:32:41 +0100 Subject: [PATCH 094/125] Update brillig_bytecode/src/lib.rs --- brillig_bytecode/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 6a685e619..5a5446c76 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -69,7 +69,8 @@ impl VM { self.status = status.clone(); status } - +/// Sets the current status of the VM to `finished`. +/// Indicating that the VM has completed execution. fn finish(&mut self) -> VMStatus { self.status(VMStatus::Finished) } From de659d91c38be153a5ebf2d892e42d0807e99f3d Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:32:50 +0100 Subject: [PATCH 095/125] Update brillig_bytecode/src/lib.rs --- brillig_bytecode/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 5a5446c76..1bba3974e 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -64,7 +64,7 @@ impl VM { call_stack: Vec::new(), } } - +/// Returns the current status of the VM. fn status(&mut self, status: VMStatus) -> VMStatus { self.status = status.clone(); status From 209d2a5111d907f9cda0045db12daf2d884fbee3 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:32:58 +0100 Subject: [PATCH 096/125] Update brillig_bytecode/src/lib.rs --- brillig_bytecode/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 1bba3974e..74ba8e512 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -106,7 +106,7 @@ impl VM { &self.memory } - // Process a single opcode and modify the program counter + /// Process a single opcode and modify the program counter. pub fn process_opcode(&mut self) -> VMStatus { let opcode = &self.bytecode[self.program_counter]; match opcode { From 0cafa26c204c330edd8246c1610e0d7b9be0b2c0 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:33:07 +0100 Subject: [PATCH 097/125] Update brillig_bytecode/src/lib.rs --- brillig_bytecode/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 74ba8e512..1667659b7 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -75,7 +75,8 @@ impl VM { self.status(VMStatus::Finished) } - /// Waits for a foreign call/oracle + /// Sets the status of the VM to `ForeignCallWait`. + /// Indicating that the VM is no waiting for a foreign call to be resolved. fn wait_for_foreign_call(&mut self, function: String, inputs: Vec) -> VMStatus { self.status(VMStatus::ForeignCallWait { function, inputs }) } From 99503875c9c3226532c227e1fa612112d37b34ec Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:33:14 +0100 Subject: [PATCH 098/125] Update acvm/src/pwg.rs --- acvm/src/pwg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acvm/src/pwg.rs b/acvm/src/pwg.rs index 291cc612b..be22c918d 100644 --- a/acvm/src/pwg.rs +++ b/acvm/src/pwg.rs @@ -548,7 +548,7 @@ mod test { // Opcodes below describe the following: // fn main(x : Field, y : pub Field) { // let z = x + y; - // constrain 1/z == Oracle("inverse", x + y); + // assert( 1/z == Oracle("inverse", x + y) ); // } let fe_0 = FieldElement::zero(); let fe_1 = FieldElement::one(); From 0a99959905df731e6145ef9094efa3d5b98e132c Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Mon, 22 May 2023 20:34:22 +0100 Subject: [PATCH 099/125] add `RegisterIndex` comment as doc comment --- brillig_bytecode/src/registers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/src/registers.rs b/brillig_bytecode/src/registers.rs index 09009290d..629ba6d02 100644 --- a/brillig_bytecode/src/registers.rs +++ b/brillig_bytecode/src/registers.rs @@ -34,7 +34,7 @@ impl Iterator for RegistersIntoIterator { Some(self.registers.inner[self.index - 1]) } } -RegisterIndex refers to the index of a register in the VM. +/// RegisterIndex refers to the index of a register in the VM. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub struct RegisterIndex(pub usize); From 3a95c22df2ba255a80b37c62859ba7cfa855e1a0 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Mon, 22 May 2023 20:34:28 +0100 Subject: [PATCH 100/125] cargo fmt --- brillig_bytecode/src/lib.rs | 16 ++++++++-------- brillig_bytecode/src/opcodes.rs | 15 +++++++-------- brillig_bytecode/src/value.rs | 2 +- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 1667659b7..7f9625ca7 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -64,13 +64,13 @@ impl VM { call_stack: Vec::new(), } } -/// Returns the current status of the VM. + /// Returns the current status of the VM. fn status(&mut self, status: VMStatus) -> VMStatus { self.status = status.clone(); status } -/// Sets the current status of the VM to `finished`. -/// Indicating that the VM has completed execution. + /// Sets the current status of the VM to `finished`. + /// Indicating that the VM has completed execution. fn finish(&mut self) -> VMStatus { self.status(VMStatus::Finished) } @@ -80,9 +80,9 @@ impl VM { fn wait_for_foreign_call(&mut self, function: String, inputs: Vec) -> VMStatus { self.status(VMStatus::ForeignCallWait { function, inputs }) } -/// Sets the current status of the VM to `fail`. -/// Indicating that the VM encoutered a `Trap` Opcode -/// or an invalid state. + /// Sets the current status of the VM to `fail`. + /// Indicating that the VM encoutered a `Trap` Opcode + /// or an invalid state. fn fail(&mut self, error_msg: &str) -> VMStatus { self.status(VMStatus::Failure); // TODO(AD): Proper error handling @@ -98,7 +98,7 @@ impl VM { ) {} self.status.clone() } -/// Returns all of the registers in the VM. + /// Returns all of the registers in the VM. pub fn get_registers(&self) -> &Registers { &self.registers } @@ -208,7 +208,7 @@ impl VM { } } } -/// Returns the current value of the program counter. + /// Returns the current value of the program counter. pub fn program_counter(self) -> usize { self.program_counter } diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 1ff68728c..421e37ab5 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -60,7 +60,7 @@ pub enum Opcode { /// this is intended for things like state tree reads, and shouldn't be confused /// with e.g. blockchain price oracles. ForeignCall { - /// Interpreted by caller context, ie this will have different meanings depending on + /// Interpreted by caller context, ie this will have different meanings depending on /// who the caller is. function: String, /// Destination register (may be a memory pointer). @@ -107,7 +107,6 @@ impl Opcode { } } - /// Binary fixed-length integer expressions #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum BinaryFieldOp { @@ -128,24 +127,24 @@ pub enum BinaryIntOp { UnsignedDiv, Cmp(Comparison), /// (&) Bitwise AND - And, + And, /// (|) Bitwise OR - Or, + Or, /// (^) Bitwise XOR - Xor, + Xor, /// (<<) Shift left Shl, /// (>>) Shift right - Shr, + Shr, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum Comparison { /// (==) equal Eq, - /// (<) Field less than + /// (<) Field less than Lt, - /// (<=) field less or equal + /// (<=) field less or equal Lte, } diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs index bcab9c30a..075b52ddf 100644 --- a/brillig_bytecode/src/value.rs +++ b/brillig_bytecode/src/value.rs @@ -28,7 +28,7 @@ impl Value { } /// Converts `Value` into a u64 and then casts it into a usize. /// Panics: If `Value` cannot fit into a u64 or `Value` does - //// not fit into a usize. + //// not fit into a usize. pub fn to_usize(&self) -> usize { usize::try_from(self.inner.try_to_u64().expect("register does not fit into u64")) .expect("register does not fit into usize") From eeb47d5a0508288eb9ab863cd8f45ba900c22326 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:35:24 +0100 Subject: [PATCH 101/125] Update acvm/src/pwg.rs --- acvm/src/pwg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acvm/src/pwg.rs b/acvm/src/pwg.rs index be22c918d..07fad7cd5 100644 --- a/acvm/src/pwg.rs +++ b/acvm/src/pwg.rs @@ -712,7 +712,7 @@ mod test { // let z = x + y; // let z_inverse = 1/z // if cond { - // constrain z_inverse == Oracle("inverse", x + y); + // assert( z_inverse == Oracle("inverse", x + y) ); // } // } let fe_0 = FieldElement::zero(); From 3d314db9ab36831c9828bf9f01bbdb24b8486b8f Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Mon, 22 May 2023 20:44:39 +0100 Subject: [PATCH 102/125] move brillig constructs into ACIR into its own module -- This will replace directives --- acir/src/circuit/brillig.rs | 30 ++++++++++++++++++++++++++++++ acir/src/circuit/mod.rs | 1 + 2 files changed, 31 insertions(+) create mode 100644 acir/src/circuit/brillig.rs diff --git a/acir/src/circuit/brillig.rs b/acir/src/circuit/brillig.rs new file mode 100644 index 000000000..c6ebeca87 --- /dev/null +++ b/acir/src/circuit/brillig.rs @@ -0,0 +1,30 @@ +use crate::native_types::{Expression, Witness}; +use brillig_bytecode::ForeignCallResult; +use serde::{Deserialize, Serialize}; + +/// Inputs for the Brillig VM. These are the initial inputs +/// that the Brillig VM will use to start. +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] +pub enum BrilligInputs { + Single(Expression), + Array(Vec), +} + +/// Outputs for the Brillig VM. Once the VM has completed +/// execution, this will the object that is returned. +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] +pub enum BrilligOutputs { + Simple(Witness), + Array(Vec), +} + +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] +pub struct Brillig { + pub inputs: Vec, + pub outputs: Vec, + /// Results of oracles/functions external to brillig like a database read + pub foreign_call_results: Vec, + pub bytecode: Vec, + /// Predicate of the Brillig execution - indicates if it should be skipped + pub predicate: Option, +} diff --git a/acir/src/circuit/mod.rs b/acir/src/circuit/mod.rs index cd79c29d9..6939b80f0 100644 --- a/acir/src/circuit/mod.rs +++ b/acir/src/circuit/mod.rs @@ -1,4 +1,5 @@ pub mod black_box_functions; +pub mod brillig; pub mod directives; pub mod opcodes; From 8819da19e20f7d0ba2fccb71da3dd7e26bf760cf Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Mon, 22 May 2023 20:44:53 +0100 Subject: [PATCH 103/125] modify rest of the codebase to match this --- acir/src/circuit/opcodes.rs | 36 +++++------------------------------- acvm/src/pwg.rs | 6 ++++-- acvm/src/pwg/brillig.rs | 2 +- 3 files changed, 10 insertions(+), 34 deletions(-) diff --git a/acir/src/circuit/opcodes.rs b/acir/src/circuit/opcodes.rs index f8fe24980..db2932eb1 100644 --- a/acir/src/circuit/opcodes.rs +++ b/acir/src/circuit/opcodes.rs @@ -1,7 +1,8 @@ -use super::directives::{Directive, LogInfo, QuotientDirective}; -use crate::native_types::{Expression, Witness}; - -use brillig_bytecode::ForeignCallResult; +use super::{ + brillig::Brillig, + directives::{Directive, LogInfo, QuotientDirective}, +}; +use crate::native_types::Expression; use serde::{Deserialize, Serialize}; mod black_box_function_call; @@ -35,33 +36,6 @@ pub enum Opcode { Brillig(Brillig), } -/// Inputs for the Brillig VM. These are the initial inputs -/// that the Brillig VM will use to start. -#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] -pub enum BrilligInputs { - Single(Expression), - Array(Vec), -} - -/// Outputs for the Brillig VM. Once the VM has completed -/// execution, this will the object that is returned. -#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] -pub enum BrilligOutputs { - Simple(Witness), - Array(Vec), -} - -#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] -pub struct Brillig { - pub inputs: Vec, - pub outputs: Vec, - /// Results of oracles/functions external to brillig like a database read - pub foreign_call_results: Vec, - pub bytecode: Vec, - /// Predicate of the Brillig execution - indicates if it should be skipped - pub predicate: Option, -} - impl Opcode { // TODO We can add a domain separator by doing something like: // TODO concat!("directive:", directive.name) diff --git a/acvm/src/pwg.rs b/acvm/src/pwg.rs index 07fad7cd5..0bbdc866c 100644 --- a/acvm/src/pwg.rs +++ b/acvm/src/pwg.rs @@ -2,7 +2,8 @@ use crate::{Language, PartialWitnessGenerator}; use acir::{ - circuit::opcodes::{BlackBoxFuncCall, Brillig, Opcode, OracleData}, + circuit::brillig::Brillig, + circuit::opcodes::{BlackBoxFuncCall, Opcode, OracleData}, native_types::{Expression, Witness, WitnessMap}, BlackBoxFunc, FieldElement, }; @@ -296,8 +297,9 @@ mod test { RegisterValueOrArray, Value, }, circuit::{ + brillig::{Brillig, BrilligInputs, BrilligOutputs}, directives::Directive, - opcodes::{Brillig, BrilligInputs, BrilligOutputs, FunctionInput, OracleData}, + opcodes::{FunctionInput, OracleData}, Opcode, }, native_types::{Expression, Witness, WitnessMap}, diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index 7be7b7b3c..c3348e1c8 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -1,6 +1,6 @@ use acir::{ brillig_bytecode::{RegisterIndex, Registers, VMStatus, Value, VM}, - circuit::opcodes::{Brillig, BrilligInputs, BrilligOutputs}, + circuit::brillig::{Brillig, BrilligInputs, BrilligOutputs}, native_types::WitnessMap, FieldElement, }; From 92adb2f00102ce3a3726486fcfbc10fd41e8fd8e Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:56:51 +0100 Subject: [PATCH 104/125] Update brillig_bytecode/src/opcodes.rs --- brillig_bytecode/src/opcodes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index 421e37ab5..e1da5645d 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -80,7 +80,7 @@ pub enum Opcode { destination_pointer: RegisterIndex, source: RegisterIndex, }, - /// Used if execution fails during evaluation + /// Used to denote execution failure Trap, /// Stop execution Stop, From 93b987baea008653ea49468fc2f0ee0af1b42334 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 22 May 2023 20:58:13 +0100 Subject: [PATCH 105/125] Update brillig_bytecode/src/opcodes.rs --- brillig_bytecode/src/opcodes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_bytecode/src/opcodes.rs index e1da5645d..831fa84dd 100644 --- a/brillig_bytecode/src/opcodes.rs +++ b/brillig_bytecode/src/opcodes.rs @@ -107,7 +107,7 @@ impl Opcode { } } -/// Binary fixed-length integer expressions +/// Binary fixed-length field expressions #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum BinaryFieldOp { Add, From 8c6614cfa56be6456cbc63072c2bbd446daf8002 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 22 May 2023 17:13:03 -0400 Subject: [PATCH 106/125] remove unused UnresolvedData struct --- acvm/src/pwg.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/acvm/src/pwg.rs b/acvm/src/pwg.rs index 0bbdc866c..23213ec2f 100644 --- a/acvm/src/pwg.rs +++ b/acvm/src/pwg.rs @@ -254,12 +254,6 @@ pub struct UnresolvedBrillig { pub foreign_call_wait_info: brillig::ForeignCallWaitInfo, } -pub struct UnresolvedData { - pub unresolved_opcodes: Vec, - pub unresolved_oracles: Vec, - pub unresolved_brilligs: Vec, -} - #[deprecated( note = "For backwards compatibility, this method allows you to derive _sensible_ defaults for opcode support based on the np language. \n Backends should simply specify what they support." )] From 9607af38a5ad983cfeca119c002c5143f068de0e Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 22 May 2023 17:20:53 -0400 Subject: [PATCH 107/125] chore: move process binary int op to more appropriate part of file --- brillig_bytecode/src/lib.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 7f9625ca7..2fb2699d3 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -230,24 +230,6 @@ impl VM { self.status.clone() } - /// Process a binary operation. - /// This method will not modify the program counter. - fn process_binary_int_op( - &mut self, - op: BinaryIntOp, - bit_size: u32, - lhs: RegisterIndex, - rhs: RegisterIndex, - result: RegisterIndex, - ) { - let lhs_value = self.registers.get(lhs); - let rhs_value = self.registers.get(rhs); - - let result_value = op.evaluate_int(lhs_value.to_u128(), rhs_value.to_u128(), bit_size); - - self.registers.set(result, result_value.into()); - } - fn resolve_foreign_call_input(&self, input: RegisterValueOrArray) -> Vec { match input { RegisterValueOrArray::RegisterIndex(index) => vec![self.registers.get(index)], @@ -274,6 +256,24 @@ impl VM { self.registers.set(result, result_value.into()) } + + /// Process a binary operation. + /// This method will not modify the program counter. + fn process_binary_int_op( + &mut self, + op: BinaryIntOp, + bit_size: u32, + lhs: RegisterIndex, + rhs: RegisterIndex, + result: RegisterIndex, + ) { + let lhs_value = self.registers.get(lhs); + let rhs_value = self.registers.get(rhs); + + let result_value = op.evaluate_int(lhs_value.to_u128(), rhs_value.to_u128(), bit_size); + + self.registers.set(result, result_value.into()); + } } pub struct VMOutputState { From 6a1262f305804ba277c8d838410e8d3cb1813674 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 22 May 2023 17:21:31 -0400 Subject: [PATCH 108/125] chore: fix comment on wait_for_foreign_call --- brillig_bytecode/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 2fb2699d3..344a2a32d 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -76,7 +76,7 @@ impl VM { } /// Sets the status of the VM to `ForeignCallWait`. - /// Indicating that the VM is no waiting for a foreign call to be resolved. + /// Indicating that the VM is not waiting for a foreign call to be resolved. fn wait_for_foreign_call(&mut self, function: String, inputs: Vec) -> VMStatus { self.status(VMStatus::ForeignCallWait { function, inputs }) } From bd80c695fc39ea8da74df06eaf9ecf7bf1d3b4f2 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 22 May 2023 21:01:27 -0400 Subject: [PATCH 109/125] now waiting --- brillig_bytecode/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 344a2a32d..2b74d9d41 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -76,7 +76,7 @@ impl VM { } /// Sets the status of the VM to `ForeignCallWait`. - /// Indicating that the VM is not waiting for a foreign call to be resolved. + /// Indicating that the VM is now waiting for a foreign call to be resolved. fn wait_for_foreign_call(&mut self, function: String, inputs: Vec) -> VMStatus { self.status(VMStatus::ForeignCallWait { function, inputs }) } From 35d81e421e9361d84cfdd0f1f0df84fd2fa6bdc0 Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 23 May 2023 06:29:53 +0100 Subject: [PATCH 110/125] chore: whitespace changes --- brillig_bytecode/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 2b74d9d41..69073d74e 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -64,11 +64,13 @@ impl VM { call_stack: Vec::new(), } } + /// Returns the current status of the VM. fn status(&mut self, status: VMStatus) -> VMStatus { self.status = status.clone(); status } + /// Sets the current status of the VM to `finished`. /// Indicating that the VM has completed execution. fn finish(&mut self) -> VMStatus { @@ -80,6 +82,7 @@ impl VM { fn wait_for_foreign_call(&mut self, function: String, inputs: Vec) -> VMStatus { self.status(VMStatus::ForeignCallWait { function, inputs }) } + /// Sets the current status of the VM to `fail`. /// Indicating that the VM encoutered a `Trap` Opcode /// or an invalid state. @@ -98,6 +101,7 @@ impl VM { ) {} self.status.clone() } + /// Returns all of the registers in the VM. pub fn get_registers(&self) -> &Registers { &self.registers @@ -208,6 +212,7 @@ impl VM { } } } + /// Returns the current value of the program counter. pub fn program_counter(self) -> usize { self.program_counter From 4bec449482bec73a1abb53bed073d8a42d83d9da Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 23 May 2023 06:31:00 +0100 Subject: [PATCH 111/125] chore: whitespace changes --- brillig_bytecode/src/registers.rs | 3 +++ brillig_bytecode/src/value.rs | 2 ++ 2 files changed, 5 insertions(+) diff --git a/brillig_bytecode/src/registers.rs b/brillig_bytecode/src/registers.rs index 629ba6d02..3774b15cc 100644 --- a/brillig_bytecode/src/registers.rs +++ b/brillig_bytecode/src/registers.rs @@ -49,10 +49,12 @@ impl Registers { pub fn load(values: Vec) -> Registers { Self { inner: values } } + /// Gets the values at register with address `index` pub fn get(&self, register: RegisterIndex) -> Value { self.inner[register.inner()] } + /// Sets the value at register with address `index` to `value` pub fn set(&mut self, index: RegisterIndex, value: Value) { if index.inner() >= self.inner.len() { @@ -61,6 +63,7 @@ impl Registers { } self.inner[index.inner()] = value } + /// Returns all of the values in the register /// This should be done at the end of the VM /// run and will be useful for mapping the values diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs index 075b52ddf..2622d1380 100644 --- a/brillig_bytecode/src/value.rs +++ b/brillig_bytecode/src/value.rs @@ -21,11 +21,13 @@ impl Value { pub fn is_zero(&self) -> bool { self.inner.is_zero() } + /// Converts `Value` into a u128. // TODO: Check what happens if `Value` cannot fit into a u128 pub fn to_u128(&self) -> u128 { self.inner.to_u128() } + /// Converts `Value` into a u64 and then casts it into a usize. /// Panics: If `Value` cannot fit into a u64 or `Value` does //// not fit into a usize. From 342c3893f5785055609ae5130c89b7e2cb79aec2 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 23 May 2023 13:34:05 +0100 Subject: [PATCH 112/125] Apply suggestions from code review --- brillig_bytecode/Cargo.toml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/brillig_bytecode/Cargo.toml b/brillig_bytecode/Cargo.toml index 3b1c9f94f..2a0f2cd67 100644 --- a/brillig_bytecode/Cargo.toml +++ b/brillig_bytecode/Cargo.toml @@ -1,15 +1,20 @@ [package] name = "brillig_bytecode" +description = "The virtual machine that processes Brillig bytecode, used to introduce non-determinism to the ACVM" version = "0.1.0" -edition = "2021" +authors.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] acir_field.workspace = true serde.workspace = true -num-bigint = "0.4" +num-bigint.workspace = true [features] +default = ["bn254"] bn254 = ["acir_field/bn254"] bls12_381 = ["acir_field/bls12_381"] From 3002f99b4ff7299acd77cf8f7e40c6835c87bdd6 Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 23 May 2023 13:36:22 +0100 Subject: [PATCH 113/125] fix: properly handle `brillig_bytecode` default feature --- acir/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/acir/Cargo.toml b/acir/Cargo.toml index 76be2cc7f..ea72a622e 100644 --- a/acir/Cargo.toml +++ b/acir/Cargo.toml @@ -12,7 +12,7 @@ rust-version.workspace = true [dependencies] acir_field.workspace = true serde.workspace = true -brillig_bytecode = { version = "0.1.0", path = "../brillig_bytecode" } +brillig_bytecode = { version = "0.1.0", path = "../brillig_bytecode", default-features = false } thiserror.workspace = true rmp-serde = "1.1.0" @@ -25,5 +25,5 @@ strum_macros = "0.24" [features] default = ["bn254"] -bn254 = ["acir_field/bn254"] -bls12_381 = ["acir_field/bls12_381"] +bn254 = ["acir_field/bn254", "brillig_bytecode/bn254"] +bls12_381 = ["acir_field/bls12_381", "brillig_bytecode/bls12_381"] From a166a500d622ba094425ea12ed8d5d74ab50b5f3 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 23 May 2023 13:37:21 +0100 Subject: [PATCH 114/125] Update acvm/src/pwg.rs --- acvm/src/pwg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acvm/src/pwg.rs b/acvm/src/pwg.rs index 23213ec2f..b7ac63de3 100644 --- a/acvm/src/pwg.rs +++ b/acvm/src/pwg.rs @@ -175,7 +175,7 @@ pub fn solve( } } // We have oracles that must be externally resolved - if !unresolved_oracles.is_empty() | !unresolved_brilligs.is_empty() { + if !unresolved_oracles.is_empty() || !unresolved_brilligs.is_empty() { return Ok(PartialWitnessGeneratorStatus::RequiresOracleData { required_oracle_data: unresolved_oracles, unsolved_opcodes: unresolved_opcodes, From f7574afe4f4f1b5057e76e626844a280d7210293 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 23 May 2023 13:38:12 +0100 Subject: [PATCH 115/125] Apply suggestions from code review --- brillig_bytecode/src/value.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs index 2622d1380..646929fc7 100644 --- a/brillig_bytecode/src/value.rs +++ b/brillig_bytecode/src/value.rs @@ -10,14 +10,14 @@ pub enum Typ { Signed { bit_size: u32 }, } -/// Value represents the base descriptor for a value in the VM. +/// `Value` represents the base descriptor for a value in the VM. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Value { pub inner: FieldElement, } impl Value { - /// Returns true if the Value represents `zero` + /// Returns `true` if the Value represents `zero` pub fn is_zero(&self) -> bool { self.inner.is_zero() } From f23f1c82cb094d778b953caaac91b60c5657e414 Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 23 May 2023 13:41:21 +0100 Subject: [PATCH 116/125] chore: add `brillig_bytecode` to `publish.yml` --- .github/workflows/publish.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9f556c01e..16ecd0c97 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -29,6 +29,12 @@ jobs: env: CARGO_REGISTRY_TOKEN: ${{ secrets.ACVM_CRATES_IO_TOKEN }} + - name: Publish brillig_bytecode + run: | + cargo publish --package brillig_bytecode + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.ACVM_CRATES_IO_TOKEN }} + - name: Publish acir run: | cargo publish --package acir @@ -46,4 +52,3 @@ jobs: cargo publish --package acvm env: CARGO_REGISTRY_TOKEN: ${{ secrets.ACVM_CRATES_IO_TOKEN }} - From 4e99d0b39e431dc38c440794517bfd3714f2eda1 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 23 May 2023 15:13:40 +0100 Subject: [PATCH 117/125] chore: make `Value` and `RegisterIndex` fields private (#308) * chore: make fields private on `Value` and `RegisterIndex` * Update brillig_bytecode/src/value.rs --------- Co-authored-by: kevaundray --- acvm/src/pwg.rs | 58 ++++++++-------- acvm/src/pwg/brillig.rs | 6 +- brillig_bytecode/src/lib.rs | 106 +++++++++++++++--------------- brillig_bytecode/src/registers.rs | 23 ++++--- brillig_bytecode/src/value.rs | 17 ++--- 5 files changed, 109 insertions(+), 101 deletions(-) diff --git a/acvm/src/pwg.rs b/acvm/src/pwg.rs index b7ac63de3..f938e1e3a 100644 --- a/acvm/src/pwg.rs +++ b/acvm/src/pwg.rs @@ -443,16 +443,16 @@ mod test { let equal_opcode = brillig_bytecode::Opcode::BinaryFieldOp { op: BinaryFieldOp::Cmp(Comparison::Eq), - lhs: RegisterIndex(0), - rhs: RegisterIndex(1), - destination: RegisterIndex(2), + lhs: RegisterIndex::from(0), + rhs: RegisterIndex::from(1), + destination: RegisterIndex::from(2), }; let less_than_opcode = brillig_bytecode::Opcode::BinaryFieldOp { op: BinaryFieldOp::Cmp(Comparison::Lt), - lhs: RegisterIndex(0), - rhs: RegisterIndex(1), - destination: RegisterIndex(3), + lhs: RegisterIndex::from(0), + rhs: RegisterIndex::from(1), + destination: RegisterIndex::from(3), }; let brillig_data = Brillig { @@ -478,8 +478,8 @@ mod test { // Oracles are named 'foreign calls' in brillig brillig_bytecode::Opcode::ForeignCall { function: "invert".into(), - destination: RegisterValueOrArray::RegisterIndex(RegisterIndex(1)), - input: RegisterValueOrArray::RegisterIndex(RegisterIndex(0)), + destination: RegisterValueOrArray::RegisterIndex(RegisterIndex::from(1)), + input: RegisterValueOrArray::RegisterIndex(RegisterIndex::from(0)), }, ], predicate: None, @@ -528,7 +528,7 @@ mod test { assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); // Alter Brillig oracle opcode brillig.foreign_call_results.push(ForeignCallResult { - values: vec![Value::from(foreign_call_wait_info.inputs[0].inner.inverse())], + values: vec![Value::from(foreign_call_wait_info.inputs[0].to_field().inverse())], }); let mut next_opcodes_for_solving = vec![Opcode::Brillig(brillig)]; next_opcodes_for_solving.extend_from_slice(&unsolved_opcodes[..]); @@ -564,16 +564,16 @@ mod test { let equal_opcode = brillig_bytecode::Opcode::BinaryFieldOp { op: BinaryFieldOp::Cmp(Comparison::Eq), - lhs: RegisterIndex(0), - rhs: RegisterIndex(1), - destination: RegisterIndex(4), + lhs: RegisterIndex::from(0), + rhs: RegisterIndex::from(1), + destination: RegisterIndex::from(4), }; let less_than_opcode = brillig_bytecode::Opcode::BinaryFieldOp { op: BinaryFieldOp::Cmp(Comparison::Lt), - lhs: RegisterIndex(0), - rhs: RegisterIndex(1), - destination: RegisterIndex(5), + lhs: RegisterIndex::from(0), + rhs: RegisterIndex::from(1), + destination: RegisterIndex::from(5), }; let brillig_data = Brillig { @@ -606,13 +606,13 @@ mod test { // Oracles are named 'foreign calls' in brillig brillig_bytecode::Opcode::ForeignCall { function: "invert".into(), - destination: RegisterValueOrArray::RegisterIndex(RegisterIndex(1)), - input: RegisterValueOrArray::RegisterIndex(RegisterIndex(0)), + destination: RegisterValueOrArray::RegisterIndex(RegisterIndex::from(1)), + input: RegisterValueOrArray::RegisterIndex(RegisterIndex::from(0)), }, brillig_bytecode::Opcode::ForeignCall { function: "invert".into(), - destination: RegisterValueOrArray::RegisterIndex(RegisterIndex(3)), - input: RegisterValueOrArray::RegisterIndex(RegisterIndex(2)), + destination: RegisterValueOrArray::RegisterIndex(RegisterIndex::from(3)), + input: RegisterValueOrArray::RegisterIndex(RegisterIndex::from(2)), }, ], predicate: None, @@ -662,7 +662,7 @@ mod test { unresolved_brilligs.remove(0); assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); - let x_plus_y_inverse = foreign_call_wait_info.inputs[0].inner.inverse(); + let x_plus_y_inverse = foreign_call_wait_info.inputs[0].to_field().inverse(); // Alter Brillig oracle opcode brillig .foreign_call_results @@ -685,7 +685,7 @@ mod test { unresolved_brilligs.remove(0); assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); - let i_plus_j_inverse = foreign_call_wait_info.inputs[0].inner.inverse(); + let i_plus_j_inverse = foreign_call_wait_info.inputs[0].to_field().inverse(); assert_ne!(x_plus_y_inverse, i_plus_j_inverse); // Alter Brillig oracle opcode brillig @@ -724,16 +724,16 @@ mod test { let equal_opcode = brillig_bytecode::Opcode::BinaryFieldOp { op: BinaryFieldOp::Cmp(Comparison::Eq), - lhs: RegisterIndex(0), - rhs: RegisterIndex(1), - destination: RegisterIndex(2), + lhs: RegisterIndex::from(0), + rhs: RegisterIndex::from(1), + destination: RegisterIndex::from(2), }; let less_than_opcode = brillig_bytecode::Opcode::BinaryFieldOp { op: BinaryFieldOp::Cmp(Comparison::Lt), - lhs: RegisterIndex(0), - rhs: RegisterIndex(1), - destination: RegisterIndex(3), + lhs: RegisterIndex::from(0), + rhs: RegisterIndex::from(1), + destination: RegisterIndex::from(3), }; let brillig_opcode = Opcode::Brillig(Brillig { @@ -757,8 +757,8 @@ mod test { // Oracles are named 'foreign calls' in brillig brillig_bytecode::Opcode::ForeignCall { function: "invert".into(), - destination: RegisterValueOrArray::RegisterIndex(RegisterIndex(1)), - input: RegisterValueOrArray::RegisterIndex(RegisterIndex(0)), + destination: RegisterValueOrArray::RegisterIndex(RegisterIndex::from(1)), + input: RegisterValueOrArray::RegisterIndex(RegisterIndex::from(0)), }, ], predicate: Some(Expression::default()), diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index c3348e1c8..445b0d813 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -116,16 +116,16 @@ impl BrilligSolver { let result = match vm_status { VMStatus::Finished => { for (i, output) in brillig.outputs.iter().enumerate() { - let register_value = vm.get_registers().get(RegisterIndex(i)); + let register_value = vm.get_registers().get(RegisterIndex::from(i)); match output { BrilligOutputs::Simple(witness) => { - insert_value(witness, register_value.inner, initial_witness)?; + insert_value(witness, register_value.to_field(), initial_witness)?; } BrilligOutputs::Array(witness_arr) => { // Treat the register value as a pointer to memory for (i, witness) in witness_arr.iter().enumerate() { let value = &vm.get_memory()[register_value.to_usize() + i]; - insert_value(witness, value.inner, initial_witness)?; + insert_value(witness, value.to_field(), initial_witness)?; } } } diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index 69073d74e..e9c9190d8 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -257,7 +257,7 @@ impl VM { let lhs_value = self.registers.get(lhs); let rhs_value = self.registers.get(rhs); - let result_value = op.evaluate_field(lhs_value.inner, rhs_value.inner); + let result_value = op.evaluate_field(lhs_value.to_field(), rhs_value.to_field()); self.registers.set(result, result_value.into()) } @@ -305,9 +305,9 @@ mod tests { let opcode = Opcode::BinaryIntOp { op: BinaryIntOp::Add, bit_size: 2, - lhs: RegisterIndex(0), - rhs: RegisterIndex(1), - destination: RegisterIndex(2), + lhs: RegisterIndex::from(0), + rhs: RegisterIndex::from(1), + destination: RegisterIndex::from(2), }; // Start VM @@ -323,7 +323,7 @@ mod tests { // The register at index `2` should have the value of 3 since we had an // add opcode let VM { registers, .. } = vm; - let output_value = registers.get(RegisterIndex(2)); + let output_value = registers.get(RegisterIndex::from(2)); assert_eq!(output_value, Value::from(3u128)) } @@ -335,17 +335,17 @@ mod tests { let lhs = { registers.push(Value::from(2u128)); - RegisterIndex(registers.len() - 1) + RegisterIndex::from(registers.len() - 1) }; let rhs = { registers.push(Value::from(2u128)); - RegisterIndex(registers.len() - 1) + RegisterIndex::from(registers.len() - 1) }; let destination = { registers.push(Value::from(0u128)); - RegisterIndex(registers.len() - 1) + RegisterIndex::from(registers.len() - 1) }; let equal_cmp_opcode = Opcode::BinaryIntOp { @@ -357,14 +357,14 @@ mod tests { }; opcodes.push(equal_cmp_opcode); opcodes.push(Opcode::Jump { location: 2 }); - opcodes.push(Opcode::JumpIf { condition: RegisterIndex(2), location: 3 }); + opcodes.push(Opcode::JumpIf { condition: RegisterIndex::from(2), location: 3 }); let mut vm = VM::new(Registers { inner: registers }, vec![], opcodes, vec![]); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_cmp_value = vm.registers.get(RegisterIndex(2)); + let output_cmp_value = vm.registers.get(RegisterIndex::from(2)); assert_eq!(output_cmp_value, Value::from(true)); let status = vm.process_opcode(); @@ -383,20 +383,21 @@ mod tests { let not_equal_cmp_opcode = Opcode::BinaryFieldOp { op: BinaryFieldOp::Cmp(Comparison::Eq), - lhs: RegisterIndex(0), - rhs: RegisterIndex(1), - destination: RegisterIndex(2), + lhs: RegisterIndex::from(0), + rhs: RegisterIndex::from(1), + destination: RegisterIndex::from(2), }; let jump_opcode = Opcode::Jump { location: 2 }; - let jump_if_not_opcode = Opcode::JumpIfNot { condition: RegisterIndex(2), location: 1 }; + let jump_if_not_opcode = + Opcode::JumpIfNot { condition: RegisterIndex::from(2), location: 1 }; let add_opcode = Opcode::BinaryFieldOp { op: BinaryFieldOp::Add, - lhs: RegisterIndex(0), - rhs: RegisterIndex(1), - destination: RegisterIndex(2), + lhs: RegisterIndex::from(0), + rhs: RegisterIndex::from(1), + destination: RegisterIndex::from(2), }; let mut vm = VM::new( @@ -412,7 +413,7 @@ mod tests { let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_cmp_value = vm.registers.get(RegisterIndex(2)); + let output_cmp_value = vm.registers.get(RegisterIndex::from(2)); assert_eq!(output_cmp_value, Value::from(false)); let status = vm.process_opcode(); @@ -423,7 +424,7 @@ mod tests { // The register at index `2` should have not changed as we jumped over the add opcode let VM { registers, .. } = vm; - let output_value = registers.get(RegisterIndex(2)); + let output_value = registers.get(RegisterIndex::from(2)); assert_eq!(output_value, Value::from(false)); } @@ -432,7 +433,8 @@ mod tests { let input_registers = Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(3u128)]); - let mov_opcode = Opcode::Mov { destination: RegisterIndex(2), source: RegisterIndex(0) }; + let mov_opcode = + Opcode::Mov { destination: RegisterIndex::from(2), source: RegisterIndex::from(0) }; let mut vm = VM::new(input_registers, vec![], vec![mov_opcode], vec![]); @@ -441,10 +443,10 @@ mod tests { let VM { registers, .. } = vm; - let destination_value = registers.get(RegisterIndex(2)); + let destination_value = registers.get(RegisterIndex::from(2)); assert_eq!(destination_value, Value::from(1u128)); - let source_value = registers.get(RegisterIndex(0)); + let source_value = registers.get(RegisterIndex::from(0)); assert_eq!(source_value, Value::from(1u128)); } @@ -461,33 +463,33 @@ mod tests { let equal_opcode = Opcode::BinaryIntOp { bit_size: 1, op: BinaryIntOp::Cmp(Comparison::Eq), - lhs: RegisterIndex(0), - rhs: RegisterIndex(1), - destination: RegisterIndex(2), + lhs: RegisterIndex::from(0), + rhs: RegisterIndex::from(1), + destination: RegisterIndex::from(2), }; let not_equal_opcode = Opcode::BinaryIntOp { bit_size: 1, op: BinaryIntOp::Cmp(Comparison::Eq), - lhs: RegisterIndex(0), - rhs: RegisterIndex(3), - destination: RegisterIndex(2), + lhs: RegisterIndex::from(0), + rhs: RegisterIndex::from(3), + destination: RegisterIndex::from(2), }; let less_than_opcode = Opcode::BinaryIntOp { bit_size: 1, op: BinaryIntOp::Cmp(Comparison::Lt), - lhs: RegisterIndex(3), - rhs: RegisterIndex(4), - destination: RegisterIndex(2), + lhs: RegisterIndex::from(3), + rhs: RegisterIndex::from(4), + destination: RegisterIndex::from(2), }; let less_than_equal_opcode = Opcode::BinaryIntOp { bit_size: 1, op: BinaryIntOp::Cmp(Comparison::Lte), - lhs: RegisterIndex(3), - rhs: RegisterIndex(4), - destination: RegisterIndex(2), + lhs: RegisterIndex::from(3), + rhs: RegisterIndex::from(4), + destination: RegisterIndex::from(2), }; let mut vm = VM::new( @@ -500,25 +502,25 @@ mod tests { let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_eq_value = vm.registers.get(RegisterIndex(2)); + let output_eq_value = vm.registers.get(RegisterIndex::from(2)); assert_eq!(output_eq_value, Value::from(true)); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_neq_value = vm.registers.get(RegisterIndex(2)); + let output_neq_value = vm.registers.get(RegisterIndex::from(2)); assert_eq!(output_neq_value, Value::from(false)); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let lt_value = vm.registers.get(RegisterIndex(2)); + let lt_value = vm.registers.get(RegisterIndex::from(2)); assert_eq!(lt_value, Value::from(true)); let status = vm.process_opcode(); assert_eq!(status, VMStatus::Finished); - let lte_value = vm.registers.get(RegisterIndex(2)); + let lte_value = vm.registers.get(RegisterIndex::from(2)); assert_eq!(lte_value, Value::from(true)); } #[test] @@ -531,9 +533,9 @@ mod tests { /// i += 1; /// } fn brillig_write_memory(memory: Vec) -> Vec { - let r_i = RegisterIndex(0); - let r_len = RegisterIndex(1); - let r_tmp = RegisterIndex(2); + let r_i = RegisterIndex::from(0); + let r_len = RegisterIndex::from(1); + let r_tmp = RegisterIndex::from(2); let start = [ // i = 0 Opcode::Const { destination: r_i, value: 0u128.into() }, @@ -594,10 +596,10 @@ mod tests { /// i += 1; /// } fn brillig_sum_memory(memory: Vec) -> Value { - let r_i = RegisterIndex(0); - let r_len = RegisterIndex(1); - let r_sum = RegisterIndex(2); - let r_tmp = RegisterIndex(3); + let r_i = RegisterIndex::from(0); + let r_len = RegisterIndex::from(1); + let r_sum = RegisterIndex::from(2); + let r_tmp = RegisterIndex::from(3); let start = [ // sum = 0 Opcode::Const { destination: r_sum, value: 0u128.into() }, @@ -667,9 +669,9 @@ mod tests { /// } /// Note we represent a 100% in-register optimized form in brillig fn brillig_recursive_write_memory(memory: Vec) -> Vec { - let r_i = RegisterIndex(0); - let r_len = RegisterIndex(1); - let r_tmp = RegisterIndex(2); + let r_i = RegisterIndex::from(0); + let r_len = RegisterIndex::from(1); + let r_tmp = RegisterIndex::from(2); let start = [ // i = 0 @@ -757,8 +759,8 @@ mod tests { #[test] fn foreign_call_opcode_register_result() { - let r_input = RegisterIndex(0); - let r_result = RegisterIndex(1); + let r_input = RegisterIndex::from(0); + let r_result = RegisterIndex::from(1); let double_program = vec![ // Load input register with value 5 @@ -802,8 +804,8 @@ mod tests { } #[test] fn foreign_call_opcode_memory_result() { - let r_input = RegisterIndex(0); - let r_output = RegisterIndex(1); + let r_input = RegisterIndex::from(0); + let r_output = RegisterIndex::from(1); // Define a simple 2x2 matrix in memory let initial_matrix = diff --git a/brillig_bytecode/src/registers.rs b/brillig_bytecode/src/registers.rs index 3774b15cc..9fd17e825 100644 --- a/brillig_bytecode/src/registers.rs +++ b/brillig_bytecode/src/registers.rs @@ -1,5 +1,4 @@ use crate::Value; -use acir_field::FieldElement; use serde::{Deserialize, Serialize}; /// Registers will store field element values during the @@ -34,16 +33,22 @@ impl Iterator for RegistersIntoIterator { Some(self.registers.inner[self.index - 1]) } } -/// RegisterIndex refers to the index of a register in the VM. +/// `RegisterIndex` refers to the index of a register in the VM. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub struct RegisterIndex(pub usize); +pub struct RegisterIndex(usize); impl RegisterIndex { - pub fn inner(self) -> usize { + pub fn to_usize(self) -> usize { self.0 } } +impl From for RegisterIndex { + fn from(value: usize) -> Self { + RegisterIndex(value) + } +} + impl Registers { /// Contiguously load the register with `values` pub fn load(values: Vec) -> Registers { @@ -52,16 +57,16 @@ impl Registers { /// Gets the values at register with address `index` pub fn get(&self, register: RegisterIndex) -> Value { - self.inner[register.inner()] + self.inner[register.to_usize()] } /// Sets the value at register with address `index` to `value` pub fn set(&mut self, index: RegisterIndex, value: Value) { - if index.inner() >= self.inner.len() { - let diff = index.inner() - self.inner.len() + 1; - self.inner.extend(vec![Value { inner: FieldElement::from(0u128) }; diff]) + if index.to_usize() >= self.inner.len() { + let diff = index.to_usize() - self.inner.len() + 1; + self.inner.extend(vec![Value::from(0u128); diff]) } - self.inner[index.inner()] = value + self.inner[index.to_usize()] = value } /// Returns all of the values in the register diff --git a/brillig_bytecode/src/value.rs b/brillig_bytecode/src/value.rs index 646929fc7..3c937a897 100644 --- a/brillig_bytecode/src/value.rs +++ b/brillig_bytecode/src/value.rs @@ -13,7 +13,7 @@ pub enum Typ { /// `Value` represents the base descriptor for a value in the VM. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Value { - pub inner: FieldElement, + inner: FieldElement, } impl Value { @@ -22,10 +22,15 @@ impl Value { self.inner.is_zero() } - /// Converts `Value` into a u128. + /// Converts `Value` into a `FieldElement`. + pub fn to_field(&self) -> FieldElement { + self.inner + } + + /// Converts `Value` into a `u128`. // TODO: Check what happens if `Value` cannot fit into a u128 pub fn to_u128(&self) -> u128 { - self.inner.to_u128() + self.to_field().to_u128() } /// Converts `Value` into a u64 and then casts it into a usize. @@ -57,11 +62,7 @@ impl From for Value { impl From for Value { fn from(value: bool) -> Self { - if value { - Value { inner: FieldElement::one() } - } else { - Value { inner: FieldElement::zero() } - } + Value { inner: FieldElement::from(value) } } } From aba2d1a74b177d185e975d22f07c834a54adfdf4 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Tue, 23 May 2023 15:34:00 +0100 Subject: [PATCH 118/125] Update brillig_bytecode/src/lib.rs Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- brillig_bytecode/src/lib.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/brillig_bytecode/src/lib.rs b/brillig_bytecode/src/lib.rs index e9c9190d8..04c32d1d2 100644 --- a/brillig_bytecode/src/lib.rs +++ b/brillig_bytecode/src/lib.rs @@ -1,8 +1,10 @@ -//! ACVM is capable of running brillig-bytecode -//! This bytecode is run in the traditional sense -//! and allows one to do non-determinism. -//! This is a generalization over the fixed directives -//! that we have in ACVM. +//! The Brillig VM is a specialized VM which allows the [ACVM][acvm] to perform custom non-determinism. +//! +//! Brillig bytecode is distinct from regular [ACIR][acir] in that it does not generate constraints. +//! This is a generalization over the fixed directives that exists within in the ACVM. +//! +//! [acir]: https://crates.io/crates/acir +//! [acvm]: https://crates.io/crates/acvm mod opcodes; mod registers; From 69bdf4f7906492c36c688d20784c92b2cdbd1296 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Tue, 23 May 2023 15:34:39 +0100 Subject: [PATCH 119/125] Update brillig_bytecode/Cargo.toml Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- brillig_bytecode/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_bytecode/Cargo.toml b/brillig_bytecode/Cargo.toml index 2a0f2cd67..a49d90c28 100644 --- a/brillig_bytecode/Cargo.toml +++ b/brillig_bytecode/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "brillig_bytecode" +name = "brillig_vm" description = "The virtual machine that processes Brillig bytecode, used to introduce non-determinism to the ACVM" version = "0.1.0" authors.workspace = true From cc2a096b393171c92bba4f4ad8b38c238574375c Mon Sep 17 00:00:00 2001 From: kevaundray Date: Tue, 23 May 2023 15:34:56 +0100 Subject: [PATCH 120/125] Update acvm/src/pwg.rs Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- acvm/src/pwg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acvm/src/pwg.rs b/acvm/src/pwg.rs index f938e1e3a..dc0cdc13f 100644 --- a/acvm/src/pwg.rs +++ b/acvm/src/pwg.rs @@ -250,7 +250,7 @@ fn insert_value( #[derive(Debug, PartialEq, Clone)] pub struct UnresolvedBrillig { pub brillig: Brillig, - // Information for, if there is a pending foreign call/oracle + /// Inputs for a pending foreign call required to restart bytecode processing. pub foreign_call_wait_info: brillig::ForeignCallWaitInfo, } From 6bb622dbaa51eab7e45817a786d6a561bb43e0a1 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Tue, 23 May 2023 15:35:07 +0100 Subject: [PATCH 121/125] Update acir/src/circuit/brillig.rs Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- acir/src/circuit/brillig.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acir/src/circuit/brillig.rs b/acir/src/circuit/brillig.rs index c6ebeca87..3cd0bfeb6 100644 --- a/acir/src/circuit/brillig.rs +++ b/acir/src/circuit/brillig.rs @@ -11,7 +11,7 @@ pub enum BrilligInputs { } /// Outputs for the Brillig VM. Once the VM has completed -/// execution, this will the object that is returned. +/// execution, this will be the object that is returned. #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] pub enum BrilligOutputs { Simple(Witness), From fc5b3c01122e30b7dcbfbfe0abedc7bed4d5b0fe Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Tue, 23 May 2023 15:38:49 +0100 Subject: [PATCH 122/125] rename brillig_bytecode to brillig_vm --- .github/workflows/publish.yml | 4 ++-- Cargo.toml | 2 +- acir/Cargo.toml | 6 ++--- acir/src/circuit/brillig.rs | 4 ++-- acir/src/lib.rs | 2 +- acvm/src/pwg.rs | 22 +++++++++---------- acvm/src/pwg/brillig.rs | 2 +- {brillig_bytecode => brillig_vm}/Cargo.toml | 0 {brillig_bytecode => brillig_vm}/src/lib.rs | 0 .../src/opcodes.rs | 0 .../src/registers.rs | 0 {brillig_bytecode => brillig_vm}/src/value.rs | 0 12 files changed, 21 insertions(+), 21 deletions(-) rename {brillig_bytecode => brillig_vm}/Cargo.toml (100%) rename {brillig_bytecode => brillig_vm}/src/lib.rs (100%) rename {brillig_bytecode => brillig_vm}/src/opcodes.rs (100%) rename {brillig_bytecode => brillig_vm}/src/registers.rs (100%) rename {brillig_bytecode => brillig_vm}/src/value.rs (100%) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 16ecd0c97..9b4cbb976 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -29,9 +29,9 @@ jobs: env: CARGO_REGISTRY_TOKEN: ${{ secrets.ACVM_CRATES_IO_TOKEN }} - - name: Publish brillig_bytecode + - name: Publish brillig_vm run: | - cargo publish --package brillig_bytecode + cargo publish --package brillig_vm env: CARGO_REGISTRY_TOKEN: ${{ secrets.ACVM_CRATES_IO_TOKEN }} diff --git a/Cargo.toml b/Cargo.toml index a0065332d..d26498607 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] -members = ["acir_field", "acir", "acvm", "stdlib", "brillig_bytecode"] +members = ["acir_field", "acir", "acvm", "stdlib", "brillig_vm"] [workspace.package] authors = ["The Noir Team "] diff --git a/acir/Cargo.toml b/acir/Cargo.toml index ea72a622e..2ff8274a2 100644 --- a/acir/Cargo.toml +++ b/acir/Cargo.toml @@ -12,7 +12,7 @@ rust-version.workspace = true [dependencies] acir_field.workspace = true serde.workspace = true -brillig_bytecode = { version = "0.1.0", path = "../brillig_bytecode", default-features = false } +brillig_vm = { version = "0.1.0", path = "../brillig_vm", default-features = false } thiserror.workspace = true rmp-serde = "1.1.0" @@ -25,5 +25,5 @@ strum_macros = "0.24" [features] default = ["bn254"] -bn254 = ["acir_field/bn254", "brillig_bytecode/bn254"] -bls12_381 = ["acir_field/bls12_381", "brillig_bytecode/bls12_381"] +bn254 = ["acir_field/bn254", "brillig_vm/bn254"] +bls12_381 = ["acir_field/bls12_381", "brillig_vm/bls12_381"] diff --git a/acir/src/circuit/brillig.rs b/acir/src/circuit/brillig.rs index 3cd0bfeb6..e37ef2948 100644 --- a/acir/src/circuit/brillig.rs +++ b/acir/src/circuit/brillig.rs @@ -1,5 +1,5 @@ use crate::native_types::{Expression, Witness}; -use brillig_bytecode::ForeignCallResult; +use brillig_vm::ForeignCallResult; use serde::{Deserialize, Serialize}; /// Inputs for the Brillig VM. These are the initial inputs @@ -24,7 +24,7 @@ pub struct Brillig { pub outputs: Vec, /// Results of oracles/functions external to brillig like a database read pub foreign_call_results: Vec, - pub bytecode: Vec, + pub bytecode: Vec, /// Predicate of the Brillig execution - indicates if it should be skipped pub predicate: Option, } diff --git a/acir/src/lib.rs b/acir/src/lib.rs index bccdb4ccf..39548011d 100644 --- a/acir/src/lib.rs +++ b/acir/src/lib.rs @@ -8,5 +8,5 @@ pub mod native_types; pub use acir_field; pub use acir_field::FieldElement; -pub use brillig_bytecode; +pub use brillig_vm; pub use circuit::black_box_functions::BlackBoxFunc; diff --git a/acvm/src/pwg.rs b/acvm/src/pwg.rs index dc0cdc13f..e8352faa9 100644 --- a/acvm/src/pwg.rs +++ b/acvm/src/pwg.rs @@ -286,7 +286,7 @@ mod test { use std::collections::BTreeMap; use acir::{ - brillig_bytecode::{ + brillig_vm::{ self, BinaryFieldOp, Comparison, ForeignCallResult, RegisterIndex, RegisterValueOrArray, Value, }, @@ -441,14 +441,14 @@ mod test { let w_equal_res = Witness(7); let w_lt_res = Witness(8); - let equal_opcode = brillig_bytecode::Opcode::BinaryFieldOp { + let equal_opcode = brillig_vm::Opcode::BinaryFieldOp { op: BinaryFieldOp::Cmp(Comparison::Eq), lhs: RegisterIndex::from(0), rhs: RegisterIndex::from(1), destination: RegisterIndex::from(2), }; - let less_than_opcode = brillig_bytecode::Opcode::BinaryFieldOp { + let less_than_opcode = brillig_vm::Opcode::BinaryFieldOp { op: BinaryFieldOp::Cmp(Comparison::Lt), lhs: RegisterIndex::from(0), rhs: RegisterIndex::from(1), @@ -476,7 +476,7 @@ mod test { equal_opcode, less_than_opcode, // Oracles are named 'foreign calls' in brillig - brillig_bytecode::Opcode::ForeignCall { + brillig_vm::Opcode::ForeignCall { function: "invert".into(), destination: RegisterValueOrArray::RegisterIndex(RegisterIndex::from(1)), input: RegisterValueOrArray::RegisterIndex(RegisterIndex::from(0)), @@ -562,14 +562,14 @@ mod test { let w_ij_oracle = Witness(11); let w_i_plus_j = Witness(12); - let equal_opcode = brillig_bytecode::Opcode::BinaryFieldOp { + let equal_opcode = brillig_vm::Opcode::BinaryFieldOp { op: BinaryFieldOp::Cmp(Comparison::Eq), lhs: RegisterIndex::from(0), rhs: RegisterIndex::from(1), destination: RegisterIndex::from(4), }; - let less_than_opcode = brillig_bytecode::Opcode::BinaryFieldOp { + let less_than_opcode = brillig_vm::Opcode::BinaryFieldOp { op: BinaryFieldOp::Cmp(Comparison::Lt), lhs: RegisterIndex::from(0), rhs: RegisterIndex::from(1), @@ -604,12 +604,12 @@ mod test { equal_opcode, less_than_opcode, // Oracles are named 'foreign calls' in brillig - brillig_bytecode::Opcode::ForeignCall { + brillig_vm::Opcode::ForeignCall { function: "invert".into(), destination: RegisterValueOrArray::RegisterIndex(RegisterIndex::from(1)), input: RegisterValueOrArray::RegisterIndex(RegisterIndex::from(0)), }, - brillig_bytecode::Opcode::ForeignCall { + brillig_vm::Opcode::ForeignCall { function: "invert".into(), destination: RegisterValueOrArray::RegisterIndex(RegisterIndex::from(3)), input: RegisterValueOrArray::RegisterIndex(RegisterIndex::from(2)), @@ -722,14 +722,14 @@ mod test { let w_equal_res = Witness(7); let w_lt_res = Witness(8); - let equal_opcode = brillig_bytecode::Opcode::BinaryFieldOp { + let equal_opcode = brillig_vm::Opcode::BinaryFieldOp { op: BinaryFieldOp::Cmp(Comparison::Eq), lhs: RegisterIndex::from(0), rhs: RegisterIndex::from(1), destination: RegisterIndex::from(2), }; - let less_than_opcode = brillig_bytecode::Opcode::BinaryFieldOp { + let less_than_opcode = brillig_vm::Opcode::BinaryFieldOp { op: BinaryFieldOp::Cmp(Comparison::Lt), lhs: RegisterIndex::from(0), rhs: RegisterIndex::from(1), @@ -755,7 +755,7 @@ mod test { equal_opcode, less_than_opcode, // Oracles are named 'foreign calls' in brillig - brillig_bytecode::Opcode::ForeignCall { + brillig_vm::Opcode::ForeignCall { function: "invert".into(), destination: RegisterValueOrArray::RegisterIndex(RegisterIndex::from(1)), input: RegisterValueOrArray::RegisterIndex(RegisterIndex::from(0)), diff --git a/acvm/src/pwg/brillig.rs b/acvm/src/pwg/brillig.rs index 445b0d813..ccb153ce8 100644 --- a/acvm/src/pwg/brillig.rs +++ b/acvm/src/pwg/brillig.rs @@ -1,5 +1,5 @@ use acir::{ - brillig_bytecode::{RegisterIndex, Registers, VMStatus, Value, VM}, + brillig_vm::{RegisterIndex, Registers, VMStatus, Value, VM}, circuit::brillig::{Brillig, BrilligInputs, BrilligOutputs}, native_types::WitnessMap, FieldElement, diff --git a/brillig_bytecode/Cargo.toml b/brillig_vm/Cargo.toml similarity index 100% rename from brillig_bytecode/Cargo.toml rename to brillig_vm/Cargo.toml diff --git a/brillig_bytecode/src/lib.rs b/brillig_vm/src/lib.rs similarity index 100% rename from brillig_bytecode/src/lib.rs rename to brillig_vm/src/lib.rs diff --git a/brillig_bytecode/src/opcodes.rs b/brillig_vm/src/opcodes.rs similarity index 100% rename from brillig_bytecode/src/opcodes.rs rename to brillig_vm/src/opcodes.rs diff --git a/brillig_bytecode/src/registers.rs b/brillig_vm/src/registers.rs similarity index 100% rename from brillig_bytecode/src/registers.rs rename to brillig_vm/src/registers.rs diff --git a/brillig_bytecode/src/value.rs b/brillig_vm/src/value.rs similarity index 100% rename from brillig_bytecode/src/value.rs rename to brillig_vm/src/value.rs From bde7784f0264a9880e7ce7d978df17286ea57d61 Mon Sep 17 00:00:00 2001 From: Kevaundray Wedderburn Date: Tue, 23 May 2023 15:42:29 +0100 Subject: [PATCH 123/125] change UnresolvedBrillig to UnresolvedBrilligCall --- acvm/src/pwg.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/acvm/src/pwg.rs b/acvm/src/pwg.rs index e8352faa9..159895e21 100644 --- a/acvm/src/pwg.rs +++ b/acvm/src/pwg.rs @@ -43,7 +43,7 @@ pub enum PartialWitnessGeneratorStatus { RequiresOracleData { required_oracle_data: Vec, unsolved_opcodes: Vec, - unresolved_brilligs: Vec, + unresolved_brilligs: Vec, }, } @@ -97,7 +97,7 @@ pub fn solve( ) -> Result { let mut unresolved_opcodes: Vec = Vec::new(); let mut unresolved_oracles: Vec = Vec::new(); - let mut unresolved_brilligs: Vec = Vec::new(); + let mut unresolved_brilligs: Vec = Vec::new(); while !opcode_to_solve.is_empty() || !unresolved_oracles.is_empty() { unresolved_opcodes.clear(); let mut stalled = true; @@ -147,7 +147,7 @@ pub fn solve( Opcode::Brillig(brillig) => brillig.clone(), _ => unreachable!("Brillig resolution for non brillig opcode"), }; - unresolved_brilligs.push(UnresolvedBrillig { + unresolved_brilligs.push(UnresolvedBrilligCall { brillig, foreign_call_wait_info: oracle_wait_info, }) @@ -248,7 +248,7 @@ fn insert_value( } #[derive(Debug, PartialEq, Clone)] -pub struct UnresolvedBrillig { +pub struct UnresolvedBrilligCall { pub brillig: Brillig, /// Inputs for a pending foreign call required to restart bytecode processing. pub foreign_call_wait_info: brillig::ForeignCallWaitInfo, @@ -302,7 +302,8 @@ mod test { use crate::{ pwg::{ - self, block::Blocks, OpcodeResolution, PartialWitnessGeneratorStatus, UnresolvedBrillig, + self, block::Blocks, OpcodeResolution, PartialWitnessGeneratorStatus, + UnresolvedBrilligCall, }, OpcodeResolutionError, PartialWitnessGenerator, }; @@ -523,7 +524,7 @@ mod test { assert_eq!(unsolved_opcodes.len(), 0, "brillig should have been removed"); assert_eq!(unresolved_brilligs.len(), 1, "should have a brillig oracle request"); - let UnresolvedBrillig { foreign_call_wait_info, mut brillig } = + let UnresolvedBrilligCall { foreign_call_wait_info, mut brillig } = unresolved_brilligs.remove(0); assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); // Alter Brillig oracle opcode @@ -658,7 +659,7 @@ mod test { assert_eq!(unsolved_opcodes.len(), 0, "brillig should have been removed"); assert_eq!(unresolved_brilligs.len(), 1, "should have a brillig oracle request"); - let UnresolvedBrillig { foreign_call_wait_info, mut brillig } = + let UnresolvedBrilligCall { foreign_call_wait_info, mut brillig } = unresolved_brilligs.remove(0); assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); @@ -681,7 +682,7 @@ mod test { assert!(unsolved_opcodes.is_empty(), "should be fully solved"); assert_eq!(unresolved_brilligs.len(), 1, "should have no unresolved oracles"); - let UnresolvedBrillig { foreign_call_wait_info, mut brillig } = + let UnresolvedBrilligCall { foreign_call_wait_info, mut brillig } = unresolved_brilligs.remove(0); assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); From 5af7637b22db0291ce09811da14d38c6ba41cd7a Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 23 May 2023 15:52:21 +0100 Subject: [PATCH 124/125] Update brillig_vm/src/value.rs --- brillig_vm/src/value.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brillig_vm/src/value.rs b/brillig_vm/src/value.rs index 3c937a897..73a7d897e 100644 --- a/brillig_vm/src/value.rs +++ b/brillig_vm/src/value.rs @@ -17,7 +17,7 @@ pub struct Value { } impl Value { - /// Returns `true` if the Value represents `zero` + /// Returns `true` if the `Value` represents `zero` pub fn is_zero(&self) -> bool { self.inner.is_zero() } From 85c9eb0f4d5f4872b33d02dc47910a845397309f Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 23 May 2023 15:57:28 +0100 Subject: [PATCH 125/125] chore: rename `unresolved_brilligs` to unresolved_brillig_calls --- acvm/src/pwg.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/acvm/src/pwg.rs b/acvm/src/pwg.rs index 159895e21..765dfe413 100644 --- a/acvm/src/pwg.rs +++ b/acvm/src/pwg.rs @@ -43,7 +43,7 @@ pub enum PartialWitnessGeneratorStatus { RequiresOracleData { required_oracle_data: Vec, unsolved_opcodes: Vec, - unresolved_brilligs: Vec, + unresolved_brillig_calls: Vec, }, } @@ -97,7 +97,7 @@ pub fn solve( ) -> Result { let mut unresolved_opcodes: Vec = Vec::new(); let mut unresolved_oracles: Vec = Vec::new(); - let mut unresolved_brilligs: Vec = Vec::new(); + let mut unresolved_brillig_calls: Vec = Vec::new(); while !opcode_to_solve.is_empty() || !unresolved_oracles.is_empty() { unresolved_opcodes.clear(); let mut stalled = true; @@ -147,7 +147,7 @@ pub fn solve( Opcode::Brillig(brillig) => brillig.clone(), _ => unreachable!("Brillig resolution for non brillig opcode"), }; - unresolved_brilligs.push(UnresolvedBrilligCall { + unresolved_brillig_calls.push(UnresolvedBrilligCall { brillig, foreign_call_wait_info: oracle_wait_info, }) @@ -175,11 +175,11 @@ pub fn solve( } } // We have oracles that must be externally resolved - if !unresolved_oracles.is_empty() || !unresolved_brilligs.is_empty() { + if !unresolved_oracles.is_empty() || !unresolved_brillig_calls.is_empty() { return Ok(PartialWitnessGeneratorStatus::RequiresOracleData { required_oracle_data: unresolved_oracles, unsolved_opcodes: unresolved_opcodes, - unresolved_brilligs, + unresolved_brillig_calls, }); } // We are stalled because of an opcode being bad @@ -517,15 +517,15 @@ mod test { // use the partial witness generation solver with our acir program let solver_status = pwg::solve(&backend, &mut witness_assignments, &mut blocks, opcodes) .expect("should stall on oracle"); - let PartialWitnessGeneratorStatus::RequiresOracleData { unsolved_opcodes, mut unresolved_brilligs, .. } = solver_status else { + let PartialWitnessGeneratorStatus::RequiresOracleData { unsolved_opcodes, mut unresolved_brillig_calls, .. } = solver_status else { panic!("Should require oracle data") }; assert_eq!(unsolved_opcodes.len(), 0, "brillig should have been removed"); - assert_eq!(unresolved_brilligs.len(), 1, "should have a brillig oracle request"); + assert_eq!(unresolved_brillig_calls.len(), 1, "should have a brillig oracle request"); let UnresolvedBrilligCall { foreign_call_wait_info, mut brillig } = - unresolved_brilligs.remove(0); + unresolved_brillig_calls.remove(0); assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); // Alter Brillig oracle opcode brillig.foreign_call_results.push(ForeignCallResult { @@ -652,15 +652,15 @@ mod test { // use the partial witness generation solver with our acir program let solver_status = pwg::solve(&backend, &mut witness_assignments, &mut blocks, opcodes) .expect("should stall on oracle"); - let PartialWitnessGeneratorStatus::RequiresOracleData { unsolved_opcodes, mut unresolved_brilligs, .. } = solver_status else { + let PartialWitnessGeneratorStatus::RequiresOracleData { unsolved_opcodes, mut unresolved_brillig_calls, .. } = solver_status else { panic!("Should require oracle data") }; assert_eq!(unsolved_opcodes.len(), 0, "brillig should have been removed"); - assert_eq!(unresolved_brilligs.len(), 1, "should have a brillig oracle request"); + assert_eq!(unresolved_brillig_calls.len(), 1, "should have a brillig oracle request"); let UnresolvedBrilligCall { foreign_call_wait_info, mut brillig } = - unresolved_brilligs.remove(0); + unresolved_brillig_calls.remove(0); assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); let x_plus_y_inverse = foreign_call_wait_info.inputs[0].to_field().inverse(); @@ -675,15 +675,15 @@ mod test { let solver_status = pwg::solve(&backend, &mut witness_assignments, &mut blocks, next_opcodes_for_solving) .expect("should stall on oracle"); - let PartialWitnessGeneratorStatus::RequiresOracleData { unsolved_opcodes, mut unresolved_brilligs, .. } = solver_status else { + let PartialWitnessGeneratorStatus::RequiresOracleData { unsolved_opcodes, mut unresolved_brillig_calls, .. } = solver_status else { panic!("Should require oracle data") }; assert!(unsolved_opcodes.is_empty(), "should be fully solved"); - assert_eq!(unresolved_brilligs.len(), 1, "should have no unresolved oracles"); + assert_eq!(unresolved_brillig_calls.len(), 1, "should have no unresolved oracles"); let UnresolvedBrilligCall { foreign_call_wait_info, mut brillig } = - unresolved_brilligs.remove(0); + unresolved_brillig_calls.remove(0); assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); let i_plus_j_inverse = foreign_call_wait_info.inputs[0].to_field().inverse();