一文上手当前热门区块链! Hyperledger Fabric 区块链实践教学

1,435 阅读2分钟

阅读完本文,你将可以:

至少部署三个节点构建完成FABRIC区块链

通过GO语言开发,实现自定义的数据提交到链,并能查询交易的数据

如果本文对你有用,麻烦请给我点个赞👍,这是对我很大的鼓励!

大致简介

文章有三个部分

  • 第一个部分是搭建Fabric节点网络;
  • 第二部分是学习使用GO语言开发链码;
  • 第三部分是学习在区块链网络上部署链码。

自身理解

  • Fabric使用test-network和docker-compose技术来搭建,比较方便
  • Go语言链码开发,需要仔细阅读开发文档,弄清楚链码接口的实现。
  • test-network部署链码比较方便。

技术路线

  • Docker

Docker是一种虚拟化容器技术,可以创建轻量级容器,将应用包及其依赖打包发布

  • Fabric

Hyperledger Fabric是IBM推出的一种新型联盟链,其不以算力竞争来产生新区块。

  • Golang

Golang是Google推出的新兴的编程语言

系统平台

  • Linux系统版本:Ubuntu 20.04 64位
  • Docker版本: Docker version 20.10.12, build 20.10.12-0ubuntu2~20.04.1
  • Go语言版本: go1.18.2 linux/arm64
  • GoLand版本:2021.2.4

实现过程

Fabric网络搭建

安装docker和docker-compose

 apt update
 apt install docker.io -y
 apt install docker-compose -y

确认Go版本

如果有旧版本的go,先卸载

安装1.18版本的golang

 wget https://go.dev/dl/go1.18.2.linux-amd64.tar.gz
 tar -C /usr/local -xzf go1.18.2.linux-amd64.tar.gz
 vim /etc/profile
 # 在文件里添加下面这一行(如果没有)
 export PATH=$PATH:/usr/local/go/bin
 ​
 source /etc/profile

Fabric的下载

由于国内云服务器访问github不稳定,所以从镜像源hub.fastgit.xyz拉取项目

git clone https://hub.fastgit.xyz/hyperledger/fabric.git

拉取完之后,本地出现fabric项目

Fabric-samples的拉取

在 fabric/scripts下有一脚本bootstrap.sh,用于下载fabric-samples测试样例

  • 因为脚本当中还是从GitHub拉取项目,所以依然需要改为镜像源
  • 一共有三处,不能改少了
  • 除了拉取项目以外,这个脚本还会下载测试网络需要的docker images

编辑脚本vim bootstrap.sh

把所以github.com都改成hub.fastgit.xyz

保存退出文件之后,. bootstrap.sh 执行脚本,下载fabric-samples

test-network的基本操作

  1. 进入fabric-samples/test-network 目录

  2. 使用./network.sh up启动网络

image.png

看到三个节点容器,网络搭建起来了

3. 使用./network.sh down关闭网络

  1. 在启动指令后加createChannel参数, 创建一个通道,默认名称为mychannel,名称全小写

  2. 在启动指令后加上-ca标签, 使用ca认证方式启动网络, 使用docker ps查看,发现多了几个ca相关容器

  3. 在启动指令后加上-s couchdb参数, 将数据库设为CouchDB

Go语言链码编写

  • 要点

    • 导入shim和peer包
    • 写一个空结构体作为资产
    • 编写Init和Invoke方法
    • 写实际需要调用的服务

先导入依赖

 go get -u github.com/hyperledger/fabric-chaincode-go/shim
 go get -u github.com/hyperledger/fabric-protos-go/peer

image.png

image.png

