使用官方测试网络搭建fabric应用

前言

这里使用的是官方测试网络,智能合约是用go实现,接口是使用了fabric gateway java技术来连接到虚拟机上的网络,本文默认已安装好各种所需的环境

测试网络的使用

启动网络

进入对应文件夹

1
cd fabric-samples/test-network		

启动测试网络

1
./network.sh up

创建通道

1
./network.sh createChannel -c 自定义通道名称

二者也可结合起来

1
./network.sh up createChannel

编写链码

创建链码目录

1
mkdir atcc && cd atcc

创建模块和源文件

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
167
168
169
170
171
172
173
174
175
176
177
package main

import (
"encoding/json"
"fmt"
"log"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)

// SmartContract provides functions for managing an Asset
type SmartContract struct {
contractapi.Contract
}
// Asset describes basic details of what makes up a simple asset
type Asset struct {
ID string `json:"ID"`
Owner string `json:"owner"`
Value int `json:"Value"`
}
// InitLedger adds a base set of assets to the ledger
func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
assets := []Asset{
{ID: "asset1", Owner: "ZhangSan", Value: 300},
{ID: "asset2", Owner: "LiSi", Value: 400},
{ID: "asset3", Owner: "Klay", Value: 500},
}

for _, asset := range assets {
assetJSON, err := json.Marshal(asset)
if err != nil {
return err
}

err = ctx.GetStub().PutState(asset.ID, assetJSON)
if err != nil {
return fmt.Errorf("failed to put to world state. %v", err)
}
}

return nil
}
// CreateAsset issues a new asset to the world state with given details.
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, owner string, Value int) error {
exists, err := s.AssetExists(ctx, id)
if err != nil {
return err
}
if exists {
return fmt.Errorf("the asset %s already exists", id)
}

asset := Asset{
ID: id,
Owner: owner,
Value: Value,
}
assetJSON, err := json.Marshal(asset)
if err != nil {
return err
}

return ctx.GetStub().PutState(id, assetJSON)
}
// ReadAsset returns the asset stored in the world state with given id.
func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
assetJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return nil, fmt.Errorf("failed to read from world state: %v", err)
}
if assetJSON == nil {
return nil, fmt.Errorf("the asset %s does not exist", id)
}

var asset Asset
err = json.Unmarshal(assetJSON, &asset)
if err != nil {
return nil, err
}

return &asset, nil
}
// UpdateAsset updates an existing asset in the world state with provided parameters.
func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, owner string, Value int) error {
exists, err := s.AssetExists(ctx, id)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("the asset %s does not exist", id)
}

// overwriting original asset with new asset
asset := Asset{
ID: id,
Owner: owner,
Value: Value,
}
assetJSON, err := json.Marshal(asset)
if err != nil {
return err
}

return ctx.GetStub().PutState(id, assetJSON)
}
// DeleteAsset deletes an given asset from the world state.
func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error {
exists, err := s.AssetExists(ctx, id)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("the asset %s does not exist", id)
}

return ctx.GetStub().DelState(id)
}
// AssetExists returns true when asset with given ID exists in world state
func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
assetJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return false, fmt.Errorf("failed to read from world state: %v", err)
}

return assetJSON != nil, nil
}
// TransferAsset updates the owner field of asset with given id in world state.
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error {
asset, err := s.ReadAsset(ctx, id)
if err != nil {
return err
}

asset.Owner = newOwner
assetJSON, err := json.Marshal(asset)
if err != nil {
return err
}

return ctx.GetStub().PutState(id, assetJSON)
}
// GetAllAssets returns all assets found in world state
func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {
// range query with empty string for startKey and endKey does an
// open-ended query of all assets in the chaincode namespace.
resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
if err != nil {
return nil, err
}
defer resultsIterator.Close()

var assets []*Asset
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return nil, err
}

var asset Asset
err = json.Unmarshal(queryResponse.Value, &asset)
if err != nil {
return nil, err
}
assets = append(assets, &asset)
}

return assets, nil
}
func main() {
assetChaincode, err := contractapi.NewChaincode(&SmartContract{})
if err != nil {
log.Panicf("Error creating asset-transfer-basic chaincode: %v", err)
}

if err := assetChaincode.Start(); err != nil {
log.Panicf("Error starting asset-transfer-basic chaincode: %v", err)
}
}

管理链码依赖

1
2
go mod tidy #增加缺失的包,移除没用的包
go mod vendor #将依赖包复制到项目下的 vendor 目录

部署链码

设置环境变量

