Fabric java sdk 1.4简明教程

在超级账本Fabric区块链中,应用通过节点的RPC协议接口访问链码。Java应用可以使用 官方提供的Fabric-sdk-java开发包来实现对链码的访问,开发包封装了Fabric 区块链的GRPC链码访问协议,有利于开发人员聚焦于业务逻辑。不过Fabric-sdk-java的文档质量保持了Hyperledger Fabric产品一贯的不知所云,看起来实在是令人捉急。本文将尝试编写 一个最简单的可以访问Fabric链码的Java应用,来帮助你快速上手Farbic区块链 的Java应用开发。

基本原理

在Fabric区块链中,应用通过节点的RPC协议接口访问链码:

fabric-sdk-java

类似于Shim API对链码通信协议的封装,Fabric Java SDK提供了 对节点RPC协议 接口的封装,其入口类为HFClient,对链码的查询和交易操作则 封装在Channel类中:

java sdk brief

由于Fabric是许可制区块链,因此应用也需要持有证书和私钥以表征自己的身份,HFClient实例 依赖于User接口的实现对象 来访问一个特定身份的证书和私钥,因此在访问 链码之前,我们需要首先定义一个简单的User接口实现类。

实现User接口

HFClient实例访问Fabric网络的身份使用User接口实现对象来表征,因此我们需要定义一个简单的 User接口实现类LocalUser

user impl

容易理解,一个用户的身份是由它的证书来标识的,同时交易还需要证书对应的私钥,因此LocalUser 的核心逻辑就是利用指定的证书和私钥PEM文件满足User接口的要求。

首先按照User接口的要求完成类LocalUser的骨架:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import org.hyperledger.fabric.sdk.User;
import org.hyperledger.fabric.sdk.security.CryptoPrimitives;

public class LocalUser implements User{ //实现User接口
private String name;
private String mspId;
private Enrollment enrollment;

LocalUser(String name,String mspId){
this.name = name;
this.mspId = mspId;
}

private Enrollment loadFromPemFile(String keyFile,String certFile){ /*见下文说明*/ }

@Override public String getName(){ return name; }
@Override public String getMspId() { return mspId}
@Override public Enrollment getEnrollment() { return enrollment; }
@Override public String getAccount() { return null; }
@Override public String getAffiliation() { return null; }
}

在Fabric Java SDK中,Enrollment接口用来提供对用户的私钥和证书的访问, 并且预置了一个适合 X509证书的实现类X509Enrollment,因此我们可以从本地 MSP目录中的PEM文件中载入用户私钥和签名证书:

1
2
3
4
5
6
7
private Enrollment loadFromPemFile(String keyFile,String certFile) throws Exception{
byte[] keyPem = Files.readAllBytes(Paths.get(keyFile)); //载入私钥PEM文本
byte[] certPem = Files.readAllBytes(Paths.get(certFile)); //载入证书PEM文本
CryptoPrimitives suite = new CryptoPrimitives(); //载入密码学套件
PrivateKey privateKey = suite.bytesToPrivateKey(keyPem); //将PEM文本转换为私钥对象
return new X509Enrollment(privateKey,new String(certPem)); //创建并返回X509Enrollment对象
}

访问链码

有了User接口实现类,接下来只要创建一个HFClient实例,然后获取通道对象, 就可以查询链码,或者提交链码交易了。

完整的实现代码如下:

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
package com.hubwiz.demo;

import org.hyperledger.fabric.sdk.User;
import org.hyperledger.fabric.sdk.HFClient;
import org.hyperledger.fabric.sdk.Channel;
import org.hyperledger.fabric.sdk.Peer;
import org.hyperledger.fabric.sdk.Orderer;
import org.hyperledger.fabric.sdk.security.CryptoSuite;
import org.hyperledger.fabric.sdk.ChaincodeID;
import org.hyperledger.fabric.sdk.QueryByChaincodeRequest;
import org.hyperledger.fabric.sdk.ProposalResponse;
import org.hyperledger.fabric.sdk.TransactionProposalRequest;
import org.hyperledger.fabric.sdk.BlockEvent.TransactionEvent;

import java.util.Collection;
import java.util.concurrent.CompletableFuture;

public class App{
public static void main(String[] args) throws Exception{
System.out.println("counter app");

//创建User实例
String keyFile = "../solo-network/msp/keystore/user-key.pem";
String certFile = "../solo-network/msp/signcerts/user-cert.pem";
LocalUser user = new LocalUser("admin","SampleOrg",keyFile,certFile);

//创建HFClient实例
HFClient client = HFClient.createNewInstance();
client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());
client.setUserContext(user);

//创建通道实例
Channel channel = client.newChannel("ch1");
Peer peer = client.newPeer("peer1`","grpc://127.0.0.1:7051");
channel.addPeer(peer);
Orderer orderer = client.newOrderer("orderer1","grpc://127.0.0.1:7050");
channel.addOrderer(orderer);
channel.initialize();

//查询链码
QueryByChaincodeRequest req = client.newQueryProposalRequest();
ChaincodeID cid = ChaincodeID.newBuilder().setName("counter-cc").build();
req.setChaincodeID(cid);
req.setFcn("value");
ProposalResponse[] rsp = channel.queryByChaincode(req).toArray(new ProposalResponse[0]);
System.out.format("rsp message => %s\n",rsp[0].getProposalResponse().getResponse().getPayload().toStringUtf8());

//提交链码交易
TransactionProposalRequest req2 = client.newTransactionProposalRequest();
req2.setChaincodeID(cid);
req2.setFcn("inc");
req2.setArgs("10");
Collection<ProposalResponse> rsp2 = channel.sendTransactionProposal(req2);
TransactionEvent event = channel.sendTransaction(rsp2).get();
System.out.format("txid: %s\n", event.getTransactionID());
System.out.format("valid: %b\n", event.isValid());
}
}

汇智网原创,转载请标明出处