探秘 Move Binding:开启 Rust 与 Sui Move 包无缝交互新时代

  • King
  • 发布于 3天前
  • 阅读 294

引言在区块链的世界里,智能合约的开发与交互是核心议题之一。Sui作为新兴的高性能区块链平台,其Move语言为智能合约开发带来了诸多优势。而MoveBinding这个Rust库,则为开发者们提供了一种强大的工具,实现了Rust与SuiMove包的无缝集成。官方仓

引言

在区块链的世界里,智能合约的开发与交互是核心议题之一。

Sui 作为新兴的高性能区块链平台,其 Move 语言为智能合约开发带来了诸多优势。

而 Move Binding 这个 Rust 库,则为开发者们提供了一种强大的工具,实现了 Rust 与 Sui Move 包的无缝集成。

官方仓库:https://github.com/MystenLabs/move-binding

本文将深入剖析 Move Binding,从其核心功能、实现原理到实际应用,为你带来一场深入浅出的技术之旅。

Move Binding 简介

Move Binding 是一个 Rust 库,其主要目标是让开发者能够轻松地与 Sui 区块链上的 Move 包进行交互。

它就像一座桥梁,连接了 Rust 编程语言和 Sui 上的 Move 智能合约。

通过 Move Binding,开发者可以直接从 Sui 区块链读取 Move 包,并自动生成对应的 Rust 结构体和函数入口点,大大简化了开发流程。

核心功能亮点

  1. 直接读取 Sui Move 包:Move Binding 可以从 Sui 区块链上直接获取 Move 包的字节码,并将其反序列化为可用的模块对象。这使得开发者无需手动处理复杂的字节码解析,节省了大量时间和精力。
  2. 生成 Rust 结构体:根据 Move 包中的结构体定义,Move Binding 能够自动生成对应的 Rust 结构体。这些结构体实现了必要的特征(如 MoveStruct),方便在 Rust 代码中进行序列化和反序列化操作。
  3. 提供函数入口点:对于 Move 合约中的函数,Move Binding 会生成相应的 Rust 函数入口点。开发者可以在 Rust 代码中直接调用这些函数,实现与 Move 合约的交互,就像调用本地函数一样简单。
  4. 无缝集成:通过上述功能,Move Binding 实现了 Move 智能合约和 Rust 应用程序之间的无缝集成,让开发者可以充分利用 Rust 的高性能和安全性来开发区块链应用。

深入剖析 Move Binding 的实现原理

项目结构与模块划分

Move Binding 项目主要包含两个子模块:move-typesmove-binding-derive

  • move-types:定义了一些基础类型和特征,用于支持 Move 和 Rust 之间的类型转换。例如,MoveType 特征用于定义 Move 类型和 Rust 类型之间的映射关系,MoveStruct 特征用于表示 Move 结构体在 Rust 中的实现。
    
    pub trait MoveType: Serialize {
    fn type_() -> TypeTag;
    }

pub trait MoveStruct: Serialize { fn struct_type() -> StructTag; }

- **`move-binding-derive`**:提供了一些过程宏,用于生成 Rust 代码。例如,`move_contract` 宏用于导入 Move 包并生成对应的 Rust 模块和函数,`move_struct_derive` 宏用于为 Move 结构体生成 `MoveStruct` 特征的实现。

