Skip to content

Commit

Permalink
Export the rust version to WASM + Use it in a Go library
Browse files Browse the repository at this point in the history
  • Loading branch information
ngotchac committed Sep 16, 2024
1 parent 73cb5b2 commit 1f6d12d
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ wasm-bindgen = { version = "0.2", optional = true }
[features]
python = ["dep:pyo3"]
javascript = ["dep:wasm-bindgen"]
wasm = []

# for dev
trace = []
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.PHONY: wasm
wasm:
cargo build --target wasm32-unknown-unknown --features wasm --release
cp target/wasm32-unknown-unknown/release/evmole.wasm go/evmole.wasm
Binary file added evmole.wasm
Binary file not shown.
78 changes: 78 additions & 0 deletions go/evmole.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package evmole

import (
"context"
_ "embed"
"encoding/binary"
"fmt"

"github.com/tetratelabs/wazero"
)

//go:embed evmole.wasm
var evmoleWASM []byte

// FunctionSelectors tries to extract from the given bytecode the function selectors.
func FunctionSelectors(ctx context.Context, code []byte) ([][4]byte, error) {
// Create a new WebAssembly runtime
runtime := wazero.NewRuntime(ctx)
defer func() { _ = runtime.Close(ctx) }()

instance, err := runtime.Instantiate(ctx, evmoleWASM)
if err != nil {
panic(fmt.Errorf("failed to instantiate WASM module: %w", err))
}
defer func() { _ = instance.Close(ctx) }()

gasLimit := 0

memory := instance.Memory()
functionSelectorsFunc := instance.ExportedFunction("function_selectors")
if functionSelectorsFunc == nil {
panic("could not find exported function: function_selectors")
}

codeOffset := uint32(0)
resultLenOffset := uint32(len(code))
resultOffset := resultLenOffset + 4
resultCapacity := uint32(512 * 4)

// Write input to memory
ok := memory.Write(codeOffset, code)
if !ok {
return nil, fmt.Errorf("failed to write input to memory")
}

// Call the WASM function
results, err := functionSelectorsFunc.Call(ctx,
uint64(codeOffset), uint64(len(code)), uint64(gasLimit),
uint64(resultLenOffset), uint64(resultOffset), uint64(resultCapacity))
if err != nil {
return nil, fmt.Errorf("failed to call function_selectors: %w", err)
}

if status := uint32(results[0]); status != 0 {
return nil, fmt.Errorf("error: status=%d", status)
}

// Read the actual result length
rawResultLen, ok := memory.Read(resultLenOffset, 4)
if !ok {
return nil, fmt.Errorf("failed to read result length")
}
resultLen := binary.LittleEndian.Uint32(rawResultLen)

// Success, read the result
result, ok := memory.Read(resultOffset, resultLen)
if !ok {
return nil, fmt.Errorf("failed to read result from memory")
}

// Convert to [][4]byte
selectors := make([][4]byte, len(result)/4)
for i := 0; i < len(result); i += 4 {
copy(selectors[i/4][:], result[i:i+4])
}

return selectors, nil
}
Binary file added go/evmole.wasm
Binary file not shown.
98 changes: 98 additions & 0 deletions go/evmole_test.go

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions go/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/cdump/evmole/go

go 1.22.5

require github.com/tetratelabs/wazero v1.7.3
2 changes: 2 additions & 0 deletions go/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/tetratelabs/wazero v1.7.3 h1:PBH5KVahrt3S2AHgEjKu4u+LlDbbk+nsGE3KLucy6Rw=
github.com/tetratelabs/wazero v1.7.3/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y=
30 changes: 30 additions & 0 deletions src/interface_wasm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#[no_mangle]
pub extern "C" fn function_selectors(
code_ptr: *const u8,
code_len: usize,
gas_limit: u32,
result_len_ptr: *mut u32,
result_ptr: *mut u8,
result_capacity: usize,
) -> u32 {
let code = unsafe { std::slice::from_raw_parts(code_ptr, code_len) };
let selectors = crate::selectors::function_selectors(code, gas_limit);

let flattened: Vec<u8> = selectors.into_iter().flatten().collect();
let result_len = flattened.len();

unsafe {
*result_len_ptr = result_len as u32;
}

if result_len > result_capacity {
// Buffer too small
return 1;
}

unsafe {
std::ptr::copy_nonoverlapping(flattened.as_ptr(), result_ptr, result_len);
}
// Success
0
}
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ mod interface_py;

#[cfg(feature = "javascript")]
mod interface_js;

#[cfg(feature = "wasm")]
mod interface_wasm;

0 comments on commit 1f6d12d

Please sign in to comment.