Skip to content

Commit

Permalink
init commit
Browse files Browse the repository at this point in the history
Signed-off-by: David Liu <david-khala@hotmail.com>
  • Loading branch information
davidkhala committed Jan 17, 2024
1 parent 62f5192 commit a2ea789
Show file tree
Hide file tree
Showing 21 changed files with 4,509 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ fabric-lib-go is a home for go code that is common across fabric repositories.

It contains:
- health check logic that is used by fabric and fabric-ca
- protobuf utilities for fabric core or API developer
7 changes: 7 additions & 0 deletions protoutil/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# protoutil

This originates as a copy of github.com/hyperledger/fabric/protoutil

## Prepare
to use `counterfeiter`
- install by running `go install github.com/maxbrunsfeld/counterfeiter/v6@latest` outside of go module
332 changes: 332 additions & 0 deletions protoutil/blockutils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,332 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package protoutil

import (
"bytes"
"crypto/sha256"
"encoding/asn1"
"encoding/base64"
"fmt"
"github.com/hyperledger/fabric-lib-go/protoutil/common/util"
"math/big"

"github.com/hyperledger/fabric-protos-go-apiv2/common"
"github.com/hyperledger/fabric-protos-go-apiv2/msp"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
)

// NewBlock constructs a block with no data and no metadata.
func NewBlock(seqNum uint64, previousHash []byte) *common.Block {
block := &common.Block{}
block.Header = &common.BlockHeader{}
block.Header.Number = seqNum
block.Header.PreviousHash = previousHash
block.Header.DataHash = []byte{}
block.Data = &common.BlockData{}

var metadataContents [][]byte
for i := 0; i < len(common.BlockMetadataIndex_name); i++ {
metadataContents = append(metadataContents, []byte{})
}
block.Metadata = &common.BlockMetadata{Metadata: metadataContents}

return block
}

type asn1Header struct {
Number *big.Int
PreviousHash []byte
DataHash []byte
}

func BlockHeaderBytes(b *common.BlockHeader) []byte {
asn1Header := asn1Header{
PreviousHash: b.PreviousHash,
DataHash: b.DataHash,
Number: new(big.Int).SetUint64(b.Number),
}
result, err := asn1.Marshal(asn1Header)
if err != nil {
// Errors should only arise for types which cannot be encoded, since the
// BlockHeader type is known a-priori to contain only encodable types, an
// error here is fatal and should not be propagated
panic(err)
}
return result
}

func BlockHeaderHash(b *common.BlockHeader) []byte {
sum := sha256.Sum256(BlockHeaderBytes(b))
return sum[:]
}

func BlockDataHash(b *common.BlockData) ([]byte, error) {
if err := VerifyTransactionsAreWellFormed(b); err != nil {
return nil, err
}
return ComputeBlockDataHash(b), nil
}

func ComputeBlockDataHash(b *common.BlockData) []byte {
sum := sha256.Sum256(bytes.Join(b.Data, nil))
return sum[:]
}

// GetChannelIDFromBlockBytes returns channel ID given byte array which represents
// the block
func GetChannelIDFromBlockBytes(bytes []byte) (string, error) {
block, err := UnmarshalBlock(bytes)
if err != nil {
return "", err
}

return GetChannelIDFromBlock(block)
}

// GetChannelIDFromBlock returns channel ID in the block
func GetChannelIDFromBlock(block *common.Block) (string, error) {
if block == nil || block.Data == nil || block.Data.Data == nil || len(block.Data.Data) == 0 {
return "", errors.New("failed to retrieve channel id - block is empty")
}
var err error
envelope, err := GetEnvelopeFromBlock(block.Data.Data[0])
if err != nil {
return "", err
}
payload, err := UnmarshalPayload(envelope.Payload)
if err != nil {
return "", err
}

if payload.Header == nil {
return "", errors.New("failed to retrieve channel id - payload header is empty")
}
chdr, err := UnmarshalChannelHeader(payload.Header.ChannelHeader)
if err != nil {
return "", err
}

return chdr.ChannelId, nil
}

