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

关键功能

  1. 纪元管理:创建和管理区块链的不同纪元阶段
  2. 纪元过渡:平滑处理纪元之间的过渡
  3. 可配置性:允许自定义纪元长度和参数
  4. 线程安全:通过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!");
}

代码说明

  1. 自定义纪元结构

    • 实现了TestEpoch结构体,包含起始slot、结束slot和验证人列表
    • TestEpoch实现了Epoch trait,定义了纪元切换逻辑
  2. 纪元边界检测

    • 实现了CustomEpochBoundary结构体用于检测纪元边界
    • 采用每200个区块作为一个纪元边界的简单逻辑
  3. 节点集成

    • Node结构体封装了纪元相关的核心逻辑
    • 提供了区块导入处理和纪元变更回调设置方法
    • 使用SharedEpochChanges实现线程安全的纪元状态共享
  4. 持久化存储

    • 使用PersistentEpochHeader实现纪元数据的持久化存储
    • 实际应用中需要传入真实的后端存储实例

注意事项

  1. 此示例展示了核心集成逻辑,实际使用时需要根据具体区块链需求调整:

    • 纪元长度和slot时间
    • 验证人选择逻辑
    • 纪元边界检测算法
  2. 生产环境实现还应考虑:

    • 错误处理
    • 性能优化
    • 与其他共识组件的集成
  3. 测试时应验证:

    • 纪元切换的正确性
    • 验证人轮换的准确性
    • 边界条件下的行为
回到顶部