
本文将使用
Java 代码基于
fabric-gateway-java 进行区块链网络的访问与交易,并集成
SpringBoot 框架。
Fabric Gateway SDK 实现 Fabric 的编程模型,提供了一系列简单的 API 给应用程序与 Fabric 区块链网络进行交互;
网络拓扑图:

应用程序将各自的网络交互委托给其网关,每个网关都了解网络信道拓扑,包括组织的多个 Peer 节点和排序节点,使应用程序专注于业务逻辑;Peer 节点可以使用 gossip 协议在组织内部和组织之间相互通信。
添加网关 sdk 的依赖:
<dependency> <groupId>org.hyperledger.fabric</groupId> <artifactId>fabric-gateway-java</artifactId> <version>2.2.3</version></dependency>
工程的目录结构如下图所示:

创建目录
crypto-config 把
orderer 和
peer 节点的证书文件复制进来。
证书文件从
fabric-samples 的
test-network 目录中复制
ordererOrganizations 与
peerOrganizations 文件夹:

创建文件
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的配置
在
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:
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
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70019616/viewspace-2905322/ ,如需转载,请注明出处,否则将追究法律责任。