mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-05 13:05:09 +00:00
evidence: modularise evidence by moving verification function into evidence package (#5234)
This commit is contained in:
@@ -8,11 +8,6 @@ import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/libs/bytes"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
"github.com/tendermint/tendermint/proto/tendermint/version"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
@@ -220,14 +215,7 @@ func TestValidateBlockEvidence(t *testing.T) {
|
||||
defaultEvidenceTime := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
evpool := &mocks.EvidencePool{}
|
||||
evpool.On("IsPending", mock.AnythingOfType("*types.DuplicateVoteEvidence")).Return(false)
|
||||
evpool.On("IsCommitted", mock.AnythingOfType("*types.DuplicateVoteEvidence")).Return(false)
|
||||
evpool.On("Header", mock.AnythingOfType("int64")).Return(func(height int64) *types.Header {
|
||||
return &types.Header{
|
||||
Time: defaultEvidenceTime,
|
||||
Height: height,
|
||||
}
|
||||
})
|
||||
evpool.On("Verify", mock.AnythingOfType("*types.DuplicateVoteEvidence")).Return(nil)
|
||||
evpool.On("Update", mock.AnythingOfType("*types.Block"), mock.AnythingOfType("state.State")).Return()
|
||||
|
||||
state.ConsensusParams.Evidence.MaxNum = 3
|
||||
@@ -289,73 +277,6 @@ func TestValidateBlockEvidence(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateFailBlockOnCommittedEvidence(t *testing.T) {
|
||||
var height int64 = 1
|
||||
state, stateDB, privVals := makeState(2, int(height))
|
||||
_, 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)
|
||||
|
||||
header := &types.Header{Time: defaultTestTime}
|
||||
|
||||
evpool := &mocks.EvidencePool{}
|
||||
evpool.On("IsPending", ev).Return(false)
|
||||
evpool.On("IsPending", ev2).Return(false)
|
||||
evpool.On("IsCommitted", ev).Return(false)
|
||||
evpool.On("IsCommitted", ev2).Return(true)
|
||||
evpool.On("Header", height).Return(header)
|
||||
|
||||
blockExec := sm.NewBlockExecutor(
|
||||
stateDB, log.TestingLogger(),
|
||||
nil,
|
||||
nil,
|
||||
evpool)
|
||||
// A block with a couple pieces of evidence passes.
|
||||
block := makeBlock(state, height)
|
||||
block.Evidence.Evidence = []types.Evidence{ev, ev2}
|
||||
block.EvidenceHash = block.Evidence.Hash()
|
||||
err := blockExec.ValidateBlock(state, block)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, err, &types.ErrEvidenceInvalid{})
|
||||
}
|
||||
|
||||
func TestValidateAlreadyPendingEvidence(t *testing.T) {
|
||||
var height int64 = 1
|
||||
state, stateDB, privVals := makeState(2, int(height))
|
||||
_, 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)
|
||||
header := &types.Header{Time: defaultTestTime}
|
||||
|
||||
evpool := &mocks.EvidencePool{}
|
||||
evpool.On("IsPending", ev).Return(false)
|
||||
evpool.On("IsPending", ev2).Return(true)
|
||||
evpool.On("IsCommitted", ev).Return(false)
|
||||
evpool.On("IsCommitted", ev2).Return(false)
|
||||
evpool.On("Header", height).Return(header)
|
||||
|
||||
blockExec := sm.NewBlockExecutor(
|
||||
stateDB, log.TestingLogger(),
|
||||
nil,
|
||||
nil,
|
||||
evpool)
|
||||
// A block with a couple pieces of evidence passes.
|
||||
block := makeBlock(state, height)
|
||||
// add one evidence seen before and one evidence that hasn't
|
||||
block.Evidence.Evidence = []types.Evidence{ev, ev2}
|
||||
block.EvidenceHash = block.Evidence.Hash()
|
||||
err := blockExec.ValidateBlock(state, block)
|
||||
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestValidateDuplicateEvidenceShouldFail(t *testing.T) {
|
||||
var height int64 = 1
|
||||
state, stateDB, privVals := makeState(2, int(height))
|
||||
@@ -379,287 +300,3 @@ func TestValidateDuplicateEvidenceShouldFail(t *testing.T) {
|
||||
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
var (
|
||||
blockID = types.BlockID{
|
||||
Hash: tmrand.Bytes(tmhash.Size),
|
||||
PartSetHeader: types.PartSetHeader{
|
||||
Total: 1,
|
||||
Hash: tmrand.Bytes(tmhash.Size),
|
||||
},
|
||||
}
|
||||
differentBlockID = types.BlockID{
|
||||
Hash: tmrand.Bytes(tmhash.Size),
|
||||
PartSetHeader: types.PartSetHeader{
|
||||
Total: 1,
|
||||
Hash: tmrand.Bytes(tmhash.Size),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestValidateUnseenAmnesiaEvidence(t *testing.T) {
|
||||
var height int64 = 1
|
||||
state, stateDB, vals := makeState(1, int(height))
|
||||
addr, val := state.Validators.GetByIndex(0)
|
||||
voteA := makeVote(height, 1, 0, addr, blockID)
|
||||
vA := voteA.ToProto()
|
||||
err := vals[val.Address.String()].SignVote(chainID, vA)
|
||||
voteA.Signature = vA.Signature
|
||||
require.NoError(t, err)
|
||||
voteB := makeVote(height, 2, 0, addr, differentBlockID)
|
||||
vB := voteB.ToProto()
|
||||
err = vals[val.Address.String()].SignVote(chainID, vB)
|
||||
voteB.Signature = vB.Signature
|
||||
require.NoError(t, err)
|
||||
pe := &types.PotentialAmnesiaEvidence{
|
||||
VoteA: voteA,
|
||||
VoteB: voteB,
|
||||
}
|
||||
ae := &types.AmnesiaEvidence{
|
||||
PotentialAmnesiaEvidence: pe,
|
||||
Polc: types.NewEmptyPOLC(),
|
||||
}
|
||||
|
||||
evpool := &mocks.EvidencePool{}
|
||||
evpool.On("IsPending", ae).Return(false)
|
||||
evpool.On("IsCommitted", ae).Return(false)
|
||||
evpool.On("AddEvidence", ae).Return(nil)
|
||||
evpool.On("AddEvidence", pe).Return(nil)
|
||||
|
||||
blockExec := sm.NewBlockExecutor(
|
||||
stateDB, log.TestingLogger(),
|
||||
nil,
|
||||
nil,
|
||||
evpool)
|
||||
// A block with a couple pieces of evidence passes.
|
||||
block := makeBlock(state, height)
|
||||
block.Evidence.Evidence = []types.Evidence{ae}
|
||||
block.EvidenceHash = block.Evidence.Hash()
|
||||
err = blockExec.ValidateBlock(state, block)
|
||||
// if we don't have this evidence and it is has an empty polc then we expect to
|
||||
// start our own trial period first
|
||||
errMsg := "Invalid evidence: amnesia evidence is new and hasn't undergone trial period yet."
|
||||
if assert.Error(t, err) {
|
||||
assert.Equal(t, errMsg, err.Error()[:len(errMsg)])
|
||||
}
|
||||
}
|
||||
|
||||
// Amnesia Evidence can be directly approved without needing to undergo the trial period
|
||||
func TestValidatePrimedAmnesiaEvidence(t *testing.T) {
|
||||
var height int64 = 1
|
||||
state, stateDB, vals := makeState(1, int(height))
|
||||
addr, val := state.Validators.GetByIndex(0)
|
||||
voteA := makeVote(height, 1, 0, addr, blockID)
|
||||
voteA.Timestamp = defaultTestTime.Add(1 * time.Minute)
|
||||
vA := voteA.ToProto()
|
||||
err := vals[val.Address.String()].SignVote(chainID, vA)
|
||||
require.NoError(t, err)
|
||||
voteA.Signature = vA.Signature
|
||||
voteB := makeVote(height, 2, 0, addr, differentBlockID)
|
||||
voteB.Timestamp = defaultTestTime
|
||||
vB := voteB.ToProto()
|
||||
err = vals[val.Address.String()].SignVote(chainID, vB)
|
||||
voteB.Signature = vB.Signature
|
||||
require.NoError(t, err)
|
||||
pe := types.NewPotentialAmnesiaEvidence(voteB, voteA, defaultTestTime)
|
||||
ae := &types.AmnesiaEvidence{
|
||||
PotentialAmnesiaEvidence: pe,
|
||||
Polc: types.NewEmptyPOLC(),
|
||||
}
|
||||
header := &types.Header{Time: defaultTestTime}
|
||||
|
||||
evpool := &mocks.EvidencePool{}
|
||||
evpool.On("IsPending", ae).Return(false)
|
||||
evpool.On("IsCommitted", ae).Return(false)
|
||||
evpool.On("AddEvidence", ae).Return(nil)
|
||||
evpool.On("AddEvidence", pe).Return(nil)
|
||||
evpool.On("Header", height).Return(header)
|
||||
|
||||
blockExec := sm.NewBlockExecutor(
|
||||
stateDB, log.TestingLogger(),
|
||||
nil,
|
||||
nil,
|
||||
evpool)
|
||||
// A block with a couple pieces of evidence passes.
|
||||
block := makeBlock(state, height)
|
||||
block.Evidence.Evidence = []types.Evidence{ae}
|
||||
block.EvidenceHash = block.Evidence.Hash()
|
||||
err = blockExec.ValidateBlock(state, block)
|
||||
// No error because this type of amnesia evidence is punishable
|
||||
// without the need of a trial period
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestVerifyEvidenceWrongAddress(t *testing.T) {
|
||||
var height int64 = 1
|
||||
state, stateDB, _ := makeState(1, int(height))
|
||||
ev := types.NewMockDuplicateVoteEvidence(height, defaultTestTime, chainID)
|
||||
|
||||
header := &types.Header{Time: defaultTestTime}
|
||||
|
||||
evpool := &mocks.EvidencePool{}
|
||||
evpool.On("IsPending", ev).Return(false)
|
||||
evpool.On("IsCommitted", ev).Return(false)
|
||||
evpool.On("Header", height).Return(header)
|
||||
|
||||
blockExec := sm.NewBlockExecutor(
|
||||
stateDB, log.TestingLogger(),
|
||||
nil,
|
||||
nil,
|
||||
evpool,
|
||||
)
|
||||
// A block with a couple pieces of evidence passes.
|
||||
block := makeBlock(state, height)
|
||||
block.Evidence.Evidence = []types.Evidence{ev}
|
||||
block.EvidenceHash = block.Evidence.Hash()
|
||||
err := blockExec.ValidateBlock(state, block)
|
||||
errMsg := "Invalid evidence: address "
|
||||
if assert.Error(t, err) {
|
||||
assert.Equal(t, err.Error()[:len(errMsg)], errMsg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyEvidenceExpiredEvidence(t *testing.T) {
|
||||
var height int64 = 4
|
||||
state, stateDB, _ := makeState(1, int(height))
|
||||
state.ConsensusParams.Evidence.MaxAgeNumBlocks = 1
|
||||
ev := types.NewMockDuplicateVoteEvidence(1, defaultTestTime, chainID)
|
||||
err := sm.VerifyEvidence(stateDB, state, ev, &types.Header{Time: defaultTestTime})
|
||||
errMsg := "evidence from height 1 (created at: 2019-01-01 00:00:00 +0000 UTC) is too old"
|
||||
if assert.Error(t, err) {
|
||||
assert.Equal(t, err.Error()[:len(errMsg)], errMsg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyEvidenceInvalidTime(t *testing.T) {
|
||||
height := 4
|
||||
state, stateDB, _ := makeState(1, height)
|
||||
differentTime := time.Date(2019, 2, 1, 0, 0, 0, 0, time.UTC)
|
||||
ev := types.NewMockDuplicateVoteEvidence(int64(height), differentTime, chainID)
|
||||
err := sm.VerifyEvidence(stateDB, state, ev, &types.Header{Time: defaultTestTime})
|
||||
errMsg := "evidence time (2019-02-01 00:00:00 +0000 UTC) is different to the time" +
|
||||
" of the header we have for the same height (2019-01-01 00:00:00 +0000 UTC)"
|
||||
if assert.Error(t, err) {
|
||||
assert.Equal(t, errMsg, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyEvidenceWithAmnesiaEvidence(t *testing.T) {
|
||||
var height int64 = 1
|
||||
state, stateDB, vals := makeState(4, int(height))
|
||||
addr, val := state.Validators.GetByIndex(0)
|
||||
addr2, val2 := state.Validators.GetByIndex(1)
|
||||
voteA := makeVote(height, 1, 0, addr, types.BlockID{})
|
||||
vA := voteA.ToProto()
|
||||
err := vals[val.Address.String()].SignVote(chainID, vA)
|
||||
voteA.Signature = vA.Signature
|
||||
require.NoError(t, err)
|
||||
voteB := makeVote(height, 2, 0, addr, blockID)
|
||||
vB := voteB.ToProto()
|
||||
err = vals[val.Address.String()].SignVote(chainID, vB)
|
||||
voteB.Signature = vB.Signature
|
||||
require.NoError(t, err)
|
||||
|
||||
pae := types.NewPotentialAmnesiaEvidence(voteA, voteB, defaultTestTime)
|
||||
|
||||
voteC := makeVote(height, 2, 1, addr2, blockID)
|
||||
vC := voteC.ToProto()
|
||||
err = vals[val2.Address.String()].SignVote(chainID, vC)
|
||||
voteC.Signature = vC.Signature
|
||||
require.NoError(t, err)
|
||||
//var ae types.Evidence
|
||||
badAe := &types.AmnesiaEvidence{
|
||||
PotentialAmnesiaEvidence: pae,
|
||||
Polc: &types.ProofOfLockChange{
|
||||
Votes: []*types.Vote{voteC},
|
||||
PubKey: val.PubKey,
|
||||
},
|
||||
}
|
||||
err = sm.VerifyEvidence(stateDB, state, badAe, &types.Header{Time: defaultTestTime})
|
||||
if assert.Error(t, err) {
|
||||
assert.Equal(t, err.Error(), "amnesia evidence contains invalid polc, err: "+
|
||||
"invalid commit -- insufficient voting power: got 1000, needed more than 2667")
|
||||
}
|
||||
addr3, val3 := state.Validators.GetByIndex(2)
|
||||
voteD := makeVote(height, 2, 2, addr3, blockID)
|
||||
vD := voteD.ToProto()
|
||||
err = vals[val3.Address.String()].SignVote(chainID, vD)
|
||||
require.NoError(t, err)
|
||||
voteD.Signature = vD.Signature
|
||||
addr4, val4 := state.Validators.GetByIndex(3)
|
||||
voteE := makeVote(height, 2, 3, addr4, blockID)
|
||||
vE := voteE.ToProto()
|
||||
err = vals[val4.Address.String()].SignVote(chainID, vE)
|
||||
voteE.Signature = vE.Signature
|
||||
require.NoError(t, err)
|
||||
|
||||
goodAe := &types.AmnesiaEvidence{
|
||||
PotentialAmnesiaEvidence: pae,
|
||||
Polc: &types.ProofOfLockChange{
|
||||
Votes: []*types.Vote{voteC, voteD, voteE},
|
||||
PubKey: val.PubKey,
|
||||
},
|
||||
}
|
||||
err = sm.VerifyEvidence(stateDB, state, goodAe, &types.Header{Time: defaultTestTime})
|
||||
assert.NoError(t, err)
|
||||
|
||||
goodAe = &types.AmnesiaEvidence{
|
||||
PotentialAmnesiaEvidence: pae,
|
||||
Polc: types.NewEmptyPOLC(),
|
||||
}
|
||||
err = sm.VerifyEvidence(stateDB, state, goodAe, &types.Header{Time: defaultTestTime})
|
||||
assert.NoError(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestVerifyEvidenceWithLunaticValidatorEvidence(t *testing.T) {
|
||||
state, stateDB, vals := makeState(4, 4)
|
||||
state.ConsensusParams.Evidence.MaxAgeNumBlocks = 1
|
||||
addr, val := state.Validators.GetByIndex(0)
|
||||
h := &types.Header{
|
||||
Version: version.Consensus{Block: 1, App: 2},
|
||||
ChainID: chainID,
|
||||
Height: 3,
|
||||
Time: defaultTestTime,
|
||||
LastBlockID: blockID,
|
||||
LastCommitHash: tmhash.Sum([]byte("last_commit_hash")),
|
||||
DataHash: tmhash.Sum([]byte("data_hash")),
|
||||
ValidatorsHash: tmhash.Sum([]byte("validators_hash")),
|
||||
NextValidatorsHash: tmhash.Sum([]byte("next_validators_hash")),
|
||||
ConsensusHash: tmhash.Sum([]byte("consensus_hash")),
|
||||
AppHash: tmhash.Sum([]byte("app_hash")),
|
||||
LastResultsHash: tmhash.Sum([]byte("last_results_hash")),
|
||||
EvidenceHash: tmhash.Sum([]byte("evidence_hash")),
|
||||
ProposerAddress: crypto.AddressHash([]byte("proposer_address")),
|
||||
}
|
||||
vote := makeVote(3, 1, 0, addr, types.BlockID{
|
||||
Hash: h.Hash(),
|
||||
PartSetHeader: types.PartSetHeader{
|
||||
Total: 100,
|
||||
Hash: crypto.CRandBytes(tmhash.Size),
|
||||
},
|
||||
})
|
||||
v := vote.ToProto()
|
||||
err := vals[val.Address.String()].SignVote(chainID, v)
|
||||
vote.Signature = v.Signature
|
||||
require.NoError(t, err)
|
||||
ev := types.NewLunaticValidatorEvidence(h, vote, "ConsensusHash", defaultTestTime)
|
||||
err = ev.ValidateBasic()
|
||||
require.NoError(t, err)
|
||||
err = sm.VerifyEvidence(stateDB, state, ev, h)
|
||||
if assert.Error(t, err) {
|
||||
assert.Equal(t, "ConsensusHash matches committed hash", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func makeVote(height int64, round, index int32, addr bytes.HexBytes, blockID types.BlockID) *types.Vote {
|
||||
return &types.Vote{
|
||||
Type: tmproto.SignedMsgType(2),
|
||||
Height: height,
|
||||
Round: round,
|
||||
BlockID: blockID,
|
||||
Timestamp: time.Now(),
|
||||
ValidatorAddress: addr,
|
||||
ValidatorIndex: index,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user