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提供的主要功能包括:

  1. 获取活跃验证人列表
  2. 获取所有提名人列表
  3. 查询验证人佣金率
  4. 查询验证人质押总额
  5. 获取当前选举周期信息
  6. 查询验证人表现统计

高级用法

对于更高级的用例,您可能需要实现自定义的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()
}

注意事项

  1. 使用这些API需要您的客户端实现相应的Runtime API trait
  2. 所有调用都是通过区块链状态进行的,不会发送任何交易
  3. 确保您的节点同步到了足够高的区块高度,以获取准确的信息
  4. 某些API可能需要特定的权限才能调用

1 回复

Rust区块链开发库pallet-staking-runtime-api的使用

概述

pallet-staking-runtime-api是Substrate框架中用于质押(staking)和验证人功能的核心运行时API。它为区块链提供了基本的质押机制,允许用户参与网络共识并获得奖励,同时支持验证人选举和管理功能。

主要功能

  1. 质押代币参与网络共识
  2. 验证人选举和奖励分配
  3. 提名验证人
  4. 解绑和提取质押资金
  5. 验证人评分和惩罚(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
        );
        // 可以在这里添加额外的惩罚逻辑
    }
}

注意事项

  1. 质押操作通常有锁定期,资金不能立即提取
  2. 验证人需要满足最低质押要求
  3. 提名多个验证人时,资金会自动分配到最需要的验证人
  4. 不活跃或行为不当的验证人可能会受到slash惩罚

通过pallet-staking-runtime-api,开发者可以构建复杂的质押经济系统,为PoS区块链提供核心的安全和共识机制。

回到顶部