Skip to content

LEA-Blockchain/vm-shim

Repository files navigation

@leachain/vm-shim

npm version GitHub license

@leachain/vm-shim is a reusable VM shim for Lea-chain WebAssembly modules, providing the necessary host environment for running smart contracts compatibly in both Node.js and browser environments.

Features

  • Environment-Agnostic: Works seamlessly in both Node.js and modern web browsers.
  • Memory Helpers: Includes utility functions (copyToWasm, readFromWasm, malloc) for easier memory management.
  • Secure by Default: Provides a sandboxed environment with no filesystem or network access unless explicitly passed in.
  • Configurable: Allows overriding the default abort handler and extending the environment with custom functions.
  • Typed API: Includes TypeScript definitions for a better developer experience.

Installation

npm install @leachain/vm-shim

Usage

The primary export is createShim, which generates the importObject and exposes helper functions under utils for a WebAssembly instance.

ES Modules (ESM)

import { promises as fs } from 'fs';
import { createShim } from '@leachain/vm-shim';

async function runWasm() {
    // 1. Create the shim instance
    const {
        importObject,
        bindInstance,
        utils: { copyToWasm, readFromWasm }
    } = createShim();

    // 2. Read your Wasm module bytes
    const wasmBytes = await fs.readFile('./path/to/your_contract.wasm');

    // 3. Instantiate the module with the shim's import object
    const { instance } = await WebAssembly.instantiate(wasmBytes, importObject);

    // 4. IMPORTANT: Bind the created instance to the shim
    // This allows host functions and helpers to access Wasm memory and exports.
    bindInstance(instance);

    // 5. Use helpers to interact with Wasm memory
    const data = new TextEncoder().encode("Hello from JS");
    const ptr = copyToWasm(data); // Allocates and copies data

    // 6. Call an exported function from your Wasm module
    const resultPtr = instance.exports.process_data(ptr, data.length);
    
    // 7. Read the result back from Wasm memory
    const result = readFromWasm(resultPtr, 13); // Assuming result is 13 bytes
    console.log(`Wasm function returned: ${new TextDecoder().decode(result)}`);
}

runWasm().catch(console.error);

CommonJS (CJS)

const { promises: fs } = require('fs');
const { createShim } = require('@leachain/vm-shim');

async function runWasm() {
    // 1. Create the shim instance
    const {
        importObject,
        bindInstance,
        utils: { copyToWasm }
    } = createShim();

    // 2. Read your Wasm module bytes
    const wasmBytes = await fs.readFile('./path/to/your_contract.wasm');

    // 3. Instantiate the module
    const { instance } = await WebAssembly.instantiate(wasmBytes, importObject);

    // 4. Bind the instance
    bindInstance(instance);

    // 5. Use a helper and call a Wasm function
    const data = new TextEncoder().encode("Hello");
    const ptr = copyToWasm(data);
    instance.exports.your_function(ptr, data.length);
}

runWasm().catch(console.error);

API Reference

createShim(config?)

  • config <object> (Optional)
    • onAbort <(message: string) => void>: Overrides the default abort behavior. The default logs an error and exits (Node.js) or throws an error (browser).
    • customEnv <object>: An object with custom host functions to add to the env namespace. See Extending the Shim.
  • Returns <object>
    • importObject <object>: The WebAssembly import object. Pass this to WebAssembly.instantiate.
    • bindInstance <(instance: WebAssembly.Instance) => void>: Binds the Wasm instance to the shim. Call after instantiation.
    • print <object>: Colored logging utility with red, orange, green, blue.
    • utils <object>: Helper functions for memory and allocation:
      • copyToWasm(data: Uint8Array): number
      • readFromWasm(ptr: number, length: number): Uint8Array
      • malloc(length: number): number
      • reset(): void

cstring(memory, ptr)

A utility function to read a null-terminated UTF-8 string from the Wasm instance's memory.

  • memory <WebAssembly.Memory>: The exported memory from your Wasm instance (instance.exports.memory).
  • ptr <number>: The pointer (memory address) of the string.
  • Returns <string>: The decoded string.

Default Host Functions

The shim provides several host functions by default, available under the env namespace.

  • __lea_abort(line: number): Handles fatal errors.
  • __lea_log(ptr: number, len: number): Prints a message from Wasm to the host console.
  • __lea_ubsen(...): Handles errors from the Undefined Behavior Sanitizer (UBSan).
  • __lea_randombytes(ptr: number, len: number): Fills a buffer in Wasm memory with secure random bytes.
  • __execution_limit(...), __address_add(...), __execution_stack_add(...): Hooks for blockchain-specific operations.

Extending the Shim

You can add your own host functions to the env namespace by passing a customEnv object in the createShim configuration.

Example: Adding a Custom Function

import { createShim } from '@leachain/vm-shim';

const customEnv = {
  my_custom_function: (arg1, arg2) => {
    console.log(`my_custom_function called with: ${arg1}, ${arg2}`);
    return 42;
  }
};

const { importObject } = createShim({ customEnv });

// ... proceed with Wasm instantiation

Your WebAssembly module can then import and call this function (e.g., extern int my_custom_function(int arg1, int arg2); in C/C++).

Contributing

Contributions are welcome! Please open an issue or submit a pull request for any bugs, features, or improvements.

License

This project is licensed under the ISC License. See the LICENSE file for details.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published