Update check_ctls with extra looking values (#1290)

This commit is contained in:
Robin Salen 2023-10-16 08:53:42 -04:00 committed by GitHub
parent d89966b076
commit c9391be024
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 182 additions and 8 deletions

View File

@ -838,13 +838,13 @@ pub(crate) mod testutils {
type MultiSet<F> = HashMap<Vec<F>, Vec<(Table, usize)>>;
/// Check that the provided traces and cross-table lookups are consistent.
#[allow(unused)] // TODO: used later?
pub(crate) fn check_ctls<F: Field>(
trace_poly_values: &[Vec<PolynomialValues<F>>],
cross_table_lookups: &[CrossTableLookup<F>],
extra_memory_looking_values: &[Vec<F>],
) {
for (i, ctl) in cross_table_lookups.iter().enumerate() {
check_ctl(trace_poly_values, ctl, i);
check_ctl(trace_poly_values, ctl, i, extra_memory_looking_values);
}
}
@ -852,6 +852,7 @@ pub(crate) mod testutils {
trace_poly_values: &[Vec<PolynomialValues<F>>],
ctl: &CrossTableLookup<F>,
ctl_index: usize,
extra_memory_looking_values: &[Vec<F>],
) {
let CrossTableLookup {
looking_tables,
@ -868,6 +869,18 @@ pub(crate) mod testutils {
}
process_table(trace_poly_values, looked_table, &mut looked_multiset);
// Extra looking values for memory
if ctl_index == Table::Memory as usize {
for row in extra_memory_looking_values.iter() {
// The table and the row index don't matter here, as we just want to enforce
// that the special extra values do appear when looking against the Memory table.
looking_multiset
.entry(row.to_vec())
.or_default()
.push((Table::Cpu, 0));
}
}
let empty = &vec![];
// Check that every row in the looking tables appears in the looked table the same number of times.
for (row, looking_locations) in &looking_multiset {

View File

@ -1,5 +1,3 @@
use std::any::type_name;
use anyhow::{ensure, Result};
use itertools::Itertools;
use once_cell::sync::Lazy;
@ -35,6 +33,10 @@ use crate::lookup::{lookup_helper_columns, Lookup, LookupCheckVars};
use crate::proof::{AllProof, PublicValues, StarkOpeningSet, StarkProof, StarkProofWithMetadata};
use crate::stark::Stark;
use crate::vanishing_poly::eval_vanishing_poly;
#[cfg(test)]
use crate::{
cross_table_lookup::testutils::check_ctls, verifier::testutils::get_memory_extra_looking_values,
};
/// Generate traces, then create all STARK proofs.
pub fn prove<F, C, const D: usize>(
@ -142,7 +144,7 @@ where
prove_with_commitments(
all_stark,
config,
trace_poly_values,
&trace_poly_values,
trace_commitments,
ctl_data_per_table,
&mut challenger,
@ -151,6 +153,15 @@ where
)?
);
#[cfg(test)]
{
check_ctls(
&trace_poly_values,
&all_stark.cross_table_lookups,
&get_memory_extra_looking_values(&public_values),
);
}
Ok(AllProof {
stark_proofs,
ctl_challenges,
@ -161,7 +172,7 @@ where
fn prove_with_commitments<F, C, const D: usize>(
all_stark: &AllStark<F, D>,
config: &StarkConfig,
trace_poly_values: [Vec<PolynomialValues<F>>; NUM_TABLES],
trace_poly_values: &[Vec<PolynomialValues<F>>; NUM_TABLES],
trace_commitments: Vec<PolynomialBatch<F, C, D>>,
ctl_data_per_table: [CtlData<F>; NUM_TABLES],
challenger: &mut Challenger<F, C::Hasher>,
@ -365,7 +376,9 @@ where
challenger.observe_cap(&auxiliary_polys_cap);
let alphas = challenger.get_n_challenges(config.num_challenges);
if cfg!(test) {
#[cfg(test)]
{
check_constraints(
stark,
trace_commitment,
@ -378,6 +391,7 @@ where
num_lookup_columns,
);
}
let quotient_polys = timed!(
timing,
"compute quotient polys",
@ -606,6 +620,7 @@ where
.collect()
}
#[cfg(test)]
/// Check that all constraints evaluate to zero on `H`.
/// Can also be used to check the degree of the constraints by evaluating on a larger subgroup.
fn check_constraints<'a, F, C, S, const D: usize>(
@ -705,7 +720,7 @@ fn check_constraints<'a, F, C, S, const D: usize>(
assert!(
v.iter().all(|x| x.is_zero()),
"Constraint failed in {}",
type_name::<S>()
std::any::type_name::<S>()
);
}
}

View File

@ -455,6 +455,152 @@ fn eval_l_0_and_l_last<F: Field>(log_n: usize, x: F) -> (F, F) {
(z_x * invs[0], z_x * invs[1])
}
#[cfg(test)]
pub(crate) mod testutils {
use super::*;
/// Output all the extra memory rows that don't appear in the CPU trace but are
/// necessary to correctly check the MemoryStark CTL.
pub(crate) fn get_memory_extra_looking_values<F, const D: usize>(
public_values: &PublicValues,
) -> Vec<Vec<F>>
where
F: RichField + Extendable<D>,
{
// Add metadata and tries writes.
let fields = [
(
GlobalMetadata::BlockBeneficiary,
U256::from_big_endian(&public_values.block_metadata.block_beneficiary.0),
),
(
GlobalMetadata::BlockTimestamp,
public_values.block_metadata.block_timestamp,
),
(
GlobalMetadata::BlockNumber,
public_values.block_metadata.block_number,
),
(
GlobalMetadata::BlockRandom,
public_values.block_metadata.block_random.into_uint(),
),
(
GlobalMetadata::BlockDifficulty,
public_values.block_metadata.block_difficulty,
),
(
GlobalMetadata::BlockGasLimit,
public_values.block_metadata.block_gaslimit,
),
(
GlobalMetadata::BlockChainId,
public_values.block_metadata.block_chain_id,
),
(
GlobalMetadata::BlockBaseFee,
public_values.block_metadata.block_base_fee,
),
(
GlobalMetadata::BlockCurrentHash,
h2u(public_values.block_hashes.cur_hash),
),
(
GlobalMetadata::BlockGasUsed,
public_values.block_metadata.block_gas_used,
),
(
GlobalMetadata::TxnNumberBefore,
public_values.extra_block_data.txn_number_before,
),
(
GlobalMetadata::TxnNumberAfter,
public_values.extra_block_data.txn_number_after,
),
(
GlobalMetadata::BlockGasUsedBefore,
public_values.extra_block_data.gas_used_before,
),
(
GlobalMetadata::BlockGasUsedAfter,
public_values.extra_block_data.gas_used_after,
),
(
GlobalMetadata::StateTrieRootDigestBefore,
h2u(public_values.trie_roots_before.state_root),
),
(
GlobalMetadata::TransactionTrieRootDigestBefore,
h2u(public_values.trie_roots_before.transactions_root),
),
(
GlobalMetadata::ReceiptTrieRootDigestBefore,
h2u(public_values.trie_roots_before.receipts_root),
),
(
GlobalMetadata::StateTrieRootDigestAfter,
h2u(public_values.trie_roots_after.state_root),
),
(
GlobalMetadata::TransactionTrieRootDigestAfter,
h2u(public_values.trie_roots_after.transactions_root),
),
(
GlobalMetadata::ReceiptTrieRootDigestAfter,
h2u(public_values.trie_roots_after.receipts_root),
),
];
let segment = F::from_canonical_u32(Segment::GlobalMetadata as u32);
let mut extra_looking_rows = Vec::new();
fields.map(|(field, val)| {
extra_looking_rows.push(add_extra_looking_row(segment, field as usize, val))
});
// Add block bloom writes.
let bloom_segment = F::from_canonical_u32(Segment::GlobalBlockBloom as u32);
for index in 0..8 {
let val = public_values.block_metadata.block_bloom[index];
extra_looking_rows.push(add_extra_looking_row(bloom_segment, index, val));
}
for index in 0..8 {
let val = public_values.extra_block_data.block_bloom_before[index];
extra_looking_rows.push(add_extra_looking_row(bloom_segment, index + 8, val));
}
for index in 0..8 {
let val = public_values.extra_block_data.block_bloom_after[index];
extra_looking_rows.push(add_extra_looking_row(bloom_segment, index + 16, val));
}
// Add Blockhashes writes.
let block_hashes_segment = F::from_canonical_u32(Segment::BlockHashes as u32);
for index in 0..256 {
let val = h2u(public_values.block_hashes.prev_hashes[index]);
extra_looking_rows.push(add_extra_looking_row(block_hashes_segment, index, val));
}
extra_looking_rows
}
fn add_extra_looking_row<F, const D: usize>(segment: F, index: usize, val: U256) -> Vec<F>
where
F: RichField + Extendable<D>,
{
let mut row = vec![F::ZERO; 13];
row[0] = F::ZERO; // is_read
row[1] = F::ZERO; // context
row[2] = segment;
row[3] = F::from_canonical_usize(index);
for j in 0..VALUE_LIMBS {
row[j + 4] = F::from_canonical_u32((val >> (j * 32)).low_u32());
}
row[12] = F::ONE; // timestamp
row
}
}
#[cfg(test)]
mod tests {
use plonky2::field::goldilocks_field::GoldilocksField;