Hyperledger-Fabric环境搭建笔记

标签:联盟链
发布时间:2018年09月11日 价值:20000.00 / 共识:24

环境搭建

前提需要电脑中存在gitdockerdocker-compose命令以及有golang开发环境。

首先创建目录存放Fabric代码,注意路径和权限,在启动服务时候会向其中写一些文件,最开始我就是没注意到这点报错了。

  1. sudo mkdir /opt/gopath/src/github.com/hyperledger/

进入刚才创建的目录后拉取代码:

  1. cd /opt/gopath/src/github.com/hyperledger
  2. git clone https://github.com/hyperledger/fabric.git

当前版本是1.1。

然后拉取所需镜像,这步速度慢需要耐心等待。

  1. sh /opt/gopath/src/github.com/hyperledger/fabric/scripts/bootstrap.sh

完成后进入/opt/gopath/src/github.com/hyperledger/fabric/examples/e2e_cli目录,执行:

  1. bash network_setup.sh up

这个文件干了几件事:

  1. 编译生成Fabric公私钥、证书的程序,程序在目录:/opt/gopath/src/github.com/hyperledger/fabric/release/linux-amd64/bin
  2. 基于configtx.yaml生成创世区块和通道相关信息,并保存在channel-artifacts文件夹。
  3. 基于crypto-config.yaml生成公私钥和证书信息,并保存在crypto-config文件夹中。
  4. 基于docker-compose-cli.yaml启动1Orderer+2org*2Peer+1CLI的Fabric容器。
  5. 在CLI启动的时候,会运行scripts/script.sh文件,这个脚本文件包含了创建Channel,加入Channel,安装Example02,运行Example02等功能。

成功后会看到提示:

  1. ===================== All GOOD, End-2-End execution completed =====================
  2. _____ _ _ ____ _____ ____ _____
  3. | ____| | \ | | | _ \ | ____| |___ \ | ____|
  4. | _| | \| | | | | | _____ | _| __) | | _|
  5. | |___ | |\ | | |_| | |_____| | |___ / __/ | |___
  6. |_____| |_| \_| |____/ |_____| |_____| |_____|

查看创建的容器:

  1. docker ps --format "{{.ID}}:{{.Names}}"
  2. a2adbbe090ce:dev-peer1.org2.example.com-mycc-1.0
  3. 434f146382d3:dev-peer0.org1.example.com-mycc-1.0
  4. f07e03296c11:dev-peer0.org2.example.com-mycc-1.0
  5. 6eee69b127ff:cli
  6. f323f3bf5b0e:orderer.example.com
  7. f8eae1488dce:kafka3
  8. 3b79d89b3928:kafka0
  9. dd5cd1d8c35a:kafka2
  10. c7953a8951f1:kafka1
  11. 7d28f6dc8c3b:peer0.org1.example.com
  12. aeb4ce4d57c3:zookeeper2
  13. 734e25a4430e:zookeeper0
  14. 6b051d7edb2e:peer1.org2.example.com
  15. f19e5663a764:zookeeper1
  16. 9494e1f87c56:peer1.org1.example.com
  17. 7bada708f807:peer0.org2.example.com

默认安装了:4个peer(2个是org1的,2个是org2的)节点、4节点构成的kafka集群、3节点构成的zookeeper集群、1个orderer节点。这是因为:fabric提供的共识机制,PBFT目前还未达到生产级别的应用,只能靠kafka+zookeeper实现PAXOS算法下的共识机制(不能有作恶结点)

简单操作

先进入容器中docker exec -it cli sh,然后看看peer命令都支持什么操作:

  1. docker exec -it cli sh
  2. # pwd
  3. /opt/gopath/src/github.com/hyperledger/fabric/peer
  4. # peer -h
  5. Usage:
  6. peer [flags]
  7. peer [command]
  8. Available Commands:
  9. chaincode Operate a chaincode: install|instantiate|invoke|package|query|signpackage|upgrade|list.
  10. channel Operate a channel: create|fetch|join|list|update|signconfigtx|getinfo.
  11. logging Log levels: getlevel|setlevel|revertlevels.
  12. node Operate a peer node: start|status.
  13. version Print fabric peer version.
  14. Flags:
  15. --logging-level string Default logging level and overrides, see core.yaml for full syntax
  16. -v, --version Display current version of fabric peer server
  17. Use "peer [command] --help" for more information about a command.

