Rust NEAR协议虚拟机运行库near-vm-runner解析:高效智能合约执行与WASM运行时支持

near-vm-runner

一个运行编译为Wasm的智能合约的引擎。这是nearcore中"合约运行时"部分的主要crate。

运行智能合约包括以下功能:

  1. Wasm的仪表化(在prepare.rs中实现),用于gas计量和各种安全检查
  2. 将Wasm编译为特定的VM表示(在cache.rs中处理)
  3. 向Wasm代码暴露区块链特定功能(在imports.rs中定义对应的宿主函数)
  4. 实际的代码执行(由wasmer_runner.rs处理)

当前支持三种Wasm执行运行时:

  • Wasmer 0.x
  • Wasmer 2.0(默认选项)
  • Wasmtime

主要使用场景:

  • 区块链本身作为主要客户端
  • 合约SDK工具作为次要客户端
  • 为开发者提供额外API(如获取gas成本明细)

测试方法

运行单元测试:

$ cargo t -p near-vm-runner --features wasmer0_vm,wasmer2_vm,wasmtime_vm,near_vm

运行模糊测试:

$ cd runtime/near-vm-runner && RUSTC_BOOTSTRAP=1 cargo fuzz run runner

性能分析技术

使用tracing crate进行性能分析的基本模式:

fn compute_thing() {
    let _span = tracing::debug_span!(target: "vm", "compute_thing").entered();
    for i in 0..99 {
        do_work()
    }
}

配置tracing订阅者:

tracing_subscriber::fmt::Subscriber::builder()
    .with_max_level(tracing::level_filters::LevelFilter::DEBUG)
    .with_span_events(tracing_subscriber::fmt::format::FmtSpan::CLOSE)
    .init();

或者使用分层分析器:

tracing_span_tree::span_tree().enable();

完整示例代码

以下是使用near-vm-runner运行智能合约的完整示例:

use near_vm_runner::{VMConfig, VMContext, VMKind, run};
use near_vm_logic::{External, StorageGetMode, VMOutcome};

// 1. 创建VM配置
let config = VMConfig::default();

// 2. 创建VM上下文
let context = VMContext {
    current_account_id: "test.near".to_string(),  // 当前合约账户
    signer_account_id: "user.near".to_string(),  // 签名者账户
    signer_account_pk: vec![0; 32],  // 签名者公钥
    predecessor_account_id: "user.near".to_string(),  // 前驱账户
    input: vec![],  // 输入数据
    block_height: 1,  // 区块高度
    block_timestamp: 1_600_000_000,  // 区块时间戳
    epoch_height: 1,  // 周期高度
    account_balance: 100,  // 账户余额
    account_locked_balance: 0,  // 锁定余额
    storage_usage: 0,  // 存储使用量
    attached_deposit: 0,  // 附加存款
    prepaid_gas: 10_000_000_000_000,  // 预支付gas
    random_seed: vec![0; 32],  // 随机种子
    is_view: false,  // 是否只读
    output_data_receivers: vec![],  // 输出数据接收者
};

// 3. 实现外部接口
struct MockExternal;
impl External for MockExternal {
    fn storage_get(&self, key: &[u8], mode: StorageGetMode) -> Option<Vec<u8>> {
        None  // 简单实现,实际需要完整实现所有方法
    }
    // 这里需要实现其他External trait方法...
}

let external = MockExternal;

// 4. 加载Wasm合约代码
let wasm_code = include_bytes!("../res/test_contract.wasm");

// 5. 执行合约
let outcome = run(
    &config,  // VM配置
    &context,  // 执行上下文
    &wasm_code,  // 合约代码
    "method_name",  // 要调用的方法
    &[],  // 方法参数
    VMKind::Wasmer2,  // 使用的VM类型
    &external,  // 外部接口
    None,  // 可选的内存限制
    None,  // 可选的栈限制
).unwrap();

// 6. 处理执行结果
match outcome {
    VMOutcome::Aborted => println!("合约执行中止"),
    VMOutcome::Ok(data) => println!("合约执行成功: {:?}", data),
}

使用注意事项:

  1. 需要准备有效的Wasm合约文件
  2. 必须完整实现External trait的所有方法
  3. 需要根据实际情况调整gas和存储参数
  4. 可以选择不同的VM实现(Wasmer0/Wasmer2/Wasmtime)

1 回复

Rust NEAR协议虚拟机运行库near-vm-runner解析

概述

near-vm-runner是NEAR协议的核心组件之一,负责在NEAR区块链上高效执行智能合约。它提供了一个安全的WASM(WebAssembly)运行时环境,专门为区块链场景优化,支持NEAR协议的智能合约执行。

主要特性

  1. 高性能WASM执行:针对区块链环境优化的WASM运行时
  2. 安全沙箱:完全隔离的合约执行环境
  3. 确定性执行:确保合约在不同节点上执行结果一致
  4. 资源计量:精确计算和限制gas消耗
  5. NEAR协议集成:深度集成NEAR区块链核心功能

使用方法

基本使用

