Skip to content

Commit

Permalink
BFT block verifier (hyperledger#3592)
Browse files Browse the repository at this point in the history
Implemented BFT block verifier in orderer and peer gossip

Signed-off-by: andrew-coleman <[email protected]>

Signed-off-by: andrew-coleman <[email protected]>
  • Loading branch information
andrew-coleman authored Aug 18, 2022
1 parent 6c61160 commit 7cd6b5b
Show file tree
Hide file tree
Showing 17 changed files with 3,541 additions and 23 deletions.
3 changes: 0 additions & 3 deletions common/channelconfig/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,6 @@ type Channel interface {
// OrdererAddresses returns the list of valid orderer addresses to connect to to invoke Broadcast/Deliver
OrdererAddresses() []string

// Orderers returns the list of consenters
Orderers() []*cb.Consenter

// Capabilities defines the capabilities for a channel
Capabilities() ChannelCapabilities
}
Expand Down
5 changes: 0 additions & 5 deletions common/channelconfig/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,6 @@ func (cc *ChannelConfig) OrdererAddresses() []string {
return cc.protos.OrdererAddresses.Addresses
}

// Orderers returns the list of consenters
func (cc *ChannelConfig) Orderers() []*cb.Consenter {
return cc.protos.Orderers.ConsenterMapping
}

// ConsortiumName returns the name of the consortium this channel was created under
func (cc *ChannelConfig) ConsortiumName() string {
return cc.protos.Consortium.Name
Expand Down
1 change: 1 addition & 0 deletions core/peer/peer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func NewTestPeer(t *testing.T) (*Peer, func()) {
signer,
deserManager,
cryptoProvider,
nil,
)
secAdv := peergossip.NewSecurityAdvisor(deserManager)
defaultSecureDialOpts := func() []grpc.DialOption { return []grpc.DialOption{grpc.WithInsecure()} }
Expand Down
1 change: 1 addition & 0 deletions core/scc/cscc/configure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@ func newPeerConfiger(t *testing.T, ledgerMgr *ledgermgmt.LedgerMgr, grpcServer *
signer,
deserManager,
cryptoProvider,
nil,
)
secAdv := peergossip.NewSecurityAdvisor(deserManager)
defaultSecureDialOpts := func() []grpc.DialOption {
Expand Down
1 change: 1 addition & 0 deletions gossip/service/gossip_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ func TestInitGossipService(t *testing.T) {
signer,
deserManager,
cryptoProvider,
nil,
)
secAdv := peergossip.NewSecurityAdvisor(deserManager)
gossipConfig, err := gossip.GlobalConfig(endpoint, nil)
Expand Down
84 changes: 69 additions & 15 deletions internal/peer/gossip/mcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ import (
"fmt"
"time"

"github.com/golang/protobuf/proto"
pcommon "github.com/hyperledger/fabric-protos-go/common"
pmsp "github.com/hyperledger/fabric-protos-go/msp"
"github.com/hyperledger/fabric/bccsp"
"github.com/hyperledger/fabric/common/channelconfig"
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/common/util"
Expand All @@ -31,6 +34,8 @@ type Hasher interface {
Hash(msg []byte, opts bccsp.HashOpts) (hash []byte, err error)
}

type ChannelConfigGetter func(cid string) channelconfig.Resources

// MSPMessageCryptoService implements the MessageCryptoService interface
// using the peer MSPs (local and channel-related)
//
Expand All @@ -45,6 +50,7 @@ type MSPMessageCryptoService struct {
localSigner identity.SignerSerializer
deserializer DeserializersManager
hasher Hasher
channelConfigGetter ChannelConfigGetter
}

// NewMCS creates a new instance of MSPMessageCryptoService
Expand All @@ -58,12 +64,14 @@ func NewMCS(
localSigner identity.SignerSerializer,
deserializer DeserializersManager,
hasher Hasher,
channelConfigGetter ChannelConfigGetter,
) *MSPMessageCryptoService {
return &MSPMessageCryptoService{
channelPolicyManagerGetter: channelPolicyManagerGetter,
localSigner: localSigner,
deserializer: deserializer,
hasher: hasher,
channelConfigGetter: channelConfigGetter,
}
}

Expand Down Expand Up @@ -145,18 +153,20 @@ func (s *MSPMessageCryptoService) VerifyBlock(chainID common.ChannelID, seqNum u
return fmt.Errorf("Block with id [%d] on channel [%s] does not have metadata. Block not valid.", block.Header.Number, chainID)
}

metadata, err := protoutil.GetMetadataFromBlock(block, pcommon.BlockMetadataIndex_SIGNATURES)
if err != nil {
return fmt.Errorf("Failed unmarshalling medatata for signatures [%s]", err)
}

// - Verify that Header.DataHash is equal to the hash of block.Data
// This is to ensure that the header is consistent with the data carried by this block
if !bytes.Equal(protoutil.BlockDataHash(block.Data), block.Header.DataHash) {
return fmt.Errorf("Header.DataHash is different from Hash(block.Data) for block with id [%d] on channel [%s]", block.Header.Number, chainID)
}

// - Get Policy for block validation
if len(block.Metadata.Metadata) < int(pcommon.BlockMetadataIndex_SIGNATURES)+1 {
return errors.Errorf("no signatures in block metadata")
}

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

// Get the policy manager for channelID
cpm := s.channelPolicyManagerGetter.Manager(channelID)
Expand All @@ -170,27 +180,71 @@ func (s *MSPMessageCryptoService) VerifyBlock(chainID common.ChannelID, seqNum u
// ok is true if it was the policy requested, or false if it is the default policy
mcsLogger.Debugf("Got block validation policy for channel [%s] with flag [%t]", channelID, ok)

// - Prepare SignedData
signatureSet := []*protoutil.SignedData{}
for _, metadataSignature := range metadata.Signatures {
shdr, err := protoutil.UnmarshalSignatureHeader(metadataSignature.SignatureHeader)
if err != nil {
return fmt.Errorf("Failed unmarshalling signature header for block with id [%d] on channel [%s]: [%s]", block.Header.Number, chainID, err)
chConfig := s.channelConfigGetter(channelID)
bftEnabled := chConfig.ChannelConfig().Capabilities().ConsensusTypeBFT()

var consenters []*pcommon.Consenter
if bftEnabled {
cfg, ok := chConfig.OrdererConfig()
if !ok {
return fmt.Errorf("no orderer section in channel config for channel [%s].", channelID)
}
consenters = cfg.Consenters()
}

var signatureSet []*protoutil.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 := protoutil.UnmarshalIdentifierHeader(metadataSignature.IdentifierHeader)
if err != nil {
return fmt.Errorf("failed unmarshalling identifier header for block %d: %v", block.Header.Number, 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, protoutil.BlockHeaderBytes(block.Header))
} else {
signatureHeader, err := protoutil.UnmarshalSignatureHeader(metadataSignature.GetSignatureHeader())
if err != nil {
return fmt.Errorf("failed unmarshalling signature header for block %d: %v", block.Header.Number, err)
}

signedPayload = util.ConcatenateBytes(md.Value, metadataSignature.SignatureHeader, protoutil.BlockHeaderBytes(block.Header))

signerIdentity = signatureHeader.Creator
}

signatureSet = append(
signatureSet,
&protoutil.SignedData{
Identity: shdr.Creator,
Data: util.ConcatenateBytes(metadata.Value, metadataSignature.SignatureHeader, protoutil.BlockHeaderBytes(block.Header)),
Identity: signerIdentity,
Data: signedPayload,
Signature: metadataSignature.Signature,
},
)
}

// - Evaluate policy
return policy.EvaluateSignedData(signatureSet)
}

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

// Sign signs msg with this peer's signing key and outputs
// the signature if no error occurred.
func (s *MSPMessageCryptoService) Sign(msg []byte) ([]byte, error) {
Expand Down
Loading

0 comments on commit 7cd6b5b

Please sign in to comment.