evidence: adr56 form amnesia evidence (#4821)

Creates Amnesia Evidence which is formed from Potential Amnesia Evidence with either a matching proof or after a period of time denoted as the Amnesia Trial Period. This also adds the code necessary so that Amnesia Evidence can be validated and committed on a block
This commit is contained in:
Callum Waters
2020-06-10 13:53:55 +02:00
committed by GitHub
parent 65909a13d5
commit 5697e144a7
19 changed files with 1658 additions and 276 deletions

View File

@@ -1,16 +1,22 @@
package state_test
import (
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/libs/bytes"
"github.com/tendermint/tendermint/proto/version"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/libs/log"
memmock "github.com/tendermint/tendermint/mempool/mock"
protostate "github.com/tendermint/tendermint/proto/state"
tmproto "github.com/tendermint/tendermint/proto/types"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/state/mocks"
@@ -285,8 +291,8 @@ func TestValidateFailBlockOnCommittedEvidence(t *testing.T) {
block.EvidenceHash = block.Evidence.Hash()
err := blockExec.ValidateBlock(state, block)
require.Error(t, err)
require.IsType(t, err, &types.ErrEvidenceInvalid{})
assert.Error(t, err)
assert.IsType(t, err, &types.ErrEvidenceInvalid{})
}
func TestValidateAlreadyPendingEvidence(t *testing.T) {
@@ -314,7 +320,7 @@ func TestValidateAlreadyPendingEvidence(t *testing.T) {
block.EvidenceHash = block.Evidence.Hash()
err := blockExec.ValidateBlock(state, block)
require.NoError(t, err)
assert.NoError(t, err)
}
func TestValidateDuplicateEvidenceShouldFail(t *testing.T) {
@@ -336,5 +342,274 @@ func TestValidateDuplicateEvidenceShouldFail(t *testing.T) {
block.EvidenceHash = block.Evidence.Hash()
err := blockExec.ValidateBlock(state, block)
require.Error(t, err)
assert.Error(t, err)
}
var blockID = types.BlockID{
Hash: []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
PartsHeader: types.PartSetHeader{
Total: 1,
Hash: []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
},
}
func TestValidateAmnesiaEvidence(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)
err := vals[val.Address.String()].SignVote(chainID, voteA)
require.NoError(t, err)
voteB := makeVote(height, 2, 0, addr, types.BlockID{})
err = vals[val.Address.String()].SignVote(chainID, voteB)
require.NoError(t, err)
ae := types.AmnesiaEvidence{
PotentialAmnesiaEvidence: types.PotentialAmnesiaEvidence{
VoteA: voteA,
VoteB: voteB,
},
Polc: types.EmptyPOLC(),
}
evpool := &mocks.EvidencePool{}
evpool.On("IsPending", ae).Return(false)
evpool.On("IsCommitted", ae).Return(false)
evpool.On("AddEvidence", ae).Return(fmt.Errorf("test error"))
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)
errMsg := "Invalid evidence: unknown amnesia evidence, trying to add to evidence pool, err: test error"
if assert.Error(t, err) {
assert.Equal(t, err.Error()[:len(errMsg)], errMsg)
}
}
func TestVerifyEvidenceWrongAddress(t *testing.T) {
var height int64 = 1
state, stateDB, _ := makeState(1, int(height))
randomAddr := []byte("wrong address")
ev := types.NewMockEvidence(height, defaultTestTime, randomAddr)
blockExec := sm.NewBlockExecutor(
stateDB, 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}
block.EvidenceHash = block.Evidence.Hash()
err := blockExec.ValidateBlock(state, block)
errMsg := "Invalid evidence: address 77726F6E672061646472657373 was not a validator at height 1"
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
addr, _ := state.Validators.GetByIndex(0)
ev := types.NewMockEvidence(1, defaultTestTime, addr)
err := sm.VerifyEvidence(stateDB, state, ev, nil)
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 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{})
err := vals[val.Address.String()].SignVote(chainID, voteA)
require.NoError(t, err)
voteB := makeVote(height, 2, 0, addr, blockID)
err = vals[val.Address.String()].SignVote(chainID, voteB)
require.NoError(t, err)
voteC := makeVote(height, 2, 1, addr2, blockID)
err = vals[val2.Address.String()].SignVote(chainID, voteC)
require.NoError(t, err)
//var ae types.Evidence
badAe := types.AmnesiaEvidence{
PotentialAmnesiaEvidence: types.PotentialAmnesiaEvidence{
VoteA: voteA,
VoteB: voteB,
},
Polc: types.ProofOfLockChange{
Votes: []types.Vote{*voteC},
PubKey: val.PubKey,
},
}
err = sm.VerifyEvidence(stateDB, state, badAe, nil)
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)
err = vals[val3.Address.String()].SignVote(chainID, voteD)
require.NoError(t, err)
addr4, val4 := state.Validators.GetByIndex(3)
voteE := makeVote(height, 2, 3, addr4, blockID)
err = vals[val4.Address.String()].SignVote(chainID, voteE)
require.NoError(t, err)
goodAe := types.AmnesiaEvidence{
PotentialAmnesiaEvidence: types.PotentialAmnesiaEvidence{
VoteA: voteA,
VoteB: voteB,
},
Polc: types.ProofOfLockChange{
Votes: []types.Vote{*voteC, *voteD, *voteE},
PubKey: val.PubKey,
},
}
err = sm.VerifyEvidence(stateDB, state, goodAe, nil)
assert.NoError(t, err)
goodAe = types.AmnesiaEvidence{
PotentialAmnesiaEvidence: types.PotentialAmnesiaEvidence{
VoteA: voteA,
VoteB: voteB,
},
Polc: types.EmptyPOLC(),
}
err = sm.VerifyEvidence(stateDB, state, goodAe, nil)
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, blockID)
err := vals[val.Address.String()].SignVote(chainID, vote)
require.NoError(t, err)
ev := types.LunaticValidatorEvidence{
Header: h,
Vote: vote,
InvalidHeaderField: "ConsensusHash",
}
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 TestVerifyEvidenceWithPhantomValidatorEvidence(t *testing.T) {
state, stateDB, vals := makeState(4, 4)
state.ConsensusParams.Evidence.MaxAgeNumBlocks = 1
addr, val := state.Validators.GetByIndex(0)
vote := makeVote(3, 1, 0, addr, blockID)
err := vals[val.Address.String()].SignVote(chainID, vote)
require.NoError(t, err)
ev := types.PhantomValidatorEvidence{
Vote: vote,
LastHeightValidatorWasInSet: 1,
}
err = ev.ValidateBasic()
require.NoError(t, err)
err = sm.VerifyEvidence(stateDB, state, ev, nil)
if assert.Error(t, err) {
assert.Equal(t, "address 576585A00DD4D58318255611D8AAC60E8E77CB32 was a validator at height 3", err.Error())
}
privVal := types.NewMockPV()
pubKey, _ := privVal.GetPubKey()
vote2 := makeVote(3, 1, 0, pubKey.Address(), blockID)
err = privVal.SignVote(chainID, vote2)
require.NoError(t, err)
ev = types.PhantomValidatorEvidence{
Vote: vote2,
LastHeightValidatorWasInSet: 1,
}
err = ev.ValidateBasic()
assert.NoError(t, err)
err = sm.VerifyEvidence(stateDB, state, ev, nil)
if assert.Error(t, err) {
assert.Equal(t, "last time validator was in the set at height 1, min: 2", err.Error())
}
ev = types.PhantomValidatorEvidence{
Vote: vote2,
LastHeightValidatorWasInSet: 2,
}
err = ev.ValidateBasic()
assert.NoError(t, err)
err = sm.VerifyEvidence(stateDB, state, ev, nil)
errMsg := "phantom validator"
if assert.Error(t, err) {
assert.Equal(t, errMsg, err.Error()[:len(errMsg)])
}
vals2, err := sm.LoadValidators(stateDB, 2)
require.NoError(t, err)
vals2.Validators = append(vals2.Validators, types.NewValidator(pubKey, 1000))
valKey := []byte("validatorsKey:2")
protoVals, err := vals2.ToProto()
require.NoError(t, err)
valInfo := &protostate.ValidatorsInfo{
LastHeightChanged: 2,
ValidatorSet: protoVals,
}
bz, err := valInfo.Marshal()
require.NoError(t, err)
stateDB.Set(valKey, bz)
ev = types.PhantomValidatorEvidence{
Vote: vote2,
LastHeightValidatorWasInSet: 2,
}
err = ev.ValidateBasic()
assert.NoError(t, err)
err = sm.VerifyEvidence(stateDB, state, ev, nil)
if !assert.NoError(t, err) {
t.Log(err)
}
}
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,
}
}