// GetMetadataFromBlock retrieves metadata at the specified index.
func GetMetadataFromBlock(block *common.Block, index common.BlockMetadataIndex) (*common.Metadata, error) {
if block.Metadata == nil {
return nil, errors.New("no metadata in block")
}

if len(block.Metadata.Metadata) <= int(index) {
return nil, errors.Errorf("no metadata at index [%s]", index)
}

md := &common.Metadata{}
err := proto.Unmarshal(block.Metadata.Metadata[index], md)
if err != nil {
return nil, errors.Wrapf(err, "error unmarshalling metadata at index [%s]", index)
}
return md, nil
}

// GetMetadataFromBlockOrPanic retrieves metadata at the specified index, or
// panics on error
func GetMetadataFromBlockOrPanic(block *common.Block, index common.BlockMetadataIndex) *common.Metadata {
md, err := GetMetadataFromBlock(block, index)
if err != nil {
panic(err)
}
return md
}

// GetConsenterMetadataFromBlock attempts to retrieve consenter metadata from the value
// stored in block metadata at index SIGNATURES (first field). If no consenter metadata
// is found there, it falls back to index ORDERER (third field).
func GetConsenterMetadataFromBlock(block *common.Block) (*common.Metadata, error) {
m, err := GetMetadataFromBlock(block, common.BlockMetadataIndex_SIGNATURES)
if err != nil {
return nil, errors.WithMessage(err, "failed to retrieve metadata")
}

obm := &common.OrdererBlockMetadata{}
err = proto.Unmarshal(m.Value, obm)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal orderer block metadata")
}

res := &common.Metadata{}
err = proto.Unmarshal(obm.ConsenterMetadata, res)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal consenter metadata")
}

return res, nil
}

// GetLastConfigIndexFromBlock retrieves the index of the last config block as
// encoded in the block metadata
func GetLastConfigIndexFromBlock(block *common.Block) (uint64, error) {
m, err := GetMetadataFromBlock(block, common.BlockMetadataIndex_SIGNATURES)
if err != nil {
return 0, errors.WithMessage(err, "failed to retrieve metadata")
}

obm := &common.OrdererBlockMetadata{}
err = proto.Unmarshal(m.Value, obm)
if err != nil {
return 0, errors.Wrap(err, "failed to unmarshal orderer block metadata")
}
return obm.LastConfig.Index, nil
}

// GetLastConfigIndexFromBlockOrPanic retrieves the index of the last config
// block as encoded in the block metadata, or panics on error
func GetLastConfigIndexFromBlockOrPanic(block *common.Block) uint64 {
index, err := GetLastConfigIndexFromBlock(block)
if err != nil {
panic(err)
}
return index
}

// CopyBlockMetadata copies metadata from one block into another
func CopyBlockMetadata(src *common.Block, dst *common.Block) {
dst.Metadata = src.Metadata
// Once copied initialize with rest of the
// required metadata positions.
InitBlockMetadata(dst)
}

// InitBlockMetadata initializes metadata structure
func InitBlockMetadata(block *common.Block) {
if block.Metadata == nil {
block.Metadata = &common.BlockMetadata{Metadata: [][]byte{{}, {}, {}, {}, {}}}
} else if len(block.Metadata.Metadata) < int(common.BlockMetadataIndex_COMMIT_HASH+1) {
for i := len(block.Metadata.Metadata); i <= int(common.BlockMetadataIndex_COMMIT_HASH); i++ {
block.Metadata.Metadata = append(block.Metadata.Metadata, []byte{})
}
}
}

type VerifierBuilder func(block *common.Block) BlockVerifierFunc

type BlockVerifierFunc func(header *common.BlockHeader, metadata *common.BlockMetadata) error

//go:generate counterfeiter -o mocks/policy.go --fake-name Policy . policy
type policy interface { // copied from common.policies to avoid circular import.
// EvaluateSignedData takes a set of SignedData and evaluates whether
// 1) the signatures are valid over the related message
// 2) the signing identities satisfy the policy
EvaluateSignedData(signatureSet []*SignedData) error
}

