dev_genesis_wasm.erl - Genesis WASM Compatibility Device
Overview
Purpose: Enable legacy AO processes to run on HyperBEAM infrastructure
Module: dev_genesis_wasm
Pattern: Delegated compute with automatic server lifecycle management
Integration: Execution device for legacy AO processes with WASM runtime
This device provides compatibility for existing AO processes from the legacy network by wrapping a genesis-wasm compute server. It automatically manages the server lifecycle, handles checkpoints, and patches outbox messages for seamless integration with HyperBEAM's process model.
Dependencies
- HyperBEAM:
hb_ao,hb_opts,hb_util,hb_cache,hb_features,hb_name - Devices:
dev_delegated_compute,dev_process,dev_patch - Arweave:
ar_wallet,ar_bundles - Records:
#tx{}frominclude/hb.hrl
Public Functions Overview
%% Lifecycle
-spec init(Msg, Msg2, Opts) -> {ok, Msg}.
-spec normalize(Msg, Msg2, Opts) -> {ok, Msg} | {error, ErrorMap}.
%% Computation
-spec compute(Msg, Msg2, Opts) -> {ok, MsgWithResults} | {error, ErrorMap}.
%% State Management
-spec snapshot(Msg, Msg2, Opts) -> {ok, SnapshotResponse}.
-spec import(Msg, Msg2, Opts) -> {ok, ImportedMsg}.
%% Utilities
-spec latest_checkpoint(ProcessID, Opts) -> {ok, CheckpointMsg} | {error, Reason}.Public Functions
1. init/3
-spec init(Msg, Msg2, Opts) -> {ok, Msg}
when
Msg :: map(),
Msg2 :: map(),
Opts :: map().Description: Initialize the genesis-wasm device. Returns message unchanged.
Test Code:-module(dev_genesis_wasm_init_test).
-include_lib("eunit/include/eunit.hrl").
init_test() ->
Msg = #{<<"device">> => <<"genesis-wasm@1.0">>},
{ok, Result} = dev_genesis_wasm:init(Msg, #{}, #{}),
?assertEqual(Msg, Result).2. normalize/3
-spec normalize(Msg, Msg2, Opts) -> {ok, Msg} | {error, ErrorMap}
when
Msg :: map(),
Msg2 :: map(),
Opts :: map(),
ErrorMap :: #{
<<"status">> => 500,
<<"message">> => binary()
}.Description: Normalize device by ensuring genesis-wasm server is running and delegating to dev_delegated_compute:normalize/3.
- Checks if genesis-wasm server is running
- Returns error if server not available
- Delegates to delegated_compute for actual normalization
-module(dev_genesis_wasm_normalize_test).
-include_lib("eunit/include/eunit.hrl").
normalize_server_running_test() ->
% Verify module exports normalize/3
code:ensure_loaded(dev_genesis_wasm),
?assert(erlang:function_exported(dev_genesis_wasm, normalize, 3)).
normalize_server_not_running_test() ->
Exports = dev_genesis_wasm:module_info(exports),
?assert(lists:member({normalize, 3}, Exports)),
?assert(lists:member({init, 3}, Exports)),
?assert(lists:member({compute, 3}, Exports)).3. compute/3
-spec compute(Msg, Msg2, Opts) -> {ok, MsgWithResults} | {error, ErrorMap}
when
Msg :: map(),
Msg2 :: map(),
Opts :: map(),
MsgWithResults :: map(),
ErrorMap :: map().Description: Execute computation using genesis-wasm server. Delegates to dev_delegated_compute and patches outbox messages.
- Validate server is running
- Delegate to
dev_delegated_compute:compute/3 - Apply
patch@1.0device to process outbox - Return patched results
- Patches from
/results/outbox - Integrates outbox messages into process state
- Maintains compatibility with legacy AO processes
-module(dev_genesis_wasm_compute_test).
-include_lib("eunit/include/eunit.hrl").
compute_success_test() ->
% Verify module exports compute/3
code:ensure_loaded(dev_genesis_wasm),
?assert(erlang:function_exported(dev_genesis_wasm, compute, 3)).
compute_server_not_running_test() ->
Exports = dev_genesis_wasm:module_info(exports),
?assert(lists:member({compute, 3}, Exports)).4. snapshot/3
-spec snapshot(Msg, Msg2, Opts) -> {ok, SnapshotResponse}
when
Msg :: map(),
Msg2 :: map(),
Opts :: map(),
SnapshotResponse :: map().Description: Create snapshot by delegating to dev_delegated_compute:snapshot/3.
-module(dev_genesis_wasm_snapshot_test).
-include_lib("eunit/include/eunit.hrl").
snapshot_test() ->
% Verify module exports snapshot/3
code:ensure_loaded(dev_genesis_wasm),
?assert(erlang:function_exported(dev_genesis_wasm, snapshot, 3)).5. import/3
-spec import(Msg, Msg2, Opts) -> {ok, ImportedMsg}
when
Msg :: map(),
Msg2 :: map(),
Opts :: map(),
ImportedMsg :: map().Description: Import latest checkpoint for a process. Fetches most recent checkpoint and loads it into the process state.
Import Flow:- Get latest checkpoint via
latest_checkpoint/2 - Set checkpoint in message state
- Normalize to load checkpoint
-module(dev_genesis_wasm_import_test).
-include_lib("eunit/include/eunit.hrl").
import_test() ->
% Verify module exports import/3
code:ensure_loaded(dev_genesis_wasm),
?assert(erlang:function_exported(dev_genesis_wasm, import, 3)).6. latest_checkpoint/2
-spec latest_checkpoint(ProcessID, Opts) -> {ok, CheckpointMsg} | {error, Reason}
when
ProcessID :: binary(),
Opts :: map(),
CheckpointMsg :: map(),
Reason :: term().Description: Fetch the latest checkpoint for a process from Arweave.
Search Strategy:- Query Arweave for checkpoint transactions
- Filter by process ID and checkpoint type
- Return most recent checkpoint
- Falls back to empty checkpoint if none found
Type = CheckpointProcess = ProcessID
-module(dev_genesis_wasm_checkpoint_test).
-include_lib("eunit/include/eunit.hrl").
latest_checkpoint_found_test() ->
% Verify module exports latest_checkpoint/2
code:ensure_loaded(dev_genesis_wasm),
?assert(erlang:function_exported(dev_genesis_wasm, latest_checkpoint, 2)).
latest_checkpoint_not_found_test() ->
Exports = dev_genesis_wasm:module_info(exports),
?assert(lists:member({latest_checkpoint, 2}, Exports)).Common Patterns
%% Use as process execution device
Process = #{
<<"device">> => <<"process@1.0">>,
<<"execution-device">> => <<"genesis-wasm@1.0">>,
<<"process-id">> => ProcessID,
<<"module">> => ModuleTXID,
<<"scheduler">> => SchedulerAddress
},
{ok, Result} = hb_ao:resolve(Process, Message, Opts).
%% Execute assignment
Msg = #{
<<"device">> => <<"genesis-wasm@1.0">>,
<<"process-id">> => ProcessID
},
Assignment = #{
<<"type">> => <<"Assignment">>,
<<"process-id">> => ProcessID,
<<"slot">> => Slot,
<<"action">> => <<"Execute">>
},
{ok, Result} = dev_genesis_wasm:compute(Msg, Assignment, Opts),
Outbox = maps:get(<<"/results/outbox">>, Result, []).
%% Import latest checkpoint
Msg = #{
<<"device">> => <<"genesis-wasm@1.0">>,
<<"process-id">> => ProcessID
},
{ok, ImportedMsg} = dev_genesis_wasm:import(Msg, #{}, Opts),
% Process now has latest checkpoint loaded
%% Create checkpoint
Msg = #{
<<"device">> => <<"genesis-wasm@1.0">>,
<<"process-id">> => ProcessID
},
{ok, Snapshot} = dev_genesis_wasm:snapshot(Msg, #{}, Opts),
case maps:get(<<"type">>, Snapshot, undefined) of
<<"Checkpoint">> ->
% Save to Arweave
CheckpointData = maps:get(<<"data">>, Snapshot),
save_checkpoint(ProcessID, CheckpointData);
_ ->
% Handle error
Error = maps:get(<<"error">>, Snapshot)
end.
%% Dry-run execution
DryRun = #{
<<"type">> => <<"Message">>,
<<"process-id">> => ProcessID,
<<"action">> => <<"Test">>
},
{ok, Result} = dev_genesis_wasm:compute(Msg, DryRun, Opts),
Results = maps:get(<<"/results">>, Result).Server Lifecycle Management
Automatic Startup
The device automatically starts the genesis-wasm server when:
- Server is not running
hb_features:genesis_wasm()returns true (compiled with genesis-wasm support)- Server files are available in expected locations
Server Paths
Release Mode:{cwd}/genesis-wasm-server/{cwd}/_build/genesis_wasm/genesis-wasm-server/Launch Script
genesis-wasm-server/launch-monitored.sh npm --prefix genesis-wasm-server run startEnvironment Configuration
Environment Variables
{
"UNIT_MODE" => "hbu",
"HB_URL" => "http://localhost:{port}",
"PORT" => "{genesis_wasm_port}",
"DB_URL" => "{database_url}",
"NODE_CONFIG_ENV" => "production",
"DEFAULT_LOG_LEVEL" => "{log_level}",
"WALLET_FILE" => "{wallet_file}",
"CHECKPOINT_CREATION_THROTTLE" => "{throttle}",
"DISABLE_CHECKPOINTING" => "{disable}",
"CHECKPOINT_DIR" => "{checkpoint_dir}"
}Configuration Options
| Option | Default | Description |
|---|---|---|
genesis_wasm_port | 6363 | Server listen port |
genesis_wasm_db_dir | cache-mainnet/genesis-wasm | Database directory |
genesis_wasm_checkpoints_dir | {db_dir}/checkpoints | Checkpoint storage |
genesis_wasm_log_level | debug | Server log level |
genesis_wasm_wallet_file | - | Wallet for checkpoints |
genesis_wasm_checkpoint_throttle | - | Throttle interval |
genesis_wasm_disable_checkpointing | - | Disable checkpoints |
Checkpoint Management
Checkpoint Structure
#{
<<"type">> => <<"Checkpoint">>,
<<"data">> => Binary,
<<"memory-limit">> => <<"1-gb">>,
<<"timestamp">> => Timestamp,
<<"block-height">> => Height,
...
}Checkpoint Loading
% Automatic on normalize
Msg = #{
<<"device">> => <<"genesis-wasm@1.0">>,
<<"snapshot">> => Checkpoint
},
{ok, Normalized} = dev_genesis_wasm:normalize(Msg, #{}, Opts).
% Checkpoint loaded to server, snapshot key removedCheckpoint Creation
% Via snapshot
{ok, Snapshot} = dev_genesis_wasm:snapshot(Msg, #{}, Opts).
% Automatic by server
% (configured via CHECKPOINT_CREATION_THROTTLE)Outbox Patching
Patch Process
% After computation
{ok, Msg3} = dev_delegated_compute:compute(Msg, Msg2, Opts),
% Patch outbox into process state
{ok, Msg4} = hb_ao:resolve(
Msg3,
{as, <<"patch@1.0">>, Msg2#{
<<"patch-from">> => <<"/results/outbox">>
}},
Opts
).Outbox Structure
#{
<<"/results/outbox">> => [
#{
<<"Target">> => TargetProcess,
<<"Data">> => MessageData,
<<"Tags">> => Tags,
...
}
]
}Legacy AO Compatibility
Supported Features
- WASM module execution
- Lua environment (AOS)
- Message handlers
- State persistence
- Checkpoint/restore
- Outbox messages
- Multi-message execution
Process Definition
#{
<<"module">> => ModuleTXID, % WASM module
<<"scheduler">> => SchedulerAddr, % Scheduler address
<<"tags">> => [
{<<"aos-version">>, Version},
{<<"memory-limit">>, <<"1-gb">>}
]
}Server Health Monitoring
Status Check
is_genesis_wasm_server_running(Opts) ->
case httpc:request(
get,
{"http://localhost:{port}/ping", []},
[{timeout, ?STATUS_TIMEOUT}],
[]
) of
{ok, {{_, 200, _}, _, _}} -> true;
_ -> false
end.Process Registration
% Server process registered as:
hb_name:lookup(<<"genesis-wasm@1.0">>)Error Handling
Server Not Running
{error, #{
<<"status">> => 500,
<<"message">> => <<"Genesis-wasm server not running.">>
}}Server Not Compiled
{error, #{
<<"status">> => 500,
<<"message">> =>
<<"HyperBEAM was not compiled with genesis-wasm@1.0 on this node.">>
}}Computation Errors
% Delegated to dev_delegated_compute
{error, ErrorMap}References
- Delegated Compute -
dev_delegated_compute.erl - Patch Device -
dev_patch.erl - Process Device -
dev_process.erl - AO Resolution -
hb_ao.erl - Features -
hb_features.erl - Name Registry -
hb_name.erl
Notes
- Legacy Compatibility: Enables existing AO processes to run on HyperBEAM
- Automatic Lifecycle: Manages server startup/shutdown automatically
- Checkpoint Support: Full checkpoint save/restore capability
- Outbox Patching: Automatically integrates outbox messages
- WASM Runtime: Runs WASM modules with Lua environment
- Server Process: Launches as external Node.js process
- Health Monitoring: Checks server status before operations
- Feature Flag: Requires genesis-wasm feature at compile time
- Database: Uses SQLite for state persistence
- Environment Config: Highly configurable via environment variables
- Port Management: Configurable server port (default 6363)
- Wallet Support: Optional wallet for checkpoint signing
- Throttling: Configurable checkpoint creation throttle
- Development Mode: Special paths for development builds
- Error Recovery: Graceful degradation when server unavailable
- AOS Support: Full support for AOS (AO Lua environment)
- Multi-Process: Can handle multiple processes simultaneously
- State Isolation: Each process maintains isolated state
- Message Queue: Handles message ordering and execution
- Dry-Run Support: Non-state-changing execution for testing