首先在Cargo.toml中添加依赖:

[dependencies]
near-vm-runner = "0.0.0"  # 使用最新版本

初始化VM运行器

use near_vm_runner::VMContext;
use near_vm_runner::run_vm;

let context = VMContext {
    current_account_id: "alice.near".to_string(),
    signer_account_id: "bob.near".to_string(),
    signer_account_pk: vec![0; 32],
    predecessor_account_id: "carol.near".to_string(),
    input: vec![],
    block_index: 0,
    block_timestamp: 0,
    account_balance: 100,
    account_locked_balance: 0,
    storage_usage: 0,
    attached_deposit: 0,
    prepaid_gas: 10u64.pow(18),
    random_seed: vec![0; 32],
    output_data_receivers: vec![],
    epoch_height: 0,
};

// 加载WASM合约代码
let wasm_code = std::fs::read("contract.wasm").unwrap();

// 执行合约
let outcome = run_vm(
    &context,
    &wasm_code,
    "method_name",
    &[],
    None,
    None,
).unwrap();

合约调用示例

use near_vm_runner::{VMContext, run_vm};
use borsh::{BorshSerialize, BorshDeserialize};

#[derive(BorshSerialize, BorshDeserialize)]
struct InputArgs {
    a: u64,
    b: u64,
}

let context = VMContext { /* 同上 */ };

let args = InputArgs { a: 5, b: 7 };
let input = args.try_to_vec().unwrap();

let outcome = run_vm(
    &context,
    &wasm_code,
    "add_numbers",
    &input,
    None,
    None,
).unwrap();

println!("Result: {:?}", outcome.return_data);

高级功能

配置VM运行器

use near_vm_runner::{Config, VMKind};

let config = Config {
    wasm_config: Default::default(),
    vm_kind: VMKind::NearVm,  // 或 Wasmer, Wasmtime等
    max_gas_burnt: 200_000_000_000_000,
    ext_costs: Default::default(),
};

let outcome = run_vm_with_config(
    &config,
    &context,
    &wasm_code,
    "method_name",
    &[],
    None,
    None,
).unwrap();

处理合约存储

use near_vm_runner::internal::MemoryLike;
use std::collections::HashMap;

struct MockMemory {
    storage: HashMap<Vec<u8>, Vec<u8>>,
}

impl MemoryLike for MockMemory {
    // 实现MemoryLike trait的方法
}

let mut memory = MockMemory {
    storage: HashMap::new(),
};

let outcome = run_vm(
    &context,
    &wasm_code,
    "write_storage",
    &[],
    Some(&mut memory),
    None,
).unwrap();

完整示例

下面是一个完整的near-vm-runner使用示例,展示了如何初始化、调用合约并处理结果:

use near_vm_runner::{VMContext, run_vm, Config, VMKind};
use borsh::{BorshSerialize, BorshDeserialize};
use std::fs;

// 合约输入参数结构体
#[derive(BorshSerialize, BorshDeserialize)]
struct ContractInput {
    x: i32,
    y: i32,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 初始化VM上下文
    let context = VMContext {
        current_account_id: "test.near".to_string(),
        signer_account_id: "user.near".to_string(),
        signer_account_pk: vec![0; 32],
        predecessor_account_id: "user.near".to_string(),
        input: vec![],
        block_index: 1,
        block_timestamp: 1_624_000_000_000_000,
        account_balance: 100,
        account_locked_balance: 0,
        storage_usage: 0,
        attached_deposit: 0,
        prepaid_gas: 10u64.pow(18),
        random_seed: vec![0; 32],
        output_data_receivers: vec![],
        epoch_height: 0,
    };

    // 加载WASM合约
    let wasm_code = fs::read("contract.wasm")?;

    // 准备调用参数
    let args = ContractInput { x: 10, y: 20 };
    let input = args.try_to_vec()?;

    // 配置VM运行器
    let config = Config {
        wasm_config: Default::default(),
        vm_kind: VMKind::NearVm,
        max_gas_burnt: 200_000_000_000_000,
        ext_costs: Default::default(),
    };

    // 执行合约方法
    let outcome = run_vm(
        &context,
        &wasm_code,
        "add",  // 合约方法名
        &input,
        None,
        None,
    )?;

    // 处理执行结果
    println!("执行结果: {:?}", outcome.return_data);
    println!("消耗的Gas: {}", outcome.gas_burnt);
    println!("执行日志: {:?}", outcome.logs);

    Ok(())
}

性能优化建议

  1. 使用VMKind::NearVm获取最佳性能
  2. 合理设置gas限制
  3. 避免在合约中执行复杂计算
  4. 使用borsh进行高效数据序列化

注意事项

  1. 确保WASM合约符合NEAR协议规范
  2. 合约执行是确定性的,不能包含随机性
  3. 所有外部调用必须通过NEAR提供的导入函数
  4. 注意gas消耗,避免合约执行失败

near-vm-runner是NEAR区块链智能合约执行的核心,通过合理使用可以构建高效、安全的去中心化应用。

回到顶部