288 lines
7.4 KiB
Go
288 lines
7.4 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package protoutil
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/golang/protobuf/ptypes/timestamp"
|
|
cb "github.com/hyperledger/fabric-protos-go/common"
|
|
"github.com/hyperledger/fabric/internal/pkg/identity"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// MarshalOrPanic serializes a protobuf message and panics if this
|
|
// operation fails
|
|
func MarshalOrPanic(pb proto.Message) []byte {
|
|
data, err := proto.Marshal(pb)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return data
|
|
}
|
|
|
|
// Marshal serializes a protobuf message.
|
|
func Marshal(pb proto.Message) ([]byte, error) {
|
|
return proto.Marshal(pb)
|
|
}
|
|
|
|
// CreateNonceOrPanic generates a nonce using the common/crypto package
|
|
// and panics if this operation fails.
|
|
func CreateNonceOrPanic() []byte {
|
|
nonce, err := CreateNonce()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return nonce
|
|
}
|
|
|
|
// CreateNonce generates a nonce using the common/crypto package.
|
|
func CreateNonce() ([]byte, error) {
|
|
nonce, err := getRandomNonce()
|
|
return nonce, errors.WithMessage(err, "error generating random nonce")
|
|
}
|
|
|
|
// UnmarshalEnvelopeOfType unmarshals an envelope of the specified type,
|
|
// including unmarshalling the payload data
|
|
func UnmarshalEnvelopeOfType(envelope *cb.Envelope, headerType cb.HeaderType, message proto.Message) (*cb.ChannelHeader, error) {
|
|
payload, err := UnmarshalPayload(envelope.Payload)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if payload.Header == nil {
|
|
return nil, errors.New("envelope must have a Header")
|
|
}
|
|
|
|
chdr, err := UnmarshalChannelHeader(payload.Header.ChannelHeader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if chdr.Type != int32(headerType) {
|
|
return nil, errors.Errorf("invalid type %s, expected %s", cb.HeaderType(chdr.Type), headerType)
|
|
}
|
|
|
|
err = proto.Unmarshal(payload.Data, message)
|
|
err = errors.Wrapf(err, "error unmarshalling message for type %s", headerType)
|
|
return chdr, err
|
|
}
|
|
|
|
// ExtractEnvelopeOrPanic retrieves the requested envelope from a given block
|
|
// and unmarshals it -- it panics if either of these operations fail
|
|
func ExtractEnvelopeOrPanic(block *cb.Block, index int) *cb.Envelope {
|
|
envelope, err := ExtractEnvelope(block, index)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return envelope
|
|
}
|
|
|
|
// ExtractEnvelope retrieves the requested envelope from a given block and
|
|
// unmarshals it
|
|
func ExtractEnvelope(block *cb.Block, index int) (*cb.Envelope, error) {
|
|
if block.Data == nil {
|
|
return nil, errors.New("block data is nil")
|
|
}
|
|
|
|
envelopeCount := len(block.Data.Data)
|
|
if index < 0 || index >= envelopeCount {
|
|
return nil, errors.New("envelope index out of bounds")
|
|
}
|
|
marshaledEnvelope := block.Data.Data[index]
|
|
envelope, err := GetEnvelopeFromBlock(marshaledEnvelope)
|
|
err = errors.WithMessagef(err, "block data does not carry an envelope at index %d", index)
|
|
return envelope, err
|
|
}
|
|
|
|
// MakeChannelHeader creates a ChannelHeader.
|
|
func MakeChannelHeader(headerType cb.HeaderType, version int32, chainID string, epoch uint64) *cb.ChannelHeader {
|
|
return &cb.ChannelHeader{
|
|
Type: int32(headerType),
|
|
Version: version,
|
|
Timestamp: ×tamp.Timestamp{
|
|
Seconds: time.Now().Unix(),
|
|
Nanos: 0,
|
|
},
|
|
ChannelId: chainID,
|
|
Epoch: epoch,
|
|
}
|
|
}
|
|
|
|
// MakeSignatureHeader creates a SignatureHeader.
|
|
func MakeSignatureHeader(serializedCreatorCertChain []byte, nonce []byte) *cb.SignatureHeader {
|
|
return &cb.SignatureHeader{
|
|
Creator: serializedCreatorCertChain,
|
|
Nonce: nonce,
|
|
}
|
|
}
|
|
|
|
// SetTxID generates a transaction id based on the provided signature header
|
|
// and sets the TxId field in the channel header
|
|
func SetTxID(channelHeader *cb.ChannelHeader, signatureHeader *cb.SignatureHeader) {
|
|
channelHeader.TxId = ComputeTxID(
|
|
signatureHeader.Nonce,
|
|
signatureHeader.Creator,
|
|
)
|
|
}
|
|
|
|
// MakePayloadHeader creates a Payload Header.
|
|
func MakePayloadHeader(ch *cb.ChannelHeader, sh *cb.SignatureHeader) *cb.Header {
|
|
return &cb.Header{
|
|
ChannelHeader: MarshalOrPanic(ch),
|
|
SignatureHeader: MarshalOrPanic(sh),
|
|
}
|
|
}
|
|
|
|
// NewSignatureHeader returns a SignatureHeader with a valid nonce.
|
|
func NewSignatureHeader(id identity.Serializer) (*cb.SignatureHeader, error) {
|
|
creator, err := id.Serialize()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
nonce, err := CreateNonce()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &cb.SignatureHeader{
|
|
Creator: creator,
|
|
Nonce: nonce,
|
|
}, nil
|
|
}
|
|
|
|
// NewSignatureHeaderOrPanic returns a signature header and panics on error.
|
|
func NewSignatureHeaderOrPanic(id identity.Serializer) *cb.SignatureHeader {
|
|
if id == nil {
|
|
panic(errors.New("invalid signer. cannot be nil"))
|
|
}
|
|
|
|
signatureHeader, err := NewSignatureHeader(id)
|
|
if err != nil {
|
|
panic(fmt.Errorf("failed generating a new SignatureHeader: %s", err))
|
|
}
|
|
|
|
return signatureHeader
|
|
}
|
|
|
|
// SignOrPanic signs a message and panics on error.
|
|
func SignOrPanic(signer identity.Signer, msg []byte) []byte {
|
|
if signer == nil {
|
|
panic(errors.New("invalid signer. cannot be nil"))
|
|
}
|
|
|
|
sigma, err := signer.Sign(msg)
|
|
if err != nil {
|
|
panic(fmt.Errorf("failed generating signature: %s", err))
|
|
}
|
|
return sigma
|
|
}
|
|
|
|
// IsConfigBlock validates whenever given block contains configuration
|
|
// update transaction
|
|
func IsConfigBlock(block *cb.Block) bool {
|
|
envelope, err := ExtractEnvelope(block, 0)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
payload, err := UnmarshalPayload(envelope.Payload)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
if payload.Header == nil {
|
|
return false
|
|
}
|
|
|
|
hdr, err := UnmarshalChannelHeader(payload.Header.ChannelHeader)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
return cb.HeaderType(hdr.Type) == cb.HeaderType_CONFIG
|
|
}
|
|
|
|
// ChannelHeader returns the *cb.ChannelHeader for a given *cb.Envelope.
|
|
func ChannelHeader(env *cb.Envelope) (*cb.ChannelHeader, error) {
|
|
if env == nil {
|
|
return nil, errors.New("Invalid envelope payload. can't be nil")
|
|
}
|
|
|
|
envPayload, err := UnmarshalPayload(env.Payload)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if envPayload.Header == nil {
|
|
return nil, errors.New("header not set")
|
|
}
|
|
|
|
if envPayload.Header.ChannelHeader == nil {
|
|
return nil, errors.New("channel header not set")
|
|
}
|
|
|
|
chdr, err := UnmarshalChannelHeader(envPayload.Header.ChannelHeader)
|
|
if err != nil {
|
|
return nil, errors.WithMessage(err, "error unmarshalling channel header")
|
|
}
|
|
|
|
return chdr, nil
|
|
}
|
|
|
|
// ChannelID returns the Channel ID for a given *cb.Envelope.
|
|
func ChannelID(env *cb.Envelope) (string, error) {
|
|
chdr, err := ChannelHeader(env)
|
|
if err != nil {
|
|
return "", errors.WithMessage(err, "error retrieving channel header")
|
|
}
|
|
|
|
return chdr.ChannelId, nil
|
|
}
|
|
|
|
// EnvelopeToConfigUpdate is used to extract a ConfigUpdateEnvelope from an envelope of
|
|
// type CONFIG_UPDATE
|
|
func EnvelopeToConfigUpdate(configtx *cb.Envelope) (*cb.ConfigUpdateEnvelope, error) {
|
|
configUpdateEnv := &cb.ConfigUpdateEnvelope{}
|
|
_, err := UnmarshalEnvelopeOfType(configtx, cb.HeaderType_CONFIG_UPDATE, configUpdateEnv)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return configUpdateEnv, nil
|
|
}
|
|
|
|
func getRandomNonce() ([]byte, error) {
|
|
key := make([]byte, 24)
|
|
|
|
_, err := rand.Read(key)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error getting random bytes")
|
|
}
|
|
return key, nil
|
|
}
|
|
|
|
func IsConfigTransaction(envelope *cb.Envelope) bool {
|
|
payload, err := UnmarshalPayload(envelope.Payload)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
if payload.Header == nil {
|
|
return false
|
|
}
|
|
|
|
hdr, err := UnmarshalChannelHeader(payload.Header.ChannelHeader)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
return cb.HeaderType(hdr.Type) == cb.HeaderType_CONFIG || cb.HeaderType(hdr.Type) == cb.HeaderType_ORDERER_TRANSACTION
|
|
}
|