Skip to main content

Build

In the Quick Start, we briefly introduced how to create and compile CKB Rust projects, which is sufficient for most daily development scenarios. This article delves deeper into the technical details of building Rust scripts. If you don't need an in-depth understanding for now, feel free to skip this section.

Early Rust Support

The Rust compiler depends on LLVM. Although LLVM added RISC-V support relatively early, it wasn't until version 16.0 that most official support became available (allowing scripts to be compiled with the official release). As a result, Rust only achieved complete RISC-V support around 2023.

The first version of ckb-std was released around 2020. During that period, developers needed to compile using a custom GNU toolchain. Due to the significant build workload, precompiled packages were not provided for every platform; instead, Docker images were used for distribution. Project management was handled with capsule, and the build command looked like this:

capsule build

Examples of projects using Capsule:

This is just a brief introduction. For detailed usage, refer to Capsule's README.

note

Capsule has stopped developing. Its functionality has been split into two parts:

Compilation

Current version of Rust offer solid RISC-V support, and developers can now directly use the latest stable Rust releases.

note

There are the following benefits of using the latest compiler:

  • Better performance optimization
  • More rigorous safety checks, helping catch potential bugs
  • Security fixes for vulnerabilities present in older versions

At this point, compiling a Rust script is very straightforward:

cargo build --target=riscv64imac-unknown-none-elf --release

Note that using a debug build (cargo build without --release) can cause issues for scripts:

  • The script size may exceed the 4 MB memory limit of ckb-vm, leading to execution failures.
  • Some problems may only happen under Release.

Therefore, --release is strongly recommended.

The --target=riscv64imac-unknown-none-elf flag specifies:

  • i: Supports the base integer instruction set (RV32I or RV64I).
  • m: Supports multiplication and division instructions (M extension).
  • a: Supports atomic operations (A extension), typically used for multi-core concurrent programming.
  • c: Supports the compressed instruction set (C extension) to reduce code size and improve performance.
  • unknown: Indicates an unknown system environment, typically for bare-metal development.
  • none: No operating system (no OS).
  • elf: Outputs binaries in the ELF (Executable and Linkable Format) format, commonly used in Linux and bare-metal development.

Reproducible Build

For security reasons, we want the same source code to always produce byte-for-byte identical contract binaries. However, local builds are heavily influenced by the environment. To address this, we recommend building inside a Docker container using a specified image.

In early projects, the build process was often manually scripted into a Makefile (for example, see the make all-via-docker target in the ckb-auth project) or handled through Capsule.

In early projects, the build process was often manually scripted into a Makefile (for example, see the make all-via-docker target in the ckb-auth project) or handled through Capsule.