Rust跨链桥接模块pallet-bridge-grandpa的使用:实现基于GRANDPA共识的区块链互操作性解决方案

根据提供的内容,我将展示完整的pallet-bridge-grandpa示例实现,包含更完善的GRANDPA justification验证和验证者集合变更处理:

//! 完整的pallet-bridge-grandpa实现示例

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

use frame_support::{
    decl_error, decl_event, decl_module, decl_storage, dispatch, ensure,
    traits::Get,
    weights::Weight,
};
use frame_system::{ensure_root, ensure_signed};
use sp_runtime::traits::Header as HeaderT;
use sp_std::prelude::*;
use bp_header_chain::{
    justification::{GrandpaJustification, JustificationVerificationError},
    ChainWithGrandpa,
};

/// 配置trait
pub trait Config: frame_system::Config {
    /// 事件类型
    type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;
    
    /// 桥接的链类型
    type BridgedChain: ChainWithGrandpa;
    
    /// pallet所有者账户
    type OwnerOrigin: EnsureOrigin<Self::Origin>;
}

/// 存储项
decl_storage! {
    trait Store for Module<T: Config> as BridgeGrandpa {
        /// 当前最佳已最终确认的区块头
        pub BestFinalizedHeader get(fn best_finalized_header): 
            Option<<T::BridgedChain as ChainWithGrandpa>::Header>;
            
        /// 当前验证者集合及其ID
        pub CurrentValidatorSet get(fn current_validator_set): 
            Option<(sp_consensus_grandpa::ValidatorSet, sp_consensus_grandpa::SetId)>;
            
        /// pallet操作模式
        pub OperatingMode get(fn operating_mode): 
            bp_runtime::BasicOperatingMode;
            
        /// pallet所有者
        pub Owner get(fn owner): Option<T::AccountId>;
    }
}

/// 事件定义
decl_event!(
    pub enum Event<T> where AccountId = <T as frame_system::Config>::AccountId {
        /// 区块头已导入事件
        HeaderImported(sp_consensus_grandpa::SetId, <T::BridgedChain as ChainWithGrandpa>::Hash),
        /// 验证者集合已更新事件
        ValidatorSetUpdated(sp_consensus_grandpa::SetId),
        /// pallet所有者已变更事件
        OwnerChanged(Option<AccountId>),
        /// pallet操作模式已变更事件
        OperatingModeChanged(bp_runtime::BasicOperatingMode),
    }
);

/// 错误定义
decl_error! {
    pub enum Error for Module<T: Config> {
        /// 桥接已初始化
        AlreadyInitialized,
        /// 桥接未初始化
        NotInitialized,
        /// 无效的GRANDPA justification
        InvalidJustification,
        /// pallet处于非正常操作模式
        Halted,
        /// 区块头已过期
        OldHeader,
    }
}

