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(())
}

关键组件说明

  1. Identity: 代表调用者的身份,可以是匿名或认证身份
  2. Agent: 主要接口,用于与互联网计算机交互
  3. Principal: 表示 Canister 或用户的唯一标识符
  4. 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(())
}

注意事项

  1. 开发环境中需要调用fetch_root_key(),但在生产环境(主网)中不需要
  2. 查询方法(query)不会改变链上状态,更新方法(update)会
  3. 更新方法需要等待共识确认,使用call_and_wait()
  4. 对于高频查询,考虑使用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(())
}

这个完整示例演示了:

  1. 创建Agent实例并连接到主网
  2. 查询容器状态
  3. 调用查询方法获取当前计数器值
  4. 调用更新方法增加计数器值并获取新值

要运行此示例,您需要:

  1. 在Cargo.toml中添加ic-agent和tokio依赖
  2. 准备一个有效的PEM格式身份文件
  3. 替换示例中的容器ID为您实际要交互的容器ID
回到顶部