Rust区块链共识引擎sp-consensus-babe的使用:Substrate框架中的BABE插槽出块协议实现
BABE的基础组件。
许可证:Apache-2.0
// 示例:BABE共识引擎的基本使用
use sp_consensus_babe::{BabeApi, BabeConfiguration};
use sp_runtime::traits::Block as BlockT;
// 初始化BABE配置
fn init_babe_config() -> BabeConfiguration {
BabeConfiguration {
slot_duration: 1000, // 每个插槽的持续时间(毫秒)
epoch_length: 100, // 每个epoch的插槽数量
c: (1, 4), // 相对权重参数
genesis_authorities: vec![], // 初始验证人集合
randomness: [0u8; 32], // 随机性值
allowed_slots: sp_consensus_babe::AllowedSlots::PrimaryAndSecondaryPlainSlots,
}
}
// 创建BABE区块导入器
fn create_babe_block_import() {
// 实现区块导入逻辑
}
// 验证BABE区块
fn verify_babe_block() {
// 实现区块验证逻辑
}
完整示例demo:
// 完整BABE共识引擎集成示例
use sp_consensus_babe::{
BabeApi, BabeConfiguration, BabeGenesisConfiguration,
AuthorityId, digests::CompatibleDigestItem
};
use sp_runtime::{traits::Block as BlockT, generic::BlockId};
use sc_client_api::backend::Backend;
use sc_consensus_babe::{BabeBlockImport, BabeLink};
use std::sync::Arc;
// BABE共识引擎配置结构
pub struct BabeConsensusConfig<B, C, BE>
where
B: BlockT,
C: ProvideRuntimeApi<B> + Send + Sync,
C::Api: BabeApi<B>,
BE: Backend<B>,
{
pub client: Arc<C>,
pub backend: Arc<BE>,
pub babe_config: BabeConfiguration,
}
// 初始化BABE共识引擎
pub fn init_babe_consensus<B, C, BE>(
config: BabeConsensusConfig<B, C, BE>,
) -> Result<(BabeBlockImport<B, C, BE>, BabeLink<B>), sp_consensus::Error>
where
B: BlockT,
C: ProvideRuntimeApi<B> + Send + Sync + 'static,
C::Api: BabeApi<B>,
BE: Backend<B> + 'static,
{
let BabeConsensusConfig {
client,
backend,
babe_config,
} = config;
// 创建BABE区块导入器
let (block_import, babe_link) = sc_consensus_babe::block_import(
babe_config,
client.clone(),
client,
backend,
)?;
Ok((block_import, babe_link))
}
// BABE区块生产函数
pub async fn produce_babe_blocks<B, C>(
babe_link: BabeLink<B>,
client: Arc<C>,
) where
B: BlockT,
C: ProvideRuntimeApi<B> + Send + Sync,
C::Api: BabeApi<B>,
{
// 获取当前epoch信息
let epoch = babe_link.epoch_changes().shared_data().current_epoch();
// 检查是否为验证人
if let Some(authority_id) = get_local_authority_id() {
// 生产区块逻辑
let slot_info = babe_link.slot_notifier().slot_info();
// 在此处实现区块生产和签名逻辑
}
}
// 辅助函数:获取本地验证人ID
fn get_local_authority_id() -> Option<AuthorityId> {
// 实现获取本地验证人密钥的逻辑
None
}
// BABE区块验证函数
pub fn verify_babe_block<B: BlockT>(
block: &B,
epoch: &sp_consensus_babe::Epoch,
) -> Result<(), sp_consensus::Error> {
// 提取BABE预处理信息
let pre_digest = sp_consensus_babe::find_pre_digest::<B>(block)
.ok_or(sp_consensus::Error::InvalidAuthoritiesSet)?;
// 验证区块签名
sp_consensus_babe::check_primary_header::<B>(
pre_digest,
epoch,
block.hash(),
)?;
Ok(())
}
// 主函数示例
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 初始化BABE配置
let babe_config = BabeConfiguration {
slot_duration: 6000, // 6秒插槽
epoch_length: 200, // 200个插槽的epoch
c: (1, 4), // 相对权重
genesis_authorities: vec![],
randomness: [0u8; 32],
allowed_slots: sp_consensus_babe::AllowedSlots::PrimaryAndSecondaryPlainSlots,
};
println!("BABE共识引擎初始化完成");
println!("插槽持续时间: {}毫秒", babe_config.slot_duration);
println!("Epoch长度: {}个插槽", babe_config.epoch_length);
Ok(())
}
1 回复
Rust区块链共识引擎sp-consensus-babe使用指南
概述
sp-consensus-babe是Substrate框架中实现BABE(Blind Assignment for Blockchain Extension)插槽出块协议的核心组件。该协议允许验证人基于可验证随机函数(VRF)随机获得出块权,实现高效且公平的区块生产。
核心特性
- 基于VRF的随机出块权分配
- 可配置的时间槽(slot)机制
- 支持权重调整和epoch转换
- 与Substrate共识框架无缝集成
使用方法
1. 基本配置
use sp_consensus_babe::{BabeBlockImport, BabeLink};
use sc_consensus_babe::{Config, start_babe};
use sc_client_api::Backend;
// 配置BABE参数
let babe_config = Config {
keystore: keystore_container.sync_keystore(),
is_authority: role.is_authority(),
can_author_with: sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone()),
block_import: Box::new(block_import),
env: proposer,
sync_oracle: network.clone(),
force_authoring: false,
backoff_authoring_blocks: Some(BackoffAuthoringOnFinalizedHeadLagging::default()),
babe_link: babe_link,
block_proposal_slot_portion: SlotProportion::new(0.5),
max_block_proposal_slot_portion: None,
telemetry: telemetry.as_ref().map(|x| x.handle()),
};
2. 启动BABE引擎
let babe_worker = sc_consensus_babe::start_babe(babe_config)?;
// 注册到服务构建器
task_manager.spawn_essential_handle().spawn(
"babe-worker",
Some("consensus"),
babe_worker,
);
3. 定义BABE运行时API
impl sp_consensus_babe::BabeApi<Block> for Runtime {
fn configuration() -> sp_consensus_babe::BabeConfiguration {
sp_consensus_babe::BabeConfiguration {
slot_duration: SLOT_DURATION,
epoch_length: EpochDuration::get(),
c: PRIMARY_PROBABILITY,
allowed_slots: sp_consensus_babe::AllowedSlots::PrimaryAndSecondaryPlainSlots,
}
}
fn current_epoch_start() -> sp_consensus_babe::Slot {
Babe::current_epoch_start()
}
fn current_epoch() -> sp_consensus_babe::Epoch {
Babe::current_epoch()
}
fn next_epoch() -> sp_consensus_babe::Epoch {
Babe::next_epoch()
}
fn generate_key_ownership_proof(
_slot: sp_consensus_babe::Slot,
_authority_id: sp_consensus_babe::AuthorityId,
) -> Option<sp_consensus_babe::OpaqueKeyOwnershipProof> {
None
}
fn submit_report_equivocation_unsigned_extrinsic(
_equivocation_proof: sp_consensus_babe::EquivocationProof<<Block as BlockT>::Header>,
_key_ownership_proof: sp_consensus_babe::OpaqueKeyOwnershipProof,
) -> Option<()> {
None
}
}
4. 处理出块逻辑
// 在区块导入器中集成BABE
let import_queue = sc_consensus_babe::import_queue(
babe_link,
block_import,
Some(Box::new(justification_import)),
client.clone(),
select_chain,
move |_, ()| async move {
let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
Ok(timestamp)
},
&task_manager.spawn_essential_handle(),
config.prometheus_registry(),
telemetry.as_ref().map(|x| x.handle()),
)?;
5. 验证人配置示例
// 设置验证人密钥
use sp_application_crypto::Pair;
use sp_consensus_babe::{AuthorityPair, AuthorityId};
let pair: AuthorityPair = /* 从keystore获取或生成 */
let authority_id: AuthorityId = pair.public();
// 在链上注册验证人
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::weight(10_000)]
pub fn register_validator(origin: OriginFor<T>) -> DispatchResult {
let who = ensure_signed(origin)?;
// 获取BABE公钥
let babe_key: AuthorityId = /* 从keystore获取 */
Validators::<T>::insert(&who, babe_key);
Ok(())
}
}
关键数据结构
Babe区块头扩展
use sp_consensus_babe::digests::{PreDigest, CompatibleDigestItem};
// 创建包含BABE信息的区块头
let pre_digest = PreDigest::Primary {
authority_index: authority_index,
slot: slot,
vrf_output: vrf_output,
vrf_proof: vrf_proof,
};
let digest_item = DigestItem::babe_pre_digest(pre_digest);
Epoch管理
// 处理epoch转换
fn on_epoch_change(
epoch_start: Slot,
epoch_end: Slot,
authorities: &[(AuthorityId, BabeAuthorityWeight)],
) -> Result<(), Error> {
// 实现epoch转换逻辑
Ok(())
}
注意事项
- 时间同步:验证人需要保持准确的时间同步以确保插槽机制正常工作
- 密钥安全:BABE密钥需要安全存储,建议使用硬件安全模块(HSM)
- 网络连接:确保良好的网络连接以减少区块传播延迟
- 监控指标:实现监控以跟踪出块性能和验证人活动
故障排除
常见问题包括:
- 时间槽错过:检查系统时间同步
- VRG生成失败:验证密钥配置
- 区块导入失败:检查运行时兼容性
通过正确配置和使用sp-consensus-babe,可以在Substrate区块链中实现高效、安全的BABE共识机制。
完整示例demo
//! 完整的BABE共识引擎集成示例
use sc_consensus_babe::{Config, start_babe, import_queue};
use sp_consensus_babe::{BabeApi, BabeConfiguration, AuthorityPair, AuthorityId};
use sc_client_api::Backend;
use sp_application_crypto::Pair;
use sp_runtime::traits::Block as BlockT;
use sc_telemetry::TelemetryHandle;
use sc_network::NetworkService;
use sc_keystore::LocalKeystore;
use sc_service::{TaskManager, Configuration};
use std::sync::Arc;
/// BABE配置结构体
pub struct BabeConsensusConfig {
/// 客户端实例
client: Arc<dyn sc_client_api::BlockBackend<Block>>,
/// 选择链策略
select_chain: sc_consensus::LongestChain<Backend, Block>,
/// 区块导入器
block_import: Box<dyn sc_consensus::BlockImport<Block>>,
/// 网络服务
network: Arc<NetworkService<Block, <Block as BlockT>::Hash>>,
/// 任务管理器
task_manager: &mut TaskManager,
/// 配置参数
config: &Configuration,
/// 遥测句柄
telemetry: Option<TelemetryHandle>,
}
/// 启动BABE共识引擎
pub fn start_babe_consensus(
babe_config: BabeConsensusConfig,
) -> Result<(), sc_service::Error> {
let BabeConsensusConfig {
client,
select_chain,
block_import,
network,
task_manager,
config,
telemetry,
} = babe_config;
// 创建keystore
let keystore = Arc::new(LocalKeystore::in_memory());
let keystore_container = sc_keystore::KeystoreContainer::new(keystore);
// 配置BABE参数
let babe_config = Config {
keystore: keystore_container.sync_keystore(),
is_authority: config.role.is_authority(),
can_author_with: sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone()),
block_import: Box::new(block_import),
env: proposer, // 需要实现proposer逻辑
sync_oracle: network.clone(),
force_authoring: false,
backoff_authoring_blocks: Some(BackoffAuthoringOnFinalizedHeadLagging::default()),
babe_link: babe_link, // 需要创建babe_link
block_proposal_slot_portion: SlotProportion::new(0.5),
max_block_proposal_slot_portion: None,
telemetry: telemetry.as_ref().map(|x| x.handle()),
};
// 启动BABE工作器
let babe_worker = sc_consensus_babe::start_babe(babe_config)?;
// 注册BABE工作器到任务管理器
task_manager.spawn_essential_handle().spawn(
"babe-worker",
Some("consensus"),
babe_worker,
);
// 创建BABE导入队列
let import_queue = import_queue(
babe_link,
block_import,
None, // justification_import
client.clone(),
select_chain,
move |_, ()| async move {
let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
Ok(timestamp)
},
&task_manager.spawn_essential_handle(),
config.prometheus_registry(),
telemetry.as_ref().map(|x| x.handle()),
)?;
Ok(())
}
/// BABE运行时API实现
#[cfg(feature = "runtime")]
mod runtime_api {
use super::*;
use sp_consensus_babe::{BabeApi, BabeConfiguration, Slot, Epoch};
use sp_runtime::traits::Block as BlockT;
impl BabeApi<Block> for Runtime {
fn configuration() -> BabeConfiguration {
BabeConfiguration {
slot_duration: SLOT_DURATION,
epoch_length: EpochDuration::get(),
c: PRIMARY_PROBABILITY,
allowed_slots: sp_consensus_babe::AllowedSlots::PrimaryAndSecondaryPlainSlots,
}
}
fn current_epoch_start() -> Slot {
Babe::current_epoch_start()
}
fn current_epoch() -> Epoch {
Babe::current_epoch()
}
fn next_epoch() -> Epoch {
Babe::next_epoch()
}
fn generate_key_ownership_proof(
_slot: Slot,
_authority_id: AuthorityId,
) -> Option<sp_consensus_babe::OpaqueKeyOwnershipProof> {
None
}
fn submit_report_equivocation_unsigned_extrinsic(
_equivocation_proof: sp_consensus_babe::EquivocationProof<<Block as BlockT>::Header>,
_key_ownership_proof: sp_consensus_babe::OpaqueKeyOwnershipProof,
) -> Option<()> {
None
}
}
}
/// 验证人注册pallet示例
#[cfg(feature = "runtime")]
mod validator_pallet {
use frame_support::{pallet, dispatch::DispatchResult};
use sp_consensus_babe::AuthorityId;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::storage]
#[pallet::getter(fn validators)]
pub type Validators<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId, AuthorityId>;
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::weight(10_000)]
pub fn register_validator(origin: OriginFor<T>) -> DispatchResult {
let who = ensure_signed(origin)?;
// 从keystore获取BABE公钥
let babe_key: AuthorityId = /* 实现keystore访问逻辑 */;
Validators::<T>::insert(&who, babe_key);
Ok(())
}
}
}
/// 区块头创建示例
fn create_babe_block_header() {
use sp_consensus_babe::digests::{PreDigest, CompatibleDigestItem};
use sp_runtime::DigestItem;
// 创建BABE预摘要
let pre_digest = PreDigest::Primary {
authority_index: 0, // 验证人索引
slot: 12345, // 时间槽
vrf_output: [0u8; 32], // VRF输出
vrf_proof: [0u8; 64], // VRF证明
};
// 创建摘要项
let digest_item = DigestItem::babe_pre_digest(pre_digest);
// 将摘要项添加到区块头
let mut header = sp_runtime::generic::Header::new(
1, // 区块号
Default::default(), // 父哈希
Default::default(), // 状态根
Default::default(), // 交易根
Default::default(), // 收据根
sp_runtime::Digest { logs: vec![digest_item] }, // 摘要
);
}
/// Epoch转换处理示例
fn handle_epoch_change() -> Result<(), sp_consensus::Error> {
use sp_consensus_babe::{Slot, AuthorityId, BabeAuthorityWeight};
fn on_epoch_change(
epoch_start: Slot,
epoch_end: Slot,
authorities: &[(AuthorityId, BabeAuthorityWeight)],
) -> Result<(), sp_consensus::Error> {
// 实现epoch转换逻辑
// 例如:更新验证人集合、调整权重等
log::info!("Epoch changed from {} to {}", epoch_start, epoch_end);
log::info!("Authorities: {:?}", authorities);
Ok(())
}
on_epoch_change(1000, 2000, &[(AuthorityId::default(), 1)])
}
/// 主函数示例
fn main() -> Result<(), sc_service::Error> {
// 初始化日志
env_logger::init();
// 创建配置(实际使用时需要从配置文件或命令行参数加载)
let config = Configuration::default();
// 创建任务管理器
let mut task_manager = TaskManager::new(config.tokio_handle.clone())?;
// 启动BABE共识引擎
start_babe_consensus(BabeConsensusConfig {
client: Arc::new(create_client()?), // 需要实现create_client
select_chain: sc_consensus::LongestChain::new(backend.clone()),
block_import: Box::new(create_block_import()?), // 需要实现create_block_import
network: Arc::new(create_network_service()?), // 需要实现create_network_service
task_manager: &mut task_manager,
config: &config,
telemetry: None,
})?;
// 运行任务管理器
task_manager.run()?;
Ok(())
}