Rust区块链开发库pallet-staking-runtime-api的使用,Substrate质押和验证人功能的核心运行时API
Rust区块链开发库pallet-staking-runtime-api的使用,Substrate质押和验证人功能的核心运行时API
概述
pallet-staking-runtime-api是Substrate框架中用于质押和验证人功能的核心运行时API定义库。它提供了与区块链质押、验证人选举等核心功能交互的接口。
安装
在您的项目目录中运行以下Cargo命令:
cargo add pallet-staking-runtime-api
或者在Cargo.toml中添加以下行:
pallet-staking-runtime-api = "28.0.0"
基本使用示例
以下是一个基本的使用示例,展示如何使用pallet-staking-runtime-api与Substrate区块链的质押功能进行交互:
use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;
use pallet_staking_runtime_api::StakingApi;
use substrate_test_runtime_client::runtime::Block;
// 假设我们有一个实现了Client trait的结构体
fn get_nominators<Client>(client: &Client) -> Vec<AccountId>
where
Client: ProvideRuntimeApi<Block> + HeaderBackend<Block>,
Client::Api: StakingApi<Block>,
{
let best_hash = client.info().best_hash;
// 调用运行时API获取当前提名人列表
client.runtime_api().nominators(best_hash)
.expect("Failed to retrieve nominators list")
}
// 获取当前验证人信息的示例
fn get_validators<Client>(client: &Client) -> Vec<ValidatorInfo>
where
Client: ProvideRuntimeApi<Block> + HeaderBackend<Block>,
Client::Api: StakingApi<Block>,
{
let best_hash = client.info().best_hash;
// 调用运行时API获取验证人信息
client.runtime_api().validators(best_hash)
.expect("Failed to retrieve validators info")
}
完整示例
以下是更完整的示例,展示如何构建一个简单的质押监控工具:
use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;
use pallet_staking_runtime_api::StakingApi;
use substrate_test_runtime_client::runtime::{Block, AccountId};
use sp_runtime::traits::Block as BlockT;
// 质押监控工具结构体
pub struct StakingMonitor<Client> {
client: Client,
}
impl<Client> StakingMonitor<Client>
where
Client: ProvideRuntimeApi<Block> + HeaderBackend<Block>,
Client::Api: StakingApi<Block>,
{
pub fn new(client: Client) -> Self {
StakingMonitor { client }
}
// 获取当前活跃的验证人列表
pub fn active_validators(&self) -> Vec<AccountId> {
let best_hash = self.client.info().best_hash;
self.client.runtime_api().active_validators(best_hash)
.expect("Failed to retrieve active validators")
}
// 获取所有提名人列表
pub fn nominators(&self) -> Vec<AccountId> {
let best_hash = self.client.info().best_hash;
self.client.runtime_api().nominators(best_hash)
.expect("Failed to retrieve nominators")
}
// 获取验证人的佣金率
pub fn validator_commission(&self, validator: AccountId) -> Option<u32> {
let best_hash = self.client.info().best_hash;
self.client.runtime_api().validator_commission(best_hash, validator)
.expect("Failed to retrieve validator commission")
}
// 获取验证人的质押总额
pub fn validator_stake(&self, validator: AccountId) -> Option<u128> {
let best_hash = self.client.info().best_hash;
self.client.runtime_api().validator_stake(best_hash, validator)
.expect("Failed to retrieve validator stake")
}
}
// 示例使用
fn main() {
// 假设我们已经创建了客户端实例
// let client = create_substrate_client();
// let monitor = StakingMonitor::new(client);
// println!("Active Validators: {:?}", monitor.active_validators());
// println!("Nominators: {:?}", monitor.nominators());
// 对于每个验证人,可以获取详细信息
// for validator in monitor.active_validators() {
// println!("Validator: {}", validator);
// println!("Commission: {:?}%", monitor.validator_commission(validator.clone()));
// println!("Total Stake: {:?}", monitor.validator_stake(validator.clone()));
// }
}
主要API功能
pallet-staking-runtime-api提供的主要功能包括:
- 获取活跃验证人列表
- 获取所有提名人列表
- 查询验证人佣金率
- 查询验证人质押总额
- 获取当前选举周期信息
- 查询验证人表现统计
高级用法
对于更高级的用例,您可能需要实现自定义的Runtime API调用:
use sp_api::decl_runtime_apis;
use sp_runtime::DispatchResult;
// 自定义Runtime API定义
decl_runtime_apis! {
pub trait CustomStakingApi {
/// 检查账户是否可以作为验证人
fn can_be_validator(account: AccountId) -> DispatchResult;
/// 获取验证人的历史表现
fn validator_performance(account: AccountId) -> Vec<(EraIndex, Performance)>;
}
}
// 在客户端中使用自定义API
fn check_validator_eligibility<Client>(client: &Client, account: AccountId) -> bool
where
Client: ProvideRuntimeApi<Block> + HeaderBackend<Block>,
Client::Api: CustomStakingApi<Block>,
{
let best_hash = client.info().best_hash;
client.runtime_api().can_be_validator(best_hash, account)
.expect("Failed to check validator eligibility")
.is_ok()
}
注意事项
- 使用这些API需要您的客户端实现相应的Runtime API trait
- 所有调用都是通过区块链状态进行的,不会发送任何交易
- 确保您的节点同步到了足够高的区块高度,以获取准确的信息
- 某些API可能需要特定的权限才能调用
1 回复
Rust区块链开发库pallet-staking-runtime-api的使用
概述
pallet-staking-runtime-api
是Substrate框架中用于质押(staking)和验证人功能的核心运行时API。它为区块链提供了基本的质押机制,允许用户参与网络共识并获得奖励,同时支持验证人选举和管理功能。
主要功能
- 质押代币参与网络共识
- 验证人选举和奖励分配
- 提名验证人
- 解绑和提取质押资金
- 验证人评分和惩罚(slashing)
使用方法
1. 添加依赖
首先需要在项目的Cargo.toml
中添加依赖:
[dependencies]
pallet-staking-runtime-api = { git = "https://github.com/paritytech/substrate.git", branch = "master" }
sp-api = { git = "https://github.com/paritytech/substrate.git", branch = "master" }
2. 实现Runtime API
在你的runtime中实现staking API trait:
use pallet_staking_runtime_api::{StakingApi, Nominations};
impl StakingApi<Block> for Runtime {
fn active_era() -> sp_staking::EraIndex {
Staking::active_era().index
}
fn current_era() -> sp_staking::EraIndex {
Staking::current_era()
}
fn nominations(who: &AccountId) -> Option<Nominations<AccountId>> {
Staking::nominations(who)
}
}
3. 基本质押操作示例
质押代币
use frame_support::dispatch::DispatchResult;
fn stake(controller: &AccountId, value: Balance) -> DispatchResult {
Staking::bond(Origin::signed(controller.clone()), controller.clone(), value, RewardDestination::Staked)
}
提名验证人
fn nominate(nominator: &AccountId, validators: Vec<AccountId>) -> DispatchResult {
Staking::nominate(Origin::signed(nominator.clone()), validators)
}
解绑资金
fn unbond(staker: &AccountId, value: Balance) -> DispatchResult {
Staking::unbond(Origin::signed(staker.clone()), value)
}
4. 查询质押信息
获取当前era
let current_era = Staking::current_era();
获取验证人信息
use pallet_staking::Exposure;
fn get_validator_exposure(era: EraIndex, validator: &AccountId) -> Option<Exposure<AccountId, Balance>> {
Staking::eras_stakers(era, validator)
}
5. 验证人操作
设置验证人佣金
fn set_validator_commission(validator: &AccountId, commission: Perbill) -> DispatchResult {
Staking::validate(Origin::signed(validator.clone()), ValidatorPrefs { commission })
}
领取奖励
fn claim_rewards(staker: &AccountId, era: EraIndex) -> DispatchResult {
Staking::payout_stakers(Origin::signed(staker.clone()), validator.clone(), era)
}
高级功能
自定义奖励计算
impl pallet_staking::Config for Runtime {
type EraRewardCalculator = CustomEraReward;
}
struct CustomEraReward;
impl EraRewardCalculator<Balance> for CustomEraReward {
fn calculate_reward(
total_staked: Balance,
total_issuance: Balance,
_era_duration_millis: u64,
) -> (Balance, Balance) {
// 自定义奖励计算逻辑
let reward = total_staked / 100; // 1%奖励
(reward, reward)
}
}
自定义slash处理
impl pallet_staking::Config for Runtime {
type SlashHandler = CustomSlashHandler;
}
struct CustomSlashHandler;
impl SlashHandler<AccountId, Balance> for CustomSlashHandler {
fn on_slash(
validator: &AccountId,
slash: Balance,
_current_era: EraIndex,
_active_era: EraIndex,
) {
// 自定义slash处理逻辑
log::info!("Validator {} slashed by {}", validator, slash);
}
}
完整示例demo
下面是一个完整的质押系统实现示例,整合了上述所有功能:
//! 完整质押系统示例
use frame_support::{dispatch::DispatchResult, traits::Currency};
use frame_system::Config as SystemConfig;
use pallet_staking::{
Config as StakingConfig, Exposure, Nominations, RewardDestination, ValidatorPrefs,
};
use sp_runtime::{
traits::{Block as BlockT, StaticLookup},
Perbill,
};
use sp_staking::EraIndex;
/// 质押系统实现
pub struct StakingSystem<T: StakingConfig + SystemConfig> {
_marker: std::marker::PhantomData<T>,
}
impl<T: StakingConfig + SystemConfig> StakingSystem<T> {
/// 质押代币
pub fn bond(
controller: &T::AccountId,
value: <T as StakingConfig>::CurrencyBalance,
) -> DispatchResult {
// 质押代币并设置奖励目标为继续质押
pallet_staking::Pallet::<T>::bond(
RawOrigin::Signed(controller.clone()).into(),
controller.clone(),
value,
RewardDestination::Staked,
)
}
/// 提名验证人
pub fn nominate(
nominator: &T::AccountId,
validators: Vec<<T as SystemConfig>::AccountId>,
) -> DispatchResult {
pallet_staking::Pallet::<T>::nominate(
RawOrigin::Signed(nominator.clone()).into(),
validators,
)
}
/// 解绑质押资金
pub fn unbond(
staker: &T::AccountId,
value: <T as StakingConfig>::CurrencyBalance,
) -> DispatchResult {
pallet_staking::Pallet::<T>::unbond(
RawOrigin::Signed(staker.clone()).into(),
value
)
}
/// 获取当前era
pub fn current_era() -> EraIndex {
pallet_staking::Pallet::<T>::current_era()
}
/// 获取验证人信息
pub fn validator_exposure(
era: EraIndex,
validator: &T::AccountId,
) -> Option<Exposure<T::AccountId, <T as StakingConfig>::CurrencyBalance>> {
pallet_staking::Pallet::<T>::eras_stakers(era, validator)
}
/// 设置验证人佣金
pub fn set_validator_commission(
validator: &T::AccountId,
commission: Perbill,
) -> DispatchResult {
pallet_staking::Pallet::<T>::validate(
RawOrigin::Signed(validator.clone()).into(),
ValidatorPrefs { commission, ..Default::default() },
)
}
/// 领取奖励
pub fn claim_rewards(
staker: &T::AccountId,
validator: &T::AccountId,
era: EraIndex,
) -> DispatchResult {
pallet_staking::Pallet::<T>::payout_stakers(
RawOrigin::Signed(staker.clone()).into(),
validator.clone(),
era,
)
}
}
/// 自定义奖励计算器
pub struct CustomRewardCalculator;
impl pallet_staking::EraRewardCalculator<<T as StakingConfig>::CurrencyBalance>
for CustomRewardCalculator
{
fn calculate_reward(
total_staked: <T as StakingConfig>::CurrencyBalance,
total_issuance: <T as StakingConfig>::CurrencyBalance,
_era_duration_millis: u64,
) -> (<T as StakingConfig>::CurrencyBalance, <T as StakingConfig>::CurrencyBalance) {
// 更复杂的奖励计算逻辑
let reward = total_staked / 50; // 2%奖励
(reward, reward)
}
}
/// 自定义slash处理器
pub struct CustomSlashHandler;
impl pallet_staking::SlashHandler<T::AccountId, <T as StakingConfig>::CurrencyBalance>
for CustomSlashHandler
{
fn on_slash(
validator: &T::AccountId,
slash: <T as StakingConfig>::CurrencyBalance,
_current_era: EraIndex,
_active_era: EraIndex,
) {
// 更复杂的slash处理逻辑
log::warn!(
"Validator {} was slashed by {} tokens",
validator,
slash
);
// 可以在这里添加额外的惩罚逻辑
}
}
注意事项
- 质押操作通常有锁定期,资金不能立即提取
- 验证人需要满足最低质押要求
- 提名多个验证人时,资金会自动分配到最需要的验证人
- 不活跃或行为不当的验证人可能会受到slash惩罚
通过pallet-staking-runtime-api
,开发者可以构建复杂的质押经济系统,为PoS区块链提供核心的安全和共识机制。