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, ¤t_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
}
}
这个完整实现包含以下关键改进:
-
完善的GRANDPA justification验证:
- 使用
bp_header_chain
提供的验证函数 - 处理不同的验证错误类型
- 支持验证者集合变更
- 使用
-
验证者集合管理:
- 存储当前验证者集合和set_id
- 自动检测并更新mandatory headers中的新验证者集合
- 发出验证者集合变更事件
-
操作模式控制:
- 支持暂停和恢复pallet操作
- 通过事件通知状态变更
-
所有权管理:
- 可配置的pallet所有者
- 所有者可以转移控制权
-
权重计算:
- 根据操作类型动态计算权重
- 考虑存储读写成本
要集成此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));
}
}
}
部署说明
- 首先部署源链和目标链节点
- 在两边分别初始化桥接模块
- 启动中继器服务连接两条链
- 测试跨链交易功能
注意事项
- 确保源链和目标链的GRANDPA参数兼容
- 监控桥接状态和权威集更新
- 定期检查存储使用情况
这个完整示例展示了如何使用pallet-bridge-grandpa模块构建一个功能完整的跨链桥接解决方案,包含链配置、初始化、交易发送和接收以及中继服务等核心组件。