From 9b0092ab1d76a8a8ba4233b7f697f921c08b4b9c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 23 May 2023 09:21:27 +0200 Subject: [PATCH] Support for type-1 transactions (#1051) * Type 1 txn * Remove magic constants * Remove useless stack element * Read correct original value for sk * EIP-3651 * Fix encoding when TO is 0 * Minor --- evm/src/cpu/kernel/asm/core/access_lists.asm | 2 +- evm/src/cpu/kernel/asm/core/call.asm | 4 +- evm/src/cpu/kernel/asm/core/intrinsic_gas.asm | 5 +- evm/src/cpu/kernel/asm/core/process_txn.asm | 5 + .../asm/transactions/common_decoding.asm | 104 ++++++++++++++++- .../cpu/kernel/asm/transactions/type_0.asm | 7 ++ .../cpu/kernel/asm/transactions/type_1.asm | 105 +++++++++++++++++- .../cpu/kernel/constants/global_metadata.rs | 14 ++- 8 files changed, 234 insertions(+), 12 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/access_lists.asm b/evm/src/cpu/kernel/asm/core/access_lists.asm index 0ba56c30..b1b9fd5d 100644 --- a/evm/src/cpu/kernel/asm/core/access_lists.asm +++ b/evm/src/cpu/kernel/asm/core/access_lists.asm @@ -87,7 +87,7 @@ remove_accessed_addresses_found: %stack (addr, key, value) -> (addr, key, value, %%after) %jump(insert_accessed_storage_keys) %%after: - // stack: cold_access + // stack: cold_access, original_value %endmacro /// Inserts the storage key and value into the access list if it is not already present. diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm index adbe7412..3a170325 100644 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -23,7 +23,7 @@ global sys_call: %u256_to_addr // Truncate to 160 bits DUP1 %insert_accessed_addresses %checkpoint // Checkpoint - DUP1 %insert_touched_addresses + DUP2 %insert_touched_addresses %call_charge_gas(1, 1) @@ -123,7 +123,7 @@ global sys_staticcall: %u256_to_addr // Truncate to 160 bits DUP1 %insert_accessed_addresses %checkpoint // Checkpoint - DUP1 %insert_touched_addresses + DUP2 %insert_touched_addresses // Add a value of 0 to the stack. Slightly inefficient but that way we can reuse %call_charge_gas. %stack (cold_access, address, gas, kexit_info) -> (cold_access, address, gas, kexit_info, 0) diff --git a/evm/src/cpu/kernel/asm/core/intrinsic_gas.asm b/evm/src/cpu/kernel/asm/core/intrinsic_gas.asm index 87737302..bb7a21b5 100644 --- a/evm/src/cpu/kernel/asm/core/intrinsic_gas.asm +++ b/evm/src/cpu/kernel/asm/core/intrinsic_gas.asm @@ -65,12 +65,11 @@ count_zeros_finish: PUSH @GAS_TRANSACTION // stack: gas_txn, gas_creation, gas_txndata, retdest - // TODO: Add num_access_list_addresses * GAS_ACCESSLISTADDRESS - // TODO: Add num_access_list_slots * GAS_ACCESSLISTSTORAGE - ADD ADD // stack: total_gas, retdest + %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_DATA_COST) + ADD SWAP1 JUMP diff --git a/evm/src/cpu/kernel/asm/core/process_txn.asm b/evm/src/cpu/kernel/asm/core/process_txn.asm index 7a044475..022c1882 100644 --- a/evm/src/cpu/kernel/asm/core/process_txn.asm +++ b/evm/src/cpu/kernel/asm/core/process_txn.asm @@ -77,6 +77,11 @@ global increment_sender_nonce: %mload_txn_field(@TXN_FIELD_ORIGIN) %increment_nonce +// EIP-3651 +global warm_coinbase: + %mload_global_metadata(@GLOBAL_METADATA_BLOCK_BENEFICIARY) + %insert_accessed_addresses_no_return + global process_based_on_type: %is_contract_creation %jumpi(process_contract_creation_txn) diff --git a/evm/src/cpu/kernel/asm/transactions/common_decoding.asm b/evm/src/cpu/kernel/asm/transactions/common_decoding.asm index 71440d1c..07d19f47 100644 --- a/evm/src/cpu/kernel/asm/transactions/common_decoding.asm +++ b/evm/src/cpu/kernel/asm/transactions/common_decoding.asm @@ -105,10 +105,15 @@ %macro decode_and_store_access_list // stack: pos + DUP1 %mstore_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START) %decode_rlp_list_len - %stack (pos, len) -> (len, pos) - %jumpi(todo_access_lists_not_supported_yet) + %stack (pos, len) -> (len, len, pos, %%after) + %jumpi(decode_and_store_access_list) + // stack: len, pos, %%after + POP SWAP1 POP // stack: pos + %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START) DUP2 SUB %mstore_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) +%%after: %endmacro %macro decode_and_store_y_parity @@ -135,5 +140,96 @@ // stack: pos %endmacro -global todo_access_lists_not_supported_yet: - PANIC + +// The access list is of the form `[[{20 bytes}, [{32 bytes}...]]...]`. +global decode_and_store_access_list: + // stack: len, pos + DUP2 ADD + // stack: end_pos, pos + // Store the RLP length. + %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START) DUP2 SUB %mstore_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) + SWAP1 +decode_and_store_access_list_loop: + // stack: pos, end_pos + DUP2 DUP2 EQ %jumpi(decode_and_store_access_list_finish) + // stack: pos, end_pos + %decode_rlp_list_len // Should be a list `[{20 bytes}, [{32 bytes}...]]` + // stack: pos, internal_len, end_pos + SWAP1 POP // We don't need the length of this list. + // stack: pos, end_pos + %decode_rlp_scalar // Address // TODO: Should panic when address is not 20 bytes? + // stack: pos, addr, end_pos + SWAP1 + // stack: addr, pos, end_pos + DUP1 %insert_accessed_addresses_no_return + // stack: addr, pos, end_pos + %add_address_cost + // stack: addr, pos, end_pos + SWAP1 + // stack: pos, addr, end_pos + %decode_rlp_list_len // Should be a list of storage keys `[{32 bytes}...]` + // stack: pos, sk_len, addr, end_pos + SWAP1 DUP2 ADD + // stack: sk_end_pos, pos, addr, end_pos + SWAP1 + // stack: pos, sk_end_pos, addr, end_pos +sk_loop: + DUP2 DUP2 EQ %jumpi(end_sk) + // stack: pos, sk_end_pos, addr, end_pos + %decode_rlp_scalar // Storage key // TODO: Should panic when key is not 32 bytes? + %stack (pos, key, sk_end_pos, addr, end_pos) -> + (addr, key, sk_loop_contd, pos, sk_end_pos, addr, end_pos) + %jump(insert_accessed_storage_keys_with_original_value) +sk_loop_contd: + // stack: pos, sk_end_pos, addr, end_pos + %add_storage_key_cost + %jump(sk_loop) +end_sk: + %stack (pos, sk_end_pos, addr, end_pos) -> (pos, end_pos) + %jump(decode_and_store_access_list_loop) +decode_and_store_access_list_finish: + %stack (pos, end_pos, retdest) -> (retdest, pos) + JUMP + +%macro add_address_cost + %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_DATA_COST) + %add_const(@GAS_ACCESSLISTADDRESS) + %mstore_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_DATA_COST) +%endmacro + +%macro add_storage_key_cost + %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_DATA_COST) + %add_const(@GAS_ACCESSLISTSTORAGE) + %mstore_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_DATA_COST) +%endmacro + +insert_accessed_storage_keys_with_original_value: + %stack (addr, key, retdest) -> (key, addr, after_read, addr, key, retdest) + %jump(sload_with_addr) +after_read: + %stack (value, addr, key, retdest) -> ( addr, key, value, retdest) + %insert_accessed_storage_keys + %pop2 + JUMP + + +sload_with_addr: + %stack (slot, addr) -> (slot, addr, after_storage_read) + %slot_to_storage_key + // stack: storage_key, addr, after_storage_read + PUSH 64 // storage_key has 64 nibbles + %stack (n64, storage_key, addr, after_storage_read) -> (addr, n64, storage_key, after_storage_read) + %mpt_read_state_trie + // stack: account_ptr, 64, storage_key, after_storage_read + DUP1 ISZERO %jumpi(ret_zero) // TODO: Fix this. This should never happen. + // stack: account_ptr, 64, storage_key, after_storage_read + %add_const(2) + // stack: storage_root_ptr_ptr + %mload_trie_data + // stack: storage_root_ptr, 64, storage_key, after_storage_read + %jump(mpt_read) + +ret_zero: + // stack: account_ptr, 64, storage_key, after_storage_read, retdest + %pop4 + PUSH 0 SWAP1 JUMP diff --git a/evm/src/cpu/kernel/asm/transactions/type_0.asm b/evm/src/cpu/kernel/asm/transactions/type_0.asm index d00b10d4..65836086 100644 --- a/evm/src/cpu/kernel/asm/transactions/type_0.asm +++ b/evm/src/cpu/kernel/asm/transactions/type_0.asm @@ -102,9 +102,16 @@ type_0_compute_signed_data: // stack: rlp_pos, rlp_start, retdest %mload_txn_field(@TXN_FIELD_TO) + DUP1 %jumpi(nonzero_to) + // stack: to, rlp_pos, rlp_start, retdest + SWAP1 %encode_rlp_scalar + %jump(after_to) +nonzero_to: + // stack: to, rlp_pos, rlp_start, retdest SWAP1 %encode_rlp_160 // stack: rlp_pos, rlp_start, retdest +after_to: %mload_txn_field(@TXN_FIELD_VALUE) SWAP1 %encode_rlp_scalar // stack: rlp_pos, rlp_start, retdest diff --git a/evm/src/cpu/kernel/asm/transactions/type_1.asm b/evm/src/cpu/kernel/asm/transactions/type_1.asm index fbd934d9..9d1b4a6d 100644 --- a/evm/src/cpu/kernel/asm/transactions/type_1.asm +++ b/evm/src/cpu/kernel/asm/transactions/type_1.asm @@ -31,6 +31,109 @@ global process_type_1_txn: POP // stack: retdest - // TODO: Check signature. +// From EIP-2930: +// The signatureYParity, signatureR, signatureS elements of this transaction represent a secp256k1 signature +// over keccak256(0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList])). +type_1_compute_signed_data: + %alloc_rlp_block + // stack: rlp_start, retdest + %mload_txn_field(@TXN_FIELD_CHAIN_ID) + // stack: chain_id, rlp_start, retdest + DUP2 + // stack: rlp_pos, chain_id, rlp_start, retdest + %encode_rlp_scalar + // stack: rlp_pos, rlp_start, retdest + %mload_txn_field(@TXN_FIELD_NONCE) + SWAP1 %encode_rlp_scalar + // stack: rlp_pos, rlp_start, retdest + + %mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) + SWAP1 %encode_rlp_scalar + // stack: rlp_pos, rlp_start, retdest + + %mload_txn_field(@TXN_FIELD_GAS_LIMIT) + SWAP1 %encode_rlp_scalar + // stack: rlp_pos, rlp_start, retdest + + %mload_txn_field(@TXN_FIELD_TO) + DUP1 %jumpi(nonzero_to) + // stack: to, rlp_pos, rlp_start, retdest + SWAP1 %encode_rlp_scalar + %jump(after_to) +nonzero_to: + // stack: to, rlp_pos, rlp_start, retdest + SWAP1 %encode_rlp_160 + // stack: rlp_pos, rlp_start, retdest + +after_to: + %mload_txn_field(@TXN_FIELD_VALUE) + SWAP1 %encode_rlp_scalar + // stack: rlp_pos, rlp_start, retdest + + // Encode txn data. + %mload_txn_field(@TXN_FIELD_DATA_LEN) + PUSH 0 // ADDR.virt + PUSH @SEGMENT_TXN_DATA + PUSH 0 // ADDR.context + // stack: ADDR: 3, len, rlp_pos, rlp_start, retdest + PUSH after_serializing_txn_data + // stack: after_serializing_txn_data, ADDR: 3, len, rlp_pos, rlp_start, retdest + SWAP5 + // stack: rlp_pos, ADDR: 3, 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_pos, rlp_start, retdest) -> + ( + 0, @SEGMENT_RLP_RAW, rlp_pos, + 0, @SEGMENT_RLP_RAW, al_start, + al_len, + after_serializing_access_list, + rlp_pos, rlp_start, retdest) + %jump(memcpy) +after_serializing_access_list: + // stack: rlp_pos, rlp_start, retdest + %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) ADD + // stack: rlp_pos, rlp_start, retdest + %prepend_rlp_list_prefix + // stack: prefix_start_pos, rlp_len, retdest + + // Store a `1` in front of the RLP + %decrement + %stack (pos) -> (0, @SEGMENT_RLP_RAW, pos, 1, pos) + MSTORE_GENERAL + // stack: pos, rlp_len, retdest + + // Hash the RLP + the leading `1` + SWAP1 %increment SWAP1 + PUSH @SEGMENT_RLP_RAW + PUSH 0 // context + // stack: ADDR: 3, 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) diff --git a/evm/src/cpu/kernel/constants/global_metadata.rs b/evm/src/cpu/kernel/constants/global_metadata.rs index 4822c3d3..2846b664 100644 --- a/evm/src/cpu/kernel/constants/global_metadata.rs +++ b/evm/src/cpu/kernel/constants/global_metadata.rs @@ -59,10 +59,16 @@ pub(crate) enum GlobalMetadata { /// Current checkpoint. CurrentCheckpoint = 28, TouchedAddressesLen = 29, + // Gas cost for the access list in type-1 txns. See EIP-2930. + AccessListDataCost = 30, + // Start of the access list in the RLP for type-1 txns. + AccessListRlpStart = 31, + // Length of the access list in the RLP for type-1 txns. + AccessListRlpLen = 32, } impl GlobalMetadata { - pub(crate) const COUNT: usize = 29; + pub(crate) const COUNT: usize = 32; pub(crate) fn all() -> [Self; Self::COUNT] { [ @@ -95,6 +101,9 @@ impl GlobalMetadata { Self::JournalDataLen, Self::CurrentCheckpoint, Self::TouchedAddressesLen, + Self::AccessListDataCost, + Self::AccessListRlpStart, + Self::AccessListRlpLen, ] } @@ -130,6 +139,9 @@ impl GlobalMetadata { Self::JournalDataLen => "GLOBAL_METADATA_JOURNAL_DATA_LEN", Self::CurrentCheckpoint => "GLOBAL_METADATA_CURRENT_CHECKPOINT", Self::TouchedAddressesLen => "GLOBAL_METADATA_TOUCHED_ADDRESSES_LEN", + Self::AccessListDataCost => "GLOBAL_METADATA_ACCESS_LIST_DATA_COST", + Self::AccessListRlpStart => "GLOBAL_METADATA_ACCESS_LIST_RLP_START", + Self::AccessListRlpLen => "GLOBAL_METADATA_ACCESS_LIST_RLP_LEN", } } }