Rust智能合约脚本库ckb-script的使用,ckb-script为Nervos CKB区块链提供灵活的脚本编程与自定义验证功能

ckb-script

这是一个 CKB 的组件,用于运行类型/锁定脚本。

最低支持的 Rust 版本政策 (MSRV)

该 crate 的最低支持 rustc 版本是 1.85.0

安装

在项目目录中运行以下 Cargo 命令:

cargo add ckb-script

或者在你的 Cargo.toml 中添加以下行:

ckb-script = "0.202.0"

使用示例

下面是一个使用 ckb-script 创建简单智能合约的完整示例:

use ckb_script::{ScriptGroup, ScriptGroupType, ScriptVerifier};
use ckb_types::{bytes::Bytes, core::ScriptHashType, packed::Script, prelude::*};

// 创建一个简单的脚本验证器
fn main() {
    // 创建脚本
    let script = Script::new_builder()
        .code_hash([0u8; 32].pack()) // 替换为实际的代码哈希
        .hash_type(ScriptHashType::Type.into())
        .args(Bytes::from("some_args").pack())
        .build();

    // 创建脚本组
    let script_group = ScriptGroup {
        script,
        group_type: ScriptGroupType::Lock,
        input_indices: vec![0], // 输入索引
        output_indices: vec![], // 输出索引
    };

    // 创建脚本验证器
    let verifier = ScriptVerifier::new(script_group);

    // 验证脚本
    let result = verifier.verify(&Default::default());
    println!("验证结果: {:?}", result);
}

完整示例代码

下面是一个更完整的 ckb-script 使用示例,包含实际交易验证场景:

use ckb_script::{ScriptGroup, ScriptGroupType, ScriptVerifier};
use ckb_types::{
    bytes::Bytes,
    core::{ScriptHashType, TransactionView},
    packed::{Byte32, Script},
    prelude::*,
};

// 示例:验证包含多个输入输出的交易脚本
fn verify_transaction_scripts() {
    // 1. 准备交易数据 (这里简化处理)
    let transaction = TransactionView::new_advanced_builder().build();
    
    // 2. 创建锁定脚本
    let lock_script = Script::new_builder()
        .code_hash(
            // 使用真实的代码哈希
            Byte32::from_slice(&[1u8; 32]).unwrap()
        )
        .hash_type(ScriptHashType::Type.into())
        .args(Bytes::from("lock_args").pack())
        .build();

    // 3. 创建类型脚本
    let type_script = Script::new_builder()
        .code_hash(
            // 使用真实的代码哈希
            Byte32::from_slice(&[2u8; 32]).unwrap()
        )
        .hash_type(ScriptHashType::Data.into())
        .args(Bytes::from("type_args").pack())
        .build();

    // 4. 创建脚本组
    let lock_group = ScriptGroup {
        script: lock_script.clone(),
        group_type: ScriptGroupType::Lock,
        input_indices: vec![0, 1], // 两个输入使用相同的锁定脚本
        output_indices: vec![],
    };

    let type_group = ScriptGroup {
        script: type_script.clone(),
        group_type: ScriptGroupType::Type,
        input_indices: vec![],
        output_indices: vec![0], // 输出0使用该类型脚本
    };

    // 5. 创建验证器并验证
    let verifier = ScriptVerifier::new(lock_group);
    let result = verifier.verify(&transaction);
    println!("锁定脚本验证结果: {:?}", result);

    let verifier = ScriptVerifier::new(type_group);
    let result = verifier.verify(&transaction);
    println!("类型脚本验证结果: {:?}", result);
}

fn main() {
    verify_transaction_scripts();
}

代码说明

  1. 首先我们创建了交易数据和两种脚本(锁定脚本和类型脚本)
  2. 然后为每种脚本创建了脚本组,指定了:
    • 脚本类型 (Lock 或 Type)
    • 输入/输出索引
  3. 最后使用 ScriptVerifier 分别验证两种脚本

文档

更多详细用法请参考官方文档。


1 回复

