diff --git a/orderer/sbft/simplebft/checkpoint.go b/orderer/sbft/simplebft/checkpoint.go index b6130e476a9..910f5c2bb5d 100644 --- a/orderer/sbft/simplebft/checkpoint.go +++ b/orderer/sbft/simplebft/checkpoint.go @@ -88,9 +88,10 @@ func (s *SBFT) handleCheckpoint(c *Checkpoint, src uint64) { c = s.cur.checkpoint[replicas[0]] if !reflect.DeepEqual(c.Digest, s.cur.subject.Digest) { - log.Fatalf("replica %d: weak checkpoint %x does not match our state %x", - s.id, c.Digest, s.cur.subject.Digest) - // NOT REACHED + log.Warningf("replica %d: weak checkpoint %x does not match our state %x --- primary %d of view %d is probably Byzantine, sending view change", + s.id, c.Digest, s.cur.subject.Digest, s.primaryID(), s.view) + s.sendViewChange() + return } // ignore null requests diff --git a/orderer/sbft/simplebft/simplebft_test.go b/orderer/sbft/simplebft/simplebft_test.go index be56c1974f0..d6f1567cbe2 100644 --- a/orderer/sbft/simplebft/simplebft_test.go +++ b/orderer/sbft/simplebft/simplebft_test.go @@ -266,6 +266,55 @@ func TestByzPrimary(t *testing.T) { } } +func TestByzPrimaryBullyingSingleReplica(t *testing.T) { + skipInShortMode(t) + N := uint64(10) + sys := newTestSystem(N) + var repls []*SBFT + var adapters []*testSystemAdapter + for i := uint64(0); i < N; i++ { + a := sys.NewAdapter(i) + s, err := New(i, &Config{N: N, F: 1, BatchDurationNsec: 2000000000, BatchSizeBytes: 1, RequestTimeoutNsec: 20000000000}, a) + if err != nil { + t.Fatal(err) + } + repls = append(repls, s) + adapters = append(adapters, a) + } + + r1 := []byte{1, 2, 3} + r2 := []byte{5, 6, 7} + + // change preprepare to 1 + sys.filterFn = func(e testElem) (testElem, bool) { + if msg, ok := e.ev.(*testMsgEvent); ok { + if pp := msg.msg.GetPreprepare(); pp != nil && msg.src == 0 && msg.dst == 1 { + pp := *pp + batch := *pp.Batch + batch.Payloads = [][]byte{r2} + pp.Batch = &batch + h := merkleHashData(batch.Payloads) + bh := &BatchHeader{} + proto.Unmarshal(pp.Batch.Header, bh) + bh.DataHash = h + bhraw, _ := proto.Marshal(bh) + pp.Batch.Header = bhraw + msg.msg = &Msg{&Msg_Preprepare{&pp}} + } + } + return e, true + } + + connectAll(sys) + repls[0].Request(r1) + sys.Run() + for _, a := range adapters { + if a.id != 1 && len(a.batches) != 1 { + t.Fatal("expected execution of 1 batch at all except replica 1") + } + } +} + func TestViewChange(t *testing.T) { skipInShortMode(t) N := uint64(4)