From 28bbeac7639694e11e03f167b44376f9a12c9b3e Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 29 Dec 2017 11:26:55 -0500 Subject: [PATCH] state: send byzantine validators in BeginBlock --- evidence/pool_test.go | 4 +-- evidence/reactor_test.go | 2 +- evidence/store_test.go | 66 +++++++--------------------------------- state/execution.go | 9 +++++- state/execution_test.go | 49 ++++++++++++++++++++++++++++- types/evidence.go | 47 ++++++++++++++++++++++++++++ 6 files changed, 117 insertions(+), 60 deletions(-) diff --git a/evidence/pool_test.go b/evidence/pool_test.go index f5b5205b3..97a29a278 100644 --- a/evidence/pool_test.go +++ b/evidence/pool_test.go @@ -53,8 +53,8 @@ func TestEvidencePool(t *testing.T) { store := NewEvidenceStore(dbm.NewMemDB()) pool := NewEvidencePool(stateDB, store) - goodEvidence := newMockGoodEvidence(height, 0, valAddr) - badEvidence := MockBadEvidence{goodEvidence} + goodEvidence := types.NewMockGoodEvidence(height, 0, valAddr) + badEvidence := types.MockBadEvidence{goodEvidence} err := pool.AddEvidence(badEvidence) assert.NotNil(err) diff --git a/evidence/reactor_test.go b/evidence/reactor_test.go index 77c58734e..11c63929b 100644 --- a/evidence/reactor_test.go +++ b/evidence/reactor_test.go @@ -101,7 +101,7 @@ func _waitForEvidence(t *testing.T, wg *sync.WaitGroup, evs types.EvidenceList, func sendEvidence(t *testing.T, evpool *EvidencePool, valAddr []byte, n int) types.EvidenceList { evList := make([]types.Evidence, n) for i := 0; i < n; i++ { - ev := newMockGoodEvidence(int64(i+1), 0, valAddr) + ev := types.NewMockGoodEvidence(int64(i+1), 0, valAddr) err := evpool.AddEvidence(ev) assert.Nil(t, err) evList[i] = ev diff --git a/evidence/store_test.go b/evidence/store_test.go index 192aabc2f..180bee58f 100644 --- a/evidence/store_test.go +++ b/evidence/store_test.go @@ -1,8 +1,6 @@ package evidence import ( - "bytes" - "fmt" "testing" "github.com/stretchr/testify/assert" @@ -20,7 +18,7 @@ func TestStoreAddDuplicate(t *testing.T) { store := NewEvidenceStore(db) priority := int64(10) - ev := newMockGoodEvidence(2, 1, []byte("val1")) + ev := types.NewMockGoodEvidence(2, 1, []byte("val1")) added := store.AddNewEvidence(ev, priority) assert.True(added) @@ -43,7 +41,7 @@ func TestStoreMark(t *testing.T) { assert.Equal(0, len(pendingEv)) priority := int64(10) - ev := newMockGoodEvidence(2, 1, []byte("val1")) + ev := types.NewMockGoodEvidence(2, 1, []byte("val1")) added := store.AddNewEvidence(ev, priority) assert.True(added) @@ -89,15 +87,15 @@ func TestStorePriority(t *testing.T) { // sorted by priority and then height cases := []struct { - ev MockGoodEvidence + ev types.MockGoodEvidence priority int64 }{ - {newMockGoodEvidence(2, 1, []byte("val1")), 17}, - {newMockGoodEvidence(5, 2, []byte("val2")), 15}, - {newMockGoodEvidence(10, 2, []byte("val2")), 13}, - {newMockGoodEvidence(100, 2, []byte("val2")), 11}, - {newMockGoodEvidence(90, 2, []byte("val2")), 11}, - {newMockGoodEvidence(80, 2, []byte("val2")), 11}, + {types.NewMockGoodEvidence(2, 1, []byte("val1")), 17}, + {types.NewMockGoodEvidence(5, 2, []byte("val2")), 15}, + {types.NewMockGoodEvidence(10, 2, []byte("val2")), 13}, + {types.NewMockGoodEvidence(100, 2, []byte("val2")), 11}, + {types.NewMockGoodEvidence(90, 2, []byte("val2")), 11}, + {types.NewMockGoodEvidence(80, 2, []byte("val2")), 11}, } for _, c := range cases { @@ -119,48 +117,6 @@ const ( var _ = wire.RegisterInterface( struct{ types.Evidence }{}, - wire.ConcreteType{MockGoodEvidence{}, evidenceTypeMockGood}, - wire.ConcreteType{MockBadEvidence{}, evidenceTypeMockBad}, + wire.ConcreteType{types.MockGoodEvidence{}, evidenceTypeMockGood}, + wire.ConcreteType{types.MockBadEvidence{}, evidenceTypeMockBad}, ) - -type MockGoodEvidence struct { - Height_ int64 - Address_ []byte - Index_ int -} - -func newMockGoodEvidence(height int64, index int, address []byte) MockGoodEvidence { - return MockGoodEvidence{height, address, index} -} - -func (e MockGoodEvidence) Height() int64 { return e.Height_ } -func (e MockGoodEvidence) Address() []byte { return e.Address_ } -func (e MockGoodEvidence) Index() int { return e.Index_ } -func (e MockGoodEvidence) Hash() []byte { - return []byte(fmt.Sprintf("%d-%d", e.Height_, e.Index_)) -} -func (e MockGoodEvidence) Verify(chainID string) error { return nil } -func (e MockGoodEvidence) Equal(ev types.Evidence) bool { - e2 := ev.(MockGoodEvidence) - return e.Height_ == e2.Height_ && - bytes.Equal(e.Address_, e2.Address_) && - e.Index_ == e2.Index_ -} -func (e MockGoodEvidence) String() string { - return fmt.Sprintf("GoodEvidence: %d/%s/%d", e.Height_, e.Address_, e.Index_) -} - -type MockBadEvidence struct { - MockGoodEvidence -} - -func (e MockBadEvidence) Verify(chainID string) error { return fmt.Errorf("MockBadEvidence") } -func (e MockBadEvidence) Equal(ev types.Evidence) bool { - e2 := ev.(MockBadEvidence) - return e.Height_ == e2.Height_ && - bytes.Equal(e.Address_, e2.Address_) && - e.Index_ == e2.Index_ -} -func (e MockBadEvidence) String() string { - return fmt.Sprintf("BadEvidence: %d/%s/%d", e.Height_, e.Address_, e.Index_) -} diff --git a/state/execution.go b/state/execution.go index 4ccf87d4a..921799b80 100644 --- a/state/execution.go +++ b/state/execution.go @@ -191,13 +191,20 @@ func execBlockOnProxyApp(logger log.Logger, proxyAppConn proxy.AppConnConsensus, } // TODO: determine which validators were byzantine + byzantineVals := make([]*abci.Evidence, len(block.Evidence.Evidence)) + for i, ev := range block.Evidence.Evidence { + byzantineVals[i] = &abci.Evidence{ + PubKey: ev.Address(), // XXX + Height: ev.Height(), + } + } // Begin block _, err := proxyAppConn.BeginBlockSync(abci.RequestBeginBlock{ Hash: block.Hash(), Header: types.TM2PB.Header(block.Header), AbsentValidators: absentVals, - ByzantineValidators: nil, + ByzantineValidators: byzantineVals, }) if err != nil { logger.Error("Error in proxyAppConn.BeginBlock", "err", err) diff --git a/state/execution_test.go b/state/execution_test.go index 9db269116..ffb10f17b 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -82,6 +82,51 @@ func TestBeginBlockAbsentValidators(t *testing.T) { } } +// TestBeginBlockByzantineValidators ensures we send byzantine validators list. +func TestBeginBlockByzantineValidators(t *testing.T) { + app := &testApp{} + cc := proxy.NewLocalClientCreator(app) + proxyApp := proxy.NewAppConns(cc, nil) + err := proxyApp.Start() + require.Nil(t, err) + defer proxyApp.Stop() + + state := state() + + prevHash := state.LastBlockID.Hash + prevParts := types.PartSetHeader{} + prevBlockID := types.BlockID{prevHash, prevParts} + + height1, idx1, val1 := int64(8), 0, []byte("val1") + height2, idx2, val2 := int64(3), 1, []byte("val2") + ev1 := types.NewMockGoodEvidence(height1, idx1, val1) + ev2 := types.NewMockGoodEvidence(height2, idx2, val2) + + testCases := []struct { + desc string + evidence []types.Evidence + expectedByzantineValidators []*abci.Evidence + }{ + {"none byzantine", []types.Evidence{}, []*abci.Evidence{}}, + {"one byzantine", []types.Evidence{ev1}, []*abci.Evidence{{ev1.Address(), ev1.Height()}}}, + {"multiple byzantine", []types.Evidence{ev1, ev2}, []*abci.Evidence{ + {ev1.Address(), ev1.Height()}, + {ev2.Address(), ev2.Height()}}}, + } + + for _, tc := range testCases { + lastCommit := &types.Commit{BlockID: prevBlockID} + + block, _ := state.MakeBlock(10, makeTxs(2), lastCommit) + block.Evidence.Evidence = tc.evidence + _, err = ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger()) + require.Nil(t, err, tc.desc) + + // -> app must receive an index of the byzantine validator + assert.Equal(t, tc.expectedByzantineValidators, app.ByzantineValidators, tc.desc) + } +} + //---------------------------------------------------------------------------- // make some bogus txs @@ -115,7 +160,8 @@ var _ abci.Application = (*testApp)(nil) type testApp struct { abci.BaseApplication - AbsentValidators []int32 + AbsentValidators []int32 + ByzantineValidators []*abci.Evidence } func NewDummyApplication() *testApp { @@ -128,6 +174,7 @@ func (app *testApp) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) { func (app *testApp) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock { app.AbsentValidators = req.AbsentValidators + app.ByzantineValidators = req.ByzantineValidators return abci.ResponseBeginBlock{} } diff --git a/types/evidence.go b/types/evidence.go index 94bab1a38..3ae3e40b1 100644 --- a/types/evidence.go +++ b/types/evidence.go @@ -167,3 +167,50 @@ func (dve *DuplicateVoteEvidence) Equal(ev Evidence) bool { // just check their hashes return bytes.Equal(merkle.SimpleHashFromBinary(dve), merkle.SimpleHashFromBinary(ev)) } + +//----------------------------------------------------------------- + +// UNSTABLE +type MockGoodEvidence struct { + Height_ int64 + Address_ []byte + Index_ int +} + +// UNSTABLE +func NewMockGoodEvidence(height int64, index int, address []byte) MockGoodEvidence { + return MockGoodEvidence{height, address, index} +} + +func (e MockGoodEvidence) Height() int64 { return e.Height_ } +func (e MockGoodEvidence) Address() []byte { return e.Address_ } +func (e MockGoodEvidence) Index() int { return e.Index_ } +func (e MockGoodEvidence) Hash() []byte { + return []byte(fmt.Sprintf("%d-%d", e.Height_, e.Index_)) +} +func (e MockGoodEvidence) Verify(chainID string) error { return nil } +func (e MockGoodEvidence) Equal(ev Evidence) bool { + e2 := ev.(MockGoodEvidence) + return e.Height_ == e2.Height_ && + bytes.Equal(e.Address_, e2.Address_) && + e.Index_ == e2.Index_ +} +func (e MockGoodEvidence) String() string { + return fmt.Sprintf("GoodEvidence: %d/%s/%d", e.Height_, e.Address_, e.Index_) +} + +// UNSTABLE +type MockBadEvidence struct { + MockGoodEvidence +} + +func (e MockBadEvidence) Verify(chainID string) error { return fmt.Errorf("MockBadEvidence") } +func (e MockBadEvidence) Equal(ev Evidence) bool { + e2 := ev.(MockBadEvidence) + return e.Height_ == e2.Height_ && + bytes.Equal(e.Address_, e2.Address_) && + e.Index_ == e2.Index_ +} +func (e MockBadEvidence) String() string { + return fmt.Sprintf("BadEvidence: %d/%s/%d", e.Height_, e.Address_, e.Index_) +}