diff --git a/evm/src/cpu/kernel/aggregator.rs b/evm/src/cpu/kernel/aggregator.rs index 0b7233e0..79d22f87 100644 --- a/evm/src/cpu/kernel/aggregator.rs +++ b/evm/src/cpu/kernel/aggregator.rs @@ -37,6 +37,7 @@ pub(crate) fn combined_kernel() -> Kernel { include_str!("asm/core/transfer.asm"), include_str!("asm/core/util.asm"), include_str!("asm/core/access_lists.asm"), + include_str!("asm/core/selfdestruct_set.asm"), include_str!("asm/curve/bls381/util.asm"), include_str!("asm/curve/bn254/curve_arithmetic/constants.asm"), include_str!("asm/curve/bn254/curve_arithmetic/curve_add.asm"), diff --git a/evm/src/cpu/kernel/asm/core/selfdestruct_set.asm b/evm/src/cpu/kernel/asm/core/selfdestruct_set.asm new file mode 100644 index 00000000..ad62c6f1 --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/selfdestruct_set.asm @@ -0,0 +1,43 @@ +/// Self-destruct set. +/// Essentially the same code as in `access_lists.asm`, with the exception that the insert function doesn't return anything. +/// TODO: Would it make sense to merge this with `access_lists.asm`? +/// TODO: Look into using a more efficient data structure. + +%macro insert_selfdestruct_set + %stack (addr) -> (addr, %%after) + %jump(insert_selfdestruct_set) +%%after: + // stack: (empty) +%endmacro + +/// Inserts the address into the self-destruct set if it is not already present. +global insert_selfdestruct_set: + // stack: addr, retdest + %mload_global_metadata(@GLOBAL_METADATA_SELFDESTRUCT_SET_LEN) + // stack: len, addr, retdest + PUSH 0 +insert_selfdestruct_set_loop: + %stack (i, len, addr, retdest) -> (i, len, i, len, addr, retdest) + EQ %jumpi(insert_address) + // stack: i, len, addr, retdest + DUP1 %mload_kernel(@SEGMENT_SELFDESTRUCT_SET) + // stack: loaded_addr, i, len, addr, retdest + DUP4 + // stack: addr, loaded_addr, i, len, addr, retdest + EQ %jumpi(insert_address_found) + // stack: i, len, addr, retdest + %increment + %jump(insert_selfdestruct_set_loop) + +insert_address: + %stack (i, len, addr, retdest) -> (i, addr, len, retdest) + %mstore_kernel(@SEGMENT_SELFDESTRUCT_SET) // Store new address at the end of the array. + // stack: len, retdest + %increment + %mstore_global_metadata(@GLOBAL_METADATA_SELFDESTRUCT_SET_LEN) // Store new length. + JUMP + +insert_address_found: + // stack: i, len, addr, retdest + %pop3 + JUMP diff --git a/evm/src/cpu/kernel/asm/core/terminate.asm b/evm/src/cpu/kernel/asm/core/terminate.asm index e9508bd7..27758e6a 100644 --- a/evm/src/cpu/kernel/asm/core/terminate.asm +++ b/evm/src/cpu/kernel/asm/core/terminate.asm @@ -22,17 +22,60 @@ global sys_return: %jump(terminate_common) global sys_selfdestruct: - // stack: kexit_info, address + // stack: kexit_info, recipient SWAP1 %u256_to_addr - DUP1 %insert_accessed_addresses_no_return // TODO: Use return value in gas calculation. - // stack: address, kexit_info - POP // TODO: Transfer balance to address. + %address DUP1 %balance + + // Insert recipient into the accessed addresses list. + // stack: balance, address, recipient, kexit_info + DUP3 %insert_accessed_addresses + + // Compute gas. + // stack: cold_access, balance, address, recipient, kexit_info + %mul_const(@GAS_COLDACCOUNTACCESS) + DUP2 + // stack: balance, gas_coldaccess, balance, address, recipient, kexit_info + ISZERO %not_bit + // stack: balance!=0, gas_coldaccess, balance, address, recipient, kexit_info + DUP5 %is_dead MUL %mul_const(@GAS_NEWACCOUNT) + // stack: gas_newaccount, gas_coldaccess, balance, address, recipient, kexit_info + ADD %add_const(@GAS_SELFDESTRUCT) + %stack (gas, balance, address, recipient, kexit_info) -> (gas, kexit_info, balance, address, recipient) + %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_set + + // Set the balance of the address to 0. + // stack: balance, address, recipient, kexit_info + PUSH 0 + // stack: 0, balance, address, recipient, kexit_info + DUP3 %mpt_read_state_trie + // stack: account_ptr, 0, balance, address, recipient, kexit_info + %add_const(1) + // stack: balance_ptr, 0, balance, address, recipient, kexit_info + %mstore_trie_data // TODO: This should be a copy-on-write operation. + + // If the recipient is the same as the address, then we're done. + // Otherwise, send the balance to the recipient. + %stack (balance, address, recipient, kexit_info) -> (address, recipient, recipient, balance, kexit_info) + EQ %jumpi(sys_selfdestruct_same_addr) + // stack: recipient, balance, kexit_info + %add_eth + // stack: kexit_info - // TODO: Add address to the access list. - %charge_gas_const(@GAS_SELFDESTRUCT) %leftover_gas // stack: leftover_gas - // TODO: Destroy account. + PUSH 1 // success + %jump(terminate_common) + +sys_selfdestruct_same_addr: + // stack: recipient, balance, kexit_info + %pop2 + %leftover_gas + // stack: leftover_gas PUSH 1 // success %jump(terminate_common) diff --git a/evm/src/cpu/kernel/asm/core/transfer.asm b/evm/src/cpu/kernel/asm/core/transfer.asm index c001e726..9ab71627 100644 --- a/evm/src/cpu/kernel/asm/core/transfer.asm +++ b/evm/src/cpu/kernel/asm/core/transfer.asm @@ -81,9 +81,10 @@ global add_eth: // stack: retdest JUMP global add_eth_new_account: - // TODO: Skip creation if amount == 0? // stack: null_account_ptr, addr, amount, retdest POP + // stack: addr, amount, retdest + DUP2 ISZERO %jumpi(add_eth_new_account_zero) %get_trie_data_size // pointer to new account we're about to create // stack: new_account_ptr, addr, amount, retdest SWAP2 @@ -98,6 +99,10 @@ global add_eth_new_account: // stack: key, new_account_ptr, retdest %jump(mpt_insert_state_trie) +add_eth_new_account_zero: + // stack: addr, amount, retdest + %pop2 JUMP + // Convenience macro to call add_eth and return where we left off. %macro add_eth %stack (addr, amount) -> (addr, amount, %%after) diff --git a/evm/src/cpu/kernel/asm/core/util.asm b/evm/src/cpu/kernel/asm/core/util.asm index dfacf1a2..a183b120 100644 --- a/evm/src/cpu/kernel/asm/core/util.asm +++ b/evm/src/cpu/kernel/asm/core/util.asm @@ -30,3 +30,43 @@ // If there is no "to" field, then this is a contract creation. // stack: to == 0 %endmacro + +// Returns 1 if the account is non-existent, 0 otherwise. +%macro is_non_existent + // stack: addr + %mpt_read_state_trie + ISZERO +%endmacro + +// Returns 1 if the account is empty, 0 otherwise. +%macro is_empty + // stack: addr + %mpt_read_state_trie + // stack: account_ptr + DUP1 ISZERO %jumpi(%%false) + // stack: account_ptr + DUP1 %mload_trie_data + // stack: nonce, account_ptr + ISZERO %not_bit %jumpi(%%false) + %increment DUP1 %mload_trie_data + // stack: balance, balance_ptr + ISZERO %not_bit %jumpi(%%false) + %add_const(2) %mload_trie_data + // stack: code_hash + PUSH @EMPTY_STRING_HASH + EQ + %jump(%%after) +%%false: + // stack: account_ptr + POP + PUSH 0 +%%after: +%endmacro + +// Returns 1 if the account is dead (i.e., empty or non-existent), 0 otherwise. +%macro is_dead + // stack: addr + DUP1 %is_non_existent + SWAP1 %is_empty + ADD // OR +%endmacro \ No newline at end of file diff --git a/evm/src/cpu/kernel/asm/util/basic_macros.asm b/evm/src/cpu/kernel/asm/util/basic_macros.asm index f85b7792..0edd0367 100644 --- a/evm/src/cpu/kernel/asm/util/basic_macros.asm +++ b/evm/src/cpu/kernel/asm/util/basic_macros.asm @@ -345,3 +345,11 @@ // stack: x %mod_const(0x10000000000000000000000000000000000000000) // 2^160 %endmacro + +%macro not_bit + // stack: b + PUSH 1 + // stack: 1, b + SUB + // stack: 1 - b +%endmacro diff --git a/evm/src/cpu/kernel/constants/global_metadata.rs b/evm/src/cpu/kernel/constants/global_metadata.rs index f04c06e1..a9e1a9fd 100644 --- a/evm/src/cpu/kernel/constants/global_metadata.rs +++ b/evm/src/cpu/kernel/constants/global_metadata.rs @@ -49,10 +49,12 @@ pub(crate) enum GlobalMetadata { AccessedAddressesLen = 23, /// Length of the storage keys access list. AccessedStorageKeysLen = 24, + /// Length of the self-destruct set. + SelfDestructSetLen = 25, } impl GlobalMetadata { - pub(crate) const COUNT: usize = 24; + pub(crate) const COUNT: usize = 25; pub(crate) fn all() -> [Self; Self::COUNT] { [ @@ -80,6 +82,7 @@ impl GlobalMetadata { Self::RefundCounter, Self::AccessedAddressesLen, Self::AccessedStorageKeysLen, + Self::SelfDestructSetLen, ] } @@ -110,6 +113,7 @@ impl GlobalMetadata { Self::RefundCounter => "GLOBAL_METADATA_REFUND_COUNTER", Self::AccessedAddressesLen => "GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN", Self::AccessedStorageKeysLen => "GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN", + Self::SelfDestructSetLen => "GLOBAL_METADATA_SELFDESTRUCT_SET_LEN", } } } diff --git a/evm/src/memory/segments.rs b/evm/src/memory/segments.rs index 61c5a911..0b74550d 100644 --- a/evm/src/memory/segments.rs +++ b/evm/src/memory/segments.rs @@ -48,10 +48,12 @@ pub enum Segment { AccessedAddresses = 23, /// List of storage keys that have been accessed in the current transaction. AccessedStorageKeys = 24, + /// List of addresses that have called SELFDESTRUCT in the current transaction. + SelfDestructSet = 25, } impl Segment { - pub(crate) const COUNT: usize = 25; + pub(crate) const COUNT: usize = 26; pub(crate) fn all() -> [Self; Self::COUNT] { [ @@ -80,6 +82,7 @@ impl Segment { Self::BnPairing, Self::AccessedAddresses, Self::AccessedStorageKeys, + Self::SelfDestructSet, ] } @@ -111,6 +114,7 @@ impl Segment { Segment::BnPairing => "SEGMENT_KERNEL_BN_PAIRING", Segment::AccessedAddresses => "SEGMENT_ACCESSED_ADDRESSES", Segment::AccessedStorageKeys => "SEGMENT_ACCESSED_STORAGE_KEYS", + Segment::SelfDestructSet => "SEGMENT_SELFDESTRUCT_SET", } } @@ -142,6 +146,7 @@ impl Segment { Segment::BnPairing => 256, Segment::AccessedAddresses => 256, Segment::AccessedStorageKeys => 256, + Segment::SelfDestructSet => 256, } } }