mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-26 15:22:15 +00:00
evidence: store tests and fixes
This commit is contained in:
parent
c2585b5525
commit
c13e93d63e
@ -25,12 +25,12 @@ Impl:
|
|||||||
|
|
||||||
Schema for indexing evidence (note you need both height and hash to find a piece of evidence):
|
Schema for indexing evidence (note you need both height and hash to find a piece of evidence):
|
||||||
|
|
||||||
"evidence-lookup"/<evidence-height>/<evidence-hash> -> evidenceInfo
|
"evidence-lookup"/<evidence-height>/<evidence-hash> -> EvidenceInfo
|
||||||
"evidence-outqueue"/<priority>/<evidence-height>/<evidence-hash> -> evidenceInfo
|
"evidence-outqueue"/<priority>/<evidence-height>/<evidence-hash> -> EvidenceInfo
|
||||||
"evidence-pending"/<evidence-height>/<evidence-hash> -> evidenceInfo
|
"evidence-pending"/<evidence-height>/<evidence-hash> -> EvidenceInfo
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type evidenceInfo struct {
|
type EvidenceInfo struct {
|
||||||
Committed bool
|
Committed bool
|
||||||
Priority int
|
Priority int
|
||||||
Evidence types.Evidence
|
Evidence types.Evidence
|
||||||
@ -46,16 +46,21 @@ func keyLookup(evidence types.Evidence) []byte {
|
|||||||
return keyLookupFromHeightAndHash(evidence.Height(), evidence.Hash())
|
return keyLookupFromHeightAndHash(evidence.Height(), evidence.Hash())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// big endian padded hex
|
||||||
|
func be(h int) string {
|
||||||
|
return fmt.Sprintf("%0.16X", h)
|
||||||
|
}
|
||||||
|
|
||||||
func keyLookupFromHeightAndHash(height int, hash []byte) []byte {
|
func keyLookupFromHeightAndHash(height int, hash []byte) []byte {
|
||||||
return _key("%s/%d/%X", baseKeyLookup, height, hash)
|
return _key("%s/%s/%X", baseKeyLookup, be(height), hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
func keyOutqueue(evidence types.Evidence, priority int) []byte {
|
func keyOutqueue(evidence types.Evidence, priority int) []byte {
|
||||||
return _key("%s/%d/%d/%X", baseKeyOutqueue, priority, evidence.Height(), evidence.Hash())
|
return _key("%s/%s/%s/%X", baseKeyOutqueue, be(priority), be(evidence.Height()), evidence.Hash())
|
||||||
}
|
}
|
||||||
|
|
||||||
func keyPending(evidence types.Evidence) []byte {
|
func keyPending(evidence types.Evidence) []byte {
|
||||||
return _key("%s/%d/%X", baseKeyPending, evidence.Height(), evidence.Hash())
|
return _key("%s/%s/%X", baseKeyPending, be(evidence.Height()), evidence.Hash())
|
||||||
}
|
}
|
||||||
|
|
||||||
func _key(fmt_ string, o ...interface{}) []byte {
|
func _key(fmt_ string, o ...interface{}) []byte {
|
||||||
@ -77,8 +82,13 @@ func NewEvidenceStore(db dbm.DB) *EvidenceStore {
|
|||||||
|
|
||||||
// PriorityEvidence returns the evidence from the outqueue, sorted by highest priority.
|
// PriorityEvidence returns the evidence from the outqueue, sorted by highest priority.
|
||||||
func (store *EvidenceStore) PriorityEvidence() (evidence []types.Evidence) {
|
func (store *EvidenceStore) PriorityEvidence() (evidence []types.Evidence) {
|
||||||
// TODO: revert order for highest first
|
// reverse the order so highest priority is first
|
||||||
return store.ListEvidence(baseKeyOutqueue)
|
l := store.ListEvidence(baseKeyOutqueue)
|
||||||
|
l2 := make([]types.Evidence, len(l))
|
||||||
|
for i, _ := range l {
|
||||||
|
l2[i] = l[len(l)-1-i]
|
||||||
|
}
|
||||||
|
return l2
|
||||||
}
|
}
|
||||||
|
|
||||||
// PendingEvidence returns all known uncommitted evidence.
|
// PendingEvidence returns all known uncommitted evidence.
|
||||||
@ -93,7 +103,7 @@ func (store *EvidenceStore) ListEvidence(prefixKey string) (evidence []types.Evi
|
|||||||
for iter.Next() {
|
for iter.Next() {
|
||||||
val := iter.Value()
|
val := iter.Value()
|
||||||
|
|
||||||
var ei evidenceInfo
|
var ei EvidenceInfo
|
||||||
wire.ReadBinaryBytes(val, &ei)
|
wire.ReadBinaryBytes(val, &ei)
|
||||||
evidence = append(evidence, ei.Evidence)
|
evidence = append(evidence, ei.Evidence)
|
||||||
}
|
}
|
||||||
@ -101,26 +111,27 @@ func (store *EvidenceStore) ListEvidence(prefixKey string) (evidence []types.Evi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetEvidence fetches the evidence with the given height and hash.
|
// GetEvidence fetches the evidence with the given height and hash.
|
||||||
func (store *EvidenceStore) GetEvidence(height int, hash []byte) types.Evidence {
|
func (store *EvidenceStore) GetEvidence(height int, hash []byte) *EvidenceInfo {
|
||||||
key := keyLookupFromHeightAndHash(height, hash)
|
key := keyLookupFromHeightAndHash(height, hash)
|
||||||
val := store.db.Get(key)
|
val := store.db.Get(key)
|
||||||
|
|
||||||
if len(val) == 0 {
|
if len(val) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var ei evidenceInfo
|
ei := new(EvidenceInfo)
|
||||||
wire.ReadBinaryBytes(val, &ei)
|
wire.ReadBinaryBytes(val, ei)
|
||||||
return ei.Evidence
|
return ei
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddNewEvidence adds the given evidence to the database.
|
// AddNewEvidence adds the given evidence to the database.
|
||||||
func (store *EvidenceStore) AddNewEvidence(evidence types.Evidence, priority int) (bool, error) {
|
func (store *EvidenceStore) AddNewEvidence(evidence types.Evidence, priority int) (bool, error) {
|
||||||
// check if we already have seen it
|
// check if we already have seen it
|
||||||
ev := store.GetEvidence(evidence.Height(), evidence.Hash())
|
ei_ := store.GetEvidence(evidence.Height(), evidence.Hash())
|
||||||
if ev != nil {
|
if ei_ != nil && ei_.Evidence != nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ei := evidenceInfo{
|
ei := EvidenceInfo{
|
||||||
Committed: false,
|
Committed: false,
|
||||||
Priority: priority,
|
Priority: priority,
|
||||||
Evidence: evidence,
|
Evidence: evidence,
|
||||||
@ -152,8 +163,8 @@ func (store *EvidenceStore) MarkEvidenceAsCommitted(evidence types.Evidence) {
|
|||||||
// if its committed, its been broadcast
|
// if its committed, its been broadcast
|
||||||
store.MarkEvidenceAsBroadcasted(evidence)
|
store.MarkEvidenceAsBroadcasted(evidence)
|
||||||
|
|
||||||
key := keyPending(evidence)
|
pendingKey := keyPending(evidence)
|
||||||
store.db.Delete(key)
|
store.db.Delete(pendingKey)
|
||||||
|
|
||||||
ei := store.getEvidenceInfo(evidence)
|
ei := store.getEvidenceInfo(evidence)
|
||||||
ei.Committed = true
|
ei.Committed = true
|
||||||
@ -161,15 +172,16 @@ func (store *EvidenceStore) MarkEvidenceAsCommitted(evidence types.Evidence) {
|
|||||||
// TODO: we should use the state db and db.Sync in state.Save instead.
|
// TODO: we should use the state db and db.Sync in state.Save instead.
|
||||||
// Else, if we call this before state.Save, we may never mark committed evidence as committed.
|
// Else, if we call this before state.Save, we may never mark committed evidence as committed.
|
||||||
// Else, if we call this after state.Save, we may get stuck broadcasting evidence we never know we committed.
|
// Else, if we call this after state.Save, we may get stuck broadcasting evidence we never know we committed.
|
||||||
store.db.SetSync(key, wire.BinaryBytes(ei))
|
lookupKey := keyLookup(evidence)
|
||||||
|
store.db.SetSync(lookupKey, wire.BinaryBytes(ei))
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------
|
//---------------------------------------------------
|
||||||
// utils
|
// utils
|
||||||
|
|
||||||
func (store *EvidenceStore) getEvidenceInfo(evidence types.Evidence) evidenceInfo {
|
func (store *EvidenceStore) getEvidenceInfo(evidence types.Evidence) EvidenceInfo {
|
||||||
key := keyLookup(evidence)
|
key := keyLookup(evidence)
|
||||||
var ei evidenceInfo
|
var ei EvidenceInfo
|
||||||
b := store.db.Get(key)
|
b := store.db.Get(key)
|
||||||
wire.ReadBinaryBytes(b, &ei)
|
wire.ReadBinaryBytes(b, &ei)
|
||||||
return ei
|
return ei
|
||||||
|
166
evidence/store_test.go
Normal file
166
evidence/store_test.go
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
package evpool
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
wire "github.com/tendermint/go-wire"
|
||||||
|
"github.com/tendermint/tendermint/types"
|
||||||
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
//-------------------------------------------
|
||||||
|
|
||||||
|
func TestStoreAddDuplicate(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
db := dbm.NewMemDB()
|
||||||
|
store := NewEvidenceStore(db)
|
||||||
|
|
||||||
|
priority := 10
|
||||||
|
ev := newMockGoodEvidence(2, 1, []byte("val1"))
|
||||||
|
|
||||||
|
added, err := store.AddNewEvidence(ev, priority)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.True(added)
|
||||||
|
|
||||||
|
// cant add twice
|
||||||
|
added, err = store.AddNewEvidence(ev, priority)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.False(added)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreMark(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
db := dbm.NewMemDB()
|
||||||
|
store := NewEvidenceStore(db)
|
||||||
|
|
||||||
|
// before we do anything, priority/pending are empty
|
||||||
|
priorityEv := store.PriorityEvidence()
|
||||||
|
pendingEv := store.PendingEvidence()
|
||||||
|
assert.Equal(0, len(priorityEv))
|
||||||
|
assert.Equal(0, len(pendingEv))
|
||||||
|
|
||||||
|
priority := 10
|
||||||
|
ev := newMockGoodEvidence(2, 1, []byte("val1"))
|
||||||
|
|
||||||
|
added, err := store.AddNewEvidence(ev, priority)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.True(added)
|
||||||
|
|
||||||
|
// get the evidence. verify. should be uncommitted
|
||||||
|
ei := store.GetEvidence(ev.Height(), ev.Hash())
|
||||||
|
assert.Equal(ev, ei.Evidence)
|
||||||
|
assert.Equal(priority, ei.Priority)
|
||||||
|
assert.False(ei.Committed)
|
||||||
|
|
||||||
|
// new evidence should be returns in priority/pending
|
||||||
|
priorityEv = store.PriorityEvidence()
|
||||||
|
pendingEv = store.PendingEvidence()
|
||||||
|
assert.Equal(1, len(priorityEv))
|
||||||
|
assert.Equal(1, len(pendingEv))
|
||||||
|
|
||||||
|
// priority is now empty
|
||||||
|
store.MarkEvidenceAsBroadcasted(ev)
|
||||||
|
priorityEv = store.PriorityEvidence()
|
||||||
|
pendingEv = store.PendingEvidence()
|
||||||
|
assert.Equal(0, len(priorityEv))
|
||||||
|
assert.Equal(1, len(pendingEv))
|
||||||
|
|
||||||
|
// priority and pending are now empty
|
||||||
|
store.MarkEvidenceAsCommitted(ev)
|
||||||
|
priorityEv = store.PriorityEvidence()
|
||||||
|
pendingEv = store.PendingEvidence()
|
||||||
|
assert.Equal(0, len(priorityEv))
|
||||||
|
assert.Equal(0, len(pendingEv))
|
||||||
|
|
||||||
|
// evidence should show committed
|
||||||
|
ei = store.GetEvidence(ev.Height(), ev.Hash())
|
||||||
|
assert.Equal(ev, ei.Evidence)
|
||||||
|
assert.Equal(priority, ei.Priority)
|
||||||
|
assert.True(ei.Committed)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStorePriority(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
db := dbm.NewMemDB()
|
||||||
|
store := NewEvidenceStore(db)
|
||||||
|
|
||||||
|
// sorted by priority and then height
|
||||||
|
cases := []struct {
|
||||||
|
ev MockGoodEvidence
|
||||||
|
priority int
|
||||||
|
}{
|
||||||
|
{newMockGoodEvidence(2, 1, []byte("val1")), 17},
|
||||||
|
{newMockGoodEvidence(5, 2, []byte("val2")), 15},
|
||||||
|
{newMockGoodEvidence(10, 2, []byte("val2")), 13},
|
||||||
|
{newMockGoodEvidence(100, 2, []byte("val2")), 11},
|
||||||
|
{newMockGoodEvidence(90, 2, []byte("val2")), 11},
|
||||||
|
{newMockGoodEvidence(80, 2, []byte("val2")), 11},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
added, err := store.AddNewEvidence(c.ev, c.priority)
|
||||||
|
assert.True(added)
|
||||||
|
assert.Nil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
evList := store.PriorityEvidence()
|
||||||
|
for i, ev := range evList {
|
||||||
|
assert.Equal(ev, cases[i].ev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------
|
||||||
|
const (
|
||||||
|
evidenceTypeMock = byte(0x01)
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = wire.RegisterInterface(
|
||||||
|
struct{ types.Evidence }{},
|
||||||
|
wire.ConcreteType{MockGoodEvidence{}, evidenceTypeMock},
|
||||||
|
)
|
||||||
|
|
||||||
|
type MockGoodEvidence struct {
|
||||||
|
Height_ int
|
||||||
|
Address_ []byte
|
||||||
|
Index_ int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMockGoodEvidence(height, index int, address []byte) MockGoodEvidence {
|
||||||
|
return MockGoodEvidence{height, address, index}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e MockGoodEvidence) Height() int { return e.Height_ }
|
||||||
|
func (e MockGoodEvidence) Address() []byte { return e.Address_ }
|
||||||
|
func (e MockGoodEvidence) Index() int { return e.Index_ }
|
||||||
|
func (e MockGoodEvidence) Hash() []byte { return []byte{byte(e.Index_)} }
|
||||||
|
func (e MockGoodEvidence) Verify(chainID string) error { return nil }
|
||||||
|
func (e MockGoodEvidence) Equal(ev types.Evidence) bool {
|
||||||
|
e2 := ev.(MockGoodEvidence)
|
||||||
|
return e.Height_ == e2.Height_ &&
|
||||||
|
bytes.Equal(e.Address_, e2.Address_) &&
|
||||||
|
e.Index_ == e2.Index_
|
||||||
|
}
|
||||||
|
func (e MockGoodEvidence) String() string {
|
||||||
|
return fmt.Sprintf("GoodEvidence: %d/%s/%d", e.Height_, e.Address_, e.Index_)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MockBadEvidence struct {
|
||||||
|
MockGoodEvidence
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e MockBadEvidence) Verify(chainID string) error { return fmt.Errorf("MockBadEvidence") }
|
||||||
|
func (e MockBadEvidence) Equal(ev types.Evidence) bool {
|
||||||
|
e2 := ev.(MockBadEvidence)
|
||||||
|
return e.Height_ == e2.Height_ &&
|
||||||
|
bytes.Equal(e.Address_, e2.Address_) &&
|
||||||
|
e.Index_ == e2.Index_
|
||||||
|
}
|
||||||
|
func (e MockBadEvidence) String() string {
|
||||||
|
return fmt.Sprintf("BadEvidence: %d/%s/%d", e.Height_, e.Address_, e.Index_)
|
||||||
|
}
|
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
|
wire "github.com/tendermint/go-wire"
|
||||||
"github.com/tendermint/tmlibs/merkle"
|
"github.com/tendermint/tmlibs/merkle"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -78,6 +79,17 @@ func (evl EvidenceList) Has(evidence Evidence) bool {
|
|||||||
|
|
||||||
//-------------------------------------------
|
//-------------------------------------------
|
||||||
|
|
||||||
|
const (
|
||||||
|
evidenceTypeDuplicateVote = byte(0x01)
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = wire.RegisterInterface(
|
||||||
|
struct{ Evidence }{},
|
||||||
|
wire.ConcreteType{&DuplicateVoteEvidence{}, evidenceTypeDuplicateVote},
|
||||||
|
)
|
||||||
|
|
||||||
|
//-------------------------------------------
|
||||||
|
|
||||||
// DuplicateVoteEvidence contains evidence a validator signed two conflicting votes.
|
// DuplicateVoteEvidence contains evidence a validator signed two conflicting votes.
|
||||||
type DuplicateVoteEvidence struct {
|
type DuplicateVoteEvidence struct {
|
||||||
PubKey crypto.PubKey
|
PubKey crypto.PubKey
|
||||||
|
Loading…
x
Reference in New Issue
Block a user