Rust区块链开发库pallet-revive-uapi的使用,实现Substrate框架高效UAPI接口集成与扩展

Rust区块链开发库pallet-revive-uapi的使用,实现Substrate框架高效UAPI接口集成与扩展

安装

在项目目录中运行以下Cargo命令:

cargo add pallet-revive-uapi

或者在Cargo.toml中添加以下行:

pallet-revive-uapi = "0.6.0"

示例代码

以下是一个使用pallet-revive-uapi在Substrate框架中实现高效UAPI接口集成与扩展的完整示例:

// 引入必要的库和模块
use frame_support::{decl_module, decl_storage, dispatch::DispatchResult};
use frame_system::ensure_signed;
use pallet_revive_uapi::traits::UApiInterface;

// 定义存储结构
pub trait Trait: frame_system::Trait {
    type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
}

// 声明存储项
decl_storage! {
    trait Store for Module<T: Trait> as ReviveUapiExample {
        // 存储示例数据
        pub ExampleData get(fn example_data): map hasher(blake2_128_concat) T::AccountId => u64;
    }
}

// 声明模块
decl_module! {
    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
        // 定义事件
        type Event = Event<T>;

        // 示例函数 - 设置数据
        #[weight = 10_000]
        pub fn set_data(origin, value: u64) -> DispatchResult {
            let sender = ensure_signed(origin)?;
            
            // 存储数据
            <ExampleData<T>>::insert(&sender, value);
            
            // 发出事件
            Self::deposit_event(Event::DataStored(sender, value));
            
            Ok(())
        }

        // 示例函数 - 获取数据
        #[weight = 10_000]
        pub fn get_data(origin) -> DispatchResult {
            let sender = ensure_signed(origin)?;
            
            // 从存储中获取数据
            let value = <ExampleData<T>>::get(&sender);
            
            // 发出事件
            Self::deposit_event(Event::DataRetrieved(sender, value));
            
            Ok(())
        }
    }
}

// 实现UApiInterface trait
impl<T: Trait> UApiInterface for Module<T> {
    type AccountId = T::AccountId;
    type Balance = u64;

    // 实现接口方法
    fn get_user_balance(user: &Self::AccountId) -> Self::Balance {
        <ExampleData<T>>::get(user)
    }

    fn set_user_balance(user: &Self::AccountId, balance: Self::Balance) {
        <ExampleData<T>>::insert(user, balance);
    }
}

// 定义事件
pub enum Event<T: Trait> {
    DataStored(T::AccountId, u64),
    DataRetrieved(T::AccountId, u64),
}

功能说明

  1. 存储功能

    • 使用ExampleData存储项保存用户账户与u64数值的映射关系
    • 提供了set_dataget_data函数来操作存储
  2. UAPI接口实现

    • 实现了UApiInterface trait
    • 提供了get_user_balanceset_user_balance方法
    • 可以与Substrate框架的其他组件无缝集成
  3. 事件系统

    • 定义了DataStoredDataRetrieved事件
    • 在操作完成时发出相应事件

使用场景

这个示例展示了如何:

  • 在Substrate runtime模块中集成pallet-revive-uapi
  • 实现自定义存储结构
  • 提供外部可调用的函数
  • 实现标准UAPI接口
  • 处理区块链事件

通过这种模式,开发者可以快速构建符合Substrate标准的自定义功能,同时保持与框架其他组件的高度兼容性。

完整示例代码

以下是一个更完整的pallet-revive-uapi使用示例,展示了如何在实际项目中实现UAPI接口:

//! 演示如何使用pallet-revive-uapi实现完整的UAPI接口

#![cfg_attr(not(feature = "std"), no_std)]

use frame_support::{
    decl_error, decl_event, decl_module, decl_storage, dispatch::DispatchResult,
    traits::Get, weights::Weight,
};
use frame_system::{self as system, ensure_signed};
use pallet_revive_uapi::traits::UApiInterface;
use sp_std::prelude::*;

// 模块配置
pub trait Trait: system::Trait {
    /// 事件类型
    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
    /// UAPI接口的最大权重
    type MaxWeight: Get<Weight>;
}

// 存储定义
decl_storage! {
    trait Store for Module<T: Trait> as UApiDemo {
        /// 存储用户余额
        pub UserBalances get(fn user_balances): map hasher(blake2_128_concat) T::AccountId => u64;
        
        /// 系统总余额
        pub TotalBalance get(fn total_balance): u64;
    }
}

// 事件定义
decl_event!(
    pub enum Event<T>
    where
        AccountId = <T as system::Trait>::AccountId,
    {
        /// 用户余额已设置
        BalanceSet(AccountId, u64),
        /// 余额已转移
        BalanceTransferred(AccountId, AccountId, u64),
        /// 总余额已更新
        TotalBalanceUpdated(u64),
    }
);

