diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c124fd..15e3b58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee` - Support for passing LLVM command line options via the prcoess input or providing one or more `--llvm-arg='..'` resolc CLI flag. This allows more fine-grained control over the LLVM backend configuration. ### Changed +- Storage keys and values are big endian. This was a pre-mature optimization because for the contract itself it this is a no-op and thus not observable. However we should consider the storage layout as part of the contract ABI. The endianness of transient storage values are still kept as-is. - Runner `resolc` using webkit is no longer supported. ### Fixed diff --git a/Cargo.lock b/Cargo.lock index 87524ad..a96d0a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4965,9 +4965,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.71" +version = "0.10.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" +checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" dependencies = [ "bitflags 2.8.0", "cfg-if", @@ -4997,9 +4997,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.106" +version = "0.9.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" +checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" dependencies = [ "cc", "libc", diff --git a/crates/integration/codesize.json b/crates/integration/codesize.json index 41368a5..d9eaafc 100644 --- a/crates/integration/codesize.json +++ b/crates/integration/codesize.json @@ -2,9 +2,9 @@ "Baseline": 1443, "Computation": 2788, "DivisionArithmetics": 9748, - "ERC20": 19200, + "ERC20": 19150, "Events": 2201, "FibonacciIterative": 2041, - "Flipper": 2632, - "SHA1": 8958 + "Flipper": 2691, + "SHA1": 8997 } \ No newline at end of file diff --git a/crates/integration/contracts/flipper.sol b/crates/integration/contracts/flipper.sol index 0c2ef61..bcb922d 100644 --- a/crates/integration/contracts/flipper.sol +++ b/crates/integration/contracts/flipper.sol @@ -15,7 +15,7 @@ pragma solidity ^0.8; "Instantiated": 0 }, "key": "0000000000000000000000000000000000000000000000000000000000000000", - "expected": "0100000000000000000000000000000000000000000000000000000000000000" + "expected": "0000000000000000000000000000000000000000000000000000000000000001" } }, { diff --git a/crates/llvm-context/src/lib.rs b/crates/llvm-context/src/lib.rs index 181c870..6fcd3a2 100644 --- a/crates/llvm-context/src/lib.rs +++ b/crates/llvm-context/src/lib.rs @@ -36,7 +36,9 @@ pub use self::polkavm::context::function::Function as PolkaVMFunction; pub use self::polkavm::context::global::Global as PolkaVMGlobal; pub use self::polkavm::context::pointer::heap::LoadWord as PolkaVMLoadHeapWordFunction; pub use self::polkavm::context::pointer::heap::StoreWord as PolkaVMStoreHeapWordFunction; +pub use self::polkavm::context::pointer::storage::LoadTransientWord as PolkaVMLoadTransientStorageWordFunction; pub use self::polkavm::context::pointer::storage::LoadWord as PolkaVMLoadStorageWordFunction; +pub use self::polkavm::context::pointer::storage::StoreTransientWord as PolkaVMStoreTransientStorageWordFunction; pub use self::polkavm::context::pointer::storage::StoreWord as PolkaVMStoreStorageWordFunction; pub use self::polkavm::context::pointer::Pointer as PolkaVMPointer; pub use self::polkavm::context::r#loop::Loop as PolkaVMLoop; diff --git a/crates/llvm-context/src/polkavm/context/pointer/storage.rs b/crates/llvm-context/src/polkavm/context/pointer/storage.rs index 0f9cdf6..a28c1cb 100644 --- a/crates/llvm-context/src/polkavm/context/pointer/storage.rs +++ b/crates/llvm-context/src/polkavm/context/pointer/storage.rs @@ -17,47 +17,20 @@ where const NAME: &'static str = "__revive_load_storage_word"; fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { - context.word_type().fn_type( - &[context.xlen_type().into(), context.word_type().into()], - false, - ) + context + .word_type() + .fn_type(&[context.word_type().into()], false) } fn emit_body<'ctx>( &self, context: &mut Context<'ctx, D>, ) -> anyhow::Result>> { - let is_transient = Self::paramater(context, 0); - let key_value = Self::paramater(context, 1); - - let key_pointer = context.build_alloca_at_entry(context.word_type(), "key_pointer"); - let value_pointer = context.build_alloca_at_entry(context.word_type(), "value_pointer"); - let length_pointer = context.build_alloca_at_entry(context.xlen_type(), "length_pointer"); - - context - .builder() - .build_store(key_pointer.value, key_value)?; - context.build_store(value_pointer, context.word_const(0))?; - context.build_store( - length_pointer, - context - .xlen_type() - .const_int(revive_common::BYTE_LENGTH_WORD as u64, false), - )?; - - let arguments = [ - is_transient, - key_pointer.to_int(context).into(), - context.xlen_type().const_all_ones().into(), - value_pointer.to_int(context).into(), - length_pointer.to_int(context).into(), - ]; - context.build_runtime_call(revive_runtime_api::polkavm_imports::GET_STORAGE, &arguments); - - // We do not to check the return value: Solidity assumes infallible loads. - // If a key doesn't exist the "zero" value is returned (ensured by above write). - - Ok(Some(context.build_load(value_pointer, "storage_value")?)) + Ok(Some(emit_load( + context, + Self::paramater(context, 0), + false, + )?)) } } @@ -74,6 +47,42 @@ where } } +/// Load a word size value from a transient storage pointer. +pub struct LoadTransientWord; + +impl RuntimeFunction for LoadTransientWord +where + D: Dependency + Clone, +{ + const NAME: &'static str = "__revive_load_transient_storage_word"; + + fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { + context + .word_type() + .fn_type(&[context.word_type().into()], false) + } + + fn emit_body<'ctx>( + &self, + context: &mut Context<'ctx, D>, + ) -> anyhow::Result>> { + Ok(Some(emit_load(context, Self::paramater(context, 0), true)?)) + } +} + +impl WriteLLVM for LoadTransientWord +where + D: Dependency + Clone, +{ + fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> { + >::declare(self, context) + } + + fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> { + >::emit(&self, context) + } +} + /// Store a word size value through a storage pointer. pub struct StoreWord; @@ -85,11 +94,7 @@ where fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { context.void_type().fn_type( - &[ - context.xlen_type().into(), - context.word_type().into(), - context.word_type().into(), - ], + &[context.word_type().into(), context.word_type().into()], false, ) } @@ -98,24 +103,12 @@ where &self, context: &mut Context<'ctx, D>, ) -> anyhow::Result>> { - let is_transient = Self::paramater(context, 0); - let key = Self::paramater(context, 1); - let value = Self::paramater(context, 2); - - let key_pointer = context.build_alloca_at_entry(context.word_type(), "key_pointer"); - let value_pointer = context.build_alloca_at_entry(context.word_type(), "value_pointer"); - - context.build_store(key_pointer, key)?; - context.build_store(value_pointer, value)?; - - let arguments = [ - is_transient, - key_pointer.to_int(context).into(), - context.xlen_type().const_all_ones().into(), - value_pointer.to_int(context).into(), - context.integer_const(crate::polkavm::XLEN, 32).into(), - ]; - context.build_runtime_call(revive_runtime_api::polkavm_imports::SET_STORAGE, &arguments); + emit_store( + context, + Self::paramater(context, 0), + Self::paramater(context, 1), + false, + )?; Ok(None) } @@ -133,3 +126,122 @@ where >::emit(&self, context) } } + +/// Store a word size value through a transient storage pointer. +pub struct StoreTransientWord; + +impl RuntimeFunction for StoreTransientWord +where + D: Dependency + Clone, +{ + const NAME: &'static str = "__revive_store_transient_storage_word"; + + fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> { + context.void_type().fn_type( + &[context.word_type().into(), context.word_type().into()], + false, + ) + } + + fn emit_body<'ctx>( + &self, + context: &mut Context<'ctx, D>, + ) -> anyhow::Result>> { + emit_store( + context, + Self::paramater(context, 0), + Self::paramater(context, 1), + true, + )?; + + Ok(None) + } +} + +impl WriteLLVM for StoreTransientWord +where + D: Dependency + Clone, +{ + fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> { + >::declare(self, context) + } + + fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> { + >::emit(&self, context) + } +} + +fn emit_load<'ctx, D: Dependency + Clone>( + context: &mut Context<'ctx, D>, + mut key: BasicValueEnum<'ctx>, + transient: bool, +) -> anyhow::Result> { + if !transient { + key = context.build_byte_swap(key)?; + } + + let key_pointer = context.build_alloca_at_entry(context.word_type(), "key_pointer"); + let value_pointer = context.build_alloca_at_entry(context.word_type(), "value_pointer"); + let length_pointer = context.build_alloca_at_entry(context.xlen_type(), "length_pointer"); + + context.builder().build_store(key_pointer.value, key)?; + context.build_store(value_pointer, context.word_const(0))?; + context.build_store( + length_pointer, + context + .xlen_type() + .const_int(revive_common::BYTE_LENGTH_WORD as u64, false), + )?; + + let is_transient = context.xlen_type().const_int(transient as u64, false); + + let arguments = [ + is_transient.into(), + key_pointer.to_int(context).into(), + context.xlen_type().const_all_ones().into(), + value_pointer.to_int(context).into(), + length_pointer.to_int(context).into(), + ]; + context.build_runtime_call(revive_runtime_api::polkavm_imports::GET_STORAGE, &arguments); + + // We do not to check the return value: Solidity assumes infallible loads. + // If a key doesn't exist the "zero" value is returned (ensured by above write). + + let value = context.build_load(value_pointer, "storage_value")?; + Ok(if transient { + value + } else { + context.build_byte_swap(value)? + }) +} + +fn emit_store<'ctx, D: Dependency + Clone>( + context: &mut Context<'ctx, D>, + mut key: BasicValueEnum<'ctx>, + mut value: BasicValueEnum<'ctx>, + transient: bool, +) -> anyhow::Result<()> { + if !transient { + key = context.build_byte_swap(key)?; + value = context.build_byte_swap(value)?; + } + + let key_pointer = context.build_alloca_at_entry(context.word_type(), "key_pointer"); + let value_pointer = context.build_alloca_at_entry(context.word_type(), "value_pointer"); + + context.build_store(key_pointer, key)?; + context.build_store(value_pointer, value)?; + + let is_transient = context.xlen_type().const_int(transient as u64, false); + + let arguments = [ + is_transient.into(), + key_pointer.to_int(context).into(), + context.xlen_type().const_all_ones().into(), + value_pointer.to_int(context).into(), + context.integer_const(crate::polkavm::XLEN, 32).into(), + ]; + context.build_runtime_call(revive_runtime_api::polkavm_imports::SET_STORAGE, &arguments); + + Ok(()) +} diff --git a/crates/llvm-context/src/polkavm/evm/storage.rs b/crates/llvm-context/src/polkavm/evm/storage.rs index cf409da..09f795e 100644 --- a/crates/llvm-context/src/polkavm/evm/storage.rs +++ b/crates/llvm-context/src/polkavm/evm/storage.rs @@ -4,7 +4,9 @@ use crate::polkavm::context::runtime::RuntimeFunction; use crate::polkavm::context::Context; use crate::polkavm::Dependency; use crate::PolkaVMLoadStorageWordFunction; +use crate::PolkaVMLoadTransientStorageWordFunction; use crate::PolkaVMStoreStorageWordFunction; +use crate::PolkaVMStoreTransientStorageWordFunction; /// Translates the storage load. pub fn load<'ctx, D>( @@ -16,7 +18,7 @@ where { let name = >::NAME; let declaration = >::declaration(context); - let arguments = [context.xlen_type().const_zero().into(), position.into()]; + let arguments = [position.into()]; Ok(context .build_call(declaration, &arguments, "storage_load") .unwrap_or_else(|| panic!("runtime function {name} should return a value"))) @@ -32,11 +34,7 @@ where D: Dependency + Clone, { let declaration = >::declaration(context); - let arguments = [ - context.xlen_type().const_zero().into(), - position.into(), - value.into(), - ]; + let arguments = [position.into(), value.into()]; context.build_call(declaration, &arguments, "storage_store"); Ok(()) } @@ -49,14 +47,12 @@ pub fn transient_load<'ctx, D>( where D: Dependency + Clone, { - let name = >::NAME; - let declaration = >::declaration(context); - let arguments = [ - context.xlen_type().const_int(1, false).into(), - position.into(), - ]; + let name = >::NAME; + let arguments = [position.into()]; + let declaration = + >::declaration(context); Ok(context - .build_call(declaration, &arguments, "storage_load") + .build_call(declaration, &arguments, "transient_storage_load") .unwrap_or_else(|| panic!("runtime function {name} should return a value"))) } @@ -69,12 +65,9 @@ pub fn transient_store<'ctx, D>( where D: Dependency + Clone, { - let declaration = >::declaration(context); - let arguments = [ - context.xlen_type().const_int(1, false).into(), - position.into(), - value.into(), - ]; - context.build_call(declaration, &arguments, "storage_store"); + let declaration = + >::declaration(context); + let arguments = [position.into(), value.into()]; + context.build_call(declaration, &arguments, "transient_storage_store"); Ok(()) } diff --git a/crates/runner/src/specs.rs b/crates/runner/src/specs.rs index 403ead0..0cf4839 100644 --- a/crates/runner/src/specs.rs +++ b/crates/runner/src/specs.rs @@ -106,14 +106,10 @@ impl SpecsAction { }; for (key, expected) in storage { - let mut key = **key; - let mut expected = **expected; - key.reverse(); - expected.reverse(); actions.push(Self::VerifyStorage { contract: account_pvm.clone(), - key, - expected, + key: **key, + expected: **expected, }); } diff --git a/crates/solidity/src/yul/parser/statement/object.rs b/crates/solidity/src/yul/parser/statement/object.rs index 435a087..55b2284 100644 --- a/crates/solidity/src/yul/parser/statement/object.rs +++ b/crates/solidity/src/yul/parser/statement/object.rs @@ -192,6 +192,8 @@ where revive_llvm_context::PolkaVMStoreHeapWordFunction.declare(context)?; revive_llvm_context::PolkaVMLoadStorageWordFunction.declare(context)?; revive_llvm_context::PolkaVMStoreStorageWordFunction.declare(context)?; + revive_llvm_context::PolkaVMLoadTransientStorageWordFunction.declare(context)?; + revive_llvm_context::PolkaVMStoreTransientStorageWordFunction.declare(context)?; revive_llvm_context::PolkaVMWordToPointerFunction.declare(context)?; revive_llvm_context::PolkaVMExitFunction.declare(context)?; @@ -242,6 +244,8 @@ where revive_llvm_context::PolkaVMStoreHeapWordFunction.into_llvm(context)?; revive_llvm_context::PolkaVMLoadStorageWordFunction.into_llvm(context)?; revive_llvm_context::PolkaVMStoreStorageWordFunction.into_llvm(context)?; + revive_llvm_context::PolkaVMLoadTransientStorageWordFunction.into_llvm(context)?; + revive_llvm_context::PolkaVMStoreTransientStorageWordFunction.into_llvm(context)?; revive_llvm_context::PolkaVMWordToPointerFunction.into_llvm(context)?; revive_llvm_context::PolkaVMExitFunction.into_llvm(context)?;