Skip to content

Commit

Permalink
feat(wavm-shims): add documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
alexfertel committed Mar 20, 2024
1 parent 5f7653a commit 9e54b08
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 10 deletions.
45 changes: 45 additions & 0 deletions lib/wavm-shims/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,46 @@
# WAVM Shims

Shims crate that mocks common host imports in Stylus `wasm` programs.

## Motivation

Without this crate 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:

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 this crate in your test modules as `wavm_shims::*` to populate the
namespace with the appropriate symbols.

```rust
#[cfg(test)]
mod tests {
use wavm_shims::*;

#[test]
fn reads_balance() {
let token = init_token(); // Init an ERC-20, for example.
let balance = token.balance_of(Address::ZERO); // Access storage.
assert_eq!(balance, U256::ZERO);
}
}
```

---

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
65 changes: 55 additions & 10 deletions lib/wavm-shims/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
//! Shim crate to mock Stylus's `vm_hooks`.
//!
//! Most of the documentation is taken from the [Stylus source].
//!
//! [Stylus source]: https://github.com/OffchainLabs/stylus/blob/484efac4f56fb70f96d4890748b8ec2543d88acd/arbitrator/wasm-libraries/user-host-trait/src/lib.rs
use std::{collections::HashMap, ptr, slice, sync::Mutex};

use once_cell::sync::Lazy;
Expand All @@ -7,8 +11,13 @@ use tiny_keccak::{Hasher, Keccak};
pub const WORD_BYTES: usize = 32;
pub 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 extern "C" fn native_keccak256(
pub unsafe extern "C" fn native_keccak256(
bytes: *const u8,
len: usize,
output: *mut u8,
Expand All @@ -22,28 +31,31 @@ pub extern "C" fn native_keccak256(
hasher.finalize(output);
}

/// Storage mock: A global mutable key-value store.
pub static STORAGE: Lazy<Mutex<HashMap<Bytes32, Bytes32>>> =
Lazy::new(|| Mutex::new(HashMap::new()));

/// Read the word at address `key`.
pub unsafe fn read_bytes32(key: *const u8) -> Bytes32 {
let mut res = Bytes32::default();
ptr::copy(key, res.as_mut_ptr(), WORD_BYTES);
res
}

/// Write the word `val` to the location pointed by `key`.
pub unsafe fn write_bytes32(key: *mut u8, val: Bytes32) {
ptr::copy(val.as_ptr(), key, WORD_BYTES);
}

/// 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
#[no_mangle]
pub extern "C" fn storage_store_bytes32(key: *const u8, value: *const u8) {
let (key, value) = unsafe { (read_bytes32(key), read_bytes32(value)) };

STORAGE.lock().unwrap().insert(key, value);
}

#[no_mangle]
pub extern "C" fn storage_load_bytes32(key: *const u8, out: *mut u8) {
pub unsafe extern "C" fn storage_load_bytes32(key: *const u8, out: *mut u8) {
let key = unsafe { read_bytes32(key) };

let value = STORAGE
Expand All @@ -56,8 +68,41 @@ pub extern "C" fn storage_load_bytes32(key: *const u8, out: *mut u8) {
unsafe { write_bytes32(out, value) };
}

const MSG_SENDER: &[u8; 42] = b"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF";
/// Stores a 32-byte value to permanent storage. Stylus's storage format is
/// identical to that of the EVM. This means that, under the hood, this hostio
/// is storing a 32-byte value into the EVM state trie at offset `key`.
/// Furthermore, refunds are tabulated exactly as in the EVM. The semantics,
/// then, are equivalent to that of the EVM's [`SSTORE`] opcode.
///
/// Note: we require the [`SSTORE`] sentry per EVM rules. The `gas_cost`
/// returned by the EVM API may exceed this amount, but that's ok because the
/// predominant cost is due to state bloat concerns.
///
/// [`SSTORE`]: https://www.evm.codes/#55
#[no_mangle]
pub unsafe extern "C" fn storage_store_bytes32(
key: *const u8,
value: *const u8,
) {
let (key, value) = unsafe { (read_bytes32(key), read_bytes32(value)) };

STORAGE.lock().unwrap().insert(key, value);
}

///
pub const MSG_SENDER: &[u8; 42] = b"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF";

/// 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
#[no_mangle]
pub unsafe extern "C" fn msg_sender(sender: *mut u8) {
let addr = const_hex::const_decode_to_array::<20>(MSG_SENDER).unwrap();
Expand Down

0 comments on commit 9e54b08

Please sign in to comment.