mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-08 14:21:14 +00:00
evidence: structs can independently form abci evidence (#5610)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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{})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user