Rust区块链共识引擎库sc-consensus-epochs的使用,实现Substrate框架下的可配置分时纪元机制
Rust区块链共识引擎库sc-consensus-epochs的使用,实现Substrate框架下的可配置分时纪元机制
概述
sc-consensus-epochs
是用于基于纪元的共识引擎的通用工具库。它提供了在Substrate框架中实现可配置分时纪元机制的基础设施。
安装
在项目目录中运行以下Cargo命令:
cargo add sc-consensus-epochs
或者在Cargo.toml中添加:
sc-consensus-epochs = "0.50.0"
示例代码
以下是一个使用sc-consensus-epochs
实现可配置分时纪元机制的完整示例:
use sc_consensus_epochs::{Epoch, epoch_changes::EpochChanges, SharedEpochChanges};
use sp_consensus::Error as ConsensusError;
use sp_runtime::traits::{Block as BlockT, NumberFor};
use std::sync::Arc;
// 定义纪元描述符结构
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct EpochDescriptor<B: BlockT> {
pub epoch_start: NumberFor<B>,
pub epoch_end: NumberFor<B>,
pub epoch_duration: NumberFor<B>,
}
// 实现纪元变化跟踪器
pub struct EpochTracker<B: BlockT, E> {
epoch_changes: SharedEpochChanges<B, E>,
}
impl<B: BlockT, E> EpochTracker<B, E> {
// 创建新的纪元跟踪器
pub fn new() -> Self {
Self {
epoch_changes: SharedEpochChanges::new(),
}
}
// 添加新纪元
pub fn add_epoch(
&self,
epoch_start: NumberFor<B>,
epoch_end: NumberFor<B>,
epoch_duration: NumberFor<B>,
epoch_data: E,
) -> Result<(), ConsensusError> {
let descriptor = EpochDescriptor {
epoch_start,
epoch_end,
epoch_duration,
};
let mut epoch_changes = self.epoch_changes.shared_data();
epoch_changes.import(
descriptor,
epoch_start,
epoch_data,
)?;
Ok(())
}
// 获取当前纪元
pub fn current_epoch(&self, block_number: NumberFor<B>) -> Option<Epoch<E>> {
let epoch_changes = self.epoch_changes.shared_data();
epoch_changes.epoch_data(&block_number, |slot| slot)
}
}
// 使用示例
fn main() {
type BlockNumber = u64;
type TestEpochData = String;
let epoch_tracker = EpochTracker::<BlockNumber, TestEpochData>::new();
// 添加纪元配置
epoch_tracker.add_epoch(
0, // 纪元开始区块
100, // 纪元结束区块
10, // 纪元持续时间(区块数量)
"First Epoch".to_string(), // 纪元数据
).unwrap();
// 查询纪元信息
if let Some(epoch) = epoch_tracker.current_epoch(50) {
println!("Current epoch data: {}", epoch.data);
println!("Epoch start: {}", epoch.start_slot);
println!("Epoch end: {}", epoch.end_slot);
}
}
完整示例扩展
下面是一个更完整的示例,展示了如何在Substrate运行时中使用sc-consensus-epochs
:
use sc_consensus_epochs::{
Epoch, epoch_changes::{EpochChanges, EpochChangesFor},
SharedEpochChanges, EpochHeader
};
use sp_consensus::{Error as ConsensusError, SelectChain};
use sp_runtime::{
traits::{Block as BlockT, NumberFor, Zero},
generic::BlockId,
};
use std::{sync::Arc, marker::PhantomData};
// 自定义纪元数据
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct CustomEpochData {
pub validator_set: Vec<String>,
pub randomness: [u8; 32],
}
// 纪元管理器
pub struct EpochManager<B: BlockT, C> {
epoch_changes: SharedEpochChanges<B, CustomEpochData>,
client: Arc<C>,
_phantom: PhantomData<B>,
}
impl<B: BlockT, C> EpochManager<B, C>
where
C: SelectChain<B>,
{
pub fn new(client: Arc<C>) -> Self {
Self {
epoch_changes: SharedEpochChanges::new(),
client,
_phantom: PhantomData,
}
}
// 初始化创世纪元
pub fn initialize_genesis_epoch(
&self,
epoch_data: CustomEpochData,
) -> Result<(), ConsensusError> {
let mut epoch_changes = self.epoch_changes.shared_data();
epoch_changes.import(
EpochHeader {
start_slot: Zero::zero(),
end_slot: Zero::zero(),
},
Zero::zero(),
epoch_data,
)?;
Ok(())
}
// 获取或创建新纪元
pub fn get_or_create_epoch(
&self,
parent_hash: B::Hash,
slot_number: NumberFor<B>,
) -> Result<Epoch<CustomEpochData>, ConsensusError> {
let epoch_changes = self.epoch_changes.shared_data();
let chain_head = self.client.best_chain()?;
epoch_changes.epoch_data_for_child_of(
&parent_hash,
&chain_head,
slot_number,
|slot| slot,
)
}
// 添加新纪元配置
pub fn add_epoch_configuration(
&self,
start_slot: NumberFor<B>,
end_slot: NumberFor<B>,
epoch_data: CustomEpochData,
) -> Result<(), ConsensusError> {
let mut epoch_changes = self.epoch_changes.shared_data();
epoch_changes.import(
EpochHeader {
start_slot,
end_slot,
},
start_slot,
epoch_data,
)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use sc_consensus::LongestChain;
use sp_core::H256;
use substrate_test_runtime_client::{
runtime::{Block, Header},
TestClientBuilder, TestClientBuilderExt,
};
#[test]
fn test_epoch_management() {
let builder = TestClientBuilder::new();
let client = Arc::new(builder.build());
let select_chain = LongestChain::new(client.backend());
let epoch_manager = EpochManager::<Block, _>::new(Arc::new(select_chain));
let genesis_data = CustomEpochData {
validator_set: vec!["Alice".into(), "Bob".into()],
randomness: [0u8; 32],
};
epoch_manager.initialize_genesis_epoch(genesis_data.clone()).unwrap();
// 添加新纪元配置
let new_epoch_data = CustomEpochData {
validator_set: vec!["Charlie".into(), "Dave".into()],
randomness: [1u8; 32],
};
epoch_manager.add_epoch_configuration(1, 100, new_epoch_data.clone()).unwrap();
// 获取纪元信息
let header = Header {
parent_hash: H256::zero(),
number: 1,
state_root: H256::zero(),
extrinsics_root: H256::zero(),
digest: Default::default(),
};
let epoch = epoch_manager.get_or_create_epoch(header.hash(), 1).unwrap();
assert_eq!(epoch.data.validator_set, new_epoch_data.validator_set);
}
}
关键功能
- 纪元管理:创建和管理区块链的不同纪元阶段
- 纪元过渡:平滑处理纪元之间的过渡
- 可配置性:允许自定义纪元长度和参数
- 线程安全:通过
SharedEpochChanges
提供线程安全的纪元访问
许可证
该库采用GPL-3.0-or-later许可证,并带有Classpath-exception-2.0附加条款。
1 回复
Rust区块链共识引擎库sc-consensus-epochs使用指南
概述
sc-consensus-epochs
是Substrate框架中用于实现可配置分时纪元机制的库,它为区块链共识提供了一种基于时间段的划分方式,允许在不同的纪元(epoch)中使用不同的共识参数或验证人集合。
主要特性
- 实现基于时间的纪元划分机制
- 支持可配置的纪元持续时间
- 提供纪元切换时的验证人集合轮换
- 与Substrate共识机制无缝集成
完整示例Demo
下面是一个完整的Substrate节点集成sc-consensus-epochs的示例:
//! 完整示例:Substrate节点集成sc-consensus-epochs
use sc_consensus_epochs::{
Epoch, EpochChanges, SharedEpochChanges, EpochChangesFor,
EpochSlot, descendent_query, digests, Config, EpochIncrement,
EpochHeader, IsEpochBoundary, PersistentEpochHeader
};
use sp_consensus::{SelectChain, BlockImport, import_queue::BasicQueue, Error as ConsensusError};
use sp_runtime::{
traits::{Block as BlockT, Header as HeaderT, NumberFor},
generic::DigestItem
};
use substrate_test_runtime_client::runtime::{Block, Hash};
use sc_client_api::backend::Backend;
// 1. 定义自定义纪元结构
#[derive(Clone)]
struct TestEpoch {
start_slot: u64,
end_slot: u64,
validators: Vec<String>,
}
impl Epoch for TestEpoch {
type NextEpochDescriptor = Vec<String>; // 使用验证人列表作为描述符
type SlotNumber = u64;
fn increment(
&self,
descriptor: Self::NextEpochDescriptor,
) -> Result<Self, ConsensusError> {
Ok(TestEpoch {
start_slot: self.end_slot + 1,
end_slot: self.end_slot + 100, // 固定100个slot的纪元长度
validators: descriptor,
})
}
fn start_slot(&self) -> Self::SlotNumber {
self.start_slot
}
fn end_slot(&self) -> Self::SlotNumber {
self.end_slot
}
}
// 2. 自定义纪元边界检测器
struct CustomEpochBoundary;
impl<B: BlockT> IsEpochBoundary<B> for CustomEpochBoundary {
fn is_epoch_boundary(
&self,
header: &B::Header,
) -> Option<EpochHeader<B::Hash, NumberFor<B>>> {
// 每200个区块作为一个纪元边界
if header.number() % 200 == 0 {
Some(EpochHeader {
start_slot: 0,
end_slot: 100,
block_hash: header.hash(),
block_number: *header.number(),
})
} else {
None
}
}
}
// 3. 主要节点集成逻辑
struct Node<E: Epoch> {
epoch_changes: SharedEpochChanges<(), Block, E>,
}
impl<E: Epoch<SlotNumber = u64> + 'static> Node<E> {
fn new() -> Self {
// 初始化纪元配置
let config = Config {
epoch_duration: 100, // 100个slot/纪元
slot_duration: 6, // 6秒/slot
randomness: [0u8; 32],
};
// 创建持久化存储
let persistent_epoch_header = PersistentEpochHeader::new(
"test_epoch_data",
// 实际应用中应传入真实的后端存储
&mut () as &mut dyn Backend<Block>
);
// 初始化纪元变化跟踪器
let epoch_changes = EpochChanges::new_with_boundary(
CustomEpochBoundary,
persistent_epoch_header,
);
Node {
epoch_changes: SharedEpochChanges::new(epoch_changes),
}
}
// 处理区块导入逻辑
fn import_block(
&self,
block: Block,
import: &mut dyn BlockImport<Block>,
) -> Result<(), ConsensusError> {
// 检查纪元变更摘要
if let Some(epoch_increment) = find_epoch_increment_digest(&block) {
self.epoch_changes.lock().import(
*block.header().parent_hash(),
epoch_increment,
)?;
}
// 导入区块
import.import_block(block, Default::default())
}
// 设置纪元变更回调
fn setup_epoch_change_handler(&self) {
self.epoch_changes.lock().on_epoch_change(
|epoch| {
// 返回新的验证人集合
Ok(epoch.into_inner().validators)
},
);
}
}
// 查找纪元变更摘要的辅助函数
fn find_epoch_increment_digest(block: &Block) -> Option<EpochIncrement<Vec<String>>> {
block.header().digest().logs.iter().find_map(|log| {
match log {
DigestItem::Consensus(id, data) if id == &digests::EPOCH_CHANGE_ID => {
EpochIncrement::decode(&mut &data[..]).ok()
}
_ => None,
}
})
}
// 示例使用
fn main() {
// 创建节点实例
let node = Node::<TestEpoch>::new();
// 设置纪元变更处理器
node.setup_epoch_change_handler();
// 这里可以添加实际的区块导入逻辑
println!("Node with epoch consensus initialized!");
}
代码说明
-
自定义纪元结构:
- 实现了
TestEpoch
结构体,包含起始slot、结束slot和验证人列表 - 为
TestEpoch
实现了Epoch
trait,定义了纪元切换逻辑
- 实现了
-
纪元边界检测:
- 实现了
CustomEpochBoundary
结构体用于检测纪元边界 - 采用每200个区块作为一个纪元边界的简单逻辑
- 实现了
-
节点集成:
Node
结构体封装了纪元相关的核心逻辑- 提供了区块导入处理和纪元变更回调设置方法
- 使用
SharedEpochChanges
实现线程安全的纪元状态共享
-
持久化存储:
- 使用
PersistentEpochHeader
实现纪元数据的持久化存储 - 实际应用中需要传入真实的后端存储实例
- 使用
注意事项
-
此示例展示了核心集成逻辑,实际使用时需要根据具体区块链需求调整:
- 纪元长度和slot时间
- 验证人选择逻辑
- 纪元边界检测算法
-
生产环境实现还应考虑:
- 错误处理
- 性能优化
- 与其他共识组件的集成
-
测试时应验证:
- 纪元切换的正确性
- 验证人轮换的准确性
- 边界条件下的行为