From aae25107a2bc19721891219a283739705839e6b8 Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Mon, 28 Oct 2024 10:18:11 +0100 Subject: [PATCH] support full storage key space (#100) - The storage pointer values will no longer be truncated to the register size, allowing for the use of arbitrary storage keys - Failed storage value reads will now guarantee to return the zero value --- crates/integration/codesize.json | 4 +- crates/integration/contracts/ERC20.sol | 49 +++++++++++++++++-- crates/integration/contracts/Storage.sol | 18 ++++++- .../llvm-context/src/polkavm/context/mod.rs | 34 ++----------- .../llvm-context/src/polkavm/evm/storage.rs | 49 ++++++------------- 5 files changed, 84 insertions(+), 70 deletions(-) diff --git a/crates/integration/codesize.json b/crates/integration/codesize.json index d3ea8ed..29ab4d8 100644 --- a/crates/integration/codesize.json +++ b/crates/integration/codesize.json @@ -2,9 +2,9 @@ "Baseline": 989, "Computation": 4153, "DivisionArithmetics": 40614, - "ERC20": 47343, + "ERC20": 47348, "Events": 1781, "FibonacciIterative": 3035, - "Flipper": 3393, + "Flipper": 3448, "SHA1": 33553 } \ No newline at end of file diff --git a/crates/integration/contracts/ERC20.sol b/crates/integration/contracts/ERC20.sol index 49426f7..26cd94e 100644 --- a/crates/integration/contracts/ERC20.sol +++ b/crates/integration/contracts/ERC20.sol @@ -4,11 +4,27 @@ pragma solidity ^0.8; /* runner.json { + "differential": true, "actions": [ - { - "Instantiate": {} - } - ] + { + "Upload": { + "code": { + "Solidity": { + "contract": "ERC20" + } + } + } + }, + { + "Instantiate": { + "code": { + "Solidity": { + "contract": "ERC20Tester" + } + } + } + } + ] } */ @@ -82,3 +98,28 @@ contract ERC20 is IERC20 { emit Transfer(msg.sender, address(0), amount); } } + +contract ERC20Tester { + constructor() { + address BOB = address(0xffffffffffffffffffffffffffffffffffffff); + ERC20 token = new ERC20(); + assert(token.decimals() == 18); + + // use call directly when code_size is implemented on pallet-revive + + address(token).call(abi.encodeWithSignature("mint(uint256)", 300)); + assert(token.balanceOf(address(this)) == 300); + token.transfer(BOB, 100); + assert(token.balanceOf(address(this)) == 200); + assert(token.balanceOf(BOB) == 100); + + token.approve(address(this), 100); + + token.transferFrom(address(this), BOB, 100); + assert(token.balanceOf(BOB) == 200); + assert(token.balanceOf(address(this)) == 100); + + address(token).call(abi.encodeWithSignature("burn(uint256)", 100)); + assert(token.balanceOf(address(this)) == 0); + } +} \ No newline at end of file diff --git a/crates/integration/contracts/Storage.sol b/crates/integration/contracts/Storage.sol index b1ef75b..8ff549e 100644 --- a/crates/integration/contracts/Storage.sol +++ b/crates/integration/contracts/Storage.sol @@ -22,6 +22,14 @@ pragma solidity ^0.8; }, "data": "fabc9efaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" } + }, + { + "Call": { + "dest": { + "Instantiated": 0 + }, + "data": "558b9f9bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + } } ] } @@ -30,10 +38,18 @@ pragma solidity ^0.8; contract Storage { function transient(uint value) public returns (uint ret) { assembly { - let slot := 123 + let slot := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 tstore(slot, value) let success := call(0, 0, 0, 0, 0, 0, 0) ret := tload(slot) } } + + function persistent(uint value) public returns (uint ret) { + assembly { + let slot := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 + sstore(slot, value) + ret := sload(slot) + } + } } diff --git a/crates/llvm-context/src/polkavm/context/mod.rs b/crates/llvm-context/src/polkavm/context/mod.rs index 212aca0..7b41346 100644 --- a/crates/llvm-context/src/polkavm/context/mod.rs +++ b/crates/llvm-context/src/polkavm/context/mod.rs @@ -669,22 +669,10 @@ where self.build_byte_swap(value) } AddressSpace::Storage | AddressSpace::TransientStorage => { - let storage_key_value = self.builder().build_ptr_to_int( - pointer.value, - self.word_type(), - "storage_ptr_to_int", - )?; - let storage_key_pointer = self.build_alloca(self.word_type(), "storage_key"); - let storage_key_pointer_casted = self.builder().build_ptr_to_int( - storage_key_pointer.value, - self.xlen_type(), - "storage_key_pointer_casted", - )?; - self.builder() - .build_store(storage_key_pointer.value, storage_key_value)?; - let storage_value_pointer = self.build_alloca(self.word_type(), "storage_value_pointer"); + self.build_store(storage_value_pointer, self.word_const(0))?; + let storage_value_length_pointer = self.build_alloca(self.xlen_type(), "storage_value_length_pointer"); self.build_store( @@ -698,7 +686,7 @@ where revive_runtime_api::polkavm_imports::GET_STORAGE, &[ self.xlen_type().const_int(transient as u64, false).into(), - storage_key_pointer_casted.into(), + pointer.to_int(self).into(), self.xlen_type().const_all_ones().into(), storage_value_pointer.to_int(self).into(), storage_value_length_pointer.to_int(self).into(), @@ -767,18 +755,6 @@ where self.word_type().as_basic_type_enum() ); - let storage_key_value = self.builder().build_ptr_to_int( - pointer.value, - self.word_type(), - "storage_ptr_to_int", - )?; - let storage_key_pointer = self.build_alloca(self.word_type(), "storage_key"); - let storage_key_pointer_casted = self.builder().build_ptr_to_int( - storage_key_pointer.value, - self.xlen_type(), - "storage_key_pointer_casted", - )?; - let storage_value_pointer = self.build_alloca(self.word_type(), "storage_value"); let storage_value_pointer_casted = self.builder().build_ptr_to_int( storage_value_pointer.value, @@ -786,8 +762,6 @@ where "storage_value_pointer_casted", )?; - self.builder() - .build_store(storage_key_pointer.value, storage_key_value)?; self.builder() .build_store(storage_value_pointer.value, value)?; @@ -797,7 +771,7 @@ where revive_runtime_api::polkavm_imports::SET_STORAGE, &[ self.xlen_type().const_int(transient as u64, false).into(), - storage_key_pointer_casted.into(), + pointer.to_int(self).into(), self.xlen_type().const_all_ones().into(), storage_value_pointer_casted.into(), self.integer_const(crate::polkavm::XLEN, 32).into(), diff --git a/crates/llvm-context/src/polkavm/evm/storage.rs b/crates/llvm-context/src/polkavm/evm/storage.rs index b399ac5..e7080a8 100644 --- a/crates/llvm-context/src/polkavm/evm/storage.rs +++ b/crates/llvm-context/src/polkavm/evm/storage.rs @@ -1,7 +1,6 @@ //! Translates the storage operations. use crate::polkavm::context::address_space::AddressSpace; -use crate::polkavm::context::pointer::Pointer; use crate::polkavm::context::Context; use crate::polkavm::Dependency; @@ -13,14 +12,10 @@ pub fn load<'ctx, D>( where D: Dependency + Clone, { - let position_pointer = Pointer::new_with_offset( - context, - AddressSpace::Storage, - context.word_type(), - position, - "storage_load_position_pointer", - ); - context.build_load(position_pointer, "storage_load_value") + let mut slot_ptr = context.build_alloca_at_entry(context.word_type(), "slot_pointer"); + slot_ptr.address_space = AddressSpace::Storage; + context.builder().build_store(slot_ptr.value, position)?; + context.build_load(slot_ptr, "storage_load_value") } /// Translates the storage store. @@ -32,14 +27,10 @@ pub fn store<'ctx, D>( where D: Dependency + Clone, { - let position_pointer = Pointer::new_with_offset( - context, - AddressSpace::Storage, - context.word_type(), - position, - "storage_store_position_pointer", - ); - context.build_store(position_pointer, value)?; + let mut slot_ptr = context.build_alloca_at_entry(context.word_type(), "slot_pointer"); + slot_ptr.address_space = AddressSpace::Storage; + context.builder().build_store(slot_ptr.value, position)?; + context.build_store(slot_ptr, value)?; Ok(()) } @@ -51,14 +42,10 @@ pub fn transient_load<'ctx, D>( where D: Dependency + Clone, { - let position_pointer = Pointer::new_with_offset( - context, - AddressSpace::TransientStorage, - context.word_type(), - position, - "transient_storage_load_position_pointer", - ); - context.build_load(position_pointer, "transient_storage_load_value") + let mut slot_ptr = context.build_alloca_at_entry(context.word_type(), "slot_pointer"); + slot_ptr.address_space = AddressSpace::TransientStorage; + context.builder().build_store(slot_ptr.value, position)?; + context.build_load(slot_ptr, "transient_storage_load_value") } /// Translates the transient storage store. @@ -70,13 +57,9 @@ pub fn transient_store<'ctx, D>( where D: Dependency + Clone, { - let position_pointer = Pointer::new_with_offset( - context, - AddressSpace::TransientStorage, - context.word_type(), - position, - "transient_storage_store_position_pointer", - ); - context.build_store(position_pointer, value)?; + let mut slot_ptr = context.build_alloca_at_entry(context.word_type(), "slot_pointer"); + slot_ptr.address_space = AddressSpace::TransientStorage; + context.builder().build_store(slot_ptr.value, position)?; + context.build_store(slot_ptr, value)?; Ok(()) }