区块链开源实现Hyperledge Fabric(三)

一、链码

1.1 什么是链码

链代码也称智能合约,是控制区块链网络中相关方相互交互的业务逻辑。链代码将业务网络交易封装在代码中,最终在一个 Docker 容器内运行。

链代码即一个Go文件,创建好文件后进行函数开发等操作。关于链码的详细信息,可以参考官方提供的链代码教程

1.2 链码开发

链码使用Go语言开发,因此在编写链码前需要准备好Go语言环境,以及需要下载Fabric库文件。

1.2.1 链码的实现步骤

第一步:在%GOPATH%/src目录下新建一个chaincode文件夹。

第二步:进入chaincode文件夹,然后新建一个Go文件,并导入shim包。

shim包提供了一些 API,以便您的链代码与底层区块链网络交互来访问状态变量、交易上下文、调用方证书和属性,并调用其他链代码和执行其他操作。

第三步:定义一个结构体,然后提供main函数。

package main

import (
	"fmt"
	"github.com/hyperledger/fabric-chaincode-go/shim"
	"github.com/hyperledger/fabric-protos-go/peer"
)

# 定义结构体
type Constract struct {}
 
# 
func main() {
	shim.Start(new(Constract ))
}

第四步:为结构体提供Init函数。该函数用于任何与初始化、引导或设置相关的任务。

func (c* Constract)Init();

Init函数在链代码首次部署到区块链网络时调用,将由部署自己的链代码实例的每个对等节点执行。

第五步:为结构体提供Invoke函数。只要修改区块链的状态,就会调用该方法。简而言之,所有创建、更新和删除操作都应封装在Invoke方法内。对此方法的所有调用都会在区块链上记录为交易,这些交易最终被写入区块中。

func (c* Constract)Invoke();

1.2.2 链码操作相关的API

(1)shim包的核心方法:

shim包主要负责和客户端进行通信。如果在链码中使用该包的方法,需要手动进行引入github.com/hyperledger/fabric/core/chaincode/shim

关于shim包里面更多的方法,可以查看shim文档

(2)ChaincodeStubInterface接口的核心方法:

在shim包中有一个接口ChaincodeStubInterface,该接口提供了一组用于操作Fabric中账本数据的方法。ChaincodeStubInterface接口的核心方法大概可以分为四大类:系统管理、存储管理、交易管理、调用外部chaincode。

  • 系统管理:
方法名 方法描述
GetFunctionAndParameters 获取chaincode客户端传递过来的参数
  • 存储管理:
方法名 方法描述
PutState 把客户端传递过来的数据保存到Fabric中
GetState 从Fabric中取出数据
GetStateByRange 根据范围查询数据,该方法返回一个迭代器接口
GetHistoryForKey 查询某个键的历史记录
DelState 从Fabric中删除数据
CreateCompositeKey 给定一组属性,将这些属性组合起来构造一个复合键
GetStateByPartialCompositeKey 根据局部的复合键返回所有的匹配的键值
SplitCompositeKey 给定一个复合键,将其拆分为复合键所有的属性
  • 交易管理:
方法名 方法描述
GetTxTimestamp 获取当前客户端发送的交易时间戳
GetTxID 获取客户端发送的交易编号
  • 调用外部chaincode:
方法名 方法描述
InvokeChaincode 调用另一个链码中的Invoke方法

1.2.3 链码示例

下面官方提供的链码示例,以供大家学习使用。

mport (
	"fmt"
	"strconv"

	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
)

// 链码结构体
type SimpleChaincode struct {
}

// 实例化链码时候调用该方法
// 调用格式为:{"Args":["init","a","100","b","200"]}
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
	// 获取要调用的方法名(init)和参数数组(['a','100','b','200'])
	_, args := stub.GetFunctionAndParameters()
	var A, B string    // 该变量用于存储实体
	var Aval, Bval int // 该变量用于存储实体资产
	var err error

	if len(args) != 4 {
		return shim.Error("Incorrect number of arguments. Expecting 4")
	}

	// 初始化链码
	A = args[0] // a
	Aval, err = strconv.Atoi(args[1])  // 100
	if err != nil {
		return shim.Error("Expecting integer value for asset holding")
	}
	B = args[2] // b
	Bval, err = strconv.Atoi(args[3]) // 200
	if err != nil {
		return shim.Error("Expecting integer value for asset holding")
	}

	// 把数据写入区块链中
	err = stub.PutState(A, []byte(strconv.Itoa(Aval))) // a = 100
	if err != nil {
		return shim.Error(err.Error())
	}

	err = stub.PutState(B, []byte(strconv.Itoa(Bval))) // b = 200
	if err != nil {
		return shim.Error(err.Error())
	}
	
	// 返回响应结果
	return shim.Success(nil)
}

