/* Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package protoutil import ( "bytes" "crypto/sha256" "encoding/asn1" "fmt" "math/big" "github.com/golang/protobuf/proto" cb "github.com/hyperledger/fabric-protos-go/common" "github.com/hyperledger/fabric-protos-go/msp" "github.com/hyperledger/fabric/common/util" "github.com/pkg/errors" ) // NewBlock constructs a block with no data and no metadata. func NewBlock(seqNum uint64, previousHash []byte) *cb.Block { block := &cb.Block{} block.Header = &cb.BlockHeader{} block.Header.Number = seqNum block.Header.PreviousHash = previousHash block.Header.DataHash = []byte{} block.Data = &cb.BlockData{} var metadataContents [][]byte for i := 0; i < len(cb.BlockMetadataIndex_name); i++ { metadataContents = append(metadataContents, []byte{}) } block.Metadata = &cb.BlockMetadata{Metadata: metadataContents} return block } type asn1Header struct { Number *big.Int PreviousHash []byte DataHash []byte } func BlockHeaderBytes(b *cb.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 *cb.BlockHeader) []byte { sum := sha256.Sum256(BlockHeaderBytes(b)) return sum[:] } func BlockDataHash(b *cb.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 *cb.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 *cb.Block, index cb.BlockMetadataIndex) (*cb.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 := &cb.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 *cb.Block, index cb.BlockMetadataIndex) *cb.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 *cb.Block) (*cb.Metadata, error) { m, err := GetMetadataFromBlock(block, cb.BlockMetadataIndex_SIGNATURES) if err != nil { return nil, errors.WithMessage(err, "failed to retrieve metadata") } // TODO FAB-15864 Remove this fallback when we can stop supporting upgrade from pre-1.4.1 orderer if len(m.Value) == 0 { return GetMetadataFromBlock(block, cb.BlockMetadataIndex_ORDERER) } obm := &cb.OrdererBlockMetadata{} err = proto.Unmarshal(m.Value, obm) if err != nil { return nil, errors.Wrap(err, "failed to unmarshal orderer block metadata") } res := &cb.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 *cb.Block) (uint64, error) { m, err := GetMetadataFromBlock(block, cb.BlockMetadataIndex_SIGNATURES) if err != nil { return 0, errors.WithMessage(err, "failed to retrieve metadata") } // TODO FAB-15864 Remove this fallback when we can stop supporting upgrade from pre-1.4.1 orderer if len(m.Value) == 0 { m, err := GetMetadataFromBlock(block, cb.BlockMetadataIndex_LAST_CONFIG) if err != nil { return 0, errors.WithMessage(err, "failed to retrieve metadata") } lc := &cb.LastConfig{} err = proto.Unmarshal(m.Value, lc) if err != nil { return 0, errors.Wrap(err, "error unmarshalling LastConfig") } return lc.Index, nil } obm := &cb.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 *cb.Block) uint64 { index, err := GetLastConfigIndexFromBlock(block) if err != nil { panic(err) } return index } // CopyBlockMetadata copies metadata from one block into another func CopyBlockMetadata(src *cb.Block, dst *cb.Block) { dst.Metadata = src.Metadata // Once copied initialize with rest of the // required metadata positions. InitBlockMetadata(dst) } // InitBlockMetadata initializes metadata structure func InitBlockMetadata(block *cb.Block) { if block.Metadata == nil { block.Metadata = &cb.BlockMetadata{Metadata: [][]byte{{}, {}, {}, {}, {}}} } else if len(block.Metadata.Metadata) < int(cb.BlockMetadataIndex_COMMIT_HASH+1) { for i := int(len(block.Metadata.Metadata)); i <= int(cb.BlockMetadataIndex_COMMIT_HASH); i++ { block.Metadata.Metadata = append(block.Metadata.Metadata, []byte{}) } } } type VerifierBuilder func(block *cb.Block) BlockVerifierFunc type BlockVerifierFunc func(header *cb.BlockHeader, metadata *cb.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 []*cb.Consenter, policy policy) BlockVerifierFunc { return func(header *cb.BlockHeader, metadata *cb.BlockMetadata) error { if len(metadata.GetMetadata()) < int(cb.BlockMetadataIndex_SIGNATURES)+1 { return errors.Errorf("no signatures in block metadata") } md := &cb.Metadata{} if err := proto.Unmarshal(metadata.Metadata[cb.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 []*cb.Consenter, identifier uint32) []byte { for _, consenter := range consenters { if consenter.Id == identifier { return MarshalOrPanic(&msp.SerializedIdentity{ Mspid: consenter.MspId, IdBytes: consenter.Identity, }) } } return nil }