Skip to content

Commit

Permalink
Eip 1153 (TLOAD/TSTORE) (#59)
Browse files Browse the repository at this point in the history
* Add transient storage

* Add basic tests and fix bugs

* Add more tests fix more bugs

* [WIP] Add revert test

* [WIP] expanding macro below global label

* Fix revert test bugs

* Fix revert test bugs

* Add missing files

* Apply suggestions from code review

Co-authored-by: Robin Salen <30937548+Nashtare@users.noreply.github.com>

* Address reviews

* chore: fix broken tests after merge

* Apply suggestions from code review

Co-authored-by: Robin Salen <30937548+Nashtare@users.noreply.github.com>

* Add kernel_mode false to  exit kernel in tests

* Fix problem with modified kernel

* Clippy

---------

Co-authored-by: Robin Salen <30937548+Nashtare@users.noreply.github.com>
Co-authored-by: David <dvdplm@gmail.com>
  • Loading branch information
3 people authored Mar 9, 2024
1 parent 23b7a28 commit e6b137c
Show file tree
Hide file tree
Showing 17 changed files with 730 additions and 181 deletions.
320 changes: 164 additions & 156 deletions evm_arithmetization/src/cpu/kernel/aggregator.rs

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions evm_arithmetization/src/cpu/kernel/asm/core/exception.asm
Original file line number Diff line number Diff line change
Expand Up @@ -393,9 +393,9 @@ gas_cost_for_opcode:
BYTES 0 // 0x59, MSIZE
BYTES 0 // 0x5a, GAS
BYTES @GAS_JUMPDEST // 0x5b, JUMPDEST
%rep 3 // 0x5c-0x5e, invalid
BYTES 0
%endrep
BYTES 0 // 0x5c, TLOAD
BYTES 0 // 0x5d, TSTORE
BYTES 0 // 0x5e, invalid

BYTES @GAS_BASE // 0x5f, PUSH0
%rep 32 // 0x60-0x7f, PUSH1-PUSH32
Expand Down
6 changes: 3 additions & 3 deletions evm_arithmetization/src/cpu/kernel/asm/core/syscall.asm
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ global syscall_jumptable:
JUMPTABLE sys_msize
JUMPTABLE sys_gas
JUMPTABLE panic // jumpdest is implemented natively
JUMPTABLE panic // 0x5c is an invalid opcode
JUMPTABLE panic // 0x5d is an invalid opcode
JUMPTABLE sys_mcopy
JUMPTABLE sys_tload
JUMPTABLE sys_tstore
JUMPTABLE panic // 0x5e is an invalid opcode
JUMPTABLE panic // 0x5f is an invalid opcode

// 0x60-0x6f
Expand Down
1 change: 0 additions & 1 deletion evm_arithmetization/src/cpu/kernel/asm/journal/journal.asm
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,6 @@
%mload_global_metadata(@GLOBAL_METADATA_CURRENT_CHECKPOINT)
%endmacro


%macro checkpoint
// stack: (empty)
%current_checkpoint
Expand Down
23 changes: 12 additions & 11 deletions evm_arithmetization/src/cpu/kernel/asm/journal/revert.asm
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@
// 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)
DUP1 %eq_const(@JOURNAL_ENTRY_CODE_CHANGE) %jumpi(revert_code_change)
DUP1 %eq_const(@JOURNAL_ENTRY_REFUND) %jumpi(revert_refund)
DUP1 %eq_const(@JOURNAL_ENTRY_ACCOUNT_CREATED) %jumpi(revert_account_created)
DUP1 %eq_const(@JOURNAL_ENTRY_LOG) %jumpi(revert_log)
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)
DUP1 %eq_const(@JOURNAL_ENTRY_CODE_CHANGE) %jumpi(revert_code_change)
DUP1 %eq_const(@JOURNAL_ENTRY_REFUND) %jumpi(revert_refund)
DUP1 %eq_const(@JOURNAL_ENTRY_ACCOUNT_CREATED) %jumpi(revert_account_created)
DUP1 %eq_const(@JOURNAL_ENTRY_LOG) %jumpi(revert_log)
DUP1 %eq_const(@JOURNAL_ENTRY_TRANSIENT_STORAGE_CHANGE) %jumpi(revert_transient_storage_change)
PANIC // This should never happen.
%%after:
// stack: journal_size-1
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// struct StorageChange { address, slot, prev_value }

