fabric后端接口框架 搭建区块链网络 1、下载 fabric 二进制工具
以 v1.4.12
版本为例, fabric 二进制工具的下载地址在:https://github.com/hyperledger/fabric/releases/tag/v1.4.12 [3]
自行根据你的系统环境下载对应的包。
其中几个主要的工具说明:
cryptogen
:用来生成 Hyperledger Fabric 密钥材料的工具,这个过程是静态的。cryptogen
工具通过一个包含网络拓扑的 crypto-config.yaml
文件,为所有组织和属于这些组织的组件生成一组证书和秘钥。cryptogen
适合用于测试开发环境,在生产环境建议使用动态的 CA 服务。
configtxgen
:用于创建和查看排序节点的创世区块、通道配置交易等相关的工具。configtxgen
使用 configtx.yaml
文件来定义网络配置。
configtxlator
:fabric 中 Protobuf
和 JSON
格式转换的工具,fabric 中任何的使用 Protobuf
定义的类型,都可使用该工具进行转换。
peer
:peer 命令有 5 个不同的子命令,每个命令都可以让指定的 peer 节点执行特定的一组任务。比如,可以使用子命令 peer channel
让一个 peer 节点加入通道,或者使用 peer chaincode
命令把智能合约链码部署到 peer 节点上。
2、将 fabric 二进制工具添加到环境变量
为了后续方便使用命令,可以将第 1 步下载的工具添加到系统环境变量中
1 export PATH=${PWD}/hyperledger-fabric-linux-amd64-1.4.12/bin:$PATH
3、生成证书和秘钥所需文件
我们将使用 cryptogen
工具生成各种加密材料( x509 证书和签名秘钥)。这些证书是身份的代表,在实体相互通信和交易的时候,可以对其身份进行签名和验证。
首先创建 crypto-config.yaml
文件,定义网络拓扑,为所有组织和属于这些组织的组件(也就是节点)生成一组证书和秘钥,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 OrdererOrgs: - Name: ZXY Domain: zxy.com Specs: - Hostname: orderer PeerOrgs: - Name: CHEN Domain: chen.com Template: Count: 2 Users: Count: 1 - Name: ZHOU Domain: zhou.com Template: Count: 2 Users: Count: 1
4、创建排序通道创世区块所需文件
我们可以使用 configtx.yaml
文件和 configtxgen
工具轻松地创建通道的配置。configtx.yaml
文件可以以易于理解和编辑的 yaml
格式来构建通道配置所需的信息。configtxgen
工具通过读取 configtx.yaml
文件中的信息,将其转成 Fabric 可以读取的 protobuf
格式。
先来创建 configtx.yaml
文件,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 Organizations: - &ZXY Name: ZXY ID: ZXYMSP MSPDir: crypto-config/ordererOrganizations/zxy.com/msp - &CHEN Name: CHEN ID: CHENMSP MSPDir: crypto-config/peerOrganizations/chen.com/msp AnchorPeers: - Host: peer0.chen.com Port: 7051 - &ZHOU Name: ZHOU ID: ZHOUMSP MSPDir: crypto-config/peerOrganizations/zhou.com/msp AnchorPeers: - Host: peer0.zhou.com Port: 7051 Orderer: &OrdererDefaults OrdererType: solo Addresses: - orderer.zxy.com:7050 BatchTimeout: 2s BatchSize: MaxMessageCount: 10 AbsoluteMaxBytes: 99 MB PreferredMaxBytes: 512 KB Organizations: Application: &ApplicationDefaults Organizations: Profiles: TwoOrgsOrdererGenesis: Orderer: <<: *OrdererDefaults Organizations: - *ZXY Consortiums: SampleConsortium: Organizations: - *CHEN - *ZHOU TwoOrgsChannel: Consortium: SampleConsortium Application: <<: *ApplicationDefaults Organizations: - *CHEN - *ZHOU
排序区块是排序服务的创世区块,通过以上命令就可以预先生成创世区块的 protobuf
格式的配置文件 ./config/genesis.block
了。这一步也是为后续做准备用的。
6、创建并启动各组织的节点
我们说过:我们假设 ZXY
作为一个运营方,提供了 1 个 Orderer 节点 orderer.zxy.com
来创建联盟链的基础设施, 而 CHEN
和 ZHOU
则是作为组织成员加入到链中,各自提供 2 个 Peer 节点 peer0.xx.com
和 peer1.xx.com
参与工作。
现在这些组织及其节点所需要的配置已经准备好了。我们接下来就可以使用 Docker Compose 来模拟启动这些节点服务。
由于这些节点之间需要互相通信,所以我们需要将这些节点都放入到一个 Docker 网络中,以 fabric_network
为例。
docker-compose.yaml
的内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 version: '2.1' volumes: orderer.zxy.com: peer0.chen.com: peer1.chen.com: peer0.zhou.com: peer1.zhou.com: networks: fabric_network: name: fabric_network services: orderer.zxy.com: container_name: orderer.zxy.com image: hyperledger/fabric-orderer:1.4.12 environment: - GODEBUG=netdns=go - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0 - ORDERER_GENERAL_GENESISMETHOD=file - ORDERER_GENERAL_GENESISFILE=/etc/hyperledger/config/genesis.block - ORDERER_GENERAL_LOCALMSPID=ZXYMSP - ORDERER_GENERAL_LOCALMSPDIR=/etc/hyperledger/orderer/msp command: orderer ports: - "7050:7050" volumes: - ./config/genesis.block:/etc/hyperledger/config/genesis.block - ./crypto-config/ordererOrganizations/zxy.com/orderers/orderer.zxy.com/:/etc/hyperledger/orderer - orderer.zxy.com:/var/hyperledger/production/orderer networks: - fabric_network peer0.chen.com: extends: file: docker-compose-base.yaml service: peer-base container_name: peer0.chen.com environment: - CORE_PEER_ID=peer0.chen.com - CORE_PEER_LOCALMSPID=CHENMSP - CORE_PEER_ADDRESS=peer0.chen.com:7051 - CORE_LEDGER_STATE_STATEDATABASE=CouchDB - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb:5984 - CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=admin - CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=123456 ports: - "7051:7051" - "7053:7053" volumes: - ./crypto-config/peerOrganizations/chen.com/peers/peer0.chen.com:/etc/hyperledger/peer - peer0.chen.com:/var/hyperledger/production depends_on: - orderer.zxy.com peer1.chen.com: extends: file: docker-compose-base.yaml service: peer-base container_name: peer1.chen.com environment: - CORE_PEER_ID=peer1.chen.com - CORE_PEER_LOCALMSPID=CHENMSP - CORE_PEER_ADDRESS=peer1.chen.com:7051 - CORE_LEDGER_STATE_STATEDATABASE=CouchDB - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb:5984 - CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=admin - CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=123456 ports: - "17051:7051" - "17053:7053" volumes: - ./crypto-config/peerOrganizations/chen.com/peers/peer1.chen.com:/etc/hyperledger/peer - peer1.chen.com:/var/hyperledger/production depends_on: - orderer.zxy.com peer0.zhou.com: extends: file: docker-compose-base.yaml service: peer-base container_name: peer0.zhou.com environment: - CORE_PEER_ID=peer0.zhou.com - CORE_PEER_LOCALMSPID=ZHOUMSP - CORE_PEER_ADDRESS=peer0.zhou.com:7051 - CORE_LEDGER_STATE_STATEDATABASE=CouchDB - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb:5984 - CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=admin - CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=123456 ports: - "27051:7051" - "27053:7053" volumes: - ./crypto-config/peerOrganizations/zhou.com/peers/peer0.zhou.com:/etc/hyperledger/peer - peer0.zhou.com:/var/hyperledger/production depends_on: - orderer.zxy.com peer1.zhou.com: extends: file: docker-compose-base.yaml service: peer-base container_name: peer1.zhou.com environment: - CORE_PEER_ID=peer1.zhou.com - CORE_PEER_LOCALMSPID=ZHOUMSP - CORE_PEER_ADDRESS=peer1.zhou.com:7051 - CORE_LEDGER_STATE_STATEDATABASE=CouchDB - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb:5984 - CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=admin - CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=123456 ports: - "37051:7051" - "37053:7053" volumes: - ./crypto-config/peerOrganizations/zhou.com/peers/peer1.zhou.com:/etc/hyperledger/peer - peer1.zhou.com:/var/hyperledger/production depends_on: - orderer.zxy.com cli: container_name: cli image: hyperledger/fabric-tools:1.4.12 tty: true environment: - GO111MODULE=auto - GOPROXY=https://goproxy.cn - CORE_PEER_ID=cli command: /bin/bash volumes: - ./config:/etc/hyperledger/config - ./crypto-config/peerOrganizations/chen.com/:/etc/hyperledger/peer/chen.com - ./crypto-config/peerOrganizations/zhou.com/:/etc/hyperledger/peer/zhou.com - ./../chaincode:/opt/gopath/src/chaincode networks: - fabric_network depends_on: - orderer.zxy.com - peer0.chen.com - peer1.chen.com - peer0.zhou.com - peer1.zhou.com couchdb: container_name: couchdb image: hyperledger/fabric-couchdb:latest environment: - COUCHDB_USER=admin - COUCHDB_PASSWORD=123456 ports: - "5984:5984" networks: - fabric_network
为了方便,这里我还定义了一个 docker-compose-base.yaml
作为 Peer 节点的公共模板,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 version: '2.1' services: peer-base: image: hyperledger/fabric-peer:1.4.12 environment: - GODEBUG=netdns=go - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock - CORE_LOGGING_PEER=info - CORE_CHAINCODE_LOGGING_LEVEL=INFO - CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/peer/msp - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=fabric_network volumes: - /var/run/docker.sock:/host/var/run/docker.sock working_dir: /opt/gopath/src/github.com/hyperledger/fabric command: peer node start networks: - fabric_network
编写智能合约 1、编写操作账本的工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 package utilsimport ( "encoding/json" "errors" "fmt" "github.com/hyperledger/fabric/core/chaincode/shim" ) func WriteLedger (obj interface {}, stub shim.ChaincodeStubInterface, objectType string , keys []string ) error { var key string if val, err := stub.CreateCompositeKey(objectType, keys); err != nil { return errors.New(fmt.Sprintf("%s-创建复合主键出错 %s" , objectType, err)) } else { key = val } bytes, err := json.Marshal(obj) if err != nil { return errors.New(fmt.Sprintf("%s-序列化json数据失败出错: %s" , objectType, err)) } if err := stub.PutState(key, bytes); err != nil { return errors.New(fmt.Sprintf("%s-写入区块链账本出错: %s" , objectType, err)) } return nil } func DelLedger (stub shim.ChaincodeStubInterface, objectType string , keys []string ) error { var key string if val, err := stub.CreateCompositeKey(objectType, keys); err != nil { return errors.New(fmt.Sprintf("%s-创建复合主键出错 %s" , objectType, err)) } else { key = val } if err := stub.DelState(key); err != nil { return errors.New(fmt.Sprintf("%s-删除区块链账本出错: %s" , objectType, err)) } return nil } func GetStateByPartialCompositeKeys (stub shim.ChaincodeStubInterface, objectType string , keys []string ) (results [][]byte , err error ) { if len (keys) == 0 { resultIterator, err := stub.GetStateByPartialCompositeKey(objectType, keys) if err != nil { return nil , errors.New(fmt.Sprintf("%s-获取全部数据出错: %s" , objectType, err)) } defer resultIterator.Close() for resultIterator.HasNext() { val, err := resultIterator.Next() if err != nil { return nil , errors.New(fmt.Sprintf("%s-返回的数据出错: %s" , objectType, err)) } results = append (results, val.GetValue()) } } else { for _, v := range keys { key, err := stub.CreateCompositeKey(objectType, []string {v}) if err != nil { return nil , errors.New(fmt.Sprintf("%s-创建组合键出错: %s" , objectType, err)) } bytes, err := stub.GetState(key) if err != nil { return nil , errors.New(fmt.Sprintf("%s-获取数据出错: %s" , objectType, err)) } if bytes != nil { results = append (results, bytes) } } } return results, nil } func GetStateByPartialCompositeKeys2 (stub shim.ChaincodeStubInterface, objectType string , keys []string ) (results [][]byte , err error ) { resultIterator, err := stub.GetStateByPartialCompositeKey(objectType, keys) if err != nil { return nil , errors.New(fmt.Sprintf("%s-获取全部数据出错: %s" , objectType, err)) } defer resultIterator.Close() for resultIterator.HasNext() { val, err := resultIterator.Next() if err != nil { return nil , errors.New(fmt.Sprintf("%s-返回的数据出错: %s" , objectType, err)) } results = append (results, val.GetValue()) } return results, nil }
2、定义结构体
1 2 3 4 5 6 7 8 package modeltype Account struct { AccountId string `json:"accountId"` UserName string `json:"userName"` Password float64 `json:"password"` }
3、编写测试链码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package apiimport ( "chaincode/pkg/utils" "github.com/hyperledger/fabric/core/chaincode/shim" pb "github.com/hyperledger/fabric/protos/peer" ) func Hello (stub shim.ChaincodeStubInterface, args []string ) pb.Response { err := utils.WriteLedger(map [string ]interface {}{"msg" : "hello" }, stub, "hello" , []string {}) if err != nil { return shim.Error(err.Error()) } return shim.Success([]byte ("hello world" )) }
4、初始化链码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package mainimport ( "chaincode/api" "fmt" "time" "github.com/hyperledger/fabric/core/chaincode/shim" pb "github.com/hyperledger/fabric/protos/peer" ) type BlockChainRealEstate struct {} func (t *BlockChainRealEstate) Init(stub shim.ChaincodeStubInterface) pb.Response { fmt.Println("链码初始化" ) return shim.Success(nil ) } func (t *BlockChainRealEstate) Invoke(stub shim.ChaincodeStubInterface) pb.Response { funcName, args := stub.GetFunctionAndParameters() switch funcName { case "hello" : return api.Hello(stub, args) default : return shim.Error(fmt.Sprintf("没有该功能: %s" , funcName)) } } func main () { timeLocal, err := time.LoadLocation("Asia/Shanghai" ) if err != nil { panic (err) } time.Local = timeLocal err = shim.Start(new (BlockChainRealEstate)) if err != nil { fmt.Printf("Error starting Simple chaincode: %s" , err) } }
5、运行以下命令获取所需依赖,会生成go.mod 和 go.sum 文件
编写应用程序 1、创建一个 config.yaml
,配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 version: 1.0 .0 client: organization: ZHOU logging: level: info cryptoconfig: path: /network/crypto-config channels: appchannel: orderers: - orderer.zxy.com peers: peer0.zhou.com: endorsingPeer: true chaincodeQuery: true ledgerQuery: true eventSource: true peer1.zhou.com: endorsingPeer: true chaincodeQuery: true ledgerQuery: true eventSource: true organizations: ZHOU: mspid: "ZHOUMSP" cryptoPath: peerOrganizations/zhou.com/users/{username}@zhou.com/msp peers: - peer0.zhou.com - peer1.zhou.com orderers: orderer.zxy.com: url: orderer.zxy.com:7050 grpcOptions: ssl-target-name-override: orderer.zxy.com keep-alive-time: 0s keep-alive-timeout: 20s keep-alive-permit: false fail-fast: false allow-insecure: true peers: peer0.zhou.com: url: peer0.zhou.com:7051 grpcOptions: ssl-target-name-override: peer0.zhou.com keep-alive-time: 0s keep-alive-timeout: 20s keep-alive-permit: false fail-fast: false allow-insecure: true peer1.zhou.com: url: peer1.zhou.com:7051 grpcOptions: ssl-target-name-override: peer1.zhou.com keep-alive-time: 0s keep-alive-timeout: 20s keep-alive-permit: false fail-fast: false allow-insecure: true peer0.chen.com: url: peer0.chen.com:7051 grpcOptions: ssl-target-name-override: peer0.chen.com keep-alive-time: 0s keep-alive-timeout: 20s keep-alive-permit: false fail-fast: false allow-insecure: true peer1.chen.com: url: peer1.chen.com:7051 grpcOptions: ssl-target-name-override: peer1.chen.com keep-alive-time: 0s keep-alive-timeout: 20s keep-alive-permit: false fail-fast: false allow-insecure: true
2、实例化 SDK ,创建 sdk.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 package blockchainimport ( "github.com/hyperledger/fabric-sdk-go/pkg/client/channel" "github.com/hyperledger/fabric-sdk-go/pkg/core/config" "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" ) var ( sdk *fabsdk.FabricSDK configPath = "config.yaml" channelName = "appchannel" user = "Admin" chainCodeName = "fabric-realty" endpoints = []string {"peer0.zhou.com" , "peer0.chen.com" } ) func Init () { var err error sdk, err = fabsdk.New(config.FromFile(configPath)) if err != nil { panic (err) } } func ChannelExecute (fcn string , args [][]byte ) (channel.Response, error ) { ctx := sdk.ChannelContext(channelName, fabsdk.WithUser(user)) cli, err := channel.New(ctx) if err != nil { return channel.Response{}, err } resp, err := cli.Execute(channel.Request{ ChaincodeID: chainCodeName, Fcn: fcn, Args: args, }, channel.WithTargetEndpoints(endpoints...)) if err != nil { return channel.Response{}, err } return resp, nil } func ChannelQuery (fcn string , args [][]byte ) (channel.Response, error ) { ctx := sdk.ChannelContext(channelName, fabsdk.WithUser(user)) cli, err := channel.New(ctx) if err != nil { return channel.Response{}, err } resp, err := cli.Query(channel.Request{ ChaincodeID: chainCodeName, Fcn: fcn, Args: args, }, channel.WithTargetEndpoints(endpoints...)) if err != nil { return channel.Response{}, err } return resp, nil }
3、定义接收的请求参数,HTTP响应中返回一个标准的JSON格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package appimport ( "github.com/gin-gonic/gin" ) type Gin struct { C *gin.Context } type Response struct { Code int `json:"code"` Msg string `json:"msg"` Data interface {} `json:"data"` } func (g *Gin) Response(httpCode int , errMsg string , data interface {}) { g.C.JSON(httpCode, Response{ Code: httpCode, Msg: errMsg, Data: data, }) return }
4、编写测试接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package v1import ( "application/pkg/app" "net/http" "github.com/gin-gonic/gin" ) func Hello (c *gin.Context) { appG := app.Gin{C: c} appG.Response(http.StatusOK, "成功" , map [string ]interface {}{ "msg" : "Hello" , }) }
5、定义访问路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package routersimport ( v1 "application/api/v1" "github.com/gin-gonic/gin" ) func InitRouter () *gin.Engine { r := gin.Default() apiV1 := r.Group("/api/v1" ) { apiV1.GET("/hello" , v1.Hello) } return r }
6、编写应用入口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package mainimport ( "fmt" "log" "net/http" "time" "application/blockchain" "application/routers" ) func main () { timeLocal, err := time.LoadLocation("Asia/Shanghai" ) if err != nil { log.Printf("时区设置失败 %s" , err) } time.Local = timeLocal blockchain.Init() endPoint := fmt.Sprintf("0.0.0.0:%d" , 8000 ) server := &http.Server{ Addr: endPoint, Handler: routers.InitRouter(), } log.Printf("[info] start http server listening %s" , endPoint) if err := server.ListenAndServe(); err != nil { log.Printf("start http server failed %s" , err) } }
7、继续使用 Docker 部署该应用程序
首先编写 Dockerfile
文件:
1 2 3 4 5 6 7 8 9 10 11 12 FROM golang:1.14 AS appENV GO111MODULE=onENV GOPROXY https://goproxy.cn,directWORKDIR /root/togettoyou COPY server/. . RUN CGO_ENABLED=0 go build -v -o "app" . FROM scratchWORKDIR /root/togettoyou/ COPY --from=app /root/togettoyou/app ./ COPY --from=app /root/togettoyou/config.yaml ./ ENTRYPOINT ["./app" ]
docker-compose.yml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 version: '2.1' networks: fabric_network: external: name: fabric_network services: fabric-realty.app: build: . image: fabric-realty/application:latest container_name: fabric-realty.app ports: - "8000:8000" volumes: - /usr/share/zoneinfo/Asia/Shanghai:/usr/share/zoneinfo/Asia/Shanghai - ./../network/crypto-config:/network/crypto-config networks: - fabric_network
8、同上,使用以下两个命令获取依赖
运行 上面只是准备了fabric后端接口框架所需要的文件,接下来是逐步启动改后端应用接口的命令
1、目录结构
application/server
: fabric-sdk-go
调用链码(即智能合约),gin
提供外部访问接口(RESTful API)
chaincode
: go 编写的链码(即智能合约)
network
: Hyperledger Fabric 区块链网络配置
2、在network目录下编写stop.sh脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 # !/bin/bash # 清除链码容器 function clearContainers() { CONTAINER_IDS=$(docker ps -a | awk '($2 ~ /dev-peer.*fabric-realty.*/) {print $1}') if [ -z "$CONTAINER_IDS" -o "$CONTAINER_IDS" == " " ]; then echo "---- No containers available for deletion ----" else docker rm -f $CONTAINER_IDS fi } # 清除链码镜像 function removeUnwantedImages() { DOCKER_IMAGE_IDS=$(docker images | awk '($1 ~ /dev-peer.*fabric-realty.*/) {print $3}') if [ -z "$DOCKER_IMAGE_IDS" -o "$DOCKER_IMAGE_IDS" == " " ]; then echo "---- No images available for deletion ----" else docker rmi -f $DOCKER_IMAGE_IDS fi } echo "清理环境" mkdir -p config mkdir -p crypto-config rm -rf config/* rm -rf crypto-config/* docker-compose down -v clearContainers removeUnwantedImages echo "清理完毕"
3、在该目录下继续编写脚本start.sh用于构建与启动network网络环境
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 # !/bin/bash if [[ `uname` == 'Darwin' ]]; then echo "Mac OS" export PATH=${PWD}/hyperledger-fabric-darwin-amd64-1.4.12/bin:$PATH fi if [[ `uname` == 'Linux' ]]; then echo "Linux" export PATH=${PWD}/hyperledger-fabric-linux-amd64-1.4.12/bin:$PATH fi echo "一、清理环境" ./stop.sh echo "二、生成证书和秘钥( MSP 材料),生成结果将保存在 crypto-config 文件夹中" cryptogen generate --config=./crypto-config.yaml echo "三、创建排序通道创世区块" configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./config/genesis.block -channelID firstchannel echo "四、生成通道配置事务'appchannel.tx'" configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./config/appchannel.tx -channelID appchannel echo "五、为 CHEN 定义锚节点" configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./config/CHENAnchor.tx -channelID appchannel -asOrg CHEN echo "六、为 ZHOU 定义锚节点" configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./config/ZHOUAnchor.tx -channelID appchannel -asOrg ZHOU echo "区块链 : 启动" docker-compose up -d echo "正在等待节点的启动完成,等待10秒" sleep 10 CHENPeer0Cli="CORE_PEER_ADDRESS=peer0.chen.com:7051 CORE_PEER_LOCALMSPID=CHENMSP CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/peer/chen.com/users/Admin@chen.com/msp" CHENPeer1Cli="CORE_PEER_ADDRESS=peer1.chen.com:7051 CORE_PEER_LOCALMSPID=CHENMSP CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/peer/chen.com/users/Admin@chen.com/msp" ZHOUPeer0Cli="CORE_PEER_ADDRESS=peer0.zhou.com:7051 CORE_PEER_LOCALMSPID=ZHOUMSP CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/peer/zhou.com/users/Admin@zhou.com/msp" ZHOUPeer1Cli="CORE_PEER_ADDRESS=peer1.zhou.com:7051 CORE_PEER_LOCALMSPID=ZHOUMSP CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/peer/zhou.com/users/Admin@zhou.com/msp" echo "七、创建通道" docker exec cli bash -c "$CHENPeer0Cli peer channel create -o orderer.zxy.com:7050 -c appchannel -f /etc/hyperledger/config/appchannel.tx" echo "八、将所有节点加入通道" docker exec cli bash -c "$CHENPeer0Cli peer channel join -b appchannel.block" docker exec cli bash -c "$CHENPeer1Cli peer channel join -b appchannel.block" docker exec cli bash -c "$ZHOUPeer0Cli peer channel join -b appchannel.block" docker exec cli bash -c "$ZHOUPeer1Cli peer channel join -b appchannel.block" echo "九、更新锚节点" docker exec cli bash -c "$CHENPeer0Cli peer channel update -o orderer.zxy.com:7050 -c appchannel -f /etc/hyperledger/config/CHENAnchor.tx" docker exec cli bash -c "$ZHOUPeer0Cli peer channel update -o orderer.zxy.com:7050 -c appchannel -f /etc/hyperledger/config/ZHOUAnchor.tx" # -n 链码名,可以自己随便设置 # -v 版本号 # -p 链码目录,在 /opt/gopath/src/ 目录下 echo "十、安装链码" docker exec cli bash -c "$CHENPeer0Cli peer chaincode install -n fabric-realty -v 1.0.0 -l golang -p chaincode" docker exec cli bash -c "$ZHOUPeer0Cli peer chaincode install -n fabric-realty -v 1.0.0 -l golang -p chaincode" # 只需要其中一个节点实例化 # -n 对应上一步安装链码的名字 # -v 版本号 # -C 是通道,在fabric的世界,一个通道就是一条不同的链 # -c 为传参,传入init参数 echo "十一、实例化链码" docker exec cli bash -c "$CHENPeer0Cli peer chaincode instantiate -o orderer.zxy.com:7050 -C appchannel -n fabric-realty -l golang -v 1.0.0 -c '{\"Args\":[\"init\"]}' -P \"AND ('CHENMSP.member','ZHOUMSP.member')\"" echo "正在等待链码实例化完成,等待5秒" sleep 5 # 进行链码交互,验证链码是否正确安装及区块链网络能否正常工作 echo "十二、验证链码" docker exec cli bash -c "$CHENPeer0Cli peer chaincode invoke -C appchannel -n fabric-realty -c '{\"Args\":[\"hello\"]}'" docker exec cli bash -c "$ZHOUPeer0Cli peer chaincode invoke -C appchannel -n fabric-realty -c '{\"Args\":[\"hello\"]}'"
4、进入application目录下,编写构建应用程序的脚本build.sh
1 2 3 # !/bin/bash docker-compose build
5、继续编写停止应用程序的脚本stop.sh
1 2 3 # !/bin/bash docker-compose down
6、编写启动应用程序的脚本start.sh
1 2 3 # !/bin/bash docker-compose up -d
7、运行
进入 network
目录,执行 ./start.sh
部署区块链网络和智能合约
进入 application
目录,执行 ./start.sh
启动前后端应用,然后就可使用浏览器访问接口 http://localhost:8000/api/v1/hello 进行测试(初次启动需要运行 ./build.sh
命令进行构建应用程序)
拓展 搭建区块链网络(未尝试) 1、在network目录下新建explorer目录,进入该目录,编写 docker-compose.yaml
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 version: '2.1' volumes: pgdata: walletstore: networks: fabric_network: external: name: fabric_network services: explorerdb.com: image: hyperledger/explorer-db:1.1.6 container_name: explorerdb.com hostname: explorerdb.com environment: - DATABASE_DATABASE=fabricexplorer - DATABASE_USERNAME=hppoc - DATABASE_PASSWORD=password healthcheck: test: "pg_isready -h localhost -p 5432 -q -U postgres" interval: 30s timeout: 10s retries: 5 volumes: - pgdata:/var/lib/postgresql/data networks: - fabric_network explorer.com: image: hyperledger/explorer:1.1.6 container_name: explorer.com hostname: explorer.com environment: - DATABASE_HOST=explorerdb.com - DATABASE_DATABASE=fabricexplorer - DATABASE_USERNAME=hppoc - DATABASE_PASSWD=password - LOG_LEVEL_APP=debug - LOG_LEVEL_DB=debug - LOG_LEVEL_CONSOLE=info - LOG_CONSOLE_STDOUT=true - DISCOVERY_AS_LOCALHOST=false volumes: - ./config.json:/opt/explorer/app/platform/fabric/config.json - ./connection-profile:/opt/explorer/app/platform/fabric/connection-profile - ../crypto-config:/tmp/crypto - walletstore:/opt/explorer/wallet ports: - "8080:8080" depends_on: explorerdb.com: condition: service_healthy networks: - fabric_network
2、编写 config.json
文件
1 2 3 4 5 6 7 8 9 { "network-configs" : { "test-network" : { "name" : "Fabric Network" , "profile" : "./connection-profile/network.json" } } , "license" : "Apache-2.0" }
3、新建目录 connection-profile
,进入该目录,编写 network_temp.json
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 { "name" : "fabric-network" , "version" : "1.0.0" , "client" : { "tlsEnable" : true , "adminCredential" : { "id" : "admin" , "password" : "123456" } , "enableAuthentication" : true , "organization" : "CHENMSP" , "connection" : { "timeout" : { "peer" : { "endorser" : "300" } , "orderer" : "300" } } } , "channels" : { "appchannel" : { "peers" : { "peer0.chen.com" : { } } } } , "organizations" : { "CHENMSP" : { "mspid" : "CHENMSP" , "adminPrivateKey" : { "path" : "/tmp/crypto/peerOrganizations/chen.com/users/Admin@chen.com/msp/keystore/priv_sk" } , "peers" : [ "peer0.chen.com" ] , "signedCert" : { "path" : "/tmp/crypto/peerOrganizations/chen.com/users/Admin@chen.com/msp/signcerts/Admin@chen.com-cert.pem" } } } , "peers" : { "peer0.chen.com" : { "tlsCACerts" : { "path" : "/tmp/crypto/peerOrganizations/chen.com/peers/peer0.chen.com/tls/ca.crt" } , "url" : "grpc://peer0.chen.com:7051" } } }
4、编写脚本 ./stop.sh
1 2 3 # !/bin/bash docker-compose down -v
5、编写脚本 ./start.sh
1 2 3 4 5 6 7 8 9 10 # !/bin/bash priv_sk_path=$(ls ../crypto-config/peerOrganizations/chen.com/users/Admin\@chen.com/msp/keystore/) cp -rf ./connection-profile/network_temp.json ./connection-profile/network.json sed -i "s/priv_sk/$priv_sk_path/" ./connection-profile/network.json docker-compose down -v docker-compose up -d
6、运行
总结 最简单的方法就是直接 拷我u盘的代码~~