evidence: change evidence time to block time (#5219)

adds blockstore interface to evidence and adds fix to byzantine test
This commit is contained in:
Callum Waters
2020-08-11 14:39:07 +02:00
committed by GitHub
parent f66b7a8e32
commit 312c4f8fe1
22 changed files with 527 additions and 213 deletions
+28 -21
View File
@@ -94,6 +94,21 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block
}
}
// NOTE: We can't actually verify it's the right proposer because we dont
// 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 {
return fmt.Errorf("expected ProposerAddress size %d, got %d",
crypto.AddressSize,
len(block.ProposerAddress),
)
}
if !state.Validators.HasAddress(block.ProposerAddress) {
return fmt.Errorf("block.Header.ProposerAddress %X is not a validator",
block.ProposerAddress,
)
}
// Validate block Time
if block.Height > 1 {
if !block.Time.After(state.LastBlockTime) {
@@ -154,12 +169,12 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block
return types.NewErrEvidenceInvalid(ev, errors.New("amnesia evidence is new and hasn't undergone trial period yet"))
}
var header *types.Header
if _, ok := ev.(*types.LunaticValidatorEvidence); ok {
header = evidencePool.Header(ev.Height())
if header == nil {
return fmt.Errorf("don't have block meta at height #%d", ev.Height())
}
// A header needs to be fetched. For lunatic evidence this is so we can verify
// that some of the fields are different to the ones we have. For all evidence it
// it so we can verify that the time of the evidence is correct
header := evidencePool.Header(ev.Height())
if header == nil {
return fmt.Errorf("don't have block meta at height #%d", ev.Height())
}
if err := VerifyEvidence(stateDB, state, ev, header); err != nil {
@@ -168,21 +183,6 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block
}
// NOTE: We can't actually verify it's the right proposer because we dont
// 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 {
return fmt.Errorf("expected ProposerAddress size %d, got %d",
crypto.AddressSize,
len(block.ProposerAddress),
)
}
if !state.Validators.HasAddress(block.ProposerAddress) {
return fmt.Errorf("block.Header.ProposerAddress %X is not a validator",
block.ProposerAddress,
)
}
return nil
}
@@ -200,6 +200,13 @@ func VerifyEvidence(stateDB dbm.DB, state State, evidence types.Evidence, commit
ageNumBlocks = height - evidence.Height()
)
if committedHeader.Time != evidence.Time() {
return fmt.Errorf("evidence time (%v) is different to the time of the header we have for the same height (%v)",
evidence.Time(),
committedHeader.Time,
)
}
if ageDuration > evidenceParams.MaxAgeDuration && ageNumBlocks > evidenceParams.MaxAgeNumBlocks {
return fmt.Errorf(
"evidence from height %d (created at: %v) is too old; min height is %d and evidence can not be older than %v",
+64 -33
View File
@@ -5,6 +5,7 @@ import (
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
@@ -216,20 +217,32 @@ func TestValidateBlockEvidence(t *testing.T) {
defer proxyApp.Stop() //nolint:errcheck // ignore for tests
state, stateDB, privVals := makeState(4, 1)
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("Update", mock.AnythingOfType("*types.Block"), mock.AnythingOfType("state.State")).Return()
state.ConsensusParams.Evidence.MaxNum = 3
blockExec := sm.NewBlockExecutor(
stateDB,
log.TestingLogger(),
proxyApp.Consensus(),
memmock.Mempool{},
sm.MockEvidencePool{},
evpool,
)
lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil)
for height := int64(1); height < validationTestsStopHeight; height++ {
proposerAddr := state.Validators.GetProposer().Address
maxNumEvidence := state.ConsensusParams.Evidence.MaxNum
t.Log(maxNumEvidence)
if height > 1 {
/*
A block with too much evidence fails
@@ -243,8 +256,10 @@ func TestValidateBlockEvidence(t *testing.T) {
}
block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, evidence, proposerAddr)
err := blockExec.ValidateBlock(state, block)
_, ok := err.(*types.ErrEvidenceOverflow)
require.True(t, ok, "expected error to be of type ErrEvidenceOverflow at height %d", height)
if assert.Error(t, err) {
_, ok := err.(*types.ErrEvidenceOverflow)
require.True(t, ok, "expected error to be of type ErrEvidenceOverflow at height %d but got %v", height, err)
}
}
/*
@@ -256,7 +271,7 @@ func TestValidateBlockEvidence(t *testing.T) {
for i := int32(0); uint32(i) < maxNumEvidence; i++ {
// make different evidence for each validator
_, val := state.Validators.GetByIndex(i)
evidence = append(evidence, types.NewMockDuplicateVoteEvidenceWithValidator(height, time.Now(),
evidence = append(evidence, types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultEvidenceTime,
privVals[val.Address.String()], chainID))
}
@@ -284,11 +299,14 @@ func TestValidateFailBlockOnCommittedEvidence(t *testing.T) {
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(),
@@ -314,12 +332,14 @@ func TestValidateAlreadyPendingEvidence(t *testing.T) {
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(),
@@ -430,30 +450,30 @@ func TestValidatePrimedAmnesiaEvidence(t *testing.T) {
state, stateDB, vals := makeState(1, int(height))
addr, val := state.Validators.GetByIndex(0)
voteA := makeVote(height, 1, 0, addr, blockID)
voteA.Timestamp = time.Now().Add(1 * time.Minute)
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.PotentialAmnesiaEvidence{
VoteA: voteB,
VoteB: voteA,
}
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(),
@@ -475,11 +495,19 @@ func TestVerifyEvidenceWrongAddress(t *testing.T) {
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,
sm.MockEvidencePool{})
evpool,
)
// A block with a couple pieces of evidence passes.
block := makeBlock(state, height)
block.Evidence.Evidence = []types.Evidence{ev}
@@ -496,13 +524,26 @@ func TestVerifyEvidenceExpiredEvidence(t *testing.T) {
state, stateDB, _ := makeState(1, int(height))
state.ConsensusParams.Evidence.MaxAgeNumBlocks = 1
ev := types.NewMockDuplicateVoteEvidence(1, defaultTestTime, chainID)
err := sm.VerifyEvidence(stateDB, state, ev, nil)
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))
@@ -518,6 +559,9 @@ func TestVerifyEvidenceWithAmnesiaEvidence(t *testing.T) {
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)
@@ -525,16 +569,13 @@ func TestVerifyEvidenceWithAmnesiaEvidence(t *testing.T) {
require.NoError(t, err)
//var ae types.Evidence
badAe := &types.AmnesiaEvidence{
PotentialAmnesiaEvidence: &types.PotentialAmnesiaEvidence{
VoteA: voteA,
VoteB: voteB,
},
PotentialAmnesiaEvidence: pae,
Polc: &types.ProofOfLockChange{
Votes: []*types.Vote{voteC},
PubKey: val.PubKey,
},
}
err = sm.VerifyEvidence(stateDB, state, badAe, nil)
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")
@@ -553,26 +594,20 @@ func TestVerifyEvidenceWithAmnesiaEvidence(t *testing.T) {
require.NoError(t, err)
goodAe := &types.AmnesiaEvidence{
PotentialAmnesiaEvidence: &types.PotentialAmnesiaEvidence{
VoteA: voteA,
VoteB: voteB,
},
PotentialAmnesiaEvidence: pae,
Polc: &types.ProofOfLockChange{
Votes: []*types.Vote{voteC, voteD, voteE},
PubKey: val.PubKey,
},
}
err = sm.VerifyEvidence(stateDB, state, goodAe, nil)
err = sm.VerifyEvidence(stateDB, state, goodAe, &types.Header{Time: defaultTestTime})
assert.NoError(t, err)
goodAe = &types.AmnesiaEvidence{
PotentialAmnesiaEvidence: &types.PotentialAmnesiaEvidence{
VoteA: voteA,
VoteB: voteB,
},
Polc: types.NewEmptyPOLC(),
PotentialAmnesiaEvidence: pae,
Polc: types.NewEmptyPOLC(),
}
err = sm.VerifyEvidence(stateDB, state, goodAe, nil)
err = sm.VerifyEvidence(stateDB, state, goodAe, &types.Header{Time: defaultTestTime})
assert.NoError(t, err)
}
@@ -608,11 +643,7 @@ func TestVerifyEvidenceWithLunaticValidatorEvidence(t *testing.T) {
err := vals[val.Address.String()].SignVote(chainID, v)
vote.Signature = v.Signature
require.NoError(t, err)
ev := &types.LunaticValidatorEvidence{
Header: h,
Vote: vote,
InvalidHeaderField: "ConsensusHash",
}
ev := types.NewLunaticValidatorEvidence(h, vote, "ConsensusHash", defaultTestTime)
err = ev.ValidateBasic()
require.NoError(t, err)
err = sm.VerifyEvidence(stateDB, state, ev, h)