Skip to content

Commit

Permalink
feat: implement 0x4a: blob_base_fee (kkrt-labs#247)
Browse files Browse the repository at this point in the history
  • Loading branch information
enitrat authored Dec 12, 2024
1 parent f7577b7 commit 3fc0683
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 20 deletions.
4 changes: 2 additions & 2 deletions cairo/ethereum/crypto/hash.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ func keccak256{range_check_ptr, bitwise_ptr: BitwiseBuiltin*, keccak_ptr: Keccak
let (local dst: felt*) = alloc();
bytes_to_bytes8_little_endian(dst, buffer.value.len, buffer.value.data);

let (code_hash) = keccak(dst, buffer.value.len);
tempvar value = new Uint256(low=code_hash.low, high=code_hash.high);
let (result) = keccak(dst, buffer.value.len);
tempvar value = new Uint256(low=result.low, high=result.high);
tempvar hash = Hash32(value=value);
return hash;
}
1 change: 1 addition & 0 deletions cairo/programs/os.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ func apply_transactions{
coinbase=header.coinbase,
base_fee=header.base_fee_per_gas.value,
block_hashes=block_hashes,
excess_blob_gas=header.excess_blob_gas,
);

Interpreter.execute(
Expand Down
25 changes: 21 additions & 4 deletions cairo/src/instructions/block_information.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from ethereum.cancun.vm.gas import calculate_blob_gas_price
from ethereum_types.numeric import U64

from starkware.cairo.common.bool import FALSE
from starkware.cairo.common.cairo_builtins import HashBuiltin, BitwiseBuiltin, KeccakBuiltin
from starkware.cairo.common.math import split_felt
Expand Down Expand Up @@ -38,7 +41,7 @@ namespace BlockInformation {
jmp selfbalance;
jmp basefee;
jmp blobhash;
jmp blobbasefee;
jmp blob_base_fee;

blockhash:
let range_check_ptr = [fp - 9];
Expand Down Expand Up @@ -126,10 +129,24 @@ namespace BlockInformation {
Stack.push_uint128(0);
jmp end;

blobbasefee:
blob_base_fee:
let stack = cast([fp - 6], model.Stack*);
Stack.push_uint128(0);
jmp end;
let evm = cast([fp - 3], model.EVM*);
let range_check_ptr = [fp - 9];
with_attr error_message("Excess blob gas is not set") {
assert evm.message.env.excess_blob_gas.is_some = 1;
}
let excess_blob_value = U64(evm.message.env.excess_blob_gas.value);
let blob_base_fee_ = calculate_blob_gas_price(excess_blob_value);
Stack.push_uint128(blob_base_fee_.value);

// Rebind unused args with fp
let pedersen_ptr = cast([fp - 10], HashBuiltin*);
let bitwise_ptr = cast([fp - 8], BitwiseBuiltin*);
let keccak_ptr = cast([fp - 7], KeccakBuiltin*);
let memory = cast([fp - 5], model.Memory*);
let state = cast([fp - 4], model.State*);
return evm;

end:
// Rebind unused args with fp
Expand Down
1 change: 1 addition & 0 deletions cairo/src/model.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ namespace model {
coinbase: felt,
base_fee: felt,
block_hashes: Uint256*,
excess_blob_gas: Option,
}

// @dev The parent EVM struct is used to store the parent EVM context of the current execution context.
Expand Down
2 changes: 2 additions & 0 deletions cairo/tests/programs/test_os.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import pytest
from eth_abi import encode
from ethereum_types.numeric import U256
from hypothesis import given
Expand All @@ -15,6 +16,7 @@

class TestOs:

@pytest.mark.slow
def test_erc20_transfer(self, cairo_run):
erc20 = get_contract("ERC20", "KethToken")
amount = int(1e18)
Expand Down
42 changes: 42 additions & 0 deletions cairo/tests/src/instructions/test_block_operations.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from starkware.cairo.common.alloc import alloc
from starkware.cairo.common.cairo_builtins import HashBuiltin, BitwiseBuiltin, KeccakBuiltin
from starkware.cairo.common.uint256 import Uint256, assert_uint256_eq
from starkware.cairo.common.memcpy import memcpy
from starkware.cairo.common.uint256 import ALL_ONES

from src.instructions.block_information import BlockInformation
from src.memory import Memory
from src.model import model
from src.stack import Stack
from src.state import State
from tests.utils.helpers import TestHelpers
from src.utils.utils import Helpers

func test__exec_blob_base_fee{
pedersen_ptr: HashBuiltin*,
range_check_ptr,
bitwise_ptr: BitwiseBuiltin*,
keccak_ptr: KeccakBuiltin*,
}() -> model.Stack* {
alloc_locals;
%{ dict_manager %}
local block: model.Block*;
local state: model.State*;
%{ block %}
%{ state %}
let stack = Stack.init();
let memory = Memory.init();
let (bytecode) = alloc();
assert [bytecode] = 0x4a; // BLOBBASEFEE
let address = 0xdead;
let (calldata) = alloc();
let evm = TestHelpers.init_evm_from_with_block_header(
state, 0, bytecode, address, 0, calldata, block.block_header
);

with stack, memory, state {
BlockInformation.exec_block_information(evm);
}

return stack;
}
17 changes: 17 additions & 0 deletions cairo/tests/src/instructions/test_block_operations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from ethereum.cancun.vm.gas import calculate_blob_gas_price
from tests.utils.data import block
from tests.utils.models import State


class TestBlockInformation:
class TestBlobBaseFee:
def test_should_push_blob_base_fee(self, cairo_run):
block_ = block()
[blob_base_fee] = cairo_run(
"test__exec_blob_base_fee", block=block_, state=State.model_validate({})
)

expected = calculate_blob_gas_price(
block_.block_header.excess_blob_gas_value
)
assert blob_base_fee == expected
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func test__exec_address__should_push_address_to_stack{
let (bytecode) = alloc();
let address = 0xdead;
let (calldata) = alloc();
let evm = TestHelpers.init_evm_at_address(initial_state, 0, bytecode, 0, address, 0, calldata);
let evm = TestHelpers.init_evm_at_address(initial_state, 0, bytecode, address, 0, calldata);

// When
with stack, memory, state {
Expand Down
2 changes: 1 addition & 1 deletion cairo/tests/src/instructions/test_memory_operations.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ func test_exec_sstore{
let initial_state = State.copy{state=state}();
let (bytecode) = alloc();
let (calldata) = alloc();
let evm = TestHelpers.init_evm_at_address(initial_state, 0, bytecode, 0, address, 0, calldata);
let evm = TestHelpers.init_evm_at_address(initial_state, 0, bytecode, address, 0, calldata);

with stack, memory, state {
Stack.push(new_value.value);
Expand Down
67 changes: 55 additions & 12 deletions cairo/tests/utils/helpers.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,43 @@ from src.utils.maths import unsigned_div_rem
from src.utils.utils import Helpers

namespace TestHelpers {
func init_evm_at_address{pedersen_ptr: HashBuiltin*, range_check_ptr}(
func init_evm_from_with_block_header{pedersen_ptr: HashBuiltin*, range_check_ptr}(
initial_state: model.State*,
bytecode_len: felt,
bytecode: felt*,
starknet_contract_address: felt,
evm_contract_address: felt,
calldata_len: felt,
calldata: felt*,
header: model.BlockHeader*,
) -> model.EVM* {
alloc_locals;
tempvar env = new model.Environment(
origin=0,
gas_price=0,
chain_id=0,
prev_randao=Uint256(0, 0),
block_number=0,
block_gas_limit=0,
block_timestamp=0,
coinbase=0,
base_fee=0,
prev_randao=header.mix_hash,
block_number=header.number,
block_gas_limit=header.gas_limit,
block_timestamp=header.timestamp,
coinbase=header.coinbase,
base_fee=header.base_fee_per_gas.value,
block_hashes=cast(0, Uint256*),
excess_blob_gas=header.excess_blob_gas,
);
return init_evm_with_env(
initial_state, env, bytecode_len, bytecode, evm_contract_address, calldata_len, calldata
);
}

func init_evm_with_env{pedersen_ptr: HashBuiltin*, range_check_ptr}(
initial_state: model.State*,
env: model.Environment*,
bytecode_len: felt,
bytecode: felt*,
evm_contract_address: felt,
calldata_len: felt,
calldata: felt*,
) -> model.EVM* {
alloc_locals;
tempvar address = evm_contract_address;
let (valid_jumpdests_start, valid_jumpdests) = Helpers.initialize_jumpdests(
bytecode_len=bytecode_len, bytecode=bytecode
Expand Down Expand Up @@ -65,19 +80,47 @@ namespace TestHelpers {
return evm;
}

func init_evm_at_address{pedersen_ptr: HashBuiltin*, range_check_ptr}(
initial_state: model.State*,
bytecode_len: felt,
bytecode: felt*,
evm_contract_address: felt,
calldata_len: felt,
calldata: felt*,
) -> model.EVM* {
alloc_locals;
tempvar empty_option = model.Option(0, 0);
tempvar env = new model.Environment(
origin=0,
gas_price=0,
chain_id=0,
prev_randao=Uint256(0, 0),
block_number=0,
block_gas_limit=0,
block_timestamp=0,
coinbase=0,
base_fee=0,
block_hashes=cast(0, Uint256*),
excess_blob_gas=empty_option,
);
return init_evm_with_env(
initial_state, env, bytecode_len, bytecode, evm_contract_address, calldata_len, calldata
);
}

func init_evm{pedersen_ptr: HashBuiltin*, range_check_ptr}(
initial_state: model.State*
) -> model.EVM* {
let (bytecode) = alloc();
let (calldata) = alloc();
return init_evm_at_address(initial_state, 0, bytecode, 0, 0, 0, calldata);
return init_evm_at_address(initial_state, 0, bytecode, 0, 0, calldata);
}

func init_evm_with_bytecode{pedersen_ptr: HashBuiltin*, range_check_ptr}(
initial_state: model.State*, bytecode_len: felt, bytecode: felt*
) -> model.EVM* {
let (calldata) = alloc();
return init_evm_at_address(initial_state, bytecode_len, bytecode, 0, 0, 0, calldata);
return init_evm_at_address(initial_state, bytecode_len, bytecode, 0, 0, calldata);
}

func init_evm_with_calldata{pedersen_ptr: HashBuiltin*, range_check_ptr}(
Expand All @@ -88,7 +131,7 @@ namespace TestHelpers {
calldata: felt*,
) -> model.EVM* {
return init_evm_at_address(
initial_state, bytecode_len, bytecode, 0, 0, calldata_len, calldata
initial_state, bytecode_len, bytecode, 0, calldata_len, calldata
);
}

Expand Down

0 comments on commit 3fc0683

Please sign in to comment.