Add blockhash sys opcode

This commit is contained in:
Linda Guiga 2023-08-21 23:32:53 +01:00
parent e6ca460623
commit 42f7038031
No known key found for this signature in database
19 changed files with 520 additions and 71 deletions

View File

@ -1,14 +1,6 @@
// Labels for unimplemented syscalls to make the kernel assemble.
// Each label should be removed from this file once it is implemented.
// This is a temporary version that returns 0 on all inputs.
// TODO: Fix this.
global sys_blockhash:
// stack: kexit_info, block_number
%charge_gas_const(@GAS_BLOCKHASH)
%stack (kexit_info, block_number) -> (kexit_info, 0)
EXIT_KERNEL
// This is a temporary version that returns the block difficulty (i.e. the old version of this opcode).
// TODO: Fix this.
// TODO: What semantics will this have for Edge?

View File

@ -235,6 +235,45 @@ global sys_basefee:
SWAP1
EXIT_KERNEL
global sys_blockhash:
// stack: kexit_info, block_number
%charge_gas_const(@GAS_BLOCKHASH)
SWAP1
// stack: block_number, kexit_info
%blockhash
EXIT_KERNEL
global blockhash:
// stack: block_number, retdest
%mload_global_metadata(@GLOBAL_METADATA_BLOCK_NUMBER)
// stack: cur_block_number, block_number, retdest
DUP1 DUP3 %increment GT %jumpi(zero_hash) // if block_number >= cur_block_number
// stack: cur_block_number, block_number, retdest
DUP2 PUSH 256 ADD
// stack: block_number+256, cur_block_number, block_number, retdest
DUP2 GT %jumpi(zero_hash) // if cur_block_number > block_number + 256
// If we are here, the provided block number is correct
SUB
// stack: cur_block_number - block_number, retdest
PUSH 256 SUB
// stack: block_hash_number, retdest
%mload_kernel(@SEGMENT_BLOCK_HASHES)
SWAP1 JUMP
JUMP
%macro blockhash
// stack: block_number
%stack (block_number) -> (block_number, %%after)
%jump(blockhash)
%%after:
%endmacro
zero_hash:
// stack: cur_block_number, block_number, retdest
%pop2
PUSH 0 SWAP1
JUMP
%macro update_mem_words
// stack: num_words, kexit_info
%mem_words

View File

