CKB JS VM: Mechanism and Capabilities
The Quick Start guide mentions that JS contracts run inside the ckb-js-vm. This document provides a detailed explanation of ckb-js-vm.
Introduction
CKB contracts are based on the RISC-V architecture, which means most programming languages can be used to develop contracts for CKB. Currently, Rust and C are the most widely used languages for contract development. However, both have relatively high barriers to entry, which discourages many developers from getting started.
We hope to expand support to more languages in order to lower the entry threshold for developers.
Most beginner-friendly languages, such as Python and JavaScript, are interpreted languages. For CKB-VM, this means they must be interpreted and executed inside the virtual machine, which adds an additional layer of complexity.
JavaScript has long been one of the most popular programming languages. In the past, we attempted to run JavaScript on CKB using Duktape, a lightweight JavaScript engine. However, due to poor performance, this effort was not continued.
Later, we developed ckb-lua-vm. From a technical perspective, Lua is well-suited for contract development and offers acceptable performance. Unfortunately, Lua's limited popularity prevented it from gaining widespread adoption.
Building on these past experiences, we created ckb-js-vm, which is based on QuickJS, and built a full set of supporting tools, including:
- JavaScript bindings with TypeScript libraries
- A JavaScript testing framework:
ckb-testtool
(QuickJS is a small and embeddable JavaScript engine. It was created by Fabrice Bellard, the author of QEMU and the original developer of FFmpeg.)
ckb-js-vm args Explanation
The ckb-js-vm script structure in molecule is below:
code_hash: <code hash of ckb-js-vm, 32 bytes>
hash_type: <hash type of ckb-js-vm, 1 byte>
args: <ckb-js-vm flags, 2 bytes> <code hash of resource cell, 32 bytes> <hash type of resource cell, 1 byte>
The first 2 bytes are parsed into an int16_t in C using little-endian format (referred to as ckb-js-vm flags). If
the lowest bit of these flags is set (v & 0x01 == 1), the file system is enabled. File system functionality will be
described in another section.
The subsequent code_hash and hash_type point to a resource cell which may contain:
- A file system
- JavaScript source code
- QuickJS bytecode
When the file system flag is enabled, the resource cell contains a file system that can also include JavaScript code.
For most scenarios, QuickJS bytecode is stored in the resource cell. When an on-chain script requires extra args,
they can be stored beginning at offset 35 (2 + 32 + 1). Compared to normal on-chain scripts in other languages,
ckb-js-vm requires these extra 35 bytes.
Comparison Between JS Contracts and Traditional Contracts
Since JavaScript contracts are executed inside the JS-VM, creating a transaction for them introduces a few differences compared to traditional contracts (such as those written in Rust or C).
The key differences are as follows:
-
Script
- Traditional contracts use their own
code_hash + hash_type. - JavaScript contracts instead reference the
code_hash + hash_typeof the ckb-js-vm.
- Traditional contracts use their own
-
Args
- Traditional contracts directly fill in the
argsfield with their parameters. - JavaScript contracts prepend JS-specific parameters at the beginning of
args(2 bytes flags + 32 bytes code hash + 1 byte hash type). This also means when reading arguments inside the contract, you need to offset by 35 bytes.
- Traditional contracts directly fill in the
-
Cell Dependencies
- Traditional contracts only require their own binary as a cell dependency.
- JavaScript contracts require both the ckb-js-vm binary and the JavaScript bytecode cell as dependencies.
-
Performance
- Traditional contracts (Rust/C) are compiled directly to RISC-V machine code, offering the best performance.
- JavaScript contracts are interpreted by ckb-js-vm, which consumes more cycles. Bytecode execution is faster than raw JavaScript but still slower than native contracts.
When to Use JavaScript Contracts
JavaScript contracts are suitable for:
- Prototyping: Quickly test ideas without learning Rust or C
- Educational purposes: Learn CKB script concepts with a familiar language
- Simple logic: Scripts with straightforward validation rules
- Development tooling: Build internal tools and utilities
For production use cases involving complex logic or high-value assets, we recommend using Rust for better performance and security.
While CKB-JS-VM is not yet deployed on mainnet, you can experiment with it on devnet and testnet. Check the current deployment status in the ecosystem-scripts documentation.