Skip to content

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{} from include/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.

Server Validation:
  • Checks if genesis-wasm server is running
  • Returns error if server not available
  • Delegates to delegated_compute for actual normalization
Test Code:
-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.

Execution Flow:
  1. Validate server is running
  2. Delegate to dev_delegated_compute:compute/3
  3. Apply patch@1.0 device to process outbox
  4. Return patched results
Outbox Patching:
  • Patches from /results/outbox
  • Integrates outbox messages into process state
  • Maintains compatibility with legacy AO processes
Test Code:
-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.

Test Code:
-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:
  1. Get latest checkpoint via latest_checkpoint/2
  2. Set checkpoint in message state
  3. Normalize to load checkpoint
Test Code:
-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:
  1. Query Arweave for checkpoint transactions
  2. Filter by process ID and checkpoint type
  3. Return most recent checkpoint
  4. Falls back to empty checkpoint if none found
Query Tags:
  • Type = Checkpoint
  • Process = ProcessID
Test Code:
-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:

  1. Server is not running
  2. hb_features:genesis_wasm() returns true (compiled with genesis-wasm support)
  3. Server files are available in expected locations

Server Paths

Release Mode:
{cwd}/genesis-wasm-server/
Development Mode:
{cwd}/_build/genesis_wasm/genesis-wasm-server/

Launch Script

genesis-wasm-server/launch-monitored.sh npm --prefix genesis-wasm-server run start

Environment 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

OptionDefaultDescription
genesis_wasm_port6363Server listen port
genesis_wasm_db_dircache-mainnet/genesis-wasmDatabase directory
genesis_wasm_checkpoints_dir{db_dir}/checkpointsCheckpoint storage
genesis_wasm_log_leveldebugServer 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 removed

Checkpoint 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

  1. Legacy Compatibility: Enables existing AO processes to run on HyperBEAM
  2. Automatic Lifecycle: Manages server startup/shutdown automatically
  3. Checkpoint Support: Full checkpoint save/restore capability
  4. Outbox Patching: Automatically integrates outbox messages
  5. WASM Runtime: Runs WASM modules with Lua environment
  6. Server Process: Launches as external Node.js process
  7. Health Monitoring: Checks server status before operations
  8. Feature Flag: Requires genesis-wasm feature at compile time
  9. Database: Uses SQLite for state persistence
  10. Environment Config: Highly configurable via environment variables
  11. Port Management: Configurable server port (default 6363)
  12. Wallet Support: Optional wallet for checkpoint signing
  13. Throttling: Configurable checkpoint creation throttle
  14. Development Mode: Special paths for development builds
  15. Error Recovery: Graceful degradation when server unavailable
  16. AOS Support: Full support for AOS (AO Lua environment)
  17. Multi-Process: Can handle multiple processes simultaneously
  18. State Isolation: Each process maintains isolated state
  19. Message Queue: Handles message ordering and execution
  20. Dry-Run Support: Non-state-changing execution for testing