Skip to content
This repository has been archived by the owner on Apr 9, 2024. It is now read-only.

feat!: Add initial oracle opcode #149

Merged
merged 12 commits into from
Mar 23, 2023
21 changes: 18 additions & 3 deletions acir/src/circuit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,10 @@ mod test {
use std::collections::BTreeSet;

use super::{
opcodes::{BlackBoxFuncCall, FunctionInput},
opcodes::{BlackBoxFuncCall, FunctionInput, OracleData},
Circuit, Opcode, PublicInputs,
};
use crate::native_types::Witness;
use crate::native_types::{Expression, Witness};
use acir_field::FieldElement;

fn and_opcode() -> Opcode {
Expand All @@ -204,12 +204,25 @@ mod test {
outputs: vec![],
})
}
fn oracle_opcode() -> Opcode {
Opcode::Oracle(OracleData {
name: String::from("oracle-name"),
inputs: vec![Expression {
mul_terms: vec![(FieldElement::from(123u128), Witness(1), Witness(2))],
linear_combinations: vec![(FieldElement::from(456u128), Witness(34))],
q_c: FieldElement::from(12345678u128),
}],
input_values: vec![],
outputs: vec![Witness(1), Witness(2), Witness(3)],
output_values: vec![],
})
}

#[test]
fn serialization_roundtrip() {
let circuit = Circuit {
current_witness_index: 5,
opcodes: vec![and_opcode(), range_opcode()],
opcodes: vec![and_opcode(), range_opcode(), oracle_opcode()],
public_parameters: PublicInputs(BTreeSet::from_iter(vec![Witness(2), Witness(12)])),
return_values: PublicInputs(BTreeSet::from_iter(vec![Witness(4), Witness(12)])),
};
Expand Down Expand Up @@ -237,6 +250,7 @@ mod test {
}),
range_opcode(),
and_opcode(),
oracle_opcode(),
],
public_parameters: PublicInputs(BTreeSet::from_iter(vec![Witness(2)])),
return_values: PublicInputs(BTreeSet::from_iter(vec![Witness(2)])),
Expand All @@ -261,6 +275,7 @@ mod test {
}),
range_opcode(),
and_opcode(),
oracle_opcode(),
],
public_parameters: PublicInputs(BTreeSet::from_iter(vec![Witness(2)])),
return_values: PublicInputs(BTreeSet::from_iter(vec![Witness(2)])),
Expand Down
124 changes: 123 additions & 1 deletion acir/src/circuit/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use std::io::{Read, Write};

use super::directives::{Directive, LogInfo};
use crate::native_types::{Expression, Witness};
use crate::serialization::{read_n, read_u16, read_u32, write_bytes, write_u16, write_u32};
use crate::serialization::{
read_bytes, read_field_element, read_n, read_u16, read_u32, write_bytes, write_u16, write_u32,
};
use crate::BlackBoxFunc;
use acir_field::FieldElement;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -85,6 +87,114 @@ impl MemoryBlock {
}
}

#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct OracleData {
/// Name of the oracle
pub name: String,
/// Inputs
pub inputs: Vec<Expression>,
/// Input values - they are progressively computed by the pwg
pub input_values: Vec<FieldElement>,
/// Output witness
pub outputs: Vec<Witness>,
/// Output values - they are computed by the (external) oracle once the input_values are known
pub output_values: Vec<FieldElement>,
}

impl OracleData {
pub(crate) fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
let name_as_bytes = self.name.as_bytes();
let name_len = name_as_bytes.len();
write_u32(&mut writer, name_len as u32)?;
write_bytes(&mut writer, name_as_bytes)?;

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())?;
}

let inputs_len = self.input_values.len() as u32;
write_u32(&mut writer, inputs_len)?;
for input in &self.input_values {
write_bytes(&mut writer, &input.to_be_bytes())?;
}

let outputs_len = self.output_values.len() as u32;
write_u32(&mut writer, outputs_len)?;
for output in &self.output_values {
write_bytes(&mut writer, &output.to_be_bytes())?;
}
Ok(())
}

