WASM in Rust
Let’s make something more than the basic WASM example that adds 2 numbers together!
https://doc.rust-lang.org/rustc/platform-support/wasm32-wasip1.html
We’ll do a toy Bitcoin address validator. (Note, it’s not a real one, we’ll just use SHA256 twice).
https://github.com/RGGH/tool_sigcheck
cargo new --lib tool_sigcheck
[package]
name = "tool_sigcheck"
version = "0.1.0"
edition = "2021"
[dependencies]
base58 = "0.2" # For decoding Base58Check encoding
hex = "0.4"
anyhow = "1"
sha2 = "0.10" # For double SHA-256 checksum
ed25519-compact = "2"
[lib]
crate-type = ["cdylib"]
use base58::FromBase58;
use sha2::{Digest, Sha256};
use std::env;
/// Computes double SHA-256 hash.
fn double_sha256(data: &[u8]) -> [u8; 32] {
let hash1 = Sha256::digest(data);
let hash2 = Sha256::digest(hash1);
hash2.into()
}
/// Validates a Bitcoin address.
fn validate_bitcoin_address(address: &str) -> bool {
let decoded = match address.from_base58() {
Ok(bytes) => bytes,
Err(_) => return false, // Invalid Base58 encoding
};
if decoded.len() < 4 {
return false;
}
let (payload, checksum) = decoded.split_at(decoded.len() - 4);
let expected_checksum = &double_sha256(payload)[..4];
checksum == expected_checksum
}
/// WASI entry point: `_start`
#[no_mangle]
pub extern "C" fn _start() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("โ ๏ธ Usage: wasmer run tool_sigcheck.wasm -- <bitcoin_address>");
return;
}
let address = &args[1];
if validate_bitcoin_address(address) {
println!("โ
Valid Bitcoin address.");
} else {
println!("โ Invalid Bitcoin address.");
}
}
๐ Rebuild & Run (WASI Preview 1)
1๏ธโฃ Build for wasip1
cargo build --release --target wasm32-wasip1
2๏ธโฃ Run with Wasmer
wasmer run target/wasm32-wasip1/release/tool_sigcheck.wasm -- 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa
๐ง Fix: Add _start
for WASI Execution
error: Loading exports failed
โฐโโถ 1: Missing export _start
This error happens because WASI expects a _start
function as the entry point, but Rust’s standard binary entry (main
) is not automatically mapped to _start
when compiled for WASI.
โ
Solution: Add a _start
Function
We need to explicitly define _start
for WASI execution.
๐น Fix in src/lib.rs
use base58::FromBase58;
use sha2::{Digest, Sha256};
use std::env;
/// Computes double SHA-256 hash.
fn double_sha256(data: &[u8]) -> [u8; 32] {
let hash1 = Sha256::digest(data);
let hash2 = Sha256::digest(hash1);
hash2.into()
}
/// Validates a Bitcoin address.
fn validate_bitcoin_address(address: &str) -> bool {
let decoded = match address.from_base58() {
Ok(bytes) => bytes,
Err(_) => return false, // Invalid Base58 encoding
};
if decoded.len() < 4 {
return false;
}
let (payload, checksum) = decoded.split_at(decoded.len() - 4);
let expected_checksum = &double_sha256(payload)[..4];
checksum == expected_checksum
}
/// WASI entry point: `_start`
#[no_mangle]
pub extern "C" fn _start() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("โ ๏ธ Usage: wasmer run tool_sigcheck.wasm -- <bitcoin_address>");
return;
}
let address = &args[1];
if validate_bitcoin_address(address) {
println!("โ
Valid Bitcoin address.");
} else {
println!("โ Invalid Bitcoin address.");
}
}
๐ Rebuild & Run (WASI Preview 1)
1๏ธโฃ Build for wasip1
cargo build --release --target wasm32-wasip1
2๏ธโฃ Run with Wasmer
wasmer run target/wasm32-wasip1/release/tool_sigcheck.wasm -- 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa
โ Expected Output:
โ
Valid Bitcoin address.
๐ก Why This Fix Works
- Rust does not automatically provide
_start
in WASI. - WASI runtimes (e.g.,
wasmer
,wasmtime
) look for_start
as the entry point. - Manually defining
_start
makes the Wasm executable valid.
๐ฏ Now it works across all WASI runtimes! ๐