@ -47,44 +47,47 @@ pub(crate) enum GlobalMetadata {
BlockGasUsedBefore = 22,
/// After current transactions block values.
BlockGasUsedAfter = 23,
/// Current block header hash
BlockCurrentHash = 24,
/// Gas to refund at the end of the transaction.
RefundCounter = 24,
RefundCounter = 25,
/// Length of the addresses access list.
AccessedAddressesLen = 25,
AccessedAddressesLen = 26,
/// Length of the storage keys access list.
AccessedStorageKeysLen = 26,
AccessedStorageKeysLen = 27,
/// Length of the self-destruct list.
SelfDestructListLen = 27,
SelfDestructListLen = 28,
/// Length of the bloom entry buffer.
BloomEntryLen = 28,
BloomEntryLen = 29,
/// Length of the journal.
JournalLen = 29,
JournalLen = 30,
/// Length of the `JournalData` segment.
JournalDataLen = 30,
JournalDataLen = 31,
/// Current checkpoint.
CurrentCheckpoint = 31,
TouchedAddressesLen = 32,
CurrentCheckpoint = 32,
TouchedAddressesLen = 33,
// Gas cost for the access list in type-1 txns. See EIP-2930.
AccessListDataCost = 33,
AccessListDataCost = 34,
// Start of the access list in the RLP for type-1 txns.
AccessListRlpStart = 34,
AccessListRlpStart = 35,
// Length of the access list in the RLP for type-1 txns.
AccessListRlpLen = 35,
AccessListRlpLen = 36,
// Boolean flag indicating if the txn is a contract creation txn.
ContractCreation = 36,
IsPrecompileFromEoa = 37,
CallStackDepth = 38,
/// Transaction logs list length.
LogsLen = 39,
LogsDataLen = 40,
LogsPayloadLen = 41,
TxnNumberBefore = 42,
TxnNumberAfter = 43,
ContractCreation = 37,
IsPrecompileFromEoa = 38,
CallStackDepth = 39,
/// Transaction logs list length
LogsLen = 40,
LogsDataLen = 41,
LogsPayloadLen = 42,
TxnNumberBefore = 43,
TxnNumberAfter = 44,
}
impl GlobalMetadata {
pub(crate) const COUNT: usize = 44;
pub(crate) const COUNT: usize = 45;
pub(crate) fn all() -> [Self; Self::COUNT] {
[
@ -130,6 +133,7 @@ impl GlobalMetadata {
Self::LogsLen,
Self::LogsDataLen,
Self::LogsPayloadLen,
Self::BlockCurrentHash,
Self::TxnNumberBefore,
Self::TxnNumberAfter,
]
@ -162,6 +166,7 @@ impl GlobalMetadata {
Self::BlockGasUsed => "GLOBAL_METADATA_BLOCK_GAS_USED",
Self::BlockGasUsedBefore => "GLOBAL_METADATA_BLOCK_GAS_USED_BEFORE",
Self::BlockGasUsedAfter => "GLOBAL_METADATA_BLOCK_GAS_USED_AFTER",
Self::BlockCurrentHash => "GLOBAL_METADATA_BLOCK_CURRENT_HASH",
Self::RefundCounter => "GLOBAL_METADATA_REFUND_COUNTER",
Self::AccessedAddressesLen => "GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN",
Self::AccessedStorageKeysLen => "GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN",

View File

@ -0,0 +1,97 @@
use anyhow::Result;
use ethereum_types::{H256, U256};
use rand::{thread_rng, Rng};
use crate::cpu::kernel::aggregator::KERNEL;
use crate::cpu::kernel::constants::global_metadata::GlobalMetadata;
use crate::cpu::kernel::interpreter::Interpreter;
use crate::memory::segments::Segment;
#[test]
fn test_correct_block_hash() -> Result<()> {
let mut rng = rand::thread_rng();
let blockhash_label = KERNEL.global_labels["blockhash"];
let retdest = 0xDEADBEEFu32.into();
let block_number: u8 = rng.gen();
let initial_stack = vec![retdest, block_number.into()];
let hashes: Vec<U256> = vec![U256::from_big_endian(&thread_rng().gen::<H256>().0); 257];
let mut interpreter = Interpreter::new_with_kernel(blockhash_label, initial_stack);
interpreter.set_memory_segment(Segment::BlockHashes, hashes[0..256].to_vec());
interpreter.set_global_metadata_field(GlobalMetadata::BlockCurrentHash, hashes[256]);
interpreter.set_global_metadata_field(GlobalMetadata::BlockNumber, 256.into());
interpreter.run()?;
let result = interpreter.stack();
assert_eq!(
result[0], hashes[block_number as usize],
"Resulting block hash {:?} different from expected hash {:?}",
result[0], hashes[block_number as usize]
);
Ok(())
}
#[test]
fn test_big_index_block_hash() -> Result<()> {
let mut rng = rand::thread_rng();
let blockhash_label = KERNEL.global_labels["blockhash"];
let retdest = 0xDEADBEEFu32.into();
let cur_block_number = 3;
let block_number: usize = rng.gen::<u8>() as usize;
let actual_block_number = block_number + cur_block_number;
let initial_stack = vec![retdest, actual_block_number.into()];
let hashes: Vec<U256> = vec![U256::from_big_endian(&thread_rng().gen::<H256>().0); 257];
let mut interpreter = Interpreter::new_with_kernel(blockhash_label, initial_stack);
interpreter.set_memory_segment(Segment::BlockHashes, hashes[0..256].to_vec());
interpreter.set_global_metadata_field(GlobalMetadata::BlockCurrentHash, hashes[256]);
interpreter.set_global_metadata_field(GlobalMetadata::BlockNumber, cur_block_number.into());
interpreter.run()?;
let result = interpreter.stack();
assert_eq!(
result[0],
0.into(),
"Resulting block hash {:?} different from expected hash {:?}",
result[0],
0
);
Ok(())
}
#[test]
fn test_small_index_block_hash() -> Result<()> {
let mut rng = rand::thread_rng();
let blockhash_label = KERNEL.global_labels["blockhash"];
let retdest = 0xDEADBEEFu32.into();
let cur_block_number = 512;
let block_number = rng.gen::<u8>() as usize;
let initial_stack = vec![retdest, block_number.into()];
let hashes: Vec<U256> = (20..277).map(|elt| elt.into()).collect();
let mut interpreter = Interpreter::new_with_kernel(blockhash_label, initial_stack);
interpreter.set_memory_segment(Segment::BlockHashes, hashes[0..256].to_vec());
interpreter.set_global_metadata_field(GlobalMetadata::BlockCurrentHash, hashes[256]);
interpreter.set_global_metadata_field(GlobalMetadata::BlockNumber, cur_block_number.into());
interpreter.run()?;
let result = interpreter.stack();
assert_eq!(
result[0],
0.into(),
"Resulting block hash {:?} different from expected hash {:?}",
result[0],
0
);
Ok(())
}

View File

@ -2,6 +2,7 @@ mod account_code;
mod balance;
mod bignum;
mod blake2_f;
mod block_hash;
mod bls381;
mod bn254;
mod core;

View File

@ -40,15 +40,15 @@ use crate::logic::LogicStark;
use crate::memory::memory_stark::MemoryStark;
use crate::permutation::{get_grand_product_challenge_set_target, GrandProductChallengeSet};
use crate::proof::{
BlockMetadataTarget, ExtraBlockDataTarget, PublicValues, PublicValuesTarget,
BlockHashesTarget, 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_extra_public_values_target, set_public_value_targets, set_trie_roots_target,
PlonkWrapperCircuit, PublicInputs, StarkWrapperCircuit,
get_memory_extra_looking_products_circuit, recursive_stark_circuit, set_block_hashes_target,
set_block_metadata_target, set_extra_public_values_target, set_public_value_targets,
set_trie_roots_target, PlonkWrapperCircuit, PublicInputs, StarkWrapperCircuit,
};
use crate::stark::Stark;
use crate::util::h256_limbs;
@ -571,6 +571,17 @@ where
let lhs_public_values = lhs.public_values(&mut builder);
let rhs_public_values = rhs.public_values(&mut builder);
// Connect all block hash values
BlockHashesTarget::connect(
&mut builder,
public_values.block_hashes,
lhs_public_values.block_hashes,
);
BlockHashesTarget::connect(
&mut builder,
public_values.block_hashes,
rhs_public_values.block_hashes,
);
// Connect all block metadata values.
BlockMetadataTarget::connect(
&mut builder,
@ -694,6 +705,9 @@ 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);
// Connect block hashes
Self::connect_block_hashes(&mut builder, &parent_block_proof, &agg_root_proof);
let parent_pv = PublicValuesTarget::from_public_inputs(&parent_block_proof.public_inputs);
let agg_pv = PublicValuesTarget::from_public_inputs(&agg_root_proof.public_inputs);
@ -723,6 +737,29 @@ where
}
}
/// Connect the 256 block hashes between two blocks
pub fn connect_block_hashes(
builder: &mut CircuitBuilder<F, D>,
lhs: &ProofWithPublicInputsTarget<D>,
rhs: &ProofWithPublicInputsTarget<D>,
) {
let lhs_public_values = PublicValuesTarget::from_public_inputs(&lhs.public_inputs);
let rhs_public_values = PublicValuesTarget::from_public_inputs(&rhs.public_inputs);
for i in 0..255 {
for j in 0..8 {
builder.connect(
lhs_public_values.block_hashes.prev_hashes[8 * (i + 1) + j],
rhs_public_values.block_hashes.prev_hashes[8 * i + j],
);
}
}
let expected_hash = lhs_public_values.block_hashes.cur_hash;
let prev_block_hash = &rhs_public_values.block_hashes.prev_hashes[255 * 8..256 * 8];
for i in 0..expected_hash.len() {
builder.connect(expected_hash[i], prev_block_hash[i]);
}
}
fn connect_block_proof(
builder: &mut CircuitBuilder<F, D>,
has_parent_block: BoolTarget,
@ -886,6 +923,11 @@ where
&self.aggregation.circuit.verifier_only,
);
set_block_hashes_target(
&mut agg_inputs,
&self.aggregation.public_values.block_hashes,
&public_values.block_hashes,
);
set_block_metadata_target(
&mut agg_inputs,
&self.aggregation.public_values.block_metadata,
@ -964,6 +1006,11 @@ where
block_inputs
.set_verifier_data_target(&self.block.cyclic_vk, &self.block.circuit.verifier_only);
set_block_hashes_target(
&mut block_inputs,
&self.block.public_values.block_hashes,
&public_values.block_hashes,
);
set_extra_public_values_target(
&mut block_inputs,
&self.block.public_values.extra_block_data,

View File

@ -21,9 +21,9 @@ 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, ExtraBlockData, PublicValues, TrieRoots};
use crate::proof::{BlockHashes, BlockMetadata, ExtraBlockData, PublicValues, TrieRoots};
use crate::util::h2u;
use crate::witness::memory::{MemoryAddress, MemoryChannel};
use crate::witness::memory::{MemoryAddress, MemoryChannel, MemoryOp};
use crate::witness::transition::transition;
pub mod mpt;
@ -55,6 +55,8 @@ pub struct GenerationInputs {
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
@ -100,6 +102,10 @@ 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::BlockCurrentHash,
U256::from_big_endian(&inputs.block_hashes.cur_hash.0),
),
(GlobalMetadata::BlockGasUsed, metadata.block_gas_used),
(GlobalMetadata::BlockGasUsedBefore, inputs.gas_used_before),
(GlobalMetadata::BlockGasUsedAfter, inputs.gas_used_after),
@ -181,6 +187,19 @@ fn apply_metadata_and_tries_memops<F: RichField + Extendable<D>, const D: usize>
})
.collect::<Vec<_>>(),
);
// 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::<Vec<_>>(),
);
state.memory.apply_ops(&ops);
state.traces.memory_ops.extend(ops);
@ -244,6 +263,7 @@ pub fn generate_traces<F: RichField + Extendable<D>, const D: usize>(
trie_roots_before,
trie_roots_after,
block_metadata: inputs.block_metadata,
block_hashes: inputs.block_hashes,
extra_block_data,
};

