Skip to content

Commit

Permalink
chore!: Move circuit serialization circuit into acir (#3345)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevaundray authored Oct 30, 2023
1 parent 4e2d35f commit 122119b
Show file tree
Hide file tree
Showing 16 changed files with 78 additions and 107 deletions.
3 changes: 1 addition & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions acvm-repo/acir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ serde.workspace = true
thiserror.workspace = true
flate2 = "1.0.24"
bincode.workspace = true
base64.workspace = true

[dev-dependencies]
serde_json = "1.0"
Expand Down
47 changes: 40 additions & 7 deletions acvm-repo/acir/src/circuit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ use thiserror::Error;

use std::{io::prelude::*, num::ParseIntError, str::FromStr};

use base64::Engine;
use flate2::Compression;
use serde::{de::Error as DeserializationError, Deserialize, Deserializer, Serialize, Serializer};

use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;

#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
Expand Down Expand Up @@ -125,21 +126,54 @@ impl Circuit {
PublicInputs(public_inputs)
}

pub fn write<W: std::io::Write>(&self, writer: W) -> std::io::Result<()> {
fn write<W: std::io::Write>(&self, writer: W) -> std::io::Result<()> {
let buf = bincode::serialize(self).unwrap();
let mut encoder = flate2::write::GzEncoder::new(writer, Compression::default());
encoder.write_all(&buf)?;
encoder.finish()?;
Ok(())
}

pub fn read<R: std::io::Read>(reader: R) -> std::io::Result<Self> {
fn read<R: std::io::Read>(reader: R) -> std::io::Result<Self> {
let mut gz_decoder = flate2::read::GzDecoder::new(reader);
let mut buf_d = Vec::new();
gz_decoder.read_to_end(&mut buf_d)?;
bincode::deserialize(&buf_d)
.map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidInput, err))
}

pub fn serialize_circuit(circuit: &Circuit) -> Vec<u8> {
let mut circuit_bytes: Vec<u8> = Vec::new();
circuit.write(&mut circuit_bytes).expect("expected circuit to be serializable");
circuit_bytes
}

pub fn deserialize_circuit(serialized_circuit: &[u8]) -> std::io::Result<Self> {
Circuit::read(serialized_circuit)
}

// Serialize and base64 encode circuit
pub fn serialize_circuit_base64<S>(circuit: &Circuit, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let circuit_bytes = Circuit::serialize_circuit(circuit);
let encoded_b64 = base64::engine::general_purpose::STANDARD.encode(circuit_bytes);
s.serialize_str(&encoded_b64)
}

// Deserialize and base64 decode circuit
pub fn deserialize_circuit_base64<'de, D>(deserializer: D) -> Result<Circuit, D::Error>
where
D: Deserializer<'de>,
{
let bytecode_b64: String = serde::Deserialize::deserialize(deserializer)?;
let circuit_bytes = base64::engine::general_purpose::STANDARD
.decode(bytecode_b64)
.map_err(D::Error::custom)?;
let circuit = Self::deserialize_circuit(&circuit_bytes).map_err(D::Error::custom)?;
Ok(circuit)
}
}

impl std::fmt::Display for Circuit {
Expand Down Expand Up @@ -229,9 +263,8 @@ mod tests {
};

fn read_write(circuit: Circuit) -> (Circuit, Circuit) {
let mut bytes = Vec::new();
circuit.write(&mut bytes).unwrap();
let got_circuit = Circuit::read(&*bytes).unwrap();
let bytes = Circuit::serialize_circuit(&circuit);
let got_circuit = Circuit::deserialize_circuit(&bytes).unwrap();
(circuit, got_circuit)
}

Expand Down Expand Up @@ -277,7 +310,7 @@ mod tests {
encoder.write_all(bad_circuit).unwrap();
encoder.finish().unwrap();

let deserialization_result = Circuit::read(&*zipped_bad_circuit);
let deserialization_result = Circuit::deserialize_circuit(&zipped_bad_circuit);
assert!(deserialization_result.is_err());
}
}
21 changes: 7 additions & 14 deletions acvm-repo/acir/tests/test_program_serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ fn addition_circuit() {
..Circuit::default()
};

