Skip to content
This repository was archived by the owner on Jan 9, 2025. It is now read-only.

feat: use blockhash syscall #1062

Merged
merged 3 commits into from
Apr 3, 2024
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
4 changes: 0 additions & 4 deletions src/backend/starknet.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,6 @@ namespace Starknet {
let (base_fee) = Kakarot_base_fee.read();
let (block_gas_limit) = Kakarot_block_gas_limit.read();
let (prev_randao) = Kakarot_prev_randao.read();
let (block_hashes) = alloc();
// TODO: fix how blockhashes are retrieved
memset(block_hashes, 0, 256 * 2);

// No idea why this is required - but trying to pass prev_randao directly causes bugs.
let prev_randao = Uint256(low=prev_randao.low, high=prev_randao.high);
Expand All @@ -141,7 +138,6 @@ namespace Starknet {
block_number=block_number,
block_gas_limit=block_gas_limit,
block_timestamp=block_timestamp,
block_hashes=cast(block_hashes, Uint256*),
coinbase=coinbase,
base_fee=base_fee,
);
Expand Down
25 changes: 15 additions & 10 deletions src/kakarot/instructions/block_information.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ from starkware.cairo.common.uint256 import Uint256

from kakarot.constants import Constants
from kakarot.evm import EVM
from kakarot.interfaces.interfaces import ICairo1Helpers
from kakarot.storages import Kakarot_precompiles_class_hash
from kakarot.model import model
from kakarot.stack import Stack
from kakarot.state import State
Expand Down Expand Up @@ -45,14 +47,14 @@ namespace BlockInformation {
jmp blobbasefee;

blockhash:
let syscall_ptr = cast([fp - 10], felt*);
let pedersen_ptr = cast([fp - 9], HashBuiltin*);
let range_check_ptr = [fp - 8];
let stack = cast([fp - 6], model.Stack*);
let evm = cast([fp - 3], model.EVM*);
Internals.blockhash(evm);

// Rebind unused args with fp
let syscall_ptr = cast([fp - 10], felt*);
let pedersen_ptr = cast([fp - 9], HashBuiltin*);
let bitwise_ptr = cast([fp - 7], BitwiseBuiltin*);
let memory = cast([fp - 5], model.Memory*);
let state = cast([fp - 4], model.State*);
Expand Down Expand Up @@ -153,26 +155,29 @@ namespace BlockInformation {
}

namespace Internals {
func blockhash{range_check_ptr, stack: model.Stack*}(evm: model.EVM*) {
func blockhash{
syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr, stack: model.Stack*
}(evm: model.EVM*) {
let (block_number) = Stack.pop();
if (block_number.high != 0) {
Stack.push_uint256(Uint256(0, 0));
return ();
}

let in_range = is_in_range(
block_number.low, evm.message.env.block_number - 256, evm.message.env.block_number
);
let lower_bound = Helpers.saturated_sub(evm.message.env.block_number, 256);
let in_range = is_in_range(block_number.low, lower_bound, evm.message.env.block_number);

if (in_range == FALSE) {
Stack.push_uint256(Uint256(0, 0));
return ();
}

let blockhash = evm.message.env.block_hashes[
evm.message.env.block_number - 1 - block_number.low
];
Stack.push_uint256(blockhash);
let (implementation) = Kakarot_precompiles_class_hash.read();
let (blockhash) = ICairo1Helpers.library_call_get_block_hash(
implementation, block_number.low
);
let (blockhash_high, blockhash_low) = split_felt(blockhash);
Stack.push_uint256(Uint256(low=blockhash_low, high=blockhash_high));
return ();
}

Expand Down
3 changes: 3 additions & 0 deletions src/kakarot/interfaces/interfaces.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ namespace ICairo1Helpers {
) {
}

func get_block_hash(block_number: felt) -> (hash: felt) {
}

func keccak(
words_len: felt, words: felt*, last_input_word: felt, last_input_num_bytes: felt
) -> (hash: Uint256) {
Expand Down
2 changes: 0 additions & 2 deletions src/kakarot/model.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ namespace model {
// @param block_number The block number of the current block.
// @param block_gas_limit The gas limit for the current block.
// @param block_timestamp The timestamp of the current block.
// @param block_hashes The last 256 accessible block hashes
// @param coinbase The address of the miner of the current block.
// @param base_fee The basefee of the current block.
struct Environment {
Expand All @@ -176,7 +175,6 @@ namespace model {
block_number: felt,
block_gas_limit: felt,
block_timestamp: felt,
block_hashes: Uint256*,
coinbase: felt,
base_fee: felt,
}
Expand Down
12 changes: 11 additions & 1 deletion src/utils/utils.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// StarkWare dependencies
from starkware.cairo.common.alloc import alloc
from starkware.cairo.common.math import assert_le, split_felt, assert_nn_le, unsigned_div_rem
from starkware.cairo.common.math_cmp import is_le
from starkware.cairo.common.math_cmp import is_le, is_nn
from starkware.cairo.common.memcpy import memcpy
from starkware.cairo.common.pow import pow
from starkware.cairo.common.uint256 import Uint256, uint256_check
Expand All @@ -29,6 +29,16 @@ namespace Helpers {
return 0;
}

// @notice Performs subtraction and returns 0 if the result is negative.
func saturated_sub{range_check_ptr}(a, b) -> felt {
let res = a - b;
let is_res_nn = is_nn(res);
if (is_res_nn != FALSE) {
return res;
}
return 0;
}

func to_uint256{range_check_ptr}(val: felt) -> Uint256* {
let (high, low) = split_felt(val);
tempvar res = new Uint256(low, high);
Expand Down
14 changes: 5 additions & 9 deletions tests/end_to_end/PlainOpcodes/test_plain_opcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ async def test_should_return_starknet_timestamp(
self, plain_opcodes, block_with_tx_hashes
):
timestamp = await plain_opcodes.opcodeTimestamp()
assert timestamp == block_with_tx_hashes("pending")["timestamp"]
assert timestamp == (await block_with_tx_hashes("pending")).timestamp

class TestBlockhash:
@pytest.mark.xfail(reason="Need to fix blockhash on real Starknet network")
Expand All @@ -45,22 +45,18 @@ async def test_should_return_blockhash_with_valid_block_number(
block_with_tx_hashes,
):
latest_block = block_with_tx_hashes("latest")
blockhash = await plain_opcodes.opcodeBlockHash(
latest_block["block_number"]
)
blockhash = await plain_opcodes.opcodeBlockHash(latest_block.block_number)

assert (
int.from_bytes(blockhash, byteorder="big") == latest_block["block_hash"]
)
assert int.from_bytes(blockhash, byteorder="big") == latest_block.block_hash

async def test_should_return_zero_with_invalid_block_number(
self,
plain_opcodes,
block_with_tx_hashes,
):
latest_block = block_with_tx_hashes("latest")
latest_block = await block_with_tx_hashes("latest")
blockhash_invalid_number = await plain_opcodes.opcodeBlockHash(
latest_block["block_number"] + 1
latest_block.block_number + 1
)

assert int.from_bytes(blockhash_invalid_number, byteorder="big") == 0
Expand Down
3 changes: 2 additions & 1 deletion tests/end_to_end/Solmate/test_erc20.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,8 @@ async def test_permit_should_fail_with_bad_deadline(
self, erc_20, block_with_tx_hashes, owner, other
):
nonce = await erc_20.nonces(owner.address)
pending_timestamp = block_with_tx_hashes("pending")["timestamp"]

pending_timestamp = (await block_with_tx_hashes("pending")).timestamp
deadline = pending_timestamp - 1
digest = get_approval_digest(
"Kakarot Token",
Expand Down
18 changes: 2 additions & 16 deletions tests/end_to_end/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,22 +267,8 @@ def block_with_tx_hashes(starknet):
https://github.com/software-mansion/starknet.py/issues/1174.
"""

import json

import requests

def _factory(block_number: Optional[int] = None):
response = requests.post(
starknet.url,
json={
"jsonrpc": "2.0",
"method": "starknet_getBlockWithTxHashes",
"params": [block_number or "latest"],
"id": 0,
},
timeout=60,
)
return json.loads(response.text)["result"]
async def _factory(block_number: Optional[int] = None):
return await starknet.get_block_with_tx_hashes(block_number=block_number)

return _factory

Expand Down
Loading