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

337 lines
9.0 KiB
Go

/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package smartbft_test
import (
"io/ioutil"
"testing"
"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/common/flogging"
mocks2 "github.com/hyperledger/fabric/orderer/consensus/mocks"
"github.com/hyperledger/fabric/orderer/consensus/smartbft"
"github.com/hyperledger/fabric/orderer/consensus/smartbft/mocks"
"github.com/hyperledger/fabric/protoutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
)
func noopUpdateLastHash(_ *cb.Block) types.Reconfig { return types.Reconfig{} }
func TestSynchronizerSync(t *testing.T) {
blockBytes, err := ioutil.ReadFile("testdata/mychannel.block")
require.NoError(t, err)
goodConfigBlock := &cb.Block{}
require.NoError(t, proto.Unmarshal(blockBytes, goodConfigBlock))
b99 := makeBlockWithMetadata(99, 42, &smartbftprotos.ViewMetadata{ViewId: 1, LatestSequence: 12})
b100 := makeBlockWithMetadata(100, 42, &smartbftprotos.ViewMetadata{ViewId: 1, LatestSequence: 13})
b101 := makeConfigBlockWithMetadata(goodConfigBlock, 101, &smartbftprotos.ViewMetadata{ViewId: 2, LatestSequence: 1})
b102 := makeBlockWithMetadata(102, 101, &smartbftprotos.ViewMetadata{ViewId: 2, LatestSequence: 3})
blockNum2configSqn := map[uint64]uint64{
99: 7,
100: 7,
101: 8,
102: 8,
}
t.Run("no remotes", func(t *testing.T) {
bp := &mocks.FakeBlockPuller{}
fakeCS := &mocks2.FakeConsenterSupport{}
fakeCS.ChannelIDReturns("mychannel")
fakeCS.HeightReturns(100)
fakeCS.BlockReturns(b99)
fakeCS.SequenceReturns(blockNum2configSqn[99])
l := flogging.NewFabricLogger(zap.NewExample())
decision := &types.SyncResponse{
Latest: types.Decision{},
}
syn := &smartbft.Synchronizer{
LatestConfig: func() (types.Configuration, []uint64) {
return types.Configuration{}, nil
},
BlockToDecision: func(block *cb.Block) *types.Decision {
if block == b99 {
return &decision.Latest
}
return nil
},
Logger: l,
BlockPuller: bp,
ClusterSize: 4,
Support: fakeCS,
OnCommit: noopUpdateLastHash,
}
d := syn.Sync()
assert.Equal(t, *decision, d)
})
t.Run("all nodes present", func(t *testing.T) {
bp := &mocks.FakeBlockPuller{}
bp.HeightsByEndpointsReturns(
map[string]uint64{
"example.com:1": 100,
"example.com:2": 102,
"example.com:3": 103,
"example.com:4": 200, // byzantine, lying
},
nil,
)
bp.PullBlockReturnsOnCall(0, b100)
bp.PullBlockReturnsOnCall(1, b101)
bp.PullBlockReturnsOnCall(2, b102)
height := uint64(100)
ledger := map[uint64]*cb.Block{99: b99}
fakeCS := &mocks2.FakeConsenterSupport{}
fakeCS.ChannelIDReturns("mychannel")
fakeCS.HeightCalls(func() uint64 { return height })
fakeCS.SequenceCalls(func() uint64 { return blockNum2configSqn[height-1] })
fakeCS.WriteConfigBlockCalls(func(b *cb.Block, m []byte) {
ledger[height] = b
height++
})
fakeCS.WriteBlockCalls(func(b *cb.Block, m []byte) {
ledger[height] = b
height++
})
fakeCS.BlockCalls(func(sqn uint64) *cb.Block {
return ledger[sqn]
})
decision := &types.SyncResponse{
Latest: types.Decision{},
}
syn := &smartbft.Synchronizer{
BlockToDecision: func(block *cb.Block) *types.Decision {
if block == b102 {
return &decision.Latest
}
return nil
},
Logger: flogging.NewFabricLogger(zap.NewExample()),
BlockPuller: bp,
ClusterSize: 4,
Support: fakeCS,
OnCommit: noopUpdateLastHash,
}
d := syn.Sync()
assert.Equal(t, *decision, d)
})
t.Run("3/4 nodes present", func(t *testing.T) {
bp := &mocks.FakeBlockPuller{}
bp.HeightsByEndpointsReturns(
map[string]uint64{
"example.com:1": 100,
"example.com:2": 102,
"example.com:4": 200, // byzantine, lying
},
nil,
)
bp.PullBlockReturnsOnCall(0, b100)
bp.PullBlockReturnsOnCall(1, b101)
bp.PullBlockReturnsOnCall(2, b102)
height := uint64(100)
ledger := map[uint64]*cb.Block{99: b99}
fakeCS := &mocks2.FakeConsenterSupport{}
fakeCS.ChannelIDReturns("mychannel")
fakeCS.HeightCalls(func() uint64 { return height })
fakeCS.SequenceCalls(func() uint64 { return blockNum2configSqn[height-1] })
fakeCS.WriteConfigBlockCalls(func(b *cb.Block, m []byte) {
ledger[height] = b
height++
})
fakeCS.WriteBlockCalls(func(b *cb.Block, m []byte) {
ledger[height] = b
height++
})
fakeCS.BlockCalls(func(sqn uint64) *cb.Block {
return ledger[sqn]
})
decision := &types.SyncResponse{
Latest: types.Decision{},
}
syn := &smartbft.Synchronizer{
BlockToDecision: func(block *cb.Block) *types.Decision {
if block == b101 {
return &decision.Latest
}
return nil
},
Logger: flogging.NewFabricLogger(zap.NewExample()),
BlockPuller: bp,
ClusterSize: 4,
Support: fakeCS,
OnCommit: noopUpdateLastHash,
}
d := syn.Sync()
assert.Equal(t, *decision, d)
})
t.Run("2/4 nodes present", func(t *testing.T) {
bp := &mocks.FakeBlockPuller{}
bp.HeightsByEndpointsReturns(
map[string]uint64{
"example.com:1": 101,
"example.com:4": 200, // byzantine, lying
},
nil,
)
bp.PullBlockReturnsOnCall(0, b100)
bp.PullBlockReturnsOnCall(1, b101)
bp.PullBlockReturnsOnCall(2, b102)
height := uint64(100)
ledger := map[uint64]*cb.Block{99: b99}
fakeCS := &mocks2.FakeConsenterSupport{}
fakeCS.ChannelIDReturns("mychannel")
fakeCS.HeightCalls(func() uint64 { return height })
fakeCS.SequenceCalls(func() uint64 { return blockNum2configSqn[height-1] })
fakeCS.WriteConfigBlockCalls(func(b *cb.Block, m []byte) {
ledger[height] = b
height++
})
fakeCS.WriteBlockCalls(func(b *cb.Block, m []byte) {
ledger[height] = b
height++
})
fakeCS.BlockCalls(func(sqn uint64) *cb.Block {
return ledger[sqn]
})
decision := &types.SyncResponse{
Latest: types.Decision{},
}
syn := &smartbft.Synchronizer{
BlockToDecision: func(block *cb.Block) *types.Decision {
if block == b100 {
return &decision.Latest
}
return nil
},
Logger: flogging.NewFabricLogger(zap.NewExample()),
BlockPuller: bp,
ClusterSize: 4,
Support: fakeCS,
OnCommit: noopUpdateLastHash,
}
d := syn.Sync()
assert.Equal(t, *decision, d)
})
t.Run("1/4 nodes present", func(t *testing.T) {
bp := &mocks.FakeBlockPuller{}
bp.HeightsByEndpointsReturns(
map[string]uint64{
"example.com:1": 100,
},
nil,
)
bp.PullBlockReturnsOnCall(0, b100)
bp.PullBlockReturnsOnCall(1, b101)
bp.PullBlockReturnsOnCall(2, b102)
height := uint64(100)
ledger := map[uint64]*cb.Block{99: b99}
fakeCS := &mocks2.FakeConsenterSupport{}
fakeCS.ChannelIDReturns("mychannel")
fakeCS.HeightCalls(func() uint64 { return height })
fakeCS.SequenceCalls(func() uint64 { return blockNum2configSqn[height-1] })
fakeCS.WriteConfigBlockCalls(func(b *cb.Block, m []byte) {
ledger[height] = b
height++
})
fakeCS.WriteBlockCalls(func(b *cb.Block, m []byte) {
ledger[height] = b
height++
})
fakeCS.BlockCalls(func(sqn uint64) *cb.Block {
return ledger[sqn]
})
decision := &types.SyncResponse{
Latest: types.Decision{},
}
syn := &smartbft.Synchronizer{
LatestConfig: func() (types.Configuration, []uint64) {
return types.Configuration{}, nil
},
BlockToDecision: func(block *cb.Block) *types.Decision {
if block == b99 {
return &decision.Latest
}
return nil
},
Logger: flogging.NewFabricLogger(zap.NewExample()),
BlockPuller: bp,
ClusterSize: 4,
Support: fakeCS,
OnCommit: noopUpdateLastHash,
}
d := syn.Sync()
assert.Equal(t, *decision, d)
})
}
func makeBlockWithMetadata(sqnNum, lastConfigIndex uint64, viewMetadata *smartbftprotos.ViewMetadata) *cb.Block {
block := protoutil.NewBlock(sqnNum, nil)
block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(
&cb.Metadata{
Value: protoutil.MarshalOrPanic(&cb.LastConfig{Index: lastConfigIndex}),
},
)
block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{
Value: protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
ConsenterMetadata: protoutil.MarshalOrPanic(viewMetadata),
LastConfig: &cb.LastConfig{
Index: sqnNum,
},
}),
})
return block
}
func makeConfigBlockWithMetadata(configBlock *cb.Block, sqnNum uint64, viewMetadata *smartbftprotos.ViewMetadata) *cb.Block {
block := proto.Clone(configBlock).(*cb.Block)
block.Header.Number = sqnNum
block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(
&cb.Metadata{
Value: protoutil.MarshalOrPanic(&cb.LastConfig{Index: sqnNum}),
},
)
block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{
Value: protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
ConsenterMetadata: protoutil.MarshalOrPanic(viewMetadata),
LastConfig: &cb.LastConfig{
Index: sqnNum,
},
}),
})
return block
}