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
    }
}

注意事项

  1. 在PoA网络中,验证人集合通常在创世块中配置并通过链下机制变更
  2. GRANDPA需要与区块生产机制(如BABE或Aura)配合使用
  3. 确保超过2/3的验证人在线才能达成最终性
  4. 生产环境应实现不良行为报告和密钥所有权证明功能

故障排除

  1. 验证人无法签署区块:

    • 检查会话密钥是否正确配置
    • 验证GRANDPA密钥是否已注册
  2. 最终性停滞:

    • 检查网络连接
    • 确保足够的验证人在线(超过2/3)
  3. 同步问题:

    • 检查所有节点使用相同的协议版本
    • 验证创世块配置是否一致
回到顶部