第4步:启动基础架构(容器)
我们使用docker-compose来配置我们的超级分层结构网络,配置文件为docker-compose.yaml。
$ docker-compose -f docker-compose.yaml up -d
执行命令后,我们可以检查正在运行的容器,这些容器应该与我们的网络设置相对应。 将有五个运行容器
- 一个订货人
- 三个对等体,对应每一个组织
- 一个CLI
请注意,CLI将用于启动命令。
$ docker ps
步骤5:在基础架构上配置通道
现在我们有容器在运行。 我们开始在对等体上配置通道。
所有命令都是从带有docker exec的CLI容器发出的。 CLI容器在peer0.org1上具有默认设置。 为方便访问,我们将启动三个新终端(或将终端分成三个,如下所示),每个终端用于组织的同行。 我们的命令稍后将在终端上发布。
在步骤3.1(在crypto-config目录中)创建的那些文件被复制到相应的容器中。
在步骤3.2中创建的那些文件(在channel-artifacts目录中)被复制到容器,它们用于配置通道。
准备多个终端
我们为Org1,Org2和Org3指定了三个新终端。
对于Org1(默认)
$ docker exec -it cli bash
对于Org2(指定Org2的环境变量)
$ docker exec -e “CORE_PEER_LOCALMSPID=Org2MSP” -e “CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt” -e “CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp” -e “CORE_PEER_ADDRESS=peer0.org2.example.com:7051” -it cli bash
对于Org3(指定Org3的环境变量)
$ docker exec -e “CORE_PEER_LOCALMSPID=Org3MSP” -e “CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt” -e “CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp” -e “CORE_PEER_ADDRESS=peer0.org3.example.com:7051” -it cli bash
为便于访问,请在所有三个终端中导出此参数ORDERER_CA。
# export ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
以下是Org1,Org2和Org3的三个终端,从上到下。
配置通道的流程
步骤5.1 ChannelAll
创建通道块文件(任何终端)
# peer channel create -o orderer.example.com:7050 -c channelall -f /opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts/channelall.tx — tls — cafile $ORDERER_CA
现在创建了一个文件channelall.block。
将三个对等点加入此通道并更新每个对等方的锚点对等方。
Org1终端
# peer channel join -b channelall.block — tls — cafile $ORDERER_CA
# peer channel update -o orderer.example.com:7050 -c channelall -f /opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts/Org1MSPanchors_channelall.tx — tls — cafile $ORDERER_CA
Org2终端
# peer channel join -b channelall.block — tls — cafile $ORDERER_CA
# peer channel update -o orderer.example.com:7050 -c channelall -f /opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts/Org2MSPanchors_channelall.tx — tls — cafile $ORDERER_CA
Org3终端
# peer channel join -b channelall.block — tls — cafile $ORDERER_CA
# peer channel update -o orderer.example.com:7050 -c channelall -f /opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts/Org3MSPanchors_channelall.tx — tls — cafile $ORDERER_CA
步骤5.2:Channel12
创建通道块文件(任何终端)
# peer channel create -o orderer.example.com:7050 -c channel12 -f /opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts/channel12.tx — tls — cafile $ORDERER_CA
现在创建了一个文件channel12.block。
将两个对等方加入此通道并更新每个对等方的锚点对等方。
Org1终端
# peer channel join -b channel12.block — tls — cafile $ORDERER_CA
# peer channel update -o orderer.example.com:7050 -c channel12 -f /opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts/Org1MSPanchors_channel12.tx — tls — cafile $ORDERER_CA
Org2终端
# peer channel join -b channel12.block — tls — cafile $ORDERER_CA
# peer channel update -o orderer.example.com:7050 -c channel12 -f /opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts/Org2MSPanchors_channel12.tx — tls — cafile $ORDERER_CA
第6步:安装Simple Asset Chaincode(SACC)
现在我们的网络已经启动并运行,可以部署链代码。
我们正在使用fabric-samples中的SACC。 这是链码:
/*
* Copyright IBM Corp All Rights Reserved
*
* SPDX-License-Identifier: Apache-2.0
*/
package main
import (
"fmt"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
)
// SimpleAsset implements a simple chaincode to manage an asset
type SimpleAsset struct {
}
// Init is called during chaincode instantiation to initialize any
// data. Note that chaincode upgrade also calls this function to reset
// or to migrate data.
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
// Get the args from the transaction proposal
args := stub.GetStringArgs()
if len(args) != 2 {
return shim.Error("Incorrect arguments. Expecting a key and a value")
}
// Set up any variables or assets here by calling stub.PutState()
// We store the key and the value on the ledger
err := stub.PutState(args[0], []byte(args[1]))
if err != nil {
return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0]))
}
return shim.Success(nil)
}
// Invoke is called per transaction on the chaincode. Each transaction is
// either a 'get' or a 'set' on the asset created by Init function. The Set
// method may create a new asset by specifying a new key-value pair.
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
// Extract the function and args from the transaction proposal
fn, args := stub.GetFunctionAndParameters()
var result string
var err error
if fn == "set" {
result, err = set(stub, args)
} else { // assume 'get' even if fn is nil
result, err = get(stub, args)
}
if err != nil {
return shim.Error(err.Error())
}
// Return the result as success payload
return shim.Success([]byte(result))
}
// Set stores the asset (both key and value) on the ledger. If the key exists,
// it will override the value with the new one
func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
if len(args) != 2 {
return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
}
err := stub.PutState(args[0], []byte(args[1]))
if err != nil {
return "", fmt.Errorf("Failed to set asset: %s", args[0])
}
return args[1], nil
}
// Get returns the value of the specified asset key
func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
if len(args) != 1 {
return "", fmt.Errorf("Incorrect arguments. Expecting a key")
}
value, err := stub.GetState(args[0])
if err != nil {
return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err)
}
if value == nil {
return "", fmt.Errorf("Asset not found: %s", args[0])
}
return string(value), nil
}
// main function starts up the chaincode in the container during instantiate
func main() {
if err := shim.Start(new(SimpleAsset)); err != nil {
fmt.Printf("Error starting SimpleAsset chaincode: %s", err)
}
}
这个链码的逻辑很简单。
- 实例化chaincode时,执行Init()。预期有两个参数,它们对应于“键”和“值”。
- 此键/值对存储在分类帐中(使用PutState)。
- 实例化后,事务由Invoke()处理。该参数由函数(或命令)和参数组成。
- 如果function = set,则需要两个参数。它是一个全新的键/值,或者如果键存在则覆盖该值。调用函数set(),结果存储在分类帐中(使用PutState)
- 如果function = anything(例如get),则预期一个参数为key。函数get()被调用。 GetState用于从分类帐中获取状态。如果key存在,则返回该值。否则显示“未找到资产”。
- 通过安装链代码,它将链代码加载到每个对等方。请注意,在实例化链代码之前,链代码尚不可用。
要在每个终端上安装链码。
# peer chaincode install -n sacc -p github.com/chaincode/sacc -v 1.0
我们应该看到下面一条消息:
现在所有节点都安装了链代码。 我们已准备好实例化此链代码。
第7步:在ChannelAll上实例化并与Chaincode交互
我们将首先关注ChannelAll。
在Org1终端上,我们在ChannelAll上实例化代码。
# peer chaincode instantiate -o orderer.example.com:7050 — tls — cafile $ORDERER_CA -C channelall -c ‘{“Args”:[“a”, “100”]}’ -n sacc -v 1.0 -P “OR(‘Org1MSP.peer’, ‘Org2MSP.peer’, ‘Org3MSP.peer’)”
我们将初始键/值对设置为/ 100。 除此之外,我们还指定了支持政策:需要三个组织之一(使用OR)。
我们将在ChannelAll中查询键“a”的值。
在Org1终端上
# peer chaincode query -C channelall -n sacc -c ‘{“Args”:[“get”,”a”]}’
现在在Org2终端上
# peer chaincode query -C channelall -n sacc -c ‘{“Args”:[“get”,”a”]}’
在Org3终端上,
# peer chaincode query -C channelall -n sacc -c ‘{“Args”:[“get”,”a”]}’
我们现在看到三个对等点获得了相同的值。 他们共享同一个分类帐。
步骤8:在Channel12上实例化并与Chaincode交互
现在我们在Channel12上实例化相同的SACC。
在Org1终端上
# peer chaincode instantiate -o orderer.example.com:7050 — tls — cafile $ORDERER_CA -C channel12 -c ‘{“Args”:[“b”, “200”]}’ -n sacc -v 1.0 -P “OR(‘Org1MSP.peer’, ‘Org2MSP.peer’)”
这次我们将初始键/值对设置为b / 200。 赞同政策需要得到任何一个组织的认可。
然后我们再次开始Org1终端。
# peer chaincode query -C channel12 -n sacc -c ‘{“Args”:[“get”,”b”]}’
然后在Org2终端上。
# peer chaincode query -C channel12 -n sacc -c ‘{“Args”:[“get”,”b”]}’
如果我们在Org3终端上发出相同的命令,我们会看到访问被拒绝的消息。 这是因为Org3不在第12通道。
# peer chaincode query -C channel12 -n sacc -c ‘{“Args”:[“get”,”b”]}’
如果我们试图在Channel12上得到键“a”的值,我们发现没有定义“a”。 每个渠道都有自己的分类帐,并且不共享状态。
在Org1或Org2终端上,
# peer chaincode query -C channel12 -n sacc -c ‘{“Args”:[“get”,”a”]}’
第9步:清理一切
一切都完成后,最好清理所有内容,包括docker容器和为此演示生成的文件。
要关闭所有容器并将其删除,我们使用docker-compose删除通过配置文件创建的容器。
$ docker-compose -f docker-compose.yaml down
仍然有一些停止的容器(从docker ps -a看到它)。 它们是实例化后的链代码。 我们也将删除它们。
$ docker ps -a
$ docker rm $(docker ps -aq)
在我们删除通道工件时,建议保留channel-artifacts目录,因为下一个演示需要这个目录(或者您需要再次创建它)。
$ rm channel-artifacts/*
最后我们删除了crypto-config的整个目录。 该目录将在下次由cryptogen创建。
$ rm -r crypto-config
总结
我们已经完成了针对三组织Hyperledger Fabric网络的多通道设置。 我们从配置文件开始,根据我们的设置打开我们的网络。 通过在不同的通道上部署相同的链代码,我们观察到每个通道都有自己的分类帐,并且状态不会相互共享。