mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-10 17:53:06 +00:00
Merge pull request #1026 from topos-protocol/memory-ctl-verifier-bus
Fix the memory CTL and implement the verifier memory bus
This commit is contained in:
commit
5b8740a729
@ -10,7 +10,7 @@ use crate::config::StarkConfig;
|
||||
use crate::cpu::cpu_stark;
|
||||
use crate::cpu::cpu_stark::CpuStark;
|
||||
use crate::cpu::membus::NUM_GP_CHANNELS;
|
||||
use crate::cross_table_lookup::{Column, CrossTableLookup, TableWithColumns};
|
||||
use crate::cross_table_lookup::{CrossTableLookup, TableWithColumns};
|
||||
use crate::keccak::keccak_stark;
|
||||
use crate::keccak::keccak_stark::KeccakStark;
|
||||
use crate::keccak_sponge::columns::KECCAK_RATE_BYTES;
|
||||
@ -97,23 +97,13 @@ impl Table {
|
||||
}
|
||||
|
||||
pub(crate) fn all_cross_table_lookups<F: Field>() -> Vec<CrossTableLookup<F>> {
|
||||
let mut ctls = vec![
|
||||
vec![
|
||||
ctl_arithmetic(),
|
||||
ctl_keccak_sponge(),
|
||||
ctl_keccak(),
|
||||
ctl_logic(),
|
||||
ctl_memory(),
|
||||
];
|
||||
// TODO: Some CTLs temporarily disabled while we get them working.
|
||||
disable_ctl(&mut ctls[4]);
|
||||
ctls
|
||||
}
|
||||
|
||||
fn disable_ctl<F: Field>(ctl: &mut CrossTableLookup<F>) {
|
||||
for table in &mut ctl.looking_tables {
|
||||
table.filter_column = Some(Column::zero());
|
||||
}
|
||||
ctl.looked_table.filter_column = Some(Column::zero());
|
||||
]
|
||||
}
|
||||
|
||||
fn ctl_arithmetic<F: Field>() -> CrossTableLookup<F> {
|
||||
|
||||
@ -48,6 +48,7 @@ pub(crate) fn generate_bootstrap_kernel<F: Field>(state: &mut GenerationState<F>
|
||||
final_cpu_row.mem_channels[2].value[0] = F::ZERO; // virt
|
||||
final_cpu_row.mem_channels[3].value[0] = F::from_canonical_usize(KERNEL.code.len()); // len
|
||||
final_cpu_row.mem_channels[4].value = KERNEL.code_hash.map(F::from_canonical_u32);
|
||||
final_cpu_row.mem_channels[4].value.reverse();
|
||||
keccak_sponge_log(
|
||||
state,
|
||||
MemoryAddress::new(0, Segment::Code, 0),
|
||||
@ -93,6 +94,7 @@ pub(crate) fn eval_bootstrap_kernel<F: Field, P: PackedField<Scalar = F>>(
|
||||
for (&expected, actual) in KERNEL
|
||||
.code_hash
|
||||
.iter()
|
||||
.rev()
|
||||
.zip(local_values.mem_channels.last().unwrap().value)
|
||||
{
|
||||
let expected = P::from(F::from_canonical_u32(expected));
|
||||
@ -153,6 +155,7 @@ pub(crate) fn eval_bootstrap_kernel_circuit<F: RichField + Extendable<D>, const
|
||||
for (&expected, actual) in KERNEL
|
||||
.code_hash
|
||||
.iter()
|
||||
.rev()
|
||||
.zip(local_values.mem_channels.last().unwrap().value)
|
||||
{
|
||||
let expected = builder.constant_extension(F::Extension::from_canonical_u32(expected));
|
||||
|
||||
@ -44,9 +44,10 @@ impl Kernel {
|
||||
prover_inputs: HashMap<usize, ProverInputFn>,
|
||||
) -> Self {
|
||||
let code_hash_bytes = keccak(&code).0;
|
||||
let code_hash = core::array::from_fn(|i| {
|
||||
let code_hash_be = core::array::from_fn(|i| {
|
||||
u32::from_le_bytes(core::array::from_fn(|j| code_hash_bytes[i * 4 + j]))
|
||||
});
|
||||
let code_hash = code_hash_be.map(u32::from_be);
|
||||
let ordered_labels = global_labels
|
||||
.keys()
|
||||
.cloned()
|
||||
|
||||
@ -244,15 +244,6 @@ pub(crate) fn cross_table_lookup_data<F: RichField, const D: usize>(
|
||||
&looked_table.filter_column,
|
||||
challenge,
|
||||
);
|
||||
|
||||
debug_assert_eq!(
|
||||
zs_looking
|
||||
.clone()
|
||||
.map(|z| *z.values.last().unwrap())
|
||||
.product::<F>(),
|
||||
*z_looked.values.last().unwrap()
|
||||
);
|
||||
|
||||
for (table, z) in looking_tables.iter().zip(zs_looking) {
|
||||
ctl_data_per_table[table.table as usize]
|
||||
.zs_columns
|
||||
@ -542,6 +533,7 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit<
|
||||
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_extra_looking_products: Vec<Vec<F>>,
|
||||
config: &StarkConfig,
|
||||
) -> Result<()> {
|
||||
let mut ctl_zs_openings = ctl_zs_lasts.iter().map(|v| v.iter()).collect::<Vec<_>>();
|
||||
@ -550,13 +542,15 @@ pub(crate) fn verify_cross_table_lookups<F: RichField + Extendable<D>, const D:
|
||||
looked_table,
|
||||
} in cross_table_lookups.iter()
|
||||
{
|
||||
for _ in 0..config.num_challenges {
|
||||
let extra_product_vec = &ctl_extra_looking_products[looked_table.table as usize];
|
||||
for c in 0..config.num_challenges {
|
||||
let looking_zs_prod = looking_tables
|
||||
.iter()
|
||||
.map(|table| *ctl_zs_openings[table.table as usize].next().unwrap())
|
||||
.product::<F>();
|
||||
let looked_z = *ctl_zs_openings[looked_table.table as usize].next().unwrap();
|
||||
.product::<F>()
|
||||
* extra_product_vec[c];
|
||||
|
||||
let looked_z = *ctl_zs_openings[looked_table.table as usize].next().unwrap();
|
||||
ensure!(
|
||||
looking_zs_prod == looked_z,
|
||||
"Cross-table lookup verification failed."
|
||||
@ -572,6 +566,7 @@ pub(crate) fn verify_cross_table_lookups_circuit<F: RichField + Extendable<D>, c
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
cross_table_lookups: Vec<CrossTableLookup<F>>,
|
||||
ctl_zs_lasts: [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<_>>();
|
||||
@ -580,14 +575,18 @@ pub(crate) fn verify_cross_table_lookups_circuit<F: RichField + Extendable<D>, c
|
||||
looked_table,
|
||||
} in cross_table_lookups.into_iter()
|
||||
{
|
||||
for _ in 0..inner_config.num_challenges {
|
||||
let looking_zs_prod = builder.mul_many(
|
||||
let extra_product_vec = &ctl_extra_looking_products[looked_table.table as usize];
|
||||
for c in 0..inner_config.num_challenges {
|
||||
let mut looking_zs_prod = builder.mul_many(
|
||||
looking_tables
|
||||
.iter()
|
||||
.map(|table| *ctl_zs_openings[table.table as usize].next().unwrap()),
|
||||
);
|
||||
|
||||
looking_zs_prod = builder.mul(looking_zs_prod, extra_product_vec[c]);
|
||||
|
||||
let looked_z = *ctl_zs_openings[looked_table.table as usize].next().unwrap();
|
||||
builder.connect(looking_zs_prod, looked_z);
|
||||
builder.connect(looked_z, looking_zs_prod);
|
||||
}
|
||||
}
|
||||
debug_assert!(ctl_zs_openings.iter_mut().all(|iter| iter.next().is_none()));
|
||||
|
||||
@ -30,17 +30,23 @@ use crate::all_stark::{all_cross_table_lookups, AllStark, Table, NUM_TABLES};
|
||||
use crate::arithmetic::arithmetic_stark::ArithmeticStark;
|
||||
use crate::config::StarkConfig;
|
||||
use crate::cpu::cpu_stark::CpuStark;
|
||||
use crate::cpu::kernel::constants::global_metadata::GlobalMetadata;
|
||||
use crate::cross_table_lookup::{verify_cross_table_lookups_circuit, CrossTableLookup};
|
||||
use crate::generation::GenerationInputs;
|
||||
use crate::keccak::keccak_stark::KeccakStark;
|
||||
use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeStark;
|
||||
use crate::logic::LogicStark;
|
||||
use crate::memory::memory_stark::MemoryStark;
|
||||
use crate::permutation::{get_grand_product_challenge_set_target, GrandProductChallengeSet};
|
||||
use crate::proof::StarkProofWithMetadata;
|
||||
use crate::memory::segments::Segment;
|
||||
use crate::memory::{NUM_CHANNELS, VALUE_LIMBS};
|
||||
use crate::permutation::{
|
||||
get_grand_product_challenge_set_target, GrandProductChallenge, GrandProductChallengeSet,
|
||||
};
|
||||
use crate::proof::{PublicValues, PublicValuesTarget, StarkProofWithMetadata};
|
||||
use crate::prover::prove;
|
||||
use crate::recursive_verifier::{
|
||||
add_common_recursion_gates, recursive_stark_circuit, PlonkWrapperCircuit, PublicInputs,
|
||||
add_common_recursion_gates, add_virtual_public_values, recursive_stark_circuit,
|
||||
set_block_metadata_target, set_trie_roots_target, PlonkWrapperCircuit, PublicInputs,
|
||||
StarkWrapperCircuit,
|
||||
};
|
||||
use crate::stark::Stark;
|
||||
@ -81,6 +87,8 @@ where
|
||||
/// 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.
|
||||
index_verifier_data: [Target; NUM_TABLES],
|
||||
/// Public inputs containing public values.
|
||||
public_values: PublicValuesTarget,
|
||||
/// Public inputs used for cyclic verification. These aren't actually used for EVM root
|
||||
/// proofs; the circuit has them just to match the structure of aggregation proofs.
|
||||
cyclic_vk: VerifierCircuitTarget,
|
||||
@ -104,6 +112,7 @@ where
|
||||
for index in self.index_verifier_data {
|
||||
buffer.write_target(index)?;
|
||||
}
|
||||
self.public_values.to_buffer(buffer)?;
|
||||
buffer.write_target_verifier_circuit(&self.cyclic_vk)?;
|
||||
Ok(())
|
||||
}
|
||||
@ -122,12 +131,14 @@ where
|
||||
for _ in 0..NUM_TABLES {
|
||||
index_verifier_data.push(buffer.read_target()?);
|
||||
}
|
||||
let public_values = PublicValuesTarget::from_buffer(buffer)?;
|
||||
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(),
|
||||
public_values,
|
||||
cyclic_vk,
|
||||
})
|
||||
}
|
||||
@ -144,6 +155,7 @@ where
|
||||
pub circuit: CircuitData<F, C, D>,
|
||||
lhs: AggregationChildTarget<D>,
|
||||
rhs: AggregationChildTarget<D>,
|
||||
public_values: PublicValuesTarget,
|
||||
cyclic_vk: VerifierCircuitTarget,
|
||||
}
|
||||
|
||||
@ -160,6 +172,7 @@ where
|
||||
) -> IoResult<()> {
|
||||
buffer.write_circuit_data(&self.circuit, gate_serializer, generator_serializer)?;
|
||||
buffer.write_target_verifier_circuit(&self.cyclic_vk)?;
|
||||
self.public_values.to_buffer(buffer)?;
|
||||
self.lhs.to_buffer(buffer)?;
|
||||
self.rhs.to_buffer(buffer)?;
|
||||
Ok(())
|
||||
@ -172,12 +185,14 @@ where
|
||||
) -> IoResult<Self> {
|
||||
let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?;
|
||||
let cyclic_vk = buffer.read_target_verifier_circuit()?;
|
||||
let public_values = PublicValuesTarget::from_buffer(buffer)?;
|
||||
let lhs = AggregationChildTarget::from_buffer(buffer)?;
|
||||
let rhs = AggregationChildTarget::from_buffer(buffer)?;
|
||||
Ok(Self {
|
||||
circuit,
|
||||
lhs,
|
||||
rhs,
|
||||
public_values,
|
||||
cyclic_vk,
|
||||
})
|
||||
}
|
||||
@ -220,6 +235,7 @@ where
|
||||
has_parent_block: BoolTarget,
|
||||
parent_block_proof: ProofWithPublicInputsTarget<D>,
|
||||
agg_root_proof: ProofWithPublicInputsTarget<D>,
|
||||
public_values: PublicValuesTarget,
|
||||
cyclic_vk: VerifierCircuitTarget,
|
||||
}
|
||||
|
||||
@ -238,6 +254,7 @@ where
|
||||
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)?;
|
||||
self.public_values.to_buffer(buffer)?;
|
||||
buffer.write_target_verifier_circuit(&self.cyclic_vk)?;
|
||||
Ok(())
|
||||
}
|
||||
@ -251,12 +268,14 @@ where
|
||||
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 public_values = PublicValuesTarget::from_buffer(buffer)?;
|
||||
let cyclic_vk = buffer.read_target_verifier_circuit()?;
|
||||
Ok(Self {
|
||||
circuit,
|
||||
has_parent_block,
|
||||
parent_block_proof,
|
||||
agg_root_proof,
|
||||
public_values,
|
||||
cyclic_vk,
|
||||
})
|
||||
}
|
||||
@ -404,6 +423,9 @@ where
|
||||
core::array::from_fn(|i| &by_table[i].final_circuits()[0].common);
|
||||
|
||||
let mut builder = CircuitBuilder::new(CircuitConfig::standard_recursion_config());
|
||||
|
||||
let public_values = add_virtual_public_values(&mut builder);
|
||||
|
||||
let recursive_proofs =
|
||||
core::array::from_fn(|i| builder.add_virtual_proof_with_pis(inner_common_data[i]));
|
||||
let pis: [_; NUM_TABLES] = core::array::from_fn(|i| {
|
||||
@ -453,11 +475,29 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// Extra products to add to the looked last value
|
||||
// Arithmetic, KeccakSponge, Keccak, Logic
|
||||
let mut extra_looking_products =
|
||||
vec![vec![builder.constant(F::ONE); stark_config.num_challenges]; NUM_TABLES - 1];
|
||||
|
||||
// Memory
|
||||
extra_looking_products.push(Vec::new());
|
||||
for c in 0..stark_config.num_challenges {
|
||||
extra_looking_products[Table::Memory as usize].push(
|
||||
Self::get_memory_extra_looking_products_circuit(
|
||||
&mut builder,
|
||||
&public_values,
|
||||
ctl_challenges.challenges[c],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Verify the CTL checks.
|
||||
verify_cross_table_lookups_circuit::<F, D>(
|
||||
&mut builder,
|
||||
all_cross_table_lookups(),
|
||||
pis.map(|p| p.ctl_zs_last),
|
||||
extra_looking_products,
|
||||
stark_config,
|
||||
);
|
||||
|
||||
@ -501,14 +541,164 @@ where
|
||||
circuit: builder.build::<C>(),
|
||||
proof_with_pis: recursive_proofs,
|
||||
index_verifier_data,
|
||||
public_values,
|
||||
cyclic_vk,
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursive version of `get_memory_extra_looking_products`.
|
||||
pub(crate) fn get_memory_extra_looking_products_circuit(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
public_values: &PublicValuesTarget,
|
||||
challenge: GrandProductChallenge<Target>,
|
||||
) -> Target {
|
||||
let mut prod = builder.constant(F::ONE);
|
||||
|
||||
// Add metadata writes.
|
||||
let block_fields_without_beneficiary = [
|
||||
(
|
||||
GlobalMetadata::BlockTimestamp,
|
||||
public_values.block_metadata.block_timestamp,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::BlockNumber,
|
||||
public_values.block_metadata.block_number,
|
||||
),
|
||||
(
|
||||
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,
|
||||
),
|
||||
];
|
||||
|
||||
let zero = builder.constant(F::ZERO);
|
||||
let one = builder.constant(F::ONE);
|
||||
let segment = builder.constant(F::from_canonical_u32(Segment::GlobalMetadata as u32));
|
||||
|
||||
let row = builder.add_virtual_targets(13);
|
||||
// is_read
|
||||
builder.connect(row[0], zero);
|
||||
// context
|
||||
builder.connect(row[1], zero);
|
||||
// segment
|
||||
builder.connect(row[2], segment);
|
||||
// virtual
|
||||
let field_target = builder.constant(F::from_canonical_usize(
|
||||
GlobalMetadata::BlockBeneficiary as usize,
|
||||
));
|
||||
builder.connect(row[3], field_target);
|
||||
// values
|
||||
for j in 0..5 {
|
||||
builder.connect(
|
||||
row[4 + j],
|
||||
public_values.block_metadata.block_beneficiary[j],
|
||||
);
|
||||
}
|
||||
for j in 5..VALUE_LIMBS {
|
||||
builder.connect(row[4 + j], zero);
|
||||
}
|
||||
// timestamp
|
||||
builder.connect(row[12], one);
|
||||
|
||||
let combined = challenge.combine_base_circuit(builder, &row);
|
||||
prod = builder.mul(prod, combined);
|
||||
|
||||
block_fields_without_beneficiary.map(|(field, target)| {
|
||||
let row = builder.add_virtual_targets(13);
|
||||
// is_read
|
||||
builder.connect(row[0], zero);
|
||||
// context
|
||||
builder.connect(row[1], zero);
|
||||
// segment
|
||||
builder.connect(row[2], segment);
|
||||
// virtual
|
||||
let field_target = builder.constant(F::from_canonical_usize(field as usize));
|
||||
builder.connect(row[3], field_target);
|
||||
// These values only have one cell.
|
||||
builder.connect(row[4], target);
|
||||
for j in 1..VALUE_LIMBS {
|
||||
builder.connect(row[4 + j], zero);
|
||||
}
|
||||
// timestamp
|
||||
builder.connect(row[12], one);
|
||||
let combined = challenge.combine_base_circuit(builder, &row);
|
||||
prod = builder.mul(prod, combined);
|
||||
});
|
||||
|
||||
// Add public values reads.
|
||||
let trie_fields = [
|
||||
(
|
||||
GlobalMetadata::StateTrieRootDigestBefore,
|
||||
public_values.trie_roots_before.state_root,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::TransactionTrieRootDigestBefore,
|
||||
public_values.trie_roots_before.transactions_root,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::ReceiptTrieRootDigestBefore,
|
||||
public_values.trie_roots_before.receipts_root,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::StateTrieRootDigestAfter,
|
||||
public_values.trie_roots_after.state_root,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::TransactionTrieRootDigestAfter,
|
||||
public_values.trie_roots_after.transactions_root,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::ReceiptTrieRootDigestAfter,
|
||||
public_values.trie_roots_after.receipts_root,
|
||||
),
|
||||
];
|
||||
|
||||
let mut timestamp_target = builder.mul_const(
|
||||
F::from_canonical_usize(NUM_CHANNELS),
|
||||
public_values.cpu_trace_len,
|
||||
);
|
||||
timestamp_target = builder.add_const(timestamp_target, F::ONE);
|
||||
trie_fields.map(|(field, targets)| {
|
||||
let row = builder.add_virtual_targets(13);
|
||||
// is_read
|
||||
builder.connect(row[0], one);
|
||||
// context
|
||||
builder.connect(row[1], zero);
|
||||
// segment
|
||||
builder.connect(row[2], segment);
|
||||
// virtual
|
||||
let field_target = builder.constant(F::from_canonical_usize(field as usize));
|
||||
builder.connect(row[3], field_target);
|
||||
// values
|
||||
for j in 0..VALUE_LIMBS {
|
||||
builder.connect(row[4 + j], targets[j]);
|
||||
}
|
||||
// timestamp
|
||||
builder.connect(row[12], timestamp_target);
|
||||
|
||||
let combined = challenge.combine_base_circuit(builder, &row);
|
||||
prod = builder.mul(prod, combined);
|
||||
});
|
||||
|
||||
prod
|
||||
}
|
||||
|
||||
fn create_aggregation_circuit(
|
||||
root: &RootCircuitData<F, C, D>,
|
||||
) -> AggregationCircuitData<F, C, D> {
|
||||
let mut builder = CircuitBuilder::<F, D>::new(root.circuit.common.config.clone());
|
||||
let public_values = add_virtual_public_values(&mut builder);
|
||||
let cyclic_vk = builder.add_verifier_data_public_inputs();
|
||||
let lhs = Self::add_agg_child(&mut builder, root);
|
||||
let rhs = Self::add_agg_child(&mut builder, root);
|
||||
@ -523,6 +713,7 @@ where
|
||||
circuit,
|
||||
lhs,
|
||||
rhs,
|
||||
public_values,
|
||||
cyclic_vk,
|
||||
}
|
||||
}
|
||||
@ -560,6 +751,7 @@ where
|
||||
};
|
||||
|
||||
let mut builder = CircuitBuilder::<F, D>::new(CircuitConfig::standard_recursion_config());
|
||||
let public_values = add_virtual_public_values(&mut builder);
|
||||
let has_parent_block = builder.add_virtual_bool_target_safe();
|
||||
let parent_block_proof = builder.add_virtual_proof_with_pis(&expected_common_data);
|
||||
let agg_root_proof = builder.add_virtual_proof_with_pis(&agg.circuit.common);
|
||||
@ -582,6 +774,7 @@ where
|
||||
has_parent_block,
|
||||
parent_block_proof,
|
||||
agg_root_proof,
|
||||
public_values,
|
||||
cyclic_vk,
|
||||
}
|
||||
}
|
||||
@ -593,7 +786,7 @@ where
|
||||
config: &StarkConfig,
|
||||
generation_inputs: GenerationInputs,
|
||||
timing: &mut TimingTree,
|
||||
) -> anyhow::Result<ProofWithPublicInputs<F, C, D>> {
|
||||
) -> anyhow::Result<(ProofWithPublicInputs<F, C, D>, PublicValues)> {
|
||||
let all_proof = prove::<F, C, D>(all_stark, config, generation_inputs, timing)?;
|
||||
let mut root_inputs = PartialWitness::new();
|
||||
|
||||
@ -620,7 +813,30 @@ where
|
||||
&self.aggregation.circuit.verifier_only,
|
||||
);
|
||||
|
||||
self.root.circuit.prove(root_inputs)
|
||||
set_block_metadata_target(
|
||||
&mut root_inputs,
|
||||
&self.root.public_values.block_metadata,
|
||||
&all_proof.public_values.block_metadata,
|
||||
);
|
||||
|
||||
root_inputs.set_target(
|
||||
self.root.public_values.cpu_trace_len,
|
||||
F::from_canonical_usize(all_proof.public_values.cpu_trace_len),
|
||||
);
|
||||
set_trie_roots_target(
|
||||
&mut root_inputs,
|
||||
&self.root.public_values.trie_roots_before,
|
||||
&all_proof.public_values.trie_roots_before,
|
||||
);
|
||||
set_trie_roots_target(
|
||||
&mut root_inputs,
|
||||
&self.root.public_values.trie_roots_after,
|
||||
&all_proof.public_values.trie_roots_after,
|
||||
);
|
||||
|
||||
let root_proof = self.root.circuit.prove(root_inputs)?;
|
||||
|
||||
Ok((root_proof, all_proof.public_values))
|
||||
}
|
||||
|
||||
pub fn verify_root(&self, agg_proof: ProofWithPublicInputs<F, C, D>) -> anyhow::Result<()> {
|
||||
@ -633,7 +849,8 @@ where
|
||||
lhs_proof: &ProofWithPublicInputs<F, C, D>,
|
||||
rhs_is_agg: bool,
|
||||
rhs_proof: &ProofWithPublicInputs<F, C, D>,
|
||||
) -> anyhow::Result<ProofWithPublicInputs<F, C, D>> {
|
||||
public_values: PublicValues,
|
||||
) -> anyhow::Result<(ProofWithPublicInputs<F, C, D>, PublicValues)> {
|
||||
let mut agg_inputs = PartialWitness::new();
|
||||
|
||||
agg_inputs.set_bool_target(self.aggregation.lhs.is_agg, lhs_is_agg);
|
||||
@ -649,7 +866,29 @@ where
|
||||
&self.aggregation.circuit.verifier_only,
|
||||
);
|
||||
|
||||
self.aggregation.circuit.prove(agg_inputs)
|
||||
set_block_metadata_target(
|
||||
&mut agg_inputs,
|
||||
&self.aggregation.public_values.block_metadata,
|
||||
&public_values.block_metadata,
|
||||
);
|
||||
|
||||
agg_inputs.set_target(
|
||||
self.aggregation.public_values.cpu_trace_len,
|
||||
F::from_canonical_usize(public_values.cpu_trace_len),
|
||||
);
|
||||
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,
|
||||
);
|
||||
|
||||
let aggregation_proof = self.aggregation.circuit.prove(agg_inputs)?;
|
||||
Ok((aggregation_proof, public_values))
|
||||
}
|
||||
|
||||
pub fn verify_aggregation(
|
||||
@ -668,7 +907,8 @@ where
|
||||
&self,
|
||||
opt_parent_block_proof: Option<&ProofWithPublicInputs<F, C, D>>,
|
||||
agg_root_proof: &ProofWithPublicInputs<F, C, D>,
|
||||
) -> anyhow::Result<ProofWithPublicInputs<F, C, D>> {
|
||||
public_values: PublicValues,
|
||||
) -> anyhow::Result<(ProofWithPublicInputs<F, C, D>, PublicValues)> {
|
||||
let mut block_inputs = PartialWitness::new();
|
||||
|
||||
block_inputs.set_bool_target(
|
||||
@ -694,7 +934,29 @@ where
|
||||
block_inputs
|
||||
.set_verifier_data_target(&self.block.cyclic_vk, &self.block.circuit.verifier_only);
|
||||
|
||||
self.block.circuit.prove(block_inputs)
|
||||
set_block_metadata_target(
|
||||
&mut block_inputs,
|
||||
&self.block.public_values.block_metadata,
|
||||
&public_values.block_metadata,
|
||||
);
|
||||
|
||||
block_inputs.set_target(
|
||||
self.block.public_values.cpu_trace_len,
|
||||
F::from_canonical_usize(public_values.cpu_trace_len),
|
||||
);
|
||||
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,
|
||||
);
|
||||
|
||||
let block_proof = self.block.circuit.prove(block_inputs)?;
|
||||
Ok((block_proof, public_values))
|
||||
}
|
||||
|
||||
pub fn verify_block(&self, block_proof: &ProofWithPublicInputs<F, C, D>) -> anyhow::Result<()> {
|
||||
|
||||
@ -22,7 +22,7 @@ use crate::generation::outputs::{get_outputs, GenerationOutputs};
|
||||
use crate::generation::state::GenerationState;
|
||||
use crate::memory::segments::Segment;
|
||||
use crate::proof::{BlockMetadata, PublicValues, TrieRoots};
|
||||
use crate::witness::memory::{MemoryAddress, MemoryChannel};
|
||||
use crate::witness::memory::{MemoryAddress, MemoryChannel, MemoryOp, MemoryOpKind};
|
||||
use crate::witness::transition::transition;
|
||||
|
||||
pub mod mpt;
|
||||
@ -38,7 +38,6 @@ use crate::witness::util::mem_write_log;
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, Default)]
|
||||
pub struct GenerationInputs {
|
||||
pub signed_txns: Vec<Vec<u8>>,
|
||||
|
||||
pub tries: TrieInputs,
|
||||
|
||||
/// Mapping between smart contract code hashes and the contract byte code.
|
||||
@ -105,7 +104,55 @@ fn apply_metadata_memops<F: RichField + Extendable<D>, const D: usize>(
|
||||
state.traces.memory_ops.extend(ops);
|
||||
}
|
||||
|
||||
pub(crate) fn generate_traces<F: RichField + Extendable<D>, const D: usize>(
|
||||
fn apply_trie_memops<F: RichField + Extendable<D>, const D: usize>(
|
||||
state: &mut GenerationState<F>,
|
||||
trie_roots_before: &TrieRoots,
|
||||
trie_roots_after: &TrieRoots,
|
||||
) {
|
||||
let fields = [
|
||||
(
|
||||
GlobalMetadata::StateTrieRootDigestBefore,
|
||||
trie_roots_before.state_root,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::TransactionTrieRootDigestBefore,
|
||||
trie_roots_before.transactions_root,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::ReceiptTrieRootDigestBefore,
|
||||
trie_roots_before.receipts_root,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::StateTrieRootDigestAfter,
|
||||
trie_roots_after.state_root,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::TransactionTrieRootDigestAfter,
|
||||
trie_roots_after.transactions_root,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::ReceiptTrieRootDigestAfter,
|
||||
trie_roots_after.receipts_root,
|
||||
),
|
||||
];
|
||||
|
||||
let channel = MemoryChannel::GeneralPurpose(0);
|
||||
|
||||
let ops = fields.map(|(field, hash)| {
|
||||
let val = hash.into_uint();
|
||||
MemoryOp::new(
|
||||
channel,
|
||||
state.traces.cpu.len(),
|
||||
MemoryAddress::new(0, Segment::GlobalMetadata, field as usize),
|
||||
MemoryOpKind::Read,
|
||||
val,
|
||||
)
|
||||
});
|
||||
|
||||
state.traces.memory_ops.extend(ops);
|
||||
}
|
||||
|
||||
pub fn generate_traces<F: RichField + Extendable<D>, const D: usize>(
|
||||
all_stark: &AllStark<F, D>,
|
||||
inputs: GenerationInputs,
|
||||
config: &StarkConfig,
|
||||
@ -147,10 +194,13 @@ pub(crate) fn generate_traces<F: RichField + Extendable<D>, const D: usize>(
|
||||
receipts_root: H256::from_uint(&read_metadata(ReceiptTrieRootDigestAfter)),
|
||||
};
|
||||
|
||||
apply_trie_memops(&mut state, &trie_roots_before, &trie_roots_after);
|
||||
|
||||
let public_values = PublicValues {
|
||||
trie_roots_before,
|
||||
trie_roots_after,
|
||||
block_metadata: inputs.block_metadata,
|
||||
cpu_trace_len: state.traces.clock(),
|
||||
};
|
||||
|
||||
let tables = timed!(
|
||||
|
||||
@ -9,6 +9,7 @@ pub(crate) const KECCAK_RATE_BYTES: usize = 136;
|
||||
pub(crate) const KECCAK_RATE_U32S: usize = KECCAK_RATE_BYTES / 4;
|
||||
pub(crate) const KECCAK_CAPACITY_BYTES: usize = 64;
|
||||
pub(crate) const KECCAK_CAPACITY_U32S: usize = KECCAK_CAPACITY_BYTES / 4;
|
||||
pub(crate) const KECCAK_DIGEST_BYTES: usize = 32;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
@ -53,6 +54,8 @@ pub(crate) struct KeccakSpongeColumnsView<T: Copy> {
|
||||
/// The entire state (rate + capacity) of the sponge, encoded as 32-bit chunks, after the
|
||||
/// permutation is applied.
|
||||
pub updated_state_u32s: [T; KECCAK_WIDTH_U32S],
|
||||
|
||||
pub updated_state_bytes: [T; KECCAK_DIGEST_BYTES],
|
||||
}
|
||||
|
||||
// `u8` is guaranteed to have a `size_of` of 1.
|
||||
|
||||
@ -25,7 +25,17 @@ use crate::witness::memory::MemoryAddress;
|
||||
|
||||
pub(crate) fn ctl_looked_data<F: Field>() -> Vec<Column<F>> {
|
||||
let cols = KECCAK_SPONGE_COL_MAP;
|
||||
let outputs = Column::singles(&cols.updated_state_u32s[..8]);
|
||||
let mut outputs = vec![];
|
||||
for i in (0..8).rev() {
|
||||
let cur_col = Column::linear_combination(
|
||||
cols.updated_state_bytes[i * 4..(i + 1) * 4]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(j, &c)| (c, F::from_canonical_u64(1 << (24 - 8 * j)))),
|
||||
);
|
||||
outputs.push(cur_col);
|
||||
}
|
||||
|
||||
Column::singles([
|
||||
cols.context,
|
||||
cols.segment,
|
||||
@ -137,7 +147,11 @@ pub(crate) fn ctl_looking_memory_filter<F: Field>(i: usize) -> Column<F> {
|
||||
// - this is a full input block, or
|
||||
// - this is a final block of length `i` or greater
|
||||
let cols = KECCAK_SPONGE_COL_MAP;
|
||||
Column::sum(once(&cols.is_full_input_block).chain(&cols.is_final_input_len[i..]))
|
||||
if i == KECCAK_RATE_BYTES - 1 {
|
||||
Column::single(cols.is_full_input_block)
|
||||
} else {
|
||||
Column::sum(once(&cols.is_full_input_block).chain(&cols.is_final_input_len[i + 1..]))
|
||||
}
|
||||
}
|
||||
|
||||
/// CTL filter for looking at XORs in the logic table.
|
||||
@ -342,6 +356,25 @@ impl<F: RichField + Extendable<D>, const D: usize> KeccakSpongeStark<F, D> {
|
||||
|
||||
keccakf_u32s(&mut sponge_state);
|
||||
row.updated_state_u32s = sponge_state.map(F::from_canonical_u32);
|
||||
let is_final_block = row.is_final_input_len.iter().copied().sum::<F>() == F::ONE;
|
||||
if is_final_block {
|
||||
for (l, &elt) in row.updated_state_u32s[..8].iter().enumerate() {
|
||||
let mut cur_bytes = vec![F::ZERO; 4];
|
||||
let mut cur_elt = elt;
|
||||
for i in 0..4 {
|
||||
cur_bytes[i] =
|
||||
F::from_canonical_u32((cur_elt.to_canonical_u64() & 0xFF) as u32);
|
||||
cur_elt = F::from_canonical_u64(cur_elt.to_canonical_u64() >> 8);
|
||||
row.updated_state_bytes[l * 4 + i] = cur_bytes[i];
|
||||
}
|
||||
|
||||
let mut s = row.updated_state_bytes[l * 4].to_canonical_u64();
|
||||
for i in 1..4 {
|
||||
s += row.updated_state_bytes[l * 4 + i].to_canonical_u64() << (8 * i);
|
||||
}
|
||||
assert_eq!(elt, F::from_canonical_u64(s), "not equal");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_padding_row(&self) -> [F; NUM_KECCAK_SPONGE_COLUMNS] {
|
||||
@ -448,6 +481,16 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for KeccakSpongeS
|
||||
let entry_match = offset - P::from(FE::from_canonical_usize(i));
|
||||
yield_constr.constraint(is_final_len * entry_match);
|
||||
}
|
||||
|
||||
// Adding constraints for byte columns.
|
||||
for (l, &elt) in local_values.updated_state_u32s[..8].iter().enumerate() {
|
||||
let mut s = local_values.updated_state_bytes[l * 4];
|
||||
for i in 1..4 {
|
||||
s += local_values.updated_state_bytes[l * 4 + i]
|
||||
* P::from(FE::from_canonical_usize(1 << (8 * i)));
|
||||
}
|
||||
yield_constr.constraint(is_final_block * (s - elt));
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_ext_circuit(
|
||||
@ -572,6 +615,21 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for KeccakSpongeS
|
||||
let constraint = builder.mul_extension(is_final_len, entry_match);
|
||||
yield_constr.constraint(builder, constraint);
|
||||
}
|
||||
|
||||
// Adding constraints for byte columns.
|
||||
for (l, &elt) in local_values.updated_state_u32s[..8].iter().enumerate() {
|
||||
let mut s = local_values.updated_state_bytes[l * 4];
|
||||
for i in 1..4 {
|
||||
s = builder.mul_const_add_extension(
|
||||
F::from_canonical_usize(1 << (8 * i)),
|
||||
local_values.updated_state_bytes[l * 4 + i],
|
||||
s,
|
||||
);
|
||||
}
|
||||
let constraint = builder.sub_extension(s, elt);
|
||||
let constraint = builder.mul_extension(is_final_block, constraint);
|
||||
yield_constr.constraint(builder, constraint);
|
||||
}
|
||||
}
|
||||
|
||||
fn constraint_degree(&self) -> usize {
|
||||
|
||||
@ -14,7 +14,9 @@ use plonky2::iop::ext_target::ExtensionTarget;
|
||||
use plonky2::iop::target::Target;
|
||||
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::plonk::plonk_common::{
|
||||
reduce_with_powers, reduce_with_powers_circuit, reduce_with_powers_ext_circuit,
|
||||
};
|
||||
use plonky2::util::reducing::{ReducingFactor, ReducingFactorTarget};
|
||||
use plonky2::util::serialization::{Buffer, IoResult, Read, Write};
|
||||
use plonky2_maybe_rayon::*;
|
||||
@ -83,6 +85,17 @@ impl GrandProductChallenge<Target> {
|
||||
}
|
||||
}
|
||||
|
||||
impl GrandProductChallenge<Target> {
|
||||
pub(crate) fn combine_base_circuit<F: RichField + Extendable<D>, const D: usize>(
|
||||
&self,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
terms: &[Target],
|
||||
) -> Target {
|
||||
let reduced = reduce_with_powers_circuit(builder, terms, self.beta);
|
||||
builder.add(reduced, self.gamma)
|
||||
}
|
||||
}
|
||||
|
||||
/// Like `PermutationChallenge`, but with `num_challenges` copies to boost soundness.
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
pub(crate) struct GrandProductChallengeSet<T: Copy + Eq + PartialEq + Debug> {
|
||||
|
||||
@ -52,9 +52,10 @@ pub struct PublicValues {
|
||||
pub trie_roots_before: TrieRoots,
|
||||
pub trie_roots_after: TrieRoots,
|
||||
pub block_metadata: BlockMetadata,
|
||||
pub cpu_trace_len: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct TrieRoots {
|
||||
pub state_root: H256,
|
||||
pub transactions_root: H256,
|
||||
@ -74,18 +75,101 @@ pub struct BlockMetadata {
|
||||
|
||||
/// Memory values which are public.
|
||||
/// Note: All the larger integers are encoded with 32-bit limbs in little-endian order.
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub struct PublicValuesTarget {
|
||||
pub trie_roots_before: TrieRootsTarget,
|
||||
pub trie_roots_after: TrieRootsTarget,
|
||||
pub block_metadata: BlockMetadataTarget,
|
||||
pub cpu_trace_len: Target,
|
||||
}
|
||||
|
||||
impl PublicValuesTarget {
|
||||
pub fn to_buffer(&self, buffer: &mut Vec<u8>) -> IoResult<()> {
|
||||
let TrieRootsTarget {
|
||||
state_root: state_root_before,
|
||||
transactions_root: transactions_root_before,
|
||||
receipts_root: receipts_root_before,
|
||||
} = self.trie_roots_before;
|
||||
|
||||
buffer.write_target_vec(&state_root_before)?;
|
||||
buffer.write_target_vec(&transactions_root_before)?;
|
||||
buffer.write_target_vec(&receipts_root_before)?;
|
||||
|
||||
let TrieRootsTarget {
|
||||
state_root: state_root_after,
|
||||
transactions_root: transactions_root_after,
|
||||
receipts_root: receipts_root_after,
|
||||
} = self.trie_roots_after;
|
||||
|
||||
buffer.write_target_vec(&state_root_after)?;
|
||||
buffer.write_target_vec(&transactions_root_after)?;
|
||||
buffer.write_target_vec(&receipts_root_after)?;
|
||||
|
||||
let BlockMetadataTarget {
|
||||
block_beneficiary,
|
||||
block_timestamp,
|
||||
block_number,
|
||||
block_difficulty,
|
||||
block_gaslimit,
|
||||
block_chain_id,
|
||||
block_base_fee,
|
||||
} = self.block_metadata;
|
||||
|
||||
buffer.write_target_vec(&block_beneficiary)?;
|
||||
buffer.write_target(block_timestamp)?;
|
||||
buffer.write_target(block_number)?;
|
||||
buffer.write_target(block_difficulty)?;
|
||||
buffer.write_target(block_gaslimit)?;
|
||||
buffer.write_target(block_chain_id)?;
|
||||
buffer.write_target(block_base_fee)?;
|
||||
|
||||
buffer.write_target(self.cpu_trace_len)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn from_buffer(buffer: &mut Buffer) -> IoResult<Self> {
|
||||
let trie_roots_before = TrieRootsTarget {
|
||||
state_root: buffer.read_target_vec()?.try_into().unwrap(),
|
||||
transactions_root: buffer.read_target_vec()?.try_into().unwrap(),
|
||||
receipts_root: buffer.read_target_vec()?.try_into().unwrap(),
|
||||
};
|
||||
|
||||
let trie_roots_after = TrieRootsTarget {
|
||||
state_root: buffer.read_target_vec()?.try_into().unwrap(),
|
||||
transactions_root: buffer.read_target_vec()?.try_into().unwrap(),
|
||||
receipts_root: buffer.read_target_vec()?.try_into().unwrap(),
|
||||
};
|
||||
|
||||
let block_metadata = BlockMetadataTarget {
|
||||
block_beneficiary: buffer.read_target_vec()?.try_into().unwrap(),
|
||||
block_timestamp: buffer.read_target()?,
|
||||
block_number: buffer.read_target()?,
|
||||
block_difficulty: buffer.read_target()?,
|
||||
block_gaslimit: buffer.read_target()?,
|
||||
block_chain_id: buffer.read_target()?,
|
||||
block_base_fee: buffer.read_target()?,
|
||||
};
|
||||
|
||||
let cpu_trace_len = buffer.read_target()?;
|
||||
|
||||
Ok(Self {
|
||||
trie_roots_before,
|
||||
trie_roots_after,
|
||||
block_metadata,
|
||||
cpu_trace_len,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub struct TrieRootsTarget {
|
||||
pub state_root: [Target; 8],
|
||||
pub transactions_root: [Target; 8],
|
||||
pub receipts_root: [Target; 8],
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub struct BlockMetadataTarget {
|
||||
pub block_beneficiary: [Target; 5],
|
||||
pub block_timestamp: Target,
|
||||
@ -119,7 +203,8 @@ where
|
||||
C: GenericConfig<D, F = F>,
|
||||
{
|
||||
pub(crate) init_challenger_state: <C::Hasher as Hasher<F>>::Permutation,
|
||||
pub(crate) proof: StarkProof<F, C, D>,
|
||||
// TODO: set it back to pub(crate) when cpu trace len is a public input
|
||||
pub proof: StarkProof<F, C, D>,
|
||||
}
|
||||
|
||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> StarkProof<F, C, D> {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use anyhow::{ensure, Result};
|
||||
use ethereum_types::BigEndianHash;
|
||||
use plonky2::field::extension::Extendable;
|
||||
use plonky2::field::types::Field;
|
||||
use plonky2::fri::witness_util::set_fri_proof_target;
|
||||
@ -38,7 +39,7 @@ use crate::proof::{
|
||||
TrieRootsTarget,
|
||||
};
|
||||
use crate::stark::Stark;
|
||||
use crate::util::{h160_limbs, h256_limbs};
|
||||
use crate::util::h160_limbs;
|
||||
use crate::vanishing_poly::eval_vanishing_poly_circuit;
|
||||
use crate::vars::StarkEvaluationTargets;
|
||||
|
||||
@ -129,10 +130,21 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
||||
ensure!(pis[i].challenger_state_before == pis[i - 1].challenger_state_after);
|
||||
}
|
||||
|
||||
// Dummy values which will make the check fail.
|
||||
// TODO: Fix this if the code isn't deprecated.
|
||||
let mut extra_looking_products = Vec::new();
|
||||
for i in 0..NUM_TABLES {
|
||||
extra_looking_products.push(Vec::new());
|
||||
for _ in 0..inner_config.num_challenges {
|
||||
extra_looking_products[i].push(F::ONE);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the CTL checks.
|
||||
verify_cross_table_lookups::<F, D>(
|
||||
&cross_table_lookups,
|
||||
pis.map(|p| p.ctl_zs_last),
|
||||
extra_looking_products,
|
||||
inner_config,
|
||||
)?;
|
||||
|
||||
@ -498,26 +510,27 @@ fn eval_l_0_and_l_last_circuit<F: RichField + Extendable<D>, const D: usize>(
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(unused)] // TODO: used later?
|
||||
pub(crate) fn add_virtual_public_values<F: RichField + Extendable<D>, const D: usize>(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
) -> PublicValuesTarget {
|
||||
let trie_roots_before = add_virtual_trie_roots(builder);
|
||||
let trie_roots_after = add_virtual_trie_roots(builder);
|
||||
let block_metadata = add_virtual_block_metadata(builder);
|
||||
let cpu_trace_len = builder.add_virtual_public_input();
|
||||
PublicValuesTarget {
|
||||
trie_roots_before,
|
||||
trie_roots_after,
|
||||
block_metadata,
|
||||
cpu_trace_len,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_virtual_trie_roots<F: RichField + Extendable<D>, const D: usize>(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
) -> TrieRootsTarget {
|
||||
let state_root = builder.add_virtual_target_arr();
|
||||
let transactions_root = builder.add_virtual_target_arr();
|
||||
let receipts_root = builder.add_virtual_target_arr();
|
||||
let state_root = builder.add_virtual_public_input_arr();
|
||||
let transactions_root = builder.add_virtual_public_input_arr();
|
||||
let receipts_root = builder.add_virtual_public_input_arr();
|
||||
TrieRootsTarget {
|
||||
state_root,
|
||||
transactions_root,
|
||||
@ -528,13 +541,13 @@ pub(crate) fn add_virtual_trie_roots<F: RichField + Extendable<D>, const D: usiz
|
||||
pub(crate) fn add_virtual_block_metadata<F: RichField + Extendable<D>, const D: usize>(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
) -> BlockMetadataTarget {
|
||||
let block_beneficiary = builder.add_virtual_target_arr();
|
||||
let block_timestamp = builder.add_virtual_target();
|
||||
let block_number = builder.add_virtual_target();
|
||||
let block_difficulty = builder.add_virtual_target();
|
||||
let block_gaslimit = builder.add_virtual_target();
|
||||
let block_chain_id = builder.add_virtual_target();
|
||||
let block_base_fee = builder.add_virtual_target();
|
||||
let block_beneficiary = builder.add_virtual_public_input_arr();
|
||||
let block_timestamp = builder.add_virtual_public_input();
|
||||
let block_number = builder.add_virtual_public_input();
|
||||
let block_difficulty = builder.add_virtual_public_input();
|
||||
let block_gaslimit = builder.add_virtual_public_input();
|
||||
let block_chain_id = builder.add_virtual_public_input();
|
||||
let block_base_fee = builder.add_virtual_public_input();
|
||||
BlockMetadataTarget {
|
||||
block_beneficiary,
|
||||
block_timestamp,
|
||||
@ -657,18 +670,50 @@ pub(crate) fn set_trie_roots_target<F, W, const D: usize>(
|
||||
F: RichField + Extendable<D>,
|
||||
W: Witness<F>,
|
||||
{
|
||||
witness.set_target_arr(
|
||||
&trie_roots_target.state_root,
|
||||
&h256_limbs(trie_roots.state_root),
|
||||
);
|
||||
witness.set_target_arr(
|
||||
&trie_roots_target.transactions_root,
|
||||
&h256_limbs(trie_roots.transactions_root),
|
||||
);
|
||||
witness.set_target_arr(
|
||||
&trie_roots_target.receipts_root,
|
||||
&h256_limbs(trie_roots.receipts_root),
|
||||
);
|
||||
for (i, limb) in trie_roots.state_root.into_uint().0.into_iter().enumerate() {
|
||||
witness.set_target(
|
||||
trie_roots_target.state_root[2 * i],
|
||||
F::from_canonical_u32(limb as u32),
|
||||
);
|
||||
witness.set_target(
|
||||
trie_roots_target.state_root[2 * i + 1],
|
||||
F::from_canonical_u32((limb >> 32) as u32),
|
||||
);
|
||||
}
|
||||
|
||||
for (i, limb) in trie_roots
|
||||
.transactions_root
|
||||
.into_uint()
|
||||
.0
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
witness.set_target(
|
||||
trie_roots_target.transactions_root[2 * i],
|
||||
F::from_canonical_u32(limb as u32),
|
||||
);
|
||||
witness.set_target(
|
||||
trie_roots_target.transactions_root[2 * i + 1],
|
||||
F::from_canonical_u32((limb >> 32) as u32),
|
||||
);
|
||||
}
|
||||
|
||||
for (i, limb) in trie_roots
|
||||
.receipts_root
|
||||
.into_uint()
|
||||
.0
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
witness.set_target(
|
||||
trie_roots_target.receipts_root[2 * i],
|
||||
F::from_canonical_u32(limb as u32),
|
||||
);
|
||||
witness.set_target(
|
||||
trie_roots_target.receipts_root[2 * i + 1],
|
||||
F::from_canonical_u32((limb >> 32) as u32),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_block_metadata_target<F, W, const D: usize>(
|
||||
|
||||
@ -61,6 +61,7 @@ pub(crate) fn u256_limbs<F: Field>(u256: U256) -> [F; 8] {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
/// Returns the 32-bit little-endian limbs of a `H256`.
|
||||
pub(crate) fn h256_limbs<F: Field>(h256: H256) -> [F; 8] {
|
||||
h256.0
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use std::any::type_name;
|
||||
|
||||
use anyhow::{ensure, Result};
|
||||
use ethereum_types::{BigEndianHash, U256};
|
||||
use plonky2::field::extension::{Extendable, FieldExtension};
|
||||
use plonky2::field::types::Field;
|
||||
use plonky2::fri::verifier::verify_fri_proof;
|
||||
@ -8,19 +9,22 @@ use plonky2::hash::hash_types::RichField;
|
||||
use plonky2::plonk::config::GenericConfig;
|
||||
use plonky2::plonk::plonk_common::reduce_with_powers;
|
||||
|
||||
use crate::all_stark::{AllStark, Table};
|
||||
use crate::all_stark::{AllStark, Table, NUM_TABLES};
|
||||
use crate::arithmetic::arithmetic_stark::ArithmeticStark;
|
||||
use crate::config::StarkConfig;
|
||||
use crate::constraint_consumer::ConstraintConsumer;
|
||||
use crate::cpu::cpu_stark::CpuStark;
|
||||
use crate::cpu::kernel::constants::global_metadata::GlobalMetadata;
|
||||
use crate::cross_table_lookup::{verify_cross_table_lookups, CtlCheckVars};
|
||||
use crate::keccak::keccak_stark::KeccakStark;
|
||||
use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeStark;
|
||||
use crate::logic::LogicStark;
|
||||
use crate::memory::memory_stark::MemoryStark;
|
||||
use crate::permutation::PermutationCheckVars;
|
||||
use crate::memory::segments::Segment;
|
||||
use crate::memory::{NUM_CHANNELS, VALUE_LIMBS};
|
||||
use crate::permutation::{GrandProductChallenge, PermutationCheckVars};
|
||||
use crate::proof::{
|
||||
AllProof, AllProofChallenges, StarkOpeningSet, StarkProof, StarkProofChallenges,
|
||||
AllProof, AllProofChallenges, PublicValues, StarkOpeningSet, StarkProof, StarkProofChallenges,
|
||||
};
|
||||
use crate::stark::Stark;
|
||||
use crate::vanishing_poly::eval_vanishing_poly;
|
||||
@ -106,13 +110,142 @@ where
|
||||
config,
|
||||
)?;
|
||||
|
||||
let public_values = all_proof.public_values;
|
||||
|
||||
// Extra products to add to the looked last value
|
||||
// Arithmetic, KeccakSponge, Keccak, Logic
|
||||
let mut extra_looking_products = vec![vec![F::ONE; config.num_challenges]; NUM_TABLES - 1];
|
||||
|
||||
// Memory
|
||||
extra_looking_products.push(Vec::new());
|
||||
let cpu_trace_len = 1 << all_proof.stark_proofs[1].proof.recover_degree_bits(config);
|
||||
for c in 0..config.num_challenges {
|
||||
extra_looking_products[Table::Memory as usize].push(get_memory_extra_looking_products(
|
||||
&public_values,
|
||||
cpu_trace_len,
|
||||
ctl_challenges.challenges[c],
|
||||
));
|
||||
}
|
||||
|
||||
verify_cross_table_lookups::<F, D>(
|
||||
cross_table_lookups,
|
||||
all_proof.stark_proofs.map(|p| p.proof.openings.ctl_zs_last),
|
||||
extra_looking_products,
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
/// Computes the extra product to multiply to the looked value. It contains memory operations not in the CPU trace:
|
||||
/// - block metadata writes before kernel bootstrapping,
|
||||
/// - public values reads at the end of the execution.
|
||||
pub(crate) fn get_memory_extra_looking_products<F, const D: usize>(
|
||||
public_values: &PublicValues,
|
||||
cpu_trace_len: usize,
|
||||
challenge: GrandProductChallenge<F>,
|
||||
) -> F
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
{
|
||||
let mut prod = F::ONE;
|
||||
|
||||
// Add metadata writes.
|
||||
let block_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::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,
|
||||
),
|
||||
];
|
||||
let is_read = F::ZERO;
|
||||
let context = F::ZERO;
|
||||
let segment = F::from_canonical_u32(Segment::GlobalMetadata as u32);
|
||||
let timestamp = F::ONE;
|
||||
|
||||
block_fields.map(|(field, val)| {
|
||||
let mut row = vec![F::ZERO; 13];
|
||||
row[0] = is_read;
|
||||
row[1] = context;
|
||||
row[2] = segment;
|
||||
row[3] = F::from_canonical_usize(field as usize);
|
||||
|
||||
for j in 0..VALUE_LIMBS {
|
||||
row[j + 4] = F::from_canonical_u32((val >> (j * 32)).low_u32());
|
||||
}
|
||||
row[12] = timestamp;
|
||||
prod *= challenge.combine(row.iter());
|
||||
});
|
||||
|
||||
// Add public values reads.
|
||||
let trie_fields = [
|
||||
(
|
||||
GlobalMetadata::StateTrieRootDigestBefore,
|
||||
public_values.trie_roots_before.state_root,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::TransactionTrieRootDigestBefore,
|
||||
public_values.trie_roots_before.transactions_root,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::ReceiptTrieRootDigestBefore,
|
||||
public_values.trie_roots_before.receipts_root,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::StateTrieRootDigestAfter,
|
||||
public_values.trie_roots_after.state_root,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::TransactionTrieRootDigestAfter,
|
||||
public_values.trie_roots_after.transactions_root,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::ReceiptTrieRootDigestAfter,
|
||||
public_values.trie_roots_after.receipts_root,
|
||||
),
|
||||
];
|
||||
let is_read = F::ONE;
|
||||
let timestamp = F::from_canonical_usize(cpu_trace_len * NUM_CHANNELS + 1);
|
||||
|
||||
trie_fields.map(|(field, hash)| {
|
||||
let mut row = vec![F::ZERO; 13];
|
||||
row[0] = is_read;
|
||||
row[1] = context;
|
||||
row[2] = segment;
|
||||
row[3] = F::from_canonical_usize(field as usize);
|
||||
|
||||
let val = hash.into_uint();
|
||||
|
||||
for j in 0..VALUE_LIMBS {
|
||||
row[j + 4] = F::from_canonical_u32((val >> (j * 32)).low_u32());
|
||||
}
|
||||
row[12] = timestamp;
|
||||
prod *= challenge.combine(row.iter());
|
||||
});
|
||||
prod
|
||||
}
|
||||
|
||||
pub(crate) fn verify_stark_proof_with_challenges<
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
|
||||
@ -141,12 +141,7 @@ pub(crate) fn generate_keccak_general<F: Field>(
|
||||
log::debug!("Hashing {:?}", input);
|
||||
|
||||
let hash = keccak(&input);
|
||||
let val_u64s: [u64; 4] =
|
||||
core::array::from_fn(|i| u64::from_le_bytes(core::array::from_fn(|j| hash.0[i * 8 + j])));
|
||||
let hash_int = U256(val_u64s);
|
||||
|
||||
let mut log_push = stack_push_log_and_fill(state, &mut row, hash_int)?;
|
||||
log_push.value = hash.into_uint();
|
||||
let log_push = stack_push_log_and_fill(state, &mut row, hash.into_uint())?;
|
||||
|
||||
keccak_sponge_log(state, base_address, input);
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ 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 eth_trie_utils::partial_trie::HashedPartialTrie;
|
||||
use keccak_hash::keccak;
|
||||
use log::info;
|
||||
use plonky2::field::goldilocks_field::GoldilocksField;
|
||||
@ -17,8 +17,6 @@ use plonky2_evm::config::StarkConfig;
|
||||
use plonky2_evm::fixed_recursive_verifier::AllRecursiveCircuits;
|
||||
use plonky2_evm::generation::{GenerationInputs, TrieInputs};
|
||||
use plonky2_evm::proof::BlockMetadata;
|
||||
use plonky2_evm::prover::prove;
|
||||
use plonky2_evm::verifier::verify_proof;
|
||||
use plonky2_evm::Node;
|
||||
|
||||
type F = GoldilocksField;
|
||||
@ -41,10 +39,6 @@ fn test_empty_txn_list() -> anyhow::Result<()> {
|
||||
let receipts_trie = HashedPartialTrie::from(Node::Empty);
|
||||
let storage_tries = vec![];
|
||||
|
||||
let state_trie_root = state_trie.hash();
|
||||
let txns_trie_root = transactions_trie.hash();
|
||||
let receipts_trie_root = receipts_trie.hash();
|
||||
|
||||
let mut contract_code = HashMap::new();
|
||||
contract_code.insert(keccak(vec![]), vec![]);
|
||||
|
||||
@ -61,40 +55,6 @@ fn test_empty_txn_list() -> anyhow::Result<()> {
|
||||
addresses: vec![],
|
||||
};
|
||||
|
||||
let mut timing = TimingTree::new("prove", log::Level::Debug);
|
||||
// TODO: This is redundant; prove_root below calls this prove method internally.
|
||||
// Just keeping it for now because the root proof returned by prove_root doesn't contain public
|
||||
// values yet, and we want those for the assertions below.
|
||||
let proof = prove::<F, C, D>(&all_stark, &config, inputs.clone(), &mut timing)?;
|
||||
timing.filter(Duration::from_millis(100)).print();
|
||||
|
||||
assert_eq!(
|
||||
proof.public_values.trie_roots_before.state_root,
|
||||
state_trie_root
|
||||
);
|
||||
assert_eq!(
|
||||
proof.public_values.trie_roots_after.state_root,
|
||||
state_trie_root
|
||||
);
|
||||
assert_eq!(
|
||||
proof.public_values.trie_roots_before.transactions_root,
|
||||
txns_trie_root
|
||||
);
|
||||
assert_eq!(
|
||||
proof.public_values.trie_roots_after.transactions_root,
|
||||
txns_trie_root
|
||||
);
|
||||
assert_eq!(
|
||||
proof.public_values.trie_roots_before.receipts_root,
|
||||
receipts_trie_root
|
||||
);
|
||||
assert_eq!(
|
||||
proof.public_values.trie_roots_after.receipts_root,
|
||||
receipts_trie_root
|
||||
);
|
||||
|
||||
verify_proof(&all_stark, proof, &config)?;
|
||||
|
||||
let all_circuits = AllRecursiveCircuits::<F, C, D>::new(
|
||||
&all_stark,
|
||||
&[16..17, 14..15, 14..15, 9..10, 12..13, 18..19], // Minimal ranges to prove an empty list
|
||||
@ -130,12 +90,18 @@ fn test_empty_txn_list() -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
let mut timing = TimingTree::new("prove", log::Level::Info);
|
||||
let root_proof = all_circuits.prove_root(&all_stark, &config, inputs, &mut timing)?;
|
||||
let (root_proof, public_values) =
|
||||
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)?;
|
||||
all_circuits.verify_aggregation(&agg_proof)
|
||||
// We can duplicate the proofs here because the state hasn't mutated.
|
||||
let (agg_proof, public_values) =
|
||||
all_circuits.prove_aggregation(false, &root_proof, false, &root_proof, public_values)?;
|
||||
all_circuits.verify_aggregation(&agg_proof)?;
|
||||
|
||||
let (block_proof, _) = all_circuits.prove_block(None, &agg_proof, public_values)?;
|
||||
all_circuits.verify_block(&block_proof)
|
||||
}
|
||||
|
||||
fn init_logger() {
|
||||
|
||||
@ -323,6 +323,12 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
t
|
||||
}
|
||||
|
||||
pub fn add_virtual_public_input_arr<const N: usize>(&mut self) -> [Target; N] {
|
||||
let ts = [0; N].map(|_| self.add_virtual_target());
|
||||
self.register_public_inputs(&ts);
|
||||
ts
|
||||
}
|
||||
|
||||
pub fn add_virtual_verifier_data(&mut self, cap_height: usize) -> VerifierCircuitTarget {
|
||||
VerifierCircuitTarget {
|
||||
constants_sigmas_cap: self.add_virtual_cap(cap_height),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user