// 错误定义
decl_error! {
    pub enum Error for Module<T: Trait> {
        /// 余额不足
        InsufficientBalance,
        /// 转账金额为零
        ZeroTransfer,
        /// 不允许自转
        SelfTransfer,
    }
}

// 模块实现
decl_module! {
    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
        type Error = Error<T>;
        type Event = Event<T>;

        /// 设置用户余额
        #[weight = T::MaxWeight::get()]
        pub fn set_balance(origin, target: T::AccountId, amount: u64) -> DispatchResult {
            let sender = ensure_signed(origin)?;
            
            // 更新存储
            <UserBalances<T>>::insert(&target, amount);
            
            // 发出事件
            Self::deposit_event(Event::BalanceSet(target, amount));
            
            Ok(())
        }

        /// 转账
        #[weight = T::MaxWeight::get()]
        pub fn transfer(
            origin,
            dest: T::AccountId,
            amount: u64,
        ) -> DispatchResult {
            let sender = ensure_signed(origin)?;

            // 验证转账
            ensure!(amount > 0, Error::<T>::ZeroTransfer);
            ensure!(sender != dest, Error::<T>::SelfTransfer);
            
            let sender_balance = <UserBalances<T>>::get(&sender);
            ensure!(sender_balance >= amount, Error::<T>::InsufficientBalance);

            // 更新余额
            <UserBalances<T>>::insert(&sender, sender_balance - amount);
            <UserBalances<T>>::mutate(&dest, |balance| *balance += amount);
            
            // 更新总余额
            <TotalBalance<T>>::mutate(|total| *total += amount);
            
            // 发出事件
            Self::deposit_event(Event::BalanceTransferred(sender, dest, amount));
            Self::deposit_event(Event::TotalBalanceUpdated(<TotalBalance<T>>::get()));
            
            Ok(())
        }
    }
}

// UApiInterface实现
impl<T: Trait> UApiInterface for Module<T> {
    type AccountId = T::AccountId;
    type Balance = u64;

    fn get_user_balance(user: &Self::AccountId) -> Self::Balance {
        <UserBalances<T>>::get(user)
    }

    fn set_user_balance(user: &Self::AccountId, balance: Self::Balance) {
        <UserBalances<T>>::insert(user, balance);
    }

    fn transfer_balance(from: &Self::AccountId, to: &Self::AccountId, amount: Self::Balance) -> DispatchResult {
        ensure!(amount > 0, Error::<T>::ZeroTransfer);
        ensure!(from != to, Error::<T>::SelfTransfer);
        
        let from_balance = <UserBalances<T>>::get(from);
        ensure!(from_balance >= amount, Error::<T>::InsufficientBalance);

        <UserBalances<T>>::insert(from, from_balance - amount);
        <UserBalances<T>>::mutate(to, |balance| *balance += amount);
        
        Self::deposit_event(Event::BalanceTransferred(from.clone(), to.clone(), amount));
        Ok(())
    }
}

完整示例功能说明

  1. 增强的存储功能

    • 使用UserBalances存储用户余额
    • 使用TotalBalance跟踪系统总余额
    • 提供了更完善的错误处理
  2. 完整的UAPI接口实现

    • 实现所有必需的UAPI方法
    • 添加了转账功能
    • 提供更完善的余额管理
  3. 增强的事件系统

    • 定义了多种事件类型
    • 在关键操作点发出事件
    • 提供更详细的操作记录
  4. 错误处理

    • 定义了多种错误类型
    • 在操作前进行验证
    • 提供更安全的操作保障

实际应用建议

  1. 在生产环境中使用时,建议:

    • 添加更多的安全性检查
    • 实现更完善的权限控制
    • 添加日志记录功能
    • 考虑性能优化
  2. 对于更复杂的应用场景:

    • 可以扩展UAPI接口
    • 添加更多业务逻辑
    • 集成其他Substrate pallet

这个完整示例展示了如何在实际项目中使用pallet-revive-uapi构建功能完善的UAPI接口,同时保持与Substrate框架的良好兼容性。


1 回复

Rust区块链开发库pallet-revive-uapi使用指南

完整示例demo

以下是基于pallet-revive-uapi的完整示例,展示了如何创建自定义端点、注册端点并通过RPC调用:

// 1. 在runtime中集成 (runtime/src/lib.rs)
use frame_support::{construct_runtime, parameter_types};

parameter_types! {
    pub const BlockHashCount: u64 = 250;
}

impl pallet_revive_uapi::Config for Runtime {
    type Event = Event;
    type WeightInfo = ();
}

