Rust区块链插件库pallet-authorship的使用:Substrate框架中区块作者验证与奖励机制实现

FRAME运行时的作者身份追踪。

这用于追踪当前区块作者和最近的叔块。

许可证:Apache-2.0

// 示例代码:pallet-authorship 基本配置
#[frame_support::pallet]
pub mod pallet {
    use frame_support::pallet_prelude::*;
    use frame_system::pallet_prelude::*;

    #[pallet::pallet]
    pub struct Pallet<T>(_);

    #[pallet::config]
    pub trait Config: frame_system::Config {
        /// 处理发现区块作者的事件
        type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
        /// 作者身份处理
        type FindAuthor: FindAuthor<Self::AccountId>;
        /// 处理叔叔区块
        type UncleGenerations: Get<u32>;
        /// 过滤叔叔区块
        type FilterUncle: FilterUncle<Self::Header, Self::AccountId>;
    }

    #[pallet::event]
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
    pub enum Event<T: Config> {
        /// 新区块作者被设置
        NewAuthor(T::AccountId),
    }

    #[pallet::storage]
    #[pallet::getter(fn author)]
    pub(super) type Author<T: Config> = StorageValue<_, T::AccountId, OptionQuery>;

    #[pallet::hooks]
    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
        fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
            // 清除当前作者
            Author::<T>::kill();
            Weight::zero()
        }

        fn on_finalize(_n: BlockNumberFor<T>) {
            // 确保作者已设置
            assert!(Author::<T>::exists(), "Author must be set at the end of the block.");
        }
    }

    #[pallet::call]
    impl<T: Config> Pallet<T> {
        /// 设置区块作者
        #[pallet::weight(10_000)]
        pub fn set_author(origin: OriginFor<T>, author: T::AccountId) -> DispatchResult {
            ensure_root(origin)?;
            Author::<T>::put(&author);
            Self::deposit_event(Event::NewAuthor(author));
            Ok(())
        }
    }
}

完整示例demo:

// pallet-authorship 完整实现示例
#![cfg_attr(not(feature = "std"), no_std)]

pub use pallet::*;

#[frame_support::pallet]
pub mod pallet {
    use frame_support::{
        pallet_prelude::*,
        traits::{FindAuthor, FilterUncle, Get},
    };
    use frame_system::pallet_prelude::*;
    use sp_runtime::traits::Header as HeaderT;
    use sp_std::prelude::*;

    /// 作者身份模块配置
    #[pallet::config]
    pub trait Config: frame_system::Config {
        /// 系统事件类型
        type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
        /// 查找作者实现
        type FindAuthor: FindAuthor<Self::AccountId>;
        /// 叔叔区块代数
        type UncleGenerations: Get<u32>;
        /// 叔叔区块过滤器
        type FilterUncle: FilterUncle<Self::Header, Self::AccountId>;
        /// 权重信息
        type WeightInfo: WeightInfo;
    }

    /// 作者身份模块
    #[pallet::pallet]
    #[pallet::generate_store(pub(super) trait Store)]
    pub struct Pallet<T>(_);

    /// 模块事件
    #[pallet::event]
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
    pub enum Event<T: Config> {
        /// 新区块作者被设置
        AuthorSet(T::AccountId),
        /// 叔叔区块被添加
        UncleIncluded(u32, T::AccountId),
    }

    /// 错误类型
    #[pallet::error]
    pub enum Error<T> {
        /// 作者未找到
        AuthorNotFound,
        /// 无效的叔叔区块
        InvalidUncle,
        /// 叔叔区块已存在
        UncleAlreadyIncluded,
    }

    /// 当前区块作者存储
    #[pallet::storage]
    #[pallet::getter(fn author)]
    pub(super) type Author<T: Config> = StorageValue<_, T::AccountId, OptionQuery>;

    /// 叔叔区块存储
    #[pallet::storage]
    pub(super) type Uncles<T: Config> = StorageValue<
        _,
        Vec<<T::Header as HeaderT>::Hash>,
        ValueQuery,
    >;

