Hyperledger Fabric 2.0 外部链码的开发/构建/部署/访问

简介:

在Hyperledger Fabric 2.0中引入的一个新特性,就是可以使用外部的链码启动器,这种外部启动链码的方式非常适合使用kubenetes或dowcker swarm来统一管理节点容器和链码容器。在这片文章中,我们将学习如何使用外部链码启动器在K8s集群中部署链码。

1、Fabric外部链码实验的前提条件

Hyperledger Fabric区块链开发教程:
Fabric Node.js开发详解 |

Fabric Java开发详解 |
Fabric Golang开发详解

下面是我们实验的一些前提条件

  • 一个kubenetes集群,你可以使用minikube或一个单节点的kubeadmin。在 本文中我们使用kubeadmin
  • hyperledger fabric 2.0.1 docker镜像
  • hyperledger fabric 2.0.1 预编译程序。我们需要其中的工具来创建 密码学资料和通道交易配置文件。可以从这里下载。
  • 这里下载本文的代码

2、安装预编译程序

使用以下命令安装预编译程序:

wget https://github.com/hyperledger/fabric/releases/download/v2.0.1/hyperledger-fabric-linux-amd64-2.0.1.tar.gz

tar -xzf hyperledger-fabric-linux-amd64-2.0.1.tar.gz# Move to the bin path

