Hyperledger Fabric 2.x Java 區塊鏈應用
一、說明
本文將使用
Java
程式碼基於
fabric-gateway-java
進行區塊鏈網路的訪問與交易,並整合
SpringBoot
框架。
Fabric Gateway SDK
實現 Fabric 的程式設計模型,提供了一系列簡單的 API 給應用程式與 Fabric 區塊鏈網路進行互動;
網路拓撲圖:
應用程式將各自的網路互動委託給其閘道器,每個閘道器都瞭解網路通道拓撲,包括組織的多個 Peer 節點和排序節點,使應用程式專注於業務邏輯;Peer 節點可以使用 gossip 協議在組織內部和組織之間相互通訊。
二、Mavn 依賴
新增閘道器 sdk 的依賴:
<dependency> <groupId>org.hyperledger.fabric</groupId> <artifactId>fabric-gateway-java</artifactId> <version>2.2.3</version></dependency>
三、準備配置檔案
工程的目錄結構如下圖所示:
3.1. 準備網路證照
建立目錄
crypto-config
把
orderer
和
peer
節點的證照檔案複製進來。
證照檔案從
fabric-samples
的
test-network
目錄中複製
ordererOrganizations
與
peerOrganizations
資料夾:
3.2. 建立網路配置
建立檔案
connection.json
內容如下:
{ "name": "basic-network", "version": "1.0.0", "client": { "organization": "Org1", "connection": { "timeout": { "peer": { "endorser": "300" }, "orderer": "300" } } }, "channels": { "mychannel": { "orderers": [ "orderer.example.com" ], "peers": { "peer0.org1.example.com": { "endorsingPeer": true, "chaincodeQuery": true, "ledgerQuery": true, "eventSource": true }, "peer0.org2.example.com": { "endorsingPeer": true, "chaincodeQuery": true, "ledgerQuery": true, "eventSource": true } } } }, "organizations": { "Org1": { "mspid": "Org1MSP", "peers": [ "peer0.org1.example.com" ], "certificateAuthorities": [ "ca-org1" ], "adminPrivateKeyPEM": { "path": "src/main/resources/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore/priv_sk" }, "signedCertPEM": { "path": "src/main/resources/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@org1.example.com-cert.pem" } }, "Org2": { "mspid": "Org2MSP", "peers": [ "peer0.org2.example.com" ], "certificateAuthorities": [ "ca-org2" ], "adminPrivateKeyPEM": { "path": "src/main/resources/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/keystore/priv_sk" }, "signedCertPEM": { "path": "src/main/resources/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/signcerts/Admin@org2.example.com-cert.pem" } } }, "orderers": { "orderer.example.com": { "url": "grpcs://192.168.28.134:7050", "mspid": "OrdererMSP", "grpcOptions": { "ssl-target-name-override": "orderer.example.com", "hostnameOverride": "orderer.example.com" }, "tlsCACerts": { "path": "src/main/resources/crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/ca.crt" }, "adminPrivateKeyPEM": { "path": "src/main/resources/crypto-config/ordererOrganizations/example.com/users/Admin@example.com/msp/keystore/priv_sk" }, "signedCertPEM": { "path": "src/main/resources/crypto-config/ordererOrganizations/example.com/users/Admin@example.com/msp/signcerts/Admin@example.com-cert.pem" } } }, "peers": { "peer0.org1.example.com": { "url": "grpcs://192.168.28.134:7051", "grpcOptions": { "ssl-target-name-override": "peer0.org1.example.com", "hostnameOverride": "peer0.org1.example.com", "request-timeout": 120001 }, "tlsCACerts": { "path": "src/main/resources/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" } }, "peer0.org2.example.com": { "url": "grpcs://192.168.28.134:9051", "grpcOptions": { "ssl-target-name-override": "peer0.org2.example.com", "hostnameOverride": "peer0.org2.example.com", "request-timeout": 120001 }, "tlsCACerts": { "path": "src/main/resources/crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt" } } }, "certificateAuthorities": { "ca-org1": { "url": "https://192.168.28.134:7054", "grpcOptions": { "verify": true }, "tlsCACerts": { "path": "src/main/resources/crypto-config/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem" }, "registrar": [ { "enrollId": "admin", "enrollSecret": "adminpw" } ] }, "ca-org2": { "url": "https://192.168.28.134:8054", "grpcOptions": { "verify": true }, "tlsCACerts": { "path": "src/main/resources/crypto-config/peerOrganizations/org2.example.com/ca/ca.org2.example.com-cert.pem" }, "registrar": [ { "enrollId": "admin", "enrollSecret": "adminpw" } ] } } }
需按實際情況修改 url 中的地址,內容中分別包含了
channels
、organizations
、orderers
、peers
、ca
的配置
3.3. SpringBoot 配置
在
application.yml
中新增以下內容,用於訪問閘道器的相關配置:
fabric: # wallet資料夾路徑(自動建立) walletDirectory: wallet # 網路配置檔案路徑 networkConfigPath: connection.json # 使用者證照路徑 certificatePath: crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/signcerts/User1@org1.example.com-cert.pem # 使用者私鑰路徑 privateKeyPath: crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/keystore/priv_sk # 訪問的組織名 mspid: Org1MSP # 使用者名稱 username: user1 # 通道名字 channelName: mychannel # 鏈碼名字 contractName: mycc
四、連線合約
分別構建閘道器、通道和合約的 Bean 物件,程式碼如下:
/** * 連線閘道器 */@Beanpublic Gateway connectGateway() throws IOException, InvalidKeyException, CertificateException { //使用org1中的user1初始化一個閘道器wallet賬戶用於連線網路 Wallet wallet = Wallets.newFileSystemWallet(Paths.get(this.walletDirectory)); X509Certificate certificate = readX509Certificate(Paths.get(this.certificatePath)); PrivateKey privateKey = getPrivateKey(Paths.get(this.privateKeyPath)); wallet.put(username, Identities.newX509Identity(this.mspid, certificate, privateKey)); //根據connection.json 獲取Fabric網路連線物件 Gateway.Builder builder = Gateway.createBuilder() .identity(wallet, username) .networkConfig(Paths.get(this.networkConfigPath)); //連線閘道器 return builder.connect(); }/** * 獲取通道 */@Beanpublic Network network(Gateway gateway) { return gateway.getNetwork(this.channelName); }/** * 獲取合約 */@Beanpublic Contract contract(Network network) { return network.getContract(this.contractName); }
五、合約呼叫
建立 controller 類,注入 Contract 物件呼叫合約方法:
@Resourceprivate Contract contract;@Resourceprivate Network network;@GetMapping("/getUser")public String getUser(String userId) throws ContractException { byte[] queryAResultBefore = contract.evaluateTransaction("getUser",userId); return new String(queryAResultBefore, StandardCharsets.UTF_8); }@GetMapping("/addUser")public String addUser(String userId, String userName, String money) throws ContractException, InterruptedException, TimeoutException { byte[] invokeResult = contract.createTransaction("addUser") .setEndorsingPeers(network.getChannel().getPeers(EnumSet.of(Peer.PeerRole.ENDORSING_PEER))) .submit(userId, userName, money); String txId = new String(invokeResult, StandardCharsets.UTF_8); return txId; }
六、測試介面
呼叫介面
getUser
:
返回:
{ "money": 300, "name": "zlt", "userId": "1"}
呼叫介面
addUser
:
返回:
2ae291bb6a366b5ba01ad49e4237da8def9e9828cc2c982e8c49d4b763af0157
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70019616/viewspace-2905322/,如需轉載,請註明出處,否則將追究法律責任。