From 1d8a8416a8146675bbe639157554bcd8c6780620 Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina <hamy.ratoanina@toposware.com> Date: Tue, 7 Nov 2023 10:15:59 -0500 Subject: [PATCH] Implement EIP-6780 (new SELFDESTRUCT) (#1327) * Implement EIP-6780 (new SELFDESTRUCT) * Fix balance transfer logic * Apply comment --- .../asm/core/create_contract_account.asm | 19 ++++- .../cpu/kernel/asm/core/selfdestruct_list.asm | 8 +- evm/src/cpu/kernel/asm/core/terminate.asm | 74 ++++++++++++++++--- evm/src/cpu/kernel/asm/core/transfer.asm | 4 +- .../kernel/asm/journal/account_created.asm | 18 ++++- .../cpu/kernel/constants/global_metadata.rs | 8 +- evm/src/memory/segments.rs | 7 +- 7 files changed, 116 insertions(+), 22 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/create_contract_account.asm b/evm/src/cpu/kernel/asm/core/create_contract_account.asm index b45d45ca5c..051540e173 100644 --- a/evm/src/cpu/kernel/asm/core/create_contract_account.asm +++ b/evm/src/cpu/kernel/asm/core/create_contract_account.asm @@ -27,7 +27,12 @@ %%add_account: // stack: existing_balance, address - DUP2 %journal_add_account_created + DUP2 PUSH 1 + // stack: is_contract, address, existing_balance, addr + %journal_add_account_created + // stack: existing_balance, addr + DUP2 + %append_created_contracts %%do_insert: // stack: new_acct_value, address // Write the new account's data to MPT data, and get a pointer to it. @@ -60,3 +65,15 @@ %%end: // stack: status %endmacro + +%macro append_created_contracts + // stack: address + %mload_global_metadata(@GLOBAL_METADATA_CREATED_CONTRACTS_LEN) + // stack: nb_created_contracts, address + SWAP1 DUP2 + // stack: nb_created_contracts, address, nb_created_contracts + %mstore_kernel(@SEGMENT_CREATED_CONTRACTS) + // stack: nb_created_contracts + %increment + %mstore_global_metadata(@GLOBAL_METADATA_CREATED_CONTRACTS_LEN) +%endmacro diff --git a/evm/src/cpu/kernel/asm/core/selfdestruct_list.asm b/evm/src/cpu/kernel/asm/core/selfdestruct_list.asm index 5084541aa2..b3903f71a0 100644 --- a/evm/src/cpu/kernel/asm/core/selfdestruct_list.asm +++ b/evm/src/cpu/kernel/asm/core/selfdestruct_list.asm @@ -13,7 +13,7 @@ %endmacro /// Remove one occurrence of the address from the list. -/// Panics if the address is not in the list. +/// No effect if the address is not in the list. global remove_selfdestruct_list: // stack: addr, retdest %mload_global_metadata(@GLOBAL_METADATA_SELFDESTRUCT_LIST_LEN) @@ -21,7 +21,7 @@ global remove_selfdestruct_list: PUSH 0 remove_selfdestruct_list_loop: %stack (i, len, addr, retdest) -> (i, len, i, len, addr, retdest) - EQ %jumpi(panic) + EQ %jumpi(remove_selfdestruct_not_found) // stack: i, len, addr, retdest DUP1 %mload_kernel(@SEGMENT_SELFDESTRUCT_LIST) // stack: loaded_addr, i, len, addr, retdest @@ -40,6 +40,10 @@ remove_selfdestruct_list_found: SWAP1 %mstore_kernel(@SEGMENT_SELFDESTRUCT_LIST) // Store the last address at the position of the removed address. JUMP +remove_selfdestruct_not_found: + // stack: i, len, addr, retdest + %pop3 + JUMP global delete_all_selfdestructed_addresses: // stack: retdest diff --git a/evm/src/cpu/kernel/asm/core/terminate.asm b/evm/src/cpu/kernel/asm/core/terminate.asm index 7bb7842fe3..6811234a9a 100644 --- a/evm/src/cpu/kernel/asm/core/terminate.asm +++ b/evm/src/cpu/kernel/asm/core/terminate.asm @@ -81,10 +81,6 @@ global sys_selfdestruct: %charge_gas %stack (kexit_info, balance, address, recipient) -> (balance, address, recipient, kexit_info) - // Insert address into the selfdestruct set. - // stack: balance, address, recipient, kexit_info - DUP2 %insert_selfdestruct_list - // Set the balance of the address to 0. // stack: balance, address, recipient, kexit_info PUSH 0 @@ -95,14 +91,17 @@ global sys_selfdestruct: // stack: balance_ptr, 0, balance, address, recipient, kexit_info %mstore_trie_data - %stack (balance, address, recipient, kexit_info) -> - (address, recipient, address, recipient, balance, kexit_info) - // If the recipient is the same as the address, then we're done. - // Otherwise, send the balance to the recipient. - // stack: address, recipient, address, recipient, balance, kexit_info - EQ %jumpi(sys_selfdestruct_journal_add) - %stack (address, recipient, balance, kexit_info) -> (recipient, balance, address, recipient, balance, kexit_info) + // EIP-6780: insert address into the selfdestruct set only if contract has been created + // during the current transaction. + // stack: balance, address, recipient, kexit_info + DUP2 %contract_just_created + // stack: is_just_created, balance, address, recipient, kexit_info + %jumpi(sys_selfdestruct_just_created) + + // Send the balance to the recipient. + %stack (balance, address, recipient, kexit_info) -> + (recipient, balance, address, recipient, balance, kexit_info) %add_eth sys_selfdestruct_journal_add: @@ -115,6 +114,21 @@ sys_selfdestruct_journal_add: PUSH 1 // success %jump(terminate_common) +sys_selfdestruct_just_created: + // Send funds to beneficiary only if the recipient isn't the same as the address. + %stack (balance, address, recipient, kexit_info) -> (address, recipient, balance, address, recipient, balance, kexit_info) + EQ ISZERO + // stack: address ≠ recipient, balance, address, recipient, balance, kexit_info + MUL + // stack: maybe_balance, address, recipient, balance, kexit_info + DUP3 + // stack: recipient, maybe_balance, address, recipient, balance, kexit_info + %add_eth + // stack: address, recipient, balance, kexit_info + DUP1 + %insert_selfdestruct_list + %jump(sys_selfdestruct_journal_add) + global sys_revert: // stack: kexit_info, offset, size %stack (kexit_info, offset, size) -> (offset, size, kexit_info, offset, size) @@ -209,3 +223,41 @@ global terminate_common: // stack: parent_pc, success, leftover_gas JUMP + + + + +// Returns 1 if the address is present in SEGMENT_CREATED_CONTRACTS, meaning that it has been +// created during this transaction. Returns 0 otherwise. +// Pre stack: addr +// Post stack: is_just_created +%macro contract_just_created + // stack: addr + %mload_global_metadata(@GLOBAL_METADATA_CREATED_CONTRACTS_LEN) + // stack: nb_created_contracts, addr + PUSH 0 +%%contract_just_created_loop: + %stack (i, nb_created_contracts, addr) -> (i, nb_created_contracts, i, nb_created_contracts, addr) + EQ %jumpi(%%contract_just_created_false) + // stack: i, nb_created_contracts, addr + DUP1 %mload_kernel(@SEGMENT_CREATED_CONTRACTS) + // stack: addr_created_contract, i, nb_created_contracts, addr + DUP4 + // stack: addr, addr_created_contract, i, nb_created_contracts, addr + EQ %jumpi(%%contract_just_created_true) + // stack: i, nb_created_contracts, addr + %increment + %jump(%%contract_just_created_loop) +%%contract_just_created_true: + // stack: i, nb_created_contracts, addr + %pop3 + PUSH 1 + // stack: 1 + %jump(%%after) +%%contract_just_created_false: + // stack: i, nb_created_contracts, addr + %pop3 + PUSH 0 + // stack: 0 +%%after: +%endmacro diff --git a/evm/src/cpu/kernel/asm/core/transfer.asm b/evm/src/cpu/kernel/asm/core/transfer.asm index 0517cf3a8f..8a5aad9fef 100644 --- a/evm/src/cpu/kernel/asm/core/transfer.asm +++ b/evm/src/cpu/kernel/asm/core/transfer.asm @@ -85,7 +85,9 @@ global add_eth_new_account: POP // stack: addr, amount, retdest DUP2 ISZERO %jumpi(add_eth_new_account_zero) - DUP1 %journal_add_account_created + DUP1 PUSH 0 + // stack: is_eoa, addr, addr, amount, retdest + %journal_add_account_created %get_trie_data_size // pointer to new account we're about to create // stack: new_account_ptr, addr, amount, retdest SWAP2 diff --git a/evm/src/cpu/kernel/asm/journal/account_created.asm b/evm/src/cpu/kernel/asm/journal/account_created.asm index 4748d5cbcb..2fd4c15fae 100644 --- a/evm/src/cpu/kernel/asm/journal/account_created.asm +++ b/evm/src/cpu/kernel/asm/journal/account_created.asm @@ -1,13 +1,23 @@ -// struct AccountCreated { address } +// struct AccountCreated { account_type, address } +// account_type is 0 for an EOA, 1 for a contract. %macro journal_add_account_created - %journal_add_1(@JOURNAL_ENTRY_ACCOUNT_CREATED) + %journal_add_2(@JOURNAL_ENTRY_ACCOUNT_CREATED) %endmacro global revert_account_created: // stack: entry_type, ptr, retdest POP - %journal_load_1 - // stack: address, retdest + %journal_load_2 + // stack: account_type, address, retdest + %jumpi(decrement_created_contracts_len) + +revert_account_finish: %delete_account JUMP + +decrement_created_contracts_len: + %mload_global_metadata(@GLOBAL_METADATA_CREATED_CONTRACTS_LEN) + %decrement + %mstore_global_metadata(@GLOBAL_METADATA_CREATED_CONTRACTS_LEN) + %jump(revert_account_finish) diff --git a/evm/src/cpu/kernel/constants/global_metadata.rs b/evm/src/cpu/kernel/constants/global_metadata.rs index b3d9600d57..77e64fe25d 100644 --- a/evm/src/cpu/kernel/constants/global_metadata.rs +++ b/evm/src/cpu/kernel/constants/global_metadata.rs @@ -79,17 +79,19 @@ pub(crate) enum GlobalMetadata { ContractCreation = 38, IsPrecompileFromEoa = 39, CallStackDepth = 40, - /// Transaction logs list length + /// Transaction logs list length. LogsLen = 41, LogsDataLen = 42, LogsPayloadLen = 43, TxnNumberBefore = 44, TxnNumberAfter = 45, BlockBlobBaseFee = 46, + /// Number of created contracts during the current transaction. + CreatedContractsLen = 47, } impl GlobalMetadata { - pub(crate) const COUNT: usize = 47; + pub(crate) const COUNT: usize = 48; pub(crate) fn all() -> [Self; Self::COUNT] { [ @@ -140,6 +142,7 @@ impl GlobalMetadata { Self::TxnNumberBefore, Self::TxnNumberAfter, Self::BlockBlobBaseFee, + Self::CreatedContractsLen, ] } @@ -193,6 +196,7 @@ impl GlobalMetadata { Self::TxnNumberBefore => "GLOBAL_METADATA_TXN_NUMBER_BEFORE", Self::TxnNumberAfter => "GLOBAL_METADATA_TXN_NUMBER_AFTER", Self::BlockBlobBaseFee => "GLOBAL_METADATA_BLOCK_BLOB_BASE_FEE", + Self::CreatedContractsLen => "GLOBAL_METADATA_CREATED_CONTRACTS_LEN", } } } diff --git a/evm/src/memory/segments.rs b/evm/src/memory/segments.rs index afaaf503b0..ede0ad5513 100644 --- a/evm/src/memory/segments.rs +++ b/evm/src/memory/segments.rs @@ -70,10 +70,12 @@ pub enum Segment { ContextCheckpoints = 35, /// List of 256 previous block hashes. BlockHashes = 36, + /// List of contracts which have been created during the current transaction. + CreatedContracts = 37, } impl Segment { - pub(crate) const COUNT: usize = 37; + pub(crate) const COUNT: usize = 38; pub(crate) fn all() -> [Self; Self::COUNT] { [ @@ -114,6 +116,7 @@ impl Segment { Self::TouchedAddresses, Self::ContextCheckpoints, Self::BlockHashes, + Self::CreatedContracts, ] } @@ -157,6 +160,7 @@ impl Segment { Segment::TouchedAddresses => "SEGMENT_TOUCHED_ADDRESSES", Segment::ContextCheckpoints => "SEGMENT_CONTEXT_CHECKPOINTS", Segment::BlockHashes => "SEGMENT_BLOCK_HASHES", + Segment::CreatedContracts => "SEGMENT_CREATED_CONTRACTS", } } @@ -200,6 +204,7 @@ impl Segment { Segment::TouchedAddresses => 256, Segment::ContextCheckpoints => 256, Segment::BlockHashes => 256, + Segment::CreatedContracts => 256, } } }