mv bin/* /bin# Check that you have successfully installed the tools by executing

configtxgen --version# Should print the following output:
# configtxgen:
#  Version: 2.0.1
#  Commit SHA: 1cfa5da98
#  Go version: go1.13.4
#  OS/Arch: linux/amd64

3、启动Hyperledger Fabric网络

一旦我们启动了kubernetes集群,就可以启动Fabric网络了。但是我们先要生成基本的密码学资料以及网络创世区块。在configtx.yaml中有一些修改以适应fabric 2.0中新的lifecycle chaincode命令,这些修改对于使用外部链码启动器是必须的。

Fabric区块链网络中包含3个RAFT排序服务节点,2个机构org1和org2各含1个peer节点以及一个CA。这些信息已经编码在configtx.yaml和crypto-config.yaml文件中,不需要修改。

有了配置文件,现在我们可以生成fabric区块链网络的密码学资料和创世区块。我们使用fabricOps.sh脚本:

$ ./fabricOps.sh start

上面的命令将生成所有的密码学资料,例如节点和CA的证书等,以及通道的创世区块。

现在需要为Hyperledger Fabric工作负载创建一个新的k8s命名空间,命令如下:

$ kubectl create ns hyperledger

然后创建一个文件夹来保存Hyperledger Fabric工作负载的持久化数据:

$ mkdir /home/storage

好了,现在可以开始部署Fabric排序节点了:

$ kubectl create -f orderer-service/

使用下面命令检查Fabric网络是否启动正常:

$ kubectl get pods -n hyperledger

### Should print a similar output
NAME                        READY   STATUS    RESTARTS   AGE
orderer0-58666b6bd7-pflf7    1/1     Running   0          5m47s
orderer1-c4fd65c7d-c27ll    1/1     Running   0          5m47s
orderer2-557cb7865-wlcmh    1/1     Running   0          5m47s

接下来创建org1的工作负载,这主要包含部署机构的CA和peer节点:

$ kubectl create -f org1/

同样用下面命令检查Fabric网络节点是否启动:

$ kubectl get pods -n hyperledger

### Should print a similar output
NAME                          READY   STATUS    RESTARTS   AGE
ca-org1-84945b8c7b-9px4s      1/1     Running   0          19m
cli-org1-bc9f895f6-zmmdc      1/1     Running   0          2m56s
orderer0-58666b6bd7-pflf7      1/1     Running   0          79m
orderer1-c4fd65c7d-c27ll      1/1     Running   0          79m
orderer2-557cb7865-wlcmh      1/1     Running   0          79m
peer0-org1-798b974467-vv4zz   1/1     Running   0          19m

重复上面的步骤来加载org2的工作负载:

$ kubectl create -f org2/

检查Fabric网络情况:

$ kubectl get pods -n hyperledger

### Should print a similar output
NAME                          READY   STATUS    RESTARTS   AGE
ca-org1-84945b8c7b-9px4s      1/1     Running   0          71m
ca-org2-7454f69c48-q8lft      1/1     Running   0          2m20s
cli-org1-bc9f895f6-zmmdc      1/1     Running   0          55m
cli-org2-7779cc8788-8q4ns     1/1     Running   0          2m20s
orderer0-58666b6bd7-pflf7      1/1     Running   0          131m
orderer1-c4fd65c7d-c27ll      1/1     Running   0          131m
orderer2-557cb7865-wlcmh      1/1     Running   0          131m
peer0-org1-798b974467-vv4zz   1/1     Running   0          71m
peer0-org2-5849c55fcd-mbn5h   1/1     Running   0          2m19s

3、设置Fabric网络的通道

一旦部署完Fabric网络的工作负载,我们可以开始创建通道并将peer节点加入通道。进入org1的cli:

$ kubectl exec -it cli_org1_pod_name sh -n hyperledger

在这个pod内执行如下命令:

$ peer channel create -o orderer0:7050 -c mychannel -f ./scripts/channel-artifacts/channel.tx --tls true --cafile $ORDERER_CA

### Should print a similar output
2020-03-06 11:54:57.582 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2020-03-06 11:54:58.903 UTC [cli.common] readBlock -> INFO 002 Received block: 0

现在mychannel通道已经创建好了,接下来将org1的peer节点加入通道:

$ peer channel join -b mychannel.block

### Should print a similar output
2020-03-06 12:01:41.608 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2020-03-06 12:01:41.688 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel

同样的操作将org2的peer节点加入通道。不过由于org1已经创建了通道,因此我们需要从排序服务提取通道创世块。首先进入pod:

$ kubectl exec -it cli_org2_pod_name sh -n hyperledger

然后在pod内执行如下操作:

$ peer channel fetch 0 mychannel.block -c mychannel -o orderer0:7050 --tls --cafile $ORDERER_CA

### Should print a similar output
2020-03-06 12:18:14.880 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2020-03-06 12:18:14.895 UTC [cli.common] readBlock -> INFO 002 Received block: 0

然后加入通道:

$ peer channel join -b mychannel.block

### Should print a similar output
2020-03-06 12:20:41.475 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2020-03-06 12:20:41.561 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel

用下面的命令检查当前节点是否已经加入了通道:

$ peer channel list

### Should print a similar output
2020-03-06 12:22:41.102 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
Channels peers has joined: 
mychannel

4、在Fabric节点安装外部链码

现在开始我们进入真正有趣的环节。我们将部署marbles链码作为外部链码。你可以在Fabric-samples
中找到原始的链码,但是为了将其作为外部链码部署,我们需要在imports和init函数中进行一些修改,
以将其声明为外部链码服务器。

新版本的Hyperledger Fabric同样包含了新的链码处理流程来支持外部方式安装和启动链码。为了使用外部链码特性,我们也需要使用这一新的流程。

第一件要做的事情就是将peer节点配置为可以处理外部链码。外部链码构建器就是基于buildpack
实现,因此我们创建3个脚本:detect、build和release。这三个脚本必须在peer容器内定义。你可以
在buildpack/bin目录下找到这些脚本。

链码构建器定义在org1目录下的builders-config.yaml中,其中的自定义构建器配置:

  # List of directories to treat as external builders and launchers for
  # chaincode. The external builder detection processing will iterate over the
  # builders in the order specified below.
  externalBuilders:
    - name: external-builder
      path: /builders/external
      environmentWhitelist:
         - GOPROXY

kubernetes使用环境变量来配置peer节点,这些环境变量将覆盖core.yaml中的默认值。

Fabric 2.0采用了与之前版本不同的方式来打包和安装链码。由于我们要使用外部链码特性,因此链码代码不需要在peer的pod内进行编译和安装,而是在另一个pod中进行。在peer进程中唯一需要的就是用来连接外部链码进程的信息。

为此,我们需要打包链码的一些准备。需要一个connection.json文件,其中包含连接外部链码服务器的信息,例如地址、TLS证书、连接超时配置等。

{
    "address": "chaincode-marbles-org1.hyperledger:7052",
    "dial_timeout": "10s",
    "tls_required": false,
    "client_auth_required": false,
    "client_key": "-----BEGIN EC PRIVATE KEY----- ... -----END EC PRIVATE KEY-----",
    "client_cert": "-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE-----",
    "root_cert": "-----BEGIN CERTIFICATE---- ... -----END CERTIFICATE-----"
}

这个文件需要打包进一个tar文件,code.tar.gz,使用如下命令进行压缩打包:

$ cd chaincode/packaging
$ tar cfz code.tar.gz connection.json

一旦有了code.tar.gz文件,我们需要将其与另一个文件metadata.json一起重新打包。在metadata.json中包含了链码类型、路径、标签等信息:

{"path":"","type":"external","label":"marbles"}

使用下面的命令打包:

$ tar cfz marbles-org1.tgz code.tar.gz metadata.json

一旦有了上面的打包文件,我们就可以使用新的lifecycle chaincode install命令来安装链码了:

$ peer lifecycle chaincode install marbles-org1.tgz

### Should print a similar output
2020-03-07 14:33:18.120 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nGdmarbles:e001937433673b11673d660d142c722fc372905db87f88d2448eee42c9c63064\022\006degree" > 
2020-03-07 14:33:18.126 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: marbles:e001937433673b11673d660d142c722fc372905db87f88d2448eee42c9c63064

记下来上面的链码包标识符,我们接下来会用到,不过你也可以随时用下面的命令查询链码包的标识符:

$ peer lifecycle chaincode queryinstalled

### Should print a similar output
Installed chaincodes on peer:
Package ID: marbles:030eec59c7d74fbb4e9fd57bbd50bb904a715ffb9de8fea85b6a6d4b8ca9ea12, Label: marbles

现在我们为org2重复上面的步骤,但是由于我们希望用另一个pod为org2的peer节点提供链码服务,
因此我们需要修改connection.json中的地址配置:

"address": "chaincode-marbles-org2.hyperledger:7052",

然后重复之前的步骤,在org2的cli pod中执行如下命令:

$ rm -f code.tar.gz
$ tar cfz code.tar.gz connection.json
$ tar cfz marbles-org2.tgz code.tar.gz metadata.json
$ peer lifecycle chaincode install marbles-org2.tgz

### Should print a similar output
2020-03-07 15:10:15.093 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nGmarbles:c422c797444e4ee25a92a8eaf97765288a8d68f9c29cedf1e0cd82e4aa2c8a5b\022\006degree" > 
2020-03-07 15:10:15.093 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: marbles:c422c797444e4ee25a92a8eaf97765288a8d68f9c29cedf1e0cd82e4aa2c8a5b

同样记录链码包的标识符,它应该与之前org1的不同。

当我们安装链码时在peer内部发生了什么?如果定义了外部构建器或启动器,那么就不会执行内置的链码构建过程。由于我们已经定义了外部构建器,那么将按下面的顺序执行外部构建脚本:

detect脚本检测要安装的链码是否在metadata.json中配置为external类型。如果脚本失败,那么peer将认为这个外部构建器不需要构建链码并尝试其他外部构建器,直到最终没有可用的外部构建器时,使用内置的docker构建流程。

#!/bin/sh

# The bin/detect script is responsible for determining whether or not a buildpack 
# should be used to build a chaincode package and launch it. 
# 
# The peer invokes detect with two arguments:
# bin/detect CHAINCODE_SOURCE_DIR CHAINCODE_METADATA_DIR
#
# When detect is invoked, CHAINCODE_SOURCE_DIR contains the chaincode source and 
# CHAINCODE_METADATA_DIR contains the metadata.json file from the chaincode package installed to the peer. 
# The CHAINCODE_SOURCE_DIR and CHAINCODE_METADATA_DIR should be treated as read only inputs. 
# If the buildpack should be applied to the chaincode source package, detect must return an exit code of 0; 
# any other exit code will indicate that the buildpack should not be applied.

CHAINCODE_METADATA_DIR="$2"

set -euo pipefail

# use jq to extract the chaincode type from metadata.json and exit with
# success if the chaincode type is golang
if [ "$(cat "$CHAINCODE_METADATA_DIR/metadata.json" | sed -e 's/[{}]/''/g' | awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/'type'\042/){print $(i+1)}}}' | tr -d '"')" = "external" ]; then
    exit 0
fi

exit 1

如果detect脚本成功,那么就调用build脚本。如果在peer内部构建链码,那么这个脚本应当生成二进制程序。但是由于我们希望作为外部服务,那么只需要简单的拷贝connection.json文件到build的输出目录就可以了。

#!/bin/sh

# The bin/build script is responsible for building, compiling, or transforming the contents 
# of a chaincode package into artifacts that can be used by release and run.
#
# The peer invokes build with three arguments:
# bin/build CHAINCODE_SOURCE_DIR CHAINCODE_METADATA_DIR BUILD_OUTPUT_DIR
#
# When build is invoked, CHAINCODE_SOURCE_DIR contains the chaincode source and 
# CHAINCODE_METADATA_DIR contains the metadata.json file from the chaincode package installed to the peer.
# BUILD_OUTPUT_DIR is the directory where build must place artifacts needed by release and run. 
# The build script should treat the input directories CHAINCODE_SOURCE_DIR and 
# CHAINCODE_METADATA_DIR as read only, but the BUILD_OUTPUT_DIR is writeable.

CHAINCODE_SOURCE_DIR="$1"
CHAINCODE_METADATA_DIR="$2"
BUILD_OUTPUT_DIR="$3"

set -euo pipefail

#external chaincodes expect connection.json file in the chaincode package
if [ ! -f "$CHAINCODE_SOURCE_DIR/connection.json" ]; then
    >&2 echo "$CHAINCODE_SOURCE_DIR/connection.json not found"
    exit 1
fi

#simply copy the endpoint information to specified output location
cp $CHAINCODE_SOURCE_DIR/connection.json $BUILD_OUTPUT_DIR/connection.json

if [ -d "$CHAINCODE_SOURCE_DIR/metadata" ]; then
    cp -a $CHAINCODE_SOURCE_DIR/metadata $BUILD_OUTPUT_DIR/metadata
fi

exit 0

最后,一旦build脚本执行完毕,就会调用release脚本。这个脚本负责提供connection.json文件给peer节点,它只需要将该文件放到release输出目录就可以了。因此,peer现在知道如何调用链码了。

#!/bin/sh

# The bin/release script is responsible for providing chaincode metadata to the peer. 
# bin/release is optional. If it is not provided, this step is skipped. 
#
# The peer invokes release with two arguments:
# bin/release BUILD_OUTPUT_DIR RELEASE_OUTPUT_DIR
#
# When release is invoked, BUILD_OUTPUT_DIR contains the artifacts 
# populated by the build program and should be treated as read only input. 
# RELEASE_OUTPUT_DIR is the directory where release must place artifacts to be consumed by the peer.

set -euo pipefail

BUILD_OUTPUT_DIR="$1"
RELEASE_OUTPUT_DIR="$2"

# copy indexes from metadata/* to the output directory
# if [ -d "$BUILD_OUTPUT_DIR/metadata" ] ; then
#    cp -a "$BUILD_OUTPUT_DIR/metadata/"* "$RELEASE_OUTPUT_DIR/"
# fi

#external chaincodes expect artifacts to be placed under "$RELEASE_OUTPUT_DIR"/chaincode/server
if [ -f $BUILD_OUTPUT_DIR/connection.json ]; then
   mkdir -p "$RELEASE_OUTPUT_DIR"/chaincode/server
   cp $BUILD_OUTPUT_DIR/connection.json "$RELEASE_OUTPUT_DIR"/chaincode/server

   #if tls_required is true, copy TLS files (using above example, the fully qualified path for these fils would be "$RELEASE_OUTPUT_DIR"/chaincode/server/tls)

   exit 0
fi

exit 1

5、Fabric外部链码的构建与部署

一旦我们在peer节点上安装了链码,就可以构建并在我们的kubernetes集群上部署这些链码了。让我们看一下必要的修改。

首先需要import一些必要的模块:

import (
    "bytes"
    "encoding/json"
    "fmt"
    "strconv"
    "strings"
    "time"
    "os"

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

需要指出的一点是,在构建代码之前需要定义go.mod文件。最新版本的shim模块中包含了对外部链码特性的支持 —— 目前只有go 链码开发包支持外部链码。

module github.com/marbles
  
go 1.12

require (
        github.com/hyperledger/fabric-chaincode-go v0.0.0-20200128192331-2d899240a7ed
        github.com/hyperledger/fabric-protos-go v0.0.0-20200124220212-e9cfc186ba7b
        golang.org/x/net v0.0.0-20200202094626-16171245cfb2 // indirect
        golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 // indirect
        golang.org/x/text v0.3.2 // indirect
        google.golang.org/genproto v0.0.0-20200218151345-dad8c97a84f5 // indirect
)

其他的变化包括修改链码服务器的监听地址、端口等。因为我们希望使用kubernetes的yaml描述文件来修改这些配置,因此我们使用os模块来获取这些值:

func main() {

    server := &shim.ChaincodeServer{
        CCID:    os.Getenv("CHAINCODE_CCID"),
        Address: os.Getenv("CHAINCODE_ADDRESS"),
        CC:      new(SimpleChaincode),
        TLSProps: shim.TLSProperties{
                Disabled: true,
        },
    }

    // Start the chaincode external server
    err := server.Start()

    if err != nil {
        fmt.Printf("Error starting Marbles02 chaincode: %s", err)
    }
}

现在可以用下面的Dockerfile来构建链码镜像:

# This image is a microservice in golang for the Degree chaincode
FROM golang:1.13.8-alpine AS build

COPY ./ /go/src/github.com/marbles
WORKDIR /go/src/github.com/marbles

# Build application
RUN go build -o chaincode -v .

# Production ready image
# Pass the binary to the prod image
FROM alpine:3.11 as prod

COPY --from=build /go/src/github.com/marbles/chaincode /app/chaincode

USER 1000

WORKDIR /app
CMD ./chaincode

链码是使用一个golang的alpine镜像构建的。执行如下命令创建链码镜像:

$ docker build -t chaincode/marbles:1.0 .

一切顺利的话,就可以部署了。修改链码部署文件org1-chaincode-deployment.yaml和org2-chaincode-deployment.yaml中的CHAINCODE_CCID变量为你需要安装的链码。

#---------------- Chaincode Deployment ---------------------
apiVersion: apps/v1 # for versions before 1.8.0 use apps/v1beta1
kind: Deployment
metadata:
  name: chaincode-marbles-org1
  namespace: hyperledger
  labels:
    app: chaincode-marbles-org1
spec:
  selector:
    matchLabels:
      app: chaincode-marbles-org1
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: chaincode-marbles-org1
    spec:
      containers:
        - image: chaincode/marbles:1.0
          name: chaincode-marbles-org1
          imagePullPolicy: IfNotPresent
          env:
            - name: CHAINCODE_CCID
              value: "marbles:d8140fbc1a0903bd88611a96c5b0077a2fdeef00a95c05bfe52e207f5f9ab79d"
            - name: CHAINCODE_ADDRESS
              value: "0.0.0.0:7052"
          ports:
            - containerPort: 7052

然后进行部署:

$ kubectl create -f chaincode/k8s

现在fabric网络看起来应该是这样:

$ kubectl get pods -n hyperledgerNAME                                      READY   STATUS    RESTARTS   AGE
ca-org1-84945b8c7b-tx59g                  1/1     Running   0          19h
ca-org2-7454f69c48-nfzsq                  1/1     Running   0          19h
chaincode-marbles-org1-6fc8858855-wdz7z   1/1     Running   0          20m
chaincode-marbles-org2-77bf56fdfb-6cdfm   1/1     Running   0          14m
cli-org1-589944999c-cvgbx                 1/1     Running   0          19h
cli-org2-656cf8dd7c-kcxd7                 1/1     Running   0          19h
orderer0-5844bd9bcc-6td8c                 1/1     Running   0          46h
orderer1-75d8df99cd-6vbjl                 1/1     Running   0          46h
orderer2-795cf7c4c-6lsdd                  1/1     Running   0          46h
peer0-org1-5bc579d766-kq2qd               1/1     Running   0          19h
peer0-org2-77f58c87fd-sczp8               1/1     Running   0          19h

现在我们需要为每个机构审批链码。这是链码生命周期过程的一个新特性,每个机构都需要同意新链码的定义。我们将为org1审批marble链码定义。在org1的cli pod内执行如下命令,记得修改CHAINCODE_CCID:

$ peer lifecycle chaincode approveformyorg --channelID mychannel --name marbles --version 1.0 --init-required --package-id marbles:e001937433673b11673d660d142c722fc372905db87f88d2448eee42c9c63064 --sequence 1 -o orderer0:7050 --tls --cafile $ORDERER_CA --signature-policy "AND ('org1MSP.peer','org2MSP.peer')"

### Should print a similar output
2020-03-08 10:02:46.192 UTC [chaincodeCmd] ClientWait -> INFO 001 txid [4d81ea5fd494e9717a0c860812d2b06bc62e4fc6c4b85fa6c3a916eee2c78e85] committed with status (VALID)

你可以用如下命令检查整个网络中的审批状态:

$ peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name marbles --version 1.0 --init-require
d --sequence 1 -o -orderer0:7050 --tls --cafile $ORDERER_CA --signature-policy "AND ('org1MSP.peer','org2MSP.peer')"

### Should print a similar output
Chaincode definition for chaincode 'marbles', version '1.0', sequence '1' on channel 'mychannel' approval status by org:
org1MSP: true
org2MSP: false

现在让我们为org2批准链码定义。在org2的cli pod中执行如下命令,记得修改CHAINCODE_CCID:

$ peer lifecycle chaincode approveformyorg --channelID mychannel --name marbles --version 1.0 --init-required --package-id marbles:25a9f6fe26161d29af928228ca1db0c41892e26e46335c84952336ee26d1fd93 --sequence 1 -o orderer0:7050 --tls --cafile $ORDERER_CA --signature-policy "AND ('org1MSP.peer','org2MSP.peer')"

### Should print a similar output
2020-03-08 10:26:43.992 UTC [chaincodeCmd] ClientWait -> INFO 001 txid [74a89f3c93c10f14c626bd4d6cb654b37889908c9e6f7b983d2cad79f1e82267] committed with status (VALID)

再次检查链码提交状态:

$ peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name marbles --version 1.0 --init-required --sequence 1 -o orderer0:7050 --tls --cafile $ORDERER_CA --signature-policy "AND ('org1MSP.peer','org2MSP.peer')"

### Should print a similar output
Chaincode definition for chaincode 'marbles', version '1.0', sequence '1' on channel 'mychannel' approval status by org:
org1MSP: true
org2MSP: true

现在我们得到所有机构的批准,让我们将链码定义在通道上提交。可以在任何peer上执行如下操作:

$ peer lifecycle chaincode commit -o orderer0:7050 --channelID mychannel --name marbles --version 1.0 --sequence 1 --init-required --tls true --cafile $ORDERER_CA --peerAddresses peer0-org1:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1/peers/peer0-org1/tls/ca.crt --peerAddresses peer0-org2:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2/peers/peer0-org2/tls/ca.crt --signature-policy "AND ('org1MSP.peer','org2MSP.peer')"

### Should print a similar output
2020-03-08 14:13:49.516 UTC [chaincodeCmd] ClientWait -> INFO 001 txid [568cb81f821698025bbc61f4c6cd3b4baf1aea632e1e1a8cfdf3ec3902d1c6bd] committed with status (VALID) at peer0-org1:7051
2020-03-08 14:13:49.533 UTC [chaincodeCmd] ClientWait -> INFO 002 txid [568cb81f821698025bbc61f4c6cd3b4baf1aea632e1e1a8cfdf3ec3902d1c6bd] committed with status (VALID) at peer0-org2:7051

好了,现在链码已经就绪,可以查询和调用了!

6、测试Fabric外部链码

我们可以从cli pod中测试链码的查询和交易调用。首先创建一些宝石:

$ peer chaincode invoke -o orderer0:7050 --tls true --cafile $ORDERER_CA -C mychannel -n marbles --peerAddresses 
peer0-org1:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1/peers/peer0-org1/tls/ca.crt --peerAddresses peer
0-org2:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2/peers/peer0-org2/tls/ca.crt -c '{"Args":["initMarble
","marble1","blue","35","tom"]}' --waitForEvent

### Should print a similar output
2020-03-08 14:23:03.569 UTC [chaincodeCmd] ClientWait -> INFO 001 txid [83aeeaac47cf6302bc139addc4aa38116a40eaff788846d87cc815d2e1318f44] committed with status (VALID) at peer0-org2:7051
2020-03-08 14:23:03.575 UTC [chaincodeCmd] ClientWait -> INFO 002 txid [83aeeaac47cf6302bc139addc4aa38116a40eaff788846d87cc815d2e1318f44] committed with status (VALID) at peer0-org1:7051
2020-03-08 14:23:03.576 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 003 Chaincode invoke successful. result: status:200

创建另一个宝石:

$ peer chaincode invoke -o orderer0:7050 --tls true --cafile $ORDERER_CA -C mychannel -n marbles --peerAddresses peer0-org1:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1/peers/peer0-org1/tls/ca.crt --peerAddresses peer0-org2:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2/peers/peer0-org2/tls/ca.crt -c '{"Args":["initMarble","marble2","red","50","tom"]}' --waitForEvent

### Should print a similar output
2020-03-08 14:23:40.404 UTC [chaincodeCmd] ClientWait -> INFO 001 txid [8391f9f8ea84887a56f99e4dc4501eaa6696cd7bd6c524e4868bd6cfd5b85e78] committed with status (VALID) at peer0-org2:7051
2020-03-08 14:23:40.434 UTC [chaincodeCmd] ClientWait -> INFO 002 txid [8391f9f8ea84887a56f99e4dc4501eaa6696cd7bd6c524e4868bd6cfd5b85e78] committed with status (VALID) at peer0-org1:7051
2020-03-08 14:23:40.434 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 003 Chaincode invoke successful. result: status:200

查询宝石信息:

$ peer chaincode query -C mychannel -n marbles -c '{"Args":["readMarble","marble1"]}'

{"docType":"marble","name":"marble1","color":"blue","size":35,"owner":"tom"}

也可以执行如下命令查询链码日志:

$ kubectl logs chaincode_pod_name -n hyperledger

### Should print a similar output
invoke is running initMarble
- start init marble
- end init marble
invoke is running initMarble
- start init marble
- end init marble
invoke is running readMarble

原文链接:Hyperledger Fabric 2.0外部链码实战 — 汇智网

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务&nbsp;ACK 容器服务&nbsp;Kubernetes&nbsp;版(简称&nbsp;ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情:&nbsp;https://www.aliyun.com/product/kubernetes
目录
相关文章
|
6月前
|
开发框架 .NET 区块链
Hyperledger fabric部署链码(五)初始化与链码升级
fabric部署chaincode-go(智能合约)系列之五
|
6月前
|
存储 JSON 安全
Hyperledger fabric智能合约编写(一)
本篇文章主要对链码编写的主要思路和部分API进行梳理。
|
6月前
|
Go API 区块链
Hyperledger Fabric相关概念介绍
在学习Hyperledger Fabric的过程中,初步对相关概念的了解。
Hyperledger Fabric相关概念介绍
|
6月前
|
JSON 区块链 数据格式
Hyperledger fabric部署链码(四)提交链码定义到channel
fabric部署chaincode-go(智能合约)系列之四
|
6月前
|
测试技术 API 区块链
Hyperledger fabric部署链码(三)批准链码定义
fabric部署chaincode-go(智能合约)系列之三
|
6月前
|
JavaScript 测试技术 Go
Hyperledger fabric部署链码(一)打包链码
fabric部署chaincode-go(智能合约)系列之一
|
6月前
|
测试技术 Go 区块链
Hyperledger fabric 测试环境部署
Hyperledger fabric 测试环境部署及相关问题解答
108 3
|
6月前
|
区块链
Hyperledger fabric部署链码(二)安装链码到fabric
fabric部署chaincode-go(智能合约)系列之二
|
8月前
|
消息中间件 Java Kafka
Hyperledger Fabric 通道配置文件和容器环境变量详解
Fabric 节点的主配置路径为 FABRIC_CFG_PATH 环境变量所指向路径(默认为 /etc/hyperledger/fabric)。在不显式指定配置路径时,会尝试从主配置路径下查找相关的配置文件。
209 0
|
Java API 区块链
Hyperledger Fabric 2.x Java区块链应用
在上一篇文章中分享了智能合约的安装并使用cli客户端进行合约的调用;本文将使用Java代码基于fabric-gateway-java进行区块链网络的访问与交易,并集成SpringBoot框架。
730 0
Hyperledger Fabric 2.x Java区块链应用