本文是在fabric-sdk-java 成功新增區塊並自定義查詢的基礎上, 實現通過txid查詢資料; 由於fabric-sdk-java 的範例網路上比較多, 我是根據 github上的demo 開發的,只實現了新增資料,和查詢功能.
demo fabric-sdk-java-demo的下載地址:
github 下載地址
碼雲 下載地址
以下的解析只是符合我自己的需求,下面是官方原始碼的位置截圖,原始碼過多我就不貼了
fabric-sdk-java的原始碼下載地址:
下載地址(github)
下載地址(碼雲)
static String printableString(String string) {
if (StringUtils.isBlank(string)) {
return null;
}
return string.replaceAll("[^\\p{Print}]", "?");
}
Map<String, Long> expectedMoveRCMap = new HashMap<>();
private static final byte[] EXPECTED_EVENT_DATA = "!".getBytes(UTF_8);
private static final String EXPECTED_EVENT_NAME = "event";
private static final Map<String, String> TX_EXPECTED;
static {
TX_EXPECTED = new HashMap<>();
TX_EXPECTED.put("readset1", "Missing readset for channel bar block 1");
TX_EXPECTED.put("writeset1", "Missing writeset for channel bar block 1");
}
正式解析區塊之前, 要說明一下;
channel 需要自己獲取,由於我進行了封裝,所以不方便展示,另外 程式碼中使用的 fabricconfig是組態檔,獲取的是 channel名稱和 chaincode鏈碼名稱
@Override
public ResultJson<List<JSONObject>> queryFromBlockByTxId(String txid) throws Exception {
// 獲取通道設定
if (this.channel == null) {
getChannel();
}
/**
* 通過txid 獲取區塊資訊
*/
BlockInfo blockInfo = this.channel.queryBlockByTransactionID(txid);
if (blockInfo == null) {
return new ResultJson<>(ResultCode.TXID_NOT_EXIST);
}
long blockNumber = blockInfo.getBlockNumber();
log.info("當前區塊編號:{} 資料hash ={}", blockNumber, Hex.encodeHexString(blockInfo.getDataHash()));
log.info("當前區塊編號:{} 前塊hash = {}", blockNumber, Hex.encodeHexString(blockInfo.getPreviousHash()));
// 如果一個通道內有多個鏈碼,就不能通過索引獲取了 --- 個人分析未論證
// BlockInfo.EnvelopeInfo tmp = blockInfo.getEnvelopeInfo(0);
// String channelId1 = tmp.getChannelId();
// System.out.println(channelId1);
Iterable<BlockInfo.EnvelopeInfo> envelopeInfos = blockInfo.getEnvelopeInfos();
List<JSONObject> list = new ArrayList<>();
for (BlockInfo.EnvelopeInfo envelopeInfo :envelopeInfos) {
// 從區塊中獲取channelId = mychannel
final String channelId = envelopeInfo.getChannelId();
log.info(" 通道 id: {}", channelId);
/**
* 判斷不是指定通道名稱,則跳過
*/
if (!channelId.equals(fabricConfig.getChannelName())) {
log.error("通道名稱不正確跳過,設定通道id= {},獲取的通道id = {}", fabricConfig.getChannelName(), channelId);
continue;
}
/**
* 獲取落塊時間
*/
String time = TimeUtil.parseToyyyyMMddmmssSSS(envelopeInfo.getTimestamp());
log.info(" 時間: {}", time);
// 需要定位是組織內的哪個節點
log.info(" 提交事務的身份: {}", envelopeInfo.getCreator().getMspid());
if (envelopeInfo.getType() != TRANSACTION_ENVELOPE) {
log.error("envelopeInfo的type值不正確,常數 ={} 解析 ={}", TRANSACTION_ENVELOPE, envelopeInfo.getType());
continue;
}
// 強轉型別
BlockInfo.TransactionEnvelopeInfo transactionEnvelopeInfo = (BlockInfo.TransactionEnvelopeInfo) envelopeInfo;
log.info(" 是否經過校驗 {}", transactionEnvelopeInfo.isValid());
// 獲取操作事務的資訊
for (BlockInfo.TransactionEnvelopeInfo.TransactionActionInfo transactionActionInfo : transactionEnvelopeInfo.getTransactionActionInfos()) {
// 鏈碼名稱
String chaincodeIDName = transactionActionInfo.getChaincodeIDName();
// 鏈碼版本
String chaincodeIDVersion = transactionActionInfo.getChaincodeIDVersion();
log.info(" proposal chaincodeIDName:{}, chaincodeIDVersion: {}", chaincodeIDName, chaincodeIDVersion);
// 操作事務的讀寫集
TxReadWriteSetInfo rwsetInfo = transactionActionInfo.getTxReadWriteSet();
if (null != rwsetInfo) {
//我只要了寫集合的資料
for (TxReadWriteSetInfo.NsRwsetInfo nsRwsetInfo : rwsetInfo.getNsRwsetInfos()) {
// 含有預設鏈碼 _lifecycle 和 自定義鏈碼 mycc
String namespace = nsRwsetInfo.getNamespace();
// 只要符合要求的鏈碼的 mycc
if (!namespace.equals(fabricConfig.getChainCodeName())) {
log.error("鏈碼名稱不正確 跳過 ,namespace ={} fabricConfig.getChainCodeName() ={}", namespace, fabricConfig.getChainCodeName());
continue;
}
KvRwset.KVRWSet rws = nsRwsetInfo.getRwset();
for (KvRwset.KVWrite writeList : rws.getWritesList()) {
String valAsString = printableString(new String(writeList.getValue().toByteArray(), UTF_8));
log.info("Namespace {} key {} has value '{}' ", namespace, writeList.getKey(), valAsString);
if (StringUtils.isNotBlank(valAsString)) {
list.add(JSON.parseObject(valAsString, JSONObject.class));
}
}
}
}
}
}
return new ResultJson<List<JSONObject>>(ResultCode.OK, list);
}