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>;
注意事项
- 确保在runtime配置中正确实现FindAuthor trait
- 奖励分配逻辑需要与货币政策协调
- 验证人集合更新需要适当的权限控制
- 考虑网络安全性,避免单点故障
这个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",
]