pub(crate) fn read<R: Read>(mut reader: R) -> std::io::Result<Self> {
let name_len = read_u32(&mut reader)?;
let name_as_bytes = read_bytes(&mut reader, name_len as usize)?;
let name: String = String::from_utf8(name_as_bytes)
.map_err(|_| std::io::Error::from(std::io::ErrorKind::InvalidData))?;

let inputs_len = read_u32(&mut reader)?;
let mut inputs = Vec::with_capacity(inputs_len as usize);
for _ in 0..inputs_len {
let input = Expression::read(&mut reader)?;
inputs.push(input);
}

let outputs_len = read_u32(&mut reader)?;
let mut outputs = Vec::with_capacity(outputs_len as usize);
for _ in 0..outputs_len {
let witness_index = read_u32(&mut reader)?;
outputs.push(Witness(witness_index));
}

const FIELD_ELEMENT_NUM_BYTES: usize = FieldElement::max_num_bytes() as usize;
let inputs_len = read_u32(&mut reader)?;
let mut input_values = Vec::with_capacity(inputs_len as usize);
for _ in 0..inputs_len {
let value = read_field_element::<FIELD_ELEMENT_NUM_BYTES, _>(&mut reader)?;
input_values.push(value);
}

let outputs_len = read_u32(&mut reader)?;
let mut output_values = Vec::with_capacity(outputs_len as usize);
for _ in 0..outputs_len {
let value = read_field_element::<FIELD_ELEMENT_NUM_BYTES, _>(&mut reader)?;
output_values.push(value);
}

Ok(OracleData { name, inputs, outputs, input_values, output_values })
}
}

impl std::fmt::Display for OracleData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ORACLE: {}", self.name)?;
let solved = if self.input_values.len() == self.inputs.len() { "solved" } else { "" };

write!(
f,
"Inputs: _{}..._{}{solved}",
self.inputs.first().unwrap(),
self.inputs.last().unwrap()
)?;

let solved = if self.output_values.len() == self.outputs.len() { "solved" } else { "" };
write!(
f,
"Outputs: _{}..._{}{solved}",
self.outputs.first().unwrap().witness_index(),
self.outputs.last().unwrap().witness_index()
)
}
}

#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Opcode {
Arithmetic(Expression),
Expand All @@ -104,6 +214,7 @@ pub enum Opcode {
/// - after MemoryBlock.len, all operations are constant expressions (0 or 1)
/// 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),
}

impl Opcode {
Expand All @@ -117,6 +228,7 @@ impl Opcode {
Opcode::Block(_) => "block",
Opcode::RAM(_) => "ram",
Opcode::ROM(_) => "rom",
Opcode::Oracle(data) => &data.name,
}
}

Expand All @@ -130,6 +242,7 @@ impl Opcode {
Opcode::Block(_) => 3,
Opcode::ROM(_) => 4,
Opcode::RAM(_) => 5,
Opcode::Oracle { .. } => 6,
}
}

Expand All @@ -154,6 +267,7 @@ impl Opcode {
Opcode::Block(mem_block) | Opcode::ROM(mem_block) | Opcode::RAM(mem_block) => {
mem_block.write(writer)
}
Opcode::Oracle(data) => data.write(writer),
}
}
pub fn read<R: Read>(mut reader: R) -> std::io::Result<Self> {
Expand Down Expand Up @@ -187,6 +301,10 @@ impl Opcode {
let block = MemoryBlock::read(reader)?;
Ok(Opcode::RAM(block))
}
6 => {
let data = OracleData::read(reader)?;
Ok(Opcode::Oracle(data))
}
_ => Err(std::io::ErrorKind::InvalidData.into()),
}
}
Expand Down Expand Up @@ -273,6 +391,10 @@ impl std::fmt::Display for Opcode {
write!(f, "RAM ")?;
write!(f, "(id: {}, len: {}) ", block.id.0, block.trace.len())
}
Opcode::Oracle(data) => {
write!(f, "ORACLE: ")?;
write!(f, "{data}")
}
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions acir/src/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ pub(crate) fn write_n<const NUM_BYTES: usize, W: Write>(
pub(crate) fn write_bytes<W: Write>(mut w: W, bytes: &[u8]) -> std::io::Result<usize> {
w.write(bytes)
}
pub(crate) fn read_bytes<R: Read>(mut r: R, num_bytes: usize) -> std::io::Result<Vec<u8>> {
let mut bytes = vec![0u8; num_bytes];
r.read_exact(&mut bytes[..])?;
Ok(bytes)
}

pub(crate) fn write_u16<W: Write>(w: W, num: u16) -> std::io::Result<usize> {
let bytes = num.to_le_bytes();
Expand Down
6 changes: 4 additions & 2 deletions acvm/src/compiler/transformers/fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ impl FallbackTransformer {
| Opcode::Directive(_)
| Opcode::Block(_)
| Opcode::ROM(_)
| Opcode::RAM(_) => {
// directive, arithmetic expression or blocks are handled by acvm
| Opcode::RAM(_)
| Opcode::Oracle { .. } => {
// directive, arithmetic expression or block are handled by acvm
// The oracle opcode is assumed to be supported.
acir_supported_opcodes.push(opcode);
continue;
}
Expand Down
Loading