fabric如何解析一個common.block的內容
fabric如何解析一個common.block的內容
在前面的文章 “fabric如何從ledger裡面讀取block的內容” 中我們介紹瞭如何從ledger檔案裡面讀取一個block,然後把block的內容寫到一個檔案中,再使用工具configtxlator把block內容翻譯成json,這樣使用者就可以讀取block的內容了。
這篇文章中我們直接用golang藉助於fabric本身的程式碼,利用程式碼來解析和讀取block的內容;也就是說當我們得到一個common.block物件時,我們直接利用fabric的資料結構和函式訪問block的內容,而不需要藉助configtxlator。
這部分程式碼是一個框架,不能單獨編譯,必須藉助於前面提到的文章"fabric如何從ledger裡面讀取block的內容”的部分程式碼。
主函式
接受一個common.block物件,然後根據是Config block還是Endorser Transaction block分別解析。
包含:
- block number
- block hash value
- channel
- txid
- creator
package main import ( "fmt" "encoding/base64" "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric/protos/common" "github.com/hyperledger/fabric/protos/utils" "github.com/hyperledger/fabric/protos/peer" "github.com/hyperledger/fabric/common/configtx" "github.com/hyperledger/fabric/core/ledger/util" "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil" ) func parseBlock(block *common.Block) error { var err error // Handle header fmt.Printf("Block: Number=[%d], CurrentBlockHash=[%s], PreviousBlockHash=[%s]\n", block.GetHeader().Number, base64.StdEncoding.EncodeToString(block.GetHeader().DataHash), base64.StdEncoding.EncodeToString(block.GetHeader().PreviousHash)) // Handle transaction var tranNo int64 = -1 txsFilter := util.TxValidationFlags(block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER]) if len(txsFilter) == 0 { txsFilter = util.NewTxValidationFlags(len(block.Data.Data)) block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsFilter } for _, envBytes := range block.Data.Data { tranNo++ if txsFilter.IsInvalid(int(tranNo)) { fmt.Printf("Transaction: No=[%d], Status=[INVALID]\n", tranNo) continue } else { fmt.Printf("Transaction: No=[%d], Status=[VALID]\n",tranNo) } var env *common.Envelope if env, err = utils.GetEnvelopeFromBlock(envBytes); err != nil { return err } var payload *common.Payload if payload, err = utils.GetPayload(env); err != nil { return err } var chdr *common.ChannelHeader if chdr, err = utils.UnmarshalChannelHeader(payload.Header.ChannelHeader); err != nil { return err } fmt.Printf("txid=[%s], channel=[%s]\n", chdr.TxId, chdr.ChannelId) var shdr *common.SignatureHeader if shdr, err = utils.GetSignatureHeader(payload.Header.SignatureHeader); err != nil { return err } var mspid, subject string if mspid, subject, err = decodeSerializedIdentity(shdr.Creator); err != nil { return err } fmt.Printf("creator=[%s:%s]\n", mspid, subject) if common.HeaderType(chdr.Type) == common.HeaderType_CONFIG { fmt.Printf("type=[CONFIG]\n") if err = parseConfig(payload); err != nil { return err } } else if common.HeaderType(chdr.Type) == common.HeaderType_ENDORSER_TRANSACTION { fmt.Printf("type=[ENDORSER_TRANSACTION]\n") if err = parseEndorserTransaction(payload); err != nil { return err } } else { fmt.Printf("txid=[%s], channel=[%s], type=[UNKNOWN]\n", chdr.TxId, chdr.ChannelId) } } return nil }
解析endorser transaction block
這個解析並不完整,只是我們專案中需要的欄位進行了解析,其他的丟棄了。
主要包括:
- endorsers
- RWSet (chaincode, key)
func parseEndorserTransaction(payload *common.Payload) error { var err error var tx *peer.Transaction if tx, err = utils.GetTransaction(payload.Data); err != nil { return err } fmt.Printf("actions\n") for _, action := range tx.Actions { var capayload *peer.ChaincodeActionPayload var ca*peer.ChaincodeAction if capayload, ca, err = utils.GetPayloads(action); err != nil { return err } fmt.Printf("endorsers\n") for _, endorser := range capayload.Action.Endorsements { var mspid, subject string if mspid, subject, err = decodeSerializedIdentity(endorser.Endorser); err != nil { return err } fmt.Printf("endorser[%s:%s]\n", mspid, subject) } fmt.Printf("RWSet\n") txRWSet := &rwsetutil.TxRwSet{} if err = txRWSet.FromProtoBytes(ca.Results); err != nil { return err } for _, nsRWSet := range txRWSet.NsRwSets { ns := nsRWSet.NameSpace if ns != "lscc" {// skip system chaincode fmt.Printf("ns=[%v]\n", ns) fmt.Printf("RDSet\n") for _, kvRead := range nsRWSet.KvRwSet.Reads { fmt.Printf("key=[%v]\n", kvRead.Key) } fmt.Printf("WRSet\n") for _, kvWrite := range nsRWSet.KvRwSet.Writes { if kvWrite.IsDelete { fmt.Printf("key=[%v] op=[delete]\n", kvWrite.Key) } else { fmt.Printf("key=[%v] op=[write]\n",kvWrite.Key) } } } } } return nil }
解析 config block
和前面一樣,也沒有解析整個config block,只解析了我們專案中需要的欄位,其實只有一個orderer address,當然其他的也很容易處理了。
func parseConfig(payload *common.Payload) error { var err error var configEnvelope *common.ConfigEnvelope if configEnvelope, err = configtx.UnmarshalConfigEnvelope(payload.Data); err != nil { return err } var configGroup * common.ConfigGroup = configEnvelope.Config.ChannelGroup fmt.Printf("Groups\n") for k, _ := range configGroup.Groups { fmt.Printf("%s\n", k) } fmt.Printf("Values\n") for k, v := range configGroup.Values { fmt.Printf("%s\n", k) if k == "OrdererAddresses" { addresses := &common.OrdererAddresses{} if err = proto.Unmarshal(v.Value, addresses); err != nil { return err } for _, address := range addresses.Addresses { fmt.Printf("[%s]\n",address) } } } return nil }