Skip to content

Commit

Permalink
Add support for config blocks parsing
Browse files Browse the repository at this point in the history
Signed-off-by: Jim Zhang <[email protected]>
  • Loading branch information
jimthematrix committed Jan 20, 2022
1 parent 27f9472 commit 1452fdd
Show file tree
Hide file tree
Showing 14 changed files with 2,016 additions and 51 deletions.
2 changes: 0 additions & 2 deletions internal/fabric/client/ledger.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
package client

import (
//nolint

"github.com/hyperledger/fabric-sdk-go/pkg/client/ledger"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/core"
Expand Down
23 changes: 19 additions & 4 deletions internal/fabric/utils/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,20 @@

package utils

import "github.com/hyperledger/fabric-protos-go/peer"
import (
"github.com/hyperledger/fabric-protos-go/common"
"github.com/hyperledger/fabric-protos-go/peer"
)

type Block struct {
Number uint64 `json:"block_number"`
DataHash string `json:"data_hash"` // hex string
PreviousHash string `json:"previous_hash"` // hex string
DataHash string `json:"data_hash"`
PreviousHash string `json:"previous_hash"`

Transactions []*Transaction `json:"transactions"`
// only for endorser transaction blocks
Transactions []*Transaction `json:"transactions,omitempty"`
// only for Config blocks
Config *ConfigRecord `json:"config,omitempty"`
}

type Creator struct {
Expand Down Expand Up @@ -51,3 +57,12 @@ type TransactionAction struct {
ProposalHash string `json:"proposal_hash"` // hex string
Event *ChaincodeEvent `json:"event"`
}

type ConfigRecord struct {
Type string `json:"type"`
Signature string `json:"signature"`
Timestamp int64 `json:"timestamp"` // unix nano
Nonce string `json:"nonce"` // hex string
Creator *Creator `json:"creator"`
Config *common.Config `json:"config"`
}
86 changes: 64 additions & 22 deletions internal/fabric/utils/blockdecoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,10 @@ func DecodeBlock(block *common.Block) (*RawBlock, *Block, error) {
blockdata.Data = dataEnvs

bloc := &Block{}
transactions := make([]*Transaction, len(block.Data.Data))
bloc.Transactions = transactions

// this array in the block header's metadata contains each transaction's status code
txFilter := []byte(block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER])
var transactions []*Transaction
for i, data := range block.Data.Data {
env, err := getEnvelopeFromBlock(data)
if err != nil {
Expand All @@ -83,13 +82,24 @@ func DecodeBlock(block *common.Block) (*RawBlock, *Block, error) {
return nil, nil, errors.New("nil envelope")
}

data, tx, err := rawblock.DecodeBlockDataEnvelope(env)
data, payload, err := rawblock.DecodeBlockDataEnvelope(env)
if err != nil {
return nil, nil, errors.Wrap(err, "error decoding block data envelope")
}
dataEnvs[i] = data
transactions[i] = tx
tx.Status = peer.TxValidationCode(txFilter[i]).String()
tx, ok := payload.(*Transaction)
if ok {
if transactions == nil {
transactions = make([]*Transaction, len(block.Data.Data))
bloc.Transactions = transactions
}
transactions[i] = tx
tx.Status = peer.TxValidationCode(txFilter[i]).String()
} else {
// there should only be one config record in the block data
config := payload.(*ConfigRecord)
bloc.Config = config
}
}

bloc.Number = rawblock.Header.Number
Expand All @@ -99,15 +109,11 @@ func DecodeBlock(block *common.Block) (*RawBlock, *Block, error) {
return rawblock, bloc, nil
}

func (block *RawBlock) DecodeBlockDataEnvelope(env *common.Envelope) (*BlockDataEnvelope, *Transaction, error) {
func (block *RawBlock) DecodeBlockDataEnvelope(env *common.Envelope) (*BlockDataEnvelope, interface{}, error) {
// used for the raw block
dataEnv := &BlockDataEnvelope{}
dataEnv.Signature = base64.StdEncoding.EncodeToString(env.Signature)

// used in the user-friendly block
transaction := &Transaction{}
transaction.Signature = dataEnv.Signature

_payload := &Payload{}
dataEnv.Payload = _payload

Expand All @@ -116,47 +122,72 @@ func (block *RawBlock) DecodeBlockDataEnvelope(env *common.Envelope) (*BlockData
return nil, nil, errors.Wrap(err, "error decoding Payload from envelope")
}

err = block.decodePayload(payload, _payload, transaction)
result, err := block.decodePayload(payload, _payload)
if err != nil {
return nil, nil, err
}
return dataEnv, transaction, nil
tx, ok := result.(*Transaction)
if ok {
tx.Signature = dataEnv.Signature
return dataEnv, tx, nil
} else {
config := result.(*ConfigRecord)
config.Signature = dataEnv.Signature
return dataEnv, config, nil
}
}

func (block *RawBlock) decodePayload(payload *common.Payload, _payload *Payload, _transaction *Transaction) error {
// based on the channel header type, returns a *Transaction (type=1) or a *ConfigRecord (type=3)
func (block *RawBlock) decodePayload(payload *common.Payload, _payload *Payload) (interface{}, error) {
_payloadData := &PayloadData{}
_payload.Data = _payloadData
_payloadHeader := &PayloadHeader{}
_payload.Header = _payloadHeader

if err := block.decodePayloadHeader(payload.Header, _payloadHeader); err != nil {
return err
return nil, err
}

timestamp := _payloadHeader.ChannelHeader.Timestamp
_transaction.Type = _payloadHeader.ChannelHeader.Type
_transaction.Timestamp = timestamp
creator := _payloadHeader.SignatureHeader.Creator
_transaction.Creator = &Creator{
_creator := &Creator{
MspID: creator.Mspid,
Cert: string(creator.IdBytes),
}
_transaction.Nonce = hex.EncodeToString([]byte(_payloadHeader.SignatureHeader.Nonce))
_transaction.TxId = _payloadHeader.ChannelHeader.TxId

// TODO: support other block types (1=ConfigEvelope, 2=ConfigUpdateEnvelope)
if _payloadHeader.ChannelHeader.Type == common.HeaderType_name[3] {
// used in the user-friendly block
_transaction := &Transaction{}
_transaction.Type = _payloadHeader.ChannelHeader.Type
_transaction.Timestamp = timestamp
_transaction.Creator = _creator
_transaction.Nonce = hex.EncodeToString([]byte(_payloadHeader.SignatureHeader.Nonce))
_transaction.TxId = _payloadHeader.ChannelHeader.TxId

if err := block.decodeTxPayloadData(payload.Data, _payloadData, _transaction); err != nil {
return err
return nil, err
}
for _, action := range _payloadData.Actions {
if action.Payload.Action.ProposalResponsePayload.Extension.Events != nil {
action.Payload.Action.ProposalResponsePayload.Extension.Events.Timestamp = strconv.FormatInt(timestamp, 10)
}
}
return _transaction, nil
} else if _payloadHeader.ChannelHeader.Type == common.HeaderType_name[1] {
_configRec := &ConfigRecord{}
_configRec.Type = _payloadHeader.ChannelHeader.Type
_configRec.Timestamp = timestamp
_configRec.Creator = _creator
_configRec.Nonce = hex.EncodeToString([]byte(_payloadHeader.SignatureHeader.Nonce))

if err := block.decodeConfigPayloadData(payload.Data, _payloadData, _configRec); err != nil {
return nil, err
}

return _configRec, nil
}

return nil
return nil, nil
}

func (block *RawBlock) decodePayloadHeader(header *common.Header, _header *PayloadHeader) error {
Expand Down Expand Up @@ -357,6 +388,17 @@ func (block *RawBlock) decodeProposalResponsePayloadExtension(_extension *Extens
return nil
}

func (block *RawBlock) decodeConfigPayloadData(payloadData []byte, _payloadData *PayloadData, _configRec *ConfigRecord) error {
configEnv := &common.ConfigEnvelope{}
if err := proto.Unmarshal(payloadData, configEnv); err != nil {
return errors.Wrap(err, "error decoding config envelope")
}
_configRec.Config = configEnv.Config
_payloadData.Config = configEnv.Config

return nil
}

func getEnvelopeFromBlock(data []byte) (*common.Envelope, error) {
// Block always begins with an envelope
var err error
Expand Down
24 changes: 21 additions & 3 deletions internal/fabric/utils/blockdecoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (

func TestDecodeEndorserBlockWithEvents(t *testing.T) {
assert := assert.New(t)
content, _ := os.ReadFile("../../../test/resources/block-2.bin")
content, _ := os.ReadFile("../../../test/resources/tx-event.block")
testblock := &common.Block{}
_ = proto.Unmarshal(content, testblock)
decoded, _, err := DecodeBlock(testblock)
Expand Down Expand Up @@ -62,7 +62,7 @@ func TestDecodeEndorserBlockWithEvents(t *testing.T) {

func TestDecodeEndorserBlockLifecycleTxs(t *testing.T) {
assert := assert.New(t)
content, _ := os.ReadFile("../../../test/resources/block-1.bin")
content, _ := os.ReadFile("../../../test/resources/chaincode-deploy.block")
testblock := &common.Block{}
_ = proto.Unmarshal(content, testblock)
decoded, _, err := DecodeBlock(testblock)
Expand All @@ -86,9 +86,27 @@ func TestDecodeEndorserBlockLifecycleTxs(t *testing.T) {
assert.Equal("ApproveChaincodeDefinitionForMyOrg", cpp.Input.ChaincodeSpec.Input.Args[0])
}

func TestDecodeConfigBlock(t *testing.T) {
assert := assert.New(t)

content, _ := os.ReadFile("../../../test/resources/config-0.block")
testblock := &common.Block{}
_ = proto.Unmarshal(content, testblock)
decoded, _, err := DecodeBlock(testblock)
assert.NoError(err)
assert.Equal(1, len(decoded.Data.Data))

content, _ = os.ReadFile("../../../test/resources/config-1.block")
testblock = &common.Block{}
_ = proto.Unmarshal(content, testblock)
decoded, _, err = DecodeBlock(testblock)
assert.NoError(err)
assert.Equal(1, len(decoded.Data.Data))
}

func TestGetEvents(t *testing.T) {
assert := assert.New(t)
content, _ := os.ReadFile("../../../test/resources/block-2.bin")
content, _ := os.ReadFile("../../../test/resources/tx-event.block")
testblock := &common.Block{}
_ = proto.Unmarshal(content, testblock)
events := GetEvents(testblock)
Expand Down
47 changes: 27 additions & 20 deletions internal/fabric/utils/rawblock.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,36 @@ type Payload struct {
Header *PayloadHeader `json:"header"`
}

type PayloadHeader struct {
ChannelHeader *ChannelHeader `json:"channel_header"`
SignatureHeader *SignatureHeader `json:"signature_header"`
}

type ChannelHeader struct {
ChannelId string `json:"channel_id"`
Epoch string `json:"epoch"`
Timestamp int64 `json:"timestamp"`
TxId string `json:"tx_id"`
Type string `json:"type"`
Version int `json:"version"`
}

type SignatureHeader struct {
Creator *msp.SerializedIdentity `json:"creator"`
Nonce string `json:"nonce"`
}

type PayloadData struct {
Actions []*Action `json:"actions"`
// Actions only exists in endorsement transaction blocks
Actions []*Action `json:"actions,omitempty"`
// Config only exists in config and config update blocks
Config *common.Config `json:"config,omitempty"`
}

//
// Types used only in endorsement transaction blocks
//

type Action struct {
Header *SignatureHeader `json:"header"`
Payload *ActionPayload `json:"payload"`
Expand Down Expand Up @@ -100,22 +126,3 @@ type ChaincodeSpecInput struct {
Args []string `json:"args"`
IsInit bool `json:"is_init"`
}

type PayloadHeader struct {
ChannelHeader *ChannelHeader `json:"channel_header"`
SignatureHeader *SignatureHeader `json:"signature_header"`
}

type ChannelHeader struct {
ChannelId string `json:"channel_id"`
Epoch string `json:"epoch"`
Timestamp int64 `json:"timestamp"`
TxId string `json:"tx_id"`
Type string `json:"type"`
Version int `json:"version"`
}

type SignatureHeader struct {
Creator *msp.SerializedIdentity `json:"creator"`
Nonce string `json:"nonce"`
}
File renamed without changes.
File renamed without changes.
Binary file added test/resources/config-0.block
Binary file not shown.
Loading

0 comments on commit 1452fdd

Please sign in to comment.