和以太坊类似,fabric中交易也要通过chaincode操作。chancode支持的命令如下:

  • package 智能合约需要打包后才能使用
  • install 智能合约必须安装后才能使用
  • instantiate 置初始状态。比如设系统一开始用户a有100元,用户b有200元
  • invoke 调用智能合约
  • query 查询状态
  • signpackage 包签名
  • upgrade 智能合约升级
  • list 显示智能合约

首先执行命令查询余额:

  1. # peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
  2. 2018-06-15 03:16:39.226 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing local MSP
  3. 2018-06-15 03:16:39.226 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining default signing identity
  4. 2018-06-15 03:16:39.226 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 003 Using default escc
  5. 2018-06-15 03:16:39.227 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 004 Using default vscc
  6. 2018-06-15 03:16:39.227 UTC [chaincodeCmd] getChaincodeSpec -> DEBU 005 java chaincode disabled
  7. 2018-06-15 03:16:39.227 UTC [msp/identity] Sign -> DEBU 006 Sign: plaintext: 0ABE070A6608031A0B0897DF8CD90510...6D7963631A0A0A0571756572790A0161
  8. 2018-06-15 03:16:39.227 UTC [msp/identity] Sign -> DEBU 007 Sign: digest: 916F0EE6336EADD59FEBF6538B30645BED4C60C64AD65255ABFCADD189655300
  9. Query Result: 90

等等,为什么我们什么安装操作都没做就能执行查询命令而且还有结果呢?原因在启动服务的过程中有这么一段:

  1. Installing chaincode on org1/peer0...
  2. ....
  3. 2018-06-15 02:35:10.647 UTC [container] WriteFileToPackage -> DEBU 00c Writing file to tarball: src/github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02/chaincode_example02.go
  4. 2018-06-15 02:35:10.648 UTC [container] WriteFileToPackage -> DEBU 00d Writing file to tarball: src/github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02/chaincode_example02_test.go
  5. 2018-06-15 02:35:10.648 UTC [msp/identity] Sign -> DEBU 00e Sign: plaintext: 0AB4070A5C08031A0C08DECB8CD90510...83C77F030000FFFF1E416A37002E0000
  6. 2018-06-15 02:35:10.648 UTC [msp/identity] Sign -> DEBU 00f Sign: digest: 45EC14B3196DC03ACB6A413DED651FBBAF8BEF4B109DA6F0D8E821F150DA7ED1
  7. 2018-06-15 02:35:10.650 UTC [chaincodeCmd] install -> DEBU 010 Installed remotely response:<status:200 payload:"OK" >
  8. 2018-06-15 02:35:10.650 UTC [main] main -> INFO 011 Exiting.....
  9. ===================== Chaincode is installed on remote peer PEER0 =====================

启动脚本中已经帮我们安装好src/github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02/chaincode_example02.go这个Chaincode并且初始化了。
如果后续需要创建或修改代码,则需要重新安装:

  1. peer chaincode install -n mycc -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02

其中-n表示合约名字,-p指向合约文件目录路径,-v是版本号。

而初始化则需要注意,是否开启了tls方法不同,见/opt/gopath/src/github.com/hyperledger/fabric/examples/e2e_cli/scripts/script.sh中:

  1. instantiateChaincode () {
  2. PEER=$1
  3. setGlobals $PEER
  4. # while 'peer chaincode' command can get the orderer endpoint from the peer (if join was successful),
  5. # lets supply it directly as we know it using the "-o" option
  6. if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then
  7. peer chaincode instantiate -o orderer.example.com:7050 -C $CHANNEL_NAME -n mycc -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "OR ('Org1MSP.member','Org2MSP.member')" >&log.txt
  8. else
  9. peer chaincode instantiate -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "OR ('Org1MSP.member','Org2MSP.member')" >&log.txt
  10. fi
  11. res=$?
  12. cat log.txt
  13. verifyResult $res "Chaincode instantiation on PEER$PEER on channel '$CHANNEL_NAME' failed"
  14. echo "===================== Chaincode Instantiation on PEER$PEER on channel '$CHANNEL_NAME' is successful ===================== "
  15. echo
  16. }