View File

@ -39,6 +39,7 @@ impl<F: Field> GenerationState<F> {
"ffe" => self.run_ffe(input_fn),
"mpt" => self.run_mpt(),
"rlp" => self.run_rlp(),
"current_hash" => self.run_current_hash(),
"account_code" => self.run_account_code(input_fn),
"bignum_modmul" => self.run_bignum_modmul(),
_ => panic!("Unrecognized prover input function."),
@ -118,6 +119,10 @@ impl<F: Field> GenerationState<F> {
.unwrap_or_else(|| panic!("Out of RLP data"))
}
fn run_current_hash(&mut self) -> U256 {
U256::from_big_endian(&self.inputs.block_hashes.cur_hash.0)
}
/// Account code.
fn run_account_code(&mut self, input_fn: &ProverInputFn) -> U256 {
match input_fn.0[1].as_str() {

View File

@ -146,6 +146,37 @@ fn observe_extra_block_data_target<
challenger.observe_elements(&extra_data.block_bloom_after);
}
fn observe_block_hashes<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
const D: usize,
>(
challenger: &mut Challenger<F, C::Hasher>,
block_hashes: &BlockHashes,
) {
for i in 0..256 {
challenger.observe_elements(
&u256_limbs::<F>(U256::from_big_endian(&block_hashes.prev_hashes[i].0))[0..8],
);
}
challenger
.observe_elements(&u256_limbs::<F>(U256::from_big_endian(&block_hashes.cur_hash.0))[0..8])
}
fn observe_block_hashes_target<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
const D: usize,
>(
challenger: &mut RecursiveChallenger<F, C::Hasher, D>,
block_hashes: &BlockHashesTarget,
) where
C::Hasher: AlgebraicHasher<F>,
{
challenger.observe_elements(&block_hashes.prev_hashes);
challenger.observe_elements(&block_hashes.cur_hash);
}
pub(crate) fn observe_public_values<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
@ -157,6 +188,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_block_hashes::<F, C, D>(challenger, &public_values.block_hashes);
observe_extra_block_data::<F, C, D>(challenger, &public_values.extra_block_data);
}
@ -173,6 +205,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_block_hashes_target::<F, C, D>(challenger, &public_values.block_hashes);
observe_extra_block_data_target::<F, C, D>(challenger, &public_values.extra_block_data);
}