construct_runtime!(
    pub enum Runtime where
        Block = Block,
        NodeBlock = opaque::Block,
        UncheckedExtrinsic = UncheckedExtrinsic
    {
        System: frame_system::{Module, Call, Config, Storage, Event<T>},
        // ...其他pallet
        ReviveUAPI: pallet_revive_uapi::{Module, Call, Storage, Event<T>},
    }
);

// 2. 创建自定义pallet并定义端点 (pallets/my-pallet/src/lib.rs)
use frame_support::{decl_module, decl_storage};
use pallet_revive_uapi::{UApiEndpoint, UApiResult};

#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
pub struct ApiResponse {
    pub status: u32,
    pub data: Vec<u8>,
}

decl_storage! {
    trait Store for Module<T: Config> as MyPallet {
        // 存储定义
    }
}

decl_module! {
    pub struct Module<T: Config> for enum Call where origin: T::Origin {
        // 模块定义
    }
}

impl<T: Config> UApiEndpoint<T> for Module<T> {
    fn register_endpoints() {
        // 注册获取区块信息的端点
        pallet_revive_uapi::register_endpoint!(
            "get_block_info",
            |block_number: u32| Self::handle_get_block_info(block_number)
        );
        
        // 注册数据处理端点
        pallet_revive_uapi::register_endpoint!(
            "process_data",
            |input: Vec<u8>| Self::handle_process_data(input)
        );
    }
}

impl<T: Config> Module<T> {
    // 处理获取区块信息的请求
    fn handle_get_block_info(block_number: u32) -> UApiResult<ApiResponse> {
        // 这里实现实际的业务逻辑
        Ok(ApiResponse {
            status: 200,
            data: format!("Block {} information", block_number).into_bytes(),
        })
    }

    // 处理数据处理的请求
    fn handle_process_data(input: Vec<u8>) -> UApiResult<ApiResponse> {
        // 这里实现实际的数据处理逻辑
        let processed = input.into_iter().map(|b| b.wrapping_add(1)).collect();
        
        Ok(ApiResponse {
            status: 200,
            data: processed,
        })
    }
}

// 3. 配置RPC服务 (node/src/rpc.rs)
use jsonrpc_core::IoHandler;
use pallet_revive_uapi_rpc::{ReviveUApi, ReviveUApiApi};

pub fn create_rpc() -> IoHandler {
    let mut io = IoHandler::default();
    
    io.extend_with(
        ReviveUApiApi::to_delegate(ReviveUApi::new())
    );
    
    io
}

// 4. 客户端调用示例 (client/src/main.rs)
use pallet_revive_uapi_rpc::{ReviveUApiApi, ReviveUApiApiServer};
use jsonrpc_core_client::transports::http;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 连接本地节点
    let client = http::connect::<ReviveUApiApi>("http://localhost:9933").await?;
    
    // 调用get_block_info端点
    let block_info: ApiResponse = client
        .request("revive_uapi_get_block_info", rpc_params![123])
        .await?;
    println!("Block info: {:?}", block_info);
    
    // 调用process_data端点
    let data = b"test data".to_vec();
    let processed: ApiResponse = client
        .request("revive_uapi_process_data", rpc_params![data])
        .await?;
    println!("Processed data: {:?}", processed);
    
    Ok(())
}

项目完整结构

/my_blockchain
├── Cargo.toml
├── runtime
│   ├── Cargo.toml
│   └── src
│       ├── lib.rs        # 主运行时文件,集成pallet-revive-uapi
│       └── uapi.rs       # 自定义API端点定义
├── node
│   ├── Cargo.toml
│   └── src
│       ├── main.rs       # 节点入口
│       ├── service.rs    # 服务配置
│       └── rpc.rs        # RPC服务配置
├── pallets
│   └── my-pallet
│       ├── Cargo.toml
│       └── src
│           └── lib.rs    # 自定义pallet实现
└── client
    ├── Cargo.toml
    └── src
        └── main.rs       # 客户端调用示例

关键点说明

  1. 运行时集成:必须在运行时中正确配置pallet-revive-uapi并实现其Config trait

  2. 端点定义

    • 每个端点函数应返回UApiResult<T>类型
    • 使用register_endpoint!宏注册端点
    • 端点名称将自动添加"revive_uapi_"前缀
  3. RPC配置

    • 需要在节点服务中配置RPC扩展
    • 确保JSON-RPC服务正确暴露pallet-revive-uapi的端点
  4. 客户端调用

    • 使用标准的JSON-RPC客户端调用端点
    • 端点名称需与注册时一致
    • 参数类型需与端点定义匹配

这个完整示例展示了从后端实现到前端调用的完整流程,开发者可以根据实际需求修改端点逻辑和数据结构。

回到顶部