mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-02 22:03:07 +00:00
Journal of state changes + state reversion (#1028)
* Add segments and global metadata * Add journal asm files * Start revert * Revert access lists * Revert balance transfer * Revert code change * Revert nonce change * Revert storage change * Checkpoints * Add some journal entries * Add some journal entries * Add some journal entries * Fix revert * Checkpoint in sys_call * Minor * PR feedback * More checkpoints * Fix checkpoint check * Minor * Checkpoints in precompiles * Storage change checkpoint * Add touched addresses * Add touched addresses revert * Add touched addresses journal events * Delete all empty touch addresses * Implement selfdestruct * Update aggregator.rs
This commit is contained in:
parent
74ba303255
commit
bfd6834dc2
@ -42,6 +42,7 @@ pub(crate) fn combined_kernel() -> Kernel {
|
||||
include_str!("asm/core/util.asm"),
|
||||
include_str!("asm/core/access_lists.asm"),
|
||||
include_str!("asm/core/selfdestruct_list.asm"),
|
||||
include_str!("asm/core/touched_addresses.asm"),
|
||||
include_str!("asm/core/precompiles/main.asm"),
|
||||
include_str!("asm/core/precompiles/ecrec.asm"),
|
||||
include_str!("asm/core/precompiles/sha256.asm"),
|
||||
@ -134,6 +135,16 @@ pub(crate) fn combined_kernel() -> Kernel {
|
||||
include_str!("asm/rlp/read_to_memory.asm"),
|
||||
include_str!("asm/shift.asm"),
|
||||
include_str!("asm/signed.asm"),
|
||||
include_str!("asm/journal/journal.asm"),
|
||||
include_str!("asm/journal/account_loaded.asm"),
|
||||
include_str!("asm/journal/account_destroyed.asm"),
|
||||
include_str!("asm/journal/account_touched.asm"),
|
||||
include_str!("asm/journal/balance_transfer.asm"),
|
||||
include_str!("asm/journal/nonce_change.asm"),
|
||||
include_str!("asm/journal/storage_change.asm"),
|
||||
include_str!("asm/journal/storage_loaded.asm"),
|
||||
include_str!("asm/journal/code_change.asm"),
|
||||
include_str!("asm/journal/revert.asm"),
|
||||
include_str!("asm/transactions/common_decoding.asm"),
|
||||
include_str!("asm/transactions/router.asm"),
|
||||
include_str!("asm/transactions/type_0.asm"),
|
||||
|
||||
@ -41,6 +41,7 @@ insert_accessed_addresses_loop:
|
||||
|
||||
insert_address:
|
||||
%stack (i, len, addr, retdest) -> (i, addr, len, retdest)
|
||||
DUP2 %journal_add_account_loaded // Add a journal entry for the loaded account.
|
||||
%mstore_kernel(@SEGMENT_ACCESSED_ADDRESSES) // Store new address at the end of the array.
|
||||
// stack: len, retdest
|
||||
%increment
|
||||
@ -52,6 +53,35 @@ insert_accessed_addresses_found:
|
||||
%stack (i, len, addr, retdest) -> (retdest, 0) // Return 0 to indicate that the address was already present.
|
||||
JUMP
|
||||
|
||||
/// Remove the address from the access list.
|
||||
/// Panics if the address is not in the access list.
|
||||
global remove_accessed_addresses:
|
||||
// stack: addr, retdest
|
||||
%mload_global_metadata(@GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN)
|
||||
// stack: len, addr, retdest
|
||||
PUSH 0
|
||||
remove_accessed_addresses_loop:
|
||||
%stack (i, len, addr, retdest) -> (i, len, i, len, addr, retdest)
|
||||
EQ %jumpi(panic)
|
||||
// stack: i, len, addr, retdest
|
||||
DUP1 %mload_kernel(@SEGMENT_ACCESSED_ADDRESSES)
|
||||
// stack: loaded_addr, i, len, addr, retdest
|
||||
DUP4
|
||||
// stack: addr, loaded_addr, i, len, addr, retdest
|
||||
EQ %jumpi(remove_accessed_addresses_found)
|
||||
// stack: i, len, addr, retdest
|
||||
%increment
|
||||
%jump(remove_accessed_addresses_loop)
|
||||
remove_accessed_addresses_found:
|
||||
%stack (i, len, addr, retdest) -> (len, 1, i, retdest)
|
||||
SUB DUP1 %mstore_global_metadata(@GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN) // Decrement the access list length.
|
||||
// stack: len-1, i, retdest
|
||||
%mload_kernel(@SEGMENT_ACCESSED_ADDRESSES) // Load the last address in the access list.
|
||||
// stack: last_addr, i, retdest
|
||||
SWAP1
|
||||
%mstore_kernel(@SEGMENT_ACCESSED_ADDRESSES) // Store the last address at the position of the removed address.
|
||||
JUMP
|
||||
|
||||
|
||||
%macro insert_accessed_storage_keys
|
||||
%stack (addr, key, value) -> (addr, key, value, %%after)
|
||||
@ -87,6 +117,8 @@ insert_accessed_storage_keys_loop:
|
||||
%jump(insert_accessed_storage_keys_loop)
|
||||
|
||||
insert_storage_key:
|
||||
// stack: i, len, addr, key, value, retdest
|
||||
DUP4 DUP4 %journal_add_storage_loaded // Add a journal entry for the loaded storage key.
|
||||
// stack: i, len, addr, key, value, retdest
|
||||
DUP1 %increment
|
||||
DUP1 %increment
|
||||
@ -106,3 +138,46 @@ insert_accessed_storage_keys_found:
|
||||
%mload_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS)
|
||||
%stack (original_value, len, addr, key, value, retdest) -> (retdest, 0, original_value) // Return 0 to indicate that the storage key was already present.
|
||||
JUMP
|
||||
|
||||
/// Remove the storage key and its value from the access list.
|
||||
/// Panics if the key is not in the list.
|
||||
global remove_accessed_storage_keys:
|
||||
// stack: addr, key, retdest
|
||||
%mload_global_metadata(@GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN)
|
||||
// stack: len, addr, key, retdest
|
||||
PUSH 0
|
||||
remove_accessed_storage_keys_loop:
|
||||
%stack (i, len, addr, key, retdest) -> (i, len, i, len, addr, key, retdest)
|
||||
EQ %jumpi(panic)
|
||||
// stack: i, len, addr, key, retdest
|
||||
DUP1 %increment %mload_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS)
|
||||
// stack: loaded_key, i, len, addr, key, retdest
|
||||
DUP2 %mload_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS)
|
||||
// stack: loaded_addr, loaded_key, i, len, addr, key, retdest
|
||||
DUP5 EQ
|
||||
// stack: loaded_addr==addr, loaded_key, i, len, addr, key, retdest
|
||||
SWAP1 DUP6 EQ
|
||||
// stack: loaded_key==key, loaded_addr==addr, i, len, addr, key, retdest
|
||||
MUL // AND
|
||||
%jumpi(remove_accessed_storage_keys_found)
|
||||
// stack: i, len, addr, key, retdest
|
||||
%add_const(3)
|
||||
%jump(remove_accessed_storage_keys_loop)
|
||||
|
||||
remove_accessed_storage_keys_found:
|
||||
%stack (i, len, addr, key, retdest) -> (len, 3, i, retdest)
|
||||
SUB DUP1 %mstore_global_metadata(@GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN) // Decrease the access list length.
|
||||
// stack: len-3, i, retdest
|
||||
DUP1 %add_const(2) %mload_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS)
|
||||
// stack: last_value, len-3, i, retdest
|
||||
DUP2 %add_const(1) %mload_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS)
|
||||
// stack: last_key, last_value, len-3, i, retdest
|
||||
DUP3 %mload_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS)
|
||||
// stack: last_addr, last_key, last_value, len-3, i, retdest
|
||||
DUP5 %mstore_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) // Move the last tuple to the position of the removed tuple.
|
||||
// stack: last_key, last_value, len-3, i, retdest
|
||||
DUP4 %add_const(1) %mstore_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS)
|
||||
// stack: last_value, len-3, i, retdest
|
||||
DUP3 %add_const(2) %mstore_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS)
|
||||
// stack: len-3, i, retdest
|
||||
%pop2 JUMP
|
||||
|
||||
@ -11,6 +11,8 @@ global sys_call:
|
||||
MUL // Cheaper than AND
|
||||
%jumpi(fault_exception)
|
||||
|
||||
%checkpoint %mstore_context_metadata(@CTX_METADATA_CHECKPOINT) // Checkpoint and store it in context metadata.
|
||||
|
||||
%stack (kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) ->
|
||||
(args_size, args_offset, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size)
|
||||
%checked_mem_expansion
|
||||
@ -21,6 +23,7 @@ global sys_call:
|
||||
SWAP2
|
||||
// stack: address, gas, kexit_info, value, args_offset, args_size, ret_offset, ret_size
|
||||
%u256_to_addr // Truncate to 160 bits
|
||||
DUP1 %insert_touched_addresses
|
||||
DUP1 %insert_accessed_addresses
|
||||
|
||||
%call_charge_gas(1, 1)
|
||||
@ -34,6 +37,7 @@ global sys_call:
|
||||
%copy_mem_to_calldata
|
||||
// stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size
|
||||
DUP5 DUP5 %address %transfer_eth %jumpi(panic) // TODO: Fix this panic.
|
||||
DUP5 DUP5 %address %journal_add_balance_transfer
|
||||
DUP3 %set_new_ctx_gas_limit
|
||||
%set_new_ctx_parent_pc(after_call_instruction)
|
||||
DUP9 DUP9 DUP4 DUP4 DUP8 // Duplicate address, new_ctx, kexit_info, ret_offset, and ret_size.
|
||||
@ -55,6 +59,8 @@ global sys_call:
|
||||
// Creates a new sub context as if calling itself, but with the code of the
|
||||
// given account. In particular the storage remains the same.
|
||||
global sys_callcode:
|
||||
%checkpoint %mstore_context_metadata(@CTX_METADATA_CHECKPOINT) // Checkpoint and store it in context metadata.
|
||||
|
||||
// stack: kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size
|
||||
%stack (kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) ->
|
||||
(args_size, args_offset, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size)
|
||||
@ -66,6 +72,7 @@ global sys_callcode:
|
||||
SWAP2
|
||||
// stack: address, gas, kexit_info, value, args_offset, args_size, ret_offset, ret_size
|
||||
%u256_to_addr // Truncate to 160 bits
|
||||
DUP1 %insert_touched_addresses
|
||||
DUP1 %insert_accessed_addresses
|
||||
|
||||
%call_charge_gas(1, 0)
|
||||
@ -103,6 +110,8 @@ global sys_callcode:
|
||||
// are CREATE, CREATE2, LOG0, LOG1, LOG2, LOG3, LOG4, SSTORE, SELFDESTRUCT and
|
||||
// CALL if the value sent is not 0.
|
||||
global sys_staticcall:
|
||||
%checkpoint %mstore_context_metadata(@CTX_METADATA_CHECKPOINT) // Checkpoint and store it in context metadata.
|
||||
|
||||
// stack: kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size
|
||||
%stack (kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) ->
|
||||
(args_size, args_offset, kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size)
|
||||
@ -114,6 +123,7 @@ global sys_staticcall:
|
||||
SWAP2
|
||||
// stack: address, gas, kexit_info, args_offset, args_size, ret_offset, ret_size
|
||||
%u256_to_addr // Truncate to 160 bits
|
||||
DUP1 %insert_touched_addresses
|
||||
DUP1 %insert_accessed_addresses
|
||||
|
||||
// Add a value of 0 to the stack. Slightly inefficient but that way we can reuse %call_charge_gas.
|
||||
@ -151,6 +161,8 @@ global sys_staticcall:
|
||||
// given account. In particular the storage, the current sender and the current
|
||||
// value remain the same.
|
||||
global sys_delegatecall:
|
||||
%checkpoint %mstore_context_metadata(@CTX_METADATA_CHECKPOINT) // Checkpoint and store it in context metadata.
|
||||
|
||||
// stack: kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size
|
||||
%stack (kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) ->
|
||||
(args_size, args_offset, kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size)
|
||||
@ -162,6 +174,7 @@ global sys_delegatecall:
|
||||
SWAP2
|
||||
// stack: address, gas, kexit_info, args_offset, args_size, ret_offset, ret_size
|
||||
%u256_to_addr // Truncate to 160 bits
|
||||
DUP1 %insert_touched_addresses
|
||||
DUP1 %insert_accessed_addresses
|
||||
|
||||
// Add a value of 0 to the stack. Slightly inefficient but that way we can reuse %call_charge_gas.
|
||||
@ -197,6 +210,8 @@ global sys_delegatecall:
|
||||
// We go here after any CALL type instruction (but not after the special call by the transaction originator).
|
||||
global after_call_instruction:
|
||||
// stack: success, leftover_gas, new_ctx, kexit_info, ret_offset, ret_size
|
||||
DUP1 ISZERO %jumpi(after_call_instruction_failed)
|
||||
after_call_instruction_contd:
|
||||
SWAP3
|
||||
// stack: kexit_info, leftover_gas, new_ctx, success, ret_offset, ret_size
|
||||
// Add the leftover gas into the appropriate bits of kexit_info.
|
||||
@ -207,6 +222,11 @@ global after_call_instruction:
|
||||
%copy_returndata_to_mem
|
||||
EXIT_KERNEL
|
||||
|
||||
after_call_instruction_failed:
|
||||
// stack: success, leftover_gas, new_ctx, kexit_info, ret_offset, ret_size
|
||||
%mload_context_metadata(@CTX_METADATA_CHECKPOINT) %revert_checkpoint
|
||||
%jump(after_call_instruction_contd)
|
||||
|
||||
// Set @CTX_METADATA_STATIC to 1. Note that there is no corresponding set_static_false routine
|
||||
// because it will already be 0 by default.
|
||||
%macro set_static_true
|
||||
@ -295,6 +315,7 @@ global after_call_instruction:
|
||||
// Switch to the new context and go to usermode with PC=0.
|
||||
DUP1 // new_ctx
|
||||
SET_CONTEXT
|
||||
%checkpoint %mstore_context_metadata(@CTX_METADATA_CHECKPOINT) // Checkpoint and store it in context metadata.
|
||||
PUSH 0 // jump dest
|
||||
EXIT_KERNEL
|
||||
// (Old context) stack: new_ctx
|
||||
|
||||
@ -91,6 +91,7 @@ global create_common:
|
||||
// stack: status, address, value, code_offset, code_len, kexit_info
|
||||
%jumpi(fault_exception)
|
||||
// stack: address, value, code_offset, code_len, kexit_info
|
||||
DUP2 DUP2 %address %journal_add_balance_transfer // Add journal entry for the balance transfer.
|
||||
|
||||
%create_context
|
||||
// stack: new_ctx, address, value, code_offset, code_len, kexit_info
|
||||
@ -192,10 +193,15 @@ after_constructor:
|
||||
// TODO: Should it be copy-on-write (with make_account_copy) instead of mutating the trie?
|
||||
global set_codehash:
|
||||
// stack: addr, codehash, retdest
|
||||
%mpt_read_state_trie
|
||||
// stack: account_ptr, codehash, retdest
|
||||
DUP1 %insert_touched_addresses
|
||||
DUP1 %mpt_read_state_trie
|
||||
// stack: account_ptr, addr, codehash, retdest
|
||||
%add_const(3)
|
||||
// stack: codehash_ptr, codehash, retdest
|
||||
// stack: codehash_ptr, addr, codehash, retdest
|
||||
DUP1 %mload_trie_data
|
||||
// stack: prev_codehash, codehash_ptr, addr, codehash, retdest
|
||||
DUP3 %journal_add_code_change // Add the code change to the journal.
|
||||
%stack (codehash_ptr, addr, codehash) -> (codehash_ptr, codehash)
|
||||
%mstore_trie_data
|
||||
// stack: retdest
|
||||
JUMP
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
// Post stack: status
|
||||
%macro create_contract_account
|
||||
// stack: value, address
|
||||
DUP2 %insert_touched_addresses
|
||||
DUP2 %mpt_read_state_trie
|
||||
// stack: existing_account_ptr, value, address
|
||||
// If the account doesn't exist, there's no need to check its balance or nonce,
|
||||
@ -23,6 +24,7 @@
|
||||
// Write the new account's data to MPT data, and get a pointer to it.
|
||||
%get_trie_data_size
|
||||
// stack: account_ptr, new_acct_value, address
|
||||
PUSH 0 DUP4 %journal_add_nonce_change
|
||||
PUSH 1 %append_to_trie_data // nonce = 1
|
||||
// stack: account_ptr, new_acct_value, address
|
||||
SWAP1 %append_to_trie_data // balance = new_acct_value
|
||||
|
||||
@ -22,17 +22,21 @@ global nonce:
|
||||
// Increment the given account's nonce. Assumes the account already exists; panics otherwise.
|
||||
global increment_nonce:
|
||||
// stack: address, retdest
|
||||
DUP1
|
||||
%mpt_read_state_trie
|
||||
// stack: account_ptr, retdest
|
||||
// stack: account_ptr, address, retdest
|
||||
DUP1 ISZERO %jumpi(increment_nonce_no_such_account)
|
||||
// stack: nonce_ptr, retdest
|
||||
// stack: nonce_ptr, address, retdest
|
||||
DUP1 %mload_trie_data
|
||||
// stack: nonce, nonce_ptr, retdest
|
||||
// stack: nonce, nonce_ptr, address, retdest
|
||||
DUP1 DUP4 %journal_add_nonce_change
|
||||
// stack: nonce, nonce_ptr, address, retdest
|
||||
%increment
|
||||
SWAP1
|
||||
// stack: nonce_ptr, nonce', retdest
|
||||
// stack: nonce_ptr, nonce', address, retdest
|
||||
%mstore_trie_data
|
||||
// stack: retdest
|
||||
// stack: address, retdest
|
||||
POP
|
||||
JUMP
|
||||
global increment_nonce_no_such_account:
|
||||
PANIC
|
||||
|
||||
@ -4,6 +4,7 @@ global precompile_blake2_f:
|
||||
// stack: new_ctx, (old stack)
|
||||
DUP1
|
||||
SET_CONTEXT
|
||||
%checkpoint %mstore_context_metadata(@CTX_METADATA_CHECKPOINT) // Checkpoint and store it in context metadata.
|
||||
// stack: (empty)
|
||||
PUSH 0x100000000 // = 2^32 (is_kernel = true)
|
||||
// stack: kexit_info
|
||||
|
||||
@ -4,6 +4,7 @@ global precompile_bn_add:
|
||||
// stack: new_ctx, (old stack)
|
||||
DUP1
|
||||
SET_CONTEXT
|
||||
%checkpoint %mstore_context_metadata(@CTX_METADATA_CHECKPOINT) // Checkpoint and store it in context metadata.
|
||||
// stack: (empty)
|
||||
PUSH 0x100000000 // = 2^32 (is_kernel = true)
|
||||
// stack: kexit_info
|
||||
|
||||
@ -4,6 +4,7 @@ global precompile_bn_mul:
|
||||
// stack: new_ctx, (old stack)
|
||||
DUP1
|
||||
SET_CONTEXT
|
||||
%checkpoint %mstore_context_metadata(@CTX_METADATA_CHECKPOINT) // Checkpoint and store it in context metadata.
|
||||
// stack: (empty)
|
||||
PUSH 0x100000000 // = 2^32 (is_kernel = true)
|
||||
// stack: kexit_info
|
||||
|
||||
@ -4,6 +4,7 @@ global precompile_ecrec:
|
||||
// stack: new_ctx, (old stack)
|
||||
DUP1
|
||||
SET_CONTEXT
|
||||
%checkpoint %mstore_context_metadata(@CTX_METADATA_CHECKPOINT) // Checkpoint and store it in context metadata.
|
||||
// stack: (empty)
|
||||
PUSH 0x100000000 // = 2^32 (is_kernel = true)
|
||||
// stack: kexit_info
|
||||
|
||||
@ -133,6 +133,7 @@ global precompile_expmod:
|
||||
// stack: new_ctx, (old stack)
|
||||
DUP1
|
||||
SET_CONTEXT
|
||||
%checkpoint %mstore_context_metadata(@CTX_METADATA_CHECKPOINT) // Checkpoint and store it in context metadata.
|
||||
// stack: (empty)
|
||||
PUSH 0x100000000 // = 2^32 (is_kernel = true)
|
||||
// stack: kexit_info
|
||||
|
||||
@ -4,6 +4,7 @@ global precompile_id:
|
||||
// stack: new_ctx, (old stack)
|
||||
DUP1
|
||||
SET_CONTEXT
|
||||
%checkpoint %mstore_context_metadata(@CTX_METADATA_CHECKPOINT) // Checkpoint and store it in context metadata.
|
||||
// stack: (empty)
|
||||
PUSH 0x100000000 // = 2^32 (is_kernel = true)
|
||||
// stack: kexit_info
|
||||
|
||||
@ -4,6 +4,7 @@ global precompile_rip160:
|
||||
// stack: new_ctx, (old stack)
|
||||
DUP1
|
||||
SET_CONTEXT
|
||||
%checkpoint %mstore_context_metadata(@CTX_METADATA_CHECKPOINT) // Checkpoint and store it in context metadata.
|
||||
// stack: (empty)
|
||||
PUSH 0x100000000 // = 2^32 (is_kernel = true)
|
||||
// stack: kexit_info
|
||||
|
||||
@ -4,6 +4,7 @@ global precompile_sha256:
|
||||
// stack: new_ctx, (old stack)
|
||||
DUP1
|
||||
SET_CONTEXT
|
||||
%checkpoint %mstore_context_metadata(@CTX_METADATA_CHECKPOINT) // Checkpoint and store it in context metadata.
|
||||
// stack: (empty)
|
||||
PUSH 0x100000000 // = 2^32 (is_kernel = true)
|
||||
// stack: kexit_info
|
||||
|
||||
@ -4,6 +4,7 @@ global precompile_snarkv:
|
||||
// stack: new_ctx, (old stack)
|
||||
DUP1
|
||||
SET_CONTEXT
|
||||
%checkpoint %mstore_context_metadata(@CTX_METADATA_CHECKPOINT) // Checkpoint and store it in context metadata.
|
||||
// stack: (empty)
|
||||
PUSH 0x100000000 // = 2^32 (is_kernel = true)
|
||||
// stack: kexit_info
|
||||
|
||||
@ -174,6 +174,8 @@ global process_contract_creation_txn_after_constructor:
|
||||
// stack: leftover_gas, new_ctx, address, retdest
|
||||
%pay_coinbase_and_refund_sender
|
||||
// TODO: Delete accounts in self-destruct list and empty touched addresses.
|
||||
%delete_all_touched_addresses
|
||||
%delete_all_selfdestructed_addresses
|
||||
// stack: new_ctx, address, retdest
|
||||
POP
|
||||
POP
|
||||
@ -265,6 +267,8 @@ global process_message_txn_after_call:
|
||||
// stack: leftover_gas, new_ctx, retdest
|
||||
%pay_coinbase_and_refund_sender
|
||||
// TODO: Delete accounts in self-destruct list and empty touched addresses.
|
||||
%delete_all_touched_addresses
|
||||
%delete_all_selfdestructed_addresses
|
||||
// stack: new_ctx, retdest
|
||||
POP
|
||||
JUMP
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
/// Self-destruct list.
|
||||
/// Implemented as an append-only array, with the length stored in the global metadata.
|
||||
/// Implemented as an array, with the length stored in the global metadata.
|
||||
/// Note: This array allows duplicates.
|
||||
|
||||
%macro insert_selfdestruct_list
|
||||
// stack: addr
|
||||
@ -10,3 +11,61 @@
|
||||
%increment
|
||||
%mstore_global_metadata(@GLOBAL_METADATA_SELFDESTRUCT_LIST_LEN) // Store new length.
|
||||
%endmacro
|
||||
|
||||
/// Remove one occurrence of the address from the list.
|
||||
/// Panics if the address is not in the list.
|
||||
global remove_selfdestruct_list:
|
||||
// stack: addr, retdest
|
||||
%mload_global_metadata(@GLOBAL_METADATA_SELFDESTRUCT_LIST_LEN)
|
||||
// stack: len, addr, retdest
|
||||
PUSH 0
|
||||
remove_selfdestruct_list_loop:
|
||||
%stack (i, len, addr, retdest) -> (i, len, i, len, addr, retdest)
|
||||
EQ %jumpi(panic)
|
||||
// stack: i, len, addr, retdest
|
||||
DUP1 %mload_kernel(@SEGMENT_SELFDESTRUCT_LIST)
|
||||
// stack: loaded_addr, i, len, addr, retdest
|
||||
DUP4
|
||||
// stack: addr, loaded_addr, i, len, addr, retdest
|
||||
EQ %jumpi(remove_selfdestruct_list_found)
|
||||
// stack: i, len, addr, retdest
|
||||
%increment
|
||||
%jump(remove_selfdestruct_list_loop)
|
||||
remove_selfdestruct_list_found:
|
||||
%stack (i, len, addr, retdest) -> (len, 1, i, retdest)
|
||||
SUB DUP1 %mstore_global_metadata(@GLOBAL_METADATA_SELFDESTRUCT_LIST_LEN) // Decrement the list length.
|
||||
// stack: len-1, i, retdest
|
||||
%mload_kernel(@SEGMENT_SELFDESTRUCT_LIST) // Load the last address in the list.
|
||||
// stack: last_addr, i, retdest
|
||||
SWAP1
|
||||
%mstore_kernel(@SEGMENT_SELFDESTRUCT_LIST) // Store the last address at the position of the removed address.
|
||||
JUMP
|
||||
|
||||
global delete_all_selfdestructed_addresses:
|
||||
// stack: retdest
|
||||
%mload_global_metadata(@GLOBAL_METADATA_SELFDESTRUCT_LIST_LEN)
|
||||
// stack: len, retdest
|
||||
PUSH 0
|
||||
delete_all_selfdestructed_addresses_loop:
|
||||
// stack: i, len, retdest
|
||||
DUP2 DUP2 EQ %jumpi(delete_all_selfdestructed_addresses_done)
|
||||
// stack: i, len, retdest
|
||||
DUP1 %mload_kernel(@SEGMENT_SELFDESTRUCT_LIST)
|
||||
// stack: loaded_addr, i, len, retdest
|
||||
DUP1 %is_non_existent ISZERO %jumpi(bingo)
|
||||
// stack: loaded_addr, i, len, retdest
|
||||
POP %increment %jump(delete_all_selfdestructed_addresses_loop)
|
||||
bingo:
|
||||
// stack: loaded_addr, i, len, retdest
|
||||
%delete_account
|
||||
%increment %jump(delete_all_selfdestructed_addresses_loop)
|
||||
delete_all_selfdestructed_addresses_done:
|
||||
// stack: i, len, retdest
|
||||
%pop2 JUMP
|
||||
|
||||
%macro delete_all_selfdestructed_addresses
|
||||
%stack () -> (%%after)
|
||||
%jump(delete_all_selfdestructed_addresses)
|
||||
%%after:
|
||||
// stack: (empty)
|
||||
%endmacro
|
||||
|
||||
@ -85,9 +85,13 @@ global sys_selfdestruct:
|
||||
// stack: balance_ptr, 0, balance, address, recipient, kexit_info
|
||||
%mstore_trie_data // TODO: This should be a copy-on-write operation.
|
||||
|
||||
%stack (balance, address, recipient, kexit_info) ->
|
||||
(address, recipient, balance, address, recipient, recipient, balance, kexit_info)
|
||||
%journal_add_account_destroyed
|
||||
|
||||
// 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)
|
||||
// stack: address, recipient, recipient, balance, kexit_info
|
||||
EQ %jumpi(sys_selfdestruct_same_addr)
|
||||
// stack: recipient, balance, kexit_info
|
||||
%add_eth
|
||||
@ -148,8 +152,8 @@ sys_revert_finish:
|
||||
// - state modification is attempted during a static call
|
||||
global fault_exception:
|
||||
// stack: (empty)
|
||||
%mload_context_metadata(@CTX_METADATA_CHECKPOINT) %revert_checkpoint
|
||||
PUSH 0 // leftover_gas
|
||||
// TODO: Revert state changes.
|
||||
// Set the parent context's return data size to 0.
|
||||
%mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 0)
|
||||
PUSH 0 // success
|
||||
|
||||
103
evm/src/cpu/kernel/asm/core/touched_addresses.asm
Normal file
103
evm/src/cpu/kernel/asm/core/touched_addresses.asm
Normal file
@ -0,0 +1,103 @@
|
||||
%macro insert_touched_addresses
|
||||
%stack (addr) -> (addr, %%after)
|
||||
%jump(insert_touched_addresses)
|
||||
%%after:
|
||||
// stack: (empty)
|
||||
%endmacro
|
||||
|
||||
%macro insert_touched_addresses_no_return
|
||||
%insert_touched_addresses
|
||||
POP
|
||||
%endmacro
|
||||
|
||||
/// Inserts the address into the list if it is not already present.
|
||||
global insert_touched_addresses:
|
||||
// stack: addr, retdest
|
||||
%mload_global_metadata(@GLOBAL_METADATA_TOUCHED_ADDRESSES_LEN)
|
||||
// stack: len, addr, retdest
|
||||
PUSH 0
|
||||
insert_touched_addresses_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_TOUCHED_ADDRESSES)
|
||||
// stack: loaded_addr, i, len, addr, retdest
|
||||
DUP4
|
||||
// stack: addr, loaded_addr, i, len, addr, retdest
|
||||
EQ %jumpi(insert_touched_addresses_found)
|
||||
// stack: i, len, addr, retdest
|
||||
%increment
|
||||
%jump(insert_touched_addresses_loop)
|
||||
|
||||
insert_address:
|
||||
%stack (i, len, addr, retdest) -> (i, addr, len, retdest)
|
||||
DUP2 %journal_add_account_touched // Add a journal entry for the touched account.
|
||||
%mstore_kernel(@SEGMENT_TOUCHED_ADDRESSES) // Store new address at the end of the array.
|
||||
// stack: len, retdest
|
||||
%increment
|
||||
%mstore_global_metadata(@GLOBAL_METADATA_TOUCHED_ADDRESSES_LEN) // Store new length.
|
||||
JUMP
|
||||
|
||||
insert_touched_addresses_found:
|
||||
%stack (i, len, addr, retdest) -> (retdest)
|
||||
JUMP
|
||||
|
||||
/// Remove the address from the list.
|
||||
/// Panics if the address is not in the list.
|
||||
/// TODO: Unused?
|
||||
global remove_touched_addresses:
|
||||
// stack: addr, retdest
|
||||
%mload_global_metadata(@GLOBAL_METADATA_TOUCHED_ADDRESSES_LEN)
|
||||
// stack: len, addr, retdest
|
||||
PUSH 0
|
||||
remove_touched_addresses_loop:
|
||||
%stack (i, len, addr, retdest) -> (i, len, i, len, addr, retdest)
|
||||
EQ %jumpi(panic)
|
||||
// stack: i, len, addr, retdest
|
||||
DUP1 %mload_kernel(@SEGMENT_TOUCHED_ADDRESSES)
|
||||
// stack: loaded_addr, i, len, addr, retdest
|
||||
DUP4
|
||||
// stack: addr, loaded_addr, i, len, addr, retdest
|
||||
EQ %jumpi(remove_touched_addresses_found)
|
||||
// stack: i, len, addr, retdest
|
||||
%increment
|
||||
%jump(remove_touched_addresses_loop)
|
||||
remove_touched_addresses_found:
|
||||
%stack (i, len, addr, retdest) -> (len, 1, i, retdest)
|
||||
SUB DUP1 %mstore_global_metadata(@GLOBAL_METADATA_TOUCHED_ADDRESSES_LEN) // Decrement the list length.
|
||||
// stack: len-1, i, retdest
|
||||
%mload_kernel(@SEGMENT_TOUCHED_ADDRESSES) // Load the last address in the list.
|
||||
// stack: last_addr, i, retdest
|
||||
SWAP1
|
||||
%mstore_kernel(@SEGMENT_TOUCHED_ADDRESSES) // Store the last address at the position of the removed address.
|
||||
JUMP
|
||||
|
||||
|
||||
global delete_all_touched_addresses:
|
||||
// stack: retdest
|
||||
%mload_global_metadata(@GLOBAL_METADATA_TOUCHED_ADDRESSES_LEN)
|
||||
// stack: len, retdest
|
||||
PUSH 0
|
||||
delete_all_touched_addresses_loop:
|
||||
// stack: i, len, retdest
|
||||
DUP2 DUP2 EQ %jumpi(delete_all_touched_addresses_done)
|
||||
// stack: i, len, retdest
|
||||
DUP1 %mload_kernel(@SEGMENT_TOUCHED_ADDRESSES)
|
||||
// stack: loaded_addr, i, len, retdest
|
||||
DUP1 %is_empty %jumpi(bingo)
|
||||
// stack: loaded_addr, i, len, retdest
|
||||
POP %increment %jump(delete_all_touched_addresses_loop)
|
||||
bingo:
|
||||
// stack: loaded_addr, i, len, retdest
|
||||
%delete_account
|
||||
%increment %jump(delete_all_touched_addresses_loop)
|
||||
delete_all_touched_addresses_done:
|
||||
// stack: i, len, retdest
|
||||
%pop2 JUMP
|
||||
|
||||
%macro delete_all_touched_addresses
|
||||
%stack () -> (%%after)
|
||||
%jump(delete_all_touched_addresses)
|
||||
%%after:
|
||||
// stack: (empty)
|
||||
%endmacro
|
||||
@ -29,6 +29,7 @@ global transfer_eth_failure:
|
||||
// TODO: Should it be copy-on-write (with make_account_copy) instead of mutating the trie?
|
||||
global deduct_eth:
|
||||
// stack: addr, amount, retdest
|
||||
DUP1 %insert_touched_addresses
|
||||
%mpt_read_state_trie
|
||||
// stack: account_ptr, amount, retdest
|
||||
DUP1 ISZERO %jumpi(deduct_eth_no_such_account) // If the account pointer is null, return 1.
|
||||
@ -65,6 +66,7 @@ global deduct_eth_insufficient_balance:
|
||||
// TODO: Should it be copy-on-write (with make_account_copy) instead of mutating the trie?
|
||||
global add_eth:
|
||||
// stack: addr, amount, retdest
|
||||
DUP1 %insert_touched_addresses
|
||||
DUP1 %mpt_read_state_trie
|
||||
// stack: account_ptr, addr, amount, retdest
|
||||
DUP1 ISZERO %jumpi(add_eth_new_account) // If the account pointer is null, we need to create the account.
|
||||
|
||||
18
evm/src/cpu/kernel/asm/journal/account_destroyed.asm
Normal file
18
evm/src/cpu/kernel/asm/journal/account_destroyed.asm
Normal file
@ -0,0 +1,18 @@
|
||||
// struct AccountDestroyed { address, target, prev_balance }
|
||||
|
||||
%macro journal_add_account_destroyed
|
||||
%journal_add_3(@JOURNAL_ENTRY_ACCOUNT_DESTROYED)
|
||||
%endmacro
|
||||
|
||||
global revert_account_destroyed:
|
||||
// stack: entry_type, ptr, retdest
|
||||
POP
|
||||
%journal_load_3
|
||||
// stack: address, target, prev_balance, retdest
|
||||
PUSH revert_account_destroyed_contd DUP2
|
||||
%jump(remove_selfdestruct_list)
|
||||
revert_account_destroyed_contd:
|
||||
// stack: address, target, prev_balance, retdest
|
||||
SWAP1 %transfer_eth %jumpi(panic)
|
||||
JUMP
|
||||
|
||||
12
evm/src/cpu/kernel/asm/journal/account_loaded.asm
Normal file
12
evm/src/cpu/kernel/asm/journal/account_loaded.asm
Normal file
@ -0,0 +1,12 @@
|
||||
// struct AccountLoaded { address }
|
||||
|
||||
%macro journal_add_account_loaded
|
||||
%journal_add_1(@JOURNAL_ENTRY_ACCOUNT_LOADED)
|
||||
%endmacro
|
||||
|
||||
global revert_account_loaded:
|
||||
// stack: entry_type, ptr, retdest
|
||||
POP
|
||||
%journal_load_1
|
||||
// stack: address, retdest
|
||||
%jump(remove_accessed_addresses)
|
||||
10
evm/src/cpu/kernel/asm/journal/account_touched.asm
Normal file
10
evm/src/cpu/kernel/asm/journal/account_touched.asm
Normal file
@ -0,0 +1,10 @@
|
||||
// struct AccountTouched { address }
|
||||
|
||||
%macro journal_add_account_touched
|
||||
%journal_add_1(@JOURNAL_ENTRY_ACCOUNT_TOUCHED)
|
||||
%endmacro
|
||||
|
||||
// Note: We don't need to remove touched addresses. In fact doing so leads to bugs because of the way we load accounts in the MPT.
|
||||
global revert_account_touched:
|
||||
// stack: entry_type, ptr, retdest
|
||||
%pop2 JUMP
|
||||
16
evm/src/cpu/kernel/asm/journal/balance_transfer.asm
Normal file
16
evm/src/cpu/kernel/asm/journal/balance_transfer.asm
Normal file
@ -0,0 +1,16 @@
|
||||
// struct BalanceTransfer { from, to, balance }
|
||||
|
||||
%macro journal_add_balance_transfer
|
||||
%journal_add_3(@JOURNAL_ENTRY_BALANCE_TRANSFER)
|
||||
%endmacro
|
||||
|
||||
global revert_balance_transfer:
|
||||
// stack: entry_type, ptr, retdest
|
||||
POP
|
||||
%journal_load_3
|
||||
// stack: from, to, balance, retdest
|
||||
SWAP1
|
||||
// stack: to, from, balance, retdest
|
||||
%transfer_eth
|
||||
%jumpi(panic) // This should never happen.
|
||||
JUMP
|
||||
17
evm/src/cpu/kernel/asm/journal/code_change.asm
Normal file
17
evm/src/cpu/kernel/asm/journal/code_change.asm
Normal file
@ -0,0 +1,17 @@
|
||||
// struct CodeChange { address, prev_codehash }
|
||||
|
||||
%macro journal_add_code_change
|
||||
%journal_add_2(@JOURNAL_ENTRY_CODE_CHANGE)
|
||||
%endmacro
|
||||
|
||||
global revert_code_change:
|
||||
// stack: ptr, retdest
|
||||
%journal_load_2
|
||||
// stack: address, prev_codehash, retdest
|
||||
%mpt_read_state_trie
|
||||
// stack: account_ptr, prev_codehash, retdest
|
||||
%add_const(3)
|
||||
// stack: codehash_ptr, prev_codehash, retdest
|
||||
%mstore_trie_data
|
||||
// stack: retdest
|
||||
JUMP
|
||||
192
evm/src/cpu/kernel/asm/journal/journal.asm
Normal file
192
evm/src/cpu/kernel/asm/journal/journal.asm
Normal file
@ -0,0 +1,192 @@
|
||||
%macro journal_size
|
||||
%mload_global_metadata(@GLOBAL_METADATA_JOURNAL_LEN)
|
||||
%endmacro
|
||||
|
||||
%macro mstore_journal
|
||||
// stack: virtual, value
|
||||
%mstore_kernel(@SEGMENT_JOURNAL)
|
||||
// stack: (empty)
|
||||
%endmacro
|
||||
|
||||
%macro mload_journal
|
||||
// stack: virtual
|
||||
%mload_kernel(@SEGMENT_JOURNAL)
|
||||
// stack: value
|
||||
%endmacro
|
||||
|
||||
%macro append_journal
|
||||
// stack: pointer
|
||||
%journal_size
|
||||
// stack: journal_size, pointer
|
||||
SWAP1 DUP2
|
||||
// stack: journal_size, pointer, journal_size
|
||||
%mstore_journal
|
||||
// stack: journal_size
|
||||
%increment
|
||||
%mstore_global_metadata(@GLOBAL_METADATA_JOURNAL_LEN)
|
||||
%endmacro
|
||||
|
||||
%macro journal_data_size
|
||||
%mload_global_metadata(@GLOBAL_METADATA_JOURNAL_DATA_LEN)
|
||||
%endmacro
|
||||
|
||||
%macro mstore_journal_data
|
||||
// stack: virtual, value
|
||||
%mstore_kernel(@SEGMENT_JOURNAL_DATA)
|
||||
// stack: (empty)
|
||||
%endmacro
|
||||
|
||||
%macro mload_journal_data
|
||||
// stack: virtual
|
||||
%mload_kernel(@SEGMENT_JOURNAL_DATA)
|
||||
// stack: value
|
||||
%endmacro
|
||||
|
||||
%macro append_journal_data
|
||||
// stack: value
|
||||
%journal_data_size
|
||||
// stack: size, value
|
||||
SWAP1 DUP2
|
||||
// stack: size, value, size
|
||||
%mstore_journal_data
|
||||
// stack: size
|
||||
%increment
|
||||
%mstore_global_metadata(@GLOBAL_METADATA_JOURNAL_DATA_LEN)
|
||||
%endmacro
|
||||
|
||||
%macro journal_add_1(type)
|
||||
// stack: w
|
||||
%journal_data_size
|
||||
// stack: ptr, w
|
||||
PUSH $type %append_journal_data
|
||||
// stack: ptr, w
|
||||
SWAP1
|
||||
// stack: w, ptr
|
||||
%append_journal_data
|
||||
// stack: ptr
|
||||
%append_journal
|
||||
%endmacro
|
||||
|
||||
%macro journal_add_2(type)
|
||||
// stack: w, x
|
||||
%journal_data_size
|
||||
// stack: ptr, w, x
|
||||
PUSH $type %append_journal_data
|
||||
// stack: ptr, w, x
|
||||
SWAP1 %append_journal_data
|
||||
// stack: ptr, x
|
||||
SWAP1 %append_journal_data
|
||||
// stack: ptr
|
||||
%append_journal
|
||||
%endmacro
|
||||
|
||||
%macro journal_add_3(type)
|
||||
// stack: w, x, y
|
||||
%journal_data_size
|
||||
// stack: ptr, w, x, y
|
||||
PUSH $type %append_journal_data
|
||||
// stack: ptr, w, x, y
|
||||
SWAP1 %append_journal_data
|
||||
// stack: ptr, x, y
|
||||
SWAP1 %append_journal_data
|
||||
// stack: ptr, y
|
||||
SWAP1 %append_journal_data
|
||||
// stack: ptr
|
||||
%append_journal
|
||||
%endmacro
|
||||
|
||||
%macro journal_add_4(type)
|
||||
// stack: w, x, y, z
|
||||
%journal_data_size
|
||||
// stack: ptr, w, x, y, z
|
||||
PUSH $type %append_journal_data
|
||||
// stack: ptr, w, x, y, z
|
||||
SWAP1 %append_journal_data
|
||||
// stack: ptr, x, y, z
|
||||
SWAP1 %append_journal_data
|
||||
// stack: ptr, y, z
|
||||
SWAP1 %append_journal_data
|
||||
// stack: ptr, z
|
||||
SWAP1 %append_journal_data
|
||||
// stack: ptr
|
||||
%append_journal
|
||||
%endmacro
|
||||
|
||||
%macro journal_load_1
|
||||
// ptr
|
||||
%add_const(1)
|
||||
%mload_journal_data
|
||||
// w
|
||||
%endmacro
|
||||
|
||||
%macro journal_load_2
|
||||
// ptr
|
||||
DUP1
|
||||
%add_const(2)
|
||||
%mload_journal_data
|
||||
// x, ptr
|
||||
SWAP1
|
||||
%add_const(1)
|
||||
%mload_journal_data
|
||||
// w, x
|
||||
%endmacro
|
||||
|
||||
%macro journal_load_3
|
||||
// ptr
|
||||
DUP1
|
||||
%add_const(3)
|
||||
%mload_journal_data
|
||||
// y, ptr
|
||||
SWAP1
|
||||
DUP1
|
||||
// ptr, ptr, y
|
||||
%add_const(2)
|
||||
%mload_journal_data
|
||||
// x, ptr, y
|
||||
SWAP1
|
||||
%add_const(1)
|
||||
%mload_journal_data
|
||||
// w, x, y
|
||||
%endmacro
|
||||
|
||||
%macro journal_load_4
|
||||
// ptr
|
||||
DUP1
|
||||
%add_const(4)
|
||||
%mload_journal_data
|
||||
// z, ptr
|
||||
SWAP1
|
||||
DUP1
|
||||
// ptr, ptr, z
|
||||
%add_const(3)
|
||||
%mload_journal_data
|
||||
// y, ptr, z
|
||||
SWAP1
|
||||
DUP1
|
||||
// ptr, ptr, y, z
|
||||
%add_const(2)
|
||||
%mload_journal_data
|
||||
// x, ptr, y, z
|
||||
SWAP1
|
||||
%add_const(1)
|
||||
%mload_journal_data
|
||||
// w, x, y, z
|
||||
%endmacro
|
||||
|
||||
%macro current_checkpoint
|
||||
%mload_global_metadata(@GLOBAL_METADATA_CURRENT_CHECKPOINT)
|
||||
%endmacro
|
||||
|
||||
|
||||
%macro checkpoint
|
||||
// stack: (empty)
|
||||
%current_checkpoint
|
||||
// stack: current_checkpoint
|
||||
%journal_size
|
||||
// stack: journal_size, current_checkpoint
|
||||
DUP2 %mstore_kernel(@SEGMENT_JOURNAL_CHECKPOINTS)
|
||||
// stack: current_checkpoint
|
||||
DUP1 %increment
|
||||
%mstore_global_metadata(@GLOBAL_METADATA_CURRENT_CHECKPOINT)
|
||||
// stack: current_checkpoint
|
||||
%endmacro
|
||||
17
evm/src/cpu/kernel/asm/journal/nonce_change.asm
Normal file
17
evm/src/cpu/kernel/asm/journal/nonce_change.asm
Normal file
@ -0,0 +1,17 @@
|
||||
// struct NonceChange { address, prev_nonce }
|
||||
|
||||
%macro journal_add_nonce_change
|
||||
%journal_add_2(@JOURNAL_ENTRY_NONCE_CHANGE)
|
||||
%endmacro
|
||||
|
||||
global revert_nonce_change:
|
||||
// stack: entry_type, ptr, retdest
|
||||
POP
|
||||
%journal_load_2
|
||||
// stack: address, prev_nonce, retdest
|
||||
%mpt_read_state_trie
|
||||
// stack: nonce_ptr, prev_nonce retdest
|
||||
%mstore_trie_data
|
||||
// stack: retdest
|
||||
JUMP
|
||||
|
||||
84
evm/src/cpu/kernel/asm/journal/revert.asm
Normal file
84
evm/src/cpu/kernel/asm/journal/revert.asm
Normal file
@ -0,0 +1,84 @@
|
||||
%macro revert
|
||||
// stack: journal_size
|
||||
%decrement
|
||||
%stack (journal_size_m_1) -> (journal_size_m_1, %%after, journal_size_m_1)
|
||||
%mload_journal
|
||||
// stack: ptr, %%after, journal_size-1
|
||||
DUP1 %mload_journal_data
|
||||
// stack: entry_type, ptr, %%after, journal_size-1
|
||||
DUP1 %eq_const(@JOURNAL_ENTRY_ACCOUNT_LOADED) %jumpi(revert_account_loaded)
|
||||
DUP1 %eq_const(@JOURNAL_ENTRY_ACCOUNT_DESTROYED) %jumpi(revert_account_destroyed)
|
||||
DUP1 %eq_const(@JOURNAL_ENTRY_ACCOUNT_TOUCHED) %jumpi(revert_account_touched)
|
||||
DUP1 %eq_const(@JOURNAL_ENTRY_BALANCE_TRANSFER) %jumpi(revert_balance_transfer)
|
||||
DUP1 %eq_const(@JOURNAL_ENTRY_NONCE_CHANGE) %jumpi(revert_nonce_change)
|
||||
DUP1 %eq_const(@JOURNAL_ENTRY_STORAGE_CHANGE) %jumpi(revert_storage_change)
|
||||
DUP1 %eq_const(@JOURNAL_ENTRY_STORAGE_LOADED) %jumpi(revert_storage_loaded)
|
||||
%eq_const(@JOURNAL_ENTRY_CODE_CHANGE) %jumpi(revert_code_change)
|
||||
PANIC // This should never happen.
|
||||
%%after:
|
||||
// stack: journal_size-1
|
||||
%endmacro
|
||||
|
||||
global revert_batch:
|
||||
// stack: target_size, retdest
|
||||
%journal_size
|
||||
// stack: journal_size, target_size, retdest
|
||||
DUP2 DUP2 LT %jumpi(panic) // Sanity check to avoid infinite loop.
|
||||
while_loop:
|
||||
// stack: journal_size, target_size, retdest
|
||||
DUP2 DUP2 EQ %jumpi(revert_batch_done)
|
||||
// stack: journal_size, target_size, retdest
|
||||
%revert
|
||||
// stack: journal_size-1, target_size, retdest
|
||||
%jump(while_loop)
|
||||
|
||||
revert_batch_done:
|
||||
// stack: journal_size, target_size, retdest
|
||||
%mstore_global_metadata(@GLOBAL_METADATA_JOURNAL_LEN)
|
||||
POP JUMP
|
||||
|
||||
revert_one_checkpoint:
|
||||
// stack: current_checkpoint, retdest
|
||||
DUP1 ISZERO %jumpi(first_checkpoint)
|
||||
// stack: current_checkpoint, retdest
|
||||
%decrement
|
||||
// stack: current_checkpoint-1, retdest
|
||||
DUP1 %mload_kernel(@SEGMENT_JOURNAL_CHECKPOINTS)
|
||||
// stack: target_size, current_checkpoints-1, retdest
|
||||
%jump(do_revert)
|
||||
first_checkpoint:
|
||||
// stack: current_checkpoint, retdest
|
||||
%decrement
|
||||
// stack: current_checkpoint-1, retdest
|
||||
PUSH 0
|
||||
// stack: target_size, current_checkpoints-1, retdest
|
||||
do_revert:
|
||||
%stack (target_size, current_checkpoints_m_1, retdest) -> (target_size, after_revert, current_checkpoints_m_1, retdest)
|
||||
%jump(revert_batch)
|
||||
after_revert:
|
||||
// stack: current_checkpoint-1, retdest
|
||||
SWAP1 JUMP
|
||||
|
||||
|
||||
global revert_checkpoint:
|
||||
// stack: target_checkpoint, retdest
|
||||
%current_checkpoint
|
||||
// stack: current_checkpoint, target_checkpoint, retdest
|
||||
DUP2 DUP2 LT %jumpi(panic) // Sanity check that current_cp >= target_cp. This should never happen.
|
||||
while:
|
||||
// stack: current_checkpoint, target_checkpoint, retdest
|
||||
DUP2 DUP2 EQ %jumpi(revert_checkpoint_done)
|
||||
%stack (current_checkpoint) -> (current_checkpoint, while)
|
||||
%jump(revert_one_checkpoint)
|
||||
revert_checkpoint_done:
|
||||
// stack: current_checkpoint, target_checkpoint, retdest
|
||||
POP
|
||||
%mstore_global_metadata(@GLOBAL_METADATA_CURRENT_CHECKPOINT)
|
||||
JUMP
|
||||
|
||||
%macro revert_checkpoint
|
||||
%stack (target_checkpoint) -> (target_checkpoint, %%after)
|
||||
%jump(revert_checkpoint)
|
||||
%%after:
|
||||
// stack: (empty)
|
||||
%endmacro
|
||||
61
evm/src/cpu/kernel/asm/journal/storage_change.asm
Normal file
61
evm/src/cpu/kernel/asm/journal/storage_change.asm
Normal file
@ -0,0 +1,61 @@
|
||||
// struct StorageChange { address, slot, prev_value }
|
||||
|
||||
%macro journal_add_storage_change
|
||||
%journal_add_3(@JOURNAL_ENTRY_STORAGE_CHANGE)
|
||||
%endmacro
|
||||
|
||||
global revert_storage_change:
|
||||
// stack: entry_type, ptr, retdest
|
||||
POP
|
||||
%journal_load_3
|
||||
// stack: address, slot, prev_value, retdest
|
||||
DUP3 ISZERO %jumpi(delete)
|
||||
// stack: address, slot, prev_value, retdest
|
||||
SWAP1 %slot_to_storage_key
|
||||
// stack: storage_key, address, prev_value, retdest
|
||||
PUSH 64 // storage_key has 64 nibbles
|
||||
// stack: 64, storage_key, address, prev_value, retdest
|
||||
DUP3 %mpt_read_state_trie
|
||||
DUP1 ISZERO %jumpi(panic)
|
||||
// stack: account_ptr, 64, storage_key, address, prev_value, retdest
|
||||
%add_const(2)
|
||||
// stack: storage_root_ptr_ptr, 64, storage_key, address, prev_value, retdest
|
||||
%mload_trie_data
|
||||
%stack (storage_root_ptr, num_nibbles, storage_key, address, prev_value, retdest) ->
|
||||
(storage_root_ptr, num_nibbles, storage_key, prev_value, new_storage_root, address, retdest)
|
||||
%jump(mpt_insert)
|
||||
|
||||
delete:
|
||||
// stack: address, slot, prev_value, retdest
|
||||
SWAP2 POP
|
||||
%stack (slot, address, retdest) -> (slot, new_storage_root, address, retdest)
|
||||
%slot_to_storage_key
|
||||
// stack: storage_key, new_storage_root, address, retdest
|
||||
PUSH 64 // storage_key has 64 nibbles
|
||||
// stack: 64, storage_key, new_storage_root, address, retdest
|
||||
DUP4 %mpt_read_state_trie
|
||||
DUP1 ISZERO %jumpi(panic)
|
||||
// stack: account_ptr, 64, storage_key, new_storage_root, address, retdest
|
||||
%add_const(2)
|
||||
// stack: storage_root_ptr_ptr, 64, storage_key, new_storage_root, address, retdest
|
||||
%mload_trie_data
|
||||
// stack: storage_root_ptr, 64, storage_key, new_storage_root, address, retdest
|
||||
%jump(mpt_delete)
|
||||
|
||||
new_storage_root:
|
||||
// stack: new_storage_root_ptr, address, retdest
|
||||
DUP2 %mpt_read_state_trie
|
||||
// stack: old_account_ptr, new_storage_root_ptr, address, retdest
|
||||
%make_account_copy
|
||||
// stack: new_account_ptr, new_storage_root_ptr, address, retdest
|
||||
|
||||
// Update the copied account with our new storage root pointer.
|
||||
%stack (new_account_ptr, new_storage_root_ptr) -> (new_account_ptr, new_storage_root_ptr, new_account_ptr)
|
||||
%add_const(2)
|
||||
// stack: new_account_storage_root_ptr_ptr, new_storage_root_ptr, new_account_ptr, address, retdest
|
||||
%mstore_trie_data
|
||||
// stack: new_account_ptr, address, retdest
|
||||
|
||||
DUP2 %addr_to_state_key
|
||||
%stack (state_key, new_account_ptr, address, retdest) -> (state_key, new_account_ptr, retdest)
|
||||
%jump(mpt_insert_state_trie)
|
||||
12
evm/src/cpu/kernel/asm/journal/storage_loaded.asm
Normal file
12
evm/src/cpu/kernel/asm/journal/storage_loaded.asm
Normal file
@ -0,0 +1,12 @@
|
||||
// struct StorageLoaded { address, slot }
|
||||
|
||||
%macro journal_add_storage_loaded
|
||||
%journal_add_2(@JOURNAL_ENTRY_STORAGE_LOADED)
|
||||
%endmacro
|
||||
|
||||
global revert_storage_loaded:
|
||||
// stack: entry_type, ptr, retdest
|
||||
POP
|
||||
%journal_load_2
|
||||
// stack: address, slot, retdest
|
||||
%jump(remove_accessed_storage_keys)
|
||||
@ -22,3 +22,24 @@ mpt_delete_leaf:
|
||||
%pop4
|
||||
PUSH 0 // empty node ptr
|
||||
SWAP1 JUMP
|
||||
|
||||
global delete_account:
|
||||
%stack (address, retdest) -> (address, delete_account_save, retdest)
|
||||
%addr_to_state_key
|
||||
// stack: key, delete_account_save, retdest
|
||||
PUSH 64
|
||||
// stack: 64, key, delete_account_save, retdest
|
||||
%mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT)
|
||||
// stack: state_root_prt, 64, key, delete_account_save, retdest
|
||||
%jump(mpt_delete)
|
||||
delete_account_save:
|
||||
// stack: updated_state_root_ptr, retdest
|
||||
%mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT)
|
||||
JUMP
|
||||
|
||||
%macro delete_account
|
||||
%stack (address) -> (address, %%after)
|
||||
%jump(delete_account)
|
||||
%%after:
|
||||
// stack: (empty)
|
||||
%endmacro
|
||||
@ -85,9 +85,13 @@ sstore_no_refund:
|
||||
sstore_after_refund:
|
||||
// stack: kexit_info, current_value, slot, value
|
||||
// Check if `value` is equal to `current_value`, and if so exit the kernel early.
|
||||
%stack (kexit_info, current_value, slot, value) -> (value, current_value, slot, value, kexit_info)
|
||||
%stack (kexit_info, current_value, slot, value) -> (value, current_value, current_value, slot, value, kexit_info)
|
||||
EQ %jumpi(sstore_noop)
|
||||
|
||||
// stack: current_value, slot, value, kexit_info
|
||||
DUP2 %address %journal_add_storage_change
|
||||
// stack: slot, value, kexit_info
|
||||
|
||||
// If the value is zero, delete the slot from the storage trie.
|
||||
// stack: slot, value, kexit_info
|
||||
DUP2 ISZERO %jumpi(sstore_delete)
|
||||
@ -134,8 +138,8 @@ after_state_insert:
|
||||
EXIT_KERNEL
|
||||
|
||||
sstore_noop:
|
||||
// stack: slot, value, kexit_info
|
||||
%pop2
|
||||
// stack: current_value, slot, value, kexit_info
|
||||
%pop3
|
||||
EXIT_KERNEL
|
||||
|
||||
// Delete the slot from the storage trie.
|
||||
|
||||
@ -28,10 +28,11 @@ pub(crate) enum ContextMetadata {
|
||||
StackSize = 11,
|
||||
/// The gas limit for this call (not the entire transaction).
|
||||
GasLimit = 12,
|
||||
Checkpoint = 13,
|
||||
}
|
||||
|
||||
impl ContextMetadata {
|
||||
pub(crate) const COUNT: usize = 13;
|
||||
pub(crate) const COUNT: usize = 14;
|
||||
|
||||
pub(crate) fn all() -> [Self; Self::COUNT] {
|
||||
[
|
||||
@ -48,6 +49,7 @@ impl ContextMetadata {
|
||||
Self::MemWords,
|
||||
Self::StackSize,
|
||||
Self::GasLimit,
|
||||
Self::Checkpoint,
|
||||
]
|
||||
}
|
||||
|
||||
@ -67,6 +69,7 @@ impl ContextMetadata {
|
||||
ContextMetadata::MemWords => "CTX_METADATA_MEM_WORDS",
|
||||
ContextMetadata::StackSize => "CTX_METADATA_STACK_SIZE",
|
||||
ContextMetadata::GasLimit => "CTX_METADATA_GAS_LIMIT",
|
||||
ContextMetadata::Checkpoint => "CTX_METADATA_CHECKPOINT",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,10 +51,18 @@ pub(crate) enum GlobalMetadata {
|
||||
AccessedStorageKeysLen = 24,
|
||||
/// Length of the self-destruct list.
|
||||
SelfDestructListLen = 25,
|
||||
|
||||
/// Length of the journal.
|
||||
JournalLen = 26,
|
||||
/// Length of the `JournalData` segment.
|
||||
JournalDataLen = 27,
|
||||
/// Current checkpoint.
|
||||
CurrentCheckpoint = 28,
|
||||
TouchedAddressesLen = 29,
|
||||
}
|
||||
|
||||
impl GlobalMetadata {
|
||||
pub(crate) const COUNT: usize = 25;
|
||||
pub(crate) const COUNT: usize = 29;
|
||||
|
||||
pub(crate) fn all() -> [Self; Self::COUNT] {
|
||||
[
|
||||
@ -83,6 +91,10 @@ impl GlobalMetadata {
|
||||
Self::AccessedAddressesLen,
|
||||
Self::AccessedStorageKeysLen,
|
||||
Self::SelfDestructListLen,
|
||||
Self::JournalLen,
|
||||
Self::JournalDataLen,
|
||||
Self::CurrentCheckpoint,
|
||||
Self::TouchedAddressesLen,
|
||||
]
|
||||
}
|
||||
|
||||
@ -114,6 +126,10 @@ impl GlobalMetadata {
|
||||
Self::AccessedAddressesLen => "GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN",
|
||||
Self::AccessedStorageKeysLen => "GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN",
|
||||
Self::SelfDestructListLen => "GLOBAL_METADATA_SELFDESTRUCT_LIST_LEN",
|
||||
Self::JournalLen => "GLOBAL_METADATA_JOURNAL_LEN",
|
||||
Self::JournalDataLen => "GLOBAL_METADATA_JOURNAL_DATA_LEN",
|
||||
Self::CurrentCheckpoint => "GLOBAL_METADATA_CURRENT_CHECKPOINT",
|
||||
Self::TouchedAddressesLen => "GLOBAL_METADATA_TOUCHED_ADDRESSES_LEN",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
43
evm/src/cpu/kernel/constants/journal_entry.rs
Normal file
43
evm/src/cpu/kernel/constants/journal_entry.rs
Normal file
@ -0,0 +1,43 @@
|
||||
#[allow(dead_code)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)]
|
||||
pub(crate) enum JournalEntry {
|
||||
AccountLoaded = 0,
|
||||
AccountDestroyed = 1,
|
||||
AccountTouched = 2,
|
||||
BalanceTransfer = 3,
|
||||
NonceChange = 4,
|
||||
StorageChange = 5,
|
||||
StorageLoaded = 6,
|
||||
CodeChange = 7,
|
||||
}
|
||||
|
||||
impl JournalEntry {
|
||||
pub(crate) const COUNT: usize = 8;
|
||||
|
||||
pub(crate) fn all() -> [Self; Self::COUNT] {
|
||||
[
|
||||
Self::AccountLoaded,
|
||||
Self::AccountDestroyed,
|
||||
Self::AccountTouched,
|
||||
Self::BalanceTransfer,
|
||||
Self::NonceChange,
|
||||
Self::StorageChange,
|
||||
Self::StorageLoaded,
|
||||
Self::CodeChange,
|
||||
]
|
||||
}
|
||||
|
||||
/// The variable name that gets passed into kernel assembly code.
|
||||
pub(crate) fn var_name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::AccountLoaded => "JOURNAL_ENTRY_ACCOUNT_LOADED",
|
||||
Self::AccountDestroyed => "JOURNAL_ENTRY_ACCOUNT_DESTROYED",
|
||||
Self::AccountTouched => "JOURNAL_ENTRY_ACCOUNT_TOUCHED",
|
||||
Self::BalanceTransfer => "JOURNAL_ENTRY_BALANCE_TRANSFER",
|
||||
Self::NonceChange => "JOURNAL_ENTRY_NONCE_CHANGE",
|
||||
Self::StorageChange => "JOURNAL_ENTRY_STORAGE_CHANGE",
|
||||
Self::StorageLoaded => "JOURNAL_ENTRY_STORAGE_LOADED",
|
||||
Self::CodeChange => "JOURNAL_ENTRY_CODE_CHANGE",
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,12 +6,14 @@ use hex_literal::hex;
|
||||
use crate::cpu::decode::invalid_opcodes_user;
|
||||
use crate::cpu::kernel::constants::context_metadata::ContextMetadata;
|
||||
use crate::cpu::kernel::constants::global_metadata::GlobalMetadata;
|
||||
use crate::cpu::kernel::constants::journal_entry::JournalEntry;
|
||||
use crate::cpu::kernel::constants::trie_type::PartialTrieType;
|
||||
use crate::cpu::kernel::constants::txn_fields::NormalizedTxnField;
|
||||
use crate::memory::segments::Segment;
|
||||
|
||||
pub(crate) mod context_metadata;
|
||||
pub(crate) mod global_metadata;
|
||||
pub(crate) mod journal_entry;
|
||||
pub(crate) mod trie_type;
|
||||
pub(crate) mod txn_fields;
|
||||
|
||||
@ -67,6 +69,9 @@ pub fn evm_constants() -> HashMap<String, U256> {
|
||||
for trie_type in PartialTrieType::all() {
|
||||
c.insert(trie_type.var_name().into(), (trie_type as u32).into());
|
||||
}
|
||||
for entry in JournalEntry::all() {
|
||||
c.insert(entry.var_name().into(), (entry as u32).into());
|
||||
}
|
||||
c.insert(
|
||||
"INVALID_OPCODES_USER".into(),
|
||||
U256::from_little_endian(&invalid_opcodes_user()),
|
||||
|
||||
@ -50,10 +50,16 @@ pub enum Segment {
|
||||
AccessedStorageKeys = 24,
|
||||
/// List of addresses that have called SELFDESTRUCT in the current transaction.
|
||||
SelfDestructList = 25,
|
||||
/// Journal of state changes. List of pointers to `JournalData`. Length in `GlobalMetadata`.
|
||||
Journal = 26,
|
||||
JournalData = 27,
|
||||
JournalCheckpoints = 28,
|
||||
/// List of addresses that have been touched in the current transaction.
|
||||
TouchedAddresses = 29,
|
||||
}
|
||||
|
||||
impl Segment {
|
||||
pub(crate) const COUNT: usize = 26;
|
||||
pub(crate) const COUNT: usize = 30;
|
||||
|
||||
pub(crate) fn all() -> [Self; Self::COUNT] {
|
||||
[
|
||||
@ -83,6 +89,10 @@ impl Segment {
|
||||
Self::AccessedAddresses,
|
||||
Self::AccessedStorageKeys,
|
||||
Self::SelfDestructList,
|
||||
Self::Journal,
|
||||
Self::JournalData,
|
||||
Self::JournalCheckpoints,
|
||||
Self::TouchedAddresses,
|
||||
]
|
||||
}
|
||||
|
||||
@ -115,6 +125,10 @@ impl Segment {
|
||||
Segment::AccessedAddresses => "SEGMENT_ACCESSED_ADDRESSES",
|
||||
Segment::AccessedStorageKeys => "SEGMENT_ACCESSED_STORAGE_KEYS",
|
||||
Segment::SelfDestructList => "SEGMENT_SELFDESTRUCT_LIST",
|
||||
Segment::Journal => "SEGMENT_JOURNAL",
|
||||
Segment::JournalData => "SEGMENT_JOURNAL_DATA",
|
||||
Segment::JournalCheckpoints => "SEGMENT_JOURNAL_CHECKPOINTS",
|
||||
Segment::TouchedAddresses => "SEGMENT_TOUCHED_ADDRESSES",
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,6 +161,10 @@ impl Segment {
|
||||
Segment::AccessedAddresses => 256,
|
||||
Segment::AccessedStorageKeys => 256,
|
||||
Segment::SelfDestructList => 256,
|
||||
Segment::Journal => 256,
|
||||
Segment::JournalData => 256,
|
||||
Segment::JournalCheckpoints => 256,
|
||||
Segment::TouchedAddresses => 256,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,6 +90,33 @@ where
|
||||
Ok((proof, outputs))
|
||||
}
|
||||
|
||||
/// Generate traces, then create all STARK proofs. Returns information about the post-state,
|
||||
/// intended for debugging, in addition to the proof.
|
||||
pub fn dont_prove_with_outputs<F, C, const D: usize>(
|
||||
all_stark: &AllStark<F, D>,
|
||||
config: &StarkConfig,
|
||||
inputs: GenerationInputs,
|
||||
timing: &mut TimingTree,
|
||||
) -> Result<(PublicValues, GenerationOutputs)>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
C: GenericConfig<D, F = F>,
|
||||
[(); ArithmeticStark::<F, D>::COLUMNS]:,
|
||||
[(); CpuStark::<F, D>::COLUMNS]:,
|
||||
[(); KeccakStark::<F, D>::COLUMNS]:,
|
||||
[(); KeccakSpongeStark::<F, D>::COLUMNS]:,
|
||||
[(); LogicStark::<F, D>::COLUMNS]:,
|
||||
[(); MemoryStark::<F, D>::COLUMNS]:,
|
||||
{
|
||||
timed!(timing, "build kernel", Lazy::force(&KERNEL));
|
||||
let (_traces, public_values, outputs) = timed!(
|
||||
timing,
|
||||
"generate all traces",
|
||||
generate_traces(all_stark, inputs, config, timing)?
|
||||
);
|
||||
Ok((public_values, outputs))
|
||||
}
|
||||
|
||||
/// Compute all STARK proofs.
|
||||
pub(crate) fn prove_with_traces<F, C, const D: usize>(
|
||||
all_stark: &AllStark<F, D>,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user