Hyperledger Fabric是由Linux基金会主导的一个开源企业级分布式账本框架,旨在为各行业提供模块化、可扩展、安全的解决方案,相较于公有链,Fabric的许可制特性、可插拔组件以及支持智能合约(链码)的多种编程语言,使其成为企业和开发者构建区块链应用的热门选择,本教程将带你一步步走进Hyperledger Fabric的开发世界,从环境搭建到应用部署,助你打下坚实基础。

Hyperledger Fabric 核心概念解析

在动手开发之前,理解Fabric的核心概念至关重要:

  1. 成员服务提供商 (MSP, Member Service Provider):负责定义和管理身份,包括用户、节点和组织,它确保了网络中参与者身份的真实性和合法性。
  2. 通道 (Channel):账本数据的隔离机制,每个通道是一个独立的账本,仅包含特定参与者(组织)的交易信息,实现了数据的隐私和保密。
  3. 排序服务 (Ordering Service):负责对来自不同通道的交易进行排序和打包,确保所有节点对交易顺序达成一致。
  4. 对等节点 (Peer):网络的基本组成部分,负责维护账本、执行链码、验证交易和响应查询,每个组织通常运行多个Peer节点。
  5. 链码 (Chaincode):运行在Peer节点上的智能合约,用于定义和操作账本上的资产(业务逻辑),可以是Go、Java或TypeScript编写。
  6. 账本 (Ledger):由两个部分组成:
    • 区块链 (Blockchain):按时间顺序记录交易数据的数据结构,一旦写入不可篡改。
    • 状态数据库 (State Database):存储当前资产状态的数据库,方便快速查询。
  7. 应用 (Application):与区块链网络交互的客户端应用,通过SDK调用链码功能,发起交易或查询账本。

开发环境准备

Fabric开发环境的搭建相对复杂,推荐使用官方提供的fabric-samplesFabric Docker镜像来简化流程。

  1. 基础软件安装

    • 操作系统:推荐Ubuntu 20.04 LTS (或CentOS 7+),也可在macOS和Windows上通过WSL2进行。
    • Docker:容器化运行环境,用于启动Fabric网络组件,请确保安装Docker和Docker Compose。
    • Go:链码主要开发语言之一,建议Go 1.18+。
    • Node.js & npm:用于开发基于Node.js的链码和应用程序,建议Node 14+ LTS。
    • Python (可选):如果使用Python链码。
    • Git:用于克隆代码仓库。
  2. Hyperledger Fabric Samples, Binaries and Docker Images

    • 克隆官方示例仓库:
      git clone https://github.com/hyperledger/fabric-samples.git
      cd fabric-samples
    • 下载二进制文件和Docker镜像(脚本会自动处理):
      cd first-network
      ./byfn.sh generate
      # 或者使用脚本下载最新版本
      curl -sSL https://bit.ly/2ysbOFE | bash -s -- 2.4.1 1.5.0
    • 将下载的二进制文件(peer, osnadmin, configtxgen, configtxlator等)添加到系统PATH环境变量中。

构建你的第一个Fabric网络:First Network示例

fabric-samples/first-network提供了一个“一键启动”的示例网络,包含两个组织(Org1和Org2)各两个Peer节点,一个排序服务,以及一个通道。

  1. 生成网络配置与证书

    • 进入first-network目录:
      cd fabric-samples/first-network
    • 运行generate.sh脚本生成必要的配置文件(crypto-config)和创世区块:
      ./byfn.sh generate
  2. 启动网络

    • 运行up.sh脚本启动网络并创建通道:
      ./byfn.sh up
    • 脚本会自动执行通道创建、Peer加入、锚点Peer更新以及链码安装、实例化和调用等操作,成功后,你会看到交易测试成功的输出。
  3. 关闭网络

    • 测试完成后,可运行down.sh脚本停止并删除容器、网络:
      ./byfn.sh down

开发自己的链码 (Chaincode)

链码是Fabric应用的核心逻辑,下面以Go语言为例,编写一个简单的“资产转移”链码。

  1. 链码结构

    • 通常包含Init(初始化)和Invoke(调用)两个主要函数。
    • Init用于初始化资产状态,Invoke用于执行具体的业务逻辑(如查询、创建、转移资产)。
  2. 示例链码 (asset-transfer-basic)

    • fabric-samples/chaincode/asset-transfer-basic/chaincode-go/目录下已有示例代码。
    • 我们可以基于此修改或创建新的链码文件,例如my_chaincode.go
    package main
    import (
        "fmt"
        "github.com/hyperledger/fabric-contract-api-go/contractapi"
    )
    // SmartContract provides functions for managing an Asset
    type SmartContract struct {
        contractapi.Contract
    }
    // Asset describes basic details of what makes up a simple asset
    type Asset struct {
        ID             string `json:"ID"`
        Owner          string `json:"owner"`
        Value          int    `json:"value"`
    }
    // InitLedger adds a base set of assets to the ledger
    func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
        assets := []Asset{
            {ID: "asset1", Owner: "Alice", Value: 100},
            {ID: "asset2", Owner: "Bob", Value: 200},
        }
        for _, asset := range assets {
            assetJSON, err := json.Marshal(asset)
            if err != nil {
                return err
            }
            err = ctx.GetStub().PutState(asset.ID, assetJSON)
            if err != nil {
                return fmt.Errorf("failed to put to world state. %v", err)
            }
        }
        return nil
    }
    // CreateAsset issues a new asset to the world state with given details
    func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, owner string, value int) error {
        exists, err := s.AssetExists(ctx, id)
        if err != nil {
            return err
        }
        if exists {
            return fmt.Errorf("the asset %s already exists", id)
        }
        asset := Asset{
            ID:    id,
            Owner: owner,
            Value: value,
        }
        assetJSON, err := json.Marshal(asset)
        if err != nil {
            return err
        }
        return ctx.GetStub().PutState(id, assetJSON)
    }
    // ReadAsset returns the asset stored in the world state with given id
    func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
        assetJSON, err := ctx.GetStub().GetState(id)
        if err != nil {
            return nil, fmt.Errorf("failed to read from world state: %v", err)
        }
        if assetJSON == nil {
            return nil, fmt.Errorf("the asset %s does not exist", id)
        }
        var asset Asset
        err = json.Unmarshal(assetJSON, &asset)
        if err != nil {
            return nil, err
        }
        return &asset, nil
    }
    // AssetExists returns true when asset with given ID exists in world state
    func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
        assetJSON, err := ctx.GetStub().GetState(id)
        if err != nil {
            return false, fmt.Errorf("failed to read from world state: %v", err)
        }
        return assetJSON != nil, nil
    }
    func main() {
        assetChaincode, err := contractapi.NewChaincode(&SmartContract{})
        if err != nil {
            fmt.Printf("Error creating asset chaincode: %v", err)
            return
        }
        if err := assetChaincode.Start(); err != nil {
            fmt.Printf("Error starting asset chaincode: %v", er
    随机配图
    r) } }
  3. 链码打包与安装

    • 打包:将链码代码打包为tar.gz文件。
      peer chaincode package mycc.tar.gz --path ./chaincode/asset-transfer-basic/chaincode-go/ --lang golang --label mycc_1