Skip to content

Latest commit

 

History

History
171 lines (131 loc) · 5.48 KB

File metadata and controls

171 lines (131 loc) · 5.48 KB

AGENTS.md - Guidance for AI Coding Agents

This document provides guidelines for AI agents working on the ckb-testtool codebase.

Project Overview

ckb-testtool is a Rust library for testing CKB (Nervos Common Knowledge Base) smart contracts. It provides a Context struct for deploying contracts, building transactions, and verifying them using CKB-VM. Originally migrated from capsule.

Rust Edition: 2024
Rust Toolchain: 1.85.0 (specified in rust-toolchain)

Build & Test Commands

Command Description
cargo build Build the library
cargo build --features="native-simulator" Build with native-simulator
cargo fmt --all -- --check Check formatting
cargo clippy --all Run clippy linting
cargo test --all Run all tests
cargo test test_name Run specific test by name
cargo test test_name -- --nocapture Run test with output
cargo test --features="native-simulator" --all Run with native-simulator
cargo test --test normal_cases Run tests in specific file
cd tests/test-contracts && make build Build test contracts
rustup target add riscv64imac-unknown-none-elf Add RISC-V target

Project Structure

ckb-testtool/
├── Cargo.toml              # Main crate manifest
├── rust-toolchain          # Rust version: 1.85.0
├── rustfmt.toml            # Formatting configuration
├── src/
│   ├── lib.rs              # Crate entry with re-exports and docs
│   ├── context.rs          # Main Context struct for testing
│   ├── builtin.rs          # Built-in contracts (ALWAYS_SUCCESS)
│   └── tx_verifier.rs      # Transaction verification (private)
└── tests/
    ├── normal_cases.rs     # Standard test cases
    ├── native_simulator_cases.rs  # Native simulator tests
    └── test-contracts/     # Test contracts workspace

Feature Flags

  • native-simulator: Enables native simulation testing with libloading and serde_json

Code Style Guidelines

Formatting (rustfmt.toml)

max_width = 100
tab_spaces = 4
reorder_imports = true
reorder_modules = true
use_try_shorthand = true

Always run cargo fmt before committing.

Import Organization

Order imports as follows:

  1. Crate-level imports (crate::)
  2. External crate imports
  3. Standard library imports (std::)

Use nested imports for multiple items from the same module:

use crate::tx_verifier::OutputsDataVerifier;
use ckb_types::{
    bytes::Bytes,
    core::{Capacity, Cycle, TransactionView},
    packed::{Byte32, CellDep, CellOutput, OutPoint, Script},
    prelude::*,
};
use std::collections::HashMap;

Naming Conventions

Element Convention Example
Functions snake_case deploy_cell, verify_tx
Types PascalCase Context, Message
Variables snake_case out_point, data_hash
Constants SCREAMING_SNAKE_CASE MAX_CYCLES, SIGNATURE_SIZE
Modules snake_case tx_verifier, context

Type Annotations

  • Always specify return types for public functions
  • Use explicit types when not obvious from context
  • Use lifetime annotations when required
pub fn random_hash() -> Byte32 { ... }
pub fn verify_tx(&self, tx: &TransactionView, max_cycles: u64) -> Result<Cycle, CKBError> { ... }

Error Handling

  • Use Result<T, E> for fallible operations
  • Use Option<T> for optional returns
  • Prefer ? operator for error propagation
  • Use .expect("descriptive message") for programmer errors
  • Use .unwrap_or_else() for complex error handling
pub fn verify_tx(&self, tx: &TransactionView, max_cycles: u64) -> Result<Cycle, CKBError> {
    self.verify_tx_consensus(tx)?;
    // ...
}

let path = self.get_contract_path(filename).expect("get contract path");

Documentation

  • Use //! for module-level documentation
  • Use /// for public API documentation
  • Include # Example sections with rust,no_run code blocks

Test Structure

  • Use #[test] attribute for test functions
  • Define constants like MAX_CYCLES at the top
  • Follow the pattern: setup context -> deploy contracts -> build tx -> verify
const MAX_CYCLES: u64 = 500_0000;

#[test]
fn test_example() {
    let mut context = Context::default();
    let out_point = context.deploy_cell(contract_bin);
    // ... build transaction ...
    context.verify_tx(&tx, MAX_CYCLES).expect("pass verification");
}

Numeric Literals

Use underscores for readability: 500_0000, 500_000_000_000

Key APIs

  • Context::default() - Create new context
  • context.deploy_cell(data) - Deploy contract, returns OutPoint
  • context.deploy_cell_by_name(name) - Deploy contract from search path
  • context.build_script(&out_point, args) - Build script from deployed contract
  • context.create_cell(output, data) - Create input cell
  • context.complete_tx(tx) - Add cell deps automatically
  • context.verify_tx(&tx, max_cycles) - Verify transaction

CI Pipeline

The CI runs on every push and PR:

  1. Format check: cargo fmt --all -- --check
  2. Clippy: cargo clippy --all
  3. Build: cargo build
  4. Tests: cargo test --all
  5. Native simulator tests: cargo test --features="native-simulator" --all

Ensure all checks pass before submitting PRs.