mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-11 10:13:09 +00:00
Merge branch 'main' into new-logup
This commit is contained in:
commit
ca44187201
@ -200,7 +200,7 @@ impl<'a> Interpreter<'a> {
|
||||
self.generation_state.memory.contexts[0].segments[segment as usize]
|
||||
.content
|
||||
.iter()
|
||||
.map(|x| x.as_u32() as u8)
|
||||
.map(|x| x.low_u32() as u8)
|
||||
.collect()
|
||||
}
|
||||
|
||||
@ -1045,7 +1045,7 @@ impl<'a> Interpreter<'a> {
|
||||
self.generation_state
|
||||
.memory
|
||||
.mload_general(context, segment, offset + i)
|
||||
.as_u32() as u8
|
||||
.low_u32() as u8
|
||||
})
|
||||
.collect();
|
||||
let value = U256::from_big_endian(&bytes);
|
||||
|
||||
@ -75,7 +75,7 @@ fn run_blake2_f(
|
||||
|
||||
Ok(hash
|
||||
.iter()
|
||||
.map(|&x| x.as_u64())
|
||||
.map(|&x| x.low_u64())
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap())
|
||||
|
||||
@ -30,6 +30,7 @@ use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars};
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Column<F: Field> {
|
||||
linear_combination: Vec<(usize, F)>,
|
||||
next_row_linear_combination: Vec<(usize, F)>,
|
||||
constant: F,
|
||||
}
|
||||
|
||||
@ -37,6 +38,7 @@ impl<F: Field> Column<F> {
|
||||
pub fn single(c: usize) -> Self {
|
||||
Self {
|
||||
linear_combination: vec![(c, F::ONE)],
|
||||
next_row_linear_combination: vec![],
|
||||
constant: F::ZERO,
|
||||
}
|
||||
}
|
||||
@ -47,9 +49,24 @@ impl<F: Field> Column<F> {
|
||||
cs.into_iter().map(|c| Self::single(*c.borrow()))
|
||||
}
|
||||
|
||||
pub fn single_next_row(c: usize) -> Self {
|
||||
Self {
|
||||
linear_combination: vec![],
|
||||
next_row_linear_combination: vec![(c, F::ONE)],
|
||||
constant: F::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn singles_next_row<I: IntoIterator<Item = impl Borrow<usize>>>(
|
||||
cs: I,
|
||||
) -> impl Iterator<Item = Self> {
|
||||
cs.into_iter().map(|c| Self::single_next_row(*c.borrow()))
|
||||
}
|
||||
|
||||
pub fn constant(constant: F) -> Self {
|
||||
Self {
|
||||
linear_combination: vec![],
|
||||
next_row_linear_combination: vec![],
|
||||
constant,
|
||||
}
|
||||
}
|
||||
@ -75,6 +92,34 @@ impl<F: Field> Column<F> {
|
||||
);
|
||||
Self {
|
||||
linear_combination: v,
|
||||
next_row_linear_combination: vec![],
|
||||
constant,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn linear_combination_and_next_row_with_constant<I: IntoIterator<Item = (usize, F)>>(
|
||||
iter: I,
|
||||
next_row_iter: I,
|
||||
constant: F,
|
||||
) -> Self {
|
||||
let v = iter.into_iter().collect::<Vec<_>>();
|
||||
let next_row_v = next_row_iter.into_iter().collect::<Vec<_>>();
|
||||
|
||||
assert!(!v.is_empty() || !next_row_v.is_empty());
|
||||
debug_assert_eq!(
|
||||
v.iter().map(|(c, _)| c).unique().count(),
|
||||
v.len(),
|
||||
"Duplicate columns."
|
||||
);
|
||||
debug_assert_eq!(
|
||||
next_row_v.iter().map(|(c, _)| c).unique().count(),
|
||||
next_row_v.len(),
|
||||
"Duplicate columns."
|
||||
);
|
||||
|
||||
Self {
|
||||
linear_combination: v,
|
||||
next_row_linear_combination: next_row_v,
|
||||
constant,
|
||||
}
|
||||
}
|
||||
@ -111,13 +156,43 @@ impl<F: Field> Column<F> {
|
||||
+ FE::from_basefield(self.constant)
|
||||
}
|
||||
|
||||
pub fn eval_with_next<FE, P, const D: usize>(&self, v: &[P], next_v: &[P]) -> P
|
||||
where
|
||||
FE: FieldExtension<D, BaseField = F>,
|
||||
P: PackedField<Scalar = FE>,
|
||||
{
|
||||
self.linear_combination
|
||||
.iter()
|
||||
.map(|&(c, f)| v[c] * FE::from_basefield(f))
|
||||
.sum::<P>()
|
||||
+ self
|
||||
.next_row_linear_combination
|
||||
.iter()
|
||||
.map(|&(c, f)| next_v[c] * FE::from_basefield(f))
|
||||
.sum::<P>()
|
||||
+ FE::from_basefield(self.constant)
|
||||
}
|
||||
|
||||
/// Evaluate on an row of a table given in column-major form.
|
||||
pub fn eval_table(&self, table: &[PolynomialValues<F>], row: usize) -> F {
|
||||
self.linear_combination
|
||||
let mut res = self
|
||||
.linear_combination
|
||||
.iter()
|
||||
.map(|&(c, f)| table[c].values[row] * f)
|
||||
.sum::<F>()
|
||||
+ self.constant
|
||||
+ self.constant;
|
||||
|
||||
// If we access the next row at the last row, for sanity, we consider the next row's values to be 0.
|
||||
// If CTLs are correctly written, the filter should be 0 in that case anyway.
|
||||
if !self.next_row_linear_combination.is_empty() && row < table.len() - 1 {
|
||||
res += self
|
||||
.next_row_linear_combination
|
||||
.iter()
|
||||
.map(|&(c, f)| table[c].values[row + 1] * f)
|
||||
.sum::<F>();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn eval_circuit<const D: usize>(
|
||||
@ -141,6 +216,36 @@ impl<F: Field> Column<F> {
|
||||
let constant = builder.constant_extension(F::Extension::from_basefield(self.constant));
|
||||
builder.inner_product_extension(F::ONE, constant, pairs)
|
||||
}
|
||||
|
||||
pub fn eval_with_next_circuit<const D: usize>(
|
||||
&self,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
v: &[ExtensionTarget<D>],
|
||||
next_v: &[ExtensionTarget<D>],
|
||||
) -> ExtensionTarget<D>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
{
|
||||
let mut pairs = self
|
||||
.linear_combination
|
||||
.iter()
|
||||
.map(|&(c, f)| {
|
||||
(
|
||||
v[c],
|
||||
builder.constant_extension(F::Extension::from_basefield(f)),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let next_row_pairs = self.next_row_linear_combination.iter().map(|&(c, f)| {
|
||||
(
|
||||
next_v[c],
|
||||
builder.constant_extension(F::Extension::from_basefield(f)),
|
||||
)
|
||||
});
|
||||
pairs.extend(next_row_pairs);
|
||||
let constant = builder.constant_extension(F::Extension::from_basefield(self.constant));
|
||||
builder.inner_product_extension(F::ONE, constant, pairs)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -403,7 +508,7 @@ fn partial_products<F: Field>(
|
||||
let mut partial_prod = F::ONE;
|
||||
let degree = trace[0].len();
|
||||
let mut res = Vec::with_capacity(degree);
|
||||
for i in 0..degree {
|
||||
for i in (0..degree).rev() {
|
||||
let filter = if let Some(column) = filter_column {
|
||||
column.eval_table(trace, i)
|
||||
} else {
|
||||
@ -420,6 +525,7 @@ fn partial_products<F: Field>(
|
||||
};
|
||||
res.push(partial_prod);
|
||||
}
|
||||
res.reverse();
|
||||
res.into()
|
||||
}
|
||||
|
||||
@ -489,6 +595,10 @@ impl<'a, F: RichField + Extendable<D>, const D: usize>
|
||||
}
|
||||
}
|
||||
|
||||
/// CTL Z partial products are upside down: the complete product is on the first row, and
|
||||
/// the first term is on the last row. This allows the transition constraint to be:
|
||||
/// Z(w) = Z(gw) * combine(w) where combine is called on the local row
|
||||
/// and not the next. This enables CTLs across two rows.
|
||||
pub(crate) fn eval_cross_table_lookup_checks<F, FE, P, S, const D: usize, const D2: usize>(
|
||||
vars: StarkEvaluationVars<FE, P, { S::COLUMNS }>,
|
||||
ctl_vars: &[CtlCheckVars<F, FE, P, D2>],
|
||||
@ -507,27 +617,23 @@ pub(crate) fn eval_cross_table_lookup_checks<F, FE, P, S, const D: usize, const
|
||||
columns,
|
||||
filter_column,
|
||||
} = lookup_vars;
|
||||
let combine = |v: &[P]| -> P {
|
||||
let evals = columns.iter().map(|c| c.eval(v)).collect::<Vec<_>>();
|
||||
challenges.combine(evals.iter())
|
||||
};
|
||||
let filter = |v: &[P]| -> P {
|
||||
if let Some(column) = filter_column {
|
||||
column.eval(v)
|
||||
} else {
|
||||
P::ONES
|
||||
}
|
||||
};
|
||||
let local_filter = filter(vars.local_values);
|
||||
let next_filter = filter(vars.next_values);
|
||||
let select = |filter, x| filter * x + P::ONES - filter;
|
||||
|
||||
// Check value of `Z(1)`
|
||||
consumer.constraint_first_row(*local_z - select(local_filter, combine(vars.local_values)));
|
||||
// Check `Z(gw) = combination * Z(w)`
|
||||
consumer.constraint_transition(
|
||||
*local_z * select(next_filter, combine(vars.next_values)) - *next_z,
|
||||
);
|
||||
let evals = columns
|
||||
.iter()
|
||||
.map(|c| c.eval_with_next(vars.local_values, vars.next_values))
|
||||
.collect::<Vec<_>>();
|
||||
let combined = challenges.combine(evals.iter());
|
||||
let local_filter = if let Some(column) = filter_column {
|
||||
column.eval(vars.local_values)
|
||||
} else {
|
||||
P::ONES
|
||||
};
|
||||
let select = local_filter * combined + P::ONES - local_filter;
|
||||
|
||||
// Check value of `Z(g^(n-1))`
|
||||
consumer.constraint_last_row(*local_z - select);
|
||||
// Check `Z(w) = combination * Z(gw)`
|
||||
consumer.constraint_transition(*next_z * select - *local_z);
|
||||
}
|
||||
}
|
||||
|
||||
@ -620,11 +726,6 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit<
|
||||
} else {
|
||||
one
|
||||
};
|
||||
let next_filter = if let Some(column) = filter_column {
|
||||
column.eval_circuit(builder, vars.next_values)
|
||||
} else {
|
||||
one
|
||||
};
|
||||
fn select<F: RichField + Extendable<D>, const D: usize>(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
filter: ExtensionTarget<D>,
|
||||
@ -635,34 +736,30 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit<
|
||||
builder.mul_add_extension(filter, x, tmp) // filter * x + 1 - filter
|
||||
}
|
||||
|
||||
// Check value of `Z(1)`
|
||||
let local_columns_eval = columns
|
||||
let evals = columns
|
||||
.iter()
|
||||
.map(|c| c.eval_circuit(builder, vars.local_values))
|
||||
.map(|c| c.eval_with_next_circuit(builder, vars.local_values, vars.next_values))
|
||||
.collect::<Vec<_>>();
|
||||
let combined_local = challenges.combine_circuit(builder, &local_columns_eval);
|
||||
let selected_local = select(builder, local_filter, combined_local);
|
||||
let first_row = builder.sub_extension(*local_z, selected_local);
|
||||
consumer.constraint_first_row(builder, first_row);
|
||||
// Check `Z(gw) = combination * Z(w)`
|
||||
let next_columns_eval = columns
|
||||
.iter()
|
||||
.map(|c| c.eval_circuit(builder, vars.next_values))
|
||||
.collect::<Vec<_>>();
|
||||
let combined_next = challenges.combine_circuit(builder, &next_columns_eval);
|
||||
let selected_next = select(builder, next_filter, combined_next);
|
||||
let transition = builder.mul_sub_extension(*local_z, selected_next, *next_z);
|
||||
|
||||
let combined = challenges.combine_circuit(builder, &evals);
|
||||
let select = select(builder, local_filter, combined);
|
||||
|
||||
// Check value of `Z(g^(n-1))`
|
||||
let last_row = builder.sub_extension(*local_z, select);
|
||||
consumer.constraint_last_row(builder, last_row);
|
||||
// Check `Z(w) = combination * Z(gw)`
|
||||
let transition = builder.mul_sub_extension(*next_z, select, *local_z);
|
||||
consumer.constraint_transition(builder, transition);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn verify_cross_table_lookups<F: RichField + Extendable<D>, const D: usize>(
|
||||
cross_table_lookups: &[CrossTableLookup<F>],
|
||||
ctl_zs_lasts: [Vec<F>; NUM_TABLES],
|
||||
ctl_zs_first: [Vec<F>; NUM_TABLES],
|
||||
ctl_extra_looking_products: Vec<Vec<F>>,
|
||||
config: &StarkConfig,
|
||||
) -> Result<()> {
|
||||
let mut ctl_zs_openings = ctl_zs_lasts.iter().map(|v| v.iter()).collect::<Vec<_>>();
|
||||
let mut ctl_zs_openings = ctl_zs_first.iter().map(|v| v.iter()).collect::<Vec<_>>();
|
||||
for (
|
||||
index,
|
||||
CrossTableLookup {
|
||||
@ -695,11 +792,11 @@ pub(crate) fn verify_cross_table_lookups<F: RichField + Extendable<D>, const D:
|
||||
pub(crate) fn verify_cross_table_lookups_circuit<F: RichField + Extendable<D>, const D: usize>(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
cross_table_lookups: Vec<CrossTableLookup<F>>,
|
||||
ctl_zs_lasts: [Vec<Target>; NUM_TABLES],
|
||||
ctl_zs_first: [Vec<Target>; NUM_TABLES],
|
||||
ctl_extra_looking_products: Vec<Vec<Target>>,
|
||||
inner_config: &StarkConfig,
|
||||
) {
|
||||
let mut ctl_zs_openings = ctl_zs_lasts.iter().map(|v| v.iter()).collect::<Vec<_>>();
|
||||
let mut ctl_zs_openings = ctl_zs_first.iter().map(|v| v.iter()).collect::<Vec<_>>();
|
||||
for CrossTableLookup {
|
||||
looking_tables,
|
||||
looked_table,
|
||||
|
||||
@ -49,9 +49,8 @@ use crate::proof::{
|
||||
use crate::prover::prove;
|
||||
use crate::recursive_verifier::{
|
||||
add_common_recursion_gates, add_virtual_public_values,
|
||||
get_memory_extra_looking_products_circuit, recursive_stark_circuit, set_block_hashes_target,
|
||||
set_block_metadata_target, set_extra_public_values_target, set_public_value_targets,
|
||||
set_trie_roots_target, PlonkWrapperCircuit, PublicInputs, StarkWrapperCircuit,
|
||||
get_memory_extra_looking_products_circuit, recursive_stark_circuit, set_public_value_targets,
|
||||
PlonkWrapperCircuit, PublicInputs, StarkWrapperCircuit,
|
||||
};
|
||||
use crate::stark::Stark;
|
||||
use crate::util::h256_limbs;
|
||||
@ -528,7 +527,7 @@ where
|
||||
verify_cross_table_lookups_circuit::<F, D>(
|
||||
&mut builder,
|
||||
all_cross_table_lookups(),
|
||||
pis.map(|p| p.ctl_zs_last),
|
||||
pis.map(|p| p.ctl_zs_first),
|
||||
extra_looking_products,
|
||||
stark_config,
|
||||
);
|
||||
@ -907,7 +906,10 @@ where
|
||||
&mut root_inputs,
|
||||
&self.root.public_values,
|
||||
&all_proof.public_values,
|
||||
);
|
||||
)
|
||||
.map_err(|_| {
|
||||
anyhow::Error::msg("Invalid conversion when setting public values targets.")
|
||||
})?;
|
||||
|
||||
let root_proof = self.root.circuit.prove(root_inputs)?;
|
||||
|
||||
@ -941,32 +943,15 @@ where
|
||||
&self.aggregation.circuit.verifier_only,
|
||||
);
|
||||
|
||||
set_block_hashes_target(
|
||||
set_public_value_targets(
|
||||
&mut agg_inputs,
|
||||
&self.aggregation.public_values.block_hashes,
|
||||
&public_values.block_hashes,
|
||||
);
|
||||
set_block_metadata_target(
|
||||
&mut agg_inputs,
|
||||
&self.aggregation.public_values.block_metadata,
|
||||
&public_values.block_metadata,
|
||||
);
|
||||
&self.aggregation.public_values,
|
||||
&public_values,
|
||||
)
|
||||
.map_err(|_| {
|
||||
anyhow::Error::msg("Invalid conversion when setting public values targets.")
|
||||
})?;
|
||||
|
||||
set_trie_roots_target(
|
||||
&mut agg_inputs,
|
||||
&self.aggregation.public_values.trie_roots_before,
|
||||
&public_values.trie_roots_before,
|
||||
);
|
||||
set_trie_roots_target(
|
||||
&mut agg_inputs,
|
||||
&self.aggregation.public_values.trie_roots_after,
|
||||
&public_values.trie_roots_after,
|
||||
);
|
||||
set_extra_public_values_target(
|
||||
&mut agg_inputs,
|
||||
&self.aggregation.public_values.extra_block_data,
|
||||
&public_values.extra_block_data,
|
||||
);
|
||||
let aggregation_proof = self.aggregation.circuit.prove(agg_inputs)?;
|
||||
Ok((aggregation_proof, public_values))
|
||||
}
|
||||
@ -1024,32 +1009,10 @@ where
|
||||
block_inputs
|
||||
.set_verifier_data_target(&self.block.cyclic_vk, &self.block.circuit.verifier_only);
|
||||
|
||||
set_block_hashes_target(
|
||||
&mut block_inputs,
|
||||
&self.block.public_values.block_hashes,
|
||||
&public_values.block_hashes,
|
||||
);
|
||||
set_extra_public_values_target(
|
||||
&mut block_inputs,
|
||||
&self.block.public_values.extra_block_data,
|
||||
&public_values.extra_block_data,
|
||||
);
|
||||
set_block_metadata_target(
|
||||
&mut block_inputs,
|
||||
&self.block.public_values.block_metadata,
|
||||
&public_values.block_metadata,
|
||||
);
|
||||
|
||||
set_trie_roots_target(
|
||||
&mut block_inputs,
|
||||
&self.block.public_values.trie_roots_before,
|
||||
&public_values.trie_roots_before,
|
||||
);
|
||||
set_trie_roots_target(
|
||||
&mut block_inputs,
|
||||
&self.block.public_values.trie_roots_after,
|
||||
&public_values.trie_roots_after,
|
||||
);
|
||||
set_public_value_targets(&mut block_inputs, &self.block.public_values, &public_values)
|
||||
.map_err(|_| {
|
||||
anyhow::Error::msg("Invalid conversion when setting public values targets.")
|
||||
})?;
|
||||
|
||||
let block_proof = self.block.circuit.prove(block_inputs)?;
|
||||
Ok((block_proof, public_values))
|
||||
|
||||
@ -116,7 +116,7 @@ impl<F: Field> GenerationState<F> {
|
||||
let code = self.memory.contexts[ctx].segments[Segment::Returndata as usize].content
|
||||
[..returndata_size]
|
||||
.iter()
|
||||
.map(|x| x.as_u32() as u8)
|
||||
.map(|x| x.low_u32() as u8)
|
||||
.collect::<Vec<_>>();
|
||||
debug_assert_eq!(keccak(&code), codehash);
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ pub(crate) struct AccountTrieRecord {
|
||||
|
||||
pub(crate) fn read_state_trie_value(slice: &[U256]) -> AccountTrieRecord {
|
||||
AccountTrieRecord {
|
||||
nonce: slice[0].as_u64(),
|
||||
nonce: slice[0].low_u64(),
|
||||
balance: slice[1],
|
||||
storage_ptr: slice[2].as_usize(),
|
||||
code_hash: H256::from_uint(&slice[3]),
|
||||
|
||||
@ -10,7 +10,8 @@ use crate::all_stark::{AllStark, NUM_TABLES};
|
||||
use crate::config::StarkConfig;
|
||||
use crate::cross_table_lookup::get_grand_product_challenge_set;
|
||||
use crate::proof::*;
|
||||
use crate::util::{h256_limbs, u256_limbs};
|
||||
use crate::util::{h256_limbs, u256_limbs, u256_to_u32, u256_to_u64};
|
||||
use crate::witness::errors::ProgramError;
|
||||
|
||||
fn observe_root<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
|
||||
challenger: &mut Challenger<F, C::Hasher>,
|
||||
@ -53,35 +54,24 @@ fn observe_block_metadata<
|
||||
>(
|
||||
challenger: &mut Challenger<F, C::Hasher>,
|
||||
block_metadata: &BlockMetadata,
|
||||
) {
|
||||
) -> Result<(), ProgramError> {
|
||||
challenger.observe_elements(
|
||||
&u256_limbs::<F>(U256::from_big_endian(&block_metadata.block_beneficiary.0))[..5],
|
||||
);
|
||||
challenger.observe_element(F::from_canonical_u32(
|
||||
block_metadata.block_timestamp.as_u32(),
|
||||
));
|
||||
challenger.observe_element(F::from_canonical_u32(block_metadata.block_number.as_u32()));
|
||||
challenger.observe_element(F::from_canonical_u32(
|
||||
block_metadata.block_difficulty.as_u32(),
|
||||
));
|
||||
challenger.observe_element(F::from_canonical_u32(
|
||||
block_metadata.block_gaslimit.as_u32(),
|
||||
));
|
||||
challenger.observe_element(F::from_canonical_u32(
|
||||
block_metadata.block_chain_id.as_u32(),
|
||||
));
|
||||
challenger.observe_element(F::from_canonical_u32(
|
||||
block_metadata.block_base_fee.as_u64() as u32,
|
||||
));
|
||||
challenger.observe_element(F::from_canonical_u32(
|
||||
(block_metadata.block_base_fee.as_u64() >> 32) as u32,
|
||||
));
|
||||
challenger.observe_element(F::from_canonical_u32(
|
||||
block_metadata.block_gas_used.as_u32(),
|
||||
));
|
||||
challenger.observe_element(u256_to_u32(block_metadata.block_timestamp)?);
|
||||
challenger.observe_element(u256_to_u32(block_metadata.block_number)?);
|
||||
challenger.observe_element(u256_to_u32(block_metadata.block_difficulty)?);
|
||||
challenger.observe_element(u256_to_u32(block_metadata.block_gaslimit)?);
|
||||
challenger.observe_element(u256_to_u32(block_metadata.block_chain_id)?);
|
||||
let basefee = u256_to_u64(block_metadata.block_base_fee)?;
|
||||
challenger.observe_element(basefee.0);
|
||||
challenger.observe_element(basefee.1);
|
||||
challenger.observe_element(u256_to_u32(block_metadata.block_gas_used)?);
|
||||
for i in 0..8 {
|
||||
challenger.observe_elements(&u256_limbs(block_metadata.block_bloom[i]));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn observe_block_metadata_target<
|
||||
@ -112,17 +102,19 @@ fn observe_extra_block_data<
|
||||
>(
|
||||
challenger: &mut Challenger<F, C::Hasher>,
|
||||
extra_data: &ExtraBlockData,
|
||||
) {
|
||||
challenger.observe_element(F::from_canonical_u32(extra_data.txn_number_before.as_u32()));
|
||||
challenger.observe_element(F::from_canonical_u32(extra_data.txn_number_after.as_u32()));
|
||||
challenger.observe_element(F::from_canonical_u32(extra_data.gas_used_before.as_u32()));
|
||||
challenger.observe_element(F::from_canonical_u32(extra_data.gas_used_after.as_u32()));
|
||||
) -> Result<(), ProgramError> {
|
||||
challenger.observe_element(u256_to_u32(extra_data.txn_number_before)?);
|
||||
challenger.observe_element(u256_to_u32(extra_data.txn_number_after)?);
|
||||
challenger.observe_element(u256_to_u32(extra_data.gas_used_before)?);
|
||||
challenger.observe_element(u256_to_u32(extra_data.gas_used_after)?);
|
||||
for i in 0..8 {
|
||||
challenger.observe_elements(&u256_limbs(extra_data.block_bloom_before[i]));
|
||||
}
|
||||
for i in 0..8 {
|
||||
challenger.observe_elements(&u256_limbs(extra_data.block_bloom_after[i]));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn observe_extra_block_data_target<
|
||||
@ -178,12 +170,12 @@ pub(crate) fn observe_public_values<
|
||||
>(
|
||||
challenger: &mut Challenger<F, C::Hasher>,
|
||||
public_values: &PublicValues,
|
||||
) {
|
||||
) -> Result<(), ProgramError> {
|
||||
observe_trie_roots::<F, C, D>(challenger, &public_values.trie_roots_before);
|
||||
observe_trie_roots::<F, C, D>(challenger, &public_values.trie_roots_after);
|
||||
observe_block_metadata::<F, C, D>(challenger, &public_values.block_metadata);
|
||||
observe_block_metadata::<F, C, D>(challenger, &public_values.block_metadata)?;
|
||||
observe_block_hashes::<F, C, D>(challenger, &public_values.block_hashes);
|
||||
observe_extra_block_data::<F, C, D>(challenger, &public_values.extra_block_data);
|
||||
observe_extra_block_data::<F, C, D>(challenger, &public_values.extra_block_data)
|
||||
}
|
||||
|
||||
pub(crate) fn observe_public_values_target<
|
||||
@ -205,19 +197,22 @@ pub(crate) fn observe_public_values_target<
|
||||
|
||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> AllProof<F, C, D> {
|
||||
/// Computes all Fiat-Shamir challenges used in the STARK proof.
|
||||
pub(crate) fn get_challenges(&self, config: &StarkConfig) -> AllProofChallenges<F, D> {
|
||||
pub(crate) fn get_challenges(
|
||||
&self,
|
||||
config: &StarkConfig,
|
||||
) -> Result<AllProofChallenges<F, D>, ProgramError> {
|
||||
let mut challenger = Challenger::<F, C::Hasher>::new();
|
||||
|
||||
for proof in &self.stark_proofs {
|
||||
challenger.observe_cap(&proof.proof.trace_cap);
|
||||
}
|
||||
|
||||
observe_public_values::<F, C, D>(&mut challenger, &self.public_values);
|
||||
observe_public_values::<F, C, D>(&mut challenger, &self.public_values)?;
|
||||
|
||||
let ctl_challenges =
|
||||
get_grand_product_challenge_set(&mut challenger, config.num_challenges);
|
||||
|
||||
AllProofChallenges {
|
||||
Ok(AllProofChallenges {
|
||||
stark_challenges: core::array::from_fn(|i| {
|
||||
challenger.compact();
|
||||
self.stark_proofs[i]
|
||||
@ -225,7 +220,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> A
|
||||
.get_challenges(&mut challenger, config)
|
||||
}),
|
||||
ctl_challenges,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(unused)] // TODO: should be used soon
|
||||
|
||||
@ -623,7 +623,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> S
|
||||
}
|
||||
|
||||
pub fn num_ctl_zs(&self) -> usize {
|
||||
self.openings.ctl_zs_last.len()
|
||||
self.openings.ctl_zs_first.len()
|
||||
}
|
||||
}
|
||||
|
||||
@ -700,8 +700,8 @@ pub struct StarkOpeningSet<F: RichField + Extendable<D>, const D: usize> {
|
||||
pub auxiliary_polys: Vec<F::Extension>,
|
||||
/// Openings of lookups and cross-table lookups `Z` polynomials at `g * zeta`.
|
||||
pub auxiliary_polys_next: Vec<F::Extension>,
|
||||
/// Openings of cross-table lookups `Z` polynomials at `g^-1`.
|
||||
pub ctl_zs_last: Vec<F>,
|
||||
/// Openings of cross-table lookups `Z` polynomials at `1`.
|
||||
pub ctl_zs_first: Vec<F>,
|
||||
/// Openings of quotient polynomials at `zeta`.
|
||||
pub quotient_polys: Vec<F::Extension>,
|
||||
}
|
||||
@ -713,7 +713,6 @@ impl<F: RichField + Extendable<D>, const D: usize> StarkOpeningSet<F, D> {
|
||||
trace_commitment: &PolynomialBatch<F, C, D>,
|
||||
auxiliary_polys_commitment: &PolynomialBatch<F, C, D>,
|
||||
quotient_commitment: &PolynomialBatch<F, C, D>,
|
||||
degree_bits: usize,
|
||||
num_lookup_columns: usize,
|
||||
) -> Self {
|
||||
let eval_commitment = |z: F::Extension, c: &PolynomialBatch<F, C, D>| {
|
||||
@ -734,10 +733,8 @@ impl<F: RichField + Extendable<D>, const D: usize> StarkOpeningSet<F, D> {
|
||||
next_values: eval_commitment(zeta_next, trace_commitment),
|
||||
auxiliary_polys: eval_commitment(zeta, auxiliary_polys_commitment),
|
||||
auxiliary_polys_next: eval_commitment(zeta_next, auxiliary_polys_commitment),
|
||||
ctl_zs_last: eval_commitment_base(
|
||||
F::primitive_root_of_unity(degree_bits).inverse(),
|
||||
auxiliary_polys_commitment,
|
||||
)[num_lookup_columns..]
|
||||
ctl_zs_first: eval_commitment_base(F::ONE, auxiliary_polys_commitment)
|
||||
[num_lookup_columns..]
|
||||
.to_vec(),
|
||||
quotient_polys: eval_commitment(zeta, quotient_commitment),
|
||||
}
|
||||
@ -761,10 +758,10 @@ impl<F: RichField + Extendable<D>, const D: usize> StarkOpeningSet<F, D> {
|
||||
.copied()
|
||||
.collect_vec(),
|
||||
};
|
||||
debug_assert!(!self.ctl_zs_last.is_empty());
|
||||
let ctl_last_batch = FriOpeningBatch {
|
||||
debug_assert!(!self.ctl_zs_first.is_empty());
|
||||
let ctl_first_batch = FriOpeningBatch {
|
||||
values: self
|
||||
.ctl_zs_last
|
||||
.ctl_zs_first
|
||||
.iter()
|
||||
.copied()
|
||||
.map(F::Extension::from_basefield)
|
||||
@ -772,7 +769,7 @@ impl<F: RichField + Extendable<D>, const D: usize> StarkOpeningSet<F, D> {
|
||||
};
|
||||
|
||||
FriOpenings {
|
||||
batches: vec![zeta_batch, zeta_next_batch, ctl_last_batch],
|
||||
batches: vec![zeta_batch, zeta_next_batch, ctl_first_batch],
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -783,7 +780,7 @@ pub struct StarkOpeningSetTarget<const D: usize> {
|
||||
pub next_values: Vec<ExtensionTarget<D>>,
|
||||
pub auxiliary_polys: Vec<ExtensionTarget<D>>,
|
||||
pub auxiliary_polys_next: Vec<ExtensionTarget<D>>,
|
||||
pub ctl_zs_last: Vec<Target>,
|
||||
pub ctl_zs_first: Vec<Target>,
|
||||
pub quotient_polys: Vec<ExtensionTarget<D>>,
|
||||
}
|
||||
|
||||
@ -793,7 +790,7 @@ impl<const D: usize> StarkOpeningSetTarget<D> {
|
||||
buffer.write_target_ext_vec(&self.next_values)?;
|
||||
buffer.write_target_ext_vec(&self.auxiliary_polys)?;
|
||||
buffer.write_target_ext_vec(&self.auxiliary_polys_next)?;
|
||||
buffer.write_target_vec(&self.ctl_zs_last)?;
|
||||
buffer.write_target_vec(&self.ctl_zs_first)?;
|
||||
buffer.write_target_ext_vec(&self.quotient_polys)?;
|
||||
Ok(())
|
||||
}
|
||||
@ -803,7 +800,7 @@ impl<const D: usize> StarkOpeningSetTarget<D> {
|
||||
let next_values = buffer.read_target_ext_vec::<D>()?;
|
||||
let auxiliary_polys = buffer.read_target_ext_vec::<D>()?;
|
||||
let auxiliary_polys_next = buffer.read_target_ext_vec::<D>()?;
|
||||
let ctl_zs_last = buffer.read_target_vec()?;
|
||||
let ctl_zs_first = buffer.read_target_vec()?;
|
||||
let quotient_polys = buffer.read_target_ext_vec::<D>()?;
|
||||
|
||||
Ok(Self {
|
||||
@ -811,7 +808,7 @@ impl<const D: usize> StarkOpeningSetTarget<D> {
|
||||
next_values,
|
||||
auxiliary_polys,
|
||||
auxiliary_polys_next,
|
||||
ctl_zs_last,
|
||||
ctl_zs_first,
|
||||
quotient_polys,
|
||||
})
|
||||
}
|
||||
@ -834,10 +831,10 @@ impl<const D: usize> StarkOpeningSetTarget<D> {
|
||||
.copied()
|
||||
.collect_vec(),
|
||||
};
|
||||
debug_assert!(!self.ctl_zs_last.is_empty());
|
||||
let ctl_last_batch = FriOpeningBatchTarget {
|
||||
debug_assert!(!self.ctl_zs_first.is_empty());
|
||||
let ctl_first_batch = FriOpeningBatchTarget {
|
||||
values: self
|
||||
.ctl_zs_last
|
||||
.ctl_zs_first
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|t| t.to_ext_target(zero))
|
||||
@ -845,7 +842,7 @@ impl<const D: usize> StarkOpeningSetTarget<D> {
|
||||
};
|
||||
|
||||
FriOpeningsTarget {
|
||||
batches: vec![zeta_batch, zeta_next_batch, ctl_last_batch],
|
||||
batches: vec![zeta_batch, zeta_next_batch, ctl_first_batch],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,7 +150,8 @@ where
|
||||
challenger.observe_cap(cap);
|
||||
}
|
||||
|
||||
observe_public_values::<F, C, D>(&mut challenger, &public_values);
|
||||
observe_public_values::<F, C, D>(&mut challenger, &public_values)
|
||||
.map_err(|_| anyhow::Error::msg("Invalid conversion of public values."))?;
|
||||
|
||||
let ctl_challenges = get_grand_product_challenge_set(&mut challenger, config.num_challenges);
|
||||
let ctl_data_per_table = timed!(
|
||||
@ -476,7 +477,6 @@ where
|
||||
trace_commitment,
|
||||
&auxiliary_polys_commitment,
|
||||
"ient_commitment,
|
||||
degree_bits,
|
||||
stark.num_lookup_helper_columns(config),
|
||||
);
|
||||
challenger.observe_openings(&openings.to_fri_openings());
|
||||
@ -491,7 +491,7 @@ where
|
||||
timing,
|
||||
"compute openings proof",
|
||||
PolynomialBatch::prove_openings(
|
||||
&stark.fri_instance(zeta, g, degree_bits, ctl_data.len(), config),
|
||||
&stark.fri_instance(zeta, g, ctl_data.len(), config),
|
||||
&initial_merkle_trees,
|
||||
challenger,
|
||||
&fri_params,
|
||||
|
||||
@ -43,9 +43,10 @@ use crate::proof::{
|
||||
TrieRootsTarget,
|
||||
};
|
||||
use crate::stark::Stark;
|
||||
use crate::util::{h256_limbs, u256_limbs};
|
||||
use crate::util::{h256_limbs, u256_limbs, u256_to_u32, u256_to_u64};
|
||||
use crate::vanishing_poly::eval_vanishing_poly_circuit;
|
||||
use crate::vars::StarkEvaluationTargets;
|
||||
use crate::witness::errors::ProgramError;
|
||||
|
||||
/// Table-wise recursive proofs of an `AllProof`.
|
||||
pub struct RecursiveAllProof<
|
||||
@ -59,7 +60,7 @@ pub struct RecursiveAllProof<
|
||||
pub(crate) struct PublicInputs<T: Copy + Default + Eq + PartialEq + Debug, P: PlonkyPermutation<T>>
|
||||
{
|
||||
pub(crate) trace_cap: Vec<Vec<T>>,
|
||||
pub(crate) ctl_zs_last: Vec<T>,
|
||||
pub(crate) ctl_zs_first: Vec<T>,
|
||||
pub(crate) ctl_challenges: GrandProductChallengeSet<T>,
|
||||
pub(crate) challenger_state_before: P,
|
||||
pub(crate) challenger_state_after: P,
|
||||
@ -85,11 +86,11 @@ impl<T: Copy + Debug + Default + Eq + PartialEq, P: PlonkyPermutation<T>> Public
|
||||
};
|
||||
let challenger_state_before = P::new(&mut iter);
|
||||
let challenger_state_after = P::new(&mut iter);
|
||||
let ctl_zs_last: Vec<_> = iter.collect();
|
||||
let ctl_zs_first: Vec<_> = iter.collect();
|
||||
|
||||
Self {
|
||||
trace_cap,
|
||||
ctl_zs_last,
|
||||
ctl_zs_first,
|
||||
ctl_challenges,
|
||||
challenger_state_before,
|
||||
challenger_state_after,
|
||||
@ -150,7 +151,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
||||
// Verify the CTL checks.
|
||||
verify_cross_table_lookups::<F, D>(
|
||||
&cross_table_lookups,
|
||||
pis.map(|p| p.ctl_zs_last),
|
||||
pis.map(|p| p.ctl_zs_first),
|
||||
extra_looking_products,
|
||||
inner_config,
|
||||
)?;
|
||||
@ -344,7 +345,7 @@ where
|
||||
let challenger_state = challenger.compact(&mut builder);
|
||||
builder.register_public_inputs(challenger_state.as_ref());
|
||||
|
||||
builder.register_public_inputs(&proof_target.openings.ctl_zs_last);
|
||||
builder.register_public_inputs(&proof_target.openings.ctl_zs_first);
|
||||
|
||||
verify_stark_proof_with_challenges_circuit::<F, C, _, D>(
|
||||
&mut builder,
|
||||
@ -410,7 +411,7 @@ fn verify_stark_proof_with_challenges_circuit<
|
||||
next_values,
|
||||
auxiliary_polys,
|
||||
auxiliary_polys_next,
|
||||
ctl_zs_last,
|
||||
ctl_zs_first,
|
||||
quotient_polys,
|
||||
} = &proof.openings;
|
||||
let vars = StarkEvaluationTargets {
|
||||
@ -485,8 +486,7 @@ fn verify_stark_proof_with_challenges_circuit<
|
||||
builder,
|
||||
challenges.stark_zeta,
|
||||
F::primitive_root_of_unity(degree_bits),
|
||||
degree_bits,
|
||||
ctl_zs_last.len(),
|
||||
ctl_zs_first.len(),
|
||||
inner_config,
|
||||
);
|
||||
builder.verify_fri_proof::<C>(
|
||||
@ -870,7 +870,7 @@ fn add_virtual_stark_opening_set<F: RichField + Extendable<D>, S: Stark<F, D>, c
|
||||
.add_virtual_extension_targets(stark.num_lookup_helper_columns(config) + num_ctl_zs),
|
||||
auxiliary_polys_next: builder
|
||||
.add_virtual_extension_targets(stark.num_lookup_helper_columns(config) + num_ctl_zs),
|
||||
ctl_zs_last: builder.add_virtual_targets(num_ctl_zs),
|
||||
ctl_zs_first: builder.add_virtual_targets(num_ctl_zs),
|
||||
quotient_polys: builder
|
||||
.add_virtual_extension_targets(stark.quotient_degree_factor() * num_challenges),
|
||||
}
|
||||
@ -906,7 +906,8 @@ pub(crate) fn set_public_value_targets<F, W, const D: usize>(
|
||||
witness: &mut W,
|
||||
public_values_target: &PublicValuesTarget,
|
||||
public_values: &PublicValues,
|
||||
) where
|
||||
) -> Result<(), ProgramError>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
W: Witness<F>,
|
||||
{
|
||||
@ -924,7 +925,7 @@ pub(crate) fn set_public_value_targets<F, W, const D: usize>(
|
||||
witness,
|
||||
&public_values_target.block_metadata,
|
||||
&public_values.block_metadata,
|
||||
);
|
||||
)?;
|
||||
set_block_hashes_target(
|
||||
witness,
|
||||
&public_values_target.block_hashes,
|
||||
@ -935,6 +936,8 @@ pub(crate) fn set_public_value_targets<F, W, const D: usize>(
|
||||
&public_values_target.extra_block_data,
|
||||
&public_values.extra_block_data,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn set_trie_roots_target<F, W, const D: usize>(
|
||||
@ -995,7 +998,8 @@ pub(crate) fn set_block_metadata_target<F, W, const D: usize>(
|
||||
witness: &mut W,
|
||||
block_metadata_target: &BlockMetadataTarget,
|
||||
block_metadata: &BlockMetadata,
|
||||
) where
|
||||
) -> Result<(), ProgramError>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
W: Witness<F>,
|
||||
{
|
||||
@ -1006,42 +1010,39 @@ pub(crate) fn set_block_metadata_target<F, W, const D: usize>(
|
||||
witness.set_target_arr(&block_metadata_target.block_beneficiary, &beneficiary_limbs);
|
||||
witness.set_target(
|
||||
block_metadata_target.block_timestamp,
|
||||
F::from_canonical_u32(block_metadata.block_timestamp.as_u32()),
|
||||
u256_to_u32(block_metadata.block_timestamp)?,
|
||||
);
|
||||
witness.set_target(
|
||||
block_metadata_target.block_number,
|
||||
F::from_canonical_u32(block_metadata.block_number.as_u32()),
|
||||
u256_to_u32(block_metadata.block_number)?,
|
||||
);
|
||||
witness.set_target(
|
||||
block_metadata_target.block_difficulty,
|
||||
F::from_canonical_u32(block_metadata.block_difficulty.as_u32()),
|
||||
u256_to_u32(block_metadata.block_difficulty)?,
|
||||
);
|
||||
witness.set_target(
|
||||
block_metadata_target.block_gaslimit,
|
||||
F::from_canonical_u32(block_metadata.block_gaslimit.as_u32()),
|
||||
u256_to_u32(block_metadata.block_gaslimit)?,
|
||||
);
|
||||
witness.set_target(
|
||||
block_metadata_target.block_chain_id,
|
||||
F::from_canonical_u32(block_metadata.block_chain_id.as_u32()),
|
||||
u256_to_u32(block_metadata.block_chain_id)?,
|
||||
);
|
||||
// Basefee fits in 2 limbs
|
||||
witness.set_target(
|
||||
block_metadata_target.block_base_fee[0],
|
||||
F::from_canonical_u32(block_metadata.block_base_fee.as_u64() as u32),
|
||||
);
|
||||
witness.set_target(
|
||||
block_metadata_target.block_base_fee[1],
|
||||
F::from_canonical_u32((block_metadata.block_base_fee.as_u64() >> 32) as u32),
|
||||
);
|
||||
let basefee = u256_to_u64(block_metadata.block_base_fee)?;
|
||||
witness.set_target(block_metadata_target.block_base_fee[0], basefee.0);
|
||||
witness.set_target(block_metadata_target.block_base_fee[1], basefee.1);
|
||||
witness.set_target(
|
||||
block_metadata_target.block_gas_used,
|
||||
F::from_canonical_u64(block_metadata.block_gas_used.as_u64()),
|
||||
u256_to_u32(block_metadata.block_gas_used)?,
|
||||
);
|
||||
let mut block_bloom_limbs = [F::ZERO; 64];
|
||||
for (i, limbs) in block_bloom_limbs.chunks_exact_mut(8).enumerate() {
|
||||
limbs.copy_from_slice(&u256_limbs(block_metadata.block_bloom[i]));
|
||||
}
|
||||
witness.set_target_arr(&block_metadata_target.block_bloom, &block_bloom_limbs);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn set_block_hashes_target<F, W, const D: usize>(
|
||||
|
||||
@ -83,7 +83,6 @@ pub trait Stark<F: RichField + Extendable<D>, const D: usize>: Sync {
|
||||
&self,
|
||||
zeta: F::Extension,
|
||||
g: F,
|
||||
degree_bits: usize,
|
||||
num_ctl_zs: usize,
|
||||
config: &StarkConfig,
|
||||
) -> FriInstanceInfo<F, D> {
|
||||
@ -128,13 +127,13 @@ pub trait Stark<F: RichField + Extendable<D>, const D: usize>: Sync {
|
||||
point: zeta.scalar_mul(g),
|
||||
polynomials: [trace_info, auxiliary_polys_info].concat(),
|
||||
};
|
||||
let ctl_last_batch = FriBatchInfo {
|
||||
point: F::Extension::primitive_root_of_unity(degree_bits).inverse(),
|
||||
let ctl_first_batch = FriBatchInfo {
|
||||
point: F::Extension::ONE,
|
||||
polynomials: ctl_zs_info,
|
||||
};
|
||||
FriInstanceInfo {
|
||||
oracles: vec![trace_oracle, auxiliary_oracle, quotient_oracle],
|
||||
batches: vec![zeta_batch, zeta_next_batch, ctl_last_batch],
|
||||
batches: vec![zeta_batch, zeta_next_batch, ctl_first_batch],
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,7 +143,6 @@ pub trait Stark<F: RichField + Extendable<D>, const D: usize>: Sync {
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
zeta: ExtensionTarget<D>,
|
||||
g: F,
|
||||
degree_bits: usize,
|
||||
num_ctl_zs: usize,
|
||||
inner_config: &StarkConfig,
|
||||
) -> FriInstanceInfoTarget<D> {
|
||||
@ -190,14 +188,13 @@ pub trait Stark<F: RichField + Extendable<D>, const D: usize>: Sync {
|
||||
point: zeta_next,
|
||||
polynomials: [trace_info, auxiliary_polys_info].concat(),
|
||||
};
|
||||
let ctl_last_batch = FriBatchInfoTarget {
|
||||
point: builder
|
||||
.constant_extension(F::Extension::primitive_root_of_unity(degree_bits).inverse()),
|
||||
let ctl_first_batch = FriBatchInfoTarget {
|
||||
point: builder.one_extension(),
|
||||
polynomials: ctl_zs_info,
|
||||
};
|
||||
FriInstanceInfoTarget {
|
||||
oracles: vec![trace_oracle, auxiliary_oracle, quotient_oracle],
|
||||
batches: vec![zeta_batch, zeta_next_batch, ctl_last_batch],
|
||||
batches: vec![zeta_batch, zeta_next_batch, ctl_first_batch],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,8 @@ use plonky2::hash::hash_types::RichField;
|
||||
use plonky2::iop::ext_target::ExtensionTarget;
|
||||
use plonky2::util::transpose;
|
||||
|
||||
use crate::witness::errors::ProgramError;
|
||||
|
||||
/// Construct an integer from its constituent bits (in little-endian order)
|
||||
pub fn limb_from_bits_le<P: PackedField>(iter: impl IntoIterator<Item = P>) -> P {
|
||||
// TODO: This is technically wrong, as 1 << i won't be canonical for all fields...
|
||||
@ -45,6 +47,29 @@ pub fn trace_rows_to_poly_values<F: Field, const COLUMNS: usize>(
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Returns the lowest LE 32-bit limb of a `U256` as a field element,
|
||||
/// and errors if the integer is actually greater.
|
||||
pub(crate) fn u256_to_u32<F: Field>(u256: U256) -> Result<F, ProgramError> {
|
||||
if TryInto::<u32>::try_into(u256).is_err() {
|
||||
return Err(ProgramError::IntegerTooLarge);
|
||||
}
|
||||
|
||||
Ok(F::from_canonical_u32(u256.low_u32()))
|
||||
}
|
||||
|
||||
/// Returns the lowest LE 64-bit word of a `U256` as two field elements
|
||||
/// each storing a 32-bit limb, and errors if the integer is actually greater.
|
||||
pub(crate) fn u256_to_u64<F: Field>(u256: U256) -> Result<(F, F), ProgramError> {
|
||||
if TryInto::<u64>::try_into(u256).is_err() {
|
||||
return Err(ProgramError::IntegerTooLarge);
|
||||
}
|
||||
|
||||
Ok((
|
||||
F::from_canonical_u32(u256.low_u64() as u32),
|
||||
F::from_canonical_u32((u256.low_u64() >> 32) as u32),
|
||||
))
|
||||
}
|
||||
|
||||
#[allow(unused)] // TODO: Remove?
|
||||
/// Returns the 32-bit little-endian limbs of a `U256`.
|
||||
pub(crate) fn u256_limbs<F: Field>(u256: U256) -> [F; 8] {
|
||||
|
||||
@ -52,7 +52,9 @@ where
|
||||
let AllProofChallenges {
|
||||
stark_challenges,
|
||||
ctl_challenges,
|
||||
} = all_proof.get_challenges(config);
|
||||
} = all_proof
|
||||
.get_challenges(config)
|
||||
.map_err(|_| anyhow::Error::msg("Invalid sampling of proof challenges."))?;
|
||||
|
||||
let num_lookup_columns = all_stark.num_lookups_helper_columns(config);
|
||||
|
||||
@ -144,7 +146,9 @@ where
|
||||
|
||||
verify_cross_table_lookups::<F, D>(
|
||||
cross_table_lookups,
|
||||
all_proof.stark_proofs.map(|p| p.proof.openings.ctl_zs_last),
|
||||
all_proof
|
||||
.stark_proofs
|
||||
.map(|p| p.proof.openings.ctl_zs_first),
|
||||
extra_looking_products,
|
||||
config,
|
||||
)
|
||||
@ -318,7 +322,7 @@ where
|
||||
next_values,
|
||||
auxiliary_polys,
|
||||
auxiliary_polys_next,
|
||||
ctl_zs_last,
|
||||
ctl_zs_first,
|
||||
quotient_polys,
|
||||
} = &proof.openings;
|
||||
let vars = StarkEvaluationVars {
|
||||
@ -393,8 +397,7 @@ where
|
||||
&stark.fri_instance(
|
||||
challenges.stark_zeta,
|
||||
F::primitive_root_of_unity(degree_bits),
|
||||
degree_bits,
|
||||
ctl_zs_last.len(),
|
||||
ctl_zs_first.len(),
|
||||
config,
|
||||
),
|
||||
&proof.openings.to_fri_openings(),
|
||||
@ -434,7 +437,7 @@ where
|
||||
next_values,
|
||||
auxiliary_polys,
|
||||
auxiliary_polys_next,
|
||||
ctl_zs_last,
|
||||
ctl_zs_first,
|
||||
quotient_polys,
|
||||
} = openings;
|
||||
|
||||
@ -451,7 +454,7 @@ where
|
||||
ensure!(next_values.len() == S::COLUMNS);
|
||||
ensure!(auxiliary_polys.len() == num_auxiliary);
|
||||
ensure!(auxiliary_polys_next.len() == num_auxiliary);
|
||||
ensure!(ctl_zs_last.len() == num_ctl_zs);
|
||||
ensure!(ctl_zs_first.len() == num_ctl_zs);
|
||||
ensure!(quotient_polys.len() == stark.num_quotient_polys(config));
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -13,6 +13,7 @@ pub enum ProgramError {
|
||||
MemoryError(MemoryError),
|
||||
GasLimitError,
|
||||
InterpreterError,
|
||||
IntegerTooLarge,
|
||||
}
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
mod errors;
|
||||
pub(crate) mod errors;
|
||||
mod gas;
|
||||
pub(crate) mod memory;
|
||||
mod operation;
|
||||
|
||||
@ -137,7 +137,7 @@ pub(crate) fn generate_keccak_general<F: Field>(
|
||||
..base_address
|
||||
};
|
||||
let val = state.memory.get(address);
|
||||
val.as_u32() as u8
|
||||
val.low_u32() as u8
|
||||
})
|
||||
.collect_vec();
|
||||
log::debug!("Hashing {:?}", input);
|
||||
@ -375,7 +375,7 @@ pub(crate) fn generate_push<F: Field>(
|
||||
Segment::Code,
|
||||
initial_offset + i,
|
||||
))
|
||||
.as_u32() as u8
|
||||
.low_u32() as u8
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
@ -709,7 +709,7 @@ pub(crate) fn generate_mload_32bytes<F: Field>(
|
||||
..base_address
|
||||
};
|
||||
let val = state.memory.get(address);
|
||||
val.as_u32() as u8
|
||||
val.low_u32() as u8
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
|
||||
@ -12,7 +12,9 @@ pub mod gadgets;
|
||||
pub mod gates;
|
||||
pub mod hash;
|
||||
pub mod iop;
|
||||
pub mod lookup_test;
|
||||
pub mod plonk;
|
||||
pub mod recursion;
|
||||
pub mod util;
|
||||
|
||||
#[cfg(test)]
|
||||
mod lookup_test;
|
||||
|
||||
@ -1,523 +1,477 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
static LOGGER_INITIALIZED: Once = Once::new();
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use std::sync::Once;
|
||||
|
||||
use itertools::Itertools;
|
||||
use log::{Level, LevelFilter};
|
||||
|
||||
use crate::gadgets::lookup::{OTHER_TABLE, SMALLER_TABLE, TIP5_TABLE};
|
||||
use crate::gates::lookup_table::LookupTable;
|
||||
use crate::gates::noop::NoopGate;
|
||||
use crate::plonk::prover::prove;
|
||||
use crate::util::timing::TimingTree;
|
||||
|
||||
#[test]
|
||||
fn test_no_lookup() -> anyhow::Result<()> {
|
||||
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
|
||||
use crate::iop::witness::PartialWitness;
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
builder.add_gate(NoopGate, vec![]);
|
||||
let pw = PartialWitness::new();
|
||||
|
||||
let data = builder.build::<C>();
|
||||
let mut timing = TimingTree::new("prove first", Level::Debug);
|
||||
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
|
||||
timing.print();
|
||||
data.verify(proof)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn test_lookup_table_not_used() {
|
||||
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let tip5_table = TIP5_TABLE.to_vec();
|
||||
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
|
||||
builder.add_lookup_table_from_pairs(table);
|
||||
|
||||
builder.build::<C>();
|
||||
}
|
||||
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn test_lookup_without_table() {
|
||||
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let dummy = builder.add_virtual_target();
|
||||
builder.add_lookup_from_index(dummy, 0);
|
||||
|
||||
builder.build::<C>();
|
||||
}
|
||||
|
||||
// Tests two lookups in one lookup table.
|
||||
#[test]
|
||||
fn test_one_lookup() -> anyhow::Result<()> {
|
||||
use crate::field::types::Field;
|
||||
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
|
||||
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
|
||||
let tip5_table = TIP5_TABLE.to_vec();
|
||||
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let initial_a = builder.add_virtual_target();
|
||||
let initial_b = builder.add_virtual_target();
|
||||
|
||||
let look_val_a = 1;
|
||||
let look_val_b = 2;
|
||||
|
||||
let out_a = table[look_val_a].1;
|
||||
let out_b = table[look_val_b].1;
|
||||
let table_index = builder.add_lookup_table_from_pairs(table);
|
||||
let output_a = builder.add_lookup_from_index(initial_a, table_index);
|
||||
|
||||
let output_b = builder.add_lookup_from_index(initial_b, table_index);
|
||||
|
||||
builder.register_public_input(initial_a);
|
||||
builder.register_public_input(initial_b);
|
||||
builder.register_public_input(output_a);
|
||||
builder.register_public_input(output_b);
|
||||
|
||||
let mut pw = PartialWitness::new();
|
||||
|
||||
pw.set_target(initial_a, F::from_canonical_usize(look_val_a));
|
||||
pw.set_target(initial_b, F::from_canonical_usize(look_val_b));
|
||||
|
||||
let data = builder.build::<C>();
|
||||
let mut timing = TimingTree::new("prove one lookup", Level::Debug);
|
||||
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
|
||||
timing.print();
|
||||
data.verify(proof.clone())?;
|
||||
|
||||
assert!(
|
||||
proof.public_inputs[2] == F::from_canonical_u16(out_a),
|
||||
"First lookup, at index {} in the Tip5 table gives an incorrect output.",
|
||||
proof.public_inputs[0]
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[3] == F::from_canonical_u16(out_b),
|
||||
"Second lookup, at index {} in the Tip5 table gives an incorrect output.",
|
||||
proof.public_inputs[1]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests one lookup in two different lookup tables.
|
||||
#[test]
|
||||
pub fn test_two_luts() -> anyhow::Result<()> {
|
||||
use crate::field::types::Field;
|
||||
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
|
||||
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let initial_a = builder.add_virtual_target();
|
||||
let initial_b = builder.add_virtual_target();
|
||||
|
||||
let look_val_a = 1;
|
||||
let look_val_b = 2;
|
||||
|
||||
let tip5_table = TIP5_TABLE.to_vec();
|
||||
|
||||
let first_out = tip5_table[look_val_a];
|
||||
let second_out = tip5_table[look_val_b];
|
||||
|
||||
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
|
||||
|
||||
let other_table = OTHER_TABLE.to_vec();
|
||||
|
||||
let table_index = builder.add_lookup_table_from_pairs(table);
|
||||
let output_a = builder.add_lookup_from_index(initial_a, table_index);
|
||||
|
||||
let output_b = builder.add_lookup_from_index(initial_b, table_index);
|
||||
let sum = builder.add(output_a, output_b);
|
||||
|
||||
let s = first_out + second_out;
|
||||
let final_out = other_table[s as usize];
|
||||
|
||||
let table2: LookupTable = Arc::new((0..256).zip_eq(other_table).collect());
|
||||
let table2_index = builder.add_lookup_table_from_pairs(table2);
|
||||
|
||||
let output_final = builder.add_lookup_from_index(sum, table2_index);
|
||||
|
||||
builder.register_public_input(initial_a);
|
||||
builder.register_public_input(initial_b);
|
||||
builder.register_public_input(sum);
|
||||
builder.register_public_input(output_a);
|
||||
builder.register_public_input(output_b);
|
||||
builder.register_public_input(output_final);
|
||||
|
||||
let mut pw = PartialWitness::new();
|
||||
pw.set_target(initial_a, F::from_canonical_usize(look_val_a));
|
||||
pw.set_target(initial_b, F::from_canonical_usize(look_val_b));
|
||||
let data = builder.build::<C>();
|
||||
let mut timing = TimingTree::new("prove two_luts", Level::Debug);
|
||||
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
|
||||
data.verify(proof.clone())?;
|
||||
timing.print();
|
||||
|
||||
assert!(
|
||||
proof.public_inputs[3] == F::from_canonical_u16(first_out),
|
||||
"First lookup, at index {} in the Tip5 table gives an incorrect output.",
|
||||
proof.public_inputs[0]
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[4] == F::from_canonical_u16(second_out),
|
||||
"Second lookup, at index {} in the Tip5 table gives an incorrect output.",
|
||||
proof.public_inputs[1]
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[2] == F::from_canonical_u16(s),
|
||||
"Sum between the first two LUT outputs is incorrect."
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[5] == F::from_canonical_u16(final_out),
|
||||
"Output of the second LUT at index {} is incorrect.",
|
||||
s
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_different_inputs() -> anyhow::Result<()> {
|
||||
use crate::field::types::Field;
|
||||
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let initial_a = builder.add_virtual_target();
|
||||
let initial_b = builder.add_virtual_target();
|
||||
|
||||
let init_a = 1;
|
||||
let init_b = 2;
|
||||
|
||||
let tab: Vec<u16> = SMALLER_TABLE.to_vec();
|
||||
let table: LookupTable = Arc::new((2..10).zip_eq(tab).collect());
|
||||
|
||||
let other_table = OTHER_TABLE.to_vec();
|
||||
|
||||
let table2: LookupTable = Arc::new((0..256).zip_eq(other_table).collect());
|
||||
let small_index = builder.add_lookup_table_from_pairs(table.clone());
|
||||
let output_a = builder.add_lookup_from_index(initial_a, small_index);
|
||||
|
||||
let output_b = builder.add_lookup_from_index(initial_b, small_index);
|
||||
let sum = builder.add(output_a, output_b);
|
||||
|
||||
let other_index = builder.add_lookup_table_from_pairs(table2.clone());
|
||||
let output_final = builder.add_lookup_from_index(sum, other_index);
|
||||
|
||||
builder.register_public_input(initial_a);
|
||||
builder.register_public_input(initial_b);
|
||||
builder.register_public_input(sum);
|
||||
builder.register_public_input(output_a);
|
||||
builder.register_public_input(output_b);
|
||||
builder.register_public_input(output_final);
|
||||
|
||||
let mut pw = PartialWitness::new();
|
||||
|
||||
let look_val_a = table[init_a].0;
|
||||
let look_val_b = table[init_b].0;
|
||||
pw.set_target(initial_a, F::from_canonical_u16(look_val_a));
|
||||
pw.set_target(initial_b, F::from_canonical_u16(look_val_b));
|
||||
|
||||
let data = builder.build::<C>();
|
||||
let mut timing = TimingTree::new("prove different lookups", Level::Debug);
|
||||
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
|
||||
data.verify(proof.clone())?;
|
||||
timing.print();
|
||||
|
||||
let out_a = table[init_a].1;
|
||||
let out_b = table[init_b].1;
|
||||
let s = out_a + out_b;
|
||||
let out_final = table2[s as usize].1;
|
||||
|
||||
assert!(
|
||||
proof.public_inputs[3] == F::from_canonical_u16(out_a),
|
||||
"First lookup, at index {} in the smaller LUT gives an incorrect output.",
|
||||
proof.public_inputs[0]
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[4] == F::from_canonical_u16(out_b),
|
||||
"Second lookup, at index {} in the smaller LUT gives an incorrect output.",
|
||||
proof.public_inputs[1]
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[2] == F::from_canonical_u16(s),
|
||||
"Sum between the first two LUT outputs is incorrect."
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[5] == F::from_canonical_u16(out_final),
|
||||
"Output of the second LUT at index {} is incorrect.",
|
||||
s
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// This test looks up over 514 values for one LookupTableGate, which means that several LookupGates are created.
|
||||
#[test]
|
||||
pub fn test_many_lookups() -> anyhow::Result<()> {
|
||||
use crate::field::types::Field;
|
||||
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let initial_a = builder.add_virtual_target();
|
||||
let initial_b = builder.add_virtual_target();
|
||||
|
||||
let look_val_a = 1;
|
||||
let look_val_b = 2;
|
||||
|
||||
let tip5_table = TIP5_TABLE.to_vec();
|
||||
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
|
||||
|
||||
let out_a = table[look_val_a].1;
|
||||
let out_b = table[look_val_b].1;
|
||||
|
||||
let tip5_index = builder.add_lookup_table_from_pairs(table);
|
||||
let output_a = builder.add_lookup_from_index(initial_a, tip5_index);
|
||||
|
||||
let output_b = builder.add_lookup_from_index(initial_b, tip5_index);
|
||||
let sum = builder.add(output_a, output_b);
|
||||
|
||||
for _ in 0..514 {
|
||||
builder.add_lookup_from_index(initial_a, tip5_index);
|
||||
}
|
||||
|
||||
let other_table = OTHER_TABLE.to_vec();
|
||||
|
||||
let table2: LookupTable = Arc::new((0..256).zip_eq(other_table).collect());
|
||||
|
||||
let s = out_a + out_b;
|
||||
let out_final = table2[s as usize].1;
|
||||
|
||||
let other_index = builder.add_lookup_table_from_pairs(table2);
|
||||
let output_final = builder.add_lookup_from_index(sum, other_index);
|
||||
|
||||
builder.register_public_input(initial_a);
|
||||
builder.register_public_input(initial_b);
|
||||
builder.register_public_input(sum);
|
||||
builder.register_public_input(output_a);
|
||||
builder.register_public_input(output_b);
|
||||
builder.register_public_input(output_final);
|
||||
|
||||
let mut pw = PartialWitness::new();
|
||||
|
||||
pw.set_target(initial_a, F::from_canonical_usize(look_val_a));
|
||||
pw.set_target(initial_b, F::from_canonical_usize(look_val_b));
|
||||
|
||||
let data = builder.build::<C>();
|
||||
let mut timing = TimingTree::new("prove different lookups", Level::Debug);
|
||||
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
|
||||
|
||||
data.verify(proof.clone())?;
|
||||
timing.print();
|
||||
|
||||
assert!(
|
||||
proof.public_inputs[3] == F::from_canonical_u16(out_a),
|
||||
"First lookup, at index {} in the Tip5 table gives an incorrect output.",
|
||||
proof.public_inputs[0]
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[4] == F::from_canonical_u16(out_b),
|
||||
"Second lookup, at index {} in the Tip5 table gives an incorrect output.",
|
||||
proof.public_inputs[1]
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[2] == F::from_canonical_u16(s),
|
||||
"Sum between the first two LUT outputs is incorrect."
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[5] == F::from_canonical_u16(out_final),
|
||||
"Output of the second LUT at index {} is incorrect.",
|
||||
s
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests whether, when adding the same LUT to the circuit, the circuit only adds one copy, with the same index.
|
||||
#[test]
|
||||
pub fn test_same_luts() -> anyhow::Result<()> {
|
||||
use crate::field::types::Field;
|
||||
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
|
||||
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let initial_a = builder.add_virtual_target();
|
||||
let initial_b = builder.add_virtual_target();
|
||||
|
||||
let look_val_a = 1;
|
||||
let look_val_b = 2;
|
||||
|
||||
let tip5_table = TIP5_TABLE.to_vec();
|
||||
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
|
||||
|
||||
let table_index = builder.add_lookup_table_from_pairs(table.clone());
|
||||
let output_a = builder.add_lookup_from_index(initial_a, table_index);
|
||||
|
||||
let output_b = builder.add_lookup_from_index(initial_b, table_index);
|
||||
let sum = builder.add(output_a, output_b);
|
||||
|
||||
let table2_index = builder.add_lookup_table_from_pairs(table);
|
||||
|
||||
let output_final = builder.add_lookup_from_index(sum, table2_index);
|
||||
|
||||
builder.register_public_input(initial_a);
|
||||
builder.register_public_input(initial_b);
|
||||
builder.register_public_input(sum);
|
||||
builder.register_public_input(output_a);
|
||||
builder.register_public_input(output_b);
|
||||
builder.register_public_input(output_final);
|
||||
|
||||
let luts_length = builder.get_luts_length();
|
||||
|
||||
assert!(
|
||||
luts_length == 1,
|
||||
"There are {} LUTs when there should be only one",
|
||||
luts_length
|
||||
);
|
||||
|
||||
let mut pw = PartialWitness::new();
|
||||
|
||||
pw.set_target(initial_a, F::from_canonical_usize(look_val_a));
|
||||
pw.set_target(initial_b, F::from_canonical_usize(look_val_b));
|
||||
|
||||
let data = builder.build::<C>();
|
||||
let mut timing = TimingTree::new("prove two_luts", Level::Debug);
|
||||
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
|
||||
data.verify(proof)?;
|
||||
timing.print();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_circuit_build_mock() {
|
||||
// This code is taken from examples/fibonacci.rs
|
||||
use crate::field::types::Field;
|
||||
use crate::iop::witness::{PartialWitness, Witness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
// The arithmetic circuit.
|
||||
let initial_a = builder.add_virtual_target();
|
||||
let initial_b = builder.add_virtual_target();
|
||||
let mut prev_target = initial_a;
|
||||
let mut cur_target = initial_b;
|
||||
for _ in 0..99 {
|
||||
let temp = builder.add(prev_target, cur_target);
|
||||
prev_target = cur_target;
|
||||
cur_target = temp;
|
||||
}
|
||||
|
||||
// Public inputs are the two initial values (provided below) and the result (which is generated).
|
||||
builder.register_public_input(initial_a);
|
||||
builder.register_public_input(initial_b);
|
||||
builder.register_public_input(cur_target);
|
||||
|
||||
// Provide initial values.
|
||||
let mut pw = PartialWitness::new();
|
||||
pw.set_target(initial_a, F::ZERO);
|
||||
pw.set_target(initial_b, F::ONE);
|
||||
|
||||
let data = builder.mock_build::<C>();
|
||||
let partition_witness = data.generate_witness(pw);
|
||||
let result = partition_witness.try_get_target(cur_target).unwrap();
|
||||
assert_eq!(result, F::from_canonical_u64(3736710860384812976));
|
||||
}
|
||||
|
||||
fn init_logger() -> anyhow::Result<()> {
|
||||
let mut builder = env_logger::Builder::from_default_env();
|
||||
builder.format_timestamp(None);
|
||||
builder.filter_level(LevelFilter::Debug);
|
||||
|
||||
builder.try_init()?;
|
||||
Ok(())
|
||||
}
|
||||
static LOGGER_INITIALIZED: Once = Once::new();
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use std::sync::Once;
|
||||
|
||||
use itertools::Itertools;
|
||||
use log::{Level, LevelFilter};
|
||||
|
||||
use crate::gadgets::lookup::{OTHER_TABLE, SMALLER_TABLE, TIP5_TABLE};
|
||||
use crate::gates::lookup_table::LookupTable;
|
||||
use crate::gates::noop::NoopGate;
|
||||
use crate::plonk::prover::prove;
|
||||
use crate::util::timing::TimingTree;
|
||||
|
||||
#[test]
|
||||
fn test_no_lookup() -> anyhow::Result<()> {
|
||||
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
|
||||
use crate::iop::witness::PartialWitness;
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
builder.add_gate(NoopGate, vec![]);
|
||||
let pw = PartialWitness::new();
|
||||
|
||||
let data = builder.build::<C>();
|
||||
let mut timing = TimingTree::new("prove first", Level::Debug);
|
||||
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
|
||||
timing.print();
|
||||
data.verify(proof)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn test_lookup_table_not_used() {
|
||||
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let tip5_table = TIP5_TABLE.to_vec();
|
||||
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
|
||||
builder.add_lookup_table_from_pairs(table);
|
||||
|
||||
builder.build::<C>();
|
||||
}
|
||||
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn test_lookup_without_table() {
|
||||
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let dummy = builder.add_virtual_target();
|
||||
builder.add_lookup_from_index(dummy, 0);
|
||||
|
||||
builder.build::<C>();
|
||||
}
|
||||
|
||||
// Tests two lookups in one lookup table.
|
||||
#[test]
|
||||
fn test_one_lookup() -> anyhow::Result<()> {
|
||||
use crate::field::types::Field;
|
||||
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
|
||||
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
|
||||
let tip5_table = TIP5_TABLE.to_vec();
|
||||
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let initial_a = builder.add_virtual_target();
|
||||
let initial_b = builder.add_virtual_target();
|
||||
|
||||
let look_val_a = 1;
|
||||
let look_val_b = 2;
|
||||
|
||||
let out_a = table[look_val_a].1;
|
||||
let out_b = table[look_val_b].1;
|
||||
let table_index = builder.add_lookup_table_from_pairs(table);
|
||||
let output_a = builder.add_lookup_from_index(initial_a, table_index);
|
||||
|
||||
let output_b = builder.add_lookup_from_index(initial_b, table_index);
|
||||
|
||||
builder.register_public_input(initial_a);
|
||||
builder.register_public_input(initial_b);
|
||||
builder.register_public_input(output_a);
|
||||
builder.register_public_input(output_b);
|
||||
|
||||
let mut pw = PartialWitness::new();
|
||||
|
||||
pw.set_target(initial_a, F::from_canonical_usize(look_val_a));
|
||||
pw.set_target(initial_b, F::from_canonical_usize(look_val_b));
|
||||
|
||||
let data = builder.build::<C>();
|
||||
let mut timing = TimingTree::new("prove one lookup", Level::Debug);
|
||||
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
|
||||
timing.print();
|
||||
data.verify(proof.clone())?;
|
||||
|
||||
assert!(
|
||||
proof.public_inputs[2] == F::from_canonical_u16(out_a),
|
||||
"First lookup, at index {} in the Tip5 table gives an incorrect output.",
|
||||
proof.public_inputs[0]
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[3] == F::from_canonical_u16(out_b),
|
||||
"Second lookup, at index {} in the Tip5 table gives an incorrect output.",
|
||||
proof.public_inputs[1]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests one lookup in two different lookup tables.
|
||||
#[test]
|
||||
pub fn test_two_luts() -> anyhow::Result<()> {
|
||||
use crate::field::types::Field;
|
||||
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
|
||||
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let initial_a = builder.add_virtual_target();
|
||||
let initial_b = builder.add_virtual_target();
|
||||
|
||||
let look_val_a = 1;
|
||||
let look_val_b = 2;
|
||||
|
||||
let tip5_table = TIP5_TABLE.to_vec();
|
||||
|
||||
let first_out = tip5_table[look_val_a];
|
||||
let second_out = tip5_table[look_val_b];
|
||||
|
||||
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
|
||||
|
||||
let other_table = OTHER_TABLE.to_vec();
|
||||
|
||||
let table_index = builder.add_lookup_table_from_pairs(table);
|
||||
let output_a = builder.add_lookup_from_index(initial_a, table_index);
|
||||
|
||||
let output_b = builder.add_lookup_from_index(initial_b, table_index);
|
||||
let sum = builder.add(output_a, output_b);
|
||||
|
||||
let s = first_out + second_out;
|
||||
let final_out = other_table[s as usize];
|
||||
|
||||
let table2: LookupTable = Arc::new((0..256).zip_eq(other_table).collect());
|
||||
let table2_index = builder.add_lookup_table_from_pairs(table2);
|
||||
|
||||
let output_final = builder.add_lookup_from_index(sum, table2_index);
|
||||
|
||||
builder.register_public_input(initial_a);
|
||||
builder.register_public_input(initial_b);
|
||||
builder.register_public_input(sum);
|
||||
builder.register_public_input(output_a);
|
||||
builder.register_public_input(output_b);
|
||||
builder.register_public_input(output_final);
|
||||
|
||||
let mut pw = PartialWitness::new();
|
||||
pw.set_target(initial_a, F::from_canonical_usize(look_val_a));
|
||||
pw.set_target(initial_b, F::from_canonical_usize(look_val_b));
|
||||
let data = builder.build::<C>();
|
||||
let mut timing = TimingTree::new("prove two_luts", Level::Debug);
|
||||
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
|
||||
data.verify(proof.clone())?;
|
||||
timing.print();
|
||||
|
||||
assert!(
|
||||
proof.public_inputs[3] == F::from_canonical_u16(first_out),
|
||||
"First lookup, at index {} in the Tip5 table gives an incorrect output.",
|
||||
proof.public_inputs[0]
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[4] == F::from_canonical_u16(second_out),
|
||||
"Second lookup, at index {} in the Tip5 table gives an incorrect output.",
|
||||
proof.public_inputs[1]
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[2] == F::from_canonical_u16(s),
|
||||
"Sum between the first two LUT outputs is incorrect."
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[5] == F::from_canonical_u16(final_out),
|
||||
"Output of the second LUT at index {} is incorrect.",
|
||||
s
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_different_inputs() -> anyhow::Result<()> {
|
||||
use crate::field::types::Field;
|
||||
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let initial_a = builder.add_virtual_target();
|
||||
let initial_b = builder.add_virtual_target();
|
||||
|
||||
let init_a = 1;
|
||||
let init_b = 2;
|
||||
|
||||
let tab: Vec<u16> = SMALLER_TABLE.to_vec();
|
||||
let table: LookupTable = Arc::new((2..10).zip_eq(tab).collect());
|
||||
|
||||
let other_table = OTHER_TABLE.to_vec();
|
||||
|
||||
let table2: LookupTable = Arc::new((0..256).zip_eq(other_table).collect());
|
||||
let small_index = builder.add_lookup_table_from_pairs(table.clone());
|
||||
let output_a = builder.add_lookup_from_index(initial_a, small_index);
|
||||
|
||||
let output_b = builder.add_lookup_from_index(initial_b, small_index);
|
||||
let sum = builder.add(output_a, output_b);
|
||||
|
||||
let other_index = builder.add_lookup_table_from_pairs(table2.clone());
|
||||
let output_final = builder.add_lookup_from_index(sum, other_index);
|
||||
|
||||
builder.register_public_input(initial_a);
|
||||
builder.register_public_input(initial_b);
|
||||
builder.register_public_input(sum);
|
||||
builder.register_public_input(output_a);
|
||||
builder.register_public_input(output_b);
|
||||
builder.register_public_input(output_final);
|
||||
|
||||
let mut pw = PartialWitness::new();
|
||||
|
||||
let look_val_a = table[init_a].0;
|
||||
let look_val_b = table[init_b].0;
|
||||
pw.set_target(initial_a, F::from_canonical_u16(look_val_a));
|
||||
pw.set_target(initial_b, F::from_canonical_u16(look_val_b));
|
||||
|
||||
let data = builder.build::<C>();
|
||||
let mut timing = TimingTree::new("prove different lookups", Level::Debug);
|
||||
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
|
||||
data.verify(proof.clone())?;
|
||||
timing.print();
|
||||
|
||||
let out_a = table[init_a].1;
|
||||
let out_b = table[init_b].1;
|
||||
let s = out_a + out_b;
|
||||
let out_final = table2[s as usize].1;
|
||||
|
||||
assert!(
|
||||
proof.public_inputs[3] == F::from_canonical_u16(out_a),
|
||||
"First lookup, at index {} in the smaller LUT gives an incorrect output.",
|
||||
proof.public_inputs[0]
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[4] == F::from_canonical_u16(out_b),
|
||||
"Second lookup, at index {} in the smaller LUT gives an incorrect output.",
|
||||
proof.public_inputs[1]
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[2] == F::from_canonical_u16(s),
|
||||
"Sum between the first two LUT outputs is incorrect."
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[5] == F::from_canonical_u16(out_final),
|
||||
"Output of the second LUT at index {} is incorrect.",
|
||||
s
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// This test looks up over 514 values for one LookupTableGate, which means that several LookupGates are created.
|
||||
#[test]
|
||||
pub fn test_many_lookups() -> anyhow::Result<()> {
|
||||
use crate::field::types::Field;
|
||||
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let initial_a = builder.add_virtual_target();
|
||||
let initial_b = builder.add_virtual_target();
|
||||
|
||||
let look_val_a = 1;
|
||||
let look_val_b = 2;
|
||||
|
||||
let tip5_table = TIP5_TABLE.to_vec();
|
||||
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
|
||||
|
||||
let out_a = table[look_val_a].1;
|
||||
let out_b = table[look_val_b].1;
|
||||
|
||||
let tip5_index = builder.add_lookup_table_from_pairs(table);
|
||||
let output_a = builder.add_lookup_from_index(initial_a, tip5_index);
|
||||
|
||||
let output_b = builder.add_lookup_from_index(initial_b, tip5_index);
|
||||
let sum = builder.add(output_a, output_b);
|
||||
|
||||
for _ in 0..514 {
|
||||
builder.add_lookup_from_index(initial_a, tip5_index);
|
||||
}
|
||||
|
||||
let other_table = OTHER_TABLE.to_vec();
|
||||
|
||||
let table2: LookupTable = Arc::new((0..256).zip_eq(other_table).collect());
|
||||
|
||||
let s = out_a + out_b;
|
||||
let out_final = table2[s as usize].1;
|
||||
|
||||
let other_index = builder.add_lookup_table_from_pairs(table2);
|
||||
let output_final = builder.add_lookup_from_index(sum, other_index);
|
||||
|
||||
builder.register_public_input(initial_a);
|
||||
builder.register_public_input(initial_b);
|
||||
builder.register_public_input(sum);
|
||||
builder.register_public_input(output_a);
|
||||
builder.register_public_input(output_b);
|
||||
builder.register_public_input(output_final);
|
||||
|
||||
let mut pw = PartialWitness::new();
|
||||
|
||||
pw.set_target(initial_a, F::from_canonical_usize(look_val_a));
|
||||
pw.set_target(initial_b, F::from_canonical_usize(look_val_b));
|
||||
|
||||
let data = builder.build::<C>();
|
||||
let mut timing = TimingTree::new("prove different lookups", Level::Debug);
|
||||
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
|
||||
|
||||
data.verify(proof.clone())?;
|
||||
timing.print();
|
||||
|
||||
assert!(
|
||||
proof.public_inputs[3] == F::from_canonical_u16(out_a),
|
||||
"First lookup, at index {} in the Tip5 table gives an incorrect output.",
|
||||
proof.public_inputs[0]
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[4] == F::from_canonical_u16(out_b),
|
||||
"Second lookup, at index {} in the Tip5 table gives an incorrect output.",
|
||||
proof.public_inputs[1]
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[2] == F::from_canonical_u16(s),
|
||||
"Sum between the first two LUT outputs is incorrect."
|
||||
);
|
||||
assert!(
|
||||
proof.public_inputs[5] == F::from_canonical_u16(out_final),
|
||||
"Output of the second LUT at index {} is incorrect.",
|
||||
s
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests whether, when adding the same LUT to the circuit, the circuit only adds one copy, with the same index.
|
||||
#[test]
|
||||
pub fn test_same_luts() -> anyhow::Result<()> {
|
||||
use crate::field::types::Field;
|
||||
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
|
||||
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let initial_a = builder.add_virtual_target();
|
||||
let initial_b = builder.add_virtual_target();
|
||||
|
||||
let look_val_a = 1;
|
||||
let look_val_b = 2;
|
||||
|
||||
let tip5_table = TIP5_TABLE.to_vec();
|
||||
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
|
||||
|
||||
let table_index = builder.add_lookup_table_from_pairs(table.clone());
|
||||
let output_a = builder.add_lookup_from_index(initial_a, table_index);
|
||||
|
||||
let output_b = builder.add_lookup_from_index(initial_b, table_index);
|
||||
let sum = builder.add(output_a, output_b);
|
||||
|
||||
let table2_index = builder.add_lookup_table_from_pairs(table);
|
||||
|
||||
let output_final = builder.add_lookup_from_index(sum, table2_index);
|
||||
|
||||
builder.register_public_input(initial_a);
|
||||
builder.register_public_input(initial_b);
|
||||
builder.register_public_input(sum);
|
||||
builder.register_public_input(output_a);
|
||||
builder.register_public_input(output_b);
|
||||
builder.register_public_input(output_final);
|
||||
|
||||
let luts_length = builder.get_luts_length();
|
||||
|
||||
assert!(
|
||||
luts_length == 1,
|
||||
"There are {} LUTs when there should be only one",
|
||||
luts_length
|
||||
);
|
||||
|
||||
let mut pw = PartialWitness::new();
|
||||
|
||||
pw.set_target(initial_a, F::from_canonical_usize(look_val_a));
|
||||
pw.set_target(initial_b, F::from_canonical_usize(look_val_b));
|
||||
|
||||
let data = builder.build::<C>();
|
||||
let mut timing = TimingTree::new("prove two_luts", Level::Debug);
|
||||
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
|
||||
data.verify(proof)?;
|
||||
timing.print();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_logger() -> anyhow::Result<()> {
|
||||
let mut builder = env_logger::Builder::from_default_env();
|
||||
builder.format_timestamp(None);
|
||||
builder.filter_level(LevelFilter::Debug);
|
||||
|
||||
builder.try_init()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user