    /// 模块hooks实现
    #[pallet::hooks]
    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
        /// 区块初始化
        fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
            // 清除当前作者
            Author::<T>::kill();
            T::WeightInfo::on_initialize()
        }

        /// 区块最终化
        fn on_finalize(_n: BlockNumberFor<T>) {
            // 验证作者已设置
            assert!(Author::<T>::exists(), "Block author must be set");
        }
    }

    /// 可调用函数
    #[pallet::call]
    impl<T: Config> Pallet<T> {
        /// 设置区块作者
        #[pallet::weight(T::WeightInfo::set_author())]
        pub fn set_author(
            origin: OriginFor<T>,
            author: T::AccountId,
        ) -> DispatchResultWithPostInfo {
            ensure_root(origin)?;
            
            Author::<T>::put(&author);
            Self::deposit_event(Event::AuthorSet(author));
            
            Ok(().into())
        }

        /// 添加叔叔区块
        #[pallet::weight(T::WeightInfo::add_uncle())]
        pub fn add_uncle(
            origin: OriginFor<T>,
            uncle_hash: <T::Header as HeaderT>::Hash,
            author: T::AccountId,
        ) -> DispatchResultWithPostInfo {
            ensure_root(origin)?;

            // 验证叔叔区块
            ensure!(
                T::FilterUncle::filter_uncle(uncle_hash, &author),
                Error::<T>::InvalidUncle
            );

            // 检查是否已包含
            let mut uncles = Uncles::<T>::get();
            ensure!(
                !uncles.contains(&uncle_hash),
                Error::<T>::UncleAlreadyIncluded
            );

            // 添加叔叔区块
            uncles.push(uncle_hash);
            Uncles::<T>::put(uncles);

            Self::deposit_event(Event::UncleIncluded(
                T::UncleGenerations::get(),
                author,
            ));

            Ok(().into())
        }
    }

    /// 权重信息trait
    pub trait WeightInfo {
        fn on_initialize() -> Weight;
        fn set_author() -> Weight;
        fn add_uncle() -> Weight;
    }

    /// 默认权重实现
    impl WeightInfo for () {
        fn on_initialize() -> Weight {
            Weight::from_ref_time(10_000)
        }
        fn set_author() -> Weight {
            Weight::from_ref_time(20_000)
        }
        fn add_uncle() -> Weight {
            Weight::from_ref_time(30_000)
        }
    }

    /// 作者身份模块接口
    impl<T: Config> Pallet<T> {
        /// 获取当前作者
        pub fn current_author() -> Option<T::AccountId> {
            Author::<T>::get()
        }

        /// 获取所有叔叔区块
        pub fn uncles() -> Vec<<T::Header as HeaderT>::Hash> {
            Uncles::<T>::get()
        }

        /// 清除叔叔区块
        pub fn clear_uncles() {
            Uncles::<T>::kill();
        }
    }
}

/// 测试模块
#[cfg(test)]
mod tests {
    use super::*;
    use frame_support::{
        assert_ok, parameter_types,
        traits::{ConstU32, ConstU64},
    };
    use sp_core::H256;
    use sp_runtime::{
        testing::Header,
        traits::{BlakeTwo256, IdentityLookup},
    };

    type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
    type Block = frame_system::mocking::MockBlock<Test>;

    frame_support::construct_runtime!(
        pub enum Test where
            Block = Block,
            NodeBlock = Block,
            UncheckedExtrinsic = UncheckedExtrinsic,
        {
            System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
            Authorship: pallet_authorship::{Pallet, Call, Storage, Event<T>},
        }
    );

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