View File

@ -68,10 +68,12 @@ pub enum Segment {
TouchedAddresses = 34,
/// List of checkpoints for the current context. Length in `ContextMetadata`.
ContextCheckpoints = 35,
/// List of 256 previous block hashes.
BlockHashes = 36,
}
impl Segment {
pub(crate) const COUNT: usize = 36;
pub(crate) const COUNT: usize = 37;
pub(crate) fn all() -> [Self; Self::COUNT] {
[
@ -111,6 +113,7 @@ impl Segment {
Self::JournalCheckpoints,
Self::TouchedAddresses,
Self::ContextCheckpoints,
Self::BlockHashes,
]
}
@ -153,6 +156,7 @@ impl Segment {
Segment::JournalCheckpoints => "SEGMENT_JOURNAL_CHECKPOINTS",
Segment::TouchedAddresses => "SEGMENT_TOUCHED_ADDRESSES",
Segment::ContextCheckpoints => "SEGMENT_CONTEXT_CHECKPOINTS",
Segment::BlockHashes => "SEGMENT_BLOCK_HASHES",
}
}
@ -195,6 +199,7 @@ impl Segment {
Segment::JournalCheckpoints => 256,
Segment::TouchedAddresses => 256,
Segment::ContextCheckpoints => 256,
Segment::BlockHashes => 256,
}
}
}

