Skip to main content

Rust

Introduction

The CKB-SDK-Rust is a powerful and efficient toolkit designed for developers working with the Nervos CKB blockchain using the Rust programming language. The Rust SDK for Nervos CKB provides several essential features for developers:

These features allow for seamless interaction with CKB and facilitate the development of decentralized applications on the CKB network.

Requirements

ComponentsVersionDescription
Rust & Cargo≥ 1.71.1Rust programming language & package manager

To verify that you have Rust and Cargo installed, you can run this in your terminal:

rustc --version
cargo --version

Setup Project

  1. Create a new project: Open a terminal and run the following commands to create a folder for your Rust project:
cargo new --bin ckb-rust-example
cd ckb-rust-example
  1. Edit cargo.toml: Open the cargo.toml file and add the ckb-sdk-rust dependency:
cargo.toml
[dependencies]
ckb-sdk = "3.2.0"
  1. Build the project: Run the following command in the project root directory:
cargo build
  1. Run unit tests: Run the following command when you want to run unit tests:
make test

Check out Makefile for other common commands.

Setup Client

CKB-SDK-Rust provides a convenient client that enables you to easily interact with CKB nodes. You can connect to different networks (Testnet, Devnet, and Mainnet) by specifying the appropriate URL.

use ckb_sdk::rpc::CkbRpcClient;

let testnet_url = "https://testnet.ckb.dev"; // Testnet
let devnet_url = "http://127.0.0.1:8114"; // Devnet
let mainnet_url = "https://mainnet.ckb.dev/rpc"; // Mainnet

// Connect to Testnet
let mut ckb_client = CkbRpcClient::new(testnet_url);

Examples

Get Block Info

You can leverage the above client to call any RPC APIs provided by CKB in Rust. Here's a simple example to get block info using a block hash:

let block = ckb_client.get_block_by_number(0.into()).unwrap();
println!("block: {}", serde_json::to_string_pretty(&block).unwrap());

Build a Transaction

The following example demonstrates how to construct a transfer transaction on CKB. You can use it to transfer a specified amount of CKB from one address to another.

note

The address and key are for demo purposes only and should not be used in a production environment.

use ckb_sdk::{
constants::SIGHASH_TYPE_HASH,
rpc::CkbRpcClient,
traits::{
DefaultCellCollector, DefaultCellDepResolver, DefaultHeaderDepResolver,
DefaultTransactionDependencyProvider, SecpCkbRawKeySigner,
},
tx_builder::{transfer::CapacityTransferBuilder, CapacityBalancer, TxBuilder},
unlock::{ScriptUnlocker, SecpSighashUnlocker},
Address, HumanCapacity, ScriptId,
};
use ckb_types::{
bytes::Bytes,
core::BlockView,
h256,
packed::{CellOutput, Script, WitnessArgs},
prelude::*,
};
use std::{collections::HashMap, str::FromStr};

// Prepare the necessary data for a CKB transaction:
// * set the RPC endpoint for the testnet
// * define the sender's address and secret key
// * define the recipient's address
// * specify the capacity to transfer
let ckb_rpc = "https://testnet.ckb.dev:8114";
let sender = Address::from_str("ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqf7v2xsyj0p8szesqrwqapvvygpc8hzg9sku954v").unwrap();
let sender_key = secp256k1::SecretKey::from_slice(
h256!("0xef4dfe655b3df20838bdd16e20afc70dfc1b9c3e87c54c276820315a570e6555").as_bytes(),
)
.unwrap();
let receiver = Address::from_str("ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqvglkprurm00l7hrs3rfqmmzyy3ll7djdsujdm6z").unwrap();
let capacity = HumanCapacity::from_str("100.0").unwrap();

// Build ScriptUnlocker
let signer = SecpCkbRawKeySigner::new_with_secret_keys(vec![sender_key]);
let sighash_unlocker = SecpSighashUnlocker::from(Box::new(signer) as Box<_>);
let sighash_script_id = ScriptId::new_type(SIGHASH_TYPE_HASH.clone());
let mut unlockers = HashMap::default();
unlockers.insert(
sighash_script_id,
Box::new(sighash_unlocker) as Box<dyn ScriptUnlocker>,
);

// Build CapacityBalancer
let placeholder_witness = WitnessArgs::new_builder()
.lock(Some(Bytes::from(vec![0u8; 65])).pack())
.build();
let balancer = CapacityBalancer::new_simple(sender.payload().into(), placeholder_witness, 1000);

// Build:
// * CellDepResolver
// * HeaderDepResolver
// * CellCollector
// * TransactionDependencyProvider
let mut ckb_client = CkbRpcClient::new(ckb_rpc);
let cell_dep_resolver = {
let genesis_block = ckb_client.get_block_by_number(0.into()).unwrap().unwrap();
DefaultCellDepResolver::from_genesis(&BlockView::from(genesis_block)).unwrap()
};
let header_dep_resolver = DefaultHeaderDepResolver::new(ckb_rpc);
let mut cell_collector = DefaultCellCollector::new(ckb_rpc);
let tx_dep_provider = DefaultTransactionDependencyProvider::new(ckb_rpc, 10);

// Build the transaction
let output = CellOutput::new_builder()
.lock(Script::from(&receiver))
.capacity(capacity.0.pack())
.build();
let builder = CapacityTransferBuilder::new(vec![(output, Bytes::default())]);
let (_tx, _) = builder
.build_unlocked(
&mut cell_collector,
&cell_dep_resolver,
&header_dep_resolver,
&tx_dep_provider,
&balancer,
&unlockers,
)
.unwrap();

Generate a New Address

In CKB, a private key can be used to generate a public key, which is then hashed using the Blake2b hashing algorithm to produce a CKB address. The public key is derived from the private key using the secp256k1 elliptic curve cryptography algorithm. This process results in a unique CKB address that can be used to receive or send CKB tokens. It is important to keep the private key secure, as anyone with access to it can potentially access the associated CKB funds.

use ckb_sdk::types::{Address, AddressPayload, NetworkType};
use rand::Rng;

let mut rng = rand::thread_rng();
let privkey_bytes: [u8; 32] = rng.gen();
let secp_secret_key = secp256k1::SecretKey::from_slice(&privkey_bytes).unwrap();
let pubkey =
secp256k1::PublicKey::from_secret_key(&ckb_crypto::secp::SECP256K1, &secp_secret_key);
let payload = AddressPayload::from_pubkey(&pubkey);
let address = Address::new(NetworkType::Mainnet, payload, true);
println!("address: {}", address.to_string());

Parse Address

In the world of CKB, a Lock Script

can be represented as an address. It is possible to parse an address from an encoded string and then obtain its network and Script.

use ckb_sdk::types::Address;
use ckb_types::packed::Script;
use std::str::FromStr;

let addr_str = "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqgvf0k9sc40s3azmpfvhyuudhahpsj72tsr8cx3d";
let addr = Address::from_str(addr_str).unwrap();
let _network = addr.network();
let _script: Script = addr.payload().into();
note

Short address and full bech32 address are deprecated. For more details about addresses, check out CKB Address and RFC-0021.


Additional Resources