Rust IC Agent库的使用:与DFINITY互联网计算机区块链交互的官方工具库
Rust IC Agent库的使用:与DFINITY互联网计算机区块链交互的官方工具库
ic-agent
是一个简单易用的 Rust 库,用于与互联网计算机(Internet Computer)交互。它是 dfx
工具的后端实现。
安装
在项目目录中运行以下 Cargo 命令:
cargo add ic-agent
或者在 Cargo.toml 中添加:
ic-agent = "0.42.0"
使用示例
以下是一个完整的示例,展示如何使用 ic-agent 与互联网计算机区块链交互:
use ic_agent::{Agent, identity::BasicIdentity};
use candid::{Encode, Decode, CandidType, Principal};
use std::time::Duration;
// 定义 Canister 接口
#[derive(CandidType)]
struct GreetArg {
name: String,
}
#[derive(CandidType)]
struct GreetResponse {
greeting: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. 创建身份 (这里使用匿名身份)
let identity = BasicIdentity::anonymous();
// 2. 创建 Agent
let agent = Agent::builder()
.with_url("https://ic0.app") // 连接到主网
.with_identity(identity)
.build()?;
// 3. 等待 Agent 准备就绪
agent.fetch_root_key().await?;
// 4. 指定要交互的 Canister ID
let canister_id = Principal::from_text("rrkah-fqaaa-aaaaa-aaaaq-cai")?; // 示例 Canister ID
// 5. 调用查询方法
let response: GreetResponse = agent
.query(&canister_id, "greet") // 方法名
.with_arg(Encode!(&GreetArg { name: "World".to_string() })?) // 参数
.call()
.await?;
println!("Response: {}", response.greeting);
// 6. 调用更新方法
let response: GreetResponse = agent
.update(&canister_id, "greet") // 方法名
.with_arg(Encode!(&GreetArg { name: "World".to_string() })?) // 参数
.call_and_wait(Duration::from_secs(5)) // 等待结果
.await?;
println!("Response: {}", response.greeting);
Ok(())
}
关键组件说明
- Identity: 代表调用者的身份,可以是匿名或认证身份
- Agent: 主要接口,用于与互联网计算机交互
- Principal: 表示 Canister 或用户的唯一标识符
- Candid: 用于序列化和反序列化数据
许可证
Apache-2.0
1 回复
Rust IC Agent库的使用:与DFINITY互联网计算机区块链交互的官方工具库
介绍
IC Agent是DFINITY基金会官方提供的Rust库,用于与互联网计算机(Internet Computer)区块链进行交互。它提供了高级抽象接口,简化了与容器(canister)智能合约的通信过程。
该库主要功能包括:
- 与互联网计算机网络节点通信
- 调用和查询容器智能合约
- 管理身份认证和签名
- 处理Candid编码/解码
安装方法
在Cargo.toml中添加依赖:
[dependencies]
ic-agent = "0.24"
基本使用方法
1. 创建Agent实例
use ic_agent::{Agent, identity::BasicIdentity};
async fn create_agent() -> Result<Agent, Box<dyn std::error::Error>> {
// 创建身份(这里使用匿名身份)
let identity = BasicIdentity::from_pem(b"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----")?;
// 创建Agent并连接到主网
let agent = Agent::builder()
.with_url("https://ic0.app") // 主网入口
.with_identity(identity)
.build()?;
// 获取根密钥(首次连接时需要)
agent.fetch_root_key().await?;
Ok(agent)
}
2. 查询容器状态
async fn query_canister(agent: &Agent, canister_id: &str) -> Result<(), Box<dyn std::error::Error>> {
let canister_id = canister_id.parse()?;
// 查询容器状态
let status = agent.status(&canister_id).await?;
println!("Canister status: {:?}", status);
Ok(())
}
3. 调用容器方法
use candid::{Encode, Decode, CandidType, Deserialize};
#[derive(CandidType, Deserialize)]
struct GreetArgs {
name: String,
}
async fn call_canister_method(
agent: &Agent,
canister_id: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let canister_id = canister_id.parse()?;
// 准备参数
let args = GreetArgs {
name: "Alice".to_string(),
};
let args_bytes = Encode!(&args)?;
// 调用查询方法(不修改状态)
let response = agent
.query(&canister_id, "greet_query")
.with_arg(args_bytes)
.call()
.await?;
let result: String = Decode!(&response, String)?;
println!("Query result: {}", result);
// 调用更新方法(会修改状态)
let response = agent
.update(&canister_id, "greet_update")
.with_arg(args_bytes)
.call_and_wait()
.await?;
let result: String = Decode!(&response, String)?;
println!("Update result: {}", result);
Ok(())
}
高级功能
1. 使用PEM文件身份
use ic_agent::identity::PemIdentity;
async fn create_agent_with_pem() -> Result<Agent, Box<dyn std::error::Error>> {
let identity = PemIdentity::from_pem_file("identity.pem")?;
let agent = Agent::builder()
.with_url("https://ic0.app")
.with_identity(identity)
.build()?;
agent.fetch_root_key().await?;
Ok(agent)
}
2. 批量调用
use ic_agent::agent::UpdateBuilder;
async fn batch_updates(agent: &Agent, canister_id: &str) -> Result<(), Box<dyn std::error::Error>> {
let canister_id = canister_id.parse()?;
let builder = UpdateBuilder::new(&agent, canister_id, "batch_operation")
.with_arg(Encode!(&"operation1")?)
.with_effective_canister_id(canister_id);
// 可以添加多个调用
let call1 = builder.clone().call_and_wait();
let call2 = builder.call_and_wait();
// 并行执行
let (res1, res2) = tokio::join!(call1, call2);
println!("Result 1: {:?}", res1);
println!("Result 2: {:?}", res2);
Ok(())
}
注意事项
- 开发环境中需要调用
fetch_root_key()
,但在生产环境(主网)中不需要 - 查询方法(
query
)不会改变链上状态,更新方法(update
)会 - 更新方法需要等待共识确认,使用
call_and_wait()
- 对于高频查询,考虑使用Agent的缓存功能
示例项目结构
典型的IC项目结构:
my_ic_project/
├── Cargo.toml
├── src/
│ ├── main.rs # 主程序
│ └── canister.rs # 容器交互模块
├── dfx.json # DFINITY项目配置
└── declarations/ # 自动生成的容器接口
完整示例Demo
下面是一个完整的示例,展示如何使用IC Agent库与互联网计算机交互:
use ic_agent::{Agent, identity::BasicIdentity};
use candid::{Encode, Decode, CandidType, Deserialize};
use std::error::Error;
#[derive(CandidType, Deserialize)]
struct CounterArgs {
increment_by: u64,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
// 1. 创建Agent实例
println!("Creating agent...");
let identity = BasicIdentity::from_pem(b"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----")?;
let agent = Agent::builder()
.with_url("https://ic0.app")
.with_identity(identity)
.build()?;
agent.fetch_root_key().await?;
// 2. 查询容器状态
let canister_id = "rrkah-fqaaa-aaaaa-aaaaq-cai"; // 示例容器ID
println!("Querying canister status...");
let status = agent.status(&canister_id.parse()?).await?;
println!("Canister status: {:?}", status);
// 3. 调用查询方法
println!("Calling query method...");
let query_response = agent
.query(&canister_id.parse()?, "get_counter")
.call()
.await?;
let counter_value: u64 = Decode!(&query_response, u64)?;
println!("Current counter value: {}", counter_value);
// 4. 调用更新方法
println!("Calling update method...");
let args = CounterArgs { increment_by: 5 };
let args_bytes = Encode!(&args)?;
let update_response = agent
.update(&canister_id.parse()?, "increment_counter")
.with_arg(args_bytes)
.call_and_wait()
.await?;
let new_counter: u64 = Decode!(&update_response, u64)?;
println!("New counter value: {}", new_counter);
Ok(())
}
这个完整示例演示了:
- 创建Agent实例并连接到主网
- 查询容器状态
- 调用查询方法获取当前计数器值
- 调用更新方法增加计数器值并获取新值
要运行此示例,您需要:
- 在Cargo.toml中添加ic-agent和tokio依赖
- 准备一个有效的PEM格式身份文件
- 替换示例中的容器ID为您实际要交互的容器ID