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),
}
功能说明
-
存储功能:
- 使用
ExampleData
存储项保存用户账户与u64数值的映射关系 - 提供了
set_data
和get_data
函数来操作存储
- 使用
-
UAPI接口实现:
- 实现了
UApiInterface
trait - 提供了
get_user_balance
和set_user_balance
方法 - 可以与Substrate框架的其他组件无缝集成
- 实现了
-
事件系统:
- 定义了
DataStored
和DataRetrieved
事件 - 在操作完成时发出相应事件
- 定义了
使用场景
这个示例展示了如何:
- 在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(())
}
}
完整示例功能说明
-
增强的存储功能:
- 使用
UserBalances
存储用户余额 - 使用
TotalBalance
跟踪系统总余额 - 提供了更完善的错误处理
- 使用
-
完整的UAPI接口实现:
- 实现所有必需的UAPI方法
- 添加了转账功能
- 提供更完善的余额管理
-
增强的事件系统:
- 定义了多种事件类型
- 在关键操作点发出事件
- 提供更详细的操作记录
-
错误处理:
- 定义了多种错误类型
- 在操作前进行验证
- 提供更安全的操作保障
实际应用建议
-
在生产环境中使用时,建议:
- 添加更多的安全性检查
- 实现更完善的权限控制
- 添加日志记录功能
- 考虑性能优化
-
对于更复杂的应用场景:
- 可以扩展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 # 客户端调用示例
关键点说明
-
运行时集成:必须在运行时中正确配置pallet-revive-uapi并实现其Config trait
-
端点定义:
- 每个端点函数应返回
UApiResult<T>
类型 - 使用
register_endpoint!
宏注册端点 - 端点名称将自动添加"revive_uapi_"前缀
- 每个端点函数应返回
-
RPC配置:
- 需要在节点服务中配置RPC扩展
- 确保JSON-RPC服务正确暴露pallet-revive-uapi的端点
-
客户端调用:
- 使用标准的JSON-RPC客户端调用端点
- 端点名称需与注册时一致
- 参数类型需与端点定义匹配
这个完整示例展示了从后端实现到前端调用的完整流程,开发者可以根据实际需求修改端点逻辑和数据结构。