Rust区块链开发库sc-block-builder的使用:Substrate框架中的区块构建工具与链上功能扩展

Rust区块链开发库sc-block-builder的使用:Substrate框架中的区块构建工具与链上功能扩展

概述

sc-block-builder是Substrate框架中的一个重要工具库,提供了BlockBuilder实用工具和对应的运行时API。该库主要作为节点中对运行时API的抽象层,用于以下操作:

  • 初始化区块
  • 推送交易(extrinsics)
  • 完成区块

许可证

GPL-3.0-or-later WITH Classpath-exception-2.0

安装

在项目目录中运行以下Cargo命令:

cargo add sc-block-builder

或者在Cargo.toml中添加以下行:

sc-block-builder = "0.46.0"

使用示例

以下是使用sc-block-builder构建区块的基本示例:

use sc_block_builder::BlockBuilder;
use sp_api::ProvideRuntimeApi;
use sp_runtime::generic::BlockId;
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};

// 假设我们有以下类型定义
type Block = sp_runtime::generic::Block<Header, sp_runtime::OpaqueExtrinsic>;
type Header = sp_runtime::generic::Header<u32, sp_runtime::traits::BlakeTwo256>;

// 创建区块构建器
fn build_block<B, C>(
    client: &C,
    at: BlockId<B>,
) -> Result<(), sp_blockchain::Error>
where
    B: BlockT,
    C: ProvideRuntimeApi<B> + 'static,
    C::Api: sp_api::ApiExt<B> + sp_block_builder::BlockBuilder<B>,
{
    // 获取父区块哈希
    let parent_hash = client
        .runtime_api()
        .block_hash(&at, 0)?
        .expect("Genesis block always exists; qed");
        
    // 创建新的区块构建器
    let mut block_builder = BlockBuilder::new(
        client,
        at,
        parent_hash,
        Default::default(),
    )?;

    // 这里可以添加交易(extrinsics)
    // block_builder.push(extrinsic)?;

    // 完成区块构建
    let block = block_builder.build()?;
    
    println!("构建的新区块: {:?}", block);
    
    Ok(())
}

完整示例

下面是一个更完整的示例,展示如何使用sc-block-builder构建包含交易的区块:

use sc_block_builder::BlockBuilder;
use sp_api::{ProvideRuntimeApi, BlockId};
use sp_runtime::{
    generic::Block,
    traits::{BlakeTwo256, Block as BlockT, Header as HeaderT},
    OpaqueExtrinsic,
};

// 定义区块类型
type BlockNumber = u32;
type Header = sp_runtime::generic::Header<BlockNumber, BlakeTwo256>;
type TestBlock = Block<Header, OpaqueExtrinsic>;

// 模拟客户端
struct TestClient;

impl ProvideRuntimeApi<TestBlock> for TestClient {
    type Api = TestRuntimeApi;

    fn runtime_api(&self) -> &Self::Api {
        &TestRuntimeApi
    }
}

// 模拟运行时API
struct TestRuntimeApi;

impl sp_api::ApiExt<TestBlock> for TestRuntimeApi {
    fn map_api_result<F: FnOnce() -> Result<R, E>, R, E>(
        &self,
        f: F
    ) -> Result<R, E> {
        f()
    }
    
    fn record_proof(&mut self) {}
    
    fn extract_proof(&mut self) -> Option<sp_state_machine::StorageProof> {
        None
    }
}

impl sp_block_builder::BlockBuilder<TestBlock> for TestRuntimeApi {}

fn main() -> Result<(), sp_blockchain::Error> {
    let client = TestClient;
    let at = BlockId::Number(0);
    
    let parent_hash = BlakeTwo256::hash(&[0u8; 32]);
    
    // 创建区块构建器
    let mut block_builder = BlockBuilder::new(
        &client,
        at,
        parent_hash,
        Default::default(),
    )?;

    // 模拟添加交易
    let extrinsic = OpaqueExtrinsic::from(vec![1, 2, 3]);
    block_builder.push(extrinsic)?;

    // 构建区块
    let built_block = block_builder.build()?;
    
    println!("构建的区块头: {:?}", built_block.block.header);
    println!("包含的交易数量: {}", built_block.block.extrinsics.len());
    
    Ok(())
}