比如没开启tls初始化代码就是:

  1. peer chaincode instantiate -o orderer.example.com:7050 -C mychannel -n mycc -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "OR ('Org1MSP.peer','Org2MSP.peer')"

其中,-C指向channel名字,-c则是初始构造json格式的消息,-P是背书策略,-o指定共识节点。这里置帐户a初始余额为100,帐户b初始余额为200。

每个chaincode都要实现Init和Invoke两个方法,其中前者用于初始化,后者是日常调用。 以我们调用的Example02.go代码为例:

  1. func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
  2. fmt.Println("ex02 Init")
  3. _, args := stub.GetFunctionAndParameters() //获取传入的参数并解析
  4. var A, B string // Entities
  5. var Aval, Bval int // Asset holdings
  6. var err error
  7. if len(args) != 4 {
  8. return shim.Error("Incorrect number of arguments. Expecting 4")
  9. }
  10. // Initialize the chaincode
  11. A = args[0]
  12. Aval, err = strconv.Atoi(args[1])
  13. if err != nil {
  14. return shim.Error("Expecting integer value for asset holding")
  15. }
  16. B = args[2]
  17. Bval, err = strconv.Atoi(args[3])
  18. if err != nil {
  19. return shim.Error("Expecting integer value for asset holding")
  20. }
  21. fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
  22. // Write the state to the ledger
  23. err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
  24. if err != nil {
  25. return shim.Error(err.Error())
  26. }
  27. err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
  28. if err != nil {
  29. return shim.Error(err.Error())
  30. }
  31. return shim.Success(nil)
  32. }

invoke函数如下,可以理解为所有日常操作的入口:

  1. func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
  2. fmt.Println("ex02 Invoke")
  3. function, args := stub.GetFunctionAndParameters()
  4. if function == "invoke" {
  5. // Make payment of X units from A to B
  6. return t.invoke(stub, args)
  7. } else if function == "delete" {
  8. // Deletes an entity from its state
  9. return t.delete(stub, args)
  10. } else if function == "query" {
  11. // the old "Query" is now implemtned in invoke
  12. return t.query(stub, args)
  13. }
  14. return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"")
  15. }

可以看到实现了3种操作,转帐、删除用户、查询余额。

先看查询余额,函数定义如下:

  1. // query callback representing the query of a chaincode
  2. func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
  3. var A string // Entities
  4. var err error
  5. if len(args) != 1 {
  6. return shim.Error("Incorrect number of arguments. Expecting name of the person to query")
  7. }
  8. A = args[0] //帐户名
  9. // Get the state from the ledger
  10. Avalbytes, err := stub.GetState(A)
  11. if err != nil {
  12. jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
  13. return shim.Error(jsonResp)
  14. }
  15. if Avalbytes == nil {
  16. jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"
  17. return shim.Error(jsonResp)
  18. }
  19. // 返回json格式结果
  20. jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"
  21. fmt.Printf("Query Response:%s\n", jsonResp)
  22. return shim.Success(Avalbytes)
  23. }

先正常从b给a支付50,这里注意我们开启了tls(疑问:查询时候为啥不用指定tls而转帐不指定则会报错?):

  1. peer chaincode invoke -o orderer.example.com:7050 --tls true --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"]}'