%macro journal_add_transient_storage_change
%journal_add_3(@JOURNAL_ENTRY_TRANSIENT_STORAGE_CHANGE)
%endmacro

global revert_transient_storage_change:
// stack: entry_type, ptr, retdest
POP
%journal_load_3
// We will always write 0 for deletions as it makes no difference.
// stack: address, slot, prev_value, retdest
%search_transient_storage
// The value must have been stored
%assert_nonzero
// stack: pos, addr, value, key, prev_value, retdest
%add_const(2)
DUP5
MSTORE_GENERAL
%pop4
JUMP
158 changes: 158 additions & 0 deletions evm_arithmetization/src/cpu/kernel/asm/memory/transient_storage.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// Transient data storage


/// The transient storage is stored in an array. The length of the array is stored in the global metadata.
/// For storage keys, the address and key are stored as two consecutive elements.
/// The array is stored in the SEGMENT_TRANSIENT_STORAGE segment in the kernel memory (context=0).
/// Searching and inserting is done by doing a linear search through the array.
/// If the key isn't found in the array, it is inserted at the end.
/// TODO: Look into using a more efficient data structure.

%macro search_transient_storage
%stack (addr, key) -> (addr, key, %%after)
%jump(search_transient_storage)
%%after:
// stack: (is_present, pos, addr, key, val)
%endmacro

/// Looks for an address, key pair into the transient storage. Returns 1 and the position in @SEGMENT_TRANSIENT_STORAGE
/// if present or 0 and @GLOBAL_METADATA_TRANSIENT_STORAGE_LEN if not.
global search_transient_storage:
// stack: addr, key, retdest
%mload_global_metadata(@GLOBAL_METADATA_TRANSIENT_STORAGE_LEN)
// stack: len, addr, key, retdest
PUSH @SEGMENT_TRANSIENT_STORAGE ADD
PUSH @SEGMENT_TRANSIENT_STORAGE
search_transient_storage_loop:
// `i` and `len` are both scaled by SEGMENT_TRANSIENT_STORAGE
%stack (i, len, addr, key, retdest) -> (i, len, i, len, addr, key, retdest)
EQ %jumpi(search_transient_storage_not_found)
// stack: i, len, addr, key, retdest
DUP1
MLOAD_GENERAL
// stack: loaded_addr, i, len, addr, key, retdest
DUP4
// stack: addr, loaded_addr, i, len, addr, key, retdest
SUB // functions as NEQ
// stack: addr != loaded_addr, i, len, addr, key, retdest
%jumpi(increment_and_loop)

// Addresses match, but we need to check for keys as well
DUP1
%increment
MLOAD_GENERAL
// stack: loaded_key, i, len, addr, key, retdest
DUP5
// stack: key, loaded_key, i, len, addr, key, retdest
EQ
%jumpi(search_transient_storage_found)
increment_and_loop:
// stack: i, len, addr, key, retdest
%increment
%jump(search_transient_storage_loop)

search_transient_storage_not_found:
%stack (i, len, addr, key, retdest) -> (retdest, 0, i, addr, 0, key) // Return 0 to indicate that the address, key was not found.
JUMP

search_transient_storage_found:
DUP1 %add_const(2)
MLOAD_GENERAL
%stack (val, i, len, addr, key, retdest) -> (retdest, 1, i, addr, val, key) // Return 1 to indicate that the address was already present.
JUMP

%macro tload_current
%stack (slot) -> (slot, %%after)
%jump(tload_current)
%%after:
%endmacro

