mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-05 13:05:09 +00:00
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:
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user