Security Considerations for WASM Contracts on Amadeus: A Complete Technical Guide
Amadeus executes WASM smart contracts inside a sandboxed Wasmer runtime with defense-in-depth validation that enforces binary size limits, structural constraints, gas metering, and a strict two-function import whitelist.
The Amadeus blockchain relies on WebAssembly (WASM) as its smart contract execution format, running inside a carefully hardened environment. Every contract undergoes rigorous static and dynamic checks before and during execution, ensuring that malicious or buggy code cannot compromise the network. This guide examines the security considerations for WASM contracts on Amadeus as implemented in the amadeusprotocol/node repository.
Core Security Architecture
The Amadeus execution model follows a defense-in-depth strategy with three interconnected layers: static validation intercepts malformed modules before instantiation; runtime metering bounds computational cost; and import isolation prevents sandbox escape.
The Wasmer Sandbox Foundation
Amadeus builds its execution environment on the Wasmer WebAssembly runtime. This provides memory-safe execution, but the node adds substantial hardening through custom middleware and validation logic concentrated in ex/native/rdb/src/consensus/bic/wasm.rs.
Static Module Validation
Every WASM binary passes through check_module_limits (lines 648–709 in wasm.rs) before execution. This function implements nine distinct security checks that reject malformed or malicious modules with explicit error strings.
Binary Size and Complexity Limits
| Limit | Constant | Purpose | Location |
|---|---|---|---|
| Maximum binary size | protocol::WASM_MAX_BINARY_SIZE |
Prevents DoS from oversized uploads | Lines 648–652 |
| Function count | Internal limit | Stops parser exhaustion attacks | Lines 658–665 |
| Global count | Internal limit | Limits module complexity | Lines 665–670 |
| Export count | Internal limit | Restricts exposed interface surface | Lines 670–676 |
| Import count | Internal limit | Enforces minimal dependency surface | Lines 676–682 |
| Code body count | Internal limit | Prevents excessive function bodies | Lines 682–686 |
Violating any of these triggers immediate rejection with identifiers like wasmparser_function_count_exceeds_limit.
Memory Layout Protections
The data section validator (lines 686–709) enforces four critical constraints:
- Offset must be
i32.const— No dynamic or complex offset calculations - Offset must be non-negative — Prevents wrapping to high memory addresses
- No overlap with reserved memory — Protects the node's internal region
- First 65 KB reserved — The VM environment occupies this region; contracts cannot write here
These rules block out-of-bounds writes that could corrupt host state or escape the sandbox.
Start Section Prohibition
Amadeus explicitly disallows WASM start sections (line 709). This prevents contracts from defining implicit entry points that execute before the intended call frame, ensuring all execution flows through the contract's declared public interface.
Runtime Security Mechanisms
Once validated, contracts execute under continuous monitoring with hard resource boundaries.
Gas Metering via Wasmer Middleware
Every WASM operation consumes gas through Wasmer's metering middleware. The configuration appears in wasm.rs (lines 22–24, 52–55):
// wasm.rs lines 22-24
use wasmer::Middleware;
use wasmer_compiler_singlepass::Singlepass;
use wasmer_middlewares::Metering;
// Cost per WASM operation
const COST_PER_OP_WASM: u64 = 1;
The metering state synchronizes with the transaction budget through budget_sync_in (lines 52–60). When gas exhausts, the VM aborts immediately—no partial state commits, no resource exhaustion.
Pointer and Return Value Limits
Two additional bounds protect against memory abuse:
WASM_MAX_PTR_LEN(~1 MiB): Enforced inimport_log_implementationandimport_return_implementation(lines 172–194). Prevents oversized copies that could overflow the gas meter or exhaust memory.WASM_MAX_PANIC_MSG_SIZE: Enforced inset_return_value(lines 45–48). Stops contracts from flooding logs with arbitrarily large panic messages.
Exceeding WASM_MAX_PTR_LEN yields the explicit error exec_ptr_term_too_long.
Artifact Cache Protections
Compiled modules are cached with a 4 GiB limit (ARTIFACT_CACHE_MAX_BYTES). The ArtifactCache implementation (lines 45–98) prevents cache-filling attacks that could exhaust node memory through repeated deployments of distinct contracts.
Host Import Isolation
The contract's only bridge to the outside world is a strict whitelist of two functions:
| Function | Purpose | Location |
|---|---|---|
log |
Emit structured log messages | import_log_implementation |
return |
Return execution results | import_return_implementation |
No filesystem access. No networking. No OS syscalls. The HostEnv struct (lines 39–44) and setup_wasm_instance configuration establish this minimal surface.
// Conceptual structure from wasm.rs analysis
struct HostEnv {
// Only log and return capabilities exposed
log_fn: fn(*const u8, usize),
return_fn: fn(*const u8, usize) -> !,
}
This isolation guarantees that contract code cannot escape the sandbox regardless of logic errors or intentional attacks.
Practical Secure Contract Development
The following patterns demonstrate compliance with Amadeus security constraints.
Minimal Compliant Contract
// contract_samples/rust/examples/counter.rs
use amadeus_sdk::wasm::{log, return_};
#[no_mangle]
pub extern "C" fn increment(state: *mut u64) {
// SAFELY read/write within contract linear memory
unsafe {
let value = state.read_unaligned();
state.write_unaligned(value.wrapping_add(1));
}
log(b"counter incremented");
// Empty payload return—respects ptr/len limits
return_(b"", 0);
}
Security characteristics:
- Uses only whitelisted host functions (
log,return_) - Operates exclusively on contract-owned linear memory
- No dynamic allocation, keeping binary size minimal
- No external pointers or host memory references
Safe Logging Pattern
use amadeus_sdk::wasm::log;
#[no_mangle]
pub extern "C" fn operation(state: *mut u8) {
// Fixed-size message guaranteed under WASM_MAX_PTR_LEN
let msg = b"operation completed";
log(msg); // ~19 bytes, well under ~1 MiB limit
}
Attempting to log a buffer exceeding WASM_MAX_PTR_LEN triggers runtime abortion with exec_ptr_term_too_long.
Deployment Validation Flow
# Compile to WASM target
cargo build --target wasm32-unknown-unknown --release
# Deploy via CLI—triggers full validation pipeline
amadeus-node contract deploy \
--binary target/wasm32-unknown-unknown/release/counter.wasm \
--init-function "increment" \
--init-args ""
The CLI invokes contract_validate (lib.rs line 819), which chains to validate_contract and check_module_limits. Any limit violation aborts deployment with a specific error code before the transaction reaches consensus.
Critical Source Files
| Path | Responsibility | Key Functions |
|---|---|---|
ex/native/rdb/src/consensus/bic/wasm.rs |
Core WASM security | check_module_limits, setup_wasm_instance, budget_sync_in, ArtifactCache |
ex/native/rdb/src/consensus/consensus_apply.rs |
Execution entry point | call_wasmvm |
ex/native/rdb/src/lib.rs |
High-level validation | contract_validate (line 819) |
contract_samples/rust/examples/counter.rs |
Reference implementation | Compliant minimal contract |
contract_samples/rust/examples/nft.rs |
Advanced patterns | Storage-safe complex contract |
Summary
- Static validation in
check_module_limitsrejects malformed modules before execution through nine structural checks - Gas metering via Wasmer middleware bounds computation with per-operation costs and budget synchronization
- Memory safety is enforced through data offset validation, reserved region protection, and pointer length limits
- Import isolation restricts contracts to exactly two host functions:
logandreturn - Cache limits prevent memory exhaustion through the 4 GiB
ArtifactCachebound - Deployment validation runs the full security pipeline via
contract_validatebefore any contract persists
Frequently Asked Questions
What happens if a WASM contract exceeds the binary size limit?
The check_module_limits function rejects the deployment immediately with error code wasmparser_binary_size_exceeds_limit. The transaction fails before the contract is stored or executed, protecting the network from denial-of-service through oversized uploads.
Can Amadeus WASM contracts perform network I/O or file system operations?
No. The import whitelist in setup_wasm_instance exposes only log and return. No networking, filesystem, or OS interfaces are available. This is enforced at the Wasmer runtime level—attempting to import forbidden functions causes instantiation failure.
How does Amadeus prevent infinite loops in WASM contracts?
Through gas metering. Every WASM operation consumes COST_PER_OP_WASM units from the transaction budget. The Metering middleware tracks consumption synchronously; exhaustion triggers immediate VM termination via budget_sync_in. This bounds execution regardless of control flow structure.
What error occurs when a contract tries to log an oversized message?
The import_log_implementation function checks buffer length against WASM_MAX_PTR_LEN (approximately 1 MiB). Exceeding this limit aborts execution with error exec_ptr_term_too_long, preventing log flooding attacks.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →