2022-09-29 13:45:46 -06:00
|
|
|
use ethereum_types::{Address, 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;
|
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-04-01 09:34:13 -04:00
|
|
|
pub stark_proofs: [StarkProofWithMetadata<F, C, D>; NUM_TABLES],
|
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>,
|
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> {
|
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
|
|
|
|
2022-05-11 14:35:33 +02:00
|
|
|
pub(crate) struct AllProofChallenges<F: RichField + Extendable<D>, const D: usize> {
|
2022-08-26 10:12:45 +02:00
|
|
|
pub stark_challenges: [StarkProofChallenges<F, D>; NUM_TABLES],
|
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-10-06 16:40:03 +02:00
|
|
|
#[allow(unused)] // TODO: should be used soon
|
2023-05-11 02:59:02 +10:00
|
|
|
pub(crate) struct AllChallengerState<F: RichField + Extendable<D>, H: Hasher<F>, const D: usize> {
|
2022-09-23 16:36:17 +02:00
|
|
|
/// Sponge state of the challenger before starting each proof,
|
|
|
|
|
/// along with the final state after all proofs are done. This final state isn't strictly needed.
|
2023-05-11 02:59:02 +10:00
|
|
|
pub states: [H::Permutation; NUM_TABLES + 1],
|
2022-09-22 11:01:27 +02:00
|
|
|
pub ctl_challenges: GrandProductChallengeSet<F>,
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-25 22:11:25 -07:00
|
|
|
/// Memory values which are public.
|
2023-08-23 12:36:11 -06:00
|
|
|
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
2022-08-25 12:24:22 -07:00
|
|
|
pub struct PublicValues {
|
|
|
|
|
pub trie_roots_before: TrieRoots,
|
|
|
|
|
pub trie_roots_after: TrieRoots,
|
|
|
|
|
pub block_metadata: BlockMetadata,
|
2023-08-21 23:32:53 +01:00
|
|
|
pub block_hashes: BlockHashes,
|
2023-08-23 23:29:58 +01:00
|
|
|
pub extra_block_data: ExtraBlockData,
|
2022-08-25 12:24:22 -07:00
|
|
|
}
|
|
|
|
|
|
2023-06-21 20:05:39 +02:00
|
|
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
2022-08-25 12:24:22 -07:00
|
|
|
pub struct TrieRoots {
|
2022-09-29 13:45:46 -06:00
|
|
|
pub state_root: H256,
|
|
|
|
|
pub transactions_root: H256,
|
|
|
|
|
pub receipts_root: H256,
|
2022-08-25 12:24:22 -07:00
|
|
|
}
|
|
|
|
|
|
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-08-21 23:32:53 +01:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
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-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.
|
2022-09-19 11:05:48 -06:00
|
|
|
#[derive(Debug, Clone, Default, 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-09-19 15:41:36 -04:00
|
|
|
/// The timestamp of this block.
|
2022-08-25 12:24:22 -07:00
|
|
|
pub block_timestamp: U256,
|
2023-09-19 15:41:36 -04: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-09-19 15:41:36 -04: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-09-19 15:41:36 -04:00
|
|
|
/// The chain id of this block.
|
2022-08-25 12:24:22 -07:00
|
|
|
pub block_chain_id: U256,
|
2023-09-19 15:41:36 -04:00
|
|
|
/// The base fee of this block.
|
2022-08-25 23:35:38 -07:00
|
|
|
pub block_base_fee: U256,
|
2023-09-19 15:41:36 -04: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-09-19 15:41:36 -04:00
|
|
|
/// Additional block data that are specific to the local transaction being proven,
|
|
|
|
|
/// unlike `BlockMetadata`.
|
2023-08-23 23:29:58 +01:00
|
|
|
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
|
|
|
|
pub struct ExtraBlockData {
|
2023-09-26 12:21:29 -04:00
|
|
|
/// The state trie digest of the genesis block.
|
2023-09-11 15:47:33 +01:00
|
|
|
pub genesis_state_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-09-19 15:41:36 -04:00
|
|
|
/// The accumulated bloom filter of this block prior execution of the local state transition,
|
|
|
|
|
/// starting with all zeros for the initial transaction of a block.
|
2023-08-23 23:29:58 +01:00
|
|
|
pub block_bloom_before: [U256; 8],
|
2023-09-19 15:41:36 -04:00
|
|
|
/// The accumulated bloom filter after execution of the local state transition. It should
|
|
|
|
|
/// match the `block_bloom` value after execution of the last transaction in a block.
|
2023-08-23 23:29:58 +01:00
|
|
|
pub block_bloom_after: [U256; 8],
|
|
|
|
|
}
|
|
|
|
|
|
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)]
|
2022-08-25 12:24:22 -07:00
|
|
|
pub struct PublicValuesTarget {
|
|
|
|
|
pub trie_roots_before: TrieRootsTarget,
|
|
|
|
|
pub trie_roots_after: TrieRootsTarget,
|
|
|
|
|
pub block_metadata: BlockMetadataTarget,
|
2023-08-21 23:32:53 +01:00
|
|
|
pub block_hashes: BlockHashesTarget,
|
2023-08-23 23:29:58 +01:00
|
|
|
pub extra_block_data: ExtraBlockDataTarget,
|
2023-06-21 20:05:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
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-06-21 20:05:39 +02:00
|
|
|
buffer.write_target(block_gaslimit)?;
|
|
|
|
|
buffer.write_target(block_chain_id)?;
|
2023-08-22 16:21:10 +01:00
|
|
|
buffer.write_target_array(&block_base_fee)?;
|
2023-08-23 23:29:58 +01: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-09-11 15:47:33 +01:00
|
|
|
genesis_state_root,
|
2023-08-23 23:29:58 +01:00
|
|
|
txn_number_before,
|
|
|
|
|
txn_number_after,
|
|
|
|
|
gas_used_before,
|
|
|
|
|
gas_used_after,
|
|
|
|
|
block_bloom_before,
|
|
|
|
|
block_bloom_after,
|
|
|
|
|
} = self.extra_block_data;
|
2023-09-11 15:47:33 +01:00
|
|
|
buffer.write_target_array(&genesis_state_root)?;
|
2023-08-23 23:29:58 +01:00
|
|
|
buffer.write_target(txn_number_before)?;
|
|
|
|
|
buffer.write_target(txn_number_after)?;
|
|
|
|
|
buffer.write_target(gas_used_before)?;
|
|
|
|
|
buffer.write_target(gas_used_after)?;
|
|
|
|
|
buffer.write_target_array(&block_bloom_before)?;
|
|
|
|
|
buffer.write_target_array(&block_bloom_after)?;
|
|
|
|
|
|
2023-06-21 20:05:39 +02:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn from_buffer(buffer: &mut Buffer) -> IoResult<Self> {
|
|
|
|
|
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-06-21 20:05:39 +02:00
|
|
|
block_gaslimit: buffer.read_target()?,
|
|
|
|
|
block_chain_id: buffer.read_target()?,
|
2023-08-22 16:27:55 +01:00
|
|
|
block_base_fee: buffer.read_target_array()?,
|
2023-08-23 23:29:58 +01: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-09-11 15:47:33 +01:00
|
|
|
genesis_state_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()?,
|
|
|
|
|
gas_used_before: buffer.read_target()?,
|
|
|
|
|
gas_used_after: buffer.read_target()?,
|
|
|
|
|
block_bloom_before: buffer.read_target_array()?,
|
|
|
|
|
block_bloom_after: buffer.read_target_array()?,
|
|
|
|
|
};
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
pub fn from_public_inputs(pis: &[Target]) -> Self {
|
2023-08-23 23:29:58 +01:00
|
|
|
assert!(
|
|
|
|
|
pis.len()
|
|
|
|
|
> TrieRootsTarget::SIZE * 2
|
|
|
|
|
+ BlockMetadataTarget::SIZE
|
2023-08-21 23:32:53 +01:00
|
|
|
+ BlockHashesTarget::BLOCK_HASHES_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
|
|
|
|
|
+ BlockHashesTarget::BLOCK_HASHES_SIZE],
|
|
|
|
|
),
|
2023-08-23 23:29:58 +01:00
|
|
|
extra_block_data: ExtraBlockDataTarget::from_public_inputs(
|
2023-09-07 12:15:17 +01:00
|
|
|
&pis[TrieRootsTarget::SIZE * 2
|
|
|
|
|
+ BlockMetadataTarget::SIZE
|
|
|
|
|
+ BlockHashesTarget::BLOCK_HASHES_SIZE
|
2023-08-23 23:29:58 +01:00
|
|
|
..TrieRootsTarget::SIZE * 2
|
|
|
|
|
+ BlockMetadataTarget::SIZE
|
2023-08-21 23:32:53 +01:00
|
|
|
+ BlockHashesTarget::BLOCK_HASHES_SIZE
|
2023-08-23 23:29:58 +01:00
|
|
|
+ ExtraBlockDataTarget::SIZE],
|
|
|
|
|
),
|
2023-08-07 21:00:32 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn select<F: RichField + Extendable<D>, const D: usize>(
|
|
|
|
|
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-08-07 21:00:32 +02:00
|
|
|
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
|
2022-08-25 12:24:22 -07:00
|
|
|
pub struct TrieRootsTarget {
|
|
|
|
|
pub state_root: [Target; 8],
|
|
|
|
|
pub transactions_root: [Target; 8],
|
|
|
|
|
pub receipts_root: [Target; 8],
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-07 21:00:32 +02:00
|
|
|
impl TrieRootsTarget {
|
2023-08-23 23:29:58 +01:00
|
|
|
pub const SIZE: usize = 24;
|
2023-08-07 21:00:32 +02:00
|
|
|
|
|
|
|
|
pub fn from_public_inputs(pis: &[Target]) -> Self {
|
|
|
|
|
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,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn select<F: RichField + Extendable<D>, const D: usize>(
|
|
|
|
|
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])
|
|
|
|
|
}),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn connect<F: RichField + Extendable<D>, const D: usize>(
|
|
|
|
|
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]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
|
2022-08-25 12:24:22 -07:00
|
|
|
pub struct BlockMetadataTarget {
|
2022-08-25 23:35:38 -07:00
|
|
|
pub block_beneficiary: [Target; 5],
|
2022-08-25 12:24:22 -07:00
|
|
|
pub block_timestamp: Target,
|
|
|
|
|
pub block_number: Target,
|
|
|
|
|
pub block_difficulty: Target,
|
2023-09-25 18:20:22 +02:00
|
|
|
pub block_random: [Target; 8],
|
2022-08-25 12:24:22 -07:00
|
|
|
pub block_gaslimit: Target,
|
|
|
|
|
pub block_chain_id: Target,
|
2023-08-19 10:23:24 -04:00
|
|
|
pub block_base_fee: [Target; 2],
|
2023-08-23 23:29:58 +01:00
|
|
|
pub block_gas_used: Target,
|
2023-05-04 09:57:02 +02:00
|
|
|
pub block_bloom: [Target; 64],
|
2022-05-20 11:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
2023-08-07 21:00:32 +02:00
|
|
|
impl BlockMetadataTarget {
|
2023-09-26 12:24:48 -04:00
|
|
|
pub const SIZE: usize = 85;
|
2023-08-07 21:00:32 +02:00
|
|
|
|
|
|
|
|
pub fn from_public_inputs(pis: &[Target]) -> Self {
|
|
|
|
|
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();
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn select<F: RichField + Extendable<D>, const D: usize>(
|
|
|
|
|
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-08-07 21:00:32 +02:00
|
|
|
block_gaslimit: builder.select(condition, bm0.block_gaslimit, bm1.block_gaslimit),
|
|
|
|
|
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-08-23 23:29:58 +01: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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn connect<F: RichField + Extendable<D>, const D: usize>(
|
|
|
|
|
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-08-07 21:00:32 +02:00
|
|
|
builder.connect(bm0.block_gaslimit, bm1.block_gaslimit);
|
|
|
|
|
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-09-26 15:05:45 +02: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-08-21 23:32:53 +01:00
|
|
|
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
|
|
|
|
|
pub struct BlockHashesTarget {
|
|
|
|
|
pub prev_hashes: [Target; 2048],
|
|
|
|
|
pub cur_hash: [Target; 8],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl BlockHashesTarget {
|
2023-09-11 15:47:33 +01:00
|
|
|
pub const BLOCK_HASHES_SIZE: usize = 2056;
|
2023-08-21 23:32:53 +01:00
|
|
|
pub fn from_public_inputs(pis: &[Target]) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
prev_hashes: pis[0..2048].try_into().unwrap(),
|
|
|
|
|
cur_hash: pis[2048..2056].try_into().unwrap(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn select<F: RichField + Extendable<D>, const D: usize>(
|
|
|
|
|
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])
|
|
|
|
|
}),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn connect<F: RichField + Extendable<D>, const D: usize>(
|
|
|
|
|
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-08-23 23:29:58 +01:00
|
|
|
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
|
|
|
|
|
pub struct ExtraBlockDataTarget {
|
2023-09-11 15:47:33 +01:00
|
|
|
pub genesis_state_root: [Target; 8],
|
2023-08-23 23:29:58 +01:00
|
|
|
pub txn_number_before: Target,
|
|
|
|
|
pub txn_number_after: Target,
|
|
|
|
|
pub gas_used_before: Target,
|
|
|
|
|
pub gas_used_after: Target,
|
|
|
|
|
pub block_bloom_before: [Target; 64],
|
|
|
|
|
pub block_bloom_after: [Target; 64],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ExtraBlockDataTarget {
|
2023-09-11 15:47:33 +01:00
|
|
|
const SIZE: usize = 140;
|
2023-08-23 23:29:58 +01:00
|
|
|
|
|
|
|
|
pub fn from_public_inputs(pis: &[Target]) -> Self {
|
2023-09-11 15:47:33 +01:00
|
|
|
let genesis_state_root = pis[0..8].try_into().unwrap();
|
|
|
|
|
let txn_number_before = pis[8];
|
|
|
|
|
let txn_number_after = pis[9];
|
|
|
|
|
let gas_used_before = pis[10];
|
|
|
|
|
let gas_used_after = pis[11];
|
|
|
|
|
let block_bloom_before = pis[12..76].try_into().unwrap();
|
|
|
|
|
let block_bloom_after = pis[76..140].try_into().unwrap();
|
2023-08-23 23:29:58 +01:00
|
|
|
|
|
|
|
|
Self {
|
2023-09-11 15:47:33 +01:00
|
|
|
genesis_state_root,
|
2023-08-23 23:29:58 +01:00
|
|
|
txn_number_before,
|
|
|
|
|
txn_number_after,
|
|
|
|
|
gas_used_before,
|
|
|
|
|
gas_used_after,
|
|
|
|
|
block_bloom_before,
|
|
|
|
|
block_bloom_after,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn select<F: RichField + Extendable<D>, const D: usize>(
|
|
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
|
|
|
|
condition: BoolTarget,
|
|
|
|
|
ed0: Self,
|
|
|
|
|
ed1: Self,
|
|
|
|
|
) -> Self {
|
|
|
|
|
Self {
|
2023-09-11 15:47:33 +01:00
|
|
|
genesis_state_root: core::array::from_fn(|i| {
|
|
|
|
|
builder.select(
|
|
|
|
|
condition,
|
|
|
|
|
ed0.genesis_state_root[i],
|
|
|
|
|
ed1.genesis_state_root[i],
|
|
|
|
|
)
|
|
|
|
|
}),
|
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),
|
|
|
|
|
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),
|
|
|
|
|
block_bloom_before: core::array::from_fn(|i| {
|
|
|
|
|
builder.select(
|
|
|
|
|
condition,
|
|
|
|
|
ed0.block_bloom_before[i],
|
|
|
|
|
ed1.block_bloom_before[i],
|
|
|
|
|
)
|
|
|
|
|
}),
|
|
|
|
|
block_bloom_after: core::array::from_fn(|i| {
|
|
|
|
|
builder.select(
|
|
|
|
|
condition,
|
|
|
|
|
ed0.block_bloom_after[i],
|
|
|
|
|
ed1.block_bloom_after[i],
|
|
|
|
|
)
|
|
|
|
|
}),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn connect<F: RichField + Extendable<D>, const D: usize>(
|
|
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
|
|
|
|
ed0: Self,
|
|
|
|
|
ed1: Self,
|
|
|
|
|
) {
|
2023-09-11 15:47:33 +01:00
|
|
|
for i in 0..8 {
|
|
|
|
|
builder.connect(ed0.genesis_state_root[i], ed1.genesis_state_root[i]);
|
|
|
|
|
}
|
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);
|
|
|
|
|
builder.connect(ed0.gas_used_before, ed1.gas_used_before);
|
|
|
|
|
builder.connect(ed1.gas_used_after, ed1.gas_used_after);
|
|
|
|
|
for i in 0..64 {
|
|
|
|
|
builder.connect(ed0.block_bloom_before[i], ed1.block_bloom_before[i]);
|
|
|
|
|
}
|
|
|
|
|
for i in 0..64 {
|
|
|
|
|
builder.connect(ed0.block_bloom_after[i], ed1.block_bloom_after[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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-05-11 02:59:02 +10:00
|
|
|
pub(crate) init_challenger_state: <C::Hasher as Hasher<F>>::Permutation,
|
2023-04-14 21:55:44 +08:00
|
|
|
// TODO: set it back to pub(crate) when cpu trace len is a public input
|
|
|
|
|
pub 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
|
|
|
|
|
|
|
|
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-02-21 21:12:03 -05:00
|
|
|
#[derive(Eq, PartialEq, Debug)]
|
2022-05-04 20:57:07 +02:00
|
|
|
pub struct StarkProofTarget<const D: usize> {
|
|
|
|
|
pub trace_cap: MerkleCapTarget,
|
2023-02-13 15:58:26 +01:00
|
|
|
pub auxiliary_polys_cap: MerkleCapTarget,
|
2022-05-04 20:57:07 +02:00
|
|
|
pub quotient_polys_cap: MerkleCapTarget,
|
|
|
|
|
pub openings: StarkOpeningSetTarget<D>,
|
|
|
|
|
pub opening_proof: FriProofTarget<D>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<const D: usize> StarkProofTarget<D> {
|
2023-02-21 21:12:03 -05:00
|
|
|
pub fn to_buffer(&self, buffer: &mut Vec<u8>) -> IoResult<()> {
|
|
|
|
|
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(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn from_buffer(buffer: &mut Buffer) -> IoResult<Self> {
|
|
|
|
|
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.
|
|
|
|
|
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> {
|
|
|
|
|
/// 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 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> {
|
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-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-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-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<_>>()
|
|
|
|
|
};
|
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),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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-02-21 21:12:03 -05:00
|
|
|
#[derive(Eq, PartialEq, Debug)]
|
2022-05-04 20:57:07 +02:00
|
|
|
pub struct StarkOpeningSetTarget<const D: usize> {
|
|
|
|
|
pub local_values: Vec<ExtensionTarget<D>>,
|
|
|
|
|
pub next_values: Vec<ExtensionTarget<D>>,
|
2023-02-13 15:58:26 +01:00
|
|
|
pub auxiliary_polys: Vec<ExtensionTarget<D>>,
|
|
|
|
|
pub auxiliary_polys_next: Vec<ExtensionTarget<D>>,
|
2023-09-11 14:11:13 -04:00
|
|
|
pub ctl_zs_first: Vec<Target>,
|
2022-05-04 20:57:07 +02:00
|
|
|
pub quotient_polys: Vec<ExtensionTarget<D>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<const D: usize> StarkOpeningSetTarget<D> {
|
2023-02-21 21:12:03 -05:00
|
|
|
pub fn to_buffer(&self, buffer: &mut Vec<u8>) -> IoResult<()> {
|
|
|
|
|
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(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn from_buffer(buffer: &mut Buffer) -> IoResult<Self> {
|
|
|
|
|
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,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|