1
2
export PATH=${PWD}/../bin:$PATH
export FABRIC_CFG_PATH=$PWD/../config/

添加链码名字的环境变量

1
export CHAINCODE_NAME=<chaincode-name> #<chaincode-name>替换成自定义的链码名字

打包链码

1
2
3
4
peer lifecycle chaincode package $CHAINCODE_NAME.tar.gz \
--path ../asset-transfer-basic/<chaincode-folder>/ \ #注意替换链码文件路径
--lang golang \
--label <chaincode-name>_1.0 #注意替换<chaincode-name>内容

以org1身份安装链码

1
2
3
4
5
6
7
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051

peer lifecycle chaincode install $CHAINCODE_NAME.tar.gz

以org2身份安装链码

1
2
3
4
5
6
7
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
export CORE_PEER_ADDRESS=localhost:9051

peer lifecycle chaincode install $CHAINCODE_NAME.tar.gz

查询链码包ID

1
peer lifecycle chaincode queryinstalled

添加链码包ID的环境变量

1
export CC_PACKAGE_ID=<Package ID> #注意替换这里的链码包ID

org2批准链码定义

1
2
3
4
5
6
7
8
9
10
peer lifecycle chaincode approveformyorg \
-o localhost:7050 \
--ordererTLSHostnameOverride orderer.example.com \
--channelID mychannel \
--name $CHAINCODE_NAME \
--version 1.0 \
--package-id $CC_PACKAGE_ID \
--sequence 1 \
--tls \
--cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

切换到org1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_ADDRESS=localhost:7051


peer lifecycle chaincode approveformyorg \
-o localhost:7050 \
--ordererTLSHostnameOverride orderer.example.com \
--channelID mychannel \
--name $CHAINCODE_NAME \
--version 1.0 \
--package-id $CC_PACKAGE_ID \
--sequence 1 \
--tls \
--cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

检查同意情况

1
2
3
4
5
6
7
8
peer lifecycle chaincode checkcommitreadiness \
--channelID mychannel \
--name $CHAINCODE_NAME \
--version 1.0 \
--sequence 1 \
--tls \
--cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \
--output json

提交链码

1
2
3
4
5
6
7
8
9
10
11
12
13
peer lifecycle chaincode commit \
-o localhost:7050 \
--ordererTLSHostnameOverride orderer.example.com \
--channelID mychannel \
--name $CHAINCODE_NAME \
--version 1.0 \
--sequence 1 \
--tls \
--cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \
--peerAddresses localhost:7051 \
--tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt \
--peerAddresses localhost:9051 \
--tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt

注意,这里是以org1身份提交的链码,亦可以用org2进行提交,只需一方提交一次即可。

确认是否提交

1
2
3
4
peer lifecycle chaincode querycommitted \
--channelID mychannel \
--name $CHAINCODE_NAME \
--cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

调用链码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 初始化 资产
peer chaincode invoke \
-o localhost:7050 \
--ordererTLSHostnameOverride orderer.example.com \
--tls \
--cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \
-C mychannel \
-n $CHAINCODE_NAME \
--peerAddresses localhost:7051 \
--tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt \
--peerAddresses localhost:9051 \
--tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt \
-c '{"function":"initLedger","Args":[]}'

# 查询所有资产
peer chaincode query \
-C mychannel \
-n $CHAINCODE_NAME \
-c '{"Args":["getAllAssets"]}'

使用fabric-gateway创建应用与fabric网络交互的SDK,用java来实现

添加maven依赖

1
2
3
4
5
<dependency>
<groupId>org.hyperledger.fabric</groupId>
<artifactId>fabric-gateway-java</artifactId>
<version>2.2.3</version>
</dependency>

准备配置文件

创建目录 crypto-configordererpeer 节点的证书文件复制进来。

证书文件从 fabric-samplestest-network 目录中复制 ordererOrganizationspeerOrganizations 文件夹

注:crypto-config的位置项目根目录下

