mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-03 14:23:07 +00:00
Add missing links between public values
This commit is contained in:
parent
760f09a8aa
commit
b07644368f
@ -2,6 +2,9 @@ global main:
|
||||
// First, initialise the shift table
|
||||
%shift_table_init
|
||||
|
||||
// Initialize the block bloom filter
|
||||
%initialize_block_bloom
|
||||
|
||||
// Second, load all MPT data from the prover.
|
||||
PUSH hash_initial_tries
|
||||
%jump(load_all_mpts)
|
||||
@ -13,9 +16,8 @@ global hash_initial_tries:
|
||||
|
||||
global start_txns:
|
||||
// stack: (empty)
|
||||
// Last mpt input is txn_nb.
|
||||
PROVER_INPUT(mpt)
|
||||
PUSH 0
|
||||
%mload_global_metadata(@GLOBAL_METADATA_TXN_NUMBER_BEFORE)
|
||||
%mload_global_metadata(@GLOBAL_METADATA_BLOCK_GAS_USED_BEFORE)
|
||||
// stack: init_used_gas, txn_nb
|
||||
|
||||
txn_loop:
|
||||
@ -37,8 +39,71 @@ global txn_loop_after:
|
||||
|
||||
global hash_final_tries:
|
||||
// stack: cum_gas, txn_nb
|
||||
%pop2
|
||||
// Check that we end up with the correct `cum_gas`, `txn_nb` and bloom filter.
|
||||
%mload_global_metadata(@GLOBAL_METADATA_BLOCK_GAS_USED_AFTER) %assert_eq
|
||||
%mload_global_metadata(@GLOBAL_METADATA_TXN_NUMBER_AFTER) %assert_eq
|
||||
%check_metadata_block_bloom
|
||||
%mpt_hash_state_trie %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_DIGEST_AFTER) %assert_eq
|
||||
%mpt_hash_txn_trie %mload_global_metadata(@GLOBAL_METADATA_TXN_TRIE_DIGEST_AFTER) %assert_eq
|
||||
%mpt_hash_receipt_trie %mload_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_AFTER) %assert_eq
|
||||
%jump(halt)
|
||||
|
||||
initialize_block_bloom:
|
||||
// stack: retdest
|
||||
PUSH 0 PUSH 8 PUSH 0
|
||||
|
||||
initialize_bloom_loop:
|
||||
// stack: i, len, offset, retdest
|
||||
DUP2 DUP2 EQ %jumpi(initialize_bloom_loop_end)
|
||||
PUSH 32 // Bloom word length
|
||||
// stack: word_len, i, len, offset, retdest
|
||||
// Load the next `block_bloom_before` word.
|
||||
DUP2 %add_const(8) %mload_kernel(@SEGMENT_GLOBAL_BLOCK_BLOOM)
|
||||
// stack: bloom_word, word_len, i, len, offset, retdest
|
||||
DUP5 PUSH @SEGMENT_BLOCK_BLOOM PUSH 0 // Bloom word address in SEGMENT_BLOCK_BLOOM
|
||||
%mstore_unpacking
|
||||
// stack: new_offset, i, len, old_offset, retdest
|
||||
SWAP3 POP %increment
|
||||
// stack: i, len, new_offset, retdest
|
||||
%jump(initialize_bloom_loop)
|
||||
|
||||
initialize_bloom_loop_end:
|
||||
// stack: len, len, offset, retdest
|
||||
%pop3
|
||||
JUMP
|
||||
|
||||
%macro initialize_block_bloom
|
||||
// stack: (empty)
|
||||
PUSH %%after
|
||||
%jump(initialize_block_bloom)
|
||||
%%after:
|
||||
%endmacro
|
||||
|
||||
check_metadata_block_bloom:
|
||||
// stack: retdest
|
||||
PUSH 0 PUSH 8 PUSH 0
|
||||
|
||||
check_bloom_loop:
|
||||
// stack: i, len, offset, retdest
|
||||
DUP2 DUP2 EQ %jumpi(check_bloom_loop_end)
|
||||
PUSH 32 // Bloom word length
|
||||
// stack: word_len, i, len, offset, retdest
|
||||
DUP4 PUSH @SEGMENT_BLOCK_BLOOM PUSH 0
|
||||
%mload_packing
|
||||
// stack: bloom_word, i, len, offset, retdest
|
||||
DUP2 %add_const(16) %mload_kernel(@SEGMENT_GLOBAL_BLOCK_BLOOM) %assert_eq
|
||||
// stack: i, len, offset, retdest
|
||||
%increment SWAP2 %add_const(32) SWAP2
|
||||
// stack: i+1, len, new_offset, retdest
|
||||
%jump(check_bloom_loop)
|
||||
|
||||
check_bloom_loop_end:
|
||||
// stack: len, len, offset, retdest
|
||||
%pop3
|
||||
JUMP
|
||||
|
||||
%macro check_metadata_block_bloom
|
||||
PUSH %%after
|
||||
%jump(check_metadata_block_bloom)
|
||||
%%after:
|
||||
%endmacro
|
||||
|
||||
@ -42,43 +42,49 @@ pub(crate) enum GlobalMetadata {
|
||||
BlockGasLimit = 18,
|
||||
BlockChainId = 19,
|
||||
BlockBaseFee = 20,
|
||||
|
||||
BlockGasUsed = 21,
|
||||
/// Before current transactions block values.
|
||||
BlockGasUsedBefore = 22,
|
||||
/// After current transactions block values.
|
||||
BlockGasUsedAfter = 23,
|
||||
/// Gas to refund at the end of the transaction.
|
||||
RefundCounter = 21,
|
||||
RefundCounter = 24,
|
||||
/// Length of the addresses access list.
|
||||
AccessedAddressesLen = 22,
|
||||
AccessedAddressesLen = 25,
|
||||
/// Length of the storage keys access list.
|
||||
AccessedStorageKeysLen = 23,
|
||||
AccessedStorageKeysLen = 26,
|
||||
/// Length of the self-destruct list.
|
||||
SelfDestructListLen = 24,
|
||||
SelfDestructListLen = 27,
|
||||
/// Length of the bloom entry buffer.
|
||||
BloomEntryLen = 25,
|
||||
BloomEntryLen = 28,
|
||||
|
||||
/// Length of the journal.
|
||||
JournalLen = 26,
|
||||
JournalLen = 29,
|
||||
/// Length of the `JournalData` segment.
|
||||
JournalDataLen = 27,
|
||||
JournalDataLen = 30,
|
||||
/// Current checkpoint.
|
||||
CurrentCheckpoint = 28,
|
||||
TouchedAddressesLen = 29,
|
||||
CurrentCheckpoint = 31,
|
||||
TouchedAddressesLen = 32,
|
||||
// Gas cost for the access list in type-1 txns. See EIP-2930.
|
||||
AccessListDataCost = 30,
|
||||
AccessListDataCost = 33,
|
||||
// Start of the access list in the RLP for type-1 txns.
|
||||
AccessListRlpStart = 31,
|
||||
AccessListRlpStart = 34,
|
||||
// Length of the access list in the RLP for type-1 txns.
|
||||
AccessListRlpLen = 32,
|
||||
AccessListRlpLen = 35,
|
||||
// Boolean flag indicating if the txn is a contract creation txn.
|
||||
ContractCreation = 33,
|
||||
IsPrecompileFromEoa = 34,
|
||||
CallStackDepth = 35,
|
||||
/// Transaction logs list length
|
||||
LogsLen = 36,
|
||||
LogsDataLen = 37,
|
||||
LogsPayloadLen = 38,
|
||||
ContractCreation = 36,
|
||||
IsPrecompileFromEoa = 37,
|
||||
CallStackDepth = 38,
|
||||
/// Transaction logs list length.
|
||||
LogsLen = 39,
|
||||
LogsDataLen = 40,
|
||||
LogsPayloadLen = 41,
|
||||
TxnNumberBefore = 42,
|
||||
TxnNumberAfter = 43,
|
||||
}
|
||||
|
||||
impl GlobalMetadata {
|
||||
pub(crate) const COUNT: usize = 39;
|
||||
pub(crate) const COUNT: usize = 44;
|
||||
|
||||
pub(crate) fn all() -> [Self; Self::COUNT] {
|
||||
[
|
||||
@ -103,6 +109,9 @@ impl GlobalMetadata {
|
||||
Self::BlockGasLimit,
|
||||
Self::BlockChainId,
|
||||
Self::BlockBaseFee,
|
||||
Self::BlockGasUsed,
|
||||
Self::BlockGasUsedBefore,
|
||||
Self::BlockGasUsedAfter,
|
||||
Self::RefundCounter,
|
||||
Self::AccessedAddressesLen,
|
||||
Self::AccessedStorageKeysLen,
|
||||
@ -121,6 +130,8 @@ impl GlobalMetadata {
|
||||
Self::LogsLen,
|
||||
Self::LogsDataLen,
|
||||
Self::LogsPayloadLen,
|
||||
Self::TxnNumberBefore,
|
||||
Self::TxnNumberAfter,
|
||||
]
|
||||
}
|
||||
|
||||
@ -148,6 +159,9 @@ impl GlobalMetadata {
|
||||
Self::BlockGasLimit => "GLOBAL_METADATA_BLOCK_GAS_LIMIT",
|
||||
Self::BlockChainId => "GLOBAL_METADATA_BLOCK_CHAIN_ID",
|
||||
Self::BlockBaseFee => "GLOBAL_METADATA_BLOCK_BASE_FEE",
|
||||
Self::BlockGasUsed => "GLOBAL_METADATA_BLOCK_GAS_USED",
|
||||
Self::BlockGasUsedBefore => "GLOBAL_METADATA_BLOCK_GAS_USED_BEFORE",
|
||||
Self::BlockGasUsedAfter => "GLOBAL_METADATA_BLOCK_GAS_USED_AFTER",
|
||||
Self::RefundCounter => "GLOBAL_METADATA_REFUND_COUNTER",
|
||||
Self::AccessedAddressesLen => "GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN",
|
||||
Self::AccessedStorageKeysLen => "GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN",
|
||||
@ -166,6 +180,8 @@ impl GlobalMetadata {
|
||||
Self::LogsLen => "GLOBAL_METADATA_LOGS_LEN",
|
||||
Self::LogsDataLen => "GLOBAL_METADATA_LOGS_DATA_LEN",
|
||||
Self::LogsPayloadLen => "GLOBAL_METADATA_LOGS_PAYLOAD_LEN",
|
||||
Self::TxnNumberBefore => "GLOBAL_METADATA_TXN_NUMBER_BEFORE",
|
||||
Self::TxnNumberAfter => "GLOBAL_METADATA_TXN_NUMBER_AFTER",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,8 @@ use core::mem::{self, MaybeUninit};
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Range;
|
||||
|
||||
use eth_trie_utils::partial_trie::{HashedPartialTrie, Node, PartialTrie};
|
||||
use ethereum_types::BigEndianHash;
|
||||
use hashbrown::HashMap;
|
||||
use itertools::{zip_eq, Itertools};
|
||||
use plonky2::field::extension::Extendable;
|
||||
@ -39,16 +41,18 @@ use crate::logic::LogicStark;
|
||||
use crate::memory::memory_stark::MemoryStark;
|
||||
use crate::permutation::{get_grand_product_challenge_set_target, GrandProductChallengeSet};
|
||||
use crate::proof::{
|
||||
BlockMetadataTarget, PublicValues, PublicValuesTarget, StarkProofWithMetadata, TrieRootsTarget,
|
||||
BlockMetadataTarget, ExtraBlockDataTarget, PublicValues, PublicValuesTarget,
|
||||
StarkProofWithMetadata, TrieRootsTarget,
|
||||
};
|
||||
use crate::prover::prove;
|
||||
use crate::recursive_verifier::{
|
||||
add_common_recursion_gates, add_virtual_public_values,
|
||||
get_memory_extra_looking_products_circuit, recursive_stark_circuit, set_block_metadata_target,
|
||||
set_public_value_targets, set_trie_roots_target, PlonkWrapperCircuit, PublicInputs,
|
||||
StarkWrapperCircuit,
|
||||
set_extra_public_values_target, set_public_value_targets, set_trie_roots_target,
|
||||
PlonkWrapperCircuit, PublicInputs, StarkWrapperCircuit,
|
||||
};
|
||||
use crate::stark::Stark;
|
||||
use crate::util::u256_limbs;
|
||||
|
||||
/// The recursion threshold. We end a chain of recursive proofs once we reach this size.
|
||||
const THRESHOLD_DEGREE_BITS: usize = 13;
|
||||
@ -598,6 +602,13 @@ where
|
||||
rhs_public_values.trie_roots_before,
|
||||
);
|
||||
|
||||
Self::connect_extra_public_values(
|
||||
&mut builder,
|
||||
&public_values.extra_block_data,
|
||||
&lhs_public_values.extra_block_data,
|
||||
&rhs_public_values.extra_block_data,
|
||||
);
|
||||
|
||||
// Pad to match the root circuit's degree.
|
||||
while log2_ceil(builder.num_gates()) < root.circuit.common.degree_bits() {
|
||||
builder.add_gate(NoopGate, vec![]);
|
||||
@ -613,6 +624,39 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn connect_extra_public_values(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
pvs: &ExtraBlockDataTarget,
|
||||
lhs: &ExtraBlockDataTarget,
|
||||
rhs: &ExtraBlockDataTarget,
|
||||
) {
|
||||
// Connect the transaction number in public values to the lhs and rhs values correctly.
|
||||
builder.connect(pvs.txn_number_before, lhs.txn_number_before);
|
||||
builder.connect(pvs.txn_number_after, rhs.txn_number_after);
|
||||
|
||||
// Connect lhs `txn_number_after`with rhs `txn_number_before`.
|
||||
builder.connect(lhs.txn_number_after, rhs.txn_number_before);
|
||||
|
||||
// Connect the gas used in public values to the lhs and rhs values correctly.
|
||||
builder.connect(pvs.gas_used_before, lhs.gas_used_before);
|
||||
builder.connect(pvs.gas_used_after, rhs.gas_used_after);
|
||||
|
||||
// Connect lhs `gas_used_after`with rhs `gas_used_before`.
|
||||
builder.connect(lhs.gas_used_after, rhs.gas_used_before);
|
||||
|
||||
// Connect the `block_bloom` in public values to the lhs and rhs values correctly.
|
||||
for (&limb0, &limb1) in pvs.block_bloom_after.iter().zip(&rhs.block_bloom_after) {
|
||||
builder.connect(limb0, limb1);
|
||||
}
|
||||
for (&limb0, &limb1) in pvs.block_bloom_before.iter().zip(&lhs.block_bloom_before) {
|
||||
builder.connect(limb0, limb1);
|
||||
}
|
||||
// Connect lhs `block_bloom_after`with rhs `block_bloom_before`.
|
||||
for (&limb0, &limb1) in lhs.block_bloom_after.iter().zip(&rhs.block_bloom_before) {
|
||||
builder.connect(limb0, limb1);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_agg_child(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
root: &RootCircuitData<F, C, D>,
|
||||
@ -651,6 +695,12 @@ where
|
||||
let parent_block_proof = builder.add_virtual_proof_with_pis(&expected_common_data);
|
||||
let agg_root_proof = builder.add_virtual_proof_with_pis(&agg.circuit.common);
|
||||
|
||||
let parent_pv = PublicValuesTarget::from_public_inputs(&parent_block_proof.public_inputs);
|
||||
let agg_pv = PublicValuesTarget::from_public_inputs(&agg_root_proof.public_inputs);
|
||||
|
||||
// Make connections between block proofs, and check initial and final block values.
|
||||
Self::connect_block_proof(&mut builder, &parent_pv, &agg_pv);
|
||||
|
||||
let cyclic_vk = builder.add_verifier_data_public_inputs();
|
||||
builder
|
||||
.conditionally_verify_cyclic_proof_or_dummy::<C>(
|
||||
@ -674,6 +724,88 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn connect_block_proof(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
lhs: &PublicValuesTarget,
|
||||
rhs: &PublicValuesTarget,
|
||||
) {
|
||||
// Between blocks, we only connect state tries.
|
||||
for (&limb0, limb1) in lhs
|
||||
.trie_roots_after
|
||||
.state_root
|
||||
.iter()
|
||||
.zip(rhs.trie_roots_before.state_root)
|
||||
{
|
||||
builder.connect(limb0, limb1);
|
||||
}
|
||||
|
||||
// Connect block numbers.
|
||||
let one = builder.one();
|
||||
let prev_block_nb = builder.sub(rhs.block_metadata.block_number, one);
|
||||
builder.connect(lhs.block_metadata.block_number, prev_block_nb);
|
||||
|
||||
// Check initial block values.
|
||||
Self::connect_initial_values_block(builder, rhs);
|
||||
|
||||
// Connect intermediary values for gas_used and bloom filters to the block's final values. We only plug on the right, so there is no need to check the left-handsidee block.
|
||||
Self::connect_final_block_values_to_intermediary(builder, rhs);
|
||||
}
|
||||
|
||||
fn connect_final_block_values_to_intermediary(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
x: &PublicValuesTarget,
|
||||
) where
|
||||
F: RichField + Extendable<D>,
|
||||
{
|
||||
builder.connect(
|
||||
x.block_metadata.block_gas_used,
|
||||
x.extra_block_data.gas_used_after,
|
||||
);
|
||||
|
||||
for (&limb0, &limb1) in x
|
||||
.block_metadata
|
||||
.block_bloom
|
||||
.iter()
|
||||
.zip(&x.extra_block_data.block_bloom_after)
|
||||
{
|
||||
builder.connect(limb0, limb1);
|
||||
}
|
||||
}
|
||||
|
||||
fn connect_initial_values_block(builder: &mut CircuitBuilder<F, D>, x: &PublicValuesTarget)
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
{
|
||||
let zero = builder.constant(F::ZERO);
|
||||
// The initial number of transactions is 0.
|
||||
builder.connect(x.extra_block_data.txn_number_before, zero);
|
||||
// The initial gas used is 0
|
||||
builder.connect(x.extra_block_data.gas_used_before, zero);
|
||||
|
||||
// The initial bloom filter is all zeroes.
|
||||
let initial_bloom = builder.constants(&[F::ZERO; 64]);
|
||||
for i in 0..x.extra_block_data.block_bloom_before.len() {
|
||||
builder.connect(x.extra_block_data.block_bloom_before[i], initial_bloom[i]);
|
||||
}
|
||||
|
||||
// The transactions and receipts tries are empty at the beginning of the block.
|
||||
let initial_trie = HashedPartialTrie::from(Node::Empty).hash();
|
||||
|
||||
for (i, limb) in initial_trie.into_uint().0.into_iter().enumerate() {
|
||||
let temp = builder.constant(F::from_canonical_u32(limb as u32));
|
||||
builder.connect(x.trie_roots_before.transactions_root[2 * i], temp);
|
||||
let temp2 = builder.constant(F::from_canonical_u32((limb >> 32) as u32));
|
||||
builder.connect(x.trie_roots_before.transactions_root[2 * i + 1], temp2);
|
||||
}
|
||||
|
||||
for (i, limb) in initial_trie.into_uint().0.into_iter().enumerate() {
|
||||
let temp = builder.constant(F::from_canonical_u32(limb as u32));
|
||||
builder.connect(x.trie_roots_before.receipts_root[2 * i], temp);
|
||||
let temp2 = builder.constant(F::from_canonical_u32((limb >> 32) as u32));
|
||||
builder.connect(x.trie_roots_before.receipts_root[2 * i + 1], temp2);
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a proof for each STARK, then combine them, eventually culminating in a root proof.
|
||||
pub fn prove_root(
|
||||
&self,
|
||||
@ -771,7 +903,11 @@ where
|
||||
&self.aggregation.public_values.trie_roots_after,
|
||||
&public_values.trie_roots_after,
|
||||
);
|
||||
|
||||
set_extra_public_values_target(
|
||||
&mut agg_inputs,
|
||||
&self.aggregation.public_values.extra_block_data,
|
||||
&public_values.extra_block_data,
|
||||
);
|
||||
let aggregation_proof = self.aggregation.circuit.prove(agg_inputs)?;
|
||||
Ok((aggregation_proof, public_values))
|
||||
}
|
||||
@ -804,12 +940,26 @@ where
|
||||
block_inputs
|
||||
.set_proof_with_pis_target(&self.block.parent_block_proof, parent_block_proof);
|
||||
} else {
|
||||
// Initialize state_root_after and the block number for correct connection between blocks.
|
||||
let state_trie_root_keys = 24..32;
|
||||
let block_number_key = TrieRootsTarget::SIZE * 2 + 6;
|
||||
let mut nonzero_pis = HashMap::new();
|
||||
for (key, &value) in state_trie_root_keys.zip_eq(&u256_limbs::<F>(
|
||||
public_values.trie_roots_before.state_root.into_uint(),
|
||||
)) {
|
||||
nonzero_pis.insert(key, value);
|
||||
}
|
||||
nonzero_pis.insert(
|
||||
block_number_key,
|
||||
F::from_canonical_usize(public_values.block_metadata.block_number.as_usize())
|
||||
- F::ONE,
|
||||
);
|
||||
block_inputs.set_proof_with_pis_target(
|
||||
&self.block.parent_block_proof,
|
||||
&cyclic_base_proof(
|
||||
&self.block.circuit.common,
|
||||
&self.block.circuit.verifier_only,
|
||||
HashMap::new(),
|
||||
nonzero_pis,
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -819,6 +969,11 @@ where
|
||||
block_inputs
|
||||
.set_verifier_data_target(&self.block.cyclic_vk, &self.block.circuit.verifier_only);
|
||||
|
||||
set_extra_public_values_target(
|
||||
&mut block_inputs,
|
||||
&self.block.public_values.extra_block_data,
|
||||
&public_values.extra_block_data,
|
||||
);
|
||||
set_block_metadata_target(
|
||||
&mut block_inputs,
|
||||
&self.block.public_values.block_metadata,
|
||||
|
||||
@ -21,7 +21,7 @@ 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::{BlockMetadata, PublicValues, TrieRoots};
|
||||
use crate::proof::{BlockMetadata, ExtraBlockData, PublicValues, TrieRoots};
|
||||
use crate::util::h2u;
|
||||
use crate::witness::memory::{MemoryAddress, MemoryChannel};
|
||||
use crate::witness::transition::transition;
|
||||
@ -38,6 +38,12 @@ 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<Vec<u8>>,
|
||||
pub tries: TrieInputs,
|
||||
/// Expected trie roots after the transactions are executed.
|
||||
@ -94,6 +100,14 @@ fn apply_metadata_and_tries_memops<F: RichField + Extendable<D>, const D: usize>
|
||||
(GlobalMetadata::BlockGasLimit, metadata.block_gaslimit),
|
||||
(GlobalMetadata::BlockChainId, metadata.block_chain_id),
|
||||
(GlobalMetadata::BlockBaseFee, metadata.block_base_fee),
|
||||
(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()),
|
||||
@ -121,14 +135,52 @@ fn apply_metadata_and_tries_memops<F: RichField + Extendable<D>, const D: usize>
|
||||
];
|
||||
|
||||
let channel = MemoryChannel::GeneralPurpose(0);
|
||||
let ops = fields.map(|(field, val)| {
|
||||
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::GlobalMetadata, field as usize),
|
||||
MemoryAddress::new(0, Segment::GlobalBlockBloom, i),
|
||||
state,
|
||||
val,
|
||||
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::<Vec<_>>(),
|
||||
);
|
||||
// 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::<Vec<_>>(),
|
||||
);
|
||||
|
||||
state.memory.apply_ops(&ops);
|
||||
state.traces.memory_ops.extend(ops);
|
||||
@ -176,10 +228,23 @@ pub fn generate_traces<F: RichField + Extendable<D>, const D: usize>(
|
||||
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 {
|
||||
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,
|
||||
extra_block_data,
|
||||
};
|
||||
|
||||
let tables = timed!(
|
||||
|
||||
@ -123,15 +123,6 @@ pub(crate) fn all_mpt_prover_inputs(trie_inputs: &TrieInputs) -> Vec<U256> {
|
||||
&parse_receipts,
|
||||
);
|
||||
|
||||
// Temporary! The actual number of transactions in the trie cannot be known if the trie
|
||||
// contains hash nodes.
|
||||
let num_transactions = trie_inputs
|
||||
.transactions_trie
|
||||
.values()
|
||||
.collect::<Vec<_>>()
|
||||
.len();
|
||||
prover_inputs.push(num_transactions.into());
|
||||
|
||||
prover_inputs
|
||||
}
|
||||
|
||||
|
||||
@ -79,6 +79,12 @@ fn observe_block_metadata<
|
||||
challenger.observe_element(F::from_canonical_u32(
|
||||
(block_metadata.block_base_fee.as_u64() >> 32) as u32,
|
||||
));
|
||||
challenger.observe_element(F::from_canonical_u32(
|
||||
block_metadata.block_gas_used.as_u32(),
|
||||
));
|
||||
for i in 0..8 {
|
||||
challenger.observe_elements(&u256_limbs(block_metadata.block_bloom[i]));
|
||||
}
|
||||
}
|
||||
|
||||
fn observe_block_metadata_target<
|
||||
@ -98,6 +104,46 @@ fn observe_block_metadata_target<
|
||||
challenger.observe_element(block_metadata.block_gaslimit);
|
||||
challenger.observe_element(block_metadata.block_chain_id);
|
||||
challenger.observe_elements(&block_metadata.block_base_fee);
|
||||
challenger.observe_element(block_metadata.block_gas_used);
|
||||
challenger.observe_elements(&block_metadata.block_bloom);
|
||||
}
|
||||
|
||||
fn observe_extra_block_data<
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
const D: usize,
|
||||
>(
|
||||
challenger: &mut Challenger<F, C::Hasher>,
|
||||
extra_data: &ExtraBlockData,
|
||||
) {
|
||||
challenger.observe_element(F::from_canonical_u32(extra_data.txn_number_before.as_u32()));
|
||||
challenger.observe_element(F::from_canonical_u32(extra_data.txn_number_after.as_u32()));
|
||||
challenger.observe_element(F::from_canonical_u32(extra_data.gas_used_before.as_u32()));
|
||||
challenger.observe_element(F::from_canonical_u32(extra_data.gas_used_after.as_u32()));
|
||||
for i in 0..8 {
|
||||
challenger.observe_elements(&u256_limbs(extra_data.block_bloom_before[i]));
|
||||
}
|
||||
for i in 0..8 {
|
||||
challenger.observe_elements(&u256_limbs(extra_data.block_bloom_after[i]));
|
||||
}
|
||||
}
|
||||
|
||||
fn observe_extra_block_data_target<
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
const D: usize,
|
||||
>(
|
||||
challenger: &mut RecursiveChallenger<F, C::Hasher, D>,
|
||||
extra_data: &ExtraBlockDataTarget,
|
||||
) where
|
||||
C::Hasher: AlgebraicHasher<F>,
|
||||
{
|
||||
challenger.observe_element(extra_data.txn_number_before);
|
||||
challenger.observe_element(extra_data.txn_number_after);
|
||||
challenger.observe_element(extra_data.gas_used_before);
|
||||
challenger.observe_element(extra_data.gas_used_after);
|
||||
challenger.observe_elements(&extra_data.block_bloom_before);
|
||||
challenger.observe_elements(&extra_data.block_bloom_after);
|
||||
}
|
||||
|
||||
pub(crate) fn observe_public_values<
|
||||
@ -111,6 +157,7 @@ pub(crate) fn observe_public_values<
|
||||
observe_trie_roots::<F, C, D>(challenger, &public_values.trie_roots_before);
|
||||
observe_trie_roots::<F, C, D>(challenger, &public_values.trie_roots_after);
|
||||
observe_block_metadata::<F, C, D>(challenger, &public_values.block_metadata);
|
||||
observe_extra_block_data::<F, C, D>(challenger, &public_values.extra_block_data);
|
||||
}
|
||||
|
||||
pub(crate) fn observe_public_values_target<
|
||||
@ -126,6 +173,7 @@ pub(crate) fn observe_public_values_target<
|
||||
observe_trie_roots_target::<F, C, D>(challenger, &public_values.trie_roots_before);
|
||||
observe_trie_roots_target::<F, C, D>(challenger, &public_values.trie_roots_after);
|
||||
observe_block_metadata_target::<F, C, D>(challenger, &public_values.block_metadata);
|
||||
observe_extra_block_data_target::<F, C, D>(challenger, &public_values.extra_block_data);
|
||||
}
|
||||
|
||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> AllProof<F, C, D> {
|
||||
|
||||
@ -51,23 +51,27 @@ pub enum Segment {
|
||||
SelfDestructList = 25,
|
||||
/// Contains the bloom filter of a transaction.
|
||||
TxnBloom = 26,
|
||||
/// Contains the bloom filter of a block.
|
||||
/// Contains the computed bloom filter of a block.
|
||||
BlockBloom = 27,
|
||||
/// Contains the final block bloom, and the block bloom filters before and after the current transaction.
|
||||
/// The first eight elements are `block_metadata.block_bloom`. The next eight are `block_bloom_before`,
|
||||
/// and the last eight are `block_bloom_after.
|
||||
GlobalBlockBloom = 28,
|
||||
/// List of log pointers pointing to the LogsData segment.
|
||||
Logs = 28,
|
||||
LogsData = 29,
|
||||
Logs = 29,
|
||||
LogsData = 30,
|
||||
/// Journal of state changes. List of pointers to `JournalData`. Length in `GlobalMetadata`.
|
||||
Journal = 30,
|
||||
JournalData = 31,
|
||||
JournalCheckpoints = 32,
|
||||
Journal = 31,
|
||||
JournalData = 32,
|
||||
JournalCheckpoints = 33,
|
||||
/// List of addresses that have been touched in the current transaction.
|
||||
TouchedAddresses = 33,
|
||||
TouchedAddresses = 34,
|
||||
/// List of checkpoints for the current context. Length in `ContextMetadata`.
|
||||
ContextCheckpoints = 34,
|
||||
ContextCheckpoints = 35,
|
||||
}
|
||||
|
||||
impl Segment {
|
||||
pub(crate) const COUNT: usize = 35;
|
||||
pub(crate) const COUNT: usize = 36;
|
||||
|
||||
pub(crate) fn all() -> [Self; Self::COUNT] {
|
||||
[
|
||||
@ -99,6 +103,7 @@ impl Segment {
|
||||
Self::SelfDestructList,
|
||||
Self::TxnBloom,
|
||||
Self::BlockBloom,
|
||||
Self::GlobalBlockBloom,
|
||||
Self::Logs,
|
||||
Self::LogsData,
|
||||
Self::Journal,
|
||||
@ -140,6 +145,7 @@ impl Segment {
|
||||
Segment::SelfDestructList => "SEGMENT_SELFDESTRUCT_LIST",
|
||||
Segment::TxnBloom => "SEGMENT_TXN_BLOOM",
|
||||
Segment::BlockBloom => "SEGMENT_BLOCK_BLOOM",
|
||||
Segment::GlobalBlockBloom => "SEGMENT_GLOBAL_BLOCK_BLOOM",
|
||||
Segment::Logs => "SEGMENT_LOGS",
|
||||
Segment::LogsData => "SEGMENT_LOGS_DATA",
|
||||
Segment::Journal => "SEGMENT_JOURNAL",
|
||||
@ -180,6 +186,7 @@ impl Segment {
|
||||
Segment::AccessedStorageKeys => 256,
|
||||
Segment::SelfDestructList => 256,
|
||||
Segment::TxnBloom => 8,
|
||||
Segment::GlobalBlockBloom => 256,
|
||||
Segment::BlockBloom => 8,
|
||||
Segment::Logs => 256,
|
||||
Segment::LogsData => 256,
|
||||
|
||||
153
evm/src/proof.rs
153
evm/src/proof.rs
@ -53,6 +53,7 @@ pub struct PublicValues {
|
||||
pub trie_roots_before: TrieRoots,
|
||||
pub trie_roots_after: TrieRoots,
|
||||
pub block_metadata: BlockMetadata,
|
||||
pub extra_block_data: ExtraBlockData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
@ -71,9 +72,20 @@ pub struct BlockMetadata {
|
||||
pub block_gaslimit: U256,
|
||||
pub block_chain_id: U256,
|
||||
pub block_base_fee: U256,
|
||||
pub block_gas_used: U256,
|
||||
pub block_bloom: [U256; 8],
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
pub struct ExtraBlockData {
|
||||
pub txn_number_before: U256,
|
||||
pub txn_number_after: U256,
|
||||
pub gas_used_before: U256,
|
||||
pub gas_used_after: U256,
|
||||
pub block_bloom_before: [U256; 8],
|
||||
pub block_bloom_after: [U256; 8],
|
||||
}
|
||||
|
||||
/// Memory values which are public.
|
||||
/// Note: All the larger integers are encoded with 32-bit limbs in little-endian order.
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
@ -81,6 +93,7 @@ pub struct PublicValuesTarget {
|
||||
pub trie_roots_before: TrieRootsTarget,
|
||||
pub trie_roots_after: TrieRootsTarget,
|
||||
pub block_metadata: BlockMetadataTarget,
|
||||
pub extra_block_data: ExtraBlockDataTarget,
|
||||
}
|
||||
|
||||
impl PublicValuesTarget {
|
||||
@ -113,6 +126,7 @@ impl PublicValuesTarget {
|
||||
block_gaslimit,
|
||||
block_chain_id,
|
||||
block_base_fee,
|
||||
block_gas_used,
|
||||
block_bloom,
|
||||
} = self.block_metadata;
|
||||
|
||||
@ -123,8 +137,24 @@ impl PublicValuesTarget {
|
||||
buffer.write_target(block_gaslimit)?;
|
||||
buffer.write_target(block_chain_id)?;
|
||||
buffer.write_target_array(&block_base_fee)?;
|
||||
buffer.write_target(block_gas_used)?;
|
||||
buffer.write_target_array(&block_bloom)?;
|
||||
|
||||
let ExtraBlockDataTarget {
|
||||
txn_number_before,
|
||||
txn_number_after,
|
||||
gas_used_before,
|
||||
gas_used_after,
|
||||
block_bloom_before,
|
||||
block_bloom_after,
|
||||
} = self.extra_block_data;
|
||||
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)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -149,18 +179,36 @@ impl PublicValuesTarget {
|
||||
block_gaslimit: buffer.read_target()?,
|
||||
block_chain_id: buffer.read_target()?,
|
||||
block_base_fee: buffer.read_target_array()?,
|
||||
block_gas_used: buffer.read_target()?,
|
||||
block_bloom: buffer.read_target_array()?,
|
||||
};
|
||||
|
||||
let extra_block_data = ExtraBlockDataTarget {
|
||||
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()?,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
trie_roots_before,
|
||||
trie_roots_after,
|
||||
block_metadata,
|
||||
extra_block_data,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_public_inputs(pis: &[Target]) -> Self {
|
||||
assert!(pis.len() > TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE - 1);
|
||||
assert!(
|
||||
pis.len()
|
||||
> TrieRootsTarget::SIZE * 2
|
||||
+ BlockMetadataTarget::SIZE
|
||||
+ ExtraBlockDataTarget::SIZE
|
||||
- 1
|
||||
);
|
||||
|
||||
Self {
|
||||
trie_roots_before: TrieRootsTarget::from_public_inputs(&pis[0..TrieRootsTarget::SIZE]),
|
||||
trie_roots_after: TrieRootsTarget::from_public_inputs(
|
||||
@ -170,6 +218,12 @@ impl PublicValuesTarget {
|
||||
&pis[TrieRootsTarget::SIZE * 2
|
||||
..TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE],
|
||||
),
|
||||
extra_block_data: ExtraBlockDataTarget::from_public_inputs(
|
||||
&pis[TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE
|
||||
..TrieRootsTarget::SIZE * 2
|
||||
+ BlockMetadataTarget::SIZE
|
||||
+ ExtraBlockDataTarget::SIZE],
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,6 +252,12 @@ impl PublicValuesTarget {
|
||||
pv0.block_metadata,
|
||||
pv1.block_metadata,
|
||||
),
|
||||
extra_block_data: ExtraBlockDataTarget::select(
|
||||
builder,
|
||||
condition,
|
||||
pv0.extra_block_data,
|
||||
pv1.extra_block_data,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -210,7 +270,7 @@ pub struct TrieRootsTarget {
|
||||
}
|
||||
|
||||
impl TrieRootsTarget {
|
||||
const SIZE: usize = 24;
|
||||
pub const SIZE: usize = 24;
|
||||
|
||||
pub fn from_public_inputs(pis: &[Target]) -> Self {
|
||||
let state_root = pis[0..8].try_into().unwrap();
|
||||
@ -269,11 +329,12 @@ pub struct BlockMetadataTarget {
|
||||
pub block_gaslimit: Target,
|
||||
pub block_chain_id: Target,
|
||||
pub block_base_fee: [Target; 2],
|
||||
pub block_gas_used: Target,
|
||||
pub block_bloom: [Target; 64],
|
||||
}
|
||||
|
||||
impl BlockMetadataTarget {
|
||||
const SIZE: usize = 76;
|
||||
const SIZE: usize = 77;
|
||||
|
||||
pub fn from_public_inputs(pis: &[Target]) -> Self {
|
||||
let block_beneficiary = pis[0..5].try_into().unwrap();
|
||||
@ -283,7 +344,8 @@ impl BlockMetadataTarget {
|
||||
let block_gaslimit = pis[8];
|
||||
let block_chain_id = pis[9];
|
||||
let block_base_fee = pis[10..12].try_into().unwrap();
|
||||
let block_bloom = pis[12..76].try_into().unwrap();
|
||||
let block_gas_used = pis[12];
|
||||
let block_bloom = pis[13..77].try_into().unwrap();
|
||||
|
||||
Self {
|
||||
block_beneficiary,
|
||||
@ -293,6 +355,7 @@ impl BlockMetadataTarget {
|
||||
block_gaslimit,
|
||||
block_chain_id,
|
||||
block_base_fee,
|
||||
block_gas_used,
|
||||
block_bloom,
|
||||
}
|
||||
}
|
||||
@ -319,6 +382,7 @@ impl BlockMetadataTarget {
|
||||
block_base_fee: core::array::from_fn(|i| {
|
||||
builder.select(condition, bm0.block_base_fee[i], bm1.block_base_fee[i])
|
||||
}),
|
||||
block_gas_used: builder.select(condition, bm0.block_gas_used, bm1.block_gas_used),
|
||||
block_bloom: core::array::from_fn(|i| {
|
||||
builder.select(condition, bm0.block_bloom[i], bm1.block_bloom[i])
|
||||
}),
|
||||
@ -347,6 +411,87 @@ impl BlockMetadataTarget {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
|
||||
pub struct ExtraBlockDataTarget {
|
||||
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 {
|
||||
const SIZE: usize = 132;
|
||||
|
||||
pub fn from_public_inputs(pis: &[Target]) -> Self {
|
||||
let txn_number_before = pis[0];
|
||||
let txn_number_after = pis[1];
|
||||
let gas_used_before = pis[2];
|
||||
let gas_used_after = pis[3];
|
||||
let block_bloom_before = pis[4..68].try_into().unwrap();
|
||||
let block_bloom_after = pis[68..132].try_into().unwrap();
|
||||
|
||||
Self {
|
||||
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 {
|
||||
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,
|
||||
) {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StarkProof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> {
|
||||
/// Merkle cap of LDEs of trace values.
|
||||
|
||||
@ -37,9 +37,9 @@ use crate::permutation::{
|
||||
PermutationCheckDataTarget,
|
||||
};
|
||||
use crate::proof::{
|
||||
BlockMetadata, BlockMetadataTarget, PublicValues, PublicValuesTarget, StarkOpeningSetTarget,
|
||||
StarkProof, StarkProofChallengesTarget, StarkProofTarget, StarkProofWithMetadata, TrieRoots,
|
||||
TrieRootsTarget,
|
||||
BlockMetadata, BlockMetadataTarget, ExtraBlockData, ExtraBlockDataTarget, PublicValues,
|
||||
PublicValuesTarget, StarkOpeningSetTarget, StarkProof, StarkProofChallengesTarget,
|
||||
StarkProofTarget, StarkProofWithMetadata, TrieRoots, TrieRootsTarget,
|
||||
};
|
||||
use crate::stark::Stark;
|
||||
use crate::util::u256_limbs;
|
||||
@ -509,7 +509,7 @@ pub(crate) fn get_memory_extra_looking_products_circuit<
|
||||
let mut product = builder.one();
|
||||
|
||||
// Add metadata writes.
|
||||
let block_fields_without_beneficiary_and_basefee = [
|
||||
let block_fields_without_beneficiary_and_basefee_and_bloom = [
|
||||
(
|
||||
GlobalMetadata::BlockTimestamp as usize,
|
||||
public_values.block_metadata.block_timestamp,
|
||||
@ -530,28 +530,97 @@ pub(crate) fn get_memory_extra_looking_products_circuit<
|
||||
GlobalMetadata::BlockChainId as usize,
|
||||
public_values.block_metadata.block_chain_id,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::BlockGasUsed as usize,
|
||||
public_values.block_metadata.block_gas_used,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::BlockGasUsedBefore as usize,
|
||||
public_values.extra_block_data.gas_used_before,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::BlockGasUsedAfter as usize,
|
||||
public_values.extra_block_data.gas_used_after,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::TxnNumberBefore as usize,
|
||||
public_values.extra_block_data.txn_number_before,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::TxnNumberAfter as usize,
|
||||
public_values.extra_block_data.txn_number_after,
|
||||
),
|
||||
];
|
||||
|
||||
product = add_metadata_write(
|
||||
builder,
|
||||
challenge,
|
||||
product,
|
||||
GlobalMetadata::BlockBeneficiary as usize,
|
||||
&public_values.block_metadata.block_beneficiary,
|
||||
);
|
||||
let beneficiary_base_fee_fields: [(usize, &[Target]); 2] = [
|
||||
(
|
||||
GlobalMetadata::BlockBeneficiary as usize,
|
||||
&public_values.block_metadata.block_beneficiary,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::BlockBaseFee as usize,
|
||||
&public_values.block_metadata.block_base_fee,
|
||||
),
|
||||
];
|
||||
|
||||
block_fields_without_beneficiary_and_basefee.map(|(field, target)| {
|
||||
let metadata_segment = builder.constant(F::from_canonical_u32(Segment::GlobalMetadata as u32));
|
||||
block_fields_without_beneficiary_and_basefee_and_bloom.map(|(field, target)| {
|
||||
// Each of those fields fit in 32 bits, hence in a single Target.
|
||||
product = add_metadata_write(builder, challenge, product, field, &[target]);
|
||||
product = add_data_write(
|
||||
builder,
|
||||
challenge,
|
||||
product,
|
||||
metadata_segment,
|
||||
field,
|
||||
&[target],
|
||||
);
|
||||
});
|
||||
|
||||
product = add_metadata_write(
|
||||
builder,
|
||||
challenge,
|
||||
product,
|
||||
GlobalMetadata::BlockBaseFee as usize,
|
||||
&public_values.block_metadata.block_base_fee,
|
||||
);
|
||||
beneficiary_base_fee_fields.map(|(field, targets)| {
|
||||
product = add_data_write(
|
||||
builder,
|
||||
challenge,
|
||||
product,
|
||||
metadata_segment,
|
||||
field,
|
||||
targets,
|
||||
);
|
||||
});
|
||||
|
||||
// Add block bloom filters writes.
|
||||
let bloom_segment = builder.constant(F::from_canonical_u32(Segment::GlobalBlockBloom as u32));
|
||||
for i in 0..8 {
|
||||
product = add_data_write(
|
||||
builder,
|
||||
challenge,
|
||||
product,
|
||||
bloom_segment,
|
||||
i,
|
||||
&public_values.block_metadata.block_bloom[i * 8..(i + 1) * 8],
|
||||
);
|
||||
}
|
||||
|
||||
for i in 0..8 {
|
||||
product = add_data_write(
|
||||
builder,
|
||||
challenge,
|
||||
product,
|
||||
bloom_segment,
|
||||
i + 8,
|
||||
&public_values.extra_block_data.block_bloom_before[i * 8..(i + 1) * 8],
|
||||
);
|
||||
}
|
||||
|
||||
for i in 0..8 {
|
||||
product = add_data_write(
|
||||
builder,
|
||||
challenge,
|
||||
product,
|
||||
bloom_segment,
|
||||
i + 16,
|
||||
&public_values.extra_block_data.block_bloom_after[i * 8..(i + 1) * 8],
|
||||
);
|
||||
}
|
||||
|
||||
// Add trie roots writes.
|
||||
let trie_fields = [
|
||||
@ -582,25 +651,32 @@ pub(crate) fn get_memory_extra_looking_products_circuit<
|
||||
];
|
||||
|
||||
trie_fields.map(|(field, targets)| {
|
||||
product = add_metadata_write(builder, challenge, product, field, &targets);
|
||||
product = add_data_write(
|
||||
builder,
|
||||
challenge,
|
||||
product,
|
||||
metadata_segment,
|
||||
field,
|
||||
&targets,
|
||||
);
|
||||
});
|
||||
|
||||
product
|
||||
}
|
||||
|
||||
fn add_metadata_write<F: RichField + Extendable<D>, const D: usize>(
|
||||
fn add_data_write<F: RichField + Extendable<D>, const D: usize>(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
challenge: GrandProductChallenge<Target>,
|
||||
running_product: Target,
|
||||
metadata_idx: usize,
|
||||
metadata: &[Target],
|
||||
segment: Target,
|
||||
idx: usize,
|
||||
val: &[Target],
|
||||
) -> Target {
|
||||
debug_assert!(metadata.len() <= VALUE_LIMBS);
|
||||
let len = core::cmp::min(metadata.len(), VALUE_LIMBS);
|
||||
debug_assert!(val.len() <= VALUE_LIMBS);
|
||||
let len = core::cmp::min(val.len(), VALUE_LIMBS);
|
||||
|
||||
let zero = builder.zero();
|
||||
let one = builder.one();
|
||||
let segment = builder.constant(F::from_canonical_u32(Segment::GlobalMetadata as u32));
|
||||
|
||||
let row = builder.add_virtual_targets(13);
|
||||
// is_read
|
||||
@ -610,12 +686,12 @@ fn add_metadata_write<F: RichField + Extendable<D>, const D: usize>(
|
||||
// segment
|
||||
builder.connect(row[2], segment);
|
||||
// virtual
|
||||
let field_target = builder.constant(F::from_canonical_usize(metadata_idx));
|
||||
let field_target = builder.constant(F::from_canonical_usize(idx));
|
||||
builder.connect(row[3], field_target);
|
||||
|
||||
// values
|
||||
for j in 0..len {
|
||||
builder.connect(row[4 + j], metadata[j]);
|
||||
builder.connect(row[4 + j], val[j]);
|
||||
}
|
||||
for j in len..VALUE_LIMBS {
|
||||
builder.connect(row[4 + j], zero);
|
||||
@ -653,10 +729,12 @@ pub(crate) fn add_virtual_public_values<F: RichField + Extendable<D>, const D: u
|
||||
let trie_roots_before = add_virtual_trie_roots(builder);
|
||||
let trie_roots_after = add_virtual_trie_roots(builder);
|
||||
let block_metadata = add_virtual_block_metadata(builder);
|
||||
let extra_block_data = add_virtual_extra_block_data(builder);
|
||||
PublicValuesTarget {
|
||||
trie_roots_before,
|
||||
trie_roots_after,
|
||||
block_metadata,
|
||||
extra_block_data,
|
||||
}
|
||||
}
|
||||
|
||||
@ -683,6 +761,7 @@ pub(crate) fn add_virtual_block_metadata<F: RichField + Extendable<D>, const D:
|
||||
let block_gaslimit = builder.add_virtual_public_input();
|
||||
let block_chain_id = builder.add_virtual_public_input();
|
||||
let block_base_fee = builder.add_virtual_public_input_arr();
|
||||
let block_gas_used = builder.add_virtual_public_input();
|
||||
let block_bloom = builder.add_virtual_public_input_arr();
|
||||
BlockMetadataTarget {
|
||||
block_beneficiary,
|
||||
@ -692,9 +771,28 @@ pub(crate) fn add_virtual_block_metadata<F: RichField + Extendable<D>, const D:
|
||||
block_gaslimit,
|
||||
block_chain_id,
|
||||
block_base_fee,
|
||||
block_gas_used,
|
||||
block_bloom,
|
||||
}
|
||||
}
|
||||
pub(crate) fn add_virtual_extra_block_data<F: RichField + Extendable<D>, const D: usize>(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
) -> ExtraBlockDataTarget {
|
||||
let txn_number_before = builder.add_virtual_public_input();
|
||||
let txn_number_after = builder.add_virtual_public_input();
|
||||
let gas_used_before = builder.add_virtual_public_input();
|
||||
let gas_used_after = builder.add_virtual_public_input();
|
||||
let block_bloom_before: [Target; 64] = builder.add_virtual_public_input_arr();
|
||||
let block_bloom_after: [Target; 64] = builder.add_virtual_public_input_arr();
|
||||
ExtraBlockDataTarget {
|
||||
txn_number_before,
|
||||
txn_number_after,
|
||||
gas_used_before,
|
||||
gas_used_after,
|
||||
block_bloom_before,
|
||||
block_bloom_after,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_virtual_stark_proof<
|
||||
F: RichField + Extendable<D>,
|
||||
@ -796,6 +894,11 @@ pub(crate) fn set_public_value_targets<F, W, const D: usize>(
|
||||
&public_values_target.block_metadata,
|
||||
&public_values.block_metadata,
|
||||
);
|
||||
set_extra_public_values_target(
|
||||
witness,
|
||||
&public_values_target.extra_block_data,
|
||||
&public_values.extra_block_data,
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn set_trie_roots_target<F, W, const D: usize>(
|
||||
@ -894,9 +997,55 @@ pub(crate) fn set_block_metadata_target<F, W, const D: usize>(
|
||||
block_metadata_target.block_base_fee[1],
|
||||
F::from_canonical_u32((block_metadata.block_base_fee.as_u64() >> 32) as u32),
|
||||
);
|
||||
witness.set_target(
|
||||
block_metadata_target.block_gas_used,
|
||||
F::from_canonical_u64(block_metadata.block_gas_used.as_u64()),
|
||||
);
|
||||
let mut block_bloom_limbs = [F::ZERO; 64];
|
||||
for (i, limbs) in block_bloom_limbs.chunks_exact_mut(8).enumerate() {
|
||||
limbs.copy_from_slice(&u256_limbs(block_metadata.block_bloom[i]));
|
||||
}
|
||||
witness.set_target_arr(&block_metadata_target.block_bloom, &block_bloom_limbs);
|
||||
}
|
||||
|
||||
pub(crate) fn set_extra_public_values_target<F, W, const D: usize>(
|
||||
witness: &mut W,
|
||||
ed_target: &ExtraBlockDataTarget,
|
||||
ed: &ExtraBlockData,
|
||||
) where
|
||||
F: RichField + Extendable<D>,
|
||||
W: Witness<F>,
|
||||
{
|
||||
witness.set_target(
|
||||
ed_target.txn_number_before,
|
||||
F::from_canonical_usize(ed.txn_number_before.as_usize()),
|
||||
);
|
||||
witness.set_target(
|
||||
ed_target.txn_number_after,
|
||||
F::from_canonical_usize(ed.txn_number_after.as_usize()),
|
||||
);
|
||||
witness.set_target(
|
||||
ed_target.gas_used_before,
|
||||
F::from_canonical_usize(ed.gas_used_before.as_usize()),
|
||||
);
|
||||
witness.set_target(
|
||||
ed_target.gas_used_after,
|
||||
F::from_canonical_usize(ed.gas_used_after.as_usize()),
|
||||
);
|
||||
|
||||
let block_bloom_before = ed.block_bloom_before;
|
||||
let mut block_bloom_limbs = [F::ZERO; 64];
|
||||
for (i, limbs) in block_bloom_limbs.chunks_exact_mut(8).enumerate() {
|
||||
limbs.copy_from_slice(&u256_limbs(block_bloom_before[i]));
|
||||
}
|
||||
|
||||
witness.set_target_arr(&ed_target.block_bloom_before, &block_bloom_limbs);
|
||||
|
||||
let block_bloom_after = ed.block_bloom_after;
|
||||
let mut block_bloom_limbs = [F::ZERO; 64];
|
||||
for (i, limbs) in block_bloom_limbs.chunks_exact_mut(8).enumerate() {
|
||||
limbs.copy_from_slice(&u256_limbs(block_bloom_after[i]));
|
||||
}
|
||||
|
||||
witness.set_target_arr(&ed_target.block_bloom_after, &block_bloom_limbs);
|
||||
}
|
||||
|
||||
@ -176,6 +176,26 @@ where
|
||||
GlobalMetadata::BlockBaseFee,
|
||||
public_values.block_metadata.block_base_fee,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::BlockGasUsed,
|
||||
public_values.block_metadata.block_gas_used,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::TxnNumberBefore,
|
||||
public_values.extra_block_data.txn_number_before,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::TxnNumberAfter,
|
||||
public_values.extra_block_data.txn_number_after,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::BlockGasUsedBefore,
|
||||
public_values.extra_block_data.gas_used_before,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::BlockGasUsedAfter,
|
||||
public_values.extra_block_data.gas_used_after,
|
||||
),
|
||||
(
|
||||
GlobalMetadata::StateTrieRootDigestBefore,
|
||||
h2u(public_values.trie_roots_before.state_root),
|
||||
@ -201,28 +221,52 @@ where
|
||||
h2u(public_values.trie_roots_after.receipts_root),
|
||||
),
|
||||
];
|
||||
let is_read = F::ZERO;
|
||||
let context = F::ZERO;
|
||||
|
||||
let segment = F::from_canonical_u32(Segment::GlobalMetadata as u32);
|
||||
let timestamp = F::ONE;
|
||||
|
||||
let mut row = vec![F::ZERO; 13];
|
||||
fields.map(|(field, val)| {
|
||||
row[0] = is_read;
|
||||
row[1] = context;
|
||||
row[2] = segment;
|
||||
row[3] = F::from_canonical_usize(field as usize);
|
||||
fields.map(|(field, val)| prod = add_data_write(challenge, segment, prod, field as usize, val));
|
||||
|
||||
for j in 0..VALUE_LIMBS {
|
||||
row[j + 4] = F::from_canonical_u32((val >> (j * 32)).low_u32());
|
||||
}
|
||||
row[12] = timestamp;
|
||||
prod *= challenge.combine(row.iter());
|
||||
});
|
||||
// Add block bloom writes.
|
||||
let bloom_segment = F::from_canonical_u32(Segment::GlobalBlockBloom as u32);
|
||||
for index in 0..8 {
|
||||
let val = public_values.block_metadata.block_bloom[index];
|
||||
prod = add_data_write(challenge, bloom_segment, prod, index, val);
|
||||
}
|
||||
|
||||
for index in 0..8 {
|
||||
let val = public_values.extra_block_data.block_bloom_before[index];
|
||||
prod = add_data_write(challenge, bloom_segment, prod, index + 8, val);
|
||||
}
|
||||
for index in 0..8 {
|
||||
let val = public_values.extra_block_data.block_bloom_after[index];
|
||||
prod = add_data_write(challenge, bloom_segment, prod, index + 16, val);
|
||||
}
|
||||
|
||||
prod
|
||||
}
|
||||
|
||||
fn add_data_write<F, const D: usize>(
|
||||
challenge: GrandProductChallenge<F>,
|
||||
segment: F,
|
||||
running_product: F,
|
||||
index: usize,
|
||||
val: U256,
|
||||
) -> F
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
{
|
||||
let mut row = vec![F::ZERO; 13];
|
||||
row[0] = F::ZERO; // is_read
|
||||
row[1] = F::ZERO; // context
|
||||
row[2] = segment;
|
||||
row[3] = F::from_canonical_usize(index);
|
||||
|
||||
for j in 0..VALUE_LIMBS {
|
||||
row[j + 4] = F::from_canonical_u32((val >> (j * 32)).low_u32());
|
||||
}
|
||||
row[12] = F::ONE; // timestamp
|
||||
running_product * challenge.combine(row.iter())
|
||||
}
|
||||
pub(crate) fn verify_stark_proof_with_challenges<
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
|
||||
@ -86,6 +86,7 @@ fn add11_yml() -> anyhow::Result<()> {
|
||||
block_gaslimit: 0xff112233u32.into(),
|
||||
block_chain_id: 1.into(),
|
||||
block_base_fee: 0xa.into(),
|
||||
block_gas_used: 0xa868u64.into(),
|
||||
block_bloom: [0.into(); 8],
|
||||
};
|
||||
|
||||
@ -148,6 +149,11 @@ fn add11_yml() -> anyhow::Result<()> {
|
||||
trie_roots_after,
|
||||
contract_code,
|
||||
block_metadata,
|
||||
txn_number_before: 0.into(),
|
||||
gas_used_before: 0.into(),
|
||||
gas_used_after: 0xa868u64.into(),
|
||||
block_bloom_before: [0.into(); 8],
|
||||
block_bloom_after: [0.into(); 8],
|
||||
addresses: vec![],
|
||||
};
|
||||
|
||||
|
||||
@ -166,6 +166,11 @@ fn test_basic_smart_contract() -> anyhow::Result<()> {
|
||||
trie_roots_after,
|
||||
contract_code,
|
||||
block_metadata,
|
||||
txn_number_before: 0.into(),
|
||||
gas_used_before: 0.into(),
|
||||
gas_used_after: gas_used.into(),
|
||||
block_bloom_before: [0.into(); 8],
|
||||
block_bloom_after: [0.into(); 8],
|
||||
addresses: vec![],
|
||||
};
|
||||
|
||||
|
||||
@ -57,6 +57,11 @@ fn test_empty_txn_list() -> anyhow::Result<()> {
|
||||
trie_roots_after,
|
||||
contract_code,
|
||||
block_metadata,
|
||||
txn_number_before: 0.into(),
|
||||
gas_used_before: 0.into(),
|
||||
gas_used_after: 0.into(),
|
||||
block_bloom_before: [0.into(); 8],
|
||||
block_bloom_after: [0.into(); 8],
|
||||
addresses: vec![],
|
||||
};
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ use bytes::Bytes;
|
||||
use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV};
|
||||
use eth_trie_utils::nibbles::Nibbles;
|
||||
use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie};
|
||||
use ethereum_types::Address;
|
||||
use ethereum_types::{Address, U256};
|
||||
use hex_literal::hex;
|
||||
use keccak_hash::keccak;
|
||||
use plonky2::field::goldilocks_field::GoldilocksField;
|
||||
@ -16,9 +16,10 @@ use plonky2::plonk::config::PoseidonGoldilocksConfig;
|
||||
use plonky2::util::timing::TimingTree;
|
||||
use plonky2_evm::all_stark::AllStark;
|
||||
use plonky2_evm::config::StarkConfig;
|
||||
use plonky2_evm::fixed_recursive_verifier::AllRecursiveCircuits;
|
||||
use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp, LegacyTransactionRlp, LogRlp};
|
||||
use plonky2_evm::generation::{GenerationInputs, TrieInputs};
|
||||
use plonky2_evm::proof::{BlockMetadata, TrieRoots};
|
||||
use plonky2_evm::proof::{BlockMetadata, ExtraBlockData, PublicValues, TrieRoots};
|
||||
use plonky2_evm::prover::prove;
|
||||
use plonky2_evm::verifier::verify_proof;
|
||||
use plonky2_evm::Node;
|
||||
@ -137,6 +138,7 @@ fn test_log_opcodes() -> anyhow::Result<()> {
|
||||
block_gaslimit: 0xffffffffu32.into(),
|
||||
block_chain_id: 1.into(),
|
||||
block_base_fee: 0xa.into(),
|
||||
block_gas_used: 0.into(),
|
||||
block_bloom: [0.into(); 8],
|
||||
};
|
||||
|
||||
@ -204,12 +206,32 @@ fn test_log_opcodes() -> anyhow::Result<()> {
|
||||
transactions_root: HashedPartialTrie::from(Node::Empty).hash(),
|
||||
receipts_root: receipts_trie.hash(),
|
||||
};
|
||||
let block_bloom_after = [
|
||||
U256::from_dec_str("392318858461667547739736838950479151006397215279002157056").unwrap(),
|
||||
0.into(),
|
||||
U256::from_dec_str(
|
||||
"55213970774324510299478046898216203619608871777363092441300193790394368",
|
||||
)
|
||||
.unwrap(),
|
||||
U256::from_dec_str("1361129467683753853853498429727072845824").unwrap(),
|
||||
U256::from_dec_str("33554432").unwrap(),
|
||||
U256::from_dec_str("98079714615416886934934209737619787760822675856605315072").unwrap(),
|
||||
U256::from_dec_str("262144").unwrap(),
|
||||
U256::from_dec_str("6739986666787659948666753771754908317446393422488596686587943714816")
|
||||
.unwrap(),
|
||||
];
|
||||
let inputs = GenerationInputs {
|
||||
signed_txns: vec![txn.to_vec()],
|
||||
tries: tries_before,
|
||||
trie_roots_after,
|
||||
contract_code,
|
||||
block_metadata,
|
||||
txn_number_before: 0.into(),
|
||||
gas_used_before: 0.into(),
|
||||
gas_used_after: gas_used.into(),
|
||||
block_bloom_before: [0.into(); 8],
|
||||
block_bloom_after,
|
||||
|
||||
addresses: vec![],
|
||||
};
|
||||
|
||||
@ -231,6 +253,370 @@ fn test_log_opcodes() -> anyhow::Result<()> {
|
||||
verify_proof(&all_stark, proof, &config)
|
||||
}
|
||||
|
||||
// Tests proving two transactions, one of which with logs, and aggregating them.
|
||||
#[test]
|
||||
#[ignore] // Too slow to run on CI.
|
||||
fn test_log_with_aggreg() -> anyhow::Result<()> {
|
||||
init_logger();
|
||||
|
||||
let code = [
|
||||
0x64, 0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0x60, 0x0, 0x52, // MSTORE(0x0, 0xA1B2C3D4E5)
|
||||
0x60, 0x0, 0x60, 0x0, 0xA0, // LOG0(0x0, 0x0)
|
||||
0x60, 99, 0x60, 98, 0x60, 5, 0x60, 27, 0xA2, // LOG2(27, 5, 98, 99)
|
||||
0x00,
|
||||
];
|
||||
|
||||
let code_gas = 3 + 3 + 3 // PUSHs and MSTORE
|
||||
+ 3 + 3 + 375 // PUSHs and LOG0
|
||||
+ 3 + 3 + 3 + 3 + 375 + 375*2 + 8*5 // PUSHs and LOG2
|
||||
+ 3 // Memory expansion
|
||||
;
|
||||
|
||||
let gas_used = 21_000 + code_gas;
|
||||
|
||||
let code_hash = keccak(code);
|
||||
|
||||
// First transaction.
|
||||
let all_stark_first = AllStark::<F, D>::default();
|
||||
let config = StarkConfig::standard_fast_config();
|
||||
|
||||
let beneficiary = hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba");
|
||||
let sender_first = hex!("af1276cbb260bb13deddb4209ae99ae6e497f446");
|
||||
let to_first = hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87");
|
||||
let to = hex!("095e7baea6a6c7c4c2dfeb977efac326af552e89");
|
||||
|
||||
let beneficiary_state_key = keccak(beneficiary);
|
||||
let sender_state_key = keccak(sender_first);
|
||||
let to_hashed = keccak(to_first);
|
||||
let to_hashed_2 = keccak(to);
|
||||
|
||||
let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap();
|
||||
let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap();
|
||||
let to_nibbles = Nibbles::from_bytes_be(to_hashed.as_bytes()).unwrap();
|
||||
let to_second_nibbles = Nibbles::from_bytes_be(to_hashed_2.as_bytes()).unwrap();
|
||||
|
||||
let beneficiary_account_before = AccountRlp {
|
||||
nonce: 1.into(),
|
||||
..AccountRlp::default()
|
||||
};
|
||||
let sender_balance_before = 1000000000000000000u64.into();
|
||||
let sender_account_before = AccountRlp {
|
||||
balance: sender_balance_before,
|
||||
..AccountRlp::default()
|
||||
};
|
||||
let to_account_before = AccountRlp {
|
||||
..AccountRlp::default()
|
||||
};
|
||||
let to_account_second_before = AccountRlp {
|
||||
code_hash,
|
||||
..AccountRlp::default()
|
||||
};
|
||||
|
||||
// In the first transaction, the sender account sends `txn_value` to `to_account`.
|
||||
let gas_price = 10;
|
||||
let txn_value = 0xau64;
|
||||
let mut state_trie_before = HashedPartialTrie::from(Node::Empty);
|
||||
state_trie_before.insert(
|
||||
beneficiary_nibbles,
|
||||
rlp::encode(&beneficiary_account_before).to_vec(),
|
||||
);
|
||||
state_trie_before.insert(sender_nibbles, rlp::encode(&sender_account_before).to_vec());
|
||||
state_trie_before.insert(to_nibbles, rlp::encode(&to_account_before).to_vec());
|
||||
state_trie_before.insert(
|
||||
to_second_nibbles,
|
||||
rlp::encode(&to_account_second_before).to_vec(),
|
||||
);
|
||||
|
||||
let tries_before = TrieInputs {
|
||||
state_trie: state_trie_before,
|
||||
transactions_trie: Node::Empty.into(),
|
||||
receipts_trie: Node::Empty.into(),
|
||||
storage_tries: vec![],
|
||||
};
|
||||
|
||||
let txn = hex!("f85f800a82520894095e7baea6a6c7c4c2dfeb977efac326af552d870a8026a0122f370ed4023a6c253350c6bfb87d7d7eb2cd86447befee99e0a26b70baec20a07100ab1b3977f2b4571202b9f4b68850858caf5469222794600b5ce1cfb348ad");
|
||||
|
||||
let block_metadata = BlockMetadata {
|
||||
block_beneficiary: Address::from(beneficiary),
|
||||
block_timestamp: 0x03e8.into(),
|
||||
block_number: 1.into(),
|
||||
block_difficulty: 0x020000.into(),
|
||||
block_gaslimit: 0x445566u32.into(),
|
||||
block_chain_id: 1.into(),
|
||||
block_base_fee: 0xa.into(),
|
||||
block_gas_used: (22570 + 21000).into(),
|
||||
block_bloom: [
|
||||
0.into(),
|
||||
0.into(),
|
||||
U256::from_dec_str(
|
||||
"55213970774324510299479508399853534522527075462195808724319849722937344",
|
||||
)
|
||||
.unwrap(),
|
||||
U256::from_dec_str("1361129467683753853853498429727072845824").unwrap(),
|
||||
33554432.into(),
|
||||
U256::from_dec_str("9223372036854775808").unwrap(),
|
||||
U256::from_dec_str(
|
||||
"3618502788666131106986593281521497120414687020801267626233049500247285563392",
|
||||
)
|
||||
.unwrap(),
|
||||
U256::from_dec_str("2722259584404615024560450425766186844160").unwrap(),
|
||||
],
|
||||
};
|
||||
|
||||
let beneficiary_account_after = AccountRlp {
|
||||
nonce: 1.into(),
|
||||
..AccountRlp::default()
|
||||
};
|
||||
|
||||
let sender_balance_after = sender_balance_before - gas_price * 21000 - txn_value;
|
||||
let sender_account_after = AccountRlp {
|
||||
balance: sender_balance_after,
|
||||
nonce: 1.into(),
|
||||
..AccountRlp::default()
|
||||
};
|
||||
let to_account_after = AccountRlp {
|
||||
balance: txn_value.into(),
|
||||
..AccountRlp::default()
|
||||
};
|
||||
|
||||
let mut contract_code = HashMap::new();
|
||||
contract_code.insert(keccak(vec![]), vec![]);
|
||||
contract_code.insert(code_hash, code.to_vec());
|
||||
|
||||
let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty);
|
||||
expected_state_trie_after.insert(
|
||||
beneficiary_nibbles,
|
||||
rlp::encode(&beneficiary_account_after).to_vec(),
|
||||
);
|
||||
expected_state_trie_after.insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec());
|
||||
expected_state_trie_after.insert(to_nibbles, rlp::encode(&to_account_after).to_vec());
|
||||
expected_state_trie_after.insert(
|
||||
to_second_nibbles,
|
||||
rlp::encode(&to_account_second_before).to_vec(),
|
||||
);
|
||||
|
||||
// Compute new receipt trie.
|
||||
let mut receipts_trie = HashedPartialTrie::from(Node::Empty);
|
||||
let receipt_0 = LegacyReceiptRlp {
|
||||
status: true,
|
||||
cum_gas_used: 21000u64.into(),
|
||||
bloom: [0x00; 256].to_vec().into(),
|
||||
logs: vec![],
|
||||
};
|
||||
receipts_trie.insert(
|
||||
Nibbles::from_str("0x80").unwrap(),
|
||||
rlp::encode(&receipt_0).to_vec(),
|
||||
);
|
||||
|
||||
let tries_after = TrieRoots {
|
||||
state_root: expected_state_trie_after.hash(),
|
||||
transactions_root: HashedPartialTrie::from(Node::Empty).hash(),
|
||||
receipts_root: receipts_trie.clone().hash(),
|
||||
};
|
||||
|
||||
let inputs_first = GenerationInputs {
|
||||
signed_txns: vec![txn.to_vec()],
|
||||
tries: tries_before,
|
||||
trie_roots_after: tries_after,
|
||||
contract_code,
|
||||
block_metadata: block_metadata.clone(),
|
||||
txn_number_before: 0.into(),
|
||||
gas_used_before: 0.into(),
|
||||
gas_used_after: 21000u64.into(),
|
||||
block_bloom_before: [0.into(); 8],
|
||||
block_bloom_after: [0.into(); 8],
|
||||
addresses: vec![],
|
||||
};
|
||||
|
||||
let mut timing = TimingTree::new("prove first", log::Level::Debug);
|
||||
let proof = prove::<F, C, D>(&all_stark_first, &config, inputs_first.clone(), &mut timing)?;
|
||||
timing.filter(Duration::from_millis(100)).print();
|
||||
|
||||
// The output bloom filter, gas used and transaction number are fed to the next transaction, so the two proofs can be correctly aggregated.
|
||||
let block_bloom_second = proof.public_values.extra_block_data.block_bloom_after;
|
||||
let gas_used_second = proof.public_values.extra_block_data.gas_used_after;
|
||||
|
||||
verify_proof(&all_stark_first, proof, &config)?;
|
||||
|
||||
// Create the aggregation circuits.
|
||||
let all_circuits_first = AllRecursiveCircuits::<F, C, D>::new(
|
||||
&all_stark_first,
|
||||
&[9..17, 9..19, 9..15, 9..11, 9..14, 9..21], // Minimal ranges to prove an empty list
|
||||
&config,
|
||||
);
|
||||
|
||||
let mut timing = TimingTree::new("prove root first", log::Level::Info);
|
||||
let (root_proof_first, first_public_values) =
|
||||
all_circuits_first.prove_root(&all_stark_first, &config, inputs_first, &mut timing)?;
|
||||
timing.filter(Duration::from_millis(100)).print();
|
||||
|
||||
// Prove second transaction. In this second transaction, the code with logs is executed.
|
||||
let all_stark = AllStark::<F, D>::default();
|
||||
let config = StarkConfig::standard_fast_config();
|
||||
|
||||
let state_trie_before = expected_state_trie_after;
|
||||
|
||||
let tries_before = TrieInputs {
|
||||
state_trie: state_trie_before,
|
||||
transactions_trie: Node::Empty.into(),
|
||||
receipts_trie: receipts_trie.clone(),
|
||||
storage_tries: vec![],
|
||||
};
|
||||
|
||||
// Prove a transaction which carries out two LOG opcodes.
|
||||
let txn_gas_price = 10;
|
||||
let txn_2 = hex!("f860010a830186a094095e7baea6a6c7c4c2dfeb977efac326af552e89808025a04a223955b0bd3827e3740a9a427d0ea43beb5bafa44a0204bf0a3306c8219f7ba0502c32d78f233e9e7ce9f5df3b576556d5d49731e0678fd5a068cdf359557b5b");
|
||||
|
||||
let mut contract_code = HashMap::new();
|
||||
contract_code.insert(keccak(vec![]), vec![]);
|
||||
contract_code.insert(code_hash, code.to_vec());
|
||||
|
||||
// Update the state and receipt tries after the transaction, so that we have the correct expected tries:
|
||||
// Update accounts.
|
||||
let beneficiary_account_after = AccountRlp {
|
||||
nonce: 1.into(),
|
||||
..AccountRlp::default()
|
||||
};
|
||||
|
||||
let sender_balance_after = sender_balance_after - gas_used * txn_gas_price;
|
||||
let sender_account_after = AccountRlp {
|
||||
balance: sender_balance_after,
|
||||
nonce: 2.into(),
|
||||
..AccountRlp::default()
|
||||
};
|
||||
let balance_after = to_account_after.balance;
|
||||
let to_account_after = AccountRlp {
|
||||
balance: balance_after,
|
||||
..AccountRlp::default()
|
||||
};
|
||||
let to_account_second_after = AccountRlp {
|
||||
balance: to_account_second_before.balance,
|
||||
code_hash,
|
||||
..AccountRlp::default()
|
||||
};
|
||||
|
||||
// Update the receipt trie.
|
||||
let first_log = LogRlp {
|
||||
address: to.into(),
|
||||
topics: vec![],
|
||||
data: Bytes::new(),
|
||||
};
|
||||
|
||||
let second_log = LogRlp {
|
||||
address: to.into(),
|
||||
topics: vec![
|
||||
hex!("0000000000000000000000000000000000000000000000000000000000000062").into(), // dec: 98
|
||||
hex!("0000000000000000000000000000000000000000000000000000000000000063").into(), // dec: 99
|
||||
],
|
||||
data: hex!("a1b2c3d4e5").to_vec().into(),
|
||||
};
|
||||
|
||||
let receipt = LegacyReceiptRlp {
|
||||
status: true,
|
||||
cum_gas_used: (22570 + 21000).into(),
|
||||
bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000001000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000800000000000000008000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000800002000000000000000000000000000").to_vec().into(),
|
||||
logs: vec![first_log, second_log],
|
||||
};
|
||||
|
||||
let receipt_nibbles = Nibbles::from_str("0x01").unwrap(); // RLP(1) = 0x1
|
||||
|
||||
receipts_trie.insert(receipt_nibbles, rlp::encode(&receipt).to_vec());
|
||||
|
||||
// Update the state trie.
|
||||
let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty);
|
||||
expected_state_trie_after.insert(
|
||||
beneficiary_nibbles,
|
||||
rlp::encode(&beneficiary_account_after).to_vec(),
|
||||
);
|
||||
expected_state_trie_after.insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec());
|
||||
expected_state_trie_after.insert(to_nibbles, rlp::encode(&to_account_after).to_vec());
|
||||
expected_state_trie_after.insert(
|
||||
to_second_nibbles,
|
||||
rlp::encode(&to_account_second_after).to_vec(),
|
||||
);
|
||||
|
||||
let trie_roots_after = TrieRoots {
|
||||
state_root: expected_state_trie_after.hash(),
|
||||
transactions_root: HashedPartialTrie::from(Node::Empty).hash(),
|
||||
receipts_root: receipts_trie.hash(),
|
||||
};
|
||||
|
||||
let block_bloom_final = [
|
||||
0.into(),
|
||||
0.into(),
|
||||
U256::from_dec_str(
|
||||
"55213970774324510299479508399853534522527075462195808724319849722937344",
|
||||
)
|
||||
.unwrap(),
|
||||
U256::from_dec_str("1361129467683753853853498429727072845824").unwrap(),
|
||||
U256::from_dec_str("33554432").unwrap(),
|
||||
U256::from_dec_str("9223372036854775808").unwrap(),
|
||||
U256::from_dec_str(
|
||||
"3618502788666131106986593281521497120414687020801267626233049500247285563392",
|
||||
)
|
||||
.unwrap(),
|
||||
U256::from_dec_str("2722259584404615024560450425766186844160").unwrap(),
|
||||
];
|
||||
let inputs = GenerationInputs {
|
||||
signed_txns: vec![txn_2.to_vec()],
|
||||
tries: tries_before,
|
||||
trie_roots_after,
|
||||
contract_code,
|
||||
block_metadata,
|
||||
txn_number_before: 1.into(),
|
||||
gas_used_before: gas_used_second,
|
||||
gas_used_after: receipt.cum_gas_used,
|
||||
block_bloom_before: block_bloom_second,
|
||||
block_bloom_after: block_bloom_final,
|
||||
addresses: vec![],
|
||||
};
|
||||
let mut timing = TimingTree::new("prove second", log::Level::Debug);
|
||||
let proof = prove::<F, C, D>(&all_stark, &config, inputs.clone(), &mut timing)?;
|
||||
timing.filter(Duration::from_millis(100)).print();
|
||||
|
||||
verify_proof(&all_stark, proof, &config)?;
|
||||
let config = StarkConfig::standard_fast_config();
|
||||
let all_circuits = AllRecursiveCircuits::<F, C, D>::new(
|
||||
&all_stark,
|
||||
&[9..17, 9..19, 9..15, 9..11, 9..14, 9..21],
|
||||
&config,
|
||||
);
|
||||
let mut timing = TimingTree::new("prove root second", log::Level::Info);
|
||||
let (root_proof, public_values) =
|
||||
all_circuits.prove_root(&all_stark, &config, inputs, &mut timing)?;
|
||||
timing.filter(Duration::from_millis(100)).print();
|
||||
|
||||
all_circuits.verify_root(root_proof.clone())?;
|
||||
|
||||
// Update public values for the aggregation.
|
||||
let agg_public_values = PublicValues {
|
||||
trie_roots_before: first_public_values.trie_roots_before,
|
||||
trie_roots_after: public_values.trie_roots_after,
|
||||
extra_block_data: ExtraBlockData {
|
||||
txn_number_before: first_public_values.extra_block_data.txn_number_before,
|
||||
txn_number_after: public_values.extra_block_data.txn_number_after,
|
||||
gas_used_before: first_public_values.extra_block_data.gas_used_before,
|
||||
gas_used_after: public_values.extra_block_data.gas_used_after,
|
||||
block_bloom_before: first_public_values.extra_block_data.block_bloom_before,
|
||||
block_bloom_after: public_values.extra_block_data.block_bloom_after,
|
||||
},
|
||||
block_metadata: public_values.block_metadata,
|
||||
};
|
||||
|
||||
// We can duplicate the proofs here because the state hasn't mutated.
|
||||
let (agg_proof, updated_agg_public_values) = all_circuits_first.prove_aggregation(
|
||||
false,
|
||||
&root_proof_first,
|
||||
false,
|
||||
&root_proof,
|
||||
agg_public_values,
|
||||
)?;
|
||||
all_circuits_first.verify_aggregation(&agg_proof)?;
|
||||
let (block_proof, _block_public_values) =
|
||||
all_circuits.prove_block(None, &agg_proof, updated_agg_public_values)?;
|
||||
all_circuits.verify_block(&block_proof)
|
||||
}
|
||||
|
||||
/// Values taken from the block 1000000 of Goerli: https://goerli.etherscan.io/txs?block=1000000
|
||||
#[test]
|
||||
fn test_txn_and_receipt_trie_hash() -> anyhow::Result<()> {
|
||||
@ -411,6 +797,7 @@ fn test_two_txn() -> anyhow::Result<()> {
|
||||
block_gaslimit: 0xffffffffu32.into(),
|
||||
block_chain_id: 1.into(),
|
||||
block_base_fee: 0xa.into(),
|
||||
block_gas_used: 0.into(),
|
||||
block_bloom: [0.into(); 8],
|
||||
};
|
||||
|
||||
@ -481,6 +868,11 @@ fn test_two_txn() -> anyhow::Result<()> {
|
||||
trie_roots_after,
|
||||
contract_code,
|
||||
block_metadata,
|
||||
txn_number_before: 0.into(),
|
||||
gas_used_before: 0.into(),
|
||||
gas_used_after: 42000u64.into(),
|
||||
block_bloom_before: [0.into(); 8],
|
||||
block_bloom_after: [0.into(); 8],
|
||||
addresses: vec![],
|
||||
};
|
||||
|
||||
|
||||
@ -155,6 +155,11 @@ fn self_balance_gas_cost() -> anyhow::Result<()> {
|
||||
trie_roots_after,
|
||||
contract_code,
|
||||
block_metadata,
|
||||
txn_number_before: 0.into(),
|
||||
gas_used_before: 0.into(),
|
||||
gas_used_after: gas_used.into(),
|
||||
block_bloom_before: [0.into(); 8],
|
||||
block_bloom_after: [0.into(); 8],
|
||||
addresses: vec![],
|
||||
};
|
||||
|
||||
|
||||
@ -74,6 +74,7 @@ fn test_simple_transfer() -> anyhow::Result<()> {
|
||||
block_gaslimit: 0xff112233u32.into(),
|
||||
block_chain_id: 1.into(),
|
||||
block_base_fee: 0xa.into(),
|
||||
block_gas_used: 21032.into(),
|
||||
block_bloom: [0.into(); 8],
|
||||
};
|
||||
|
||||
@ -135,6 +136,11 @@ fn test_simple_transfer() -> anyhow::Result<()> {
|
||||
trie_roots_after,
|
||||
contract_code,
|
||||
block_metadata,
|
||||
txn_number_before: 0.into(),
|
||||
gas_used_before: 0.into(),
|
||||
gas_used_after: 21032.into(),
|
||||
block_bloom_before: [0.into(); 8],
|
||||
block_bloom_after: [0.into(); 8],
|
||||
addresses: vec![],
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user