/// 模块定义
decl_module! {
    pub struct Module<T: Config> for enum Call where origin: T::Origin {
        type Error = Error<T>;

        fn deposit_event() = default;

        /// 初始化桥接
        #[weight = T::DbWeight::get().reads_writes(2, 2)]
        pub fn initialize(
            origin,
            init_header: <T::BridgedChain as ChainWithGrandpa>::Header,
            init_validators: sp_consensus_grandpa::ValidatorSet,
            init_set_id: sp_consensus_grandpa::SetId,
        ) -> dispatch::DispatchResult {
            // 只有root或pallet所有者可以初始化
            T::OwnerOrigin::try_origin(origin)
                .map(|_| ())
                .or_else(|origin| ensure_root(origin))?;
            
            // 检查是否已初始化
            ensure!(BestFinalizedHeader::get().is_none(), Error::<T>::AlreadyInitialized);
            
            // 存储初始数据
            BestFinalizedHeader::put(init_header);
            CurrentValidatorSet::put((init_validators, init_set_id));
            
            Ok(())
        }

        /// 提交最终性证明
        #[weight = Self::compute_submit_finality_proof_weight(&header)]
        pub fn submit_finality_proof(
            origin,
            header: <T::BridgedChain as ChainWithGrandpa>::Header,
            justification: GrandpaJustification<T::BridgedChain::Hash>,
        ) -> dispatch::DispatchResult {
            // 检查操作模式
            ensure!(
                OperatingMode::get() == bp_runtime::BasicOperatingMode::Normal,
                Error::<T>::Halted
            );
            
            // 验证签名
            let _ = ensure_signed(origin)?;
            
            // 获取当前验证者集合
            let (current_validators, current_set_id) = CurrentValidatorSet::get()
                .ok_or(Error::<T>::NotInitialized)?;
            
            // 验证justification
            justification
                .verify(current_set_id, &current_validators)
                .map_err(|e| match e {
                    JustificationVerificationError::InvalidJustification(_) => 
                        Error::<T>::InvalidJustification,
                    JustificationVerificationError::JustificationForFutureSet(_) => 
                        Error::<T>::InvalidJustification,
                    JustificationVerificationError::JustificationForOldSet(_) => 
                        Error::<T>::InvalidJustification,
                })?;
                
            // 检查区块头是否比当前最佳更新
            if let Some(best_header) = BestFinalizedHeader::get() {
                ensure!(
                    header.number() > best_header.number(),
                    Error::<T>::OldHeader
                );
            }
            
            // 更新最佳区块头
            BestFinalizedHeader::put(header.clone());
            
            // 检查是否为mandatory header(包含新验证者集合)
            if let Some(new_set) = justification.commit.target_block.new_validator_set() {
                let new_set_id = current_set_id + 1;
                CurrentValidatorSet::put((new_set, new_set_id));
                Self::deposit_event(RawEvent::ValidatorSetUpdated(new_set_id));
            }
            
            Self::deposit_event(RawEvent::HeaderImported(
                current_set_id,
                header.hash()
            ));
            
            Ok(())
        }
        
        /// 设置pallet所有者
        #[weight = T::DbWeight::get().writes(1)]
        pub fn set_owner(origin, new_owner: Option<T::AccountId>) -> dispatch::DispatchResult {
            let current_owner = Owner::get();
            ensure!(current_owner == Some(ensure_signed(origin)?), dispatch::DispatchError::BadOrigin);
            
            Owner::put(new_owner.clone());
            Self::deposit_event(RawEvent::OwnerChanged(new_owner));
            
            Ok(())
        }
        
        /// 设置操作模式
        #[weight = T::DbWeight::get().writes(1)]
        pub fn set_operating_mode(
            origin,
            operating_mode: bp_runtime::BasicOperatingMode,
        ) -> dispatch::DispatchResult {
            T::OwnerOrigin::try_origin(origin)
                .map(|_| ())
                .or_else(|origin| ensure_root(origin))?;
                
            OperatingMode::put(operating_mode);
            Self::deposit_event(RawEvent::OperatingModeChanged(operating_mode));
            
            Ok(())
        }
    }
}

impl<T: Config> Module<T> {
    /// 计算提交最终性证明的权重
    fn compute_submit_finality_proof_weight(
        header: &<T::BridgedChain as ChainWithGrandpa>::Header,
    ) -> Weight {
        let mut weight = T::DbWeight::get().reads_writes(3, 2);
        
        // 如果包含新验证者集合,需要额外权重
        if header.digest().logs.iter().any(|log| log.is_authorities_change()) {
            weight = weight.saturating_add(T::DbWeight::get().writes(1));
        }
        
        weight
    }
}

这个完整实现包含以下关键改进:

  1. 完善的GRANDPA justification验证:

    • 使用bp_header_chain提供的验证函数
    • 处理不同的验证错误类型
    • 支持验证者集合变更
  2. 验证者集合管理:

    • 存储当前验证者集合和set_id
    • 自动检测并更新mandatory headers中的新验证者集合
    • 发出验证者集合变更事件
  3. 操作模式控制:

    • 支持暂停和恢复pallet操作
    • 通过事件通知状态变更
  4. 所有权管理:

    • 可配置的pallet所有者
    • 所有者可以转移控制权
  5. 权重计算:

    • 根据操作类型动态计算权重
    • 考虑存储读写成本

要集成此pallet到运行时,需要:

impl pallet_bridge_grandpa::Config for Runtime {
    type Event = Event;
    type BridgedChain = BridgedChain; // 你的桥接链类型
    type OwnerOrigin = EnsureRootOrHalfCouncil; // 你的所有权管理策略
}

这个实现提供了生产环境所需的核心功能,包括安全性检查、状态管理和可扩展性设计。


1 回复

Rust跨链桥接模块pallet-bridge-grandpa的使用指南

完整示例demo

以下是一个完整的跨链桥接实现示例,包含源链和目标链的配置:

源链配置示例

// 定义源链运行时
pub struct SourceRuntime;

// 定义桥接的目标链
pub struct TargetChain;