// 执行交易时候调用
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	// 获取调用的方法名和参数数组
	function, args := stub.GetFunctionAndParameters()
	// 根据方法名进行不同的处理
	if function == "invoke" {
		// 执行转账
		return t.invoke(stub, args)
	} else if function == "delete" {
		// 从区块链中删除实体
		return t.delete(stub, args)
	} else if function == "query" {
		// 查询数据
		return t.query(stub, args)
	}
	return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"")
}

// 转账交易
// 调用格式:{"Args":["invoke","a","b","10"]}
func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	var A, B string    // 存储实体的变量
	var Aval, Bval int // 存储实体资产的变量
	var X int          // 转账金额
	var err error

	if len(args) != 3 {
		return shim.Error("Incorrect number of arguments. Expecting 3")
	}

	A = args[0] // a
	B = args[1] // b	

	// 从区块链中获取a的数据
	Avalbytes, err := stub.GetState(A)
	// 如果获取失败,返回错误信息`Failed to get state`
	if err != nil {
		return shim.Error("Failed to get state")
	}
	// 如果实体不存在,返回错误信息`Entity not found`
	if Avalbytes == nil {
		return shim.Error("Entity not found")
	}
	Aval, _ = strconv.Atoi(string(Avalbytes)) // 100
 
 	// 从区块链中获取b的数据
	Bvalbytes, err := stub.GetState(B)
	
	if err != nil {
		return shim.Error("Failed to get state")
	}
	if Bvalbytes == nil {
		return shim.Error("Entity not found")
	}
	Bval, _ = strconv.Atoi(string(Bvalbytes)) // 200

	// 执行转账
	X, err = strconv.Atoi(args[2])
	if err != nil {
		return shim.Error("Invalid transaction amount, expecting a integer value")
	}
	Aval = Aval - X
	Bval = Bval + X
	fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)

	// 将数据写入区块链中
	err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
	if err != nil {
		return shim.Error(err.Error())
	}

	err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
	if err != nil {
		return shim.Error(err.Error())
	}
	
	return shim.Success(nil)
}

// 删除实体
func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	if len(args) != 1 {
		return shim.Error("Incorrect number of arguments. Expecting 1")
	}

	A := args[0]

	// Delete the key from the state in ledger
	err := stub.DelState(A)
	if err != nil {
		return shim.Error("Failed to delete state")
	}

	return shim.Success(nil)
}

// 查询
// 调用格式:查询数据-{"Args":["query","a"]} 
func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	var A string // 该变量存储要查询的实体
	var err error

	if len(args) != 1 {
		return shim.Error("Incorrect number of arguments. Expecting name of the person to query")
	}

	A = args[0] // a

	// 从区块链中读取a的数据,该数据为字节数据
	Avalbytes, err := stub.GetState(A)
	if err != nil {
		jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
		return shim.Error(jsonResp)
	}

	if Avalbytes == nil {
		jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"
		return shim.Error(jsonResp)
	}
	
	// 将读取到的数据封装成json后返回调用者
	jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"
	fmt.Printf("Query Response:%s\n", jsonResp)
	return shim.Success(Avalbytes)
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

二、交易背书Endorse

在Fabric中有一个非常重要的概念称为Endorsement,中文名为背书。背书的过程是一笔交易被确认的过程。而背书策略被用来指示对相关的参与方如何对交易讲行确认。当一个节点接收到一个交易请求的时候,会调用VSCC(系统Chaincode,专门负责处理背书相关的操作)与交易的chaincode共同来验证交易的合法性。在VSCC和交易的Chaincode共同对交易的确认中, 通常会做以下的校验:

  • 参与背书的签名是否有效。
  • 参与背书的数量是否满足要求。
  • 所有背书参与方是否满足要求。