View File

@ -53,6 +53,7 @@ pub struct PublicValues {
pub trie_roots_before: TrieRoots,
pub trie_roots_after: TrieRoots,
pub block_metadata: BlockMetadata,
pub block_hashes: BlockHashes,
pub extra_block_data: ExtraBlockData,
}
@ -63,6 +64,22 @@ pub struct TrieRoots {
pub receipts_root: H256,
}
// 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(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlockHashes {
pub prev_hashes: Vec<H256>,
pub cur_hash: H256,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct BlockMetadata {
pub block_beneficiary: Address,
@ -93,6 +110,7 @@ pub struct PublicValuesTarget {
pub trie_roots_before: TrieRootsTarget,
pub trie_roots_after: TrieRootsTarget,
pub block_metadata: BlockMetadataTarget,
pub block_hashes: BlockHashesTarget,
pub extra_block_data: ExtraBlockDataTarget,
}
@ -140,6 +158,13 @@ impl PublicValuesTarget {
buffer.write_target(block_gas_used)?;
buffer.write_target_array(&block_bloom)?;
let BlockHashesTarget {
prev_hashes,
cur_hash,
} = self.block_hashes;
buffer.write_target_array(&prev_hashes)?;
buffer.write_target_array(&cur_hash)?;
let ExtraBlockDataTarget {
txn_number_before,
txn_number_after,
@ -183,6 +208,11 @@ impl PublicValuesTarget {
block_bloom: buffer.read_target_array()?,
};
let block_hashes = BlockHashesTarget {
prev_hashes: buffer.read_target_array()?,
cur_hash: buffer.read_target_array()?,
};
let extra_block_data = ExtraBlockDataTarget {
txn_number_before: buffer.read_target()?,
txn_number_after: buffer.read_target()?,
@ -196,6 +226,7 @@ impl PublicValuesTarget {
trie_roots_before,
trie_roots_after,
block_metadata,
block_hashes,
extra_block_data,
})
}
@ -205,6 +236,7 @@ impl PublicValuesTarget {
pis.len()
> TrieRootsTarget::SIZE * 2
+ BlockMetadataTarget::SIZE
+ BlockHashesTarget::BLOCK_HASHES_SIZE
+ ExtraBlockDataTarget::SIZE
- 1
);
@ -218,10 +250,17 @@ impl PublicValuesTarget {
&pis[TrieRootsTarget::SIZE * 2
..TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE],
),
block_hashes: BlockHashesTarget::from_public_inputs(
&pis[TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE
..TrieRootsTarget::SIZE * 2
+ BlockMetadataTarget::SIZE
+ BlockHashesTarget::BLOCK_HASHES_SIZE],
),
extra_block_data: ExtraBlockDataTarget::from_public_inputs(
&pis[TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE
..TrieRootsTarget::SIZE * 2
+ BlockMetadataTarget::SIZE
+ BlockHashesTarget::BLOCK_HASHES_SIZE
+ ExtraBlockDataTarget::SIZE],
),
}
@ -252,6 +291,12 @@ impl PublicValuesTarget {
pv0.block_metadata,
pv1.block_metadata,
),
block_hashes: BlockHashesTarget::select(
builder,
condition,
pv0.block_hashes,
pv1.block_hashes,
),
extra_block_data: ExtraBlockDataTarget::select(
builder,
condition,
@ -411,6 +456,51 @@ impl BlockMetadataTarget {
}
}
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub struct BlockHashesTarget {
pub prev_hashes: [Target; 2048],
pub cur_hash: [Target; 8],
}
impl BlockHashesTarget {
const BLOCK_HASHES_SIZE: usize = 2056;
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]);
}
}
}
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub struct ExtraBlockDataTarget {
pub txn_number_before: Target,

View File

@ -37,9 +37,10 @@ use crate::permutation::{
PermutationCheckDataTarget,
};
use crate::proof::{
BlockMetadata, BlockMetadataTarget, ExtraBlockData, ExtraBlockDataTarget, PublicValues,
PublicValuesTarget, StarkOpeningSetTarget, StarkProof, StarkProofChallengesTarget,
StarkProofTarget, StarkProofWithMetadata, TrieRoots, TrieRootsTarget,
BlockHashes, BlockHashesTarget, BlockMetadata, BlockMetadataTarget, ExtraBlockData,
ExtraBlockDataTarget, PublicValues, PublicValuesTarget, StarkOpeningSetTarget, StarkProof,
StarkProofChallengesTarget, StarkProofTarget, StarkProofWithMetadata, TrieRoots,
TrieRootsTarget,
};
use crate::stark::Stark;
use crate::util::u256_limbs;
@ -587,6 +588,27 @@ pub(crate) fn get_memory_extra_looking_products_circuit<
);
});
// Add block hashes writes.
product = add_data_write(
builder,
challenge,
product,
metadata_segment,
GlobalMetadata::BlockCurrentHash as usize,
&public_values.block_hashes.cur_hash,
);
let block_hashes_segment = builder.constant(F::from_canonical_u32(Segment::BlockHashes as u32));
for i in 0..256 {
product = add_data_write(
builder,
challenge,
product,
block_hashes_segment,
i,
&public_values.block_hashes.prev_hashes[8 * i..8 * (i + 1)],
);
}
// Add block bloom filters writes.
let bloom_segment = builder.constant(F::from_canonical_u32(Segment::GlobalBlockBloom as u32));
for i in 0..8 {
@ -598,7 +620,6 @@ pub(crate) fn get_memory_extra_looking_products_circuit<
i,
&public_values.block_metadata.block_bloom[i * 8..(i + 1) * 8],
);
}
for i in 0..8 {
product = add_data_write(
@ -621,6 +642,7 @@ pub(crate) fn get_memory_extra_looking_products_circuit<
&public_values.extra_block_data.block_bloom_after[i * 8..(i + 1) * 8],
);
}
}
// Add trie roots writes.
let trie_fields = [
@ -729,11 +751,13 @@ 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 block_hashes = add_virtual_block_hashes(builder);
let extra_block_data = add_virtual_extra_block_data(builder);
PublicValuesTarget {
trie_roots_before,
trie_roots_after,
block_metadata,
block_hashes,
extra_block_data,
}
}
@ -775,6 +799,17 @@ pub(crate) fn add_virtual_block_metadata<F: RichField + Extendable<D>, const D:
block_bloom,
}
}
pub(crate) fn add_virtual_block_hashes<F: RichField + Extendable<D>, const D: usize>(
builder: &mut CircuitBuilder<F, D>,
) -> BlockHashesTarget {
let prev_hashes = builder.add_virtual_public_input_arr();
let cur_hash = builder.add_virtual_public_input_arr();
BlockHashesTarget {
prev_hashes,
cur_hash,
}
}
pub(crate) fn add_virtual_extra_block_data<F: RichField + Extendable<D>, const D: usize>(
builder: &mut CircuitBuilder<F, D>,
) -> ExtraBlockDataTarget {
@ -894,6 +929,11 @@ pub(crate) fn set_public_value_targets<F, W, const D: usize>(
&public_values_target.block_metadata,
&public_values.block_metadata,
);
set_block_hashes_target(
witness,
&public_values_target.block_hashes,
&public_values.block_hashes,
);
set_extra_public_values_target(
witness,
&public_values_target.extra_block_data,
@ -1008,6 +1048,31 @@ pub(crate) fn set_block_metadata_target<F, W, const D: usize>(
witness.set_target_arr(&block_metadata_target.block_bloom, &block_bloom_limbs);
}
pub(crate) fn set_block_hashes_target<F, W, const D: usize>(
witness: &mut W,
block_hashes_target: &BlockHashesTarget,
block_hashes: &BlockHashes,
) where
F: RichField + Extendable<D>,
W: Witness<F>,
{
for i in 0..256 {
let block_hash_limbs: [F; 8] =
u256_limbs::<F>(U256::from_big_endian(&block_hashes.prev_hashes[i].0))[..8]
.try_into()
.unwrap();
witness.set_target_arr(
&block_hashes_target.prev_hashes[8 * i..8 * (i + 1)],
&block_hash_limbs,
);
}
let cur_block_hash_limbs: [F; 8] =
u256_limbs::<F>(U256::from_big_endian(&block_hashes.cur_hash.0))[..8]
.try_into()
.unwrap();
witness.set_target_arr(&block_hashes_target.cur_hash, &cur_block_hash_limbs);
}
pub(crate) fn set_extra_public_values_target<F, W, const D: usize>(
witness: &mut W,
ed_target: &ExtraBlockDataTarget,

View File

@ -176,6 +176,10 @@ where
GlobalMetadata::BlockBaseFee,
public_values.block_metadata.block_base_fee,
),
(
GlobalMetadata::BlockCurrentHash,
U256::from_big_endian(&public_values.block_hashes.cur_hash.0),
),
(
GlobalMetadata::BlockGasUsed,
public_values.block_metadata.block_gas_used,
@ -242,6 +246,13 @@ where
prod = add_data_write(challenge, bloom_segment, prod, index + 16, val);
}
// Add Blockhashes writes.
let block_hashes_segment = F::from_canonical_u32(Segment::BlockHashes as u32);
for index in 0..256 {
let val = h2u(public_values.block_hashes.prev_hashes[index]);
prod = add_data_write(challenge, block_hashes_segment, prod, index, val);
}
prod
}
@ -267,6 +278,7 @@ where
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>,

View File

@ -5,7 +5,7 @@ use std::time::Duration;
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, H256};
use hex_literal::hex;
use keccak_hash::keccak;
use plonky2::field::goldilocks_field::GoldilocksField;
@ -15,7 +15,7 @@ use plonky2_evm::all_stark::AllStark;
use plonky2_evm::config::StarkConfig;
use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp};
use plonky2_evm::generation::{GenerationInputs, TrieInputs};
use plonky2_evm::proof::{BlockMetadata, TrieRoots};
use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots};
use plonky2_evm::prover::prove;
use plonky2_evm::verifier::verify_proof;
use plonky2_evm::Node;
@ -154,6 +154,10 @@ fn add11_yml() -> anyhow::Result<()> {
gas_used_after: 0xa868u64.into(),
block_bloom_before: [0.into(); 8],
block_bloom_after: [0.into(); 8],
block_hashes: BlockHashes {
prev_hashes: vec![H256::default(); 256],
cur_hash: H256::default(),
},
addresses: vec![],
};