let mut bytes = Vec::new();
circuit.write(&mut bytes).unwrap();
let bytes = Circuit::serialize_circuit(&circuit);

let expected_serialization: Vec<u8> = vec![
31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 144, 187, 13, 192, 32, 12, 68, 249, 100, 32, 27,
Expand Down Expand Up @@ -73,8 +72,7 @@ fn fixed_base_scalar_mul_circuit() {
..Circuit::default()
};

let mut bytes = Vec::new();
circuit.write(&mut bytes).unwrap();
let bytes = Circuit::serialize_circuit(&circuit);

let expected_serialization: Vec<u8> = vec![
31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 77, 138, 91, 10, 0, 48, 12, 194, 178, 215, 215, 46, 189,
Expand All @@ -101,8 +99,7 @@ fn pedersen_circuit() {
..Circuit::default()
};

let mut bytes = Vec::new();
circuit.write(&mut bytes).unwrap();
let bytes = Circuit::serialize_circuit(&circuit);

let expected_serialization: Vec<u8> = vec![
31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 138, 9, 10, 0, 64, 8, 2, 103, 15, 250, 255, 139,
Expand Down Expand Up @@ -143,8 +140,7 @@ fn schnorr_verify_circuit() {
..Circuit::default()
};

let mut bytes = Vec::new();
circuit.write(&mut bytes).unwrap();
let bytes = Circuit::serialize_circuit(&circuit);

let expected_serialization: Vec<u8> = vec![
31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 77, 210, 87, 78, 2, 1, 20, 134, 209, 177, 247, 222, 123,
Expand Down Expand Up @@ -197,8 +193,7 @@ fn simple_brillig_foreign_call() {
..Circuit::default()
};

let mut bytes = Vec::new();
circuit.write(&mut bytes).unwrap();
let bytes = Circuit::serialize_circuit(&circuit);

let expected_serialization: Vec<u8> = vec![
31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 49, 10, 64, 33, 12, 67, 99, 63, 124, 60, 142,
Expand Down Expand Up @@ -271,8 +266,7 @@ fn complex_brillig_foreign_call() {
..Circuit::default()
};

let mut bytes = Vec::new();
circuit.write(&mut bytes).unwrap();
let bytes = Circuit::serialize_circuit(&circuit);

let expected_serialization: Vec<u8> = vec![
31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 83, 219, 10, 128, 48, 8, 117, 174, 139, 159, 179,
Expand Down Expand Up @@ -310,8 +304,7 @@ fn memory_op_circuit() {
return_values: PublicInputs([Witness(4)].into()),
..Circuit::default()
};
let mut bytes = Vec::new();
circuit.write(&mut bytes).unwrap();
let bytes = Circuit::serialize_circuit(&circuit);

let expected_serialization: Vec<u8> = vec![
31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 146, 49, 14, 0, 32, 8, 3, 139, 192, 127, 240, 7,
Expand Down
3 changes: 2 additions & 1 deletion acvm-repo/acvm_js/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ pub async fn execute_circuit_with_black_box_solver(
foreign_call_handler: ForeignCallHandler,
) -> Result<JsWitnessMap, Error> {
console_error_panic_hook::set_once();
let circuit: Circuit = Circuit::read(&*circuit).expect("Failed to deserialize circuit");
let circuit: Circuit =
Circuit::deserialize_circuit(&circuit).expect("Failed to deserialize circuit");

let mut acvm = ACVM::new(&solver.0, &circuit.opcodes, initial_witness.into());

Expand Down
9 changes: 6 additions & 3 deletions acvm-repo/acvm_js/src/public_witness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ pub fn get_return_witness(
witness_map: JsWitnessMap,
) -> Result<JsWitnessMap, JsString> {
console_error_panic_hook::set_once();
let circuit: Circuit = Circuit::read(&*circuit).expect("Failed to deserialize circuit");
let circuit: Circuit =
Circuit::deserialize_circuit(&circuit).expect("Failed to deserialize circuit");
let witness_map = WitnessMap::from(witness_map);

let return_witness =
Expand All @@ -50,7 +51,8 @@ pub fn get_public_parameters_witness(
solved_witness: JsWitnessMap,
) -> Result<JsWitnessMap, JsString> {
console_error_panic_hook::set_once();
let circuit: Circuit = Circuit::read(&*circuit).expect("Failed to deserialize circuit");
let circuit: Circuit =
Circuit::deserialize_circuit(&circuit).expect("Failed to deserialize circuit");
let witness_map = WitnessMap::from(solved_witness);

let public_params_witness =
Expand All @@ -70,7 +72,8 @@ pub fn get_public_witness(
solved_witness: JsWitnessMap,
) -> Result<JsWitnessMap, JsString> {
console_error_panic_hook::set_once();
let circuit: Circuit = Circuit::read(&*circuit).expect("Failed to deserialize circuit");
let circuit: Circuit =
Circuit::deserialize_circuit(&circuit).expect("Failed to deserialize circuit");
let witness_map = WitnessMap::from(solved_witness);

let public_witness =
Expand Down
1 change: 0 additions & 1 deletion compiler/noirc_driver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,4 @@ acvm.workspace = true
iter-extended.workspace = true
fm.workspace = true
serde.workspace = true
base64.workspace = true
fxhash.workspace = true
6 changes: 4 additions & 2 deletions compiler/noirc_driver/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use noirc_errors::debug_info::DebugInfo;
use noirc_evaluator::errors::SsaReport;

use super::debug::DebugFile;
use crate::program::{deserialize_circuit, serialize_circuit};

/// Describes the types of smart contract functions that are allowed.
/// Unlike the similar enum in noirc_frontend, 'open' and 'unconstrained'
Expand Down Expand Up @@ -62,7 +61,10 @@ pub struct ContractFunction {

pub abi: Abi,

#[serde(serialize_with = "serialize_circuit", deserialize_with = "deserialize_circuit")]
#[serde(
serialize_with = "Circuit::serialize_circuit_base64",
deserialize_with = "Circuit::deserialize_circuit_base64"
)]
pub bytecode: Circuit,

pub debug: DebugInfo,
Expand Down
31 changes: 5 additions & 26 deletions compiler/noirc_driver/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ use std::collections::BTreeMap;
use acvm::acir::circuit::Circuit;
use fm::FileId;

use base64::Engine;
use noirc_errors::debug_info::DebugInfo;
use noirc_evaluator::errors::SsaReport;
use serde::{de::Error as DeserializationError, ser::Error as SerializationError};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::{Deserialize, Serialize};

use super::debug::DebugFile;

Expand All @@ -20,32 +18,13 @@ pub struct CompiledProgram {
/// Used to short-circuit compilation in the case of the source code not changing since the last compilation.
pub hash: u64,

#[serde(serialize_with = "serialize_circuit", deserialize_with = "deserialize_circuit")]
#[serde(
serialize_with = "Circuit::serialize_circuit_base64",
deserialize_with = "Circuit::deserialize_circuit_base64"
)]
pub circuit: Circuit,
pub abi: noirc_abi::Abi,
pub debug: DebugInfo,
pub file_map: BTreeMap<FileId, DebugFile>,
pub warnings: Vec<SsaReport>,
}

pub(crate) fn serialize_circuit<S>(circuit: &Circuit, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut circuit_bytes: Vec<u8> = Vec::new();
circuit.write(&mut circuit_bytes).map_err(S::Error::custom)?;

let encoded_b64 = base64::engine::general_purpose::STANDARD.encode(circuit_bytes);
s.serialize_str(&encoded_b64)
}

pub(crate) fn deserialize_circuit<'de, D>(deserializer: D) -> Result<Circuit, D::Error>
where
D: Deserializer<'de>,
{
let bytecode_b64: String = serde::Deserialize::deserialize(deserializer)?;
let circuit_bytes =
base64::engine::general_purpose::STANDARD.decode(bytecode_b64).map_err(D::Error::custom)?;
let circuit = Circuit::read(&*circuit_bytes).map_err(D::Error::custom)?;
Ok(circuit)
}
6 changes: 2 additions & 4 deletions compiler/wasm/src/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@ use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn acir_read_bytes(bytes: Vec<u8>) -> JsValue {
console_error_panic_hook::set_once();
let circuit = Circuit::read(&*bytes).unwrap();
let circuit = Circuit::deserialize_circuit(&bytes).unwrap();
<JsValue as JsValueSerdeExt>::from_serde(&circuit).unwrap()
}

#[wasm_bindgen]
pub fn acir_write_bytes(acir: JsValue) -> Vec<u8> {
console_error_panic_hook::set_once();
let circuit: Circuit = JsValueSerdeExt::into_serde(&acir).unwrap();
let mut bytes = Vec::new();
circuit.write(&mut bytes).unwrap();
bytes
Circuit::serialize_circuit(&circuit)
}
16 changes: 4 additions & 12 deletions tooling/backend_interface/src/proof_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ impl Backend {

// Create a temporary file for the circuit
let circuit_path = temp_directory.join("circuit").with_extension("bytecode");
let serialized_circuit = serialize_circuit(circuit);
let serialized_circuit = Circuit::serialize_circuit(circuit);
write_to_file(&serialized_circuit, &circuit_path);

GatesCommand { crs_path: self.crs_directory(), bytecode_path: circuit_path }
Expand Down Expand Up @@ -57,7 +57,7 @@ impl Backend {
// Create a temporary file for the circuit
//
let bytecode_path = temp_directory.join("circuit").with_extension("bytecode");
let serialized_circuit = serialize_circuit(circuit);
let serialized_circuit = Circuit::serialize_circuit(circuit);
write_to_file(&serialized_circuit, &bytecode_path);

// Create proof and store it in the specified path
Expand Down Expand Up @@ -97,7 +97,7 @@ impl Backend {

// Create a temporary file for the circuit
let bytecode_path = temp_directory.join("circuit").with_extension("bytecode");
let serialized_circuit = serialize_circuit(circuit);
let serialized_circuit = Circuit::serialize_circuit(circuit);
write_to_file(&serialized_circuit, &bytecode_path);

// Create the verification key and write it to the specified path
Expand Down Expand Up @@ -130,7 +130,7 @@ impl Backend {
// Create a temporary file for the circuit
//
let bytecode_path = temp_directory.join("circuit").with_extension("bytecode");
let serialized_circuit = serialize_circuit(circuit);
let serialized_circuit = Circuit::serialize_circuit(circuit);
write_to_file(&serialized_circuit, &bytecode_path);

// Create the verification key and write it to the specified path
Expand Down Expand Up @@ -174,11 +174,3 @@ pub(super) fn write_to_file(bytes: &[u8], path: &Path) -> String {
Ok(_) => display.to_string(),
}
}

// TODO: See nargo/src/artifacts/mod.rs
// TODO: This method should live in ACVM and be the default method for serializing/deserializing circuits
pub(super) fn serialize_circuit(circuit: &Circuit) -> Vec<u8> {
let mut circuit_bytes: Vec<u8> = Vec::new();
circuit.write(&mut circuit_bytes).unwrap();
circuit_bytes
}
4 changes: 2 additions & 2 deletions tooling/backend_interface/src/smart_contract.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::proof_system::{serialize_circuit, write_to_file};
use super::proof_system::write_to_file;
use crate::{
cli::{ContractCommand, WriteVkCommand},
Backend, BackendError,
Expand All @@ -16,7 +16,7 @@ impl Backend {

// Create a temporary file for the circuit
let bytecode_path = temp_directory_path.join("circuit").with_extension("bytecode");
let serialized_circuit = serialize_circuit(circuit);
let serialized_circuit = Circuit::serialize_circuit(circuit);
write_to_file(&serialized_circuit, &bytecode_path);

// Create the verification key and write it to the specified path
Expand Down
1 change: 0 additions & 1 deletion tooling/nargo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,4 @@ noirc_printable_type.workspace = true
iter-extended.workspace = true
serde.workspace = true
thiserror.workspace = true
base64.workspace = true
codespan-reporting.workspace = true
Loading

0 comments on commit 122119b

Please sign in to comment.