mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-02 22:03:07 +00:00
Merge branch 'precompile-fixes' into blake_precompile
This commit is contained in:
commit
9339847243
@ -13,7 +13,7 @@ edition = "2021"
|
||||
anyhow = "1.0.40"
|
||||
blake2 = "0.10.5"
|
||||
env_logger = "0.10.0"
|
||||
eth_trie_utils = "0.5.0"
|
||||
eth_trie_utils = "0.6.0"
|
||||
ethereum-types = "0.14.0"
|
||||
hex = { version = "0.4.3", optional = true }
|
||||
hex-literal = "0.3.4"
|
||||
|
||||
@ -5,8 +5,13 @@
|
||||
// Post stack: address
|
||||
global sys_create:
|
||||
%check_static
|
||||
|
||||
%stack (kexit_info, value, code_offset, code_len) -> (code_len, code_offset, kexit_info, value, code_offset, code_len)
|
||||
%checked_mem_expansion
|
||||
// stack: kexit_info, value, code_offset, code_len
|
||||
// TODO: Charge gas.
|
||||
%charge_gas_const(@GAS_CREATE)
|
||||
// TODO: If using EIP-3860, we should limit and charge gas on `code_len`.
|
||||
|
||||
%stack (kexit_info, value, code_offset, code_len)
|
||||
-> (sys_create_got_address, value, code_offset, code_len, kexit_info)
|
||||
%address
|
||||
@ -27,8 +32,15 @@ sys_create_got_address:
|
||||
// Post stack: address
|
||||
global sys_create2:
|
||||
%check_static
|
||||
|
||||
// stack: kexit_info, value, code_offset, code_len, salt
|
||||
// TODO: Charge gas.
|
||||
%stack (kexit_info, value, code_offset, code_len) -> (code_len, code_offset, kexit_info, value, code_offset, code_len)
|
||||
%checked_mem_expansion
|
||||
// stack: kexit_info, value, code_offset, code_len, salt
|
||||
DUP4 %num_bytes_to_num_words
|
||||
%mul_const(@GAS_KECCAK256WORD) %add_const(@GAS_CREATE) %charge_gas
|
||||
// TODO: If using EIP-3860, we should limit and charge gas on `code_len`.
|
||||
|
||||
SWAP4
|
||||
%stack (salt) -> (salt, create_common)
|
||||
// stack: salt, create_common, value, code_offset, code_len, kexit_info
|
||||
@ -78,7 +90,10 @@ global create_common:
|
||||
GET_CONTEXT
|
||||
// stack: src_ctx, new_ctx, address, value, code_offset, code_len, kexit_info
|
||||
|
||||
// Copy the code from txdata to the new context's code segment.
|
||||
%stack (src_ctx, new_ctx, address, value, code_offset, code_len) ->
|
||||
(code_len, new_ctx, src_ctx, new_ctx, address, value, code_offset, code_len)
|
||||
%set_new_ctx_code_size POP
|
||||
// Copy the code from memory to the new context's code segment.
|
||||
%stack (src_ctx, new_ctx, address, value, code_offset, code_len)
|
||||
-> (new_ctx, @SEGMENT_CODE, 0, // DST
|
||||
src_ctx, @SEGMENT_MAIN_MEMORY, code_offset, // SRC
|
||||
@ -113,7 +128,35 @@ after_constructor:
|
||||
// stack: success, leftover_gas, new_ctx, address, kexit_info
|
||||
SWAP2
|
||||
// stack: new_ctx, leftover_gas, success, address, kexit_info
|
||||
POP // TODO: Ignoring new_ctx for now, but we will need it to store code that was returned, if any.
|
||||
POP
|
||||
|
||||
// TODO: EIP-3541: Reject new contract code starting with the 0xEF byte
|
||||
|
||||
// TODO: Skip blocks below if success is false.
|
||||
// Charge gas for the code size.
|
||||
SWAP3
|
||||
// stack: kexit_info, success, address, leftover_gas
|
||||
%returndatasize // Size of the code.
|
||||
// stack: code_size, kexit_info, success, address, leftover_gas
|
||||
DUP1 %gt_const(@MAX_CODE_SIZE)
|
||||
%jumpi(fault_exception)
|
||||
// stack: code_size, kexit_info, success, address, leftover_gas
|
||||
%mul_const(@GAS_CODEDEPOSIT) %charge_gas
|
||||
SWAP3
|
||||
|
||||
// Store the code hash of the new contract.
|
||||
GET_CONTEXT
|
||||
%returndatasize
|
||||
%stack (size, ctx) -> (ctx, @SEGMENT_RETURNDATA, 0, size) // context, segment, offset, len
|
||||
KECCAK_GENERAL
|
||||
// stack: codehash, leftover_gas, success, address, kexit_info
|
||||
DUP4
|
||||
// stack: address, codehash, leftover_gas, success, address, kexit_info
|
||||
%set_codehash
|
||||
|
||||
// Set the return data size to 0.
|
||||
%mstore_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 0)
|
||||
|
||||
// stack: leftover_gas, success, address, kexit_info
|
||||
%shl_const(192)
|
||||
// stack: leftover_gas << 192, success, address, kexit_info
|
||||
@ -123,6 +166,26 @@ after_constructor:
|
||||
// stack: address_if_success, leftover_gas << 192, kexit_info
|
||||
SWAP2
|
||||
// stack: kexit_info, leftover_gas << 192, address_if_success
|
||||
ADD
|
||||
SUB
|
||||
// stack: kexit_info, address_if_success
|
||||
EXIT_KERNEL
|
||||
|
||||
%macro set_codehash
|
||||
%stack (addr, codehash) -> (addr, codehash, %%after)
|
||||
%jump(set_codehash)
|
||||
%%after:
|
||||
// stack: (empty)
|
||||
%endmacro
|
||||
|
||||
// Pre stack: addr, codehash, redest
|
||||
// Post stack: (empty)
|
||||
// TODO: Should it be copy-on-write (with make_account_copy) instead of mutating the trie?
|
||||
set_codehash:
|
||||
// stack: addr, codehash, retdest
|
||||
%mpt_read_state_trie
|
||||
// stack: account_ptr, codehash, retdest
|
||||
%add_const(3)
|
||||
// stack: codehash_ptr, codehash, retdest
|
||||
%mstore_trie_data
|
||||
// stack: retdest
|
||||
JUMP
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
global precompile_bn_add:
|
||||
// stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size
|
||||
// stack: address, retdest, new_ctx, (old stack)
|
||||
%pop2
|
||||
// stack: new_ctx, kexit_info, ret_offset, ret_size
|
||||
// stack: new_ctx, (old stack)
|
||||
DUP1
|
||||
SET_CONTEXT
|
||||
// stack: (empty)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
global precompile_bn_mul:
|
||||
// stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size
|
||||
// stack: address, retdest, new_ctx, (old stack)
|
||||
%pop2
|
||||
// stack: new_ctx, kexit_info, ret_offset, ret_size
|
||||
// stack: new_ctx, (old stack)
|
||||
DUP1
|
||||
SET_CONTEXT
|
||||
// stack: (empty)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
global precompile_ecrec:
|
||||
// stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size
|
||||
// stack: address, retdest, new_ctx, (old stack)
|
||||
%pop2
|
||||
// stack: new_ctx, kexit_info, ret_offset, ret_size
|
||||
// stack: new_ctx, (old stack)
|
||||
DUP1
|
||||
SET_CONTEXT
|
||||
// stack: (empty)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
global precompile_id:
|
||||
// stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size
|
||||
// stack: address, retdest, new_ctx, (old stack)
|
||||
%pop2
|
||||
// stack: new_ctx, kexit_info, ret_offset, ret_size
|
||||
// stack: new_ctx, (old stack)
|
||||
DUP1
|
||||
SET_CONTEXT
|
||||
// stack: (empty)
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
%macro handle_precompiles
|
||||
// stack: address, new_ctx, kexit_info, ret_offset, ret_size
|
||||
// stack: address, new_ctx, (old stack)
|
||||
PUSH %%after
|
||||
SWAP1
|
||||
// stack: address, %%after, new_ctx, kexit_info, ret_offset, ret_size
|
||||
// stack: address, %%after, new_ctx, (old stack)
|
||||
%jump(handle_precompiles)
|
||||
%%after:
|
||||
// stack: new_ctx, kexit_info, ret_offset, ret_size
|
||||
// stack: new_ctx, (old stack)
|
||||
%pop4
|
||||
%endmacro
|
||||
|
||||
global handle_precompiles:
|
||||
// stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size
|
||||
// stack: address, retdest, new_ctx, (old stack)
|
||||
DUP1 %eq_const(@ECREC) %jumpi(precompile_ecrec)
|
||||
DUP1 %eq_const(@SHA256) %jumpi(precompile_sha256)
|
||||
DUP1 %eq_const(@RIP160) %jumpi(precompile_rip160)
|
||||
@ -31,6 +31,38 @@ global pop_and_return_success:
|
||||
PUSH 1 // success
|
||||
%jump(terminate_common)
|
||||
|
||||
%macro handle_precompiles_from_eoa
|
||||
// stack: retdest
|
||||
%mload_txn_field(@TXN_FIELD_TO)
|
||||
// stack: addr, retdest
|
||||
DUP1 %ge_const(@ECREC) DUP2 %le_const(@BLAKE2_F)
|
||||
// stack: addr<=9, addr>=1, addr, retdest
|
||||
MUL // Cheaper than AND
|
||||
%jumpi(handle_precompiles_from_eoa)
|
||||
// stack: addr, retdest
|
||||
POP
|
||||
%endmacro
|
||||
|
||||
global handle_precompiles_from_eoa:
|
||||
// stack: addr, retdest
|
||||
%create_context
|
||||
// stack: new_ctx, addr, retdest
|
||||
%set_new_ctx_parent_pc(process_message_txn_after_call)
|
||||
%non_intrinisic_gas %set_new_ctx_gas_limit
|
||||
// stack: new_ctx, addr, retdest
|
||||
|
||||
// Set calldatasize and copy txn data to calldata.
|
||||
%mload_txn_field(@TXN_FIELD_DATA_LEN)
|
||||
%stack (calldata_size, new_ctx) -> (calldata_size, new_ctx, calldata_size)
|
||||
%set_new_ctx_calldata_size
|
||||
%stack (new_ctx, calldata_size) -> (new_ctx, @SEGMENT_CALLDATA, 0, 0, @SEGMENT_TXN_DATA, 0, calldata_size, handle_precompiles_from_eoa_finish, new_ctx)
|
||||
%jump(memcpy)
|
||||
|
||||
handle_precompiles_from_eoa_finish:
|
||||
%stack (new_ctx, addr, retdest) -> (addr, new_ctx, retdest)
|
||||
%handle_precompiles
|
||||
PANIC // We already checked that a precompile is called, so this should be unreachable.
|
||||
|
||||
%macro zero_out_kernel_general
|
||||
PUSH 0 PUSH 0 %mstore_kernel_general
|
||||
PUSH 0 PUSH 1 %mstore_kernel_general
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
global precompile_rip160:
|
||||
// stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size
|
||||
// stack: address, retdest, new_ctx, (old stack)
|
||||
%pop2
|
||||
// stack: new_ctx, kexit_info, ret_offset, ret_size
|
||||
// stack: new_ctx, (old stack)
|
||||
DUP1
|
||||
SET_CONTEXT
|
||||
// stack: (empty)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
global precompile_sha256:
|
||||
// stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size
|
||||
// stack: address, retdest, new_ctx, (old stack)
|
||||
%pop2
|
||||
// stack: new_ctx, kexit_info, ret_offset, ret_size
|
||||
// stack: new_ctx, (old stack)
|
||||
DUP1
|
||||
SET_CONTEXT
|
||||
// stack: (empty)
|
||||
|
||||
@ -145,6 +145,16 @@ global process_contract_creation_txn_after_constructor:
|
||||
// stack: success, leftover_gas, new_ctx, address, retdest
|
||||
POP // TODO: Success will go into the receipt when we support that.
|
||||
// stack: leftover_gas, new_ctx, address, retdest
|
||||
%returndatasize // Size of the code.
|
||||
// stack: code_size, leftover_gas, new_ctx, address, retdest
|
||||
DUP1 %gt_const(@MAX_CODE_SIZE) %jumpi(panic) // TODO: need to revert changes here.
|
||||
// stack: code_size, leftover_gas, new_ctx, address, retdest
|
||||
%mul_const(@GAS_CODEDEPOSIT) SWAP1
|
||||
// stack: leftover_gas, codedeposit_cost, new_ctx, address, retdest
|
||||
DUP2 DUP2 LT %jumpi(panic) // TODO: need to revert changes here.
|
||||
// stack: leftover_gas, codedeposit_cost, new_ctx, address, retdest
|
||||
SUB
|
||||
// stack: leftover_gas, new_ctx, address, retdest
|
||||
%pay_coinbase_and_refund_sender
|
||||
// TODO: Delete accounts in self-destruct list and empty touched addresses.
|
||||
// stack: new_ctx, address, retdest
|
||||
@ -165,6 +175,13 @@ global process_message_txn:
|
||||
%jumpi(process_message_txn_insufficient_balance)
|
||||
// stack: retdest
|
||||
|
||||
%handle_precompiles_from_eoa
|
||||
|
||||
// If to's code is empty, return.
|
||||
%mload_txn_field(@TXN_FIELD_TO) %ext_code_empty
|
||||
// stack: code_empty, retdest
|
||||
%jumpi(process_message_txn_return)
|
||||
|
||||
// Add precompiles to accessed addresses.
|
||||
PUSH @ECREC %insert_accessed_addresses_no_return
|
||||
PUSH @SHA256 %insert_accessed_addresses_no_return
|
||||
@ -175,12 +192,6 @@ global process_message_txn:
|
||||
PUSH @BN_MUL %insert_accessed_addresses_no_return
|
||||
PUSH @SNARKV %insert_accessed_addresses_no_return
|
||||
PUSH @BLAKE2_F %insert_accessed_addresses_no_return
|
||||
// TODO: Handle precompiles.
|
||||
|
||||
// If to's code is empty, return.
|
||||
%mload_txn_field(@TXN_FIELD_TO) %ext_code_empty
|
||||
// stack: code_empty, retdest
|
||||
%jumpi(process_message_txn_return)
|
||||
|
||||
// Otherwise, load to's code and execute it in a new context.
|
||||
// stack: retdest
|
||||
|
||||
@ -34,6 +34,15 @@
|
||||
// stack: (empty)
|
||||
%endmacro
|
||||
|
||||
// Store the given context metadata field to memory.
|
||||
%macro mstore_context_metadata(field, value)
|
||||
PUSH $value
|
||||
PUSH $field
|
||||
// stack: offset, value
|
||||
%mstore_current(@SEGMENT_CONTEXT_METADATA)
|
||||
// stack: (empty)
|
||||
%endmacro
|
||||
|
||||
%macro mstore_parent_context_metadata(field)
|
||||
// stack: value
|
||||
%mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT)
|
||||
|
||||
@ -45,7 +45,7 @@ mload_packing_return:
|
||||
|
||||
%macro mload_packing
|
||||
%stack (addr: 3, len) -> (addr, len, %%after)
|
||||
%jump(extcodehash)
|
||||
%jump(mload_packing)
|
||||
%%after:
|
||||
%endmacro
|
||||
|
||||
|
||||
@ -40,6 +40,10 @@ pub fn evm_constants() -> HashMap<String, U256> {
|
||||
c.insert(name.into(), U256::from(value));
|
||||
}
|
||||
|
||||
for (name, value) in CODE_SIZE_LIMIT {
|
||||
c.insert(name.into(), U256::from(value));
|
||||
}
|
||||
|
||||
for segment in Segment::all() {
|
||||
c.insert(segment.var_name().into(), (segment as u32).into());
|
||||
}
|
||||
@ -227,3 +231,9 @@ const PRECOMPILES_GAS: [(&str, u16); 13] = [
|
||||
("SNARKV_DYNAMIC_GAS", 34_000),
|
||||
("BLAKE2_F__GAS", 1),
|
||||
];
|
||||
|
||||
const CODE_SIZE_LIMIT: [(&str, u64); 3] = [
|
||||
("MAX_CODE_SIZE", 0x6000),
|
||||
("MAX_INITCODE_SIZE", 0xc000),
|
||||
("INITCODE_WORD_COST", 2),
|
||||
];
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use core::mem::{self, MaybeUninit};
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Range;
|
||||
|
||||
@ -19,6 +20,9 @@ use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher};
|
||||
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
||||
use plonky2::recursion::cyclic_recursion::check_cyclic_proof_verifier_data;
|
||||
use plonky2::recursion::dummy_circuit::cyclic_base_proof;
|
||||
use plonky2::util::serialization::{
|
||||
Buffer, GateSerializer, IoResult, Read, WitnessGeneratorSerializer, Write,
|
||||
};
|
||||
use plonky2::util::timing::TimingTree;
|
||||
use plonky2_util::log2_ceil;
|
||||
|
||||
@ -47,6 +51,7 @@ const THRESHOLD_DEGREE_BITS: usize = 13;
|
||||
/// `degree_bits`, this contains a chain of recursive circuits for shrinking that STARK from
|
||||
/// `degree_bits` to a constant `THRESHOLD_DEGREE_BITS`. It also contains a special root circuit
|
||||
/// for combining each STARK's shrunk wrapper proof into a single proof.
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub struct AllRecursiveCircuits<F, C, const D: usize>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
@ -64,12 +69,13 @@ where
|
||||
|
||||
/// Data for the EVM root circuit, which is used to combine each STARK's shrunk wrapper proof
|
||||
/// into a single proof.
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub struct RootCircuitData<F, C, const D: usize>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
circuit: CircuitData<F, C, D>,
|
||||
pub circuit: CircuitData<F, C, D>,
|
||||
proof_with_pis: [ProofWithPublicInputsTarget<D>; NUM_TABLES],
|
||||
/// For each table, various inner circuits may be used depending on the initial table size.
|
||||
/// This target holds the index of the circuit (within `final_circuits()`) that was used.
|
||||
@ -79,37 +85,182 @@ where
|
||||
cyclic_vk: VerifierCircuitTarget,
|
||||
}
|
||||
|
||||
impl<F, C, const D: usize> RootCircuitData<F, C, D>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
pub fn to_buffer(
|
||||
&self,
|
||||
buffer: &mut Vec<u8>,
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
|
||||
) -> IoResult<()> {
|
||||
buffer.write_circuit_data(&self.circuit, gate_serializer, generator_serializer)?;
|
||||
for proof in &self.proof_with_pis {
|
||||
buffer.write_target_proof_with_public_inputs(proof)?;
|
||||
}
|
||||
for index in self.index_verifier_data {
|
||||
buffer.write_target(index)?;
|
||||
}
|
||||
buffer.write_target_verifier_circuit(&self.cyclic_vk)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn from_buffer(
|
||||
buffer: &mut Buffer,
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
|
||||
) -> IoResult<Self> {
|
||||
let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?;
|
||||
let mut proof_with_pis = Vec::with_capacity(NUM_TABLES);
|
||||
for _ in 0..NUM_TABLES {
|
||||
proof_with_pis.push(buffer.read_target_proof_with_public_inputs()?);
|
||||
}
|
||||
let mut index_verifier_data = Vec::with_capacity(NUM_TABLES);
|
||||
for _ in 0..NUM_TABLES {
|
||||
index_verifier_data.push(buffer.read_target()?);
|
||||
}
|
||||
let cyclic_vk = buffer.read_target_verifier_circuit()?;
|
||||
|
||||
Ok(Self {
|
||||
circuit,
|
||||
proof_with_pis: proof_with_pis.try_into().unwrap(),
|
||||
index_verifier_data: index_verifier_data.try_into().unwrap(),
|
||||
cyclic_vk,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Data for the aggregation circuit, which is used to compress two proofs into one. Each inner
|
||||
/// proof can be either an EVM root proof or another aggregation proof.
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub struct AggregationCircuitData<F, C, const D: usize>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
circuit: CircuitData<F, C, D>,
|
||||
pub circuit: CircuitData<F, C, D>,
|
||||
lhs: AggregationChildTarget<D>,
|
||||
rhs: AggregationChildTarget<D>,
|
||||
cyclic_vk: VerifierCircuitTarget,
|
||||
}
|
||||
|
||||
impl<F, C, const D: usize> AggregationCircuitData<F, C, D>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
pub fn to_buffer(
|
||||
&self,
|
||||
buffer: &mut Vec<u8>,
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
|
||||
) -> IoResult<()> {
|
||||
buffer.write_circuit_data(&self.circuit, gate_serializer, generator_serializer)?;
|
||||
buffer.write_target_verifier_circuit(&self.cyclic_vk)?;
|
||||
self.lhs.to_buffer(buffer)?;
|
||||
self.rhs.to_buffer(buffer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn from_buffer(
|
||||
buffer: &mut Buffer,
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
|
||||
) -> IoResult<Self> {
|
||||
let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?;
|
||||
let cyclic_vk = buffer.read_target_verifier_circuit()?;
|
||||
let lhs = AggregationChildTarget::from_buffer(buffer)?;
|
||||
let rhs = AggregationChildTarget::from_buffer(buffer)?;
|
||||
Ok(Self {
|
||||
circuit,
|
||||
lhs,
|
||||
rhs,
|
||||
cyclic_vk,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub struct AggregationChildTarget<const D: usize> {
|
||||
is_agg: BoolTarget,
|
||||
agg_proof: ProofWithPublicInputsTarget<D>,
|
||||
evm_proof: ProofWithPublicInputsTarget<D>,
|
||||
}
|
||||
|
||||
impl<const D: usize> AggregationChildTarget<D> {
|
||||
pub fn to_buffer(&self, buffer: &mut Vec<u8>) -> IoResult<()> {
|
||||
buffer.write_target_bool(self.is_agg)?;
|
||||
buffer.write_target_proof_with_public_inputs(&self.agg_proof)?;
|
||||
buffer.write_target_proof_with_public_inputs(&self.evm_proof)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn from_buffer(buffer: &mut Buffer) -> IoResult<Self> {
|
||||
let is_agg = buffer.read_target_bool()?;
|
||||
let agg_proof = buffer.read_target_proof_with_public_inputs()?;
|
||||
let evm_proof = buffer.read_target_proof_with_public_inputs()?;
|
||||
Ok(Self {
|
||||
is_agg,
|
||||
agg_proof,
|
||||
evm_proof,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub struct BlockCircuitData<F, C, const D: usize>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
circuit: CircuitData<F, C, D>,
|
||||
pub circuit: CircuitData<F, C, D>,
|
||||
has_parent_block: BoolTarget,
|
||||
parent_block_proof: ProofWithPublicInputsTarget<D>,
|
||||
agg_root_proof: ProofWithPublicInputsTarget<D>,
|
||||
cyclic_vk: VerifierCircuitTarget,
|
||||
}
|
||||
|
||||
impl<F, C, const D: usize> BlockCircuitData<F, C, D>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
pub fn to_buffer(
|
||||
&self,
|
||||
buffer: &mut Vec<u8>,
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
|
||||
) -> IoResult<()> {
|
||||
buffer.write_circuit_data(&self.circuit, gate_serializer, generator_serializer)?;
|
||||
buffer.write_target_bool(self.has_parent_block)?;
|
||||
buffer.write_target_proof_with_public_inputs(&self.parent_block_proof)?;
|
||||
buffer.write_target_proof_with_public_inputs(&self.agg_root_proof)?;
|
||||
buffer.write_target_verifier_circuit(&self.cyclic_vk)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn from_buffer(
|
||||
buffer: &mut Buffer,
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
|
||||
) -> IoResult<Self> {
|
||||
let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?;
|
||||
let has_parent_block = buffer.read_target_bool()?;
|
||||
let parent_block_proof = buffer.read_target_proof_with_public_inputs()?;
|
||||
let agg_root_proof = buffer.read_target_proof_with_public_inputs()?;
|
||||
let cyclic_vk = buffer.read_target_verifier_circuit()?;
|
||||
Ok(Self {
|
||||
circuit,
|
||||
has_parent_block,
|
||||
parent_block_proof,
|
||||
agg_root_proof,
|
||||
cyclic_vk,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, C, const D: usize> AllRecursiveCircuits<F, C, D>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
@ -124,44 +275,105 @@ where
|
||||
[(); C::HCO::WIDTH]:,
|
||||
[(); C::HCI::WIDTH]:,
|
||||
{
|
||||
pub fn to_bytes(
|
||||
&self,
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
|
||||
) -> IoResult<Vec<u8>> {
|
||||
// TODO: would be better to initialize it dynamically based on the supported max degree.
|
||||
let mut buffer = Vec::with_capacity(1 << 34);
|
||||
self.root
|
||||
.to_buffer(&mut buffer, gate_serializer, generator_serializer)?;
|
||||
self.aggregation
|
||||
.to_buffer(&mut buffer, gate_serializer, generator_serializer)?;
|
||||
self.block
|
||||
.to_buffer(&mut buffer, gate_serializer, generator_serializer)?;
|
||||
for table in &self.by_table {
|
||||
table.to_buffer(&mut buffer, gate_serializer, generator_serializer)?;
|
||||
}
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
pub fn from_bytes(
|
||||
bytes: &[u8],
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
|
||||
) -> IoResult<Self> {
|
||||
let mut buffer = Buffer::new(bytes.to_vec());
|
||||
let root =
|
||||
RootCircuitData::from_buffer(&mut buffer, gate_serializer, generator_serializer)?;
|
||||
let aggregation = AggregationCircuitData::from_buffer(
|
||||
&mut buffer,
|
||||
gate_serializer,
|
||||
generator_serializer,
|
||||
)?;
|
||||
let block =
|
||||
BlockCircuitData::from_buffer(&mut buffer, gate_serializer, generator_serializer)?;
|
||||
|
||||
// Tricky use of MaybeUninit to remove the need for implementing Debug
|
||||
// for all underlying types, necessary to convert a by_table Vec to an array.
|
||||
let by_table = {
|
||||
let mut by_table: [MaybeUninit<RecursiveCircuitsForTable<F, C, D>>; NUM_TABLES] =
|
||||
unsafe { MaybeUninit::uninit().assume_init() };
|
||||
for table in &mut by_table[..] {
|
||||
let value = RecursiveCircuitsForTable::from_buffer(
|
||||
&mut buffer,
|
||||
gate_serializer,
|
||||
generator_serializer,
|
||||
)?;
|
||||
*table = MaybeUninit::new(value);
|
||||
}
|
||||
unsafe {
|
||||
mem::transmute::<_, [RecursiveCircuitsForTable<F, C, D>; NUM_TABLES]>(by_table)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
root,
|
||||
aggregation,
|
||||
block,
|
||||
by_table,
|
||||
})
|
||||
}
|
||||
|
||||
/// Preprocess all recursive circuits used by the system.
|
||||
pub fn new(
|
||||
all_stark: &AllStark<F, D>,
|
||||
degree_bits_range: Range<usize>,
|
||||
degree_bits_ranges: &[Range<usize>; NUM_TABLES],
|
||||
stark_config: &StarkConfig,
|
||||
) -> Self {
|
||||
let cpu = RecursiveCircuitsForTable::new(
|
||||
Table::Cpu,
|
||||
&all_stark.cpu_stark,
|
||||
degree_bits_range.clone(),
|
||||
degree_bits_ranges[0].clone(),
|
||||
&all_stark.cross_table_lookups,
|
||||
stark_config,
|
||||
);
|
||||
let keccak = RecursiveCircuitsForTable::new(
|
||||
Table::Keccak,
|
||||
&all_stark.keccak_stark,
|
||||
degree_bits_range.clone(),
|
||||
degree_bits_ranges[1].clone(),
|
||||
&all_stark.cross_table_lookups,
|
||||
stark_config,
|
||||
);
|
||||
let keccak_sponge = RecursiveCircuitsForTable::new(
|
||||
Table::KeccakSponge,
|
||||
&all_stark.keccak_sponge_stark,
|
||||
degree_bits_range.clone(),
|
||||
degree_bits_ranges[2].clone(),
|
||||
&all_stark.cross_table_lookups,
|
||||
stark_config,
|
||||
);
|
||||
let logic = RecursiveCircuitsForTable::new(
|
||||
Table::Logic,
|
||||
&all_stark.logic_stark,
|
||||
degree_bits_range.clone(),
|
||||
degree_bits_ranges[3].clone(),
|
||||
&all_stark.cross_table_lookups,
|
||||
stark_config,
|
||||
);
|
||||
let memory = RecursiveCircuitsForTable::new(
|
||||
Table::Memory,
|
||||
&all_stark.memory_stark,
|
||||
degree_bits_range,
|
||||
degree_bits_ranges[4].clone(),
|
||||
&all_stark.cross_table_lookups,
|
||||
stark_config,
|
||||
);
|
||||
@ -484,7 +696,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
struct RecursiveCircuitsForTable<F, C, const D: usize>
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub struct RecursiveCircuitsForTable<F, C, const D: usize>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
@ -504,6 +717,39 @@ where
|
||||
[(); C::HCO::WIDTH]:,
|
||||
[(); C::HCI::WIDTH]:,
|
||||
{
|
||||
pub fn to_buffer(
|
||||
&self,
|
||||
buffer: &mut Vec<u8>,
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
|
||||
) -> IoResult<()> {
|
||||
buffer.write_usize(self.by_stark_size.len())?;
|
||||
for (&size, table) in &self.by_stark_size {
|
||||
buffer.write_usize(size)?;
|
||||
table.to_buffer(buffer, gate_serializer, generator_serializer)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn from_buffer(
|
||||
buffer: &mut Buffer,
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
|
||||
) -> IoResult<Self> {
|
||||
let length = buffer.read_usize()?;
|
||||
let mut by_stark_size = BTreeMap::new();
|
||||
for _ in 0..length {
|
||||
let key = buffer.read_usize()?;
|
||||
let table = RecursiveCircuitsForTableSize::from_buffer(
|
||||
buffer,
|
||||
gate_serializer,
|
||||
generator_serializer,
|
||||
)?;
|
||||
by_stark_size.insert(key, table);
|
||||
}
|
||||
Ok(Self { by_stark_size })
|
||||
}
|
||||
|
||||
fn new<S: Stark<F, D>>(
|
||||
table: Table,
|
||||
stark: &S,
|
||||
@ -549,6 +795,7 @@ where
|
||||
|
||||
/// A chain of shrinking wrapper circuits, ending with a final circuit with `degree_bits`
|
||||
/// `THRESHOLD_DEGREE_BITS`.
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
struct RecursiveCircuitsForTableSize<F, C, const D: usize>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
@ -568,6 +815,67 @@ where
|
||||
[(); C::HCO::WIDTH]:,
|
||||
[(); C::HCI::WIDTH]:,
|
||||
{
|
||||
pub fn to_buffer(
|
||||
&self,
|
||||
buffer: &mut Vec<u8>,
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
|
||||
) -> IoResult<()> {
|
||||
buffer.write_usize(self.shrinking_wrappers.len())?;
|
||||
if !self.shrinking_wrappers.is_empty() {
|
||||
buffer.write_common_circuit_data(
|
||||
&self.shrinking_wrappers[0].circuit.common,
|
||||
gate_serializer,
|
||||
)?;
|
||||
}
|
||||
for wrapper in &self.shrinking_wrappers {
|
||||
buffer.write_prover_only_circuit_data(
|
||||
&wrapper.circuit.prover_only,
|
||||
generator_serializer,
|
||||
)?;
|
||||
buffer.write_verifier_only_circuit_data(&wrapper.circuit.verifier_only)?;
|
||||
buffer.write_target_proof_with_public_inputs(&wrapper.proof_with_pis_target)?;
|
||||
}
|
||||
self.initial_wrapper
|
||||
.to_buffer(buffer, gate_serializer, generator_serializer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn from_buffer(
|
||||
buffer: &mut Buffer,
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
|
||||
) -> IoResult<Self> {
|
||||
let length = buffer.read_usize()?;
|
||||
let mut shrinking_wrappers = Vec::with_capacity(length);
|
||||
if length != 0 {
|
||||
let common = buffer.read_common_circuit_data(gate_serializer)?;
|
||||
|
||||
for _ in 0..length {
|
||||
let prover_only =
|
||||
buffer.read_prover_only_circuit_data(generator_serializer, &common)?;
|
||||
let verifier_only = buffer.read_verifier_only_circuit_data()?;
|
||||
let proof_with_pis_target = buffer.read_target_proof_with_public_inputs()?;
|
||||
shrinking_wrappers.push(PlonkWrapperCircuit {
|
||||
circuit: CircuitData {
|
||||
common: common.clone(),
|
||||
prover_only,
|
||||
verifier_only,
|
||||
},
|
||||
proof_with_pis_target,
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let initial_wrapper =
|
||||
StarkWrapperCircuit::from_buffer(buffer, gate_serializer, generator_serializer)?;
|
||||
|
||||
Ok(Self {
|
||||
initial_wrapper,
|
||||
shrinking_wrappers,
|
||||
})
|
||||
}
|
||||
|
||||
fn new<S: Stark<F, D>>(
|
||||
table: Table,
|
||||
stark: &S,
|
||||
|
||||
@ -17,6 +17,7 @@ use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
use plonky2::plonk::config::{AlgebraicHasher, Hasher};
|
||||
use plonky2::plonk::plonk_common::{reduce_with_powers, reduce_with_powers_ext_circuit};
|
||||
use plonky2::util::reducing::{ReducingFactor, ReducingFactorTarget};
|
||||
use plonky2::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
use plonky2_maybe_rayon::*;
|
||||
|
||||
use crate::config::StarkConfig;
|
||||
@ -89,6 +90,30 @@ pub(crate) struct GrandProductChallengeSet<T: Copy + Eq + PartialEq + Debug> {
|
||||
pub(crate) challenges: Vec<GrandProductChallenge<T>>,
|
||||
}
|
||||
|
||||
impl GrandProductChallengeSet<Target> {
|
||||
pub fn to_buffer(&self, buffer: &mut Vec<u8>) -> IoResult<()> {
|
||||
buffer.write_usize(self.challenges.len())?;
|
||||
for challenge in &self.challenges {
|
||||
buffer.write_target(challenge.beta)?;
|
||||
buffer.write_target(challenge.gamma)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn from_buffer(buffer: &mut Buffer) -> IoResult<Self> {
|
||||
let length = buffer.read_usize()?;
|
||||
let mut challenges = Vec::with_capacity(length);
|
||||
for _ in 0..length {
|
||||
challenges.push(GrandProductChallenge {
|
||||
beta: buffer.read_target()?,
|
||||
gamma: buffer.read_target()?,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(GrandProductChallengeSet { challenges })
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute all Z polynomials (for permutation arguments).
|
||||
pub(crate) fn compute_permutation_z_polys<F, S, const D: usize>(
|
||||
stark: &S,
|
||||
|
||||
@ -12,6 +12,7 @@ use plonky2::hash::merkle_tree::MerkleCap;
|
||||
use plonky2::iop::ext_target::ExtensionTarget;
|
||||
use plonky2::iop::target::Target;
|
||||
use plonky2::plonk::config::GenericConfig;
|
||||
use plonky2::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
use plonky2_maybe_rayon::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -148,6 +149,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> S
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub struct StarkProofTarget<const D: usize> {
|
||||
pub trace_cap: MerkleCapTarget,
|
||||
pub permutation_ctl_zs_cap: MerkleCapTarget,
|
||||
@ -157,6 +159,31 @@ pub struct StarkProofTarget<const D: usize> {
|
||||
}
|
||||
|
||||
impl<const D: usize> StarkProofTarget<D> {
|
||||
pub fn to_buffer(&self, buffer: &mut Vec<u8>) -> IoResult<()> {
|
||||
buffer.write_target_merkle_cap(&self.trace_cap)?;
|
||||
buffer.write_target_merkle_cap(&self.permutation_ctl_zs_cap)?;
|
||||
buffer.write_target_merkle_cap(&self.quotient_polys_cap)?;
|
||||
buffer.write_target_fri_proof(&self.opening_proof)?;
|
||||
self.openings.to_buffer(buffer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn from_buffer(buffer: &mut Buffer) -> IoResult<Self> {
|
||||
let trace_cap = buffer.read_target_merkle_cap()?;
|
||||
let permutation_ctl_zs_cap = buffer.read_target_merkle_cap()?;
|
||||
let quotient_polys_cap = buffer.read_target_merkle_cap()?;
|
||||
let opening_proof = buffer.read_target_fri_proof()?;
|
||||
let openings = StarkOpeningSetTarget::from_buffer(buffer)?;
|
||||
|
||||
Ok(Self {
|
||||
trace_cap,
|
||||
permutation_ctl_zs_cap,
|
||||
quotient_polys_cap,
|
||||
openings,
|
||||
opening_proof,
|
||||
})
|
||||
}
|
||||
|
||||
/// Recover the length of the trace from a STARK proof and a STARK config.
|
||||
pub fn recover_degree_bits(&self, config: &StarkConfig) -> usize {
|
||||
let initial_merkle_proof = &self.opening_proof.query_round_proofs[0]
|
||||
@ -276,6 +303,7 @@ impl<F: RichField + Extendable<D>, const D: usize> StarkOpeningSet<F, D> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub struct StarkOpeningSetTarget<const D: usize> {
|
||||
pub local_values: Vec<ExtensionTarget<D>>,
|
||||
pub next_values: Vec<ExtensionTarget<D>>,
|
||||
@ -286,6 +314,34 @@ pub struct StarkOpeningSetTarget<const D: usize> {
|
||||
}
|
||||
|
||||
impl<const D: usize> StarkOpeningSetTarget<D> {
|
||||
pub fn to_buffer(&self, buffer: &mut Vec<u8>) -> IoResult<()> {
|
||||
buffer.write_target_ext_vec(&self.local_values)?;
|
||||
buffer.write_target_ext_vec(&self.next_values)?;
|
||||
buffer.write_target_ext_vec(&self.permutation_ctl_zs)?;
|
||||
buffer.write_target_ext_vec(&self.permutation_ctl_zs_next)?;
|
||||
buffer.write_target_vec(&self.ctl_zs_last)?;
|
||||
buffer.write_target_ext_vec(&self.quotient_polys)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn from_buffer(buffer: &mut Buffer) -> IoResult<Self> {
|
||||
let local_values = buffer.read_target_ext_vec::<D>()?;
|
||||
let next_values = buffer.read_target_ext_vec::<D>()?;
|
||||
let permutation_ctl_zs = buffer.read_target_ext_vec::<D>()?;
|
||||
let permutation_ctl_zs_next = buffer.read_target_ext_vec::<D>()?;
|
||||
let ctl_zs_last = buffer.read_target_vec()?;
|
||||
let quotient_polys = buffer.read_target_ext_vec::<D>()?;
|
||||
|
||||
Ok(Self {
|
||||
local_values,
|
||||
next_values,
|
||||
permutation_ctl_zs,
|
||||
permutation_ctl_zs_next,
|
||||
ctl_zs_last,
|
||||
quotient_polys,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn to_fri_openings(&self, zero: Target) -> FriOpeningsTarget<D> {
|
||||
let zeta_batch = FriOpeningBatchTarget {
|
||||
values: self
|
||||
|
||||
@ -19,6 +19,9 @@ use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, VerifierCircuitDa
|
||||
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
|
||||
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
||||
use plonky2::util::reducing::ReducingFactorTarget;
|
||||
use plonky2::util::serialization::{
|
||||
Buffer, GateSerializer, IoResult, Read, WitnessGeneratorSerializer, Write,
|
||||
};
|
||||
use plonky2::with_context;
|
||||
use plonky2_util::log2_ceil;
|
||||
|
||||
@ -157,6 +160,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
||||
}
|
||||
|
||||
/// Represents a circuit which recursively verifies a STARK proof.
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub(crate) struct StarkWrapperCircuit<F, C, const D: usize>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
@ -178,6 +182,39 @@ where
|
||||
[(); C::HCO::WIDTH]:,
|
||||
[(); C::HCI::WIDTH]:,
|
||||
{
|
||||
pub fn to_buffer(
|
||||
&self,
|
||||
buffer: &mut Vec<u8>,
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
|
||||
) -> IoResult<()> {
|
||||
buffer.write_circuit_data(&self.circuit, gate_serializer, generator_serializer)?;
|
||||
buffer.write_target_vec(&self.init_challenger_state_target)?;
|
||||
buffer.write_target(self.zero_target)?;
|
||||
self.stark_proof_target.to_buffer(buffer)?;
|
||||
self.ctl_challenges_target.to_buffer(buffer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn from_buffer(
|
||||
buffer: &mut Buffer,
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
|
||||
) -> IoResult<Self> {
|
||||
let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?;
|
||||
let init_challenger_state_target = buffer.read_target_vec()?;
|
||||
let zero_target = buffer.read_target()?;
|
||||
let stark_proof_target = StarkProofTarget::from_buffer(buffer)?;
|
||||
let ctl_challenges_target = GrandProductChallengeSet::from_buffer(buffer)?;
|
||||
Ok(Self {
|
||||
circuit,
|
||||
stark_proof_target,
|
||||
ctl_challenges_target,
|
||||
init_challenger_state_target: init_challenger_state_target.try_into().unwrap(),
|
||||
zero_target,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn prove(
|
||||
&self,
|
||||
proof_with_metadata: &StarkProofWithMetadata<F, C, D>,
|
||||
@ -212,6 +249,7 @@ where
|
||||
}
|
||||
|
||||
/// Represents a circuit which recursively verifies a PLONK proof.
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub(crate) struct PlonkWrapperCircuit<F, C, const D: usize>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
#![allow(clippy::upper_case_acronyms)]
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
use std::time::Duration;
|
||||
|
||||
use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV};
|
||||
use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie};
|
||||
use keccak_hash::keccak;
|
||||
use log::info;
|
||||
use plonky2::field::goldilocks_field::GoldilocksField;
|
||||
use plonky2::plonk::config::PoseidonGoldilocksConfig;
|
||||
use plonky2::util::serialization::{DefaultGateSerializer, DefaultGeneratorSerializer};
|
||||
use plonky2::util::timing::TimingTree;
|
||||
use plonky2_evm::all_stark::AllStark;
|
||||
use plonky2_evm::config::StarkConfig;
|
||||
@ -92,8 +95,43 @@ fn test_empty_txn_list() -> anyhow::Result<()> {
|
||||
|
||||
verify_proof(&all_stark, proof, &config)?;
|
||||
|
||||
let all_circuits = AllRecursiveCircuits::<F, C, D>::new(&all_stark, 9..19, &config);
|
||||
let all_circuits = AllRecursiveCircuits::<F, C, D>::new(
|
||||
&all_stark,
|
||||
&[9..15, 9..15, 9..10, 9..12, 9..18], // Minimal ranges to prove an empty list
|
||||
&config,
|
||||
);
|
||||
|
||||
{
|
||||
let gate_serializer = DefaultGateSerializer;
|
||||
let generator_serializer = DefaultGeneratorSerializer {
|
||||
_phantom: PhantomData::<C>,
|
||||
};
|
||||
|
||||
let timing = TimingTree::new("serialize AllRecursiveCircuits", log::Level::Info);
|
||||
let all_circuits_bytes = all_circuits
|
||||
.to_bytes(&gate_serializer, &generator_serializer)
|
||||
.map_err(|_| anyhow::Error::msg("AllRecursiveCircuits serialization failed."))?;
|
||||
timing.filter(Duration::from_millis(100)).print();
|
||||
info!(
|
||||
"AllRecursiveCircuits length: {} bytes",
|
||||
all_circuits_bytes.len()
|
||||
);
|
||||
|
||||
let timing = TimingTree::new("deserialize AllRecursiveCircuits", log::Level::Info);
|
||||
let all_circuits_from_bytes = AllRecursiveCircuits::<F, C, D>::from_bytes(
|
||||
&all_circuits_bytes,
|
||||
&gate_serializer,
|
||||
&generator_serializer,
|
||||
)
|
||||
.map_err(|_| anyhow::Error::msg("AllRecursiveCircuits deserialization failed."))?;
|
||||
timing.filter(Duration::from_millis(100)).print();
|
||||
|
||||
assert_eq!(all_circuits, all_circuits_from_bytes);
|
||||
}
|
||||
|
||||
let mut timing = TimingTree::new("prove", log::Level::Info);
|
||||
let root_proof = all_circuits.prove_root(&all_stark, &config, inputs, &mut timing)?;
|
||||
timing.filter(Duration::from_millis(100)).print();
|
||||
all_circuits.verify_root(root_proof.clone())?;
|
||||
|
||||
let agg_proof = all_circuits.prove_aggregation(false, &root_proof, false, &root_proof)?;
|
||||
|
||||
@ -46,13 +46,13 @@ impl Hash for GoldilocksField {
|
||||
|
||||
impl Display for GoldilocksField {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
Display::fmt(&self.0, f)
|
||||
Display::fmt(&self.to_canonical_u64(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for GoldilocksField {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
Debug::fmt(&self.0, f)
|
||||
Debug::fmt(&self.to_canonical_u64(), f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ use plonky2::plonk::circuit_data::{CircuitConfig, CommonCircuitData, VerifierOnl
|
||||
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, PoseidonGoldilocksConfig};
|
||||
use plonky2::plonk::proof::{CompressedProofWithPublicInputs, ProofWithPublicInputs};
|
||||
use plonky2::plonk::prover::prove;
|
||||
use plonky2::util::serialization::DefaultGateSerializer;
|
||||
use plonky2::util::timing::TimingTree;
|
||||
use plonky2_field::extension::Extendable;
|
||||
use plonky2_maybe_rayon::rayon;
|
||||
@ -181,6 +182,19 @@ where
|
||||
CompressedProofWithPublicInputs::from_bytes(compressed_proof_bytes, cd)?;
|
||||
assert_eq!(compressed_proof, compressed_proof_from_bytes);
|
||||
|
||||
let gate_serializer = DefaultGateSerializer;
|
||||
let common_data_bytes = cd
|
||||
.to_bytes(&gate_serializer)
|
||||
.map_err(|_| anyhow::Error::msg("CommonCircuitData serialization failed."))?;
|
||||
info!(
|
||||
"Common circuit data length: {} bytes",
|
||||
common_data_bytes.len()
|
||||
);
|
||||
let common_data_from_bytes =
|
||||
CommonCircuitData::<F, D>::from_bytes(common_data_bytes, &gate_serializer)
|
||||
.map_err(|_| anyhow::Error::msg("CommonCircuitData deserialization failed."))?;
|
||||
assert_eq!(cd, &common_data_from_bytes);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@ -4,18 +4,28 @@ use core::marker::PhantomData;
|
||||
|
||||
use anyhow::Result;
|
||||
use plonky2::field::types::{PrimeField, Sample};
|
||||
use plonky2::gates::arithmetic_base::ArithmeticBaseGenerator;
|
||||
use plonky2::gates::poseidon::PoseidonGenerator;
|
||||
use plonky2::gates::poseidon_mds::PoseidonMdsGenerator;
|
||||
use plonky2::hash::hash_types::RichField;
|
||||
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
|
||||
use plonky2::iop::generator::{
|
||||
ConstantGenerator, GeneratedValues, RandomValueGenerator, SimpleGenerator,
|
||||
};
|
||||
use plonky2::iop::target::Target;
|
||||
use plonky2::iop::witness::{PartialWitness, PartitionWitness, Witness, WitnessWrite};
|
||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
use plonky2::plonk::circuit_data::CircuitConfig;
|
||||
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData};
|
||||
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, PoseidonGoldilocksConfig};
|
||||
use plonky2::recursion::dummy_circuit::DummyProofGenerator;
|
||||
use plonky2::util::serialization::{
|
||||
Buffer, DefaultGateSerializer, IoResult, Read, WitnessGeneratorSerializer, Write,
|
||||
};
|
||||
use plonky2::{get_generator_tag_impl, impl_generator_serializer, read_generator_impl};
|
||||
use plonky2_field::extension::Extendable;
|
||||
|
||||
/// A generator used by the prover to calculate the square root (`x`) of a given value
|
||||
/// (`x_squared`), outside of the circuit, in order to supply it as an additional public input.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
struct SquareRootGenerator<F: RichField + Extendable<D>, const D: usize> {
|
||||
x: Target,
|
||||
x_squared: Target,
|
||||
@ -25,6 +35,10 @@ struct SquareRootGenerator<F: RichField + Extendable<D>, const D: usize> {
|
||||
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||
for SquareRootGenerator<F, D>
|
||||
{
|
||||
fn id(&self) -> String {
|
||||
"SquareRootGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
vec![self.x_squared]
|
||||
}
|
||||
@ -37,6 +51,43 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||
|
||||
out_buffer.set_target(self.x, x);
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_target(self.x)?;
|
||||
dst.write_target(self.x_squared)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let x = src.read_target()?;
|
||||
let x_squared = src.read_target()?;
|
||||
Ok(Self {
|
||||
x,
|
||||
x_squared,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CustomGeneratorSerializer<C: GenericConfig<D>, const D: usize> {
|
||||
pub _phantom: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<F, C, const D: usize> WitnessGeneratorSerializer<F, D> for CustomGeneratorSerializer<C, D>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F> + 'static,
|
||||
C::Hasher: AlgebraicHasher<F, C::HCO>,
|
||||
{
|
||||
impl_generator_serializer! {
|
||||
CustomGeneratorSerializer,
|
||||
DummyProofGenerator<F, C, D>,
|
||||
ArithmeticBaseGenerator<F, D>,
|
||||
ConstantGenerator<F>,
|
||||
PoseidonGenerator<F, D>,
|
||||
PoseidonMdsGenerator<D>,
|
||||
RandomValueGenerator,
|
||||
SquareRootGenerator<F, D>
|
||||
}
|
||||
}
|
||||
|
||||
/// An example of using Plonky2 to prove a statement of the form
|
||||
@ -79,5 +130,26 @@ fn main() -> Result<()> {
|
||||
let x_squared_actual = proof.public_inputs[0];
|
||||
println!("Field element (square): {x_squared_actual}");
|
||||
|
||||
// Test serialization
|
||||
{
|
||||
let gate_serializer = DefaultGateSerializer;
|
||||
let generator_serializer = CustomGeneratorSerializer {
|
||||
_phantom: PhantomData::<C>,
|
||||
};
|
||||
|
||||
let data_bytes = data
|
||||
.to_bytes(&gate_serializer, &generator_serializer)
|
||||
.map_err(|_| anyhow::Error::msg("CircuitData serialization failed."))?;
|
||||
|
||||
let data_from_bytes = CircuitData::<F, C, D>::from_bytes(
|
||||
&data_bytes,
|
||||
&gate_serializer,
|
||||
&generator_serializer,
|
||||
)
|
||||
.map_err(|_| anyhow::Error::msg("CircuitData deserialization failed."))?;
|
||||
|
||||
assert_eq!(data, data_from_bytes);
|
||||
}
|
||||
|
||||
data.verify(proof)
|
||||
}
|
||||
|
||||
@ -27,6 +27,7 @@ use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place, transp
|
||||
pub const SALT_SIZE: usize = 4;
|
||||
|
||||
/// Represents a FRI oracle, i.e. a batch of polynomials which have been Merklized.
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub struct PolynomialBatch<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
||||
{
|
||||
pub polynomials: Vec<PolynomialCoeffs<F>>,
|
||||
|
||||
@ -33,7 +33,7 @@ pub struct FriQueryStep<
|
||||
pub merkle_proof: MerkleProof<F, HC, H>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct FriQueryStepTarget<const D: usize> {
|
||||
pub evals: Vec<ExtensionTarget<D>>,
|
||||
pub merkle_proof: MerkleProofTarget,
|
||||
@ -58,7 +58,7 @@ impl<F: RichField, HC: HashConfig, H: Hasher<F, HC>> FriInitialTreeProof<F, HC,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct FriInitialTreeProofTarget {
|
||||
pub evals_proofs: Vec<(Vec<Target>, MerkleProofTarget)>,
|
||||
}
|
||||
@ -92,7 +92,7 @@ pub struct FriQueryRound<
|
||||
pub steps: Vec<FriQueryStep<F, HC, H, D>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct FriQueryRoundTarget<const D: usize> {
|
||||
pub initial_trees_proof: FriInitialTreeProofTarget,
|
||||
pub steps: Vec<FriQueryStepTarget<D>>,
|
||||
@ -129,7 +129,7 @@ pub struct FriProof<F: RichField + Extendable<D>, HC: HashConfig, H: Hasher<F, H
|
||||
pub pow_witness: F,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct FriProofTarget<const D: usize> {
|
||||
pub commit_phase_merkle_caps: Vec<MerkleCapTarget>,
|
||||
pub query_round_proofs: Vec<FriQueryRoundTarget<D>>,
|
||||
|
||||
@ -11,6 +11,7 @@ use crate::iop::generator::{GeneratedValues, SimpleGenerator};
|
||||
use crate::iop::target::{BoolTarget, Target};
|
||||
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Computes `-x`.
|
||||
@ -370,8 +371,8 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct EqualityGenerator {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct EqualityGenerator {
|
||||
x: Target,
|
||||
y: Target,
|
||||
equal: BoolTarget,
|
||||
@ -379,6 +380,10 @@ struct EqualityGenerator {
|
||||
}
|
||||
|
||||
impl<F: RichField> SimpleGenerator<F> for EqualityGenerator {
|
||||
fn id(&self) -> String {
|
||||
"EqualityGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
vec![self.x, self.y]
|
||||
}
|
||||
@ -392,6 +397,21 @@ impl<F: RichField> SimpleGenerator<F> for EqualityGenerator {
|
||||
out_buffer.set_bool_target(self.equal, x == y);
|
||||
out_buffer.set_target(self.inv, inv);
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_target(self.x)?;
|
||||
dst.write_target(self.y)?;
|
||||
dst.write_target_bool(self.equal)?;
|
||||
dst.write_target(self.inv)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let x = src.read_target()?;
|
||||
let y = src.read_target()?;
|
||||
let equal = src.read_target_bool()?;
|
||||
let inv = src.read_target()?;
|
||||
Ok(Self { x, y, equal, inv })
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a base arithmetic operation in the circuit. Used to memoize results.
|
||||
|
||||
@ -13,6 +13,7 @@ use crate::iop::target::Target;
|
||||
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::util::bits_u64;
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
pub fn arithmetic_extension(
|
||||
@ -493,8 +494,8 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct QuotientGeneratorExtension<const D: usize> {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct QuotientGeneratorExtension<const D: usize> {
|
||||
numerator: ExtensionTarget<D>,
|
||||
denominator: ExtensionTarget<D>,
|
||||
quotient: ExtensionTarget<D>,
|
||||
@ -503,6 +504,10 @@ struct QuotientGeneratorExtension<const D: usize> {
|
||||
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||
for QuotientGeneratorExtension<D>
|
||||
{
|
||||
fn id(&self) -> String {
|
||||
"QuotientGeneratorExtension".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
let mut deps = self.numerator.to_target_array().to_vec();
|
||||
deps.extend(self.denominator.to_target_array());
|
||||
@ -515,6 +520,23 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||
let quotient = num / dem;
|
||||
out_buffer.set_extension_target(self.quotient, quotient)
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_target_ext(self.numerator)?;
|
||||
dst.write_target_ext(self.denominator)?;
|
||||
dst.write_target_ext(self.quotient)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let numerator = src.read_target_ext()?;
|
||||
let denominator = src.read_target_ext()?;
|
||||
let quotient = src.read_target_ext()?;
|
||||
Ok(Self {
|
||||
numerator,
|
||||
denominator,
|
||||
quotient,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the powers of a certain base element `b`: `b^0, b^1, b^2, ...`.
|
||||
|
||||
@ -7,4 +7,4 @@ pub mod random_access;
|
||||
pub mod range_check;
|
||||
pub mod select;
|
||||
pub mod split_base;
|
||||
pub(crate) mod split_join;
|
||||
pub mod split_join;
|
||||
|
||||
@ -7,7 +7,7 @@ use crate::iop::target::Target;
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::util::reducing::ReducingFactorTarget;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct PolynomialCoeffsExtTarget<const D: usize>(pub Vec<ExtensionTarget<D>>);
|
||||
|
||||
impl<const D: usize> PolynomialCoeffsExtTarget<D> {
|
||||
|
||||
@ -7,6 +7,7 @@ use crate::iop::generator::{GeneratedValues, SimpleGenerator};
|
||||
use crate::iop::target::{BoolTarget, Target};
|
||||
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Checks that `x < 2^n_log` using a `BaseSumGate`.
|
||||
@ -51,8 +52,8 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct LowHighGenerator {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct LowHighGenerator {
|
||||
integer: Target,
|
||||
n_log: usize,
|
||||
low: Target,
|
||||
@ -60,6 +61,10 @@ struct LowHighGenerator {
|
||||
}
|
||||
|
||||
impl<F: RichField> SimpleGenerator<F> for LowHighGenerator {
|
||||
fn id(&self) -> String {
|
||||
"LowHighGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
vec![self.integer]
|
||||
}
|
||||
@ -72,4 +77,24 @@ impl<F: RichField> SimpleGenerator<F> for LowHighGenerator {
|
||||
out_buffer.set_target(self.low, F::from_canonical_u64(low));
|
||||
out_buffer.set_target(self.high, F::from_canonical_u64(high));
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_target(self.integer)?;
|
||||
dst.write_usize(self.n_log)?;
|
||||
dst.write_target(self.low)?;
|
||||
dst.write_target(self.high)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let integer = src.read_target()?;
|
||||
let n_log = src.read_usize()?;
|
||||
let low = src.read_target()?;
|
||||
let high = src.read_target()?;
|
||||
Ok(Self {
|
||||
integer,
|
||||
n_log,
|
||||
low,
|
||||
high,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ use crate::iop::target::{BoolTarget, Target};
|
||||
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::util::log_floor;
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Split the given element into a list of targets, where each one represents a
|
||||
@ -79,13 +80,17 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct BaseSumGenerator<const B: usize> {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct BaseSumGenerator<const B: usize> {
|
||||
row: usize,
|
||||
limbs: Vec<BoolTarget>,
|
||||
}
|
||||
|
||||
impl<F: Field, const B: usize> SimpleGenerator<F> for BaseSumGenerator<B> {
|
||||
fn id(&self) -> String {
|
||||
"BaseSumGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
self.limbs.iter().map(|b| b.target).collect()
|
||||
}
|
||||
@ -102,6 +107,17 @@ impl<F: Field, const B: usize> SimpleGenerator<F> for BaseSumGenerator<B> {
|
||||
|
||||
out_buffer.set_target(Target::wire(self.row, BaseSumGate::<B>::WIRE_SUM), sum);
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.row)?;
|
||||
dst.write_target_bool_vec(&self.limbs)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let row = src.read_usize()?;
|
||||
let limbs = src.read_target_bool_vec()?;
|
||||
Ok(Self { row, limbs })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -9,6 +9,7 @@ use crate::iop::target::{BoolTarget, Target};
|
||||
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::util::ceil_div_usize;
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Split the given integer into a list of wires, where each one represents a
|
||||
@ -55,13 +56,17 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SplitGenerator {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SplitGenerator {
|
||||
integer: Target,
|
||||
bits: Vec<Target>,
|
||||
}
|
||||
|
||||
impl<F: RichField> SimpleGenerator<F> for SplitGenerator {
|
||||
fn id(&self) -> String {
|
||||
"SplitGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
vec![self.integer]
|
||||
}
|
||||
@ -80,16 +85,31 @@ impl<F: RichField> SimpleGenerator<F> for SplitGenerator {
|
||||
"Integer too large to fit in given number of bits"
|
||||
);
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_target(self.integer)?;
|
||||
dst.write_target_vec(&self.bits)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let integer = src.read_target()?;
|
||||
let bits = src.read_target_vec()?;
|
||||
Ok(Self { integer, bits })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct WireSplitGenerator {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct WireSplitGenerator {
|
||||
integer: Target,
|
||||
gates: Vec<usize>,
|
||||
num_limbs: usize,
|
||||
}
|
||||
|
||||
impl<F: RichField> SimpleGenerator<F> for WireSplitGenerator {
|
||||
fn id(&self) -> String {
|
||||
"WireSplitGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
vec![self.integer]
|
||||
}
|
||||
@ -120,4 +140,21 @@ impl<F: RichField> SimpleGenerator<F> for WireSplitGenerator {
|
||||
self.gates.len()
|
||||
);
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_target(self.integer)?;
|
||||
dst.write_usize_vec(&self.gates)?;
|
||||
dst.write_usize(self.num_limbs)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let integer = src.read_target()?;
|
||||
let gates = src.read_usize_vec()?;
|
||||
let num_limbs = src.read_usize()?;
|
||||
Ok(Self {
|
||||
integer,
|
||||
gates,
|
||||
num_limbs,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
@ -10,7 +9,7 @@ use crate::gates::packed_util::PackedEvaluableBase;
|
||||
use crate::gates::util::StridedConstraintConsumer;
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::iop::ext_target::ExtensionTarget;
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef};
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
@ -19,6 +18,7 @@ use crate::plonk::vars::{
|
||||
EvaluationTargets, EvaluationVars, EvaluationVarsBase, EvaluationVarsBaseBatch,
|
||||
EvaluationVarsBasePacked,
|
||||
};
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
/// A gate which can perform a weighted multiply-add, i.e. `result = c0 x y + c1 z`. If the config
|
||||
/// supports enough routed wires, it can support several such operations in one gate.
|
||||
@ -60,6 +60,15 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ArithmeticGate
|
||||
format!("{self:?}")
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.num_ops)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let num_ops = src.read_usize()?;
|
||||
Ok(Self { num_ops })
|
||||
}
|
||||
|
||||
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
|
||||
let const_0 = vars.local_constants[0];
|
||||
let const_1 = vars.local_constants[1];
|
||||
@ -117,10 +126,10 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ArithmeticGate
|
||||
constraints
|
||||
}
|
||||
|
||||
fn generators(&self, row: usize, local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
|
||||
fn generators(&self, row: usize, local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
|
||||
(0..self.num_ops)
|
||||
.map(|i| {
|
||||
let g: Box<dyn WitnessGenerator<F>> = Box::new(
|
||||
WitnessGeneratorRef::new(
|
||||
ArithmeticBaseGenerator {
|
||||
row,
|
||||
const_0: local_constants[0],
|
||||
@ -128,8 +137,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ArithmeticGate
|
||||
i,
|
||||
}
|
||||
.adapter(),
|
||||
);
|
||||
g
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@ -172,8 +180,8 @@ impl<F: RichField + Extendable<D>, const D: usize> PackedEvaluableBase<F, D> for
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct ArithmeticBaseGenerator<F: RichField + Extendable<D>, const D: usize> {
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ArithmeticBaseGenerator<F: RichField + Extendable<D>, const D: usize> {
|
||||
row: usize,
|
||||
const_0: F,
|
||||
const_1: F,
|
||||
@ -183,6 +191,10 @@ struct ArithmeticBaseGenerator<F: RichField + Extendable<D>, const D: usize> {
|
||||
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||
for ArithmeticBaseGenerator<F, D>
|
||||
{
|
||||
fn id(&self) -> String {
|
||||
"ArithmeticBaseGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
[
|
||||
ArithmeticGate::wire_ith_multiplicand_0(self.i),
|
||||
@ -208,6 +220,26 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||
|
||||
out_buffer.set_target(output_target, computed_output)
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.row)?;
|
||||
dst.write_field(self.const_0)?;
|
||||
dst.write_field(self.const_1)?;
|
||||
dst.write_usize(self.i)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let row = src.read_usize()?;
|
||||
let const_0 = src.read_field()?;
|
||||
let const_1 = src.read_field()?;
|
||||
let i = src.read_usize()?;
|
||||
Ok(Self {
|
||||
row,
|
||||
const_0,
|
||||
const_1,
|
||||
i,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
@ -9,12 +8,13 @@ use crate::gates::gate::Gate;
|
||||
use crate::gates::util::StridedConstraintConsumer;
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::iop::ext_target::ExtensionTarget;
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef};
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
/// A gate which can perform a weighted multiply-add, i.e. `result = c0 x y + c1 z`. If the config
|
||||
/// supports enough routed wires, it can support several such operations in one gate.
|
||||
@ -56,6 +56,15 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ArithmeticExte
|
||||
format!("{self:?}")
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.num_ops)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let num_ops = src.read_usize()?;
|
||||
Ok(Self { num_ops })
|
||||
}
|
||||
|
||||
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
|
||||
let const_0 = vars.local_constants[0];
|
||||
let const_1 = vars.local_constants[1];
|
||||
@ -122,10 +131,10 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ArithmeticExte
|
||||
constraints
|
||||
}
|
||||
|
||||
fn generators(&self, row: usize, local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
|
||||
fn generators(&self, row: usize, local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
|
||||
(0..self.num_ops)
|
||||
.map(|i| {
|
||||
let g: Box<dyn WitnessGenerator<F>> = Box::new(
|
||||
WitnessGeneratorRef::new(
|
||||
ArithmeticExtensionGenerator {
|
||||
row,
|
||||
const_0: local_constants[0],
|
||||
@ -133,8 +142,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ArithmeticExte
|
||||
i,
|
||||
}
|
||||
.adapter(),
|
||||
);
|
||||
g
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@ -156,8 +164,8 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ArithmeticExte
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct ArithmeticExtensionGenerator<F: RichField + Extendable<D>, const D: usize> {
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ArithmeticExtensionGenerator<F: RichField + Extendable<D>, const D: usize> {
|
||||
row: usize,
|
||||
const_0: F,
|
||||
const_1: F,
|
||||
@ -167,6 +175,10 @@ struct ArithmeticExtensionGenerator<F: RichField + Extendable<D>, const D: usize
|
||||
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||
for ArithmeticExtensionGenerator<F, D>
|
||||
{
|
||||
fn id(&self) -> String {
|
||||
"ArithmeticExtensionGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
ArithmeticExtensionGate::<D>::wires_ith_multiplicand_0(self.i)
|
||||
.chain(ArithmeticExtensionGate::<D>::wires_ith_multiplicand_1(
|
||||
@ -201,6 +213,26 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||
|
||||
out_buffer.set_extension_target(output_target, computed_output)
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.row)?;
|
||||
dst.write_field(self.const_0)?;
|
||||
dst.write_field(self.const_1)?;
|
||||
dst.write_usize(self.i)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let row = src.read_usize()?;
|
||||
let const_0 = src.read_field()?;
|
||||
let const_1 = src.read_field()?;
|
||||
let i = src.read_usize()?;
|
||||
Ok(Self {
|
||||
row,
|
||||
const_0,
|
||||
const_1,
|
||||
i,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::{format, vec};
|
||||
@ -12,7 +11,7 @@ use crate::gates::packed_util::PackedEvaluableBase;
|
||||
use crate::gates::util::StridedConstraintConsumer;
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::iop::ext_target::ExtensionTarget;
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef};
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
@ -23,6 +22,7 @@ use crate::plonk::vars::{
|
||||
EvaluationVarsBasePacked,
|
||||
};
|
||||
use crate::util::log_floor;
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
/// A gate which can decompose a number into base B little-endian limbs.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
@ -55,6 +55,15 @@ impl<F: RichField + Extendable<D>, const D: usize, const B: usize> Gate<F, D> fo
|
||||
format!("{self:?} + Base: {B}")
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.num_limbs)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let num_limbs = src.read_usize()?;
|
||||
Ok(Self { num_limbs })
|
||||
}
|
||||
|
||||
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
|
||||
let sum = vars.local_wires[Self::WIRE_SUM];
|
||||
let limbs = vars.local_wires[self.limbs()].to_vec();
|
||||
@ -109,12 +118,12 @@ impl<F: RichField + Extendable<D>, const D: usize, const B: usize> Gate<F, D> fo
|
||||
constraints
|
||||
}
|
||||
|
||||
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
|
||||
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
|
||||
let gen = BaseSplitGenerator::<B> {
|
||||
row,
|
||||
num_limbs: self.num_limbs,
|
||||
};
|
||||
vec![Box::new(gen.adapter())]
|
||||
vec![WitnessGeneratorRef::new(gen.adapter())]
|
||||
}
|
||||
|
||||
// 1 for the sum then `num_limbs` for the limbs.
|
||||
@ -160,13 +169,17 @@ impl<F: RichField + Extendable<D>, const D: usize, const B: usize> PackedEvaluab
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct BaseSplitGenerator<const B: usize> {
|
||||
row: usize,
|
||||
num_limbs: usize,
|
||||
}
|
||||
|
||||
impl<F: RichField, const B: usize> SimpleGenerator<F> for BaseSplitGenerator<B> {
|
||||
fn id(&self) -> String {
|
||||
"BaseSplitGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
vec![Target::wire(self.row, BaseSumGate::<B>::WIRE_SUM)]
|
||||
}
|
||||
@ -195,6 +208,17 @@ impl<F: RichField, const B: usize> SimpleGenerator<F> for BaseSplitGenerator<B>
|
||||
out_buffer.set_target(b, b_value);
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.row)?;
|
||||
dst.write_usize(self.num_limbs)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let row = src.read_usize()?;
|
||||
let num_limbs = src.read_usize()?;
|
||||
Ok(Self { row, num_limbs })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::{format, vec};
|
||||
@ -10,12 +9,13 @@ use crate::gates::packed_util::PackedEvaluableBase;
|
||||
use crate::gates::util::StridedConstraintConsumer;
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::iop::ext_target::ExtensionTarget;
|
||||
use crate::iop::generator::WitnessGenerator;
|
||||
use crate::iop::generator::WitnessGeneratorRef;
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::vars::{
|
||||
EvaluationTargets, EvaluationVars, EvaluationVarsBase, EvaluationVarsBaseBatch,
|
||||
EvaluationVarsBasePacked,
|
||||
};
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
/// A gate which takes a single constant parameter and outputs that value.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
@ -40,6 +40,15 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ConstantGate {
|
||||
format!("{self:?}")
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.num_consts)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let num_consts = src.read_usize()?;
|
||||
Ok(Self { num_consts })
|
||||
}
|
||||
|
||||
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
|
||||
(0..self.num_consts)
|
||||
.map(|i| {
|
||||
@ -75,7 +84,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ConstantGate {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn generators(&self, _row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
|
||||
fn generators(&self, _row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::{format, vec};
|
||||
@ -13,12 +12,13 @@ use crate::gates::gate::Gate;
|
||||
use crate::gates::util::StridedConstraintConsumer;
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::iop::ext_target::{ExtensionAlgebraTarget, ExtensionTarget};
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef};
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::wire::Wire;
|
||||
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
/// One of the instantiations of `InterpolationGate`: allows constraints of variable
|
||||
/// degree, up to `1<<subgroup_bits`.
|
||||
@ -45,7 +45,7 @@ use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
///
|
||||
/// Then e[N] is the final interpolated value. The non-routed wires hold every (d - 1)'th
|
||||
/// intermediate value of p and e, starting at p[d] and e[d], where d is the gate degree.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct CosetInterpolationGate<F: RichField + Extendable<D>, const D: usize> {
|
||||
pub subgroup_bits: usize,
|
||||
pub degree: usize,
|
||||
@ -168,6 +168,26 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for CosetInterpola
|
||||
format!("{self:?}<D={D}>")
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.subgroup_bits)?;
|
||||
dst.write_usize(self.degree)?;
|
||||
dst.write_usize(self.barycentric_weights.len())?;
|
||||
dst.write_field_vec(&self.barycentric_weights)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let subgroup_bits = src.read_usize()?;
|
||||
let degree = src.read_usize()?;
|
||||
let length = src.read_usize()?;
|
||||
let barycentric_weights: Vec<F> = src.read_field_vec(length)?;
|
||||
Ok(Self {
|
||||
subgroup_bits,
|
||||
degree,
|
||||
barycentric_weights,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
|
||||
let mut constraints = Vec::with_capacity(self.num_constraints());
|
||||
|
||||
@ -342,9 +362,9 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for CosetInterpola
|
||||
constraints
|
||||
}
|
||||
|
||||
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
|
||||
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
|
||||
let gen = InterpolationGenerator::<F, D>::new(row, self.clone());
|
||||
vec![Box::new(gen.adapter())]
|
||||
vec![WitnessGeneratorRef::new(gen.adapter())]
|
||||
}
|
||||
|
||||
fn num_wires(&self) -> usize {
|
||||
@ -366,8 +386,8 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for CosetInterpola
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct InterpolationGenerator<F: RichField + Extendable<D>, const D: usize> {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct InterpolationGenerator<F: RichField + Extendable<D>, const D: usize> {
|
||||
row: usize,
|
||||
gate: CosetInterpolationGate<F, D>,
|
||||
interpolation_domain: Vec<F>,
|
||||
@ -389,6 +409,10 @@ impl<F: RichField + Extendable<D>, const D: usize> InterpolationGenerator<F, D>
|
||||
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||
for InterpolationGenerator<F, D>
|
||||
{
|
||||
fn id(&self) -> String {
|
||||
"InterpolationGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
let local_target = |column| {
|
||||
Target::Wire(Wire {
|
||||
@ -471,6 +495,17 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||
let evaluation_value_wires = self.gate.wires_evaluation_value().map(local_wire);
|
||||
out_buffer.set_ext_wires(evaluation_value_wires, computed_eval);
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.row)?;
|
||||
self.gate.serialize(dst)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let row = src.read_usize()?;
|
||||
let gate = CosetInterpolationGate::deserialize(src)?;
|
||||
Ok(Self::new(row, gate))
|
||||
}
|
||||
}
|
||||
|
||||
/// Interpolate the polynomial defined by its values on an arbitrary domain at the given point `x`.
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::{format, vec};
|
||||
@ -13,7 +12,7 @@ use crate::gates::packed_util::PackedEvaluableBase;
|
||||
use crate::gates::util::StridedConstraintConsumer;
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::iop::ext_target::ExtensionTarget;
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef};
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::wire::Wire;
|
||||
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||
@ -23,9 +22,10 @@ use crate::plonk::vars::{
|
||||
EvaluationTargets, EvaluationVars, EvaluationVarsBase, EvaluationVarsBaseBatch,
|
||||
EvaluationVarsBasePacked,
|
||||
};
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
/// A gate for raising a value to a power.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ExponentiationGate<F: RichField + Extendable<D>, const D: usize> {
|
||||
pub num_power_bits: usize,
|
||||
pub _phantom: PhantomData<F>,
|
||||
@ -76,6 +76,15 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for Exponentiation
|
||||
format!("{self:?}<D={D}>")
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.num_power_bits)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let num_power_bits = src.read_usize()?;
|
||||
Ok(Self::new(num_power_bits))
|
||||
}
|
||||
|
||||
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
|
||||
let base = vars.local_wires[self.wire_base()];
|
||||
|
||||
@ -164,12 +173,12 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for Exponentiation
|
||||
constraints
|
||||
}
|
||||
|
||||
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
|
||||
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
|
||||
let gen = ExponentiationGenerator::<F, D> {
|
||||
row,
|
||||
gate: self.clone(),
|
||||
};
|
||||
vec![Box::new(gen.adapter())]
|
||||
vec![WitnessGeneratorRef::new(gen.adapter())]
|
||||
}
|
||||
|
||||
fn num_wires(&self) -> usize {
|
||||
@ -228,8 +237,8 @@ impl<F: RichField + Extendable<D>, const D: usize> PackedEvaluableBase<F, D>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ExponentiationGenerator<F: RichField + Extendable<D>, const D: usize> {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ExponentiationGenerator<F: RichField + Extendable<D>, const D: usize> {
|
||||
row: usize,
|
||||
gate: ExponentiationGate<F, D>,
|
||||
}
|
||||
@ -237,6 +246,10 @@ struct ExponentiationGenerator<F: RichField + Extendable<D>, const D: usize> {
|
||||
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||
for ExponentiationGenerator<F, D>
|
||||
{
|
||||
fn id(&self) -> String {
|
||||
"ExponentiationGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
let local_target = |column| Target::wire(self.row, column);
|
||||
|
||||
@ -281,6 +294,17 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||
let output_wire = local_wire(self.gate.wire_output());
|
||||
out_buffer.set_wire(output_wire, intermediate_values[num_power_bits - 1]);
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.row)?;
|
||||
self.gate.serialize(dst)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let row = src.read_usize()?;
|
||||
let gate = ExponentiationGate::deserialize(src)?;
|
||||
Ok(Self { row, gate })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::any::Any;
|
||||
use core::fmt::{Debug, Error, Formatter};
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::ops::Range;
|
||||
@ -16,16 +16,23 @@ use crate::gates::selectors::UNUSED_SELECTOR;
|
||||
use crate::gates::util::StridedConstraintConsumer;
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::iop::ext_target::ExtensionTarget;
|
||||
use crate::iop::generator::WitnessGenerator;
|
||||
use crate::iop::generator::WitnessGeneratorRef;
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::vars::{
|
||||
EvaluationTargets, EvaluationVars, EvaluationVarsBase, EvaluationVarsBaseBatch,
|
||||
};
|
||||
use crate::util::serialization::{Buffer, IoResult};
|
||||
|
||||
/// A custom gate.
|
||||
pub trait Gate<F: RichField + Extendable<D>, const D: usize>: 'static + Send + Sync {
|
||||
fn id(&self) -> String;
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()>;
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension>;
|
||||
|
||||
/// Like `eval_unfiltered`, but specialized for points in the base field.
|
||||
@ -162,7 +169,7 @@ pub trait Gate<F: RichField + Extendable<D>, const D: usize>: 'static + Send + S
|
||||
|
||||
/// The generators used to populate the witness.
|
||||
/// Note: This should return exactly 1 generator per operation in the gate.
|
||||
fn generators(&self, row: usize, local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>>;
|
||||
fn generators(&self, row: usize, local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>>;
|
||||
|
||||
/// The number of wires used by this gate.
|
||||
fn num_wires(&self) -> usize;
|
||||
@ -191,9 +198,20 @@ pub trait Gate<F: RichField + Extendable<D>, const D: usize>: 'static + Send + S
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper around an `Rc<Gate>` which implements `PartialEq`, `Eq` and `Hash` based on gate IDs.
|
||||
/// A wrapper trait over a `Gate`, to allow for gate serialization.
|
||||
pub trait AnyGate<F: RichField + Extendable<D>, const D: usize>: Gate<F, D> {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
}
|
||||
|
||||
impl<T: Gate<F, D>, F: RichField + Extendable<D>, const D: usize> AnyGate<F, D> for T {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper around an `Arc<AnyGate>` which implements `PartialEq`, `Eq` and `Hash` based on gate IDs.
|
||||
#[derive(Clone)]
|
||||
pub struct GateRef<F: RichField + Extendable<D>, const D: usize>(pub(crate) Arc<dyn Gate<F, D>>);
|
||||
pub struct GateRef<F: RichField + Extendable<D>, const D: usize>(pub(crate) Arc<dyn AnyGate<F, D>>);
|
||||
|
||||
impl<F: RichField + Extendable<D>, const D: usize> GateRef<F, D> {
|
||||
pub fn new<G: Gate<F, D>>(gate: G) -> GateRef<F, D> {
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
@ -9,12 +8,13 @@ use crate::gates::gate::Gate;
|
||||
use crate::gates::util::StridedConstraintConsumer;
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::iop::ext_target::ExtensionTarget;
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef};
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
/// A gate which can perform a weighted multiplication, i.e. `result = c0 x y`. If the config
|
||||
/// supports enough routed wires, it can support several such operations in one gate.
|
||||
@ -53,6 +53,15 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for MulExtensionGa
|
||||
format!("{self:?}")
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.num_ops)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let num_ops = src.read_usize()?;
|
||||
Ok(Self { num_ops })
|
||||
}
|
||||
|
||||
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
|
||||
let const_0 = vars.local_constants[0];
|
||||
|
||||
@ -110,18 +119,17 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for MulExtensionGa
|
||||
constraints
|
||||
}
|
||||
|
||||
fn generators(&self, row: usize, local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
|
||||
fn generators(&self, row: usize, local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
|
||||
(0..self.num_ops)
|
||||
.map(|i| {
|
||||
let g: Box<dyn WitnessGenerator<F>> = Box::new(
|
||||
WitnessGeneratorRef::new(
|
||||
MulExtensionGenerator {
|
||||
row,
|
||||
const_0: local_constants[0],
|
||||
i,
|
||||
}
|
||||
.adapter(),
|
||||
);
|
||||
g
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@ -143,8 +151,8 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for MulExtensionGa
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct MulExtensionGenerator<F: RichField + Extendable<D>, const D: usize> {
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct MulExtensionGenerator<F: RichField + Extendable<D>, const D: usize> {
|
||||
row: usize,
|
||||
const_0: F,
|
||||
i: usize,
|
||||
@ -153,6 +161,10 @@ struct MulExtensionGenerator<F: RichField + Extendable<D>, const D: usize> {
|
||||
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||
for MulExtensionGenerator<F, D>
|
||||
{
|
||||
fn id(&self) -> String {
|
||||
"MulExtensionGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
MulExtensionGate::<D>::wires_ith_multiplicand_0(self.i)
|
||||
.chain(MulExtensionGate::<D>::wires_ith_multiplicand_1(self.i))
|
||||
@ -178,6 +190,19 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||
|
||||
out_buffer.set_extension_target(output_target, computed_output)
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.row)?;
|
||||
dst.write_field(self.const_0)?;
|
||||
dst.write_usize(self.i)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let row = src.read_usize()?;
|
||||
let const_0 = src.read_field()?;
|
||||
let i = src.read_usize()?;
|
||||
Ok(Self { row, const_0, i })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
@ -6,9 +5,10 @@ use crate::field::extension::Extendable;
|
||||
use crate::gates::gate::Gate;
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::iop::ext_target::ExtensionTarget;
|
||||
use crate::iop::generator::WitnessGenerator;
|
||||
use crate::iop::generator::WitnessGeneratorRef;
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBaseBatch};
|
||||
use crate::util::serialization::{Buffer, IoResult};
|
||||
|
||||
/// A gate which does nothing.
|
||||
pub struct NoopGate;
|
||||
@ -18,6 +18,14 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for NoopGate {
|
||||
"NoopGate".into()
|
||||
}
|
||||
|
||||
fn serialize(&self, _dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(_src: &mut Buffer) -> IoResult<Self> {
|
||||
Ok(Self)
|
||||
}
|
||||
|
||||
fn eval_unfiltered(&self, _vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
|
||||
Vec::new()
|
||||
}
|
||||
@ -34,7 +42,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for NoopGate {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn generators(&self, _row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
|
||||
fn generators(&self, _row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::{format, vec};
|
||||
@ -13,12 +12,13 @@ use crate::hash::hash_types::RichField;
|
||||
use crate::hash::poseidon;
|
||||
use crate::hash::poseidon::{Poseidon, SPONGE_WIDTH};
|
||||
use crate::iop::ext_target::ExtensionTarget;
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef};
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::wire::Wire;
|
||||
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
/// Evaluates a full Poseidon permutation with 12 state elements.
|
||||
///
|
||||
@ -99,6 +99,14 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for PoseidonGate<F
|
||||
format!("{self:?}<WIDTH={SPONGE_WIDTH}>")
|
||||
}
|
||||
|
||||
fn serialize(&self, _dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(_src: &mut Buffer) -> IoResult<Self> {
|
||||
Ok(PoseidonGate::new())
|
||||
}
|
||||
|
||||
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
|
||||
let mut constraints = Vec::with_capacity(self.num_constraints());
|
||||
|
||||
@ -372,12 +380,12 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for PoseidonGate<F
|
||||
constraints
|
||||
}
|
||||
|
||||
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
|
||||
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
|
||||
let gen = PoseidonGenerator::<F, D> {
|
||||
row,
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
vec![Box::new(gen.adapter())]
|
||||
vec![WitnessGeneratorRef::new(gen.adapter())]
|
||||
}
|
||||
|
||||
fn num_wires(&self) -> usize {
|
||||
@ -401,8 +409,8 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for PoseidonGate<F
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PoseidonGenerator<F: RichField + Extendable<D> + Poseidon, const D: usize> {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct PoseidonGenerator<F: RichField + Extendable<D> + Poseidon, const D: usize> {
|
||||
row: usize,
|
||||
_phantom: PhantomData<F>,
|
||||
}
|
||||
@ -410,6 +418,10 @@ struct PoseidonGenerator<F: RichField + Extendable<D> + Poseidon, const D: usize
|
||||
impl<F: RichField + Extendable<D> + Poseidon, const D: usize> SimpleGenerator<F>
|
||||
for PoseidonGenerator<F, D>
|
||||
{
|
||||
fn id(&self) -> String {
|
||||
"PoseidonGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
(0..SPONGE_WIDTH)
|
||||
.map(|i| PoseidonGate::<F, D>::wire_input(i))
|
||||
@ -499,6 +511,18 @@ impl<F: RichField + Extendable<D> + Poseidon, const D: usize> SimpleGenerator<F>
|
||||
out_buffer.set_wire(local_wire(PoseidonGate::<F, D>::wire_output(i)), state[i]);
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.row)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let row = src.read_usize()?;
|
||||
Ok(Self {
|
||||
row,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::{format, vec};
|
||||
@ -13,11 +12,12 @@ use crate::gates::util::StridedConstraintConsumer;
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::hash::poseidon::{Poseidon, SPONGE_WIDTH};
|
||||
use crate::iop::ext_target::{ExtensionAlgebraTarget, ExtensionTarget};
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef};
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
/// Poseidon MDS Gate
|
||||
#[derive(Debug, Default)]
|
||||
@ -118,6 +118,14 @@ impl<F: RichField + Extendable<D> + Poseidon, const D: usize> Gate<F, D> for Pos
|
||||
format!("{self:?}<WIDTH={SPONGE_WIDTH}>")
|
||||
}
|
||||
|
||||
fn serialize(&self, _dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(_src: &mut Buffer) -> IoResult<Self> {
|
||||
Ok(PoseidonMdsGate::new())
|
||||
}
|
||||
|
||||
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
|
||||
let inputs: [_; SPONGE_WIDTH] = (0..SPONGE_WIDTH)
|
||||
.map(|i| vars.get_local_ext_algebra(Self::wires_input(i)))
|
||||
@ -179,9 +187,9 @@ impl<F: RichField + Extendable<D> + Poseidon, const D: usize> Gate<F, D> for Pos
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
|
||||
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
|
||||
let gen = PoseidonMdsGenerator::<D> { row };
|
||||
vec![Box::new(gen.adapter())]
|
||||
vec![WitnessGeneratorRef::new(gen.adapter())]
|
||||
}
|
||||
|
||||
fn num_wires(&self) -> usize {
|
||||
@ -201,14 +209,18 @@ impl<F: RichField + Extendable<D> + Poseidon, const D: usize> Gate<F, D> for Pos
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct PoseidonMdsGenerator<const D: usize> {
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct PoseidonMdsGenerator<const D: usize> {
|
||||
row: usize,
|
||||
}
|
||||
|
||||
impl<F: RichField + Extendable<D> + Poseidon, const D: usize> SimpleGenerator<F>
|
||||
for PoseidonMdsGenerator<D>
|
||||
{
|
||||
fn id(&self) -> String {
|
||||
"PoseidonMdsGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
(0..SPONGE_WIDTH)
|
||||
.flat_map(|i| {
|
||||
@ -237,6 +249,15 @@ impl<F: RichField + Extendable<D> + Poseidon, const D: usize> SimpleGenerator<F>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.row)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let row = src.read_usize()?;
|
||||
Ok(Self { row })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use core::ops::Range;
|
||||
@ -10,12 +9,13 @@ use crate::gates::packed_util::PackedEvaluableBase;
|
||||
use crate::gates::util::StridedConstraintConsumer;
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::iop::ext_target::ExtensionTarget;
|
||||
use crate::iop::generator::WitnessGenerator;
|
||||
use crate::iop::generator::WitnessGeneratorRef;
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::vars::{
|
||||
EvaluationTargets, EvaluationVars, EvaluationVarsBase, EvaluationVarsBaseBatch,
|
||||
EvaluationVarsBasePacked,
|
||||
};
|
||||
use crate::util::serialization::{Buffer, IoResult};
|
||||
|
||||
/// A gate whose first four wires will be equal to a hash of public inputs.
|
||||
pub struct PublicInputGate;
|
||||
@ -31,6 +31,14 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for PublicInputGat
|
||||
"PublicInputGate".into()
|
||||
}
|
||||
|
||||
fn serialize(&self, _dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(_src: &mut Buffer) -> IoResult<Self> {
|
||||
Ok(Self)
|
||||
}
|
||||
|
||||
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
|
||||
Self::wires_public_inputs_hash()
|
||||
.zip(vars.public_inputs_hash.elements)
|
||||
@ -64,7 +72,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for PublicInputGat
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn generators(&self, _row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
|
||||
fn generators(&self, _row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::{format, vec};
|
||||
@ -14,7 +13,7 @@ use crate::gates::packed_util::PackedEvaluableBase;
|
||||
use crate::gates::util::StridedConstraintConsumer;
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::iop::ext_target::ExtensionTarget;
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef};
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::wire::Wire;
|
||||
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||
@ -24,9 +23,10 @@ use crate::plonk::vars::{
|
||||
EvaluationTargets, EvaluationVars, EvaluationVarsBase, EvaluationVarsBaseBatch,
|
||||
EvaluationVarsBasePacked,
|
||||
};
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
/// A gate for checking that a particular element of a list matches a given value.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct RandomAccessGate<F: RichField + Extendable<D>, const D: usize> {
|
||||
/// Number of bits in the index (log2 of the list size).
|
||||
pub bits: usize,
|
||||
@ -122,6 +122,20 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for RandomAccessGa
|
||||
format!("{self:?}<D={D}>")
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.bits)?;
|
||||
dst.write_usize(self.num_copies)?;
|
||||
dst.write_usize(self.num_extra_constants)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let bits = src.read_usize()?;
|
||||
let num_copies = src.read_usize()?;
|
||||
let num_extra_constants = src.read_usize()?;
|
||||
Ok(Self::new(num_copies, bits, num_extra_constants))
|
||||
}
|
||||
|
||||
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
|
||||
let mut constraints = Vec::with_capacity(self.num_constraints());
|
||||
|
||||
@ -238,18 +252,17 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for RandomAccessGa
|
||||
constraints
|
||||
}
|
||||
|
||||
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
|
||||
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
|
||||
(0..self.num_copies)
|
||||
.map(|copy| {
|
||||
let g: Box<dyn WitnessGenerator<F>> = Box::new(
|
||||
WitnessGeneratorRef::new(
|
||||
RandomAccessGenerator {
|
||||
row,
|
||||
gate: *self,
|
||||
copy,
|
||||
}
|
||||
.adapter(),
|
||||
);
|
||||
g
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@ -325,8 +338,8 @@ impl<F: RichField + Extendable<D>, const D: usize> PackedEvaluableBase<F, D>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RandomAccessGenerator<F: RichField + Extendable<D>, const D: usize> {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct RandomAccessGenerator<F: RichField + Extendable<D>, const D: usize> {
|
||||
row: usize,
|
||||
gate: RandomAccessGate<F, D>,
|
||||
copy: usize,
|
||||
@ -335,6 +348,10 @@ struct RandomAccessGenerator<F: RichField + Extendable<D>, const D: usize> {
|
||||
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||
for RandomAccessGenerator<F, D>
|
||||
{
|
||||
fn id(&self) -> String {
|
||||
"RandomAccessGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
let local_target = |column| Target::wire(self.row, column);
|
||||
|
||||
@ -376,6 +393,19 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||
set_local_wire(self.gate.wire_bit(i, copy), bit);
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.row)?;
|
||||
dst.write_usize(self.copy)?;
|
||||
self.gate.serialize(dst)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let row = src.read_usize()?;
|
||||
let copy = src.read_usize()?;
|
||||
let gate = RandomAccessGate::<F, D>::deserialize(src)?;
|
||||
Ok(Self { row, gate, copy })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::{format, vec};
|
||||
@ -9,14 +8,15 @@ use crate::gates::gate::Gate;
|
||||
use crate::gates::util::StridedConstraintConsumer;
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::iop::ext_target::ExtensionTarget;
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef};
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
/// Computes `sum alpha^i c_i` for a vector `c_i` of `num_coeffs` elements of the base field.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct ReducingGate<const D: usize> {
|
||||
pub num_coeffs: usize,
|
||||
}
|
||||
@ -60,6 +60,19 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ReducingGate<D
|
||||
format!("{self:?}")
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.num_coeffs)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let num_coeffs = src.read_usize()?;
|
||||
Ok(Self::new(num_coeffs))
|
||||
}
|
||||
|
||||
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
|
||||
let alpha = vars.get_local_ext_algebra(Self::wires_alpha());
|
||||
let old_acc = vars.get_local_ext_algebra(Self::wires_old_acc());
|
||||
@ -137,8 +150,8 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ReducingGate<D
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
|
||||
vec![Box::new(
|
||||
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
|
||||
vec![WitnessGeneratorRef::new(
|
||||
ReducingGenerator {
|
||||
row,
|
||||
gate: self.clone(),
|
||||
@ -164,13 +177,17 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ReducingGate<D
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ReducingGenerator<const D: usize> {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ReducingGenerator<const D: usize> {
|
||||
row: usize,
|
||||
gate: ReducingGate<D>,
|
||||
}
|
||||
|
||||
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F> for ReducingGenerator<D> {
|
||||
fn id(&self) -> String {
|
||||
"ReducingGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
ReducingGate::<D>::wires_alpha()
|
||||
.chain(ReducingGate::<D>::wires_old_acc())
|
||||
@ -207,6 +224,17 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F> for Reduci
|
||||
}
|
||||
out_buffer.set_extension_target(output, acc);
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.row)?;
|
||||
<ReducingGate<D> as Gate<F, D>>::serialize(&self.gate, dst)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let row = src.read_usize()?;
|
||||
let gate = <ReducingGate<D> as Gate<F, D>>::deserialize(src)?;
|
||||
Ok(Self { row, gate })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::{format, vec};
|
||||
@ -9,14 +8,15 @@ use crate::gates::gate::Gate;
|
||||
use crate::gates::util::StridedConstraintConsumer;
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::iop::ext_target::ExtensionTarget;
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef};
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
/// Computes `sum alpha^i c_i` for a vector `c_i` of `num_coeffs` elements of the extension field.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ReducingExtensionGate<const D: usize> {
|
||||
pub num_coeffs: usize,
|
||||
}
|
||||
@ -63,6 +63,19 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ReducingExtens
|
||||
format!("{self:?}")
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.num_coeffs)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let num_coeffs = src.read_usize()?;
|
||||
Ok(Self::new(num_coeffs))
|
||||
}
|
||||
|
||||
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
|
||||
let alpha = vars.get_local_ext_algebra(Self::wires_alpha());
|
||||
let old_acc = vars.get_local_ext_algebra(Self::wires_old_acc());
|
||||
@ -137,8 +150,8 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ReducingExtens
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
|
||||
vec![Box::new(
|
||||
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F>> {
|
||||
vec![WitnessGeneratorRef::new(
|
||||
ReducingGenerator {
|
||||
row,
|
||||
gate: self.clone(),
|
||||
@ -164,13 +177,17 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ReducingExtens
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ReducingGenerator<const D: usize> {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ReducingGenerator<const D: usize> {
|
||||
row: usize,
|
||||
gate: ReducingExtensionGate<D>,
|
||||
}
|
||||
|
||||
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F> for ReducingGenerator<D> {
|
||||
fn id(&self) -> String {
|
||||
"ReducingExtensionGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
ReducingExtensionGate::<D>::wires_alpha()
|
||||
.chain(ReducingExtensionGate::<D>::wires_old_acc())
|
||||
@ -201,6 +218,17 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F> for Reduci
|
||||
acc = computed_acc;
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.row)?;
|
||||
<ReducingExtensionGate<D> as Gate<F, D>>::serialize(&self.gate, dst)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let row = src.read_usize()?;
|
||||
let gate = <ReducingExtensionGate<D> as Gate<F, D>>::deserialize(src)?;
|
||||
Ok(Self { row, gate })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -110,7 +110,7 @@ impl<F: Field> Default for HashOut<F> {
|
||||
}
|
||||
|
||||
/// Represents a ~256 bit hash output.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct HashOutTarget {
|
||||
pub elements: [Target; 4],
|
||||
}
|
||||
@ -148,7 +148,7 @@ impl TryFrom<&[Target]> for HashOutTarget {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct MerkleCapTarget(pub Vec<HashOutTarget>);
|
||||
|
||||
/// Hash consisting of a byte array.
|
||||
|
||||
@ -31,7 +31,7 @@ impl<F: RichField, HC: HashConfig, H: Hasher<F, HC>> MerkleProof<F, HC, H> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct MerkleProofTarget {
|
||||
/// The Merkle digest of each sibling subtree, staying from the bottommost layer.
|
||||
pub siblings: Vec<HashOutTarget>,
|
||||
|
||||
@ -36,7 +36,7 @@ impl<F: RichField, HC: HashConfig, H: Hasher<F, HC>> MerkleCap<F, HC, H> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct MerkleTree<F: RichField, HC: HashConfig, H: Hasher<F, HC>> {
|
||||
/// The data in the leaves of the Merkle tree.
|
||||
pub leaves: Vec<Vec<F>>,
|
||||
|
||||
@ -12,6 +12,12 @@ use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct ExtensionTarget<const D: usize>(pub [Target; D]);
|
||||
|
||||
impl<const D: usize> Default for ExtensionTarget<D> {
|
||||
fn default() -> Self {
|
||||
Self([Target::default(); D])
|
||||
}
|
||||
}
|
||||
|
||||
impl<const D: usize> ExtensionTarget<D> {
|
||||
pub fn to_target_array(&self) -> [Target; D] {
|
||||
self.0
|
||||
|
||||
@ -12,6 +12,7 @@ use crate::iop::wire::Wire;
|
||||
use crate::iop::witness::{PartialWitness, PartitionWitness, Witness, WitnessWrite};
|
||||
use crate::plonk::circuit_data::{CommonCircuitData, ProverOnlyCircuitData};
|
||||
use crate::plonk::config::GenericConfig;
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
/// Given a `PartitionWitness` that has only inputs set, populates the rest of the witness using the
|
||||
/// given set of generators.
|
||||
@ -58,7 +59,7 @@ pub(crate) fn generate_partial_witness<
|
||||
continue;
|
||||
}
|
||||
|
||||
let finished = generators[generator_idx].run(&witness, &mut buffer);
|
||||
let finished = generators[generator_idx].0.run(&witness, &mut buffer);
|
||||
if finished {
|
||||
generator_is_expired[generator_idx] = true;
|
||||
remaining_generators -= 1;
|
||||
@ -98,6 +99,8 @@ pub(crate) fn generate_partial_witness<
|
||||
|
||||
/// A generator participates in the generation of the witness.
|
||||
pub trait WitnessGenerator<F: Field>: 'static + Send + Sync + Debug {
|
||||
fn id(&self) -> String;
|
||||
|
||||
/// Targets to be "watched" by this generator. Whenever a target in the watch list is populated,
|
||||
/// the generator will be queued to run.
|
||||
fn watch_list(&self) -> Vec<Target>;
|
||||
@ -106,6 +109,36 @@ pub trait WitnessGenerator<F: Field>: 'static + Send + Sync + Debug {
|
||||
/// flag is true, the generator will never be run again, otherwise it will be queued for another
|
||||
/// run next time a target in its watch list is populated.
|
||||
fn run(&self, witness: &PartitionWitness<F>, out_buffer: &mut GeneratedValues<F>) -> bool;
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()>;
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
/// A wrapper around an `Box<WitnessGenerator>` which implements `PartialEq`
|
||||
/// and `Eq` based on generator IDs.
|
||||
pub struct WitnessGeneratorRef<F: Field>(pub Box<dyn WitnessGenerator<F>>);
|
||||
|
||||
impl<F: Field> WitnessGeneratorRef<F> {
|
||||
pub fn new<G: WitnessGenerator<F>>(generator: G) -> WitnessGeneratorRef<F> {
|
||||
WitnessGeneratorRef(Box::new(generator))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> PartialEq for WitnessGeneratorRef<F> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0.id() == other.0.id()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Eq for WitnessGeneratorRef<F> {}
|
||||
|
||||
impl<F: Field> Debug for WitnessGeneratorRef<F> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{}", self.0.id())
|
||||
}
|
||||
}
|
||||
|
||||
/// Values generated by a generator invocation.
|
||||
@ -158,6 +191,8 @@ impl<F: Field> GeneratedValues<F> {
|
||||
|
||||
/// A generator which runs once after a list of dependencies is present in the witness.
|
||||
pub trait SimpleGenerator<F: Field>: 'static + Send + Sync + Debug {
|
||||
fn id(&self) -> String;
|
||||
|
||||
fn dependencies(&self) -> Vec<Target>;
|
||||
|
||||
fn run_once(&self, witness: &PartitionWitness<F>, out_buffer: &mut GeneratedValues<F>);
|
||||
@ -171,6 +206,12 @@ pub trait SimpleGenerator<F: Field>: 'static + Send + Sync + Debug {
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()>;
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -180,6 +221,10 @@ pub struct SimpleGeneratorAdapter<F: Field, SG: SimpleGenerator<F> + ?Sized> {
|
||||
}
|
||||
|
||||
impl<F: Field, SG: SimpleGenerator<F>> WitnessGenerator<F> for SimpleGeneratorAdapter<F, SG> {
|
||||
fn id(&self) -> String {
|
||||
self.inner.id()
|
||||
}
|
||||
|
||||
fn watch_list(&self) -> Vec<Target> {
|
||||
self.inner.dependencies()
|
||||
}
|
||||
@ -192,16 +237,31 @@ impl<F: Field, SG: SimpleGenerator<F>> WitnessGenerator<F> for SimpleGeneratorAd
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
self.inner.serialize(dst)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
Ok(Self {
|
||||
inner: SG::deserialize(src)?,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A generator which copies one wire to another.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CopyGenerator {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct CopyGenerator {
|
||||
pub(crate) src: Target,
|
||||
pub(crate) dst: Target,
|
||||
}
|
||||
|
||||
impl<F: Field> SimpleGenerator<F> for CopyGenerator {
|
||||
fn id(&self) -> String {
|
||||
"CopyGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
vec![self.src]
|
||||
}
|
||||
@ -210,15 +270,30 @@ impl<F: Field> SimpleGenerator<F> for CopyGenerator {
|
||||
let value = witness.get_target(self.src);
|
||||
out_buffer.set_target(self.dst, value);
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_target(self.src)?;
|
||||
dst.write_target(self.dst)
|
||||
}
|
||||
|
||||
fn deserialize(source: &mut Buffer) -> IoResult<Self> {
|
||||
let src = source.read_target()?;
|
||||
let dst = source.read_target()?;
|
||||
Ok(Self { src, dst })
|
||||
}
|
||||
}
|
||||
|
||||
/// A generator for including a random value
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct RandomValueGenerator {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct RandomValueGenerator {
|
||||
pub(crate) target: Target,
|
||||
}
|
||||
|
||||
impl<F: Field> SimpleGenerator<F> for RandomValueGenerator {
|
||||
fn id(&self) -> String {
|
||||
"RandomValueGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
Vec::new()
|
||||
}
|
||||
@ -227,16 +302,29 @@ impl<F: Field> SimpleGenerator<F> for RandomValueGenerator {
|
||||
let random_value = F::rand();
|
||||
out_buffer.set_target(self.target, random_value);
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_target(self.target)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let target = src.read_target()?;
|
||||
Ok(Self { target })
|
||||
}
|
||||
}
|
||||
|
||||
/// A generator for testing if a value equals zero
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct NonzeroTestGenerator {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct NonzeroTestGenerator {
|
||||
pub(crate) to_test: Target,
|
||||
pub(crate) dummy: Target,
|
||||
}
|
||||
|
||||
impl<F: Field> SimpleGenerator<F> for NonzeroTestGenerator {
|
||||
fn id(&self) -> String {
|
||||
"NonzeroTestGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
vec![self.to_test]
|
||||
}
|
||||
@ -252,11 +340,22 @@ impl<F: Field> SimpleGenerator<F> for NonzeroTestGenerator {
|
||||
|
||||
out_buffer.set_target(self.dummy, dummy_value);
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_target(self.to_test)?;
|
||||
dst.write_target(self.dummy)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let to_test = src.read_target()?;
|
||||
let dummy = src.read_target()?;
|
||||
Ok(Self { to_test, dummy })
|
||||
}
|
||||
}
|
||||
|
||||
/// Generator used to fill an extra constant.
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ConstantGenerator<F: Field> {
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ConstantGenerator<F: Field> {
|
||||
pub row: usize,
|
||||
pub constant_index: usize,
|
||||
pub wire_index: usize,
|
||||
@ -269,7 +368,11 @@ impl<F: Field> ConstantGenerator<F> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> SimpleGenerator<F> for ConstantGenerator<F> {
|
||||
impl<F: RichField> SimpleGenerator<F> for ConstantGenerator<F> {
|
||||
fn id(&self) -> String {
|
||||
"ConstantGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
vec![]
|
||||
}
|
||||
@ -277,4 +380,24 @@ impl<F: Field> SimpleGenerator<F> for ConstantGenerator<F> {
|
||||
fn run_once(&self, _witness: &PartitionWitness<F>, out_buffer: &mut GeneratedValues<F>) {
|
||||
out_buffer.set_target(Target::wire(self.row, self.wire_index), self.constant);
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_usize(self.row)?;
|
||||
dst.write_usize(self.constant_index)?;
|
||||
dst.write_usize(self.wire_index)?;
|
||||
dst.write_field(self.constant)
|
||||
}
|
||||
|
||||
fn deserialize(src: &mut Buffer) -> IoResult<Self> {
|
||||
let row = src.read_usize()?;
|
||||
let constant_index = src.read_usize()?;
|
||||
let wire_index = src.read_usize()?;
|
||||
let constant = src.read_field()?;
|
||||
Ok(Self {
|
||||
row,
|
||||
constant_index,
|
||||
wire_index,
|
||||
constant,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,12 @@ pub enum Target {
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for Target {
|
||||
fn default() -> Self {
|
||||
Self::VirtualTarget { index: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Target {
|
||||
pub fn wire(row: usize, column: usize) -> Self {
|
||||
Self::Wire(Wire { row, column })
|
||||
@ -49,7 +55,7 @@ impl Target {
|
||||
}
|
||||
|
||||
/// A `Target` which has already been constrained such that it can only be 0 or 1.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||
#[allow(clippy::manual_non_exhaustive)]
|
||||
pub struct BoolTarget {
|
||||
pub target: Target,
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
@ -33,7 +32,7 @@ use crate::hash::merkle_proofs::MerkleProofTarget;
|
||||
use crate::hash::merkle_tree::MerkleCap;
|
||||
use crate::iop::ext_target::ExtensionTarget;
|
||||
use crate::iop::generator::{
|
||||
ConstantGenerator, CopyGenerator, RandomValueGenerator, SimpleGenerator, WitnessGenerator,
|
||||
ConstantGenerator, CopyGenerator, RandomValueGenerator, SimpleGenerator, WitnessGeneratorRef,
|
||||
};
|
||||
use crate::iop::target::{BoolTarget, Target};
|
||||
use crate::iop::wire::Wire;
|
||||
@ -77,7 +76,7 @@ pub struct CircuitBuilder<F: RichField + Extendable<D>, const D: usize> {
|
||||
context_log: ContextTree,
|
||||
|
||||
/// Generators used to generate the witness.
|
||||
generators: Vec<Box<dyn WitnessGenerator<F>>>,
|
||||
generators: Vec<WitnessGeneratorRef<F>>,
|
||||
|
||||
constants_to_targets: HashMap<F, Target>,
|
||||
targets_to_constants: HashMap<Target, F>,
|
||||
@ -367,12 +366,13 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
self.connect(x, one);
|
||||
}
|
||||
|
||||
pub fn add_generators(&mut self, generators: Vec<Box<dyn WitnessGenerator<F>>>) {
|
||||
pub fn add_generators(&mut self, generators: Vec<WitnessGeneratorRef<F>>) {
|
||||
self.generators.extend(generators);
|
||||
}
|
||||
|
||||
pub fn add_simple_generator<G: SimpleGenerator<F>>(&mut self, generator: G) {
|
||||
self.generators.push(Box::new(generator.adapter()));
|
||||
self.generators
|
||||
.push(WitnessGeneratorRef::new(generator.adapter()));
|
||||
}
|
||||
|
||||
/// Returns a routable target with a value of 0.
|
||||
@ -865,7 +865,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
// Index generator indices by their watched targets.
|
||||
let mut generator_indices_by_watches = BTreeMap::new();
|
||||
for (i, generator) in self.generators.iter().enumerate() {
|
||||
for watch in generator.watch_list() {
|
||||
for watch in generator.0.watch_list() {
|
||||
let watch_index = forest.target_index(watch);
|
||||
let watch_rep_index = forest.parents[watch_index];
|
||||
generator_indices_by_watches
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
@ -22,7 +21,7 @@ use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget, RichField};
|
||||
use crate::hash::hashing::HashConfig;
|
||||
use crate::hash::merkle_tree::MerkleCap;
|
||||
use crate::iop::ext_target::ExtensionTarget;
|
||||
use crate::iop::generator::WitnessGenerator;
|
||||
use crate::iop::generator::WitnessGeneratorRef;
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::witness::PartialWitness;
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
@ -31,6 +30,9 @@ use crate::plonk::plonk_common::PlonkOracle;
|
||||
use crate::plonk::proof::{CompressedProofWithPublicInputs, ProofWithPublicInputs};
|
||||
use crate::plonk::prover::prove;
|
||||
use crate::plonk::verifier::verify;
|
||||
use crate::util::serialization::{
|
||||
Buffer, GateSerializer, IoResult, Read, WitnessGeneratorSerializer, Write,
|
||||
};
|
||||
use crate::util::timing::TimingTree;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
@ -107,6 +109,7 @@ impl CircuitConfig {
|
||||
}
|
||||
|
||||
/// Circuit data required by the prover or the verifier.
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub struct CircuitData<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> {
|
||||
pub prover_only: ProverOnlyCircuitData<F, C, D>,
|
||||
pub verifier_only: VerifierOnlyCircuitData<C, D>,
|
||||
@ -116,6 +119,25 @@ pub struct CircuitData<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>,
|
||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
||||
CircuitData<F, C, D>
|
||||
{
|
||||
pub fn to_bytes(
|
||||
&self,
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
|
||||
) -> IoResult<Vec<u8>> {
|
||||
let mut buffer = Vec::new();
|
||||
buffer.write_circuit_data(self, gate_serializer, generator_serializer)?;
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
pub fn from_bytes(
|
||||
bytes: &[u8],
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
|
||||
) -> IoResult<Self> {
|
||||
let mut buffer = Buffer::new(bytes.to_vec());
|
||||
buffer.read_circuit_data(gate_serializer, generator_serializer)
|
||||
}
|
||||
|
||||
pub fn prove(&self, inputs: PartialWitness<F>) -> Result<ProofWithPublicInputs<F, C, D>>
|
||||
where
|
||||
[(); C::HCO::WIDTH]:,
|
||||
@ -214,6 +236,25 @@ pub struct ProverCircuitData<
|
||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
||||
ProverCircuitData<F, C, D>
|
||||
{
|
||||
pub fn to_bytes(
|
||||
&self,
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
|
||||
) -> IoResult<Vec<u8>> {
|
||||
let mut buffer = Vec::new();
|
||||
buffer.write_prover_circuit_data(self, gate_serializer, generator_serializer)?;
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
pub fn from_bytes(
|
||||
bytes: &[u8],
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
generator_serializer: &dyn WitnessGeneratorSerializer<F, D>,
|
||||
) -> IoResult<Self> {
|
||||
let mut buffer = Buffer::new(bytes.to_vec());
|
||||
buffer.read_prover_circuit_data(gate_serializer, generator_serializer)
|
||||
}
|
||||
|
||||
pub fn prove(&self, inputs: PartialWitness<F>) -> Result<ProofWithPublicInputs<F, C, D>>
|
||||
where
|
||||
[(); C::HCO::WIDTH]:,
|
||||
@ -242,6 +283,20 @@ pub struct VerifierCircuitData<
|
||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
||||
VerifierCircuitData<F, C, D>
|
||||
{
|
||||
pub fn to_bytes(&self, gate_serializer: &dyn GateSerializer<F, D>) -> IoResult<Vec<u8>> {
|
||||
let mut buffer = Vec::new();
|
||||
buffer.write_verifier_circuit_data(self, gate_serializer)?;
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
pub fn from_bytes(
|
||||
bytes: Vec<u8>,
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
) -> IoResult<Self> {
|
||||
let mut buffer = Buffer::new(bytes);
|
||||
buffer.read_verifier_circuit_data(gate_serializer)
|
||||
}
|
||||
|
||||
pub fn verify(&self, proof_with_pis: ProofWithPublicInputs<F, C, D>) -> Result<()>
|
||||
where
|
||||
[(); C::HCO::WIDTH]:,
|
||||
@ -263,12 +318,13 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
||||
}
|
||||
|
||||
/// Circuit data required by the prover, but not the verifier.
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub struct ProverOnlyCircuitData<
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
const D: usize,
|
||||
> {
|
||||
pub generators: Vec<Box<dyn WitnessGenerator<F>>>,
|
||||
pub generators: Vec<WitnessGeneratorRef<F>>,
|
||||
/// Generator indices (within the `Vec` above), indexed by the representative of each target
|
||||
/// they watch.
|
||||
pub generator_indices_by_watches: BTreeMap<usize, Vec<usize>>,
|
||||
@ -300,6 +356,19 @@ pub struct VerifierOnlyCircuitData<C: GenericConfig<D>, const D: usize> {
|
||||
pub circuit_digest: <<C as GenericConfig<D>>::Hasher as Hasher<C::F, C::HCO>>::Hash,
|
||||
}
|
||||
|
||||
impl<C: GenericConfig<D>, const D: usize> VerifierOnlyCircuitData<C, D> {
|
||||
pub fn to_bytes(&self) -> IoResult<Vec<u8>> {
|
||||
let mut buffer = Vec::new();
|
||||
buffer.write_verifier_only_circuit_data(self)?;
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: Vec<u8>) -> IoResult<Self> {
|
||||
let mut buffer = Buffer::new(bytes);
|
||||
buffer.read_verifier_only_circuit_data()
|
||||
}
|
||||
}
|
||||
|
||||
/// Circuit data required by both the prover and the verifier.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct CommonCircuitData<F: RichField + Extendable<D>, const D: usize> {
|
||||
@ -332,6 +401,20 @@ pub struct CommonCircuitData<F: RichField + Extendable<D>, const D: usize> {
|
||||
}
|
||||
|
||||
impl<F: RichField + Extendable<D>, const D: usize> CommonCircuitData<F, D> {
|
||||
pub fn to_bytes(&self, gate_serializer: &dyn GateSerializer<F, D>) -> IoResult<Vec<u8>> {
|
||||
let mut buffer = Vec::new();
|
||||
buffer.write_common_circuit_data(self, gate_serializer)?;
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
pub fn from_bytes(
|
||||
bytes: Vec<u8>,
|
||||
gate_serializer: &dyn GateSerializer<F, D>,
|
||||
) -> IoResult<Self> {
|
||||
let mut buffer = Buffer::new(bytes);
|
||||
buffer.read_common_circuit_data(gate_serializer)
|
||||
}
|
||||
|
||||
pub const fn degree_bits(&self) -> usize {
|
||||
self.fri_params.degree_bits
|
||||
}
|
||||
@ -503,7 +586,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CommonCircuitData<F, D> {
|
||||
/// is intentionally missing certain fields, such as `CircuitConfig`, because we support only a
|
||||
/// limited form of dynamic inner circuits. We can't practically make things like the wire count
|
||||
/// dynamic, at least not without setting a maximum wire count and paying for the worst case.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct VerifierCircuitTarget {
|
||||
/// A commitment to each constant polynomial and each permutation polynomial.
|
||||
pub constants_sigmas_cap: MerkleCapTarget,
|
||||
|
||||
@ -41,7 +41,7 @@ pub struct Proof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const
|
||||
pub opening_proof: FriProof<F, C::HCO, C::Hasher, D>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ProofTarget<const D: usize> {
|
||||
pub wires_cap: MerkleCapTarget,
|
||||
pub plonk_zs_partial_products_cap: MerkleCapTarget,
|
||||
@ -305,13 +305,13 @@ pub(crate) struct FriInferredElements<F: RichField + Extendable<D>, const D: usi
|
||||
pub Vec<F::Extension>,
|
||||
);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ProofWithPublicInputsTarget<const D: usize> {
|
||||
pub proof: ProofTarget<D>,
|
||||
pub public_inputs: Vec<Target>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)]
|
||||
/// The purported values of each polynomial at a single point.
|
||||
pub struct OpeningSet<F: RichField + Extendable<D>, const D: usize> {
|
||||
pub constants: Vec<F::Extension>,
|
||||
@ -377,7 +377,7 @@ impl<F: RichField + Extendable<D>, const D: usize> OpeningSet<F, D> {
|
||||
}
|
||||
|
||||
/// The purported values of each polynomial at a single point.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct OpeningSetTarget<const D: usize> {
|
||||
pub constants: Vec<ExtensionTarget<D>>,
|
||||
pub plonk_sigmas: Vec<ExtensionTarget<D>>,
|
||||
|
||||
@ -13,6 +13,7 @@ use crate::plonk::circuit_data::{
|
||||
};
|
||||
use crate::plonk::config::{AlgebraicHasher, GenericConfig};
|
||||
use crate::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
impl<C: GenericConfig<D>, const D: usize> VerifierOnlyCircuitData<C, D> {
|
||||
fn from_slice(slice: &[C::F], common_data: &CommonCircuitData<C::F, D>) -> Result<Self>
|
||||
@ -41,6 +42,23 @@ impl<C: GenericConfig<D>, const D: usize> VerifierOnlyCircuitData<C, D> {
|
||||
}
|
||||
|
||||
impl VerifierCircuitTarget {
|
||||
pub fn to_bytes(&self) -> IoResult<Vec<u8>> {
|
||||
let mut buffer = Vec::new();
|
||||
buffer.write_target_merkle_cap(&self.constants_sigmas_cap)?;
|
||||
buffer.write_target_hash(&self.circuit_digest)?;
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: Vec<u8>) -> IoResult<Self> {
|
||||
let mut buffer = Buffer::new(bytes);
|
||||
let constants_sigmas_cap = buffer.read_target_merkle_cap()?;
|
||||
let circuit_digest = buffer.read_target_hash()?;
|
||||
Ok(Self {
|
||||
constants_sigmas_cap,
|
||||
circuit_digest,
|
||||
})
|
||||
}
|
||||
|
||||
fn from_slice<F: RichField + Extendable<D>, const D: usize>(
|
||||
slice: &[Target],
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
|
||||
@ -3,11 +3,15 @@ use alloc::vec::Vec;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use plonky2_field::extension::Extendable;
|
||||
use plonky2_field::polynomial::PolynomialCoeffs;
|
||||
use plonky2_util::ceil_div_usize;
|
||||
|
||||
use crate::fri::proof::{FriProof, FriProofTarget};
|
||||
use crate::gadgets::polynomial::PolynomialCoeffsExtTarget;
|
||||
use crate::gates::noop::NoopGate;
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget, RichField};
|
||||
use crate::hash::hashing::HashConfig;
|
||||
use crate::hash::merkle_tree::MerkleCap;
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::witness::{PartialWitness, PartitionWitness, WitnessWrite};
|
||||
@ -15,8 +19,12 @@ use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::{
|
||||
CircuitData, CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData,
|
||||
};
|
||||
use crate::plonk::config::{AlgebraicHasher, GenericConfig};
|
||||
use crate::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
||||
use crate::plonk::config::{AlgebraicHasher, GenericConfig, GenericHashOut, Hasher};
|
||||
use crate::plonk::proof::{
|
||||
OpeningSet, OpeningSetTarget, Proof, ProofTarget, ProofWithPublicInputs,
|
||||
ProofWithPublicInputsTarget,
|
||||
};
|
||||
use crate::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
|
||||
/// Creates a dummy proof which is suitable for use as a base proof in a cyclic recursion tree.
|
||||
/// Such a base proof will not actually be verified, so most of its data is arbitrary. However, its
|
||||
@ -146,7 +154,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct DummyProofGenerator<F, C, const D: usize>
|
||||
pub struct DummyProofGenerator<F, C, const D: usize>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
@ -157,12 +165,101 @@ where
|
||||
pub(crate) verifier_data: VerifierOnlyCircuitData<C, D>,
|
||||
}
|
||||
|
||||
impl<F, C, const D: usize> Default for DummyProofGenerator<F, C, D>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
fn default() -> Self {
|
||||
let proof_with_pis_target = ProofWithPublicInputsTarget {
|
||||
proof: ProofTarget {
|
||||
wires_cap: MerkleCapTarget(vec![]),
|
||||
plonk_zs_partial_products_cap: MerkleCapTarget(vec![]),
|
||||
quotient_polys_cap: MerkleCapTarget(vec![]),
|
||||
openings: OpeningSetTarget::default(),
|
||||
opening_proof: FriProofTarget {
|
||||
commit_phase_merkle_caps: vec![],
|
||||
query_round_proofs: vec![],
|
||||
final_poly: PolynomialCoeffsExtTarget(vec![]),
|
||||
pow_witness: Target::default(),
|
||||
},
|
||||
},
|
||||
public_inputs: vec![],
|
||||
};
|
||||
|
||||
let proof_with_pis = ProofWithPublicInputs {
|
||||
proof: Proof {
|
||||
wires_cap: MerkleCap(vec![]),
|
||||
plonk_zs_partial_products_cap: MerkleCap(vec![]),
|
||||
quotient_polys_cap: MerkleCap(vec![]),
|
||||
openings: OpeningSet::default(),
|
||||
opening_proof: FriProof {
|
||||
commit_phase_merkle_caps: vec![],
|
||||
query_round_proofs: vec![],
|
||||
final_poly: PolynomialCoeffs { coeffs: vec![] },
|
||||
pow_witness: F::ZERO,
|
||||
},
|
||||
},
|
||||
public_inputs: vec![],
|
||||
};
|
||||
|
||||
let verifier_data_target = VerifierCircuitTarget {
|
||||
constants_sigmas_cap: MerkleCapTarget(vec![]),
|
||||
circuit_digest: HashOutTarget {
|
||||
elements: [Target::default(); 4],
|
||||
},
|
||||
};
|
||||
|
||||
let verifier_data = VerifierOnlyCircuitData {
|
||||
constants_sigmas_cap: MerkleCap(vec![]),
|
||||
circuit_digest:
|
||||
<<C as GenericConfig<D>>::Hasher as Hasher<C::F, C::HCO>>::Hash::from_bytes(
|
||||
&vec![0; <<C as GenericConfig<D>>::Hasher as Hasher<C::F, C::HCO>>::HASH_SIZE],
|
||||
),
|
||||
};
|
||||
|
||||
Self {
|
||||
proof_with_pis_target,
|
||||
proof_with_pis,
|
||||
verifier_data_target,
|
||||
verifier_data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, C, const D: usize> DummyProofGenerator<F, C, D>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F> + 'static,
|
||||
C::Hasher: AlgebraicHasher<F, C::HCO>,
|
||||
{
|
||||
pub fn deserialize_with_circuit_data(
|
||||
src: &mut Buffer,
|
||||
cd: &CommonCircuitData<F, D>,
|
||||
) -> IoResult<Self> {
|
||||
let proof_with_pis_target = src.read_target_proof_with_public_inputs()?;
|
||||
let proof_with_pis = src.read_proof_with_public_inputs(cd)?;
|
||||
let verifier_data_target = src.read_target_verifier_circuit()?;
|
||||
let verifier_data = src.read_verifier_only_circuit_data()?;
|
||||
Ok(Self {
|
||||
proof_with_pis_target,
|
||||
proof_with_pis,
|
||||
verifier_data_target,
|
||||
verifier_data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, C, const D: usize> SimpleGenerator<F> for DummyProofGenerator<F, C, D>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F> + 'static,
|
||||
C::Hasher: AlgebraicHasher<F, C::HCO>,
|
||||
{
|
||||
fn id(&self) -> String {
|
||||
"DummyProofGenerator".to_string()
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
vec![]
|
||||
}
|
||||
@ -171,4 +268,15 @@ where
|
||||
out_buffer.set_proof_with_pis_target(&self.proof_with_pis_target, &self.proof_with_pis);
|
||||
out_buffer.set_verifier_data_target(&self.verifier_data_target, &self.verifier_data);
|
||||
}
|
||||
|
||||
fn serialize(&self, dst: &mut Vec<u8>) -> IoResult<()> {
|
||||
dst.write_target_proof_with_public_inputs(&self.proof_with_pis_target)?;
|
||||
dst.write_proof_with_public_inputs(&self.proof_with_pis)?;
|
||||
dst.write_target_verifier_circuit(&self.verifier_data_target)?;
|
||||
dst.write_verifier_only_circuit_data(&self.verifier_data)
|
||||
}
|
||||
|
||||
fn deserialize(_src: &mut Buffer) -> IoResult<Self> {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,6 @@ use crate::field::types::Field;
|
||||
|
||||
pub(crate) mod context_tree;
|
||||
pub(crate) mod partial_products;
|
||||
|
||||
pub mod reducing;
|
||||
pub mod serialization;
|
||||
pub mod strided_view;
|
||||
|
||||
@ -1,835 +0,0 @@
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::convert::Infallible;
|
||||
use core::fmt::{Debug, Display, Formatter};
|
||||
use core::mem::size_of;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
|
||||
use crate::field::extension::{Extendable, FieldExtension};
|
||||
use crate::field::polynomial::PolynomialCoeffs;
|
||||
use crate::field::types::{Field64, PrimeField64};
|
||||
use crate::fri::proof::{
|
||||
CompressedFriProof, CompressedFriQueryRounds, FriInitialTreeProof, FriProof, FriQueryRound,
|
||||
FriQueryStep,
|
||||
};
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::hash::hashing::HashConfig;
|
||||
use crate::hash::merkle_proofs::MerkleProof;
|
||||
use crate::hash::merkle_tree::MerkleCap;
|
||||
use crate::plonk::circuit_data::CommonCircuitData;
|
||||
use crate::plonk::config::{GenericConfig, GenericHashOut, Hasher};
|
||||
use crate::plonk::plonk_common::salt_size;
|
||||
use crate::plonk::proof::{
|
||||
CompressedProof, CompressedProofWithPublicInputs, OpeningSet, Proof, ProofWithPublicInputs,
|
||||
};
|
||||
|
||||
/// A no_std compatible variant of `std::io::Error`
|
||||
#[derive(Debug)]
|
||||
pub struct IoError;
|
||||
|
||||
impl Display for IoError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
Debug::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A no_std compatible variant of `std::io::Result`
|
||||
pub type IoResult<T> = Result<T, IoError>;
|
||||
|
||||
/// A `Read` which is able to report how many bytes are remaining.
|
||||
pub trait Remaining: Read {
|
||||
/// Returns the number of bytes remaining in the buffer.
|
||||
fn remaining(&self) -> usize;
|
||||
|
||||
/// Returns whether zero bytes are remaining.
|
||||
fn is_empty(&self) -> bool {
|
||||
self.remaining() == 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Similar to `std::io::Read`, but works with no_std.
|
||||
pub trait Read {
|
||||
/// Reads exactly the length of `bytes` from `self` and writes it to `bytes`.
|
||||
fn read_exact(&mut self, bytes: &mut [u8]) -> IoResult<()>;
|
||||
|
||||
/// Reads a `u8` value from `self`.
|
||||
#[inline]
|
||||
fn read_u8(&mut self) -> IoResult<u8> {
|
||||
let mut buf = [0; size_of::<u8>()];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(buf[0])
|
||||
}
|
||||
|
||||
/// Reads a `u32` value from `self`.
|
||||
#[inline]
|
||||
fn read_u32(&mut self) -> IoResult<u32> {
|
||||
let mut buf = [0; size_of::<u32>()];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(u32::from_le_bytes(buf))
|
||||
}
|
||||
|
||||
/// Reads a element from the field `F` with size less than `2^64` from `self.`
|
||||
#[inline]
|
||||
fn read_field<F>(&mut self) -> IoResult<F>
|
||||
where
|
||||
F: Field64,
|
||||
{
|
||||
let mut buf = [0; size_of::<u64>()];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(F::from_canonical_u64(u64::from_le_bytes(buf)))
|
||||
}
|
||||
|
||||
/// Reads a vector of elements from the field `F` from `self`.
|
||||
#[inline]
|
||||
fn read_field_vec<F>(&mut self, length: usize) -> IoResult<Vec<F>>
|
||||
where
|
||||
F: Field64,
|
||||
{
|
||||
(0..length)
|
||||
.map(|_| self.read_field())
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
}
|
||||
|
||||
/// Reads an element from the field extension of `F` from `self.`
|
||||
#[inline]
|
||||
fn read_field_ext<F, const D: usize>(&mut self) -> IoResult<F::Extension>
|
||||
where
|
||||
F: Field64 + Extendable<D>,
|
||||
{
|
||||
let mut arr = [F::ZERO; D];
|
||||
for a in arr.iter_mut() {
|
||||
*a = self.read_field()?;
|
||||
}
|
||||
Ok(<F::Extension as FieldExtension<D>>::from_basefield_array(
|
||||
arr,
|
||||
))
|
||||
}
|
||||
|
||||
/// Reads a vector of elements from the field extension of `F` from `self`.
|
||||
#[inline]
|
||||
fn read_field_ext_vec<F, const D: usize>(
|
||||
&mut self,
|
||||
length: usize,
|
||||
) -> IoResult<Vec<F::Extension>>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
{
|
||||
(0..length).map(|_| self.read_field_ext::<F, D>()).collect()
|
||||
}
|
||||
|
||||
/// Reads a hash value from `self`.
|
||||
#[inline]
|
||||
fn read_hash<F, HC, H>(&mut self) -> IoResult<H::Hash>
|
||||
where
|
||||
F: RichField,
|
||||
HC: HashConfig,
|
||||
H: Hasher<F, HC>,
|
||||
{
|
||||
let mut buf = vec![0; H::HASH_SIZE];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(H::Hash::from_bytes(&buf))
|
||||
}
|
||||
|
||||
/// Reads a value of type [`MerkleCap`] from `self` with the given `cap_height`.
|
||||
#[inline]
|
||||
fn read_merkle_cap<F, HC, H>(&mut self, cap_height: usize) -> IoResult<MerkleCap<F, HC, H>>
|
||||
where
|
||||
F: RichField,
|
||||
HC: HashConfig,
|
||||
H: Hasher<F, HC>,
|
||||
{
|
||||
let cap_length = 1 << cap_height;
|
||||
Ok(MerkleCap(
|
||||
(0..cap_length)
|
||||
.map(|_| self.read_hash::<F, HC, H>())
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
))
|
||||
}
|
||||
|
||||
/// Reads a value of type [`OpeningSet`] from `self` with the given `common_data`.
|
||||
#[inline]
|
||||
fn read_opening_set<F, C, const D: usize>(
|
||||
&mut self,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> IoResult<OpeningSet<F, D>>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
let config = &common_data.config;
|
||||
let constants = self.read_field_ext_vec::<F, D>(common_data.num_constants)?;
|
||||
let plonk_sigmas = self.read_field_ext_vec::<F, D>(config.num_routed_wires)?;
|
||||
let wires = self.read_field_ext_vec::<F, D>(config.num_wires)?;
|
||||
let plonk_zs = self.read_field_ext_vec::<F, D>(config.num_challenges)?;
|
||||
let plonk_zs_next = self.read_field_ext_vec::<F, D>(config.num_challenges)?;
|
||||
let partial_products = self
|
||||
.read_field_ext_vec::<F, D>(common_data.num_partial_products * config.num_challenges)?;
|
||||
let quotient_polys = self.read_field_ext_vec::<F, D>(
|
||||
common_data.quotient_degree_factor * config.num_challenges,
|
||||
)?;
|
||||
Ok(OpeningSet {
|
||||
constants,
|
||||
plonk_sigmas,
|
||||
wires,
|
||||
plonk_zs,
|
||||
plonk_zs_next,
|
||||
partial_products,
|
||||
quotient_polys,
|
||||
})
|
||||
}
|
||||
|
||||
/// Reads a value of type [`MerkleProof`] from `self`.
|
||||
#[inline]
|
||||
fn read_merkle_proof<F, HC, H>(&mut self) -> IoResult<MerkleProof<F, HC, H>>
|
||||
where
|
||||
F: RichField,
|
||||
HC: HashConfig,
|
||||
H: Hasher<F, HC>,
|
||||
{
|
||||
let length = self.read_u8()?;
|
||||
Ok(MerkleProof {
|
||||
siblings: (0..length)
|
||||
.map(|_| self.read_hash::<F, HC, H>())
|
||||
.collect::<Result<_, _>>()?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Reads a value of type [`FriInitialTreeProof`] from `self` with the given `common_data`.
|
||||
#[inline]
|
||||
fn read_fri_initial_proof<F, C, const D: usize>(
|
||||
&mut self,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> IoResult<FriInitialTreeProof<F, C::HCO, C::Hasher>>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
let config = &common_data.config;
|
||||
let salt = salt_size(common_data.fri_params.hiding);
|
||||
let mut evals_proofs = Vec::with_capacity(4);
|
||||
|
||||
let constants_sigmas_v =
|
||||
self.read_field_vec(common_data.num_constants + config.num_routed_wires)?;
|
||||
let constants_sigmas_p = self.read_merkle_proof()?;
|
||||
evals_proofs.push((constants_sigmas_v, constants_sigmas_p));
|
||||
|
||||
let wires_v = self.read_field_vec(config.num_wires + salt)?;
|
||||
let wires_p = self.read_merkle_proof()?;
|
||||
evals_proofs.push((wires_v, wires_p));
|
||||
|
||||
let zs_partial_v = self.read_field_vec(
|
||||
config.num_challenges * (1 + common_data.num_partial_products) + salt,
|
||||
)?;
|
||||
let zs_partial_p = self.read_merkle_proof()?;
|
||||
evals_proofs.push((zs_partial_v, zs_partial_p));
|
||||
|
||||
let quotient_v =
|
||||
self.read_field_vec(config.num_challenges * common_data.quotient_degree_factor + salt)?;
|
||||
let quotient_p = self.read_merkle_proof()?;
|
||||
evals_proofs.push((quotient_v, quotient_p));
|
||||
|
||||
Ok(FriInitialTreeProof { evals_proofs })
|
||||
}
|
||||
|
||||
/// Reads a value of type [`FriQueryStep`] from `self` with the given `arity` and `compressed`
|
||||
/// flag.
|
||||
#[inline]
|
||||
fn read_fri_query_step<F, C, const D: usize>(
|
||||
&mut self,
|
||||
arity: usize,
|
||||
compressed: bool,
|
||||
) -> IoResult<FriQueryStep<F, C::HCO, C::Hasher, D>>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
let evals = self.read_field_ext_vec::<F, D>(arity - usize::from(compressed))?;
|
||||
let merkle_proof = self.read_merkle_proof()?;
|
||||
Ok(FriQueryStep {
|
||||
evals,
|
||||
merkle_proof,
|
||||
})
|
||||
}
|
||||
|
||||
/// Reads a vector of [`FriQueryRound`]s from `self` with `common_data`.
|
||||
#[inline]
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn read_fri_query_rounds<F, C, const D: usize>(
|
||||
&mut self,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> IoResult<Vec<FriQueryRound<F, C::HCO, C::Hasher, D>>>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
let config = &common_data.config;
|
||||
let mut fqrs = Vec::with_capacity(config.fri_config.num_query_rounds);
|
||||
for _ in 0..config.fri_config.num_query_rounds {
|
||||
let initial_trees_proof = self.read_fri_initial_proof::<F, C, D>(common_data)?;
|
||||
let steps = common_data
|
||||
.fri_params
|
||||
.reduction_arity_bits
|
||||
.iter()
|
||||
.map(|&ar| self.read_fri_query_step::<F, C, D>(1 << ar, false))
|
||||
.collect::<Result<_, _>>()?;
|
||||
fqrs.push(FriQueryRound {
|
||||
initial_trees_proof,
|
||||
steps,
|
||||
})
|
||||
}
|
||||
Ok(fqrs)
|
||||
}
|
||||
|
||||
/// Reads a value of type [`FriProof`] from `self` with `common_data`.
|
||||
#[inline]
|
||||
fn read_fri_proof<F, C, const D: usize>(
|
||||
&mut self,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> IoResult<FriProof<F, C::HCO, C::Hasher, D>>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
let config = &common_data.config;
|
||||
let commit_phase_merkle_caps = (0..common_data.fri_params.reduction_arity_bits.len())
|
||||
.map(|_| self.read_merkle_cap(config.fri_config.cap_height))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let query_round_proofs = self.read_fri_query_rounds::<F, C, D>(common_data)?;
|
||||
let final_poly = PolynomialCoeffs::new(
|
||||
self.read_field_ext_vec::<F, D>(common_data.fri_params.final_poly_len())?,
|
||||
);
|
||||
let pow_witness = self.read_field()?;
|
||||
Ok(FriProof {
|
||||
commit_phase_merkle_caps,
|
||||
query_round_proofs,
|
||||
final_poly,
|
||||
pow_witness,
|
||||
})
|
||||
}
|
||||
|
||||
/// Reads a value of type [`Proof`] from `self` with `common_data`.
|
||||
#[inline]
|
||||
fn read_proof<F, C, const D: usize>(
|
||||
&mut self,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> IoResult<Proof<F, C, D>>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
let config = &common_data.config;
|
||||
let wires_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
|
||||
let plonk_zs_partial_products_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
|
||||
let quotient_polys_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
|
||||
let openings = self.read_opening_set::<F, C, D>(common_data)?;
|
||||
let opening_proof = self.read_fri_proof::<F, C, D>(common_data)?;
|
||||
Ok(Proof {
|
||||
wires_cap,
|
||||
plonk_zs_partial_products_cap,
|
||||
quotient_polys_cap,
|
||||
openings,
|
||||
opening_proof,
|
||||
})
|
||||
}
|
||||
|
||||
/// Reads a value of type [`ProofWithPublicInputs`] from `self` with `common_data`.
|
||||
#[inline]
|
||||
fn read_proof_with_public_inputs<F, C, const D: usize>(
|
||||
&mut self,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> IoResult<ProofWithPublicInputs<F, C, D>>
|
||||
where
|
||||
Self: Remaining,
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
let proof = self.read_proof(common_data)?;
|
||||
let public_inputs = self.read_field_vec(self.remaining() / size_of::<u64>())?;
|
||||
Ok(ProofWithPublicInputs {
|
||||
proof,
|
||||
public_inputs,
|
||||
})
|
||||
}
|
||||
|
||||
/// Reads a value of type [`CompressedFriQueryRounds`] from `self` with `common_data`.
|
||||
#[inline]
|
||||
fn read_compressed_fri_query_rounds<F, C, const D: usize>(
|
||||
&mut self,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> IoResult<CompressedFriQueryRounds<F, C::HCO, C::Hasher, D>>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
let config = &common_data.config;
|
||||
let original_indices = (0..config.fri_config.num_query_rounds)
|
||||
.map(|_| self.read_u32().map(|i| i as usize))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let mut indices = original_indices.clone();
|
||||
indices.sort_unstable();
|
||||
indices.dedup();
|
||||
let mut pairs = Vec::new();
|
||||
for &i in &indices {
|
||||
pairs.push((i, self.read_fri_initial_proof::<F, C, D>(common_data)?));
|
||||
}
|
||||
let initial_trees_proofs = HashMap::from_iter(pairs);
|
||||
|
||||
let mut steps = Vec::with_capacity(common_data.fri_params.reduction_arity_bits.len());
|
||||
for &a in &common_data.fri_params.reduction_arity_bits {
|
||||
indices.iter_mut().for_each(|x| {
|
||||
*x >>= a;
|
||||
});
|
||||
indices.dedup();
|
||||
let query_steps = (0..indices.len())
|
||||
.map(|_| self.read_fri_query_step::<F, C, D>(1 << a, true))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
steps.push(
|
||||
indices
|
||||
.iter()
|
||||
.copied()
|
||||
.zip(query_steps)
|
||||
.collect::<HashMap<_, _>>(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(CompressedFriQueryRounds {
|
||||
indices: original_indices,
|
||||
initial_trees_proofs,
|
||||
steps,
|
||||
})
|
||||
}
|
||||
|
||||
/// Reads a value of type [`CompressedFriProof`] from `self` with `common_data`.
|
||||
#[inline]
|
||||
fn read_compressed_fri_proof<F, C, const D: usize>(
|
||||
&mut self,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> IoResult<CompressedFriProof<F, C::HCO, C::Hasher, D>>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
let config = &common_data.config;
|
||||
let commit_phase_merkle_caps = (0..common_data.fri_params.reduction_arity_bits.len())
|
||||
.map(|_| self.read_merkle_cap(config.fri_config.cap_height))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let query_round_proofs = self.read_compressed_fri_query_rounds::<F, C, D>(common_data)?;
|
||||
let final_poly = PolynomialCoeffs::new(
|
||||
self.read_field_ext_vec::<F, D>(common_data.fri_params.final_poly_len())?,
|
||||
);
|
||||
let pow_witness = self.read_field()?;
|
||||
Ok(CompressedFriProof {
|
||||
commit_phase_merkle_caps,
|
||||
query_round_proofs,
|
||||
final_poly,
|
||||
pow_witness,
|
||||
})
|
||||
}
|
||||
|
||||
/// Reads a value of type [`CompressedProof`] from `self` with `common_data`.
|
||||
#[inline]
|
||||
fn read_compressed_proof<F, C, const D: usize>(
|
||||
&mut self,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> IoResult<CompressedProof<F, C, D>>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
let config = &common_data.config;
|
||||
let wires_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
|
||||
let plonk_zs_partial_products_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
|
||||
let quotient_polys_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
|
||||
let openings = self.read_opening_set::<F, C, D>(common_data)?;
|
||||
let opening_proof = self.read_compressed_fri_proof::<F, C, D>(common_data)?;
|
||||
Ok(CompressedProof {
|
||||
wires_cap,
|
||||
plonk_zs_partial_products_cap,
|
||||
quotient_polys_cap,
|
||||
openings,
|
||||
opening_proof,
|
||||
})
|
||||
}
|
||||
|
||||
/// Reads a value of type [`CompressedProofWithPublicInputs`] from `self` with `common_data`.
|
||||
#[inline]
|
||||
fn read_compressed_proof_with_public_inputs<F, C, const D: usize>(
|
||||
&mut self,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> IoResult<CompressedProofWithPublicInputs<F, C, D>>
|
||||
where
|
||||
Self: Remaining,
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
let proof = self.read_compressed_proof(common_data)?;
|
||||
let public_inputs = self.read_field_vec(self.remaining() / size_of::<u64>())?;
|
||||
Ok(CompressedProofWithPublicInputs {
|
||||
proof,
|
||||
public_inputs,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Writing
|
||||
pub trait Write {
|
||||
/// Error Type
|
||||
type Error;
|
||||
|
||||
/// Writes all `bytes` to `self`.
|
||||
fn write_all(&mut self, bytes: &[u8]) -> IoResult<()>;
|
||||
|
||||
/// Writes a byte `x` to `self`.
|
||||
#[inline]
|
||||
fn write_u8(&mut self, x: u8) -> IoResult<()> {
|
||||
self.write_all(&[x])
|
||||
}
|
||||
|
||||
/// Writes a word `x` to `self.`
|
||||
#[inline]
|
||||
fn write_u32(&mut self, x: u32) -> IoResult<()> {
|
||||
self.write_all(&x.to_le_bytes())
|
||||
}
|
||||
|
||||
/// Writes an element `x` from the field `F` to `self`.
|
||||
#[inline]
|
||||
fn write_field<F>(&mut self, x: F) -> IoResult<()>
|
||||
where
|
||||
F: PrimeField64,
|
||||
{
|
||||
self.write_all(&x.to_canonical_u64().to_le_bytes())
|
||||
}
|
||||
|
||||
/// Writes a vector `v` of elements from the field `F` to `self`.
|
||||
#[inline]
|
||||
fn write_field_vec<F>(&mut self, v: &[F]) -> IoResult<()>
|
||||
where
|
||||
F: PrimeField64,
|
||||
{
|
||||
for &a in v {
|
||||
self.write_field(a)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes an element `x` from the field extension of `F` to `self`.
|
||||
#[inline]
|
||||
fn write_field_ext<F, const D: usize>(&mut self, x: F::Extension) -> IoResult<()>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
{
|
||||
for &a in &x.to_basefield_array() {
|
||||
self.write_field(a)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes a vector `v` of elements from the field extension of `F` to `self`.
|
||||
#[inline]
|
||||
fn write_field_ext_vec<F, const D: usize>(&mut self, v: &[F::Extension]) -> IoResult<()>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
{
|
||||
for &a in v {
|
||||
self.write_field_ext::<F, D>(a)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes a hash `h` to `self`.
|
||||
#[inline]
|
||||
fn write_hash<F, HC, H>(&mut self, h: H::Hash) -> IoResult<()>
|
||||
where
|
||||
F: RichField,
|
||||
HC: HashConfig,
|
||||
H: Hasher<F, HC>,
|
||||
{
|
||||
self.write_all(&h.to_bytes())
|
||||
}
|
||||
|
||||
/// Writes `cap`, a value of type [`MerkleCap`], to `self`.
|
||||
#[inline]
|
||||
fn write_merkle_cap<F, HC, H>(&mut self, cap: &MerkleCap<F, HC, H>) -> IoResult<()>
|
||||
where
|
||||
F: RichField,
|
||||
HC: HashConfig,
|
||||
H: Hasher<F, HC>,
|
||||
{
|
||||
for &a in &cap.0 {
|
||||
self.write_hash::<F, HC, H>(a)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes a value `os` of type [`OpeningSet`] to `self.`
|
||||
#[inline]
|
||||
fn write_opening_set<F, const D: usize>(&mut self, os: &OpeningSet<F, D>) -> IoResult<()>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
{
|
||||
self.write_field_ext_vec::<F, D>(&os.constants)?;
|
||||
self.write_field_ext_vec::<F, D>(&os.plonk_sigmas)?;
|
||||
self.write_field_ext_vec::<F, D>(&os.wires)?;
|
||||
self.write_field_ext_vec::<F, D>(&os.plonk_zs)?;
|
||||
self.write_field_ext_vec::<F, D>(&os.plonk_zs_next)?;
|
||||
self.write_field_ext_vec::<F, D>(&os.partial_products)?;
|
||||
self.write_field_ext_vec::<F, D>(&os.quotient_polys)
|
||||
}
|
||||
|
||||
/// Writes a value `p` of type [`MerkleProof`] to `self.`
|
||||
#[inline]
|
||||
fn write_merkle_proof<F, HC, H>(&mut self, p: &MerkleProof<F, HC, H>) -> IoResult<()>
|
||||
where
|
||||
F: RichField,
|
||||
HC: HashConfig,
|
||||
H: Hasher<F, HC>,
|
||||
{
|
||||
let length = p.siblings.len();
|
||||
self.write_u8(
|
||||
length
|
||||
.try_into()
|
||||
.expect("Merkle proof length must fit in u8."),
|
||||
)?;
|
||||
for &h in &p.siblings {
|
||||
self.write_hash::<F, HC, H>(h)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes a value `fitp` of type [`FriInitialTreeProof`] to `self.`
|
||||
#[inline]
|
||||
fn write_fri_initial_proof<F, C, const D: usize>(
|
||||
&mut self,
|
||||
fitp: &FriInitialTreeProof<F, C::HCO, C::Hasher>,
|
||||
) -> IoResult<()>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
for (v, p) in &fitp.evals_proofs {
|
||||
self.write_field_vec(v)?;
|
||||
self.write_merkle_proof(p)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes a value `fqs` of type [`FriQueryStep`] to `self.`
|
||||
#[inline]
|
||||
fn write_fri_query_step<F, C, const D: usize>(
|
||||
&mut self,
|
||||
fqs: &FriQueryStep<F, C::HCO, C::Hasher, D>,
|
||||
) -> IoResult<()>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
self.write_field_ext_vec::<F, D>(&fqs.evals)?;
|
||||
self.write_merkle_proof(&fqs.merkle_proof)
|
||||
}
|
||||
|
||||
/// Writes a value `fqrs` of type [`FriQueryRound`] to `self.`
|
||||
#[inline]
|
||||
fn write_fri_query_rounds<F, C, const D: usize>(
|
||||
&mut self,
|
||||
fqrs: &[FriQueryRound<F, C::HCO, C::Hasher, D>],
|
||||
) -> IoResult<()>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
for fqr in fqrs {
|
||||
self.write_fri_initial_proof::<F, C, D>(&fqr.initial_trees_proof)?;
|
||||
for fqs in &fqr.steps {
|
||||
self.write_fri_query_step::<F, C, D>(fqs)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes a value `fq` of type [`FriProof`] to `self.`
|
||||
#[inline]
|
||||
fn write_fri_proof<F, C, const D: usize>(
|
||||
&mut self,
|
||||
fp: &FriProof<F, C::HCO, C::Hasher, D>,
|
||||
) -> IoResult<()>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
for cap in &fp.commit_phase_merkle_caps {
|
||||
self.write_merkle_cap(cap)?;
|
||||
}
|
||||
self.write_fri_query_rounds::<F, C, D>(&fp.query_round_proofs)?;
|
||||
self.write_field_ext_vec::<F, D>(&fp.final_poly.coeffs)?;
|
||||
self.write_field(fp.pow_witness)
|
||||
}
|
||||
|
||||
/// Writes a value `proof` of type [`Proof`] to `self.`
|
||||
#[inline]
|
||||
fn write_proof<F, C, const D: usize>(&mut self, proof: &Proof<F, C, D>) -> IoResult<()>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
self.write_merkle_cap(&proof.wires_cap)?;
|
||||
self.write_merkle_cap(&proof.plonk_zs_partial_products_cap)?;
|
||||
self.write_merkle_cap(&proof.quotient_polys_cap)?;
|
||||
self.write_opening_set(&proof.openings)?;
|
||||
self.write_fri_proof::<F, C, D>(&proof.opening_proof)
|
||||
}
|
||||
|
||||
/// Writes a value `proof_with_pis` of type [`ProofWithPublicInputs`] to `self.`
|
||||
#[inline]
|
||||
fn write_proof_with_public_inputs<F, C, const D: usize>(
|
||||
&mut self,
|
||||
proof_with_pis: &ProofWithPublicInputs<F, C, D>,
|
||||
) -> IoResult<()>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
let ProofWithPublicInputs {
|
||||
proof,
|
||||
public_inputs,
|
||||
} = proof_with_pis;
|
||||
self.write_proof(proof)?;
|
||||
self.write_field_vec(public_inputs)
|
||||
}
|
||||
|
||||
/// Writes a value `cfqrs` of type [`CompressedFriQueryRounds`] to `self.`
|
||||
#[inline]
|
||||
fn write_compressed_fri_query_rounds<F, C, const D: usize>(
|
||||
&mut self,
|
||||
cfqrs: &CompressedFriQueryRounds<F, C::HCO, C::Hasher, D>,
|
||||
) -> IoResult<()>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
for &i in &cfqrs.indices {
|
||||
self.write_u32(i as u32)?;
|
||||
}
|
||||
let mut initial_trees_proofs = cfqrs.initial_trees_proofs.iter().collect::<Vec<_>>();
|
||||
initial_trees_proofs.sort_by_key(|&x| x.0);
|
||||
for (_, itp) in initial_trees_proofs {
|
||||
self.write_fri_initial_proof::<F, C, D>(itp)?;
|
||||
}
|
||||
for h in &cfqrs.steps {
|
||||
let mut fri_query_steps = h.iter().collect::<Vec<_>>();
|
||||
fri_query_steps.sort_by_key(|&x| x.0);
|
||||
for (_, fqs) in fri_query_steps {
|
||||
self.write_fri_query_step::<F, C, D>(fqs)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes a value `fq` of type [`CompressedFriProof`] to `self.`
|
||||
#[inline]
|
||||
fn write_compressed_fri_proof<F, C, const D: usize>(
|
||||
&mut self,
|
||||
fp: &CompressedFriProof<F, C::HCO, C::Hasher, D>,
|
||||
) -> IoResult<()>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
for cap in &fp.commit_phase_merkle_caps {
|
||||
self.write_merkle_cap(cap)?;
|
||||
}
|
||||
self.write_compressed_fri_query_rounds::<F, C, D>(&fp.query_round_proofs)?;
|
||||
self.write_field_ext_vec::<F, D>(&fp.final_poly.coeffs)?;
|
||||
self.write_field(fp.pow_witness)
|
||||
}
|
||||
|
||||
/// Writes a value `proof` of type [`CompressedProof`] to `self.`
|
||||
#[inline]
|
||||
fn write_compressed_proof<F, C, const D: usize>(
|
||||
&mut self,
|
||||
proof: &CompressedProof<F, C, D>,
|
||||
) -> IoResult<()>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
self.write_merkle_cap(&proof.wires_cap)?;
|
||||
self.write_merkle_cap(&proof.plonk_zs_partial_products_cap)?;
|
||||
self.write_merkle_cap(&proof.quotient_polys_cap)?;
|
||||
self.write_opening_set(&proof.openings)?;
|
||||
self.write_compressed_fri_proof::<F, C, D>(&proof.opening_proof)
|
||||
}
|
||||
|
||||
/// Writes a value `proof_with_pis` of type [`CompressedProofWithPublicInputs`] to `self.`
|
||||
#[inline]
|
||||
fn write_compressed_proof_with_public_inputs<F, C, const D: usize>(
|
||||
&mut self,
|
||||
proof_with_pis: &CompressedProofWithPublicInputs<F, C, D>,
|
||||
) -> IoResult<()>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
let CompressedProofWithPublicInputs {
|
||||
proof,
|
||||
public_inputs,
|
||||
} = proof_with_pis;
|
||||
self.write_compressed_proof(proof)?;
|
||||
self.write_field_vec(public_inputs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Vec<u8> {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, bytes: &[u8]) -> IoResult<()> {
|
||||
self.extend_from_slice(bytes);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Buffer
|
||||
#[cfg(feature = "std")]
|
||||
#[derive(Debug)]
|
||||
pub struct Buffer {
|
||||
bytes: Vec<u8>,
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Buffer {
|
||||
/// Builds a new [`Buffer`] over `buffer`.
|
||||
#[inline]
|
||||
pub fn new(bytes: Vec<u8>) -> Self {
|
||||
Self { bytes, pos: 0 }
|
||||
}
|
||||
|
||||
/// Returns the inner buffer.
|
||||
#[inline]
|
||||
pub fn bytes(self) -> Vec<u8> {
|
||||
self.bytes
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Remaining for Buffer {
|
||||
fn remaining(&self) -> usize {
|
||||
self.bytes.len() - self.pos
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Read for Buffer {
|
||||
#[inline]
|
||||
fn read_exact(&mut self, bytes: &mut [u8]) -> IoResult<()> {
|
||||
let n = bytes.len();
|
||||
if self.remaining() < n {
|
||||
Err(IoError)
|
||||
} else {
|
||||
bytes.copy_from_slice(&self.bytes[self.pos..][..n]);
|
||||
self.pos += n;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
105
plonky2/src/util/serialization/gate_serialization.rs
Normal file
105
plonky2/src/util/serialization/gate_serialization.rs
Normal file
@ -0,0 +1,105 @@
|
||||
use plonky2_field::extension::Extendable;
|
||||
|
||||
use crate::gates::gate::GateRef;
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::util::serialization::{Buffer, IoResult};
|
||||
|
||||
pub trait GateSerializer<F: RichField + Extendable<D>, const D: usize> {
|
||||
fn read_gate(&self, buf: &mut Buffer) -> IoResult<GateRef<F, D>>;
|
||||
fn write_gate(&self, buf: &mut Vec<u8>, gate: &GateRef<F, D>) -> IoResult<()>;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! read_gate_impl {
|
||||
($buf:expr, $tag:expr, $($gate_types:ty),+) => {{
|
||||
let tag = $tag;
|
||||
let buf = $buf;
|
||||
let mut i = 0..;
|
||||
$(if tag == i.next().unwrap() {
|
||||
let gate = <$gate_types as $crate::gates::gate::Gate<F, D>>::deserialize(buf)?;
|
||||
Ok($crate::gates::gate::GateRef::<F, D>::new(gate))
|
||||
} else)*
|
||||
{
|
||||
Err($crate::util::serialization::IoError)
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! get_gate_tag_impl {
|
||||
($gate:expr, $($gate_types:ty),+) => {{
|
||||
let gate_any = $gate.0.as_any();
|
||||
let mut i = 0..;
|
||||
$(if let (tag, true) = (i.next().unwrap(), gate_any.is::<$gate_types>()) {
|
||||
Ok(tag)
|
||||
} else)*
|
||||
{
|
||||
log::log!(log::Level::Error, "attempted to serialize gate with id `{}` which is unsupported by this gate serializer", $gate.0.id());
|
||||
Err($crate::util::serialization::IoError)
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
/// Macro implementing the `GateSerializer` trait.
|
||||
/// To serialize a list of gates used for a circuit,
|
||||
/// this macro should be called with a struct on which to implement
|
||||
/// this as first argument, followed by all the targeted gates.
|
||||
macro_rules! impl_gate_serializer {
|
||||
($target:ty, $($gate_types:ty),+) => {
|
||||
fn read_gate(&self, buf: &mut $crate::util::serialization::Buffer) -> $crate::util::serialization::IoResult<$crate::gates::gate::GateRef<F, D>> {
|
||||
let tag = $crate::util::serialization::Read::read_u32(buf)?;
|
||||
read_gate_impl!(buf, tag, $($gate_types),+)
|
||||
}
|
||||
|
||||
fn write_gate(&self, buf: &mut Vec<u8>, gate: &$crate::gates::gate::GateRef<F, D>) -> $crate::util::serialization::IoResult<()> {
|
||||
let tag = get_gate_tag_impl!(gate, $($gate_types),+)?;
|
||||
|
||||
$crate::util::serialization::Write::write_u32(buf, tag)?;
|
||||
gate.0.serialize(buf)?;
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub mod default {
|
||||
use plonky2_field::extension::Extendable;
|
||||
|
||||
use crate::gates::arithmetic_base::ArithmeticGate;
|
||||
use crate::gates::arithmetic_extension::ArithmeticExtensionGate;
|
||||
use crate::gates::base_sum::BaseSumGate;
|
||||
use crate::gates::constant::ConstantGate;
|
||||
use crate::gates::coset_interpolation::CosetInterpolationGate;
|
||||
use crate::gates::exponentiation::ExponentiationGate;
|
||||
use crate::gates::multiplication_extension::MulExtensionGate;
|
||||
use crate::gates::noop::NoopGate;
|
||||
use crate::gates::poseidon::PoseidonGate;
|
||||
use crate::gates::poseidon_mds::PoseidonMdsGate;
|
||||
use crate::gates::public_input::PublicInputGate;
|
||||
use crate::gates::random_access::RandomAccessGate;
|
||||
use crate::gates::reducing::ReducingGate;
|
||||
use crate::gates::reducing_extension::ReducingExtensionGate;
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::util::serialization::GateSerializer;
|
||||
|
||||
pub struct DefaultGateSerializer;
|
||||
impl<F: RichField + Extendable<D>, const D: usize> GateSerializer<F, D> for DefaultGateSerializer {
|
||||
impl_gate_serializer! {
|
||||
DefaultGateSerializer,
|
||||
ArithmeticGate,
|
||||
ArithmeticExtensionGate<D>,
|
||||
BaseSumGate<2>,
|
||||
ConstantGate,
|
||||
CosetInterpolationGate<F, D>,
|
||||
ExponentiationGate<F, D>,
|
||||
MulExtensionGate<D>,
|
||||
NoopGate,
|
||||
PoseidonMdsGate<F, D>,
|
||||
PoseidonGate<F, D>,
|
||||
PublicInputGate,
|
||||
RandomAccessGate<F, D>,
|
||||
ReducingExtensionGate<D>,
|
||||
ReducingGate<D>
|
||||
}
|
||||
}
|
||||
}
|
||||
164
plonky2/src/util/serialization/generator_serialization.rs
Normal file
164
plonky2/src/util/serialization/generator_serialization.rs
Normal file
@ -0,0 +1,164 @@
|
||||
//! A module to help with WitnessGeneratorRef serialization
|
||||
|
||||
use plonky2_field::extension::Extendable;
|
||||
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::iop::generator::WitnessGeneratorRef;
|
||||
use crate::plonk::circuit_data::CommonCircuitData;
|
||||
use crate::util::serialization::{Buffer, IoResult};
|
||||
|
||||
pub trait WitnessGeneratorSerializer<F: RichField + Extendable<D>, const D: usize> {
|
||||
fn read_generator(
|
||||
&self,
|
||||
buf: &mut Buffer,
|
||||
common: &CommonCircuitData<F, D>,
|
||||
) -> IoResult<WitnessGeneratorRef<F>>;
|
||||
fn write_generator(
|
||||
&self,
|
||||
buf: &mut Vec<u8>,
|
||||
generator: &WitnessGeneratorRef<F>,
|
||||
) -> IoResult<()>;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! read_generator_impl {
|
||||
($buf:expr, $tag:expr, $common:expr, $($generator_types:ty),+) => {{
|
||||
let tag = $tag;
|
||||
let buf = $buf;
|
||||
let mut i = 0..;
|
||||
|
||||
if tag == 0 {
|
||||
let generator: $crate::recursion::dummy_circuit::DummyProofGenerator<F, C, D> =
|
||||
$crate::recursion::dummy_circuit::DummyProofGenerator::deserialize_with_circuit_data(buf, $common)?;
|
||||
return Ok($crate::iop::generator::WitnessGeneratorRef::<F>::new(
|
||||
$crate::iop::generator::SimpleGenerator::<F>::adapter(generator),
|
||||
));
|
||||
}
|
||||
|
||||
$(if tag == i.next().unwrap() {
|
||||
let generator =
|
||||
<$generator_types as $crate::iop::generator::SimpleGenerator<F>>::deserialize(buf)?;
|
||||
Ok($crate::iop::generator::WitnessGeneratorRef::<F>::new(
|
||||
$crate::iop::generator::SimpleGenerator::<F>::adapter(generator),
|
||||
))
|
||||
} else)*
|
||||
{
|
||||
Err($crate::util::serialization::IoError)
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! get_generator_tag_impl {
|
||||
($generator:expr, $($generator_types:ty),+) => {{
|
||||
let mut i = 0..;
|
||||
$(if let (tag, true) = (i.next().unwrap(), $generator.0.id() == $crate::iop::generator::SimpleGenerator::<F>::id(&<$generator_types>::default())) {
|
||||
Ok(tag)
|
||||
} else)*
|
||||
{
|
||||
log::log!(log::Level::Error, "attempted to serialize generator with id {} which is unsupported by this generator serializer", $generator.0.id());
|
||||
Err($crate::util::serialization::IoError)
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
/// Macro implementing the `WitnessGeneratorSerializer` trait.
|
||||
/// To serialize a list of generators used for a circuit,
|
||||
/// this macro should be called with a struct on which to implement
|
||||
/// this as first argument, followed by all the targeted generators.
|
||||
///
|
||||
/// ***NOTE:*** If you need to include `DummyProofGenerator`, you **MUST**
|
||||
/// place it at the *beginning* of the generators list, right after
|
||||
/// the serializer struct.
|
||||
macro_rules! impl_generator_serializer {
|
||||
($target:ty, $($generator_types:ty),+) => {
|
||||
fn read_generator(
|
||||
&self,
|
||||
buf: &mut $crate::util::serialization::Buffer,
|
||||
common: &$crate::plonk::circuit_data::CommonCircuitData<F, D>,
|
||||
) -> $crate::util::serialization::IoResult<$crate::iop::generator::WitnessGeneratorRef<F>> {
|
||||
let tag = $crate::util::serialization::Read::read_u32(buf)?;
|
||||
read_generator_impl!(buf, tag, common, $($generator_types),+)
|
||||
}
|
||||
|
||||
fn write_generator(
|
||||
&self,
|
||||
buf: &mut Vec<u8>,
|
||||
generator: &$crate::iop::generator::WitnessGeneratorRef<F>,
|
||||
) -> $crate::util::serialization::IoResult<()> {
|
||||
let tag = get_generator_tag_impl!(generator, $($generator_types),+)?;
|
||||
|
||||
$crate::util::serialization::Write::write_u32(buf, tag)?;
|
||||
generator.0.serialize(buf)?;
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub mod default {
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use plonky2_field::extension::Extendable;
|
||||
|
||||
use crate::gadgets::arithmetic::EqualityGenerator;
|
||||
use crate::gadgets::arithmetic_extension::QuotientGeneratorExtension;
|
||||
use crate::gadgets::range_check::LowHighGenerator;
|
||||
use crate::gadgets::split_base::BaseSumGenerator;
|
||||
use crate::gadgets::split_join::{SplitGenerator, WireSplitGenerator};
|
||||
use crate::gates::arithmetic_base::ArithmeticBaseGenerator;
|
||||
use crate::gates::arithmetic_extension::ArithmeticExtensionGenerator;
|
||||
use crate::gates::base_sum::BaseSplitGenerator;
|
||||
use crate::gates::coset_interpolation::InterpolationGenerator;
|
||||
use crate::gates::exponentiation::ExponentiationGenerator;
|
||||
use crate::gates::multiplication_extension::MulExtensionGenerator;
|
||||
use crate::gates::poseidon::PoseidonGenerator;
|
||||
use crate::gates::poseidon_mds::PoseidonMdsGenerator;
|
||||
use crate::gates::random_access::RandomAccessGenerator;
|
||||
use crate::gates::reducing::ReducingGenerator;
|
||||
use crate::gates::reducing_extension::ReducingGenerator as ReducingExtensionGenerator;
|
||||
use crate::hash::hash_types::RichField;
|
||||
use crate::iop::generator::{
|
||||
ConstantGenerator, CopyGenerator, NonzeroTestGenerator, RandomValueGenerator,
|
||||
};
|
||||
use crate::plonk::config::{AlgebraicHasher, GenericConfig};
|
||||
use crate::recursion::dummy_circuit::DummyProofGenerator;
|
||||
use crate::util::serialization::WitnessGeneratorSerializer;
|
||||
|
||||
pub struct DefaultGeneratorSerializer<C: GenericConfig<D>, const D: usize> {
|
||||
pub _phantom: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<F, C, const D: usize> WitnessGeneratorSerializer<F, D> for DefaultGeneratorSerializer<C, D>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F> + 'static,
|
||||
C::Hasher: AlgebraicHasher<F, C::HCO>,
|
||||
{
|
||||
impl_generator_serializer! {
|
||||
DefaultGeneratorSerializer,
|
||||
DummyProofGenerator<F, C, D>,
|
||||
ArithmeticBaseGenerator<F, D>,
|
||||
ArithmeticExtensionGenerator<F, D>,
|
||||
BaseSplitGenerator<2>,
|
||||
BaseSumGenerator<2>,
|
||||
ConstantGenerator<F>,
|
||||
CopyGenerator,
|
||||
EqualityGenerator,
|
||||
ExponentiationGenerator<F, D>,
|
||||
InterpolationGenerator<F, D>,
|
||||
LowHighGenerator,
|
||||
MulExtensionGenerator<F, D>,
|
||||
NonzeroTestGenerator,
|
||||
PoseidonGenerator<F, D>,
|
||||
PoseidonMdsGenerator<D>,
|
||||
QuotientGeneratorExtension<D>,
|
||||
RandomAccessGenerator<F, D>,
|
||||
RandomValueGenerator,
|
||||
ReducingGenerator<D>,
|
||||
ReducingExtensionGenerator<D>,
|
||||
SplitGenerator,
|
||||
WireSplitGenerator
|
||||
}
|
||||
}
|
||||
}
|
||||
2080
plonky2/src/util/serialization/mod.rs
Normal file
2080
plonky2/src/util/serialization/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user