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,
         }
     }
 }