Nervos CKB Docs
  • Basics
  • Reference
  • Labs
  • Integrate
  • Essays

›Essays

Basics

  • Basics Introduction
  • Concepts

    • Nervos Blockchain
    • Cell Model
    • Consensus
    • Economics
    • CKB-VM

    Guides

    • Run a CKB Dev Blockchain
    • Run a CKB Mainnet Node
    • Run a CKB Testnet Node
    • Neuron Wallet Guide
    • Run a CKB Mainnet Node and Testnet Node with Docker
    • Get CKB Binary
    • Get CKB Binary on Windows (experimental)
  • Tools
  • Glossary

Reference

  • Introduction
  • Cell
  • Script
  • Transaction
  • JSON-RPC

Labs

  • Introduction
  • Write a SUDT script by Capsule
  • Introduction to Lumos via NervosDAO
  • Dynamic loading in Capsule

Integrate

  • Nervos CKB Mainnet - Integration Guide
  • Q&A | For Wallets/Exchanges/Mining Pools
  • Nervos CKB SDK

Essays

  • Introduction
  • Developer Materials Guide
  • A Tour of RFCs
  • Transaction validation lifecycle
  • Tips for debugging CKB script
  • Tips for profiling CKB script
  • The General Workflow for Constructing a Transaction
  • Script dependencies
  • Introduction to CKB Studio
  • Technical Bits on Polyjuice
  • CKB FAQs
  • Tips for CKB development
  • Integrity Check for CKB Release
  • Mint SUDT via Contract

Script dependencies

Deploy a VM and run code on the VM

Since we are working with a virtualized mini computer in CKB VM, there’s nothing stopping us from embedding another VM as a CKB script that runs on CKB VM and in this article we will explore this VM on top of VM path.

Through this method, we can have JavaScript on CKB via duktape, Ruby on CKB via mruby, we can even have Bitcoin Script or EVM on chain if we just compile those VMs and store them as scripts on CKB. This compatibility ensures CKB VM can both help to preserve legacy code and build a diversified ecosystem.

All languages should are treated equal on CKB, giving freedom to blockchain contract developers to build on top of CKB however they feel is best.

To use duktape on CKB, first you need to compile duktape itself into a RISC-V executable binary:

$ git clone https://github.com/xxuejie/ckb-duktape
$ cd ckb-duktape
$ git submodule init
$ git submodule update
$ sudo docker run --rm -it -v `pwd`:/code nervos/ckb-riscv-gnu-toolchain:xenial bash
[email protected]0d31cad7a539:~# cd /code
[email protected]0d31cad7a539:/code# make
riscv64-unknown-elf-gcc -Os -DCKB_NO_MMU -D__riscv_soft_float -D__riscv_float_abi_soft -Iduktape -Ic -Wall -Werror c/entry.c -c -o build/entry.o
riscv64-unknown-elf-gcc -Os -DCKB_NO_MMU -D__riscv_soft_float -D__riscv_float_abi_soft -Iduktape -Ic -Wall -Werror duktape/duktape.c -c -o build/duktape.o
riscv64-unknown-elf-gcc build/entry.o build/duktape.o -o build/duktape -lm -Wl,-static -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,-s
[email protected]0d31cad7a539:/code# exit
exit
$ ls build/duktape
build/duktape*

Here we use the ruby SDK to interact with CKB, please refer to the official README for how to set it up. Then deploy the duktape script code in a CKB cell:

pry(main)> data = File.read("../ckb-duktape/build/duktape")
pry(main)> duktape_data.bytesize
=> 269064
pry(main)> duktape_tx_hash = wallet.send_capacity(wallet.address, CKB::Utils.byte_to_shannon(280000), CKB::Utils.bin_to_hex(duktape_data))
pry(main)> duktape_data_hash = CKB::Blake2b.hexdigest(duktape_data)
pry(main)> duktape_cell_dep = CKB::Types::CellDep.new(out_point: CKB::Types::OutPoint.new(tx_hash: duktape_tx_hash, index: 0))

The duktape script code now requires one argument: the JavaScript source you want to execute

pry(main)> duktape_hello_type_script = CKB::Types::Script.new(code_hash: duktape_data_hash, args: CKB::Utils.bin_to_hex("CKB.debug(\"I'm running in JS!\")"))

Notice that with a different argument, you can create a different duktape powered type script for a different use case:

pry(main)> duktape_hello_type_script = CKB::Types::Script.new(code_hash: duktape_data_hash, args: CKB::Utils.bin_to_hex("var a = 1;\nvar b = a + 2;"))

This echoes the differences mentioned above on script code vs script: here duktape serves as script code providing a JavaScript engine, while a different script leveraging duktape script code serves a different function on chain.

Now we can create a cell with the duktape type script attached:

pry(main)> tx = wallet.generate_tx(wallet2.address, CKB::Utils.byte_to_shannon(200))
pry(main)> tx.cell_deps.push(duktape_out_point.dup)
pry(main)> tx.outputs.type = duktape_hello_type_script.dup
pry(main)> tx.witnesses[0] = "0x"
pry(main)> tx = tx.sign(wallet.key, api.compute_transaction_hash(tx))
pry(main)> api.send_transaction(tx)
=> "0x2e4d3aab4284bc52fc6f07df66e7c8fc0e236916b8a8b8417abb2a2c60824028"

We can see that the script executes successfully and if you have the ckb-script module’s log level set to debug in your ckb.toml file, you will also notice the following log:

2019-07-15 05:59:13.551 +00:00 http.worker8 DEBUG ckb-script  script group: c35b9fed5fc0dd6eaef5a918cd7a4e4b77ea93398bece4d4572b67a474874641 DEBUG OUTPUT: I'm running in JS!

Now you have successfully deployed a JavaScript engine on CKB, and have also run JavaScript-based script on CKB! Feel free to try any JavaScript code you want here.

Dynamic linking

There are two dynamic linking functions implemented in nervosnetwork/ckb-c-stdlib, which are ckb_dlopen() and ckb_dlsym().

ckb_dlopen() loads the dynamic library from a cell by its data hash and returns an opaque "handle" for the dynamic library. ckb_dlsym() takes a "handle" of a dynamic library returned by ckb_dlopen() and the symbol name, and returns the address where that symbol is loaded into memory.

nervosnetwork/ckb-miscellaneous-scripts has a simple example for using these two functions.

int ckb_dlopen(const uint8_t *dep_cell_data_hash, uint8_t *aligned_addr,
               size_t aligned_size, void **handle, size_t *consumed_size);

void *ckb_dlsym(void *handle, const char *symbol);

How dependencies work

There are two different dependency fields in the transaction data structure: cell_deps and header_deps.

cell_deps allow scripts in the transaction to access (read-only) referenced live cells.

header_deps allow scripts in the transaction to access (read-only) data of referenced past block headers of the blockchain.

Please refer to the CKB Transaction Structure RFC for more details.

← The General Workflow for Constructing a TransactionIntroduction to CKB Studio →
  • Deploy a VM and run code on the VM
  • Dynamic linking
  • How dependencies work
Foundation
About Us
Developer
GitHubWhitepaperRFCs
TwitterBlogTelegramRedditYouTubeForum
Copyright © 2020 Nervos Foundation. All Rights Reserved.
Note we've completely rebuilt Nervos Doc site! For the old doc site, please see docs-old.