View File

@ -5,7 +5,7 @@ use std::time::Duration;
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, U256};
use ethereum_types::{Address, H256, U256};
use hex_literal::hex;
use keccak_hash::keccak;
use plonky2::field::goldilocks_field::GoldilocksField;
@ -16,7 +16,7 @@ use plonky2_evm::config::StarkConfig;
use plonky2_evm::cpu::kernel::opcodes::{get_opcode, get_push_opcode};
use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp};
use plonky2_evm::generation::{GenerationInputs, TrieInputs};
use plonky2_evm::proof::{BlockMetadata, TrieRoots};
use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots};
use plonky2_evm::prover::prove;
use plonky2_evm::verifier::verify_proof;
use plonky2_evm::Node;
@ -171,6 +171,10 @@ fn test_basic_smart_contract() -> anyhow::Result<()> {
gas_used_after: gas_used.into(),
block_bloom_before: [0.into(); 8],
block_bloom_after: [0.into(); 8],
block_hashes: BlockHashes {
prev_hashes: vec![H256::default(); 256],
cur_hash: H256::default(),
},
addresses: vec![],
};

View File

@ -4,6 +4,7 @@ use std::time::Duration;
use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV};
use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie};
use ethereum_types::H256;
use keccak_hash::keccak;
use log::info;
use plonky2::field::goldilocks_field::GoldilocksField;
@ -14,7 +15,7 @@ use plonky2_evm::all_stark::AllStark;
use plonky2_evm::config::StarkConfig;
use plonky2_evm::fixed_recursive_verifier::AllRecursiveCircuits;
use plonky2_evm::generation::{GenerationInputs, TrieInputs};
use plonky2_evm::proof::{BlockMetadata, TrieRoots};
use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots};
use plonky2_evm::Node;
type F = GoldilocksField;
@ -62,6 +63,10 @@ fn test_empty_txn_list() -> anyhow::Result<()> {
gas_used_after: 0.into(),
block_bloom_before: [0.into(); 8],
block_bloom_after: [0.into(); 8],
block_hashes: BlockHashes {
prev_hashes: vec![H256::default(); 256],
cur_hash: H256::default(),
},
addresses: vec![],
};

