mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-07 16:23:12 +00:00
SSTORE gas (#1007)
* Update storage access list and sload * Add noop for sys_sstore * Comments * Fix access list test
This commit is contained in:
parent
efd5a81bb7
commit
ab721fa340
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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::<Address>(), U256(rng.gen())))
|
||||
.map(|_| (rng.gen::<Address>(), U256(rng.gen()), U256(rng.gen())))
|
||||
.collect::<HashSet<_>>()
|
||||
.into_iter()
|
||||
.collect::<Vec<(Address, U256)>>();
|
||||
.collect::<Vec<(Address, U256, U256)>>();
|
||||
let storage_key_in_list = storage_keys[rng.gen_range(0..n)];
|
||||
let storage_key_not_in_list = (rng.gen::<Address>(), U256(rng.gen()));
|
||||
let storage_key_not_in_list = (rng.gen::<Address>(), 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(())
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user