diff --git a/src/generation/mod.rs b/src/generation/mod.rs new file mode 100644 index 00000000..5b0fb63d --- /dev/null +++ b/src/generation/mod.rs @@ -0,0 +1,299 @@ +use std::collections::HashMap; + +use anyhow::anyhow; +use ethereum_types::{Address, BigEndianHash, H256, U256}; +use plonky2::field::extension::Extendable; +use plonky2::field::polynomial::PolynomialValues; +use plonky2::hash::hash_types::RichField; +use plonky2::timed; +use plonky2::util::timing::TimingTree; +use serde::{Deserialize, Serialize}; +use GlobalMetadata::{ + ReceiptTrieRootDigestAfter, ReceiptTrieRootDigestBefore, StateTrieRootDigestAfter, + StateTrieRootDigestBefore, TransactionTrieRootDigestAfter, TransactionTrieRootDigestBefore, +}; + +use crate::all_stark::{AllStark, NUM_TABLES}; +use crate::config::StarkConfig; +use crate::cpu::bootstrap_kernel::generate_bootstrap_kernel; +use crate::cpu::columns::CpuColumnsView; +use crate::cpu::kernel::aggregator::KERNEL; +use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; +use crate::generation::outputs::{get_outputs, GenerationOutputs}; +use crate::generation::state::GenerationState; +use crate::memory::segments::Segment; +use crate::proof::{BlockHashes, BlockMetadata, ExtraBlockData, PublicValues, TrieRoots}; +use crate::util::h2u; +use crate::witness::memory::{MemoryAddress, MemoryChannel}; +use crate::witness::transition::transition; + +use crate::witness::util::mem_write_log; + +/// Inputs needed for trace generation. +#[derive(Clone, Debug, Deserialize, Serialize, Default)] +pub struct GenerationInputs { + pub txn_number_before: U256, + pub gas_used_before: U256, + pub block_bloom_before: [U256; 8], + pub gas_used_after: U256, + pub block_bloom_after: [U256; 8], + + pub signed_txns: Vec>, + /// Expected trie roots after the transactions are executed. + pub trie_roots_after: TrieRoots, + /// State trie root of the genesis block. + pub genesis_state_trie_root: H256, + + /// Mapping between smart contract code hashes and the contract byte code. + /// All account smart contracts that are invoked will have an entry present. + pub contract_code: HashMap>, + + pub block_metadata: BlockMetadata, + + pub block_hashes: BlockHashes, + + /// A list of known addresses in the input state trie (which itself doesn't hold addresses, + /// only state keys). This is only useful for debugging, so that we can return addresses in the + /// post-state rather than state keys. (See `GenerationOutputs`, and in particular + /// `AddressOrStateKey`.) If the caller is not interested in the post-state, this can be left + /// empty. + pub addresses: Vec
, +} + +fn apply_metadata_and_tries_memops, const D: usize>( + state: &mut GenerationState, + inputs: &GenerationInputs, +) { + let metadata = &inputs.block_metadata; + let tries = &inputs.tries; + let trie_roots_after = &inputs.trie_roots_after; + let fields = [ + ( + GlobalMetadata::BlockBeneficiary, + U256::from_big_endian(&metadata.block_beneficiary.0), + ), + (GlobalMetadata::BlockTimestamp, metadata.block_timestamp), + (GlobalMetadata::BlockNumber, metadata.block_number), + (GlobalMetadata::BlockDifficulty, metadata.block_difficulty), + ( + GlobalMetadata::BlockRandom, + metadata.block_random.into_uint(), + ), + (GlobalMetadata::BlockGasLimit, metadata.block_gaslimit), + (GlobalMetadata::BlockChainId, metadata.block_chain_id), + (GlobalMetadata::BlockBaseFee, metadata.block_base_fee), + ( + GlobalMetadata::BlockCurrentHash, + h2u(inputs.block_hashes.cur_hash), + ), + (GlobalMetadata::BlockGasUsed, metadata.block_gas_used), + (GlobalMetadata::BlockGasUsedBefore, inputs.gas_used_before), + (GlobalMetadata::BlockGasUsedAfter, inputs.gas_used_after), + (GlobalMetadata::TxnNumberBefore, inputs.txn_number_before), + ( + GlobalMetadata::TxnNumberAfter, + inputs.txn_number_before + inputs.signed_txns.len(), + ), + ( + GlobalMetadata::StateTrieRootDigestBefore, + h2u(tries.state_trie.hash()), + ), + ( + GlobalMetadata::TransactionTrieRootDigestBefore, + h2u(tries.transactions_trie.hash()), + ), + ( + GlobalMetadata::ReceiptTrieRootDigestBefore, + h2u(tries.receipts_trie.hash()), + ), + ( + GlobalMetadata::StateTrieRootDigestAfter, + h2u(trie_roots_after.state_root), + ), + ( + GlobalMetadata::TransactionTrieRootDigestAfter, + h2u(trie_roots_after.transactions_root), + ), + ( + GlobalMetadata::ReceiptTrieRootDigestAfter, + h2u(trie_roots_after.receipts_root), + ), + ]; + + let channel = MemoryChannel::GeneralPurpose(0); + let mut ops = fields + .map(|(field, val)| { + mem_write_log( + channel, + MemoryAddress::new(0, Segment::GlobalMetadata, field as usize), + state, + val, + ) + }) + .to_vec(); + + // Write the block's final block bloom filter. + ops.extend((0..8).map(|i| { + mem_write_log( + channel, + MemoryAddress::new(0, Segment::GlobalBlockBloom, i), + state, + metadata.block_bloom[i], + ) + })); + // Write the block's bloom filter before the current transaction. + ops.extend( + (0..8) + .map(|i| { + mem_write_log( + channel, + MemoryAddress::new(0, Segment::GlobalBlockBloom, i + 8), + state, + inputs.block_bloom_before[i], + ) + }) + .collect::>(), + ); + // Write the block's bloom filter after the current transaction. + ops.extend( + (0..8) + .map(|i| { + mem_write_log( + channel, + MemoryAddress::new(0, Segment::GlobalBlockBloom, i + 16), + state, + inputs.block_bloom_after[i], + ) + }) + .collect::>(), + ); + // Write previous block hashes. + ops.extend( + (0..256) + .map(|i| { + mem_write_log( + channel, + MemoryAddress::new(0, Segment::BlockHashes, i), + state, + h2u(inputs.block_hashes.prev_hashes[i]), + ) + }) + .collect::>(), + ); + + state.memory.apply_ops(&ops); + state.traces.memory_ops.extend(ops); +} + +pub fn generate_traces, const D: usize>( + all_stark: &AllStark, + inputs: GenerationInputs, + config: &StarkConfig, + timing: &mut TimingTree, +) -> anyhow::Result<( + [Vec>; NUM_TABLES], + PublicValues, + GenerationOutputs, +)> { + let mut state = GenerationState::::new(inputs.clone(), &KERNEL.code) + .map_err(|err| anyhow!("Failed to parse all the initial prover inputs: {:?}", err))?; + + apply_metadata_and_tries_memops(&mut state, &inputs); + + generate_bootstrap_kernel::(&mut state); + + timed!(timing, "simulate CPU", simulate_cpu(&mut state)?); + + assert!( + state.mpt_prover_inputs.is_empty(), + "All MPT data should have been consumed" + ); + + log::info!( + "Trace lengths (before padding): {:?}", + state.traces.get_lengths() + ); + + let outputs = get_outputs(&mut state) + .map_err(|err| anyhow!("Failed to generate post-state info: {:?}", err))?; + + let read_metadata = |field| state.memory.read_global_metadata(field); + let trie_roots_before = TrieRoots { + state_root: H256::from_uint(&read_metadata(StateTrieRootDigestBefore)), + transactions_root: H256::from_uint(&read_metadata(TransactionTrieRootDigestBefore)), + receipts_root: H256::from_uint(&read_metadata(ReceiptTrieRootDigestBefore)), + }; + let trie_roots_after = TrieRoots { + state_root: H256::from_uint(&read_metadata(StateTrieRootDigestAfter)), + transactions_root: H256::from_uint(&read_metadata(TransactionTrieRootDigestAfter)), + receipts_root: H256::from_uint(&read_metadata(ReceiptTrieRootDigestAfter)), + }; + + let gas_used_after = read_metadata(GlobalMetadata::BlockGasUsedAfter); + let txn_number_after = read_metadata(GlobalMetadata::TxnNumberAfter); + + let extra_block_data = ExtraBlockData { + genesis_state_trie_root: inputs.genesis_state_trie_root, + txn_number_before: inputs.txn_number_before, + txn_number_after, + gas_used_before: inputs.gas_used_before, + gas_used_after, + block_bloom_before: inputs.block_bloom_before, + block_bloom_after: inputs.block_bloom_after, + }; + + let public_values = PublicValues { + trie_roots_before, + trie_roots_after, + block_metadata: inputs.block_metadata, + block_hashes: inputs.block_hashes, + extra_block_data, + }; + + let tables = timed!( + timing, + "convert trace data to tables", + state.traces.into_tables(all_stark, config, timing) + ); + Ok((tables, public_values, outputs)) +} + +fn simulate_cpu, const D: usize>( + state: &mut GenerationState, +) -> anyhow::Result<()> { + let halt_pc = KERNEL.global_labels["halt"]; + + loop { + // If we've reached the kernel's halt routine, and our trace length is a power of 2, stop. + let pc = state.registers.program_counter; + let halt = state.registers.is_kernel && pc == halt_pc; + if halt { + log::info!("CPU halted after {} cycles", state.traces.clock()); + + // Padding + let mut row = CpuColumnsView::::default(); + row.clock = F::from_canonical_usize(state.traces.clock()); + row.context = F::from_canonical_usize(state.registers.context); + row.program_counter = F::from_canonical_usize(pc); + row.is_kernel_mode = F::ONE; + row.gas = [ + F::from_canonical_u32(state.registers.gas_used as u32), + F::from_canonical_u32((state.registers.gas_used >> 32) as u32), + ]; + row.stack_len = F::from_canonical_usize(state.registers.stack_len); + + loop { + state.traces.push_cpu(row); + row.clock += F::ONE; + if state.traces.clock().is_power_of_two() { + break; + } + } + log::info!("CPU trace padded to {} cycles", state.traces.clock()); + + return Ok(()); + } + + transition(state)?; + } +} diff --git a/src/lib.rs b/src/lib.rs index 8389fe5c..becaa51e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,19 +1,20 @@ -pub mod all_stark; -pub mod arithmetic; -pub mod config; -pub mod constraint_consumer; -pub mod cpu; -pub mod cross_table_lookup; -pub mod evaluation_frame; -pub mod keccak; -pub mod keccak_sponge; -pub mod logic; -pub mod lookup; -pub mod memory; -pub mod proof; -pub mod prover; -pub mod stark; -pub mod stark_testing; -pub mod util; -pub mod vanishing_poly; -pub mod witness; +pub(crate) mod all_stark; +pub(crate) mod arithmetic; +pub(crate) mod config; +pub(crate) mod constraint_consumer; +pub(crate) mod cpu; +pub(crate) mod cross_table_lookup; +pub(crate) mod evaluation_frame; +pub(crate) mod keccak; +pub(crate) mod keccak_sponge; +pub(crate) mod logic; +pub(crate) mod lookup; +pub(crate) mod memory; +pub(crate) mod proof; +pub(crate) mod prover; +pub(crate) mod stark; +pub(crate) mod stark_testing; +pub(crate) mod util; +pub(crate) mod vanishing_poly; +pub(crate) mod witness; +pub(crate) mod generation; diff --git a/src/prover.rs b/src/prover.rs index 2cb6b530..258fc35a 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -29,14 +29,13 @@ use crate::cross_table_lookup::{ }; use crate::evaluation_frame::StarkEvaluationFrame; //use crate::generation::outputs::GenerationOutputs; -//use crate::generation::{generate_traces, GenerationInputs}; +use crate::generation::{generate_traces, GenerationInputs}; //use crate::get_challenges::observe_public_values; use crate::lookup::{lookup_helper_columns, Lookup, LookupCheckVars}; use crate::proof::{AllProof, PublicValues, StarkOpeningSet, StarkProof, StarkProofWithMetadata}; use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; -/* /// Generate traces, then create all STARK proofs. pub fn prove( all_stark: &AllStark, @@ -73,7 +72,6 @@ where let proof = prove_with_traces(all_stark, config, traces, public_values, timing)?; Ok((proof, outputs)) } -*/ /// Compute all STARK proofs. pub(crate) fn prove_with_traces(