View File

@ -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, U256};
use ethereum_types::{Address, H256, U256};
use hex_literal::hex;
use keccak_hash::keccak;
use plonky2::field::goldilocks_field::GoldilocksField;
@ -19,7 +19,7 @@ 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, ExtraBlockData, PublicValues, TrieRoots};
use plonky2_evm::proof::{BlockHashes, BlockMetadata, ExtraBlockData, PublicValues, TrieRoots};
use plonky2_evm::prover::prove;
use plonky2_evm::verifier::verify_proof;
use plonky2_evm::Node;
@ -232,6 +232,10 @@ fn test_log_opcodes() -> anyhow::Result<()> {
block_bloom_before: [0.into(); 8],
block_bloom_after,
block_hashes: BlockHashes {
prev_hashes: vec![H256::default(); 256],
cur_hash: H256::default(),
},
addresses: vec![],
};
@ -425,6 +429,10 @@ fn test_log_with_aggreg() -> anyhow::Result<()> {
gas_used_after: 21000u64.into(),
block_bloom_before: [0.into(); 8],
block_bloom_after: [0.into(); 8],
block_hashes: BlockHashes {
prev_hashes: vec![H256::default(); 256],
cur_hash: H256::default(),
},
addresses: vec![],
};
@ -562,6 +570,10 @@ fn test_log_with_aggreg() -> anyhow::Result<()> {
gas_used_after: receipt.cum_gas_used,
block_bloom_before: block_bloom_second,
block_bloom_after: block_bloom_final,
block_hashes: BlockHashes {
prev_hashes: vec![H256::default(); 256],
cur_hash: H256::default(),
},
addresses: vec![],
};
@ -585,6 +597,7 @@ fn test_log_with_aggreg() -> anyhow::Result<()> {
block_bloom_after: public_values.extra_block_data.block_bloom_after,
},
block_metadata: public_values.block_metadata,
block_hashes: public_values.block_hashes,
};
// We can duplicate the proofs here because the state hasn't mutated.
@ -857,6 +870,10 @@ fn test_two_txn() -> anyhow::Result<()> {
gas_used_after: 42000u64.into(),
block_bloom_before: [0.into(); 8],
block_bloom_after: [0.into(); 8],
block_hashes: BlockHashes {
prev_hashes: vec![H256::default(); 256],
cur_hash: H256::default(),
},
addresses: vec![],
};

