在超級(jí)賬本Fabric區(qū)塊鏈中乒疏,應(yīng)用通過(guò)節(jié)點(diǎn)的RPC協(xié)議接口訪問(wèn)鏈碼。Java應(yīng)用可以使用官方提供的Fabric-sdk-java開發(fā)包來(lái)實(shí)現(xiàn)對(duì)鏈碼的訪問(wèn)翁都,開發(fā)包封裝了Fabric區(qū)塊鏈的GRPC鏈碼訪問(wèn)協(xié)議,有利于開發(fā)人員聚焦于業(yè)務(wù)邏輯横堡。不過(guò)Fabric-sdk-java的文檔質(zhì)量保持了Hyperledger Fabric產(chǎn)品一貫的不知所云,看起來(lái)實(shí)在是令人捉急。本文將嘗試編寫一個(gè)最簡(jiǎn)單的可以訪問(wèn)Fabric鏈碼的Java應(yīng)用,來(lái)幫助你快速上手Farbic區(qū)塊鏈的Java應(yīng)用開發(fā)耘柱。
如果希望快速掌握Fabric區(qū)塊鏈的鏈碼及應(yīng)用開發(fā),建議訪問(wèn)匯智網(wǎng)的在線互動(dòng)課程:
基本原理
在Fabric區(qū)塊鏈中棍现,應(yīng)用通過(guò)節(jié)點(diǎn)的RPC協(xié)議接口訪問(wèn)鏈碼:
類似于Shim API對(duì)鏈碼通信協(xié)議的封裝,F(xiàn)abric Java SDK提供了 對(duì)節(jié)點(diǎn)RPC協(xié)議接口的封裝镜遣,其入口類為HFClient己肮,對(duì)鏈碼的查詢和交易操作則 封裝在Channel類中:
由于Fabric是許可制區(qū)塊鏈,因此應(yīng)用也需要持有證書和私鑰以表征自己的身份悲关,HFClient實(shí)例依賴于User接口的實(shí)現(xiàn)對(duì)象來(lái)訪問(wèn)一個(gè)特定身份的證書和私鑰谎僻,因此在訪問(wèn) 鏈碼之前,我們需要首先定義一個(gè)簡(jiǎn)單的User接口實(shí)現(xiàn)類寓辱。
實(shí)現(xiàn)User接口
HFClient
實(shí)例訪問(wèn)Fabric網(wǎng)絡(luò)的身份使用User
接口實(shí)現(xiàn)對(duì)象來(lái)表征艘绍,因此我們需要定義一個(gè)簡(jiǎn)單的User接口實(shí)現(xiàn)類LocalUser
:
容易理解,一個(gè)用戶的身份是由它的證書來(lái)標(biāo)識(shí)的秫筏,同時(shí)交易還需要證書對(duì)應(yīng)的私鑰诱鞠,因此LocalUser的核心邏輯就是利用指定的證書和私鑰PEM文件滿足User接口的要求挎挖。
首先按照User接口的要求完成類LocalUser
的骨架:
import org.hyperledger.fabric.sdk.User;
import org.hyperledger.fabric.sdk.security.CryptoPrimitives;
public class LocalUser implements User{ //實(shí)現(xiàn)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){ /*見下文說(shuō)明*/ }
@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
接口用來(lái)提供對(duì)用戶的私鑰和證書的訪問(wèn)航夺,并且預(yù)置了一個(gè)適合X509證書的實(shí)現(xiàn)類X509Enrollment
蕉朵,因此我們可以從本地MSP目錄中的PEM文件中載入用戶私鑰和簽名證書:
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(); //載入密碼學(xué)套件
PrivateKey privateKey = suite.bytesToPrivateKey(keyPem); //將PEM文本轉(zhuǎn)換為私鑰對(duì)象
return new X509Enrollment(privateKey,new String(certPem)); //創(chuàng)建并返回X509Enrollment對(duì)象
}
訪問(wèn)鏈碼
有了User接口實(shí)現(xiàn)類,接下來(lái)只要?jiǎng)?chuàng)建一個(gè)HFClient實(shí)例阳掐,然后獲取通道對(duì)象始衅,就可以查詢鏈碼,或者提交鏈碼交易了缭保。
完整的實(shí)現(xiàn)代碼如下:
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");
//創(chuàng)建User實(shí)例
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);
//創(chuàng)建HFClient實(shí)例
HFClient client = HFClient.createNewInstance();
client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());
client.setUserContext(user);
//創(chuàng)建通道實(shí)例
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());
}
}
匯智網(wǎng)原創(chuàng)汛闸,轉(zhuǎn)載請(qǐng)表明出處