diff --git a/evm/src/cpu/kernel/asm/core/access_lists.asm b/evm/src/cpu/kernel/asm/core/access_lists.asm index 80d96e43..0c5e595e 100644 --- a/evm/src/cpu/kernel/asm/core/access_lists.asm +++ b/evm/src/cpu/kernel/asm/core/access_lists.asm @@ -54,49 +54,55 @@ insert_accessed_addresses_found: %macro insert_accessed_storage_keys - %stack (addr, key) -> (addr, key, %%after) + %stack (addr, key, value) -> (addr, key, value, %%after) %jump(insert_accessed_storage_keys) %%after: // stack: cold_access %endmacro -/// Inserts the storage key into the access list if it is not already present. -/// Return 1 if the storage key was inserted, 0 if it was already present. +/// Inserts the storage key and value into the access list if it is not already present. +/// `value` should be the current storage value at the slot `(addr, key)`. +/// Return `1, original_value` if the storage key was inserted, `0, original_value` if it was already present. global insert_accessed_storage_keys: - // stack: addr, key, retdest + // stack: addr, key, value, retdest %mload_global_metadata(@GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN) - // stack: len, addr, key, retdest + // stack: len, addr, key, value, retdest PUSH 0 insert_accessed_storage_keys_loop: - %stack (i, len, addr, key, retdest) -> (i, len, i, len, addr, key, retdest) + %stack (i, len, addr, key, value, retdest) -> (i, len, i, len, addr, key, value, retdest) EQ %jumpi(insert_storage_key) - // stack: i, len, addr, key, retdest + // stack: i, len, addr, key, value, retdest DUP1 %increment %mload_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) - // stack: loaded_key, i, len, addr, key, retdest + // stack: loaded_key, i, len, addr, key, value, retdest DUP2 %mload_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) - // stack: loaded_addr, loaded_key, i, len, addr, key, retdest + // stack: loaded_addr, loaded_key, i, len, addr, key, value, retdest DUP5 EQ - // stack: loaded_addr==addr, loaded_key, i, len, addr, key, retdest + // stack: loaded_addr==addr, loaded_key, i, len, addr, key, value, retdest SWAP1 DUP6 EQ - // stack: loaded_key==key, loaded_addr==addr, i, len, addr, key, retdest + // stack: loaded_key==key, loaded_addr==addr, i, len, addr, key, value, retdest MUL // AND %jumpi(insert_accessed_storage_keys_found) - // stack: i, len, addr, key, retdest - %add_const(2) + // stack: i, len, addr, key, value, retdest + %add_const(3) %jump(insert_accessed_storage_keys_loop) insert_storage_key: - // stack: i, len, addr, key, retdest + // stack: i, len, addr, key, value, retdest DUP1 %increment - %stack (i_plus_1, i, len, addr, key, retdest) -> (i, addr, i_plus_1, key, i_plus_1, retdest) + DUP1 %increment + %stack (i_plus_2, i_plus_1, i, len, addr, key, value) -> (i, addr, i_plus_1, key, i_plus_2, value, i_plus_2, value) %mstore_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) // Store new address at the end of the array. %mstore_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) // Store new key after that - // stack: i_plus_1, retdest + %mstore_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) // Store new value after that + // stack: i_plus_2, value, retdest %increment - %mstore_global_metadata(@GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN) // Store new length in front of the array. - PUSH 1 // Return 1 to indicate that the storage key was inserted. - SWAP1 JUMP + %mstore_global_metadata(@GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN) // Store new length. + %stack (value, retdest) -> (retdest, 1, value) // Return 1 to indicate that the storage key was inserted. + JUMP insert_accessed_storage_keys_found: - %stack (i, len, addr, key, retdest) -> (retdest, 0) // Return 0 to indicate that the storage key was already present. + // stack: i, len, addr, key, value, retdest + %add_const(2) + %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 diff --git a/evm/src/cpu/kernel/asm/mpt/storage/storage_read.asm b/evm/src/cpu/kernel/asm/mpt/storage/storage_read.asm index edb63e2d..84d8d0ef 100644 --- a/evm/src/cpu/kernel/asm/mpt/storage/storage_read.asm +++ b/evm/src/cpu/kernel/asm/mpt/storage/storage_read.asm @@ -1,3 +1,34 @@ +%macro sload_current + %stack (slot) -> (slot, %%after) + %jump(sload_current) +%%after: +%endmacro + +global sload_current: + %stack (slot) -> (slot, after_storage_read) + %slot_to_storage_key + // stack: storage_key, after_storage_read + PUSH 64 // storage_key has 64 nibbles + %current_storage_trie + // stack: storage_root_ptr, 64, storage_key, after_storage_read + %jump(mpt_read) + +global after_storage_read: + // stack: value_ptr, retdest + DUP1 %jumpi(storage_key_exists) + + // Storage key not found. Return default value_ptr = 0, + // which derefs to 0 since @SEGMENT_TRIE_DATA[0] = 0. + %stack (value_ptr, retdest) -> (retdest, 0) + JUMP + +global storage_key_exists: + // stack: value_ptr, retdest + %mload_trie_data + // stack: value, retdest + SWAP1 + JUMP + // Read a word from the current account's storage trie. // // Pre stack: kexit_info, slot @@ -6,38 +37,20 @@ global sys_sload: // stack: kexit_info, slot SWAP1 - // stack: slot, kexit_info - DUP1 %address - // stack: addr, slot, slot, kexit_info - %insert_accessed_storage_keys PUSH @GAS_COLDSLOAD_MINUS_WARMACCESS - MUL - PUSH @GAS_WARMACCESS - ADD - %stack (gas, slot, kexit_info) -> (gas, kexit_info, slot) + DUP1 + // stack: slot, slot, kexit_info + %sload_current + + %stack (value, slot, kexit_info) -> (slot, value, kexit_info, value) + %address + // stack: addr, slot, value, kexit_info, value + %insert_accessed_storage_keys + // stack: cold_access, old_value, kexit_info, value + SWAP1 POP + // stack: cold_access, kexit_info, value + %mul_const(@GAS_COLDSLOAD_MINUS_WARMACCESS) + %add_const(@GAS_WARMACCESS) %charge_gas - // stack: kexit_info, slot - - SWAP1 - %stack (slot) -> (slot, after_storage_read) - %slot_to_storage_key - // stack: storage_key, after_storage_read, kexit_info - PUSH 64 // storage_key has 64 nibbles - %current_storage_trie - // stack: storage_root_ptr, 64, storage_key, after_storage_read, kexit_info - %jump(mpt_read) - -after_storage_read: - // stack: value_ptr, kexit_info - DUP1 %jumpi(storage_key_exists) - - // Storage key not found. Return default value_ptr = 0, - // which derefs to 0 since @SEGMENT_TRIE_DATA[0] = 0. - %stack (value_ptr, kexit_info) -> (kexit_info, 0) + // stack: kexit_info, value EXIT_KERNEL -storage_key_exists: - // stack: value_ptr, kexit_info - %mload_trie_data - // stack: value, kexit_info - SWAP1 - EXIT_KERNEL diff --git a/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm b/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm index 6da8f567..af8bc912 100644 --- a/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm +++ b/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm @@ -6,14 +6,42 @@ global sys_sstore: %check_static %stack (kexit_info, slot, value) -> (slot, kexit_info, slot, value) - %address %insert_accessed_storage_keys POP // TODO: Use return value in gas calculation. - // TODO: Assuming a cold zero -> nonzero write for now. - PUSH @GAS_COLDSLOAD - PUSH @GAS_SSET - ADD + %sload_current + %address + %stack (addr, current_value, kexit_info, slot, value) -> (addr, slot, current_value, current_value, kexit_info, slot, value) + %insert_accessed_storage_keys + // stack: cold_access, original_value, current_value, kexit_info, slot, value + %mul_const(@GAS_COLDSLOAD) + + // Check for warm access. + %stack (gas, original_value, current_value, kexit_info, slot, value) -> + (value, current_value, current_value, original_value, gas, original_value, current_value, kexit_info, slot, value) + EQ SWAP2 EQ ISZERO + // stack: current_value==original_value, value==current_value, gas, original_value, current_value, kexit_info, slot, value) + ADD // OR + %jumpi(sstore_warm) + + // Check for sset (set a zero storage slot to a non-zero value). + // stack: gas, original_value, current_value, kexit_info, slot, value + DUP2 ISZERO %mul_const(@GAS_SSET) ADD + + // Check for sreset (set a non-zero storage slot to a non-zero value). + // stack: gas, original_value, current_value, kexit_info, slot, value + DUP2 ISZERO ISZERO %mul_const(@GAS_SRESET) ADD + %jump(sstore_charge_gas) + +sstore_warm: + // stack: gas, original_value, current_value, kexit_info, slot, value) + %add_const(@GAS_WARMACCESS) + +sstore_charge_gas: + %stack (gas, original_value, current_value, kexit_info, slot, value) -> (gas, kexit_info, current_value, slot, value) %charge_gas - %stack (kexit_info, slot, value) -> (slot, value, kexit_info) + // 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) + EQ %jumpi(sstore_noop) + // TODO: If value = 0, delete the key instead of inserting 0. // stack: slot, value, kexit_info @@ -57,3 +85,8 @@ after_storage_insert: after_state_insert: // stack: kexit_info EXIT_KERNEL + +sstore_noop: + // stack: slot, value, kexit_info + %pop2 + EXIT_KERNEL diff --git a/evm/src/cpu/kernel/tests/core/access_lists.rs b/evm/src/cpu/kernel/tests/core/access_lists.rs index 2b647811..c62d4865 100644 --- a/evm/src/cpu/kernel/tests/core/access_lists.rs +++ b/evm/src/cpu/kernel/tests/core/access_lists.rs @@ -99,12 +99,12 @@ fn test_insert_accessed_storage_keys() -> Result<()> { let mut rng = thread_rng(); let n = rng.gen_range(1..10); let storage_keys = (0..n) - .map(|_| (rng.gen::
(), U256(rng.gen()))) + .map(|_| (rng.gen::
(), U256(rng.gen()), U256(rng.gen()))) .collect::>() .into_iter() - .collect::>(); + .collect::>(); let storage_key_in_list = storage_keys[rng.gen_range(0..n)]; - let storage_key_not_in_list = (rng.gen::
(), U256(rng.gen())); + let storage_key_not_in_list = (rng.gen::
(), U256(rng.gen()), U256(rng.gen())); assert!( !storage_keys.contains(&storage_key_not_in_list), "Cosmic luck or bad RNG?" @@ -113,6 +113,7 @@ fn test_insert_accessed_storage_keys() -> Result<()> { // Test for storage key already in list. let initial_stack = vec![ retaddr, + storage_key_in_list.2, storage_key_in_list.1, U256::from(storage_key_in_list.0 .0.as_slice()), ]; @@ -122,30 +123,35 @@ fn test_insert_accessed_storage_keys() -> Result<()> { interpreter .generation_state .memory - .set(MemoryAddress::new(0, AccessedStorageKeys, 2 * i), addr); + .set(MemoryAddress::new(0, AccessedStorageKeys, 3 * i), addr); interpreter.generation_state.memory.set( - MemoryAddress::new(0, AccessedStorageKeys, 2 * i + 1), + MemoryAddress::new(0, AccessedStorageKeys, 3 * i + 1), storage_keys[i].1, ); + interpreter.generation_state.memory.set( + MemoryAddress::new(0, AccessedStorageKeys, 3 * i + 2), + storage_keys[i].2, + ); } interpreter.generation_state.memory.set( MemoryAddress::new(0, GlobalMetadata, AccessedStorageKeysLen as usize), - U256::from(2 * n), + U256::from(3 * n), ); interpreter.run()?; - assert_eq!(interpreter.stack(), &[U256::zero()]); + assert_eq!(interpreter.stack(), &[storage_key_in_list.2, U256::zero()]); assert_eq!( interpreter.generation_state.memory.get(MemoryAddress::new( 0, GlobalMetadata, AccessedStorageKeysLen as usize )), - U256::from(2 * n) + U256::from(3 * n) ); // Test for storage key not in list. let initial_stack = vec![ retaddr, + storage_key_not_in_list.2, storage_key_not_in_list.1, U256::from(storage_key_not_in_list.0 .0.as_slice()), ]; @@ -155,41 +161,56 @@ fn test_insert_accessed_storage_keys() -> Result<()> { interpreter .generation_state .memory - .set(MemoryAddress::new(0, AccessedStorageKeys, 2 * i), addr); + .set(MemoryAddress::new(0, AccessedStorageKeys, 3 * i), addr); interpreter.generation_state.memory.set( - MemoryAddress::new(0, AccessedStorageKeys, 2 * i + 1), + MemoryAddress::new(0, AccessedStorageKeys, 3 * i + 1), storage_keys[i].1, ); + interpreter.generation_state.memory.set( + MemoryAddress::new(0, AccessedStorageKeys, 3 * i + 2), + storage_keys[i].2, + ); } interpreter.generation_state.memory.set( MemoryAddress::new(0, GlobalMetadata, AccessedStorageKeysLen as usize), - U256::from(2 * n), + U256::from(3 * n), ); interpreter.run()?; - assert_eq!(interpreter.stack(), &[U256::one()]); + assert_eq!( + interpreter.stack(), + &[storage_key_not_in_list.2, U256::one()] + ); assert_eq!( interpreter.generation_state.memory.get(MemoryAddress::new( 0, GlobalMetadata, AccessedStorageKeysLen as usize )), - U256::from(2 * (n + 1)) + U256::from(3 * (n + 1)) ); assert_eq!( interpreter .generation_state .memory - .get(MemoryAddress::new(0, AccessedStorageKeys, 2 * n,)), + .get(MemoryAddress::new(0, AccessedStorageKeys, 3 * n,)), U256::from(storage_key_not_in_list.0 .0.as_slice()) ); assert_eq!( interpreter.generation_state.memory.get(MemoryAddress::new( 0, AccessedStorageKeys, - 2 * n + 1, + 3 * n + 1, )), storage_key_not_in_list.1 ); + assert_eq!( + interpreter.generation_state.memory.get(MemoryAddress::new( + 0, + AccessedStorageKeys, + 3 * n + 2, + )), + storage_key_not_in_list.2 + ); Ok(()) }