plonky2/evm/src/proof.rs
BGluth 8e08b218d2 Trie roots now use H256 instead of U256
- `H256` preserves any leading `0`s, which could be critical in some
  situations. Also just a slightly more appropriate type for hashes.
2022-09-29 15:56:09 -06:00

299 lines
10 KiB
Rust

use ethereum_types::{Address, H256, U256};
use itertools::Itertools;
use maybe_rayon::*;
use plonky2::field::extension::{Extendable, FieldExtension};
use plonky2::fri::oracle::PolynomialBatch;
use plonky2::fri::proof::{FriChallenges, FriChallengesTarget, FriProof, FriProofTarget};
use plonky2::fri::structure::{
FriOpeningBatch, FriOpeningBatchTarget, FriOpenings, FriOpeningsTarget,
};
use plonky2::hash::hash_types::{MerkleCapTarget, RichField};
use plonky2::hash::merkle_tree::MerkleCap;
use plonky2::iop::ext_target::ExtensionTarget;
use plonky2::iop::target::Target;
use plonky2::plonk::config::GenericConfig;
use serde::{Deserialize, Serialize};
use crate::all_stark::NUM_TABLES;
use crate::config::StarkConfig;
use crate::permutation::GrandProductChallengeSet;
#[derive(Debug, Clone)]
pub struct AllProof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> {
pub stark_proofs: [StarkProof<F, C, D>; NUM_TABLES],
pub public_values: PublicValues,
}
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> AllProof<F, C, D> {
pub fn degree_bits(&self, config: &StarkConfig) -> [usize; NUM_TABLES] {
std::array::from_fn(|i| self.stark_proofs[i].recover_degree_bits(config))
}
pub fn nums_ctl_zs(&self) -> [usize; NUM_TABLES] {
std::array::from_fn(|i| self.stark_proofs[i].openings.ctl_zs_last.len())
}
}
pub(crate) struct AllProofChallenges<F: RichField + Extendable<D>, const D: usize> {
pub stark_challenges: [StarkProofChallenges<F, D>; NUM_TABLES],
pub ctl_challenges: GrandProductChallengeSet<F>,
}
pub struct AllProofTarget<const D: usize> {
pub stark_proofs: [StarkProofTarget<D>; NUM_TABLES],
pub public_values: PublicValuesTarget,
}
/// Memory values which are public.
#[derive(Debug, Clone, Default)]
pub struct PublicValues {
pub trie_roots_before: TrieRoots,
pub trie_roots_after: TrieRoots,
pub block_metadata: BlockMetadata,
}
#[derive(Debug, Clone, Default)]
pub struct TrieRoots {
pub state_root: H256,
pub transactions_root: H256,
pub receipts_root: H256,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct BlockMetadata {
pub block_beneficiary: Address,
pub block_timestamp: U256,
pub block_number: U256,
pub block_difficulty: U256,
pub block_gaslimit: U256,
pub block_chain_id: U256,
pub block_base_fee: U256,
}
/// Memory values which are public.
/// Note: All the larger integers are encoded with 32-bit limbs in little-endian order.
pub struct PublicValuesTarget {
pub trie_roots_before: TrieRootsTarget,
pub trie_roots_after: TrieRootsTarget,
pub block_metadata: BlockMetadataTarget,
}
pub struct TrieRootsTarget {
pub state_root: [Target; 8],
pub transactions_root: [Target; 8],
pub receipts_root: [Target; 8],
}
pub struct BlockMetadataTarget {
pub block_beneficiary: [Target; 5],
pub block_timestamp: Target,
pub block_number: Target,
pub block_difficulty: Target,
pub block_gaslimit: Target,
pub block_chain_id: Target,
pub block_base_fee: Target,
}
pub(crate) struct AllProofChallengesTarget<const D: usize> {
pub stark_challenges: [StarkProofChallengesTarget<D>; NUM_TABLES],
pub ctl_challenges: GrandProductChallengeSet<Target>,
}
#[derive(Debug, Clone)]
pub struct StarkProof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> {
/// Merkle cap of LDEs of trace values.
pub trace_cap: MerkleCap<F, C::Hasher>,
/// Merkle cap of LDEs of permutation Z values.
pub permutation_ctl_zs_cap: MerkleCap<F, C::Hasher>,
/// Merkle cap of LDEs of trace values.
pub quotient_polys_cap: MerkleCap<F, C::Hasher>,
/// Purported values of each polynomial at the challenge point.
pub openings: StarkOpeningSet<F, D>,
/// A batch FRI argument for all openings.
pub opening_proof: FriProof<F, C::Hasher, D>,
}
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> StarkProof<F, C, D> {
/// Recover the length of the trace from a STARK proof and a STARK config.
pub fn recover_degree_bits(&self, config: &StarkConfig) -> usize {
let initial_merkle_proof = &self.opening_proof.query_round_proofs[0]
.initial_trees_proof
.evals_proofs[0]
.1;
let lde_bits = config.fri_config.cap_height + initial_merkle_proof.siblings.len();
lde_bits - config.fri_config.rate_bits
}
}
pub struct StarkProofTarget<const D: usize> {
pub trace_cap: MerkleCapTarget,
pub permutation_ctl_zs_cap: MerkleCapTarget,
pub quotient_polys_cap: MerkleCapTarget,
pub openings: StarkOpeningSetTarget<D>,
pub opening_proof: FriProofTarget<D>,
}
impl<const D: usize> StarkProofTarget<D> {
/// Recover the length of the trace from a STARK proof and a STARK config.
pub fn recover_degree_bits(&self, config: &StarkConfig) -> usize {
let initial_merkle_proof = &self.opening_proof.query_round_proofs[0]
.initial_trees_proof
.evals_proofs[0]
.1;
let lde_bits = config.fri_config.cap_height + initial_merkle_proof.siblings.len();
lde_bits - config.fri_config.rate_bits
}
}
pub(crate) struct StarkProofChallenges<F: RichField + Extendable<D>, const D: usize> {
/// Randomness used in any permutation arguments.
pub permutation_challenge_sets: Option<Vec<GrandProductChallengeSet<F>>>,
/// Random values used to combine STARK constraints.
pub stark_alphas: Vec<F>,
/// Point at which the STARK polynomials are opened.
pub stark_zeta: F::Extension,
pub fri_challenges: FriChallenges<F, D>,
}
pub(crate) struct StarkProofChallengesTarget<const D: usize> {
pub permutation_challenge_sets: Option<Vec<GrandProductChallengeSet<Target>>>,
pub stark_alphas: Vec<Target>,
pub stark_zeta: ExtensionTarget<D>,
pub fri_challenges: FriChallengesTarget<D>,
}
/// Purported values of each polynomial at the challenge point.
#[derive(Debug, Clone)]
pub struct StarkOpeningSet<F: RichField + Extendable<D>, const D: usize> {
/// Openings of trace polynomials at `zeta`.
pub local_values: Vec<F::Extension>,
/// Openings of trace polynomials at `g * zeta`.
pub next_values: Vec<F::Extension>,
/// Openings of permutations and cross-table lookups `Z` polynomials at `zeta`.
pub permutation_ctl_zs: Vec<F::Extension>,
/// Openings of permutations and cross-table lookups `Z` polynomials at `g * zeta`.
pub permutation_ctl_zs_next: Vec<F::Extension>,
/// Openings of cross-table lookups `Z` polynomials at `g^-1`.
pub ctl_zs_last: Vec<F>,
/// Openings of quotient polynomials at `zeta`.
pub quotient_polys: Vec<F::Extension>,
}
impl<F: RichField + Extendable<D>, const D: usize> StarkOpeningSet<F, D> {
pub fn new<C: GenericConfig<D, F = F>>(
zeta: F::Extension,
g: F,
trace_commitment: &PolynomialBatch<F, C, D>,
permutation_ctl_zs_commitment: &PolynomialBatch<F, C, D>,
quotient_commitment: &PolynomialBatch<F, C, D>,
degree_bits: usize,
num_permutation_zs: usize,
) -> Self {
let eval_commitment = |z: F::Extension, c: &PolynomialBatch<F, C, D>| {
c.polynomials
.par_iter()
.map(|p| p.to_extension().eval(z))
.collect::<Vec<_>>()
};
let eval_commitment_base = |z: F, c: &PolynomialBatch<F, C, D>| {
c.polynomials
.par_iter()
.map(|p| p.eval(z))
.collect::<Vec<_>>()
};
let zeta_next = zeta.scalar_mul(g);
Self {
local_values: eval_commitment(zeta, trace_commitment),
next_values: eval_commitment(zeta_next, trace_commitment),
permutation_ctl_zs: eval_commitment(zeta, permutation_ctl_zs_commitment),
permutation_ctl_zs_next: eval_commitment(zeta_next, permutation_ctl_zs_commitment),
ctl_zs_last: eval_commitment_base(
F::primitive_root_of_unity(degree_bits).inverse(),
permutation_ctl_zs_commitment,
)[num_permutation_zs..]
.to_vec(),
quotient_polys: eval_commitment(zeta, quotient_commitment),
}
}
pub(crate) fn to_fri_openings(&self) -> FriOpenings<F, D> {
let zeta_batch = FriOpeningBatch {
values: self
.local_values
.iter()
.chain(&self.permutation_ctl_zs)
.chain(&self.quotient_polys)
.copied()
.collect_vec(),
};
let zeta_next_batch = FriOpeningBatch {
values: self
.next_values
.iter()
.chain(&self.permutation_ctl_zs_next)
.copied()
.collect_vec(),
};
debug_assert!(!self.ctl_zs_last.is_empty());
let ctl_last_batch = FriOpeningBatch {
values: self
.ctl_zs_last
.iter()
.copied()
.map(F::Extension::from_basefield)
.collect(),
};
FriOpenings {
batches: vec![zeta_batch, zeta_next_batch, ctl_last_batch],
}
}
}
pub struct StarkOpeningSetTarget<const D: usize> {
pub local_values: Vec<ExtensionTarget<D>>,
pub next_values: Vec<ExtensionTarget<D>>,
pub permutation_ctl_zs: Vec<ExtensionTarget<D>>,
pub permutation_ctl_zs_next: Vec<ExtensionTarget<D>>,
pub ctl_zs_last: Vec<Target>,
pub quotient_polys: Vec<ExtensionTarget<D>>,
}
impl<const D: usize> StarkOpeningSetTarget<D> {
pub(crate) fn to_fri_openings(&self, zero: Target) -> FriOpeningsTarget<D> {
let zeta_batch = FriOpeningBatchTarget {
values: self
.local_values
.iter()
.chain(&self.permutation_ctl_zs)
.chain(&self.quotient_polys)
.copied()
.collect_vec(),
};
let zeta_next_batch = FriOpeningBatchTarget {
values: self
.next_values
.iter()
.chain(&self.permutation_ctl_zs_next)
.copied()
.collect_vec(),
};
debug_assert!(!self.ctl_zs_last.is_empty());
let ctl_last_batch = FriOpeningBatchTarget {
values: self
.ctl_zs_last
.iter()
.copied()
.map(|t| t.to_ext_target(zero))
.collect(),
};
FriOpeningsTarget {
batches: vec![zeta_batch, zeta_next_batch, ctl_last_batch],
}
}
}