Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add blockhash opcode #1208

Merged
merged 7 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions evm/src/cpu/kernel/asm/core/syscall_stubs.asm
Original file line number Diff line number Diff line change
@@ -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?
Expand Down
42 changes: 42 additions & 0 deletions evm/src/cpu/kernel/asm/memory/metadata.asm
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,48 @@ 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
// Check for an overflow, since we're incrementing `block_number` afterwards.
DUP2 %eq_const(@U256_MAX) %jumpi(zero_hash)
// 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
Expand Down
49 changes: 27 additions & 22 deletions evm/src/cpu/kernel/constants/global_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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] {
[
Expand Down Expand Up @@ -130,6 +133,7 @@ impl GlobalMetadata {
Self::LogsLen,
Self::LogsDataLen,
Self::LogsPayloadLen,
Self::BlockCurrentHash,
Self::TxnNumberBefore,
Self::TxnNumberAfter,
]
Expand Down Expand Up @@ -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",
Expand Down
125 changes: 125 additions & 0 deletions evm/src/cpu/kernel/tests/block_hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
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> = 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_block_hash_with_overflow() -> Result<()> {
let blockhash_label = KERNEL.global_labels["blockhash"];
let retdest = 0xDEADBEEFu32.into();
let cur_block_number = 1;
let block_number = U256::MAX;
let initial_stack = vec![retdest, block_number];

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(())
}
LindaGuiga marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions evm/src/cpu/kernel/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod account_code;
mod balance;
mod bignum;
mod blake2_f;
mod block_hash;
mod bls381;
mod bn254;
mod core;
Expand Down
55 changes: 51 additions & 4 deletions evm/src/fixed_recursive_verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
Loading
Loading