### 读取 Move 包的过程
`MoveModuleProvider` 结构体负责从 Sui 区块链获取 Move 包。它通过发送 GraphQL 请求到 Sui 的 RPC 节点,获取包的字节码和类型起源信息。
```rust
fn get_package(&self, package_id: Address) -> Package {
    let client = reqwest::blocking::Client::new();
    let request = format!(
        r#"{{package(address: "{package_id}") {{moduleBcs, typeOrigins{{module, struct, definingId}}, version}}}}"#
    );
    let res = client
       .post(self.network.gql())
       .header(CONTENT_TYPE, "application/json")
       .json(&json!({
            "query": request,
            "variables": Value::Null
        }))
       .send()
       .ok()
       .expect("Error fetching package from Sui GQL.");

    // 解析响应数据
    let value = res.json::<Value>().unwrap();
    let module_bcs: String =
        serde_json::from_value(value["data"]["package"]["moduleBcs"].clone()).unwrap();
    let module_bytes = Base64::decode(&module_bcs).unwrap();
    let module_map: BTreeMap<String, Vec<u8>> = bcs::from_bytes(&module_bytes).unwrap();

    let module_map = module_map
       .iter()
       .map(|(name, bytes)| {
            let module = CompiledModule::deserialize_with_defaults(bytes).unwrap();
            let normalized = Module::new(&module);
            (name.clone(), normalized)
        })
       .collect();

    // 处理类型起源信息
    let type_origin_table: Vec<Value> =
        serde_json::from_value(value["data"]["package"]["typeOrigins"].clone()).unwrap();
    let type_origin_table = type_origin_table.iter().fold(
        HashMap::new(),
        |mut results: HashMap<String, HashMap<String, AccountAddress>>, v| {
            let module = v["module"].as_str().unwrap();
            let struct_ = v["struct"].as_str().unwrap();
            let defining_id = v["definingId"].as_str().unwrap();
            results.entry(module.to_string()).or_default().insert(
                struct_.to_string(),
                AccountAddress::from_str(defining_id).unwrap(),
            );
            results
        },
    );

    let version = serde_json::from_value(value["data"]["package"]["version"].clone()).unwrap();

    Package {
        module_map,
        type_origin_table,
        version,
    }
}

生成 Rust 代码的过程

move_contract 宏是生成 Rust 代码的核心。它接收 Move 包的相关信息(如别名、包地址、依赖等),然后根据这些信息生成对应的 Rust 模块和函数。

#[proc_macro]
pub fn move_contract(input: TokenStream) -> TokenStream {
    let MoveContractArgs {
        network,
        package_alias,
        package,
        deps,
    } = parse_macro_input!(input as MoveContractArgs);

    let package_id = if package.contains("@") || package.contains(".sui") {
        resolve_mvr_name(package, &network.gql()).expect("Cannot resolve mvr name")
    } else {
        Address::from_str(&package).expect("Error parsing package id.")
    };

    let module_provider = MoveModuleProvider::new(network);
    let package = module_provider.get_package(package_id);

    let module_tokens = package.module_map.iter().map(|(module_name, module)| {
        let module_ident = Ident::new(module_name, proc_macro2::Span::call_site());

        let type_origin_table = package
           .type_origin_table
           .get(module_name)
           .cloned()
           .unwrap_or_default();
        let mut struct_fun_tokens = create_structs(&module.structs, &type_origin_table);

        if !module.functions.is_empty() {
            let fun_impl = create_funs(&module.functions);
            struct_fun_tokens.extend(fun_impl);
        }

        if struct_fun_tokens.is_empty() {
            quote! {}
        } else {
            let addr_byte_ident = module.address.to_vec();
            quote! {
                pub mod #module_ident{
                    use super::*;
                    pub const PACKAGE_ID: Address = Address::new([#(#addr_byte_ident),*]);
                    pub const MODULE_NAME: &str = #module_name;
                    #(#struct_fun_tokens)*
                }
            }
        }
    });

    let package_ident = Ident::new(&package_alias, proc_macro2::Span::call_site());
    let version = package.version;
    let expanded = quote! {
        pub mod #package_ident{
            #(use #deps::*;)*
            use std::str::FromStr;
            use move_binding_derive::{Key, MoveStruct};
            use move_types::{MoveType, Address, Identifier, TypeTag, StructTag};
            use move_types::functions::{Arg, Ref, MutRef};
            pub const PACKAGE_VERSION:u64 = #version;
            #(#module_tokens)*
        }
    };
    expanded.into()
}

实际应用示例

导入 Move 包

下面的代码示例展示了如何使用 move_contract 宏导入 Sui 上的 Move 包,并读取其中的对象。

use std::str::FromStr;
use sui_client::Client;
use sui_sdk_types::{Address, ObjectData};

use crate::bridge::bridge::BridgeInner;
use crate::sui::dynamic_field::Field;
use move_binding_derive::move_contract;

move_contract! {alias = "sui", package = "0x2"}
move_contract! {alias = "bridge", package = "0xb", deps = [crate::sui]}

#[tokio::main]
async fn main() {
    let client = Client::new("https://sui-mainnet.mystenlabs.com/graphql").unwrap();
    let bridge_obj = client
       .object(
            Address::from_str(
                "0x00ba8458097a879607d609817a05599dc3e9e73ce942f97d4f1262605a8bf0fc".into(),
            )
               .unwrap(),
            None,
        )
       .await
       .unwrap()
       .unwrap();

    if let ObjectData::Struct(o) = bridge_obj.data() {
        let bridge: Field<u64, BridgeInner> = bcs::from_bytes(o.contents()).unwrap();
        println!("Deserialized Bridge object: {:?}", bridge);
    }
}

调用 Move 函数

以下代码示例展示了如何在 Rust 代码中调用 Move 合约中的函数。

use std::str::FromStr;
use sui_client::Client;
use sui_sdk_types::{Address, ObjectId};
use sui_transaction_builder::TransactionBuilder;
use sui_transaction_builder::unresolved::Input;
use move_binding_derive::move_contract;

move_contract! {alias = "sui", package = "0x2"}

#[tokio::main]
async fn main() {
    let client = Client::new("https://sui-mainnet.mystenlabs.com/graphql").unwrap();
    let owner = Address::from_str("0x2").unwrap();
    let gas = ObjectId::from_str("0x726b714a3c4c681d8a9b1ff1833ad368585579a273362e1cbd738c0c8f70dabd").unwrap();
    let gas = client.object(gas.into(), None).await.unwrap().unwrap();

    let mut builder = TransactionBuilder::new();
    builder.set_sender(owner);
    builder.add_gas_objects(vec![Input::owned(gas.object_id(), gas.version(), gas.digest())]);
    builder.set_gas_budget(10000000);
    builder.set_gas_price(1000);

    let mut new_bag = sui::bag::new(&mut builder);
    sui::bag::add(&mut builder, new_bag.borrow_mut(), "Test".into(), "Test_value".into());
    sui::bag::add(&mut builder, new_bag.borrow_mut(), "Test2".into(), "Test_value2".into());
    sui::transfer::public_transfer(&mut builder, new_bag, owner.into());

    let tx = builder.finish().unwrap();
    let result = client.dry_run_tx(&tx, None).await.unwrap();

    println!("{:?}", result);
}

总结与展望

Move Binding 为开发者提供了一种高效、便捷的方式来与 Sui 区块链上的 Move 包进行交互。

通过自动生成 Rust 代码,它大大降低了开发难度,提高了开发效率。

在未来,随着 Sui 生态的不断发展,Move Binding 有望在更多的区块链应用场景中发挥重要作用,如去中心化金融(DeFi)、非同质化代币(NFT)等。

同时,开发者也可以基于 Move Binding 进行二次开发,扩展其功能,满足更多个性化的需求。相信在不久的将来,Move Binding 将成为 Sui 生态中不可或缺的一部分。

希望本文能够帮助你深入理解 Move Binding 的核心原理和应用方法,让你在区块链开发的道路上更进一步。

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
King
King
0x56af...a0dd
擅长Rust/Solidity/FunC/Move开发