
与我的另一篇文章结合着使用
区块链环境配置教程 | CodeCook (dettry.github.io)
一、准备
在Vmware里有一个干净的Debian 11系统的虚机
- 虚机名:deb-hlf 2核,4G内存,已安装JDK8,没有防火墙
- 静态IP:192.168.138.31,网关、DNS:192.168.138.2
- Vmware主机IP:192.168.138.1
- 虚机通过主机网关访问外网OK,主机和同网段其他虚机通过IP访问本虚机OK
- 主机可通过Putty密码,密钥方式远程连接虚机
- 用户root / 4geT%not,zadmin / d00r%Key 是sudo组成员
- 软件源:清华大学,华为,中科大
安装Hyperledge Fabric过程参考:https://hyperledger-fabric.readthedocs.io/en/latest/getting_started.html
说明:
1.1 关于操作系统
1. 硬件
本例中原来分配给虚机的硬件配置是1核2G
实际运行中发现速度比较快,内存使用率从来没超过60%,但最终还是把硬件配置调成了2核4G,以防万一。
2. Debian
本例使用了Debian 11,基础安装,没有GUI,这也是硬件开销非常小的原因。
CentOS除了安装软件源apt和yum的区别外,其他的linux命令与Debian基本一样。所以后面的内容基于CentOS实现应该没有任何问题。Debian默认没有安装防火墙,如果使用CentOS需要关闭防火墙。
采用Debian的原因,首先是因为Hyperledge官网的教程是基于Debian的。其次CentOS从版本8之后已经丧失了开源Linux服务器的领导地位,Debian被认为是最好的继任者,其稳定性不弱于CentOS。再次,Debian的基础系统比CentOS最小化安装更节省硬件资源。
Debian的默认文本编辑器是nano,比vi更容易上手。如果习惯用vi或vim的话,在Debian里需要先删除系统里的vi tiny版,安装完整版。
apt remove vim-common -y
apt install vim -y
最后,Debian里操作软件源,软件安装,建议使用apt而不是apt-get或dpkg
1.2 关于远程连接虚机
建议用Putty远程连接虚机,配置好root密钥连接。
1. Debian控制台的字体太小,难看,Putty连接的控制台字体看的舒服得多。且Putty终端控制台窗口可以调整大小。Putty控制台窗口可以在命令行输入中文。
2. Putty控制台有滚动条,大段的输出可以翻看。从Putty控制台鼠标选中大段文字,自动复制到剪贴板,然后可以粘贴到Word或其他Windows编辑工具中。从Word或网页复制的内容,在Putty控制台里点鼠标右键可自动粘贴到光标处,或粘贴到nano里。
3. Putty可以开启多个root终端连接到同一个虚机。后面的步骤里除了主终端窗口外,还需要开启另一个终端窗口,观察docker运行日志。
二、安装前置软件
1. 安装Git
1 | apt install git -y |
2. 安装curl
1 | apt install curl -y |
3. 安装Docker
1 | apt install docker-compose -y |
安装完毕后,确认Docker和Docker Compose的版本
1 | docker --version |
建议重启一下,虽然不需要
4. 安装Go
5. 安装JQ
1 | apt install jq |
三、安装Fabric
参考:https://hyperledger-fabric.readthedocs.io/en/latest/install.html
1. 准备工作目录
mkdir -p /opt/go/src/github.com/zlz8x8
cd /opt/go/src/github.com/zlz8x8
目录创建在 /opt 下,这里一般是空的。zlz8x8 是我在github 的用户名
2. 下载安装脚本
curl -sSLO https://raw.githubusercontent.com/hyperledger/fabric/main/scripts/install-fabric.sh && chmod +x install-fabric.sh
3. 执行安装脚本
./install-fabric.sh
没有参数,表示默认 docker binary samples,正是我们要的
主机全程开VPN,否则无法连接github,大概500M内容,VPN如果有15~30分钟稳定即可完成安装。
中间如果VPN不稳定,失败后重新执行脚本,下载过的内容会跳过,下载没有完成的会继续完成。
四、使用Fabric test network
4.1 开始之前
参考:https://hyperledger-fabric.readthedocs.io/en/latest/test_network.html
使用Fabric test network,包括:
- 两个peer orgnazation和一个ordering organization
- 为简单化,配置了一个单独节点Raft ordering service
- 为简单化,没有部署TLS CA,所有证书由root CA颁发
- 这个sample network用Docker Compose部署了一个Fabric网络。因为在Docker Compose network里的所有节点是隔离开(孤立)的,test network没有配置连接其他运行中的Fabric节点。
4.2 启动test network
1. 启动test network
工作目录 /opt/go/src/github.com/zlz8x8
1 | cd fabric-samples/test-network |
就启动了,下面的命令关闭
1 | ./network.sh down |
2. 检查test network各组件
1 | docker ps -a |
输出可以看到fabric-tools,fabric-peer,fabric-orderer,fabric-peer和各自使用的端口。
peer是Fabric网络的基本组件,peer保存区块链账本并在交易transaction提交到账本前进行验证。peer运行智能合约smart contract(里面包含了商业逻辑business logic),用来管理区块链上的资产asset。
每个peer必须属于一个organization。在test network里,每个organization只有一个peer。从上面图可看出,两个organization是org1.example.com和org2.example.com,对应的两个peer分别是peer0.org1.example.com和peer0.org2.example.com。
每个Fabric网络包括一个排序服务ordering service,…
这个sample network使用了单个节点Raft ordering service,由排序节点orderer的组织来操作,就是上面的 orderer.example.com
4.3 创建通道
创建三个通道:
1 | ./network.sh createChannel |
第一个通道没有指定名字,默认创建了mychannel,后面用到的。
上面的命令指定了两个通道的名字。命名规则:ASCII小写字母数字,.,-,小于250字符,开头为字母。如果不指定名字./network.sh createChannel,会自动生成一个名字。
这三个通道都是org1和org2之间的,并且org1和org2的peer都被邀请进了通道,每个通道有分开的区块链账本。
4.4 在channel中执行chaincode
代码是go代码,执行过程中要下载相关依赖,要开VPN
先关机poweroff,做个快照,以防万一
当前目录:/opt/go/src/github.com/zlz8x8/fabric-samples/test-network
经测试发现,当./network.sh down关闭网络之后,再./network.sh up,所有的channel就都没有了。这次起网络和创建channel同时进行 ./network.sh up createChannel
然后执行chaincode,需要主机开启VPN,执行过程中要从github和golan.org下载,大概也就5分钟不到。
./network.sh deployCC -ccn basic -ccp …/asset-transfer-basic/chaincode-go -ccl go
从输出看,chaincode在通道上提交了,并在peer0.org1和peer0.org2上都成功
4.5 与网络交互
在网络起来后,可以用pear CLI(命令行界面)与网络进行交互。peer CLI可以调用已部署的智能合约,更新通道,或安装并部署新的智能合约。
工作路径:/opt/go/src/github.com/zlz8x8
test network路径:/opt/go/src/github.com/zlz8x8/fabric-samples/test-network
将此路径定义为 $HLF_TEST_PATH,以下路径以此为基准。
CLI路径为 $HLF_TEST_PATH/…/bin,这个路径需要放到 $PATH里
配置文件core.yaml所在路径 $FABRIC_CFG_PATH=$HLF_TEST/…/config/ 需要定义在 /etc/profile 里。
nano /etc/profile 在文件最后添加如下内容
1 | export HLF\_TEST\_PATH=/opt/go/src/github.com/zlz8x8/fabric-samples/test-network |
保存后,更新系统变量 source /etc/profile,然后 echo $HLF_TEST_PATH看看系统变量生效没有。
以下默认在 /opt/go/src/github.com/zlz8x8/fabric-samples/test-network 路径工作,以下的命令都以此路径为基准,下文中出现的 ${PWD} 代表的就是这个路径。
命令行输入如下命令,设置系统变量,后面的peer命令就是在Org1上运行的了
这些系统变量没有写在 /etc/profile 里面,是临时的,系统关闭或重启就没有了。可以在该目录下新建一个文件setOrgEnv1,把下面的命令放在里面,需要执行这些命令的时候只需要执行source setOrgEnv1就可以了。
因此,此时执行 source setOrgEnv1 即可,或执行下面命令:
1 | export CORE\_PEER\_TLS\_ENABLED=true |
输入下面命令初始化资产的账本
1 | 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 mychannel -n basic --peerAddresses localhost:7051 --tlsRootCertFiles "$*{PWD}*/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "$*{PWD}*/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt" -c '{"function":"InitLedger","Args":[]}' |
命令虽然很长,执行很快,不到1秒,看到输出 status:200 说明成功了
现在可以通过CLI查询账本,运行下面命令查询添加到通道账本的资产
1 | peer chaincode query -C mychannel -n basic -c '{"Args":["GetAllAssets"]}' |
执行成功,查询结果输出如下:注意asset6的owner是Michel
当一个网络成员希望转移或更改账本中的某项资产,就可以调用链码chaincode
1 | 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 mychannel -n basic --peerAddresses localhost:7051 --tlsRootCertFiles "$*{PWD}*/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "$*{PWD}*/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt" -c '{"function":"TransferAsset","Args":["asset6","Christopher"]}' |
执行成功,输出如下 status:200
因为背书endorsement策略,资产转移 asset-tranfer (basic) 链码需要Org1和Org2的签名,因此链码调用需要用 --peerAddresses 标签指向 peer0.org1.example.com 和 peer0.org2.example.com 。另外,网络使用了TLS连接,命令需要用 --tlsRootCertFiles 引用TLS证书。
在上述链码执行后,我们可以用另一个查询取看看这次调用如何改变了账本里的资产。已经使用org1进行了查询,下面使用org2 peer进行查询。
命令行输入如下命令,设置系统变量,后面的peer命令就是在Org2上运行的了
可以在该目录下新建一个文件setOrgEnv2,把下面的命令放在里面,需要执行这些命令的时候只需要执行source setOrgEnv2就可以了。
执行 source setOrgEnv2 即可,或执行下面命令:
1 | export CORE\_PEER\_TLS\_ENABLED=true |
相同的系统变量名,不同的值,会覆盖之前的Org1设置。
然后执行下面命令查询资产转移(跑在peer0.org2.example.com上的basic链码)
1 | peer chaincode query -C mychannel -n basic -c '{"Args":["ReadAsset","asset6"]}' |
执行成功,可以看到asset6的owner已经变成了Christopher
4.6 关闭网络
使用下面命令关闭网络。这将停止并移除node和chaincode的容器,删除organization的加密内容,移除链码在Docker Registry里的镜像image,移除之前运行的通道和docker volume。
./network.sh down
4.7 启动有CA的网络
Hyperledge Fabric使用PKI(Public key infrastructure,公钥基础设施)验证所有的网络参与者。每个节点node,网络管理员和用户提交交易都必须提供公钥和私钥进行身份验证。这些证书由CA颁发。
默认情况下,前面运行的 ./network.sh up 提供了加密工具cryptogen,供开发和测试。观察 ./network.sh up 的输出日志,可以看到加密工具为Org1,Org2和Orderer Org创建了证书和密钥。
test network也提供了选项在启动网络时使用CA(Certificate Authority,证书中心)。在生产网络production network,每个organization都运行一个CA(称为 中间CA)为其组织颁发证书。这些CA共享一个根信任root trust。虽然使用CA比使用cryptogen花费更多的时间,但分布式生产网络需要CA部署。CA还可以添加新的客户端身份认证和为应用程序创建证书和私钥。
下面命令启动带CA的网络,第二个命令同时创建通道,第三个命令把执行命令的输出写到文件out.log中,方便查看,加参数 -a 表示末尾添加模式而不是删除重写模式。
1 | ./network.sh up -ca |
如果是用Putty连接虚机,执行命令后可以看到完整输出(窗口有滚动条)。可以看到创建网络后第一件是就是创建了3个CA。
用下面命令查看Org1的MSP(Membership service provider)文件夹。执行发现没有tree命令,安装 apt install tree,然后执行下面命令。
1 | tree organizations/peerOrganizations/org1.example.com/users/Admin**@org1**.example.com/ |
可以看到 *.pem 就是密钥文件,keystore里保存的也是密钥。
五、向通道部署智能合约
首先进入工作路径,启动网络:
1 | cd /opt/go/src/github.com/zlz8x8/fabric-samples/test-network |
设置Logspout,可选。
如果是直接系统登录情况下是无法打开第二个终端窗口的。用Putty连接虚机,进行工作操作,然后再打开一个Putty连接到同一个虚机,就有两个终端窗口了。从第二个终端窗口进入工作目录,开启logspout
1 | cd /opt/go/src/github.com/zlz8x8/fabric-samples/test-network |
如果是第一次运行,需要拉取相应的image,似乎不需要VPN,很快
然后光标就停在一长串数字下面了,这是正常的,等待输出。
从第一个窗口工作路径下运行 docker stop logspout 就可以停止第二个窗口的等待状态,进入光标。从第二个窗口再次运行 ./monitordocker.sh fabric_test 就再次监视docker的状态,等待输出。
5.1 打包智能合约
本例中的智能合约有三种语言go,javascript,typescript写的代码。go语言的包被用在后面的步骤“向通道安装智能合约”中,javascript语言的包被用在后面的步骤“升级智能合约”中,typescript的包没有使用,要用的话步骤和go和javascript是一样的。
5.1.1 Go
在工作路径:/opt/go/src/github.com/zlz8x8/fabric-samples/test-network
1 | cd ../asset-transfer-basic/chaincode-go |
可以看一下这个目录下 go.mod 的内容,智能合约的部分是如何写的。
GO111MODULE=on go mod vendor
开始下载依赖包,编译,应该需要VPN,大概1秒钟,生成的模块在vendor目录下。
1 | cd ../../test-network |
在工作路径 /opt/go/src/github.com/zlz8x8/fabric-samples/test-network 下就生成了一个basic.tar.gz包。打包go完成。
5.1.2 Javascript
安装nodejs和npm,大概下载500M,设置了清华源,很快
1 | apt install nodejs npm |
设置npm代理,否则运行时会卡死
1 | npm config set registry https://registry.npm.taobao.org |
在工作路径:/opt/go/src/github.com/zlz8x8/fabric-samples/test-network
1 | cd ../asset-transfer-basic/chaincode-javascript |
可以看一下 lib/assetTransfer.js 的内容,智能合约的部分是如何写的。
1 | npm install |
会新生成一个node_modules目录,生成的模块在此目录下
1 | cd ../../test-network |
不执行打包命令,后面会执行打包命令为升级包2.0。要执行的话,把go的打包命令中的–path …/asset-transfer-basic/chaincode-javascript/ 换了,–lang node 换了,并且要更改包名,否则会覆盖前面生成的 basic.tar.gz
5.1.3 Typescript
在工作路径:/opt/go/src/github.com/zlz8x8/fabric-samples/test-network
1 | cd ../asset-transfer-basic/chaincode-typescript |
可以看一下 src/assetTransfer.ts 的内容,智能合约部分是如何写的。
1 | npm install |
会新生成一个node_modules目录,生成的模块在此目录下
1 | cd ../../test-network |
不执行打包命令,要执行的话,把go的打包命令中的–path …/asset-transfer-basic/chaincode-typescript/ 换了,–lang node 换了,并且要更改包名,否则会覆盖前面生成的 basic.tar.gz
5.2 安装链码包
在工作路径:/opt/go/src/github.com/zlz8x8/fabric-samples/test-network
确认目录下有 basic.tar.gz,就是之前打包的go链码。关闭网络后,这个文件就被删除了,需要重新执行打包步骤。可以 cp basic.tar.gz basic.tar.gz.bak 留个备份。下次启动网络,可以跳过打包步骤,从备份拷贝回来cp basic.tar.gz.bak basic.tar.gz
确认网络已启动,管道已创建
确认有两个命令行窗口,一个是主窗口,另一个运行了./monitordocker.sh fabric_test 在等待输出。
首先在org1 peer上安装链码,设置系统org1参数。执行 source setOrgEnv1 即可,或执行下面命令:
1 | export CORE\_PEER\_TLS\_ENABLED=true |
执行下面命令。可以看到监视窗口有大量输出,可以看到安装链码的是 peer0.org1
peer lifecycle chaincode install basic.tar.gz
主窗口只输出结果,和监视窗口最后的输出是一样的。Putty窗口鼠标选中的内容就自动复制到剪贴板了,到这里粘贴如下:
2022-12-10 21:34:34.245 CST 0001 INFO [cli.lifecycle.chaincode] submitInstallProposal -> Installed remotely: response:<status:200 payload:“\nJbasic_1.0:56069c8c46fe01a7837a218a6e2ab49a2b6c4a715c95ab2ab321a863b642d021\022\tbasic_1.0” >
2022-12-10 21:34:34.260 CST 0002 INFO [cli.lifecycle.chaincode] submitInstallProposal -> Chaincode code package identifier: basic_1.0:56069c8c46fe01a7837a218a6e2ab49a2b6c4a715c95ab2ab321a863b642d021
然后设置org2系统变量,在org2 peer上安装链码。执行 source setOrgEnv2 即可,或执行下面命令:
1 | export CORE\_PEER\_LOCALMSPID="Org2MSP" |
执行下面命令。可以看到监视窗口有大量输出,主窗口输出和上面一样,安装成功。
1 | peer lifecycle chaincode install basic.tar.gz |
5.3 确认链码定义
紧跟上面步骤,现在身份是org2,首先查询已安装的链码:
1 | peer lifecycle chaincode queryinstalled |
输出如下:
Installed chaincodes on peer:
Package ID: basic_1.0:56069c8c46fe01a7837a218a6e2ab49a2b6c4a715c95ab2ab321a863b642d021, Label: basic_1.0
这里的Package ID在确认链码的时候要用,我们用它设置一个系统变量:
1 | export CC\_PACKAGE\_ID=basic\_1.0:56069c8c46fe01a7837a218a6e2ab49a2b6c4a715c95ab2ab321a863b642d021 |
设置了这个系统参数,后面org1确认链码的时候也可以用,所以org1在确认的时候就省略这一步了。
确认链码定义:
1 | peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name basic --version 1.0 --package-id $CC\_PACKAGE\_ID --sequence 1 --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" |
输出如下:
2022-12-11 17:04:26.913 CST 0001 INFO [chaincodeCmd] ClientWait -> txid [700a4f809b4baa7b7ae2debf5ffd20626c4a035436b3e7e7e6430c76d27ba4be] committed with status (VALID) at localhost:9051
上面步骤的解释(未翻译):
The command above uses the --package-id flag to include the package identifier in the chaincode definition. The --sequence parameter is an integer that keeps track of the number of times a chaincode has been defined or updated. Because the chaincode is being deployed to the channel for the first time, the sequence number is 1. When the asset-transfer (basic) chaincode is upgraded, the sequence number will be incremented to 2. If you are using the low level APIs provided by the Fabric Chaincode Shim API, you could pass the --init-required flag to the command above to request the execution of the Init function to initialize the chaincode. The first invoke of the chaincode would need to target the Init function and include the --isInit flag before you could use the other functions in the chaincode to interact with the ledger.
We could have provided a --signature-policy or --channel-config-policy argument to the approveformyorg command to specify a chaincode endorsement policy. The endorsement policy specifies how many peers belonging to different channel members need to validate a transaction against a given chaincode. Because we did not set a policy, the definition of asset-transfer (basic) will use the default endorsement policy, which requires that a transaction be endorsed by a majority of channel members present when the transaction is submitted. This implies that if new organizations are added or removed from the channel, the endorsement policy is updated automatically to require more or fewer endorsements. In this tutorial, the default policy will require a majority of 2 out of 2 and transactions will need to be endorsed by a peer from Org1 and Org2.
下面用org1来确认链码,执行 source setOrgEnv1 即可,或执行下面命令:
1 | export CORE\_PEER\_LOCALMSPID="Org1MSP" |
然后用org1确认链码定义:
1 | peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name basic --version 1.0 --package-id $CC\_PACKAGE\_ID --sequence 1 --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" |
输出如下,跟org2的输出比较一下,最后的端口号不一样了。
2022-12-11 17:48:34.122 CST 0001 INFO [chaincodeCmd] ClientWait -> txid [f6cd1365d631880f7753a7e4e91d1de34444e17659b6d5dc11869d5a7815e670] committed with status (VALID) at localhost:7051
5.4 向通道提交链码定义
紧跟上述步骤,现在peer身份是org1
下面命令查询有哪些成员确认了链码:
1 | peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name basic --version 1.0 --sequence 1 --tls --cafile "$*{PWD}*/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" --output json |
输出如下,说明org1和org2已经确认了链码:
{
“approvals”: {
“Org1MSP”: true,
“Org2MSP”: true
}
}
网络里只有org1和org2,都已经确认,现在链码定义已经可以提交到通道,用下面的命令提交链码定义:从下面的命令可以看出,以org1和org2身份提交了。
1 | peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name basic --version 1.0 --sequence 1 --tls --cafile "$*{PWD}*/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" --peerAddresses localhost:7051 --tlsRootCertFiles "$*{PWD}*/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "$*{PWD}*/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt" |
输出如下,说明org1和org2都提交了链码:
2022-12-11 17:55:17.072 CST 0001 INFO [chaincodeCmd] ClientWait -> txid [2950340b01b19046b2ed345900c6b50a32a947ff529263e728c53eea7247d718] committed with status (VALID) at localhost:9051
2022-12-11 17:55:17.088 CST 0002 INFO [chaincodeCmd] ClientWait -> txid [2950340b01b19046b2ed345900c6b50a32a947ff529263e728c53eea7247d718] committed with status (VALID) at localhost:7051
用下面的命令查询已提交的链码:
1 | peer lifecycle chaincode querycommitted --channelID mychannel --name basic --cafile "$*{PWD}*/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" |
输出如下,包含了已提交的链码的信息:
Committed chaincode definition for chaincode ‘basic’ on channel ‘mychannel’:
Version: 1.0, Sequence: 1, Endorsement Plugin: escc, Validation Plugin: vscc, Approvals: [Org1MSP: true, Org2MSP: true]
5.5 调用链码
链码已经提交到通道,可以被client application调用,并且链码会在各个加入的peer(org1和org2)上运行。
执行下面命令调用链码,在账本创建一个初始化的资产集。注意调用命令要标的(target)足够多的peer以满足背书策略,现在只有org1和org2。
1 | 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 mychannel -n basic --peerAddresses localhost:7051 --tlsRootCertFiles "$*{PWD}*/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "$*{PWD}*/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt" -c '{"function":"InitLedger","Args":[]}' |
输出如下,执行成功:
2022-12-11 18:07:14.083 CST 0001 INFO [chaincodeCmd] chaincodeInvokeOrQuery -> Chaincode invoke successful. result: status:200
用下面的命令查询刚才创建的资产(cars)集:
1 | peer chaincode query -C mychannel -n basic -c '{"Args":["GetAllAssets"]}' |
输出如下:
[{“AppraisedValue”:300,“Color”:“blue”,“ID”:“asset1”,“Owner”:“Tomoko”,“Size”:5},{“AppraisedValue”:400,“Color”:“red”,“ID”:“asset2”,“Owner”:“Brad”,“Size”:5},{“AppraisedValue”:500,“Color”:“green”,“ID”:“asset3”,“Owner”:“Jin Soo”,“Size”:10},{“AppraisedValue”:600,“Color”:“yellow”,“ID”:“asset4”,“Owner”:“Max”,“Size”:10},{“AppraisedValue”:700,“Color”:“black”,“ID”:“asset5”,“Owner”:“Adriana”,“Size”:15},{“AppraisedValue”:800,“Color”:“white”,“ID”:“asset6”,“Owner”:“Michel”,“Size”:15}]
5.6 升级智能合约
前面步骤已经安装、提交、调用了go链码。下面打包javascript链码并安装,对智能合约进行升级。
5.6.1 打包链码
前面没有执行javascript链码打包,现在执行打包命令:
1 | cd ../asset-transfer-basic/chaincode-javascript |
回到工作路径
1 | cd ../../test-network |
执行系统参数设置:
1 | export PATH=${PWD}/../bin:$PATH |
注意:前两项设置已经在“4.5 与网络交互”写在 /etc/profile 里了,开机已经有了,这里只需要执行第三行。
执行打包命令:
1 | peer lifecycle chaincode package basic\_2.tar.gz --path ../asset-transfer-basic/chaincode-javascript/ --lang node --label basic\_2.0 |
工作目录下会多一个basic_2.tar.gz,网络关闭后,这个文件会被删除,我们备份一下cp basic_2.tar.gz basic_2.tar.gz.bak,下次重启网络,可以跳过打包步骤,把备份的文件恢复回来就行了cp basic_2.tar.gz.bak basic_2.tar.gz
5.6.2 安装链码
下面用org1来安装链码,执行 source setOrgEnv1 即可,或执行下面命令:
1 | export CORE\_PEER\_TLS\_ENABLED=true |
安装链码:
1 | peer lifecycle chaincode install basic\_2.tar.gz |
查询已安装的链码:
peer lifecycle chaincode queryinstalled
输出如下,可以看到已安装了两个链码:
Installed chaincodes on peer:
Package ID: basic_2.0:a496470e6e35a564619781f41bc17eef4db685c222aa0b89ed3dfb4b668206f5, Label: basic_2.0
Package ID: basic_1.0:56069c8c46fe01a7837a218a6e2ab49a2b6c4a715c95ab2ab321a863b642d021, Label: basic_1.0
5.6.3 确认链码
用上面的链码ID设置一个系统变量,注意这里的ID要和你的查询输出里的一致:
1 | export NEW\_CC\_PACKAGE\_ID=basic\_2.0:a496470e6e35a564619781f41bc17eef4db685c222aa0b89ed3dfb4b668206f5 |
现在以org1确认链码:
1 | peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name basic --version 2.0 --package-id $NEW\_CC\_PACKAGE\_ID --sequence 2 --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" |
下面用org2来安装、确认链码,执行 source setOrgEnv2 即可,或执行下面命令:
1 | export CORE\_PEER\_LOCALMSPID="Org2MSP" |
安装链码:
1 | peer lifecycle chaincode install basic\_2.tar.gz |
确认链码:
1 | peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name basic --version 2.0 --package-id $NEW\_CC\_PACKAGE\_ID --sequence 2 --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" |
org1和org2都安装,确认了链码。检查一下basic_2是否已准备好提交了:
1 | peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name basic --version 2.0 --sequence 2 --tls --cafile "$*{PWD}*/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" --output json |
从输出来看,org1和org2都确认了,可以提交:
{
“approvals”: {
“Org1MSP”: true,
“Org2MSP”: true
}
}
5.6.4 提交链码
提交新的链码,从前面的步骤可知,下面的命令是以org1或org2的身份执行的,都会以两者的身份提交。
1 | peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name basic --version 2.0 --sequence 2 --tls --cafile "$*{PWD}*/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" --peerAddresses localhost:7051 --tlsRootCertFiles "$*{PWD}*/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "$*{PWD}*/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt" |
验证一下新的链码已经在各个peer上运行了:
1 | docker ps |
下面输出(部分),可看到,basic_2.0在org1和org2上都有:
7c2ad4f2b66d dev-peer0.org2.example.com-basic_2.0-a496470e6e35a564619781f41bc17eef4db685c222aa0b89ed3dfb4b668206f5-a394028e6792b45700d3e09782af3f93d9c18f9bfb8be6f4f761730f94faad53 “docker-entrypoint.s…” 54 seconds ago Up 53 seconds dev-peer0.org2.example.com-basic_2.0-a496470e6e35a564619781f41bc17eef4db685c222aa0b89ed3dfb4b668206f5
298b812c2dee dev-peer0.org1.example.com-basic_2.0-a496470e6e35a564619781f41bc17eef4db685c222aa0b89ed3dfb4b668206f5-5113a692e1e282cd0168c26a4ec379486b6a8dfb8fe03e6043a805669c5b2696 “docker-entrypoint.s…” 54 seconds ago Up 53 seconds dev-peer0.org1.example.com-basic_2.0-a496470e6e35a564619781f41bc17eef4db685c222aa0b89ed3dfb4b668206f5
5.6.5 调用链码
如果使用了 --init-required 参数,则需要在使用链码前调用 Init 方法。本例中不需要执行 Init,下面测试创建一个新 car
1 | 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 mychannel -n basic --peerAddresses localhost:7051 --tlsRootCertFiles "$*{PWD}*/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "$*{PWD}*/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt" -c '{"function":"CreateAsset","Args":["asset8","blue","16","Kelley","750"]}' |
输出返回值 status:200 成功了。
用下面的命令查询资产(cars)集:
1 | peer chaincode query -C mychannel -n basic -c '{"Args":["GetAllAssets"]}' |
输出如下,可以看到比“5.5 调用链码”里的输出多了一个asset8
[{“AppraisedValue”:300,“Color”:“blue”,“ID”:“asset1”,“Owner”:“Tomoko”,“Size”:5},{“AppraisedValue”:400,“Color”:“red”,“ID”:“asset2”,“Owner”:“Brad”,“Size”:5},{“AppraisedValue”:500,“Color”:“green”,“ID”:“asset3”,“Owner”:“Jin Soo”,“Size”:10},{“AppraisedValue”:600,“Color”:“yellow”,“ID”:“asset4”,“Owner”:“Max”,“Size”:10},{“AppraisedValue”:700,“Color”:“black”,“ID”:“asset5”,“Owner”:“Adriana”,“Size”:15},{“AppraisedValue”:800,“Color”:“white”,“ID”:“asset6”,“Owner”:“Michel”,“Size”:15},{“AppraisedValue”:“750”,“Color”:“blue”,“ID”:“asset8”,“Owner”:“Kelley”,“Size”:“16”}]
5.6.6 清理并关闭网络
如果开了监视窗口,监视窗口无法输入,在主窗口执行下面命令停止监视:
1 | docker stop logspout |
关闭网络:
1 | ./network.sh down |
建议这里把虚拟机拍个快照。虽然关闭网络把前面在网络里做的事情都清除了,但快照可以保留:tree,nodejs,npm的安装,自己写的文档 setOrgEnv1,setOrgEnv2,链码包的备份,写在 /etc/profile 里长期生效的系统变量。