diff --git a/acvm-repo/acir/README.md b/acvm-repo/acir/README.md index 12f5ea52c96..1b9c125e3e8 100644 --- a/acvm-repo/acir/README.md +++ b/acvm-repo/acir/README.md @@ -1,12 +1,4 @@ -# ACIR documentation (draft) - -## Abstract - -This document describes the purpose of ACIR, what it is and how ACIR programs -can be used by compilers and proving systems. It is intended to be a reference -documentation for ACIR. - -## Introduction +# The Abstract Circuit Intermediate Representation (ACIR) The purpose of ACIR is to make the link between a generic proving system, such as Aztec's Barretenberg, and a frontend, such as Noir, which describes user @@ -25,8 +17,8 @@ proving system, that can be used both by proving system as well as a target for frontends. So, at the end of the day, an ACIR program is just another representation of a program, dedicated to proving systems. -## Abstract Circuit Intermediate Representation -ACIR stands for abstract circuit intermediate representation: +## ACIR +ACIR stands for Abstract Circuit Intermediate Representation: - **abstract circuit**: circuits are a simple computation model where basic computation units, named gates, are connected with wires. Data flows through the wires while gates compute output wires based on their input. @@ -37,13 +29,13 @@ ACIR stands for abstract circuit intermediate representation: lose any expressiveness when using a circuit as it is well known that any bounded computation can be translated into an arithmetic circuit (i.e a circuit with only addition and multiplication gates). -The term abstract here simply means that we do not refer to an actual physical -circuit (such as an electronic circuit). Furthermore, we will not exactly use -the circuit model, but another model even better suited to ZKPs, the constraint -model (see below). + The term abstract here simply means that we do not refer to an actual physical + circuit (such as an electronic circuit). Furthermore, we will not exactly use + the circuit model, but another model even better suited to ZKPs, the constraint + model (see below). - **intermediate representation**: The ACIR representation is intermediate -because it lies between a frontend and its proving system. ACIR bytecode makes -the link between noir compiler output and the proving system backend input. + because it lies between a frontend and its proving system. ACIR bytecode makes + the link between noir compiler output and the proving system backend input. ## The constraint model @@ -60,7 +52,6 @@ addition gate `output_wire = input_wire_1 + input_wire_2` can be expressed with the following arithmetic constraint: `output_wire - (input_wire_1 + input_wire_2) = 0` - ## Solving Because of these constraints, executing an ACIR program is called solving the @@ -90,194 +81,26 @@ will refer to it as `FieldElement` or ACIR field. The proving system needs the values of all the partial witnesses and all the constraints in order to generate a proof. -*Remark*: The value of a partial witness is unique and fixed throughout a program +_Note_: The value of a partial witness is unique and fixed throughout a program execution, although in some rare cases, multiple values are possible for a same execution and witness (when there are several valid solutions to the constraints). Having multiple possible values for a witness may indicate that the circuit is not safe. -*Remark*: Why do we use the term partial witnesses? It is because the proving +_Note_: Why do we use the term partial witnesses? It is because the proving system may create other constraints and witnesses (especially with - `BlackBoxFuncCall`, see below). A proof refers to a full witness assignments - and their constraints. ACIR opcodes and their partial witnesses are still an + `BlackBoxFuncCall`, see below). A proof refers to a full witness assignment + and its constraints. ACIR opcodes and their partial witnesses are still an intermediate representation before getting the full list of constraints and witnesses. For the sake of simplicity, we will refer to witness instead of partial witness from now on. - -## ACIR Reference - -We assume here that the proving system is Barretenberg. Some parameters may -slightly change with another proving system, in particular the bit size of -`FieldElement`, which is 254 for Barretenberg. - -Some opcodes have inputs and outputs, which means that the output is constrained -to be the result of the opcode computation from the inputs. The solver expects -that all inputs are known when solving such opcodes. - -Some opcodes are not constrained, which means they will not be used by the -proving system and are only used by the solver. - -Finally, some opcodes will have a predicate, whose value is `0` or `1`. Its -purpose is to nullify the opcode when the value is `0`, so that it has no -effect. Note that removing the opcode is not a solution because this modifies -the circuit (the circuit being mainly the list of the opcodes). - -*Remark*: Opcodes operate on witnesses, but we will see that some opcode work on +_Note_: Opcodes operate on witnesses, but we will see that some opcodes work on expressions of witnesses. We call an expression a linear combination of - witnesses and/or products of two witnesses (and also a constant term). A + witnesses and/or products of two witnesses (also with a constant term). A single witness is a (simple) expression, and conversely, an expression can - be turned into a single witness using an assert-zero opcode (see below). So - basically, using witnesses or expressions is equivalent, but the latter can - avoid the creation of witness in some cases. - -### AssertZero opcode - -An `AssertZero` opcode adds the constraint that `P(w) = 0`, where -`w=(w_1,..w_n)` is a tuple of `n` witnesses, and `P` is a multi-variate -polynomial of total degree at most `2`. - -### BlackBoxFuncCall opcode - -These opcodes represent a specific computation. Even if any computation can be -done using only assert-zero opcodes, it is not always efficient. Some proving -systems, and in particular the proving system from Aztec, can implement several -computations more efficiently using for instance look-up tables. The -`BlackBoxFuncCall` opcode is used to ask the proving system to handle the -computation by itself. - -All black box functions take as input a tuple `(witness, num_bits)`, where -`num_bits` is a constant representing the bit size of the input witness, and they -have one or several witnesses as output. - -Some more advanced computations assume that the proving system has an -"embedded curve". It is a curve that cycles with the main curve of the proving -system, i.e the scalar field of the embedded curve is the base field of the main -one, and vice-versa. The curves used by the proving system are dependent on the -proving system (and/or its configuration). Aztec's Barretenberg uses BN254 as the -main curve and Grumpkin as the embedded curve. - -NOTE: see the [black_box_functions](src/circuit/black_box_functions.rs) file for -the most up-to-date documentation on these opcodes. - -The black box functions supported by ACIR are: - -**AES128Encrypt**: ciphers the provided plaintext using AES128 in CBC mode, padding the input using PKCS#7. - -**AND**: performs the bitwise AND of `lhs` and `rhs`. `bit_size` must be the same for both inputs. - -**XOR**: performs the bitwise XOR of `lhs` and `rhs`. `bit_size` must be the same for both inputs. - -**RANGE**: constraint the input to be of the provided bit size - -**SHA256**: computes sha256 of the inputs - -**Blake2s**: computes the Blake2s hash of the inputs, as specified in https://tools.ietf.org/html/rfc7693 - -**Blake3**: computes the Blake3 hash of the inputs - -**SchnorrVerify**: Verify a Schnorr signature over the embedded curve - -**PedersenCommitment**: Computes a Pedersen commitments of the inputs using generators of the embedded curve - -**PedersenHash**: Computes a Pedersen commitments of the inputs and their number, using generators of the embedded curve - -**EcdsaSecp256k1**: Verify an ECDSA signature over Secp256k1 - -**EcdsaSecp256r1**: Same as EcdsaSecp256k1, but done over another curve. - -**MultiScalarMul**: scalar multiplication with a variable base/input point (`P`) of the embedded curve - -**Keccak256**: Computes the Keccak-256 (Ethereum version) of the inputs. - -**Keccakf1600**: Keccak Permutation function of width 1600 - -**EmbeddedCurveAdd**: Embedded curve addition - -**BigIntAdd**: BigInt addition - -**BigIntSub**: BigInt subtraction - -**BigIntMul**: BigInt multiplication - -**BigIntDiv**: BigInt division - -**BigIntFromLeBytes**: BigInt from le bytes - -**BigIntToLeBytes**: BigInt to le bytes - -**Poseidon2Permutation**: Permutation function of Poseidon2 - -**Sha256Compression**: SHA256 compression function - -**RecursiveAggregation**: verify a proof inside the circuit. - -Computes a recursive aggregation object internally when verifying a proof inside -another circuit. -The outputted aggregation object will then be either checked in a -top-level verifier or aggregated upon again. -The aggregation object should be maintained by the backend implementer. - -This opcode prepares the verification of the final proof. -In order to fully verify a recursive proof, some operations may still be required -to be done by the final verifier (e.g. a pairing check). -This is why this black box function does not say if verification is passing or not. -It delays the expensive part of verification out of the SNARK -and leaves it to the final verifier outside of the SNARK circuit. - -This opcode also verifies that the key_hash is indeed a hash of verification_key, -allowing the user to use the verification key as private inputs and only -have the key_hash as public input, which is more performant. - -**Warning: the key hash logic does not need to be part of the black box and subject to be removed.** - -If one of the recursive proofs you verify with the black box function fails to -verify, then the verification of the final proof of the main ACIR program will -ultimately fail. - -### Brillig - -This opcode is used as a hint for the solver when executing (solving) the -circuit. The opcode does not generate any constraint and is usually the result -of the compilation of an unconstrained noir function. - -Let's see an example with euclidean division. -The normal way to compute `a/b`, where `a` and `b` are 8-bits integers, is to -implement the Euclidean algorithm which computes in a loop (or recursively) -modulus of the kind 'a mod b'. Doing this computation requires a lot of steps to -be properly implemented in ACIR, especially the loop with a condition. However, -euclidean division can be easily constrained with one assert-zero opcode: -`a = bq+r`, assuming `q` is 8 bits and `r { } /// Id for the function being called. +/// Indexes into the table of Brillig function's specified in a [program][super::Program] #[derive( Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash, Copy, Default, PartialOrd, Ord, )] diff --git a/acvm-repo/acir/src/circuit/mod.rs b/acvm-repo/acir/src/circuit/mod.rs index 57f9ec7663f..43eb94959d2 100644 --- a/acvm-repo/acir/src/circuit/mod.rs +++ b/acvm-repo/acir/src/circuit/mod.rs @@ -1,3 +1,5 @@ +//! Native structures for representing ACIR + pub mod black_box_functions; pub mod brillig; pub mod opcodes; @@ -39,7 +41,7 @@ pub enum ExpressionWidth { }, } -/// A program represented by multiple ACIR circuits. The execution trace of these +/// A program represented by multiple ACIR [circuit][Circuit]'s. The execution trace of these /// circuits is dictated by construction of the [crate::native_types::WitnessStack]. #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Default, Hash)] #[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))] @@ -48,13 +50,20 @@ pub struct Program { pub unconstrained_functions: Vec>, } +/// Representation of a single ACIR circuit. The execution trace of this structure +/// is dictated by the construction of a [crate::native_types::WitnessMap] #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Default, Hash)] #[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))] pub struct Circuit { - // current_witness_index is the highest witness index in the circuit. The next witness to be added to this circuit - // will take on this value. (The value is cached here as an optimization.) + /// current_witness_index is the highest witness index in the circuit. The next witness to be added to this circuit + /// will take on this value. (The value is cached here as an optimization.) pub current_witness_index: u32, + /// The circuit opcodes representing the relationship between witness values. + /// + /// The opcodes should be further converted into a backend-specific circuit representation. + /// When initial witness inputs are provided, these opcodes can also be used for generating an execution trace. pub opcodes: Vec>, + /// Maximum width of the [expression][Expression]'s which will be constrained pub expression_width: ExpressionWidth, /// The set of private inputs to the circuit. @@ -76,6 +85,7 @@ pub struct Circuit { pub assert_messages: Vec<(OpcodeLocation, AssertionPayload)>, } +/// Enumeration of either an [expression][Expression] or a [memory identifier][BlockId]. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] #[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))] pub enum ExpressionOrMemory { @@ -83,13 +93,24 @@ pub enum ExpressionOrMemory { Memory(BlockId), } +/// Payload tied to an assertion failure. +/// This data allows users to specify feedback upon a constraint not being satisfied in the circuit. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] #[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))] pub struct AssertionPayload { + /// Selector that maps a hash of either a constant string or an internal compiler error type + /// to an ABI type. The ABI type should then be used to appropriately resolve the payload data. pub error_selector: u64, + /// The dynamic payload data. + /// + /// Upon fetching the appropriate ABI type from the `error_selector`, the values + /// in this payload can be decoded into the given ABI type. + /// The payload is expected to be empty in the case of a constant string + /// as the string can be contained entirely within the error type and ABI type. pub payload: Vec>, } +/// Value for differentiating error types. Used internally by an [AssertionPayload]. #[derive(Debug, Copy, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)] pub struct ErrorSelector(u64); @@ -134,6 +155,8 @@ pub enum OpcodeLocation { Brillig { acir_index: usize, brillig_index: usize }, } +/// Index of Brillig opcode within a list of Brillig opcodes. +/// To be used by callers for resolving debug information. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] pub struct BrilligOpcodeLocation(pub usize); diff --git a/acvm-repo/acir/src/circuit/opcodes.rs b/acvm-repo/acir/src/circuit/opcodes.rs index 87f7c42d27d..d11680ed0f4 100644 --- a/acvm-repo/acir/src/circuit/opcodes.rs +++ b/acvm-repo/acir/src/circuit/opcodes.rs @@ -1,3 +1,6 @@ +//! ACIR opcodes +//! +//! This module defines the core set opcodes used in ACIR. use super::brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}; pub mod function_id; @@ -15,11 +18,21 @@ pub use black_box_function_call::{ }; pub use memory_operation::{BlockId, MemOp}; +/// Type for a memory block #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)] #[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))] pub enum BlockType { + /// The default type of memory block. + /// Virtually all user memory blocks are expected to be of this type + /// unless the backend wishes to expose special handling for call/return data. Memory, + /// Indicate to the backend that this memory comes from a circuit's inputs. + /// + /// This is most useful for schemes which require passing a lot of circuit inputs + /// through multiple circuits (such as in a recursive proof scheme). + /// Stores a constant identifier to distinguish between multiple calldata inputs. CallData(u32), + /// Similar to calldata except it states that this memory is returned in the circuit outputs. ReturnData, } @@ -29,6 +42,9 @@ impl BlockType { } } +/// Defines an operation within an ACIR circuit +/// +/// Expects a type parameter `F` which implements [AcirField]. #[allow(clippy::large_enum_variant)] #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] #[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))] @@ -62,48 +78,49 @@ pub enum Opcode { /// Often used for exposing more efficient implementations of /// SNARK-unfriendly computations. /// - /// All black box functions take as input a tuple `(witness, num_bits)`, - /// where `num_bits` is a constant representing the bit size of the input - /// witness, and they have one or several witnesses as output. + /// All black box function inputs are specified as [FunctionInput], + /// and they have one or several witnesses as output. /// /// Some more advanced computations assume that the proving system has an /// 'embedded curve'. It is a curve that cycles with the main curve of the /// proving system, i.e the scalar field of the embedded curve is the base /// field of the main one, and vice-versa. - /// - /// Aztec's Barretenberg uses BN254 as the main curve and Grumpkin as the + /// e.g. Aztec's Barretenberg uses BN254 as the main curve and Grumpkin as the /// embedded curve. BlackBoxFuncCall(BlackBoxFuncCall), /// Atomic operation on a block of memory /// /// ACIR is able to address any array of witnesses. Each array is assigned - /// an id (BlockId) and needs to be initialized with the MemoryInit opcode. + /// an id ([BlockId]) and needs to be initialized with the [Opcode::MemoryInit] opcode. /// Then it is possible to read and write from/to an array by providing the /// index and the value we read/write as arithmetic expressions. Note that - /// ACIR arrays all have a known fixed length (given in the MemoryInit + /// ACIR arrays all have a known fixed length (given in the [Opcode::MemoryInit] /// opcode below) - /// - /// - predicate: an arithmetic expression that disables the execution of the - /// opcode when the expression evaluates to zero MemoryOp { - /// identifier of the array + /// Identifier of the array block_id: BlockId, - /// describe the memory operation to perform + /// Describe the memory operation to perform op: MemOp, /// Predicate of the memory operation - indicates if it should be skipped + /// Disables the execution of the opcode when the expression evaluates to zero predicate: Option>, }, /// Initialize an ACIR array from a vector of witnesses. - /// - block_id: identifier of the array - /// - init: Vector of witnesses specifying the initial value of the array /// /// There must be only one MemoryInit per block_id, and MemoryOp opcodes must /// come after the MemoryInit. - MemoryInit { block_id: BlockId, init: Vec, block_type: BlockType }, + MemoryInit { + /// Identifier of the array + block_id: BlockId, + /// Vector of witnesses specifying the initial value of the array + init: Vec, + /// Specify what type of memory we should initialize + block_type: BlockType, + }, - /// Calls to unconstrained functions + /// Calls to unconstrained functions. Unconstrained functions are constructed with [Brillig][super::brillig]. BrilligCall { /// Id for the function being called. It is the responsibility of the executor /// to fetch the appropriate Brillig bytecode from this id. diff --git a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs index c6aa1d0105f..b9d2c1952d3 100644 --- a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs @@ -1,3 +1,8 @@ +//! Black box functions are ACIR opcodes which rely on backends implementing +//! support for specialized constraints. +//! This makes certain zk-snark unfriendly computations cheaper than if they were +//! implemented in more basic constraints. + use std::collections::BTreeSet; use crate::native_types::Witness; @@ -6,20 +11,25 @@ use crate::{AcirField, BlackBoxFunc}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use thiserror::Error; -// Note: Some functions will not use all of the witness -// So we need to supply how many bits of the witness is needed - +/// Enumeration for black box function inputs #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)] #[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))] pub enum ConstantOrWitnessEnum { + /// A constant field element Constant(F), + /// A witness element, representing dynamic inputs Witness(Witness), } +/// Input to a black box call #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)] #[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))] pub struct FunctionInput { + /// The actual input value input: ConstantOrWitnessEnum, + /// A constant representing the bit size of the input value + /// Some functions will not use all of the witness + /// So we need to supply how many bits of the witness is needed num_bits: u32, } @@ -81,6 +91,11 @@ impl std::fmt::Display for FunctionInput { } } +/// These opcodes represent a specialized computation. +/// Even if any computation can be done using only assert-zero opcodes, +/// it is not always efficient. +/// Some proving systems, can implement several computations more efficiently using +/// techniques such as custom gates and lookup tables. #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] pub enum BlackBoxFuncCall { /// Ciphers (encrypts) the provided plaintext using AES128 in CBC mode, diff --git a/acvm-repo/acir/src/circuit/opcodes/function_id.rs b/acvm-repo/acir/src/circuit/opcodes/function_id.rs index e87e52ce967..62cfc0447fe 100644 --- a/acvm-repo/acir/src/circuit/opcodes/function_id.rs +++ b/acvm-repo/acir/src/circuit/opcodes/function_id.rs @@ -1,5 +1,7 @@ use serde::{Deserialize, Serialize}; +/// Id for the function being called. +/// Indexes into the table of ACIR function's specified in a [program][crate::circuit::Program] #[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize, Hash)] #[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))] #[serde(transparent)] diff --git a/acvm-repo/acir/src/circuit/opcodes/memory_operation.rs b/acvm-repo/acir/src/circuit/opcodes/memory_operation.rs index 66034166b23..4917f47bfaf 100644 --- a/acvm-repo/acir/src/circuit/opcodes/memory_operation.rs +++ b/acvm-repo/acir/src/circuit/opcodes/memory_operation.rs @@ -2,6 +2,7 @@ use crate::native_types::{Expression, Witness}; use acir_field::AcirField; use serde::{Deserialize, Serialize}; +/// Identifier for a block of memory #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash, Copy, Default)] #[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))] pub struct BlockId(pub u32); diff --git a/acvm-repo/acir/src/lib.rs b/acvm-repo/acir/src/lib.rs index 63a1253cbe1..ae359fd823b 100644 --- a/acvm-repo/acir/src/lib.rs +++ b/acvm-repo/acir/src/lib.rs @@ -3,8 +3,7 @@ #![warn(clippy::semicolon_if_nothing_returned)] #![cfg_attr(not(test), warn(unused_crate_dependencies, unused_extern_crates))] -// Arbitrary Circuit Intermediate Representation - +#[doc = include_str!("../README.md")] pub mod circuit; pub mod native_types; mod proto; diff --git a/acvm-repo/acir/src/native_types/expression/mod.rs b/acvm-repo/acir/src/native_types/expression/mod.rs index b82b6729422..a9c1538cebc 100644 --- a/acvm-repo/acir/src/native_types/expression/mod.rs +++ b/acvm-repo/acir/src/native_types/expression/mod.rs @@ -5,25 +5,41 @@ use std::cmp::Ordering; mod operators; mod ordering; -// In the addition polynomial -// We can have arbitrary fan-in/out, so we need more than wL,wR and wO -// When looking at the assert-zero opcode for the quotient polynomial in standard plonk -// You can think of it as fan-in 2 and fan out-1 , or you can think of it as fan-in 1 and fan-out 2 -// -// In the multiplication polynomial -// XXX: If we allow the degree of the quotient polynomial to be arbitrary, then we will need a vector of wire values +/// An expression representing a quadratic polynomial. +/// +/// This struct is primarily used to express arithmetic relations between variables. +/// It includes multiplication terms, linear combinations, and a constant term. +/// +/// # Addition polynomial +/// - Unlike standard plonk constraints with fixed wire assignments (wL, wR, wO), +/// we allow arbitrary fan-in and fan-out. This means we need a more flexible representation +/// and we need more than wL, wR, and wO. +/// - When looking at the quotient polynomial for the assert-zero opcode in standard plonk, +/// you can interpret the structure in two ways: +/// 1. Fan-in 2 and fan-out 1 +/// 2. Fan-in 1 and fan-out 2 +/// +/// # Multiplication polynomial +/// - If we were allow the degree of the quotient polynomial to be arbitrary, then we will need a vector of wire values. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)] #[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))] pub struct Expression { - // To avoid having to create intermediate variables pre-optimization - // We collect all of the multiplication terms in the assert-zero opcode - // A multiplication term if of the form q_M * wL * wR - // Hence this vector represents the following sum: q_M1 * wL1 * wR1 + q_M2 * wL2 * wR2 + .. + + /// Collection of multiplication terms. + /// + /// To avoid having to create intermediate variables pre-optimization + /// We collect all of the multiplication terms in the assert-zero opcode + /// A multiplication term is of the form q_M * wL * wR + /// Hence this vector represents the following sum: q_M1 * wL1 * wR1 + q_M2 * wL2 * wR2 + .. + pub mul_terms: Vec<(F, Witness, Witness)>, + /// Collection of linear terms in the expression. + /// + /// Each term follows the form: `q_L * w`, where `q_L` is a coefficient + /// and `w` is a witness. pub linear_combinations: Vec<(F, Witness)>, + /// A constant term in the expression // TODO: rename q_c to `constant` moreover q_X is not clear to those who - // TODO are not familiar with PLONK + // TODO: are not familiar with PLONK pub q_c: F, } diff --git a/acvm-repo/acir/src/native_types/mod.rs b/acvm-repo/acir/src/native_types/mod.rs index eb9d1f6fd03..52b97db6fbc 100644 --- a/acvm-repo/acir/src/native_types/mod.rs +++ b/acvm-repo/acir/src/native_types/mod.rs @@ -1,3 +1,5 @@ +//! Low-level native types used within the [crate::circuit] module for representing ACIR. + mod expression; mod witness; mod witness_map; diff --git a/acvm-repo/acir/src/native_types/witness.rs b/acvm-repo/acir/src/native_types/witness.rs index d97702174b8..33fd91003d3 100644 --- a/acvm-repo/acir/src/native_types/witness.rs +++ b/acvm-repo/acir/src/native_types/witness.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -// Witness might be a misnomer. This is an index that represents the position a witness will take +/// An index that represents the position a witness value will take #[derive( Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Serialize, Deserialize, )] diff --git a/acvm-repo/acir/src/native_types/witness_stack.rs b/acvm-repo/acir/src/native_types/witness_stack.rs index 686b3ee98fd..0e23e640893 100644 --- a/acvm-repo/acir/src/native_types/witness_stack.rs +++ b/acvm-repo/acir/src/native_types/witness_stack.rs @@ -25,6 +25,7 @@ enum SerializationError { Deserialize(String), } +/// Native error for serializing/deserializating a witness stack. #[derive(Debug, Error)] #[error(transparent)] pub struct WitnessStackError(#[from] SerializationError); @@ -46,18 +47,22 @@ pub struct StackItem { } impl WitnessStack { + /// Append an element to the top of the stack pub fn push(&mut self, index: u32, witness: WitnessMap) { self.stack.push(StackItem { index, witness }); } + /// Removes the top element from the stack and return its pub fn pop(&mut self) -> Option> { self.stack.pop() } + /// Returns the top element of the stack, or `None` if it is empty pub fn peek(&self) -> Option<&StackItem> { self.stack.last() } + /// Returns the size of the stack pub fn length(&self) -> usize { self.stack.len() } diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index d470ae59254..f84d098a662 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -117,12 +117,20 @@ impl std::fmt::Display for ErrorLocation { } } +/// A dynamic assertion payload whose data has been resolved. +/// This is instantiated upon hitting an assertion failure. #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] pub struct RawAssertionPayload { + /// Selector to the respective ABI type the data in this payload represents pub selector: ErrorSelector, + /// Resolved data that represents some ABI type. + /// To be decoded in the final step of error resolution. pub data: Vec, } +/// Enumeration of possible resolved assertion payloads. +/// This is instantiated upon hitting an assertion failure, +/// and can either be static strings or dynamic payloads. #[derive(Clone, PartialEq, Eq, Debug)] pub enum ResolvedAssertionPayload { String(String), diff --git a/tooling/nargo/src/errors.rs b/tooling/nargo/src/errors.rs index 6062adfc099..b60b3ac8ad8 100644 --- a/tooling/nargo/src/errors.rs +++ b/tooling/nargo/src/errors.rs @@ -83,11 +83,11 @@ impl NargoError { } } -#[derive(Debug, Copy, Clone)] /// The opcode location for a call to a separate ACIR circuit /// This includes the function index of the caller within a [program][acvm::acir::circuit::Program] /// and the index in the callers ACIR to the specific call opcode. /// This is only resolved and set during circuit execution. +#[derive(Debug, Copy, Clone)] pub struct ResolvedOpcodeLocation { pub acir_function_index: usize, pub opcode_location: OpcodeLocation,