5.Fabric v2.0 java-sdk 根據TXID查詢區塊並從區塊解析資料

2020-09-19 16:00:02

根據TXID查詢資料

本文是在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);
    }