func BlockSignatureVerifier(bftEnabled bool, consenters []*common.Consenter, policy policy) BlockVerifierFunc {
return func(header *common.BlockHeader, metadata *common.BlockMetadata) error {
if len(metadata.GetMetadata()) < int(common.BlockMetadataIndex_SIGNATURES)+1 {
return errors.Errorf("no signatures in block metadata")
}

md := &common.Metadata{}
if err := proto.Unmarshal(metadata.Metadata[common.BlockMetadataIndex_SIGNATURES], md); err != nil {
return errors.Wrapf(err, "error unmarshalling signatures from metadata: %v", err)
}

var signatureSet []*SignedData
for _, metadataSignature := range md.Signatures {
var signerIdentity []byte
var signedPayload []byte
// if the SignatureHeader is empty and the IdentifierHeader is present, then the consenter expects us to fetch its identity by its numeric identifier
if bftEnabled && len(metadataSignature.GetSignatureHeader()) == 0 && len(metadataSignature.GetIdentifierHeader()) > 0 {
identifierHeader, err := UnmarshalIdentifierHeader(metadataSignature.IdentifierHeader)
if err != nil {
return fmt.Errorf("failed unmarshalling identifier header for block %d: %v", header.GetNumber(), err)
}
identifier := identifierHeader.GetIdentifier()
signerIdentity = searchConsenterIdentityByID(consenters, identifier)
if len(signerIdentity) == 0 {
// The identifier is not within the consenter set
continue
}
signedPayload = util.ConcatenateBytes(md.Value, metadataSignature.IdentifierHeader, BlockHeaderBytes(header))
} else {
signatureHeader, err := UnmarshalSignatureHeader(metadataSignature.GetSignatureHeader())
if err != nil {
return fmt.Errorf("failed unmarshalling signature header for block %d: %v", header.GetNumber(), err)
}

signedPayload = util.ConcatenateBytes(md.Value, metadataSignature.SignatureHeader, BlockHeaderBytes(header))

signerIdentity = signatureHeader.Creator
}

signatureSet = append(
signatureSet,
&SignedData{
Identity: signerIdentity,
Data: signedPayload,
Signature: metadataSignature.Signature,
},
)
}

return policy.EvaluateSignedData(signatureSet)
}
}

func searchConsenterIdentityByID(consenters []*common.Consenter, identifier uint32) []byte {
for _, consenter := range consenters {
if consenter.Id == identifier {
return MarshalOrPanic(&msp.SerializedIdentity{
Mspid: consenter.MspId,
IdBytes: consenter.Identity,
})
}
}
return nil
}

func VerifyTransactionsAreWellFormed(bd *common.BlockData) error {
if bd == nil || bd.Data == nil || len(bd.Data) == 0 {
return fmt.Errorf("empty block")
}

// If we have a single transaction, and the block is a config block, then no need to check
// well formed-ness, because there cannot be another transaction in the original block.
if HasConfigTx(bd) {
return nil
}

for i, rawTx := range bd.Data {
env := &common.Envelope{}
if err := proto.Unmarshal(rawTx, env); err != nil {
return fmt.Errorf("transaction %d is invalid: %v", i, err)
}

if len(env.Payload) == 0 {
return fmt.Errorf("transaction %d has no payload", i)
}

if len(env.Signature) == 0 {
return fmt.Errorf("transaction %d has no signature", i)
}

expected, err := proto.Marshal(env)
if err != nil {
return fmt.Errorf("failed re-marshaling envelope: %v", err)
}

if len(expected) < len(rawTx) {
return fmt.Errorf("transaction %d has %d trailing bytes", i, len(rawTx)-len(expected))
}
if !bytes.Equal(expected, rawTx) {
return fmt.Errorf("transaction %d (%s) does not match its raw form (%s)", i,
base64.StdEncoding.EncodeToString(expected), base64.StdEncoding.EncodeToString(rawTx))
}
}

return nil
}
Loading

0 comments on commit a2ea789

Please sign in to comment.