global tload_current:
%address
// stack: addr, slot, retdest
%search_transient_storage
// stack: found, pos, addr, val, slot, retdest
%jumpi(tload_found)
// The value is not in memory so we return 0
%stack (pos, addr, val, slot, retdest) -> (retdest, 0)
JUMP
tload_found:
// stack: pos, addr, val, slot, retdest
%stack (pos, addr, val, slot, retdest) -> (retdest, val)
JUMP

// Read a word from the current account's transient storage list
//
// Pre stack: kexit_info, slot
// Post stack: value
global sys_tload:
// stack: kexit_info, slot
SWAP1
// stack: slot, kexit_info
%tload_current
SWAP1

%charge_gas_const(@GAS_WARMACCESS)
// stack: kexit_info, value
EXIT_KERNEL

// Write a word to the current account's transient storage.
//
// Pre stack: kexit_info, slot, value
// Post stack: (empty)

global sys_tstore:
%check_static
%charge_gas_const(@GAS_WARMACCESS)
%stack (kexit_info, slot, value) -> (slot, value, kexit_info)
%address
%search_transient_storage
// stack: found, pos, addr, original_value, slot, value, kexit_info
POP
// If the address and slot pair was not present pos will be pointing to the end of the array.
DUP1 DUP3
// stack: addr, pos, pos, addr, original_value, slot, value, kexit_info
MSTORE_GENERAL
%increment DUP1
DUP5
// stack: slot, pos', pos', addr, original_value, slot, value, kexit_info
MSTORE_GENERAL
%increment DUP1
DUP6
MSTORE_GENERAL
// stack: pos'', addr, original_value, slot, value, kexit_info
// If pos'' > @GLOBAL_METADATA_TRANSIENT_STORAGE_LEN we need to also store the new @GLOBAL_METADATA_TRANSIENT_STORAGE_LEN
%mload_global_metadata(@GLOBAL_METADATA_TRANSIENT_STORAGE_LEN)
DUP2
GT
%jumpi(new_transient_storage_len)
POP
sys_tstore_charge_gas:
// stack: addr, original_value, slot, value, kexit_info
// Check if `value` is equal to `current_value`, and if so exit the kernel early.
%stack
(addr, original_value, slot, value, kexit_info) ->
(value, original_value, addr, slot, original_value, kexit_info)
EQ %jumpi(sstore_noop)

// stack: addr, slot, original_value, kexit_info
global debug_journal:
%journal_add_transient_storage_change

// stack: kexit_info
EXIT_KERNEL

new_transient_storage_len:
// Store the new (unscaled) length.
// stack: addr, original_value, slot, value, kexit_info
PUSH @SEGMENT_TRANSIENT_STORAGE
PUSH 1
SUB // 1 - seg
ADD // new_len = (addr - seg) + 1
%mstore_global_metadata(@GLOBAL_METADATA_TRANSIENT_STORAGE_LEN)
%jump(sys_tstore_charge_gas)

