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();
}
代码说明
- 首先我们创建了交易数据和两种脚本(锁定脚本和类型脚本)
- 然后为每种脚本创建了脚本组,指定了:
- 脚本类型 (Lock 或 Type)
- 输入/输出索引
- 最后使用 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),
}
}
最佳实践
- 脚本优化:保持脚本尽可能精简,减少执行周期(cycles)消耗
- 错误处理:提供清晰的错误信息,方便调试
- 测试:使用ckb-test工具对脚本进行充分测试
- 安全考虑:特别注意边界条件和异常输入的处理
调试技巧
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提供了强大的脚本编程能力,通过灵活运用可以构建各种复杂的区块链业务逻辑。