Rust智能合约脚本库ckb-script使用指南

介绍

ckb-script是Nervos CKB(Common Knowledge Base)区块链上的一个Rust库,专门用于编写和执行智能合约脚本。它为开发者提供了在CKB上创建自定义验证逻辑的能力,是构建去中心化应用(DApp)和扩展CKB功能的核心工具。

CKB采用独特的Cell模型而非账户模型,ckb-script允许开发者定义如何验证Cell的消费和创建,从而实现各种复杂的业务逻辑。

主要特性

  • 提供在CKB上执行自定义脚本的基础设施
  • 支持多种编程语言编写的脚本(通过RISC-V VM)
  • 灵活的验证逻辑定义能力
  • 与CKB-VM深度集成
  • 高性能的脚本执行环境

安装方法

在Cargo.toml中添加依赖:

[dependencies]
ckb-script = "0.5.0"

基本使用方法

1. 编写简单脚本

use ckb_script::Script;
use ckb_types::{packed, prelude::*};

fn main() {
    // 创建一个简单的锁定脚本
    let args: Vec<u8> = vec![1, 2, 3, 4];
    let script = Script::new_builder()
        .code_hash(Default::default())
        .hash_type(Default::default())
        .args(args.pack())
        .build();
    
    println!("Script created: {:?}", script);
}

2. 脚本验证示例

use ckb_script::{ScriptError, ScriptGroup, ScriptVerifier};
use ckb_types::core::TransactionView;

fn verify_transaction(tx: TransactionView) ->

Result<(), ScriptError> {
    let script_group = ScriptGroup::from_transaction(&tx)?;
    let verifier = ScriptVerifier::new(tx);
    
    for group in script_group.groups() {
        verifier.verify(&group)?;
    }
    
    Ok(())
}

3. 自定义脚本实现

use ckb_script::{ScriptFn, ScriptResult};
use ckb_types::{bytes::Bytes, core::ScriptHashType};

#[derive(Debug, Clone)]
pub struct MyCustomScript;

impl ScriptFn for MyCustomScript {
    fn verify(&self, data: &[u8], args: &[u8]) -> ScriptResult {
        // 自定义验证逻辑
        if args.len() < 4 {
            return Err("Args too short".into());
        }
        
        if data.len() > 1024 {
            return Err("Data too large".into());
        }
        
        Ok(())
    }
    
    fn script_hash_type(&self) -> ScriptHashType {
        ScriptHashType::Data
    }
}

高级用法

1. 与CKB-VM集成

use ckb_script::{ScriptConfig, ScriptVersion};
use ckb_vm::machine::asm::AsmCoreMachine;

let config = ScriptConfig::new_builder()
    .version(ScriptVersion::V1)
    .build();

let machine = AsmCoreMachine::new(
    config.max_cycles(),
    config.vm_version(),
    config.isa(),
);

2. 处理脚本组

use ckb_script::{ScriptGroup, ScriptGroupType};

let tx = get_transaction(); // 获取交易
let script_groups = ScriptGroup::from_transaction(&tx)?;

for group in script_groups.groups() {
    match group.group_type() {
        ScriptGroupType::Lock => println!("Lock script group"),
        ScriptGroupType::Type => println!("Type script group"),
    }
    
    println!("Input indices: {:?}", group.input_indices());
    println!("Output indices: {:?}", group.output_indices());
}

实际应用示例

实现一个多签脚本

use ckb_script::{ScriptFn, ScriptResult};
use ckb_types::{bytes::Bytes, core::ScriptHashType};
use secp256k1::{PublicKey, Signature, Message};

pub struct MultiSigScript {
    pub public_keys: Vec<PublicKey>,
    pub threshold: usize,
}

