mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-30 19:55:42 +00:00
evidence: introduction of LightClientAttackEvidence and refactor of evidence lifecycle (#5361)
evidence: modify evidence types (#5342) light: detect light client attacks (#5344) evidence: refactor evidence pool (#5345) abci: application evidence prepared by evidence pool (#5354)
This commit is contained in:
+11
-19
@@ -131,9 +131,12 @@ func (blockExec *BlockExecutor) ApplyBlock(
|
||||
return state, 0, ErrInvalidBlock(err)
|
||||
}
|
||||
|
||||
// Update evpool with the block and state and get any byzantine validators for that block
|
||||
byzVals := blockExec.evpool.ABCIEvidence(block.Height, block.Evidence.Evidence)
|
||||
|
||||
startTime := time.Now().UnixNano()
|
||||
abciResponses, err := execBlockOnProxyApp(blockExec.logger, blockExec.proxyApp, block,
|
||||
blockExec.store, state.InitialHeight)
|
||||
blockExec.store, state.InitialHeight, byzVals)
|
||||
endTime := time.Now().UnixNano()
|
||||
blockExec.metrics.BlockProcessingTime.Observe(float64(endTime-startTime) / 1000000)
|
||||
if err != nil {
|
||||
@@ -175,8 +178,8 @@ func (blockExec *BlockExecutor) ApplyBlock(
|
||||
return state, 0, fmt.Errorf("commit failed for application: %v", err)
|
||||
}
|
||||
|
||||
// Update evpool with the block and state.
|
||||
blockExec.evpool.Update(block, state)
|
||||
// Update evpool with the latest state.
|
||||
blockExec.evpool.Update(state)
|
||||
|
||||
fail.Fail() // XXX
|
||||
|
||||
@@ -258,6 +261,7 @@ func execBlockOnProxyApp(
|
||||
block *types.Block,
|
||||
store Store,
|
||||
initialHeight int64,
|
||||
byzVals []abci.Evidence,
|
||||
) (*tmstate.ABCIResponses, error) {
|
||||
var validTxs, invalidTxs = 0, 0
|
||||
|
||||
@@ -285,7 +289,7 @@ func execBlockOnProxyApp(
|
||||
}
|
||||
proxyAppConn.SetResponseCallback(proxyCb)
|
||||
|
||||
commitInfo, byzVals := getBeginBlockValidatorInfo(block, store, initialHeight)
|
||||
commitInfo := getBeginBlockValidatorInfo(block, store, initialHeight)
|
||||
|
||||
// Begin block
|
||||
var err error
|
||||
@@ -325,7 +329,7 @@ func execBlockOnProxyApp(
|
||||
}
|
||||
|
||||
func getBeginBlockValidatorInfo(block *types.Block, store Store,
|
||||
initialHeight int64) (abci.LastCommitInfo, []abci.Evidence) {
|
||||
initialHeight int64) abci.LastCommitInfo {
|
||||
voteInfos := make([]abci.VoteInfo, block.LastCommit.Size())
|
||||
// Initial block -> LastCommitInfo.Votes are empty.
|
||||
// Remember that the first LastCommit is intentionally empty, so it makes
|
||||
@@ -356,22 +360,10 @@ func getBeginBlockValidatorInfo(block *types.Block, store Store,
|
||||
}
|
||||
}
|
||||
|
||||
byzVals := make([]abci.Evidence, len(block.Evidence.Evidence))
|
||||
for i, ev := range block.Evidence.Evidence {
|
||||
// We need the validator set. We already did this in validateBlock.
|
||||
// TODO: Should we instead cache the valset in the evidence itself and add
|
||||
// `SetValidatorSet()` and `ToABCI` methods ?
|
||||
valset, err := store.LoadValidators(ev.Height())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
byzVals[i] = types.TM2PB.Evidence(ev, valset)
|
||||
}
|
||||
|
||||
return abci.LastCommitInfo{
|
||||
Round: block.LastCommit.Round,
|
||||
Votes: voteInfos,
|
||||
}, byzVals
|
||||
}
|
||||
}
|
||||
|
||||
func validateValidatorUpdates(abciUpdates []abci.ValidatorUpdate,
|
||||
@@ -533,7 +525,7 @@ func ExecCommitBlock(
|
||||
store Store,
|
||||
initialHeight int64,
|
||||
) ([]byte, error) {
|
||||
_, err := execBlockOnProxyApp(logger, appConnConsensus, block, store, initialHeight)
|
||||
_, err := execBlockOnProxyApp(logger, appConnConsensus, block, store, initialHeight, []abci.Evidence{})
|
||||
if err != nil {
|
||||
logger.Error("Error executing block on proxy app", "height", block.Height, "err", err)
|
||||
return nil, err
|
||||
|
||||
+40
-48
@@ -6,16 +6,18 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
cryptoenc "github.com/tendermint/tendermint/crypto/encoding"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/mempool/mock"
|
||||
mmock "github.com/tendermint/tendermint/mempool/mock"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/state/mocks"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
@@ -38,7 +40,7 @@ func TestApplyBlock(t *testing.T) {
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
|
||||
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(),
|
||||
mock.Mempool{}, sm.MockEvidencePool{})
|
||||
mmock.Mempool{}, sm.EmptyEvidencePool{})
|
||||
|
||||
block := makeBlock(state, 1)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
|
||||
@@ -123,55 +125,45 @@ func TestBeginBlockByzantineValidators(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
defer proxyApp.Stop() //nolint:errcheck // ignore for tests
|
||||
|
||||
state, stateDB, privVals := makeState(2, 12)
|
||||
state, stateDB, _ := makeState(1, 1)
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
|
||||
prevHash := state.LastBlockID.Hash
|
||||
prevParts := types.PartSetHeader{}
|
||||
prevBlockID := types.BlockID{Hash: prevHash, PartSetHeader: prevParts}
|
||||
defaultEvidenceTime := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
height1, val1 := int64(8), state.Validators.Validators[0].Address
|
||||
height2, val2 := int64(3), state.Validators.Validators[1].Address
|
||||
ev1 := types.NewMockDuplicateVoteEvidenceWithValidator(height1, time.Now(), privVals[val1.String()], chainID)
|
||||
ev2 := types.NewMockDuplicateVoteEvidenceWithValidator(height2, time.Now(), privVals[val2.String()], chainID)
|
||||
|
||||
now := tmtime.Now()
|
||||
valSet := state.Validators
|
||||
testCases := []struct {
|
||||
desc string
|
||||
evidence []types.Evidence
|
||||
expectedByzantineValidators []abci.Evidence
|
||||
}{
|
||||
{"none byzantine", []types.Evidence{}, []abci.Evidence{}},
|
||||
{"one byzantine", []types.Evidence{ev1}, []abci.Evidence{types.TM2PB.Evidence(ev1, valSet)}},
|
||||
{"multiple byzantine", []types.Evidence{ev1, ev2}, []abci.Evidence{
|
||||
types.TM2PB.Evidence(ev1, valSet),
|
||||
types.TM2PB.Evidence(ev2, valSet)}},
|
||||
abciEv := []abci.Evidence{
|
||||
{
|
||||
Type: abci.EvidenceType_DUPLICATE_VOTE,
|
||||
Height: 3,
|
||||
Time: defaultEvidenceTime,
|
||||
Validator: types.TM2PB.Validator(state.Validators.Validators[0]),
|
||||
TotalVotingPower: 33,
|
||||
},
|
||||
{
|
||||
Type: abci.EvidenceType_LIGHT_CLIENT_ATTACK,
|
||||
Height: 8,
|
||||
Time: defaultEvidenceTime,
|
||||
Validator: types.TM2PB.Validator(state.Validators.Validators[0]),
|
||||
TotalVotingPower: 12,
|
||||
},
|
||||
}
|
||||
|
||||
var (
|
||||
commitSig0 = types.NewCommitSigForBlock(
|
||||
[]byte("Signature1"),
|
||||
state.Validators.Validators[0].Address,
|
||||
now)
|
||||
commitSig1 = types.NewCommitSigForBlock(
|
||||
[]byte("Signature2"),
|
||||
state.Validators.Validators[1].Address,
|
||||
now)
|
||||
)
|
||||
commitSigs := []types.CommitSig{commitSig0, commitSig1}
|
||||
lastCommit := types.NewCommit(9, 0, prevBlockID, commitSigs)
|
||||
for _, tc := range testCases {
|
||||
evpool := &mocks.EvidencePool{}
|
||||
evpool.On("ABCIEvidence", mock.AnythingOfType("int64"), mock.AnythingOfType("[]types.Evidence")).Return(abciEv)
|
||||
evpool.On("Update", mock.AnythingOfType("state.State")).Return()
|
||||
evpool.On("CheckEvidence", mock.AnythingOfType("types.EvidenceList")).Return(nil)
|
||||
|
||||
block, _ := state.MakeBlock(10, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address)
|
||||
block.Time = now
|
||||
block.Evidence.Evidence = tc.evidence
|
||||
_, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateStore, 1)
|
||||
require.Nil(t, err, tc.desc)
|
||||
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(),
|
||||
mmock.Mempool{}, evpool)
|
||||
|
||||
// -> app must receive an index of the byzantine validator
|
||||
assert.Equal(t, tc.expectedByzantineValidators, app.ByzantineValidators, tc.desc)
|
||||
}
|
||||
block := makeBlock(state, 1)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
|
||||
|
||||
state, retainHeight, err := blockExec.ApplyBlock(state, blockID, block)
|
||||
require.Nil(t, err)
|
||||
assert.EqualValues(t, retainHeight, 1)
|
||||
|
||||
// TODO check state and mempool
|
||||
assert.Equal(t, abciEv, app.ByzantineValidators)
|
||||
}
|
||||
|
||||
func TestValidateValidatorUpdates(t *testing.T) {
|
||||
@@ -320,8 +312,8 @@ func TestEndBlockValidatorUpdates(t *testing.T) {
|
||||
stateStore,
|
||||
log.TestingLogger(),
|
||||
proxyApp.Consensus(),
|
||||
mock.Mempool{},
|
||||
sm.MockEvidencePool{},
|
||||
mmock.Mempool{},
|
||||
sm.EmptyEvidencePool{},
|
||||
)
|
||||
|
||||
eventBus := types.NewEventBus()
|
||||
@@ -390,8 +382,8 @@ func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) {
|
||||
stateStore,
|
||||
log.TestingLogger(),
|
||||
proxyApp.Consensus(),
|
||||
mock.Mempool{},
|
||||
sm.MockEvidencePool{},
|
||||
mmock.Mempool{},
|
||||
sm.EmptyEvidencePool{},
|
||||
)
|
||||
|
||||
block := makeBlock(state, 1)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// Code generated by mockery v2.2.1. DO NOT EDIT.
|
||||
// Code generated by mockery v2.1.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
abcitypes "github.com/tendermint/tendermint/abci/types"
|
||||
|
||||
state "github.com/tendermint/tendermint/state"
|
||||
|
||||
types "github.com/tendermint/tendermint/types"
|
||||
@@ -14,6 +16,22 @@ type EvidencePool struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// ABCIEvidence provides a mock function with given fields: _a0, _a1
|
||||
func (_m *EvidencePool) ABCIEvidence(_a0 int64, _a1 []types.Evidence) []abcitypes.Evidence {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 []abcitypes.Evidence
|
||||
if rf, ok := ret.Get(0).(func(int64, []types.Evidence) []abcitypes.Evidence); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]abcitypes.Evidence)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// AddEvidence provides a mock function with given fields: _a0
|
||||
func (_m *EvidencePool) AddEvidence(_a0 types.Evidence) error {
|
||||
ret := _m.Called(_a0)
|
||||
@@ -28,6 +46,20 @@ func (_m *EvidencePool) AddEvidence(_a0 types.Evidence) error {
|
||||
return r0
|
||||
}
|
||||
|
||||
// CheckEvidence provides a mock function with given fields: _a0
|
||||
func (_m *EvidencePool) CheckEvidence(_a0 types.EvidenceList) error {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(types.EvidenceList) error); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// PendingEvidence provides a mock function with given fields: _a0
|
||||
func (_m *EvidencePool) PendingEvidence(_a0 uint32) []types.Evidence {
|
||||
ret := _m.Called(_a0)
|
||||
@@ -44,21 +76,7 @@ func (_m *EvidencePool) PendingEvidence(_a0 uint32) []types.Evidence {
|
||||
return r0
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: _a0, _a1
|
||||
func (_m *EvidencePool) Update(_a0 *types.Block, _a1 state.State) {
|
||||
_m.Called(_a0, _a1)
|
||||
}
|
||||
|
||||
// Verify provides a mock function with given fields: _a0
|
||||
func (_m *EvidencePool) Verify(_a0 types.Evidence) error {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(types.Evidence) error); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
// Update provides a mock function with given fields: _a0
|
||||
func (_m *EvidencePool) Update(_a0 state.State) {
|
||||
_m.Called(_a0)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
// Code generated by mockery v2.1.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
state "github.com/tendermint/tendermint/state"
|
||||
|
||||
tendermintstate "github.com/tendermint/tendermint/proto/tendermint/state"
|
||||
|
||||
tenderminttypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
types "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
// Store is an autogenerated mock type for the Store type
|
||||
type Store struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Bootstrap provides a mock function with given fields: _a0
|
||||
func (_m *Store) Bootstrap(_a0 state.State) error {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(state.State) error); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Load provides a mock function with given fields:
|
||||
func (_m *Store) Load() (state.State, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 state.State
|
||||
if rf, ok := ret.Get(0).(func() state.State); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(state.State)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// LoadABCIResponses provides a mock function with given fields: _a0
|
||||
func (_m *Store) LoadABCIResponses(_a0 int64) (*tendermintstate.ABCIResponses, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *tendermintstate.ABCIResponses
|
||||
if rf, ok := ret.Get(0).(func(int64) *tendermintstate.ABCIResponses); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*tendermintstate.ABCIResponses)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(int64) error); ok {
|
||||
r1 = rf(_a0)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// LoadConsensusParams provides a mock function with given fields: _a0
|
||||
func (_m *Store) LoadConsensusParams(_a0 int64) (types.ConsensusParams, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 types.ConsensusParams
|
||||
if rf, ok := ret.Get(0).(func(int64) types.ConsensusParams); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Get(0).(types.ConsensusParams)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(int64) error); ok {
|
||||
r1 = rf(_a0)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// LoadFromDBOrGenesisDoc provides a mock function with given fields: _a0
|
||||
func (_m *Store) LoadFromDBOrGenesisDoc(_a0 *tenderminttypes.GenesisDoc) (state.State, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 state.State
|
||||
if rf, ok := ret.Get(0).(func(*tenderminttypes.GenesisDoc) state.State); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Get(0).(state.State)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*tenderminttypes.GenesisDoc) error); ok {
|
||||
r1 = rf(_a0)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// LoadFromDBOrGenesisFile provides a mock function with given fields: _a0
|
||||
func (_m *Store) LoadFromDBOrGenesisFile(_a0 string) (state.State, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 state.State
|
||||
if rf, ok := ret.Get(0).(func(string) state.State); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Get(0).(state.State)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(_a0)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// LoadValidators provides a mock function with given fields: _a0
|
||||
func (_m *Store) LoadValidators(_a0 int64) (*tenderminttypes.ValidatorSet, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *tenderminttypes.ValidatorSet
|
||||
if rf, ok := ret.Get(0).(func(int64) *tenderminttypes.ValidatorSet); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*tenderminttypes.ValidatorSet)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(int64) error); ok {
|
||||
r1 = rf(_a0)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// PruneStates provides a mock function with given fields: _a0, _a1
|
||||
func (_m *Store) PruneStates(_a0 int64, _a1 int64) error {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(int64, int64) error); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Save provides a mock function with given fields: _a0
|
||||
func (_m *Store) Save(_a0 state.State) error {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(state.State) error); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SaveABCIResponses provides a mock function with given fields: _a0, _a1
|
||||
func (_m *Store) SaveABCIResponses(_a0 int64, _a1 *tendermintstate.ABCIResponses) error {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(int64, *tendermintstate.ABCIResponses) error); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
+20
-10
@@ -1,6 +1,9 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -37,19 +40,26 @@ type BlockStore interface {
|
||||
|
||||
//go:generate mockery --case underscore --name EvidencePool
|
||||
|
||||
// EvidencePool defines the EvidencePool interface used by the ConsensusState.
|
||||
// Get/Set/Commit
|
||||
// EvidencePool defines the EvidencePool interface used by State.
|
||||
type EvidencePool interface {
|
||||
PendingEvidence(uint32) []types.Evidence
|
||||
AddEvidence(types.Evidence) error
|
||||
Update(*types.Block, State)
|
||||
Verify(types.Evidence) error
|
||||
Update(State)
|
||||
CheckEvidence(types.EvidenceList) error
|
||||
ABCIEvidence(int64, []types.Evidence) []abci.Evidence
|
||||
}
|
||||
|
||||
// MockEvidencePool is an empty implementation of EvidencePool, useful for testing.
|
||||
type MockEvidencePool struct{}
|
||||
// EmptyEvidencePool is an empty implementation of EvidencePool, useful for testing. It also complies
|
||||
// to the consensus evidence pool interface
|
||||
type EmptyEvidencePool struct{}
|
||||
|
||||
func (me MockEvidencePool) PendingEvidence(uint32) []types.Evidence { return nil }
|
||||
func (me MockEvidencePool) AddEvidence(types.Evidence) error { return nil }
|
||||
func (me MockEvidencePool) Update(*types.Block, State) {}
|
||||
func (me MockEvidencePool) Verify(types.Evidence) error { return nil }
|
||||
func (EmptyEvidencePool) PendingEvidence(uint32) []types.Evidence { return nil }
|
||||
func (EmptyEvidencePool) AddEvidence(types.Evidence) error { return nil }
|
||||
func (EmptyEvidencePool) Update(State) {}
|
||||
func (EmptyEvidencePool) CheckEvidence(evList types.EvidenceList) error { return nil }
|
||||
func (EmptyEvidencePool) ABCIEvidence(int64, []types.Evidence) []abci.Evidence {
|
||||
return []abci.Evidence{}
|
||||
}
|
||||
func (EmptyEvidencePool) AddEvidenceFromConsensus(types.Evidence, time.Time, *types.ValidatorSet) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -39,6 +39,12 @@ func calcABCIResponsesKey(height int64) []byte {
|
||||
|
||||
//----------------------
|
||||
|
||||
//go:generate mockery --case underscore --name Store
|
||||
|
||||
// Store defines the state store interface
|
||||
//
|
||||
// It is used to retrieve current state and save and load ABCI responses,
|
||||
// validators and consensus parameters
|
||||
type Store interface {
|
||||
// LoadFromDBOrGenesisFile loads the most recent state.
|
||||
// If the chain is new it will use the genesis file from the provided genesis file path as the current state.
|
||||
|
||||
+2
-18
@@ -96,7 +96,7 @@ func validateBlock(evidencePool EvidencePool, state State, block *types.Block) e
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: We can't actually verify it's the right proposer because we dont
|
||||
// NOTE: We can't actually verify it's the right proposer because we don't
|
||||
// know what round the block was first proposed. So just check that it's
|
||||
// a legit address and a known validator.
|
||||
if len(block.ProposerAddress) != crypto.AddressSize {
|
||||
@@ -148,21 +148,5 @@ func validateBlock(evidencePool EvidencePool, state State, block *types.Block) e
|
||||
}
|
||||
|
||||
// Validate all evidence.
|
||||
for idx, ev := range block.Evidence.Evidence {
|
||||
// Check that no evidence has been submitted more than once
|
||||
for i := idx + 1; i < len(block.Evidence.Evidence); i++ {
|
||||
if ev.Equal(block.Evidence.Evidence[i]) {
|
||||
return types.NewErrEvidenceInvalid(ev, errors.New("evidence was submitted twice"))
|
||||
}
|
||||
}
|
||||
|
||||
// Verify evidence using the evidence pool
|
||||
err := evidencePool.Verify(ev)
|
||||
if err != nil {
|
||||
return types.NewErrEvidenceInvalid(ev, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
return evidencePool.CheckEvidence(block.Evidence.Evidence)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
@@ -21,8 +22,6 @@ import (
|
||||
|
||||
const validationTestsStopHeight int64 = 10
|
||||
|
||||
var defaultTestTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
func TestValidateBlockHeader(t *testing.T) {
|
||||
proxyApp := newTestApp()
|
||||
require.NoError(t, proxyApp.Start())
|
||||
@@ -35,7 +34,7 @@ func TestValidateBlockHeader(t *testing.T) {
|
||||
log.TestingLogger(),
|
||||
proxyApp.Consensus(),
|
||||
memmock.Mempool{},
|
||||
sm.MockEvidencePool{},
|
||||
sm.EmptyEvidencePool{},
|
||||
)
|
||||
lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil)
|
||||
|
||||
@@ -106,7 +105,7 @@ func TestValidateBlockCommit(t *testing.T) {
|
||||
log.TestingLogger(),
|
||||
proxyApp.Consensus(),
|
||||
memmock.Mempool{},
|
||||
sm.MockEvidencePool{},
|
||||
sm.EmptyEvidencePool{},
|
||||
)
|
||||
lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil)
|
||||
wrongSigsCommit := types.NewCommit(1, 0, types.BlockID{}, nil)
|
||||
@@ -218,8 +217,10 @@ func TestValidateBlockEvidence(t *testing.T) {
|
||||
defaultEvidenceTime := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
evpool := &mocks.EvidencePool{}
|
||||
evpool.On("Verify", mock.AnythingOfType("*types.DuplicateVoteEvidence")).Return(nil)
|
||||
evpool.On("Update", mock.AnythingOfType("*types.Block"), mock.AnythingOfType("state.State")).Return()
|
||||
evpool.On("CheckEvidence", mock.AnythingOfType("types.EvidenceList")).Return(nil)
|
||||
evpool.On("Update", mock.AnythingOfType("state.State")).Return()
|
||||
evpool.On("ABCIEvidence", mock.AnythingOfType("int64"), mock.AnythingOfType("[]types.Evidence")).Return(
|
||||
[]abci.Evidence{})
|
||||
|
||||
state.ConsensusParams.Evidence.MaxNum = 3
|
||||
blockExec := sm.NewBlockExecutor(
|
||||
@@ -279,28 +280,3 @@ func TestValidateBlockEvidence(t *testing.T) {
|
||||
require.NoError(t, err, "height %d", height)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDuplicateEvidenceShouldFail(t *testing.T) {
|
||||
var height int64 = 1
|
||||
state, stateDB, privVals := makeState(2, int(height))
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
_, val := state.Validators.GetByIndex(0)
|
||||
_, val2 := state.Validators.GetByIndex(1)
|
||||
ev := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultTestTime,
|
||||
privVals[val.Address.String()], chainID)
|
||||
ev2 := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultTestTime,
|
||||
privVals[val2.Address.String()], chainID)
|
||||
|
||||
blockExec := sm.NewBlockExecutor(
|
||||
stateStore, log.TestingLogger(),
|
||||
nil,
|
||||
nil,
|
||||
sm.MockEvidencePool{})
|
||||
// A block with a couple pieces of evidence passes.
|
||||
block := makeBlock(state, height)
|
||||
block.Evidence.Evidence = []types.Evidence{ev, ev2, ev2}
|
||||
block.EvidenceHash = block.Evidence.Hash()
|
||||
err := blockExec.ValidateBlock(state, block)
|
||||
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user