sstore_noop:
// stack: current_value, slot, value, kexit_info
%pop3
EXIT_KERNEL
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub(crate) const INVALID_OPCODES_USER: U256 = u256_from_set_index_ranges(&[
0x1e..=0x1f,
0x21..=0x2f,
0x4a..=0x4f,
0x5c..=0x5e,
0x5e..=0x5e,
0xa5..=0xef,
0xf6..=0xf9,
0xfb..=0xfc,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ pub(crate) enum GlobalMetadata {
KernelHash,
KernelLen,

/// The length of the transient storage segment.
TransientStorageLen,

// Start of the blob versioned hashes in the RLP for type-3 txns.
BlobVersionedHashesRlpStart,
// Length of the blob versioned hashes in the RLP for type-3 txns.
Expand All @@ -112,7 +115,7 @@ pub(crate) enum GlobalMetadata {
}

impl GlobalMetadata {
pub(crate) const COUNT: usize = 55;
pub(crate) const COUNT: usize = 56;

/// Unscales this virtual offset by their respective `Segment` value.
pub(crate) const fn unscale(&self) -> usize {
Expand Down Expand Up @@ -173,6 +176,7 @@ impl GlobalMetadata {
Self::CreatedContractsLen,
Self::KernelHash,
Self::KernelLen,
Self::TransientStorageLen,
Self::BlobVersionedHashesRlpStart,
Self::BlobVersionedHashesRlpLen,
Self::BlobVersionedHashesLen,
Expand Down Expand Up @@ -234,6 +238,7 @@ impl GlobalMetadata {
Self::CreatedContractsLen => "GLOBAL_METADATA_CREATED_CONTRACTS_LEN",
Self::KernelHash => "GLOBAL_METADATA_KERNEL_HASH",
Self::KernelLen => "GLOBAL_METADATA_KERNEL_LEN",
Self::TransientStorageLen => "GLOBAL_METADATA_TRANSIENT_STORAGE_LEN",
Self::BlobVersionedHashesRlpStart => "GLOBAL_METADATA_BLOB_VERSIONED_HASHES_RLP_START",
Self::BlobVersionedHashesRlpLen => "GLOBAL_METADATA_BLOB_VERSIONED_HASHES_RLP_LEN",
Self::BlobVersionedHashesLen => "GLOBAL_METADATA_BLOB_VERSIONED_HASHES_LEN",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ pub(crate) enum JournalEntry {
Refund = 8,
AccountCreated = 9,
Log = 10,
TransientStorageChange = 11,
}

impl JournalEntry {
pub(crate) const COUNT: usize = 11;
pub(crate) const COUNT: usize = 12;

pub(crate) const fn all() -> [Self; Self::COUNT] {
[
Expand All @@ -29,6 +30,7 @@ impl JournalEntry {
Self::Refund,
Self::AccountCreated,
Self::Log,
Self::TransientStorageChange,
]
}

Expand All @@ -46,6 +48,7 @@ impl JournalEntry {
Self::Refund => "JOURNAL_ENTRY_REFUND",
Self::AccountCreated => "JOURNAL_ENTRY_ACCOUNT_CREATED",
Self::Log => "JOURNAL_ENTRY_LOG",
Self::TransientStorageChange => "JOURNAL_ENTRY_TRANSIENT_STORAGE_CHANGE",
}
}
}
2 changes: 2 additions & 0 deletions evm_arithmetization/src/cpu/kernel/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ pub fn get_opcode(mnemonic: &str) -> u8 {
"MSIZE" => 0x59,
"GAS" => 0x5a,
"JUMPDEST" => 0x5b,
"TLOAD" => 0x5c,
"TSTORE" => 0x5d,
"MCOPY" => 0x5e,
"DUP1" => 0x80,
"DUP2" => 0x81,
Expand Down
2 changes: 1 addition & 1 deletion evm_arithmetization/src/cpu/kernel/stack/permutations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ fn combine_cycles<T: Eq + Hash + Clone>(mut perm: Vec<Vec<usize>>, lst_a: &[T])
if cycl.contains(term) {
if joinedperm.is_empty() {
// This is the first cycle we have found including an element of positions.
joinedperm = cycl.clone();
joinedperm.clone_from(&cycl);
pos = cycl.iter().position(|x| x == term).unwrap();
} else {
// Need to merge 2 cycles. If A_i = A_j then the permutations
Expand Down
5 changes: 5 additions & 0 deletions evm_arithmetization/src/cpu/kernel/tests/checkpoint_label.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
global checkpoint:
// stack: (empty)
%checkpoint
// stack: (empty)
JUMP
1 change: 1 addition & 0 deletions evm_arithmetization/src/cpu/kernel/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod receipt;
mod rlp;
mod signed_syscalls;
mod transaction_parsing;
mod transient_storage;

use std::str::FromStr;

Expand Down
Loading

0 comments on commit e6b137c

Please sign in to comment.