Rust区块链共识插件pallet-grandpa的使用:Substrate框架下权威证明(PoA)的GRANDPA最终性工具
Rust区块链共识插件pallet-grandpa的使用:Substrate框架下权威证明(PoA)的GRANDPA最终性工具
概述
GRANDPA是Substrate框架中的最终性共识模块,专为运行时环境设计。它管理着GRANDPA权威集合的准备工作,这些权威仅用于GRANDPA最终性确认,而非整体共识机制。
未来,该模块还将处理不当行为报告和链上最终性通知。
安装
在项目目录中运行以下Cargo命令:
cargo add pallet-grandpa
或在Cargo.toml中添加:
pallet-grandpa = "42.0.0"
核心功能
- 管理GRANDPA权威集合
- 为原生代码准备最终性小工具
- 未来将支持不当行为报告和链上最终性通知
完整集成
要与GRANDPA完全集成,需要实现GrandpaApi
。必要的项可通过fg_primitives
crate重新导出。
示例代码
以下是Substrate链中使用pallet-grandpa的基本配置示例:
// runtime/src/lib.rs
// 1. 引入必要的依赖
use frame_support::traits::{GenesisBuild, OnFinalize, OnInitialize};
use sp_consensus_grandpa::AuthorityId;
// 2. 配置pallet-grandpa
impl pallet_grandpa::Config for Runtime {
type Event = Event;
type Call = Call;
type KeyOwnerProofSystem = ();
type KeyOwnerProof = <Self::KeyOwnerProofSystem as KeyOwnerProofSystem<(
KeyTypeId,
AuthorityId,
)>>::Proof;
type KeyOwnerIdentification = <Self::KeyOwnerProofSystem as KeyOwnerProofSystem<(
KeyTypeId,
AuthorityId,
)>>::IdentificationTuple;
type HandleEquivocation = ();
type WeightInfo = ();
}
// 3. 在construct_runtime!宏中包含pallet-grandpa
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
// ... 其他pallet
Grandpa: pallet_grandpa::{Module, Call, Storage, Config, Event},
}
);
// 4. 实现GRANDPA API
sp_api::impl_runtime_apis! {
impl sp_consensus_grandpa::GrandpaApi<Block> for Runtime {
fn grandpa_authorities() -> Vec<(AuthorityId, u64)> {
Grandpa::grandpa_authorities()
}
// ... 其他必要的方法实现
}
}
// 5. 创世配置示例
pub struct GenesisConfig {
pub grandpa: pallet_grandpa::GenesisConfig,
}
impl genesis::GenesisConfig for GenesisConfig {
fn genesis_state(&self) -> Vec<u8> {
let mut storage = vec![];
pallet_grandpa::GenesisConfig::assimilate_storage(&self.grandpa, &mut storage)
.expect("Failed to assimilate storage for Grandpa pallet");
storage
}
}
完整示例demo
以下是一个完整的pallet-grandpa集成示例,包含运行时配置和创世设置:
// runtime/src/lib.rs
use frame_support::{
construct_runtime, parameter_types,
traits::{GenesisBuild, KeyOwnerProofSystem, OnFinalize, OnInitialize},
weights::Weight,
};
use sp_consensus_grandpa::AuthorityId as GrandpaId;
use sp_runtime::{
generic,
traits::{BlakeTwo256, IdentifyAccount, Verify},
MultiSignature, RuntimeDebug,
};
// 1. 定义基本类型
pub type Signature = MultiSignature;
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
pub type BlockNumber = u32;
pub type Index = u32;
pub type Balance = u128;
// 2. 定义区块类型
type Header = generic::Header<BlockNumber, BlakeTwo256>;
type Block = generic::Block<Header, UncheckedExtrinsic>;
type UncheckedExtrinsic = generic::UncheckedExtrinsic<AccountId, Call, Signature, ()>;
// 3. 配置pallet-grandpa
parameter_types! {
pub const MaxAuthorities: u32 = 32;
}
impl pallet_grandpa::Config for Runtime {
type Event = Event;
type Call = Call;
// 定义密钥所有者证明系统
type KeyOwnerProofSystem = ();
type KeyOwnerProof = <Self::KeyOwnerProofSystem as KeyOwnerProofSystem<(
sp_consensus_grandpa::KEY_TYPE,
GrandpaId,
)>>::Proof;
type KeyOwnerIdentification = <Self::KeyOwnerProofSystem as KeyOwnerProofSystem<(
sp_consensus_grandpa::KEY_TYPE,
GrandpaId,
)>>::IdentificationTuple;
type HandleEquivocation = ();
type WeightInfo = ();
}
// 4. 构建运行时
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: frame_system::{Module, Call, Config, Storage, Event<T>},
Grandpa: pallet_grandpa::{Module, Call, Storage, Config, Event},
}
);
// 5. 实现GRANDPA API
sp_api::impl_runtime_apis! {
impl sp_consensus_grandpa::GrandpaApi<Block> for Runtime {
fn grandpa_authorities() -> Vec<(GrandpaId, u64)> {
Grandpa::grandpa_authorities()
}
fn submit_report_equivocation_unsigned_extrinsic(
_equivocation_proof: sp_consensus_grandpa::EquivocationProof<
<Block as sp_runtime::traits::Block>::Hash,
sp_runtime::traits::NumberFor<Block>,
>,
_key_owner_proof: sp_consensus_grandpa::OpaqueKeyOwnershipProof,
) -> Option<()> {
None
}
fn generate_key_ownership_proof(
_set_id: sp_consensus_grandpa::SetId,
_authority_id: GrandpaId,
) -> Option<sp_consensus_grandpa::OpaqueKeyOwnershipProof> {
None
}
}
}
// 6. 创世配置
pub struct GenesisConfig {
pub system: frame_system::GenesisConfig,
pub grandpa: pallet_grandpa::GenesisConfig,
}
impl genesis::GenesisConfig for GenesisConfig {
fn genesis_state(&self) -> Vec<u8> {
let mut storage = vec![];
// 初始化系统配置
frame_system::GenesisConfig::assimilate_storage(&self.system, &mut storage)
.expect("Failed to assimilate system storage");
// 初始化GRANDPA配置
pallet_grandpa::GenesisConfig::assimilate_storage(&self.grandpa, &mut storage)
.expect("Failed to assimilate storage for Grandpa pallet");
storage
}
}
注意事项
- 许可证:Apache-2.0
- 该模块是Polkadot SDK的一部分
- 需要与
fg_primitives
crate配合使用以实现完整功能
通过正确配置和使用pallet-grandpa,开发者可以在Substrate框架中实现基于权威证明(PoA)的GRANDPA最终性共识机制。
1 回复
Rust区块链共识插件pallet-grandpa的使用:Substrate框架下权威证明(PoA)的GRANDPA最终性工具
概述
pallet-grandpa
是Substrate框架中实现GRANDPA(GHOST-based Recursive ANcestor Deriving Prefix Agreement)最终性小工具的模块。它为基于Substrate的区块链提供了最终性确定机制,与BABE或Aura等区块生产机制配合使用。
GRANDPA的特点:
- 不是区块生产算法,而是最终性工具
- 可以在多个区块上达成共识并同时完成
- 具有抗异步安全性
- 适用于权威证明(PoA)和权益证明(PoS)网络
完整示例代码
以下是基于PoA网络的完整配置示例:
// runtime/src/lib.rs
// 1. 引入必要的依赖
use sp_runtime::traits::{BlakeTwo256, Block as BlockT, Verify};
use sp_finality_grandpa::{AuthorityId as GrandpaId, AuthorityList};
use sp_runtime::generic::UncheckedExtrinsic;
use frame_support::traits::KeyOwnerProofSystem;
// 2. 配置GRANDPA pallet
impl pallet_grandpa::Config for Runtime {
type Event = Event;
type Call = Call;
// 使用历史证明作为密钥所有权证明
type KeyOwnerProof = sp_session::HistoricalProof;
// 使用Session pallet进行密钥所有者识别
type KeyOwnerIdentification = <SessionModule as KeyOwnerProofSystem<(
key_types::GRANDPA,
GrandpaId,
)>>::IdentificationTuple;
// 不处理不良行为(生产环境应实现)
type HandleEquivocation = ();
}
// 3. 在运行时构建宏中添加GRANDPA
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: frame_system,
// ...其他pallet
Grandpa: pallet_grandpa::{Module, Call, Storage, Config, Event},
Session: pallet_session,
}
);
// node/src/chain_spec.rs
// 4. 配置创世块中的初始验证人
pub fn authority_keys_from_seed(
seed: &str,
) -> (AccountId, GrandpaId, BabeId, ImOnlineId) {
// 生成各种密钥
let account_id = get_account_id_from_seed::<sr25519::Public>(seed);
let grandpa_key = get_from_seed::<GrandpaId>(seed);
// ...其他密钥
(account_id, grandpa_key, babe_key, im_online_key)
}
pub fn testnet_genesis(
initial_authorities: Vec<(AccountId, GrandpaId, BabeId, ImOnlineId)>,
root_key: AccountId,
endowed_accounts: Vec<AccountId>,
) -> GenesisConfig {
GenesisConfig {
// ...其他配置
grandpa: Some(GrandpaConfig {
// 设置初始权威验证人,每个权重为1
authorities: initial_authorities.iter()
.map(|x| (x.1.clone(), 1))
.collect(),
}),
session: Some(SessionConfig {
// 配置会话密钥
keys: initial_authorities.iter().map(|x| (
x.0.clone(),
x.0.clone(),
session_keys(x.1.clone(), x.2.clone(), x.3.clone())
)).collect::<Vec<_>>(),
}),
// ...其他配置
}
}
// primitives/src/lib.rs
// 5. 定义GRANDPA密钥类型
pub mod key_types {
use sp_runtime::KeyTypeId;
pub const GRANDPA: KeyTypeId = KeyTypeId(*b"gran");
}
// 6. 实现GRANDPA运行时API
impl sp_finality_grandpa::GrandpaApi<Block> for Runtime {
fn grandpa_authorities() -> AuthorityList {
Grandpa::grandpa_authorities()
}
fn submit_report_equivocation_unsigned_extrinsic(
_equivocation_proof: sp_finality_grandpa::EquivocationProof<
<Block as BlockT>::Hash,
NumberFor<Block>,
>,
_key_owner_proof: sp_session::MembershipProof,
) -> Option<()> {
// 生产环境应实现此功能
None
}
fn generate_key_ownership_proof(
_set_id: sp_finality_grandpa::SetId,
_authority_id: GrandpaId,
) -> Option<sp_session::MembershipProof> {
// 生产环境应实现此功能
None
}
}
注意事项
- 在PoA网络中,验证人集合通常在创世块中配置并通过链下机制变更
- GRANDPA需要与区块生产机制(如BABE或Aura)配合使用
- 确保超过2/3的验证人在线才能达成最终性
- 生产环境应实现不良行为报告和密钥所有权证明功能
故障排除
-
验证人无法签署区块:
- 检查会话密钥是否正确配置
- 验证GRANDPA密钥是否已注册
-
最终性停滞:
- 检查网络连接
- 确保足够的验证人在线(超过2/3)
-
同步问题:
- 检查所有节点使用相同的协议版本
- 验证创世块配置是否一致