From e6b137cbc6b791e1ad5c3490218e77d84b7c7774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alonso=20Gonz=C3=A1lez?= Date: Sat, 9 Mar 2024 10:46:01 +0100 Subject: [PATCH] Eip 1153 (TLOAD/TSTORE) (#59) * 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 --- .../src/cpu/kernel/aggregator.rs | 320 +++++++++-------- .../src/cpu/kernel/asm/core/exception.asm | 6 +- .../src/cpu/kernel/asm/core/syscall.asm | 6 +- .../src/cpu/kernel/asm/journal/journal.asm | 1 - .../src/cpu/kernel/asm/journal/revert.asm | 23 +- .../asm/journal/transient_storage_change.asm | 21 ++ .../kernel/asm/memory/transient_storage.asm | 158 ++++++++ .../src/cpu/kernel/constants/exc_bitfields.rs | 2 +- .../cpu/kernel/constants/global_metadata.rs | 7 +- .../src/cpu/kernel/constants/journal_entry.rs | 5 +- evm_arithmetization/src/cpu/kernel/opcodes.rs | 2 + .../src/cpu/kernel/stack/permutations.rs | 2 +- .../src/cpu/kernel/tests/checkpoint_label.asm | 5 + .../src/cpu/kernel/tests/mod.rs | 1 + .../src/cpu/kernel/tests/transient_storage.rs | 339 ++++++++++++++++++ evm_arithmetization/src/memory/segments.rs | 11 +- evm_arithmetization/src/witness/transition.rs | 2 + 17 files changed, 730 insertions(+), 181 deletions(-) create mode 100644 evm_arithmetization/src/cpu/kernel/asm/journal/transient_storage_change.asm create mode 100644 evm_arithmetization/src/cpu/kernel/asm/memory/transient_storage.asm create mode 100644 evm_arithmetization/src/cpu/kernel/tests/checkpoint_label.asm create mode 100644 evm_arithmetization/src/cpu/kernel/tests/transient_storage.rs diff --git a/evm_arithmetization/src/cpu/kernel/aggregator.rs b/evm_arithmetization/src/cpu/kernel/aggregator.rs index 3620fdea3..9b2b0a85f 100644 --- a/evm_arithmetization/src/cpu/kernel/aggregator.rs +++ b/evm_arithmetization/src/cpu/kernel/aggregator.rs @@ -7,168 +7,176 @@ use super::assembler::{assemble, Kernel}; use crate::cpu::kernel::constants::evm_constants; use crate::cpu::kernel::parser::parse; -pub static KERNEL: Lazy = Lazy::new(combined_kernel); +pub const NUMBER_KERNEL_FILES: usize = 152; -pub(crate) fn combined_kernel() -> Kernel { - let files = vec![ - "global jumped_to_0: PANIC", - "global jumped_to_1: PANIC", - include_str!("asm/beacon_roots.asm"), - include_str!("asm/bignum/add.asm"), - include_str!("asm/bignum/addmul.asm"), - include_str!("asm/bignum/cmp.asm"), - include_str!("asm/bignum/isone.asm"), - include_str!("asm/bignum/iszero.asm"), - include_str!("asm/bignum/modexp.asm"), - include_str!("asm/bignum/modmul.asm"), - include_str!("asm/bignum/mul.asm"), - include_str!("asm/bignum/shr.asm"), - include_str!("asm/bignum/util.asm"), - include_str!("asm/core/call.asm"), - include_str!("asm/core/call_gas.asm"), - include_str!("asm/core/create.asm"), - include_str!("asm/core/create_addresses.asm"), - include_str!("asm/core/create_contract_account.asm"), - include_str!("asm/core/exception.asm"), - include_str!("asm/core/create_receipt.asm"), - include_str!("asm/core/gas.asm"), - include_str!("asm/core/intrinsic_gas.asm"), - include_str!("asm/core/jumpdest_analysis.asm"), - include_str!("asm/core/nonce.asm"), - include_str!("asm/core/process_txn.asm"), - include_str!("asm/core/syscall.asm"), - include_str!("asm/core/terminate.asm"), - include_str!("asm/core/transfer.asm"), - include_str!("asm/core/util.asm"), - include_str!("asm/core/access_lists.asm"), - include_str!("asm/core/log.asm"), - include_str!("asm/core/selfdestruct_list.asm"), - include_str!("asm/core/touched_addresses.asm"), - include_str!("asm/core/withdrawals.asm"), - include_str!("asm/core/precompiles/main.asm"), - include_str!("asm/core/precompiles/ecrec.asm"), - include_str!("asm/core/precompiles/sha256.asm"), - include_str!("asm/core/precompiles/rip160.asm"), - include_str!("asm/core/precompiles/id.asm"), - include_str!("asm/core/precompiles/expmod.asm"), - include_str!("asm/core/precompiles/bn_add.asm"), - include_str!("asm/core/precompiles/bn_mul.asm"), - include_str!("asm/core/precompiles/snarkv.asm"), - include_str!("asm/core/precompiles/blake2_f.asm"), - include_str!("asm/curve/bls381/util.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/constants.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/curve_add.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/curve_mul.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/final_exponent.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/glv.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/miller_loop.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/msm.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/pairing.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/precomputation.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/twisted_curve.asm"), - include_str!("asm/curve/bn254/field_arithmetic/degree_6_mul.asm"), - include_str!("asm/curve/bn254/field_arithmetic/degree_12_mul.asm"), - include_str!("asm/curve/bn254/field_arithmetic/frobenius.asm"), - include_str!("asm/curve/bn254/field_arithmetic/inverse.asm"), - include_str!("asm/curve/bn254/field_arithmetic/util.asm"), - include_str!("asm/curve/common.asm"), - include_str!("asm/curve/secp256k1/curve_add.asm"), - include_str!("asm/curve/secp256k1/ecrecover.asm"), - include_str!("asm/curve/secp256k1/inverse_scalar.asm"), - include_str!("asm/curve/secp256k1/lift_x.asm"), - include_str!("asm/curve/secp256k1/moddiv.asm"), - include_str!("asm/curve/secp256k1/glv.asm"), - include_str!("asm/curve/secp256k1/precomputation.asm"), - include_str!("asm/curve/wnaf.asm"), - include_str!("asm/exp.asm"), - include_str!("asm/halt.asm"), - include_str!("asm/hash/blake2/addresses.asm"), - include_str!("asm/hash/blake2/blake2_f.asm"), - // include_str!("asm/hash/blake2/blake2b.asm"), - // include_str!("asm/hash/blake2/compression.asm"), - include_str!("asm/hash/blake2/g_functions.asm"), - include_str!("asm/hash/blake2/hash.asm"), - include_str!("asm/hash/blake2/iv.asm"), - include_str!("asm/hash/blake2/ops.asm"), - include_str!("asm/hash/blake2/permutations.asm"), - include_str!("asm/hash/ripemd/box.asm"), - include_str!("asm/hash/ripemd/compression.asm"), - include_str!("asm/hash/ripemd/constants.asm"), - include_str!("asm/hash/ripemd/functions.asm"), - include_str!("asm/hash/ripemd/main.asm"), - include_str!("asm/hash/ripemd/update.asm"), - include_str!("asm/hash/sha2/compression.asm"), - include_str!("asm/hash/sha2/constants.asm"), - include_str!("asm/hash/sha2/main.asm"), - include_str!("asm/hash/sha2/message_schedule.asm"), - include_str!("asm/hash/sha2/ops.asm"), - include_str!("asm/hash/sha2/temp_words.asm"), - include_str!("asm/hash/sha2/write_length.asm"), - include_str!("asm/main.asm"), - include_str!("asm/memory/core.asm"), - include_str!("asm/memory/memcpy.asm"), - include_str!("asm/memory/memset.asm"), - include_str!("asm/memory/metadata.asm"), - include_str!("asm/memory/packing.asm"), - include_str!("asm/memory/syscalls.asm"), - include_str!("asm/memory/txn_fields.asm"), - include_str!("asm/mpt/accounts.asm"), - include_str!("asm/mpt/delete/delete.asm"), - include_str!("asm/mpt/delete/delete_branch.asm"), - include_str!("asm/mpt/delete/delete_extension.asm"), - include_str!("asm/mpt/hash/hash.asm"), - include_str!("asm/mpt/hash/hash_trie_specific.asm"), - include_str!("asm/mpt/hex_prefix.asm"), - include_str!("asm/mpt/insert/insert.asm"), - include_str!("asm/mpt/insert/insert_extension.asm"), - include_str!("asm/mpt/insert/insert_leaf.asm"), - include_str!("asm/mpt/insert/insert_trie_specific.asm"), - include_str!("asm/mpt/read.asm"), - include_str!("asm/mpt/storage/storage_read.asm"), - include_str!("asm/mpt/storage/storage_write.asm"), - include_str!("asm/mpt/util.asm"), - include_str!("asm/rlp/decode.asm"), - include_str!("asm/rlp/encode.asm"), - include_str!("asm/rlp/encode_rlp_scalar.asm"), - include_str!("asm/rlp/encode_rlp_string.asm"), - include_str!("asm/rlp/increment_bounded_rlp.asm"), - include_str!("asm/rlp/num_bytes.asm"), - 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/refund.asm"), - include_str!("asm/journal/account_created.asm"), - include_str!("asm/journal/revert.asm"), - include_str!("asm/journal/log.asm"), - include_str!("asm/transactions/common_decoding.asm"), - include_str!("asm/transactions/router.asm"), - include_str!("asm/transactions/type_0.asm"), - include_str!("asm/transactions/type_1.asm"), - include_str!("asm/transactions/type_2.asm"), - include_str!("asm/transactions/type_3.asm"), - include_str!("asm/util/assertions.asm"), - include_str!("asm/util/basic_macros.asm"), - include_str!("asm/util/keccak.asm"), - include_str!("asm/util/math.asm"), - include_str!("asm/account_code.asm"), - include_str!("asm/balance.asm"), - include_str!("asm/bloom_filter.asm"), - include_str!("asm/global_exit_root.asm"), - ]; +pub static KERNEL_FILES: [&str; NUMBER_KERNEL_FILES] = [ + "global jumped_to_0: PANIC", + "global jumped_to_1: PANIC", + include_str!("asm/beacon_roots.asm"), + include_str!("asm/bignum/add.asm"), + include_str!("asm/bignum/addmul.asm"), + include_str!("asm/bignum/cmp.asm"), + include_str!("asm/bignum/isone.asm"), + include_str!("asm/bignum/iszero.asm"), + include_str!("asm/bignum/modexp.asm"), + include_str!("asm/bignum/modmul.asm"), + include_str!("asm/bignum/mul.asm"), + include_str!("asm/bignum/shr.asm"), + include_str!("asm/bignum/util.asm"), + include_str!("asm/core/call.asm"), + include_str!("asm/core/call_gas.asm"), + include_str!("asm/core/create.asm"), + include_str!("asm/core/create_addresses.asm"), + include_str!("asm/core/create_contract_account.asm"), + include_str!("asm/core/exception.asm"), + include_str!("asm/core/create_receipt.asm"), + include_str!("asm/core/gas.asm"), + include_str!("asm/core/intrinsic_gas.asm"), + include_str!("asm/core/jumpdest_analysis.asm"), + include_str!("asm/core/nonce.asm"), + include_str!("asm/core/process_txn.asm"), + include_str!("asm/core/syscall.asm"), + include_str!("asm/core/terminate.asm"), + include_str!("asm/core/transfer.asm"), + include_str!("asm/core/util.asm"), + include_str!("asm/core/access_lists.asm"), + include_str!("asm/core/log.asm"), + include_str!("asm/core/selfdestruct_list.asm"), + include_str!("asm/core/touched_addresses.asm"), + include_str!("asm/core/withdrawals.asm"), + include_str!("asm/core/precompiles/main.asm"), + include_str!("asm/core/precompiles/ecrec.asm"), + include_str!("asm/core/precompiles/sha256.asm"), + include_str!("asm/core/precompiles/rip160.asm"), + include_str!("asm/core/precompiles/id.asm"), + include_str!("asm/core/precompiles/expmod.asm"), + include_str!("asm/core/precompiles/bn_add.asm"), + include_str!("asm/core/precompiles/bn_mul.asm"), + include_str!("asm/core/precompiles/snarkv.asm"), + include_str!("asm/core/precompiles/blake2_f.asm"), + include_str!("asm/curve/bls381/util.asm"), + include_str!("asm/curve/bn254/curve_arithmetic/constants.asm"), + include_str!("asm/curve/bn254/curve_arithmetic/curve_add.asm"), + include_str!("asm/curve/bn254/curve_arithmetic/curve_mul.asm"), + include_str!("asm/curve/bn254/curve_arithmetic/final_exponent.asm"), + include_str!("asm/curve/bn254/curve_arithmetic/glv.asm"), + include_str!("asm/curve/bn254/curve_arithmetic/miller_loop.asm"), + include_str!("asm/curve/bn254/curve_arithmetic/msm.asm"), + include_str!("asm/curve/bn254/curve_arithmetic/pairing.asm"), + include_str!("asm/curve/bn254/curve_arithmetic/precomputation.asm"), + include_str!("asm/curve/bn254/curve_arithmetic/twisted_curve.asm"), + include_str!("asm/curve/bn254/field_arithmetic/degree_6_mul.asm"), + include_str!("asm/curve/bn254/field_arithmetic/degree_12_mul.asm"), + include_str!("asm/curve/bn254/field_arithmetic/frobenius.asm"), + include_str!("asm/curve/bn254/field_arithmetic/inverse.asm"), + include_str!("asm/curve/bn254/field_arithmetic/util.asm"), + include_str!("asm/curve/common.asm"), + include_str!("asm/curve/secp256k1/curve_add.asm"), + include_str!("asm/curve/secp256k1/ecrecover.asm"), + include_str!("asm/curve/secp256k1/inverse_scalar.asm"), + include_str!("asm/curve/secp256k1/lift_x.asm"), + include_str!("asm/curve/secp256k1/moddiv.asm"), + include_str!("asm/curve/secp256k1/glv.asm"), + include_str!("asm/curve/secp256k1/precomputation.asm"), + include_str!("asm/curve/wnaf.asm"), + include_str!("asm/exp.asm"), + include_str!("asm/halt.asm"), + include_str!("asm/hash/blake2/addresses.asm"), + include_str!("asm/hash/blake2/blake2_f.asm"), + // include_str!("asm/hash/blake2/blake2b.asm"), + // include_str!("asm/hash/blake2/compression.asm"), + include_str!("asm/hash/blake2/g_functions.asm"), + include_str!("asm/hash/blake2/hash.asm"), + include_str!("asm/hash/blake2/iv.asm"), + include_str!("asm/hash/blake2/ops.asm"), + include_str!("asm/hash/blake2/permutations.asm"), + include_str!("asm/hash/ripemd/box.asm"), + include_str!("asm/hash/ripemd/compression.asm"), + include_str!("asm/hash/ripemd/constants.asm"), + include_str!("asm/hash/ripemd/functions.asm"), + include_str!("asm/hash/ripemd/main.asm"), + include_str!("asm/hash/ripemd/update.asm"), + include_str!("asm/hash/sha2/compression.asm"), + include_str!("asm/hash/sha2/constants.asm"), + include_str!("asm/hash/sha2/main.asm"), + include_str!("asm/hash/sha2/message_schedule.asm"), + include_str!("asm/hash/sha2/ops.asm"), + include_str!("asm/hash/sha2/temp_words.asm"), + include_str!("asm/hash/sha2/write_length.asm"), + include_str!("asm/main.asm"), + include_str!("asm/memory/core.asm"), + include_str!("asm/memory/memcpy.asm"), + include_str!("asm/memory/memset.asm"), + include_str!("asm/memory/metadata.asm"), + include_str!("asm/memory/packing.asm"), + include_str!("asm/memory/syscalls.asm"), + include_str!("asm/memory/txn_fields.asm"), + include_str!("asm/memory/transient_storage.asm"), + include_str!("asm/mpt/accounts.asm"), + include_str!("asm/mpt/delete/delete.asm"), + include_str!("asm/mpt/delete/delete_branch.asm"), + include_str!("asm/mpt/delete/delete_extension.asm"), + include_str!("asm/mpt/hash/hash.asm"), + include_str!("asm/mpt/hash/hash_trie_specific.asm"), + include_str!("asm/mpt/hex_prefix.asm"), + include_str!("asm/mpt/insert/insert.asm"), + include_str!("asm/mpt/insert/insert_extension.asm"), + include_str!("asm/mpt/insert/insert_leaf.asm"), + include_str!("asm/mpt/insert/insert_trie_specific.asm"), + include_str!("asm/mpt/read.asm"), + include_str!("asm/mpt/storage/storage_read.asm"), + include_str!("asm/mpt/storage/storage_write.asm"), + include_str!("asm/mpt/util.asm"), + include_str!("asm/rlp/decode.asm"), + include_str!("asm/rlp/encode.asm"), + include_str!("asm/rlp/encode_rlp_scalar.asm"), + include_str!("asm/rlp/encode_rlp_string.asm"), + include_str!("asm/rlp/increment_bounded_rlp.asm"), + include_str!("asm/rlp/num_bytes.asm"), + 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/refund.asm"), + include_str!("asm/journal/account_created.asm"), + include_str!("asm/journal/revert.asm"), + include_str!("asm/journal/log.asm"), + include_str!("asm/journal/transient_storage_change.asm"), + include_str!("asm/transactions/common_decoding.asm"), + include_str!("asm/transactions/router.asm"), + include_str!("asm/transactions/type_0.asm"), + include_str!("asm/transactions/type_1.asm"), + include_str!("asm/transactions/type_2.asm"), + include_str!("asm/transactions/type_3.asm"), + include_str!("asm/util/assertions.asm"), + include_str!("asm/util/basic_macros.asm"), + include_str!("asm/util/keccak.asm"), + include_str!("asm/util/math.asm"), + include_str!("asm/account_code.asm"), + include_str!("asm/balance.asm"), + include_str!("asm/bloom_filter.asm"), + include_str!("asm/global_exit_root.asm"), +]; + +pub static KERNEL: Lazy = Lazy::new(combined_kernel); +pub(crate) fn combined_kernel_from_files(files: [&str; N]) -> Kernel { let parsed_files = files.iter().map(|f| parse(f)).collect_vec(); assemble(parsed_files, evm_constants(), true) } +pub(crate) fn combined_kernel() -> Kernel { + combined_kernel_from_files(KERNEL_FILES) +} + #[cfg(test)] mod tests { use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; diff --git a/evm_arithmetization/src/cpu/kernel/asm/core/exception.asm b/evm_arithmetization/src/cpu/kernel/asm/core/exception.asm index 54500ec3e..fb260a20e 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/core/exception.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/core/exception.asm @@ -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 diff --git a/evm_arithmetization/src/cpu/kernel/asm/core/syscall.asm b/evm_arithmetization/src/cpu/kernel/asm/core/syscall.asm index 6a42c5070..f74b0d529 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/core/syscall.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/core/syscall.asm @@ -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 diff --git a/evm_arithmetization/src/cpu/kernel/asm/journal/journal.asm b/evm_arithmetization/src/cpu/kernel/asm/journal/journal.asm index 39b6c9f1b..96fa8aae8 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/journal/journal.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/journal/journal.asm @@ -182,7 +182,6 @@ %mload_global_metadata(@GLOBAL_METADATA_CURRENT_CHECKPOINT) %endmacro - %macro checkpoint // stack: (empty) %current_checkpoint diff --git a/evm_arithmetization/src/cpu/kernel/asm/journal/revert.asm b/evm_arithmetization/src/cpu/kernel/asm/journal/revert.asm index 857bf612b..fd9770bbc 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/journal/revert.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/journal/revert.asm @@ -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 diff --git a/evm_arithmetization/src/cpu/kernel/asm/journal/transient_storage_change.asm b/evm_arithmetization/src/cpu/kernel/asm/journal/transient_storage_change.asm new file mode 100644 index 000000000..dcd43e456 --- /dev/null +++ b/evm_arithmetization/src/cpu/kernel/asm/journal/transient_storage_change.asm @@ -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 \ No newline at end of file diff --git a/evm_arithmetization/src/cpu/kernel/asm/memory/transient_storage.asm b/evm_arithmetization/src/cpu/kernel/asm/memory/transient_storage.asm new file mode 100644 index 000000000..6c7b3929c --- /dev/null +++ b/evm_arithmetization/src/cpu/kernel/asm/memory/transient_storage.asm @@ -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 diff --git a/evm_arithmetization/src/cpu/kernel/constants/exc_bitfields.rs b/evm_arithmetization/src/cpu/kernel/constants/exc_bitfields.rs index cb5bb539f..9ffc626f5 100644 --- a/evm_arithmetization/src/cpu/kernel/constants/exc_bitfields.rs +++ b/evm_arithmetization/src/cpu/kernel/constants/exc_bitfields.rs @@ -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, diff --git a/evm_arithmetization/src/cpu/kernel/constants/global_metadata.rs b/evm_arithmetization/src/cpu/kernel/constants/global_metadata.rs index c0c84d3bd..43182bc1d 100644 --- a/evm_arithmetization/src/cpu/kernel/constants/global_metadata.rs +++ b/evm_arithmetization/src/cpu/kernel/constants/global_metadata.rs @@ -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. @@ -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 { @@ -173,6 +176,7 @@ impl GlobalMetadata { Self::CreatedContractsLen, Self::KernelHash, Self::KernelLen, + Self::TransientStorageLen, Self::BlobVersionedHashesRlpStart, Self::BlobVersionedHashesRlpLen, Self::BlobVersionedHashesLen, @@ -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", diff --git a/evm_arithmetization/src/cpu/kernel/constants/journal_entry.rs b/evm_arithmetization/src/cpu/kernel/constants/journal_entry.rs index d84f2ade8..7a21fd646 100644 --- a/evm_arithmetization/src/cpu/kernel/constants/journal_entry.rs +++ b/evm_arithmetization/src/cpu/kernel/constants/journal_entry.rs @@ -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] { [ @@ -29,6 +30,7 @@ impl JournalEntry { Self::Refund, Self::AccountCreated, Self::Log, + Self::TransientStorageChange, ] } @@ -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", } } } diff --git a/evm_arithmetization/src/cpu/kernel/opcodes.rs b/evm_arithmetization/src/cpu/kernel/opcodes.rs index cfa44e01f..6491003f1 100644 --- a/evm_arithmetization/src/cpu/kernel/opcodes.rs +++ b/evm_arithmetization/src/cpu/kernel/opcodes.rs @@ -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, diff --git a/evm_arithmetization/src/cpu/kernel/stack/permutations.rs b/evm_arithmetization/src/cpu/kernel/stack/permutations.rs index 77a3a7725..72bcf42a7 100644 --- a/evm_arithmetization/src/cpu/kernel/stack/permutations.rs +++ b/evm_arithmetization/src/cpu/kernel/stack/permutations.rs @@ -157,7 +157,7 @@ fn combine_cycles(mut perm: Vec>, 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 diff --git a/evm_arithmetization/src/cpu/kernel/tests/checkpoint_label.asm b/evm_arithmetization/src/cpu/kernel/tests/checkpoint_label.asm new file mode 100644 index 000000000..22406f426 --- /dev/null +++ b/evm_arithmetization/src/cpu/kernel/tests/checkpoint_label.asm @@ -0,0 +1,5 @@ +global checkpoint: + // stack: (empty) + %checkpoint + // stack: (empty) + JUMP \ No newline at end of file diff --git a/evm_arithmetization/src/cpu/kernel/tests/mod.rs b/evm_arithmetization/src/cpu/kernel/tests/mod.rs index bd61c897e..e738e55d2 100644 --- a/evm_arithmetization/src/cpu/kernel/tests/mod.rs +++ b/evm_arithmetization/src/cpu/kernel/tests/mod.rs @@ -19,6 +19,7 @@ mod receipt; mod rlp; mod signed_syscalls; mod transaction_parsing; +mod transient_storage; use std::str::FromStr; diff --git a/evm_arithmetization/src/cpu/kernel/tests/transient_storage.rs b/evm_arithmetization/src/cpu/kernel/tests/transient_storage.rs new file mode 100644 index 000000000..9eb377d84 --- /dev/null +++ b/evm_arithmetization/src/cpu/kernel/tests/transient_storage.rs @@ -0,0 +1,339 @@ +use std::array; + +use anyhow::Result; +use ethereum_types::{Address, U256}; +use once_cell::sync::Lazy; +use pest::error::Error; +use plonky2::field::goldilocks_field::GoldilocksField as F; +use rand::{thread_rng, Rng}; + +use crate::cpu::kernel::aggregator::{ + combined_kernel_from_files, KERNEL_FILES, NUMBER_KERNEL_FILES, +}; +use crate::cpu::kernel::assembler::Kernel; +use crate::cpu::kernel::constants::context_metadata::ContextMetadata; +use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; +use crate::cpu::kernel::interpreter::{self, Interpreter}; +use crate::generation::state::GenerationState; +use crate::memory::segments::Segment; +use crate::witness::errors::ProgramError; +use crate::witness::memory::MemoryAddress; +use crate::GenerationInputs; + +#[test] +fn test_tstore() -> Result<()> { + let sys_tstore = crate::cpu::kernel::aggregator::KERNEL.global_labels["sys_tstore"]; + + let kexit_info = U256::from(0xdeadbeefu32) + (U256::from(u64::from(true)) << 32); + + let initial_stack = vec![ + 1.into(), // val + 2.into(), // slot + kexit_info, + ]; + + let mut interpreter: Interpreter = Interpreter::new(sys_tstore, initial_stack); + let gas_limit_address = MemoryAddress { + context: 0, + segment: Segment::ContextMetadata.unscale(), + virt: ContextMetadata::GasLimit.unscale(), + }; + let addr_addr = MemoryAddress { + context: 0, + segment: Segment::ContextMetadata.unscale(), + virt: ContextMetadata::Address.unscale(), + }; + interpreter + .generation_state + .memory + .set(gas_limit_address, 100.into()); + interpreter.generation_state.memory.set(addr_addr, 3.into()); + + interpreter.run()?; + + assert_eq!(interpreter.generation_state.registers.gas_used, 100); + + let stored_addr = MemoryAddress::new(0, Segment::TransientStorage, 0); + let stored_slot = MemoryAddress::new(0, Segment::TransientStorage, 1); + let stored_val = MemoryAddress::new(0, Segment::TransientStorage, 2); + assert_eq!( + interpreter + .generation_state + .memory + .get(stored_addr) + .unwrap(), + 3.into(), + ); + assert_eq!( + interpreter + .generation_state + .memory + .get(stored_slot) + .unwrap(), + 2.into(), + ); + assert_eq!( + interpreter.generation_state.memory.get(stored_val).unwrap(), + 1.into(), + ); + + Ok(()) +} + +#[test] +fn test_tstore_tload() -> Result<()> { + let sys_tstore = crate::cpu::kernel::aggregator::KERNEL.global_labels["sys_tstore"]; + + let kexit_info = U256::from(0xdeadbeefu32) + (U256::from(u64::from(true)) << 32); + + let initial_stack = vec![ + 1.into(), // val + 2.into(), // slot + kexit_info, + ]; + + let mut interpreter: Interpreter = Interpreter::new(sys_tstore, initial_stack); + let gas_limit_address = MemoryAddress { + context: 0, + segment: Segment::ContextMetadata.unscale(), + virt: ContextMetadata::GasLimit.unscale(), + }; + let addr_addr = MemoryAddress { + context: 0, + segment: Segment::ContextMetadata.unscale(), + virt: ContextMetadata::Address.unscale(), + }; + interpreter + .generation_state + .memory + .set(gas_limit_address, 200.into()); + interpreter.generation_state.memory.set(addr_addr, 3.into()); + + interpreter.run()?; + println!("MEjor aca"); + + assert_eq!(interpreter.generation_state.registers.gas_used, 100); + + let sys_tload = crate::cpu::kernel::aggregator::KERNEL.global_labels["sys_tload"]; + let kexit_info = U256::from(0xdeadbeefu32) + + (U256::from(u64::from(true)) << 32) + + (U256::from(interpreter.generation_state.registers.gas_used) << 192); + interpreter.generation_state.registers.program_counter = sys_tload; + interpreter.generation_state.registers.is_kernel = true; + interpreter.push(2.into()); + interpreter.push(kexit_info); + + interpreter.run()?; + println!("2"); + + assert_eq!(interpreter.generation_state.registers.gas_used, 200); + + let val = interpreter.pop().unwrap(); + + assert_eq!(val, 1.into()); + + // Load non-existing slot + interpreter.generation_state.registers.program_counter = sys_tload; + interpreter.generation_state.registers.is_kernel = true; + let slot: U256 = 4.into(); + interpreter.push(slot); + interpreter.push(kexit_info); + + interpreter.run()?; + + let val = interpreter.stack()[0]; + + assert_eq!(U256::zero(), val); + Ok(()) +} + +#[test] +fn test_many_tstore_many_tload() -> Result<()> { + let kexit_info = U256::from(0xdeadbeefu32) + (U256::from(u64::from(true)) << 32); + + let initial_stack = vec![ + 1.into(), // val + 2.into(), // slot + kexit_info, + ]; + + let mut interpreter: Interpreter = Interpreter::new(0, initial_stack); + let gas_limit_address = MemoryAddress { + context: 0, + segment: Segment::ContextMetadata.unscale(), + virt: ContextMetadata::GasLimit.unscale(), + }; + let addr_addr = MemoryAddress { + context: 0, + segment: Segment::ContextMetadata.unscale(), + virt: ContextMetadata::Address.unscale(), + }; + + interpreter + .generation_state + .memory + .set(gas_limit_address, (10 * 200).into()); + interpreter.generation_state.memory.set(addr_addr, 3.into()); + + let sys_tstore = crate::cpu::kernel::aggregator::KERNEL.global_labels["sys_tstore"]; + + for i in (0..10) { + interpreter.generation_state.registers.program_counter = sys_tstore; + interpreter.generation_state.registers.is_kernel = true; + let kexit_info = U256::from(0xdeadbeefu32) + + (U256::from(u64::from(true)) << 32) + + (U256::from(interpreter.generation_state.registers.gas_used) << 192); + let val: U256 = i.into(); + let slot: U256 = i.into(); + interpreter.push(val); + interpreter.push(slot); + interpreter.push(kexit_info); + + interpreter.run()?; + assert_eq!( + interpreter.generation_state.registers.gas_used, + 100 * (i + 1) + ); + } + + let sys_tload = crate::cpu::kernel::aggregator::KERNEL.global_labels["sys_tload"]; + + for i in (0..10) { + interpreter.generation_state.registers.program_counter = sys_tload; + interpreter.generation_state.registers.is_kernel = true; + let kexit_info = U256::from(0xdeadbeefu32) + + (U256::from(u64::from(true)) << 32) + + (U256::from(interpreter.generation_state.registers.gas_used) << 192); + let slot: U256 = i.into(); + interpreter.push(slot); + interpreter.push(kexit_info); + + interpreter.run()?; + assert_eq!( + interpreter.generation_state.registers.gas_used, + 100 * (i + 10 + 1) + ); + + assert_eq!(U256::from(i), interpreter.pop().unwrap()); + } + + Ok(()) +} + +#[test] +fn test_revert() -> Result<()> { + // We use a modified kernel with an extra file defining a label + // where the `checkpoint` macro from file cpu/kernel/asm/journal/journal.asm + // is expanded. + const NUMBER_FILES: usize = NUMBER_KERNEL_FILES + 1; + static KERNEL: Lazy = Lazy::new(|| { + combined_kernel_from_files(std::array::from_fn::<_, NUMBER_FILES, _>(|i| { + if i < NUMBER_KERNEL_FILES { + KERNEL_FILES[i] + } else { + include_str!("checkpoint_label.asm") + } + })) + }); + + let sys_tstore = KERNEL.global_labels["sys_tstore"]; + let mut interpreter = Interpreter::::new(sys_tstore, vec![]); + interpreter.generation_state = + GenerationState::::new(GenerationInputs::default(), &KERNEL.code).unwrap(); + + let gas_limit_address = MemoryAddress { + context: 0, + segment: Segment::ContextMetadata.unscale(), + virt: ContextMetadata::GasLimit.unscale(), + }; + let addr_addr = MemoryAddress { + context: 0, + segment: Segment::ContextMetadata.unscale(), + virt: ContextMetadata::Address.unscale(), + }; + + interpreter + .generation_state + .memory + .set(gas_limit_address, (20 * 100).into()); + interpreter.generation_state.memory.set(addr_addr, 3.into()); + + // Store different values at slot 1 + for i in (0..10) { + interpreter.generation_state.registers.program_counter = sys_tstore; + interpreter.generation_state.registers.is_kernel = true; + let kexit_info = U256::from(0xdeadbeefu32) + + (U256::from(u64::from(true)) << 32) + + (U256::from(interpreter.generation_state.registers.gas_used) << 192); + let val: U256 = i.into(); + let slot: U256 = 1.into(); + interpreter.push(val); + interpreter.push(slot); + interpreter.push(kexit_info); + + interpreter.run()?; + assert_eq!( + interpreter.generation_state.registers.gas_used, + 100 * (i + 1) + ); + } + + let gas_before_checkpoint = interpreter.generation_state.registers.gas_used; + + // We will revert to the point where `val` was 9 + let checkpoint = KERNEL.global_labels["checkpoint"]; + interpreter.generation_state.registers.program_counter = checkpoint; + interpreter.generation_state.registers.is_kernel = true; + interpreter.push(0xdeadbeefu32.into()); + interpreter.run()?; + assert!(interpreter.stack().is_empty()); + + // Don't charge gas for the checkpoint + interpreter.generation_state.registers.gas_used = gas_before_checkpoint; + + // Now we change `val` 10 more times + for i in (10..20) { + interpreter.generation_state.registers.program_counter = sys_tstore; + interpreter.generation_state.registers.is_kernel = true; + let kexit_info = U256::from(0xdeadbeefu32) + + (U256::from(u64::from(true)) << 32) + + (U256::from(interpreter.generation_state.registers.gas_used) << 192); + let val: U256 = i.into(); + let slot: U256 = 1.into(); + interpreter.push(val); + interpreter.push(slot); + interpreter.push(kexit_info); + + interpreter.run()?; + assert_eq!( + interpreter.generation_state.registers.gas_used, + 100 * (i + 1) + ); + } + + // The interpreter will run out of gas and revert to the checkpoint + interpreter.generation_state.registers.program_counter = sys_tstore; + interpreter.generation_state.registers.is_kernel = true; + let kexit_info = U256::from(0xdeadbeefu32) + + (U256::from(u64::from(true)) << 32) + + (U256::from(interpreter.generation_state.registers.gas_used) << 192); + interpreter.push(3.into()); // val + interpreter.push(2.into()); // slot + interpreter.push(kexit_info); + assert!(interpreter.run().is_err()); + + // Now we should load the value before the revert + let sys_tload = KERNEL.global_labels["sys_tload"]; + interpreter.generation_state.registers.program_counter = sys_tload; + interpreter.generation_state.registers.gas_used = 0; + let kexit_info = U256::from(0xdeadbeefu32) + (U256::from(u64::from(true)) << 32); + interpreter.generation_state.registers.is_kernel = true; + interpreter.push(1.into()); + interpreter.push(kexit_info); + + interpreter.run()?; + + assert_eq!(interpreter.pop().unwrap(), 9.into()); + + Ok(()) +} diff --git a/evm_arithmetization/src/memory/segments.rs b/evm_arithmetization/src/memory/segments.rs index fafba7985..67aa93a6d 100644 --- a/evm_arithmetization/src/memory/segments.rs +++ b/evm_arithmetization/src/memory/segments.rs @@ -73,15 +73,17 @@ pub(crate) enum Segment { ContextCheckpoints = 32 << SEGMENT_SCALING_FACTOR, /// List of 256 previous block hashes. BlockHashes = 33 << SEGMENT_SCALING_FACTOR, + // The transient storage of the current transaction. + TransientStorage = 34 << SEGMENT_SCALING_FACTOR, /// List of contracts which have been created during the current /// transaction. - CreatedContracts = 34 << SEGMENT_SCALING_FACTOR, + CreatedContracts = 35 << SEGMENT_SCALING_FACTOR, /// Blob versioned hashes specified in a type-3 transaction. - TxnBlobVersionedHashes = 35 << SEGMENT_SCALING_FACTOR, + TxnBlobVersionedHashes = 36 << SEGMENT_SCALING_FACTOR, } impl Segment { - pub(crate) const COUNT: usize = 36; + pub(crate) const COUNT: usize = 37; /// Unscales this segment by `SEGMENT_SCALING_FACTOR`. pub(crate) const fn unscale(&self) -> usize { @@ -124,6 +126,7 @@ impl Segment { Self::TouchedAddresses, Self::ContextCheckpoints, Self::BlockHashes, + Self::TransientStorage, Self::CreatedContracts, Self::TxnBlobVersionedHashes, ] @@ -166,6 +169,7 @@ impl Segment { Segment::TouchedAddresses => "SEGMENT_TOUCHED_ADDRESSES", Segment::ContextCheckpoints => "SEGMENT_CONTEXT_CHECKPOINTS", Segment::BlockHashes => "SEGMENT_BLOCK_HASHES", + Segment::TransientStorage => "SEGMENT_TRANSIENT_STORAGE", Segment::CreatedContracts => "SEGMENT_CREATED_CONTRACTS", Segment::TxnBlobVersionedHashes => "SEGMENT_TXN_BLOB_VERSIONED_HASHES", } @@ -207,6 +211,7 @@ impl Segment { Segment::TouchedAddresses => 256, Segment::ContextCheckpoints => 256, Segment::BlockHashes => 256, + Segment::TransientStorage => 256, Segment::CreatedContracts => 256, Segment::TxnBlobVersionedHashes => 256, } diff --git a/evm_arithmetization/src/witness/transition.rs b/evm_arithmetization/src/witness/transition.rs index 458fc11b3..0a82f54a3 100644 --- a/evm_arithmetization/src/witness/transition.rs +++ b/evm_arithmetization/src/witness/transition.rs @@ -127,6 +127,8 @@ pub(crate) fn decode(registers: RegistersState, opcode: u8) -> Result Ok(Operation::Syscall(opcode, 0, true)), // MSIZE (0x5a, _) => Ok(Operation::Syscall(opcode, 0, true)), // GAS (0x5b, _) => Ok(Operation::Jumpdest), + (0x5c, _) => Ok(Operation::Syscall(opcode, 1, false)), // TLOAD + (0x5d, _) => Ok(Operation::Syscall(opcode, 2, false)), // TSTORE (0x5e, _) => Ok(Operation::Syscall(opcode, 3, false)), // MCOPY (0x5f..=0x7f, _) => Ok(Operation::Push(opcode - 0x5f)), (0x80..=0x8f, _) => Ok(Operation::Dup(opcode & 0xf)),