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

Implement Blob transactions (type-3) #50

Merged
merged 9 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from 8 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 evm_arithmetization/src/cpu/kernel/aggregator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ pub(crate) fn combined_kernel() -> Kernel {
include_str!("asm/transactions/type_0.asm"),
include_str!("asm/transactions/type_1.asm"),
include_str!("asm/transactions/type_2.asm"),
include_str!("asm/transactions/type_3.asm"),
include_str!("asm/util/assertions.asm"),
include_str!("asm/util/basic_macros.asm"),
include_str!("asm/util/keccak.asm"),
Expand Down
6 changes: 3 additions & 3 deletions evm_arithmetization/src/cpu/kernel/asm/core/exception.asm
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ min_stack_len_for_opcode:
BYTES 0 // 0x46, CHAINID
BYTES 0 // 0x47, SELFBALANCE
BYTES 0 // 0x48, BASEFEE
BYTES 0 // 0x49, invalid
BYTES 1 // 0x49, BLOBHASH
BYTES 0 // 0x4a, BLOBBASEFEE
%rep 5 // 0x4b-0x4f, invalid
BYTES 0
Expand Down Expand Up @@ -371,11 +371,11 @@ gas_cost_for_opcode:
BYTES 0
%endrep

%rep 25 //0x30-0x48, only syscalls
%rep 26 //0x30-0x49, only syscalls
BYTES 0
%endrep

%rep 7 // 0x49-0x4f, invalid
%rep 6 // 0x4a-0x4f, invalid
BYTES 0
%endrep

Expand Down
2 changes: 1 addition & 1 deletion evm_arithmetization/src/cpu/kernel/asm/core/syscall.asm
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ global syscall_jumptable:
JUMPTABLE sys_chainid
JUMPTABLE sys_selfbalance
JUMPTABLE sys_basefee
JUMPTABLE panic // 0x49 is invalid
JUMPTABLE sys_blobhash
JUMPTABLE sys_blobbasefee
%rep 5
JUMPTABLE panic // 0x4b-0x4f are invalid opcodes
Expand Down
33 changes: 33 additions & 0 deletions evm_arithmetization/src/cpu/kernel/asm/memory/metadata.asm
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,39 @@ global sys_basefee:
SWAP1
EXIT_KERNEL

global sys_blobhash:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you can add an interpreter test for this function?

// stack: kexit_info, index
%charge_gas_const(@GAS_HASH_OPCODE)
// stack: kexit_info, index
%blobhash
// stack: blobhash, kexit_info
SWAP1
EXIT_KERNEL

%macro blobhash
// stack: kexit_info, index
SWAP1
// stack: index, kexit_info
%mload_global_metadata(@GLOBAL_METADATA_BLOB_VERSIONED_HASHES_LEN)
DUP2
LT ISZERO // == GE
// stack: index >= len, index, kexit_info
%jumpi(%%index_too_big)
PUSH @SEGMENT_TXN_BLOB_VERSIONED_HASHES
%build_kernel_address
// stack: read_addr, kexit_info
MLOAD_GENERAL
%jump(%%end)
%%index_too_big:
// The index is larger than the list, just push 0.
// stack: index, kexit_info
POP
Nashtare marked this conversation as resolved.
Show resolved Hide resolved
PUSH 0
// stack: 0, kexit_info
%%end:
// stack: blobhash, kexit_info
%endmacro

global sys_blobbasefee:
// stack: kexit_info
%charge_gas_const(@GAS_BASE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,58 @@
%%after:
%endmacro

%macro decode_and_store_max_fee_per_blob_gas
// stack: rlp_addr
%decode_rlp_scalar
%stack (rlp_addr, max_fee_per_blob_gas) -> (max_fee_per_blob_gas, rlp_addr)
%mstore_txn_field(@TXN_FIELD_MAX_FEE_PER_BLOB_GAS)
// stack: rlp_addr
%endmacro

%macro decode_and_store_blob_versioned_hashes
// stack: rlp_addr
DUP1 %mstore_global_metadata(@GLOBAL_METADATA_BLOB_VERSIONED_HASHES_RLP_START)
%decode_rlp_list_len
%stack (rlp_addr, len) -> (len, len, rlp_addr, %%after)
%jumpi(decode_and_store_blob_versioned_hashes)
// stack: len, rlp_addr, %%after
POP SWAP1 POP
// stack: rlp_addr
%mload_global_metadata(@GLOBAL_METADATA_BLOB_VERSIONED_HASHES_RLP_START) DUP2 SUB %mstore_global_metadata(@GLOBAL_METADATA_BLOB_VERSIONED_HASHES_RLP_LEN)
%%after:
%endmacro

// The blob versioned hashes are just a list of hashes.
global decode_and_store_blob_versioned_hashes:
// stack: len, rlp_addr
// Store the list length
DUP1 %mstore_global_metadata(@GLOBAL_METADATA_BLOB_VERSIONED_HASHES_LEN)

// stack: len, rlp_addr
DUP2 ADD
// stack: end_rlp_addr, rlp_addr
// Store the RLP length.
%mload_global_metadata(@GLOBAL_METADATA_BLOB_VERSIONED_HASHES_RLP_START) DUP2 SUB %mstore_global_metadata(@GLOBAL_METADATA_BLOB_VERSIONED_HASHES_RLP_LEN)
// stack: end_rlp_addr, rlp_addr
PUSH @SEGMENT_TXN_BLOB_VERSIONED_HASHES // initial address to write to
SWAP2
decode_and_store_blob_versioned_hashes_loop:
// stack: rlp_addr, end_rlp_addr, store_addr
DUP2 DUP2 EQ %jumpi(decode_and_store_blob_versioned_hashes_finish)
// stack: rlp_addr, end_rlp_addr, store_addr
%decode_rlp_scalar // blob_versioned_hashes[i]
// stack: rlp_addr, hash, end_rlp_addr, store_addr
SWAP3 DUP1 SWAP2
// stack: hash, store_addr, store_addr, end_rlp_addr, rlp_addr
MSTORE_GENERAL
// stack: store_addr, end_rlp_addr, rlp_addr
%increment SWAP2
// stack: rlp_addr, end_rlp_addr, store_addr'
%jump(decode_and_store_blob_versioned_hashes_loop)
decode_and_store_blob_versioned_hashes_finish:
%stack (rlp_addr, end_rlp_addr, store_addr, retdest) -> (retdest, rlp_addr)
JUMP

%macro decode_and_store_y_parity
// stack: rlp_addr
%decode_rlp_scalar
Expand Down
22 changes: 15 additions & 7 deletions evm_arithmetization/src/cpu/kernel/asm/transactions/router.asm
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,33 @@ read_txn_from_memory:
// stack: retdest

// We will peak at the first byte to determine what type of transaction this is.
// Note that type 1 and 2 transactions have a first byte of 1 and 2, respectively.
// Note that type 1, 2 and 3 transactions have a first byte of 1, 2 and 3, respectively.
// Type 0 (legacy) transactions have no such prefix, but their RLP will have a
// first byte >= 0xc0, so there is no overlap.

PUSH @SEGMENT_RLP_RAW // ctx == virt == 0
DUP1
MLOAD_GENERAL
%eq_const(1)
// stack: first_byte == 1, retdest
// stack: first_byte == 1, rlp_segment, retdest
%jumpi(process_type_1_txn)
// stack: retdest
// stack: rlp_segment, retdest

PUSH @SEGMENT_RLP_RAW // ctx == virt == 0
DUP1
MLOAD_GENERAL
%eq_const(2)
// stack: first_byte == 2, retdest
// stack: first_byte == 2, rlp_segment, retdest
%jumpi(process_type_2_txn)
// stack: retdest
// stack: rlp_segment, retdest

DUP1
MLOAD_GENERAL
%eq_const(3)
// stack: first_byte == 3, rlp_segment, retdest
%jumpi(process_type_3_txn)
// stack: rlp_segment, retdest

// At this point, since it's not a type 1 or 2 transaction,
// At this point, since it's not a type 1, 2 or 3 transaction,
// it must be a legacy (aka type 0) transaction.
%jump(process_type_0_txn)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
// keccak256(rlp([nonce, gas_price, gas_limit, to, value, data]))

global process_type_0_txn:
// stack: retdest
PUSH @SEGMENT_RLP_RAW // ctx == virt == 0
// stack: rlp_addr, retdest
%decode_rlp_list_len
// We don't actually need the length.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@
// data, access_list]))

global process_type_1_txn:
// stack: retdest
// stack: rlp_addr, retdest
// Initial rlp address offset of 1 (skipping over the 0x01 byte)
PUSH 1
PUSH @SEGMENT_RLP_RAW
%build_kernel_address
%add_const(1)
// stack: rlp_addr, retdest
%decode_rlp_list_len
// We don't actually need the length.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@
// access_list]))

global process_type_2_txn:
// stack: retdest
// stack: rlp_addr, retdest
// Initial rlp address offset of 1 (skipping over the 0x02 byte)
PUSH 1
PUSH @SEGMENT_RLP_RAW
%build_kernel_address
%add_const(1)
// stack: rlp_addr, retdest
%decode_rlp_list_len
// We don't actually need the length.
Expand Down
157 changes: 157 additions & 0 deletions evm_arithmetization/src/cpu/kernel/asm/transactions/type_3.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// Type 3 transactions, introduced by EIP 4844, have the format
// 0x03 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value,
// data, access_list, max_fee_per_blob_gas, blob_versioned_hashes, y_parity, r, s])
//
// The signed data is
// keccak256(0x03 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit,
// to, value, data, access_list, max_fee_per_blob_gas, blob_versioned_hashes]))

global process_type_3_txn:
// stack: rlp_addr, retdest
// Initial rlp address offset of 1 (skipping over the 0x03 byte)
%add_const(1)
// stack: rlp_addr, retdest
%decode_rlp_list_len
// We don't actually need the length.
%stack (rlp_addr, len) -> (rlp_addr)

// stack: rlp_addr, retdest
%store_chain_id_present_true
%decode_and_store_chain_id
%decode_and_store_nonce
%decode_and_store_max_priority_fee
%decode_and_store_max_fee
%decode_and_store_gas_limit
%decode_and_store_to
%decode_and_store_value
%decode_and_store_data
%decode_and_store_access_list
%decode_and_store_max_fee_per_blob_gas
%decode_and_store_blob_versioned_hashes
%decode_and_store_y_parity
%decode_and_store_r
%decode_and_store_s

// stack: rlp_addr, retdest
POP
// stack: retdest

// From EIP-4844:
// The signature_y_parity, signature_r, signature_s elements of this transaction represent a secp256k1 signature over
// keccak256(0x03 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, data, access_list, max_fee_per_blob_gas, blob_versioned_hashes]))
type_3_compute_signed_data:
%alloc_rlp_block
// stack: rlp_addr_start, retdest
%mload_txn_field(@TXN_FIELD_CHAIN_ID)
// stack: chain_id, rlp_start, retdest
DUP2
// stack: rlp_addr, chain_id, rlp_start, retdest
%encode_rlp_scalar
// stack: rlp_addr, rlp_start, retdest

%mload_txn_field(@TXN_FIELD_NONCE)
%encode_rlp_scalar_swapped_inputs
// stack: rlp_addr, rlp_start, retdest

%mload_txn_field(@TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS)
%encode_rlp_scalar_swapped_inputs
// stack: rlp_addr, rlp_start, retdest

%mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS)
%encode_rlp_scalar_swapped_inputs
// stack: rlp_addr, rlp_start, retdest

%mload_txn_field(@TXN_FIELD_GAS_LIMIT)
%encode_rlp_scalar_swapped_inputs
// stack: rlp_addr, rlp_start, retdest

// As per EIP-4844, blob transactions cannot have the form of a create transaction.
%mload_txn_field(@TXN_FIELD_TO)
%mload_global_metadata(@GLOBAL_METADATA_CONTRACT_CREATION) %jumpi(panic)
// stack: to, rlp_addr, rlp_start, retdest
SWAP1 %encode_rlp_160
// stack: rlp_addr, rlp_start, retdest

%mload_txn_field(@TXN_FIELD_VALUE)
%encode_rlp_scalar_swapped_inputs
// stack: rlp_addr, rlp_start, retdest

// Encode txn data.
%mload_txn_field(@TXN_FIELD_DATA_LEN)
PUSH @SEGMENT_TXN_DATA // ctx == virt == 0
// stack: ADDR, len, rlp_addr, rlp_start, retdest
PUSH after_serializing_txn_data
// stack: after_serializing_txn_data, ADDR, len, rlp_addr, rlp_start, retdest
SWAP3
// stack: rlp_addr, ADDR, len, after_serializing_txn_data, rlp_start, retdest
%jump(encode_rlp_string)

after_serializing_txn_data:
// Instead of manually encoding the access list, we just copy the raw RLP from the transaction.
%mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START)
%mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN)
%stack (al_len, al_start, rlp_addr, rlp_start, retdest) ->
(
rlp_addr,
al_start,
al_len,
after_serializing_access_list,
rlp_addr, rlp_start, retdest)
%jump(memcpy_bytes)
after_serializing_access_list:
// stack: rlp_addr, rlp_start, retdest
%mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) ADD
// stack: rlp_addr, rlp_start, retdest