在main.go里写入下面的内容

 package main
 ​
 import (
   "fmt"
   "github.com/hyperledger/fabric-chaincode-go/shim"
   "github.com/hyperledger/fabric-protos-go/peer"
 )
 ​
 type Asset struct {
   // 资产定义
   // 空结构体,后面使用
 }
 ​
 //Init 是初始化方法,是必须要实现的
 func (a *Asset) Init(stub shim.ChaincodeStubInterface) peer.Response {
   // args用字符串切片来存储参数
   args := stub.GetStringArgs()
   // 参数是键值对,切片长度为2
   if len(args)!=2 {
     return  shim.Error("参数错误 .... ")
   }
   err := stub.PutState(args[0],[]byte(args[1])) //把键值对传入fabric数据库
   if err!=nil {
     return shim.Error("创建资产失败: "+args[0])
   }
   return shim.Success(nil)
 }
 ​
 // Invoke 是调用链码的方式,调用其他函数
 func (a *Asset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
   // 拿到函数和参数,我们设定有set函数和get函数,对应键值对的读写
   fn,args := stub.GetFunctionAndParameters()
 ​
   var result string
   var err error
   if fn == "set" {//调用 set函数
     result,err = set(stub,args)
   }else {
     //那就是get函数
     result,err = get(stub,args)
   }
   if err != nil {
     return shim.Error(err.Error())
   }
   return shim.Success([]byte(result))
 }
 ​
 // 下面是set和get函数的具体实现
 // set 存入键值对
 func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
   if len(args) != 2 {
     return "", fmt.Errorf("参数错误")
   }
   // 把键值对传入数据库状态
   err := stub.PutState(args[0], []byte(args[1]))
   if err != nil {
     return "", fmt.Errorf("创建资产失败:%s", args[0])
   }
   return args[1], nil
 }
 ​
 // get 读取键值对
 func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
   if len(args) != 1 {
     return "", fmt.Errorf("参数错误")
   }
 ​
   value, err := stub.GetState(args[0])
   if err != nil {
     return "", fmt.Errorf("获取资产失败: %s ,发生错误为: %s", args[0], err)
   }
   if value == nil {
     return "", fmt.Errorf("不存在数据 %s", args[0])
   }
   return string(value), nil
 }
 ​
 // 在 main函数里启动链码
 func main() {
   if err := shim.Start(new(Asset)); err != nil {
     fmt.Printf("启动链码失败: %s", err)
   }
 }

链码编写完毕

Go语言链码部署

将自己编写的链码main.go文件放在 fabric-samples/chaincode/ 下(没有就自己创建此文件夹)

  • 设置goproxy并下载依赖
 export GO111MODULE=on
 export GOPROXY=https://proxy.golang.com.cn,direct
 go mod tidy

进入测试网络目录

cd test-network

先启动测试网络 . network up createChannel -c testchannel

网络启动好之后,部署链码 . network deployCC -ccn demo -ccl go -ccp ../chaincode/ -c testchannel,使用-c来指定通道名称

image-20220530上午125511644.png

image-20220530上午125551413.png

测试

设定环境变量

进入fabric-samples/test-network

设定环境变量,使用peer0.org1.example.com 节点身份

 export CORE_PEER_TLS_ENABLED=true
 export CORE_PEER_LOCALMSPID="Org1MSP"
 export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
 export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
 export CORE_PEER_ADDRESS=localhost:7051
 export FABRIC_CFG_PATH=$PWD/../config/
 export PATH=${PWD}/../bin:$PATH

调用链码

调用编写的set函数,传入参数"foo"---"bar",存储此键值对

 peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" -C testchannel -n demo --peerAddresses localhost:7051 --tlsRootCertFiles organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"set","Args":["foo","bar"]}'

调用编写的get函数,传入参数“foo"来查存入的值

 peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" -C testchannel -n demo --peerAddresses localhost:7051 --tlsRootCertFiles organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"get","Args":["foo"]}'

image-20220530上午103529345.png

下面是对这个调用指令的解析

 //该指令用于将已经提交到通道的链码进行函数的调用 
 peer chaincode invoke 
 //本地端口 
 -o localhost:7050 
 //验证TLS连接时使用的排序节点主机名 
 --ordererTLSHostnameOverride orderer.example.com 
 //与排序节点通信时使用 TLS 
 --tls 
 //包含排序端点的 PEM 编码可信证书的文件路径 
 --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" 
 //在哪个通道上执行,应该是已经提交完成的通道上 
 -C testchannel 
 //链码名字 
 -n demo 
 //要连接的背书节点端口地址 --peerAddresses localhost:7051 
 //如果TLS启用了,背书节点需要链接到TLS根证书的路径。 
 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" 
 //第二个背书节点的端口地址 
 --peerAddresses localhost:9051 
 //第二个背书节点需要链接到TLS根证书的路径 
 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt" 
 //JSON 格式的链码的构造函数消息 
 -c '{"function":"set","Args":["foo","bar"]}'

总结

  • 这里,我们主要实现了Fabric测试网络的部署,以及通过Go编程进行简要的数据交互。
  • 从源码搭建Fabric网络需要修改大量的配置,过程复杂,没有基础的掘友可以从测试网络fabric-samples/test-network开始学起,通过对network.sh脚本的阅读,理清fabric网络的启动过程

关于network.sh脚本解析的内容,可以继续阅读 juejin.cn/post/713791…

\