查看a的余额:

  1. # peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
  2. 2018-06-15 03:31:01.268 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing local MSP
  3. 2018-06-15 03:31:01.268 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining default signing identity
  4. 2018-06-15 03:31:01.268 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 003 Using default escc
  5. 2018-06-15 03:31:01.268 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 004 Using default vscc
  6. 2018-06-15 03:31:01.268 UTC [chaincodeCmd] getChaincodeSpec -> DEBU 005 java chaincode disabled
  7. 2018-06-15 03:31:01.268 UTC [msp/identity] Sign -> DEBU 006 Sign: plaintext: 0ABF070A6708031A0C08F5E58CD90510...6D7963631A0A0A0571756572790A0161
  8. 2018-06-15 03:31:01.268 UTC [msp/identity] Sign -> DEBU 007 Sign: digest: 6BAE5E272C5A3FD13D3921B98F0C4C35D6BBD6433DD66CFFF42B04561CDBE688
  9. Query Result: 140

再看b的:

  1. # peer chaincode query -C mychannel -n mycc -c '{"Args":["query","b"]}'
  2. 2018-06-15 03:48:24.458 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing local MSP
  3. 2018-06-15 03:48:24.458 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining default signing identity
  4. 2018-06-15 03:48:24.458 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 003 Using default escc
  5. 2018-06-15 03:48:24.458 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 004 Using default vscc
  6. 2018-06-15 03:48:24.458 UTC [chaincodeCmd] getChaincodeSpec -> DEBU 005 java chaincode disabled
  7. 2018-06-15 03:48:24.459 UTC [msp/identity] Sign -> DEBU 006 Sign: plaintext: 0ABF070A6708031A0C0888EE8CD90510...6D7963631A0A0A0571756572790A0162
  8. 2018-06-15 03:48:24.459 UTC [msp/identity] Sign -> DEBU 007 Sign: digest: B422147DF2CBDF964CAFA860B2FEF3E0928F1B317F599188284860D5EA5902A2
  9. Query Result: 160

a有140,b有160。

  1. peer chaincode invoke -o orderer.example.com:7050 --tls true --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","a","b","150"]}'

这里我们从a向b转150,结果也成功执行了,a余额变成-10,因为invoke代码中并没有对余额和转帐金额的大小进行判断。

转帐函数invoke定义如下:

  1. // Transaction makes payment of X units from A to B
  2. func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response {
  3. var A, B string // Entities
  4. var Aval, Bval int // Asset holdings
  5. var X int // Transaction value
  6. var err error
  7. if len(args) != 3 {
  8. return shim.Error("Incorrect number of arguments. Expecting 3")
  9. }
  10. A = args[0] //转出帐户
  11. B = args[1] //转入帐户
  12. // Get the state from the ledger
  13. // TODO: will be nice to have a GetAllState call to ledger
  14. Avalbytes, err := stub.GetState(A)
  15. if err != nil {
  16. return shim.Error("Failed to get state")
  17. }
  18. if Avalbytes == nil {
  19. return shim.Error("Entity not found")
  20. }
  21. Aval, _ = strconv.Atoi(string(Avalbytes))
  22. Bvalbytes, err := stub.GetState(B)
  23. if err != nil {
  24. return shim.Error("Failed to get state")
  25. }
  26. if Bvalbytes == nil {
  27. return shim.Error("Entity not found")
  28. }
  29. Bval, _ = strconv.Atoi(string(Bvalbytes))
  30. // Perform the execution
  31. X, err = strconv.Atoi(args[2])
  32. if err != nil {
  33. return shim.Error("Invalid transaction amount, expecting a integer value")
  34. }
  35. //关键在于这里缺少判断
  36. Aval = Aval - X
  37. Bval = Bval + X
  38. fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
  39. // Write the state back to the ledger
  40. err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
  41. if err != nil {
  42. return shim.Error(err.Error())
  43. }
  44. err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
  45. if err != nil {
  46. return shim.Error(err.Error())
  47. }
  48. return shim.Success(nil)
  49. }

这样,我们就可以尝试修改这个chaincode并重新安装部署初始化了,具体操作下次记录。


参考链接:

  1. http://www.taohui.pub/530.html
  2. http://nm1024.com/?p=10
  • 分享 收藏
0 条评论
  • 这篇文章暂无评论,赶紧评论一下吧~