操作版本v1.0.0
Fabric更新很快,但是基本的流程还是如本文详解的流程。
本人能力有限,如有错误的地方,还望不吝指教。
注意 先搭建好基础环境,再执行下面的步骤。
git clone https://github.com/hyperledger/fabric-samples.git
cd fabric-samples
下载完成后,会有如下文件,这次我们使用first-network
来部署网络:
cd first-network
./byfn.sh -m up
看到下面输出,就说明网络启动成功了。它会自动执行了如下几个操作:1. 创建区块链网络(4个peer+1个order)2. 创建channel 把peer加入到channel中 3. 部署/ 执行chaincode 。
.......
========= All GOOD, BYFN execution completed ===========
_____ _ _ ____
| ____| | \ | | | _ \
| _| | \| | | | | |
| |___ | |\ | | |_| |
|_____| |_| \_| |____/
byfn.sh
是demo中封装好的自动化部署脚本,我们来一步一步分析它都执行了哪些操作。
./byfn.sh -m up
最终会执行networkUp
:
function networkUp () {
if [ ! -d "crypto-config" ]; then
generateCerts # 1
replacePrivateKey #2
generateChannelArtifacts # 3
fi
CHANNEL_NAME=$CHANNEL_NAME TIMEOUT=$CLI_TIMEOUT docker-compose -f $COMPOSE_FILE up -d 2>&1 #4
if [ $? -ne 0 ]; then
echo "ERROR !!!! Unable to start network"
docker logs -f cli
exit 1
fi
docker logs -f cli
}
它都做了什么:
cryptogen
根据crypto-config.yaml
文件,为网络中的每个节点以及组织生成证书/秘钥。configtxgen
根据configtx.yaml
文件,生成通道所需资料 。docker-compose
运行docker-compose-cli.yaml
里面定义的服务,每个服务都是运行在独立的docker容器中。此时网络就启动了,通过docker ps
查看下当前的网络 :
fa5e433e52d3 hyperledger/fabric-tools "/bin/bash -c './s..." 31 minutes ago Up 31 minutes cli
5f767fa25e10 hyperledger/fabric-orderer "orderer" 31 minutes ago Up 31 minutes 0.0.0.0:7050->7050/tcp orderer.example.com
2d4c09ceed5f hyperledger/fabric-peer "peer node start" 31 minutes ago Up 31 minutes 0.0.0.0:8051->7051/tcp, 0.0.0.0:8053->7053/tcp peer1.org1.example.com
6d865dbcc698 hyperledger/fabric-peer "peer node start" 31 minutes ago Up 31 minutes 0.0.0.0:9051->7051/tcp, 0.0.0.0:9053->7053/tcp peer0.org2.example.com
e1f2ebe3f527 hyperledger/fabric-peer "peer node start" 31 minutes ago Up 31 minutes 0.0.0.0:7051->7051/tcp, 0.0.0.0:7053->7053/tcp peer0.org1.example.com
b74aed9e23f2 hyperledger/fabric-peer "peer node start" 31 minutes ago Up 31 minutes 0.0.0.0:10051->7051/tcp, 0.0.0.0:10053->7053/tcp peer1.org2.example.com
/scripts/script.sh
脚本其中cli
服务是最后启动的,启动成功后会自动执行/scripts/script.sh
这个脚本(cli服务中配置了 command: /bin/bash -c './scripts/script.sh ${CHANNEL_NAME}; sleep $TIMEOUT'
)。
脚本了主要做了如下操作:
## 1.创建channel
echo "Creating channel..."
createChannel
## 2.把所有的peer加入到channel中
echo "Having all peers join the channel..."
joinChannel
## 3.设置每个组织中的锚点peer
echo "Updating anchor peers for org1..."
updateAnchorPeers 0
echo "Updating anchor peers for org2..."
updateAnchorPeers 2
## 4.在peer0 peer2节点上安装chaincode
echo "Installing chaincode on org1/peer0..."
installChaincode 0
echo "Install chaincode on org2/peer2..."
installChaincode 2
# 5. 在peer2节点上初始化chaincode
echo "Instantiating chaincode on org2/peer2..."
instantiateChaincode 2
# 6.在Peer0节点上执行chaincode的查询操作
echo "Querying chaincode on org1/peer0..."
chaincodeQuery 0 100
# 7.在Peer0节点上执行chaincode的invoke操作
echo "Sending invoke transaction on org1/peer0..."
chaincodeInvoke 0
## 8. 在peer3上安装chaincode
echo "Installing chaincode on org2/peer3..."
installChaincode 3
# 9.在Peer3上执行查询操作,查看结果是否为90
echo "Querying chaincode on org2/peer3..."
chaincodeQuery 3 90
如果要自己执行channel/chaincode相关操作该如何操作呢? 先执行./byfn.sh -m down
关闭网络,再禁止自动执行/scripts/script.sh
脚本。
./byfn.sh -m up
完成后,在另外一个命令面板里 docker ps
查看网络(两个组织,每个组织两个peer):
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
91203e17dff2 hyperledger/fabric-tools "/bin/bash" 37 seconds ago Up 35 seconds cli
50eed8db37e5 hyperledger/fabric-peer "peer node start" 46 seconds ago Up 38 seconds 0.0.0.0:10051->7051/tcp, 0.0.0.0:10053->7053/tcp peer1.org2.example.com
fccc67250e79 hyperledger/fabric-peer "peer node start" 46 seconds ago Up 38 seconds 0.0.0.0:8051->7051/tcp, 0.0.0.0:8053->7053/tcp peer1.org1.example.com
04ba77fe9c07 hyperledger/fabric-peer "peer node start" 46 seconds ago Up 37 seconds 0.0.0.0:9051->7051/tcp, 0.0.0.0:9053->7053/tcp peer0.org2.example.com
e2a14716ff5b hyperledger/fabric-orderer "orderer" 46 seconds ago Up 37 seconds 0.0.0.0:7050->7050/tcp orderer.example.com
b88215b96968 hyperledger/fabric-peer "peer node start" 46 seconds ago Up 39 seconds 0.0.0.0:7051->7051/tcp, 0.0.0.0:7053->7053/tcp peer0.org1.example.com
docker exec -it cli bash
执行结果:
在cli里设置org1的peer0的环境变量,直接复制到命令面板中:
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected]/msp
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
执行结果:
在org1的peer0节点上创建channel:
peer channel create -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/channel.tx --tls $CORE_PEER_TLS_ENABLED --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
执行结果:
把4个peer加入到channel中,因为每个peer节点的环境变量是不一样的,所以先要配置正确的环境变量,再执行加入命令。
设置org1-peer0
的环境变量:
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected]/msp
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
加入到channel:
peer channel join -b mychannel.block
执行结果:
其他节点的操作也是如此,其他节点环境变量如下:
org1-peer1
:
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected]/msp
CORE_PEER_ADDRESS=peer1.org1.example.com:7051
org2-peer0
:
CORE_PEER_LOCALMSPID="Org2MSP"
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
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/[email protected]/msp
CORE_PEER_ADDRESS=peer0.org2.example.com:7051
org2-peer1
:
CORE_PEER_LOCALMSPID="Org2MSP"
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
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/[email protected]/msp
CORE_PEER_ADDRESS=peer1.org2.example.com:7051
我们使用demo中的chaincode,需要指定chaincode的名字 /版本/路径
peer chaincode install -n mycc -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02
结果:
chaincode文件是这么映射的:
peer chaincode instantiate -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}' -P "OR ('Org1MSP.member','Org2MSP.member')"
结果:
注意以下几点:
-c '{"Args":["init","a", "100", "b","200"]}'
Init
方法,分别向a账户/b账户存入100/200元。-P "OR ('Org1MSP.member','Org2MSP.member')"
-p
参数来指定的。这句话的意思是交易时必须通过org1或者org2的一个成员的支持(背书),更多语法详情看这里。dev
-节点
-chaincode名称
-chaincode 版本
5. chaincode的初始化只需要一次,但是chaincode需要在channel内的所有peer上进行安装。目前我们只是在org2-peer1
节点上安装了chaincode,按照前面的环境变量,切换到其他对应的节点,来执行chaincode的安装。
查询a的余额:
peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
查询结果:
b向a转账50元:
peer chaincode invoke -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc -c '{"Args":["invoke","b","a","50"]}'
执行结果:
再次查询余额:
从v1.0.0开始,我们写的chaincode只要实现ChaincodeStubInterface
接口即可:
type Chaincode interface {
// 初始化 或者 更新chaincode的时候 会执行这个操作
Init(stub ChaincodeStubInterface) pb.Response
// 定义 CURD的方法
Invoke(stub ChaincodeStubInterface) pb.Response
// 旧版本有个 Query 方法,现在整合到了Invoke里面
}
我们看下chaincode_example02.go
是怎么实现的:
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
fmt.Println("ex02 Init")
_, args := stub.GetFunctionAndParameters()
.......
return shim.Success(nil)
}
// 定义了三个方法
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
fmt.Println("ex02 Invoke")
function, args := stub.GetFunctionAndParameters()
if function == "invoke" {
//a-转账->b x元
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\"")
}
......
所有的逻辑都最终会进入Invoke
来分发,比如删除b
账户的操作:
peer chaincode invoke -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc -c '{"Args":["delete","b"]}'
其他方法的演示只需要把'{"Args":["delete","b"]}'
里面的delelete
换成你定义的方法,后面带上合适的参数即可。