    impl frame_system::Config for Test {
        type BaseCallFilter = frame_support::traits::Everything;
        type BlockWeights = ();
        type BlockLength = ();
        type DbWeight = ();
        type Origin = Origin;
        type Call = Call;
        type Index = u64;
        type BlockNumber = u64;
        type Hash = H256;
        type Hashing = BlakeTwo256;
        type AccountId = u64;
        type Lookup = IdentityLookup<Self::AccountId>;
        type Header = Header;
        type Event = Event;
        type BlockHashCount = ConstU64<250>;
        type Version = ();
        type PalletInfo = PalletInfo;
        type AccountData = ();
        type OnNewAccount = ();
        type OnKilledAccount = ();
        type SystemWeightInfo = ();
        type SS58Prefix = ConstU8<42>;
        type OnSetCode = ();
        type MaxConsumers = ConstU32<16>;
    }

    parameter_types! {
        pub const UncleGenerations: u32 = 5;
    }

    pub struct TestFindAuthor;
    impl FindAuthor<u64> for TestFindAuthor {
        fn find_author<'a, I>(_digests: I) -> Option<u64>
        where
            I: 'a + IntoIterator<Item = (frame_support::ConsensusEngineId, &'a [u8])>,
        {
            Some(1)
        }
    }

    pub struct TestFilterUncle;
    impl FilterUncle<Header, u64> for TestFilterUncle {
        fn filter_uncle(_hash: H256, _author: &u64) -> bool {
            true
        }
    }

    impl Config for Test {
        type Event = Event;
        type FindAuthor = TestFindAuthor;
        type UncleGenerations = UncleGenerations;
        type FilterUncle = TestFilterUncle;
        type WeightInfo = ();
    }

    #[test]
    fn test_set_author() {
        new_test_ext().execute_with(|| {
            // 设置作者
            assert_ok!(Authorship::set_author(Origin::root(), 42));
            
            // 验证事件
            assert_eq!(
                System::events().last().unwrap().event,
                Event::Authorship(crate::Event::AuthorSet(42))
            );
            
            // 验证存储
            assert_eq!(Authorship::author(), Some(42));
        });
    }

    #[test]
    fn test_add_uncle() {
        new_test_ext().execute_with(|| {
            let uncle_hash = H256::repeat_byte(1);
            
            // 添加叔叔区块
            assert_ok!(Authorship::add_uncle(Origin::root(), uncle_hash, 123));
            
            // 验证事件
            assert_eq!(
                System::events().last().unwrap().event,
                Event::Authorship(crate::Event::UncleIncluded(5, 123))
            );
            
            // 验证存储
            assert!(Authorship::uncles().contains(&uncle_hash));
        });
    }
}

1 回复

Rust区块链插件库pallet-authorship的使用:Substrate框架中区块作者验证与奖励机制实现

概述

pallet-authorship是Substrate框架中的一个核心插件库,专门用于处理区块链网络中的区块作者验证和奖励分配机制。该模块通过管理验证人集合和区块生产者,确保网络共识机制的正常运行,并为区块生产者提供相应的经济激励。

核心功能

1. 区块作者验证

  • 验证区块签名和作者身份
  • 维护当前会话的验证人集合
  • 确保只有授权的验证人才能生产区块

2. 奖励分配机制

  • 跟踪区块生产者的贡献
  • 分配区块奖励和交易费用
  • 支持可配置的奖励参数

使用方法

安装依赖

Cargo.toml中添加依赖:

[dependencies]
pallet-authorship = { version = "4.0.0", default-features = false }

基本配置

use frame_support::pallet_prelude::*;
use sp_runtime::traits::BlakeTwo256;

pub trait Config: frame_system::Config {
    type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;
    type FindAuthor: FindAuthor<Self::AccountId>;
}

#[pallet::pallet]
pub struct Pallet<T>(_);

实现示例

1. 初始化验证人集合

#[pallet::call]
impl<T: Config> Pallet<T> {
    #[pallet::weight(10_000)]
    pub fn set_validators(
        origin: OriginFor<T>,
        validators: Vec<T::AccountId>,
    ) -> DispatchResult {
        ensure_root(origin)?;
        
        Validators::<T>::put(validators);
        Self::deposit_event(Event::ValidatorsUpdated);
        Ok(())
    }
}

