區塊鏈Hyperledger Fabric 2.x 自定義智慧合約
一、說明
為了持續地進行資訊的更新,以及對賬本進行管理(寫入交易,進行查詢等),區塊鏈網路引入了智慧合約來實現對賬本的訪問和控制;智慧合約在 Fabric 中稱之為
鏈碼
,是區塊鏈應用的業務邏輯。
本文分享如何使用 Java 語言開發智慧合約,以及合約的安裝與使用。
二、環境準備
1、部署好
Fabric
的測試網路,按照上一篇文章《Hyperledger Fabric 2.x 環境搭建》的內容執行第 1 至 5 步
- 啟動好兩個 peer 節點和一個 orderer 節點- 建立好 mychannel 通道
2、在環境變數中配置好執行命令 (bin)、配置 (config) 與 MSP 資料夾的路徑: 執行
vim /etc/profile
新增以下內容:
export FABRIC_PATH=/opt/gopath/src/github.com/hyperledger/fabric-samplesexport FABRIC_CFG_PATH=${FABRIC_PATH}/config/export MSP_PATH=${FABRIC_PATH}/test-network/organizationsexport CORE_PEER_TLS_ENABLED=trueexport PATH=${FABRIC_PATH}/bin:$PATH
FABRIC_PATH 路徑按實際進行修改。
三、下載合約程式碼
四、程式碼解析
在
Fabric 2.x
版本後的合約編寫方式與舊版本略有不同,需要實現
ContractInterface
介面,下面是官方的一段說明:
All chaincode implementations must extend the abstract class ChaincodeBase. It is possible to implement chaincode by extending ChaincodeBase directly however new projects should implement org.hyperledger.fabric.contract.ContractInterface and use the contract programming model instead.
4.1. pom.xml 檔案
配置遠端倉庫
<repositories> <repository> <id>central</id> <url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </repository> <repository> <id>jitpack.io</id> <url> </repository> <repository> <id>artifactory</id> <url> </repository></repositories>
依賴合約 sdk
<dependency> <groupId>org.hyperledger.fabric-chaincode-java</groupId> <artifactId>fabric-chaincode-shim</artifactId> <version>${fabric-chaincode-java.version}</version></dependency>
通過外掛
maven-shade-plugin
指定
mainClass
為
org.hyperledger.fabric.contract.ContractRouter
新版本所有合約的
mainClass
都為org.hyperledger.fabric.contract.ContractRouter
<build> <sourceDirectory>src/main/java</sourceDirectory> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.1.0</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <finalName>chaincode</finalName> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>org.hyperledger.fabric.contract.ContractRouter</mainClass> </transformer> </transformers> <filters> <filter> <!-- filter out signature files from signed dependencies, else repackaging fails with security ex --> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> </excludes> </filter> </filters> </configuration> </execution> </executions> </plugin> </plugins></build>
4.2. model
建立合約的資料物件
User
使用
@DataType
註解標識,定義三個欄位
userId
、
name
、
money
使用
@Property
註解標識:
@DataTypepublic class User { @Property private final String userId; @Property private final String name; @Property private final double money; public User(final String userId, final String name, final double money) { this.userId = userId; this.name = name; this.money = money; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if ((obj == null) || (getClass() != obj.getClass())) { return false; } User other = (User) obj; return Objects.deepEquals( new String[] {getUserId(), getName()}, new String[] {other.getUserId(), other.getName()}) && Objects.deepEquals( new double[] {getMoney()}, new double[] {other.getMoney()}); } @Override public int hashCode() { return Objects.hash(getUserId(), getName(), getMoney()); } @Override public String toString() { return JSON.toJSONString(this); } public String getUserId() { return userId; } public String getName() { return name; } public double getMoney() { return money; } }
4.3. 合約邏輯
- 合約類使用
@Contract
與@Default
註解標識,並實現ContractInterface
介面 - 合約方法使用
@Transaction
註解標識Transaction.TYPE.SUBMIT 為 寫入交易 Transaction.TYPE.EVALUATE 為 查詢
- 包含 3 個交易方法:
init
、addUser
、transfer
- 包含 2 個查詢方法:
getUser
、queryAll
@Contract(name = "mycc")@Defaultpublic class MyAssetChaincode implements ContractInterface { public MyAssetChaincode() {} /** * 初始化3條記錄 */ @Transaction(intent = Transaction.TYPE.SUBMIT) public void init(final Context ctx) { addUser(ctx, "1", "zlt",100D); addUser(ctx, "2", "admin",200D); addUser(ctx, "3", "guest",300D); } /** * 新增使用者 */ @Transaction(intent = Transaction.TYPE.SUBMIT) public User addUser(final Context ctx, final String userId, final String name, final double money) { ChaincodeStub stub = ctx.getStub(); User user = new User(userId, name, money); String userJson = JSON.toJSONString(user); stub.putStringState(userId, userJson); return user; } /** * 查詢某個使用者 */ @Transaction(intent = Transaction.TYPE.EVALUATE) public User getUser(final Context ctx, final String userId) { ChaincodeStub stub = ctx.getStub(); String userJSON = stub.getStringState(userId); if (userJSON == null || userJSON.isEmpty()) { String errorMessage = String.format("User %s does not exist", userId); throw new ChaincodeException(errorMessage); } User user = JSON.parseObject(userJSON, User.class); return user; } /** * 查詢所有使用者 */ @Transaction(intent = Transaction.TYPE.EVALUATE) public String queryAll(final Context ctx) { ChaincodeStub stub = ctx.getStub(); List<User> userList = new ArrayList<>(); QueryResultsIterator<KeyValue> results = stub.getStateByRange("", ""); for (KeyValue result: results) { User user = JSON.parseObject(result.getStringValue(), User.class); System.out.println(user); userList.add(user); } return JSON.toJSONString(userList); } /** * 轉賬 * @param sourceId 源使用者id * @param targetId 目標使用者id * @param money 金額 */ @Transaction(intent = Transaction.TYPE.SUBMIT) public void transfer(final Context ctx, final String sourceId, final String targetId, final double money) { ChaincodeStub stub = ctx.getStub(); User sourceUser = getUser(ctx, sourceId); User targetUser = getUser(ctx, targetId); if (sourceUser.getMoney() < money) { String errorMessage = String.format("The balance of user %s is insufficient", sourceId); throw new ChaincodeException(errorMessage); } User newSourceUser = new User(sourceUser.getUserId(), sourceUser.getName(), sourceUser.getMoney() - money); User newTargetUser = new User(targetUser.getUserId(), targetUser.getName(), targetUser.getMoney() + money); stub.putStringState(sourceId, JSON.toJSONString(newSourceUser)); stub.putStringState(targetId, JSON.toJSONString(newTargetUser)); } }
五、打包合約程式碼
把合約原始碼打包成壓縮檔案,用於後續安裝:
peer lifecycle chaincode package mycc.tar.gz --path /opt/app/my-fabric-chaincode-java --lang java --label mycc
六、安裝合約
在指定 peer 節點上安裝鏈碼,下面分別為兩個機構安裝。
6.1. 為機構 peer0.org1 安裝合約
執行以下命令,設定
peer0.org1
環境:
export CORE_PEER_LOCALMSPID="Org1MSP"export CORE_PEER_TLS_ROOTCERT_FILE=${MSP_PATH}/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crtexport CORE_PEER_MSPCONFIGPATH=${MSP_PATH}/peerOrganizations/org1.example.com/users/Admin@org1.example.com/mspexport CORE_PEER_ADDRESS=localhost:7051
執行以下命令安裝:
peer lifecycle chaincode install mycc.tar.gz
成功後返回:
2022-02-09 22:09:13.498 EST 0001 INFO [cli.lifecycle.chaincode] submitInstallProposal -> Installed remotely: response:<status:200 payload:"\nEmycc:4c8dce2c7f746d26293ca8f27a3ccdec8d6438090f873f40f8ac9508c01973ae\022\004mycc" > 2022-02-09 22:09:13.498 EST 0002 INFO [cli.lifecycle.chaincode] submitInstallProposal -> Chaincode code package identifier: mycc:4c8dce2c7f746d26293ca8f27a3ccdec8d6438090f873f40f8ac9508c01973ae
6.2. 為機構 peer0.org2 安裝合約
執行以下命令,設定
peer0.org2
環境:
export CORE_PEER_LOCALMSPID="Org2MSP"export CORE_PEER_TLS_ROOTCERT_FILE=${MSP_PATH}/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crtexport CORE_PEER_MSPCONFIGPATH=${MSP_PATH}/peerOrganizations/org2.example.com/users/Admin@org2.example.com/mspexport CORE_PEER_ADDRESS=localhost:9051
執行以下命令安裝:
peer lifecycle chaincode install mycc.tar.gz
成功後返回:
2022-02-09 22:14:14.862 EST 0001 INFO [cli.lifecycle.chaincode] submitInstallProposal -> Installed remotely: response:<status:200 payload:"\nEmycc:4c8dce2c7f746d26293ca8f27a3ccdec8d6438090f873f40f8ac9508c01973ae\022\004mycc" > 2022-02-09 22:14:14.862 EST 0002 INFO [cli.lifecycle.chaincode] submitInstallProposal -> Chaincode code package identifier: mycc:4c8dce2c7f746d26293ca8f27a3ccdec8d6438090f873f40f8ac9508c01973ae
檢視安裝的合約清單:
peer lifecycle chaincode queryinstalled
返回合約的
Package ID
與
Label
:
Installed chaincodes on peer:Package ID: mycc:4c8dce2c7f746d26293ca8f27a3ccdec8d6438090f873f40f8ac9508c01973ae, Label: mycc
七、審批合約
當合約安裝後,需經過機構的審批達成一致後才允許使用。
7.1. 為機構 peer0.org1 審批合約定義
執行以下命令,設定
peer0.org1
環境:
export CORE_PEER_LOCALMSPID="Org1MSP"export CORE_PEER_TLS_ROOTCERT_FILE=${MSP_PATH}/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crtexport CORE_PEER_MSPCONFIGPATH=${MSP_PATH}/peerOrganizations/org1.example.com/users/Admin@org1.example.com/mspexport CORE_PEER_ADDRESS=localhost:7051
執行以下命令審批合約:
peer lifecycle chaincode approveformyorg \ -o localhost:7050 \ --ordererTLSHostnameOverride orderer.example.com \ --tls --cafile ${MSP_PATH}/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \ --channelID mychannel \ --name mycc \ --version 1.0 \ --package-id mycc:4c8dce2c7f746d26293ca8f27a3ccdec8d6438090f873f40f8ac9508c01973ae \ --sequence 1
package-id 的值按實際進行修改。
成功後返回:
2022-02-09 22:22:38.403 EST 0001 INFO [chaincodeCmd] ClientWait -> txid [2531db2811945a641947000cb15cfd19e0b72da594dfba994f5f79b6bc51bce2] committed with status (VALID) at localhost:7051
7.2. 為機構 peer0.org2 審批合約定義
執行以下命令,設定
peer0.org2
環境:
export CORE_PEER_LOCALMSPID="Org2MSP"export CORE_PEER_TLS_ROOTCERT_FILE=${MSP_PATH}/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crtexport CORE_PEER_MSPCONFIGPATH=${MSP_PATH}/peerOrganizations/org2.example.com/users/Admin@org2.example.com/mspexport CORE_PEER_ADDRESS=localhost:9051
執行以下命令審批合約:
peer lifecycle chaincode approveformyorg \ -o localhost:7050 \ --ordererTLSHostnameOverride orderer.example.com \ --tls \ --cafile ${MSP_PATH}/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \ --channelID mychannel \ --name mycc \ --version 1.0 \ --package-id mycc:4c8dce2c7f746d26293ca8f27a3ccdec8d6438090f873f40f8ac9508c01973ae \ --sequence 1
package-id 的值按實際進行修改。
成功後返回:
2022-02-09 22:22:47.711 EST 0001 INFO [chaincodeCmd] ClientWait -> txid [796a1e0a735e69425bcd5911bdf4b2a8003bbac977c5e60c769f84da6b86ef86] committed with status (VALID) at localhost:9051
7.3. 合約提交檢查
檢查合約的審批情況,是否可以向通道進行提交:
peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name mycc --version 1.0 --sequence 1 --output json
返回:
{ "approvals": { "Org1MSP": true, "Org2MSP": true } }
代表 Org1 和 Org2 都審批通過
八、提交合約
執行以下命令,向通道提交合約:
peer lifecycle chaincode commit \ -o localhost:7050 \ --ordererTLSHostnameOverride orderer.example.com \ --tls \ --cafile ${MSP_PATH}/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \ --channelID mychannel \ --name mycc \ --peerAddresses localhost:7051 \ --tlsRootCertFiles ${MSP_PATH}/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt \ --peerAddresses localhost:9051 \ --tlsRootCertFiles ${MSP_PATH}/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt \ --version 1.0 \ --sequence 1
成功後返回:
2022-02-09 22:22:57.445 EST 0001 INFO [chaincodeCmd] ClientWait -> txid [97ded758675113b9339dc9b378a13c0790ea3780855bb8f651758bfb007fc1ec] committed with status (VALID) at localhost:70512022-02-09 22:22:57.456 EST 0002 INFO [chaincodeCmd] ClientWait -> txid [97ded758675113b9339dc9b378a13c0790ea3780855bb8f651758bfb007fc1ec] committed with status (VALID) at localhost:9051
檢視通道上已經提交的合約:
peer lifecycle chaincode querycommitted --channelID mychannel --name mycc --output json
返回:
{ "sequence": 1, "version": "1.0", "endorsement_plugin": "escc", "validation_plugin": "vscc", "validation_parameter": "EiAvQ2hhbm5lbC9BcHBsaWNhdGlvbi9FbmRvcnNlbWVudA==", "collections": {}, "approvals": { "Org1MSP": true, "Org2MSP": true } }
九、測試智慧合約
- 交易資料使用
peer chaincode invoke [flags]
命令,該命令將嘗試向網路提交背書過的交易。 - 查詢資料使用
peer chaincode query [flags]
,該命令不會生成交易。
由於
invoke
命令所需要的引數較多,所以我們先建立一個指令碼命令。 執行
vim invoke.sh
新增以下內容:
peer chaincode invoke -o localhost:7050 \ --ordererTLSHostnameOverride orderer.example.com \ --tls \ --cafile ${MSP_PATH}/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \ -C mychannel \ -n mycc \ --peerAddresses localhost:7051 \ --tlsRootCertFiles ${MSP_PATH}/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt \ --peerAddresses localhost:9051 \ --tlsRootCertFiles ${MSP_PATH}/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt \ -c ${1}
9.1. 初始化賬本
執行以下命令,呼叫合約的
init
方法初始化 3 條賬本記錄:
sh invoke.sh '{"function":"init","Args":[]}'
9.2. 查詢資料
需要連線其中一個 peer 節點進行資料查詢
執行以下命令,設定
peer0.org1
環境:
export CORE_PEER_LOCALMSPID="Org1MSP"export CORE_PEER_TLS_ROOTCERT_FILE=${MSP_PATH}/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crtexport CORE_PEER_MSPCONFIGPATH=${MSP_PATH}/peerOrganizations/org1.example.com/users/Admin@org1.example.com/mspexport CORE_PEER_ADDRESS=localhost:7051
執行下面命令,呼叫
queryAll
方法,查詢所有資料:
peer chaincode query -C mychannel -n mycc -c '{"Args":["queryAll"]}'
執行後返回 3 條資料的陣列:
[{"money":100.0,"name":"zlt","userId":"1"},{"money":200.0,"name":"admin","userId":"2"},{"money":300.0,"name":"guest","userId":"3"}]
執行下面命令,呼叫
getUser
方法傳入
1
引數,查詢單個資料:
peer chaincode query -C mychannel -n mycc -c '{"Args":["getUser", "1"]}'
執行後返回 id 為 1 的資料:
{"money":100,"name":"zlt","userId":"1"}
9.3. 新增資料
執行以下命令,呼叫
addUser
方法,新增一條 id 為 4 的記錄:
sh invoke.sh '{"function":"addUser","Args":["4","test","400"]}'
9.4. 轉賬
執行以下命令,呼叫
transfer
方法,進行轉賬操作:
sh invoke.sh '{"function":"transfer","Args":["4","1","400"]}'
轉賬成功後,使用查詢命令進行檢視:
peer chaincode query -C mychannel -n mycc -c '{"Args":["queryAll"]}'
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70019616/viewspace-2905324/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Hyperledger Fabric 2.x 自定義智慧合約
- 區塊鏈100講:Hyperledger Fabric 中的鏈碼(智慧合約)區塊鏈
- Hyperledger Fabric 2.x Java區塊鏈應用Java區塊鏈
- Hyperledger Fabric 2.x Java 區塊鏈應用Java區塊鏈
- Hyperledger Fabric 2.x 動態更新智慧合約
- 2.06 hyperledger fabric智慧合約
- Hyperledger Fabric Node.js 智慧合約鏈碼開發Node.js
- Hyperledger Fabric Node.js 智慧合約即鏈碼開發Node.js
- 區塊鏈開發Hyperledger Fabric入門區塊鏈
- 區塊鏈 2.0:Hyperledger Fabric 介紹(十)區塊鏈
- HyperLedger Fabric和區塊鏈是什麼關係?區塊鏈
- Hyperledger Fabric 手動搭建【區塊鏈學習三】區塊鏈
- 區塊鏈智慧合約開發區塊鏈
- Hyperledger Fabric 中智慧合約的基本元件概述元件
- Fabric 2.x 智慧合約開發記錄
- Hyperledger Fabric 智慧合約開發及 fabric-sdk-go/fabric-gateway 使用示例GoGateway
- Hyperledger Fabric 使用 CouchDB 和複雜智慧合約開發
- 區塊鏈智慧合約是什麼?區塊鏈
- ATourofEthereum——區塊鏈與智慧合約之旅區塊鏈
- 區塊鏈智慧合約解決方案區塊鏈
- 區塊鏈——以太坊、智慧合約簡介區塊鏈
- 區塊鏈BSC智慧合約DAPP開發區塊鏈APP
- 區塊鏈合同智慧合約上鍊聯盟鏈區塊鏈
- Hyperledger Fabric 2.x 環境搭建
- 用大白話解釋區塊鏈分類賬本Hyperledger Fabric區塊鏈
- 白話智慧合約與區塊鏈技術區塊鏈
- 智慧合約-區塊鏈核心技術之一區塊鏈
- Hyperledger Fabric 超級賬本 java開發區塊鏈環境搭建Java區塊鏈
- 利用Hyperledger Fabric開發你的第一個區塊鏈應用區塊鏈
- 區塊鏈2.0以太坊智慧合約solidity之helloworld區塊鏈Solid
- 使用truffle部署以太坊智慧合約到區塊鏈區塊鏈
- 區塊鏈智慧合約技術系統開發區塊鏈
- 區塊鏈構建和履行智慧合約的步驟區塊鏈
- JAva智慧合約DAPP系統開發(區塊鏈)JavaAPP區塊鏈
- 南京區塊鏈智慧合約交易系統開發方案區塊鏈
- 鄭州區塊鏈智慧合約開發技術公司區塊鏈
- Hyperledger Fabric(Hyperledger Fabric模型)模型
- 2.07 hyperledger fabric鏈碼案例