Skip to content

Commit

Permalink
Raft to BFT migration: add and edit test cases
Browse files Browse the repository at this point in the history
Signed-off-by: May Rosenbaum <[email protected]>
  • Loading branch information
MayRosenbaum committed Jan 18, 2024
1 parent 6a21a90 commit 1877849
Show file tree
Hide file tree
Showing 6 changed files with 322 additions and 93 deletions.
255 changes: 198 additions & 57 deletions integration/raft/migration_test.go

Large diffs are not rendered by default.

44 changes: 42 additions & 2 deletions orderer/common/msgprocessor/maintenancefilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import (
"time"

"github.com/SmartBFT-Go/consensus/pkg/types"

"github.com/golang/protobuf/proto"
cb "github.com/hyperledger/fabric-protos-go/common"
"github.com/hyperledger/fabric-protos-go/orderer"
"github.com/hyperledger/fabric-protos-go/orderer/etcdraft"
"github.com/hyperledger/fabric-protos-go/orderer/smartbft"
"github.com/hyperledger/fabric/bccsp"
"github.com/hyperledger/fabric/common/channelconfig"
Expand Down Expand Up @@ -142,9 +142,14 @@ func (mf *MaintenanceFilter) inspect(configEnvelope *cb.ConfigEnvelope, ordererC
}

_, err := validateBFTMetadataOptions(1, updatedMetadata)
if updatedMetadata.XXX_unrecognized != nil || err != nil {
if err != nil {
return errors.New("invalid BFT metadata configuration")
}

err = validateBFTConsenterMapping(ordererConfig, nextOrdererConfig)
if err != nil {
return errors.Wrap(err, "invalid BFT consenter mapping configuration")
}
}

logger.Infof("[channel: %s] consensus-type migration: about to change from %s to %s",
Expand Down Expand Up @@ -260,3 +265,38 @@ func validateBFTMetadataOptions(selfID uint64, options *smartbft.Options) (types

return config, nil
}

func validateBFTConsenterMapping(currentOrdererConfig channelconfig.Orderer, nextOrdererConfig channelconfig.Orderer) error {
// extract raft consenters from consensusTypeValue.metadata
raftMetadata := &etcdraft.ConfigMetadata{}
proto.Unmarshal(currentOrdererConfig.ConsensusMetadata(), raftMetadata)
raftConsenters := raftMetadata.GetConsenters()

// extract bft consenters
bftConsenters := nextOrdererConfig.Consenters()

if len(bftConsenters) == 0 {
return errors.Errorf("Invalid new config: bft consenters are missing")
}

if len(raftConsenters) != len(bftConsenters) {
return errors.Errorf("Invalid new config: the number of bft consenters: %d is not equal to the number of raft consenters: %d", len(bftConsenters), len(raftConsenters))
}

for _, raftConsenter := range raftConsenters {
flag := false
for _, bftConsenter := range bftConsenters {
if raftConsenter.Port == bftConsenter.Port && raftConsenter.Host == bftConsenter.Host &&
bytes.Equal(raftConsenter.ServerTlsCert, bftConsenter.ServerTlsCert) &&
bytes.Equal(raftConsenter.ClientTlsCert, bftConsenter.ClientTlsCert) {
flag = true
break
}
}
if flag == false {
return errors.Errorf("No suitable BFT consenter for Raft consenter: %v", raftConsenter)
}
}

return nil
}
104 changes: 75 additions & 29 deletions orderer/common/msgprocessor/maintenancefilter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"path"
"testing"

"github.com/hyperledger/fabric-protos-go/orderer/etcdraft"

"github.com/SmartBFT-Go/consensus/pkg/types"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric-protos-go/orderer/smartbft"
Expand All @@ -38,6 +40,9 @@ func newMockOrdererConfig(migration bool, state orderer.ConsensusType_State) *mo
mockOrderer.CapabilitiesReturns(mockCapabilities)
mockOrderer.ConsensusTypeReturns("etcdraft")
mockOrderer.ConsensusStateReturns(state)
mockOrderer.ConsensusMetadataReturns(protoutil.MarshalOrPanic(&etcdraft.ConfigMetadata{
Consenters: []*etcdraft.Consenter{{Host: "127.0.0.1", Port: 4000, ClientTlsCert: []byte{1, 2, 3}, ServerTlsCert: []byte{4, 5, 6}},
{Host: "127.0.0.1", Port: 4001, ClientTlsCert: []byte{7, 8, 9}, ServerTlsCert: []byte{1, 2, 3}}}}))
return mockOrderer
}

Expand All @@ -61,10 +66,11 @@ func TestMaintenanceDisabled(t *testing.T) {
require.NoError(t, err)
mf := NewMaintenanceFilter(msInactive, cryptoProvider)
require.NotNil(t, mf)
current := consensusTypeInfo{ordererType: "etcdraft", metadata: []byte{}, state: orderer.ConsensusType_STATE_NORMAL}
raftMetadata := protoutil.MarshalOrPanic(&etcdraft.ConfigMetadata{Consenters: []*etcdraft.Consenter{{Host: "127.0.0.1", Port: 4000, ClientTlsCert: []byte{1, 2, 3}, ServerTlsCert: []byte{4, 5, 6}}, {Host: "127.0.0.1", Port: 4001, ClientTlsCert: []byte{7, 8, 9}, ServerTlsCert: []byte{1, 2, 3}}}})
current := consensusTypeInfo{ordererType: "etcdraft", metadata: raftMetadata, state: orderer.ConsensusType_STATE_NORMAL}

t.Run("Good", func(t *testing.T) {
configTx := makeConfigEnvelopeWithExtraStuff(t, current, current, 3)
configTx := makeConfigEnvelopeWithExtraStuff(t, current, current, 3, 0)
err := mf.Apply(configTx)
require.NoError(t, err)
})
Expand Down Expand Up @@ -173,12 +179,12 @@ func TestMaintenanceInspectEntry(t *testing.T) {
mf := NewMaintenanceFilter(msActive, cryptoProvider)
require.NotNil(t, mf)
bogusMetadata := []byte{1, 2, 3, 4}
validMetadata := []byte{}
validRaftMetadata := protoutil.MarshalOrPanic(&etcdraft.ConfigMetadata{Consenters: []*etcdraft.Consenter{{Host: "127.0.0.1", Port: 4000, ClientTlsCert: []byte{1, 2, 3}, ServerTlsCert: []byte{4, 5, 6}}, {Host: "127.0.0.1", Port: 4001, ClientTlsCert: []byte{7, 8, 9}, ServerTlsCert: []byte{1, 2, 3}}}})
bftMetadata := protoutil.MarshalOrPanic(createValidBFTMetadata())
current := consensusTypeInfo{ordererType: "etcdraft", metadata: validMetadata, state: orderer.ConsensusType_STATE_NORMAL}
current := consensusTypeInfo{ordererType: "etcdraft", metadata: validRaftMetadata, state: orderer.ConsensusType_STATE_NORMAL}

t.Run("Good", func(t *testing.T) {
next := consensusTypeInfo{ordererType: "etcdraft", metadata: validMetadata, state: orderer.ConsensusType_STATE_MAINTENANCE}
next := consensusTypeInfo{ordererType: "etcdraft", metadata: validRaftMetadata, state: orderer.ConsensusType_STATE_MAINTENANCE}
configTx := makeConfigEnvelope(t, current, next)
err := mf.Apply(configTx)
require.NoError(t, err)
Expand All @@ -201,8 +207,8 @@ func TestMaintenanceInspectEntry(t *testing.T) {
})

t.Run("Bad: concurrent change to state & orderer value", func(t *testing.T) {
next := consensusTypeInfo{ordererType: "etcdraft", metadata: validMetadata, state: orderer.ConsensusType_STATE_MAINTENANCE}
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 4)
next := consensusTypeInfo{ordererType: "etcdraft", metadata: validRaftMetadata, state: orderer.ConsensusType_STATE_MAINTENANCE}
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 0, 1)
err := mf.Apply(configTx)
require.EqualError(t, err,
"config transaction inspection failed: config update contains changes to groups within the Orderer group")
Expand All @@ -226,18 +232,19 @@ func TestMaintenanceInspectChange(t *testing.T) {
mf := NewMaintenanceFilter(msActive, cryptoProvider)
require.NotNil(t, mf)
bogusMetadata := []byte{1, 2, 3, 4}
validMetadata := protoutil.MarshalOrPanic(createValidBFTMetadata())
current := consensusTypeInfo{ordererType: "etcdraft", metadata: []byte{}, state: orderer.ConsensusType_STATE_MAINTENANCE}
validBFTMetadata := protoutil.MarshalOrPanic(createValidBFTMetadata())
raftMetadata := protoutil.MarshalOrPanic(&etcdraft.ConfigMetadata{Consenters: []*etcdraft.Consenter{{Host: "127.0.0.1", Port: 4000, ClientTlsCert: []byte{1, 2, 3}, ServerTlsCert: []byte{4, 5, 6}}, {Host: "127.0.0.1", Port: 4001, ClientTlsCert: []byte{7, 8, 9}, ServerTlsCert: []byte{1, 2, 3}}}})
current := consensusTypeInfo{ordererType: "etcdraft", metadata: raftMetadata, state: orderer.ConsensusType_STATE_MAINTENANCE}

t.Run("Good type change with valid BFT metadata and consenter mapping", func(t *testing.T) {
next := consensusTypeInfo{ordererType: "BFT", metadata: validMetadata, state: orderer.ConsensusType_STATE_MAINTENANCE}
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 4)
t.Run("Good type change with valid BFT metadata and suitable consenter mapping", func(t *testing.T) {
next := consensusTypeInfo{ordererType: "BFT", metadata: validBFTMetadata, state: orderer.ConsensusType_STATE_MAINTENANCE}
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 0, 1)
err := mf.Apply(configTx)
require.NoError(t, err)
})

t.Run("Good exit, no change", func(t *testing.T) {
next := consensusTypeInfo{ordererType: "etcdraft", metadata: []byte{}, state: orderer.ConsensusType_STATE_NORMAL}
next := consensusTypeInfo{ordererType: "etcdraft", metadata: raftMetadata, state: orderer.ConsensusType_STATE_NORMAL}
configTx := makeConfigEnvelope(t, current, next)
err := mf.Apply(configTx)
require.NoError(t, err)
Expand All @@ -261,6 +268,33 @@ func TestMaintenanceInspectChange(t *testing.T) {
"config transaction inspection failed: failed to unmarshal BFT metadata configuration")
})

t.Run("Bad: good type change with valid BFT metadata but missing consenters mapping", func(t *testing.T) {
next := consensusTypeInfo{ordererType: "BFT", metadata: validBFTMetadata, state: orderer.ConsensusType_STATE_MAINTENANCE}
configTx := makeConfigEnvelope(t, current, next)
err := mf.Apply(configTx)
require.Error(t, err)
require.EqualError(t, err,
"config transaction inspection failed: invalid BFT consenter mapping configuration: Invalid new config: bft consenters are missing")
})

t.Run("Bad: good type change with valid BFT metadata but corrupt consenters mapping", func(t *testing.T) {
next := consensusTypeInfo{ordererType: "BFT", metadata: validBFTMetadata, state: orderer.ConsensusType_STATE_MAINTENANCE}
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 0, 2)
err := mf.Apply(configTx)
require.Error(t, err)
require.EqualError(t, err,
"config transaction inspection failed: invalid BFT consenter mapping configuration: No suitable BFT consenter for Raft consenter: host:\"127.0.0.1\" port:4000 client_tls_cert:\"\\001\\002\\003\" server_tls_cert:\"\\004\\005\\006\" ")
})

t.Run("Bad: good type change with valid BFT metadata but missing consenters", func(t *testing.T) {
next := consensusTypeInfo{ordererType: "BFT", metadata: validBFTMetadata, state: orderer.ConsensusType_STATE_MAINTENANCE}
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 0, 3)
err := mf.Apply(configTx)
require.Error(t, err)
require.EqualError(t, err,
"config transaction inspection failed: invalid BFT consenter mapping configuration: Invalid new config: the number of bft consenters: 1 is not equal to the number of raft consenters: 2")
})

t.Run("Bad: unsupported consensus type", func(t *testing.T) {
next := consensusTypeInfo{ordererType: "unsupported", metadata: bogusMetadata, state: orderer.ConsensusType_STATE_MAINTENANCE}
configTx := makeConfigEnvelope(t, current, next)
Expand All @@ -270,7 +304,7 @@ func TestMaintenanceInspectChange(t *testing.T) {
})

t.Run("Bad: concurrent change to consensus type & state", func(t *testing.T) {
next := consensusTypeInfo{ordererType: "BFT", metadata: validMetadata, state: orderer.ConsensusType_STATE_NORMAL}
next := consensusTypeInfo{ordererType: "BFT", metadata: validBFTMetadata, state: orderer.ConsensusType_STATE_NORMAL}
configTx := makeConfigEnvelope(t, current, next)
err := mf.Apply(configTx)
require.EqualError(t, err,
Expand Down Expand Up @@ -318,28 +352,28 @@ func TestMaintenanceInspectExit(t *testing.T) {

t.Run("Bad: exit with extra group", func(t *testing.T) {
next := consensusTypeInfo{ordererType: "BFT", metadata: validMetadata, state: orderer.ConsensusType_STATE_NORMAL}
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 1)
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 1, 1)
err := mf.Apply(configTx)
require.EqualError(t, err, "config transaction inspection failed: config update contains changes to more than one group")
})

t.Run("Bad: exit with extra value", func(t *testing.T) {
next := consensusTypeInfo{ordererType: "BFT", metadata: validMetadata, state: orderer.ConsensusType_STATE_NORMAL}
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 2)
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 2, 1)
err := mf.Apply(configTx)
require.EqualError(t, err, "config transaction inspection failed: config update contains changes to values in group Channel")
})

t.Run("Bad: exit with extra orderer value", func(t *testing.T) {
next := consensusTypeInfo{ordererType: "BFT", metadata: validMetadata, state: orderer.ConsensusType_STATE_NORMAL}
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 3)
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 3, 0)
err := mf.Apply(configTx)
require.EqualError(t, err, "config transaction inspection failed: config update contain more then just the ConsensusType value in the Orderer group")
})

t.Run("Bad: exit with extra orderer value required for BFT", func(t *testing.T) {
next := consensusTypeInfo{ordererType: "BFT", metadata: validMetadata, state: orderer.ConsensusType_STATE_NORMAL}
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 4)
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 0, 1)
err := mf.Apply(configTx)
require.EqualError(t, err, "config transaction inspection failed: config update contains changes to groups within the Orderer group")
})
Expand All @@ -353,33 +387,34 @@ func TestMaintenanceExtra(t *testing.T) {
require.NoError(t, err)
mf := NewMaintenanceFilter(msActive, cryptoProvider)
require.NotNil(t, mf)
current := consensusTypeInfo{ordererType: "etcdraft", metadata: nil, state: orderer.ConsensusType_STATE_MAINTENANCE}
raftMetadata := protoutil.MarshalOrPanic(&etcdraft.ConfigMetadata{Consenters: []*etcdraft.Consenter{{Host: "127.0.0.1", Port: 4000, ClientTlsCert: []byte{1, 2, 3}, ServerTlsCert: []byte{4, 5, 6}}, {Host: "127.0.0.1", Port: 4001, ClientTlsCert: []byte{7, 8, 9}, ServerTlsCert: []byte{1, 2, 3}}}})
current := consensusTypeInfo{ordererType: "etcdraft", metadata: raftMetadata, state: orderer.ConsensusType_STATE_MAINTENANCE}
validMetadata := protoutil.MarshalOrPanic(createValidBFTMetadata())

t.Run("Good: with extra group", func(t *testing.T) {
next := consensusTypeInfo{ordererType: "BFT", metadata: validMetadata, state: orderer.ConsensusType_STATE_MAINTENANCE}
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 1)
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 1, 1)
err := mf.Apply(configTx)
require.NoError(t, err)
})

