Skip to content

Commit

Permalink
Merge pull request #1208 from topos-protocol/blockhash_opcode
Browse files Browse the repository at this point in the history
Add blockhash opcode
  • Loading branch information
LindaGuiga authored Sep 7, 2023
2 parents 71b2ece + 170f7d8 commit 180c209
Show file tree
Hide file tree
Showing 19 changed files with 526 additions and 58 deletions.
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(())
}
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

0 comments on commit 180c209

Please sign in to comment.