-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
62f5192
commit 2d692e0
Showing
21 changed files
with
4,509 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.