t.Run("Good: with extra value", func(t *testing.T) {
next := consensusTypeInfo{ordererType: "BFT", metadata: validMetadata, state: orderer.ConsensusType_STATE_MAINTENANCE}
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 2)
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 2, 1)
err := mf.Apply(configTx)
require.NoError(t, err)
})

t.Run("Good: with extra orderer value", func(t *testing.T) {
next := consensusTypeInfo{ordererType: "BFT", metadata: validMetadata, state: orderer.ConsensusType_STATE_MAINTENANCE}
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 3)
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 3, 1)
err := mf.Apply(configTx)
require.NoError(t, err)
})

t.Run("Good: with extra orderer value required for BFT", func(t *testing.T) {
next := consensusTypeInfo{ordererType: "BFT", metadata: validMetadata, state: orderer.ConsensusType_STATE_MAINTENANCE}
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 4)
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 0, 1)
err := mf.Apply(configTx)
require.NoError(t, err)
})
Expand All @@ -395,7 +430,7 @@ func TestMaintenanceMissingConsensusType(t *testing.T) {
require.NotNil(t, mf)
current := consensusTypeInfo{ordererType: "etcdraft", metadata: nil, state: orderer.ConsensusType_STATE_MAINTENANCE}
for i := 1; i < 4; i++ {
configTx := makeConfigEnvelopeWithExtraStuff(t, current, current, i)
configTx := makeConfigEnvelopeWithExtraStuff(t, current, current, i, 1)
err := mf.Apply(configTx)
require.NoError(t, err)
}
Expand Down Expand Up @@ -436,7 +471,7 @@ func makeConfigEnvelope(t *testing.T, current, next consensusTypeInfo) *common.E
return configTx
}