书策略的设置是通过部署时instantiate命令中-P-参数来设置的。

peer chaincode instantiate -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C testchannel -n testcc -l golang -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "AND('OrgGoMSP.member','OrgJavaMSG.member')"

上面-P参数说明的是当前Chaincode发起的交易,需要组织编号为OrgGoMSP和组织编号为OrgJavaMSP的组织中的任何一个用户共同参与交易的确认并且同意,这样交易才能生效并被记录到区块链中。通过上述背书策略的实例我们可以知道背书策略是通过一定的关键字和系统的属性组成的。

除了可以使用AND关键字以外,Fabric还提供了OR关键字,表示参与方的任何一方参与背书即完成交易的确认。

"OR ('Org1MSP.member', 'Org2MSP.member', 'Org3MSP.member')" 

另外,Fabric还支持AND和OR关键字的混合使用,例如:

"OR ('Org1MSP.member', AND('Org2MSP.member', 'Org3MSP.member'))" 

上面背书策略有两种办法让交易生效。
1)组织Org1MSP中的某个成员对交易进行验证;
2)组织Org2MSP和组织Org3MSP中的成员共同就交易进行验证;

值得注意的是,背书规则只针对chaincode中写入数据的操作进行校验,对于查询类操作不背书。

三、solo多机多节点部署

3.1 环境准备

第一步:准备三台主机。

名称 IP hostname 组织机构
orderer 192.168.31.20 orderer.test.com OrdererOrg
peer0 192.168.31.21 peer0.orggo.test.com OrgGo
peer0 192.168.31.22 peer0.orgcpp.test.com OrgCpp

第二步:在每台主机主目录下新建一个文件夹作为工作目录。

# 192.168.31.20
mkdir ~/testwork
# 192.168.31.21
mkdir ~/testwork
# 192.168.31.22
mkdir ~/testwork

3.2 部署orderer节点

3.2.1 编写证书配置文件

crypto-config.yaml

OrdererOrgs:
  - Name: Orderer
    Domain: test.com
    Specs:
      - Hostname: orderer
      - 
PeerOrgs:
  - Name: OrgGo
    Domain: orggo.test.com
    EnableNodeOUs: false
    Template:
      Count: 1
    Users:
      Count: 1

  - Name: OrgCpp
    Domain: orgcpp.test.com
    EnableNodeOUs: false
    Template:
      Count: 1
    Users:
      Count: 1

3.2.2 编写通道和创世块配置文件

configtx.yaml

Organizations:
    - &OrdererOrg
        Name: OrdererOrg
        ID: OrdererMSP
        MSPDir: crypto-config/ordererOrganizations/test.com/msp
    - &OrgGo
        Name: OrgGoMSP
        ID: OrgGoMSP
        MSPDir: crypto-config/peerOrganizations/orggo.test.com/msp
        AnchorPeers:
            - Host: peer0.orggo.test.com
              Port: 7051
    - &OrgCpp
        Name: OrgCppMSP
        ID: OrgCppMSP
        MSPDir: crypto-config/peerOrganizations/orgcpp.test.com/msp
        AnchorPeers:
            - Host: peer0.orgcpp.test.com
              Port: 7051

Capabilities:
    Global: &ChannelCapabilities
        V1_1: true
    Orderer: &OrdererCapabilities
        V1_1: true
    Application: &ApplicationCapabilities
        V1_2: true

Application: &ApplicationDefaults
    Organizations:

Orderer: &OrdererDefaults
    OrdererType: solo
    Addresses:
        - orderer.test.com:7050
    BatchTimeout: 2s
    BatchSize:
        MaxMessageCount: 10
        AbsoluteMaxBytes: 99 MB
        PreferredMaxBytes: 512 KB
    Kafka:
        Brokers:
            - 127.0.0.1:9092
    Organizations:

Profiles:

    TwoOrgsOrdererGenesis:
        Capabilities:
            <<: *ChannelCapabilities
        Orderer:
            <<: *OrdererDefaults
            Organizations:
                - *OrdererOrg
            Capabilities:
                <<: *OrdererCapabilities
        Consortiums:
            SampleConsortium:
                Organizations:
                    - *OrgGo
                    - *OrgCpp
    TwoOrgsChannel:
        Consortium: SampleConsortium
        Application:
            <<: *ApplicationDefaults
            Organizations:
                - *OrgGo
                - *OrgCpp
            Capabilities:
                <<: *ApplicationCapabilities

3.2.3 生成配置文件

# 生成证书文件
cryptogen generate --config=crypto-config.yaml

# 生成创世块文件
mkdir ./channel-artifacts
configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block

# 生成通道文件
configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID testchannel

3.2.4 编写docker-compse

version: '2'

services: 
  orderer.test.com:
    container_name: orderer.test.com
    image: hyperledger/fabric-orderer:latest
    environment:
      - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=testwork_default
      - ORDERER_GENERAL_LOGLEVEL=DEBUG
      - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
      - ORDERER_GENERAL_LISTENPORT=7050
      - ORDERER_GENERAL_GENESISMETHOD=file
      - ORDERER_GENERAL_GENESISFILE=/var/hyperledger/orderer/orderer.genesis.block
      - ORDERER_GENERAL_LOCALMSPID=OrdererMSP
      - ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/orderer/msp
      # enabled TLS
      - ORDERER_GENERAL_TLS_ENABLED=true
      - ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/orderer/tls/server.key
      - ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/orderer/tls/server.crt
      - ORDERER_GENERAL_TLS_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt]
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric
    command: orderer
    volumes:
    - ./channel-artifacts/genesis.block:/var/hyperledger/orderer/orderer.genesis.block
    - ./crypto-config/ordererOrganizations/test.com/orderers/orderer.test.com/msp:/var/hyperledger/orderer/msp
    - ./crypto-config/ordererOrganizations/test.com/orderers/orderer.test.com/tls/:/var/hyperledger/orderer/tls
    networks:
      default:
        aliases:
          - testwork
    ports:
      - 7050:7050

完成上面所有工作后,启动docker容器。

docker-compose up -d

3.3 部署peer0.orggo节点

3.3.1 拷贝文件

将上面生成的证书目录拷贝到当前主机的testwork工作目录下。
在这里插入图片描述

3.3.2 准备chaincode文件

在工作目录下新建chaincode目录,该目录用于存放chaincode文件。
在这里插入图片描述
进入chaincode目录,然后将官方提供的chaincode示例文件拷贝到这里。
在这里插入图片描述

hyperledge-fabric安装包提供的chaincode示例文件在hyperledger-febric/fabric-samples/chaincode/chaincode_example02/go目录下。

3.3.3 编写docker-compose文件

version: '2'

services:
  peer0.orggo.test.com:
    container_name: peer0.orggo.test.com
    image: hyperledger/fabric-peer:latest
    environment:
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=testwork_default
      - FABRIC_LOGGING_SPEC=DEBUG
      - CORE_PEER_GOSSIP_USELEADERELECTION=true
      - CORE_PEER_GOSSIP_ORGLEADER=false
      - CORE_PEER_PROFILE_ENABLED=true
      - CORE_PEER_LOCALMSPID=OrgGoMSP
      - CORE_PEER_ID=peer0.orggo.test.com
      - CORE_PEER_ADDRESS=peer0.orggo.test.com:7051
      - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.orggo.test.com:7051
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.orggo.test.com:7051
      # TLS
      - CORE_PEER_TLS_ENABLED=true
      - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt
      - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key
      - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt
    volumes:
      - /var/run/:/host/var/run/
      - ./crypto-config/peerOrganizations/orggo.test.com/peers/peer0.orggo.test.com/msp:/etc/hyperledger/fabric/msp
      - ./crypto-config/peerOrganizations/orggo.test.com/peers/peer0.orggo.test.com/tls:/etc/hyperledger/fabric/tls
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: peer node start
    networks:
      default:
        aliases:
          - testwork
    ports:
      - 7051:7051
      - 7053:7053
    extra_hosts: 
      - "orderer.test.com:192.168.31.20"
      - "orgcpp.test.com:192.168.31.22"
      
  cli:
    container_name: cli
    image: hyperledger/fabric-tools:latest
    tty: true
    stdin_open: true
    environment:
      - GOPATH=/opt/gopath
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      - FABRIC_LOGGING_SPEC=INFO
      - CORE_PEER_ID=cli
      - CORE_PEER_ADDRESS=peer0.orggo.test.com:7051
      - CORE_PEER_LOCALMSPID=OrgGoMSP
      - CORE_PEER_TLS_ENABLED=true
      - CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/orggo.test.com/peers/peer0.orggo.test.com/tls/server.crt
      - CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/orggo.test.com/peers/peer0.orggo.test.com/tls/server.key
      - CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/orggo.test.com/peers/peer0.orggo.test.com/tls/ca.crt
      - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/orggo.test.com/users/[email protected]/msp
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: /bin/bash
    volumes:
        - /var/run/:/host/var/run/
        - ./chaincode/:/opt/gopath/src/github.com/chaincode
        - ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
        - ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts
    depends_on: 
      - peer0.orggo.test.com
    
    networks:
      default:
        aliases:
          - testwork
    extra_hosts:
      - "orderer.test.com:192.168.31.20"
      - "peer0.orggo.test.com:192.168.31.21"
      - "peer0.orgcpp.test.com:192.168.31.22"