%mload_txn_field(@TXN_FIELD_MAX_FEE_PER_BLOB_GAS)
%encode_rlp_scalar_swapped_inputs
// stack: rlp_addr, rlp_start, retdest

// Instead of manually encoding the blob versioned hashes, we just copy the raw RLP from the transaction.
%mload_global_metadata(@GLOBAL_METADATA_BLOB_VERSIONED_HASHES_RLP_START)
%mload_global_metadata(@GLOBAL_METADATA_BLOB_VERSIONED_HASHES_RLP_LEN)
%stack (bvh_len, bvh_start, rlp_addr, rlp_start, retdest) ->
(
rlp_addr,
bvh_start,
bvh_len,
after_serializing_blob_versioned_hashes,
rlp_addr, rlp_start, retdest)
%jump(memcpy_bytes)
after_serializing_blob_versioned_hashes:
%prepend_rlp_list_prefix
// stack: prefix_start_pos, rlp_len, retdest

// Store a `3` in front of the RLP
%decrement
%stack (rlp_addr) -> (3, rlp_addr, rlp_addr)
MSTORE_GENERAL
// stack: rlp_addr, rlp_len, retdest

// Hash the RLP + the leading `3`
SWAP1 %increment SWAP1
// stack: ADDR, len, retdest
KECCAK_GENERAL
// stack: hash, retdest

%mload_txn_field(@TXN_FIELD_S)
%mload_txn_field(@TXN_FIELD_R)
%mload_txn_field(@TXN_FIELD_Y_PARITY) %add_const(27) // ecrecover interprets v as y_parity + 27

PUSH store_origin
// stack: store_origin, v, r, s, hash, retdest
SWAP4
// stack: hash, v, r, s, store_origin, retdest
%jump(ecrecover)

store_origin:
// stack: address, retdest
// If ecrecover returned u256::MAX, that indicates failure.
DUP1
%eq_const(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
%jumpi(panic)

// stack: address, retdest
%mstore_txn_field(@TXN_FIELD_ORIGIN)
// stack: retdest
%jump(process_normalized_txn)
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub(crate) const INVALID_OPCODES_USER: U256 = u256_from_set_index_ranges(&[
0x0c..=0x0f,
0x1e..=0x1f,
0x21..=0x2f,
0x49..=0x4f,
0x4a..=0x4f,
0x5c..=0x5e,
0xa5..=0xef,
0xf6..=0xf9,
Expand Down
Loading