func makeConfigEnvelopeWithExtraStuff(t *testing.T, current, next consensusTypeInfo, extra int) *common.Envelope {
func makeConfigEnvelopeWithExtraStuff(t *testing.T, current, next consensusTypeInfo, extra int, addBFTConsenterMapping int) *common.Envelope {
original := makeBaseConfig(t)
updated := proto.Clone(original).(*common.Config)

Expand Down Expand Up @@ -479,13 +514,24 @@ func makeConfigEnvelopeWithExtraStuff(t *testing.T, current, next consensusTypeI
}),
ModPolicy: channelconfig.AdminsPolicyKey,
}
case 4:
}

switch addBFTConsenterMapping {
case 1:
updated.ChannelGroup.Groups[channelconfig.OrdererGroupKey].Values[channelconfig.OrderersKey] = &common.ConfigValue{
Value: protoutil.MarshalOrPanic(&common.Orderers{ConsenterMapping: []*common.Consenter{{Id: 1, Host: "127.0.0.1", Port: 4000, ClientTlsCert: []byte{1, 2, 3}, ServerTlsCert: []byte{4, 5, 6}}, {Id: 2, Host: "127.0.0.1", Port: 4001, ClientTlsCert: []byte{7, 8, 9}, ServerTlsCert: []byte{1, 2, 3}}}}),
ModPolicy: channelconfig.AdminsPolicyKey,
}
case 2:
updated.ChannelGroup.Groups[channelconfig.OrdererGroupKey].Values[channelconfig.OrderersKey] = &common.ConfigValue{
Value: protoutil.MarshalOrPanic(&common.Orderers{ConsenterMapping: []*common.Consenter{{Id: 1, Host: "127.0.0.1", Port: 4005, ClientTlsCert: []byte{1, 2, 3}, ServerTlsCert: []byte{4, 5, 6}}, {Id: 2, Host: "127.0.0.1", Port: 4001, ClientTlsCert: []byte{7, 8, 9}, ServerTlsCert: []byte{1, 2, 3}}}}),
ModPolicy: channelconfig.AdminsPolicyKey,
}
case 3:
updated.ChannelGroup.Groups[channelconfig.OrdererGroupKey].Values[channelconfig.OrderersKey] = &common.ConfigValue{
Value: protoutil.MarshalOrPanic(&common.Orderers{ConsenterMapping: []*common.Consenter{{Id: 1, Host: "", Port: 0}}}),
Value: protoutil.MarshalOrPanic(&common.Orderers{ConsenterMapping: []*common.Consenter{{Id: 1, Host: "127.0.0.1", Port: 4000, ClientTlsCert: []byte{1, 2, 3}, ServerTlsCert: []byte{4, 5, 6}}}}),
ModPolicy: channelconfig.AdminsPolicyKey,
}
default:
return nil
}