2. 区块验证实现

impl<T: Config> FindAuthor<T::AccountId> for Pallet<T> {
    fn find_author<'a, I>(digests: I) -> Option<T::AccountId>
    where
        I: 'a + IntoIterator<Item = (ConsensusEngineId, &'a [u8])>,
    {
        // 从digests中提取作者信息
        for (id, data) in digests {
            if id == BABE_ENGINE_ID {
                return Some(T::AccountId::decode(&mut &data[..]).unwrap());
            }
        }
        None
    }
}

3. 奖励分配逻辑

#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
    fn on_finalize(n: T::BlockNumber) {
        if let Some(author) = Author::<T>::get() {
            // 计算区块奖励
            let reward = 100; // 基础奖励
            let total_fees = T::Currency::total_issuance() / 1000;
            
            // 分配奖励
            T::Currency::deposit_creating(&author, reward + total_fees);
            
            Self::deposit_event(Event::RewardDistributed(author, reward));
        }
    }
}

完整示例配置

// runtime/src/lib.rs
impl pallet_authorship::Config for Runtime {
    type Event = Event;
    type FindAuthor = Babe;
}

// 在construct_runtime!宏中包含pallet
construct_runtime!(
    pub enum Runtime where
        Block = Block,
        NodeBlock = opaque::Block,
        UncheckedExtrinsic = UncheckedExtrinsic
    {
        // ... 其他pallet
        Authorship: pallet_authorship::{Pallet, Call, Storage, Event<T>},
    }
);

事件处理

#[pallet::event]
pub enum Event<T: Config> {
    /// 新区块作者设置
    AuthorSet(T::AccountId),
    /// 验证人集合更新
    ValidatorsUpdated,
    /// 奖励分配事件
    RewardDistributed(T::AccountId, BalanceOf<T>),
}

存储项

#[pallet::storage]
#[pallet::getter(fn author)]
pub type Author<T> = StorageValue<_, T::AccountId>;

#[pallet::storage]
pub type Validators<T> = StorageValue<_, Vec<T::AccountId>, ValueQuery>;

注意事项

  1. 确保在runtime配置中正确实现FindAuthor trait
  2. 奖励分配逻辑需要与货币政策协调
  3. 验证人集合更新需要适当的权限控制
  4. 考虑网络安全性,避免单点故障

这个pallet为Substrate区块链提供了基础的区块生产和奖励分配功能,是构建PoS共识机制的重要组成部分。

完整示例demo

// 文件:pallets/authorship/src/lib.rs

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

use frame_support::{
    pallet_prelude::*,
    traits::{Currency, FindAuthor},
    weights::Weight,
};
use frame_system::pallet_prelude::*;
use sp_runtime::{
    traits::{BlakeTwo256, IdentifyAccount, Verify},
    ConsensusEngineId,
};
use sp_std::prelude::*;

// 定义BABE共识引擎ID
const BABE_ENGINE_ID: ConsensusEngineId = *b"BABE";

// 配置trait
pub trait Config: frame_system::Config {
    type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;
    type Currency: Currency<Self::AccountId>;
    type FindAuthor: FindAuthor<Self::AccountId>;
}

// 声明存储项
#[pallet::storage]
#[pallet::getter(fn author)]
pub type Author<T: Config> = StorageValue<_, T::AccountId, ValueQuery>;

#[pallet::storage]
pub type Validators<T: Config> = StorageValue<_, Vec<T::AccountId>, ValueQuery>;

// 声明事件
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
    AuthorSet(T::AccountId),
    ValidatorsUpdated,
    RewardDistributed(T::AccountId, BalanceOf<T>),
}

// 声明错误
#[pallet::error]
pub enum Error<T> {
    InvalidValidator,
    Unauthorized,
}

// 声明pallet
#[pallet::pallet]
pub struct Pallet<T>(_);

