evidence: structs can independently form abci evidence (#5610)

This commit is contained in:
Callum Waters
2020-11-04 17:14:48 +01:00
parent 70a62be5c6
commit 9d354c842e
35 changed files with 1532 additions and 1917 deletions

View File

@@ -115,7 +115,11 @@ func (blockExec *BlockExecutor) CreateProposalBlock(
// Validation does not mutate state, but does require historical information from the stateDB,
// ie. to verify evidence from a validator at an old height.
func (blockExec *BlockExecutor) ValidateBlock(state State, block *types.Block) error {
return validateBlock(blockExec.evpool, state, block)
err := validateBlock(state, block)
if err != nil {
return err
}
return blockExec.evpool.CheckEvidence(block.Evidence.Evidence)
}
// ApplyBlock validates the block against the state, executes it against the app,
@@ -128,16 +132,13 @@ func (blockExec *BlockExecutor) ApplyBlock(
state State, blockID types.BlockID, block *types.Block,
) (State, int64, error) {
if err := blockExec.ValidateBlock(state, block); err != nil {
if err := validateBlock(state, block); err != nil {
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, byzVals)
blockExec.store, state.InitialHeight)
endTime := time.Now().UnixNano()
blockExec.metrics.BlockProcessingTime.Observe(float64(endTime-startTime) / 1000000)
if err != nil {
@@ -180,7 +181,7 @@ func (blockExec *BlockExecutor) ApplyBlock(
}
// Update evpool with the latest state.
blockExec.evpool.Update(state)
blockExec.evpool.Update(state, block.Evidence.Evidence)
fail.Fail() // XXX
@@ -262,7 +263,6 @@ func execBlockOnProxyApp(
block *types.Block,
store Store,
initialHeight int64,
byzVals []abci.Evidence,
) (*tmstate.ABCIResponses, error) {
var validTxs, invalidTxs = 0, 0
@@ -292,6 +292,11 @@ func execBlockOnProxyApp(
commitInfo := getBeginBlockValidatorInfo(block, store, initialHeight)
byzVals := make([]abci.Evidence, 0)
for _, evidence := range block.Evidence.Evidence {
byzVals = append(byzVals, evidence.ABCI()...)
}
// Begin block
var err error
pbh := block.Header.ToProto()
@@ -526,7 +531,7 @@ func ExecCommitBlock(
store Store,
initialHeight int64,
) ([]byte, error) {
_, err := execBlockOnProxyApp(logger, appConnConsensus, block, store, initialHeight, []abci.Evidence{})
_, err := execBlockOnProxyApp(logger, appConnConsensus, block, store, initialHeight)
if err != nil {
logger.Error("Error executing block on proxy app", "height", block.Height, "err", err)
return nil, err

View File

@@ -10,16 +10,20 @@ import (
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
cryptoenc "github.com/tendermint/tendermint/crypto/encoding"
"github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/libs/log"
mmock "github.com/tendermint/tendermint/mempool/mock"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmversion "github.com/tendermint/tendermint/proto/tendermint/version"
"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"
"github.com/tendermint/tendermint/version"
)
var (
@@ -125,10 +129,52 @@ func TestBeginBlockByzantineValidators(t *testing.T) {
require.Nil(t, err)
defer proxyApp.Stop() //nolint:errcheck // ignore for tests
state, stateDB, _ := makeState(1, 1)
state, stateDB, privVals := makeState(1, 1)
stateStore := sm.NewStore(stateDB)
defaultEvidenceTime := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
privVal := privVals[state.Validators.Validators[0].Address.String()]
blockID := makeBlockID([]byte("headerhash"), 1000, []byte("partshash"))
header := &types.Header{
Version: tmversion.Consensus{Block: version.BlockProtocol, App: 1},
ChainID: state.ChainID,
Height: 10,
Time: defaultEvidenceTime,
LastBlockID: blockID,
LastCommitHash: crypto.CRandBytes(tmhash.Size),
DataHash: crypto.CRandBytes(tmhash.Size),
ValidatorsHash: state.Validators.Hash(),
NextValidatorsHash: state.Validators.Hash(),
ConsensusHash: crypto.CRandBytes(tmhash.Size),
AppHash: crypto.CRandBytes(tmhash.Size),
LastResultsHash: crypto.CRandBytes(tmhash.Size),
EvidenceHash: crypto.CRandBytes(tmhash.Size),
ProposerAddress: crypto.CRandBytes(crypto.AddressSize),
}
// we don't need to worry about validating the evidence as long as they pass validate basic
dve := types.NewMockDuplicateVoteEvidenceWithValidator(3, defaultEvidenceTime, privVal, state.ChainID)
dve.ValidatorPower = 1000
lcae := &types.LightClientAttackEvidence{
ConflictingBlock: &types.LightBlock{
SignedHeader: &types.SignedHeader{
Header: header,
Commit: types.NewCommit(10, 0, makeBlockID(header.Hash(), 100, []byte("partshash")), []types.CommitSig{{
BlockIDFlag: types.BlockIDFlagNil,
ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
Timestamp: defaultEvidenceTime,
Signature: crypto.CRandBytes(types.MaxSignatureSize),
}}),
},
ValidatorSet: state.Validators,
},
CommonHeight: 8,
ByzantineValidators: []*types.Validator{state.Validators.Validators[0]},
TotalVotingPower: 12,
Timestamp: defaultEvidenceTime,
}
ev := []types.Evidence{dve, lcae}
abciEv := []abci.Evidence{
{
@@ -136,7 +182,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) {
Height: 3,
Time: defaultEvidenceTime,
Validator: types.TM2PB.Validator(state.Validators.Validators[0]),
TotalVotingPower: 33,
TotalVotingPower: 10,
},
{
Type: abci.EvidenceType_LIGHT_CLIENT_ATTACK,
@@ -148,15 +194,17 @@ func TestBeginBlockByzantineValidators(t *testing.T) {
}
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("PendingEvidence", mock.AnythingOfType("int64")).Return(ev, int64(100))
evpool.On("Update", mock.AnythingOfType("state.State"), mock.AnythingOfType("types.EvidenceList")).Return()
evpool.On("CheckEvidence", mock.AnythingOfType("types.EvidenceList")).Return(nil)
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(),
mmock.Mempool{}, evpool)
block := makeBlock(state, 1)
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
block.Evidence = types.EvidenceData{Evidence: ev}
block.Header.EvidenceHash = block.Evidence.Hash()
blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
state, retainHeight, err := blockExec.ApplyBlock(state, blockID, block)
require.Nil(t, err)
@@ -400,3 +448,19 @@ func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) {
assert.NotNil(t, err)
assert.NotEmpty(t, state.NextValidators.Validators)
}
func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) types.BlockID {
var (
h = make([]byte, tmhash.Size)
psH = make([]byte, tmhash.Size)
)
copy(h, hash)
copy(psH, partSetHash)
return types.BlockID{
Hash: h,
PartSetHeader: types.PartSetHeader{
Total: partSetSize,
Hash: psH,
},
}
}

View File

@@ -4,8 +4,6 @@ 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"
@@ -16,22 +14,6 @@ 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)
@@ -60,13 +42,13 @@ func (_m *EvidencePool) CheckEvidence(_a0 types.EvidenceList) error {
return r0
}
// PendingEvidence provides a mock function with given fields: _a0
func (_m *EvidencePool) PendingEvidence(_a0 int64) ([]types.Evidence, int64) {
ret := _m.Called(_a0)
// PendingEvidence provides a mock function with given fields: maxBytes
func (_m *EvidencePool) PendingEvidence(maxBytes int64) ([]types.Evidence, int64) {
ret := _m.Called(maxBytes)
var r0 []types.Evidence
if rf, ok := ret.Get(0).(func(int64) []types.Evidence); ok {
r0 = rf(_a0)
r0 = rf(maxBytes)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]types.Evidence)
@@ -75,7 +57,7 @@ func (_m *EvidencePool) PendingEvidence(_a0 int64) ([]types.Evidence, int64) {
var r1 int64
if rf, ok := ret.Get(1).(func(int64) int64); ok {
r1 = rf(_a0)
r1 = rf(maxBytes)
} else {
r1 = ret.Get(1).(int64)
}
@@ -83,7 +65,7 @@ func (_m *EvidencePool) PendingEvidence(_a0 int64) ([]types.Evidence, int64) {
return r0, r1
}
// Update provides a mock function with given fields: _a0
func (_m *EvidencePool) Update(_a0 state.State) {
_m.Called(_a0)
// Update provides a mock function with given fields: _a0, _a1
func (_m *EvidencePool) Update(_a0 state.State, _a1 types.EvidenceList) {
_m.Called(_a0, _a1)
}

View File

@@ -1,9 +1,6 @@
package state
import (
"time"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/types"
)
@@ -45,9 +42,8 @@ type BlockStore interface {
type EvidencePool interface {
PendingEvidence(maxBytes int64) (ev []types.Evidence, size int64)
AddEvidence(types.Evidence) error
Update(State)
Update(State, types.EvidenceList)
CheckEvidence(types.EvidenceList) error
ABCIEvidence(int64, []types.Evidence) []abci.Evidence
}
// EmptyEvidencePool is an empty implementation of EvidencePool, useful for testing. It also complies
@@ -58,11 +54,8 @@ func (EmptyEvidencePool) PendingEvidence(maxBytes int64) (ev []types.Evidence, s
return nil, 0
}
func (EmptyEvidencePool) AddEvidence(types.Evidence) error { return nil }
func (EmptyEvidencePool) Update(State) {}
func (EmptyEvidencePool) Update(State, types.EvidenceList) {}
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 {
func (EmptyEvidencePool) AddEvidenceFromConsensus(evidence types.Evidence) error {
return nil
}

View File

@@ -12,7 +12,7 @@ import (
//-----------------------------------------------------
// Validate block
func validateBlock(evidencePool EvidencePool, state State, block *types.Block) error {
func validateBlock(state State, block *types.Block) error {
// Validate internal consistency.
if err := block.ValidateBasic(); err != nil {
return err
@@ -147,6 +147,5 @@ func validateBlock(evidencePool EvidencePool, state State, block *types.Block) e
return types.NewErrEvidenceOverflow(max, got)
}
// Validate all evidence.
return evidencePool.CheckEvidence(block.Evidence.Evidence)
return nil
}

View File

@@ -218,7 +218,7 @@ func TestValidateBlockEvidence(t *testing.T) {
evpool := &mocks.EvidencePool{}
evpool.On("CheckEvidence", mock.AnythingOfType("types.EvidenceList")).Return(nil)
evpool.On("Update", mock.AnythingOfType("state.State")).Return()
evpool.On("Update", mock.AnythingOfType("state.State"), mock.AnythingOfType("types.EvidenceList")).Return()
evpool.On("ABCIEvidence", mock.AnythingOfType("int64"), mock.AnythingOfType("[]types.Evidence")).Return(
[]abci.Evidence{})