Browse Source

evidence: remove phantom validator evidence (#5181)

pull/5189/head
Callum Waters 4 years ago
committed by GitHub
parent
commit
3c21c3546c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 104 additions and 852 deletions
  1. +6
    -0
      CHANGELOG_PENDING.md
  2. +23
    -18
      docs/architecture/adr-047-handling-evidence-from-light-client.md
  3. +15
    -124
      evidence/pool.go
  4. +0
    -21
      evidence/pool_test.go
  5. +45
    -348
      proto/tendermint/types/evidence.pb.go
  6. +0
    -6
      proto/tendermint/types/evidence.proto
  7. +12
    -42
      state/validation.go
  8. +0
    -80
      state/validation_test.go
  9. +3
    -170
      types/evidence.go
  10. +0
    -40
      types/evidence_test.go
  11. +0
    -3
      types/protobuf.go

+ 6
- 0
CHANGELOG_PENDING.md View File

@ -4,6 +4,12 @@ Special thanks to external contributors on this release:
Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint).
### BREAKING CHANGES
- Go API
- [evidence] [\#5181](https://github.com/tendermint/tendermint/pull/5181) Phantom validator evidence was removed (also from abci) (@cmwaters)
### FEATURES:
- [abci] [\#5174](https://github.com/tendermint/tendermint/pull/5174) Add amnesia evidence and remove mock and potential amnesia evidence from abci (@cmwaters)

+ 23
- 18
docs/architecture/adr-047-handling-evidence-from-light-client.md View File

@ -4,11 +4,12 @@
* 18-02-2020: Initial draft
* 24-02-2020: Second version
* 13-04-2020: Add PotentialAmnesiaEvidence and a few remarks
* 31-07-2020: Remove PhantomValidatorEvidence
## Context
If the light client is under attack, either directly -> lunatic/phantom
validators (light fork) or indirectly -> full fork, it's supposed to halt and
If the light client is under attack, either directly -> lunatic
attack (light fork) or indirectly -> full fork, it's supposed to halt and
send evidence of misbehavior to a correct full node. Upon receiving an
evidence, the full node should punish malicious validators (if possible).
@ -102,21 +103,6 @@ If we'd go with breaking evidence, here are the types we'll need:
Existing `DuplicateVoteEvidence` needs to be created and gossiped.
### F4. Phantom validators
A new type of evidence needs to be created:
```go
type PhantomValidatorEvidence struct {
Header types.Header
Vote types.Vote
LastHeightValidatorWasInSet int64
}
```
It contains a validator's public key and a vote for a block, where this
validator is not part of the validator set. `LastHeightValidatorWasInSet`
indicates the last height validator was in the validator set.
### F5. Lunatic validator
@ -184,7 +170,10 @@ accountability process should then use this evidence to request additional
information from offended validators and construct a new type of evidence to
punish those who conducted an amnesia attack.
See ADR-056 for the architecture of the fork accountability procedure.
See ADR-056 for the architecture of the handling amnesia attacks.
NOTE: Conflicting headers evidence used to also create PhantomValidatorEvidence
but this has since been removed. Refer to Appendix B.
## Status
@ -216,3 +205,19 @@ If there is an actual fork (full fork), a full node may follow either one or
another branch. So both H1 or H2 can be considered committed depending on which
branch the full node is following. It's supposed to halt if it notices an
actual fork, but there's a small chance it doesn't.
## Appendix B
PhantomValidatorEvidence was used to capture when a validator that was still staked
(i.e. within the bonded period) but was not in the current validator set had voted for a block.
In later discussions it was argued that although possible to keep phantom validator
evidence, any case a phantom validator that could have the capacity to be involved
in fooling a light client would have to be aided by 1/3+ lunatic validators.
It would also be very unlikely that the new validators injected by the lunatic attack
would be validators that currently still have something staked.
Not only this but there was a large degree of extra computation required in storing all
the currently staked validators that could possibly fall into the group of being
a phantom validator. Given this, it was removed.

+ 15
- 124
evidence/pool.go View File

@ -39,18 +39,13 @@ type Pool struct {
mtx sync.Mutex
// latest state
state sm.State
// a map of active validators and respective last heights validator is active
// if it was in validator set after EvidenceParams.MaxAgeNumBlocks or
// currently is (ie. [MaxAgeNumBlocks, CurrentHeight])
// In simple words, it means it's still bonded -> therefore slashable.
valToLastHeight valToLastHeightMap
// This is the closest height where at one or more of the current trial periods
// will have ended and we will need to then upgrade the evidence to amnesia evidence.
// It is set to -1 when we don't have any evidence on trial.
nextEvidenceTrialEndedHeight int64
}
// Validator.Address -> Last height it was in validator set
type valToLastHeightMap map[string]int64
// Creates a new pool. If using an existing evidence store, it will add all pending evidence
// to the concurrent list.
func NewPool(stateDB, evidenceDB dbm.DB, blockStore *store.BlockStore) (*Pool, error) {
@ -58,11 +53,6 @@ func NewPool(stateDB, evidenceDB dbm.DB, blockStore *store.BlockStore) (*Pool, e
state = sm.LoadState(stateDB)
)
valToLastHeight, err := buildValToLastHeightMap(state, stateDB, blockStore)
if err != nil {
return nil, err
}
pool := &Pool{
stateDB: stateDB,
blockStore: blockStore,
@ -70,7 +60,6 @@ func NewPool(stateDB, evidenceDB dbm.DB, blockStore *store.BlockStore) (*Pool, e
logger: log.NewNopLogger(),
evidenceStore: evidenceDB,
evidenceList: clist.New(),
valToLastHeight: valToLastHeight,
nextEvidenceTrialEndedHeight: -1,
}
@ -104,8 +93,8 @@ func (evpool *Pool) AllPendingEvidence() []types.Evidence {
return evidence
}
// Update uses the latest block & state to update its copy of the state,
// validator to last height map and calls MarkEvidenceAsCommitted.
// Update uses the latest block & state to update any evidence that has been committed, to prune all expired evidence
// and to check if any trial period of potential amnesia evidence has finished.
func (evpool *Pool) Update(block *types.Block, state sm.State) {
// sanity check
if state.LastBlockHeight != block.Height {
@ -126,13 +115,14 @@ func (evpool *Pool) Update(block *types.Block, state sm.State) {
if block.Height%state.ConsensusParams.Evidence.MaxAgeNumBlocks == 0 {
evpool.logger.Debug("Pruning no longer necessary evidence")
evpool.pruneExpiredPOLC()
// NOTE: As this is periodic, this implies that there may be some pending evidence in the
// db that have already expired. However, expired evidence will also be removed whenever
// PendingEvidence() is called ensuring that no expired evidence is proposed.
evpool.removeExpiredPendingEvidence()
}
evpool.updateValToLastHeight(block.Height, state)
if evpool.nextEvidenceTrialEndedHeight > 0 && block.Height > evpool.nextEvidenceTrialEndedHeight {
evpool.logger.Debug("Upgrading all potential evidence that have served the trial period")
evpool.logger.Debug("Upgrading all potential amnesia evidence that have served the trial period")
evpool.nextEvidenceTrialEndedHeight = evpool.upgradePotentialAmnesiaEvidence()
}
}
@ -181,15 +171,7 @@ func (evpool *Pool) AddEvidence(evidence types.Evidence) error {
return err
}
// XXX: Copy here since this should be a rare case.
evpool.mtx.Lock()
valToLastHeightCopy := make(valToLastHeightMap, len(evpool.valToLastHeight))
for k, v := range evpool.valToLastHeight {
valToLastHeightCopy[k] = v
}
evpool.mtx.Unlock()
evList = ce.Split(&blockMeta.Header, valSet, valToLastHeightCopy)
evList = ce.Split(&blockMeta.Header, valSet)
}
for _, ev := range evList {
@ -224,6 +206,8 @@ func (evpool *Pool) AddEvidence(evidence types.Evidence) error {
}
continue
} else if ae, ok := ev.(*types.AmnesiaEvidence); ok {
// we have received an new amnesia evidence that we have never seen before so we must extract out the
// potential amnesia evidence part and run our own trial
if ae.Polc.IsAbsent() && ae.PotentialAmnesiaEvidence.VoteA.Round <
ae.PotentialAmnesiaEvidence.VoteB.Round {
if err := evpool.handleInboundPotentialAmnesiaEvidence(ae.PotentialAmnesiaEvidence); err != nil {
@ -231,8 +215,9 @@ func (evpool *Pool) AddEvidence(evidence types.Evidence) error {
}
continue
} else {
// we are going to add this amnesia evidence and check if we already have an amnesia evidence or potential
// amnesia evidence that addesses the same case
// we are going to add this amnesia evidence as it's already punishable.
// We also check if we already have an amnesia evidence or potential
// amnesia evidence that addesses the same case that we will need to remove
aeWithoutPolc := types.NewAmnesiaEvidence(ae.PotentialAmnesiaEvidence, types.NewEmptyPOLC())
if evpool.IsPending(aeWithoutPolc) {
evpool.removePendingEvidence(aeWithoutPolc)
@ -404,21 +389,6 @@ func (evpool *Pool) Header(height int64) *types.Header {
return &blockMeta.Header
}
// ValidatorLastHeight returns the last height of the validator w/ the
// given address. 0 - if address never was a validator or was such a
// long time ago (> ConsensusParams.Evidence.MaxAgeDuration && >
// ConsensusParams.Evidence.MaxAgeNumBlocks).
func (evpool *Pool) ValidatorLastHeight(address []byte) int64 {
evpool.mtx.Lock()
defer evpool.mtx.Unlock()
h, ok := evpool.valToLastHeight[string(address)]
if !ok {
return 0
}
return h
}
// State returns the current state of the evpool.
func (evpool *Pool) State() sm.State {
evpool.mtx.Lock()
@ -711,85 +681,6 @@ func evMapKey(ev types.Evidence) string {
return string(ev.Hash())
}
func (evpool *Pool) updateValToLastHeight(blockHeight int64, state sm.State) {
evpool.mtx.Lock()
defer evpool.mtx.Unlock()
// Update current validators & add new ones.
for _, val := range state.Validators.Validators {
evpool.valToLastHeight[string(val.Address)] = blockHeight
}
// Remove validators outside of MaxAgeNumBlocks & MaxAgeDuration.
removeHeight := blockHeight - state.ConsensusParams.Evidence.MaxAgeNumBlocks
if removeHeight >= 1 {
for val, height := range evpool.valToLastHeight {
if height <= removeHeight {
delete(evpool.valToLastHeight, val)
}
}
}
}
func buildValToLastHeightMap(state sm.State, stateDB dbm.DB, blockStore *store.BlockStore) (valToLastHeightMap, error) {
var (
valToLastHeight = make(map[string]int64)
params = state.ConsensusParams.Evidence
numBlocks = int64(0)
minAgeTime = time.Now().Add(-params.MaxAgeDuration)
height = state.LastBlockHeight
)
if height == 0 {
return valToLastHeight, nil
}
meta := blockStore.LoadBlockMeta(height)
if meta == nil {
return nil, fmt.Errorf("block meta for height %d not found", height)
}
blockTime := meta.Header.Time
// From state.LastBlockHeight, build a map of "active" validators until
// MaxAgeNumBlocks is passed and block time is less than now() -
// MaxAgeDuration.
for height >= 1 && (numBlocks <= params.MaxAgeNumBlocks || !blockTime.Before(minAgeTime)) {
valSet, err := sm.LoadValidators(stateDB, height)
if err != nil {
// last stored height -> return
if _, ok := err.(sm.ErrNoValSetForHeight); ok {
return valToLastHeight, nil
}
return nil, fmt.Errorf("validator set for height %d not found", height)
}
for _, val := range valSet.Validators {
key := string(val.Address)
if _, ok := valToLastHeight[key]; !ok {
valToLastHeight[key] = height
}
}
height--
if height > 0 {
// NOTE: we assume here blockStore and state.Validators are in sync. I.e if
// block N is stored, then validators for height N are also stored in
// state.
meta := blockStore.LoadBlockMeta(height)
if meta == nil {
return nil, fmt.Errorf("block meta for height %d not found", height)
}
blockTime = meta.Header.Time
}
numBlocks++
}
return valToLastHeight, nil
}
// big endian padded hex
func bE(h int64) string {
return fmt.Sprintf("%0.16X", h)


+ 0
- 21
evidence/pool_test.go View File

@ -182,27 +182,6 @@ func TestEvidencePoolUpdate(t *testing.T) {
// a) Update marks evidence as committed
assert.True(t, pool.IsCommitted(evidence))
// b) Update updates valToLastHeight map
assert.Equal(t, height+1, pool.ValidatorLastHeight(valAddr))
}
func TestEvidencePoolNewPool(t *testing.T) {
var (
val = types.NewMockPV()
valAddr = val.PrivKey.PubKey().Address()
height = int64(1)
stateDB = initializeValidatorState(val, height)
evidenceDB = dbm.NewMemDB()
blockStoreDB = dbm.NewMemDB()
state = sm.LoadState(stateDB)
blockStore = initializeBlockStore(blockStoreDB, state, valAddr)
)
pool, err := NewPool(stateDB, evidenceDB, blockStore)
require.NoError(t, err)
assert.Equal(t, height, pool.ValidatorLastHeight(valAddr))
assert.EqualValues(t, 0, pool.ValidatorLastHeight([]byte("non-existent-validator")))
}
func TestAddingAndPruningPOLC(t *testing.T) {


+ 45
- 348
proto/tendermint/types/evidence.pb.go View File

@ -302,58 +302,6 @@ func (m *LunaticValidatorEvidence) GetInvalidHeaderField() string {
return ""
}
type PhantomValidatorEvidence struct {
Vote *Vote `protobuf:"bytes,1,opt,name=vote,proto3" json:"vote,omitempty"`
LastHeightValidatorWasInSet int64 `protobuf:"varint,2,opt,name=last_height_validator_was_in_set,json=lastHeightValidatorWasInSet,proto3" json:"last_height_validator_was_in_set,omitempty"`
}
func (m *PhantomValidatorEvidence) Reset() { *m = PhantomValidatorEvidence{} }
func (m *PhantomValidatorEvidence) String() string { return proto.CompactTextString(m) }
func (*PhantomValidatorEvidence) ProtoMessage() {}
func (*PhantomValidatorEvidence) Descriptor() ([]byte, []int) {
return fileDescriptor_6825fabc78e0a168, []int{5}
}
func (m *PhantomValidatorEvidence) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *PhantomValidatorEvidence) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_PhantomValidatorEvidence.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *PhantomValidatorEvidence) XXX_Merge(src proto.Message) {
xxx_messageInfo_PhantomValidatorEvidence.Merge(m, src)
}
func (m *PhantomValidatorEvidence) XXX_Size() int {
return m.Size()
}
func (m *PhantomValidatorEvidence) XXX_DiscardUnknown() {
xxx_messageInfo_PhantomValidatorEvidence.DiscardUnknown(m)
}
var xxx_messageInfo_PhantomValidatorEvidence proto.InternalMessageInfo
func (m *PhantomValidatorEvidence) GetVote() *Vote {
if m != nil {
return m.Vote
}
return nil
}
func (m *PhantomValidatorEvidence) GetLastHeightValidatorWasInSet() int64 {
if m != nil {
return m.LastHeightValidatorWasInSet
}
return 0
}
type Evidence struct {
// Types that are valid to be assigned to Sum:
// *Evidence_DuplicateVoteEvidence
@ -361,7 +309,6 @@ type Evidence struct {
// *Evidence_LunaticValidatorEvidence
// *Evidence_PotentialAmnesiaEvidence
// *Evidence_AmnesiaEvidence
// *Evidence_PhantomValidatorEvidence
Sum isEvidence_Sum `protobuf_oneof:"sum"`
}
@ -369,7 +316,7 @@ func (m *Evidence) Reset() { *m = Evidence{} }
func (m *Evidence) String() string { return proto.CompactTextString(m) }
func (*Evidence) ProtoMessage() {}
func (*Evidence) Descriptor() ([]byte, []int) {
return fileDescriptor_6825fabc78e0a168, []int{6}
return fileDescriptor_6825fabc78e0a168, []int{5}
}
func (m *Evidence) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -419,16 +366,12 @@ type Evidence_PotentialAmnesiaEvidence struct {
type Evidence_AmnesiaEvidence struct {
AmnesiaEvidence *AmnesiaEvidence `protobuf:"bytes,5,opt,name=amnesia_evidence,json=amnesiaEvidence,proto3,oneof" json:"amnesia_evidence,omitempty"`
}
type Evidence_PhantomValidatorEvidence struct {
PhantomValidatorEvidence *PhantomValidatorEvidence `protobuf:"bytes,6,opt,name=phantom_validator_evidence,json=phantomValidatorEvidence,proto3,oneof" json:"phantom_validator_evidence,omitempty"`
}
func (*Evidence_DuplicateVoteEvidence) isEvidence_Sum() {}
func (*Evidence_ConflictingHeadersEvidence) isEvidence_Sum() {}
func (*Evidence_LunaticValidatorEvidence) isEvidence_Sum() {}
func (*Evidence_PotentialAmnesiaEvidence) isEvidence_Sum() {}
func (*Evidence_AmnesiaEvidence) isEvidence_Sum() {}
func (*Evidence_PhantomValidatorEvidence) isEvidence_Sum() {}
func (m *Evidence) GetSum() isEvidence_Sum {
if m != nil {
@ -472,13 +415,6 @@ func (m *Evidence) GetAmnesiaEvidence() *AmnesiaEvidence {
return nil
}
func (m *Evidence) GetPhantomValidatorEvidence() *PhantomValidatorEvidence {
if x, ok := m.GetSum().(*Evidence_PhantomValidatorEvidence); ok {
return x.PhantomValidatorEvidence
}
return nil
}
// XXX_OneofWrappers is for the internal use of the proto package.
func (*Evidence) XXX_OneofWrappers() []interface{} {
return []interface{}{
@ -487,7 +423,6 @@ func (*Evidence) XXX_OneofWrappers() []interface{} {
(*Evidence_LunaticValidatorEvidence)(nil),
(*Evidence_PotentialAmnesiaEvidence)(nil),
(*Evidence_AmnesiaEvidence)(nil),
(*Evidence_PhantomValidatorEvidence)(nil),
}
}
@ -501,7 +436,7 @@ func (m *EvidenceData) Reset() { *m = EvidenceData{} }
func (m *EvidenceData) String() string { return proto.CompactTextString(m) }
func (*EvidenceData) ProtoMessage() {}
func (*EvidenceData) Descriptor() ([]byte, []int) {
return fileDescriptor_6825fabc78e0a168, []int{7}
return fileDescriptor_6825fabc78e0a168, []int{6}
}
func (m *EvidenceData) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -553,7 +488,7 @@ func (m *ProofOfLockChange) Reset() { *m = ProofOfLockChange{} }
func (m *ProofOfLockChange) String() string { return proto.CompactTextString(m) }
func (*ProofOfLockChange) ProtoMessage() {}
func (*ProofOfLockChange) Descriptor() ([]byte, []int) {
return fileDescriptor_6825fabc78e0a168, []int{8}
return fileDescriptor_6825fabc78e0a168, []int{7}
}
func (m *ProofOfLockChange) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -602,7 +537,6 @@ func init() {
proto.RegisterType((*AmnesiaEvidence)(nil), "tendermint.types.AmnesiaEvidence")
proto.RegisterType((*ConflictingHeadersEvidence)(nil), "tendermint.types.ConflictingHeadersEvidence")
proto.RegisterType((*LunaticValidatorEvidence)(nil), "tendermint.types.LunaticValidatorEvidence")
proto.RegisterType((*PhantomValidatorEvidence)(nil), "tendermint.types.PhantomValidatorEvidence")
proto.RegisterType((*Evidence)(nil), "tendermint.types.Evidence")
proto.RegisterType((*EvidenceData)(nil), "tendermint.types.EvidenceData")
proto.RegisterType((*ProofOfLockChange)(nil), "tendermint.types.ProofOfLockChange")
@ -611,53 +545,48 @@ func init() {
func init() { proto.RegisterFile("tendermint/types/evidence.proto", fileDescriptor_6825fabc78e0a168) }
var fileDescriptor_6825fabc78e0a168 = []byte{
// 728 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0xcd, 0x4e, 0xdb, 0x4a,
0x18, 0x8d, 0x49, 0xc8, 0xe5, 0x7e, 0x20, 0xc1, 0xb5, 0xe0, 0xd6, 0x4a, 0x91, 0x01, 0x77, 0x51,
0x84, 0xa8, 0x03, 0xa9, 0x2a, 0x36, 0xdd, 0xf0, 0x57, 0xa5, 0x02, 0xb5, 0xd4, 0x48, 0x54, 0xea,
0xc6, 0x9d, 0xd8, 0x13, 0x7b, 0xc0, 0x99, 0xb1, 0xe2, 0x71, 0xda, 0x48, 0x7d, 0x85, 0x4a, 0xdd,
0xf7, 0x0d, 0xba, 0xee, 0x43, 0xb0, 0x64, 0xd9, 0x55, 0x55, 0xc1, 0x5b, 0x74, 0x55, 0x79, 0x3c,
0x71, 0xa2, 0x38, 0x86, 0x76, 0xd3, 0x8d, 0x65, 0xcd, 0x77, 0xe6, 0x9c, 0x33, 0x33, 0xe7, 0x9b,
0x81, 0x15, 0x8e, 0xa9, 0x8b, 0xbb, 0x1d, 0x42, 0x79, 0x9d, 0xf7, 0x43, 0x1c, 0xd5, 0x71, 0x8f,
0xb8, 0x98, 0x3a, 0xd8, 0x0c, 0xbb, 0x8c, 0x33, 0x75, 0x61, 0x08, 0x30, 0x05, 0xa0, 0xb6, 0xe8,
0x31, 0x8f, 0x89, 0x62, 0x3d, 0xf9, 0x4b, 0x71, 0xb5, 0xe5, 0x1c, 0x91, 0xf8, 0x4e, 0xa8, 0x3a,
0xdd, 0x7e, 0xc8, 0x59, 0xfd, 0x02, 0xf7, 0x65, 0xd5, 0x88, 0x61, 0xe9, 0x20, 0x0e, 0x03, 0xe2,
0x20, 0x8e, 0xcf, 0x18, 0xc7, 0x87, 0xd2, 0x82, 0xfa, 0x08, 0xaa, 0x3d, 0xc6, 0xb1, 0x8d, 0x34,
0x65, 0x55, 0x59, 0x9f, 0x6d, 0xfc, 0x6f, 0x8e, 0xbb, 0x31, 0x13, 0xbc, 0x35, 0x9d, 0xa0, 0x76,
0x33, 0x78, 0x4b, 0x9b, 0xba, 0x1b, 0xbe, 0x67, 0x7c, 0x56, 0x40, 0x3b, 0x61, 0x1c, 0x53, 0x4e,
0x50, 0xb0, 0xdb, 0xa1, 0x38, 0x22, 0xe8, 0xef, 0x48, 0xab, 0x6b, 0x30, 0xe7, 0x63, 0xe2, 0xf9,
0xdc, 0x8e, 0x38, 0xea, 0x84, 0x5a, 0x79, 0x55, 0x59, 0x2f, 0x5b, 0xb3, 0xe9, 0xd8, 0x69, 0x32,
0x64, 0x7c, 0x55, 0x60, 0x7e, 0xdc, 0x94, 0x0f, 0xb5, 0x70, 0x60, 0xd8, 0x46, 0x69, 0xd1, 0x1e,
0x1c, 0x98, 0x34, 0xba, 0x91, 0x57, 0x2e, 0x5a, 0xa4, 0xa5, 0x85, 0x45, 0xcb, 0xdf, 0x81, 0x4a,
0xc8, 0x02, 0x47, 0xae, 0xe6, 0xc1, 0x04, 0xce, 0x2e, 0x63, 0xed, 0x97, 0xed, 0x63, 0xe6, 0x5c,
0xec, 0xfb, 0x88, 0x7a, 0xd8, 0x12, 0x13, 0x8c, 0x0f, 0x50, 0xdb, 0x67, 0xb4, 0x1d, 0x10, 0x87,
0x13, 0xea, 0x35, 0x31, 0x72, 0x71, 0x37, 0xca, 0x68, 0x4d, 0x98, 0xf2, 0xb7, 0xa5, 0x51, 0x3d,
0x4f, 0x7a, 0x4a, 0x3c, 0x8a, 0xdd, 0x74, 0x92, 0x35, 0xe5, 0x6f, 0x0b, 0x7c, 0x43, 0x9a, 0xb8,
0x1b, 0xdf, 0x30, 0xbe, 0x28, 0xa0, 0x1d, 0xc7, 0x14, 0x71, 0xe2, 0x9c, 0xa1, 0x80, 0xb8, 0x88,
0xb3, 0x6e, 0x26, 0xbe, 0x05, 0x55, 0x5f, 0x40, 0xa5, 0x01, 0x2d, 0x4f, 0x28, 0xa9, 0x24, 0x4e,
0xdd, 0x80, 0x4a, 0x72, 0x5e, 0x77, 0x9c, 0xa9, 0xc0, 0xa8, 0x5b, 0xb0, 0x48, 0x68, 0x2f, 0x11,
0xb5, 0xd3, 0xd9, 0x76, 0x9b, 0xe0, 0xc0, 0x15, 0x47, 0xfb, 0xaf, 0xa5, 0xca, 0x5a, 0x2a, 0xf0,
0x2c, 0xa9, 0x18, 0x1f, 0x93, 0xfc, 0xf9, 0x88, 0x72, 0xd6, 0xc9, 0x9b, 0x1d, 0x48, 0x2b, 0xbf,
0x21, 0x7d, 0x08, 0xab, 0x01, 0x8a, 0xb8, 0x2d, 0x23, 0xd5, 0x1b, 0x90, 0xd9, 0xef, 0x50, 0x64,
0x13, 0x6a, 0x47, 0x98, 0x8b, 0x25, 0x94, 0xad, 0xfb, 0x09, 0xae, 0x29, 0x60, 0x99, 0xe4, 0x6b,
0x14, 0x3d, 0xa7, 0xa7, 0x98, 0x1b, 0x3f, 0x2b, 0x30, 0x93, 0xe9, 0x23, 0xb8, 0xe7, 0x0e, 0x7a,
0xd2, 0x16, 0xd1, 0x1e, 0xcb, 0xd9, 0xc3, 0xbc, 0xa5, 0x89, 0x4d, 0xdc, 0x2c, 0x59, 0x4b, 0xee,
0xc4, 0xee, 0x0e, 0x61, 0xd9, 0x19, 0x46, 0x45, 0xee, 0x5a, 0x34, 0xd4, 0x49, 0x77, 0x7d, 0x33,
0xaf, 0x53, 0x1c, 0xb0, 0x66, 0xc9, 0xaa, 0x39, 0xc5, 0xf1, 0x3b, 0x87, 0x5a, 0x90, 0xa6, 0x63,
0x64, 0x93, 0x32, 0xbd, 0x72, 0x51, 0xff, 0x14, 0x25, 0xaa, 0x59, 0xb2, 0xb4, 0xa0, 0x28, 0x6d,
0xe7, 0xb7, 0xf6, 0x6a, 0xe5, 0x4f, 0x7b, 0x35, 0xd1, 0x2a, 0xec, 0xd6, 0x17, 0xb0, 0x90, 0x53,
0x98, 0x16, 0x0a, 0x6b, 0x79, 0x85, 0x3c, 0xf1, 0x3c, 0x1a, 0xe3, 0x4b, 0xbc, 0xa7, 0xc1, 0x9c,
0xb4, 0x4f, 0xd5, 0x42, 0xef, 0x05, 0x61, 0x16, 0xde, 0x0b, 0x6a, 0x7b, 0xd3, 0x50, 0x8e, 0xe2,
0x8e, 0xf1, 0x16, 0xe6, 0x06, 0x43, 0x07, 0x88, 0x23, 0xf5, 0x29, 0xcc, 0x8c, 0x04, 0xae, 0xbc,
0x3e, 0xdb, 0xa8, 0xe5, 0x05, 0x33, 0x92, 0xca, 0xe5, 0xf7, 0x95, 0x92, 0x95, 0xcd, 0x50, 0x55,
0xa8, 0xf8, 0x28, 0xf2, 0x45, 0x84, 0xe6, 0x2c, 0xf1, 0x6f, 0xbc, 0x87, 0xff, 0x72, 0x97, 0x96,
0xba, 0x09, 0xe2, 0x46, 0x8e, 0xa4, 0xc6, 0xad, 0xd7, 0x76, 0xa4, 0x3e, 0x81, 0x7f, 0xc2, 0xb8,
0x65, 0x5f, 0xe0, 0xbe, 0x0c, 0xe7, 0xf2, 0x28, 0x3e, 0x7d, 0xd8, 0xcc, 0x93, 0xb8, 0x15, 0x10,
0xe7, 0x08, 0xf7, 0xad, 0x6a, 0x18, 0xb7, 0x8e, 0x70, 0x7f, 0xef, 0xd5, 0xe5, 0xb5, 0xae, 0x5c,
0x5d, 0xeb, 0xca, 0x8f, 0x6b, 0x5d, 0xf9, 0x74, 0xa3, 0x97, 0xae, 0x6e, 0xf4, 0xd2, 0xb7, 0x1b,
0xbd, 0xf4, 0x66, 0xc7, 0x23, 0xdc, 0x8f, 0x5b, 0xa6, 0xc3, 0x3a, 0xf5, 0xd1, 0x07, 0x74, 0xf8,
0x9b, 0x3e, 0xb4, 0xe3, 0x8f, 0x6b, 0xab, 0x2a, 0xc6, 0x1f, 0xff, 0x0a, 0x00, 0x00, 0xff, 0xff,
0xdc, 0x62, 0x93, 0xc8, 0xc0, 0x07, 0x00, 0x00,
// 656 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0x4f, 0x4f, 0xd4, 0x40,
0x1c, 0x6d, 0xd9, 0x65, 0xc5, 0x1f, 0x24, 0x60, 0x03, 0xda, 0x34, 0xa4, 0x40, 0x3d, 0x48, 0x08,
0x76, 0x61, 0x8d, 0xe1, 0xe2, 0x85, 0x3f, 0x9a, 0x4d, 0x20, 0x8a, 0x43, 0xc2, 0xc1, 0x4b, 0x9d,
0x6d, 0x67, 0xdb, 0x81, 0x6e, 0xa7, 0xd9, 0x4e, 0x37, 0x6e, 0xe2, 0x67, 0x30, 0xde, 0xfd, 0x06,
0x9e, 0xfd, 0x10, 0x1c, 0x39, 0x7a, 0x32, 0x06, 0xbe, 0x88, 0xe9, 0x74, 0x76, 0x17, 0xb7, 0x5b,
0x36, 0x5e, 0xbc, 0x34, 0xcd, 0xef, 0xf7, 0xe6, 0xbd, 0x37, 0x79, 0xf3, 0x9b, 0x81, 0x35, 0x4e,
0x22, 0x8f, 0x74, 0x3b, 0x34, 0xe2, 0x75, 0xde, 0x8f, 0x49, 0x52, 0x27, 0x3d, 0xea, 0x91, 0xc8,
0x25, 0x76, 0xdc, 0x65, 0x9c, 0x69, 0x4b, 0x23, 0x80, 0x2d, 0x00, 0xc6, 0xb2, 0xcf, 0x7c, 0x26,
0x9a, 0xf5, 0xec, 0x2f, 0xc7, 0x19, 0xab, 0x05, 0x22, 0xf1, 0x9d, 0xd0, 0x75, 0xbb, 0xfd, 0x98,
0xb3, 0xfa, 0x25, 0xe9, 0xcb, 0xae, 0x95, 0xc2, 0xca, 0x51, 0x1a, 0x87, 0xd4, 0xc5, 0x9c, 0x9c,
0x33, 0x4e, 0x5e, 0x4b, 0x0b, 0xda, 0x73, 0xa8, 0xf5, 0x18, 0x27, 0x0e, 0xd6, 0xd5, 0x75, 0x75,
0x73, 0xbe, 0xf1, 0xd8, 0x1e, 0x77, 0x63, 0x67, 0x78, 0x34, 0x9b, 0xa1, 0xf6, 0x87, 0xf0, 0x96,
0x3e, 0x33, 0x1d, 0x7e, 0x60, 0x7d, 0x53, 0x41, 0x3f, 0x65, 0x9c, 0x44, 0x9c, 0xe2, 0x70, 0xbf,
0x13, 0x91, 0x84, 0xe2, 0xff, 0x23, 0xad, 0x6d, 0xc0, 0x42, 0x40, 0xa8, 0x1f, 0x70, 0x27, 0xe1,
0xb8, 0x13, 0xeb, 0x95, 0x75, 0x75, 0xb3, 0x82, 0xe6, 0xf3, 0xda, 0x59, 0x56, 0xb2, 0x7e, 0xa8,
0xb0, 0x38, 0x6e, 0x2a, 0x00, 0x23, 0x1e, 0x18, 0x76, 0x70, 0xde, 0x74, 0x06, 0x81, 0x49, 0xa3,
0x5b, 0x45, 0xe5, 0xb2, 0x4d, 0x22, 0x3d, 0x2e, 0xdb, 0xfe, 0x1e, 0x54, 0x63, 0x16, 0xba, 0x72,
0x37, 0x4f, 0x27, 0x70, 0x76, 0x19, 0x6b, 0xbf, 0x6b, 0x9f, 0x30, 0xf7, 0xf2, 0x30, 0xc0, 0x91,
0x4f, 0x90, 0x58, 0x60, 0x7d, 0x06, 0xe3, 0x90, 0x45, 0xed, 0x90, 0xba, 0x9c, 0x46, 0x7e, 0x93,
0x60, 0x8f, 0x74, 0x93, 0x21, 0xad, 0x0d, 0x33, 0xc1, 0xae, 0x34, 0x6a, 0x16, 0x49, 0xcf, 0xa8,
0x1f, 0x11, 0x2f, 0x5f, 0x84, 0x66, 0x82, 0x5d, 0x81, 0x6f, 0x48, 0x13, 0xd3, 0xf1, 0x0d, 0xeb,
0xbb, 0x0a, 0xfa, 0x49, 0x1a, 0x61, 0x4e, 0xdd, 0x73, 0x1c, 0x52, 0x0f, 0x73, 0xd6, 0x1d, 0x8a,
0xef, 0x40, 0x2d, 0x10, 0x50, 0x69, 0x40, 0x2f, 0x12, 0x4a, 0x2a, 0x89, 0xd3, 0xb6, 0xa0, 0x9a,
0xe5, 0x35, 0x25, 0x53, 0x81, 0xd1, 0x76, 0x60, 0x99, 0x46, 0xbd, 0x4c, 0xd4, 0xc9, 0x57, 0x3b,
0x6d, 0x4a, 0x42, 0x4f, 0x44, 0xfb, 0x10, 0x69, 0xb2, 0x97, 0x0b, 0xbc, 0xc9, 0x3a, 0xd6, 0x97,
0x2a, 0xcc, 0x0d, 0xcd, 0x61, 0x78, 0xe2, 0x0d, 0x66, 0xc0, 0x11, 0x47, 0x69, 0x2c, 0xd7, 0x67,
0x45, 0xf5, 0x89, 0x43, 0xd3, 0x54, 0xd0, 0x8a, 0x37, 0x71, 0x9a, 0x62, 0x58, 0x75, 0x47, 0xd1,
0x48, 0x97, 0xc9, 0x48, 0x27, 0xdf, 0xe5, 0x76, 0x51, 0xa7, 0x3c, 0xd0, 0xa6, 0x82, 0x0c, 0xb7,
0x3c, 0xee, 0x0b, 0x30, 0xc2, 0x3c, 0x0d, 0xa7, 0x37, 0x88, 0x63, 0xa4, 0x57, 0x29, 0x3b, 0xaf,
0x65, 0x09, 0x36, 0x15, 0xa4, 0x87, 0x65, 0xe9, 0x5e, 0xdc, 0x3b, 0x1b, 0xd5, 0x7f, 0x9d, 0x8d,
0x4c, 0xab, 0x74, 0x3a, 0xde, 0xc2, 0x52, 0x41, 0x61, 0x56, 0x28, 0x6c, 0x14, 0x15, 0x8a, 0xc4,
0x8b, 0xf8, 0xef, 0xd2, 0xc1, 0x2c, 0x54, 0x92, 0xb4, 0x63, 0x7d, 0x84, 0x85, 0x41, 0xe9, 0x08,
0x73, 0xac, 0xbd, 0x82, 0xb9, 0x3b, 0x87, 0xa0, 0xb2, 0x39, 0xdf, 0x30, 0x8a, 0xf4, 0x43, 0x92,
0xea, 0xd5, 0xaf, 0x35, 0x05, 0x0d, 0x57, 0x68, 0x1a, 0x54, 0x03, 0x9c, 0x04, 0x22, 0xd6, 0x05,
0x24, 0xfe, 0xad, 0x4f, 0xf0, 0xa8, 0x30, 0xb8, 0xda, 0x36, 0x88, 0x5b, 0x29, 0x91, 0x1a, 0xf7,
0x5e, 0x5d, 0x89, 0xf6, 0x12, 0x1e, 0xc4, 0x69, 0xcb, 0xb9, 0x24, 0x7d, 0x79, 0x60, 0x56, 0xef,
0xe2, 0xf3, 0xcb, 0xdd, 0x3e, 0x4d, 0x5b, 0x21, 0x75, 0x8f, 0x49, 0x1f, 0xd5, 0xe2, 0xb4, 0x75,
0x4c, 0xfa, 0x07, 0xef, 0xaf, 0x6e, 0x4c, 0xf5, 0xfa, 0xc6, 0x54, 0x7f, 0xdf, 0x98, 0xea, 0xd7,
0x5b, 0x53, 0xb9, 0xbe, 0x35, 0x95, 0x9f, 0xb7, 0xa6, 0xf2, 0x61, 0xcf, 0xa7, 0x3c, 0x48, 0x5b,
0xb6, 0xcb, 0x3a, 0xf5, 0xbb, 0x8f, 0xc8, 0xe8, 0x37, 0x7f, 0x6c, 0xc6, 0x1f, 0x98, 0x56, 0x4d,
0xd4, 0x5f, 0xfc, 0x09, 0x00, 0x00, 0xff, 0xff, 0xb8, 0xe6, 0x16, 0x08, 0xc4, 0x06, 0x00, 0x00,
}
func (m *DuplicateVoteEvidence) Marshal() (dAtA []byte, err error) {
@ -907,46 +836,6 @@ func (m *LunaticValidatorEvidence) MarshalToSizedBuffer(dAtA []byte) (int, error
return len(dAtA) - i, nil
}
func (m *PhantomValidatorEvidence) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *PhantomValidatorEvidence) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *PhantomValidatorEvidence) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.LastHeightValidatorWasInSet != 0 {
i = encodeVarintEvidence(dAtA, i, uint64(m.LastHeightValidatorWasInSet))
i--
dAtA[i] = 0x10
}
if m.Vote != nil {
{
size, err := m.Vote.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintEvidence(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *Evidence) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@ -1084,27 +973,6 @@ func (m *Evidence_AmnesiaEvidence) MarshalToSizedBuffer(dAtA []byte) (int, error
}
return len(dAtA) - i, nil
}
func (m *Evidence_PhantomValidatorEvidence) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Evidence_PhantomValidatorEvidence) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
if m.PhantomValidatorEvidence != nil {
{
size, err := m.PhantomValidatorEvidence.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintEvidence(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x32
}
return len(dAtA) - i, nil
}
func (m *EvidenceData) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@ -1301,22 +1169,6 @@ func (m *LunaticValidatorEvidence) Size() (n int) {
return n
}
func (m *PhantomValidatorEvidence) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Vote != nil {
l = m.Vote.Size()
n += 1 + l + sovEvidence(uint64(l))
}
if m.LastHeightValidatorWasInSet != 0 {
n += 1 + sovEvidence(uint64(m.LastHeightValidatorWasInSet))
}
return n
}
func (m *Evidence) Size() (n int) {
if m == nil {
return 0
@ -1389,18 +1241,6 @@ func (m *Evidence_AmnesiaEvidence) Size() (n int) {
}
return n
}
func (m *Evidence_PhantomValidatorEvidence) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.PhantomValidatorEvidence != nil {
l = m.PhantomValidatorEvidence.Size()
n += 1 + l + sovEvidence(uint64(l))
}
return n
}
func (m *EvidenceData) Size() (n int) {
if m == nil {
return 0
@ -2121,114 +1961,6 @@ func (m *LunaticValidatorEvidence) Unmarshal(dAtA []byte) error {
}
return nil
}
func (m *PhantomValidatorEvidence) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEvidence
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: PhantomValidatorEvidence: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: PhantomValidatorEvidence: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Vote", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEvidence
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthEvidence
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthEvidence
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Vote == nil {
m.Vote = &Vote{}
}
if err := m.Vote.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field LastHeightValidatorWasInSet", wireType)
}
m.LastHeightValidatorWasInSet = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEvidence
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.LastHeightValidatorWasInSet |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipEvidence(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthEvidence
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthEvidence
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *Evidence) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
@ -2433,41 +2165,6 @@ func (m *Evidence) Unmarshal(dAtA []byte) error {
}
m.Sum = &Evidence_AmnesiaEvidence{v}
iNdEx = postIndex
case 6:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field PhantomValidatorEvidence", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEvidence
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthEvidence
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthEvidence
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
v := &PhantomValidatorEvidence{}
if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
m.Sum = &Evidence_PhantomValidatorEvidence{v}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipEvidence(dAtA[iNdEx:])


+ 0
- 6
proto/tendermint/types/evidence.proto View File

@ -37,11 +37,6 @@ message LunaticValidatorEvidence {
string invalid_header_field = 3;
}
message PhantomValidatorEvidence {
Vote vote = 1;
int64 last_height_validator_was_in_set = 2;
}
message Evidence {
oneof sum {
DuplicateVoteEvidence duplicate_vote_evidence = 1;
@ -49,7 +44,6 @@ message Evidence {
LunaticValidatorEvidence lunatic_validator_evidence = 3;
PotentialAmnesiaEvidence potential_amnesia_evidence = 4;
AmnesiaEvidence amnesia_evidence = 5;
PhantomValidatorEvidence phantom_validator_evidence = 6;
}
}


+ 12
- 42
state/validation.go View File

@ -225,51 +225,21 @@ func VerifyEvidence(stateDB dbm.DB, state State, evidence types.Evidence, commit
addr := evidence.Address()
var val *types.Validator
// For PhantomValidatorEvidence, check evidence.Address was not part of the
// validator set at height evidence.Height, but was a validator before OR
// after.
if phve, ok := evidence.(*types.PhantomValidatorEvidence); ok {
// confirm that it hasn't been forged
_, val = valset.GetByAddress(addr)
if val != nil {
return fmt.Errorf("address %X was a validator at height %d", addr, evidence.Height())
}
// check if last height validator was in the validator set is within
// MaxAgeNumBlocks.
if height-phve.LastHeightValidatorWasInSet > evidenceParams.MaxAgeNumBlocks {
return fmt.Errorf("last time validator was in the set at height %d, min: %d",
phve.LastHeightValidatorWasInSet, height-phve.LastHeightValidatorWasInSet)
}
valset, err := LoadValidators(stateDB, phve.LastHeightValidatorWasInSet)
if err != nil {
// TODO: if err is just that we cant find it cuz we pruned, ignore.
// TODO: if its actually bad evidence, punish peer
return err
}
_, val = valset.GetByAddress(addr)
if val == nil {
return fmt.Errorf("phantom validator %X not found", addr)
}
} else {
if ae, ok := evidence.(*types.AmnesiaEvidence); ok {
// check the validator set against the polc to make sure that a majority of valid votes was reached
if !ae.Polc.IsAbsent() {
err = ae.Polc.ValidateVotes(valset, state.ChainID)
if err != nil {
return fmt.Errorf("amnesia evidence contains invalid polc, err: %w", err)
}
if ae, ok := evidence.(*types.AmnesiaEvidence); ok {
// check the validator set against the polc to make sure that a majority of valid votes was reached
if !ae.Polc.IsAbsent() {
err = ae.Polc.ValidateVotes(valset, state.ChainID)
if err != nil {
return fmt.Errorf("amnesia evidence contains invalid polc, err: %w", err)
}
}
}
// For all other types, expect evidence.Address to be a validator at height
// evidence.Height.
_, val = valset.GetByAddress(addr)
if val == nil {
return fmt.Errorf("address %X was not a validator at height %d", addr, evidence.Height())
}
// For all other types, expect evidence.Address to be a validator at height
// evidence.Height.
_, val = valset.GetByAddress(addr)
if val == nil {
return fmt.Errorf("address %X was not a validator at height %d", addr, evidence.Height())
}
if err := evidence.Verify(state.ChainID, val.PubKey); err != nil {


+ 0
- 80
state/validation_test.go View File

@ -16,7 +16,6 @@ import (
"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/tendermint/state"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/state/mocks"
@ -622,85 +621,6 @@ func TestVerifyEvidenceWithLunaticValidatorEvidence(t *testing.T) {
}
}
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)
v := vote.ToProto()
err := vals[val.Address.String()].SignVote(chainID, v)
vote.Signature = v.Signature
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)
v2 := vote2.ToProto()
err = privVal.SignVote(chainID, v2)
vote2.Signature = v2.Signature
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)
err = stateDB.Set(valKey, bz)
require.NoError(t, err)
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),


+ 3
- 170
types/evidence.go View File

@ -80,7 +80,7 @@ type Evidence interface {
type CompositeEvidence interface {
VerifyComposite(committedHeader *Header, valSet *ValidatorSet) error
Split(committedHeader *Header, valSet *ValidatorSet, valToLastHeight map[string]int64) []Evidence
Split(committedHeader *Header, valSet *ValidatorSet) []Evidence
}
func EvidenceToProto(evidence Evidence) (*tmproto.Evidence, error) {
@ -119,17 +119,6 @@ func EvidenceToProto(evidence Evidence) (*tmproto.Evidence, error) {
return tp, nil
case *PhantomValidatorEvidence:
pbevi := evi.ToProto()
tp := &tmproto.Evidence{
Sum: &tmproto.Evidence_PhantomValidatorEvidence{
PhantomValidatorEvidence: pbevi,
},
}
return tp, nil
case *PotentialAmnesiaEvidence:
pbevi := evi.ToProto()
@ -172,8 +161,6 @@ func EvidenceFromProto(evidence *tmproto.Evidence) (Evidence, error) {
return PotentialAmnesiaEvidenceFromProto(evi.PotentialAmnesiaEvidence)
case *tmproto.Evidence_AmnesiaEvidence:
return AmnesiaEvidenceFromProto(evi.AmnesiaEvidence)
case *tmproto.Evidence_PhantomValidatorEvidence:
return PhantomValidatorEvidenceFromProto(evi.PhantomValidatorEvidence)
default:
return nil, errors.New("evidence is not recognized")
}
@ -182,7 +169,6 @@ func EvidenceFromProto(evidence *tmproto.Evidence) (Evidence, error) {
func init() {
tmjson.RegisterType(&DuplicateVoteEvidence{}, "tendermint/DuplicateVoteEvidence")
tmjson.RegisterType(&ConflictingHeadersEvidence{}, "tendermint/ConflictingHeadersEvidence")
tmjson.RegisterType(&PhantomValidatorEvidence{}, "tendermint/PhantomValidatorEvidence")
tmjson.RegisterType(&LunaticValidatorEvidence{}, "tendermint/LunaticValidatorEvidence")
tmjson.RegisterType(&PotentialAmnesiaEvidence{}, "tendermint/PotentialAmnesiaEvidence")
tmjson.RegisterType(&AmnesiaEvidence{}, "tendermint/AmnesiaEvidence")
@ -417,17 +403,13 @@ func NewConflictingHeadersEvidence(h1, h2 *SignedHeader) *ConflictingHeadersEvid
return &ConflictingHeadersEvidence{H1: h1, H2: h2}
}
// Split breaks up evidence into smaller chunks (one per validator except for
// PotentialAmnesiaEvidence): PhantomValidatorEvidence,
// Split breaks up evidence into smaller chunks of evidence:
// LunaticValidatorEvidence, DuplicateVoteEvidence and
// PotentialAmnesiaEvidence.
//
// committedHeader - header at height H1.Height == H2.Height
// valSet - validator set at height H1.Height == H2.Height
// valToLastHeight - map between active validators and respective last heights
func (ev *ConflictingHeadersEvidence) Split(committedHeader *Header, valSet *ValidatorSet,
valToLastHeight map[string]int64) []Evidence {
func (ev *ConflictingHeadersEvidence) Split(committedHeader *Header, valSet *ValidatorSet) []Evidence {
evList := make([]Evidence, 0)
var alternativeHeader *SignedHeader
@ -437,28 +419,6 @@ func (ev *ConflictingHeadersEvidence) Split(committedHeader *Header, valSet *Val
alternativeHeader = ev.H1
}
// If there are signers(alternativeHeader) that are not part of
// validators(committedHeader), they misbehaved as they are signing protocol
// messages in heights they are not validators => immediately slashable
// (#F4).
for i, sig := range alternativeHeader.Commit.Signatures {
if sig.Absent() {
continue
}
lastHeightValidatorWasInSet, ok := valToLastHeight[string(sig.ValidatorAddress)]
if !ok {
continue
}
if !valSet.HasAddress(sig.ValidatorAddress) {
evList = append(evList, &PhantomValidatorEvidence{
Vote: alternativeHeader.Commit.GetVote(int32(i)),
LastHeightValidatorWasInSet: lastHeightValidatorWasInSet,
})
}
}
// If ValidatorsHash, NextValidatorsHash, ConsensusHash, AppHash, and
// LastResultsHash in alternativeHeader are different (incorrect application
// state transition), then it is a lunatic misbehavior => immediately
@ -707,133 +667,6 @@ func ConflictingHeadersEvidenceFromProto(pb *tmproto.ConflictingHeadersEvidence)
//-------------------------------------------
type PhantomValidatorEvidence struct {
Vote *Vote `json:"vote"`
LastHeightValidatorWasInSet int64 `json:"last_height_validator_was_in_set"`
}
var _ Evidence = &PhantomValidatorEvidence{}
// NewPhantomValidatorEvidence creates a new instance of the respective evidence
func NewPhantomValidatorEvidence(vote *Vote, lastHeightValidatorWasInSet int64) *PhantomValidatorEvidence {
return &PhantomValidatorEvidence{
Vote: vote,
LastHeightValidatorWasInSet: lastHeightValidatorWasInSet,
}
}
func (e *PhantomValidatorEvidence) Height() int64 {
return e.Vote.Height
}
func (e *PhantomValidatorEvidence) Time() time.Time {
return e.Vote.Timestamp
}
func (e *PhantomValidatorEvidence) Address() []byte {
return e.Vote.ValidatorAddress
}
func (e *PhantomValidatorEvidence) Hash() []byte {
pbe := e.ToProto()
bz, err := pbe.Marshal()
if err != nil {
panic(err)
}
return tmhash.Sum(bz)
}
func (e *PhantomValidatorEvidence) Bytes() []byte {
pbe := e.ToProto()
bz, err := pbe.Marshal()
if err != nil {
panic(err)
}
return bz
}
func (e *PhantomValidatorEvidence) Verify(chainID string, pubKey crypto.PubKey) error {
v := e.Vote.ToProto()
if !pubKey.VerifyBytes(VoteSignBytes(chainID, v), e.Vote.Signature) {
return errors.New("invalid signature")
}
return nil
}
func (e *PhantomValidatorEvidence) Equal(ev Evidence) bool {
if e2, ok := ev.(*PhantomValidatorEvidence); ok {
return e.Vote.Height == e2.Vote.Height &&
bytes.Equal(e.Vote.ValidatorAddress, e2.Vote.ValidatorAddress)
}
return false
}
func (e *PhantomValidatorEvidence) ValidateBasic() error {
if e == nil {
return errors.New("empty phantom validator evidence")
}
if e.Vote == nil {
return errors.New("empty vote")
}
if err := e.Vote.ValidateBasic(); err != nil {
return fmt.Errorf("invalid vote: %w", err)
}
if !e.Vote.BlockID.IsComplete() {
return errors.New("expected vote for block")
}
if e.LastHeightValidatorWasInSet <= 0 {
return errors.New("negative or zero LastHeightValidatorWasInSet")
}
return nil
}
func (e *PhantomValidatorEvidence) String() string {
return fmt.Sprintf("PhantomValidatorEvidence{%X voted at height %d}",
e.Vote.ValidatorAddress, e.Vote.Height)
}
func (e *PhantomValidatorEvidence) ToProto() *tmproto.PhantomValidatorEvidence {
vpb := e.Vote.ToProto()
tp := &tmproto.PhantomValidatorEvidence{
Vote: vpb,
LastHeightValidatorWasInSet: e.LastHeightValidatorWasInSet,
}
return tp
}
func PhantomValidatorEvidenceFromProto(pb *tmproto.PhantomValidatorEvidence) (*PhantomValidatorEvidence, error) {
if pb == nil {
return nil, errors.New("nil PhantomValidatorEvidence")
}
vpb, err := VoteFromProto(pb.Vote)
if err != nil {
return nil, err
}
tp := &PhantomValidatorEvidence{
Vote: vpb,
LastHeightValidatorWasInSet: pb.LastHeightValidatorWasInSet,
}
return tp, tp.ValidateBasic()
}
//-------------------------------------------
type LunaticValidatorEvidence struct {
Header *Header `json:"header"`
Vote *Vote `json:"vote"`


+ 0
- 40
types/evidence_test.go View File

@ -118,13 +118,6 @@ func TestMaxEvidenceBytes(t *testing.T) {
// InvalidHeaderField: "",
// }
// evp := &PhantomValidatorEvidence{
// Header: makeHeaderRandom(),
// Vote: makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64, blockID2),
// LastHeightValidatorWasInSet: math.MaxInt64,
// }
// signedHeader := SignedHeader{Header: makeHeaderRandom(), Commit: randCommit(time.Now())}
// evc := &ConflictingHeadersEvidence{
// H1: &signedHeader,
@ -137,7 +130,6 @@ func TestMaxEvidenceBytes(t *testing.T) {
}{
{"DuplicateVote", ev},
// {"LunaticValidatorEvidence", evl},
// {"PhantomValidatorEvidence", evp},
// {"ConflictingHeadersEvidence", evc},
}
@ -270,33 +262,6 @@ func TestLunaticValidatorEvidence(t *testing.T) {
}
func TestPhantomValidatorEvidence(t *testing.T) {
var (
blockID = makeBlockIDRandom()
header = makeHeaderRandom()
val = NewMockPV()
vote = makeVote(t, val, header.ChainID, 0, header.Height, 0, 2, blockID, defaultVoteTime)
)
ev := NewPhantomValidatorEvidence(vote, header.Height-1)
assert.Equal(t, header.Height, ev.Height())
assert.Equal(t, defaultVoteTime, ev.Time())
assert.EqualValues(t, vote.ValidatorAddress, ev.Address())
assert.NotEmpty(t, ev.Hash())
assert.NotEmpty(t, ev.Bytes())
pubKey, err := val.GetPubKey()
require.NoError(t, err)
assert.NoError(t, ev.Verify(header.ChainID, pubKey))
assert.Error(t, ev.Verify("other", pubKey))
privKey2 := ed25519.GenPrivKey()
pubKey2 := privKey2.PubKey()
assert.Error(t, ev.Verify("other", pubKey2))
assert.True(t, ev.Equal(ev))
assert.NoError(t, ev.ValidateBasic())
assert.NotEmpty(t, ev.String())
}
func TestConflictingHeadersEvidence(t *testing.T) {
const (
chainID = "TestConflictingHeadersEvidence"
@ -739,11 +704,6 @@ func TestEvidenceProto(t *testing.T) {
{"PotentialAmnesiaEvidence nil VoteB", &PotentialAmnesiaEvidence{VoteA: v, VoteB: nil}, false, true},
{"PotentialAmnesiaEvidence nil VoteA", &PotentialAmnesiaEvidence{VoteA: nil, VoteB: v2}, false, true},
{"PotentialAmnesiaEvidence success", &PotentialAmnesiaEvidence{VoteA: v2, VoteB: v}, false, false},
{"PhantomValidatorEvidence empty fail", &PhantomValidatorEvidence{}, false, true},
{"PhantomValidatorEvidence nil LastHeightValidatorWasInSet", &PhantomValidatorEvidence{Vote: v}, false, true},
{"PhantomValidatorEvidence nil Vote", &PhantomValidatorEvidence{LastHeightValidatorWasInSet: 2}, false, true},
{"PhantomValidatorEvidence success", &PhantomValidatorEvidence{Vote: v2, LastHeightValidatorWasInSet: 2},
false, false},
{"AmnesiaEvidence nil ProofOfLockChange", &AmnesiaEvidence{PotentialAmnesiaEvidence: &PotentialAmnesiaEvidence{},
Polc: NewEmptyPOLC()}, false, true},
{"AmnesiaEvidence nil Polc",


+ 0
- 3
types/protobuf.go View File

@ -17,7 +17,6 @@ import (
const (
ABCIEvidenceTypeDuplicateVote = "duplicate/vote"
ABCIEvidenceTypePhantom = "phantom"
ABCIEvidenceTypeLunatic = "lunatic"
ABCIEvidenceTypeAmnesia = "amnesia"
)
@ -132,8 +131,6 @@ func (tm2pb) Evidence(ev Evidence, valSet *ValidatorSet, evTime time.Time) abci.
switch ev.(type) {
case *DuplicateVoteEvidence:
evType = ABCIEvidenceTypeDuplicateVote
case *PhantomValidatorEvidence:
evType = ABCIEvidenceTypePhantom
case *LunaticValidatorEvidence:
evType = ABCIEvidenceTypeLunatic
case *AmnesiaEvidence:


Loading…
Cancel
Save