上面工作完成后启动容器:

docker-compose up -d

3.3.4 在节点中进行相关操作

第一步:进入cli容器;

docker exec -it cli bash

第二步:创建通道;

peer channel create -o orderer.test.com:7050 -c testchannel -f ./channel-artifacts/channel.tx --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/test.com/msp/tlscacerts/tlsca.test.com-cert.pem

第三步:将生成的通道文件移入channel-artifacts目录中;

mv testchannel.block ./channel-artifacts

第四步:加入通道;

peer channel join -b ./channel-artifacts/testchannel.block

第五步:安装链码;

peer chaincode install -n testcc -v 1.0 -l golang -p github.com/chaincode

第六步:将链码进行打包;

peer chaincode package -n testcc -p github.com/chaincode -v 1.0 testcc.1.0.out

打包完成后,将生成的testcc.1.0.out文件移入到channel-artifacts文件夹里面。

3.4 部署peer0.orgcpp节点

3.4.1 拷贝文件

将上面生成的testchannel.block、testcc.1.0.out、以及证书文件夹拷贝到当前主机的testwork工作目录下。在这里插入图片描述

3.4.2 编写docker-compose文件

version: '2'

services:
    peer0.orgcpp.test.com:
      container_name: peer0.orgcpp.test.com
      image: hyperledger/fabric-peer:latest
      environment:
        - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
        - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=testwork_default
        - FABRIC_LOGGING_SPEC=DEBUG
        - CORE_PEER_GOSSIP_USELEADERELECTION=true
        - CORE_PEER_GOSSIP_ORGLEADER=false
        - CORE_PEER_PROFILE_ENABLED=true
        - CORE_PEER_LOCALMSPID=OrgCppMSP
        - CORE_PEER_ID=peer0.orgcpp.test.com
        - CORE_PEER_ADDRESS=peer0.orgcpp.test.com:7051
        - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.orgcpp.test.com:7051
        - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.orgcpp.test.com:7051
        # TLS
        - CORE_PEER_TLS_ENABLED=true
        - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt
        - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key
        - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt
      volumes:
        - /var/run/:/host/var/run/
        - ./crypto-config/peerOrganizations/orgcpp.test.com/peers/peer0.orgcpp.test.com/msp:/etc/hyperledger/fabric/msp
        - ./crypto-config/peerOrganizations/orgcpp.test.com/peers/peer0.orgcpp.test.com/tls:/etc/hyperledger/fabric/tls
      working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
      command: peer node start
      networks:
       default:
         aliases:
           - testwork
      ports:
        - 7051:7051
        - 7053:7053
      extra_hosts:
        - "orderer.test.com:192.168.31.20"
        - "peer0.orggo.test.com:192.168.31.21"
        
    cli:
      container_name: cliCpp
      image: hyperledger/fabric-tools:latest
      tty: true
      stdin_open: true
      environment:
        - GOPATH=/opt/gopath
        - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
        - FABRIC_LOGGING_SPEC=INFO
        - CORE_PEER_ID=cliCpp
        - CORE_PEER_ADDRESS=peer0.orgcpp.test.com:7051
        - CORE_PEER_LOCALMSPID=OrgCppMSP
        - CORE_PEER_TLS_ENABLED=true
        - CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/orgcpp.test.com/peers/peer0.orgcpp.test.com/tls/server.crt
        - CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/orgcpp.test.com/peers/peer0.orgcpp.test.com/tls/server.key
        - CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/orgcpp.test.com/peers/peer0.orgcpp.test.com/tls/ca.crt
        - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/orgcpp.test.com/users/[email protected]/msp
      working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
      command: /bin/bash
      volumes:
          - /var/run/:/host/var/run/
          - ./chaincode/:/opt/gopath/src/github.com/chaincode
          - ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
          - ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts
      depends_on: 
        - peer0.orgcpp.test.com
      
      networks:
        default:
          aliases:
            - testwork
      extra_hosts:
        - "orderer.test.com:192.168.31.20"
        - "peer0.orggo.test.com:192.168.31.21"
        - "peer0.orgcpp.test.com:192.168.31.22"

完成后启动docker容器:

docker-compose up -d

3.4.3 节点相关操作

第一步:进入cliCpp容器;

docker exec -it cliCpp bash

第二步:加入通道;

peer channel join -b ./channel-artifacts/testchannel.block

第三步:安装链码;

peer chaincode install ./channel-artifacts/testcc.1.0.out

第四步:实例化链码;

peer chaincode instantiate -o orderer.test.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/test.com/msp/tlscacerts/tlsca.test.com-cert.pem -C testchannel -n testcc -l golang -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "AND('OrgGoMSP.member', 'OrgCppMSP.member')"

3.5 测试

  • 查询
peer chaincode query -C testchannel -n testcc -c '{"Args":["query","a"]}'
  • 交易
peer chaincode invoke -o orderer.test.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/test.com/msp/tlscacerts/tlsca.test.com-cert.pem -C testchannel -n testcc --peerAddresses peer0.orggo.test.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/orggo.test.com/peers/peer0.orggo.test.com/tls/ca.crt --peerAddresses peer0.orgcpp.test.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/orgcpp.test.com/peers/peer0.orgcpp.test.com/tls/ca.crt -c '{"Args":["invoke","a","b","10"]}'

3.6 可能遇到的问题

问题一:链码指纹不匹配

在这里插入图片描述
原因分析:由于安装链码文件时候生成的hash值不相同。

解决办法:在第一个安装链码的节点上对链码文件进行打包,然后把打包后的文件发给其他节点进行安装即可。

# 打包链码
peer chaincode package -n testcc -p github.com/chaincode -v 1.0 testcc.1.0.out

# 安装链码
peer chaincode install ./channel-artifacts/testcc.1.0.out

问题二:连接orderer.test.com:7050失败

在这里插入图片描述
原因分析:由于不同主机的docker容器网络互相隔离所导致。

解决办法:
1)分别为每个节点的docker容器设置网段。

修改配置文件/etc/docker/daemon.json,添加以下配置:

# 192.168.31.20
{
  "debug" : true,
  "default-address-pools" : [
    {
      "base" : "172.17.0.1/16",
      "size" : 24
    }
  ]
}

# 192.168.31.21
{
  "debug" : true,
  "default-address-pools" : [
    {
      "base" : "172.18.0.1/16",
      "size" : 24
    }
  ]
}

# 192.168.31.22
{
  "debug" : true,
  "default-address-pools" : [
    {
      "base" : "172.19.0.1/16",
      "size" : 24
    }
  ]
}

上面12.11.0.0/16可以根据实际情况自行指定。配置完成后重启docker服务。

systemctl restart docker

2)查看orderer容器的ip地址(比如:172.17.0.1)。

3)给peer0.orggo.test.com和peer0.orgcpp.test.com节点上添加静态路由。IP地址是orderer节点容器的IP和order节点宿主机IP的对应关系

ip route add 172.17.0.1 via 192.168.31.20
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -F
iptables -L -n
发布了111 篇原创文章 · 获赞 41 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/zhongliwen1981/article/details/104547551