View File

@ -5,7 +5,7 @@ use std::time::Duration;
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, H256};
use hex_literal::hex;
use keccak_hash::keccak;
use plonky2::field::goldilocks_field::GoldilocksField;
@ -15,7 +15,7 @@ use plonky2_evm::all_stark::AllStark;
use plonky2_evm::config::StarkConfig;
use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp};
use plonky2_evm::generation::{GenerationInputs, TrieInputs};
use plonky2_evm::proof::{BlockMetadata, TrieRoots};
use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots};
use plonky2_evm::prover::prove;
use plonky2_evm::verifier::verify_proof;
use plonky2_evm::Node;
@ -160,6 +160,10 @@ fn self_balance_gas_cost() -> anyhow::Result<()> {
gas_used_after: gas_used.into(),
block_bloom_before: [0.into(); 8],
block_bloom_after: [0.into(); 8],
block_hashes: BlockHashes {
prev_hashes: vec![H256::default(); 256],
cur_hash: H256::default(),
},
addresses: vec![],
};

View File

@ -5,7 +5,7 @@ use std::time::Duration;
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, U256};
use ethereum_types::{Address, H256, U256};
use hex_literal::hex;
use keccak_hash::keccak;
use plonky2::field::goldilocks_field::GoldilocksField;
@ -15,7 +15,7 @@ use plonky2_evm::all_stark::AllStark;
use plonky2_evm::config::StarkConfig;
use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp};
use plonky2_evm::generation::{GenerationInputs, TrieInputs};
use plonky2_evm::proof::{BlockMetadata, TrieRoots};
use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots};
use plonky2_evm::prover::prove;
use plonky2_evm::verifier::verify_proof;
use plonky2_evm::Node;
@ -141,6 +141,10 @@ fn test_simple_transfer() -> anyhow::Result<()> {
gas_used_after: 21032.into(),
block_bloom_before: [0.into(); 8],
block_bloom_after: [0.into(); 8],
block_hashes: BlockHashes {
prev_hashes: vec![H256::default(); 256],
cur_hash: H256::default(),
},
addresses: vec![],
};