共计 6287 个字符,预计需要花费 16 分钟才能阅读完成。
一、阐明
在上一篇文章中《Hyperledger Fabric 2.x 自定义智能合约》分享了智能合约的装置并应用 cli
客户端进行合约的调用;本文将应用 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 对象,代码如下:
/**
* 连贯网关
*/
@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);
}
五、合约调用
创立 controller 类,注入 Contract 对象调用合约办法:
@Resource
private Contract contract;
@Resource
private 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
:
http://127.0.0.1:9001/getUser?userId=1
返回:
{
"money": 300,
"name": "zlt",
"userId": "1"
}
调用接口 addUser
:
http://127.0.0.1:9001/addUser?userId=6&userName=test6&money=600
返回:
2ae291bb6a366b5ba01ad49e4237da8def9e9828cc2c982e8c49d4b763af0157
七、代码下载
gitee:https://gitee.com/zlt2000/my-fabric-application-java
github:https://github.com/zlt2000/my-fabric-application-java
扫码关注有惊喜!