Skip to content

Latest commit

 

History

History
135 lines (108 loc) · 5.46 KB

intro.md

File metadata and controls

135 lines (108 loc) · 5.46 KB

CKB-JS-VM Introduction

This project enables developers to write smart contracts in JavaScript on CKB. As we know, C and Rust can be used to write smart contracts on CKB. They can be compiled into RISC-V binary instructions as smart contracts. JavaScript can be run on virtual machines, depending on variable implementations. If these virtual machines are ported to CKB, JavaScript can also be run on CKB.

QuickJS is a famous JavaScript virtual machine implementation by Fabrice Bellard. This project aims to port it to CKB, enabling JavaScript capabilities in CKB programming.

Basic

The project is finally compiled into a single binary: ckb-js-vm which can be found in build folder after executing:

make

This smart contract can be executed on ckb-vm directly. Without any arguments, it reads code_hash/hash_type from args in Script. Then run the JavaScript code(JavaScript source file or bytecode) in a cell denoted by code_hash/hash_type. Below is the structure of the ckb-js-vm Script:

code_hash: <code hash of ckb-js-vm, 32 bytes>
hash_type: <hash type of ckb-js-vm, 1 byte>
args: <args, 2 bytes> <code hash of JavaScript code, 32 bytes> <hash type of JavaScript code, 1 byte>

Please note that the first 2 bytes in the args field are reserved for future use.

Command Line Options Explained

Smart contracts on ckb-vm can receive arguments, similar to other Linux executables. Depending on the provided arguments, ckb-js-vm behaves differently. There are four command-line options supported by ckb-js-vm:

  • -e
  • -f
  • -c
  • -r

Thanks to the power of exec or spawn, ckb-js-vm can easily run with arguments.

When -e is provided, it can accept a string and evaluate it. This behavior is identical to the -e option in the native qjs:

-e  --eval EXPR    evaluate EXPR

Below is an example about how to use it:

-e 'console.log("hello,world")'

As argument can be very long on ckb-vm(depending on stack size), a very long JavaScript code can be executed via this method.

When -f is provided, it treats JavaScript code as a file system, rather than a single JavaScript code file. JavaScript module is based on file system. See more Simple File System and JavaScript Module.

When -c is provided, See section below.

When -r is provided, it can read a local file via ckb-debugger, but this functionality is intended for testing purposes only. It does not function in a production environment. For additional examples, please refer to the tests folder.

Bytecode

When -c is provided, it can compile a JavaScript source file into JavaScript bytecode with output as hexadecimal. Below is a recipe about how to compile JavaScript source file:

ckb-debugger --read-file hello.js --bin build/ckb-js-vm -- -c | awk -f $(ROOT_DIR)/../../tools/compile.awk | xxd -r -p > hello.bc

It reads hello.js and then compiles the JavaScript source file into bytecode in hex formatting. Then, using the power of awk and xxd, it can be converted into binary. Finally, it is written as hello.bc.

ckb-js-vm can transparently run JavaScript bytecode or source files, which can also be in file systems.

Script

A ckb-js-vm script contains following data structure:

code_hash: <code_hash to ckb-js-vm cell>
hash_type: <hash_type>
args: <ckb-js-vm args, 2 bytes> <code_hash to JavaScript code cell, 32 bytes> <hash_type to JavaScript code cell, 1 byte> <JavaScript code args, variable length>

The tailing bytes are JavaScript code arguments which can be used by JavaScript. Note: 2 bytes ckb-js-vm args are reserved for further use.

Performance and Memory Usage

The JavaScript code implementation for a "hello, world" costs an approximate expense of 2.9 M cycles. The utilization of the SimpleUDT consumes around 5.1 M cycles(3.4 M for bytecode), while the ckb-lua-vm takes a cost of roughly 2.0 M(1.3 M for bytecode) cycles.

The memory usage of the SimpleUDT is about 139K(130K in heap and 9K in stack).

Integration

There are four approaches to integrating ckb-js-vm:

  • spawn
  • exec
  • Static library
  • Dynamic library

Among these options, spawn is the recommended method for integrating ckb-js-vm. It's straightforward and easy to use. exec is similar to spawn, but it lacks the ability to maintain the execution context and is no longer recommended following the availability of spawn.

Static linking is not practical due to the substantial binary size of ckb-js-vm (around 500K). The limitation on binary size is approximately 600K, leaving less than 100K for additional code.

For dynamic library integration, it also presents memory usage challenges, as it involves three components of memory usage:

  1. ckb-js-vm binary, approximately 500K.
  2. Heap memory utilized by malloc, which depends on the JavaScript code; typically, 500K is suggested.
  3. Stack memory, which varies but usually 100K is sufficient for most cases.

Overall, 1M bytes will be allocated for the dynamic library, leaving a total usable memory of 4M. This leaves only 3M bytes for the host script. It's important to note that in certain critical scenarios, the peak memory usage can exceed the mentioned limits. Dynamic libraries also face security concerns, as detailed in security.md.