From d79ab5da885c91678593693ec4c2311f9a17df94 Mon Sep 17 00:00:00 2001 From: Gustavo Gonzalez Date: Fri, 13 Dec 2024 18:20:44 -0300 Subject: [PATCH] remove motsu from the repo --- .github/codecov.yml | 3 - .github/workflows/check-publish.yml | 10 +- .github/workflows/publish-motsu-proc.yml | 30 -- .github/workflows/publish-motsu.yml | 30 -- Cargo.lock | 8 +- Cargo.toml | 9 +- README.md | 2 +- lib/motsu-proc/Cargo.toml | 26 -- lib/motsu-proc/README.md | 5 - lib/motsu-proc/src/lib.rs | 57 ---- lib/motsu-proc/src/test.rs | 72 ----- lib/motsu/Cargo.toml | 20 -- lib/motsu/README.md | 63 ----- lib/motsu/src/context.rs | 109 ------- lib/motsu/src/lib.rs | 50 ---- lib/motsu/src/prelude.rs | 5 - lib/motsu/src/shims.rs | 346 ----------------------- 17 files changed, 10 insertions(+), 835 deletions(-) delete mode 100644 .github/workflows/publish-motsu-proc.yml delete mode 100644 .github/workflows/publish-motsu.yml delete mode 100644 lib/motsu-proc/Cargo.toml delete mode 100644 lib/motsu-proc/README.md delete mode 100644 lib/motsu-proc/src/lib.rs delete mode 100644 lib/motsu-proc/src/test.rs delete mode 100644 lib/motsu/Cargo.toml delete mode 100644 lib/motsu/README.md delete mode 100644 lib/motsu/src/context.rs delete mode 100644 lib/motsu/src/lib.rs delete mode 100644 lib/motsu/src/prelude.rs delete mode 100644 lib/motsu/src/shims.rs diff --git a/.github/codecov.yml b/.github/codecov.yml index 9359748ed..0bfaa55da 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -15,14 +15,11 @@ coverage: default: informational: true # Docs and examples are not relevant to coverage. -# Motsu and e2e should be moved outside of this repo. ignore: - "docs" - "examples" - "lib/e2e" - "lib/e2e-proc" - - "lib/motsu" - - "lib/motsu-proc" # Make comments less noisy. comment: layout: "files" diff --git a/.github/workflows/check-publish.yml b/.github/workflows/check-publish.yml index ae73301c7..86c734c50 100644 --- a/.github/workflows/check-publish.yml +++ b/.github/workflows/check-publish.yml @@ -4,9 +4,9 @@ permissions: contents: read on: push: - branches: [ main, release/* ] + branches: [main, release/*] pull_request: - branches: [ main, release/* ] + branches: [main, release/*] concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} @@ -25,12 +25,6 @@ jobs: with: rustflags: "" - - name: Check motsu-proc - run: cargo publish -p motsu-proc --dry-run - - - name: Check motsu - run: cargo publish -p motsu --dry-run - - name: Check openzeppelin-crypto run: cargo publish -p openzeppelin-crypto --target wasm32-unknown-unknown --dry-run diff --git a/.github/workflows/publish-motsu-proc.yml b/.github/workflows/publish-motsu-proc.yml deleted file mode 100644 index 5e392138c..000000000 --- a/.github/workflows/publish-motsu-proc.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: publish motsu-proc -# This workflow publishes motsu-proc on crates.io. -permissions: - contents: read -on: - workflow_dispatch -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true -env: - CARGO_TERM_COLOR: always -jobs: - check-publish: - name: Publish motsu-proc on crates.io - env: - MOTSU_PROC_TOKEN: ${{ secrets.MOTSU_PROC_TOKEN }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Install rust - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - rustflags: "" - - - name: Check motsu-proc - run: cargo publish -p motsu-proc --dry-run - - - name: Publish motsu-proc - run: cargo publish -p motsu-proc --token $MOTSU_PROC_TOKEN diff --git a/.github/workflows/publish-motsu.yml b/.github/workflows/publish-motsu.yml deleted file mode 100644 index ec7b4e48e..000000000 --- a/.github/workflows/publish-motsu.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: publish motsu -# This workflow publishes motsu on crates.io. -permissions: - contents: read -on: - workflow_dispatch -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true -env: - CARGO_TERM_COLOR: always -jobs: - check-publish: - name: Publish motsu on crates.io - env: - MOTSU_TOKEN: ${{ secrets.MOTSU_TOKEN }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Install rust - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - rustflags: "" - - - name: Check motsu - run: cargo publish -p motsu --dry-run - - - name: Publish motsu - run: cargo publish -p motsu --token $MOTSU_TOKEN diff --git a/Cargo.lock b/Cargo.lock index ff7be5413..eed494b31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2471,6 +2471,8 @@ checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" [[package]] name = "motsu" version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e491f7b1aa9de2d3bb01479aafb76185b71e379306ad32d0073802e7f973ea01" dependencies = [ "const-hex", "dashmap 6.1.0", @@ -2483,13 +2485,11 @@ dependencies = [ [[package]] name = "motsu-proc" version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c3ac69e816a83121f1c0b1115a6880bd3eec07e47dfc2d4ff0f69004d0306a" dependencies = [ - "alloy-primitives", - "alloy-sol-types", - "motsu", "proc-macro2", "quote", - "stylus-sdk", "syn 2.0.68", ] diff --git a/Cargo.toml b/Cargo.toml index 66270beb7..b504d070f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,8 +3,6 @@ members = [ "contracts", "contracts-proc", "lib/crypto", - "lib/motsu", - "lib/motsu-proc", "lib/e2e", "lib/e2e-proc", "examples/erc20", @@ -30,8 +28,6 @@ default-members = [ "contracts", "contracts-proc", "lib/crypto", - "lib/motsu", - "lib/motsu-proc", "lib/e2e-proc", "examples/erc20", "examples/erc20-permit", @@ -121,12 +117,13 @@ syn = { version = "2.0.58", features = ["full"] } proc-macro2 = "1.0.79" quote = "1.0.35" +# test helpers +motsu = "0.2.0" + # members openzeppelin-stylus = { path = "contracts" } openzeppelin-stylus-proc = { path = "contracts-proc", version = "0.1.0" } openzeppelin-crypto = { path = "lib/crypto" } -motsu = { path = "lib/motsu" } -motsu-proc = { path = "lib/motsu-proc", version = "0.2.0" } e2e = { path = "lib/e2e" } e2e-proc = { path = "lib/e2e-proc" } diff --git a/README.md b/README.md index 17300df0a..d776a93e7 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ [`koba`]: https://github.com/OpenZeppelin/koba -[Unit]: ./lib/motsu/README.md +[Unit]: https://github.com/OpenZeppelin/stylus-test-helpers/Readme.md [integration]: ./lib/e2e/README.md diff --git a/lib/motsu-proc/Cargo.toml b/lib/motsu-proc/Cargo.toml deleted file mode 100644 index 58375a3ea..000000000 --- a/lib/motsu-proc/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "motsu-proc" -description = "Mostu's Procedural Macros" -edition.workspace = true -categories = ["development-tools::testing", "cryptography::cryptocurrencies"] -keywords = ["arbitrum", "ethereum", "stylus", "unit-tests", "tests"] -license.workspace = true -repository.workspace = true -version = "0.2.0" - -[dependencies] -proc-macro2.workspace = true -quote.workspace = true -syn.workspace = true - -[dev-dependencies] -motsu.workspace = true -alloy-primitives.workspace = true -alloy-sol-types.workspace = true -stylus-sdk.workspace = true - -[lib] -proc-macro = true - -[lints] -workspace = true diff --git a/lib/motsu-proc/README.md b/lib/motsu-proc/README.md deleted file mode 100644 index 40d2ef4b1..000000000 --- a/lib/motsu-proc/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Motsu's Procedural Macros - -This crate contains procedural macros used in [`motsu`]. - -[motsu]: ../motsu/README.md diff --git a/lib/motsu-proc/src/lib.rs b/lib/motsu-proc/src/lib.rs deleted file mode 100644 index c58093e86..000000000 --- a/lib/motsu-proc/src/lib.rs +++ /dev/null @@ -1,57 +0,0 @@ -//! Procedural macro definitions used in `motsu`. -use proc_macro::TokenStream; - -/// Shorthand to print nice errors. -/// -/// Note that it's defined before the module declarations. -macro_rules! error { - ($tokens:expr, $($msg:expr),+ $(,)?) => {{ - let error = syn::Error::new(syn::spanned::Spanned::span(&$tokens), format!($($msg),+)); - return error.to_compile_error().into(); - }}; - (@ $tokens:expr, $($msg:expr),+ $(,)?) => {{ - return Err(syn::Error::new(syn::spanned::Spanned::span(&$tokens), format!($($msg),+))) - }}; -} - -mod test; - -/// Defines a unit test that provides access to Stylus' execution context. -/// -/// Internally, this is a thin wrapper over `#[test]` that gives access to -/// affordances like contract storage and `msg::sender`. If you don't need -/// them, you can pass no arguments to the test function or simply use -/// `#[test]` instead of `#[motsu::test]`. -/// -/// # Examples -/// -/// ```rust,ignore -/// #[cfg(test)] -/// mod tests { -/// #[motsu::test] -/// fn reads_balance(contract: Erc20) { -/// let balance = contract.balance_of(Address::ZERO); -/// assert_eq!(U256::ZERO, balance); -/// -/// let owner = msg::sender(); -/// let one = U256::from(1); -/// contract._balances.setter(owner).set(one); -/// let balance = contract.balance_of(owner); -/// assert_eq!(one, balance); -/// } -/// } -/// ``` -/// -/// ```rust,ignore -/// #[cfg(test)] -/// mod tests { -/// #[motsu::test] -/// fn t() { // If no params, it expands to a `#[test]`. -/// ... -/// } -/// } -/// ``` -#[proc_macro_attribute] -pub fn test(attr: TokenStream, input: TokenStream) -> TokenStream { - test::test(&attr, input) -} diff --git a/lib/motsu-proc/src/test.rs b/lib/motsu-proc/src/test.rs deleted file mode 100644 index 2e88961cd..000000000 --- a/lib/motsu-proc/src/test.rs +++ /dev/null @@ -1,72 +0,0 @@ -//! Defines the `#[motsu::test]` procedural macro. -use proc_macro::TokenStream; -use quote::quote; -use syn::{parse_macro_input, FnArg}; - -/// Defines a unit test that provides access to Stylus execution context. -/// -/// For more information see [`crate::test`]. -pub(crate) fn test(_attr: &TokenStream, input: TokenStream) -> TokenStream { - let item_fn = parse_macro_input!(input as syn::ItemFn); - let attrs = &item_fn.attrs; - let sig = &item_fn.sig; - let fn_name = &sig.ident; - let fn_return_type = &sig.output; - let fn_block = &item_fn.block; - let fn_args = &sig.inputs; - - // Currently, more than one contract per unit test is not supported. - if fn_args.len() > 1 { - error!(fn_args, "expected at most one contract in test signature"); - } - - // Whether 1 or none contracts will be declared. - let arg_binding_and_ty = match fn_args - .into_iter() - .map(|arg| { - let FnArg::Typed(arg) = arg else { - error!(@arg, "unexpected receiver argument in test signature"); - }; - let contract_arg_binding = &arg.pat; - let contract_ty = &arg.ty; - Ok((contract_arg_binding, contract_ty)) - }) - .collect::, _>>() - { - Ok(res) => res, - Err(err) => return err.to_compile_error().into(), - }; - - let contract_arg_defs = - arg_binding_and_ty.iter().map(|(arg_binding, contract_ty)| { - // Test case assumes, that contract's variable has `&mut` reference - // to contract's type. - quote! { - #arg_binding: &mut #contract_ty - } - }); - - let contract_args = - arg_binding_and_ty.iter().map(|(_arg_binding, contract_ty)| { - // Pass mutable reference to the contract. - quote! { - &mut <#contract_ty>::default() - } - }); - - // Declare test case closure. - // Pass mut ref to the test closure and call it. - // Reset storage for the test context and return test's output. - quote! { - #( #attrs )* - #[test] - fn #fn_name() #fn_return_type { - use ::motsu::prelude::DefaultStorage; - let test = | #( #contract_arg_defs ),* | #fn_block; - let res = test( #( #contract_args ),* ); - ::motsu::prelude::Context::current().reset_storage(); - res - } - } - .into() -} diff --git a/lib/motsu/Cargo.toml b/lib/motsu/Cargo.toml deleted file mode 100644 index 2bfa39776..000000000 --- a/lib/motsu/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "motsu" -description = "Unit Testing for Stylus" -edition.workspace = true -categories = ["development-tools::testing", "cryptography::cryptocurrencies"] -keywords = ["arbitrum", "ethereum", "stylus", "unit-tests", "tests"] -license.workspace = true -repository.workspace = true -version = "0.2.0" - -[dependencies] -const-hex.workspace = true -once_cell.workspace = true -tiny-keccak.workspace = true -stylus-sdk.workspace = true -motsu-proc.workspace = true -dashmap.workspace = true - -[lints] -workspace = true diff --git a/lib/motsu/README.md b/lib/motsu/README.md deleted file mode 100644 index f7f10674e..000000000 --- a/lib/motsu/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# Motsu (持つ) - Unit Testing for Stylus - -This crate enables unit-testing for Stylus contracts. It abstracts away the -machinery necessary for writing tests behind a `#[motsu::test]` procedural -macro. - -`motsu` means ["to hold"](https://jisho.org/word/%E6%8C%81%E3%81%A4) in -Japanese -- we hold a stylus in our hand. - -## Usage - -Annotate tests with `#[motsu::test]` instead of `#[test]` to get access to VM -affordances. - -Note that we require contracts to implement `stylus_sdk::prelude::StorageType`. -This trait is typically implemented by default with `stylus_proc::sol_storage` -or `stylus_proc::storage` macros. - -```rust -#[cfg(test)] -mod tests { - use contracts::token::erc20::Erc20; - - #[motsu::test] - fn reads_balance(contract: Erc20) { - let balance = contract.balance_of(Address::ZERO); // Access storage. - assert_eq!(balance, U256::ZERO); - } -} -``` - -Annotating a test function that accepts no parameters will make `#[motsu::test]` -behave the same as `#[test]`. - -```rust,ignore -#[cfg(test)] -mod tests { - #[motsu::test] - fn t() { // If no params, it expands to a `#[test]`. - // ... - } -} -``` - -Note that currently, test suites using `motsu::test` will run serially because -of global access to storage. - -### Notice - -We maintain this crate on a best-effort basis. We use it extensively on our own -tests, so we will add here any symbols we may need. However, since we expect -this to be a temporary solution, don't expect us to address all requests. - -That being said, please do open an issue to start a discussion, keeping in mind -our [code of conduct] and [contribution guidelines]. - -[code of conduct]: ../../CODE_OF_CONDUCT.md - -[contribution guidelines]: ../../CONTRIBUTING.md - -## Security - -Refer to our [Security Policy](../../SECURITY.md) for more details. diff --git a/lib/motsu/src/context.rs b/lib/motsu/src/context.rs deleted file mode 100644 index 381d504b9..000000000 --- a/lib/motsu/src/context.rs +++ /dev/null @@ -1,109 +0,0 @@ -//! Unit-testing context for Stylus contracts. - -use std::{collections::HashMap, ptr}; - -use dashmap::DashMap; -use once_cell::sync::Lazy; -use stylus_sdk::{alloy_primitives::uint, prelude::StorageType}; - -use crate::prelude::{Bytes32, WORD_BYTES}; - -/// Context of stylus unit tests associated with the current test thread. -#[allow(clippy::module_name_repetitions)] -pub struct Context { - thread_name: ThreadName, -} - -impl Context { - /// Get test context associated with the current test thread. - #[must_use] - pub fn current() -> Self { - Self { thread_name: ThreadName::current() } - } - - /// Get the value at `key` in storage. - pub(crate) fn get_bytes(self, key: &Bytes32) -> Bytes32 { - let storage = STORAGE.entry(self.thread_name).or_default(); - storage.contract_data.get(key).copied().unwrap_or_default() - } - - /// Get the raw value at `key` in storage and write it to `value`. - pub(crate) unsafe fn get_bytes_raw(self, key: *const u8, value: *mut u8) { - let key = read_bytes32(key); - - write_bytes32(value, self.get_bytes(&key)); - } - - /// Set the value at `key` in storage to `value`. - pub(crate) fn set_bytes(self, key: Bytes32, value: Bytes32) { - let mut storage = STORAGE.entry(self.thread_name).or_default(); - storage.contract_data.insert(key, value); - } - - /// Set the raw value at `key` in storage to `value`. - pub(crate) unsafe fn set_bytes_raw(self, key: *const u8, value: *const u8) { - let (key, value) = (read_bytes32(key), read_bytes32(value)); - self.set_bytes(key, value); - } - - /// Clears storage, removing all key-value pairs associated with the current - /// test thread. - pub fn reset_storage(self) { - STORAGE.remove(&self.thread_name); - } -} - -/// Storage mock: A global mutable key-value store. -/// Allows concurrent access. -/// -/// The key is the name of the test thread, and the value is the storage of the -/// test case. -static STORAGE: Lazy> = - Lazy::new(DashMap::new); - -/// Test thread name metadata. -#[derive(Clone, Eq, PartialEq, Hash)] -struct ThreadName(String); - -impl ThreadName { - /// Get the name of the current test thread. - fn current() -> Self { - let current_thread_name = std::thread::current() - .name() - .expect("should retrieve current thread name") - .to_string(); - Self(current_thread_name) - } -} - -/// Storage for unit test's mock data. -#[derive(Default)] -struct MockStorage { - /// Contract's mock data storage. - contract_data: HashMap, -} - -/// Read the word from location pointed by `ptr`. -unsafe fn read_bytes32(ptr: *const u8) -> Bytes32 { - let mut res = Bytes32::default(); - ptr::copy(ptr, res.as_mut_ptr(), WORD_BYTES); - res -} - -/// Write the word `bytes` to the location pointed by `ptr`. -unsafe fn write_bytes32(ptr: *mut u8, bytes: Bytes32) { - ptr::copy(bytes.as_ptr(), ptr, WORD_BYTES); -} - -/// Initializes fields of contract storage and child contract storages with -/// default values. -pub trait DefaultStorage: StorageType { - /// Initializes fields of contract storage and child contract storages with - /// default values. - #[must_use] - fn default() -> Self { - unsafe { Self::new(uint!(0_U256), 0) } - } -} - -impl DefaultStorage for ST {} diff --git a/lib/motsu/src/lib.rs b/lib/motsu/src/lib.rs deleted file mode 100644 index 7636b7541..000000000 --- a/lib/motsu/src/lib.rs +++ /dev/null @@ -1,50 +0,0 @@ -//! # Motsu - Unit Testing for Stylus -//! -//! This crate enables unit-testing for Stylus contracts. It abstracts away the -//! machinery necessary for writing tests behind a -//! [`#[motsu::test]`][test_attribute] procedural macro. -//! -//! The name `motsu` is an analogy to the place where you put your fingers to -//! hold a stylus pen. -//! -//! ## Usage -//! -//! Annotate tests with [`#[motsu::test]`][test_attribute] instead of `#[test]` -//! to get access to VM affordances. -//! -//! Note that we require contracts to implement -//! `stylus_sdk::prelude::StorageType`. This trait is typically implemented by -//! default with `stylus_proc::sol_storage` or `stylus_proc::storage` macros. -//! -//! ```rust -//! #[cfg(test)] -//! mod tests { -//! use contracts::token::erc20::Erc20; -//! -//! #[motsu::test] -//! fn reads_balance(contract: Erc20) { -//! let balance = contract.balance_of(Address::ZERO); // Access storage. -//! assert_eq!(balance, U256::ZERO); -//! } -//! } -//! ``` -//! -//! Annotating a test function that accepts no parameters will make -//! [`#[motsu::test]`][test_attribute] behave the same as `#[test]`. -//! -//! ```rust,ignore -//! #[cfg(test)] -//! mod tests { -//! #[motsu::test] // Equivalent to #[test] -//! fn test_fn() { -//! ... -//! } -//! } -//! ``` -//! -//! [test_attribute]: crate::test -mod context; -pub mod prelude; -mod shims; - -pub use motsu_proc::test; diff --git a/lib/motsu/src/prelude.rs b/lib/motsu/src/prelude.rs deleted file mode 100644 index 5c8798297..000000000 --- a/lib/motsu/src/prelude.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! Common imports for `motsu` tests. -pub use crate::{ - context::{Context, DefaultStorage}, - shims::*, -}; diff --git a/lib/motsu/src/shims.rs b/lib/motsu/src/shims.rs deleted file mode 100644 index 162f9e2fd..000000000 --- a/lib/motsu/src/shims.rs +++ /dev/null @@ -1,346 +0,0 @@ -//! Shims that mock common host imports in Stylus `wasm` programs. -//! -//! Most of the documentation is taken from the [Stylus source]. -//! -//! We allow unsafe here because safety is guaranteed by the Stylus team. -//! -//! [Stylus source]: https://github.com/OffchainLabs/stylus/blob/484efac4f56fb70f96d4890748b8ec2543d88acd/arbitrator/wasm-libraries/user-host-trait/src/lib.rs -//! -//! ## Motivation -//! -//! Without these shims we can't currently run unit tests for stylus contracts, -//! since the symbols the compiled binaries expect to find are not there. -//! -//! If you run `cargo test` on a fresh Stylus project, it will error with: -//! -//! ```terminal -//! dyld[97792]: missing symbol called -//! ``` -//! -//! This crate is a temporary solution until the Stylus team provides us with a -//! different and more stable mechanism for unit-testing our contracts. -//! -//! ## Usage -//! -//! Import these shims in your test modules as `motsu::prelude::*` to populate -//! the namespace with the appropriate symbols. -//! -//! ```rust,ignore -//! #[cfg(test)] -//! mod tests { -//! use contracts::token::erc20::Erc20; -//! -//! #[motsu::test] -//! fn reads_balance(contract: Erc20) { -//! let balance = contract.balance_of(Address::ZERO); // Access storage. -//! assert_eq!(balance, U256::ZERO); -//! } -//! } -//! ``` -#![allow(clippy::missing_safety_doc)] -use std::slice; - -use tiny_keccak::{Hasher, Keccak}; - -use crate::context::Context; - -pub(crate) const WORD_BYTES: usize = 32; -pub(crate) type Bytes32 = [u8; WORD_BYTES]; - -/// Efficiently computes the [`keccak256`] hash of the given preimage. -/// The semantics are equivalent to that of the EVM's [`SHA3`] opcode. -/// -/// [`keccak256`]: https://en.wikipedia.org/wiki/SHA-3 -/// [`SHA3`]: https://www.evm.codes/#20 -#[no_mangle] -pub unsafe extern "C" fn native_keccak256( - bytes: *const u8, - len: usize, - output: *mut u8, -) { - let mut hasher = Keccak::v256(); - - let data = slice::from_raw_parts(bytes, len); - hasher.update(data); - - let output = slice::from_raw_parts_mut(output, WORD_BYTES); - hasher.finalize(output); -} - -/// Reads a 32-byte value from permanent storage. -/// -/// Stylus's storage format is identical to that of the EVM. This means that, -/// under the hood, this hostio is accessing the 32-byte value stored in the EVM -/// state trie at offset `key`, which will be `0` when not previously set. The -/// semantics, then, are equivalent to that of the EVM's [`SLOAD`] opcode. -/// -/// [`SLOAD`]: https://www.evm.codes/#54 -/// -/// # Panics -/// -/// May panic if unable to lock `STORAGE`. -#[no_mangle] -pub unsafe extern "C" fn storage_load_bytes32(key: *const u8, out: *mut u8) { - Context::current().get_bytes_raw(key, out); -} - -/// Writes a 32-byte value to the permanent storage cache. -/// -/// Stylus's storage format is identical to that of the EVM. This means that, -/// under the hood, this hostio represents storing a 32-byte value into the EVM -/// state trie at offset `key`. Refunds are tabulated exactly as in the EVM. The -/// semantics, then, are equivalent to that of the EVM's [`SSTORE`] opcode. -/// -/// Note: because the value is cached, one must call `storage_flush_cache` to -/// persist it. -/// -/// [`SSTORE`]: https://www.evm.codes/#55 -/// -/// # Panics -/// -/// May panic if unable to lock `STORAGE`. -#[no_mangle] -pub unsafe extern "C" fn storage_cache_bytes32( - key: *const u8, - value: *const u8, -) { - Context::current().set_bytes_raw(key, value); -} - -/// Persists any dirty values in the storage cache to the EVM state trie, -/// dropping the cache entirely if requested. Analogous to repeated invocations -/// of [`SSTORE`]. -/// -/// [`SSTORE`]: https://www.evm.codes/#55 -pub fn storage_flush_cache(_: bool) { - // No-op: we don't use the cache in our unit-tests. -} - -/// Dummy msg sender set for tests. -pub const MSG_SENDER: &[u8; 42] = b"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"; - -/// Dummy contract address set for tests. -pub const CONTRACT_ADDRESS: &[u8; 42] = - b"0xdCE82b5f92C98F27F116F70491a487EFFDb6a2a9"; - -/// Arbitrum's CHAID ID. -pub const CHAIN_ID: u64 = 42161; - -/// Externally Owned Account (EOA) code hash. -pub const EOA_CODEHASH: &[u8; 66] = - b"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"; - -/// Gets the address of the account that called the program. -/// -/// For normal L2-to-L2 transactions the semantics are equivalent to that of the -/// EVM's [`CALLER`] opcode, including in cases arising from [`DELEGATE_CALL`]. -/// -/// For L1-to-L2 retryable ticket transactions, the top-level sender's address -/// will be aliased. See [`Retryable Ticket Address Aliasing`][aliasing] for -/// more information on how this works. -/// -/// [`CALLER`]: https://www.evm.codes/#33 -/// [`DELEGATE_CALL`]: https://www.evm.codes/#f4 -/// [aliasing]: https://developer.arbitrum.io/arbos/l1-to-l2-messaging#address-aliasing -/// -/// # Panics -/// -/// May panic if fails to parse `MSG_SENDER` as an address. -#[no_mangle] -pub unsafe extern "C" fn msg_sender(sender: *mut u8) { - let addr = const_hex::const_decode_to_array::<20>(MSG_SENDER).unwrap(); - std::ptr::copy(addr.as_ptr(), sender, 20); -} - -/// Gets the address of the current program. The semantics are equivalent to -/// that of the EVM's [`ADDRESS`] opcode. -/// -/// [`ADDRESS`]: https://www.evm.codes/#30 -/// -/// # Panics -/// -/// May panic if fails to parse `CONTRACT_ADDRESS` as an address. -#[no_mangle] -pub unsafe extern "C" fn contract_address(address: *mut u8) { - let addr = - const_hex::const_decode_to_array::<20>(CONTRACT_ADDRESS).unwrap(); - std::ptr::copy(addr.as_ptr(), address, 20); -} - -/// Gets the chain ID of the current chain. The semantics are equivalent to -/// that of the EVM's [`CHAINID`] opcode. -/// -/// [`CHAINID`]: https://www.evm.codes/#46 -#[no_mangle] -pub unsafe extern "C" fn chainid() -> u64 { - CHAIN_ID -} - -/// Emits an EVM log with the given number of topics and data, the first bytes -/// of which should be the 32-byte-aligned topic data. -/// -/// The semantics are equivalent to that of the EVM's [`LOG0`], [`LOG1`], -/// [`LOG2`], [`LOG3`], and [`LOG4`] opcodes based on the number of topics -/// specified. Requesting more than `4` topics will induce a revert. -/// -/// [`LOG0`]: https://www.evm.codes/#a0 -/// [`LOG1`]: https://www.evm.codes/#a1 -/// [`LOG2`]: https://www.evm.codes/#a2 -/// [`LOG3`]: https://www.evm.codes/#a3 -/// [`LOG4`]: https://www.evm.codes/#a4 -#[no_mangle] -pub unsafe extern "C" fn emit_log(_: *const u8, _: usize, _: usize) { - // No-op: we don't check for events in our unit-tests. -} - -/// Gets the code hash of the account at the given address. -/// -/// The semantics are equivalent to that of the EVM's [`EXT_CODEHASH`] opcode. -/// Note that the code hash of an account without code will be the empty hash -/// `keccak("") = -/// c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`. -/// -/// [`EXT_CODEHASH`]: https://www.evm.codes/#3F -/// -/// # Panics -/// -/// May panic if fails to parse `ACCOUNT_CODEHASH` as a keccack hash. -#[no_mangle] -pub unsafe extern "C" fn account_codehash(_address: *const u8, dest: *mut u8) { - let account_codehash = - const_hex::const_decode_to_array::<32>(EOA_CODEHASH).unwrap(); - - std::ptr::copy(account_codehash.as_ptr(), dest, 32); -} - -/// Returns the length of the last EVM call or deployment return result, or `0` -/// if neither have happened during the program's execution. -/// -/// The semantics are equivalent to that of the EVM's [`RETURN_DATA_SIZE`] -/// opcode. -/// -/// [`RETURN_DATA_SIZE`]: https://www.evm.codes/#3d -#[no_mangle] -pub unsafe extern "C" fn return_data_size() -> usize { - // TODO: #156 - // No-op: we do not use this function in our unit-tests, - // but the binary does include it. - 0 -} - -/// Copies the bytes of the last EVM call or deployment return result. -/// -/// Does not revert if out of bounds, but rather copies the overlapping portion. -/// The semantics are otherwise equivalent to that of the EVM's -/// [`RETURN_DATA_COPY`] opcode. -/// -/// Returns the number of bytes written. -/// -/// [`RETURN_DATA_COPY`]: https://www.evm.codes/#3e -#[no_mangle] -pub unsafe extern "C" fn read_return_data( - _dest: *mut u8, - _offset: usize, - _size: usize, -) -> usize { - // TODO: #156 - // No-op: we do not use this function in our unit-tests, - // but the binary does include it. - 0 -} - -/// Calls the contract at the given address with options for passing value and -/// to limit the amount of gas supplied. The return status indicates whether the -/// call succeeded, and is nonzero on failure. -/// -/// In both cases `return_data_len` will store the length of the result, the -/// bytes of which can be read via the `read_return_data` hostio. The bytes are -/// not returned directly so that the programmer can potentially save gas by -/// choosing which subset of the return result they'd like to copy. -/// -/// The semantics are equivalent to that of the EVM's [`CALL`] opcode, including -/// callvalue stipends and the 63/64 gas rule. This means that supplying the -/// `u64::MAX` gas can be used to send as much as possible. -/// -/// [`CALL`]: https://www.evm.codes/#f1 -#[no_mangle] -pub unsafe extern "C" fn call_contract( - _contract: *const u8, - _calldata: *const u8, - _calldata_len: usize, - _value: *const u8, - _gas: u64, - _return_data_len: *mut usize, -) -> u8 { - // TODO: #156 - // No-op: we do not use this function in our unit-tests, - // but the binary does include it. - 0 -} - -/// Static calls the contract at the given address, with the option to limit the -/// amount of gas supplied. The return status indicates whether the call -/// succeeded, and is nonzero on failure. -/// -/// In both cases `return_data_len` will store the length of the result, the -/// bytes of which can be read via the `read_return_data` hostio. The bytes are -/// not returned directly so that the programmer can potentially save gas by -/// choosing which subset of the return result they'd like to copy. -/// -/// The semantics are equivalent to that of the EVM's [`STATIC_CALL`] opcode, -/// including the 63/64 gas rule. This means that supplying `u64::MAX` gas can -/// be used to send as much as possible. -/// -/// [`STATIC_CALL`]: https://www.evm.codes/#FA -#[no_mangle] -pub unsafe extern "C" fn static_call_contract( - _contract: *const u8, - _calldata: *const u8, - _calldata_len: usize, - _gas: u64, - _return_data_len: *mut usize, -) -> u8 { - // TODO: #156 - // No-op: we do not use this function in our unit-tests, - // but the binary does include it. - 0 -} - -/// Delegate calls the contract at the given address, with the option to limit -/// the amount of gas supplied. The return status indicates whether the call -/// succeeded, and is nonzero on failure. -/// -/// In both cases `return_data_len` will store the length of the result, the -/// bytes of which can be read via the `read_return_data` hostio. The bytes are -/// not returned directly so that the programmer can potentially save gas by -/// choosing which subset of the return result they'd like to copy. -/// -/// The semantics are equivalent to that of the EVM's [`DELEGATE_CALL`] opcode, -/// including the 63/64 gas rule. This means that supplying `u64::MAX` gas can -/// be used to send as much as possible. -/// -/// [`DELEGATE_CALL`]: https://www.evm.codes/#F4 -#[no_mangle] -pub unsafe extern "C" fn delegate_call_contract( - _contract: *const u8, - _calldata: *const u8, - _calldata_len: usize, - _gas: u64, - _return_data_len: *mut usize, -) -> u8 { - // TODO: #156 - // No-op: we do not use this function in our unit-tests, - // but the binary does include it. - 0 -} - -/// Gets a bounded estimate of the Unix timestamp at which the Sequencer -/// sequenced the transaction. See [`Block Numbers and Time`] for more -/// information on how this value is determined. -/// -/// [`Block Numbers and Time`]: https://developer.arbitrum.io/time -#[no_mangle] -pub unsafe extern "C" fn block_timestamp() -> u64 { - // Epoch timestamp: 1st January 2025 00::00::00 - 1_735_689_600 -}