impl ScriptFn for MultiSigScript {
    fn verify(&self, witness: &[u8], args: &[u8]) -> ScriptResult {
        let signatures: Vec<Signature> = deserialize_signatures(witness)?;
        
        if signatures.len() < self.threshold {
            return Err("Not enough signatures".into());
        }
        
        let message = Message::from_slice(args).map_err(|_| "Invalid message")?;
        
        let mut valid_sigs = 0;
        for sig in signatures {
            for pubkey in &self.public_keys {
                if sig.verify(&message, pubkey).is_ok() {
                    valid_sigs += 1;
                    break;
                }
            }
        }
        
        if valid_sigs >= self.threshold {
            Ok(())
        } else {
            Err("Not enough valid signatures".into())
        }
    }
    
    fn script_hash_type(&self) -> ScriptHashType {
        ScriptHashType::Type
    }
}

完整示例Demo

下面是一个完整的ckb-script使用示例,展示了如何创建一个简单的资产转移合约:

use ckb_script::{Script, ScriptFn, ScriptResult, ScriptVerifier};
use ckb_types::{
    bytes::Bytes,
    core::{ScriptHashType, TransactionView},
    packed,
    prelude::*,
};

// 自定义资产转移脚本
#[derive(Debug, Clone)]
pub struct AssetTransferScript {
    pub min_balance: u64,
}

impl ScriptFn for AssetTransferScript {
    fn verify(&self, data: &[u8], args: &[u8]) -> ScriptResult {
        // 验证输入参数长度
        if args.len() != 32 {
            return Err("Invalid args length, expected 32-byte asset ID".into());
        }
        
        // 解析数据中的余额
        if data.len() != 8 {
            return Err("Invalid data length, expected 8-byte balance".into());
        }
        
        let balance = u64::from_le_bytes(data.try_into().unwrap());
        if balance < self.min_balance {
            return Err(format!(
                "Balance {} is below minimum required {}", 
                balance, 
                self.min_balance
            ).into());
        }
        
        Ok(())
    }
    
    fn script_hash_type(&self) -> ScriptHashType {
        ScriptHashType::Data
    }
}

fn main() {
    // 1. 创建脚本
    let script_args = vec![1; 32]; // 32字节的资产ID
    let script = Script::new_builder()
        .code_hash([0u8; 32].pack()) // 实际使用中替换为真实的code hash
        .hash_type(ScriptHashType::Data.into())
        .args(script_args.pack())
        .build();
    
    println!("Created script: {:?}", script);
    
    // 2. 准备模拟交易数据
    let tx = TransactionView::new_advanced_builder()
        .build();
    
    // 3. 创建脚本验证器
    let verifier = ScriptVerifier::new(tx.clone());
    
    // 4. 创建脚本组
    if let Ok(script_group) = ScriptGroup::from_transaction(&tx) {
        for group in script_group.groups() {
            println!("Processing script group: {:?}", group);
            
            // 5. 验证脚本
            if let Err(e) = verifier.verify(&group) {
                println!("Script verification failed: {}", e);
            } else {
                println!("Script verified successfully!");
            }
        }
    }
    
    // 6. 使用自定义脚本
    let asset_script = AssetTransferScript { min_balance: 100 };
    let test_data = 150u64.to_le_bytes();
    let test_args = vec![1; 32];
    
    match asset_script.verify(&test_data, &test_args) {
        Ok(_) => println!("Asset transfer validation passed"),
        Err(e) => println!("Validation failed: {}", e),
    }
}

最佳实践

  1. 脚本优化:保持脚本尽可能精简,减少执行周期(cycles)消耗
  2. 错误处理:提供清晰的错误信息,方便调试
  3. 测试:使用ckb-test工具对脚本进行充分测试
  4. 安全考虑:特别注意边界条件和异常输入的处理

调试技巧

use ckb_script::syscalls::Debugger;

struct MyDebugger;

impl Debugger for MyDebugger {
    fn debug(&self, message: &str) {
        println!("[SCRIPT DEBUG] {}", message);
    }
}

let debugger = MyDebugger;
let verifier = ScriptVerifier::new(tx).with_debugger(&debugger);

ckb-script为Nervos CKB提供了强大的脚本编程能力,通过灵活运用可以构建各种复杂的区块链业务逻辑。

回到顶部