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

Feat/blockhash #435

Merged
merged 5 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions crates/evm/src/errors.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const WRITE_IN_STATIC_CONTEXT: felt252 = 'KKT: WriteInStaticContext';

// STARKNET_SYSCALLS
const READ_SYSCALL_FAILED: felt252 = 'KKT: read syscall failed';
const BLOCK_HASH_SYSCALL_FAILED: felt252 = 'KKT: block_hash syscall failed';

#[derive(Drop, Copy, PartialEq)]
enum EVMError {
Expand Down
27 changes: 24 additions & 3 deletions crates/evm/src/instructions/block_information.cairo
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use evm::balance::balance;
//! Block Information.

use evm::errors::EVMError;
use evm::errors::{EVMError, BLOCK_HASH_SYSCALL_FAILED};
use evm::machine::{Machine, MachineCurrentContextTrait};
use evm::stack::StackTrait;

// Corelib imports
use starknet::info::{get_block_number, get_block_timestamp};
use starknet::info::{get_block_number, get_block_timestamp, get_block_info};
use starknet::{get_block_hash_syscall};
use utils::constants::CHAIN_ID;

#[generate_trait]
Expand All @@ -15,7 +16,27 @@ impl BlockInformation of BlockInformationTrait {
/// Get the hash of one of the 256 most recent complete blocks.
/// # Specification: https://www.evm.codes/#40?fork=shanghai
fn exec_blockhash(ref self: Machine) -> Result<(), EVMError> {
Result::Err(EVMError::NotImplemented)
let block_number = self.stack.pop_u64()?;
let current_block = get_block_number();

// If input block number is lower than current_block - 256, return 0
// If input block number is higher than current_block - 10, return 0
// Note: in the specs, input block number can be equal - at most - to the current block number minus one.
// In Starknet, the `get_block_hash_syscall` is capped at current block minus ten.
// TODO: monitor the changes in the `get_block_hash_syscall` syscall.
// source: https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/system-calls-cairo1/#get_block_hash
if block_number + 10 > current_block || block_number + 256 < current_block {
return self.stack.push(0);
}

let block_hash = get_block_hash_syscall(block_number);
enitrat marked this conversation as resolved.
Show resolved Hide resolved
match block_hash {
Result::Ok(block_hash) => self.stack.push(block_hash.into()),
// This syscall should not error out, as we made sure block_number =< current_block - 10
// In case of failed syscall, we can either return 0, or revert.
// Since this situation would be highly breaking, we choose to revert.
Result::Err(_) => Result::Err(EVMError::SyscallFailed(BLOCK_HASH_SYSCALL_FAILED)),
}
}

/// 0x41 - COINBASE
Expand Down
17 changes: 16 additions & 1 deletion crates/evm/src/stack.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ trait StackTrait {
fn push(ref self: Stack, item: u256) -> Result<(), EVMError>;
fn pop(ref self: Stack) -> Result<u256, EVMError>;
fn pop_usize(ref self: Stack) -> Result<usize, EVMError>;
fn pop_u64(ref self: Stack) -> Result<u64, EVMError>;
fn pop_i256(ref self: Stack) -> Result<i256, EVMError>;
fn pop_eth_address(ref self: Stack) -> Result<EthAddress, EVMError>;
fn pop_n(ref self: Stack, n: usize) -> Result<Array<u256>, EVMError>;
Expand Down Expand Up @@ -113,11 +114,25 @@ impl StackImpl of StackTrait {
#[inline(always)]
fn pop_usize(ref self: Stack) -> Result<usize, EVMError> {
let item: u256 = self.pop()?;
// item.try_into().ok_or(EVMError::TypeConversionError(TYPE_CONVERSION_ERROR))
let item: usize = item.try_into_result()?;
Result::Ok(item)
}

/// Calls `Stack::pop` and tries to convert it to usize
enitrat marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Errors
///
/// Returns `EVMError::StackError` with appropriate message
/// In case:
/// - Stack is empty
/// - Type conversion failed
#[inline(always)]
fn pop_u64(ref self: Stack) -> Result<u64, EVMError> {
let item: u256 = self.pop()?;
let item: u64 = item.try_into_result()?;
Result::Ok(item)
}

/// Calls `Stack::pop` and convert it to i256
///
/// # Errors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,58 @@ use openzeppelin::token::erc20::interface::IERC20CamelDispatcherTrait;
use starknet::testing::{set_block_timestamp, set_block_number, set_contract_address};
use utils::constants::CHAIN_ID;

/// 0x40 - BLOCKHASH
#[test]
#[available_gas(20000000)]
fn test_block_hash_below_bounds() {
enitrat marked this conversation as resolved.
Show resolved Hide resolved
// Given
let mut machine = setup_machine();

set_block_number(500);

// When
machine.stack.push(243).unwrap();
machine.exec_blockhash();

// Then
assert(machine.stack.peek().unwrap() == 0, 'stack top should be 1692873993');
enitrat marked this conversation as resolved.
Show resolved Hide resolved
}

#[test]
#[available_gas(20000000)]
fn test_block_hash_above_bounds() {
enitrat marked this conversation as resolved.
Show resolved Hide resolved
// Given
let mut machine = setup_machine();

set_block_number(500);

// When
machine.stack.push(491).unwrap();
machine.exec_blockhash();

// Then
assert(machine.stack.peek().unwrap() == 0, 'stack top should be 1692873993');
enitrat marked this conversation as resolved.
Show resolved Hide resolved
}

// TODO: implement exec_blockhash testing for block number within bounds
// https://github.com/starkware-libs/cairo/blob/77a7e7bc36aa1c317bb8dd5f6f7a7e6eef0ab4f3/crates/cairo-lang-starknet/cairo_level_tests/interoperability.cairo#L173
#[ignore]
#[test]
#[available_gas(20000000)]
fn test_block_hash_within_bounds() {
enitrat marked this conversation as resolved.
Show resolved Hide resolved
// Given
let mut machine = setup_machine();

set_block_number(500);

// When
machine.stack.push(244).unwrap();
machine.exec_blockhash();
// Then
assert(machine.stack.peek().unwrap() == 0xF, 'stack top should be 0xF');
}


#[test]
#[available_gas(20000000)]
fn test_block_timestamp_set_to_1692873993() {
Expand Down
4 changes: 2 additions & 2 deletions crates/utils/src/traits.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ trait TryIntoResult<T, U> {
fn try_into_result(self: T) -> Result<U, EVMError>;
}

impl U256TryIntoResultU32 of TryIntoResult<u256, usize> {
impl U256TryIntoResultU32<U, +TryInto<u256, U>> of TryIntoResult<u256, U> {
enitrat marked this conversation as resolved.
Show resolved Hide resolved
/// Converts a u256 into a Result<u32, EVMError>
/// If the u256 is larger than MAX_U32, it returns an error.
/// Otherwise, it returns the casted value.
fn try_into_result(self: u256) -> Result<usize, EVMError> {
fn try_into_result(self: u256) -> Result<U, EVMError> {
match self.try_into() {
Option::Some(value) => Result::Ok(value),
Option::None => Result::Err(EVMError::TypeConversionError(TYPE_CONVERSION_ERROR))
Expand Down