Selfdestruct gas and set (#947)

* Add new segment and global metadata

* Insert into self-destruct set

* Implement sys_selfdestruct

* PR feedback

* Fix stack underflow

* Forgot that NOT 1 ≠ 0. Added %not_bit macro for that.
This commit is contained in:
wborgeaud 2023-03-31 11:13:36 +02:00 committed by GitHub
parent 786a71d678
commit 2ca00a9ad4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 159 additions and 10 deletions

View File

@ -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"),

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

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

View File

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