impl bp_header_chain::Chain for TargetChain {
    type BlockNumber = u32;
    type Hash = sp_core::H256;
    type Hasher = sp_runtime::traits::BlakeTwo256;
    type Header = sp_runtime::generic::Header<u32, sp_runtime::traits::BlakeTwo256>;
}

// 实现源链的桥接配置
impl pallet_bridge_grandpa::Config for SourceRuntime {
    type RuntimeEvent = RuntimeEvent;
    type BridgedChain = TargetChain;
    type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>;
    type HeadersToKeep = ConstU32<2048>;
    type WeightInfo = ();
}

// 初始化源链桥接
fn initialize_source_bridge() {
    let genesis_header = TargetChain::Header::new(
        0,
        Default::default(),
        Default::default(),
        Default::default(),
        Default::default()
    );
    
    let genesis_authority_set = sp_finality_grandpa::AuthorityList::new();
    
    BridgeGrandpa::initialize(
        RuntimeOrigin::root(),
        genesis_header,
        genesis_authority_set
    ).unwrap();
}

目标链配置示例

// 定义目标链运行时
pub struct TargetRuntime;

// 包含桥接模块
construct_runtime!(
    pub enum Runtime where
        Block = Block,
        NodeBlock = opaque::Block,
        UncheckedExtrinsic = UncheckedExtrinsic,
    {
        System: frame_system,
        // ... 其他模块
        BridgeGrandpa: pallet_bridge_grandpa,
    }
);

// 实现目标链的桥接配置
impl pallet_bridge_grandpa::Config for Runtime {
    type RuntimeEvent = RuntimeEvent;
    type BridgedChain = SourceChain;
    type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>;
    type HeadersToKeep = ConstU32<2048>;
    type WeightInfo = ();
}

// 初始化目标链桥接
fn initialize_target_bridge() {
    let genesis_header = SourceChain::Header::new(
        0,
        Default::default(),
        Default::default(),
        Default::default(),
        Default::default()
    );
    
    let genesis_authority_set = sp_finality_grandpa::AuthorityList::new();
    
    BridgeGrandpa::initialize(
        RuntimeOrigin::root(),
        genesis_header,
        genesis_authority_set
    ).unwrap();
}

跨链交易完整示例

// 源链上的跨链调用实现
fn send_cross_chain_transfer() {
    // 定义跨链调用
    let call = BridgeCall::transfer {
        recipient: target_chain_recipient,
        amount: 100,
    };
    
    // 发送跨链消息
    BridgeGrandpa::send_message(
        RuntimeOrigin::signed(sender),
        call.encode()
    ).unwrap();
    
    println!("跨链转账已发送");
}

// 目标链上的消息处理
impl pallet_bridge_messages::Config for Runtime {
    // 配置消息处理参数
}

// 处理接收到的跨链消息
fn handle_incoming_message(message: Vec<u8>) {
    if let Ok(call) = BridgeCall::decode(&mut &message[..]) {
        match call {
            BridgeCall::transfer { recipient, amount } => {
                // 执行转账逻辑
                Balances::deposit_creating(&recipient, amount);
                println!("收到跨链转账: {} 给 {}", amount, recipient);
            }
            _ => println!("未知的跨链调用"),
        }
    }
}

中继服务示例

// 中继器服务实现
struct RelayerService;

impl RelayerService {
    // 监听源链新区块
    fn listen_source_chain() {
        // 实现区块监听逻辑
    }
    
    // 提交头信息到目标链
    fn submit_header_to_target(
        header: BridgedHeader,
        justification: GrandpaJustification<BridgedHeader>
    ) {
        BridgeGrandpa::submit_finality_proof(
            RuntimeOrigin::signed(relayer_account),
            header,
            justification
        ).unwrap();
    }
    
    // 启动中继服务
    fn run() {
        loop {
            if let Some((header, justification)) = Self::get_new_finalized_header() {
                Self::submit_header_to_target(header, justification);
            }
            // 添加适当的延迟
            std::thread::sleep(std::time::Duration::from_secs(6));
        }
    }
}

部署说明

  1. 首先部署源链和目标链节点
  2. 在两边分别初始化桥接模块
  3. 启动中继器服务连接两条链
  4. 测试跨链交易功能

注意事项

  1. 确保源链和目标链的GRANDPA参数兼容
  2. 监控桥接状态和权威集更新
  3. 定期检查存储使用情况

这个完整示例展示了如何使用pallet-bridge-grandpa模块构建一个功能完整的跨链桥接解决方案,包含链配置、初始化、交易发送和接收以及中继服务等核心组件。

回到顶部