2023-12-05 11:42:40 -05:00
|
|
|
use ethereum_types::{Address, H160, H256, U256};
|
2022-05-04 20:57:07 +02:00
|
|
|
use itertools::Itertools;
|
2022-06-27 07:18:21 -07:00
|
|
|
use plonky2::field::extension::{Extendable, FieldExtension};
|
2022-05-04 20:57:07 +02:00
|
|
|
use plonky2::fri::oracle::PolynomialBatch;
|
2022-08-25 12:16:52 -07:00
|
|
|
use plonky2::fri::proof::{FriChallenges, FriChallengesTarget, FriProof, FriProofTarget};
|
2022-05-04 20:57:07 +02:00
|
|
|
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;
|
2023-08-07 21:00:32 +02:00
|
|
|
use plonky2::iop::target::{BoolTarget, Target};
|
|
|
|
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
2023-05-11 02:59:02 +10:00
|
|
|
use plonky2::plonk::config::{GenericConfig, Hasher};
|
2023-02-21 21:12:03 -05:00
|
|
|
use plonky2::util::serialization::{Buffer, IoResult, Read, Write};
|
2023-01-30 08:51:33 -08:00
|
|
|
use plonky2_maybe_rayon::*;
|
2022-09-19 11:05:48 -06:00
|
|
|
use serde::{Deserialize, Serialize};
|
2022-05-04 20:57:07 +02:00
|
|
|
|
2022-08-26 10:12:45 +02:00
|
|
|
use crate::all_stark::NUM_TABLES;
|
2022-05-04 20:57:07 +02:00
|
|
|
use crate::config::StarkConfig;
|
2023-02-13 15:58:26 +01:00
|
|
|
use crate::cross_table_lookup::GrandProductChallengeSet;
|
2023-12-07 12:08:47 -05:00
|
|
|
use crate::generation::mpt::TrieRootPtrs;
|
2023-12-05 11:42:40 -05:00
|
|
|
use crate::util::{get_h160, get_h256, h2u};
|
2022-05-11 14:35:33 +02:00
|
|
|
|
Shrink STARK proofs to a constant degree
The goal here is to end up with a single "root" circuit representing any EVM proof. I.e. it must verify each STARK, but be general enough to work with any combination of STARK sizes (within some range of sizes that we chose to support). This root circuit can then be plugged into our aggregation circuit.
In particular, for each STARK, and for each initial `degree_bits` (within a range that we choose to support), this adds a "shrinking chain" of circuits. Such a chain shrinks a STARK proof from that initial `degree_bits` down to a constant, `THRESHOLD_DEGREE_BITS`.
The root circuit then combines these shrunk-to-constant proofs for each table. It's similar to `RecursiveAllProof::verify_circuit`; I adapted the code from there and I think we can remove it after. The main difference is that now instead of having one verification key per STARK, we have several possible VKs, one per initial `degree_bits`. We bake the list of possible VKs into the root circuit, and have the prover indicate the index of the VK they're actually using.
This also partially removes the default feature of CTLs. So far we've used filters instead of defaults. Until now it was easy to keep supporting defaults just in case, but here maintaining support would require some more work. E.g. we couldn't use `exp_u64` any more, since the size delta is now dynamic, it can't be hardcoded. If there are no concerns, I'll fully remove the feature after.
2022-12-27 18:15:18 -08:00
|
|
|
/// A STARK proof for each table, plus some metadata used to create recursive wrapper proofs.
|
2022-05-11 14:35:33 +02:00
|
|
|
#[derive(Debug, Clone)]
|
2023-05-11 02:59:02 +10:00
|
|
|
pub struct AllProof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Proofs for all the different STARK modules.
|
2023-04-01 09:34:13 -04:00
|
|
|
pub stark_proofs: [StarkProofWithMetadata<F, C, D>; NUM_TABLES],
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Cross-table lookup challenges.
|
Shrink STARK proofs to a constant degree
The goal here is to end up with a single "root" circuit representing any EVM proof. I.e. it must verify each STARK, but be general enough to work with any combination of STARK sizes (within some range of sizes that we chose to support). This root circuit can then be plugged into our aggregation circuit.
In particular, for each STARK, and for each initial `degree_bits` (within a range that we choose to support), this adds a "shrinking chain" of circuits. Such a chain shrinks a STARK proof from that initial `degree_bits` down to a constant, `THRESHOLD_DEGREE_BITS`.
The root circuit then combines these shrunk-to-constant proofs for each table. It's similar to `RecursiveAllProof::verify_circuit`; I adapted the code from there and I think we can remove it after. The main difference is that now instead of having one verification key per STARK, we have several possible VKs, one per initial `degree_bits`. We bake the list of possible VKs into the root circuit, and have the prover indicate the index of the VK they're actually using.
This also partially removes the default feature of CTLs. So far we've used filters instead of defaults. Until now it was easy to keep supporting defaults just in case, but here maintaining support would require some more work. E.g. we couldn't use `exp_u64` any more, since the size delta is now dynamic, it can't be hardcoded. If there are no concerns, I'll fully remove the feature after.
2022-12-27 18:15:18 -08:00
|
|
|
pub(crate) ctl_challenges: GrandProductChallengeSet<F>,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Public memory values used for the recursive proofs.
|
2022-08-25 12:24:22 -07:00
|
|
|
pub public_values: PublicValues,
|
2022-05-11 14:35:33 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-11 02:59:02 +10:00
|
|
|
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> AllProof<F, C, D> {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Returns the degree (i.e. the trace length) of each STARK.
|
2022-08-26 10:12:45 +02:00
|
|
|
pub fn degree_bits(&self, config: &StarkConfig) -> [usize; NUM_TABLES] {
|
2023-01-30 08:51:33 -08:00
|
|
|
core::array::from_fn(|i| self.stark_proofs[i].proof.recover_degree_bits(config))
|
2022-05-26 16:27:15 +02:00
|
|
|
}
|
2022-09-05 08:38:57 +02:00
|
|
|
}
|
2022-05-26 16:27:15 +02:00
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Randomness for all STARKs.
|
2022-05-11 14:35:33 +02:00
|
|
|
pub(crate) struct AllProofChallenges<F: RichField + Extendable<D>, const D: usize> {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Randomness used in each STARK proof.
|
2022-08-26 10:12:45 +02:00
|
|
|
pub stark_challenges: [StarkProofChallenges<F, D>; NUM_TABLES],
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Randomness used for cross-table lookups. It is shared by all STARKs.
|
2022-05-12 13:51:02 +02:00
|
|
|
pub ctl_challenges: GrandProductChallengeSet<F>,
|
2022-05-11 14:35:33 +02:00
|
|
|
}
|
2022-05-04 20:57:07 +02:00
|
|
|
|
2022-08-25 22:11:25 -07:00
|
|
|
/// Memory values which are public.
|
2023-12-05 11:42:40 -05:00
|
|
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
|
2022-08-25 12:24:22 -07:00
|
|
|
pub struct PublicValues {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Trie hashes before the execution of the local state transition
|
2022-08-25 12:24:22 -07:00
|
|
|
pub trie_roots_before: TrieRoots,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Trie hashes after the execution of the local state transition.
|
2022-08-25 12:24:22 -07:00
|
|
|
pub trie_roots_after: TrieRoots,
|
2023-11-04 23:07:36 +08:00
|
|
|
/// Block metadata: it remains unchanged within a block.
|
2022-08-25 12:24:22 -07:00
|
|
|
pub block_metadata: BlockMetadata,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// 256 previous block hashes and current block's hash.
|
2023-08-21 23:32:53 +01:00
|
|
|
pub block_hashes: BlockHashes,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Extra block data that is specific to the current proof.
|
2023-08-23 23:29:58 +01:00
|
|
|
pub extra_block_data: ExtraBlockData,
|
2022-08-25 12:24:22 -07:00
|
|
|
}
|
|
|
|
|
|
2023-12-05 11:42:40 -05:00
|
|
|
impl PublicValues {
|
|
|
|
|
/// Extracts public values from the given public inputs of a proof.
|
|
|
|
|
/// Public values are always the first public inputs added to the circuit,
|
|
|
|
|
/// so we can start extracting at index 0.
|
|
|
|
|
pub fn from_public_inputs<F: RichField>(pis: &[F]) -> Self {
|
|
|
|
|
assert!(
|
|
|
|
|
pis.len()
|
|
|
|
|
> TrieRootsTarget::SIZE * 2
|
|
|
|
|
+ BlockMetadataTarget::SIZE
|
|
|
|
|
+ BlockHashesTarget::SIZE
|
|
|
|
|
+ ExtraBlockDataTarget::SIZE
|
|
|
|
|
- 1
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let trie_roots_before = TrieRoots::from_public_inputs(&pis[0..TrieRootsTarget::SIZE]);
|
|
|
|
|
let trie_roots_after =
|
|
|
|
|
TrieRoots::from_public_inputs(&pis[TrieRootsTarget::SIZE..TrieRootsTarget::SIZE * 2]);
|
|
|
|
|
let block_metadata = BlockMetadata::from_public_inputs(
|
|
|
|
|
&pis[TrieRootsTarget::SIZE * 2..TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE],
|
|
|
|
|
);
|
|
|
|
|
let block_hashes = BlockHashes::from_public_inputs(
|
|
|
|
|
&pis[TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE
|
|
|
|
|
..TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE],
|
|
|
|
|
);
|
|
|
|
|
let extra_block_data = ExtraBlockData::from_public_inputs(
|
|
|
|
|
&pis[TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE
|
|
|
|
|
..TrieRootsTarget::SIZE * 2
|
|
|
|
|
+ BlockMetadataTarget::SIZE
|
|
|
|
|
+ BlockHashesTarget::SIZE
|
|
|
|
|
+ ExtraBlockDataTarget::SIZE],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
Self {
|
|
|
|
|
trie_roots_before,
|
|
|
|
|
trie_roots_after,
|
|
|
|
|
block_metadata,
|
|
|
|
|
block_hashes,
|
|
|
|
|
extra_block_data,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Trie hashes.
|
2023-12-05 11:42:40 -05:00
|
|
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
2022-08-25 12:24:22 -07:00
|
|
|
pub struct TrieRoots {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// State trie hash.
|
2022-09-29 13:45:46 -06:00
|
|
|
pub state_root: H256,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Transaction trie hash.
|
2022-09-29 13:45:46 -06:00
|
|
|
pub transactions_root: H256,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Receipts trie hash.
|
2022-09-29 13:45:46 -06:00
|
|
|
pub receipts_root: H256,
|
2022-08-25 12:24:22 -07:00
|
|
|
}
|
|
|
|
|
|
2023-12-05 11:42:40 -05:00
|
|
|
impl TrieRoots {
|
|
|
|
|
pub fn from_public_inputs<F: RichField>(pis: &[F]) -> Self {
|
|
|
|
|
assert!(pis.len() == TrieRootsTarget::SIZE);
|
|
|
|
|
|
|
|
|
|
let state_root = get_h256(&pis[0..8]);
|
|
|
|
|
let transactions_root = get_h256(&pis[8..16]);
|
|
|
|
|
let receipts_root = get_h256(&pis[16..24]);
|
|
|
|
|
|
|
|
|
|
Self {
|
|
|
|
|
state_root,
|
|
|
|
|
transactions_root,
|
|
|
|
|
receipts_root,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-21 23:32:53 +01:00
|
|
|
// There should be 256 previous hashes stored, so the default should also contain 256 values.
|
|
|
|
|
impl Default for BlockHashes {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
prev_hashes: vec![H256::default(); 256],
|
|
|
|
|
cur_hash: H256::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-19 15:41:36 -04:00
|
|
|
/// User-provided helper values to compute the `BLOCKHASH` opcode.
|
|
|
|
|
/// The proofs across consecutive blocks ensure that these values
|
|
|
|
|
/// are consistent (i.e. shifted by one to the left).
|
|
|
|
|
///
|
|
|
|
|
/// When the block number is less than 256, dummy values, i.e. `H256::default()`,
|
|
|
|
|
/// should be used for the additional block hashes.
|
2023-12-05 11:42:40 -05:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
2023-08-21 23:32:53 +01:00
|
|
|
pub struct BlockHashes {
|
2023-09-19 15:41:36 -04:00
|
|
|
/// The previous 256 hashes to the current block. The leftmost hash, i.e. `prev_hashes[0]`,
|
|
|
|
|
/// is the oldest, and the rightmost, i.e. `prev_hashes[255]` is the hash of the parent block.
|
2023-08-21 23:32:53 +01:00
|
|
|
pub prev_hashes: Vec<H256>,
|
2023-09-19 15:41:36 -04:00
|
|
|
// The hash of the current block.
|
2023-08-21 23:32:53 +01:00
|
|
|
pub cur_hash: H256,
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-05 11:42:40 -05:00
|
|
|
impl BlockHashes {
|
|
|
|
|
pub fn from_public_inputs<F: RichField>(pis: &[F]) -> Self {
|
|
|
|
|
assert!(pis.len() == BlockHashesTarget::SIZE);
|
|
|
|
|
|
|
|
|
|
let prev_hashes: [H256; 256] = core::array::from_fn(|i| get_h256(&pis[8 * i..8 + 8 * i]));
|
|
|
|
|
let cur_hash = get_h256(&pis[2048..2056]);
|
|
|
|
|
|
|
|
|
|
Self {
|
|
|
|
|
prev_hashes: prev_hashes.to_vec(),
|
|
|
|
|
cur_hash,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-19 15:41:36 -04:00
|
|
|
/// Metadata contained in a block header. Those are identical between
|
|
|
|
|
/// all state transition proofs within the same block.
|
2023-12-05 11:42:40 -05:00
|
|
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
|
2022-08-25 12:24:22 -07:00
|
|
|
pub struct BlockMetadata {
|
2023-09-19 15:41:36 -04:00
|
|
|
/// The address of this block's producer.
|
2022-08-25 23:35:38 -07:00
|
|
|
pub block_beneficiary: Address,
|
2023-11-17 10:01:26 -05:00
|
|
|
/// The timestamp of this block.
|
2022-08-25 12:24:22 -07:00
|
|
|
pub block_timestamp: U256,
|
2023-11-17 10:01:26 -05:00
|
|
|
/// The index of this block.
|
2022-08-25 12:24:22 -07:00
|
|
|
pub block_number: U256,
|
2023-09-19 15:41:36 -04:00
|
|
|
/// The difficulty (before PoS transition) of this block.
|
2022-08-25 12:24:22 -07:00
|
|
|
pub block_difficulty: U256,
|
2023-09-25 18:20:22 +02:00
|
|
|
pub block_random: H256,
|
2023-11-17 10:01:26 -05:00
|
|
|
/// The gas limit of this block. It must fit in a `u32`.
|
2022-08-25 12:24:22 -07:00
|
|
|
pub block_gaslimit: U256,
|
2023-11-17 10:01:26 -05:00
|
|
|
/// The chain id of this block.
|
2022-08-25 12:24:22 -07:00
|
|
|
pub block_chain_id: U256,
|
2023-11-17 10:01:26 -05:00
|
|
|
/// The base fee of this block.
|
2022-08-25 23:35:38 -07:00
|
|
|
pub block_base_fee: U256,
|
2023-11-17 10:01:26 -05:00
|
|
|
/// The total gas used in this block. It must fit in a `u32`.
|
2023-08-23 23:29:58 +01:00
|
|
|
pub block_gas_used: U256,
|
2023-09-19 15:41:36 -04:00
|
|
|
/// The block bloom of this block, represented as the consecutive
|
|
|
|
|
/// 32-byte chunks of a block's final bloom filter string.
|
2023-05-04 09:57:02 +02:00
|
|
|
pub block_bloom: [U256; 8],
|
2022-08-25 12:24:22 -07:00
|
|
|
}
|
|
|
|
|
|
2023-12-05 11:42:40 -05:00
|
|
|
impl BlockMetadata {
|
|
|
|
|
pub fn from_public_inputs<F: RichField>(pis: &[F]) -> Self {
|
|
|
|
|
assert!(pis.len() == BlockMetadataTarget::SIZE);
|
|
|
|
|
|
|
|
|
|
let block_beneficiary = get_h160(&pis[0..5]);
|
|
|
|
|
let block_timestamp = pis[5].to_canonical_u64().into();
|
|
|
|
|
let block_number = pis[6].to_canonical_u64().into();
|
|
|
|
|
let block_difficulty = pis[7].to_canonical_u64().into();
|
|
|
|
|
let block_random = get_h256(&pis[8..16]);
|
|
|
|
|
let block_gaslimit = pis[16].to_canonical_u64().into();
|
|
|
|
|
let block_chain_id = pis[17].to_canonical_u64().into();
|
|
|
|
|
let block_base_fee =
|
|
|
|
|
(pis[18].to_canonical_u64() + (pis[19].to_canonical_u64() << 32)).into();
|
|
|
|
|
let block_gas_used = pis[20].to_canonical_u64().into();
|
|
|
|
|
let block_bloom = core::array::from_fn(|i| h2u(get_h256(&pis[21 + 8 * i..29 + 8 * i])));
|
|
|
|
|
|
|
|
|
|
Self {
|
|
|
|
|
block_beneficiary,
|
|
|
|
|
block_timestamp,
|
|
|
|
|
block_number,
|
|
|
|
|
block_difficulty,
|
|
|
|
|
block_random,
|
|
|
|
|
block_gaslimit,
|
|
|
|
|
block_chain_id,
|
|
|
|
|
block_base_fee,
|
|
|
|
|
block_gas_used,
|
|
|
|
|
block_bloom,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-19 15:41:36 -04:00
|
|
|
/// Additional block data that are specific to the local transaction being proven,
|
|
|
|
|
/// unlike `BlockMetadata`.
|
2023-12-05 11:42:40 -05:00
|
|
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
|
2023-08-23 23:29:58 +01:00
|
|
|
pub struct ExtraBlockData {
|
2023-12-09 06:26:55 +01:00
|
|
|
/// The state trie digest of the checkpoint block.
|
|
|
|
|
pub checkpoint_state_trie_root: H256,
|
2023-09-19 15:41:36 -04:00
|
|
|
/// The transaction count prior execution of the local state transition, starting
|
|
|
|
|
/// at 0 for the initial transaction of a block.
|
2023-08-23 23:29:58 +01:00
|
|
|
pub txn_number_before: U256,
|
2023-09-19 15:41:36 -04:00
|
|
|
/// The transaction count after execution of the local state transition.
|
2023-08-23 23:29:58 +01:00
|
|
|
pub txn_number_after: U256,
|
2023-09-19 15:41:36 -04:00
|
|
|
/// The accumulated gas used prior execution of the local state transition, starting
|
|
|
|
|
/// at 0 for the initial transaction of a block.
|
2023-08-23 23:29:58 +01:00
|
|
|
pub gas_used_before: U256,
|
2023-09-19 15:41:36 -04:00
|
|
|
/// The accumulated gas used after execution of the local state transition. It should
|
|
|
|
|
/// match the `block_gas_used` value after execution of the last transaction in a block.
|
2023-08-23 23:29:58 +01:00
|
|
|
pub gas_used_after: U256,
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-05 11:42:40 -05:00
|
|
|
impl ExtraBlockData {
|
|
|
|
|
pub fn from_public_inputs<F: RichField>(pis: &[F]) -> Self {
|
|
|
|
|
assert!(pis.len() == ExtraBlockDataTarget::SIZE);
|
|
|
|
|
|
2023-12-09 06:26:55 +01:00
|
|
|
let checkpoint_state_trie_root = get_h256(&pis[0..8]);
|
2023-12-05 11:42:40 -05:00
|
|
|
let txn_number_before = pis[8].to_canonical_u64().into();
|
|
|
|
|
let txn_number_after = pis[9].to_canonical_u64().into();
|
|
|
|
|
let gas_used_before = pis[10].to_canonical_u64().into();
|
|
|
|
|
let gas_used_after = pis[11].to_canonical_u64().into();
|
|
|
|
|
|
|
|
|
|
Self {
|
2023-12-09 06:26:55 +01:00
|
|
|
checkpoint_state_trie_root,
|
2023-12-05 11:42:40 -05:00
|
|
|
txn_number_before,
|
|
|
|
|
txn_number_after,
|
|
|
|
|
gas_used_before,
|
|
|
|
|
gas_used_after,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-25 22:11:25 -07:00
|
|
|
/// Memory values which are public.
|
2022-08-25 12:24:22 -07:00
|
|
|
/// Note: All the larger integers are encoded with 32-bit limbs in little-endian order.
|
2023-06-21 20:05:39 +02:00
|
|
|
#[derive(Eq, PartialEq, Debug)]
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) struct PublicValuesTarget {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Trie hashes before the execution of the local state transition.
|
2022-08-25 12:24:22 -07:00
|
|
|
pub trie_roots_before: TrieRootsTarget,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Trie hashes after the execution of the local state transition.
|
2022-08-25 12:24:22 -07:00
|
|
|
pub trie_roots_after: TrieRootsTarget,
|
2023-11-04 23:07:36 +08:00
|
|
|
/// Block metadata: it remains unchanged within a block.
|
2022-08-25 12:24:22 -07:00
|
|
|
pub block_metadata: BlockMetadataTarget,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// 256 previous block hashes and current block's hash.
|
2023-08-21 23:32:53 +01:00
|
|
|
pub block_hashes: BlockHashesTarget,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Extra block data that is specific to the current proof.
|
2023-08-23 23:29:58 +01:00
|
|
|
pub extra_block_data: ExtraBlockDataTarget,
|
2023-06-21 20:05:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl PublicValuesTarget {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Serializes public value targets.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn to_buffer(&self, buffer: &mut Vec<u8>) -> IoResult<()> {
|
2023-06-21 20:05:39 +02:00
|
|
|
let TrieRootsTarget {
|
|
|
|
|
state_root: state_root_before,
|
|
|
|
|
transactions_root: transactions_root_before,
|
|
|
|
|
receipts_root: receipts_root_before,
|
|
|
|
|
} = self.trie_roots_before;
|
|
|
|
|
|
2023-07-30 11:07:05 -04:00
|
|
|
buffer.write_target_array(&state_root_before)?;
|
|
|
|
|
buffer.write_target_array(&transactions_root_before)?;
|
|
|
|
|
buffer.write_target_array(&receipts_root_before)?;
|
2023-06-21 20:05:39 +02:00
|
|
|
|
|
|
|
|
let TrieRootsTarget {
|
|
|
|
|
state_root: state_root_after,
|
|
|
|
|
transactions_root: transactions_root_after,
|
|
|
|
|
receipts_root: receipts_root_after,
|
|
|
|
|
} = self.trie_roots_after;
|
|
|
|
|
|
2023-07-30 11:07:05 -04:00
|
|
|
buffer.write_target_array(&state_root_after)?;
|
|
|
|
|
buffer.write_target_array(&transactions_root_after)?;
|
|
|
|
|
buffer.write_target_array(&receipts_root_after)?;
|
2023-06-21 20:05:39 +02:00
|
|
|
|
|
|
|
|
let BlockMetadataTarget {
|
|
|
|
|
block_beneficiary,
|
|
|
|
|
block_timestamp,
|
|
|
|
|
block_number,
|
|
|
|
|
block_difficulty,
|
2023-09-25 18:20:22 +02:00
|
|
|
block_random,
|
2023-06-21 20:05:39 +02:00
|
|
|
block_gaslimit,
|
|
|
|
|
block_chain_id,
|
|
|
|
|
block_base_fee,
|
2023-08-23 23:29:58 +01:00
|
|
|
block_gas_used,
|
2023-05-04 09:57:02 +02:00
|
|
|
block_bloom,
|
2023-06-21 20:05:39 +02:00
|
|
|
} = self.block_metadata;
|
|
|
|
|
|
2023-07-30 11:07:05 -04:00
|
|
|
buffer.write_target_array(&block_beneficiary)?;
|
2023-06-21 20:05:39 +02:00
|
|
|
buffer.write_target(block_timestamp)?;
|
|
|
|
|
buffer.write_target(block_number)?;
|
|
|
|
|
buffer.write_target(block_difficulty)?;
|
2023-09-25 18:20:22 +02:00
|
|
|
buffer.write_target_array(&block_random)?;
|
2023-11-17 10:01:26 -05:00
|
|
|
buffer.write_target(block_gaslimit)?;
|
2023-06-21 20:05:39 +02:00
|
|
|
buffer.write_target(block_chain_id)?;
|
2023-08-22 16:21:10 +01:00
|
|
|
buffer.write_target_array(&block_base_fee)?;
|
2023-11-17 10:01:26 -05:00
|
|
|
buffer.write_target(block_gas_used)?;
|
2023-08-22 16:21:10 +01:00
|
|
|
buffer.write_target_array(&block_bloom)?;
|
2023-06-21 20:05:39 +02:00
|
|
|
|
2023-08-21 23:32:53 +01:00
|
|
|
let BlockHashesTarget {
|
|
|
|
|
prev_hashes,
|
|
|
|
|
cur_hash,
|
|
|
|
|
} = self.block_hashes;
|
|
|
|
|
buffer.write_target_array(&prev_hashes)?;
|
|
|
|
|
buffer.write_target_array(&cur_hash)?;
|
|
|
|
|
|
2023-08-23 23:29:58 +01:00
|
|
|
let ExtraBlockDataTarget {
|
2023-12-09 06:26:55 +01:00
|
|
|
checkpoint_state_trie_root,
|
2023-08-23 23:29:58 +01:00
|
|
|
txn_number_before,
|
|
|
|
|
txn_number_after,
|
|
|
|
|
gas_used_before,
|
|
|
|
|
gas_used_after,
|
|
|
|
|
} = self.extra_block_data;
|
2023-12-09 06:26:55 +01:00
|
|
|
buffer.write_target_array(&checkpoint_state_trie_root)?;
|
2023-08-23 23:29:58 +01:00
|
|
|
buffer.write_target(txn_number_before)?;
|
|
|
|
|
buffer.write_target(txn_number_after)?;
|
2023-11-17 10:01:26 -05:00
|
|
|
buffer.write_target(gas_used_before)?;
|
|
|
|
|
buffer.write_target(gas_used_after)?;
|
2023-08-23 23:29:58 +01:00
|
|
|
|
2023-06-21 20:05:39 +02:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Deserializes public value targets.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn from_buffer(buffer: &mut Buffer) -> IoResult<Self> {
|
2023-06-21 20:05:39 +02:00
|
|
|
let trie_roots_before = TrieRootsTarget {
|
2023-08-22 16:27:55 +01:00
|
|
|
state_root: buffer.read_target_array()?,
|
|
|
|
|
transactions_root: buffer.read_target_array()?,
|
|
|
|
|
receipts_root: buffer.read_target_array()?,
|
2023-06-21 20:05:39 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let trie_roots_after = TrieRootsTarget {
|
2023-08-22 16:27:55 +01:00
|
|
|
state_root: buffer.read_target_array()?,
|
|
|
|
|
transactions_root: buffer.read_target_array()?,
|
|
|
|
|
receipts_root: buffer.read_target_array()?,
|
2023-06-21 20:05:39 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let block_metadata = BlockMetadataTarget {
|
2023-08-22 16:27:55 +01:00
|
|
|
block_beneficiary: buffer.read_target_array()?,
|
2023-06-21 20:05:39 +02:00
|
|
|
block_timestamp: buffer.read_target()?,
|
|
|
|
|
block_number: buffer.read_target()?,
|
|
|
|
|
block_difficulty: buffer.read_target()?,
|
2023-09-25 18:20:22 +02:00
|
|
|
block_random: buffer.read_target_array()?,
|
2023-11-17 10:01:26 -05:00
|
|
|
block_gaslimit: buffer.read_target()?,
|
2023-06-21 20:05:39 +02:00
|
|
|
block_chain_id: buffer.read_target()?,
|
2023-08-22 16:27:55 +01:00
|
|
|
block_base_fee: buffer.read_target_array()?,
|
2023-11-17 10:01:26 -05:00
|
|
|
block_gas_used: buffer.read_target()?,
|
2023-08-22 16:27:55 +01:00
|
|
|
block_bloom: buffer.read_target_array()?,
|
2023-06-21 20:05:39 +02:00
|
|
|
};
|
|
|
|
|
|
2023-08-21 23:32:53 +01:00
|
|
|
let block_hashes = BlockHashesTarget {
|
|
|
|
|
prev_hashes: buffer.read_target_array()?,
|
|
|
|
|
cur_hash: buffer.read_target_array()?,
|
|
|
|
|
};
|
|
|
|
|
|
2023-08-23 23:29:58 +01:00
|
|
|
let extra_block_data = ExtraBlockDataTarget {
|
2023-12-09 06:26:55 +01:00
|
|
|
checkpoint_state_trie_root: buffer.read_target_array()?,
|
2023-08-23 23:29:58 +01:00
|
|
|
txn_number_before: buffer.read_target()?,
|
|
|
|
|
txn_number_after: buffer.read_target()?,
|
2023-11-17 10:01:26 -05:00
|
|
|
gas_used_before: buffer.read_target()?,
|
|
|
|
|
gas_used_after: buffer.read_target()?,
|
2023-08-23 23:29:58 +01:00
|
|
|
};
|
|
|
|
|
|
2023-06-21 20:05:39 +02:00
|
|
|
Ok(Self {
|
|
|
|
|
trie_roots_before,
|
|
|
|
|
trie_roots_after,
|
|
|
|
|
block_metadata,
|
2023-08-21 23:32:53 +01:00
|
|
|
block_hashes,
|
2023-08-23 23:29:58 +01:00
|
|
|
extra_block_data,
|
2023-06-21 20:05:39 +02:00
|
|
|
})
|
|
|
|
|
}
|
2023-08-07 21:00:32 +02:00
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Extracts public value `Target`s from the given public input `Target`s.
|
|
|
|
|
/// Public values are always the first public inputs added to the circuit,
|
|
|
|
|
/// so we can start extracting at index 0.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn from_public_inputs(pis: &[Target]) -> Self {
|
2023-08-23 23:29:58 +01:00
|
|
|
assert!(
|
|
|
|
|
pis.len()
|
|
|
|
|
> TrieRootsTarget::SIZE * 2
|
|
|
|
|
+ BlockMetadataTarget::SIZE
|
2023-12-05 11:42:40 -05:00
|
|
|
+ BlockHashesTarget::SIZE
|
2023-08-23 23:29:58 +01:00
|
|
|
+ ExtraBlockDataTarget::SIZE
|
|
|
|
|
- 1
|
|
|
|
|
);
|
|
|
|
|
|
2023-08-07 21:00:32 +02:00
|
|
|
Self {
|
|
|
|
|
trie_roots_before: TrieRootsTarget::from_public_inputs(&pis[0..TrieRootsTarget::SIZE]),
|
|
|
|
|
trie_roots_after: TrieRootsTarget::from_public_inputs(
|
|
|
|
|
&pis[TrieRootsTarget::SIZE..TrieRootsTarget::SIZE * 2],
|
|
|
|
|
),
|
|
|
|
|
block_metadata: BlockMetadataTarget::from_public_inputs(
|
|
|
|
|
&pis[TrieRootsTarget::SIZE * 2
|
|
|
|
|
..TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE],
|
|
|
|
|
),
|
2023-08-21 23:32:53 +01:00
|
|
|
block_hashes: BlockHashesTarget::from_public_inputs(
|
|
|
|
|
&pis[TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE
|
|
|
|
|
..TrieRootsTarget::SIZE * 2
|
|
|
|
|
+ BlockMetadataTarget::SIZE
|
2023-12-05 11:42:40 -05:00
|
|
|
+ BlockHashesTarget::SIZE],
|
2023-08-21 23:32:53 +01:00
|
|
|
),
|
2023-08-23 23:29:58 +01:00
|
|
|
extra_block_data: ExtraBlockDataTarget::from_public_inputs(
|
2023-12-05 11:42:40 -05:00
|
|
|
&pis[TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE
|
2023-08-23 23:29:58 +01:00
|
|
|
..TrieRootsTarget::SIZE * 2
|
|
|
|
|
+ BlockMetadataTarget::SIZE
|
2023-12-05 11:42:40 -05:00
|
|
|
+ BlockHashesTarget::SIZE
|
2023-08-23 23:29:58 +01:00
|
|
|
+ ExtraBlockDataTarget::SIZE],
|
|
|
|
|
),
|
2023-08-07 21:00:32 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Returns the public values in `pv0` or `pv1` depening on `condition`.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn select<F: RichField + Extendable<D>, const D: usize>(
|
2023-08-07 21:00:32 +02:00
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
|
|
|
|
condition: BoolTarget,
|
|
|
|
|
pv0: Self,
|
|
|
|
|
pv1: Self,
|
|
|
|
|
) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
trie_roots_before: TrieRootsTarget::select(
|
|
|
|
|
builder,
|
|
|
|
|
condition,
|
|
|
|
|
pv0.trie_roots_before,
|
|
|
|
|
pv1.trie_roots_before,
|
|
|
|
|
),
|
|
|
|
|
trie_roots_after: TrieRootsTarget::select(
|
|
|
|
|
builder,
|
|
|
|
|
condition,
|
|
|
|
|
pv0.trie_roots_after,
|
|
|
|
|
pv1.trie_roots_after,
|
|
|
|
|
),
|
|
|
|
|
block_metadata: BlockMetadataTarget::select(
|
|
|
|
|
builder,
|
|
|
|
|
condition,
|
|
|
|
|
pv0.block_metadata,
|
|
|
|
|
pv1.block_metadata,
|
|
|
|
|
),
|
2023-08-21 23:32:53 +01:00
|
|
|
block_hashes: BlockHashesTarget::select(
|
|
|
|
|
builder,
|
|
|
|
|
condition,
|
|
|
|
|
pv0.block_hashes,
|
|
|
|
|
pv1.block_hashes,
|
|
|
|
|
),
|
2023-08-23 23:29:58 +01:00
|
|
|
extra_block_data: ExtraBlockDataTarget::select(
|
|
|
|
|
builder,
|
|
|
|
|
condition,
|
|
|
|
|
pv0.extra_block_data,
|
|
|
|
|
pv1.extra_block_data,
|
|
|
|
|
),
|
2023-08-07 21:00:32 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-08-25 12:24:22 -07:00
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Circuit version of `TrieRoots`.
|
|
|
|
|
/// `Target`s for trie hashes. Since a `Target` holds a 32-bit limb, each hash requires 8 `Target`s.
|
2023-08-07 21:00:32 +02:00
|
|
|
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) struct TrieRootsTarget {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Targets for the state trie hash.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) state_root: [Target; 8],
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Targets for the transactions trie hash.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) transactions_root: [Target; 8],
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Targets for the receipts trie hash.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) receipts_root: [Target; 8],
|
2022-08-25 12:24:22 -07:00
|
|
|
}
|
|
|
|
|
|
2023-08-07 21:00:32 +02:00
|
|
|
impl TrieRootsTarget {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Number of `Target`s required for all trie hashes.
|
2023-11-22 13:30:52 -05:00
|
|
|
pub(crate) const HASH_SIZE: usize = 8;
|
|
|
|
|
pub(crate) const SIZE: usize = Self::HASH_SIZE * 3;
|
2023-08-07 21:00:32 +02:00
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Extracts trie hash `Target`s for all tries from the provided public input `Target`s.
|
|
|
|
|
/// The provided `pis` should start with the trie hashes.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn from_public_inputs(pis: &[Target]) -> Self {
|
2023-08-07 21:00:32 +02:00
|
|
|
let state_root = pis[0..8].try_into().unwrap();
|
|
|
|
|
let transactions_root = pis[8..16].try_into().unwrap();
|
|
|
|
|
let receipts_root = pis[16..24].try_into().unwrap();
|
|
|
|
|
|
|
|
|
|
Self {
|
|
|
|
|
state_root,
|
|
|
|
|
transactions_root,
|
|
|
|
|
receipts_root,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// If `condition`, returns the trie hashes in `tr0`,
|
|
|
|
|
/// otherwise returns the trie hashes in `tr1`.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn select<F: RichField + Extendable<D>, const D: usize>(
|
2023-08-07 21:00:32 +02:00
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
|
|
|
|
condition: BoolTarget,
|
|
|
|
|
tr0: Self,
|
|
|
|
|
tr1: Self,
|
|
|
|
|
) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
state_root: core::array::from_fn(|i| {
|
|
|
|
|
builder.select(condition, tr0.state_root[i], tr1.state_root[i])
|
|
|
|
|
}),
|
|
|
|
|
transactions_root: core::array::from_fn(|i| {
|
|
|
|
|
builder.select(
|
|
|
|
|
condition,
|
|
|
|
|
tr0.transactions_root[i],
|
|
|
|
|
tr1.transactions_root[i],
|
|
|
|
|
)
|
|
|
|
|
}),
|
|
|
|
|
receipts_root: core::array::from_fn(|i| {
|
|
|
|
|
builder.select(condition, tr0.receipts_root[i], tr1.receipts_root[i])
|
|
|
|
|
}),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Connects the trie hashes in `tr0` and in `tr1`.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn connect<F: RichField + Extendable<D>, const D: usize>(
|
2023-08-07 21:00:32 +02:00
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
|
|
|
|
tr0: Self,
|
|
|
|
|
tr1: Self,
|
|
|
|
|
) {
|
|
|
|
|
for i in 0..8 {
|
|
|
|
|
builder.connect(tr0.state_root[i], tr1.state_root[i]);
|
|
|
|
|
builder.connect(tr0.transactions_root[i], tr1.transactions_root[i]);
|
|
|
|
|
builder.connect(tr0.receipts_root[i], tr1.receipts_root[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Circuit version of `BlockMetadata`.
|
|
|
|
|
/// Metadata contained in a block header. Those are identical between
|
|
|
|
|
/// all state transition proofs within the same block.
|
2023-08-07 21:00:32 +02:00
|
|
|
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) struct BlockMetadataTarget {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target`s for the address of this block's producer.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) block_beneficiary: [Target; 5],
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target` for the timestamp of this block.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) block_timestamp: Target,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target` for the index of this block.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) block_number: Target,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target` for the difficulty (before PoS transition) of this block.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) block_difficulty: Target,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target`s for the `mix_hash` value of this block.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) block_random: [Target; 8],
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target`s for the gas limit of this block.
|
2023-11-17 10:01:26 -05:00
|
|
|
pub(crate) block_gaslimit: Target,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target` for the chain id of this block.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) block_chain_id: Target,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target`s for the base fee of this block.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) block_base_fee: [Target; 2],
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target`s for the gas used of this block.
|
2023-11-17 10:01:26 -05:00
|
|
|
pub(crate) block_gas_used: Target,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target`s for the block bloom of this block.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) block_bloom: [Target; 64],
|
2022-05-20 11:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
2023-08-07 21:00:32 +02:00
|
|
|
impl BlockMetadataTarget {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Number of `Target`s required for the block metadata.
|
2023-11-17 15:45:38 -05:00
|
|
|
pub(crate) const SIZE: usize = 85;
|
2023-08-07 21:00:32 +02:00
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Extracts block metadata `Target`s from the provided public input `Target`s.
|
|
|
|
|
/// The provided `pis` should start with the block metadata.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn from_public_inputs(pis: &[Target]) -> Self {
|
2023-08-07 21:00:32 +02:00
|
|
|
let block_beneficiary = pis[0..5].try_into().unwrap();
|
|
|
|
|
let block_timestamp = pis[5];
|
|
|
|
|
let block_number = pis[6];
|
|
|
|
|
let block_difficulty = pis[7];
|
2023-09-25 18:20:22 +02:00
|
|
|
let block_random = pis[8..16].try_into().unwrap();
|
2023-11-17 10:01:26 -05:00
|
|
|
let block_gaslimit = pis[16];
|
|
|
|
|
let block_chain_id = pis[17];
|
|
|
|
|
let block_base_fee = pis[18..20].try_into().unwrap();
|
|
|
|
|
let block_gas_used = pis[20];
|
|
|
|
|
let block_bloom = pis[21..85].try_into().unwrap();
|
2023-08-07 21:00:32 +02:00
|
|
|
|
|
|
|
|
Self {
|
|
|
|
|
block_beneficiary,
|
|
|
|
|
block_timestamp,
|
|
|
|
|
block_number,
|
|
|
|
|
block_difficulty,
|
2023-09-25 18:20:22 +02:00
|
|
|
block_random,
|
2023-08-07 21:00:32 +02:00
|
|
|
block_gaslimit,
|
|
|
|
|
block_chain_id,
|
|
|
|
|
block_base_fee,
|
2023-08-23 23:29:58 +01:00
|
|
|
block_gas_used,
|
2023-05-04 09:57:02 +02:00
|
|
|
block_bloom,
|
2023-08-07 21:00:32 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// If `condition`, returns the block metadata in `bm0`,
|
|
|
|
|
/// otherwise returns the block metadata in `bm1`.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn select<F: RichField + Extendable<D>, const D: usize>(
|
2023-08-07 21:00:32 +02:00
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
|
|
|
|
condition: BoolTarget,
|
|
|
|
|
bm0: Self,
|
|
|
|
|
bm1: Self,
|
|
|
|
|
) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
block_beneficiary: core::array::from_fn(|i| {
|
|
|
|
|
builder.select(
|
|
|
|
|
condition,
|
|
|
|
|
bm0.block_beneficiary[i],
|
|
|
|
|
bm1.block_beneficiary[i],
|
|
|
|
|
)
|
|
|
|
|
}),
|
|
|
|
|
block_timestamp: builder.select(condition, bm0.block_timestamp, bm1.block_timestamp),
|
|
|
|
|
block_number: builder.select(condition, bm0.block_number, bm1.block_number),
|
|
|
|
|
block_difficulty: builder.select(condition, bm0.block_difficulty, bm1.block_difficulty),
|
2023-09-25 18:20:22 +02:00
|
|
|
block_random: core::array::from_fn(|i| {
|
|
|
|
|
builder.select(condition, bm0.block_random[i], bm1.block_random[i])
|
|
|
|
|
}),
|
2023-11-17 10:01:26 -05:00
|
|
|
block_gaslimit: builder.select(condition, bm0.block_gaslimit, bm1.block_gaslimit),
|
2023-08-07 21:00:32 +02:00
|
|
|
block_chain_id: builder.select(condition, bm0.block_chain_id, bm1.block_chain_id),
|
2023-08-19 10:23:24 -04:00
|
|
|
block_base_fee: core::array::from_fn(|i| {
|
|
|
|
|
builder.select(condition, bm0.block_base_fee[i], bm1.block_base_fee[i])
|
|
|
|
|
}),
|
2023-11-17 10:01:26 -05:00
|
|
|
block_gas_used: builder.select(condition, bm0.block_gas_used, bm1.block_gas_used),
|
2023-05-04 09:57:02 +02:00
|
|
|
block_bloom: core::array::from_fn(|i| {
|
|
|
|
|
builder.select(condition, bm0.block_bloom[i], bm1.block_bloom[i])
|
|
|
|
|
}),
|
2023-08-07 21:00:32 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Connects the block metadata in `bm0` to the block metadata in `bm1`.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn connect<F: RichField + Extendable<D>, const D: usize>(
|
2023-08-07 21:00:32 +02:00
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
|
|
|
|
bm0: Self,
|
|
|
|
|
bm1: Self,
|
|
|
|
|
) {
|
|
|
|
|
for i in 0..5 {
|
|
|
|
|
builder.connect(bm0.block_beneficiary[i], bm1.block_beneficiary[i]);
|
|
|
|
|
}
|
|
|
|
|
builder.connect(bm0.block_timestamp, bm1.block_timestamp);
|
|
|
|
|
builder.connect(bm0.block_number, bm1.block_number);
|
|
|
|
|
builder.connect(bm0.block_difficulty, bm1.block_difficulty);
|
2023-09-25 18:20:22 +02:00
|
|
|
for i in 0..8 {
|
|
|
|
|
builder.connect(bm0.block_random[i], bm1.block_random[i]);
|
|
|
|
|
}
|
2023-11-17 10:01:26 -05:00
|
|
|
builder.connect(bm0.block_gaslimit, bm1.block_gaslimit);
|
2023-08-07 21:00:32 +02:00
|
|
|
builder.connect(bm0.block_chain_id, bm1.block_chain_id);
|
2023-08-19 10:23:24 -04:00
|
|
|
for i in 0..2 {
|
|
|
|
|
builder.connect(bm0.block_base_fee[i], bm1.block_base_fee[i])
|
|
|
|
|
}
|
2023-11-17 10:01:26 -05:00
|
|
|
builder.connect(bm0.block_gas_used, bm1.block_gas_used);
|
2023-05-04 09:57:02 +02:00
|
|
|
for i in 0..64 {
|
|
|
|
|
builder.connect(bm0.block_bloom[i], bm1.block_bloom[i])
|
|
|
|
|
}
|
2023-08-07 21:00:32 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Circuit version of `BlockHashes`.
|
|
|
|
|
/// `Target`s for the user-provided previous 256 block hashes and current block hash.
|
|
|
|
|
/// Each block hash requires 8 `Target`s.
|
|
|
|
|
/// The proofs across consecutive blocks ensure that these values
|
|
|
|
|
/// are consistent (i.e. shifted by eight `Target`s to the left).
|
|
|
|
|
///
|
|
|
|
|
/// When the block number is less than 256, dummy values, i.e. `H256::default()`,
|
|
|
|
|
/// should be used for the additional block hashes.
|
2023-08-21 23:32:53 +01:00
|
|
|
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) struct BlockHashesTarget {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target`s for the previous 256 hashes to the current block. The leftmost hash, i.e. `prev_hashes[0..8]`,
|
|
|
|
|
/// is the oldest, and the rightmost, i.e. `prev_hashes[255 * 7..255 * 8]` is the hash of the parent block.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) prev_hashes: [Target; 2048],
|
2023-10-30 14:28:24 -04:00
|
|
|
// `Target` for the hash of the current block.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) cur_hash: [Target; 8],
|
2023-08-21 23:32:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl BlockHashesTarget {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Number of `Target`s required for previous and current block hashes.
|
2023-12-05 11:42:40 -05:00
|
|
|
pub(crate) const SIZE: usize = 2056;
|
2023-10-30 14:28:24 -04:00
|
|
|
|
|
|
|
|
/// Extracts the previous and current block hash `Target`s from the public input `Target`s.
|
|
|
|
|
/// The provided `pis` should start with the block hashes.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn from_public_inputs(pis: &[Target]) -> Self {
|
2023-08-21 23:32:53 +01:00
|
|
|
Self {
|
|
|
|
|
prev_hashes: pis[0..2048].try_into().unwrap(),
|
|
|
|
|
cur_hash: pis[2048..2056].try_into().unwrap(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// If `condition`, returns the block hashes in `bm0`,
|
|
|
|
|
/// otherwise returns the block hashes in `bm1`.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn select<F: RichField + Extendable<D>, const D: usize>(
|
2023-08-21 23:32:53 +01:00
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
|
|
|
|
condition: BoolTarget,
|
|
|
|
|
bm0: Self,
|
|
|
|
|
bm1: Self,
|
|
|
|
|
) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
prev_hashes: core::array::from_fn(|i| {
|
|
|
|
|
builder.select(condition, bm0.prev_hashes[i], bm1.prev_hashes[i])
|
|
|
|
|
}),
|
|
|
|
|
cur_hash: core::array::from_fn(|i| {
|
|
|
|
|
builder.select(condition, bm0.cur_hash[i], bm1.cur_hash[i])
|
|
|
|
|
}),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Connects the block hashes in `bm0` to the block hashes in `bm1`.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn connect<F: RichField + Extendable<D>, const D: usize>(
|
2023-08-21 23:32:53 +01:00
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
|
|
|
|
bm0: Self,
|
|
|
|
|
bm1: Self,
|
|
|
|
|
) {
|
|
|
|
|
for i in 0..2048 {
|
|
|
|
|
builder.connect(bm0.prev_hashes[i], bm1.prev_hashes[i]);
|
|
|
|
|
}
|
|
|
|
|
for i in 0..8 {
|
|
|
|
|
builder.connect(bm0.cur_hash[i], bm1.cur_hash[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Circuit version of `ExtraBlockData`.
|
|
|
|
|
/// Additional block data that are specific to the local transaction being proven,
|
|
|
|
|
/// unlike `BlockMetadata`.
|
2023-08-23 23:29:58 +01:00
|
|
|
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) struct ExtraBlockDataTarget {
|
2023-12-09 06:26:55 +01:00
|
|
|
/// `Target`s for the state trie digest of the checkpoint block.
|
|
|
|
|
pub checkpoint_state_trie_root: [Target; 8],
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target` for the transaction count prior execution of the local state transition, starting
|
|
|
|
|
/// at 0 for the initial trnasaction of a block.
|
2023-08-23 23:29:58 +01:00
|
|
|
pub txn_number_before: Target,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target` for the transaction count after execution of the local state transition.
|
2023-08-23 23:29:58 +01:00
|
|
|
pub txn_number_after: Target,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target` for the accumulated gas used prior execution of the local state transition, starting
|
|
|
|
|
/// at 0 for the initial transaction of a block.
|
2023-11-17 10:01:26 -05:00
|
|
|
pub gas_used_before: Target,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target` for the accumulated gas used after execution of the local state transition. It should
|
|
|
|
|
/// match the `block_gas_used` value after execution of the last transaction in a block.
|
2023-11-17 10:01:26 -05:00
|
|
|
pub gas_used_after: Target,
|
2023-08-23 23:29:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ExtraBlockDataTarget {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Number of `Target`s required for the extra block data.
|
2023-11-30 13:11:38 -05:00
|
|
|
const SIZE: usize = 12;
|
2023-08-23 23:29:58 +01:00
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Extracts the extra block data `Target`s from the public input `Target`s.
|
|
|
|
|
/// The provided `pis` should start with the extra vblock data.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn from_public_inputs(pis: &[Target]) -> Self {
|
2023-12-09 06:26:55 +01:00
|
|
|
let checkpoint_state_trie_root = pis[0..8].try_into().unwrap();
|
2023-09-11 15:47:33 +01:00
|
|
|
let txn_number_before = pis[8];
|
|
|
|
|
let txn_number_after = pis[9];
|
2023-11-17 10:01:26 -05:00
|
|
|
let gas_used_before = pis[10];
|
|
|
|
|
let gas_used_after = pis[11];
|
2023-08-23 23:29:58 +01:00
|
|
|
|
|
|
|
|
Self {
|
2023-12-09 06:26:55 +01:00
|
|
|
checkpoint_state_trie_root,
|
2023-08-23 23:29:58 +01:00
|
|
|
txn_number_before,
|
|
|
|
|
txn_number_after,
|
|
|
|
|
gas_used_before,
|
|
|
|
|
gas_used_after,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// If `condition`, returns the extra block data in `ed0`,
|
|
|
|
|
/// otherwise returns the extra block data in `ed1`.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn select<F: RichField + Extendable<D>, const D: usize>(
|
2023-08-23 23:29:58 +01:00
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
|
|
|
|
condition: BoolTarget,
|
|
|
|
|
ed0: Self,
|
|
|
|
|
ed1: Self,
|
|
|
|
|
) -> Self {
|
|
|
|
|
Self {
|
2023-12-09 06:26:55 +01:00
|
|
|
checkpoint_state_trie_root: core::array::from_fn(|i| {
|
2023-09-11 15:47:33 +01:00
|
|
|
builder.select(
|
|
|
|
|
condition,
|
2023-12-09 06:26:55 +01:00
|
|
|
ed0.checkpoint_state_trie_root[i],
|
|
|
|
|
ed1.checkpoint_state_trie_root[i],
|
2023-09-11 15:47:33 +01:00
|
|
|
)
|
|
|
|
|
}),
|
2023-08-23 23:29:58 +01:00
|
|
|
txn_number_before: builder.select(
|
|
|
|
|
condition,
|
|
|
|
|
ed0.txn_number_before,
|
|
|
|
|
ed1.txn_number_before,
|
|
|
|
|
),
|
|
|
|
|
txn_number_after: builder.select(condition, ed0.txn_number_after, ed1.txn_number_after),
|
2023-11-17 10:01:26 -05:00
|
|
|
gas_used_before: builder.select(condition, ed0.gas_used_before, ed1.gas_used_before),
|
|
|
|
|
gas_used_after: builder.select(condition, ed0.gas_used_after, ed1.gas_used_after),
|
2023-08-23 23:29:58 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Connects the extra block data in `ed0` with the extra block data in `ed1`.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn connect<F: RichField + Extendable<D>, const D: usize>(
|
2023-08-23 23:29:58 +01:00
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
|
|
|
|
ed0: Self,
|
|
|
|
|
ed1: Self,
|
|
|
|
|
) {
|
2023-09-11 15:47:33 +01:00
|
|
|
for i in 0..8 {
|
2023-10-03 17:47:10 +02:00
|
|
|
builder.connect(
|
2023-12-09 06:26:55 +01:00
|
|
|
ed0.checkpoint_state_trie_root[i],
|
|
|
|
|
ed1.checkpoint_state_trie_root[i],
|
2023-10-03 17:47:10 +02:00
|
|
|
);
|
2023-09-11 15:47:33 +01:00
|
|
|
}
|
2023-08-23 23:29:58 +01:00
|
|
|
builder.connect(ed0.txn_number_before, ed1.txn_number_before);
|
|
|
|
|
builder.connect(ed0.txn_number_after, ed1.txn_number_after);
|
2023-11-17 10:01:26 -05:00
|
|
|
builder.connect(ed0.gas_used_before, ed1.gas_used_before);
|
2023-12-07 12:08:47 -05:00
|
|
|
builder.connect(ed0.gas_used_after, ed1.gas_used_after);
|
2023-08-23 23:29:58 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Merkle caps and openings that form the proof of a single STARK.
|
2022-05-04 20:57:07 +02:00
|
|
|
#[derive(Debug, Clone)]
|
2023-04-01 09:34:13 -04:00
|
|
|
pub struct StarkProof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> {
|
2022-05-04 20:57:07 +02:00
|
|
|
/// Merkle cap of LDEs of trace values.
|
2023-05-11 02:59:02 +10:00
|
|
|
pub trace_cap: MerkleCap<F, C::Hasher>,
|
2023-02-13 15:58:26 +01:00
|
|
|
/// Merkle cap of LDEs of lookup helper and CTL columns.
|
|
|
|
|
pub auxiliary_polys_cap: MerkleCap<F, C::Hasher>,
|
2023-09-05 14:20:46 -07:00
|
|
|
/// Merkle cap of LDEs of quotient polynomial evaluations.
|
2023-05-11 02:59:02 +10:00
|
|
|
pub quotient_polys_cap: MerkleCap<F, C::Hasher>,
|
2022-05-04 20:57:07 +02:00
|
|
|
/// Purported values of each polynomial at the challenge point.
|
|
|
|
|
pub openings: StarkOpeningSet<F, D>,
|
|
|
|
|
/// A batch FRI argument for all openings.
|
2023-05-11 02:59:02 +10:00
|
|
|
pub opening_proof: FriProof<F, C::Hasher, D>,
|
2022-05-04 20:57:07 +02:00
|
|
|
}
|
|
|
|
|
|
Shrink STARK proofs to a constant degree
The goal here is to end up with a single "root" circuit representing any EVM proof. I.e. it must verify each STARK, but be general enough to work with any combination of STARK sizes (within some range of sizes that we chose to support). This root circuit can then be plugged into our aggregation circuit.
In particular, for each STARK, and for each initial `degree_bits` (within a range that we choose to support), this adds a "shrinking chain" of circuits. Such a chain shrinks a STARK proof from that initial `degree_bits` down to a constant, `THRESHOLD_DEGREE_BITS`.
The root circuit then combines these shrunk-to-constant proofs for each table. It's similar to `RecursiveAllProof::verify_circuit`; I adapted the code from there and I think we can remove it after. The main difference is that now instead of having one verification key per STARK, we have several possible VKs, one per initial `degree_bits`. We bake the list of possible VKs into the root circuit, and have the prover indicate the index of the VK they're actually using.
This also partially removes the default feature of CTLs. So far we've used filters instead of defaults. Until now it was easy to keep supporting defaults just in case, but here maintaining support would require some more work. E.g. we couldn't use `exp_u64` any more, since the size delta is now dynamic, it can't be hardcoded. If there are no concerns, I'll fully remove the feature after.
2022-12-27 18:15:18 -08:00
|
|
|
/// A `StarkProof` along with some metadata about the initial Fiat-Shamir state, which is used when
|
|
|
|
|
/// creating a recursive wrapper proof around a STARK proof.
|
|
|
|
|
#[derive(Debug, Clone)]
|
2023-04-01 09:34:13 -04:00
|
|
|
pub struct StarkProofWithMetadata<F, C, const D: usize>
|
Shrink STARK proofs to a constant degree
The goal here is to end up with a single "root" circuit representing any EVM proof. I.e. it must verify each STARK, but be general enough to work with any combination of STARK sizes (within some range of sizes that we chose to support). This root circuit can then be plugged into our aggregation circuit.
In particular, for each STARK, and for each initial `degree_bits` (within a range that we choose to support), this adds a "shrinking chain" of circuits. Such a chain shrinks a STARK proof from that initial `degree_bits` down to a constant, `THRESHOLD_DEGREE_BITS`.
The root circuit then combines these shrunk-to-constant proofs for each table. It's similar to `RecursiveAllProof::verify_circuit`; I adapted the code from there and I think we can remove it after. The main difference is that now instead of having one verification key per STARK, we have several possible VKs, one per initial `degree_bits`. We bake the list of possible VKs into the root circuit, and have the prover indicate the index of the VK they're actually using.
This also partially removes the default feature of CTLs. So far we've used filters instead of defaults. Until now it was easy to keep supporting defaults just in case, but here maintaining support would require some more work. E.g. we couldn't use `exp_u64` any more, since the size delta is now dynamic, it can't be hardcoded. If there are no concerns, I'll fully remove the feature after.
2022-12-27 18:15:18 -08:00
|
|
|
where
|
|
|
|
|
F: RichField + Extendable<D>,
|
2023-04-01 09:34:13 -04:00
|
|
|
C: GenericConfig<D, F = F>,
|
Shrink STARK proofs to a constant degree
The goal here is to end up with a single "root" circuit representing any EVM proof. I.e. it must verify each STARK, but be general enough to work with any combination of STARK sizes (within some range of sizes that we chose to support). This root circuit can then be plugged into our aggregation circuit.
In particular, for each STARK, and for each initial `degree_bits` (within a range that we choose to support), this adds a "shrinking chain" of circuits. Such a chain shrinks a STARK proof from that initial `degree_bits` down to a constant, `THRESHOLD_DEGREE_BITS`.
The root circuit then combines these shrunk-to-constant proofs for each table. It's similar to `RecursiveAllProof::verify_circuit`; I adapted the code from there and I think we can remove it after. The main difference is that now instead of having one verification key per STARK, we have several possible VKs, one per initial `degree_bits`. We bake the list of possible VKs into the root circuit, and have the prover indicate the index of the VK they're actually using.
This also partially removes the default feature of CTLs. So far we've used filters instead of defaults. Until now it was easy to keep supporting defaults just in case, but here maintaining support would require some more work. E.g. we couldn't use `exp_u64` any more, since the size delta is now dynamic, it can't be hardcoded. If there are no concerns, I'll fully remove the feature after.
2022-12-27 18:15:18 -08:00
|
|
|
{
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Initial Fiat-Shamir state.
|
2023-05-11 02:59:02 +10:00
|
|
|
pub(crate) init_challenger_state: <C::Hasher as Hasher<F>>::Permutation,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Proof for a single STARK.
|
2023-10-09 09:07:01 -04:00
|
|
|
pub(crate) proof: StarkProof<F, C, D>,
|
Shrink STARK proofs to a constant degree
The goal here is to end up with a single "root" circuit representing any EVM proof. I.e. it must verify each STARK, but be general enough to work with any combination of STARK sizes (within some range of sizes that we chose to support). This root circuit can then be plugged into our aggregation circuit.
In particular, for each STARK, and for each initial `degree_bits` (within a range that we choose to support), this adds a "shrinking chain" of circuits. Such a chain shrinks a STARK proof from that initial `degree_bits` down to a constant, `THRESHOLD_DEGREE_BITS`.
The root circuit then combines these shrunk-to-constant proofs for each table. It's similar to `RecursiveAllProof::verify_circuit`; I adapted the code from there and I think we can remove it after. The main difference is that now instead of having one verification key per STARK, we have several possible VKs, one per initial `degree_bits`. We bake the list of possible VKs into the root circuit, and have the prover indicate the index of the VK they're actually using.
This also partially removes the default feature of CTLs. So far we've used filters instead of defaults. Until now it was easy to keep supporting defaults just in case, but here maintaining support would require some more work. E.g. we couldn't use `exp_u64` any more, since the size delta is now dynamic, it can't be hardcoded. If there are no concerns, I'll fully remove the feature after.
2022-12-27 18:15:18 -08:00
|
|
|
}
|
|
|
|
|
|
2023-04-01 09:34:13 -04:00
|
|
|
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> StarkProof<F, C, D> {
|
2022-05-04 20:57:07 +02:00
|
|
|
/// 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
|
|
|
|
|
}
|
2022-08-25 22:04:28 +02:00
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Returns the number of cross-table lookup polynomials computed for the current STARK.
|
2022-08-25 22:04:28 +02:00
|
|
|
pub fn num_ctl_zs(&self) -> usize {
|
2023-09-11 14:11:13 -04:00
|
|
|
self.openings.ctl_zs_first.len()
|
2022-08-25 22:04:28 +02:00
|
|
|
}
|
2022-05-04 20:57:07 +02:00
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Circuit version of `StarkProof`.
|
|
|
|
|
/// Merkle caps and openings that form the proof of a single STARK.
|
2023-02-21 21:12:03 -05:00
|
|
|
#[derive(Eq, PartialEq, Debug)]
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) struct StarkProofTarget<const D: usize> {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target` for the Merkle cap if LDEs of trace values.
|
2022-05-04 20:57:07 +02:00
|
|
|
pub trace_cap: MerkleCapTarget,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target` for the Merkle cap of LDEs of lookup helper and CTL columns.
|
2023-02-13 15:58:26 +01:00
|
|
|
pub auxiliary_polys_cap: MerkleCapTarget,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target` for the Merkle cap of LDEs of quotient polynomial evaluations.
|
2022-05-04 20:57:07 +02:00
|
|
|
pub quotient_polys_cap: MerkleCapTarget,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target`s for the purported values of each polynomial at the challenge point.
|
2022-05-04 20:57:07 +02:00
|
|
|
pub openings: StarkOpeningSetTarget<D>,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target`s for the batch FRI argument for all openings.
|
2022-05-04 20:57:07 +02:00
|
|
|
pub opening_proof: FriProofTarget<D>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<const D: usize> StarkProofTarget<D> {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Serializes a STARK proof.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn to_buffer(&self, buffer: &mut Vec<u8>) -> IoResult<()> {
|
2023-02-21 21:12:03 -05:00
|
|
|
buffer.write_target_merkle_cap(&self.trace_cap)?;
|
2023-02-13 15:58:26 +01:00
|
|
|
buffer.write_target_merkle_cap(&self.auxiliary_polys_cap)?;
|
2023-02-21 21:12:03 -05:00
|
|
|
buffer.write_target_merkle_cap(&self.quotient_polys_cap)?;
|
|
|
|
|
buffer.write_target_fri_proof(&self.opening_proof)?;
|
|
|
|
|
self.openings.to_buffer(buffer)?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Deserializes a STARK proof.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn from_buffer(buffer: &mut Buffer) -> IoResult<Self> {
|
2023-02-21 21:12:03 -05:00
|
|
|
let trace_cap = buffer.read_target_merkle_cap()?;
|
2023-02-13 15:58:26 +01:00
|
|
|
let auxiliary_polys_cap = buffer.read_target_merkle_cap()?;
|
2023-02-21 21:12:03 -05:00
|
|
|
let quotient_polys_cap = buffer.read_target_merkle_cap()?;
|
|
|
|
|
let opening_proof = buffer.read_target_fri_proof()?;
|
|
|
|
|
let openings = StarkOpeningSetTarget::from_buffer(buffer)?;
|
|
|
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
|
trace_cap,
|
2023-02-13 15:58:26 +01:00
|
|
|
auxiliary_polys_cap,
|
2023-02-21 21:12:03 -05:00
|
|
|
quotient_polys_cap,
|
|
|
|
|
openings,
|
|
|
|
|
opening_proof,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-04 20:57:07 +02:00
|
|
|
/// Recover the length of the trace from a STARK proof and a STARK config.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn recover_degree_bits(&self, config: &StarkConfig) -> usize {
|
2022-05-04 20:57:07 +02:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Randomness used for a STARK proof.
|
2022-05-04 20:57:07 +02:00
|
|
|
pub(crate) struct StarkProofChallenges<F: RichField + Extendable<D>, const D: usize> {
|
|
|
|
|
/// 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,
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Randomness used in FRI.
|
2022-05-04 20:57:07 +02:00
|
|
|
pub fri_challenges: FriChallenges<F, D>,
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Circuit version of `StarkProofChallenges`.
|
2022-05-04 20:57:07 +02:00
|
|
|
pub(crate) struct StarkProofChallengesTarget<const D: usize> {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target`s for the random values used to combine STARK constraints.
|
2022-05-04 20:57:07 +02:00
|
|
|
pub stark_alphas: Vec<Target>,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `ExtensionTarget` for the point at which the STARK polynomials are opened.
|
2022-05-04 20:57:07 +02:00
|
|
|
pub stark_zeta: ExtensionTarget<D>,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `Target`s for the randomness used in FRI.
|
2022-05-04 20:57:07 +02:00
|
|
|
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> {
|
2022-05-16 20:45:30 +02:00
|
|
|
/// Openings of trace polynomials at `zeta`.
|
2022-05-04 20:57:07 +02:00
|
|
|
pub local_values: Vec<F::Extension>,
|
2022-05-16 20:45:30 +02:00
|
|
|
/// Openings of trace polynomials at `g * zeta`.
|
2022-05-04 20:57:07 +02:00
|
|
|
pub next_values: Vec<F::Extension>,
|
2023-02-13 15:58:26 +01:00
|
|
|
/// Openings of lookups and cross-table lookups `Z` polynomials at `zeta`.
|
|
|
|
|
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>,
|
2023-09-11 14:11:13 -04:00
|
|
|
/// Openings of cross-table lookups `Z` polynomials at `1`.
|
|
|
|
|
pub ctl_zs_first: Vec<F>,
|
2022-05-16 20:45:30 +02:00
|
|
|
/// Openings of quotient polynomials at `zeta`.
|
2022-05-04 20:57:07 +02:00
|
|
|
pub quotient_polys: Vec<F::Extension>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<F: RichField + Extendable<D>, const D: usize> StarkOpeningSet<F, D> {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Returns a `StarkOpeningSet` given all the polynomial commitments, the number of permutation `Z`polynomials,
|
|
|
|
|
/// the evaluation point and a generator `g`.
|
|
|
|
|
/// Polynomials are evaluated at point `zeta` and, if necessary, at `g * zeta`.
|
2023-04-01 09:34:13 -04:00
|
|
|
pub fn new<C: GenericConfig<D, F = F>>(
|
2022-05-04 20:57:07 +02:00
|
|
|
zeta: F::Extension,
|
|
|
|
|
g: F,
|
2023-04-01 09:34:13 -04:00
|
|
|
trace_commitment: &PolynomialBatch<F, C, D>,
|
2023-02-13 15:58:26 +01:00
|
|
|
auxiliary_polys_commitment: &PolynomialBatch<F, C, D>,
|
2023-04-01 09:34:13 -04:00
|
|
|
quotient_commitment: &PolynomialBatch<F, C, D>,
|
2023-02-13 15:58:26 +01:00
|
|
|
num_lookup_columns: usize,
|
2022-05-04 20:57:07 +02:00
|
|
|
) -> Self {
|
2023-10-30 14:28:24 -04:00
|
|
|
// Batch evaluates polynomials on the LDE, at a point `z`.
|
2023-04-01 09:34:13 -04:00
|
|
|
let eval_commitment = |z: F::Extension, c: &PolynomialBatch<F, C, D>| {
|
2022-05-04 20:57:07 +02:00
|
|
|
c.polynomials
|
|
|
|
|
.par_iter()
|
|
|
|
|
.map(|p| p.to_extension().eval(z))
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
};
|
2023-10-30 14:28:24 -04:00
|
|
|
// Batch evaluates polynomials at a base field point `z`.
|
2023-04-01 09:34:13 -04:00
|
|
|
let eval_commitment_base = |z: F, c: &PolynomialBatch<F, C, D>| {
|
2022-05-11 16:09:12 +02:00
|
|
|
c.polynomials
|
|
|
|
|
.par_iter()
|
|
|
|
|
.map(|p| p.eval(z))
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
};
|
2023-10-30 14:28:24 -04:00
|
|
|
// `g * zeta`.
|
2022-06-02 23:55:56 +02:00
|
|
|
let zeta_next = zeta.scalar_mul(g);
|
2022-05-04 20:57:07 +02:00
|
|
|
Self {
|
|
|
|
|
local_values: eval_commitment(zeta, trace_commitment),
|
2022-06-02 23:55:56 +02:00
|
|
|
next_values: eval_commitment(zeta_next, trace_commitment),
|
2023-02-13 15:58:26 +01:00
|
|
|
auxiliary_polys: eval_commitment(zeta, auxiliary_polys_commitment),
|
|
|
|
|
auxiliary_polys_next: eval_commitment(zeta_next, auxiliary_polys_commitment),
|
2023-09-19 10:56:17 -04:00
|
|
|
ctl_zs_first: eval_commitment_base(F::ONE, auxiliary_polys_commitment)
|
|
|
|
|
[num_lookup_columns..]
|
2022-05-17 09:41:06 +02:00
|
|
|
.to_vec(),
|
2022-05-04 20:57:07 +02:00
|
|
|
quotient_polys: eval_commitment(zeta, quotient_commitment),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Constructs the openings required by FRI.
|
|
|
|
|
/// All openings but `ctl_zs_first` are grouped together.
|
2022-05-04 20:57:07 +02:00
|
|
|
pub(crate) fn to_fri_openings(&self) -> FriOpenings<F, D> {
|
|
|
|
|
let zeta_batch = FriOpeningBatch {
|
|
|
|
|
values: self
|
|
|
|
|
.local_values
|
|
|
|
|
.iter()
|
2023-02-13 15:58:26 +01:00
|
|
|
.chain(&self.auxiliary_polys)
|
2022-05-04 20:57:07 +02:00
|
|
|
.chain(&self.quotient_polys)
|
|
|
|
|
.copied()
|
|
|
|
|
.collect_vec(),
|
|
|
|
|
};
|
2022-06-02 23:55:56 +02:00
|
|
|
let zeta_next_batch = FriOpeningBatch {
|
2022-05-04 20:57:07 +02:00
|
|
|
values: self
|
|
|
|
|
.next_values
|
|
|
|
|
.iter()
|
2023-02-13 15:58:26 +01:00
|
|
|
.chain(&self.auxiliary_polys_next)
|
2022-05-04 20:57:07 +02:00
|
|
|
.copied()
|
|
|
|
|
.collect_vec(),
|
|
|
|
|
};
|
2023-09-11 14:11:13 -04:00
|
|
|
debug_assert!(!self.ctl_zs_first.is_empty());
|
|
|
|
|
let ctl_first_batch = FriOpeningBatch {
|
2022-05-20 11:21:13 +02:00
|
|
|
values: self
|
2023-09-11 14:11:13 -04:00
|
|
|
.ctl_zs_first
|
2022-05-20 11:21:13 +02:00
|
|
|
.iter()
|
|
|
|
|
.copied()
|
|
|
|
|
.map(F::Extension::from_basefield)
|
|
|
|
|
.collect(),
|
|
|
|
|
};
|
2022-05-10 15:21:09 +02:00
|
|
|
|
2022-05-20 11:21:13 +02:00
|
|
|
FriOpenings {
|
2023-09-11 14:11:13 -04:00
|
|
|
batches: vec![zeta_batch, zeta_next_batch, ctl_first_batch],
|
2022-05-20 11:21:13 +02:00
|
|
|
}
|
2022-05-04 20:57:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Circuit version of `StarkOpeningSet`.
|
|
|
|
|
/// `Target`s for the purported values of each polynomial at the challenge point.
|
2023-02-21 21:12:03 -05:00
|
|
|
#[derive(Eq, PartialEq, Debug)]
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) struct StarkOpeningSetTarget<const D: usize> {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `ExtensionTarget`s for the openings of trace polynomials at `zeta`.
|
2022-05-04 20:57:07 +02:00
|
|
|
pub local_values: Vec<ExtensionTarget<D>>,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `ExtensionTarget`s for the opening of trace polynomials at `g * zeta`.
|
2022-05-04 20:57:07 +02:00
|
|
|
pub next_values: Vec<ExtensionTarget<D>>,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `ExtensionTarget`s for the opening of lookups and cross-table lookups `Z` polynomials at `zeta`.
|
2023-02-13 15:58:26 +01:00
|
|
|
pub auxiliary_polys: Vec<ExtensionTarget<D>>,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `ExtensionTarget`s for the opening of lookups and cross-table lookups `Z` polynomials at `g * zeta`.
|
2023-02-13 15:58:26 +01:00
|
|
|
pub auxiliary_polys_next: Vec<ExtensionTarget<D>>,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// /// `ExtensionTarget`s for the opening of lookups and cross-table lookups `Z` polynomials at 1.
|
2023-09-11 14:11:13 -04:00
|
|
|
pub ctl_zs_first: Vec<Target>,
|
2023-10-30 14:28:24 -04:00
|
|
|
/// `ExtensionTarget`s for the opening of quotient polynomials at `zeta`.
|
2022-05-04 20:57:07 +02:00
|
|
|
pub quotient_polys: Vec<ExtensionTarget<D>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<const D: usize> StarkOpeningSetTarget<D> {
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Serializes a STARK's opening set.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn to_buffer(&self, buffer: &mut Vec<u8>) -> IoResult<()> {
|
2023-02-21 21:12:03 -05:00
|
|
|
buffer.write_target_ext_vec(&self.local_values)?;
|
|
|
|
|
buffer.write_target_ext_vec(&self.next_values)?;
|
2023-02-13 15:58:26 +01:00
|
|
|
buffer.write_target_ext_vec(&self.auxiliary_polys)?;
|
|
|
|
|
buffer.write_target_ext_vec(&self.auxiliary_polys_next)?;
|
2023-09-11 14:11:13 -04:00
|
|
|
buffer.write_target_vec(&self.ctl_zs_first)?;
|
2023-02-21 21:12:03 -05:00
|
|
|
buffer.write_target_ext_vec(&self.quotient_polys)?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Deserializes a STARK's opening set.
|
2023-11-13 09:26:56 -05:00
|
|
|
pub(crate) fn from_buffer(buffer: &mut Buffer) -> IoResult<Self> {
|
2023-02-21 21:12:03 -05:00
|
|
|
let local_values = buffer.read_target_ext_vec::<D>()?;
|
|
|
|
|
let next_values = buffer.read_target_ext_vec::<D>()?;
|
2023-02-13 15:58:26 +01:00
|
|
|
let auxiliary_polys = buffer.read_target_ext_vec::<D>()?;
|
|
|
|
|
let auxiliary_polys_next = buffer.read_target_ext_vec::<D>()?;
|
2023-09-11 14:11:13 -04:00
|
|
|
let ctl_zs_first = buffer.read_target_vec()?;
|
2023-02-21 21:12:03 -05:00
|
|
|
let quotient_polys = buffer.read_target_ext_vec::<D>()?;
|
|
|
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
|
local_values,
|
|
|
|
|
next_values,
|
2023-02-13 15:58:26 +01:00
|
|
|
auxiliary_polys,
|
|
|
|
|
auxiliary_polys_next,
|
2023-09-11 14:11:13 -04:00
|
|
|
ctl_zs_first,
|
2023-02-21 21:12:03 -05:00
|
|
|
quotient_polys,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:28:24 -04:00
|
|
|
/// Circuit version of `to_fri_openings`for `FriOpenings`.
|
|
|
|
|
/// Constructs the `Target`s the circuit version of FRI.
|
|
|
|
|
/// All openings but `ctl_zs_first` are grouped together.
|
2022-05-20 11:21:13 +02:00
|
|
|
pub(crate) fn to_fri_openings(&self, zero: Target) -> FriOpeningsTarget<D> {
|
2022-05-04 20:57:07 +02:00
|
|
|
let zeta_batch = FriOpeningBatchTarget {
|
|
|
|
|
values: self
|
|
|
|
|
.local_values
|
|
|
|
|
.iter()
|
2023-02-13 15:58:26 +01:00
|
|
|
.chain(&self.auxiliary_polys)
|
2022-05-04 20:57:07 +02:00
|
|
|
.chain(&self.quotient_polys)
|
|
|
|
|
.copied()
|
|
|
|
|
.collect_vec(),
|
|
|
|
|
};
|
2022-06-02 23:55:56 +02:00
|
|
|
let zeta_next_batch = FriOpeningBatchTarget {
|
2022-05-04 20:57:07 +02:00
|
|
|
values: self
|
|
|
|
|
.next_values
|
|
|
|
|
.iter()
|
2023-02-13 15:58:26 +01:00
|
|
|
.chain(&self.auxiliary_polys_next)
|
2022-05-04 20:57:07 +02:00
|
|
|
.copied()
|
|
|
|
|
.collect_vec(),
|
|
|
|
|
};
|
2023-09-11 14:11:13 -04:00
|
|
|
debug_assert!(!self.ctl_zs_first.is_empty());
|
|
|
|
|
let ctl_first_batch = FriOpeningBatchTarget {
|
2022-05-20 11:21:13 +02:00
|
|
|
values: self
|
2023-09-11 14:11:13 -04:00
|
|
|
.ctl_zs_first
|
2022-05-20 11:21:13 +02:00
|
|
|
.iter()
|
|
|
|
|
.copied()
|
|
|
|
|
.map(|t| t.to_ext_target(zero))
|
|
|
|
|
.collect(),
|
|
|
|
|
};
|
|
|
|
|
|
2022-05-04 20:57:07 +02:00
|
|
|
FriOpeningsTarget {
|
2023-09-11 14:11:13 -04:00
|
|
|
batches: vec![zeta_batch, zeta_next_batch, ctl_first_batch],
|
2022-05-04 20:57:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|