// 实现pallet
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
    fn on_finalize(_n: T::BlockNumber) {
        if let Some(author) = Author::<T>::get() {
            // 计算区块奖励(示例值)
            let base_reward = 100u32.into();
            let total_fees = T::Currency::total_issuance() / 1000u32.into();
            
            // 分配奖励给区块作者
            T::Currency::deposit_creating(&author, base_reward + total_fees);
            
            // 触发奖励分配事件
            Self::deposit_event(Event::RewardDistributed(author, base_reward));
        }
    }
}

// 实现可调用函数
#[pallet::call]
impl<T: Config> Pallet<T> {
    #[pallet::weight(10_000)]
    pub fn set_validators(
        origin: OriginFor<T>,
        validators: Vec<T::AccountId>,
    ) -> DispatchResultWithPostInfo {
        // 确保只有root账户可以调用
        ensure_root(origin)?;
        
        // 更新验证人集合
        Validators::<T>::put(validators);
        
        // 触发验证人更新事件
        Self::deposit_event(Event::ValidatorsUpdated);
        
        Ok(().into())
    }

    #[pallet::weight(5_000)]
    pub fn set_author(
        origin: OriginFor<T>,
        author: T::AccountId,
    ) -> DispatchResultWithPostInfo {
        // 检查调用者权限
        ensure_signed(origin)?;
        
        // 验证作者是否为有效验证人
        let validators = Validators::<T>::get();
        ensure!(
            validators.contains(&author),
            Error::<T>::InvalidValidator
        );
        
        // 设置当前区块作者
        Author::<T>::put(&author);
        
        // 触发作者设置事件
        Self::deposit_event(Event::AuthorSet(author));
        
        Ok(().into())
    }
}

// 实现FindAuthor trait用于区块验证
impl<T: Config> FindAuthor<T::AccountId> for Pallet<T> {
    fn find_author<'a, I>(digests: I) -> Option<T::AccountId>
    where
        I: 'a + IntoIterator<Item = (ConsensusEngineId, &'a [u8])>,
    {
        // 遍历digests查找BABE共识数据
        for (id, data) in digests {
            if id == BABE_ENGINE_ID {
                // 解码账户ID
                return T::AccountId::decode(&mut &data[..]).ok();
            }
        }
        None
    }
}

// 实现辅助函数
impl<T: Config> Pallet<T> {
    /// 获取当前验证人列表
    pub fn get_validators() -> Vec<T::AccountId> {
        Validators::<T>::get()
    }
    
    /// 获取当前区块作者
    pub fn get_author() -> Option<T::AccountId> {
        Author::<T>::get()
    }
}
// 文件:runtime/src/lib.rs

// 在runtime配置中实现pallet-authorship的配置
impl pallet_authorship::Config for Runtime {
    type Event = Event;
    type Currency = Balances;
    type FindAuthor = Babe;
}

// 在construct_runtime!宏中包含pallet
construct_runtime!(
    pub enum Runtime where
        Block = Block,
        NodeBlock = opaque::Block,
        UncheckedExtrinsic = UncheckedExtrinsic
    {
        System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
        Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent},
        Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
        Authorship: pallet_authorship::{Pallet, Call, Storage, Event<T>},
        // ... 其他pallet
    }
);
# 文件:pallets/authorship/Cargo.toml

[package]
name = "pallet-authorship"
version = "4.0.0"
description = "Substrate authorship pallet for block producer validation and reward distribution"
edition = "2021"

[dependencies]
codec = { package = "parity-scale-codec", version = "3.0", default-features = false, features = ["derive"] }
frame-support = { version = "4.0.0", default-features = false }
frame-system = { version = "4.0.0", default-features = false }
sp-runtime = { version = "6.0", default-features = false }
sp-std = { version = "4.0", default-features = false }

[features]
default = ["std"]
std = [
    "codec/std",
    "frame-support/std",
    "frame-system/std",
    "sp-runtime/std",
    "sp-std/std",
]
回到顶部