功能说明

  1. 区块初始化BlockBuilder::new()方法用于初始化一个新的区块构建器,需要提供客户端、区块ID、父区块哈希和其他参数。

  2. 添加交易:使用push()方法可以将交易(extrinsics)添加到区块中。

  3. 构建区块build()方法完成区块构建,返回包含区块头和交易的完整区块。

  4. 错误处理:所有操作都返回Result类型,可以处理可能出现的错误。


1 回复

以下是关于sc-block-builder的完整示例代码,基于您提供的内容整理:

use sc_block_builder::{BlockBuilder, BlockBuilderApi};
use sp_runtime::{generic::BlockId, traits::Block as BlockT};
use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;

// 自定义区块构建器实现
struct CustomBlockBuilder<Block: BlockT, Client> {
    inner: BlockBuilder<Block, Client>,
    custom_logs: Vec<String>,
}

impl<Block, Client> BlockBuilderApi<Block> for CustomBlockBuilder<Block, Client> 
where
    Block: BlockT,
    Client: ProvideRuntimeApi<Block> + HeaderBackend<Block> + Send + Sync + 'static,
{
    fn push(&mut self, extrinsic: Block::Extrinsic) -> Result<(), sp_blockchain::Error> {
        // 记录交易哈希到自定义日志
        let tx_hash = format!("{:?}", extrinsic.hash());
        self.custom_logs.push(format!("Added extrinsic: {}", tx_hash));
        
        // 调用内部构建器处理交易
        self.inner.push(extrinsic)
    }
    
    fn build(self) -> Result<sc_block_builder::BuiltBlock<Block>, sp_blockchain::Error> {
        // 构建前输出自定义日志
        println!("Block construction logs:");
        for log in &self.custom_logs {
            println!("- {}", log);
        }
        
        // 调用内部构建器构建区块
        self.inner.build()
    }
}

// 使用示例
fn build_custom_block<Block, Client>(
    client: &Client,
    parent_hash: Block::Hash,
    extrinsics: Vec<Block::Extrinsic>,
) -> Result<(), sp_blockchain::Error>
where
    Block: BlockT,
    Client: ProvideRuntimeApi<Block> + HeaderBackend<Block> + Send + Sync + 'static,
{
    // 创建基础区块构建器
    let block_builder = BlockBuilder::new(
        client,
        &BlockId::Hash(parent_hash),
        Default::default(), // inherent_data
        Default::default(), // inherent_digests
        false, // record_proof
        Default::default(), // inherent_digests
        &sp_state_machine::Backend::new(), // backend
    )?;

    // 包装成自定义构建器
    let mut custom_builder = CustomBlockBuilder {
        inner: block_builder,
        custom_logs: Vec::new(),
    };

    // 添加交易
    for extrinsic in extrinsics {
        custom_builder.push(extrinsic)?;
    }

    // 构建区块
    let built_block = custom_builder.build()?;
    println!("Block built: {:?}", built_block.block);

    Ok(())
}

这个完整示例展示了:

  1. 自定义CustomBlockBuilder结构体,包装了原始的BlockBuilder并添加了日志功能
  2. 实现了BlockBuilderApi trait的自定义逻辑
  3. 完整的区块构建流程:
    • 创建基础构建器
    • 添加交易(记录自定义日志)
    • 构建区块(输出构建日志)
  4. 类型安全约束确保兼容Substrate框架

使用说明:

  1. 将此代码放入Substrate节点的适当位置(如service.rs
  2. 确保正确传递client、parent_hash和extrinsics参数
  3. 可以通过扩展CustomBlockBuilder添加更多自定义逻辑

这个示例保持了与Substrate框架的兼容性,同时展示了如何通过sc-block-builder扩展区块构建功能。

回到顶部