创建文件 connection.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
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
{
"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中的地址,内容中分别包含了 channelsorganizationsordererspeersca 的配置

application.yml 中添加以下内容,用于访问网关的相关配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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: chenyi

分别构建网关、通道和合约的Bean对象

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
package org.zlt.fabric.config;

import lombok.extern.slf4j.Slf4j;
import org.hyperledger.fabric.gateway.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
* TODO
*
* @author zlt
* @version 1.0
* @date 2022/2/16
* <p>
* Blog: https://zlt2000.gitee.io
* Github: https://github.com/zlt2000
*/
@Slf4j
@Configuration
public class GatewayConfig {
/**
* wallet文件夹路径
*/
@Value("${fabric.walletDirectory}")
private String walletDirectory;
/**
* 网络配置文件路径
*/
@Value("${fabric.networkConfigPath}")
private String networkConfigPath;
/**
* 用户证书路径
*/
@Value("${fabric.certificatePath}")
private String certificatePath;
/**
* 用户私钥路径
*/
@Value("${fabric.privateKeyPath}")
private String privateKeyPath;
/**
* 访问的组织名
*/
@Value("${fabric.mspid}")
private String mspid;
/**
* 用户名
*/
@Value("${fabric.username}")
private String username;
/**
* 通道名字
*/
@Value("${fabric.channelName}")
private String channelName;
/**
* 链码名字
*/
@Value("${fabric.contractName}")
private String contractName;

/**
* 连接网关
*/
@Bean
public 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();
}

/**
* 获取通道
*/
@Bean
public Network network(Gateway gateway) {
return gateway.getNetwork(this.channelName);
}

/**
* 获取合约
*/
@Bean
public Contract contract(Network network) {
return network.getContract(this.contractName);
}

private static X509Certificate readX509Certificate(final Path certificatePath) throws IOException, CertificateException {
try (Reader certificateReader = Files.newBufferedReader(certificatePath, StandardCharsets.UTF_8)) {
return Identities.readX509Certificate(certificateReader);
}
}

private static PrivateKey getPrivateKey(final Path privateKeyPath) throws IOException, InvalidKeyException {
try (Reader privateKeyReader = Files.newBufferedReader(privateKeyPath, StandardCharsets.UTF_8)) {
return Identities.readPrivateKey(privateKeyReader);
}
}
}

创建Result类,统一格式返回

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
package org.zlt.fabric.common;

/**
* @author ChenYi
* @date 2023年02月15日 15:03
*/
public class Result<T> {
private String code;
private String msg;
private T data;

public String getCode() {
return code;
}

public void setCode(String code) {
this.code = code;
}

public String getMsg() {
return msg;
}

public void setMsg(String msg) {
this.msg = msg;
}

public T getData() {
return data;
}

public void setData(T data) {
this.data = data;
}
public Result(){

}

public Result(T data) {
this.data = data;
}
public static Result success(){
Result result = new Result<>();
result.setCode("0");
result.setMsg("成功");
return result;
}

public static <T> Result<T> success(T data){
Result<T> result = new Result<>(data);
result.setCode("0");
result.setMsg("成功");
return result;
}
public static Result error(String code, String msg){
Result result = new Result();
result.setCode(code);
result.setMsg(msg);
return result;
}
}

创建controller类,注入Contract对象调用合约方法

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
package org.zlt.fabric.controller;

import org.hyperledger.fabric.gateway.Contract;
import org.hyperledger.fabric.gateway.ContractException;
import org.hyperledger.fabric.gateway.Network;
import org.hyperledger.fabric.sdk.Peer;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.zlt.fabric.common.Result;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.EnumSet;
import java.util.concurrent.TimeoutException;

/**
* TODO
*
* @author zlt
* @version 1.0
* @date 2022/2/21
* <p>
* Blog: https://zlt2000.gitee.io
* Github: https://github.com/zlt2000
*/
@RestController
public class TestController {
@Resource
private Contract contract;

@Resource
private Network network;

@GetMapping("/GetAllAssets")
public String getUser() throws ContractException {
byte[] queryAResultBefore = contract.evaluateTransaction("GetAllAssets");
return new String(queryAResultBefore, StandardCharsets.UTF_8);
}

@GetMapping("/CreateAsset")
public Result<?> addUser(String userId, String userName, String money) throws ContractException, InterruptedException, TimeoutException {
byte[] invokeResult = contract.createTransaction("CreateAsset")
.setEndorsingPeers(network.getChannel().getPeers(EnumSet.of(Peer.PeerRole.ENDORSING_PEER)))
.submit(userId, userName, money);
String txId = new String(invokeResult, StandardCharsets.UTF_8);
return Result.success("add success");
}
}

应用创建完毕,测试接口

访问:

http://127.0.0.1:9001/GetAllAssets

返回:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[
{
"ID": "6",
"owner": "test6",
"Value": 600
},
{
"ID": "666",
"owner": "chenyi",
"Value": 0
},
{
"ID": "8989",
"owner": "chenyi6666",
"Value": 8989
}
]

访问:

``http://127.0.0.1:9001/CreateAsset?ID=1&owner=chenyi&Value=8989`

返回:

1
{"code":"0","msg":"成功","data":"添加成功"}