Navigation Menu

Skip to content

Chia-Mine/clvm_tools-js

Repository files navigation

clvm_tools

Javascript implementation of clvm_tools (clvm = Chia Lisp VM)

Warning - future of version 0.x.x

In version 0.x.x, I may introduce several breaking changes in the future.
So please consider clvm_tools-js < v1.0.0 as a trial version and Proof of Concept.

I'm currently planning a breaking change, which is filesystem abstraction on browser.
So if you intend to use clvm_tools-js in NodeJS (not browser), it is possible that version 0.x.x and 1.0.0 will have no difference. (I cannot assure you though)

At current version, you can create virtual files into browser's localStorage as described in Using localStorage as a pseudo file system
But this is a poor implementation compared to actual filesystem on OS as it lacks concept of 'directory'. (You can still mimic a directory by setting sharing part of path as a key of localStorage)

Maybe You want to set --include/-i option to load chialisp libraries to run the same program as that of chia-blockchain.
But it is so painful to create and manage library files with such a poor pseudo filesystem with localStorage. That's why I decided take time to bring more well-designed filesystem implementation.

Install

npm install clvm_tools
# or
yarn add clvm_tools

Test

clvm_tools-js passes all the test equivalent to Python's original clvm_tools.
You can compare test files for Javascript and Python
To run the test, execute the following command.

git clone https://github.com/Chia-Mine/clvm_tools-js
cd clvm_tools-js

npm install
npm run test
# or
yarn
yarn test

If you find something not compatible with Python's clvm_tools, please report it to GitHub issues.

Note: Currently wasm build of clvm_rs is not fully compatible with Python's build of clvm_rs. See details here.

Compatibility

This code is compatible with:

Examples

Command line

If you have npx installed, you even do not need to have clvm_tools installed.

$ npx clvm_tools run "(mod ARGUMENT (+ ARGUMENT 3))"
(+ 1 (q . 3))
$ npx clvm_tools brun "(+ 1 (q . 3))" "2"
5

NodeJS

const clvm_tools = require("clvm_tools");

// You can skip async-initialization below until `op_pubkey_for_exp` or `op_point_add` is called
// or dispatch run/brun command with "--experiment-backend rust" option
await clvm_tools.initialize(); 

clvm_tools.go("run", "(mod ARGUMENT (+ ARGUMENT 3))");
// (+ 1 (q . 3))
clvm_tools.go("brun", "(+ 1 (q . 3))", "2");
// 5

// use clvm_rs for backend
clvm_tools.go("brun", "(+ 1 (q . 3))", "2", "--time", "--experiment-backend", "rust");
// assemble_from_ir: 0.00034061598777768154
// to_sexp_f: 0.0005161969661706678
// run_program: 0.0003017840385446391
// 5

Integrate clvm_tools into web application

If you develop web application with clvm_tools which runs on browser, you need to import module from clvm_tools/browser.

import * as clvm_tools from "clvm_tools/browser";

clvm_tools/browser switches read/write IO operation to browser's localStorage from OS's file system.
So if you do clvm_tools.go("brun", "/path/to/clvm/file", "2"), it searches localStorage with the key "/path/to/clvm/file".
In such a case, you need to pre-allocate clvm content into localStorage just like saving contents into a file.

Using localStorage as a pseudo file system

NOTE: As I described at the very beginning of this README, I will introduce breaking change to this pseudo file system at v1.0.0

window.localStorage is a simple key-value store.
It stores string key and string data, and it returns string data by associated key.
If you want to manually store data "(+ 1 (q . 3))" to the file path "/path/to/clvm/file" with localStorage,
you need to follow the steps described below.

