From 39d78179d462400effed6cba9caedfd652a319b1 Mon Sep 17 00:00:00 2001 From: xermicus Date: Wed, 5 Jun 2024 17:34:17 +0200 Subject: [PATCH] implement transient storage Signed-off-by: xermicus --- crates/integration/codesize.json | 6 ++-- crates/integration/contracts/Storage.sol | 14 ++++++++ crates/integration/src/cases.rs | 18 ++++++++++ crates/integration/src/mock_runtime.rs | 35 +++++++++++++++---- crates/integration/src/tests.rs | 13 +++++++ .../llvm-context/src/polkavm/context/mod.rs | 12 ++++--- .../src/polkavm_guest.c | 4 +-- .../ethereal_ir/function/block/element/mod.rs | 17 ++++++--- .../statement/expression/function_call/mod.rs | 23 ++++++------ 9 files changed, 112 insertions(+), 30 deletions(-) create mode 100644 crates/integration/contracts/Storage.sol diff --git a/crates/integration/codesize.json b/crates/integration/codesize.json index d766505..bb6ef56 100644 --- a/crates/integration/codesize.json +++ b/crates/integration/codesize.json @@ -2,9 +2,9 @@ "Baseline": 934, "Computation": 4360, "DivisionArithmetics": 39448, - "ERC20": 52961, + "ERC20": 46624, "Events": 1749, "FibonacciIterative": 2973, - "Flipper": 3549, - "SHA1": 32786 + "Flipper": 3563, + "SHA1": 32709 } \ No newline at end of file diff --git a/crates/integration/contracts/Storage.sol b/crates/integration/contracts/Storage.sol new file mode 100644 index 0000000..b0c474b --- /dev/null +++ b/crates/integration/contracts/Storage.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8; + +contract Storage { + function transient(uint value) public returns (uint ret) { + assembly { + let slot := 123 + tstore(slot, value) + let success := call(0, 0, 0, 0, 0, 0, 0) + ret := tload(slot) + } + } +} diff --git a/crates/integration/src/cases.rs b/crates/integration/src/cases.rs index b636767..5d9ccec 100644 --- a/crates/integration/src/cases.rs +++ b/crates/integration/src/cases.rs @@ -160,6 +160,12 @@ sol!( } ); +sol!( + contract Storage { + function transient(uint value) public returns (uint ret); + } +); + impl Contract { /// Execute the contract. /// @@ -526,6 +532,18 @@ impl Contract { calldata: Bitwise::opByteCall::new((index, value)).abi_encode(), } } + + pub fn storage_transient(value: U256) -> Self { + let code = include_str!("../contracts/Storage.sol"); + let name = "Storage"; + + Self { + name, + evm_runtime: crate::compile_evm_bin_runtime(name, code), + pvm_runtime: crate::compile_blob(name, code), + calldata: Storage::transientCall::new((value,)).abi_encode(), + } + } } #[cfg(test)] diff --git a/crates/integration/src/mock_runtime.rs b/crates/integration/src/mock_runtime.rs index 7aba163..89b81f2 100644 --- a/crates/integration/src/mock_runtime.rs +++ b/crates/integration/src/mock_runtime.rs @@ -106,6 +106,7 @@ impl Default for Frame { #[derive(Default, Clone, Debug)] pub struct Transaction { state: State, + transient_state: State, stack: Vec, } @@ -137,6 +138,16 @@ impl Transaction { fn create2(&self, salt: B256, blob_hash: B256) -> Address { self.top_frame().callee.create2(salt, blob_hash) } + + fn transient_account_mut(&mut self) -> &mut Account { + let address = self.top_frame().callee; + let account = self + .transient_state + .accounts_mut() + .entry(address) + .or_default(); + account + } } /// Helper to create valid transactions. @@ -277,6 +288,7 @@ impl From for TransactionBuilder { state_before: state.clone(), context: Transaction { state, + transient_state: Default::default(), stack: Default::default(), }, } @@ -312,6 +324,7 @@ impl State { state_before: self.clone(), context: Transaction { state: self, + transient_state: Default::default(), stack: vec![Default::default()], }, } @@ -435,6 +448,7 @@ fn link_host_functions(engine: &Engine) -> Linker { .func_wrap( runtime_api::imports::SET_STORAGE, |caller: Caller, + transient: u32, key_ptr: u32, key_len: u32, value_ptr: u32, @@ -461,7 +475,12 @@ fn link_host_functions(engine: &Engine) -> Linker { log::info!("set storage {key} = {value}"); - transaction.top_account_mut().storage.insert(key, value); + let storage = if transient == 0 { + &mut transaction.top_account_mut().storage + } else { + &mut transaction.transient_account_mut().storage + }; + storage.insert(key, value); Ok(0) }, @@ -472,6 +491,7 @@ fn link_host_functions(engine: &Engine) -> Linker { .func_wrap( runtime_api::imports::GET_STORAGE, |caller: Caller, + transient: u32, key_ptr: u32, key_len: u32, out_ptr: u32, @@ -488,12 +508,13 @@ fn link_host_functions(engine: &Engine) -> Linker { ); let key = U256::from_le_bytes::<32>(key.try_into().unwrap()); - let value = transaction - .top_account_mut() - .storage - .get(&key) - .cloned() - .unwrap_or_default(); + + let storage = if transient == 0 { + &transaction.top_account_mut().storage + } else { + &transaction.transient_account_mut().storage + }; + let value = storage.get(&key).cloned().unwrap_or_default(); log::info!("get storage {key} = {value}"); diff --git a/crates/integration/src/tests.rs b/crates/integration/src/tests.rs index 271c3cd..fa8d836 100644 --- a/crates/integration/src/tests.rs +++ b/crates/integration/src/tests.rs @@ -614,3 +614,16 @@ fn bitwise_byte() { assert_eq!(expected, received) } } + +#[test] +fn transient_storage() { + let expected = U256::MAX; + let (state, output) = assert_success(&Contract::storage_transient(expected), false); + let received = U256::abi_decode(&output.data, true).unwrap(); + assert_eq!(expected, received); + + assert!(state + .accounts() + .values() + .all(|account| account.storage.is_empty())); +} diff --git a/crates/llvm-context/src/polkavm/context/mod.rs b/crates/llvm-context/src/polkavm/context/mod.rs index 391ee7b..7fddf22 100644 --- a/crates/llvm-context/src/polkavm/context/mod.rs +++ b/crates/llvm-context/src/polkavm/context/mod.rs @@ -673,8 +673,7 @@ where self.build_byte_swap(value) } - AddressSpace::TransientStorage => todo!(), - AddressSpace::Storage => { + AddressSpace::Storage | AddressSpace::TransientStorage => { let storage_key_value = self.builder().build_ptr_to_int( pointer.value, self.word_type(), @@ -692,9 +691,12 @@ where let (storage_value_pointer, storage_value_length_pointer) = self .build_stack_parameter(revive_common::BIT_LENGTH_WORD, "storage_value_pointer"); + let transient = pointer.address_space == AddressSpace::TransientStorage; + self.build_runtime_call( runtime_api::imports::GET_STORAGE, &[ + self.xlen_type().const_int(transient as u64, false).into(), storage_key_pointer_casted.into(), self.integer_const(crate::polkavm::XLEN, 32).into(), storage_value_pointer.to_int(self).into(), @@ -758,8 +760,7 @@ where .set_alignment(revive_common::BYTE_LENGTH_BYTE as u32) .expect("Alignment is valid"); } - AddressSpace::TransientStorage => todo!(), - AddressSpace::Storage => { + AddressSpace::Storage | AddressSpace::TransientStorage => { assert_eq!( value.as_basic_value_enum().get_type(), self.word_type().as_basic_type_enum() @@ -789,9 +790,12 @@ where self.builder() .build_store(storage_value_pointer.value, value)?; + let transient = pointer.address_space == AddressSpace::TransientStorage; + self.build_runtime_call( runtime_api::imports::SET_STORAGE, &[ + self.xlen_type().const_int(transient as u64, false).into(), storage_key_pointer_casted.into(), self.integer_const(crate::polkavm::XLEN, 32).into(), storage_value_pointer_casted.into(), diff --git a/crates/pallet-contracts-pvm-llapi/src/polkavm_guest.c b/crates/pallet-contracts-pvm-llapi/src/polkavm_guest.c index f0c0f08..b9e5d58 100644 --- a/crates/pallet-contracts-pvm-llapi/src/polkavm_guest.c +++ b/crates/pallet-contracts-pvm-llapi/src/polkavm_guest.c @@ -67,9 +67,9 @@ POLKAVM_IMPORT(void, returndatacopy, uint32_t, uint32_t, uint32_t) POLKAVM_IMPORT(void, value_transferred, uint32_t, uint32_t) -POLKAVM_IMPORT(uint32_t, set_storage, uint32_t, uint32_t, uint32_t, uint32_t) +POLKAVM_IMPORT(uint32_t, set_storage, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) -POLKAVM_IMPORT(uint32_t, get_storage, uint32_t, uint32_t, uint32_t, uint32_t) +POLKAVM_IMPORT(uint32_t, get_storage, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) POLKAVM_IMPORT(uint32_t, clear_storage, uint32_t, uint32_t) diff --git a/crates/solidity/src/evmla/ethereal_ir/function/block/element/mod.rs b/crates/solidity/src/evmla/ethereal_ir/function/block/element/mod.rs index 0af649c..b7626b9 100644 --- a/crates/solidity/src/evmla/ethereal_ir/function/block/element/mod.rs +++ b/crates/solidity/src/evmla/ethereal_ir/function/block/element/mod.rs @@ -742,12 +742,21 @@ where .map(|_| None) } InstructionName::TLOAD => { - let _arguments = self.pop_arguments_llvm(context); - anyhow::bail!("The `TLOAD` instruction is not supported until zkVM v1.5.0"); + let arguments = self.pop_arguments_llvm(context); + revive_llvm_context::polkavm_evm_storage::transient_load( + context, + arguments[0].into_int_value(), + ) + .map(Some) } InstructionName::TSTORE => { - let _arguments = self.pop_arguments_llvm(context); - anyhow::bail!("The `TSTORE` instruction is not supported until zkVM v1.5.0"); + let arguments = self.pop_arguments_llvm(context); + revive_llvm_context::polkavm_evm_storage::transient_store( + context, + arguments[0].into_int_value(), + arguments[1].into_int_value(), + ) + .map(|_| None) } InstructionName::PUSHIMMUTABLE => { let key = self diff --git a/crates/solidity/src/yul/parser/statement/expression/function_call/mod.rs b/crates/solidity/src/yul/parser/statement/expression/function_call/mod.rs index 772e10d..ffdc3ee 100644 --- a/crates/solidity/src/yul/parser/statement/expression/function_call/mod.rs +++ b/crates/solidity/src/yul/parser/statement/expression/function_call/mod.rs @@ -485,18 +485,21 @@ impl FunctionCall { .map(|_| None) } Name::TLoad => { - let _arguments = self.pop_arguments_llvm::(context)?; - anyhow::bail!( - "{} The `TLOAD` instruction is not supported until zkVM v1.5.0", - location - ); + let arguments = self.pop_arguments_llvm::(context)?; + revive_llvm_context::polkavm_evm_storage::transient_load( + context, + arguments[0].into_int_value(), + ) + .map(Some) } Name::TStore => { - let _arguments = self.pop_arguments_llvm::(context)?; - anyhow::bail!( - "{} The `TSTORE` instruction is not supported until zkVM v1.5.0", - location - ); + let arguments = self.pop_arguments_llvm::(context)?; + revive_llvm_context::polkavm_evm_storage::transient_store( + context, + arguments[0].into_int_value(), + arguments[1].into_int_value(), + ) + .map(|_| None) } Name::LoadImmutable => todo!(), Name::SetImmutable => {