configTx := makeConfigTx(original, updated, t)
Expand Down
6 changes: 4 additions & 2 deletions orderer/consensus/etcdraft/chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -553,15 +553,17 @@ var _ = Describe("Chain", func() {
"ConsensusType": {
Version: 4,
Value: marshalOrPanic(&orderer.ConsensusType{
Type: "etcdraft",
Type: "etcdraft",
Metadata: []byte{1, 2, 3},
}),
},
}
oldValues := map[string]*common.ConfigValue{
"ConsensusType": {
Version: 4,
Value: marshalOrPanic(&orderer.ConsensusType{
Type: "etcdraft",
Type: "etcdraft",
Metadata: []byte{1, 2, 3},
}),
},
}
Expand Down
4 changes: 1 addition & 3 deletions orderer/consensus/etcdraft/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,7 @@ func MetadataFromConfigValue(configValue *common.ConfigValue) (*etcdraft.ConfigM
}

if consensusTypeValue.Type != "etcdraft" {
if consensusTypeValue.Type == "BFT" {
return nil, consensusTypeValue, nil
}
return nil, consensusTypeValue, nil
}

updatedMetadata := &etcdraft.ConfigMetadata{}
Expand Down
2 changes: 2 additions & 0 deletions orderer/consensus/etcdraft/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,8 @@ func TestVerifyConfigMetadata(t *testing.T) {
})
}

// This test checks that MetadataFromConfigValue, which reads and translates configuration updates from config value,
// returns an empty raft metadata when the consensus type is BFT.
func TestMetadataFromConfigValue(t *testing.T) {
configValue := &common.ConfigValue{
Value: protoutil.MarshalOrPanic(&orderer.ConsensusType{
Expand Down

0 comments on commit 1877849

Please sign in to comment.