diff --git a/flake.lock b/flake.lock index 8d6445d3..53b65eb4 100644 --- a/flake.lock +++ b/flake.lock @@ -10,11 +10,11 @@ ] }, "locked": { - "lastModified": 1683314474, - "narHash": "sha256-gfHYpOnVTfS+4fhScBhfkB/e5z+jPFCi8zSy+aEh+8s=", + "lastModified": 1683909802, + "narHash": "sha256-2CL8NYKLYwwy6n0RyldvH86ULgrSvfzHrgq2Qf0ZUkE=", "owner": "AztecProtocol", "repo": "barretenberg", - "rev": "ad615ee7dc931d3dbea041e47c96b9d8dccebf98", + "rev": "97c9bc72aebab850b4a647d6e6cc50085226eafb", "type": "github" }, "original": { diff --git a/src/barretenberg_structures.rs b/src/barretenberg_structures.rs index ec2334a8..18af9d15 100644 --- a/src/barretenberg_structures.rs +++ b/src/barretenberg_structures.rs @@ -1,3 +1,4 @@ +use acvm::acir::circuit::opcodes::MemoryBlock; use acvm::acir::circuit::{Circuit, Opcode}; use acvm::acir::native_types::Expression; use acvm::acir::BlackBoxFunc; @@ -426,6 +427,7 @@ pub(crate) struct ConstraintSystem { schnorr_constraints: Vec, ecdsa_secp256k1_constraints: Vec, blake2s_constraints: Vec, + block_constraints: Vec, keccak_constraints: Vec, pedersen_constraints: Vec, hash_to_field_constraints: Vec, @@ -538,6 +540,11 @@ impl ConstraintSystem { self.constraints = constraints; self } + + pub(crate) fn block_constraints(mut self, block_constraints: Vec) -> Self { + self.block_constraints = block_constraints; + self + } } impl ConstraintSystem { @@ -641,13 +648,91 @@ impl ConstraintSystem { buffer.extend(&constraint.to_bytes()); } + // Serialize each Block constraint + let len = self.block_constraints.len() as u32; + buffer.extend_from_slice(&len.to_be_bytes()); + for constraint in self.block_constraints.iter() { + buffer.extend(&constraint.to_bytes()); + } + + buffer + } +} + +#[derive(Clone, Hash, Debug)] +pub(crate) struct MemOpBarretenberg { + pub(crate) is_store: i8, + pub(crate) index: Constraint, + pub(crate) value: Constraint, +} +impl MemOpBarretenberg { + fn to_bytes(&self) -> Vec { + let mut buffer = Vec::new(); + + buffer.extend_from_slice(&self.is_store.to_be_bytes()); + buffer.extend_from_slice(&self.index.to_bytes()); + buffer.extend_from_slice(&self.value.to_bytes()); + + buffer + } +} + +#[derive(Clone, Hash, Debug)] +pub(crate) struct BlockConstraint { + pub(crate) init: Vec, + pub(crate) trace: Vec, + pub(crate) is_ram: i8, +} + +impl BlockConstraint { + fn to_bytes(&self) -> Vec { + let mut buffer = Vec::new(); + + let len = self.init.len() as u32; + buffer.extend_from_slice(&len.to_be_bytes()); + for value in self.init.iter() { + buffer.extend_from_slice(&value.to_bytes()); + } + + let len = self.trace.len() as u32; + buffer.extend_from_slice(&len.to_be_bytes()); + for op in self.trace.iter() { + buffer.extend_from_slice(&op.to_bytes()); + } + buffer.extend_from_slice(&self.is_ram.to_be_bytes()); + buffer } + + fn from_memory_block(b: &MemoryBlock, is_ram_block: bool) -> BlockConstraint { + let mut init = Vec::new(); + let mut trace = Vec::new(); + let len = b.len as usize; + for op in b.trace.iter().take(len) { + assert_eq!(op.operation, Expression::one()); + init.push(serialize_arithmetic_gates(&op.value)); + } + for op in b.trace.iter().skip(len) { + let index = serialize_arithmetic_gates(&op.index); + let value = serialize_arithmetic_gates(&op.value); + let bb_op = MemOpBarretenberg { + is_store: op.operation.to_const().unwrap().to_u128() as i8, + index, + value, + }; + trace.push(bb_op); + } + let is_ram = i8::from(is_ram_block); + BlockConstraint { + init, + trace, + is_ram, + } + } } impl TryFrom<&Circuit> for ConstraintSystem { type Error = Error; - /// Converts an `IR` into the `StandardFormat` constraint system fn try_from(circuit: &Circuit) -> Result { // Create constraint system @@ -656,6 +741,7 @@ impl TryFrom<&Circuit> for ConstraintSystem { let mut logic_constraints: Vec = Vec::new(); let mut sha256_constraints: Vec = Vec::new(); let mut blake2s_constraints: Vec = Vec::new(); + let mut block_constraints: Vec = Vec::new(); let mut keccak_constraints: Vec = Vec::new(); let mut pedersen_constraints: Vec = Vec::new(); let mut compute_merkle_root_constraints: Vec = Vec::new(); @@ -1044,8 +1130,14 @@ impl TryFrom<&Circuit> for ConstraintSystem { Opcode::Directive(_) | Opcode::Oracle(_) => { // Directives & Oracles are only needed by the pwg } - Opcode::Block(_) | Opcode::RAM(_) | Opcode::ROM(_) => { - // TODO: implement serialization to match BB's interface + Opcode::Block(_) => { + // Block is managed by ACVM + } + Opcode::RAM(block) => { + block_constraints.push(BlockConstraint::from_memory_block(block, true)) + } + Opcode::ROM(block) => { + block_constraints.push(BlockConstraint::from_memory_block(block, false)) } } } @@ -1062,6 +1154,7 @@ impl TryFrom<&Circuit> for ConstraintSystem { schnorr_constraints, ecdsa_secp256k1_constraints, blake2s_constraints, + block_constraints, keccak_constraints, hash_to_field_constraints, constraints, diff --git a/src/composer.rs b/src/composer.rs index 990b7bf3..2fa724ad 100644 --- a/src/composer.rs +++ b/src/composer.rs @@ -398,8 +398,9 @@ mod test { use super::*; use crate::{ barretenberg_structures::{ - ComputeMerkleRootConstraint, Constraint, Keccak256Constraint, LogicConstraint, - PedersenConstraint, RangeConstraint, SchnorrConstraint, + BlockConstraint, ComputeMerkleRootConstraint, Constraint, Keccak256Constraint, + LogicConstraint, MemOpBarretenberg, PedersenConstraint, RangeConstraint, + SchnorrConstraint, }, merkle::{MerkleTree, MessageHasher}, }; @@ -769,6 +770,112 @@ mod test { test_composer_with_pk_vk(constraint_system, vec![case_1]) } + #[test] + fn test_memory_constraints() -> Result<(), Error> { + let two_field = FieldElement::one() + FieldElement::one(); + let one = Constraint { + a: 0, + b: 0, + c: 0, + qm: FieldElement::zero(), + ql: FieldElement::zero(), + qr: FieldElement::zero(), + qo: FieldElement::zero(), + qc: FieldElement::one(), + }; + + let two_x_constraint = Constraint { + a: 1, + b: 0, + c: 0, + qm: FieldElement::zero(), + ql: two_field, + qr: FieldElement::zero(), + qo: FieldElement::zero(), + qc: FieldElement::zero(), + }; + let x_1_constraint = Constraint { + a: 1, + b: 0, + c: 0, + qm: FieldElement::zero(), + ql: FieldElement::one(), + qr: FieldElement::zero(), + qo: FieldElement::zero(), + qc: FieldElement::one(), + }; + + let y_constraint = Constraint { + a: 2, + b: 0, + c: 0, + qm: FieldElement::zero(), + ql: FieldElement::one(), + qr: FieldElement::zero(), + qo: FieldElement::zero(), + qc: FieldElement::zero(), + }; + let z_constraint = Constraint { + a: 3, + b: 0, + c: 0, + qm: FieldElement::zero(), + ql: FieldElement::one(), + qr: FieldElement::zero(), + qo: FieldElement::zero(), + qc: FieldElement::zero(), + }; + let op1 = MemOpBarretenberg { + index: two_x_constraint, + value: y_constraint, + is_store: 0, + }; + let op2 = MemOpBarretenberg { + index: x_1_constraint.clone(), + value: z_constraint, + is_store: 0, + }; + let block_constraint = BlockConstraint { + init: vec![one, x_1_constraint], + trace: vec![op1, op2], + is_ram: 0, + }; + + let result_constraint = Constraint { + a: 2, + b: 3, + c: 0, + qm: FieldElement::zero(), + ql: FieldElement::one(), + qr: FieldElement::one(), + qo: FieldElement::zero(), + qc: -(two_field), + }; + let constraint_system = ConstraintSystem::new() + .var_num(4) + .block_constraints(vec![block_constraint]) + .constraints(vec![result_constraint]); + + let scalar_0 = FieldElement::zero(); + let scalar_1 = FieldElement::one(); + let witness_values = vec![scalar_0, scalar_1, scalar_1]; + + let case_1 = WitnessResult { + witness: witness_values.into(), + public_inputs: Assignments::default(), + result: true, + }; + + let bad_values = vec![scalar_0, scalar_1, scalar_1 + scalar_1]; + let case_2 = WitnessResult { + witness: bad_values.into(), + public_inputs: Assignments::default(), + result: false, + }; + + test_composer_with_pk_vk(constraint_system, vec![case_1, case_2]) + } + #[test] fn test_compute_merkle_root_constraint() -> Result<(), Error> { use tempfile::tempdir;