// 1. Create a file object
var fileObj = {
  mTimeMs: Date.now(), // Modified time.
  encode: "string", // "string" or "hex". For now, there are no situations to use 'hex'.
  data: "(+ 1 (q . 3))", // If 'encode' is 'hex' this should be hex string and it will be decoded as binary data.
};
// 2. Stringify the object
var fileObjStr = JSON.stringify(fileObj);
// 3. Finally save it to the localStorage
localStorage.setItem("/path/to/clvm/file", fileObjStr);

// Then clvm_tools recognizes it as a file.
clvm_tools.go("brun", "/path/to/clvm/file", "2");
// output: 5

Redirect output/error from console.log

By default, command outputs are printed to browser's dev console.
So if you want to capture those output, you need to redirect console output to somewhere you want.

import * as clvm_tools from "clvm_tools/browser";

const stdoutEl = document.getElementById("...") as HTMLElement;
const printFn = (...messages: any[]) => {
  stdoutEl.textContent = (stdoutEl.textContent || "") + (messages.join(" ") + "\n");
};

clvm_tools.setPrintFunction(printFn);

WebAssembly files

Some parts of clvm_tools/clvm depend on WebAssembly.
For example:

  • op_point_add and op_pubkey_for_exp relies on wasm build of bls-signatures.
  • option --experiment-backend clvm_rs for run and brun commands relies on wasm build of clvm_rs

.wasm file installation

In order for those wasm files to be loaded correctly, you need to make sure that the wasm files are stored in the same folder as the main js file, which clvm_tools is bundled into.

├── ...
├── main.js          # js file which clvm_tools is compiled into.
├── clvm_rs_bg.wasm  # copy it from node_modules/clvm_tools/browser/clvm_rs_bg.wasm
└── blsjs.wasm       # copy it from node_modules/clvm_tools/browser/blsjs.wasm

Note1: If you use React, please copy blsjs.wasm and clvm_rs_bg.wasm in node_modules/clvm_tools/browser/ to <react-project-root>/public/static/js/.
React automatically copies wasm files next to the main js file on building. (if you use react-scripts, or you started project by create-react-app)
Note2: Redistributing your project with bundled blsjs.wasm and/or clvm_rs_bg.wasm must be compliant with Apache2.0 License provided by Chia-Network

.wasm file manual loading

The .wasm files are not loaded automatically. It requires programmer to fetch and load wasm files in the following way.

// Typescript
import * as clvm_tools from "clvm_tools/browser";
// ...

// This 'clvm_tools.initialize()' fetches and loads wasm files from the same path of the current js file location.
// 
// For example, if url of the js file currently running is 'https://example.com/aaa/bbb/main.js',
// it tries to fetch wasm files from 'https://example.com/aaa/bbb/blsjs.wasm'
// and 'https://example.com/aaa/bbb/clvm_rs_bg.wasm'
await clvm_tools.initialize();

// ...

Note that if you are really sure that you never use op_point_add and op_pubkey_for_exp and clvm_rs as a backend,
then you can skip the above async initialization. It never raises an exception until those wasm files are actually required.

Browser compatibility

clvm-tools-js uses BigInt. So if runtime environment does not support BigInt, clvm_tools-js doesn't work as well.
If you transpile code using babel or something which uses babel (like create-react-app), you need to tell the transpiler to optimize code only for the target browsers.
Just copy and paste below to your package.json and you can avoid a lot of runtime incompatibility issues.

"browserslist": [
  "edge >= 79",
  "firefox >= 68",
  "chrome >= 67",
  "safari > 14",
  "opera >= 54",
  "ios_saf >= 14.4",
  "android >= 67",
  "op_mob >= 48",
  "and_chr >= 67",
  "and_ff >= 68",
  "samsung >= 9.2",
  "node >= 10.4.0",
  "electron >= 4.0.0"
]

Example

clvm_tools license

clvm_tools-js is based on clvm_tools with the Apache license 2.0

clvm_rs license

Wasm build of clvm_rs is redistributed under the Apache license 2.0

bls-signatures license

Wasm build of bls-signatures is redistributed under the Apache license 2.0