go_study/fabric-main/orderer/consensus/smartbft/verifier.go

448 lines
15 KiB
Go

/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package smartbft
import (
"bytes"
"encoding/base64"
"encoding/hex"
"fmt"
"sync"
"sync/atomic"
"github.com/SmartBFT-Go/consensus/pkg/types"
"github.com/SmartBFT-Go/consensus/smartbftprotos"
"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/flogging"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/protoutil"
"github.com/pkg/errors"
"go.uber.org/zap/zapcore"
)
//go:generate mockery -dir . -name Sequencer -case underscore -output mocks
// Sequencer returns sequences
type Sequencer interface {
Sequence() uint64
}
//go:generate mockery -dir . -name ConsenterVerifier -case underscore -output mocks
// ConsenterVerifier is used to determine whether a signature from one of the consenters is valid
type ConsenterVerifier interface {
// Evaluate takes a set of SignedData and evaluates whether this set of signatures satisfies the policy
Evaluate(signatureSet []*protoutil.SignedData) error
}
//go:generate mockery -dir . -name AccessController -case underscore -output mocks
// AccessController is used to determine if a signature of a certain client is valid
type AccessController interface {
// Evaluate takes a set of SignedData and evaluates whether this set of signatures satisfies the policy
Evaluate(signatureSet []*protoutil.SignedData) error
}
type requestVerifier func(req []byte, isolated bool) (types.RequestInfo, error)
// NodeIdentitiesByID stores Identities by id
type NodeIdentitiesByID map[uint64][]byte
// IdentityToID looks up the Identity in NodeIdentitiesByID and returns id and flag true if found
func (nibd NodeIdentitiesByID) IdentityToID(identity []byte) (uint64, bool) {
sID := &msp.SerializedIdentity{}
if err := proto.Unmarshal(identity, sID); err != nil {
return 0, false
}
for id, currIdentity := range nibd {
currentID := &msp.SerializedIdentity{}
if err := proto.Unmarshal(currIdentity, currentID); err != nil {
return 0, false
}
if proto.Equal(currentID, sID) {
return id, true
}
}
return 0, false
}
// Verifier verifies proposals and signatures
type Verifier struct {
RuntimeConfig *atomic.Value
ReqInspector *RequestInspector
ConsenterVerifier ConsenterVerifier
AccessController AccessController
VerificationSequencer Sequencer
Ledger Ledger
Logger *flogging.FabricLogger
ConfigValidator ConfigValidator
}
// AuxiliaryData unmarshals and returns auxiliary data from signature
func (v *Verifier) AuxiliaryData(msg []byte) []byte {
sig := &Signature{}
if err := sig.Unmarshal(msg); err != nil {
v.Logger.Warnf("Failed unmarshalling signature message %s: %v", hex.EncodeToString(msg), err)
}
return nil
}
// VerifyProposal verifies proposal and returns []RequestInfo
func (v *Verifier) VerifyProposal(proposal types.Proposal) ([]types.RequestInfo, error) {
block, err := ProposalToBlock(proposal)
if err != nil {
return nil, err
}
rtc := v.RuntimeConfig.Load().(RuntimeConfig)
if err := verifyHashChain(block, rtc.LastCommittedBlockHash); err != nil {
return nil, err
}
requests, err := v.verifyBlockDataAndMetadata(block, proposal.Metadata)
if err != nil {
return nil, err
}
verificationSeq := v.VerificationSequence()
if verificationSeq != uint64(proposal.VerificationSequence) {
return nil, errors.Errorf("expected verification sequence %d, but proposal has %d", verificationSeq, proposal.VerificationSequence)
}
return requests, nil
}
// RequestsFromProposal converts proposal to []RequestInfo
func (v *Verifier) RequestsFromProposal(proposal types.Proposal) []types.RequestInfo {
block, err := ProposalToBlock(proposal)
if err != nil {
return []types.RequestInfo{}
}
if block.Data == nil {
return []types.RequestInfo{}
}
var res []types.RequestInfo
for _, txn := range block.Data.Data {
req := v.ReqInspector.RequestID(txn)
res = append(res, req)
}
return res
}
// VerifySignature verifies signature
func (v *Verifier) VerifySignature(signature types.Signature) error {
id2Identity := v.RuntimeConfig.Load().(RuntimeConfig).ID2Identities
identity, exists := id2Identity[signature.ID]
if !exists {
return errors.Errorf("node with id of %d doesn't exist", signature.ID)
}
return v.ConsenterVerifier.Evaluate([]*protoutil.SignedData{
{Identity: identity, Data: signature.Msg, Signature: signature.Value},
})
}
// VerifyRequest verifies raw request
func (v *Verifier) VerifyRequest(rawRequest []byte) (types.RequestInfo, error) {
return v.verifyRequest(rawRequest, false)
}
func (v *Verifier) verifyRequest(rawRequest []byte, noConfigAllowed bool) (types.RequestInfo, error) {
req, err := v.ReqInspector.unwrapReq(rawRequest)
if err != nil {
return types.RequestInfo{}, err
}
err = v.AccessController.Evaluate([]*protoutil.SignedData{
{Identity: req.sigHdr.Creator, Data: req.envelope.Payload, Signature: req.envelope.Signature},
})
if err != nil {
return types.RequestInfo{}, errors.Wrap(err, "access denied")
}
if noConfigAllowed && req.chHdr.Type != int32(cb.HeaderType_ENDORSER_TRANSACTION) {
return types.RequestInfo{}, errors.Errorf("only endorser transactions can be sent with other transactions")
}
switch req.chHdr.Type {
case int32(cb.HeaderType_CONFIG):
case int32(cb.HeaderType_ORDERER_TRANSACTION):
return types.RequestInfo{}, fmt.Errorf("orderer transactions are not supported in v3")
case int32(cb.HeaderType_ENDORSER_TRANSACTION):
default:
return types.RequestInfo{}, errors.Errorf("transaction of type %s is not allowed to be included in blocks", cb.HeaderType_name[req.chHdr.Type])
}
if req.chHdr.Type == int32(cb.HeaderType_CONFIG) {
err := v.ConfigValidator.ValidateConfig(req.envelope)
if err != nil {
v.Logger.Errorf("Error verifying config update: %v", err)
return types.RequestInfo{}, err
}
}
return v.ReqInspector.requestIDFromSigHeader(req.sigHdr)
}
// VerifyConsenterSig verifies consenter signature
func (v *Verifier) VerifyConsenterSig(signature types.Signature, prop types.Proposal) ([]byte, error) {
id2Identity := v.RuntimeConfig.Load().(RuntimeConfig).ID2Identities
identity, exists := id2Identity[signature.ID]
if !exists {
return nil, errors.Errorf("node with id of %d doesn't exist", signature.ID)
}
sig := &Signature{}
if err := sig.Unmarshal(signature.Msg); err != nil {
v.Logger.Errorf("Failed unmarshaling signature from %d: %v", signature.ID, err)
v.Logger.Errorf("Offending signature Msg: %s", base64.StdEncoding.EncodeToString(signature.Msg))
v.Logger.Errorf("Offending signature Value: %s", base64.StdEncoding.EncodeToString(signature.Value))
return nil, errors.Wrap(err, "malformed signature format")
}
if err := v.verifySignatureIsBoundToProposal(sig, signature.ID, prop); err != nil {
return nil, err
}
expectedMsgToBeSigned := util.ConcatenateBytes(sig.OrdererBlockMetadata, sig.IdentifierHeader, sig.BlockHeader, nil)
signedData := &protoutil.SignedData{
Signature: signature.Value,
Data: expectedMsgToBeSigned,
Identity: identity,
}
return nil, v.ConsenterVerifier.Evaluate([]*protoutil.SignedData{signedData})
}
// VerificationSequence returns verification sequence
func (v *Verifier) VerificationSequence() uint64 {
return v.VerificationSequencer.Sequence()
}
func verifyHashChain(block *cb.Block, prevHeaderHash string) error {
thisHdrHashOfPrevHdr := hex.EncodeToString(block.Header.PreviousHash)
if prevHeaderHash != thisHdrHashOfPrevHdr {
return errors.Errorf("previous header hash is %s but expected %s", thisHdrHashOfPrevHdr, prevHeaderHash)
}
dataHash := hex.EncodeToString(block.Header.DataHash)
actualHashOfData := hex.EncodeToString(protoutil.BlockDataHash(block.Data))
if dataHash != actualHashOfData {
return errors.Errorf("data hash is %s but expected %s", dataHash, actualHashOfData)
}
return nil
}
func (v *Verifier) verifyBlockDataAndMetadata(block *cb.Block, metadata []byte) ([]types.RequestInfo, error) {
if block.Data == nil || len(block.Data.Data) == 0 {
return nil, errors.New("empty block data")
}
if block.Metadata == nil || len(block.Metadata.Metadata) < len(cb.BlockMetadataIndex_name) {
return nil, errors.New("block metadata is either missing or contains too few entries")
}
signatureMetadata, err := protoutil.GetMetadataFromBlock(block, cb.BlockMetadataIndex_SIGNATURES)
if err != nil {
return nil, err
}
ordererMetadataFromSignature := &cb.OrdererBlockMetadata{}
if err := proto.Unmarshal(signatureMetadata.Value, ordererMetadataFromSignature); err != nil {
return nil, errors.Wrap(err, "failed unmarshaling OrdererBlockMetadata")
}
// Ensure the view metadata in the block signature and in the proposal are the same
metadataInBlock := &smartbftprotos.ViewMetadata{}
if err := proto.Unmarshal(ordererMetadataFromSignature.ConsenterMetadata, metadataInBlock); err != nil {
return nil, errors.Wrap(err, "failed unmarshaling smartbft metadata from block")
}
metadataFromProposal := &smartbftprotos.ViewMetadata{}
if err := proto.Unmarshal(metadata, metadataFromProposal); err != nil {
return nil, errors.Wrap(err, "failed unmarshaling smartbft metadata from proposal")
}
if !proto.Equal(metadataInBlock, metadataFromProposal) {
return nil, errors.Errorf("expected metadata in block to be [view_id:%d latest_sequence:%d] but got [view_id:%d latest_sequence:%d]",
metadataFromProposal.GetViewId(), metadataFromProposal.GetLatestSequence(),
metadataInBlock.GetViewId(), metadataInBlock.GetLatestSequence(),
)
}
rtc := v.RuntimeConfig.Load().(RuntimeConfig)
lastConfig := rtc.LastConfigBlock.Header.Number
if protoutil.IsConfigBlock(block) {
lastConfig = block.Header.Number
}
// Verify last config
if ordererMetadataFromSignature.LastConfig == nil {
return nil, errors.Errorf("last config is nil")
}
if ordererMetadataFromSignature.LastConfig.Index != lastConfig {
return nil, errors.Errorf("last config in block orderer metadata points to %d but our persisted last config is %d", ordererMetadataFromSignature.LastConfig.Index, lastConfig)
}
rawLastConfig, err := protoutil.GetMetadataFromBlock(block, cb.BlockMetadataIndex_LAST_CONFIG)
if err != nil {
return nil, err
}
lastConf := &cb.LastConfig{}
if err := proto.Unmarshal(rawLastConfig.Value, lastConf); err != nil {
return nil, err
}
if lastConf.Index != lastConfig {
return nil, errors.Errorf("last config in block metadata points to %d but our persisted last config is %d", ordererMetadataFromSignature.LastConfig.Index, lastConfig)
}
return validateTransactions(block.Data.Data, v.verifyRequest)
}
func validateTransactions(blockData [][]byte, verifyReq requestVerifier) ([]types.RequestInfo, error) {
var validationFinished sync.WaitGroup
validationFinished.Add(len(blockData))
type txnValidation struct {
indexInBlock int
extractedInfo types.RequestInfo
validationErr error
}
noConfigAllowed := len(blockData) > 1
validations := make(chan txnValidation, len(blockData))
for i, payload := range blockData {
go func(indexInBlock int, payload []byte) {
defer validationFinished.Done()
reqInfo, err := verifyReq(payload, noConfigAllowed)
validations <- txnValidation{
indexInBlock: indexInBlock,
extractedInfo: reqInfo,
validationErr: err,
}
}(i, payload)
}
validationFinished.Wait()
close(validations)
indexToRequestInfo := make(map[int]types.RequestInfo)
for validationResult := range validations {
indexToRequestInfo[validationResult.indexInBlock] = validationResult.extractedInfo
if validationResult.validationErr != nil {
return nil, validationResult.validationErr
}
}
var res []types.RequestInfo
for indexInBlock := range blockData {
res = append(res, indexToRequestInfo[indexInBlock])
}
return res, nil
}
func (v *Verifier) verifySignatureIsBoundToProposal(sig *Signature, identityID uint64, prop types.Proposal) error {
// We verify the following fields:
// ConsenterMetadata []byte
// SignatureHeader []byte
// BlockHeader []byte
// OrdererBlockMetadata []byte
// Ensure block header is equal
if !bytes.Equal(prop.Header, sig.BlockHeader) {
v.Logger.Errorf("Expected block header %s but got %s", base64.StdEncoding.EncodeToString(prop.Header),
base64.StdEncoding.EncodeToString(sig.BlockHeader))
return errors.Errorf("mismatched block header")
}
// Ensure signature header matches the identity
sigHdr := &cb.IdentifierHeader{}
if err := proto.Unmarshal(sig.IdentifierHeader, sigHdr); err != nil {
return errors.Wrap(err, "malformed signature header")
}
if identityID != uint64(sigHdr.Identifier) {
v.Logger.Warnf("Expected identity %d but got %d", identityID,
sigHdr.Identifier)
return errors.Errorf("identity in signature header does not match expected identity")
}
// Ensure orderer block metadata's consenter MD matches the proposal
ordererMD := &cb.OrdererBlockMetadata{}
if err := proto.Unmarshal(sig.OrdererBlockMetadata, ordererMD); err != nil {
return errors.Wrap(err, "malformed orderer metadata in signature")
}
if !bytes.Equal(ordererMD.ConsenterMetadata, prop.Metadata) {
v.Logger.Warnf("Expected consenter metadata %s but got %s in proposal",
base64.StdEncoding.EncodeToString(ordererMD.ConsenterMetadata), base64.StdEncoding.EncodeToString(prop.Metadata))
return errors.Errorf("consenter metadata in OrdererBlockMetadata doesn't match proposal")
}
block, err := ProposalToBlock(prop)
if err != nil {
v.Logger.Warnf("got malformed proposal: %v", err)
return err
}
// Ensure Metadata slice is of the right size
if len(block.Metadata.Metadata) != len(cb.BlockMetadataIndex_name) {
return errors.Errorf("block metadata is of size %d but should be of size %d",
len(block.Metadata.Metadata), len(cb.BlockMetadataIndex_name))
}
signatureMetadata := &cb.Metadata{}
if err := proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES], signatureMetadata); err != nil {
return errors.Wrap(err, "malformed signature metadata")
}
ordererMDFromBlock := &cb.OrdererBlockMetadata{}
if err := proto.Unmarshal(signatureMetadata.Value, ordererMDFromBlock); err != nil {
return errors.Wrap(err, "malformed orderer metadata in block")
}
// Ensure the block's OrdererBlockMetadata matches the signature.
if !proto.Equal(ordererMDFromBlock, ordererMD) {
return errors.Errorf("signature's OrdererBlockMetadata and OrdererBlockMetadata extracted from block do not match")
}
return nil
}
type consenterVerifier struct {
logger *flogging.FabricLogger
channel string
policyManager policies.Manager
}
// Evaluate evaluates signed data and returns no error if signature is valid and satisfies the policy
func (cv *consenterVerifier) Evaluate(signatureSet []*protoutil.SignedData) error {
policy, ok := cv.policyManager.GetPolicy(policies.ChannelOrdererWriters)
if !ok {
cv.logger.Errorf("[%s] Error: could not find policy %s in policy manager %v", cv.channel, policies.ChannelOrdererWriters, cv.policyManager)
return errors.Errorf("could not find policy %s", policies.ChannelOrdererWriters)
}
if cv.logger.IsEnabledFor(zapcore.DebugLevel) {
cv.logger.Debugf("== Evaluating %T Policy %s ==", policy, policies.ChannelOrdererWriters)
defer cv.logger.Debugf("== Done Evaluating %T Policy %s", policy, policies.ChannelOrdererWriters)
}
return policy.EvaluateSignedData(signatureSet)
}