Add a light-client provider in the kvstore
This commit is contained in:
parent
1b75d9431b
commit
b150c865f9
|
@ -1,17 +1,24 @@
|
|||
package ibc
|
||||
|
||||
import (
|
||||
wire "github.com/tendermint/go-wire"
|
||||
"github.com/tendermint/light-client/certifiers"
|
||||
|
||||
"github.com/tendermint/basecoin/stack"
|
||||
"github.com/tendermint/basecoin/state"
|
||||
)
|
||||
|
||||
const (
|
||||
prefixHash = "v"
|
||||
prefixHeight = "h"
|
||||
prefixPacket = "p"
|
||||
)
|
||||
|
||||
// newCertifier loads up the current state of this chain to make a proper
|
||||
func newCertifier(chainID string, store state.KVStore) (*certifiers.InquiringCertifier, error) {
|
||||
// each chain has their own prefixed subspace
|
||||
space := stack.PrefixedStore(chainID, store)
|
||||
p := dbProvider{space}
|
||||
p := newDBProvider(space)
|
||||
|
||||
// this gets the most recent verified seed
|
||||
seed, err := certifiers.LatestSeed(p)
|
||||
|
@ -27,18 +34,41 @@ func newCertifier(chainID string, store state.KVStore) (*certifiers.InquiringCer
|
|||
|
||||
// dbProvider wraps our kv store so it integrates with light-client verification
|
||||
type dbProvider struct {
|
||||
store state.KVStore
|
||||
byHash state.KVStore
|
||||
byHeight *state.Span
|
||||
}
|
||||
|
||||
var _ certifiers.Provider = dbProvider{}
|
||||
func newDBProvider(store state.KVStore) *dbProvider {
|
||||
return &dbProvider{
|
||||
byHash: stack.PrefixedStore(prefixHash, store),
|
||||
byHeight: state.NewSpan(stack.PrefixedStore(prefixHeight, store)),
|
||||
}
|
||||
}
|
||||
|
||||
func (d dbProvider) StoreSeed(seed certifiers.Seed) error {
|
||||
var _ certifiers.Provider = &dbProvider{}
|
||||
|
||||
func (d *dbProvider) StoreSeed(seed certifiers.Seed) error {
|
||||
// TODO: don't duplicate data....
|
||||
b := wire.BinaryBytes(seed)
|
||||
d.byHash.Set(seed.Hash(), b)
|
||||
d.byHeight.Set(uint64(seed.Height()), b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d dbProvider) GetByHeight(h int) (certifiers.Seed, error) {
|
||||
return certifiers.Seed{}, certifiers.ErrSeedNotFound()
|
||||
func (d *dbProvider) GetByHeight(h int) (seed certifiers.Seed, err error) {
|
||||
b, _ := d.byHeight.LTE(uint64(h))
|
||||
if b == nil {
|
||||
return seed, certifiers.ErrSeedNotFound()
|
||||
}
|
||||
func (d dbProvider) GetByHash(hash []byte) (certifiers.Seed, error) {
|
||||
return certifiers.Seed{}, certifiers.ErrSeedNotFound()
|
||||
err = wire.ReadBinaryBytes(b, &seed)
|
||||
return
|
||||
}
|
||||
|
||||
func (d *dbProvider) GetByHash(hash []byte) (seed certifiers.Seed, err error) {
|
||||
b := d.byHash.Get(hash)
|
||||
if b == nil {
|
||||
return seed, certifiers.ErrSeedNotFound()
|
||||
}
|
||||
err = wire.ReadBinaryBytes(b, &seed)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
package ibc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/basecoin/state"
|
||||
"github.com/tendermint/light-client/certifiers"
|
||||
)
|
||||
|
||||
func assertSeedEqual(t *testing.T, s, s2 certifiers.Seed) {
|
||||
assert := assert.New(t)
|
||||
assert.Equal(s.Height(), s2.Height())
|
||||
assert.Equal(s.Hash(), s2.Hash())
|
||||
// TODO: more
|
||||
}
|
||||
|
||||
func TestProviderStore(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
// make a few seeds
|
||||
keys := certifiers.GenValKeys(2)
|
||||
seeds := makeSeeds(keys, 4, "some-chain", "demo-store")
|
||||
|
||||
// make a provider
|
||||
store := state.NewMemKVStore()
|
||||
p := newDBProvider(store)
|
||||
|
||||
// check it...
|
||||
_, err := p.GetByHeight(20)
|
||||
require.NotNil(err)
|
||||
assert.True(certifiers.IsSeedNotFoundErr(err))
|
||||
|
||||
// add a seed
|
||||
for _, s := range seeds {
|
||||
err = p.StoreSeed(s)
|
||||
require.Nil(err)
|
||||
}
|
||||
|
||||
// make sure we get it...
|
||||
s := seeds[0]
|
||||
val, err := p.GetByHeight(s.Height())
|
||||
if assert.Nil(err) {
|
||||
assertSeedEqual(t, s, val)
|
||||
}
|
||||
|
||||
// make sure we get higher
|
||||
val, err = p.GetByHeight(s.Height() + 2)
|
||||
if assert.Nil(err) {
|
||||
assertSeedEqual(t, s, val)
|
||||
}
|
||||
|
||||
// below is nothing
|
||||
_, err = p.GetByHeight(s.Height() - 2)
|
||||
assert.True(certifiers.IsSeedNotFoundErr(err))
|
||||
|
||||
// make sure we get highest
|
||||
val, err = certifiers.LatestSeed(p)
|
||||
if assert.Nil(err) {
|
||||
assertSeedEqual(t, seeds[3], val)
|
||||
}
|
||||
|
||||
// make sure by hash also (note all have same hash, so overwritten)
|
||||
val, err = p.GetByHash(seeds[1].Hash())
|
||||
if assert.Nil(err) {
|
||||
assertSeedEqual(t, seeds[3], val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDBProvider(t *testing.T) {
|
||||
store := state.NewMemKVStore()
|
||||
p := newDBProvider(store)
|
||||
checkProvider(t, p, "test-db", "bling")
|
||||
}
|
||||
|
||||
func makeSeeds(keys certifiers.ValKeys, count int, chainID, app string) []certifiers.Seed {
|
||||
appHash := []byte(app)
|
||||
seeds := make([]certifiers.Seed, count)
|
||||
for i := 0; i < count; i++ {
|
||||
// two seeds for each validator, to check how we handle dups
|
||||
// (10, 0), (10, 1), (10, 1), (10, 2), (10, 2), ...
|
||||
vals := keys.ToValidators(10, int64(count/2))
|
||||
h := 20 + 10*i
|
||||
check := keys.GenCheckpoint(chainID, h, nil, vals, appHash, 0, len(keys))
|
||||
seeds[i] = certifiers.Seed{check, vals}
|
||||
}
|
||||
return seeds
|
||||
}
|
||||
|
||||
func checkProvider(t *testing.T, p certifiers.Provider, chainID, app string) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
keys := certifiers.GenValKeys(5)
|
||||
count := 10
|
||||
|
||||
// make a bunch of seeds...
|
||||
seeds := makeSeeds(keys, count, chainID, app)
|
||||
|
||||
// check provider is empty
|
||||
seed, err := p.GetByHeight(20)
|
||||
require.NotNil(err)
|
||||
assert.True(certifiers.IsSeedNotFoundErr(err))
|
||||
|
||||
seed, err = p.GetByHash(seeds[3].Hash())
|
||||
require.NotNil(err)
|
||||
assert.True(certifiers.IsSeedNotFoundErr(err))
|
||||
|
||||
// now add them all to the provider
|
||||
for _, s := range seeds {
|
||||
err = p.StoreSeed(s)
|
||||
require.Nil(err)
|
||||
// and make sure we can get it back
|
||||
s2, err := p.GetByHash(s.Hash())
|
||||
assert.Nil(err)
|
||||
assertSeedEqual(t, s, s2)
|
||||
// by height as well
|
||||
s2, err = p.GetByHeight(s.Height())
|
||||
assert.Nil(err)
|
||||
assertSeedEqual(t, s, s2)
|
||||
}
|
||||
|
||||
// make sure we get the last hash if we overstep
|
||||
seed, err = p.GetByHeight(5000)
|
||||
if assert.Nil(err) {
|
||||
assertSeedEqual(t, seeds[count-1], seed)
|
||||
}
|
||||
|
||||
// and middle ones as well
|
||||
seed, err = p.GetByHeight(47)
|
||||
if assert.Nil(err) {
|
||||
// we only step by 10, so 40 must be the one below this
|
||||
assert.Equal(40, seed.Height())
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue