From 9375649bc43172ce8cdca35c57a9174532ecbdd5 Mon Sep 17 00:00:00 2001 From: mossid Date: Fri, 7 Jun 2019 23:44:43 +0200 Subject: [PATCH 001/166] add mapping --- store/mapping/base.go | 91 +++++++++++++++++ store/mapping/boolean.go | 43 ++++++++ store/mapping/enum.go | 74 ++++++++++++++ store/mapping/indexer.go | 159 ++++++++++++++++++++++++++++++ store/mapping/integer.go | 83 ++++++++++++++++ store/mapping/mapping.go | 186 +++++++++++++++++++++++++++++++++++ store/mapping/queue.go | 43 ++++++++ store/mapping/sorted.go | 66 +++++++++++++ store/mapping/test_common.go | 55 +++++++++++ store/mapping/types.go | 14 +++ store/mapping/value.go | 156 +++++++++++++++++++++++++++++ store/mapping/value_test.go | 32 ++++++ 12 files changed, 1002 insertions(+) create mode 100644 store/mapping/base.go create mode 100644 store/mapping/boolean.go create mode 100644 store/mapping/enum.go create mode 100644 store/mapping/indexer.go create mode 100644 store/mapping/integer.go create mode 100644 store/mapping/mapping.go create mode 100644 store/mapping/queue.go create mode 100644 store/mapping/sorted.go create mode 100644 store/mapping/test_common.go create mode 100644 store/mapping/types.go create mode 100644 store/mapping/value.go create mode 100644 store/mapping/value_test.go diff --git a/store/mapping/base.go b/store/mapping/base.go new file mode 100644 index 000000000000..4d9da596ec9e --- /dev/null +++ b/store/mapping/base.go @@ -0,0 +1,91 @@ +package mapping + +import ( + // "github.com/tendermint/tendermint/crypto/merkle" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +/* +type keypath struct { + keypath merkle.KeyPath + prefix []byte +} + +func (path keypath) _prefix(prefix []byte) (res keypath) { + res.keypath = path.keypath + res.prefix = make([]byte, len(path.prefix)+len(prefix)) + copy(res.prefix[:len(path.prefix)], path.prefix) + copy(res.prefix[len(path.prefix):], prefix) + return +} +*/ +type Base struct { + cdc *codec.Codec + storefn func(Context) KVStore + prefix []byte + // keypath keypath // temporal +} + +func EmptyBase() Base { + return NewBase(nil, nil) +} + +func NewBase(cdc *codec.Codec, key sdk.StoreKey) Base { + return Base{ + cdc: cdc, + storefn: func(ctx Context) KVStore { return ctx.KVStore(key) }, + /* + keypath: keypath{ + keypath: new(KeyPath).AppendKey([]byte(key.Name()), merkle.KeyEncodingHex), + }, + */ + } +} + +func NewBaseWithGetter(cdc *codec.Codec, storefn func(Context) KVStore) Base { + return Base{ + cdc: cdc, + storefn: storefn, + } +} + +func (base Base) store(ctx Context) KVStore { + return prefix.NewStore(base.storefn(ctx), base.prefix) +} + +func (base Base) Prefix(prefix []byte) (res Base) { + res = Base{ + cdc: base.cdc, + storefn: base.storefn, + //keypath: base.keypath._prefix(prefix), + } + res.prefix = join(base.prefix, prefix) + return +} + +func (base Base) Cdc() *codec.Codec { + return base.cdc +} + +func (base Base) key(key []byte) []byte { + return join(base.prefix, key) +} + +func join(a, b []byte) (res []byte) { + res = make([]byte, len(a)+len(b)) + copy(res, a) + copy(res[len(a):], b) + return +} + +/* +func (base Base) KeyPath() merkle.KeyPath { + if len(base.keypath.prefix) != 0 { + return base.keypath.keypath.AppendKey(base.keypath.prefix, merkle.KeyEncodingHex) + } + return base.keypath.keypath +} +*/ diff --git a/store/mapping/boolean.go b/store/mapping/boolean.go new file mode 100644 index 000000000000..0c78707bf6fa --- /dev/null +++ b/store/mapping/boolean.go @@ -0,0 +1,43 @@ +package mapping + +// XXX: interface +type Boolean struct { + Enum +} + +func NewBoolean(v Value) Boolean { + return Boolean{NewEnum(v)} +} + +func (v Boolean) Get(ctx Context) bool { + return v.Enum.Get(ctx) != 0x00 +} + +func (v Boolean) GetIfExists(ctx Context) bool { + return v.Enum.GetIfExists(ctx) != 0x00 +} + +func (v Boolean) GetSafe(ctx Context) (bool, error) { + res, err := v.Enum.GetSafe(ctx) + return res != 0x00, err +} + +func (v Boolean) Set(ctx Context, value bool) { + if value { + v.Enum.Set(ctx, 0x01) + } else { + v.Enum.Set(ctx, 0x00) + } +} + +func (v Boolean) Flip(ctx Context) (res bool) { + res = !v.GetIfExists(ctx) + v.Set(ctx, res) + return +} + +/* +func (v Boolean) Key() []byte { + return v.base.key(v.key) +} +*/ diff --git a/store/mapping/enum.go b/store/mapping/enum.go new file mode 100644 index 000000000000..dffc2b8fc6e4 --- /dev/null +++ b/store/mapping/enum.go @@ -0,0 +1,74 @@ +package mapping + +type Enum interface { + coreValue + Get(Context) byte + GetIfExists(Context) byte + GetSafe(Context) (byte, error) + Set(Context, byte) + Transit(Context, byte, byte) bool + Is(Context, byte) bool +} + +var _ Enum = enum{} + +type enum struct { + Value +} + +func NewEnum(v Value) enum { + return enum{v} +} + +/* +func (v Value) enum() enum { + return enum{v} +} +*/ +func (v enum) Get(ctx Context) byte { + return v.Value.GetRaw(ctx)[0] +} + +func (v enum) GetIfExists(ctx Context) byte { + res := v.Value.GetRaw(ctx) + if res != nil { + return res[0] + } + return 0x00 +} + +func (v enum) GetSafe(ctx Context) (byte, error) { + res := v.Value.GetRaw(ctx) + if res == nil { + return 0x00, &GetSafeError{} + } + return res[0], nil +} + +func (v enum) Set(ctx Context, value byte) { + v.Value.SetRaw(ctx, []byte{value}) +} + +func (v enum) Incr(ctx Context) (res byte) { + res = v.GetIfExists(ctx) + 1 + v.Set(ctx, res) + return +} + +func (v enum) Transit(ctx Context, from, to byte) bool { + if v.GetIfExists(ctx) != from { + return false + } + v.Set(ctx, to) + return true +} + +func (v enum) Is(ctx Context, value byte) bool { + return v.Get(ctx) == value +} + +/* +func (v enum) Key() []byte { + return v.base.key(v.key) +} +*/ diff --git a/store/mapping/indexer.go b/store/mapping/indexer.go new file mode 100644 index 000000000000..5d2cf5f7371f --- /dev/null +++ b/store/mapping/indexer.go @@ -0,0 +1,159 @@ +package mapping + +import ( + "encoding/binary" + "fmt" + "strconv" +) + +type IntEncoding byte + +const ( + Dec IntEncoding = iota + Hex + Bin +) + +type Indexer struct { + m Mapping + + enc IntEncoding +} + +func NewIndexer(base Base, prefix []byte, enc IntEncoding) Indexer { + return Indexer{ + m: NewMapping(base, prefix), + enc: enc, + } +} + +// Identical length independent from the index, ensure ordering +func EncodeInt(index uint64, enc IntEncoding) (res []byte) { + switch enc { + case Dec: + return []byte(fmt.Sprintf("%020d", index)) + case Hex: + return []byte(fmt.Sprintf("%020x", index)) + case Bin: + res = make([]byte, 8) + binary.BigEndian.PutUint64(res, index) + return + default: + panic("invalid IntEncoding") + } +} + +func DecodeInt(bz []byte, enc IntEncoding) (res uint64, err error) { + switch enc { + case Dec: + return strconv.ParseUint(string(bz), 10, 64) + case Hex: + return strconv.ParseUint(string(bz), 16, 64) + case Bin: + return binary.BigEndian.Uint64(bz), nil + default: + panic("invalid IntEncoding") + } +} + +func (ix Indexer) Value(index uint64) Value { + return ix.m.Value(EncodeInt(index, ix.enc)) +} + +func (ix Indexer) Get(ctx Context, index uint64, ptr interface{}) { + ix.Value(index).Get(ctx, ptr) +} + +func (ix Indexer) GetIfExists(ctx Context, index uint64, ptr interface{}) { + ix.Value(index).GetIfExists(ctx, ptr) +} + +func (ix Indexer) GetSafe(ctx Context, index uint64, ptr interface{}) error { + return ix.Value(index).GetSafe(ctx, ptr) +} + +func (ix Indexer) Set(ctx Context, index uint64, o interface{}) { + ix.Value(index).Set(ctx, o) +} + +func (ix Indexer) Has(ctx Context, index uint64) bool { + return ix.Value(index).Exists(ctx) +} + +func (ix Indexer) Delete(ctx Context, index uint64) { + ix.Value(index).Delete(ctx) +} + +func (ix Indexer) IsEmpty(ctx Context) bool { + return ix.m.IsEmpty(ctx) +} + +func (ix Indexer) Prefix(prefix []byte) Indexer { + return Indexer{ + m: ix.m.Prefix(prefix), + + enc: ix.enc, + } +} + +func (ix Indexer) Range(start, end uint64) Indexer { + return Indexer{ + m: ix.m.Range(EncodeInt(start, ix.enc), EncodeInt(end, ix.enc)), + + enc: ix.enc, + } +} + +func (ix Indexer) IterateAscending(ctx Context, ptr interface{}, fn func(uint64) bool) { + ix.m.Iterate(ctx, ptr, func(bz []byte) bool { + key, err := DecodeInt(bz, ix.enc) + if err != nil { + panic(err) + } + return fn(key) + }) +} + +func (ix Indexer) IterateDescending(ctx Context, ptr interface{}, fn func(uint64) bool) { + ix.m.ReverseIterate(ctx, ptr, func(bz []byte) bool { + key, err := DecodeInt(bz, ix.enc) + if err != nil { + panic(err) + } + return fn(key) + }) +} + +func (ix Indexer) First(ctx Context, ptr interface{}) (key uint64, ok bool) { + keybz, ok := ix.m.First(ctx, ptr) + if !ok { + return + } + if len(keybz) != 0 { + key, err := DecodeInt(keybz, ix.enc) + if err != nil { + return key, false + } + } + return +} + +func (ix Indexer) Last(ctx Context, ptr interface{}) (key uint64, ok bool) { + keybz, ok := ix.m.Last(ctx, ptr) + if !ok { + return + } + if len(keybz) != 0 { + key, err := DecodeInt(keybz, ix.enc) + if err != nil { + return key, false + } + } + return +} + +/* +func (ix Indexer) Key(index uint64) []byte { + return ix.m.Key(EncodeInt(index, ix.enc)) +} +*/ diff --git a/store/mapping/integer.go b/store/mapping/integer.go new file mode 100644 index 000000000000..45b484f67a16 --- /dev/null +++ b/store/mapping/integer.go @@ -0,0 +1,83 @@ +package mapping + +type Integer interface { + coreValue + Get(Context) uint64 + GetIfExists(Context) uint64 + GetSafe(Context) (uint64, error) + Set(Context, uint64) + Incr(Context) uint64 + Is(Context, uint64) bool +} + +var _ Integer = integer{} + +type integer struct { + Value + + enc IntEncoding +} + +func NewInteger(v Value, enc IntEncoding) integer { + return integer{ + Value: v, + enc: enc, + } +} + +/* +func (v Value) integer() integer { + return integer{v} +} +*/ +func (v integer) Get(ctx Context) uint64 { + res, err := DecodeInt(v.GetRaw(ctx), v.enc) + if err != nil { + panic(err) + } + return res +} + +func (v integer) GetIfExists(ctx Context) (res uint64) { + bz := v.GetRaw(ctx) + if bz == nil { + return 0 + } + res, err := DecodeInt(bz, v.enc) + if err != nil { + panic(err) + } + return res +} + +func (v integer) GetSafe(ctx Context) (uint64, error) { + bz := v.GetRaw(ctx) + if bz == nil { + return 0, &GetSafeError{} + } + res, err := DecodeInt(bz, v.enc) + if err != nil { + panic(err) + } + return res, nil +} + +func (v integer) Set(ctx Context, value uint64) { + v.SetRaw(ctx, EncodeInt(value, v.enc)) +} + +func (v integer) Incr(ctx Context) (res uint64) { + res = v.GetIfExists(ctx) + 1 + v.Set(ctx, res) + return +} + +func (v integer) Is(ctx Context, value uint64) bool { + return v.Get(ctx) == value +} + +/* +func (v integer) Key() []byte { + return v.base.key(v.key) +} +*/ diff --git a/store/mapping/mapping.go b/store/mapping/mapping.go new file mode 100644 index 000000000000..62343f58e4d4 --- /dev/null +++ b/store/mapping/mapping.go @@ -0,0 +1,186 @@ +package mapping + +import ( + "reflect" + + "github.com/cosmos/cosmos-sdk/store" +) + +type Mapping struct { + base Base + start, end []byte +} + +func NewMapping(base Base, prefix []byte) Mapping { + return Mapping{ + base: base.Prefix(prefix), + start: []byte{}, // preventing nil key access in store.Last + } +} + +func (m Mapping) store(ctx Context) KVStore { + return m.base.store(ctx) +} + +/* +func (m Mapping) keyPath() (res KeyPath) { + if len(m.prefix) != 0 { + return m.base.Prefix(m.prefix).KeyPath() + } + return m.base.KeyPath() +} +*/ +func (m Mapping) Value(key []byte, constructor ...func(Base, []byte) Value) Value { + if len(constructor) == 1 { + return constructor[0](m.base, key) + } + return NewValue(m.base, key) +} + +func (m Mapping) Get(ctx Context, key []byte, ptr interface{}) { + m.Value(key).Get(ctx, ptr) +} + +func (m Mapping) GetIfExists(ctx Context, key []byte, ptr interface{}) { + m.Value(key).GetIfExists(ctx, ptr) +} + +func (m Mapping) GetSafe(ctx Context, key []byte, ptr interface{}) error { + return m.Value(key).GetSafe(ctx, ptr) +} + +func (m Mapping) Set(ctx Context, key []byte, o interface{}) { + if o == nil { + m.Delete(ctx, key) + return + } + m.Value(key).Set(ctx, o) +} + +func (m Mapping) Has(ctx Context, key []byte) bool { + return m.Value(key).Exists(ctx) +} + +func (m Mapping) Delete(ctx Context, key []byte) { + m.Value(key).Delete(ctx) +} + +func (m Mapping) IsEmpty(ctx Context) bool { + iter := m.store(ctx).Iterator(nil, nil) + defer iter.Close() + return iter.Valid() +} + +func (m Mapping) Prefix(prefix []byte) Mapping { + return NewMapping(m.base, prefix) +} + +func (m Mapping) Range(start, end []byte) Mapping { + return Mapping{ + base: m.base, + start: start, + end: end, + } +} + +// go-amino does not support decoding to a non-nil interface +func setnil(ptr interface{}) { + v := reflect.ValueOf(ptr) + v.Elem().Set(reflect.Zero(v.Elem().Type())) +} + +func (m Mapping) Iterate(ctx Context, ptr interface{}, fn func([]byte) bool) { + iter := m.store(ctx).Iterator(m.start, m.end) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + setnil(ptr) + + v := iter.Value() + + m.base.cdc.MustUnmarshalBinaryBare(v, ptr) + + if fn(iter.Key()) { + break + } + } +} + +func (m Mapping) IterateKeys(ctx Context, fn func([]byte) bool) { + iter := m.store(ctx).Iterator(m.start, m.end) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + if fn(iter.Key()) { + break + } + } +} + +func (m Mapping) ReverseIterate(ctx Context, ptr interface{}, fn func([]byte) bool) { + iter := m.store(ctx).ReverseIterator(m.start, m.end) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + setnil(ptr) + + v := iter.Value() + + m.base.cdc.MustUnmarshalBinaryBare(v, ptr) + + if fn(iter.Key()) { + break + } + } +} + +func (m Mapping) ReverseIterateKeys(ctx Context, fn func([]byte) bool) { + iter := m.store(ctx).ReverseIterator(m.start, m.end) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + if fn(iter.Key()) { + break + } + } +} +func (m Mapping) First(ctx Context, ptr interface{}) (key []byte, ok bool) { + kvp, ok := store.First(m.store(ctx), m.start, m.end) + if !ok { + return + } + key = kvp.Key + if ptr != nil { + m.base.cdc.MustUnmarshalBinaryBare(kvp.Value, ptr) + } + return +} + +func (m Mapping) Last(ctx Context, ptr interface{}) (key []byte, ok bool) { + kvp, ok := store.Last(m.store(ctx), m.start, m.end) + if !ok { + return + } + key = kvp.Key + if ptr != nil { + m.base.cdc.MustUnmarshalBinaryBare(kvp.Value, ptr) + } + return +} + +func (m Mapping) Clear(ctx Context) { + var keys [][]byte + + iter := m.store(ctx).ReverseIterator(m.start, m.end) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + keys = append(keys, iter.Key()) + } + + store := m.store(ctx) + for _, key := range keys { + store.Delete(key) + } +} + +/* +func (m Mapping) Key(key []byte) []byte { + return m.Value(key).Key() +} +*/ diff --git a/store/mapping/queue.go b/store/mapping/queue.go new file mode 100644 index 000000000000..ccdbde77b2c6 --- /dev/null +++ b/store/mapping/queue.go @@ -0,0 +1,43 @@ +package mapping + +type Queue struct { + Indexer +} + +func NewQueue(base Base, prefix []byte, enc IntEncoding) Queue { + return Queue{ + Indexer: NewIndexer(base, prefix, enc), + } +} + +func (q Queue) Prefix(prefix []byte) Queue { + return Queue{ + Indexer: q.Indexer.Prefix(prefix), + } +} + +func (q Queue) Peek(ctx Context, ptr interface{}) (ok bool) { + _, ok = q.First(ctx, ptr) + return +} + +func (q Queue) Pop(ctx Context, ptr interface{}) (ok bool) { + key, ok := q.First(ctx, ptr) + if !ok { + return + } + + q.Delete(ctx, key) + return +} + +func (q Queue) Push(ctx Context, o interface{}) { + key, ok := q.Last(ctx, nil) + if !ok { + key = 0 + } else { + key = key + 1 + } + + q.Set(ctx, key, o) +} diff --git a/store/mapping/sorted.go b/store/mapping/sorted.go new file mode 100644 index 000000000000..b78811f710cc --- /dev/null +++ b/store/mapping/sorted.go @@ -0,0 +1,66 @@ +package mapping + +type Sorted struct { + m Mapping + + enc IntEncoding +} + +func NewSorted(base Base, prefix []byte, enc IntEncoding) Sorted { + return Sorted{ + m: NewMapping(base, prefix), + + enc: enc, + } +} + +func (s Sorted) key(power uint64, key []byte) []byte { + return append(EncodeInt(power, s.enc), key...) +} + +func (s Sorted) Value(power uint64, key []byte) Value { + return s.m.Value(s.key(power, key)) +} + +func (s Sorted) Get(ctx Context, power uint64, key []byte, ptr interface{}) { + s.Value(power, key).Get(ctx, ptr) +} + +func (s Sorted) GetIfExists(ctx Context, power uint64, key []byte, ptr interface{}) { + s.Value(power, key).GetIfExists(ctx, ptr) +} + +func (s Sorted) Set(ctx Context, power uint64, key []byte, o interface{}) { + s.Value(power, key).Set(ctx, o) +} + +func (s Sorted) Has(ctx Context, power uint64, key []byte) bool { + return s.Value(power, key).Exists(ctx) +} + +func (s Sorted) Delete(ctx Context, power uint64, key []byte) { + s.Value(power, key).Delete(ctx) +} + +func (s Sorted) IsEmpty(ctx Context) bool { + return s.m.IsEmpty(ctx) +} + +func (s Sorted) Prefix(prefix []byte) Sorted { + return Sorted{ + m: s.m.Prefix(prefix), + enc: s.enc, + } +} + +func (s Sorted) IterateAscending(ctx Context, ptr interface{}, fn func([]byte) bool) { + s.m.Iterate(ctx, ptr, fn) +} + +func (s Sorted) IterateDescending(ctx Context, ptr interface{}, fn func([]byte) bool) { + s.m.ReverseIterate(ctx, ptr, fn) +} + +func (s Sorted) Clear(ctx Context) { + s.m.Clear(ctx) +} diff --git a/store/mapping/test_common.go b/store/mapping/test_common.go new file mode 100644 index 000000000000..250410f88ced --- /dev/null +++ b/store/mapping/test_common.go @@ -0,0 +1,55 @@ +package mapping + +import ( + // "testing" + "math/rand" + + // "github.com/stretchr/testify/require" + + abci "github.com/tendermint/tendermint/abci/types" + dbm "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const testsize = 10 + +type test interface { + test() +} + +type teststruct struct { + I uint64 + B bool + SL []byte +} + +var _ test = teststruct{} + +func (teststruct) test() {} + +func newtest() test { + var res teststruct + res.I = rand.Uint64() + res.B = rand.Int()%2 == 0 + res.SL = make([]byte, 20) + rand.Read(res.SL) + return res +} + +func defaultComponents() (sdk.StoreKey, Context, *codec.Codec) { + key := sdk.NewKVStoreKey("test") + db := dbm.NewMemDB() + cms := store.NewCommitMultiStore(db) + cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) + cms.LoadLatestVersion() + ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) + cdc := codec.New() + cdc.RegisterInterface((*test)(nil), nil) + cdc.RegisterConcrete(teststruct{}, "test/struct", nil) + cdc.Seal() + return key, ctx, cdc +} diff --git a/store/mapping/types.go b/store/mapping/types.go new file mode 100644 index 000000000000..74ca2a9168b0 --- /dev/null +++ b/store/mapping/types.go @@ -0,0 +1,14 @@ +package mapping + +import ( + "github.com/tendermint/tendermint/crypto/merkle" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type KVStore = sdk.KVStore +type Context = sdk.Context + +type KeyPath = merkle.KeyPath + +const KeyEncodingHex = merkle.KeyEncodingHex diff --git a/store/mapping/value.go b/store/mapping/value.go new file mode 100644 index 000000000000..f9325f83ec93 --- /dev/null +++ b/store/mapping/value.go @@ -0,0 +1,156 @@ +package mapping + +import ( + "bytes" + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" +) + +// TODO: expose +type coreValue interface { + // Corresponds to the KVStore methods + GetRaw(Context) []byte // Get + SetRaw(Context, []byte) // Set + Exists(Context) bool // Has + Delete(Context) // Delete + Key() []byte +} + +type Value interface { + coreValue + Get(Context, interface{}) + GetIfExists(Context, interface{}) + GetSafe(Context, interface{}) error + Set(Context, interface{}) + Is(Context, interface{}) bool +} + +var _ Value = value{} + +type value struct { + base Base + key []byte +} + +func NewValue(base Base, key []byte) Value { + return value{ + base: base, + key: key, + } +} + +func (v value) store(ctx Context) KVStore { + return v.base.store(ctx) +} + +func (v value) Cdc() *codec.Codec { + return v.base.Cdc() +} + +func (v value) Get(ctx Context, ptr interface{}) { + v.base.cdc.MustUnmarshalBinaryBare(v.store(ctx).Get(v.key), ptr) +} + +func (v value) GetIfExists(ctx Context, ptr interface{}) { + bz := v.store(ctx).Get(v.key) + if bz != nil { + v.base.cdc.MustUnmarshalBinaryBare(bz, ptr) + } +} + +func (v value) GetSafe(ctx Context, ptr interface{}) error { + bz := v.store(ctx).Get(v.key) + if bz == nil { + return ErrEmptyvalue() + } + err := v.base.cdc.UnmarshalBinaryBare(bz, ptr) + if err != nil { + return ErrUnmarshal(err) + } + return nil +} + +func (v value) GetRaw(ctx Context) []byte { + return v.store(ctx).Get(v.key) +} + +func (v value) Set(ctx Context, o interface{}) { + v.store(ctx).Set(v.key, v.base.cdc.MustMarshalBinaryBare(o)) +} + +func (v value) SetRaw(ctx Context, bz []byte) { + v.store(ctx).Set(v.key, bz) +} + +func (v value) Exists(ctx Context) bool { + return v.store(ctx).Has(v.key) +} + +func (v value) Delete(ctx Context) { + v.store(ctx).Delete(v.key) +} + +func (v value) Is(ctx Context, o interface{}) bool { + return bytes.Equal(v.GetRaw(ctx), v.base.cdc.MustMarshalBinaryBare(o)) +} + +func (v value) Key() []byte { + return v.base.key(v.key) +} + +/* +func (v value) KeyPath() KeyPath { + return v.base.KeyPath().AppendKey(v.key, KeyEncodingHex) +} +*/ +type GetSafeErrorType byte + +const ( + ErrTypeEmptyvalue GetSafeErrorType = iota + ErrTypeUnmarshal +) + +func (ty GetSafeErrorType) Format(msg string) (res string) { + switch ty { + case ErrTypeEmptyvalue: + res = fmt.Sprintf("Empty value found") + case ErrTypeUnmarshal: + res = fmt.Sprintf("Error while unmarshal") + default: + panic("Unknown error type") + } + + if msg != "" { + res = fmt.Sprintf("%s: %s", res, msg) + } + + return +} + +type GetSafeError struct { + ty GetSafeErrorType + inner error +} + +var _ error = (*GetSafeError)(nil) // TODO: sdk.Error + +func (err *GetSafeError) Error() string { + if err.inner == nil { + return err.ty.Format("") + } + return err.ty.Format(err.inner.Error()) +} + +func ErrEmptyvalue() *GetSafeError { + return &GetSafeError{ + ty: ErrTypeEmptyvalue, + } +} + +func ErrUnmarshal(err error) *GetSafeError { + return &GetSafeError{ + ty: ErrTypeUnmarshal, + inner: err, + } +} diff --git a/store/mapping/value_test.go b/store/mapping/value_test.go new file mode 100644 index 000000000000..f92ce7327587 --- /dev/null +++ b/store/mapping/value_test.go @@ -0,0 +1,32 @@ +package mapping + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/require" +) + +type valuepair struct { + key []byte + value test +} + +func TestValue(t *testing.T) { + key, ctx, cdc := defaultComponents() + base := NewBase(cdc, key) + + cases := make([]valuepair, testsize) + for i := range cases { + cases[i].key = make([]byte, 20) + rand.Read(cases[i].key) + cases[i].value = newtest() + NewValue(base, cases[i].key).Set(ctx, cases[i].value) + } + + for i := range cases { + var val test + NewValue(base, cases[i].key).Get(ctx, &val) + require.Equal(t, cases[i].value, val) + } +} From 16eea6808057c8f24ac5f42fff986108d2e92921 Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 11 Jun 2019 19:22:15 +0200 Subject: [PATCH 002/166] rm unused mapping/*, rm interfaces --- store/mapping/boolean.go | 1 - store/mapping/enum.go | 38 ++++++++--------------- store/mapping/integer.go | 36 ++++++++-------------- store/mapping/queue.go | 43 -------------------------- store/mapping/sorted.go | 66 ---------------------------------------- store/mapping/value.go | 63 +++++++++++++------------------------- 6 files changed, 46 insertions(+), 201 deletions(-) delete mode 100644 store/mapping/queue.go delete mode 100644 store/mapping/sorted.go diff --git a/store/mapping/boolean.go b/store/mapping/boolean.go index 0c78707bf6fa..158639a0eab5 100644 --- a/store/mapping/boolean.go +++ b/store/mapping/boolean.go @@ -1,6 +1,5 @@ package mapping -// XXX: interface type Boolean struct { Enum } diff --git a/store/mapping/enum.go b/store/mapping/enum.go index dffc2b8fc6e4..b7aa90332c2d 100644 --- a/store/mapping/enum.go +++ b/store/mapping/enum.go @@ -1,35 +1,23 @@ package mapping -type Enum interface { - coreValue - Get(Context) byte - GetIfExists(Context) byte - GetSafe(Context) (byte, error) - Set(Context, byte) - Transit(Context, byte, byte) bool - Is(Context, byte) bool -} - -var _ Enum = enum{} - -type enum struct { +type Enum struct { Value } -func NewEnum(v Value) enum { - return enum{v} +func NewEnum(v Value) Enum { + return Enum{v} } /* -func (v Value) enum() enum { - return enum{v} +func (v Value) Enum() Enum { + return Enum{v} } */ -func (v enum) Get(ctx Context) byte { +func (v Enum) Get(ctx Context) byte { return v.Value.GetRaw(ctx)[0] } -func (v enum) GetIfExists(ctx Context) byte { +func (v Enum) GetIfExists(ctx Context) byte { res := v.Value.GetRaw(ctx) if res != nil { return res[0] @@ -37,7 +25,7 @@ func (v enum) GetIfExists(ctx Context) byte { return 0x00 } -func (v enum) GetSafe(ctx Context) (byte, error) { +func (v Enum) GetSafe(ctx Context) (byte, error) { res := v.Value.GetRaw(ctx) if res == nil { return 0x00, &GetSafeError{} @@ -45,17 +33,17 @@ func (v enum) GetSafe(ctx Context) (byte, error) { return res[0], nil } -func (v enum) Set(ctx Context, value byte) { +func (v Enum) Set(ctx Context, value byte) { v.Value.SetRaw(ctx, []byte{value}) } -func (v enum) Incr(ctx Context) (res byte) { +func (v Enum) Incr(ctx Context) (res byte) { res = v.GetIfExists(ctx) + 1 v.Set(ctx, res) return } -func (v enum) Transit(ctx Context, from, to byte) bool { +func (v Enum) Transit(ctx Context, from, to byte) bool { if v.GetIfExists(ctx) != from { return false } @@ -63,12 +51,12 @@ func (v enum) Transit(ctx Context, from, to byte) bool { return true } -func (v enum) Is(ctx Context, value byte) bool { +func (v Enum) Is(ctx Context, value byte) bool { return v.Get(ctx) == value } /* -func (v enum) Key() []byte { +func (v Enum) Key() []byte { return v.base.key(v.key) } */ diff --git a/store/mapping/integer.go b/store/mapping/integer.go index 45b484f67a16..159332e624b2 100644 --- a/store/mapping/integer.go +++ b/store/mapping/integer.go @@ -1,36 +1,24 @@ package mapping -type Integer interface { - coreValue - Get(Context) uint64 - GetIfExists(Context) uint64 - GetSafe(Context) (uint64, error) - Set(Context, uint64) - Incr(Context) uint64 - Is(Context, uint64) bool -} - -var _ Integer = integer{} - -type integer struct { +type Integer struct { Value enc IntEncoding } -func NewInteger(v Value, enc IntEncoding) integer { - return integer{ +func NewInteger(v Value, enc IntEncoding) Integer { + return Integer{ Value: v, enc: enc, } } /* -func (v Value) integer() integer { - return integer{v} +func (v Value) Integer() Integer { + return Integer{v} } */ -func (v integer) Get(ctx Context) uint64 { +func (v Integer) Get(ctx Context) uint64 { res, err := DecodeInt(v.GetRaw(ctx), v.enc) if err != nil { panic(err) @@ -38,7 +26,7 @@ func (v integer) Get(ctx Context) uint64 { return res } -func (v integer) GetIfExists(ctx Context) (res uint64) { +func (v Integer) GetIfExists(ctx Context) (res uint64) { bz := v.GetRaw(ctx) if bz == nil { return 0 @@ -50,7 +38,7 @@ func (v integer) GetIfExists(ctx Context) (res uint64) { return res } -func (v integer) GetSafe(ctx Context) (uint64, error) { +func (v Integer) GetSafe(ctx Context) (uint64, error) { bz := v.GetRaw(ctx) if bz == nil { return 0, &GetSafeError{} @@ -62,22 +50,22 @@ func (v integer) GetSafe(ctx Context) (uint64, error) { return res, nil } -func (v integer) Set(ctx Context, value uint64) { +func (v Integer) Set(ctx Context, value uint64) { v.SetRaw(ctx, EncodeInt(value, v.enc)) } -func (v integer) Incr(ctx Context) (res uint64) { +func (v Integer) Incr(ctx Context) (res uint64) { res = v.GetIfExists(ctx) + 1 v.Set(ctx, res) return } -func (v integer) Is(ctx Context, value uint64) bool { +func (v Integer) Is(ctx Context, value uint64) bool { return v.Get(ctx) == value } /* -func (v integer) Key() []byte { +func (v Integer) Key() []byte { return v.base.key(v.key) } */ diff --git a/store/mapping/queue.go b/store/mapping/queue.go deleted file mode 100644 index ccdbde77b2c6..000000000000 --- a/store/mapping/queue.go +++ /dev/null @@ -1,43 +0,0 @@ -package mapping - -type Queue struct { - Indexer -} - -func NewQueue(base Base, prefix []byte, enc IntEncoding) Queue { - return Queue{ - Indexer: NewIndexer(base, prefix, enc), - } -} - -func (q Queue) Prefix(prefix []byte) Queue { - return Queue{ - Indexer: q.Indexer.Prefix(prefix), - } -} - -func (q Queue) Peek(ctx Context, ptr interface{}) (ok bool) { - _, ok = q.First(ctx, ptr) - return -} - -func (q Queue) Pop(ctx Context, ptr interface{}) (ok bool) { - key, ok := q.First(ctx, ptr) - if !ok { - return - } - - q.Delete(ctx, key) - return -} - -func (q Queue) Push(ctx Context, o interface{}) { - key, ok := q.Last(ctx, nil) - if !ok { - key = 0 - } else { - key = key + 1 - } - - q.Set(ctx, key, o) -} diff --git a/store/mapping/sorted.go b/store/mapping/sorted.go deleted file mode 100644 index b78811f710cc..000000000000 --- a/store/mapping/sorted.go +++ /dev/null @@ -1,66 +0,0 @@ -package mapping - -type Sorted struct { - m Mapping - - enc IntEncoding -} - -func NewSorted(base Base, prefix []byte, enc IntEncoding) Sorted { - return Sorted{ - m: NewMapping(base, prefix), - - enc: enc, - } -} - -func (s Sorted) key(power uint64, key []byte) []byte { - return append(EncodeInt(power, s.enc), key...) -} - -func (s Sorted) Value(power uint64, key []byte) Value { - return s.m.Value(s.key(power, key)) -} - -func (s Sorted) Get(ctx Context, power uint64, key []byte, ptr interface{}) { - s.Value(power, key).Get(ctx, ptr) -} - -func (s Sorted) GetIfExists(ctx Context, power uint64, key []byte, ptr interface{}) { - s.Value(power, key).GetIfExists(ctx, ptr) -} - -func (s Sorted) Set(ctx Context, power uint64, key []byte, o interface{}) { - s.Value(power, key).Set(ctx, o) -} - -func (s Sorted) Has(ctx Context, power uint64, key []byte) bool { - return s.Value(power, key).Exists(ctx) -} - -func (s Sorted) Delete(ctx Context, power uint64, key []byte) { - s.Value(power, key).Delete(ctx) -} - -func (s Sorted) IsEmpty(ctx Context) bool { - return s.m.IsEmpty(ctx) -} - -func (s Sorted) Prefix(prefix []byte) Sorted { - return Sorted{ - m: s.m.Prefix(prefix), - enc: s.enc, - } -} - -func (s Sorted) IterateAscending(ctx Context, ptr interface{}, fn func([]byte) bool) { - s.m.Iterate(ctx, ptr, fn) -} - -func (s Sorted) IterateDescending(ctx Context, ptr interface{}, fn func([]byte) bool) { - s.m.ReverseIterate(ctx, ptr, fn) -} - -func (s Sorted) Clear(ctx Context) { - s.m.Clear(ctx) -} diff --git a/store/mapping/value.go b/store/mapping/value.go index f9325f83ec93..0be43cc2932f 100644 --- a/store/mapping/value.go +++ b/store/mapping/value.go @@ -7,62 +7,41 @@ import ( "github.com/cosmos/cosmos-sdk/codec" ) -// TODO: expose -type coreValue interface { - // Corresponds to the KVStore methods - GetRaw(Context) []byte // Get - SetRaw(Context, []byte) // Set - Exists(Context) bool // Has - Delete(Context) // Delete - Key() []byte -} - -type Value interface { - coreValue - Get(Context, interface{}) - GetIfExists(Context, interface{}) - GetSafe(Context, interface{}) error - Set(Context, interface{}) - Is(Context, interface{}) bool -} - -var _ Value = value{} - -type value struct { +type Value struct { base Base key []byte } func NewValue(base Base, key []byte) Value { - return value{ + return Value{ base: base, key: key, } } -func (v value) store(ctx Context) KVStore { +func (v Value) store(ctx Context) KVStore { return v.base.store(ctx) } -func (v value) Cdc() *codec.Codec { +func (v Value) Cdc() *codec.Codec { return v.base.Cdc() } -func (v value) Get(ctx Context, ptr interface{}) { +func (v Value) Get(ctx Context, ptr interface{}) { v.base.cdc.MustUnmarshalBinaryBare(v.store(ctx).Get(v.key), ptr) } -func (v value) GetIfExists(ctx Context, ptr interface{}) { +func (v Value) GetIfExists(ctx Context, ptr interface{}) { bz := v.store(ctx).Get(v.key) if bz != nil { v.base.cdc.MustUnmarshalBinaryBare(bz, ptr) } } -func (v value) GetSafe(ctx Context, ptr interface{}) error { +func (v Value) GetSafe(ctx Context, ptr interface{}) error { bz := v.store(ctx).Get(v.key) if bz == nil { - return ErrEmptyvalue() + return ErrEmptyValue() } err := v.base.cdc.UnmarshalBinaryBare(bz, ptr) if err != nil { @@ -71,50 +50,50 @@ func (v value) GetSafe(ctx Context, ptr interface{}) error { return nil } -func (v value) GetRaw(ctx Context) []byte { +func (v Value) GetRaw(ctx Context) []byte { return v.store(ctx).Get(v.key) } -func (v value) Set(ctx Context, o interface{}) { +func (v Value) Set(ctx Context, o interface{}) { v.store(ctx).Set(v.key, v.base.cdc.MustMarshalBinaryBare(o)) } -func (v value) SetRaw(ctx Context, bz []byte) { +func (v Value) SetRaw(ctx Context, bz []byte) { v.store(ctx).Set(v.key, bz) } -func (v value) Exists(ctx Context) bool { +func (v Value) Exists(ctx Context) bool { return v.store(ctx).Has(v.key) } -func (v value) Delete(ctx Context) { +func (v Value) Delete(ctx Context) { v.store(ctx).Delete(v.key) } -func (v value) Is(ctx Context, o interface{}) bool { +func (v Value) Is(ctx Context, o interface{}) bool { return bytes.Equal(v.GetRaw(ctx), v.base.cdc.MustMarshalBinaryBare(o)) } -func (v value) Key() []byte { +func (v Value) Key() []byte { return v.base.key(v.key) } /* -func (v value) KeyPath() KeyPath { +func (v Value) KeyPath() KeyPath { return v.base.KeyPath().AppendKey(v.key, KeyEncodingHex) } */ type GetSafeErrorType byte const ( - ErrTypeEmptyvalue GetSafeErrorType = iota + ErrTypeEmptyValue GetSafeErrorType = iota ErrTypeUnmarshal ) func (ty GetSafeErrorType) Format(msg string) (res string) { switch ty { - case ErrTypeEmptyvalue: - res = fmt.Sprintf("Empty value found") + case ErrTypeEmptyValue: + res = fmt.Sprintf("Empty Value found") case ErrTypeUnmarshal: res = fmt.Sprintf("Error while unmarshal") default: @@ -142,9 +121,9 @@ func (err *GetSafeError) Error() string { return err.ty.Format(err.inner.Error()) } -func ErrEmptyvalue() *GetSafeError { +func ErrEmptyValue() *GetSafeError { return &GetSafeError{ - ty: ErrTypeEmptyvalue, + ty: ErrTypeEmptyValue, } } From f73215e989e246483b301623477f0a9c141f18b5 Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 12 Jun 2019 00:14:34 +0200 Subject: [PATCH 003/166] rm unused code --- store/mapping/base.go | 53 +++------------- store/mapping/boolean.go | 12 ---- store/mapping/enum.go | 15 ----- store/mapping/indexer.go | 62 ------------------- store/mapping/integer.go | 15 ----- store/mapping/mapping.go | 129 +-------------------------------------- store/mapping/types.go | 6 -- store/mapping/value.go | 14 ----- 8 files changed, 9 insertions(+), 297 deletions(-) diff --git a/store/mapping/base.go b/store/mapping/base.go index 4d9da596ec9e..4fb049d3240e 100644 --- a/store/mapping/base.go +++ b/store/mapping/base.go @@ -8,25 +8,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -/* -type keypath struct { - keypath merkle.KeyPath - prefix []byte -} - -func (path keypath) _prefix(prefix []byte) (res keypath) { - res.keypath = path.keypath - res.prefix = make([]byte, len(path.prefix)+len(prefix)) - copy(res.prefix[:len(path.prefix)], path.prefix) - copy(res.prefix[len(path.prefix):], prefix) - return -} -*/ type Base struct { cdc *codec.Codec storefn func(Context) KVStore prefix []byte - // keypath keypath // temporal } func EmptyBase() Base { @@ -37,18 +22,6 @@ func NewBase(cdc *codec.Codec, key sdk.StoreKey) Base { return Base{ cdc: cdc, storefn: func(ctx Context) KVStore { return ctx.KVStore(key) }, - /* - keypath: keypath{ - keypath: new(KeyPath).AppendKey([]byte(key.Name()), merkle.KeyEncodingHex), - }, - */ - } -} - -func NewBaseWithGetter(cdc *codec.Codec, storefn func(Context) KVStore) Base { - return Base{ - cdc: cdc, - storefn: storefn, } } @@ -56,13 +29,19 @@ func (base Base) store(ctx Context) KVStore { return prefix.NewStore(base.storefn(ctx), base.prefix) } +func join(a, b []byte) (res []byte) { + res = make([]byte, len(a)+len(b)) + copy(res, a) + copy(res[len(a):], b) + return +} + func (base Base) Prefix(prefix []byte) (res Base) { res = Base{ cdc: base.cdc, storefn: base.storefn, - //keypath: base.keypath._prefix(prefix), + prefix: join(base.prefix, prefix), } - res.prefix = join(base.prefix, prefix) return } @@ -73,19 +52,3 @@ func (base Base) Cdc() *codec.Codec { func (base Base) key(key []byte) []byte { return join(base.prefix, key) } - -func join(a, b []byte) (res []byte) { - res = make([]byte, len(a)+len(b)) - copy(res, a) - copy(res[len(a):], b) - return -} - -/* -func (base Base) KeyPath() merkle.KeyPath { - if len(base.keypath.prefix) != 0 { - return base.keypath.keypath.AppendKey(base.keypath.prefix, merkle.KeyEncodingHex) - } - return base.keypath.keypath -} -*/ diff --git a/store/mapping/boolean.go b/store/mapping/boolean.go index 158639a0eab5..aa6ae0d39c99 100644 --- a/store/mapping/boolean.go +++ b/store/mapping/boolean.go @@ -28,15 +28,3 @@ func (v Boolean) Set(ctx Context, value bool) { v.Enum.Set(ctx, 0x00) } } - -func (v Boolean) Flip(ctx Context) (res bool) { - res = !v.GetIfExists(ctx) - v.Set(ctx, res) - return -} - -/* -func (v Boolean) Key() []byte { - return v.base.key(v.key) -} -*/ diff --git a/store/mapping/enum.go b/store/mapping/enum.go index b7aa90332c2d..85f973fef144 100644 --- a/store/mapping/enum.go +++ b/store/mapping/enum.go @@ -8,11 +8,6 @@ func NewEnum(v Value) Enum { return Enum{v} } -/* -func (v Value) Enum() Enum { - return Enum{v} -} -*/ func (v Enum) Get(ctx Context) byte { return v.Value.GetRaw(ctx)[0] } @@ -50,13 +45,3 @@ func (v Enum) Transit(ctx Context, from, to byte) bool { v.Set(ctx, to) return true } - -func (v Enum) Is(ctx Context, value byte) bool { - return v.Get(ctx) == value -} - -/* -func (v Enum) Key() []byte { - return v.base.key(v.key) -} -*/ diff --git a/store/mapping/indexer.go b/store/mapping/indexer.go index 5d2cf5f7371f..28603fc907c2 100644 --- a/store/mapping/indexer.go +++ b/store/mapping/indexer.go @@ -95,65 +95,3 @@ func (ix Indexer) Prefix(prefix []byte) Indexer { enc: ix.enc, } } - -func (ix Indexer) Range(start, end uint64) Indexer { - return Indexer{ - m: ix.m.Range(EncodeInt(start, ix.enc), EncodeInt(end, ix.enc)), - - enc: ix.enc, - } -} - -func (ix Indexer) IterateAscending(ctx Context, ptr interface{}, fn func(uint64) bool) { - ix.m.Iterate(ctx, ptr, func(bz []byte) bool { - key, err := DecodeInt(bz, ix.enc) - if err != nil { - panic(err) - } - return fn(key) - }) -} - -func (ix Indexer) IterateDescending(ctx Context, ptr interface{}, fn func(uint64) bool) { - ix.m.ReverseIterate(ctx, ptr, func(bz []byte) bool { - key, err := DecodeInt(bz, ix.enc) - if err != nil { - panic(err) - } - return fn(key) - }) -} - -func (ix Indexer) First(ctx Context, ptr interface{}) (key uint64, ok bool) { - keybz, ok := ix.m.First(ctx, ptr) - if !ok { - return - } - if len(keybz) != 0 { - key, err := DecodeInt(keybz, ix.enc) - if err != nil { - return key, false - } - } - return -} - -func (ix Indexer) Last(ctx Context, ptr interface{}) (key uint64, ok bool) { - keybz, ok := ix.m.Last(ctx, ptr) - if !ok { - return - } - if len(keybz) != 0 { - key, err := DecodeInt(keybz, ix.enc) - if err != nil { - return key, false - } - } - return -} - -/* -func (ix Indexer) Key(index uint64) []byte { - return ix.m.Key(EncodeInt(index, ix.enc)) -} -*/ diff --git a/store/mapping/integer.go b/store/mapping/integer.go index 159332e624b2..9b74f7828130 100644 --- a/store/mapping/integer.go +++ b/store/mapping/integer.go @@ -13,11 +13,6 @@ func NewInteger(v Value, enc IntEncoding) Integer { } } -/* -func (v Value) Integer() Integer { - return Integer{v} -} -*/ func (v Integer) Get(ctx Context) uint64 { res, err := DecodeInt(v.GetRaw(ctx), v.enc) if err != nil { @@ -59,13 +54,3 @@ func (v Integer) Incr(ctx Context) (res uint64) { v.Set(ctx, res) return } - -func (v Integer) Is(ctx Context, value uint64) bool { - return v.Get(ctx) == value -} - -/* -func (v Integer) Key() []byte { - return v.base.key(v.key) -} -*/ diff --git a/store/mapping/mapping.go b/store/mapping/mapping.go index 62343f58e4d4..1d018ae38d9c 100644 --- a/store/mapping/mapping.go +++ b/store/mapping/mapping.go @@ -1,11 +1,5 @@ package mapping -import ( - "reflect" - - "github.com/cosmos/cosmos-sdk/store" -) - type Mapping struct { base Base start, end []byte @@ -22,18 +16,7 @@ func (m Mapping) store(ctx Context) KVStore { return m.base.store(ctx) } -/* -func (m Mapping) keyPath() (res KeyPath) { - if len(m.prefix) != 0 { - return m.base.Prefix(m.prefix).KeyPath() - } - return m.base.KeyPath() -} -*/ -func (m Mapping) Value(key []byte, constructor ...func(Base, []byte) Value) Value { - if len(constructor) == 1 { - return constructor[0](m.base, key) - } +func (m Mapping) Value(key []byte) Value { return NewValue(m.base, key) } @@ -74,113 +57,3 @@ func (m Mapping) IsEmpty(ctx Context) bool { func (m Mapping) Prefix(prefix []byte) Mapping { return NewMapping(m.base, prefix) } - -func (m Mapping) Range(start, end []byte) Mapping { - return Mapping{ - base: m.base, - start: start, - end: end, - } -} - -// go-amino does not support decoding to a non-nil interface -func setnil(ptr interface{}) { - v := reflect.ValueOf(ptr) - v.Elem().Set(reflect.Zero(v.Elem().Type())) -} - -func (m Mapping) Iterate(ctx Context, ptr interface{}, fn func([]byte) bool) { - iter := m.store(ctx).Iterator(m.start, m.end) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - setnil(ptr) - - v := iter.Value() - - m.base.cdc.MustUnmarshalBinaryBare(v, ptr) - - if fn(iter.Key()) { - break - } - } -} - -func (m Mapping) IterateKeys(ctx Context, fn func([]byte) bool) { - iter := m.store(ctx).Iterator(m.start, m.end) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - if fn(iter.Key()) { - break - } - } -} - -func (m Mapping) ReverseIterate(ctx Context, ptr interface{}, fn func([]byte) bool) { - iter := m.store(ctx).ReverseIterator(m.start, m.end) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - setnil(ptr) - - v := iter.Value() - - m.base.cdc.MustUnmarshalBinaryBare(v, ptr) - - if fn(iter.Key()) { - break - } - } -} - -func (m Mapping) ReverseIterateKeys(ctx Context, fn func([]byte) bool) { - iter := m.store(ctx).ReverseIterator(m.start, m.end) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - if fn(iter.Key()) { - break - } - } -} -func (m Mapping) First(ctx Context, ptr interface{}) (key []byte, ok bool) { - kvp, ok := store.First(m.store(ctx), m.start, m.end) - if !ok { - return - } - key = kvp.Key - if ptr != nil { - m.base.cdc.MustUnmarshalBinaryBare(kvp.Value, ptr) - } - return -} - -func (m Mapping) Last(ctx Context, ptr interface{}) (key []byte, ok bool) { - kvp, ok := store.Last(m.store(ctx), m.start, m.end) - if !ok { - return - } - key = kvp.Key - if ptr != nil { - m.base.cdc.MustUnmarshalBinaryBare(kvp.Value, ptr) - } - return -} - -func (m Mapping) Clear(ctx Context) { - var keys [][]byte - - iter := m.store(ctx).ReverseIterator(m.start, m.end) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - keys = append(keys, iter.Key()) - } - - store := m.store(ctx) - for _, key := range keys { - store.Delete(key) - } -} - -/* -func (m Mapping) Key(key []byte) []byte { - return m.Value(key).Key() -} -*/ diff --git a/store/mapping/types.go b/store/mapping/types.go index 74ca2a9168b0..2454084324ec 100644 --- a/store/mapping/types.go +++ b/store/mapping/types.go @@ -1,14 +1,8 @@ package mapping import ( - "github.com/tendermint/tendermint/crypto/merkle" - sdk "github.com/cosmos/cosmos-sdk/types" ) type KVStore = sdk.KVStore type Context = sdk.Context - -type KeyPath = merkle.KeyPath - -const KeyEncodingHex = merkle.KeyEncodingHex diff --git a/store/mapping/value.go b/store/mapping/value.go index 0be43cc2932f..c61a64049c56 100644 --- a/store/mapping/value.go +++ b/store/mapping/value.go @@ -1,7 +1,6 @@ package mapping import ( - "bytes" "fmt" "github.com/cosmos/cosmos-sdk/codec" @@ -70,19 +69,6 @@ func (v Value) Delete(ctx Context) { v.store(ctx).Delete(v.key) } -func (v Value) Is(ctx Context, o interface{}) bool { - return bytes.Equal(v.GetRaw(ctx), v.base.cdc.MustMarshalBinaryBare(o)) -} - -func (v Value) Key() []byte { - return v.base.key(v.key) -} - -/* -func (v Value) KeyPath() KeyPath { - return v.base.KeyPath().AppendKey(v.key, KeyEncodingHex) -} -*/ type GetSafeErrorType byte const ( From cf0b8f29c3a9c4070ea79cd21ff3fca54522ebaa Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 12 Jun 2019 00:23:53 +0200 Subject: [PATCH 004/166] mv mapping -> state, rm x/ibc --- store/{mapping => state}/base.go | 2 +- store/{mapping => state}/boolean.go | 2 +- store/{mapping => state}/enum.go | 2 +- store/{mapping => state}/indexer.go | 2 +- store/{mapping => state}/integer.go | 2 +- store/{mapping => state}/mapping.go | 2 +- store/{mapping => state}/test_common.go | 7 +- store/{mapping => state}/types.go | 2 +- store/{mapping => state}/value.go | 2 +- store/{mapping => state}/value_test.go | 2 +- x/ibc/app_test.go | 88 ---------- x/ibc/client/cli/README.md | 157 ------------------ x/ibc/client/cli/ibctx.go | 73 -------- x/ibc/client/cli/relay.go | 210 ------------------------ x/ibc/client/rest/transfer.go | 62 ------- x/ibc/codec.go | 11 -- x/ibc/errors.go | 50 ------ x/ibc/expected_keepers.go | 9 - x/ibc/handler.go | 60 ------- x/ibc/handler_test.go | 19 --- x/ibc/ibc_test.go | 160 ------------------ x/ibc/mapper.go | 130 --------------- x/ibc/types.go | 125 -------------- x/ibc/types_test.go | 111 ------------- 24 files changed, 14 insertions(+), 1276 deletions(-) rename store/{mapping => state}/base.go (98%) rename store/{mapping => state}/boolean.go (96%) rename store/{mapping => state}/enum.go (97%) rename store/{mapping => state}/indexer.go (99%) rename store/{mapping => state}/integer.go (98%) rename store/{mapping => state}/mapping.go (98%) rename store/{mapping => state}/test_common.go (93%) rename store/{mapping => state}/types.go (87%) rename store/{mapping => state}/value.go (99%) rename store/{mapping => state}/value_test.go (97%) delete mode 100644 x/ibc/app_test.go delete mode 100644 x/ibc/client/cli/README.md delete mode 100644 x/ibc/client/cli/ibctx.go delete mode 100644 x/ibc/client/cli/relay.go delete mode 100644 x/ibc/client/rest/transfer.go delete mode 100644 x/ibc/codec.go delete mode 100644 x/ibc/errors.go delete mode 100644 x/ibc/expected_keepers.go delete mode 100644 x/ibc/handler.go delete mode 100644 x/ibc/handler_test.go delete mode 100644 x/ibc/ibc_test.go delete mode 100644 x/ibc/mapper.go delete mode 100644 x/ibc/types.go delete mode 100644 x/ibc/types_test.go diff --git a/store/mapping/base.go b/store/state/base.go similarity index 98% rename from store/mapping/base.go rename to store/state/base.go index 4fb049d3240e..25760b77a947 100644 --- a/store/mapping/base.go +++ b/store/state/base.go @@ -1,4 +1,4 @@ -package mapping +package state import ( // "github.com/tendermint/tendermint/crypto/merkle" diff --git a/store/mapping/boolean.go b/store/state/boolean.go similarity index 96% rename from store/mapping/boolean.go rename to store/state/boolean.go index aa6ae0d39c99..c691ceaf9a00 100644 --- a/store/mapping/boolean.go +++ b/store/state/boolean.go @@ -1,4 +1,4 @@ -package mapping +package state type Boolean struct { Enum diff --git a/store/mapping/enum.go b/store/state/enum.go similarity index 97% rename from store/mapping/enum.go rename to store/state/enum.go index 85f973fef144..a44f820a6e5d 100644 --- a/store/mapping/enum.go +++ b/store/state/enum.go @@ -1,4 +1,4 @@ -package mapping +package state type Enum struct { Value diff --git a/store/mapping/indexer.go b/store/state/indexer.go similarity index 99% rename from store/mapping/indexer.go rename to store/state/indexer.go index 28603fc907c2..212be3d0f603 100644 --- a/store/mapping/indexer.go +++ b/store/state/indexer.go @@ -1,4 +1,4 @@ -package mapping +package state import ( "encoding/binary" diff --git a/store/mapping/integer.go b/store/state/integer.go similarity index 98% rename from store/mapping/integer.go rename to store/state/integer.go index 9b74f7828130..758656e29679 100644 --- a/store/mapping/integer.go +++ b/store/state/integer.go @@ -1,4 +1,4 @@ -package mapping +package state type Integer struct { Value diff --git a/store/mapping/mapping.go b/store/state/mapping.go similarity index 98% rename from store/mapping/mapping.go rename to store/state/mapping.go index 1d018ae38d9c..eaf0278ebf3f 100644 --- a/store/mapping/mapping.go +++ b/store/state/mapping.go @@ -1,4 +1,4 @@ -package mapping +package state type Mapping struct { base Base diff --git a/store/mapping/test_common.go b/store/state/test_common.go similarity index 93% rename from store/mapping/test_common.go rename to store/state/test_common.go index 250410f88ced..691b1f0d294b 100644 --- a/store/mapping/test_common.go +++ b/store/state/test_common.go @@ -1,4 +1,4 @@ -package mapping +package state import ( // "testing" @@ -45,7 +45,10 @@ func defaultComponents() (sdk.StoreKey, Context, *codec.Codec) { db := dbm.NewMemDB() cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) - cms.LoadLatestVersion() + err := cms.LoadLatestVersion() + if err != nil { + panic(err) + } ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) cdc := codec.New() cdc.RegisterInterface((*test)(nil), nil) diff --git a/store/mapping/types.go b/store/state/types.go similarity index 87% rename from store/mapping/types.go rename to store/state/types.go index 2454084324ec..a8d34d582a82 100644 --- a/store/mapping/types.go +++ b/store/state/types.go @@ -1,4 +1,4 @@ -package mapping +package state import ( sdk "github.com/cosmos/cosmos-sdk/types" diff --git a/store/mapping/value.go b/store/state/value.go similarity index 99% rename from store/mapping/value.go rename to store/state/value.go index c61a64049c56..cb5d7699168c 100644 --- a/store/mapping/value.go +++ b/store/state/value.go @@ -1,4 +1,4 @@ -package mapping +package state import ( "fmt" diff --git a/store/mapping/value_test.go b/store/state/value_test.go similarity index 97% rename from store/mapping/value_test.go rename to store/state/value_test.go index f92ce7327587..24e4e708c458 100644 --- a/store/mapping/value_test.go +++ b/store/state/value_test.go @@ -1,4 +1,4 @@ -package mapping +package state import ( "math/rand" diff --git a/x/ibc/app_test.go b/x/ibc/app_test.go deleted file mode 100644 index ca51ae9d8b6f..000000000000 --- a/x/ibc/app_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package ibc - -import ( - "testing" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/cosmos/cosmos-sdk/x/mock" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/secp256k1" -) - -// initialize the mock application for this module -func getMockApp(t *testing.T) *mock.App { - mapp := mock.NewApp() - - RegisterCodec(mapp.Cdc) - keyIBC := sdk.NewKVStoreKey("ibc") - ibcMapper := NewMapper(mapp.Cdc, keyIBC, DefaultCodespace) - bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper, - mapp.ParamsKeeper.Subspace(bank.DefaultParamspace), - bank.DefaultCodespace) - mapp.Router().AddRoute("ibc", NewHandler(ibcMapper, bankKeeper)) - - require.NoError(t, mapp.CompleteSetup(keyIBC)) - return mapp -} - -func TestIBCMsgs(t *testing.T) { - mapp := getMockApp(t) - - sourceChain := "source-chain" - destChain := "dest-chain" - - priv1 := secp256k1.GenPrivKey() - addr1 := sdk.AccAddress(priv1.PubKey().Address()) - coins := sdk.Coins{sdk.NewInt64Coin("foocoin", 10)} - var emptyCoins sdk.Coins - - acc := &auth.BaseAccount{ - Address: addr1, - Coins: coins, - } - accs := []auth.Account{acc} - - mock.SetGenesis(mapp, accs) - - // A checkTx context (true) - ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{}) - res1 := mapp.AccountKeeper.GetAccount(ctxCheck, addr1) - require.Equal(t, acc, res1) - - packet := IBCPacket{ - SrcAddr: addr1, - DestAddr: addr1, - Coins: coins, - SrcChain: sourceChain, - DestChain: destChain, - } - - transferMsg := MsgIBCTransfer{ - IBCPacket: packet, - } - - receiveMsg := MsgIBCReceive{ - IBCPacket: packet, - Relayer: addr1, - Sequence: 0, - } - - header := abci.Header{Height: mapp.LastBlockHeight() + 1} - mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, header, []sdk.Msg{transferMsg}, []uint64{0}, []uint64{0}, true, true, priv1) - mock.CheckBalance(t, mapp, addr1, emptyCoins) - - header = abci.Header{Height: mapp.LastBlockHeight() + 1} - mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, header, []sdk.Msg{transferMsg}, []uint64{0}, []uint64{1}, false, false, priv1) - - header = abci.Header{Height: mapp.LastBlockHeight() + 1} - mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, header, []sdk.Msg{receiveMsg}, []uint64{0}, []uint64{2}, true, true, priv1) - mock.CheckBalance(t, mapp, addr1, coins) - - header = abci.Header{Height: mapp.LastBlockHeight() + 1} - mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, header, []sdk.Msg{receiveMsg}, []uint64{0}, []uint64{2}, false, false, priv1) -} diff --git a/x/ibc/client/cli/README.md b/x/ibc/client/cli/README.md deleted file mode 100644 index ab9e8e555840..000000000000 --- a/x/ibc/client/cli/README.md +++ /dev/null @@ -1,157 +0,0 @@ -# IBC Doubble Hubble - -## Remove remaining data - -```console -> rm -r ~/.chain1 -> rm -r ~/.chain2 -> rm -r ~/.basecli -``` - -## Initialize both chains - -```console -> basecoind init --home ~/.chain1 -I[04-02|14:03:33.704] Generated private validator module=main path=/home/mossid/.chain1/config/priv_validator.json -I[04-02|14:03:33.705] Generated genesis file module=main path=/home/mossid/.chain1/config/genesis.json -{ - "secret": "crunch ignore trigger neither differ dance cheap brick situate floor luxury citizen husband decline arrow abandon", - "account": "C69FEB398A29AAB1B3C4F07DE22208F35E711BCC", - "validator": { - "pub_key": { - "type": "ed25519", - "data": "8C9917D5E982E221F5A1450103102B44BBFC1E8768126C606246CB37B5794F4D" - }, - "power": 10, - "name": "" - }, - "node_id": "3ac8e6242315fd62143dc3e52c161edaaa6b1a64", - "chain_id": "test-chain-ZajMfr" -} -> ADDR1=C69FEB398A29AAB1B3C4F07DE22208F35E711BCC -> ID1=test-chain-ZajMfr -> NODE1=tcp://0.0.0.0:36657 -> basecli keys add key1 --recover -Enter a passphrase for your key: -Repeat the passphrase: -Enter your recovery seed phrase: -crunch ignore trigger neither differ dance cheap brick situate floor luxury citizen husband decline arrow abandon -key1 C69FEB398A29AAB1B3C4F07DE22208F35E711BCC - - -> basecoind init --home ~/.chain2 -I[04-02|14:09:14.453] Generated private validator module=main path=/home/mossid/.chain2/config/priv_validator.json -I[04-02|14:09:14.453] Generated genesis file module=main path=/home/mossid/.chain2/config/genesis.json -{ - "secret": "age guide awesome month female left oxygen soccer define high grocery work desert dinner arena abandon", - "account": "DC26002735D3AA9573707CFA6D77C12349E49868", - "validator": { - "pub_key": { - "type": "ed25519", - "data": "A94FE4B9AD763D301F4DD5A2766009812495FB7A79F1275FB8A5AF09B44FD5F3" - }, - "power": 10, - "name": "" - }, - "node_id": "ad26831330e1c72b85276d53c20f0680e6fd4cf5" - "chain_id": "test-chain-4XHTPn" -} -> ADDR2=DC26002735D3AA9573707CFA6D77C12349E49868 -> ID2=test-chain-4XHTPn -> NODE2=tcp://0.0.0.0:26657 -> basecli keys add key2 --recover -Enter a passphrase for your key: -Repeat the passphrase: -Enter your recovery seed phrase: -age guide awesome month female left oxygen soccer define high grocery work desert dinner arena abandon -key2 DC26002735D3AA9573707CFA6D77C12349E49868 - - -> basecoind start --home ~/.chain1 --address tcp://0.0.0.0:36658 --rpc.laddr tcp://0.0.0.0:36657 --p2p.laddr tcp://0.0.0.0:36656 -... - -> basecoind start --home ~/.chain2 # --address tcp://0.0.0.0:26658 --rpc.laddr tcp://0.0.0.0:26657 --p2p.laddr tcp://0.0.0.0:26656 -... -``` -## Check balance - -```console -> basecli account $ADDR1 --node $NODE1 -{ - "address": "C69FEB398A29AAB1B3C4F07DE22208F35E711BCC", - "coins": [ - { - "denom": "mycoin", - "amount": 9007199254740992 - } - ], - "public_key": null, - "sequence": 0, - "name": "" -} - -> basecli account $ADDR2 --node $NODE2 -{ - "address": "DC26002735D3AA9573707CFA6D77C12349E49868", - "coins": [ - { - "denom": "mycoin", - "amount": 9007199254740992 - } - ], - "public_key": null, - "sequence": 0, - "name": "" -} - -``` - -## Transfer coins (addr1:chain1 -> addr2:chain2) - -```console -> basecli transfer --from key1 --to $ADDR2 --amount 10mycoin --chain $ID2 --chain-id $ID1 --node $NODE1 -Password to sign with 'key1': -Committed at block 1022. Hash: E16019DCC4AA08CA70AFCFBC96028ABCC51B6AD0 -> basecli account $ADDR1 --node $NODE1 -{ - "address": "C69FEB398A29AAB1B3C4F07DE22208F35E711BCC", - "coins": [ - { - "denom": "mycoin", - "amount": 9007199254740982 - } - ], - "public_key": { - "type": "ed25519", - "data": "9828FF1780A066A0D93D840737566B697035448D6C880807322BED8919348B2B" - }, - "sequence": 1, - "name": "" -} -``` - -## Relay IBC packets - -```console -> basecli relay --from key2 --from-chain-id $ID1 --from-chain-node $NODE1 --to-chain-id $ID2 --to-chain-node $NODE2 --chain-id $ID2 -Password to sign with 'key2': -I[04-03|16:18:59.984] Detected IBC packet number=0 -I[04-03|16:19:00.869] Relayed IBC packet number=0 -> basecli account $ADDR2 --node $NODE2 -{ - "address": "DC26002735D3AA9573707CFA6D77C12349E49868", - "coins": [ - { - "denom": "mycoin", - "amount": 9007199254741002 - } - ], - "public_key": { - "type": "ed25519", - "data": "F52B4FA545F4E9BFE5D7AF1DD2236899FDEF905F9B3057C38D7C01BF1B8EB52E" - }, - "sequence": 1, - "name": "" -} - -``` diff --git a/x/ibc/client/cli/ibctx.go b/x/ibc/client/cli/ibctx.go deleted file mode 100644 index 9a5ac05ee661..000000000000 --- a/x/ibc/client/cli/ibctx.go +++ /dev/null @@ -1,73 +0,0 @@ -package cli - -import ( - "encoding/hex" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" - "github.com/cosmos/cosmos-sdk/x/ibc" - - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -const ( - flagTo = "to" - flagAmount = "amount" - flagChain = "chain" -) - -// IBCTransferCmd implements the IBC transfer command. -func IBCTransferCmd(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "transfer", - RunE: func(cmd *cobra.Command, args []string) error { - txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithAccountDecoder(cdc) - - from := cliCtx.GetFromAddress() - msg, err := buildMsg(from) - if err != nil { - return err - } - - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) - }, - } - - cmd.Flags().String(flagTo, "", "Address to send coins") - cmd.Flags().String(flagAmount, "", "Amount of coins to send") - cmd.Flags().String(flagChain, "", "Destination chain to send coins") - - return cmd -} - -func buildMsg(from sdk.AccAddress) (sdk.Msg, error) { - amount := viper.GetString(flagAmount) - coins, err := sdk.ParseCoins(amount) - if err != nil { - return nil, err - } - - dest := viper.GetString(flagTo) - bz, err := hex.DecodeString(dest) - if err != nil { - return nil, err - } - to := sdk.AccAddress(bz) - - packet := ibc.NewIBCPacket(from, to, coins, viper.GetString(client.FlagChainID), - viper.GetString(flagChain)) - - msg := ibc.MsgIBCTransfer{ - IBCPacket: packet, - } - - return msg, nil -} diff --git a/x/ibc/client/cli/relay.go b/x/ibc/client/cli/relay.go deleted file mode 100644 index c1c45611eb3f..000000000000 --- a/x/ibc/client/cli/relay.go +++ /dev/null @@ -1,210 +0,0 @@ -package cli - -import ( - "os" - "time" - - "github.com/cosmos/cosmos-sdk/client/utils" - - bam "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/keys" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" - "github.com/cosmos/cosmos-sdk/x/ibc" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/tendermint/tendermint/libs/log" -) - -// flags -const ( - FlagFromChainID = "from-chain-id" - FlagFromChainNode = "from-chain-node" - FlagToChainID = "to-chain-id" - FlagToChainNode = "to-chain-node" -) - -type relayCommander struct { - cdc *codec.Codec - address sdk.AccAddress - decoder auth.AccountDecoder - mainStore string - ibcStore string - accStore string - - logger log.Logger -} - -// IBCRelayCmd implements the IBC relay command. -func IBCRelayCmd(cdc *codec.Codec) *cobra.Command { - cmdr := relayCommander{ - cdc: cdc, - decoder: context.GetAccountDecoder(cdc), - ibcStore: "ibc", - mainStore: bam.MainStoreKey, - accStore: auth.StoreKey, - - logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)), - } - - cmd := &cobra.Command{ - Use: "relay", - Run: cmdr.runIBCRelay, - } - - cmd.Flags().String(FlagFromChainID, "", "Chain ID for ibc node to check outgoing packets") - cmd.Flags().String(FlagFromChainNode, "tcp://localhost:26657", ": to tendermint rpc interface for this chain") - cmd.Flags().String(FlagToChainID, "", "Chain ID for ibc node to broadcast incoming packets") - cmd.Flags().String(FlagToChainNode, "tcp://localhost:36657", ": to tendermint rpc interface for this chain") - - cmd.MarkFlagRequired(FlagFromChainID) - cmd.MarkFlagRequired(FlagFromChainNode) - cmd.MarkFlagRequired(FlagToChainID) - cmd.MarkFlagRequired(FlagToChainNode) - - viper.BindPFlag(FlagFromChainID, cmd.Flags().Lookup(FlagFromChainID)) - viper.BindPFlag(FlagFromChainNode, cmd.Flags().Lookup(FlagFromChainNode)) - viper.BindPFlag(FlagToChainID, cmd.Flags().Lookup(FlagToChainID)) - viper.BindPFlag(FlagToChainNode, cmd.Flags().Lookup(FlagToChainNode)) - - return cmd -} - -// nolint: unparam -func (c relayCommander) runIBCRelay(cmd *cobra.Command, args []string) { - fromChainID := viper.GetString(FlagFromChainID) - fromChainNode := viper.GetString(FlagFromChainNode) - toChainID := viper.GetString(FlagToChainID) - toChainNode := viper.GetString(FlagToChainNode) - - address := context.NewCLIContext().GetFromAddress() - c.address = address - - c.loop(fromChainID, fromChainNode, toChainID, toChainNode) -} - -// This is nolinted as someone is in the process of refactoring this to remove the goto -func (c relayCommander) loop(fromChainID, fromChainNode, toChainID, toChainNode string) { - cliCtx := context.NewCLIContext() - - name := cliCtx.GetFromName() - passphrase, err := keys.ReadPassphraseFromStdin(name) - if err != nil { - panic(err) - } - - ingressKey := ibc.IngressSequenceKey(fromChainID) - lengthKey := ibc.EgressLengthKey(toChainID) - -OUTER: - for { - time.Sleep(5 * time.Second) - - processedbz, err := query(toChainNode, ingressKey, c.ibcStore) - if err != nil { - panic(err) - } - - var processed uint64 - if processedbz == nil { - processed = 0 - } else if err = c.cdc.UnmarshalBinaryLengthPrefixed(processedbz, &processed); err != nil { - panic(err) - } - - egressLengthbz, err := query(fromChainNode, lengthKey, c.ibcStore) - if err != nil { - c.logger.Error("error querying outgoing packet list length", "err", err) - continue OUTER // TODO replace with continue (I think it should just to the correct place where OUTER is now) - } - - var egressLength uint64 - if egressLengthbz == nil { - egressLength = 0 - } else if err = c.cdc.UnmarshalBinaryLengthPrefixed(egressLengthbz, &egressLength); err != nil { - panic(err) - } - - if egressLength > processed { - c.logger.Info("Detected IBC packet", "number", egressLength-1) - } - - seq := c.getSequence(toChainNode) - - for i := processed; i < egressLength; i++ { - egressbz, err := query(fromChainNode, ibc.EgressKey(toChainID, i), c.ibcStore) - if err != nil { - c.logger.Error("error querying egress packet", "err", err) - continue OUTER // TODO replace to break, will break first loop then send back to the beginning (aka OUTER) - } - - err = c.broadcastTx(toChainNode, c.refine(egressbz, i, seq, passphrase)) - - seq++ - - if err != nil { - c.logger.Error("error broadcasting ingress packet", "err", err) - continue OUTER // TODO replace to break, will break first loop then send back to the beginning (aka OUTER) - } - - c.logger.Info("Relayed IBC packet", "number", i) - } - } -} - -func query(node string, key []byte, storeName string) (res []byte, err error) { - return context.NewCLIContext().WithNodeURI(node).QueryStore(key, storeName) -} - -// nolint: unparam -func (c relayCommander) broadcastTx(node string, tx []byte) error { - _, err := context.NewCLIContext().WithNodeURI(node).BroadcastTx(tx) - return err -} - -func (c relayCommander) getSequence(node string) uint64 { - res, err := query(node, auth.AddressStoreKey(c.address), c.accStore) - if err != nil { - panic(err) - } - - if nil != res { - account, err := c.decoder(res) - if err != nil { - panic(err) - } - - return account.GetSequence() - } - - return 0 -} - -func (c relayCommander) refine(bz []byte, ibcSeq, accSeq uint64, passphrase string) []byte { - var packet ibc.IBCPacket - if err := c.cdc.UnmarshalBinaryLengthPrefixed(bz, &packet); err != nil { - panic(err) - } - - msg := ibc.MsgIBCReceive{ - IBCPacket: packet, - Relayer: c.address, - Sequence: ibcSeq, - } - - txBldr := authtxb.NewTxBuilderFromCLI().WithSequence(accSeq).WithTxEncoder(utils.GetTxEncoder(c.cdc)) - cliCtx := context.NewCLIContext() - - name := cliCtx.GetFromName() - res, err := txBldr.BuildAndSign(name, passphrase, []sdk.Msg{msg}) - if err != nil { - panic(err) - } - - return res -} diff --git a/x/ibc/client/rest/transfer.go b/x/ibc/client/rest/transfer.go deleted file mode 100644 index ee2b4da8cdf1..000000000000 --- a/x/ibc/client/rest/transfer.go +++ /dev/null @@ -1,62 +0,0 @@ -package rest - -import ( - "net/http" - - "github.com/cosmos/cosmos-sdk/client/context" - clientrest "github.com/cosmos/cosmos-sdk/client/rest" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keys" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/ibc" - - "github.com/gorilla/mux" -) - -// RegisterRoutes - Central function to define routes that get registered by the main application -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, kb keys.Keybase) { - r.HandleFunc("/ibc/{destchain}/{address}/send", TransferRequestHandlerFn(cdc, kb, cliCtx)).Methods("POST") -} - -type transferReq struct { - BaseReq rest.BaseReq `json:"base_req"` - Amount sdk.Coins `json:"amount"` -} - -// TransferRequestHandler - http request handler to transfer coins to a address -// on a different chain via IBC. -func TransferRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - destChainID := vars["destchain"] - bech32Addr := vars["address"] - - to, err := sdk.AccAddressFromBech32(bech32Addr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - var req transferReq - if !rest.ReadRESTReq(w, r, cdc, &req) { - return - } - - req.BaseReq = req.BaseReq.Sanitize() - if !req.BaseReq.ValidateBasic(w) { - return - } - - from, err := sdk.AccAddressFromBech32(req.BaseReq.From) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - packet := ibc.NewIBCPacket(from, to, req.Amount, req.BaseReq.ChainID, destChainID) - msg := ibc.MsgIBCTransfer{IBCPacket: packet} - - clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) - } -} diff --git a/x/ibc/codec.go b/x/ibc/codec.go deleted file mode 100644 index d47abde1abbf..000000000000 --- a/x/ibc/codec.go +++ /dev/null @@ -1,11 +0,0 @@ -package ibc - -import ( - "github.com/cosmos/cosmos-sdk/codec" -) - -// Register concrete types on codec codec -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(MsgIBCTransfer{}, "cosmos-sdk/MsgIBCTransfer", nil) - cdc.RegisterConcrete(MsgIBCReceive{}, "cosmos-sdk/MsgIBCReceive", nil) -} diff --git a/x/ibc/errors.go b/x/ibc/errors.go deleted file mode 100644 index 96ae58066a5c..000000000000 --- a/x/ibc/errors.go +++ /dev/null @@ -1,50 +0,0 @@ -package ibc - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// IBC errors reserve 200 ~ 299. -const ( - DefaultCodespace sdk.CodespaceType = "ibc" - - // IBC errors reserve 200 - 299. - CodeInvalidSequence sdk.CodeType = 200 - CodeIdenticalChains sdk.CodeType = 201 - CodeUnknownRequest sdk.CodeType = sdk.CodeUnknownRequest -) - -func codeToDefaultMsg(code sdk.CodeType) string { - switch code { - case CodeInvalidSequence: - return "invalid IBC packet sequence" - case CodeIdenticalChains: - return "source and destination chain cannot be identical" - default: - return sdk.CodeToDefaultMsg(code) - } -} - -// nolint -func ErrInvalidSequence(codespace sdk.CodespaceType) sdk.Error { - return newError(codespace, CodeInvalidSequence, "") -} -func ErrIdenticalChains(codespace sdk.CodespaceType) sdk.Error { - return newError(codespace, CodeIdenticalChains, "") -} - -// ------------------------- -// Helpers - -// nolint: unparam -func newError(codespace sdk.CodespaceType, code sdk.CodeType, msg string) sdk.Error { - msg = msgOrDefaultMsg(msg, code) - return sdk.NewError(codespace, code, msg) -} - -func msgOrDefaultMsg(msg string, code sdk.CodeType) string { - if msg != "" { - return msg - } - return codeToDefaultMsg(code) -} diff --git a/x/ibc/expected_keepers.go b/x/ibc/expected_keepers.go deleted file mode 100644 index 54b70b4dd8ef..000000000000 --- a/x/ibc/expected_keepers.go +++ /dev/null @@ -1,9 +0,0 @@ -package ibc - -import sdk "github.com/cosmos/cosmos-sdk/types" - -// expected bank keeper -type BankKeeper interface { - AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Error) - SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Error) -} diff --git a/x/ibc/handler.go b/x/ibc/handler.go deleted file mode 100644 index b1b958218a00..000000000000 --- a/x/ibc/handler.go +++ /dev/null @@ -1,60 +0,0 @@ -package ibc - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func NewHandler(ibcm Mapper, ck BankKeeper) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { - switch msg := msg.(type) { - case MsgIBCTransfer: - return handleIBCTransferMsg(ctx, ibcm, ck, msg) - - case MsgIBCReceive: - return handleIBCReceiveMsg(ctx, ibcm, ck, msg) - - default: - errMsg := fmt.Sprintf("unrecognized IBC message type: %T", msg) - return sdk.ErrUnknownRequest(errMsg).Result() - } - } -} - -// MsgIBCTransfer deducts coins from the account and creates an egress IBC packet. -func handleIBCTransferMsg(ctx sdk.Context, ibcm Mapper, ck BankKeeper, msg MsgIBCTransfer) sdk.Result { - packet := msg.IBCPacket - - _, err := ck.SubtractCoins(ctx, packet.SrcAddr, packet.Coins) - if err != nil { - return err.Result() - } - - err = ibcm.PostIBCPacket(ctx, packet) - if err != nil { - return err.Result() - } - - return sdk.Result{} -} - -// MsgIBCReceive adds coins to the destination address and creates an ingress IBC packet. -func handleIBCReceiveMsg(ctx sdk.Context, ibcm Mapper, ck BankKeeper, msg MsgIBCReceive) sdk.Result { - packet := msg.IBCPacket - - seq := ibcm.GetIngressSequence(ctx, packet.SrcChain) - if msg.Sequence != seq { - return ErrInvalidSequence(ibcm.codespace).Result() - } - - // XXX Check that packet.Coins is valid and positive (nonzero) - _, err := ck.AddCoins(ctx, packet.DestAddr, packet.Coins) - if err != nil { - return err.Result() - } - - ibcm.SetIngressSequence(ctx, packet.SrcChain, seq+1) - - return sdk.Result{} -} diff --git a/x/ibc/handler_test.go b/x/ibc/handler_test.go deleted file mode 100644 index fca563474737..000000000000 --- a/x/ibc/handler_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package ibc - -import ( - "strings" - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/stretchr/testify/require" -) - -func TestInvalidMsg(t *testing.T) { - m := Mapper{} - h := NewHandler(m, nil) - - res := h(sdk.Context{}, sdk.NewTestMsg()) - require.False(t, res.IsOK()) - require.True(t, strings.Contains(res.Log, "unrecognized IBC message type")) -} diff --git a/x/ibc/ibc_test.go b/x/ibc/ibc_test.go deleted file mode 100644 index 3492b723c108..000000000000 --- a/x/ibc/ibc_test.go +++ /dev/null @@ -1,160 +0,0 @@ -package ibc - -import ( - "testing" - - "github.com/stretchr/testify/require" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/ed25519" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/cosmos/cosmos-sdk/x/params" -) - -// AccountKeeper(/Keeper) and IBCMapper should use different StoreKey later - -type testInput struct { - cdc *codec.Codec - ctx sdk.Context - ak auth.AccountKeeper - bk bank.BaseKeeper - ibcKey *sdk.KVStoreKey -} - -func setupTestInput() testInput { - db := dbm.NewMemDB() - cdc := makeCodec() - - ibcKey := sdk.NewKVStoreKey("ibcCapKey") - authCapKey := sdk.NewKVStoreKey("authCapKey") - fckCapKey := sdk.NewKVStoreKey("fckCapKey") - keyParams := sdk.NewKVStoreKey("params") - tkeyParams := sdk.NewTransientStoreKey("transient_params") - - ms := store.NewCommitMultiStore(db) - ms.MountStoreWithDB(ibcKey, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(authCapKey, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(fckCapKey, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db) - ms.LoadLatestVersion() - - pk := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace) - ak := auth.NewAccountKeeper( - cdc, authCapKey, pk.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount, - ) - bk := bank.NewBaseKeeper(ak, pk.Subspace(bank.DefaultParamspace), bank.DefaultCodespace) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "test-chain-id"}, false, log.NewNopLogger()) - - ak.SetParams(ctx, auth.DefaultParams()) - - return testInput{cdc: cdc, ctx: ctx, ak: ak, bk: bk, ibcKey: ibcKey} -} - -func makeCodec() *codec.Codec { - var cdc = codec.New() - - // Register Msgs - cdc.RegisterInterface((*sdk.Msg)(nil), nil) - cdc.RegisterConcrete(bank.MsgSend{}, "test/ibc/Send", nil) - cdc.RegisterConcrete(MsgIBCTransfer{}, "test/ibc/MsgIBCTransfer", nil) - cdc.RegisterConcrete(MsgIBCReceive{}, "test/ibc/MsgIBCReceive", nil) - - // Register AppAccount - cdc.RegisterInterface((*auth.Account)(nil), nil) - cdc.RegisterConcrete(&auth.BaseAccount{}, "test/ibc/Account", nil) - codec.RegisterCrypto(cdc) - - cdc.Seal() - - return cdc -} - -func newAddress() sdk.AccAddress { - return sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) -} - -func getCoins(ck bank.Keeper, ctx sdk.Context, addr sdk.AccAddress) (sdk.Coins, sdk.Error) { - zero := sdk.Coins(nil) - coins, err := ck.AddCoins(ctx, addr, zero) - return coins, err -} - -func TestIBC(t *testing.T) { - input := setupTestInput() - ctx := input.ctx - - src := newAddress() - dest := newAddress() - chainid := "ibcchain" - zero := sdk.Coins(nil) - mycoins := sdk.Coins{sdk.NewInt64Coin("mycoin", 10)} - - coins, err := input.bk.AddCoins(ctx, src, mycoins) - require.Nil(t, err) - require.Equal(t, mycoins, coins) - - ibcm := NewMapper(input.cdc, input.ibcKey, DefaultCodespace) - h := NewHandler(ibcm, input.bk) - packet := IBCPacket{ - SrcAddr: src, - DestAddr: dest, - Coins: mycoins, - SrcChain: chainid, - DestChain: chainid, - } - - store := ctx.KVStore(input.ibcKey) - - var msg sdk.Msg - var res sdk.Result - var egl uint64 - var igs uint64 - - egl = ibcm.getEgressLength(store, chainid) - require.Equal(t, egl, uint64(0)) - - msg = MsgIBCTransfer{ - IBCPacket: packet, - } - res = h(ctx, msg) - require.True(t, res.IsOK()) - - coins, err = getCoins(input.bk, ctx, src) - require.Nil(t, err) - require.Equal(t, zero, coins) - - egl = ibcm.getEgressLength(store, chainid) - require.Equal(t, egl, uint64(1)) - - igs = ibcm.GetIngressSequence(ctx, chainid) - require.Equal(t, igs, uint64(0)) - - msg = MsgIBCReceive{ - IBCPacket: packet, - Relayer: src, - Sequence: 0, - } - res = h(ctx, msg) - require.True(t, res.IsOK()) - - coins, err = getCoins(input.bk, ctx, dest) - require.Nil(t, err) - require.Equal(t, mycoins, coins) - - igs = ibcm.GetIngressSequence(ctx, chainid) - require.Equal(t, igs, uint64(1)) - - res = h(ctx, msg) - require.False(t, res.IsOK()) - - igs = ibcm.GetIngressSequence(ctx, chainid) - require.Equal(t, igs, uint64(1)) -} diff --git a/x/ibc/mapper.go b/x/ibc/mapper.go deleted file mode 100644 index 101fac03393e..000000000000 --- a/x/ibc/mapper.go +++ /dev/null @@ -1,130 +0,0 @@ -package ibc - -import ( - "fmt" - - codec "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// IBC Mapper -type Mapper struct { - key sdk.StoreKey - cdc *codec.Codec - codespace sdk.CodespaceType -} - -// XXX: The Mapper should not take a CoinKeeper. Rather have the CoinKeeper -// take an Mapper. -func NewMapper(cdc *codec.Codec, key sdk.StoreKey, codespace sdk.CodespaceType) Mapper { - // XXX: How are these codecs supposed to work? - return Mapper{ - key: key, - cdc: cdc, - codespace: codespace, - } -} - -// XXX: This is not the public API. This will change in MVP2 and will henceforth -// only be invoked from another module directly and not through a user -// transaction. -// TODO: Handle invalid IBC packets and return errors. -func (ibcm Mapper) PostIBCPacket(ctx sdk.Context, packet IBCPacket) sdk.Error { - // write everything into the state - store := ctx.KVStore(ibcm.key) - index := ibcm.getEgressLength(store, packet.DestChain) - bz, err := ibcm.cdc.MarshalBinaryLengthPrefixed(packet) - if err != nil { - panic(err) - } - - store.Set(EgressKey(packet.DestChain, index), bz) - bz, err = ibcm.cdc.MarshalBinaryLengthPrefixed(index + 1) - if err != nil { - panic(err) - } - store.Set(EgressLengthKey(packet.DestChain), bz) - - return nil -} - -// XXX: In the future every module is able to register it's own handler for -// handling it's own IBC packets. The "ibc" handler will only route the packets -// to the appropriate callbacks. -// XXX: For now this handles all interactions with the CoinKeeper. -// XXX: This needs to do some authentication checking. -func (ibcm Mapper) ReceiveIBCPacket(ctx sdk.Context, packet IBCPacket) sdk.Error { - return nil -} - -// -------------------------- -// Functions for accessing the underlying KVStore. - -func marshalBinaryPanic(cdc *codec.Codec, value interface{}) []byte { - res, err := cdc.MarshalBinaryLengthPrefixed(value) - if err != nil { - panic(err) - } - return res -} - -func unmarshalBinaryPanic(cdc *codec.Codec, bz []byte, ptr interface{}) { - err := cdc.UnmarshalBinaryLengthPrefixed(bz, ptr) - if err != nil { - panic(err) - } -} - -// TODO add description -func (ibcm Mapper) GetIngressSequence(ctx sdk.Context, srcChain string) uint64 { - store := ctx.KVStore(ibcm.key) - key := IngressSequenceKey(srcChain) - - bz := store.Get(key) - if bz == nil { - zero := marshalBinaryPanic(ibcm.cdc, int64(0)) - store.Set(key, zero) - return 0 - } - - var res uint64 - unmarshalBinaryPanic(ibcm.cdc, bz, &res) - return res -} - -// TODO add description -func (ibcm Mapper) SetIngressSequence(ctx sdk.Context, srcChain string, sequence uint64) { - store := ctx.KVStore(ibcm.key) - key := IngressSequenceKey(srcChain) - - bz := marshalBinaryPanic(ibcm.cdc, sequence) - store.Set(key, bz) -} - -// Retrieves the index of the currently stored outgoing IBC packets. -func (ibcm Mapper) getEgressLength(store sdk.KVStore, destChain string) uint64 { - bz := store.Get(EgressLengthKey(destChain)) - if bz == nil { - zero := marshalBinaryPanic(ibcm.cdc, int64(0)) - store.Set(EgressLengthKey(destChain), zero) - return 0 - } - var res uint64 - unmarshalBinaryPanic(ibcm.cdc, bz, &res) - return res -} - -// Stores an outgoing IBC packet under "egress/chain_id/index". -func EgressKey(destChain string, index uint64) []byte { - return []byte(fmt.Sprintf("egress/%s/%d", destChain, index)) -} - -// Stores the number of outgoing IBC packets under "egress/index". -func EgressLengthKey(destChain string) []byte { - return []byte(fmt.Sprintf("egress/%s", destChain)) -} - -// Stores the sequence number of incoming IBC packet under "ingress/index". -func IngressSequenceKey(srcChain string) []byte { - return []byte(fmt.Sprintf("ingress/%s", srcChain)) -} diff --git a/x/ibc/types.go b/x/ibc/types.go deleted file mode 100644 index 3b626fdf8d27..000000000000 --- a/x/ibc/types.go +++ /dev/null @@ -1,125 +0,0 @@ -package ibc - -import ( - "encoding/json" - - codec "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -var ( - msgCdc *codec.Codec -) - -func init() { - msgCdc = codec.New() -} - -// ------------------------------ -// IBCPacket - -// nolint - TODO rename to Packet as IBCPacket stutters (golint) -// IBCPacket defines a piece of data that can be send between two separate -// blockchains. -type IBCPacket struct { - SrcAddr sdk.AccAddress `json:"src_addr"` - DestAddr sdk.AccAddress `json:"dest_addr"` - Coins sdk.Coins `json:"coins"` - SrcChain string `json:"src_chain"` - DestChain string `json:"dest_chain"` -} - -func NewIBCPacket(srcAddr sdk.AccAddress, destAddr sdk.AccAddress, coins sdk.Coins, - srcChain string, destChain string) IBCPacket { - - return IBCPacket{ - SrcAddr: srcAddr, - DestAddr: destAddr, - Coins: coins, - SrcChain: srcChain, - DestChain: destChain, - } -} - -//nolint -func (p IBCPacket) GetSignBytes() []byte { - b, err := msgCdc.MarshalJSON(p) - if err != nil { - panic(err) - } - return sdk.MustSortJSON(b) -} - -// validator the ibc packey -func (p IBCPacket) ValidateBasic() sdk.Error { - if p.SrcChain == p.DestChain { - return ErrIdenticalChains(DefaultCodespace).TraceSDK("") - } - if !p.Coins.IsValid() { - return sdk.ErrInvalidCoins("") - } - return nil -} - -// ---------------------------------- -// MsgIBCTransfer - -// nolint - TODO rename to TransferMsg as folks will reference with ibc.TransferMsg -// MsgIBCTransfer defines how another module can send an IBCPacket. -type MsgIBCTransfer struct { - IBCPacket -} - -// nolint -func (msg MsgIBCTransfer) Route() string { return "ibc" } -func (msg MsgIBCTransfer) Type() string { return "transfer" } - -// x/bank/tx.go MsgSend.GetSigners() -func (msg MsgIBCTransfer) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.SrcAddr} } - -// get the sign bytes for ibc transfer message -func (msg MsgIBCTransfer) GetSignBytes() []byte { - return msg.IBCPacket.GetSignBytes() -} - -// validate ibc transfer message -func (msg MsgIBCTransfer) ValidateBasic() sdk.Error { - return msg.IBCPacket.ValidateBasic() -} - -// ---------------------------------- -// MsgIBCReceive - -// nolint - TODO rename to ReceiveMsg as folks will reference with ibc.ReceiveMsg -// MsgIBCReceive defines the message that a relayer uses to post an IBCPacket -// to the destination chain. -type MsgIBCReceive struct { - IBCPacket - Relayer sdk.AccAddress - Sequence uint64 -} - -// nolint -func (msg MsgIBCReceive) Route() string { return "ibc" } -func (msg MsgIBCReceive) Type() string { return "receive" } -func (msg MsgIBCReceive) ValidateBasic() sdk.Error { return msg.IBCPacket.ValidateBasic() } - -// x/bank/tx.go MsgSend.GetSigners() -func (msg MsgIBCReceive) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Relayer} } - -// get the sign bytes for ibc receive message -func (msg MsgIBCReceive) GetSignBytes() []byte { - b, err := msgCdc.MarshalJSON(struct { - IBCPacket json.RawMessage - Relayer sdk.AccAddress - Sequence uint64 - }{ - IBCPacket: json.RawMessage(msg.IBCPacket.GetSignBytes()), - Relayer: msg.Relayer, - Sequence: msg.Sequence, - }) - if err != nil { - panic(err) - } - return sdk.MustSortJSON(b) -} diff --git a/x/ibc/types_test.go b/x/ibc/types_test.go deleted file mode 100644 index 674b4498215e..000000000000 --- a/x/ibc/types_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package ibc - -import ( - "testing" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// -------------------------------- -// IBCPacket Tests - -func TestIBCPacketValidation(t *testing.T) { - cases := []struct { - valid bool - packet IBCPacket - }{ - {true, constructIBCPacket(true)}, - {false, constructIBCPacket(false)}, - } - - for i, tc := range cases { - err := tc.packet.ValidateBasic() - if tc.valid { - require.Nil(t, err, "%d: %+v", i, err) - } else { - require.NotNil(t, err, "%d", i) - } - } -} - -// ------------------------------- -// MsgIBCTransfer Tests - -func TestIBCTransferMsg(t *testing.T) { - packet := constructIBCPacket(true) - msg := MsgIBCTransfer{packet} - - require.Equal(t, msg.Route(), "ibc") -} - -func TestIBCTransferMsgValidation(t *testing.T) { - validPacket := constructIBCPacket(true) - invalidPacket := constructIBCPacket(false) - - cases := []struct { - valid bool - msg MsgIBCTransfer - }{ - {true, MsgIBCTransfer{validPacket}}, - {false, MsgIBCTransfer{invalidPacket}}, - } - - for i, tc := range cases { - err := tc.msg.ValidateBasic() - if tc.valid { - require.Nil(t, err, "%d: %+v", i, err) - } else { - require.NotNil(t, err, "%d", i) - } - } -} - -// ------------------------------- -// MsgIBCReceive Tests - -func TestIBCReceiveMsg(t *testing.T) { - packet := constructIBCPacket(true) - msg := MsgIBCReceive{packet, sdk.AccAddress([]byte("relayer")), 0} - - require.Equal(t, msg.Route(), "ibc") -} - -func TestIBCReceiveMsgValidation(t *testing.T) { - validPacket := constructIBCPacket(true) - invalidPacket := constructIBCPacket(false) - - cases := []struct { - valid bool - msg MsgIBCReceive - }{ - {true, MsgIBCReceive{validPacket, sdk.AccAddress([]byte("relayer")), 0}}, - {false, MsgIBCReceive{invalidPacket, sdk.AccAddress([]byte("relayer")), 0}}, - } - - for i, tc := range cases { - err := tc.msg.ValidateBasic() - if tc.valid { - require.Nil(t, err, "%d: %+v", i, err) - } else { - require.NotNil(t, err, "%d", i) - } - } -} - -// ------------------------------- -// Helpers - -func constructIBCPacket(valid bool) IBCPacket { - srcAddr := sdk.AccAddress([]byte("source")) - destAddr := sdk.AccAddress([]byte("destination")) - coins := sdk.Coins{sdk.NewInt64Coin("atom", 10)} - srcChain := "source-chain" - destChain := "dest-chain" - - if valid { - return NewIBCPacket(srcAddr, destAddr, coins, srcChain, destChain) - } - return NewIBCPacket(srcAddr, destAddr, coins, srcChain, srcChain) -} From 2b17a39da71b29b1a058da15791a6e8904b13efc Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 25 Jun 2019 16:11:05 +0200 Subject: [PATCH 005/166] rm GetIfExists --- store/state/boolean.go | 4 ---- store/state/enum.go | 8 ++------ store/state/indexer.go | 4 ---- store/state/integer.go | 12 ++---------- store/state/mapping.go | 4 ---- store/state/value.go | 4 ---- 6 files changed, 4 insertions(+), 32 deletions(-) diff --git a/store/state/boolean.go b/store/state/boolean.go index c691ceaf9a00..b442d9cd2f76 100644 --- a/store/state/boolean.go +++ b/store/state/boolean.go @@ -12,10 +12,6 @@ func (v Boolean) Get(ctx Context) bool { return v.Enum.Get(ctx) != 0x00 } -func (v Boolean) GetIfExists(ctx Context) bool { - return v.Enum.GetIfExists(ctx) != 0x00 -} - func (v Boolean) GetSafe(ctx Context) (bool, error) { res, err := v.Enum.GetSafe(ctx) return res != 0x00, err diff --git a/store/state/enum.go b/store/state/enum.go index a44f820a6e5d..3f1f8b4e2c85 100644 --- a/store/state/enum.go +++ b/store/state/enum.go @@ -9,10 +9,6 @@ func NewEnum(v Value) Enum { } func (v Enum) Get(ctx Context) byte { - return v.Value.GetRaw(ctx)[0] -} - -func (v Enum) GetIfExists(ctx Context) byte { res := v.Value.GetRaw(ctx) if res != nil { return res[0] @@ -33,13 +29,13 @@ func (v Enum) Set(ctx Context, value byte) { } func (v Enum) Incr(ctx Context) (res byte) { - res = v.GetIfExists(ctx) + 1 + res = v.Get(ctx) + 1 v.Set(ctx, res) return } func (v Enum) Transit(ctx Context, from, to byte) bool { - if v.GetIfExists(ctx) != from { + if v.Get(ctx) != from { return false } v.Set(ctx, to) diff --git a/store/state/indexer.go b/store/state/indexer.go index 212be3d0f603..281dea761fd5 100644 --- a/store/state/indexer.go +++ b/store/state/indexer.go @@ -64,10 +64,6 @@ func (ix Indexer) Get(ctx Context, index uint64, ptr interface{}) { ix.Value(index).Get(ctx, ptr) } -func (ix Indexer) GetIfExists(ctx Context, index uint64, ptr interface{}) { - ix.Value(index).GetIfExists(ctx, ptr) -} - func (ix Indexer) GetSafe(ctx Context, index uint64, ptr interface{}) error { return ix.Value(index).GetSafe(ctx, ptr) } diff --git a/store/state/integer.go b/store/state/integer.go index 758656e29679..9dd00cf45d1d 100644 --- a/store/state/integer.go +++ b/store/state/integer.go @@ -13,15 +13,7 @@ func NewInteger(v Value, enc IntEncoding) Integer { } } -func (v Integer) Get(ctx Context) uint64 { - res, err := DecodeInt(v.GetRaw(ctx), v.enc) - if err != nil { - panic(err) - } - return res -} - -func (v Integer) GetIfExists(ctx Context) (res uint64) { +func (v Integer) Get(ctx Context) (res uint64) { bz := v.GetRaw(ctx) if bz == nil { return 0 @@ -50,7 +42,7 @@ func (v Integer) Set(ctx Context, value uint64) { } func (v Integer) Incr(ctx Context) (res uint64) { - res = v.GetIfExists(ctx) + 1 + res = v.Get(ctx) + 1 v.Set(ctx, res) return } diff --git a/store/state/mapping.go b/store/state/mapping.go index eaf0278ebf3f..0b45ab3d37db 100644 --- a/store/state/mapping.go +++ b/store/state/mapping.go @@ -24,10 +24,6 @@ func (m Mapping) Get(ctx Context, key []byte, ptr interface{}) { m.Value(key).Get(ctx, ptr) } -func (m Mapping) GetIfExists(ctx Context, key []byte, ptr interface{}) { - m.Value(key).GetIfExists(ctx, ptr) -} - func (m Mapping) GetSafe(ctx Context, key []byte, ptr interface{}) error { return m.Value(key).GetSafe(ctx, ptr) } diff --git a/store/state/value.go b/store/state/value.go index cb5d7699168c..df918aaba9fd 100644 --- a/store/state/value.go +++ b/store/state/value.go @@ -27,10 +27,6 @@ func (v Value) Cdc() *codec.Codec { } func (v Value) Get(ctx Context, ptr interface{}) { - v.base.cdc.MustUnmarshalBinaryBare(v.store(ctx).Get(v.key), ptr) -} - -func (v Value) GetIfExists(ctx Context, ptr interface{}) { bz := v.store(ctx).Get(v.key) if bz != nil { v.base.cdc.MustUnmarshalBinaryBare(bz, ptr) From dc84e1e031464f38bba529af8ca41bd3574ddbcf Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 25 Jun 2019 16:23:04 +0200 Subject: [PATCH 006/166] add key --- store/state/value.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/store/state/value.go b/store/state/value.go index df918aaba9fd..a087db6064ce 100644 --- a/store/state/value.go +++ b/store/state/value.go @@ -65,6 +65,10 @@ func (v Value) Delete(ctx Context) { v.store(ctx).Delete(v.key) } +func (v Value) Key() []byte { + return v.base.key(v.key) +} + type GetSafeErrorType byte const ( From 19d71558f897afcc72432162937ecff930ec0d85 Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 26 Jun 2019 15:23:45 +0200 Subject: [PATCH 007/166] rm custom encoding/decoding in enum/bool --- store/state/boolean.go | 21 +++++++++------------ store/state/enum.go | 20 +++++++------------- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/store/state/boolean.go b/store/state/boolean.go index b442d9cd2f76..2fb3701680be 100644 --- a/store/state/boolean.go +++ b/store/state/boolean.go @@ -1,26 +1,23 @@ package state type Boolean struct { - Enum + Value } func NewBoolean(v Value) Boolean { - return Boolean{NewEnum(v)} + return Boolean{v} } -func (v Boolean) Get(ctx Context) bool { - return v.Enum.Get(ctx) != 0x00 +func (v Boolean) Get(ctx Context) (res bool) { + v.Value.Get(ctx, &res) + return } -func (v Boolean) GetSafe(ctx Context) (bool, error) { - res, err := v.Enum.GetSafe(ctx) - return res != 0x00, err +func (v Boolean) GetSafe(ctx Context) (res bool, err error) { + err = v.Value.GetSafe(ctx, &res) + return } func (v Boolean) Set(ctx Context, value bool) { - if value { - v.Enum.Set(ctx, 0x01) - } else { - v.Enum.Set(ctx, 0x00) - } + v.Value.Set(ctx, value) } diff --git a/store/state/enum.go b/store/state/enum.go index 3f1f8b4e2c85..1cf1318f6652 100644 --- a/store/state/enum.go +++ b/store/state/enum.go @@ -8,24 +8,18 @@ func NewEnum(v Value) Enum { return Enum{v} } -func (v Enum) Get(ctx Context) byte { - res := v.Value.GetRaw(ctx) - if res != nil { - return res[0] - } - return 0x00 +func (v Enum) Get(ctx Context) (res byte) { + v.Value.Get(ctx, &res) + return } -func (v Enum) GetSafe(ctx Context) (byte, error) { - res := v.Value.GetRaw(ctx) - if res == nil { - return 0x00, &GetSafeError{} - } - return res[0], nil +func (v Enum) GetSafe(ctx Context) (res byte, err error) { + v.Value.GetSafe(ctx, &res) + return } func (v Enum) Set(ctx Context, value byte) { - v.Value.SetRaw(ctx, []byte{value}) + v.Value.Set(ctx, value) } func (v Enum) Incr(ctx Context) (res byte) { From 8affaa75b5e35b0571fbe493641ab3d9ffd03dfd Mon Sep 17 00:00:00 2001 From: mossid Date: Sat, 29 Jun 2019 15:10:12 +0200 Subject: [PATCH 008/166] fix lint --- store/state/enum.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/store/state/enum.go b/store/state/enum.go index 1cf1318f6652..8598f10324b7 100644 --- a/store/state/enum.go +++ b/store/state/enum.go @@ -14,7 +14,7 @@ func (v Enum) Get(ctx Context) (res byte) { } func (v Enum) GetSafe(ctx Context) (res byte, err error) { - v.Value.GetSafe(ctx, &res) + err = v.Value.GetSafe(ctx, &res) return } From a7898b457a2a05176191a79d8d273b5218599d13 Mon Sep 17 00:00:00 2001 From: mossid Date: Sun, 7 Jul 2019 18:23:47 +0200 Subject: [PATCH 009/166] rm tests --- store/state/mapping.go | 1 + store/state/test_common.go | 58 -------------------------------------- store/state/value_test.go | 32 --------------------- 3 files changed, 1 insertion(+), 90 deletions(-) delete mode 100644 store/state/test_common.go delete mode 100644 store/state/value_test.go diff --git a/store/state/mapping.go b/store/state/mapping.go index 0b45ab3d37db..127f5a0afb7c 100644 --- a/store/state/mapping.go +++ b/store/state/mapping.go @@ -9,6 +9,7 @@ func NewMapping(base Base, prefix []byte) Mapping { return Mapping{ base: base.Prefix(prefix), start: []byte{}, // preventing nil key access in store.Last + end: nil, } } diff --git a/store/state/test_common.go b/store/state/test_common.go deleted file mode 100644 index 691b1f0d294b..000000000000 --- a/store/state/test_common.go +++ /dev/null @@ -1,58 +0,0 @@ -package state - -import ( - // "testing" - "math/rand" - - // "github.com/stretchr/testify/require" - - abci "github.com/tendermint/tendermint/abci/types" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -const testsize = 10 - -type test interface { - test() -} - -type teststruct struct { - I uint64 - B bool - SL []byte -} - -var _ test = teststruct{} - -func (teststruct) test() {} - -func newtest() test { - var res teststruct - res.I = rand.Uint64() - res.B = rand.Int()%2 == 0 - res.SL = make([]byte, 20) - rand.Read(res.SL) - return res -} - -func defaultComponents() (sdk.StoreKey, Context, *codec.Codec) { - key := sdk.NewKVStoreKey("test") - db := dbm.NewMemDB() - cms := store.NewCommitMultiStore(db) - cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) - err := cms.LoadLatestVersion() - if err != nil { - panic(err) - } - ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) - cdc := codec.New() - cdc.RegisterInterface((*test)(nil), nil) - cdc.RegisterConcrete(teststruct{}, "test/struct", nil) - cdc.Seal() - return key, ctx, cdc -} diff --git a/store/state/value_test.go b/store/state/value_test.go deleted file mode 100644 index 24e4e708c458..000000000000 --- a/store/state/value_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package state - -import ( - "math/rand" - "testing" - - "github.com/stretchr/testify/require" -) - -type valuepair struct { - key []byte - value test -} - -func TestValue(t *testing.T) { - key, ctx, cdc := defaultComponents() - base := NewBase(cdc, key) - - cases := make([]valuepair, testsize) - for i := range cases { - cases[i].key = make([]byte, 20) - rand.Read(cases[i].key) - cases[i].value = newtest() - NewValue(base, cases[i].key).Set(ctx, cases[i].value) - } - - for i := range cases { - var val test - NewValue(base, cases[i].key).Get(ctx, &val) - require.Equal(t, cases[i].value, val) - } -} From afcca90a29b99f7652fbd151eb867272d33eeed9 Mon Sep 17 00:00:00 2001 From: mossid Date: Fri, 7 Jun 2019 23:46:16 +0200 Subject: [PATCH 010/166] add commitment --- x/ibc/23-commitment/context.go | 17 +++++ x/ibc/23-commitment/store.go | 112 +++++++++++++++++++++++++++++++++ x/ibc/23-commitment/types.go | 19 ++++++ x/ibc/23-commitment/value.go | 61 ++++++++++++++++++ 4 files changed, 209 insertions(+) create mode 100644 x/ibc/23-commitment/context.go create mode 100644 x/ibc/23-commitment/store.go create mode 100644 x/ibc/23-commitment/types.go create mode 100644 x/ibc/23-commitment/value.go diff --git a/x/ibc/23-commitment/context.go b/x/ibc/23-commitment/context.go new file mode 100644 index 000000000000..6d8faa9bbcca --- /dev/null +++ b/x/ibc/23-commitment/context.go @@ -0,0 +1,17 @@ +package commitment + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// TODO: define Context type which embeds sdk.Context and ensures the existence of RemoteKVStore + +type ContextKeyRemoteKVStore struct{} + +func WithStore(ctx sdk.Context, store Store) sdk.Context { + return ctx.WithValue(ContextKeyRemoteKVStore{}, store) +} + +func GetStore(ctx sdk.Context) sdk.KVStore { + return ctx.Value(ContextKeyRemoteKVStore{}).(sdk.KVStore) +} diff --git a/x/ibc/23-commitment/store.go b/x/ibc/23-commitment/store.go new file mode 100644 index 000000000000..ad70b28d5cfe --- /dev/null +++ b/x/ibc/23-commitment/store.go @@ -0,0 +1,112 @@ +package commitment + +import ( + "io" + + "github.com/cosmos/cosmos-sdk/store/cachekv" + "github.com/cosmos/cosmos-sdk/store/types" +) + +var _ types.KVStore = Store{} + +// Panics when there is no corresponding proof or the proof is invalid +// (to be compatible with KVStore interface) +// The semantics of the methods are redefined and does not compatible(should be improved) +// Get -> Returns the value if the corresponding proof is already verified +// Set -> Proof corresponding to the provided key is verified with the provided value +// Has -> Returns true if the proof is verified, returns false in any other case +// Delete -> Proof corresponding to the provided key is verified with nil value +// Other methods should not be used +type Store struct { + root Root + proofs map[string]Proof + verified map[string][]byte +} + +// Proofs must be provided +func NewStore(root Root, proofs []Proof, fullProofs []FullProof) (store Store, err error) { + store = Store{ + root: root, + proofs: make(map[string]Proof), + verified: make(map[string][]byte), + } + + for _, proof := range proofs { + store.proofs[string(proof.Key())] = proof + } + + for _, proof := range fullProofs { + err = proof.Verify(root) + if err != nil { + return + } + store.verified[string(proof.Proof.Key())] = proof.Value + } + + return +} + +func (store Store) GetStoreType() types.StoreType { + return types.StoreTypeTransient // XXX: check is it right +} + +func (store Store) CacheWrap() types.CacheWrap { + return cachekv.NewStore(store) +} + +func (store Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { + // FIXME + return store.CacheWrap() +} + +func (store Store) Get(key []byte) []byte { + res, ok := store.verified[string(key)] + if !ok { + panic(UnverifiedKeyError{}) + } + return res +} + +func (store Store) Set(key, value []byte) { + proof, ok := store.proofs[string(key)] + if !ok { + return + } + err := proof.Verify(store.root, key, value) + if err == nil { + store.verified[string(key)] = value + } + + return +} + +// TODO: consider using this method to check whether the proof provided or not +// which may violate KVStore semantics +func (store Store) Has(key []byte) bool { + _, ok := store.verified[string(key)] + return ok +} + +func (store Store) Delete(key []byte) { + store.Set(key, nil) +} + +func (store Store) Iterator(begin, end []byte) types.Iterator { + panic(MethodError{}) +} + +func (store Store) ReverseIterator(begin, end []byte) types.Iterator { + panic(MethodError{}) +} + +type NoProofError struct { + // XXX +} + +type MethodError struct { + // XXX +} + +type UnverifiedKeyError struct { + // XXX +} diff --git a/x/ibc/23-commitment/types.go b/x/ibc/23-commitment/types.go new file mode 100644 index 000000000000..546fd70825eb --- /dev/null +++ b/x/ibc/23-commitment/types.go @@ -0,0 +1,19 @@ +package commitment + +// XXX: []byte? +type Root interface{} + +// XXX: need to separate membership and non membership proof types +type Proof interface { + Key() []byte + Verify(Root, []byte, []byte) error +} + +type FullProof struct { + Proof Proof + Value []byte +} + +func (proof FullProof) Verify(root Root) error { + return proof.Proof.Verify(root, proof.Proof.Key(), proof.Value) +} diff --git a/x/ibc/23-commitment/value.go b/x/ibc/23-commitment/value.go new file mode 100644 index 000000000000..36632cca5a61 --- /dev/null +++ b/x/ibc/23-commitment/value.go @@ -0,0 +1,61 @@ +package commitment + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/store/mapping" +) + +var _ mapping.Value = value{} + +type value struct { + mapping.Value +} + +func Value(base mapping.Base, key []byte) mapping.Value { + return value{mapping.NewValue(base, key)} +} + +func (v value) Is(ctx sdk.Context, value interface{}) bool { + // CONTRACT: commitment.value must be used with commitment.Store as its underlying KVStore + // TODO: enforce it + + v.Set(ctx, value) + return v.Exists(ctx) +} + +var _ mapping.Enum = enum{} + +type enum struct { + mapping.Enum +} + +func Enum(v mapping.Value) mapping.Enum { + return enum{mapping.NewEnum(v)} +} + +func (v enum) Is(ctx sdk.Context, value byte) bool { + // CONTRACT: commitment.enum must be used with commitment.Store as its underlying KVStore + // TODO: enforce it + + v.Set(ctx, value) + return v.Exists(ctx) +} + +var _ mapping.Integer = integer{} + +type integer struct { + mapping.Integer +} + +func Integer(v mapping.Value, enc mapping.IntEncoding) mapping.Integer { + return integer{mapping.NewInteger(v, enc)} +} + +func (v integer) Is(ctx sdk.Context, value uint64) bool { + // CONTRACT: commitment.integer must be used with commitment.Store as its underlying KVStore + // TODO: enforce it + + v.Set(ctx, value) + return v.Exists(ctx) +} From d29b601b58cedd563897b3888c075d30bd8011db Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 11 Jun 2019 17:48:51 +0200 Subject: [PATCH 011/166] newtyped remote values --- store/state/base.go | 2 +- store/state/mapping.go | 2 +- store/state/value.go | 2 +- x/ibc/23-commitment/context.go | 14 +++--- x/ibc/23-commitment/store.go | 80 +++++++--------------------------- x/ibc/23-commitment/value.go | 68 +++++++++++++++-------------- 6 files changed, 62 insertions(+), 106 deletions(-) diff --git a/store/state/base.go b/store/state/base.go index 25760b77a947..f163c18b98a0 100644 --- a/store/state/base.go +++ b/store/state/base.go @@ -25,7 +25,7 @@ func NewBase(cdc *codec.Codec, key sdk.StoreKey) Base { } } -func (base Base) store(ctx Context) KVStore { +func (base Base) Store(ctx Context) KVStore { return prefix.NewStore(base.storefn(ctx), base.prefix) } diff --git a/store/state/mapping.go b/store/state/mapping.go index 127f5a0afb7c..566077a62237 100644 --- a/store/state/mapping.go +++ b/store/state/mapping.go @@ -14,7 +14,7 @@ func NewMapping(base Base, prefix []byte) Mapping { } func (m Mapping) store(ctx Context) KVStore { - return m.base.store(ctx) + return m.base.Store(ctx) } func (m Mapping) Value(key []byte) Value { diff --git a/store/state/value.go b/store/state/value.go index a087db6064ce..e948956e3d57 100644 --- a/store/state/value.go +++ b/store/state/value.go @@ -19,7 +19,7 @@ func NewValue(base Base, key []byte) Value { } func (v Value) store(ctx Context) KVStore { - return v.base.store(ctx) + return v.base.Store(ctx) } func (v Value) Cdc() *codec.Codec { diff --git a/x/ibc/23-commitment/context.go b/x/ibc/23-commitment/context.go index 6d8faa9bbcca..adb3bf27e8c6 100644 --- a/x/ibc/23-commitment/context.go +++ b/x/ibc/23-commitment/context.go @@ -4,14 +4,16 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// TODO: define Context type which embeds sdk.Context and ensures the existence of RemoteKVStore +type Context struct { + sdk.Context +} -type ContextKeyRemoteKVStore struct{} +type contextKeyRemoteKVStore struct{} -func WithStore(ctx sdk.Context, store Store) sdk.Context { - return ctx.WithValue(ContextKeyRemoteKVStore{}, store) +func WithStore(ctx sdk.Context, store Store) Context { + return Context{ctx.WithValue(contextKeyRemoteKVStore{}, store)} } -func GetStore(ctx sdk.Context) sdk.KVStore { - return ctx.Value(ContextKeyRemoteKVStore{}).(sdk.KVStore) +func (ctx Context) Store() Store { + return ctx.Value(contextKeyRemoteKVStore{}).(Store) } diff --git a/x/ibc/23-commitment/store.go b/x/ibc/23-commitment/store.go index ad70b28d5cfe..6a3e2e33fae1 100644 --- a/x/ibc/23-commitment/store.go +++ b/x/ibc/23-commitment/store.go @@ -1,22 +1,9 @@ package commitment import ( - "io" - - "github.com/cosmos/cosmos-sdk/store/cachekv" - "github.com/cosmos/cosmos-sdk/store/types" + "bytes" ) -var _ types.KVStore = Store{} - -// Panics when there is no corresponding proof or the proof is invalid -// (to be compatible with KVStore interface) -// The semantics of the methods are redefined and does not compatible(should be improved) -// Get -> Returns the value if the corresponding proof is already verified -// Set -> Proof corresponding to the provided key is verified with the provided value -// Has -> Returns true if the proof is verified, returns false in any other case -// Delete -> Proof corresponding to the provided key is verified with nil value -// Other methods should not be used type Store struct { root Root proofs map[string]Proof @@ -46,67 +33,30 @@ func NewStore(root Root, proofs []Proof, fullProofs []FullProof) (store Store, e return } -func (store Store) GetStoreType() types.StoreType { - return types.StoreTypeTransient // XXX: check is it right -} - -func (store Store) CacheWrap() types.CacheWrap { - return cachekv.NewStore(store) -} - -func (store Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { - // FIXME - return store.CacheWrap() -} - -func (store Store) Get(key []byte) []byte { +func (store Store) Get(key []byte) ([]byte, bool) { res, ok := store.verified[string(key)] - if !ok { - panic(UnverifiedKeyError{}) - } - return res + return res, ok } -func (store Store) Set(key, value []byte) { +func (store Store) Prove(key, value []byte) bool { + stored, ok := store.Get(key) + if ok && bytes.Equal(stored, value) { + return true + } proof, ok := store.proofs[string(key)] if !ok { - return + return false } err := proof.Verify(store.root, key, value) - if err == nil { - store.verified[string(key)] = value + if err != nil { + return false } + store.verified[string(key)] = value - return + return true } -// TODO: consider using this method to check whether the proof provided or not -// which may violate KVStore semantics -func (store Store) Has(key []byte) bool { - _, ok := store.verified[string(key)] +func (store Store) Proven(key []byte) bool { + _, ok := store.Get(key) return ok } - -func (store Store) Delete(key []byte) { - store.Set(key, nil) -} - -func (store Store) Iterator(begin, end []byte) types.Iterator { - panic(MethodError{}) -} - -func (store Store) ReverseIterator(begin, end []byte) types.Iterator { - panic(MethodError{}) -} - -type NoProofError struct { - // XXX -} - -type MethodError struct { - // XXX -} - -type UnverifiedKeyError struct { - // XXX -} diff --git a/x/ibc/23-commitment/value.go b/x/ibc/23-commitment/value.go index 36632cca5a61..69810320cb98 100644 --- a/x/ibc/23-commitment/value.go +++ b/x/ibc/23-commitment/value.go @@ -1,61 +1,65 @@ package commitment import ( - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/mapping" ) -var _ mapping.Value = value{} +type Base struct { + cdc *codec.Codec + prefix []byte +} -type value struct { - mapping.Value +func NewBase(cdc *codec.Codec) Base { + return Base{ + cdc: cdc, + } } -func Value(base mapping.Base, key []byte) mapping.Value { - return value{mapping.NewValue(base, key)} +func (base Base) Store(ctx Context) Store { + return ctx.Store() } -func (v value) Is(ctx sdk.Context, value interface{}) bool { - // CONTRACT: commitment.value must be used with commitment.Store as its underlying KVStore - // TODO: enforce it +type Value struct { + base Base + key []byte +} - v.Set(ctx, value) - return v.Exists(ctx) +func NewValue(base Base, key []byte) Value { + return Value{base, key} } -var _ mapping.Enum = enum{} +func (v Value) Is(ctx Context, value interface{}) bool { + return v.base.Store(ctx).Prove(v.key, v.base.cdc.MustMarshalBinaryBare(value)) +} -type enum struct { - mapping.Enum +func (v Value) IsRaw(ctx Context, value []byte) bool { + return v.base.Store(ctx).Prove(v.key, value) } -func Enum(v mapping.Value) mapping.Enum { - return enum{mapping.NewEnum(v)} +type Enum struct { + Value } -func (v enum) Is(ctx sdk.Context, value byte) bool { - // CONTRACT: commitment.enum must be used with commitment.Store as its underlying KVStore - // TODO: enforce it +func NewEnum(v Value) Enum { + return Enum{v} +} - v.Set(ctx, value) - return v.Exists(ctx) +func (v Enum) Is(ctx Context, value byte) bool { + return v.Value.IsRaw(ctx, []byte{value}) } -var _ mapping.Integer = integer{} +type Integer struct { + Value -type integer struct { - mapping.Integer + enc mapping.IntEncoding } -func Integer(v mapping.Value, enc mapping.IntEncoding) mapping.Integer { - return integer{mapping.NewInteger(v, enc)} +func NewInteger(v Value, enc mapping.IntEncoding) Integer { + return Integer{v, enc} } -func (v integer) Is(ctx sdk.Context, value uint64) bool { - // CONTRACT: commitment.integer must be used with commitment.Store as its underlying KVStore - // TODO: enforce it - - v.Set(ctx, value) - return v.Exists(ctx) +func (v Integer) Is(ctx Context, value uint64) bool { + return v.Value.IsRaw(ctx, mapping.EncodeInt(value, v.enc)) } From dd86fa965fc580c7b740355a59a5a920aa109e9a Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 11 Jun 2019 18:05:23 +0200 Subject: [PATCH 012/166] newtyped context --- x/ibc/23-commitment/context.go | 2 +- x/ibc/23-commitment/store.go | 40 +++++++++++++++++++++++++++------- x/ibc/23-commitment/value.go | 26 +++++++++++++++++++++- 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/x/ibc/23-commitment/context.go b/x/ibc/23-commitment/context.go index adb3bf27e8c6..a3e4a077e13e 100644 --- a/x/ibc/23-commitment/context.go +++ b/x/ibc/23-commitment/context.go @@ -14,6 +14,6 @@ func WithStore(ctx sdk.Context, store Store) Context { return Context{ctx.WithValue(contextKeyRemoteKVStore{}, store)} } -func (ctx Context) Store() Store { +func (ctx Context) RemoteStore() Store { return ctx.Value(contextKeyRemoteKVStore{}).(Store) } diff --git a/x/ibc/23-commitment/store.go b/x/ibc/23-commitment/store.go index 6a3e2e33fae1..94977a9824ea 100644 --- a/x/ibc/23-commitment/store.go +++ b/x/ibc/23-commitment/store.go @@ -4,22 +4,46 @@ import ( "bytes" ) -type Store struct { +type Store interface { + Prove(key, value []byte) bool +} + +var _ Store = prefix{} + +type prefix struct { + store Store + prefix []byte +} + +func NewPrefix(store Store, pref []byte) prefix { + return prefix{ + store: store, + prefix: pref, + } +} + +func (prefix prefix) Prove(key, value []byte) bool { + return prefix.store.Prove(join(prefix.prefix, key), value) +} + +var _ Store = store{} + +type store struct { root Root proofs map[string]Proof verified map[string][]byte } // Proofs must be provided -func NewStore(root Root, proofs []Proof, fullProofs []FullProof) (store Store, err error) { - store = Store{ +func Newstore(root Root, proofs []Proof, fullProofs []FullProof) (res store, err error) { + res = store{ root: root, proofs: make(map[string]Proof), verified: make(map[string][]byte), } for _, proof := range proofs { - store.proofs[string(proof.Key())] = proof + res.proofs[string(proof.Key())] = proof } for _, proof := range fullProofs { @@ -27,18 +51,18 @@ func NewStore(root Root, proofs []Proof, fullProofs []FullProof) (store Store, e if err != nil { return } - store.verified[string(proof.Proof.Key())] = proof.Value + res.verified[string(proof.Proof.Key())] = proof.Value } return } -func (store Store) Get(key []byte) ([]byte, bool) { +func (store store) Get(key []byte) ([]byte, bool) { res, ok := store.verified[string(key)] return res, ok } -func (store Store) Prove(key, value []byte) bool { +func (store store) Prove(key, value []byte) bool { stored, ok := store.Get(key) if ok && bytes.Equal(stored, value) { return true @@ -56,7 +80,7 @@ func (store Store) Prove(key, value []byte) bool { return true } -func (store Store) Proven(key []byte) bool { +func (store store) Proven(key []byte) bool { _, ok := store.Get(key) return ok } diff --git a/x/ibc/23-commitment/value.go b/x/ibc/23-commitment/value.go index 69810320cb98..77a99d1b44ac 100644 --- a/x/ibc/23-commitment/value.go +++ b/x/ibc/23-commitment/value.go @@ -18,7 +18,31 @@ func NewBase(cdc *codec.Codec) Base { } func (base Base) Store(ctx Context) Store { - return ctx.Store() + return NewPrefix(ctx.RemoteStore(), base.prefix) +} + +func join(a, b []byte) (res []byte) { + res = make([]byte, len(a)+len(b)) + copy(res, a) + copy(res[len(a):], b) + return +} + +func (base Base) Prefix(prefix []byte) Base { + return Base{ + cdc: base.cdc, + prefix: join(base.prefix, prefix), + } +} + +type Mapping struct { + base Base +} + +func NewMapping(base Base, prefix []byte) Mapping { + return Mapping{ + base: base.Prefix(prefix), + } } type Value struct { From 6aad15ff17e1b31d1b7b8e38351d6794de1dfdcd Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 11 Jun 2019 18:41:00 +0200 Subject: [PATCH 013/166] revert context newtype --- x/ibc/23-commitment/context.go | 14 ++++++-------- x/ibc/23-commitment/value.go | 14 +++++++------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/x/ibc/23-commitment/context.go b/x/ibc/23-commitment/context.go index a3e4a077e13e..3680fb6b09bd 100644 --- a/x/ibc/23-commitment/context.go +++ b/x/ibc/23-commitment/context.go @@ -4,16 +4,14 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -type Context struct { - sdk.Context -} +// TODO: define Context type which embeds sdk.Context and ensures the existence of RemoteKVStore -type contextKeyRemoteKVStore struct{} +type ContextKeyRemoteKVStore struct{} -func WithStore(ctx sdk.Context, store Store) Context { - return Context{ctx.WithValue(contextKeyRemoteKVStore{}, store)} +func WithStore(ctx sdk.Context, store Store) sdk.Context { + return ctx.WithValue(ContextKeyRemoteKVStore{}, store) } -func (ctx Context) RemoteStore() Store { - return ctx.Value(contextKeyRemoteKVStore{}).(Store) +func GetStore(ctx sdk.Context) Store { + return ctx.Value(ContextKeyRemoteKVStore{}).(Store) } diff --git a/x/ibc/23-commitment/value.go b/x/ibc/23-commitment/value.go index 77a99d1b44ac..74f72492bf53 100644 --- a/x/ibc/23-commitment/value.go +++ b/x/ibc/23-commitment/value.go @@ -2,8 +2,8 @@ package commitment import ( "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/mapping" + sdk "github.com/cosmos/cosmos-sdk/types" ) type Base struct { @@ -17,8 +17,8 @@ func NewBase(cdc *codec.Codec) Base { } } -func (base Base) Store(ctx Context) Store { - return NewPrefix(ctx.RemoteStore(), base.prefix) +func (base Base) Store(ctx sdk.Context) Store { + return NewPrefix(GetStore(ctx), base.prefix) } func join(a, b []byte) (res []byte) { @@ -54,11 +54,11 @@ func NewValue(base Base, key []byte) Value { return Value{base, key} } -func (v Value) Is(ctx Context, value interface{}) bool { +func (v Value) Is(ctx sdk.Context, value interface{}) bool { return v.base.Store(ctx).Prove(v.key, v.base.cdc.MustMarshalBinaryBare(value)) } -func (v Value) IsRaw(ctx Context, value []byte) bool { +func (v Value) IsRaw(ctx sdk.Context, value []byte) bool { return v.base.Store(ctx).Prove(v.key, value) } @@ -70,7 +70,7 @@ func NewEnum(v Value) Enum { return Enum{v} } -func (v Enum) Is(ctx Context, value byte) bool { +func (v Enum) Is(ctx sdk.Context, value byte) bool { return v.Value.IsRaw(ctx, []byte{value}) } @@ -84,6 +84,6 @@ func NewInteger(v Value, enc mapping.IntEncoding) Integer { return Integer{v, enc} } -func (v Integer) Is(ctx Context, value uint64) bool { +func (v Integer) Is(ctx sdk.Context, value uint64) bool { return v.Value.IsRaw(ctx, mapping.EncodeInt(value, v.enc)) } From 088836dcc2d6784830a911a8e758b51654ff3013 Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 11 Jun 2019 23:37:31 +0200 Subject: [PATCH 014/166] add README, keypath --- x/ibc/23-commitment/README.md | 50 ++++++++++++++++++++++++++++++++++ x/ibc/23-commitment/keypath.go | 27 ++++++++++++++++++ x/ibc/23-commitment/store.go | 12 ++------ x/ibc/23-commitment/types.go | 11 +------- 4 files changed, 80 insertions(+), 20 deletions(-) create mode 100644 x/ibc/23-commitment/README.md create mode 100644 x/ibc/23-commitment/keypath.go diff --git a/x/ibc/23-commitment/README.md b/x/ibc/23-commitment/README.md new file mode 100644 index 000000000000..61a46b99d8a4 --- /dev/null +++ b/x/ibc/23-commitment/README.md @@ -0,0 +1,50 @@ +# ICS 23: Commitment + +Package `commitment` defines types and methods to verify other chain's state. The main type is `Store`, containing +proofs that can be verified when the correct value is provided. The spec functions those are directly related to +verification are: + +## Spec + +```typescript +type verifyMembership = (root: CommitmentRoot, proof: CommitmentProof, key: Key, value: Value) => bool +type verifyNonMembership = (root: CommitmentRoot, proof: CommitmentProof, key: Key) => bool +``` + +## Impl + +### types.go + +`type Proof` implements `spec: type CommitmentProof`. CommitmentProof is an arbitrary object which can be used as +an argument for `spec: verifyMembership` / `spec: verifyNonMembership`, constructed with `spec: createMembershipProof` / +`spec: createNonMembershipProof`. The implementation type `Proof` defines `spec: verify(Non)Membership` as its method +`Verify(Root, []byte) error`, which takes the commitment root and the value bytes argument. The method acts as +`spec: verifyMembership` when the value bytes is not nil, and `spec: verifyNonMembership` if it is nil. + +`type Root` implements `spec: type CommitmentRoot`. + +In Cosmos-SDK implementation, `Root` will be the `AppHash []byte`, and `Proof` will be `merkle.Proof`, which consists +of `SimpleProof` and `IAVLValueProof` + +### store.go + +`Store` assumes that the keys are already known at the time when the transaction is included, so the type `Proof` has +the method `Key() []byte`. The values should also have to be provided in order to verify the proof, but to reduce the +size of the transaction, they are excluded from `Proof` and provided by the application on runtime. + +`NewStore` takes `[]Proof` as its argument, without verifying, since the values are yet unknown. They are stored in +`store.proofs`. + +Proofs can be verified with `store.Prove()` method which takes the key of the proof it will verify and the value +that will be given to the `proof.Verify()`. Verified proofs are stored in `store.verified`. + +### context.go + +All of the ICS internals that requires verification on other chains' state are expected to take `ctx sdk.Context` +argument initialized by `WithStore()`. `WithStore()` sets the `Store` that contains the proofs for the other chain +in the context. Any attept to verify other chain's state without setting `Store` will lead to panic. + +### value.go + +Types in `value.go` is a replication of `store/mapping/*.go`, but only with a single method +`Is(ctx sdk.Context, value T) bool`, which access on the underlying `Store` and performs verification. diff --git a/x/ibc/23-commitment/keypath.go b/x/ibc/23-commitment/keypath.go new file mode 100644 index 000000000000..e3dc62f8af73 --- /dev/null +++ b/x/ibc/23-commitment/keypath.go @@ -0,0 +1,27 @@ +package commitment + +import ( + "github.com/tendermint/tendermint/crypto/merkle" +) + +// Hard coded for now +func SDKPrefix() merkle.KeyPath { + return new(merkle.KeyPath). + AppendKey([]byte("ibc"), merkle.KeyEncodingHex). + AppendKey([]byte{0x00}, merkle.KeyEncodingHex) +} + +func PrefixKeyPath(prefix string, key []byte) (res merkle.KeyPath, err error) { + keys, err := merkle.KeyPathToKeys(prefix) + if err != nil { + return + } + + keys[len(keys)-1] = append(keys[len(keys)], key...) + + for _, key := range keys { + res = res.AppendKey(key, merkle.KeyEncodingHex) + } + + return +} diff --git a/x/ibc/23-commitment/store.go b/x/ibc/23-commitment/store.go index 94977a9824ea..92fad62e2771 100644 --- a/x/ibc/23-commitment/store.go +++ b/x/ibc/23-commitment/store.go @@ -35,7 +35,7 @@ type store struct { } // Proofs must be provided -func Newstore(root Root, proofs []Proof, fullProofs []FullProof) (res store, err error) { +func Newstore(root Root, proofs []Proof) (res store, err error) { res = store{ root: root, proofs: make(map[string]Proof), @@ -46,14 +46,6 @@ func Newstore(root Root, proofs []Proof, fullProofs []FullProof) (res store, err res.proofs[string(proof.Key())] = proof } - for _, proof := range fullProofs { - err = proof.Verify(root) - if err != nil { - return - } - res.verified[string(proof.Proof.Key())] = proof.Value - } - return } @@ -71,7 +63,7 @@ func (store store) Prove(key, value []byte) bool { if !ok { return false } - err := proof.Verify(store.root, key, value) + err := proof.Verify(store.root, value) if err != nil { return false } diff --git a/x/ibc/23-commitment/types.go b/x/ibc/23-commitment/types.go index 546fd70825eb..5b8cca0a9f5e 100644 --- a/x/ibc/23-commitment/types.go +++ b/x/ibc/23-commitment/types.go @@ -6,14 +6,5 @@ type Root interface{} // XXX: need to separate membership and non membership proof types type Proof interface { Key() []byte - Verify(Root, []byte, []byte) error -} - -type FullProof struct { - Proof Proof - Value []byte -} - -func (proof FullProof) Verify(root Root) error { - return proof.Proof.Verify(root, proof.Proof.Key(), proof.Value) + Verify(Root, []byte) error } From cd6f1f2e59aef987a30d6e48af2f8b8b4cb1f2dd Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 12 Jun 2019 00:25:20 +0200 Subject: [PATCH 015/166] reflect downstream ics --- x/ibc/23-commitment/value.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x/ibc/23-commitment/value.go b/x/ibc/23-commitment/value.go index 74f72492bf53..70b8502a44e6 100644 --- a/x/ibc/23-commitment/value.go +++ b/x/ibc/23-commitment/value.go @@ -2,7 +2,7 @@ package commitment import ( "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/mapping" + "github.com/cosmos/cosmos-sdk/store/state" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -77,13 +77,13 @@ func (v Enum) Is(ctx sdk.Context, value byte) bool { type Integer struct { Value - enc mapping.IntEncoding + enc state.IntEncoding } -func NewInteger(v Value, enc mapping.IntEncoding) Integer { +func NewInteger(v Value, enc state.IntEncoding) Integer { return Integer{v, enc} } func (v Integer) Is(ctx sdk.Context, value uint64) bool { - return v.Value.IsRaw(ctx, mapping.EncodeInt(value, v.enc)) + return v.Value.IsRaw(ctx, state.EncodeInt(value, v.enc)) } From d0777cd28e08821c0b71ba2bf318968479fa5514 Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 12 Jun 2019 00:51:21 +0200 Subject: [PATCH 016/166] add merkle --- x/ibc/23-commitment/keypath.go | 27 ----------- x/ibc/23-commitment/merkle/merkle.go | 71 ++++++++++++++++++++++++++++ x/ibc/23-commitment/store.go | 2 +- x/ibc/23-commitment/types.go | 2 +- 4 files changed, 73 insertions(+), 29 deletions(-) delete mode 100644 x/ibc/23-commitment/keypath.go create mode 100644 x/ibc/23-commitment/merkle/merkle.go diff --git a/x/ibc/23-commitment/keypath.go b/x/ibc/23-commitment/keypath.go deleted file mode 100644 index e3dc62f8af73..000000000000 --- a/x/ibc/23-commitment/keypath.go +++ /dev/null @@ -1,27 +0,0 @@ -package commitment - -import ( - "github.com/tendermint/tendermint/crypto/merkle" -) - -// Hard coded for now -func SDKPrefix() merkle.KeyPath { - return new(merkle.KeyPath). - AppendKey([]byte("ibc"), merkle.KeyEncodingHex). - AppendKey([]byte{0x00}, merkle.KeyEncodingHex) -} - -func PrefixKeyPath(prefix string, key []byte) (res merkle.KeyPath, err error) { - keys, err := merkle.KeyPathToKeys(prefix) - if err != nil { - return - } - - keys[len(keys)-1] = append(keys[len(keys)], key...) - - for _, key := range keys { - res = res.AppendKey(key, merkle.KeyEncodingHex) - } - - return -} diff --git a/x/ibc/23-commitment/merkle/merkle.go b/x/ibc/23-commitment/merkle/merkle.go new file mode 100644 index 000000000000..8e234f4873fe --- /dev/null +++ b/x/ibc/23-commitment/merkle/merkle.go @@ -0,0 +1,71 @@ +package merkle + +import ( + "errors" + + "github.com/tendermint/iavl" + "github.com/tendermint/tendermint/crypto/merkle" + + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" +) + +// merkle.Proof implementation of Proof +// Applied on SDK-based IBC implementation + +var _ commitment.Root = Root{} + +type Root = []byte + +var _ commitment.Proof = Proof{} + +type Proof struct { + Proof *merkle.Proof + Key []byte +} + +func (proof Proof) GetKey() []byte { + return proof.Key +} + +func (proof Proof) Verify(croot commitment.Root, value []byte) error { + root, ok := croot.(Root) + if !ok { + return errors.New("invalid commitment root type") + } + + keypath, err := PrefixKeyPath(SDKPrefix().String(), proof.Key) + if err != nil { + return err + } + // Hard coded for now + runtime := merkle.DefaultProofRuntime() + runtime.RegisterOpDecoder(iavl.ProofOpIAVLAbsence, iavl.IAVLAbsenceOpDecoder) + runtime.RegisterOpDecoder(iavl.ProofOpIAVLValue, iavl.IAVLValueOpDecoder) + + if value != nil { + return runtime.VerifyValue(proof.Proof, root, keypath.String(), value) + } + return runtime.VerifyAbsence(proof.Proof, root, keypath.String()) +} + +// Hard coded for now +func SDKPrefix() merkle.KeyPath { + return new(merkle.KeyPath). + AppendKey([]byte("ibc"), merkle.KeyEncodingHex). + AppendKey([]byte{0x00}, merkle.KeyEncodingHex) +} + +func PrefixKeyPath(prefix string, key []byte) (res merkle.KeyPath, err error) { + keys, err := merkle.KeyPathToKeys(prefix) + if err != nil { + return + } + + keys[len(keys)-1] = append(keys[len(keys)], key...) + + for _, key := range keys { + res = res.AppendKey(key, merkle.KeyEncodingHex) + } + + return +} diff --git a/x/ibc/23-commitment/store.go b/x/ibc/23-commitment/store.go index 92fad62e2771..a82c59cc5fd7 100644 --- a/x/ibc/23-commitment/store.go +++ b/x/ibc/23-commitment/store.go @@ -43,7 +43,7 @@ func Newstore(root Root, proofs []Proof) (res store, err error) { } for _, proof := range proofs { - res.proofs[string(proof.Key())] = proof + res.proofs[string(proof.GetKey())] = proof } return diff --git a/x/ibc/23-commitment/types.go b/x/ibc/23-commitment/types.go index 5b8cca0a9f5e..7d5d374512be 100644 --- a/x/ibc/23-commitment/types.go +++ b/x/ibc/23-commitment/types.go @@ -5,6 +5,6 @@ type Root interface{} // XXX: need to separate membership and non membership proof types type Proof interface { - Key() []byte + GetKey() []byte Verify(Root, []byte) error } From df8c05e05545b3f2dc81c0117f6a87e32c8fc2b5 Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 12 Jun 2019 14:06:17 +0200 Subject: [PATCH 017/166] add test for proving --- x/ibc/23-commitment/README.md | 2 +- x/ibc/23-commitment/merkle/merkle.go | 9 ++- x/ibc/23-commitment/merkle/merkle_test.go | 72 +++++++++++++++++++++++ x/ibc/23-commitment/store.go | 6 +- 4 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 x/ibc/23-commitment/merkle/merkle_test.go diff --git a/x/ibc/23-commitment/README.md b/x/ibc/23-commitment/README.md index 61a46b99d8a4..759128b0c6de 100644 --- a/x/ibc/23-commitment/README.md +++ b/x/ibc/23-commitment/README.md @@ -24,7 +24,7 @@ an argument for `spec: verifyMembership` / `spec: verifyNonMembership`, construc `type Root` implements `spec: type CommitmentRoot`. In Cosmos-SDK implementation, `Root` will be the `AppHash []byte`, and `Proof` will be `merkle.Proof`, which consists -of `SimpleProof` and `IAVLValueProof` +of `SimpleProof` and `IAVLValueProof`. Defined in `merkle/` ### store.go diff --git a/x/ibc/23-commitment/merkle/merkle.go b/x/ibc/23-commitment/merkle/merkle.go index 8e234f4873fe..1eb48385afad 100644 --- a/x/ibc/23-commitment/merkle/merkle.go +++ b/x/ibc/23-commitment/merkle/merkle.go @@ -3,9 +3,10 @@ package merkle import ( "errors" - "github.com/tendermint/iavl" "github.com/tendermint/tendermint/crypto/merkle" + "github.com/cosmos/cosmos-sdk/store/rootmulti" + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -38,9 +39,7 @@ func (proof Proof) Verify(croot commitment.Root, value []byte) error { return err } // Hard coded for now - runtime := merkle.DefaultProofRuntime() - runtime.RegisterOpDecoder(iavl.ProofOpIAVLAbsence, iavl.IAVLAbsenceOpDecoder) - runtime.RegisterOpDecoder(iavl.ProofOpIAVLValue, iavl.IAVLValueOpDecoder) + runtime := rootmulti.DefaultProofRuntime() if value != nil { return runtime.VerifyValue(proof.Proof, root, keypath.String(), value) @@ -61,7 +60,7 @@ func PrefixKeyPath(prefix string, key []byte) (res merkle.KeyPath, err error) { return } - keys[len(keys)-1] = append(keys[len(keys)], key...) + keys[len(keys)-1] = append(keys[len(keys)-1], key...) for _, key := range keys { res = res.AppendKey(key, merkle.KeyEncodingHex) diff --git a/x/ibc/23-commitment/merkle/merkle_test.go b/x/ibc/23-commitment/merkle/merkle_test.go new file mode 100644 index 000000000000..e252a13268c5 --- /dev/null +++ b/x/ibc/23-commitment/merkle/merkle_test.go @@ -0,0 +1,72 @@ +package merkle + +import ( + "testing" + + "github.com/stretchr/testify/require" + + abci "github.com/tendermint/tendermint/abci/types" + dbm "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store" + "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" +) + +func defaultComponents() (sdk.StoreKey, sdk.Context, types.CommitMultiStore, *codec.Codec) { + key := sdk.NewKVStoreKey("ibc") + db := dbm.NewMemDB() + cms := store.NewCommitMultiStore(db) + cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) + err := cms.LoadLatestVersion() + if err != nil { + panic(err) + } + ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) + cdc := codec.New() + return key, ctx, cms, cdc +} + +func key(str string) []byte { + return append([]byte{0x00}, []byte(str)...) +} + +func query(t *testing.T, cms types.CommitMultiStore, k string) (value []byte, proof Proof) { + qres := cms.(types.Queryable).Query(abci.RequestQuery{Path: "/ibc/key", Data: key(k), Prove: true}) + require.Equal(t, uint32(0), qres.Code, qres.Log) + value = qres.Value + proof = Proof{ + Key: []byte(k), + Proof: qres.Proof, + } + return +} + +func TestStore(t *testing.T) { + k, ctx, cms, _ := defaultComponents() + kvstore := ctx.KVStore(k) + + kvstore.Set(key("hello"), []byte("world")) + kvstore.Set(key("merkle"), []byte("tree")) + kvstore.Set(key("block"), []byte("chain")) + + cid := cms.Commit() + + v1, p1 := query(t, cms, "hello") + require.Equal(t, []byte("world"), v1) + v2, p2 := query(t, cms, "merkle") + require.Equal(t, []byte("tree"), v2) + v3, p3 := query(t, cms, "block") + require.Equal(t, []byte("chain"), v3) + + cstore, err := commitment.NewStore(cid.Hash, []commitment.Proof{p1, p2, p3}) + require.NoError(t, err) + + require.True(t, cstore.Prove([]byte("hello"), []byte("world"))) + require.True(t, cstore.Prove([]byte("merkle"), []byte("tree"))) + require.True(t, cstore.Prove([]byte("block"), []byte("chain"))) +} diff --git a/x/ibc/23-commitment/store.go b/x/ibc/23-commitment/store.go index a82c59cc5fd7..bdc0bb8902dc 100644 --- a/x/ibc/23-commitment/store.go +++ b/x/ibc/23-commitment/store.go @@ -2,6 +2,7 @@ package commitment import ( "bytes" + "fmt" ) type Store interface { @@ -35,7 +36,7 @@ type store struct { } // Proofs must be provided -func Newstore(root Root, proofs []Proof) (res store, err error) { +func NewStore(root Root, proofs []Proof) (res store, err error) { res = store{ root: root, proofs: make(map[string]Proof), @@ -57,14 +58,17 @@ func (store store) Get(key []byte) ([]byte, bool) { func (store store) Prove(key, value []byte) bool { stored, ok := store.Get(key) if ok && bytes.Equal(stored, value) { + fmt.Println(1) return true } proof, ok := store.proofs[string(key)] if !ok { + fmt.Println(2) return false } err := proof.Verify(store.root, value) if err != nil { + fmt.Println(err) return false } store.verified[string(key)] = value From 47c33f0d2edfc09b2bc19592117ed0a5091cc0af Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 25 Jun 2019 11:15:10 +0200 Subject: [PATCH 018/166] soft coded root keypath --- x/ibc/23-commitment/merkle/merkle.go | 40 ++++++++++++++++------- x/ibc/23-commitment/merkle/merkle_test.go | 13 +++++--- x/ibc/23-commitment/store.go | 9 ++--- x/ibc/23-commitment/types.go | 7 ++-- 4 files changed, 47 insertions(+), 22 deletions(-) diff --git a/x/ibc/23-commitment/merkle/merkle.go b/x/ibc/23-commitment/merkle/merkle.go index 1eb48385afad..c8362ce7fafb 100644 --- a/x/ibc/23-commitment/merkle/merkle.go +++ b/x/ibc/23-commitment/merkle/merkle.go @@ -15,7 +15,21 @@ import ( var _ commitment.Root = Root{} -type Root = []byte +type Root struct { + Hash []byte + KeyPrefix [][]byte +} + +func NewRoot(hash []byte, prefixes [][]byte) Root { + return Root{ + Hash: hash, + KeyPrefix: prefixes, + } +} + +func (Root) CommitmentKind() string { + return "merkle" +} var _ commitment.Proof = Proof{} @@ -24,6 +38,10 @@ type Proof struct { Key []byte } +func (Proof) CommitmentKind() string { + return "merkle" +} + func (proof Proof) GetKey() []byte { return proof.Key } @@ -34,24 +52,24 @@ func (proof Proof) Verify(croot commitment.Root, value []byte) error { return errors.New("invalid commitment root type") } - keypath, err := PrefixKeyPath(SDKPrefix().String(), proof.Key) + keypath := merkle.KeyPath{} + + for _, key := range root.KeyPrefix { + keypath = keypath.AppendKey(key, merkle.KeyEncodingHex) + } + + keypath, err := PrefixKeyPath(keypath.String(), proof.Key) if err != nil { return err } + // Hard coded for now runtime := rootmulti.DefaultProofRuntime() if value != nil { - return runtime.VerifyValue(proof.Proof, root, keypath.String(), value) + return runtime.VerifyValue(proof.Proof, root.Hash, keypath.String(), value) } - return runtime.VerifyAbsence(proof.Proof, root, keypath.String()) -} - -// Hard coded for now -func SDKPrefix() merkle.KeyPath { - return new(merkle.KeyPath). - AppendKey([]byte("ibc"), merkle.KeyEncodingHex). - AppendKey([]byte{0x00}, merkle.KeyEncodingHex) + return runtime.VerifyAbsence(proof.Proof, root.Hash, keypath.String()) } func PrefixKeyPath(prefix string, key []byte) (res merkle.KeyPath, err error) { diff --git a/x/ibc/23-commitment/merkle/merkle_test.go b/x/ibc/23-commitment/merkle/merkle_test.go index e252a13268c5..6070de09082b 100644 --- a/x/ibc/23-commitment/merkle/merkle_test.go +++ b/x/ibc/23-commitment/merkle/merkle_test.go @@ -18,7 +18,7 @@ import ( ) func defaultComponents() (sdk.StoreKey, sdk.Context, types.CommitMultiStore, *codec.Codec) { - key := sdk.NewKVStoreKey("ibc") + key := sdk.NewKVStoreKey("test") db := dbm.NewMemDB() cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) @@ -36,7 +36,7 @@ func key(str string) []byte { } func query(t *testing.T, cms types.CommitMultiStore, k string) (value []byte, proof Proof) { - qres := cms.(types.Queryable).Query(abci.RequestQuery{Path: "/ibc/key", Data: key(k), Prove: true}) + qres := cms.(types.Queryable).Query(abci.RequestQuery{Path: "/test/key", Data: key(k), Prove: true}) require.Equal(t, uint32(0), qres.Code, qres.Log) value = qres.Value proof = Proof{ @@ -46,6 +46,11 @@ func query(t *testing.T, cms types.CommitMultiStore, k string) (value []byte, pr return } +func commit(cms types.CommitMultiStore) (root Root) { + cid := cms.Commit() + return NewRoot(cid.Hash, [][]byte{[]byte("test"), []byte{0x00}}) +} + func TestStore(t *testing.T) { k, ctx, cms, _ := defaultComponents() kvstore := ctx.KVStore(k) @@ -54,7 +59,7 @@ func TestStore(t *testing.T) { kvstore.Set(key("merkle"), []byte("tree")) kvstore.Set(key("block"), []byte("chain")) - cid := cms.Commit() + root := commit(cms) v1, p1 := query(t, cms, "hello") require.Equal(t, []byte("world"), v1) @@ -63,7 +68,7 @@ func TestStore(t *testing.T) { v3, p3 := query(t, cms, "block") require.Equal(t, []byte("chain"), v3) - cstore, err := commitment.NewStore(cid.Hash, []commitment.Proof{p1, p2, p3}) + cstore, err := commitment.NewStore(root, []commitment.Proof{p1, p2, p3}) require.NoError(t, err) require.True(t, cstore.Prove([]byte("hello"), []byte("world"))) diff --git a/x/ibc/23-commitment/store.go b/x/ibc/23-commitment/store.go index bdc0bb8902dc..8cfaef4baaa1 100644 --- a/x/ibc/23-commitment/store.go +++ b/x/ibc/23-commitment/store.go @@ -2,7 +2,7 @@ package commitment import ( "bytes" - "fmt" + "errors" ) type Store interface { @@ -44,6 +44,10 @@ func NewStore(root Root, proofs []Proof) (res store, err error) { } for _, proof := range proofs { + if proof.CommitmentKind() != root.CommitmentKind() { + err = errors.New("proof type not matching with root's") + return + } res.proofs[string(proof.GetKey())] = proof } @@ -58,17 +62,14 @@ func (store store) Get(key []byte) ([]byte, bool) { func (store store) Prove(key, value []byte) bool { stored, ok := store.Get(key) if ok && bytes.Equal(stored, value) { - fmt.Println(1) return true } proof, ok := store.proofs[string(key)] if !ok { - fmt.Println(2) return false } err := proof.Verify(store.root, value) if err != nil { - fmt.Println(err) return false } store.verified[string(key)] = value diff --git a/x/ibc/23-commitment/types.go b/x/ibc/23-commitment/types.go index 7d5d374512be..77561719cd1a 100644 --- a/x/ibc/23-commitment/types.go +++ b/x/ibc/23-commitment/types.go @@ -1,10 +1,11 @@ package commitment -// XXX: []byte? -type Root interface{} +type Root interface { + CommitmentKind() string +} -// XXX: need to separate membership and non membership proof types type Proof interface { + CommitmentKind() string GetKey() []byte Verify(Root, []byte) error } From 12ef4027f7f4388ef1d8f306fc55b9423150daa3 Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 25 Jun 2019 11:56:38 +0200 Subject: [PATCH 019/166] add update --- x/ibc/23-commitment/merkle/merkle.go | 19 +++++++++++++++ x/ibc/23-commitment/merkle/merkle_test.go | 28 ++++++++++++++++++++--- x/ibc/23-commitment/types.go | 5 ++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/x/ibc/23-commitment/merkle/merkle.go b/x/ibc/23-commitment/merkle/merkle.go index c8362ce7fafb..a5aa577947e7 100644 --- a/x/ibc/23-commitment/merkle/merkle.go +++ b/x/ibc/23-commitment/merkle/merkle.go @@ -31,6 +31,25 @@ func (Root) CommitmentKind() string { return "merkle" } +func (r Root) Update(cupdate commitment.RootUpdate) (commitment.Root, error) { + update, ok := cupdate.(RootUpdate) + if !ok { + return nil, errors.New("invalid type") + } + return Root{ + Hash: update.Hash, + KeyPrefix: r.KeyPrefix, + }, nil +} + +type RootUpdate struct { + Hash []byte +} + +func (RootUpdate) CommitmentKind() string { + return "merkle" +} + var _ commitment.Proof = Proof{} type Proof struct { diff --git a/x/ibc/23-commitment/merkle/merkle_test.go b/x/ibc/23-commitment/merkle/merkle_test.go index 6070de09082b..6fefe4f51d62 100644 --- a/x/ibc/23-commitment/merkle/merkle_test.go +++ b/x/ibc/23-commitment/merkle/merkle_test.go @@ -46,9 +46,11 @@ func query(t *testing.T, cms types.CommitMultiStore, k string) (value []byte, pr return } -func commit(cms types.CommitMultiStore) (root Root) { +func commit(t *testing.T, cms types.CommitMultiStore, root commitment.Root) commitment.Root { cid := cms.Commit() - return NewRoot(cid.Hash, [][]byte{[]byte("test"), []byte{0x00}}) + res, err := root.Update(RootUpdate{cid.Hash}) + require.NoError(t, err) + return res } func TestStore(t *testing.T) { @@ -59,7 +61,7 @@ func TestStore(t *testing.T) { kvstore.Set(key("merkle"), []byte("tree")) kvstore.Set(key("block"), []byte("chain")) - root := commit(cms) + root := commit(t, cms, Root{KeyPrefix: [][]byte{[]byte("test"), []byte{0x00}}}) v1, p1 := query(t, cms, "hello") require.Equal(t, []byte("world"), v1) @@ -74,4 +76,24 @@ func TestStore(t *testing.T) { require.True(t, cstore.Prove([]byte("hello"), []byte("world"))) require.True(t, cstore.Prove([]byte("merkle"), []byte("tree"))) require.True(t, cstore.Prove([]byte("block"), []byte("chain"))) + + kvstore.Set(key("12345"), []byte("67890")) + kvstore.Set(key("qwerty"), []byte("zxcv")) + kvstore.Set(key("hello"), []byte("dlrow")) + + root = commit(t, cms, root) + + v1, p1 = query(t, cms, "12345") + require.Equal(t, []byte("67890"), v1) + v2, p2 = query(t, cms, "qwerty") + require.Equal(t, []byte("zxcv"), v2) + v3, p3 = query(t, cms, "hello") + require.Equal(t, []byte("dlrow"), v3) + + cstore, err = commitment.NewStore(root, []commitment.Proof{p1, p2, p3}) + require.NoError(t, err) + + require.True(t, cstore.Prove([]byte("12345"), []byte("67890"))) + require.True(t, cstore.Prove([]byte("qwerty"), []byte("zxcv"))) + require.True(t, cstore.Prove([]byte("hello"), []byte("dlrow"))) } diff --git a/x/ibc/23-commitment/types.go b/x/ibc/23-commitment/types.go index 77561719cd1a..7ef8ec1a6efa 100644 --- a/x/ibc/23-commitment/types.go +++ b/x/ibc/23-commitment/types.go @@ -2,6 +2,11 @@ package commitment type Root interface { CommitmentKind() string + Update(RootUpdate) (Root, error) +} + +type RootUpdate interface { + CommitmentKind() string } type Proof interface { From 03d5d17b718bfa4bba4882f55327f8deac195b14 Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 25 Jun 2019 12:13:21 +0200 Subject: [PATCH 020/166] remove RootUpdate --- x/ibc/23-commitment/merkle/merkle.go | 18 +++--------------- x/ibc/23-commitment/merkle/merkle_test.go | 10 ++++------ x/ibc/23-commitment/types.go | 6 +----- 3 files changed, 8 insertions(+), 26 deletions(-) diff --git a/x/ibc/23-commitment/merkle/merkle.go b/x/ibc/23-commitment/merkle/merkle.go index a5aa577947e7..4c89a6e498e5 100644 --- a/x/ibc/23-commitment/merkle/merkle.go +++ b/x/ibc/23-commitment/merkle/merkle.go @@ -31,23 +31,11 @@ func (Root) CommitmentKind() string { return "merkle" } -func (r Root) Update(cupdate commitment.RootUpdate) (commitment.Root, error) { - update, ok := cupdate.(RootUpdate) - if !ok { - return nil, errors.New("invalid type") - } +func (r Root) Update(hash []byte) commitment.Root { return Root{ - Hash: update.Hash, + Hash: hash, KeyPrefix: r.KeyPrefix, - }, nil -} - -type RootUpdate struct { - Hash []byte -} - -func (RootUpdate) CommitmentKind() string { - return "merkle" + } } var _ commitment.Proof = Proof{} diff --git a/x/ibc/23-commitment/merkle/merkle_test.go b/x/ibc/23-commitment/merkle/merkle_test.go index 6fefe4f51d62..ce67d6c41251 100644 --- a/x/ibc/23-commitment/merkle/merkle_test.go +++ b/x/ibc/23-commitment/merkle/merkle_test.go @@ -46,11 +46,9 @@ func query(t *testing.T, cms types.CommitMultiStore, k string) (value []byte, pr return } -func commit(t *testing.T, cms types.CommitMultiStore, root commitment.Root) commitment.Root { +func commit(cms types.CommitMultiStore, root Root) Root { cid := cms.Commit() - res, err := root.Update(RootUpdate{cid.Hash}) - require.NoError(t, err) - return res + return root.Update(cid.Hash).(Root) } func TestStore(t *testing.T) { @@ -61,7 +59,7 @@ func TestStore(t *testing.T) { kvstore.Set(key("merkle"), []byte("tree")) kvstore.Set(key("block"), []byte("chain")) - root := commit(t, cms, Root{KeyPrefix: [][]byte{[]byte("test"), []byte{0x00}}}) + root := commit(cms, Root{KeyPrefix: [][]byte{[]byte("test"), []byte{0x00}}}) v1, p1 := query(t, cms, "hello") require.Equal(t, []byte("world"), v1) @@ -81,7 +79,7 @@ func TestStore(t *testing.T) { kvstore.Set(key("qwerty"), []byte("zxcv")) kvstore.Set(key("hello"), []byte("dlrow")) - root = commit(t, cms, root) + root = commit(cms, root) v1, p1 = query(t, cms, "12345") require.Equal(t, []byte("67890"), v1) diff --git a/x/ibc/23-commitment/types.go b/x/ibc/23-commitment/types.go index 7ef8ec1a6efa..e12d8f0fcef6 100644 --- a/x/ibc/23-commitment/types.go +++ b/x/ibc/23-commitment/types.go @@ -2,11 +2,7 @@ package commitment type Root interface { CommitmentKind() string - Update(RootUpdate) (Root, error) -} - -type RootUpdate interface { - CommitmentKind() string + Update([]byte) Root } type Proof interface { From 0b79b1dde7881253e8a69ee3a3556b5d28d379e8 Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 25 Jun 2019 15:07:00 +0200 Subject: [PATCH 021/166] separate keypath and keuprefix --- x/ibc/23-commitment/merkle/merkle.go | 33 ++++------------ x/ibc/23-commitment/merkle/merkle_test.go | 48 ++++++++++------------- x/ibc/23-commitment/merkle/utils.go | 28 +++++++++++++ 3 files changed, 56 insertions(+), 53 deletions(-) create mode 100644 x/ibc/23-commitment/merkle/utils.go diff --git a/x/ibc/23-commitment/merkle/merkle.go b/x/ibc/23-commitment/merkle/merkle.go index 4c89a6e498e5..bc01e0f112ee 100644 --- a/x/ibc/23-commitment/merkle/merkle.go +++ b/x/ibc/23-commitment/merkle/merkle.go @@ -17,13 +17,15 @@ var _ commitment.Root = Root{} type Root struct { Hash []byte - KeyPrefix [][]byte + KeyPath [][]byte + KeyPrefix []byte } -func NewRoot(hash []byte, prefixes [][]byte) Root { +func NewRoot(hash []byte, keypath [][]byte, prefix []byte) Root { return Root{ Hash: hash, - KeyPrefix: prefixes, + KeyPath: keypath, + KeyPrefix: prefix, } } @@ -34,6 +36,7 @@ func (Root) CommitmentKind() string { func (r Root) Update(hash []byte) commitment.Root { return Root{ Hash: hash, + KeyPath: r.KeyPath, KeyPrefix: r.KeyPrefix, } } @@ -60,15 +63,10 @@ func (proof Proof) Verify(croot commitment.Root, value []byte) error { } keypath := merkle.KeyPath{} - - for _, key := range root.KeyPrefix { + for _, key := range root.KeyPath { keypath = keypath.AppendKey(key, merkle.KeyEncodingHex) } - - keypath, err := PrefixKeyPath(keypath.String(), proof.Key) - if err != nil { - return err - } + keypath = keypath.AppendKey(append(root.KeyPrefix, proof.Key...), merkle.KeyEncodingHex) // Hard coded for now runtime := rootmulti.DefaultProofRuntime() @@ -78,18 +76,3 @@ func (proof Proof) Verify(croot commitment.Root, value []byte) error { } return runtime.VerifyAbsence(proof.Proof, root.Hash, keypath.String()) } - -func PrefixKeyPath(prefix string, key []byte) (res merkle.KeyPath, err error) { - keys, err := merkle.KeyPathToKeys(prefix) - if err != nil { - return - } - - keys[len(keys)-1] = append(keys[len(keys)-1], key...) - - for _, key := range keys { - res = res.AppendKey(key, merkle.KeyEncodingHex) - } - - return -} diff --git a/x/ibc/23-commitment/merkle/merkle_test.go b/x/ibc/23-commitment/merkle/merkle_test.go index ce67d6c41251..487e9496163e 100644 --- a/x/ibc/23-commitment/merkle/merkle_test.go +++ b/x/ibc/23-commitment/merkle/merkle_test.go @@ -31,21 +31,6 @@ func defaultComponents() (sdk.StoreKey, sdk.Context, types.CommitMultiStore, *co return key, ctx, cms, cdc } -func key(str string) []byte { - return append([]byte{0x00}, []byte(str)...) -} - -func query(t *testing.T, cms types.CommitMultiStore, k string) (value []byte, proof Proof) { - qres := cms.(types.Queryable).Query(abci.RequestQuery{Path: "/test/key", Data: key(k), Prove: true}) - require.Equal(t, uint32(0), qres.Code, qres.Log) - value = qres.Value - proof = Proof{ - Key: []byte(k), - Proof: qres.Proof, - } - return -} - func commit(cms types.CommitMultiStore, root Root) Root { cid := cms.Commit() return root.Update(cid.Hash).(Root) @@ -54,18 +39,22 @@ func commit(cms types.CommitMultiStore, root Root) Root { func TestStore(t *testing.T) { k, ctx, cms, _ := defaultComponents() kvstore := ctx.KVStore(k) + root := Root{KeyPath: [][]byte{[]byte("test")}, KeyPrefix: []byte{0x01, 0x03, 0x05}} - kvstore.Set(key("hello"), []byte("world")) - kvstore.Set(key("merkle"), []byte("tree")) - kvstore.Set(key("block"), []byte("chain")) + kvstore.Set(root.Key([]byte("hello")), []byte("world")) + kvstore.Set(root.Key([]byte("merkle")), []byte("tree")) + kvstore.Set(root.Key([]byte("block")), []byte("chain")) - root := commit(cms, Root{KeyPrefix: [][]byte{[]byte("test"), []byte{0x00}}}) + root = commit(cms, root) - v1, p1 := query(t, cms, "hello") + c1, v1, p1 := root.Query(cms, []byte("hello")) + require.Equal(t, uint32(0), c1) require.Equal(t, []byte("world"), v1) - v2, p2 := query(t, cms, "merkle") + c2, v2, p2 := root.Query(cms, []byte("merkle")) + require.Equal(t, uint32(0), c2) require.Equal(t, []byte("tree"), v2) - v3, p3 := query(t, cms, "block") + c3, v3, p3 := root.Query(cms, []byte("block")) + require.Equal(t, uint32(0), c3) require.Equal(t, []byte("chain"), v3) cstore, err := commitment.NewStore(root, []commitment.Proof{p1, p2, p3}) @@ -75,17 +64,20 @@ func TestStore(t *testing.T) { require.True(t, cstore.Prove([]byte("merkle"), []byte("tree"))) require.True(t, cstore.Prove([]byte("block"), []byte("chain"))) - kvstore.Set(key("12345"), []byte("67890")) - kvstore.Set(key("qwerty"), []byte("zxcv")) - kvstore.Set(key("hello"), []byte("dlrow")) + kvstore.Set(root.Key([]byte("12345")), []byte("67890")) + kvstore.Set(root.Key([]byte("qwerty")), []byte("zxcv")) + kvstore.Set(root.Key([]byte("hello")), []byte("dlrow")) root = commit(cms, root) - v1, p1 = query(t, cms, "12345") + c1, v1, p1 = root.Query(cms, []byte("12345")) + require.Equal(t, uint32(0), c1) require.Equal(t, []byte("67890"), v1) - v2, p2 = query(t, cms, "qwerty") + c2, v2, p2 = root.Query(cms, []byte("qwerty")) + require.Equal(t, uint32(0), c2) require.Equal(t, []byte("zxcv"), v2) - v3, p3 = query(t, cms, "hello") + c3, v3, p3 = root.Query(cms, []byte("hello")) + require.Equal(t, uint32(0), c3) require.Equal(t, []byte("dlrow"), v3) cstore, err = commitment.NewStore(root, []commitment.Proof{p1, p2, p3}) diff --git a/x/ibc/23-commitment/merkle/utils.go b/x/ibc/23-commitment/merkle/utils.go new file mode 100644 index 000000000000..1fa13f6a3d32 --- /dev/null +++ b/x/ibc/23-commitment/merkle/utils.go @@ -0,0 +1,28 @@ +package merkle + +import ( + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/store/types" +) + +func (root Root) RequestQuery(key []byte) abci.RequestQuery { + path := "" + for _, inter := range root.KeyPath { + path = path + "/" + string(inter) + } + path = path + "/key" + + data := append(root.KeyPrefix, key...) + + return abci.RequestQuery{Path: path, Data: data, Prove: true} +} + +func (root Root) Query(cms types.CommitMultiStore, key []byte) (uint32, []byte, Proof) { + qres := cms.(types.Queryable).Query(root.RequestQuery(key)) + return qres.Code, qres.Value, Proof{Key: key, Proof: qres.Proof} +} + +func (root Root) Key(key []byte) []byte { + return append(root.KeyPrefix, key...) // XXX: cloneAppend +} From 70469532b0d98e76b0b5c038981029ab287fd226 Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 26 Jun 2019 19:12:24 +0200 Subject: [PATCH 022/166] add codec --- x/ibc/23-commitment/codec.go | 10 ++++++++++ x/ibc/23-commitment/merkle/codec.go | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 x/ibc/23-commitment/codec.go create mode 100644 x/ibc/23-commitment/merkle/codec.go diff --git a/x/ibc/23-commitment/codec.go b/x/ibc/23-commitment/codec.go new file mode 100644 index 000000000000..33d5bc490d39 --- /dev/null +++ b/x/ibc/23-commitment/codec.go @@ -0,0 +1,10 @@ +package commitment + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterInterface((*Root)(nil), nil) + cdc.RegisterInterface((*Proof)(nil), nil) +} diff --git a/x/ibc/23-commitment/merkle/codec.go b/x/ibc/23-commitment/merkle/codec.go new file mode 100644 index 000000000000..a5e088650b0c --- /dev/null +++ b/x/ibc/23-commitment/merkle/codec.go @@ -0,0 +1,10 @@ +package merkle + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterConcrete(Root{}, "ibc/commitment/merkle/Root", nil) + cdc.RegisterConcrete(Proof{}, "ibc/commitment/merkle/Proof", nil) +} From db93a8ad6c614fb7e6a96f71ac433a249c549448 Mon Sep 17 00:00:00 2001 From: mossid Date: Fri, 12 Jul 2019 01:21:00 +0900 Subject: [PATCH 023/166] separate root/path --- x/ibc/23-commitment/merkle/merkle.go | 48 ++++++++++++++--------- x/ibc/23-commitment/merkle/merkle_test.go | 38 +++++++++--------- x/ibc/23-commitment/merkle/utils.go | 22 +++++------ x/ibc/23-commitment/store.go | 11 +++++- x/ibc/23-commitment/types.go | 7 +++- 5 files changed, 74 insertions(+), 52 deletions(-) diff --git a/x/ibc/23-commitment/merkle/merkle.go b/x/ibc/23-commitment/merkle/merkle.go index bc01e0f112ee..9fb11c7f38cb 100644 --- a/x/ibc/23-commitment/merkle/merkle.go +++ b/x/ibc/23-commitment/merkle/merkle.go @@ -10,37 +10,44 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) +const merkleKind = "merkle" + // merkle.Proof implementation of Proof // Applied on SDK-based IBC implementation - var _ commitment.Root = Root{} type Root struct { - Hash []byte - KeyPath [][]byte - KeyPrefix []byte + Hash []byte } -func NewRoot(hash []byte, keypath [][]byte, prefix []byte) Root { +func NewRoot(hash []byte) Root { return Root{ - Hash: hash, - KeyPath: keypath, - KeyPrefix: prefix, + Hash: hash, } } func (Root) CommitmentKind() string { - return "merkle" + return merkleKind } -func (r Root) Update(hash []byte) commitment.Root { - return Root{ - Hash: hash, - KeyPath: r.KeyPath, - KeyPrefix: r.KeyPrefix, +var _ commitment.Path = Path{} + +type Path struct { + KeyPath [][]byte + KeyPrefix []byte +} + +func NewPath(keypath [][]byte, keyprefix []byte) Path { + return Path{ + KeyPath: keypath, + KeyPrefix: keyprefix, } } +func (Path) CommitmentKind() string { + return merkleKind +} + var _ commitment.Proof = Proof{} type Proof struct { @@ -49,24 +56,29 @@ type Proof struct { } func (Proof) CommitmentKind() string { - return "merkle" + return merkleKind } func (proof Proof) GetKey() []byte { return proof.Key } -func (proof Proof) Verify(croot commitment.Root, value []byte) error { +func (proof Proof) Verify(croot commitment.Root, cpath commitment.Path, value []byte) error { root, ok := croot.(Root) if !ok { return errors.New("invalid commitment root type") } + path, ok := cpath.(Path) + if !ok { + return errors.New("invalid commitment path type") + } + keypath := merkle.KeyPath{} - for _, key := range root.KeyPath { + for _, key := range path.KeyPath { keypath = keypath.AppendKey(key, merkle.KeyEncodingHex) } - keypath = keypath.AppendKey(append(root.KeyPrefix, proof.Key...), merkle.KeyEncodingHex) + keypath = keypath.AppendKey(append(path.KeyPrefix, proof.Key...), merkle.KeyEncodingHex) // Hard coded for now runtime := rootmulti.DefaultProofRuntime() diff --git a/x/ibc/23-commitment/merkle/merkle_test.go b/x/ibc/23-commitment/merkle/merkle_test.go index 487e9496163e..f93f64943609 100644 --- a/x/ibc/23-commitment/merkle/merkle_test.go +++ b/x/ibc/23-commitment/merkle/merkle_test.go @@ -31,56 +31,56 @@ func defaultComponents() (sdk.StoreKey, sdk.Context, types.CommitMultiStore, *co return key, ctx, cms, cdc } -func commit(cms types.CommitMultiStore, root Root) Root { +func commit(cms types.CommitMultiStore) Root { cid := cms.Commit() - return root.Update(cid.Hash).(Root) + return NewRoot(cid.Hash) } func TestStore(t *testing.T) { k, ctx, cms, _ := defaultComponents() kvstore := ctx.KVStore(k) - root := Root{KeyPath: [][]byte{[]byte("test")}, KeyPrefix: []byte{0x01, 0x03, 0x05}} + path := Path{KeyPath: [][]byte{[]byte("test")}, KeyPrefix: []byte{0x01, 0x03, 0x05}} - kvstore.Set(root.Key([]byte("hello")), []byte("world")) - kvstore.Set(root.Key([]byte("merkle")), []byte("tree")) - kvstore.Set(root.Key([]byte("block")), []byte("chain")) + kvstore.Set(path.Key([]byte("hello")), []byte("world")) + kvstore.Set(path.Key([]byte("merkle")), []byte("tree")) + kvstore.Set(path.Key([]byte("block")), []byte("chain")) - root = commit(cms, root) + root := commit(cms) - c1, v1, p1 := root.Query(cms, []byte("hello")) + c1, v1, p1 := path.Query(cms, []byte("hello")) require.Equal(t, uint32(0), c1) require.Equal(t, []byte("world"), v1) - c2, v2, p2 := root.Query(cms, []byte("merkle")) + c2, v2, p2 := path.Query(cms, []byte("merkle")) require.Equal(t, uint32(0), c2) require.Equal(t, []byte("tree"), v2) - c3, v3, p3 := root.Query(cms, []byte("block")) + c3, v3, p3 := path.Query(cms, []byte("block")) require.Equal(t, uint32(0), c3) require.Equal(t, []byte("chain"), v3) - cstore, err := commitment.NewStore(root, []commitment.Proof{p1, p2, p3}) + cstore, err := commitment.NewStore(root, path, []commitment.Proof{p1, p2, p3}) require.NoError(t, err) require.True(t, cstore.Prove([]byte("hello"), []byte("world"))) require.True(t, cstore.Prove([]byte("merkle"), []byte("tree"))) require.True(t, cstore.Prove([]byte("block"), []byte("chain"))) - kvstore.Set(root.Key([]byte("12345")), []byte("67890")) - kvstore.Set(root.Key([]byte("qwerty")), []byte("zxcv")) - kvstore.Set(root.Key([]byte("hello")), []byte("dlrow")) + kvstore.Set(path.Key([]byte("12345")), []byte("67890")) + kvstore.Set(path.Key([]byte("qwerty")), []byte("zxcv")) + kvstore.Set(path.Key([]byte("hello")), []byte("dlrow")) - root = commit(cms, root) + root = commit(cms) - c1, v1, p1 = root.Query(cms, []byte("12345")) + c1, v1, p1 = path.Query(cms, []byte("12345")) require.Equal(t, uint32(0), c1) require.Equal(t, []byte("67890"), v1) - c2, v2, p2 = root.Query(cms, []byte("qwerty")) + c2, v2, p2 = path.Query(cms, []byte("qwerty")) require.Equal(t, uint32(0), c2) require.Equal(t, []byte("zxcv"), v2) - c3, v3, p3 = root.Query(cms, []byte("hello")) + c3, v3, p3 = path.Query(cms, []byte("hello")) require.Equal(t, uint32(0), c3) require.Equal(t, []byte("dlrow"), v3) - cstore, err = commitment.NewStore(root, []commitment.Proof{p1, p2, p3}) + cstore, err = commitment.NewStore(root, path, []commitment.Proof{p1, p2, p3}) require.NoError(t, err) require.True(t, cstore.Prove([]byte("12345"), []byte("67890"))) diff --git a/x/ibc/23-commitment/merkle/utils.go b/x/ibc/23-commitment/merkle/utils.go index 1fa13f6a3d32..d2eabdaed99d 100644 --- a/x/ibc/23-commitment/merkle/utils.go +++ b/x/ibc/23-commitment/merkle/utils.go @@ -6,23 +6,23 @@ import ( "github.com/cosmos/cosmos-sdk/store/types" ) -func (root Root) RequestQuery(key []byte) abci.RequestQuery { - path := "" - for _, inter := range root.KeyPath { - path = path + "/" + string(inter) +func (path Path) RequestQuery(key []byte) abci.RequestQuery { + pathstr := "" + for _, inter := range path.KeyPath { + pathstr = pathstr + "/" + string(inter) } - path = path + "/key" + pathstr = pathstr + "/key" - data := append(root.KeyPrefix, key...) + data := append(path.KeyPrefix, key...) - return abci.RequestQuery{Path: path, Data: data, Prove: true} + return abci.RequestQuery{Path: pathstr, Data: data, Prove: true} } -func (root Root) Query(cms types.CommitMultiStore, key []byte) (uint32, []byte, Proof) { - qres := cms.(types.Queryable).Query(root.RequestQuery(key)) +func (path Path) Query(cms types.CommitMultiStore, key []byte) (uint32, []byte, Proof) { + qres := cms.(types.Queryable).Query(path.RequestQuery(key)) return qres.Code, qres.Value, Proof{Key: key, Proof: qres.Proof} } -func (root Root) Key(key []byte) []byte { - return append(root.KeyPrefix, key...) // XXX: cloneAppend +func (path Path) Key(key []byte) []byte { + return append(path.KeyPrefix, key...) // XXX: cloneAppend } diff --git a/x/ibc/23-commitment/store.go b/x/ibc/23-commitment/store.go index 8cfaef4baaa1..0bae09c8c669 100644 --- a/x/ibc/23-commitment/store.go +++ b/x/ibc/23-commitment/store.go @@ -31,14 +31,21 @@ var _ Store = store{} type store struct { root Root + path Path proofs map[string]Proof verified map[string][]byte } // Proofs must be provided -func NewStore(root Root, proofs []Proof) (res store, err error) { +func NewStore(root Root, path Path, proofs []Proof) (res store, err error) { + if root.CommitmentKind() != path.CommitmentKind() { + err = errors.New("path type not matching with root's") + return + } + res = store{ root: root, + path: path, proofs: make(map[string]Proof), verified: make(map[string][]byte), } @@ -68,7 +75,7 @@ func (store store) Prove(key, value []byte) bool { if !ok { return false } - err := proof.Verify(store.root, value) + err := proof.Verify(store.root, store.path, value) if err != nil { return false } diff --git a/x/ibc/23-commitment/types.go b/x/ibc/23-commitment/types.go index e12d8f0fcef6..6453511a24e3 100644 --- a/x/ibc/23-commitment/types.go +++ b/x/ibc/23-commitment/types.go @@ -2,11 +2,14 @@ package commitment type Root interface { CommitmentKind() string - Update([]byte) Root +} + +type Path interface { + CommitmentKind() string } type Proof interface { CommitmentKind() string GetKey() []byte - Verify(Root, []byte) error + Verify(Root, Path, []byte) error } From 37387bfc951e2f662706c63d0e1ce1764fb00240 Mon Sep 17 00:00:00 2001 From: mossid Date: Sun, 14 Jul 2019 03:37:06 +0900 Subject: [PATCH 024/166] add path to codec --- x/ibc/23-commitment/codec.go | 1 + x/ibc/23-commitment/merkle/codec.go | 1 + 2 files changed, 2 insertions(+) diff --git a/x/ibc/23-commitment/codec.go b/x/ibc/23-commitment/codec.go index 33d5bc490d39..8e0bdf49aa9a 100644 --- a/x/ibc/23-commitment/codec.go +++ b/x/ibc/23-commitment/codec.go @@ -6,5 +6,6 @@ import ( func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*Root)(nil), nil) + cdc.RegisterInterface((*Path)(nil), nil) cdc.RegisterInterface((*Proof)(nil), nil) } diff --git a/x/ibc/23-commitment/merkle/codec.go b/x/ibc/23-commitment/merkle/codec.go index a5e088650b0c..993c46603a3e 100644 --- a/x/ibc/23-commitment/merkle/codec.go +++ b/x/ibc/23-commitment/merkle/codec.go @@ -6,5 +6,6 @@ import ( func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(Root{}, "ibc/commitment/merkle/Root", nil) + cdc.RegisterConcrete(Path{}, "ibc/commitment/merkle/Path", nil) cdc.RegisterConcrete(Proof{}, "ibc/commitment/merkle/Proof", nil) } From 5266fc92c6e4fbce706992cbf59c375b6fe9043e Mon Sep 17 00:00:00 2001 From: mossid Date: Fri, 7 Jun 2019 23:47:01 +0200 Subject: [PATCH 025/166] add client --- x/ibc/02-client/manager.go | 146 ++++++++++++++++++++++++++++ x/ibc/02-client/tendermint/types.go | 79 +++++++++++++++ x/ibc/02-client/types.go | 46 +++++++++ 3 files changed, 271 insertions(+) create mode 100644 x/ibc/02-client/manager.go create mode 100644 x/ibc/02-client/tendermint/types.go create mode 100644 x/ibc/02-client/types.go diff --git a/x/ibc/02-client/manager.go b/x/ibc/02-client/manager.go new file mode 100644 index 000000000000..89a8a878df49 --- /dev/null +++ b/x/ibc/02-client/manager.go @@ -0,0 +1,146 @@ +package client + +import ( + "errors" + "strconv" + + "github.com/cosmos/cosmos-sdk/store/mapping" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type IDGenerator func(sdk.Context /*Header,*/, mapping.Value) string + +func IntegerIDGenerator(ctx sdk.Context, v mapping.Value) string { + id := mapping.NewInteger(v, mapping.Dec).Incr(ctx) + return strconv.FormatUint(id, 10) +} + +type Manager struct { + protocol mapping.Mapping + + idval mapping.Value + idgen IDGenerator +} + +func NewManager(protocol, free mapping.Base, idgen IDGenerator) Manager { + return Manager{ + protocol: mapping.NewMapping(protocol, []byte("/")), + idval: mapping.NewValue(free, []byte("/id")), + idgen: idgen, + } +} + +/* +func (man Manager) RegisterKind(kind Kind, pred ValidityPredicate) Manager { + if _, ok := man.pred[kind]; ok { + panic("Kind already registered") + } + man.pred[kind] = pred + return man +} +*/ +func (man Manager) object(id string) Object { + return Object{ + id: id, + client: man.protocol.Value([]byte(id)), + freeze: mapping.NewBoolean(man.protocol.Value([]byte(id + "/freeze"))), + } +} + +func (man Manager) Create(ctx sdk.Context, cs Client) string { + id := man.idgen(ctx, man.idval) + err := man.object(id).create(ctx, cs) + if err != nil { + panic(err) + } + return id +} + +func (man Manager) Query(ctx sdk.Context, id string) (Object, error) { + res := man.object(id) + if !res.exists(ctx) { + return Object{}, errors.New("client not exists") + } + return res, nil +} + +type Object struct { + id string + client mapping.Value + freeze mapping.Boolean +} + +func (obj Object) create(ctx sdk.Context, st Client) error { + if obj.exists(ctx) { + return errors.New("Create client on already existing id") + } + obj.client.Set(ctx, st) + return nil +} + +func (obj Object) exists(ctx sdk.Context) bool { + return obj.client.Exists(ctx) +} + +func (obj Object) ID() string { + return obj.id +} + +func (obj Object) Value(ctx sdk.Context) (res Client) { + obj.client.Get(ctx, &res) + return +} + +func (obj Object) Is(ctx sdk.Context, client Client) bool { + return obj.client.Is(ctx, client) +} + +func (obj Object) Update(ctx sdk.Context, header Header) error { + if !obj.exists(ctx) { + panic("should not update nonexisting client") + } + + if obj.freeze.Get(ctx) { + return errors.New("client is frozen") + } + + var stored Client + obj.client.GetIfExists(ctx, &stored) + updated, err := stored.Validate(header) + if err != nil { + return err + } + + obj.client.Set(ctx, updated) + + return nil +} + +func (obj Object) Freeze(ctx sdk.Context) error { + if !obj.exists(ctx) { + panic("should not freeze nonexisting client") + } + + if obj.freeze.Get(ctx) { + return errors.New("client is already frozen") + } + + obj.freeze.Set(ctx, true) + + return nil +} + +func (obj Object) Delete(ctx sdk.Context) error { + if !obj.exists(ctx) { + panic("should not delete nonexisting client") + } + + if !obj.freeze.Get(ctx) { + return errors.New("client is not frozen") + } + + obj.client.Delete(ctx) + obj.freeze.Delete(ctx) + + return nil +} diff --git a/x/ibc/02-client/tendermint/types.go b/x/ibc/02-client/tendermint/types.go new file mode 100644 index 000000000000..54655cf702d4 --- /dev/null +++ b/x/ibc/02-client/tendermint/types.go @@ -0,0 +1,79 @@ +package tendermint + +import ( + "bytes" + + "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/x/ibc/02-client" + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" +) + +// Ref tendermint/lite/base_verifier.go + +var _ client.ValidityPredicateBase = ValidityPredicateBase{} + +type ValidityPredicateBase struct { + Height int64 + NextValidatorSet *types.ValidatorSet +} + +func (ValidityPredicateBase) Kind() client.Kind { + return client.Tendermint +} + +func (base ValidityPredicateBase) GetHeight() int64 { + return base.Height +} + +func (base ValidityPredicateBase) Equal(cbase client.ValidityPredicateBase) bool { + base0, ok := cbase.(ValidityPredicateBase) + if !ok { + return false + } + return base.Height == base0.Height && + bytes.Equal(base.NextValidatorSet.Hash(), base0.NextValidatorSet.Hash()) +} + +var _ client.Client = Client{} + +type Client struct { + Base ValidityPredicateBase + Root commitment.Root +} + +func (Client) Kind() client.Kind { + return client.Tendermint +} + +func (client Client) GetBase() client.ValidityPredicateBase { + return client.Base +} + +func (client Client) GetRoot() commitment.Root { + return client.Root +} + +func (client Client) Validate(header client.Header) (client.Client, error) { + return client, nil // XXX +} + +var _ client.Header = Header{} + +type Header struct { + Base ValidityPredicateBase + Root commitment.Root + Votes []*types.CommitSig +} + +func (header Header) Kind() client.Kind { + return client.Tendermint +} + +func (header Header) GetBase() client.ValidityPredicateBase { + return header.Base +} + +func (header Header) GetRoot() commitment.Root { + return header.Root +} diff --git a/x/ibc/02-client/types.go b/x/ibc/02-client/types.go new file mode 100644 index 000000000000..7b9f81ec7789 --- /dev/null +++ b/x/ibc/02-client/types.go @@ -0,0 +1,46 @@ +package client + +import ( + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" +) + +// TODO: types in this file should be (de/)serialized with proto in the future + +type AminoMarshaler interface { + MarshalAmino() (string, error) + UnmarshalAmino(string) error +} + +type ValidityPredicateBase interface { + Kind() Kind + GetHeight() int64 + Equal(ValidityPredicateBase) bool +} + +// ConsensusState +type Client interface { + Kind() Kind + GetBase() ValidityPredicateBase + GetRoot() commitment.Root + Validate(Header) (Client, error) // ValidityPredicate +} + +func Equal(client1, client2 Client) bool { + return client1.Kind() == client2.Kind() && + client1.GetBase().Equal(client2.GetBase()) +} + +type Header interface { + Kind() Kind + // Proof() HeaderProof + GetBase() ValidityPredicateBase // can be nil + GetRoot() commitment.Root +} + +// XXX: Kind should be enum? + +type Kind byte + +const ( + Tendermint Kind = iota +) From 06257bdfa0595474ff1fb48fe45b76bba435b823 Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 11 Jun 2019 18:14:19 +0200 Subject: [PATCH 026/166] add counterpartymanager --- x/ibc/02-client/manager.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/x/ibc/02-client/manager.go b/x/ibc/02-client/manager.go index 89a8a878df49..97f281cc9596 100644 --- a/x/ibc/02-client/manager.go +++ b/x/ibc/02-client/manager.go @@ -6,6 +6,8 @@ import ( "github.com/cosmos/cosmos-sdk/store/mapping" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) type IDGenerator func(sdk.Context /*Header,*/, mapping.Value) string @@ -24,12 +26,22 @@ type Manager struct { func NewManager(protocol, free mapping.Base, idgen IDGenerator) Manager { return Manager{ - protocol: mapping.NewMapping(protocol, []byte("/")), - idval: mapping.NewValue(free, []byte("/id")), + protocol: mapping.NewMapping(protocol, []byte("/client")), + idval: mapping.NewValue(free, []byte("/client/id")), idgen: idgen, } } +type CounterpartyManager struct { + protocol commitment.Mapping +} + +func NewCounterpartyManager(protocol commitment.Base) CounterpartyManager { + return CounterpartyManager{ + protocol: commitment.NewMapping(protocol, []byte("/client")), + } +} + /* func (man Manager) RegisterKind(kind Kind, pred ValidityPredicate) Manager { if _, ok := man.pred[kind]; ok { From 5b089e71a2f7671facd3f28e47d31e829cecc888 Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 11 Jun 2019 18:29:24 +0200 Subject: [PATCH 027/166] fix manager --- x/ibc/02-client/manager.go | 16 ++++++++++++++++ x/ibc/23-commitment/value.go | 7 +++++++ 2 files changed, 23 insertions(+) diff --git a/x/ibc/02-client/manager.go b/x/ibc/02-client/manager.go index 97f281cc9596..c22576b18cd2 100644 --- a/x/ibc/02-client/manager.go +++ b/x/ibc/02-client/manager.go @@ -76,12 +76,28 @@ func (man Manager) Query(ctx sdk.Context, id string) (Object, error) { return res, nil } +func (man CounterpartyManager) object(id string) CounterObject { + return CounterObject{ + id: id, + client: man.protocol.Value([]byte(id)), + } +} + +func (man CounterpartyManager) Query(id string) CounterObject { + return man.object(id) +} + type Object struct { id string client mapping.Value freeze mapping.Boolean } +type CounterObject struct { + id string + client commitment.Value +} + func (obj Object) create(ctx sdk.Context, st Client) error { if obj.exists(ctx) { return errors.New("Create client on already existing id") diff --git a/x/ibc/23-commitment/value.go b/x/ibc/23-commitment/value.go index 70b8502a44e6..10b409b28b99 100644 --- a/x/ibc/23-commitment/value.go +++ b/x/ibc/23-commitment/value.go @@ -45,6 +45,13 @@ func NewMapping(base Base, prefix []byte) Mapping { } } +func (m Mapping) Value(key []byte) Value { + return Value{ + base: m.base, + key: key, + } +} + type Value struct { base Base key []byte From d0f61b054c4256f540a6da849445c452a24ad8be Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 11 Jun 2019 18:44:25 +0200 Subject: [PATCH 028/166] add Is() to counterobject --- x/ibc/02-client/manager.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x/ibc/02-client/manager.go b/x/ibc/02-client/manager.go index c22576b18cd2..502478104757 100644 --- a/x/ibc/02-client/manager.go +++ b/x/ibc/02-client/manager.go @@ -123,6 +123,10 @@ func (obj Object) Is(ctx sdk.Context, client Client) bool { return obj.client.Is(ctx, client) } +func (obj CounterObject) Is(ctx sdk.Context, client Client) bool { + return obj.client.Is(ctx, client) +} + func (obj Object) Update(ctx sdk.Context, header Header) error { if !obj.exists(ctx) { panic("should not update nonexisting client") From a7b866f438f1e5127b8d1b379e7f2d3167170dc9 Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 12 Jun 2019 00:08:04 +0200 Subject: [PATCH 029/166] add readme, reflect ICS02 revision --- x/ibc/02-client/README.md | 49 ++++++++++++++++++++++++++++++++++++++ x/ibc/02-client/manager.go | 31 ++++++++++-------------- x/ibc/02-client/types.go | 26 ++++++++------------ 3 files changed, 72 insertions(+), 34 deletions(-) create mode 100644 x/ibc/02-client/README.md diff --git a/x/ibc/02-client/README.md b/x/ibc/02-client/README.md new file mode 100644 index 000000000000..a9bd6892771b --- /dev/null +++ b/x/ibc/02-client/README.md @@ -0,0 +1,49 @@ +# ICS 02: Client + +Package `client` defines types and method to store and update light clients which tracks on other chain's state. +The main type is `Client`, which provides `commitment.Root` to verify state proofs and `ConsensusState` to +verify header proofs. + +## Spec + +```typescript +interface ConsensusState { + height: uint64 + root: CommitmentRoot + validityPredicate: ValidityPredicate + eqivocationPredicate: EquivocationPredicate +} + +interface ClientState { + consensusState: ConsensusState + verifiedRoots: Map + frozen: bool +} + +interface Header { + height: uint64 + proof: HeaderProof + state: Maybe[ConsensusState] + root: CommitmentRoot +} + +type ValidityPredicate = (ConsensusState, Header) => Error | ConsensusState + +type EquivocationPredicate = (ConsensusState, Header, Header) => bool +``` + +## Impl + +### types.go + +`spec: interface ConsensusState` is implemented by `type ConsensusState`. `ConsensusState.{GetHeight(), GetRoot(), +Validate(), Equivocation()}` each corresponds to `spec: ConsensusState.{height, root, validityPredicate, +equivocationPredicate}`. `ConsensusState.Kind()` returns `Kind`, which is an enum indicating the type of the +consensus algorithm. + +`spec: interface Header` is implemented by `type Header`. `Header{GetHeight(), Proof(), State(), GetRoot()}` +each corresponds to `spec: Header.{height, proof, state, root}`. + +### manager.go + +`spec: interface ClientState` is implemented by `type Object`. // TODO diff --git a/x/ibc/02-client/manager.go b/x/ibc/02-client/manager.go index 502478104757..0134d8340d55 100644 --- a/x/ibc/02-client/manager.go +++ b/x/ibc/02-client/manager.go @@ -10,6 +10,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) +// XXX: implement spec: ClientState.verifiedRoots + type IDGenerator func(sdk.Context /*Header,*/, mapping.Value) string func IntegerIDGenerator(ctx sdk.Context, v mapping.Value) string { @@ -59,13 +61,14 @@ func (man Manager) object(id string) Object { } } -func (man Manager) Create(ctx sdk.Context, cs Client) string { +func (man Manager) Create(ctx sdk.Context, cs ConsensusState) (Object, error) { id := man.idgen(ctx, man.idval) - err := man.object(id).create(ctx, cs) - if err != nil { - panic(err) + obj := man.object(id) + if obj.exists(ctx) { + return Object{}, errors.New("Create client on already existing id") } - return id + obj.client.Set(ctx, cs) + return obj, nil } func (man Manager) Query(ctx sdk.Context, id string) (Object, error) { @@ -89,7 +92,7 @@ func (man CounterpartyManager) Query(id string) CounterObject { type Object struct { id string - client mapping.Value + client mapping.Value // ConsensusState freeze mapping.Boolean } @@ -98,14 +101,6 @@ type CounterObject struct { client commitment.Value } -func (obj Object) create(ctx sdk.Context, st Client) error { - if obj.exists(ctx) { - return errors.New("Create client on already existing id") - } - obj.client.Set(ctx, st) - return nil -} - func (obj Object) exists(ctx sdk.Context) bool { return obj.client.Exists(ctx) } @@ -114,16 +109,16 @@ func (obj Object) ID() string { return obj.id } -func (obj Object) Value(ctx sdk.Context) (res Client) { +func (obj Object) Value(ctx sdk.Context) (res ConsensusState) { obj.client.Get(ctx, &res) return } -func (obj Object) Is(ctx sdk.Context, client Client) bool { +func (obj Object) Is(ctx sdk.Context, client ConsensusState) bool { return obj.client.Is(ctx, client) } -func (obj CounterObject) Is(ctx sdk.Context, client Client) bool { +func (obj CounterObject) Is(ctx sdk.Context, client ConsensusState) bool { return obj.client.Is(ctx, client) } @@ -136,7 +131,7 @@ func (obj Object) Update(ctx sdk.Context, header Header) error { return errors.New("client is frozen") } - var stored Client + var stored ConsensusState obj.client.GetIfExists(ctx, &stored) updated, err := stored.Validate(header) if err != nil { diff --git a/x/ibc/02-client/types.go b/x/ibc/02-client/types.go index 7b9f81ec7789..2ae484853354 100644 --- a/x/ibc/02-client/types.go +++ b/x/ibc/02-client/types.go @@ -5,35 +5,29 @@ import ( ) // TODO: types in this file should be (de/)serialized with proto in the future - -type AminoMarshaler interface { - MarshalAmino() (string, error) - UnmarshalAmino(string) error -} - -type ValidityPredicateBase interface { - Kind() Kind - GetHeight() int64 - Equal(ValidityPredicateBase) bool -} +// currently amkno codec handles it // ConsensusState -type Client interface { +type ConsensusState interface { Kind() Kind - GetBase() ValidityPredicateBase + GetHeight() uint64 GetRoot() commitment.Root - Validate(Header) (Client, error) // ValidityPredicate + Validate(Header) (ConsensusState, error) // ValidityPredicate + Equivocation(Header, Header) bool // EquivocationPredicate } -func Equal(client1, client2 Client) bool { +/* +func Equal(client1, client2 ConsensusState) bool { return client1.Kind() == client2.Kind() && client1.GetBase().Equal(client2.GetBase()) } +*/ type Header interface { Kind() Kind + GetHeight() uint64 // Proof() HeaderProof - GetBase() ValidityPredicateBase // can be nil + State() ConsensusState // can be nil GetRoot() commitment.Root } From 392e3abb3f81e0ce383583123389ba4bbd1b62ea Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 12 Jun 2019 00:28:13 +0200 Subject: [PATCH 030/166] reflect downstream ics --- x/ibc/02-client/manager.go | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/x/ibc/02-client/manager.go b/x/ibc/02-client/manager.go index 0134d8340d55..196d62e62fdc 100644 --- a/x/ibc/02-client/manager.go +++ b/x/ibc/02-client/manager.go @@ -4,7 +4,7 @@ import ( "errors" "strconv" - "github.com/cosmos/cosmos-sdk/store/mapping" + "github.com/cosmos/cosmos-sdk/store/state" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" @@ -12,24 +12,24 @@ import ( // XXX: implement spec: ClientState.verifiedRoots -type IDGenerator func(sdk.Context /*Header,*/, mapping.Value) string +type IDGenerator func(sdk.Context /*Header,*/, state.Value) string -func IntegerIDGenerator(ctx sdk.Context, v mapping.Value) string { - id := mapping.NewInteger(v, mapping.Dec).Incr(ctx) +func IntegerIDGenerator(ctx sdk.Context, v state.Value) string { + id := state.NewInteger(v, state.Dec).Incr(ctx) return strconv.FormatUint(id, 10) } type Manager struct { - protocol mapping.Mapping + protocol state.Mapping - idval mapping.Value + idval state.Value idgen IDGenerator } -func NewManager(protocol, free mapping.Base, idgen IDGenerator) Manager { +func NewManager(protocol, free state.Base, idgen IDGenerator) Manager { return Manager{ - protocol: mapping.NewMapping(protocol, []byte("/client")), - idval: mapping.NewValue(free, []byte("/client/id")), + protocol: state.NewMapping(protocol, []byte("/client")), + idval: state.NewValue(free, []byte("/client/id")), idgen: idgen, } } @@ -57,7 +57,7 @@ func (man Manager) object(id string) Object { return Object{ id: id, client: man.protocol.Value([]byte(id)), - freeze: mapping.NewBoolean(man.protocol.Value([]byte(id + "/freeze"))), + freeze: state.NewBoolean(man.protocol.Value([]byte(id + "/freeze"))), } } @@ -92,8 +92,8 @@ func (man CounterpartyManager) Query(id string) CounterObject { type Object struct { id string - client mapping.Value // ConsensusState - freeze mapping.Boolean + client state.Value // ConsensusState + freeze state.Boolean } type CounterObject struct { @@ -114,10 +114,6 @@ func (obj Object) Value(ctx sdk.Context) (res ConsensusState) { return } -func (obj Object) Is(ctx sdk.Context, client ConsensusState) bool { - return obj.client.Is(ctx, client) -} - func (obj CounterObject) Is(ctx sdk.Context, client ConsensusState) bool { return obj.client.Is(ctx, client) } From 5d46c96f54ab118a7994e92150114835f54b2a8b Mon Sep 17 00:00:00 2001 From: mossid Date: Thu, 13 Jun 2019 16:00:18 +0200 Subject: [PATCH 031/166] test in progress --- x/ibc/02-client/tendermint/types.go | 96 ++++++----- x/ibc/02-client/tendermint/types_test.go | 131 ++++++++++++++ x/ibc/02-client/tendermint/valset_test.go | 197 ++++++++++++++++++++++ x/ibc/02-client/types.go | 3 - 4 files changed, 386 insertions(+), 41 deletions(-) create mode 100644 x/ibc/02-client/tendermint/types_test.go create mode 100644 x/ibc/02-client/tendermint/valset_test.go diff --git a/x/ibc/02-client/tendermint/types.go b/x/ibc/02-client/tendermint/types.go index 54655cf702d4..1442d91d6c99 100644 --- a/x/ibc/02-client/tendermint/types.go +++ b/x/ibc/02-client/tendermint/types.go @@ -2,78 +2,98 @@ package tendermint import ( "bytes" + "errors" + "fmt" + lerr "github.com/tendermint/tendermint/lite/errors" "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) -// Ref tendermint/lite/base_verifier.go - -var _ client.ValidityPredicateBase = ValidityPredicateBase{} +var _ client.ConsensusState = ConsensusState{} -type ValidityPredicateBase struct { - Height int64 +// Ref tendermint/lite/base_verifier.go +type ConsensusState struct { + ChainID string + Height uint64 + Root commitment.Root NextValidatorSet *types.ValidatorSet } -func (ValidityPredicateBase) Kind() client.Kind { +func (ConsensusState) Kind() client.Kind { return client.Tendermint } -func (base ValidityPredicateBase) GetHeight() int64 { - return base.Height +func (cs ConsensusState) GetHeight() uint64 { + return cs.Height } -func (base ValidityPredicateBase) Equal(cbase client.ValidityPredicateBase) bool { - base0, ok := cbase.(ValidityPredicateBase) - if !ok { - return false +func (cs ConsensusState) GetRoot() commitment.Root { + return cs.Root +} + +func (cs ConsensusState) update(header Header) ConsensusState { + return ConsensusState{ + ChainID: cs.ChainID, + Height: uint64(header.Height), + Root: header.AppHash, + NextValidatorSet: header.NextValidatorSet, } - return base.Height == base0.Height && - bytes.Equal(base.NextValidatorSet.Hash(), base0.NextValidatorSet.Hash()) } -var _ client.Client = Client{} +func (cs ConsensusState) Validate(cheader client.Header) (client.ConsensusState, error) { + header, ok := cheader.(Header) + if !ok { + return nil, errors.New("invalid type") + } -type Client struct { - Base ValidityPredicateBase - Root commitment.Root -} + nextvalset := cs.NextValidatorSet + nexthash := nextvalset.Hash() -func (Client) Kind() client.Kind { - return client.Tendermint -} + if cs.Height == uint64(header.Height-1) { + nexthash = cs.NextValidatorSet.Hash() + if !bytes.Equal(header.ValidatorsHash, nexthash) { + fmt.Println(111) + return nil, lerr.ErrUnexpectedValidators(header.ValidatorsHash, nexthash) + } + } -func (client Client) GetBase() client.ValidityPredicateBase { - return client.Base -} + if !bytes.Equal(header.NextValidatorsHash, header.NextValidatorSet.Hash()) { + fmt.Println(header) + return nil, lerr.ErrUnexpectedValidators(header.NextValidatorsHash, header.NextValidatorSet.Hash()) + } + + err := header.ValidateBasic(cs.ChainID) + if err != nil { + return nil, err + } -func (client Client) GetRoot() commitment.Root { - return client.Root + err = cs.NextValidatorSet.VerifyCommit(cs.ChainID, header.Commit.BlockID, header.Height, header.Commit) + if err != nil { + return nil, err + } + + return cs.update(header), nil } -func (client Client) Validate(header client.Header) (client.Client, error) { - return client, nil // XXX +func (cs ConsensusState) Equivocation(header1, header2 client.Header) bool { + return false // XXX } var _ client.Header = Header{} type Header struct { - Base ValidityPredicateBase - Root commitment.Root - Votes []*types.CommitSig + // XXX: don't take the entire struct + types.SignedHeader + NextValidatorSet *types.ValidatorSet } func (header Header) Kind() client.Kind { return client.Tendermint } -func (header Header) GetBase() client.ValidityPredicateBase { - return header.Base -} - -func (header Header) GetRoot() commitment.Root { - return header.Root +func (header Header) GetHeight() uint64 { + return uint64(header.Height) } diff --git a/x/ibc/02-client/tendermint/types_test.go b/x/ibc/02-client/tendermint/types_test.go new file mode 100644 index 000000000000..44dc0fc9aba9 --- /dev/null +++ b/x/ibc/02-client/tendermint/types_test.go @@ -0,0 +1,131 @@ +package tendermint + +import ( + "testing" + + "github.com/stretchr/testify/require" + + abci "github.com/tendermint/tendermint/abci/types" + dbm "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store" + stypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const chainid = "testchain" + +func defaultComponents() (sdk.StoreKey, sdk.Context, stypes.CommitMultiStore, *codec.Codec) { + key := sdk.NewKVStoreKey("ibc") + db := dbm.NewMemDB() + cms := store.NewCommitMultiStore(db) + cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) + err := cms.LoadLatestVersion() + if err != nil { + panic(err) + } + ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) + cdc := codec.New() + return key, ctx, cms, cdc +} + +type node struct { + valset MockValidators + + cms sdk.CommitMultiStore + store sdk.KVStore + + commits []tmtypes.SignedHeader +} + +func NewNode(valset MockValidators) *node { + key, ctx, cms, _ := defaultComponents() + return &node{ + valset: valset, + cms: cms, + store: ctx.KVStore(key), + commits: nil, + } +} + +func (node *node) last() tmtypes.SignedHeader { + if len(node.commits) == 0 { + return tmtypes.SignedHeader{} + } + return node.commits[len(node.commits)-1] +} + +func (node *node) Commit() tmtypes.SignedHeader { + valsethash := node.valset.ValidatorSet().Hash() + nextvalset := node.valset.Mutate(false) + nextvalsethash := nextvalset.ValidatorSet().Hash() + commitid := node.cms.Commit() + + header := tmtypes.Header{ + ChainID: chainid, + Height: int64(len(node.commits) + 1), + LastBlockID: tmtypes.BlockID{ + Hash: node.last().Header.Hash(), + }, + + ValidatorsHash: valsethash, + NextValidatorsHash: nextvalsethash, + AppHash: commitid.Hash, + } + + commit := node.valset.Sign(header) + + node.commits = append(node.commits, commit) + + return commit +} + +func (node *node) Set(key, value string) { + node.store.Set(append([]byte{0x00}, []byte(key)...), []byte(value)) +} + +type Verifier struct { + ConsensusState +} + +func NewVerifier(header tmtypes.SignedHeader, nextvalset MockValidators) *Verifier { + return &Verifier{ + ConsensusState{ + ChainID: chainid, + Height: uint64(header.Height), + Root: header.AppHash, + NextValidatorSet: nextvalset.ValidatorSet(), + }, + } +} + +func (v *Verifier) Validate(header tmtypes.SignedHeader, nextvalset MockValidators) error { + newcs, err := v.ConsensusState.Validate( + Header{ + SignedHeader: header, + NextValidatorSet: nextvalset.ValidatorSet(), + }, + ) + if err != nil { + return err + } + v.ConsensusState = newcs.(ConsensusState) + + return nil +} + +func TestUpdate(t *testing.T) { + node := NewNode(NewMockValidators(100, 10)) + + node.Commit() + + verifier := NewVerifier(node.last(), node.valset) + + header := node.Commit() + + err := verifier.Validate(header, node.valset) + require.NoError(t, err) +} diff --git a/x/ibc/02-client/tendermint/valset_test.go b/x/ibc/02-client/tendermint/valset_test.go new file mode 100644 index 000000000000..10aa5dc0eae7 --- /dev/null +++ b/x/ibc/02-client/tendermint/valset_test.go @@ -0,0 +1,197 @@ +package tendermint + +import ( + "bytes" + "fmt" + "sort" + + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" + tmtypes "github.com/tendermint/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// reimplementing tmtypes.MockPV to make it marshallable +type mockPV struct { + PrivKey crypto.PrivKey +} + +var _ tmtypes.PrivValidator = (*mockPV)(nil) + +func newMockPV() *mockPV { + return &mockPV{ed25519.GenPrivKey()} +} + +func (pv *mockPV) GetAddress() tmtypes.Address { + return pv.PrivKey.PubKey().Address() +} + +func (pv *mockPV) GetPubKey() crypto.PubKey { + return pv.PrivKey.PubKey() +} + +func (pv *mockPV) SignVote(chainID string, vote *tmtypes.Vote) error { + signBytes := vote.SignBytes(chainID) + sig, err := pv.PrivKey.Sign(signBytes) + if err != nil { + return err + } + vote.Signature = sig + return nil +} + +func (pv *mockPV) SignProposal(string, *tmtypes.Proposal) error { + panic("not needed") +} + +// MockValset +type MockValidator struct { + MockPV *mockPV + Power sdk.Dec +} + +func NewMockValidator(power sdk.Dec) MockValidator { + return MockValidator{ + MockPV: newMockPV(), + Power: power, + } +} + +func (val MockValidator) GetOperator() sdk.ValAddress { + return sdk.ValAddress(val.MockPV.GetAddress()) +} + +func (val MockValidator) GetConsAddr() sdk.ConsAddress { + return sdk.GetConsAddress(val.MockPV.GetPubKey()) +} + +func (val MockValidator) GetConsPubKey() crypto.PubKey { + return val.MockPV.GetPubKey() +} + +func (val MockValidator) GetPower() sdk.Dec { + return val.Power +} + +func (val MockValidator) Validator() *tmtypes.Validator { + return tmtypes.NewValidator( + val.GetConsPubKey(), + val.GetPower().RoundInt64(), + ) +} + +type MockValidators []MockValidator + +var _ sort.Interface = MockValidators{} + +func NewMockValidators(num int, power int64) MockValidators { + res := make(MockValidators, num) + for i := range res { + res[i] = NewMockValidator(sdk.NewDec(power)) + } + + // ddd + fmt.Println(333) + for _, val := range res { + fmt.Println(val) + } + + // ddd + return res +} + +func (vals MockValidators) Len() int { + return len(vals) +} + +func (vals MockValidators) Less(i, j int) bool { + return bytes.Compare([]byte(vals[i].GetConsAddr()), []byte(vals[j].GetConsAddr())) == -1 +} + +func (vals MockValidators) Swap(i, j int) { + it := vals[j] + vals[j] = vals[i] + vals[i] = it +} + +func (vals MockValidators) TotalPower() sdk.Dec { + res := sdk.ZeroDec() + for _, val := range vals { + res = res.Add(val.Power) + } + return res +} + +func (vals MockValidators) Sign(header tmtypes.Header) tmtypes.SignedHeader { + precommits := make([]*tmtypes.CommitSig, len(vals)) + for i, val := range vals { + precommits[i] = (&tmtypes.Vote{ + BlockID: tmtypes.BlockID{ + Hash: header.Hash(), + }, + ValidatorAddress: val.MockPV.GetAddress(), + ValidatorIndex: i, + Height: header.Height, + Type: tmtypes.PrecommitType, + }).CommitSig() + val.MockPV.SignVote("", (*tmtypes.Vote)(precommits[i])) + } + + return tmtypes.SignedHeader{ + Header: &header, + Commit: &tmtypes.Commit{ + BlockID: tmtypes.BlockID{ + Hash: header.Hash(), + }, + Precommits: precommits, + }, + } +} + +// Mutate valset +func (vals MockValidators) Mutate(majority bool) MockValidators { + var num int + if majority { + num = len(vals) * 2 / 3 + } else { + num = len(vals) * 1 / 6 + } + + res := make(MockValidators, len(vals)) + + for i := 0; i < len(vals)-num; i++ { + res[i] = vals[num:][i] + } + + for i := len(vals) - num; i < len(vals); i++ { + res[i] = NewMockValidator(vals[0].Power) + } + + // ddd + fmt.Println(333) + for _, val := range res { + fmt.Println(val) + } + + // ddd + + return res +} + +func (vals MockValidators) ValidatorSet() *tmtypes.ValidatorSet { + tmvals := make([]*tmtypes.Validator, len(vals)) + + for i, val := range vals { + tmvals[i] = val.Validator() + } + + // ddd + fmt.Println(333444) + for _, val := range tmvals { + fmt.Println(val) + } + + // ddd + return tmtypes.NewValidatorSet(tmvals) +} diff --git a/x/ibc/02-client/types.go b/x/ibc/02-client/types.go index 2ae484853354..2a34855085b9 100644 --- a/x/ibc/02-client/types.go +++ b/x/ibc/02-client/types.go @@ -26,9 +26,6 @@ func Equal(client1, client2 ConsensusState) bool { type Header interface { Kind() Kind GetHeight() uint64 - // Proof() HeaderProof - State() ConsensusState // can be nil - GetRoot() commitment.Root } // XXX: Kind should be enum? From b934d57bc256a81d61e29ab658a46062eb4a9dc4 Mon Sep 17 00:00:00 2001 From: mossid Date: Thu, 13 Jun 2019 19:08:13 +0200 Subject: [PATCH 032/166] add test --- x/ibc/02-client/tendermint/types.go | 6 +- x/ibc/02-client/tendermint/types_test.go | 108 +++++++++++++++++++--- x/ibc/02-client/tendermint/valset_test.go | 46 +++------ 3 files changed, 114 insertions(+), 46 deletions(-) diff --git a/x/ibc/02-client/tendermint/types.go b/x/ibc/02-client/tendermint/types.go index 1442d91d6c99..8dd3cfca7294 100644 --- a/x/ibc/02-client/tendermint/types.go +++ b/x/ibc/02-client/tendermint/types.go @@ -3,7 +3,6 @@ package tendermint import ( "bytes" "errors" - "fmt" lerr "github.com/tendermint/tendermint/lite/errors" "github.com/tendermint/tendermint/types" @@ -55,13 +54,11 @@ func (cs ConsensusState) Validate(cheader client.Header) (client.ConsensusState, if cs.Height == uint64(header.Height-1) { nexthash = cs.NextValidatorSet.Hash() if !bytes.Equal(header.ValidatorsHash, nexthash) { - fmt.Println(111) return nil, lerr.ErrUnexpectedValidators(header.ValidatorsHash, nexthash) } } if !bytes.Equal(header.NextValidatorsHash, header.NextValidatorSet.Hash()) { - fmt.Println(header) return nil, lerr.ErrUnexpectedValidators(header.NextValidatorsHash, header.NextValidatorSet.Hash()) } @@ -70,7 +67,7 @@ func (cs ConsensusState) Validate(cheader client.Header) (client.ConsensusState, return nil, err } - err = cs.NextValidatorSet.VerifyCommit(cs.ChainID, header.Commit.BlockID, header.Height, header.Commit) + err = cs.NextValidatorSet.VerifyFutureCommit(header.ValidatorSet, cs.ChainID, header.Commit.BlockID, header.Height, header.Commit) if err != nil { return nil, err } @@ -87,6 +84,7 @@ var _ client.Header = Header{} type Header struct { // XXX: don't take the entire struct types.SignedHeader + ValidatorSet *types.ValidatorSet NextValidatorSet *types.ValidatorSet } diff --git a/x/ibc/02-client/tendermint/types_test.go b/x/ibc/02-client/tendermint/types_test.go index 44dc0fc9aba9..14ca7adad0ea 100644 --- a/x/ibc/02-client/tendermint/types_test.go +++ b/x/ibc/02-client/tendermint/types_test.go @@ -1,11 +1,13 @@ package tendermint import ( + "math/rand" "testing" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" + cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" tmtypes "github.com/tendermint/tendermint/types" @@ -14,6 +16,9 @@ import ( "github.com/cosmos/cosmos-sdk/store" stypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) const chainid = "testchain" @@ -33,7 +38,8 @@ func defaultComponents() (sdk.StoreKey, sdk.Context, stypes.CommitMultiStore, *c } type node struct { - valset MockValidators + prevvalset MockValidators + valset MockValidators cms sdk.CommitMultiStore store sdk.KVStore @@ -60,7 +66,7 @@ func (node *node) last() tmtypes.SignedHeader { func (node *node) Commit() tmtypes.SignedHeader { valsethash := node.valset.ValidatorSet().Hash() - nextvalset := node.valset.Mutate(false) + nextvalset := node.valset.Mutate() nextvalsethash := nextvalset.ValidatorSet().Hash() commitid := node.cms.Commit() @@ -78,15 +84,13 @@ func (node *node) Commit() tmtypes.SignedHeader { commit := node.valset.Sign(header) + node.prevvalset = node.valset + node.valset = nextvalset node.commits = append(node.commits, commit) return commit } -func (node *node) Set(key, value string) { - node.store.Set(append([]byte{0x00}, []byte(key)...), []byte(value)) -} - type Verifier struct { ConsensusState } @@ -102,10 +106,11 @@ func NewVerifier(header tmtypes.SignedHeader, nextvalset MockValidators) *Verifi } } -func (v *Verifier) Validate(header tmtypes.SignedHeader, nextvalset MockValidators) error { +func (v *Verifier) Validate(header tmtypes.SignedHeader, valset, nextvalset MockValidators) error { newcs, err := v.ConsensusState.Validate( Header{ SignedHeader: header, + ValidatorSet: valset.ValidatorSet(), NextValidatorSet: nextvalset.ValidatorSet(), }, ) @@ -117,15 +122,96 @@ func (v *Verifier) Validate(header tmtypes.SignedHeader, nextvalset MockValidato return nil } -func TestUpdate(t *testing.T) { +func testUpdate(t *testing.T, interval int, ok bool) { node := NewNode(NewMockValidators(100, 10)) node.Commit() verifier := NewVerifier(node.last(), node.valset) - header := node.Commit() + for i := 0; i < 100; i++ { + header := node.Commit() + + if i%interval == 0 { + err := verifier.Validate(header, node.prevvalset, node.valset) + if ok { + require.NoError(t, err) + } else { + require.Error(t, err) + } + } + } +} + +func TestEveryBlockUpdate(t *testing.T) { + testUpdate(t, 1, true) +} + +func TestEvenBlockUpdate(t *testing.T) { + testUpdate(t, 2, true) +} + +func TestSixthBlockUpdate(t *testing.T) { + testUpdate(t, 6, true) +} + +/* +// This should fail, since the amount of mutation is so large +// Commented out because it sometimes success +func TestTenthBlockUpdate(t *testing.T) { + testUpdate(t, 10, false) +} +*/ + +func key(str []byte) []byte { + return append([]byte{0x00}, str...) +} + +func (node *node) query(t *testing.T, k []byte) ([]byte, commitment.Proof) { + qres := node.cms.(stypes.Queryable).Query(abci.RequestQuery{Path: "/ibc/key", Data: key(k), Prove: true}) + require.Equal(t, uint32(0), qres.Code, qres.Log) + proof := merkle.Proof{ + Key: []byte(k), + Proof: qres.Proof, + } + return qres.Value, proof +} + +func (node *node) Set(k, value []byte) { + node.store.Set(key(k), value) +} + +func testProof(t *testing.T) { + node := NewNode(NewMockValidators(100, 10)) + + node.Commit() + + kvps := cmn.KVPairs{} + for h := 0; h < 20; h++ { + for i := 0; i < 100; i++ { + k := make([]byte, 32) + v := make([]byte, 32) + rand.Read(k) + rand.Read(v) + kvps = append(kvps, cmn.KVPair{Key: k, Value: v}) + node.Set(k, v) + } + header := node.Commit() + proofs := []commitment.Proof{} + for _, kvp := range kvps { + v, p := node.query(t, []byte(kvp.Key)) + require.Equal(t, kvp.Value, v) + proofs = append(proofs, p) + } + cstore, err := commitment.NewStore([]byte(header.AppHash), proofs) + require.NoError(t, err) + + for _, kvp := range kvps { + require.True(t, cstore.Prove(kvp.Key, kvp.Value)) + } + } +} - err := verifier.Validate(header, node.valset) - require.NoError(t, err) +func TestProofs(t *testing.T) { + testProof(t) } diff --git a/x/ibc/02-client/tendermint/valset_test.go b/x/ibc/02-client/tendermint/valset_test.go index 10aa5dc0eae7..5649a3533273 100644 --- a/x/ibc/02-client/tendermint/valset_test.go +++ b/x/ibc/02-client/tendermint/valset_test.go @@ -2,7 +2,6 @@ package tendermint import ( "bytes" - "fmt" "sort" "github.com/tendermint/tendermint/crypto" @@ -91,13 +90,8 @@ func NewMockValidators(num int, power int64) MockValidators { res[i] = NewMockValidator(sdk.NewDec(power)) } - // ddd - fmt.Println(333) - for _, val := range res { - fmt.Println(val) - } + sort.Sort(res) - // ddd return res } @@ -124,9 +118,10 @@ func (vals MockValidators) TotalPower() sdk.Dec { } func (vals MockValidators) Sign(header tmtypes.Header) tmtypes.SignedHeader { + precommits := make([]*tmtypes.CommitSig, len(vals)) for i, val := range vals { - precommits[i] = (&tmtypes.Vote{ + vote := &tmtypes.Vote{ BlockID: tmtypes.BlockID{ Hash: header.Hash(), }, @@ -134,8 +129,9 @@ func (vals MockValidators) Sign(header tmtypes.Header) tmtypes.SignedHeader { ValidatorIndex: i, Height: header.Height, Type: tmtypes.PrecommitType, - }).CommitSig() - val.MockPV.SignVote("", (*tmtypes.Vote)(precommits[i])) + } + val.MockPV.SignVote(chainid, vote) + precommits[i] = vote.CommitSig() } return tmtypes.SignedHeader{ @@ -150,13 +146,8 @@ func (vals MockValidators) Sign(header tmtypes.Header) tmtypes.SignedHeader { } // Mutate valset -func (vals MockValidators) Mutate(majority bool) MockValidators { - var num int - if majority { - num = len(vals) * 2 / 3 - } else { - num = len(vals) * 1 / 6 - } +func (vals MockValidators) Mutate() MockValidators { + num := len(vals) / 20 // 5% change each block res := make(MockValidators, len(vals)) @@ -168,15 +159,15 @@ func (vals MockValidators) Mutate(majority bool) MockValidators { res[i] = NewMockValidator(vals[0].Power) } - // ddd - fmt.Println(333) - for _, val := range res { - fmt.Println(val) - } + sort.Sort(res) - // ddd + for i, val := range vals { + if val != res[i] { + return res + } + } - return res + panic("not mutated") } func (vals MockValidators) ValidatorSet() *tmtypes.ValidatorSet { @@ -186,12 +177,5 @@ func (vals MockValidators) ValidatorSet() *tmtypes.ValidatorSet { tmvals[i] = val.Validator() } - // ddd - fmt.Println(333444) - for _, val := range tmvals { - fmt.Println(val) - } - - // ddd return tmtypes.NewValidatorSet(tmvals) } From 2ac7d8c0482d9d56af262adb47d44dec6c8d99d1 Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 19 Jun 2019 13:07:06 +0100 Subject: [PATCH 033/166] in progres --- x/ibc/client/cli/query.go | 216 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 x/ibc/client/cli/query.go diff --git a/x/ibc/client/cli/query.go b/x/ibc/client/cli/query.go new file mode 100644 index 000000000000..ca979151c499 --- /dev/null +++ b/x/ibc/client/cli/query.go @@ -0,0 +1,216 @@ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/tendermint" + ibc "github.com/cosmos/cosmos-sdk/x/ibc/keeper" +) + +func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { + ibcQueryCmd := &cobra.Command{ + Use: "ibc", + Short: "IBC query subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + } + + ibcQueryCmd.AddCommand(client.GetCommands( + GetCmdQueryConsensusState(cdc), + GetCmdQueryHeader(cdc), + GetCmdQueryClient(cdc), + GetCmdQueryConnection(cdc), + GetCmdQueryChannel(cdc), + )...) + return ibcQueryCmd +} + +func GetCmdQueryClient(cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "client", + Short: "Query stored client", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.NewCLIContext().WithCodec(cdc) + + keeper := ibc.DummyKeeper() + + var state ibc.ConsensusState + statebz, _, err := query(ctx, keeper.Client.Object(args[0]).Key()) + if err != nil { + return err + } + cdc.MustUnmarshalBinaryBare(statebz, &state) + + fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, state)) + + return nil + }, + } +} + +func GetCmdQueryConsensusState(cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "consensus-state", + Short: "Query the latest consensus state of the running chain", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.NewCLIContext().WithCodec(cdc) + + node, err := ctx.GetNode() + if err != nil { + return err + } + + info, err := node.ABCIInfo() + if err != nil { + return err + } + + height := info.Response.LastBlockHeight + prevheight := height - 1 + + commit, err := node.Commit(&height) + if err != nil { + return err + } + + validators, err := node.Validators(&prevheight) + if err != nil { + return err + } + + state := tendermint.ConsensusState{ + ChainID: commit.ChainID, + Height: uint64(commit.Height), + Root: []byte(commit.AppHash), + NextValidatorSet: tmtypes.NewValidatorSet(validators.Validators), + } + + fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, state)) + + return nil + }, + } +} + +func GetCmdQueryHeader(cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "header", + Short: "Query the latest header of the running chain", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.NewCLIContext().WithCodec(cdc) + + node, err := ctx.GetNode() + if err != nil { + return err + } + + info, err := node.ABCIInfo() + if err != nil { + return err + } + + height := info.Response.LastBlockHeight + prevheight := height - 1 + + commit, err := node.Commit(&height) + if err != nil { + return err + } + + validators, err := node.Validators(&prevheight) + if err != nil { + return err + } + + nextvalidators, err := node.Validators(&height) + if err != nil { + return err + } + + header := tendermint.Header{ + SignedHeader: commit.SignedHeader, + ValidatorSet: tmtypes.NewValidatorSet(validators.Validators), + NextValidatorSet: tmtypes.NewValidatorSet(nextvalidators.Validators), + } + + fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, header)) + + return nil + }, + } +} + +func GetCmdQueryConnection(cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "connection", + Short: "Query an existing connection", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.NewCLIContext().WithCodec(cdc) + + keeper := ibc.DummyKeeper() + + var conn ibc.Connection + connbz, _, err := query(ctx, keeper.Connection.Object(args[0]).Key()) + if err != nil { + return err + } + cdc.MustUnmarshalBinaryBare(connbz, &conn) + + fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, conn)) + + return nil + }, + } +} + +func GetCmdQueryChannel(cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "channel", + Short: "Query an existing channel", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.NewCLIContext().WithCodec(cdc) + + keeper := ibc.DummyKeeper() + + var conn ibc.Channel + connbz, _, err := query(ctx, keeper.Channel.Object(args[0], args[1]).Key()) + if err != nil { + return err + } + cdc.MustUnmarshalBinaryBare(connbz, &conn) + + fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, conn)) + + return nil + }, + } +} + +func GetCmdQuerySendSequence(cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "send-sequence", + Short: "Query the send sequence of a channel", + Args: cobra.ExactArgs(), + RunE: func(cmd *cobra.Command, args []string) error { + + }, + } +} + +func GetCmdQueryReceiveSequence(cdc *codec.Codec) *cobra.Command { + +} + +func GetCmdQueryPacket(cdc *codec.Codec) *cobra.Command { +} From a5d86b7c627f806d25a1f7d47942c4b6e0eaefcf Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 25 Jun 2019 12:20:15 +0200 Subject: [PATCH 034/166] fin rebase --- x/ibc/02-client/tendermint/types.go | 2 +- x/ibc/02-client/tendermint/types_test.go | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/x/ibc/02-client/tendermint/types.go b/x/ibc/02-client/tendermint/types.go index 8dd3cfca7294..26a4c35370e6 100644 --- a/x/ibc/02-client/tendermint/types.go +++ b/x/ibc/02-client/tendermint/types.go @@ -37,7 +37,7 @@ func (cs ConsensusState) update(header Header) ConsensusState { return ConsensusState{ ChainID: cs.ChainID, Height: uint64(header.Height), - Root: header.AppHash, + Root: cs.GetRoot().Update(header.AppHash), NextValidatorSet: header.NextValidatorSet, } } diff --git a/x/ibc/02-client/tendermint/types_test.go b/x/ibc/02-client/tendermint/types_test.go index 14ca7adad0ea..8bde76d10302 100644 --- a/x/ibc/02-client/tendermint/types_test.go +++ b/x/ibc/02-client/tendermint/types_test.go @@ -24,7 +24,7 @@ import ( const chainid = "testchain" func defaultComponents() (sdk.StoreKey, sdk.Context, stypes.CommitMultiStore, *codec.Codec) { - key := sdk.NewKVStoreKey("ibc") + key := sdk.NewKVStoreKey("test") db := dbm.NewMemDB() cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) @@ -91,6 +91,13 @@ func (node *node) Commit() tmtypes.SignedHeader { return commit } +func keyPrefix() [][]byte { + return [][]byte{ + []byte("test"), + []byte{0x00}, + } +} + type Verifier struct { ConsensusState } @@ -100,7 +107,7 @@ func NewVerifier(header tmtypes.SignedHeader, nextvalset MockValidators) *Verifi ConsensusState{ ChainID: chainid, Height: uint64(header.Height), - Root: header.AppHash, + Root: merkle.NewRoot(header.AppHash, keyPrefix()), NextValidatorSet: nextvalset.ValidatorSet(), }, } @@ -168,7 +175,7 @@ func key(str []byte) []byte { } func (node *node) query(t *testing.T, k []byte) ([]byte, commitment.Proof) { - qres := node.cms.(stypes.Queryable).Query(abci.RequestQuery{Path: "/ibc/key", Data: key(k), Prove: true}) + qres := node.cms.(stypes.Queryable).Query(abci.RequestQuery{Path: "/test/key", Data: key(k), Prove: true}) require.Equal(t, uint32(0), qres.Code, qres.Log) proof := merkle.Proof{ Key: []byte(k), @@ -203,7 +210,7 @@ func testProof(t *testing.T) { require.Equal(t, kvp.Value, v) proofs = append(proofs, p) } - cstore, err := commitment.NewStore([]byte(header.AppHash), proofs) + cstore, err := commitment.NewStore(merkle.NewRoot([]byte(header.AppHash), keyPrefix()), proofs) require.NoError(t, err) for _, kvp := range kvps { From acd44f3067b751f7bc4ee3137fb00ef7aa663468 Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 25 Jun 2019 16:07:00 +0200 Subject: [PATCH 035/166] in progress --- x/ibc/02-client/cli.go | 18 ++++++++++ x/ibc/02-client/manager.go | 34 ++++++++++--------- x/ibc/02-client/tendermint/types_test.go | 42 ++++++++++-------------- 3 files changed, 54 insertions(+), 40 deletions(-) create mode 100644 x/ibc/02-client/cli.go diff --git a/x/ibc/02-client/cli.go b/x/ibc/02-client/cli.go new file mode 100644 index 000000000000..5c245ba0378e --- /dev/null +++ b/x/ibc/02-client/cli.go @@ -0,0 +1,18 @@ +package client + +import () + +// CLIObject stores the key for each object fields +type CLIObject struct { + ID string + ConsensusState []byte + Frozen []byte +} + +func (object Object) CLI() CLIObject { + return CLIObject{ + ID: object.id, + ConsensusState: object.consensusState.Key(), + Frozen: object.frozen.Key(), + } +} diff --git a/x/ibc/02-client/manager.go b/x/ibc/02-client/manager.go index 196d62e62fdc..a99a4bcc785c 100644 --- a/x/ibc/02-client/manager.go +++ b/x/ibc/02-client/manager.go @@ -55,9 +55,9 @@ func (man Manager) RegisterKind(kind Kind, pred ValidityPredicate) Manager { */ func (man Manager) object(id string) Object { return Object{ - id: id, - client: man.protocol.Value([]byte(id)), - freeze: state.NewBoolean(man.protocol.Value([]byte(id + "/freeze"))), + id: id, + consensusState: man.protocol.Value([]byte(id)), + frozen: state.NewBoolean(man.protocol.Value([]byte(id + "/freeze"))), } } @@ -67,7 +67,7 @@ func (man Manager) Create(ctx sdk.Context, cs ConsensusState) (Object, error) { if obj.exists(ctx) { return Object{}, errors.New("Create client on already existing id") } - obj.client.Set(ctx, cs) + obj.consensusState.Set(ctx, cs) return obj, nil } @@ -91,9 +91,9 @@ func (man CounterpartyManager) Query(id string) CounterObject { } type Object struct { - id string - client state.Value // ConsensusState - freeze state.Boolean + id string + consensusState state.Value // ConsensusState + frozen state.Boolean } type CounterObject struct { @@ -101,34 +101,38 @@ type CounterObject struct { client commitment.Value } -func (obj Object) exists(ctx sdk.Context) bool { - return obj.client.Exists(ctx) -} - func (obj Object) ID() string { return obj.id } -func (obj Object) Value(ctx sdk.Context) (res ConsensusState) { - obj.client.Get(ctx, &res) +func (obj Object) ConsensusState(ctx sdk.Context) (res ConsensusState) { + obj.consensusState.Get(ctx, &res) return } +func (obj Object) Frozen(ctx sdk.Context) bool { + return obj.frozen.Get(ctx) +} + func (obj CounterObject) Is(ctx sdk.Context, client ConsensusState) bool { return obj.client.Is(ctx, client) } +func (obj Object) exists(ctx sdk.Context) bool { + return obj.consensusState.Exists(ctx) +} + func (obj Object) Update(ctx sdk.Context, header Header) error { if !obj.exists(ctx) { panic("should not update nonexisting client") } - if obj.freeze.Get(ctx) { + if obj.Frozen(ctx) { return errors.New("client is frozen") } var stored ConsensusState - obj.client.GetIfExists(ctx, &stored) + obj.client.Get(ctx, &stored) updated, err := stored.Validate(header) if err != nil { return err diff --git a/x/ibc/02-client/tendermint/types_test.go b/x/ibc/02-client/tendermint/types_test.go index 8bde76d10302..3cb99f52b549 100644 --- a/x/ibc/02-client/tendermint/types_test.go +++ b/x/ibc/02-client/tendermint/types_test.go @@ -91,23 +91,16 @@ func (node *node) Commit() tmtypes.SignedHeader { return commit } -func keyPrefix() [][]byte { - return [][]byte{ - []byte("test"), - []byte{0x00}, - } -} - type Verifier struct { ConsensusState } -func NewVerifier(header tmtypes.SignedHeader, nextvalset MockValidators) *Verifier { +func NewVerifier(header tmtypes.SignedHeader, nextvalset MockValidators, root merkle.Root) *Verifier { return &Verifier{ ConsensusState{ ChainID: chainid, Height: uint64(header.Height), - Root: merkle.NewRoot(header.AppHash, keyPrefix()), + Root: root.Update(header.AppHash), NextValidatorSet: nextvalset.ValidatorSet(), }, } @@ -129,12 +122,18 @@ func (v *Verifier) Validate(header tmtypes.SignedHeader, valset, nextvalset Mock return nil } +func newRoot() merkle.Root { + return merkle.NewRoot(nil, [][]byte{[]byte("test")}, []byte{0x12, 0x34}) +} + func testUpdate(t *testing.T, interval int, ok bool) { node := NewNode(NewMockValidators(100, 10)) node.Commit() - verifier := NewVerifier(node.last(), node.valset) + root := newRoot() + + verifier := NewVerifier(node.last(), node.valset, root) for i := 0; i < 100; i++ { header := node.Commit() @@ -170,22 +169,14 @@ func TestTenthBlockUpdate(t *testing.T) { } */ -func key(str []byte) []byte { - return append([]byte{0x00}, str...) -} - -func (node *node) query(t *testing.T, k []byte) ([]byte, commitment.Proof) { - qres := node.cms.(stypes.Queryable).Query(abci.RequestQuery{Path: "/test/key", Data: key(k), Prove: true}) - require.Equal(t, uint32(0), qres.Code, qres.Log) - proof := merkle.Proof{ - Key: []byte(k), - Proof: qres.Proof, - } - return qres.Value, proof +func (node *node) query(t *testing.T, root merkle.Root, k []byte) ([]byte, commitment.Proof) { + code, value, proof := root.Query(node.cms, k) + require.Equal(t, uint32(0), code) + return value, proof } func (node *node) Set(k, value []byte) { - node.store.Set(key(k), value) + node.store.Set(newRoot().Key(k), value) } func testProof(t *testing.T) { @@ -205,12 +196,13 @@ func testProof(t *testing.T) { } header := node.Commit() proofs := []commitment.Proof{} + root := newRoot().Update(header.AppHash) for _, kvp := range kvps { - v, p := node.query(t, []byte(kvp.Key)) + v, p := node.query(t, root.(merkle.Root), []byte(kvp.Key)) require.Equal(t, kvp.Value, v) proofs = append(proofs, p) } - cstore, err := commitment.NewStore(merkle.NewRoot([]byte(header.AppHash), keyPrefix()), proofs) + cstore, err := commitment.NewStore(root, proofs) require.NoError(t, err) for _, kvp := range kvps { From 582fd6ddd1778845fc05e8228b9a2f5b0d51b959 Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 25 Jun 2019 16:15:17 +0200 Subject: [PATCH 036/166] fin rebase --- x/ibc/02-client/manager.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/x/ibc/02-client/manager.go b/x/ibc/02-client/manager.go index a99a4bcc785c..e94d14702b72 100644 --- a/x/ibc/02-client/manager.go +++ b/x/ibc/02-client/manager.go @@ -81,8 +81,8 @@ func (man Manager) Query(ctx sdk.Context, id string) (Object, error) { func (man CounterpartyManager) object(id string) CounterObject { return CounterObject{ - id: id, - client: man.protocol.Value([]byte(id)), + id: id, + consensusState: man.protocol.Value([]byte(id)), } } @@ -97,8 +97,8 @@ type Object struct { } type CounterObject struct { - id string - client commitment.Value + id string + consensusState commitment.Value } func (obj Object) ID() string { @@ -115,7 +115,7 @@ func (obj Object) Frozen(ctx sdk.Context) bool { } func (obj CounterObject) Is(ctx sdk.Context, client ConsensusState) bool { - return obj.client.Is(ctx, client) + return obj.consensusState.Is(ctx, client) } func (obj Object) exists(ctx sdk.Context) bool { @@ -132,13 +132,13 @@ func (obj Object) Update(ctx sdk.Context, header Header) error { } var stored ConsensusState - obj.client.Get(ctx, &stored) + obj.consensusState.Get(ctx, &stored) updated, err := stored.Validate(header) if err != nil { return err } - obj.client.Set(ctx, updated) + obj.consensusState.Set(ctx, updated) return nil } @@ -148,11 +148,11 @@ func (obj Object) Freeze(ctx sdk.Context) error { panic("should not freeze nonexisting client") } - if obj.freeze.Get(ctx) { + if obj.Frozen(ctx) { return errors.New("client is already frozen") } - obj.freeze.Set(ctx, true) + obj.frozen.Set(ctx, true) return nil } @@ -162,12 +162,12 @@ func (obj Object) Delete(ctx sdk.Context) error { panic("should not delete nonexisting client") } - if !obj.freeze.Get(ctx) { + if !obj.Frozen(ctx) { return errors.New("client is not frozen") } - obj.client.Delete(ctx) - obj.freeze.Delete(ctx) + obj.consensusState.Delete(ctx) + obj.frozen.Delete(ctx) return nil } From cb3081df2603576a06b837709b4390d3e3eb5ae0 Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 25 Jun 2019 17:10:21 +0200 Subject: [PATCH 037/166] add CLIObject in progress --- client/context/query.go | 38 ++++++++++++++++++++++++-------------- x/ibc/02-client/cli.go | 24 ++++++++++++++++-------- 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/client/context/query.go b/client/context/query.go index 87f96aece166..1e52442c6c61 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -31,21 +31,31 @@ func (ctx CLIContext) GetNode() (rpcclient.Client, error) { // Query performs a query to a Tendermint node with the provided path. // It returns the result and height of the query upon success or an error if // the query fails. -func (ctx CLIContext) Query(path string) ([]byte, int64, error) { - return ctx.query(path, nil) +func (ctx CLIContext) Query(path string) (val []byte, height int64, err error) { + val, _, height, err = ctx.query(path, nil) + return } // QueryWithData performs a query to a Tendermint node with the provided path // and a data payload. It returns the result and height of the query upon success // or an error if the query fails. -func (ctx CLIContext) QueryWithData(path string, data []byte) ([]byte, int64, error) { - return ctx.query(path, data) +func (ctx CLIContext) QueryWithData(path string, data []byte) (val []byte, height int64, err error) { + val, _, height, err = ctx.query(path, data) + return } // QueryStore performs a query to a Tendermint node with the provided key and // store name. It returns the result and height of the query upon success // or an error if the query fails. -func (ctx CLIContext) QueryStore(key cmn.HexBytes, storeName string) ([]byte, int64, error) { +func (ctx CLIContext) QueryStore(key cmn.HexBytes, storeName string) (val []byte, height int64, err error) { + val, _, height, err = ctx.queryStore(key, storeName, "key") + return +} + +// QueryProof performs a query to a Tendermint node with the provided key and +// store name. It returns the result, the proof, and height of the query +// upon success or an error if the query fails. +func (ctx CLIContext) QueryProof(key cmn.HexBytes, storeName string) (val []byte, proof *merkle.Proof, height int64, err error) { return ctx.queryStore(key, storeName, "key") } @@ -53,7 +63,7 @@ func (ctx CLIContext) QueryStore(key cmn.HexBytes, storeName string) ([]byte, in // store name and subspace. It returns key value pair and height of the query // upon success or an error if the query fails. func (ctx CLIContext) QuerySubspace(subspace []byte, storeName string) (res []sdk.KVPair, height int64, err error) { - resRaw, height, err := ctx.queryStore(subspace, storeName, "subspace") + resRaw, _, height, err := ctx.queryStore(subspace, storeName, "subspace") if err != nil { return res, height, err } @@ -75,10 +85,10 @@ func (ctx CLIContext) GetFromName() string { // query performs a query to a Tendermint node with the provided store name // and path. It returns the result and height of the query upon success // or an error if the query fails. -func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, height int64, err error) { +func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, proof *merkle.Proof, height int64, err error) { node, err := ctx.GetNode() if err != nil { - return res, height, err + return res, proof, height, err } opts := rpcclient.ABCIQueryOptions{ @@ -88,25 +98,25 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, height i result, err := node.ABCIQueryWithOptions(path, key, opts) if err != nil { - return res, height, err + return res, proof, height, err } resp := result.Response if !resp.IsOK() { - return res, height, errors.New(resp.Log) + return res, proof, height, errors.New(resp.Log) } // data from trusted node or subspace query doesn't need verification if ctx.TrustNode || !isQueryStoreWithProof(path) { - return resp.Value, resp.Height, nil + return resp.Value, resp.Proof, resp.Height, nil } err = ctx.verifyProof(path, resp) if err != nil { - return res, height, err + return res, proof, height, err } - return resp.Value, resp.Height, nil + return resp.Value, resp.Proof, resp.Height, nil } // Verify verifies the consensus proof at given height. @@ -165,7 +175,7 @@ func (ctx CLIContext) verifyProof(queryPath string, resp abci.ResponseQuery) err // queryStore performs a query to a Tendermint node with the provided a store // name and path. It returns the result and height of the query upon success // or an error if the query fails. -func (ctx CLIContext) queryStore(key cmn.HexBytes, storeName, endPath string) ([]byte, int64, error) { +func (ctx CLIContext) queryStore(key cmn.HexBytes, storeName, endPath string) ([]byte, *merkle.Proof, int64, error) { path := fmt.Sprintf("/store/%s/%s", storeName, endPath) return ctx.query(path, key) } diff --git a/x/ibc/02-client/cli.go b/x/ibc/02-client/cli.go index 5c245ba0378e..8767c3032724 100644 --- a/x/ibc/02-client/cli.go +++ b/x/ibc/02-client/cli.go @@ -1,18 +1,26 @@ package client -import () +import ( + "github.com/cosmos/cosmos-sdk/client/context" + + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" +) // CLIObject stores the key for each object fields type CLIObject struct { - ID string - ConsensusState []byte - Frozen []byte + ID string + ConsensusStateKey []byte + FrozenKey []byte } -func (object Object) CLI() CLIObject { +func (obj Object) CLI() CLIObject { return CLIObject{ - ID: object.id, - ConsensusState: object.consensusState.Key(), - Frozen: object.frozen.Key(), + ID: obj.id, + ConsensusStateKey: obj.consensusState.Key(), + FrozenKey: obj.frozen.Key(), } } + +func (obj CLIObject) ConsensusState(ctx context.CLIContext, root merkle.Root) (res ConsensusState, proof merkle..Proof) { + val, proof, _, err := ctx.QueryProof(obj.ConsensusStateKey) +} From e48fd1c2bd9eee01986028b0476e73716554321a Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 26 Jun 2019 15:19:42 +0200 Subject: [PATCH 038/166] cli in progress --- client/context/query.go | 69 +++++++++++++++++++++++------------------ store/state/types.go | 5 +++ x/ibc/02-client/cli.go | 34 ++++++++++++++++++-- 3 files changed, 76 insertions(+), 32 deletions(-) diff --git a/client/context/query.go b/client/context/query.go index 1e52442c6c61..4738808ba4fe 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -31,39 +31,35 @@ func (ctx CLIContext) GetNode() (rpcclient.Client, error) { // Query performs a query to a Tendermint node with the provided path. // It returns the result and height of the query upon success or an error if // the query fails. -func (ctx CLIContext) Query(path string) (val []byte, height int64, err error) { - val, _, height, err = ctx.query(path, nil) - return +func (ctx CLIContext) Query(path string) ([]byte, int64, error) { + return ctx.query(path, nil) } // QueryWithData performs a query to a Tendermint node with the provided path // and a data payload. It returns the result and height of the query upon success // or an error if the query fails. -func (ctx CLIContext) QueryWithData(path string, data []byte) (val []byte, height int64, err error) { - val, _, height, err = ctx.query(path, data) - return +func (ctx CLIContext) QueryWithData(path string, data []byte) ([]byte, int64, error) { + return ctx.query(path, data) } // QueryStore performs a query to a Tendermint node with the provided key and // store name. It returns the result and height of the query upon success // or an error if the query fails. -func (ctx CLIContext) QueryStore(key cmn.HexBytes, storeName string) (val []byte, height int64, err error) { - val, _, height, err = ctx.queryStore(key, storeName, "key") - return +func (ctx CLIContext) QueryStore(key cmn.HexBytes, storeName string) ([]byte, int64, error) { + return ctx.queryStore(key, storeName, "key") } -// QueryProof performs a query to a Tendermint node with the provided key and -// store name. It returns the result, the proof, and height of the query -// upon success or an error if the query fails. -func (ctx CLIContext) QueryProof(key cmn.HexBytes, storeName string) (val []byte, proof *merkle.Proof, height int64, err error) { - return ctx.queryStore(key, storeName, "key") +// QueryABCI performs a query to a Tendermint node with the provide RequestQuery. +// It returns the ResultQuery obtained from the query. +func (ctx CLIContext) QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { + return ctx.queryABCI(req) } // QuerySubspace performs a query to a Tendermint node with the provided // store name and subspace. It returns key value pair and height of the query // upon success or an error if the query fails. func (ctx CLIContext) QuerySubspace(subspace []byte, storeName string) (res []sdk.KVPair, height int64, err error) { - resRaw, _, height, err := ctx.queryStore(subspace, storeName, "subspace") + resRaw, height, err := ctx.queryStore(subspace, storeName, "subspace") if err != nil { return res, height, err } @@ -82,13 +78,10 @@ func (ctx CLIContext) GetFromName() string { return ctx.FromName } -// query performs a query to a Tendermint node with the provided store name -// and path. It returns the result and height of the query upon success -// or an error if the query fails. -func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, proof *merkle.Proof, height int64, err error) { +func (ctx CLIContext) queryABCI(req abci.RequestQuery) (resp abci.ResponseQuery, err error) { node, err := ctx.GetNode() if err != nil { - return res, proof, height, err + return } opts := rpcclient.ABCIQueryOptions{ @@ -96,27 +89,43 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, proof *m Prove: !ctx.TrustNode, } - result, err := node.ABCIQueryWithOptions(path, key, opts) + result, err := node.ABCIQueryWithOptions(req.Path, req.Data, opts) if err != nil { - return res, proof, height, err + return } - resp := result.Response + resp = result.Response if !resp.IsOK() { - return res, proof, height, errors.New(resp.Log) + err = errors.New(resp.Log) + return } // data from trusted node or subspace query doesn't need verification - if ctx.TrustNode || !isQueryStoreWithProof(path) { - return resp.Value, resp.Proof, resp.Height, nil + if ctx.TrustNode || !isQueryStoreWithProof(req.Path) { + return resp, nil } - err = ctx.verifyProof(path, resp) + err = ctx.verifyProof(req.Path, resp) + if err != nil { + return + } + + return +} + +// query performs a query to a Tendermint node with the provided store name +// and path. It returns the result and height of the query upon success +// or an error if the query fails. +func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, height int64, err error) { + resp, err := ctx.queryABCI(abci.RequestQuery{ + Path: path, + Data: key, + }) if err != nil { - return res, proof, height, err + return } - return resp.Value, resp.Proof, resp.Height, nil + return resp.Value, resp.Height, nil } // Verify verifies the consensus proof at given height. @@ -175,7 +184,7 @@ func (ctx CLIContext) verifyProof(queryPath string, resp abci.ResponseQuery) err // queryStore performs a query to a Tendermint node with the provided a store // name and path. It returns the result and height of the query upon success // or an error if the query fails. -func (ctx CLIContext) queryStore(key cmn.HexBytes, storeName, endPath string) ([]byte, *merkle.Proof, int64, error) { +func (ctx CLIContext) queryStore(key cmn.HexBytes, storeName, endPath string) ([]byte, int64, error) { path := fmt.Sprintf("/store/%s/%s", storeName, endPath) return ctx.query(path, key) } diff --git a/store/state/types.go b/store/state/types.go index a8d34d582a82..f5dbb4dc8648 100644 --- a/store/state/types.go +++ b/store/state/types.go @@ -1,8 +1,13 @@ package state import ( + "github.com/tendermint/tendermint/crypto/merkle" + + "github.com/cosmos/cosmos-sdk/client/context" sdk "github.com/cosmos/cosmos-sdk/types" ) type KVStore = sdk.KVStore type Context = sdk.Context +type CLIContext = context.CLIContext +type Proof = merkle.Proof diff --git a/x/ibc/02-client/cli.go b/x/ibc/02-client/cli.go index 8767c3032724..f51b882e7725 100644 --- a/x/ibc/02-client/cli.go +++ b/x/ibc/02-client/cli.go @@ -2,6 +2,7 @@ package client import ( "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) @@ -11,6 +12,7 @@ type CLIObject struct { ID string ConsensusStateKey []byte FrozenKey []byte + Cdc *codec.Codec } func (obj Object) CLI() CLIObject { @@ -21,6 +23,34 @@ func (obj Object) CLI() CLIObject { } } -func (obj CLIObject) ConsensusState(ctx context.CLIContext, root merkle.Root) (res ConsensusState, proof merkle..Proof) { - val, proof, _, err := ctx.QueryProof(obj.ConsensusStateKey) +func query(ctx context.CLIContext, root merkle.Root, key []byte) ([]byte, merkle.Proof, error) { + resp, err := ctx.QueryABCI(root.RequestQuery(key)) + if err != nil { + return nil, merkle.Proof{}, err + } + proof := merkle.Proof{ + Key: key, + Proof: resp.Proof, + } + return resp.Value, proof, nil + +} + +func (obj CLIObject) ConsensusState(ctx context.CLIContext, root merkle.Root) (res ConsensusState, proof merkle.Proof, err error) { + val, proof, err := query(ctx, root, obj.ConsensusStateKey) + obj.Cdc.MustUnmarshalBinaryBare(val, &res) + return +} + +func (obj CLIObject) Frozen(ctx context.CLIContext, root merkle.Root) (res bool, proof merkle.Proof, err error) { + val, tmproof, _, err := ctx.QueryProof(obj.FrozenKey, "ibc") // TODO + if err != nil { + return + } + proof = merkle.Proof{ + Key: obj.FrozenKey, + Proof: tmproof, + } + obj.Cdc.MustUnmarshalBinaryBare(val, &res) + return } From a372f5607fbfd290e8165f748d790cdc5742e818 Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 26 Jun 2019 15:40:30 +0200 Subject: [PATCH 039/166] add CLIObject --- x/ibc/02-client/cli.go | 31 ++++++++++------------- x/ibc/02-client/tendermint/types_test.go | 2 +- x/ibc/23-commitment/merkle/merkle_test.go | 12 ++++----- x/ibc/23-commitment/merkle/utils.go | 27 ++++++++++++-------- 4 files changed, 38 insertions(+), 34 deletions(-) diff --git a/x/ibc/02-client/cli.go b/x/ibc/02-client/cli.go index f51b882e7725..2592e697573c 100644 --- a/x/ibc/02-client/cli.go +++ b/x/ibc/02-client/cli.go @@ -12,45 +12,42 @@ type CLIObject struct { ID string ConsensusStateKey []byte FrozenKey []byte - Cdc *codec.Codec + + Root merkle.Root + Cdc *codec.Codec } -func (obj Object) CLI() CLIObject { +func (obj Object) CLI(root merkle.Root) CLIObject { return CLIObject{ ID: obj.id, ConsensusStateKey: obj.consensusState.Key(), FrozenKey: obj.frozen.Key(), + + Root: root, + Cdc: obj.consensusState.Cdc(), } } -func query(ctx context.CLIContext, root merkle.Root, key []byte) ([]byte, merkle.Proof, error) { - resp, err := ctx.QueryABCI(root.RequestQuery(key)) +func (obj CLIObject) query(ctx context.CLIContext, key []byte, ptr interface{}) (merkle.Proof, error) { + resp, err := ctx.QueryABCI(obj.Root.RequestQuery(key)) if err != nil { - return nil, merkle.Proof{}, err + return merkle.Proof{}, err } proof := merkle.Proof{ Key: key, Proof: resp.Proof, } - return resp.Value, proof, nil + err = obj.Cdc.UnmarshalBinaryBare(resp.Value, ptr) + return proof, err } func (obj CLIObject) ConsensusState(ctx context.CLIContext, root merkle.Root) (res ConsensusState, proof merkle.Proof, err error) { - val, proof, err := query(ctx, root, obj.ConsensusStateKey) - obj.Cdc.MustUnmarshalBinaryBare(val, &res) + proof, err = obj.query(ctx, obj.ConsensusStateKey, &res) return } func (obj CLIObject) Frozen(ctx context.CLIContext, root merkle.Root) (res bool, proof merkle.Proof, err error) { - val, tmproof, _, err := ctx.QueryProof(obj.FrozenKey, "ibc") // TODO - if err != nil { - return - } - proof = merkle.Proof{ - Key: obj.FrozenKey, - Proof: tmproof, - } - obj.Cdc.MustUnmarshalBinaryBare(val, &res) + proof, err = obj.query(ctx, obj.FrozenKey, &res) return } diff --git a/x/ibc/02-client/tendermint/types_test.go b/x/ibc/02-client/tendermint/types_test.go index 3cb99f52b549..2b89f9be11c8 100644 --- a/x/ibc/02-client/tendermint/types_test.go +++ b/x/ibc/02-client/tendermint/types_test.go @@ -170,7 +170,7 @@ func TestTenthBlockUpdate(t *testing.T) { */ func (node *node) query(t *testing.T, root merkle.Root, k []byte) ([]byte, commitment.Proof) { - code, value, proof := root.Query(node.cms, k) + code, value, proof := root.QueryMultiStore(node.cms, k) require.Equal(t, uint32(0), code) return value, proof } diff --git a/x/ibc/23-commitment/merkle/merkle_test.go b/x/ibc/23-commitment/merkle/merkle_test.go index f93f64943609..dc7da05dfbc2 100644 --- a/x/ibc/23-commitment/merkle/merkle_test.go +++ b/x/ibc/23-commitment/merkle/merkle_test.go @@ -47,13 +47,13 @@ func TestStore(t *testing.T) { root := commit(cms) - c1, v1, p1 := path.Query(cms, []byte("hello")) + c1, v1, p1 := path.QueryMultiStore(cms, []byte("hello")) require.Equal(t, uint32(0), c1) require.Equal(t, []byte("world"), v1) - c2, v2, p2 := path.Query(cms, []byte("merkle")) + c2, v2, p2 := path.QueryMultiStore(cms, []byte("merkle")) require.Equal(t, uint32(0), c2) require.Equal(t, []byte("tree"), v2) - c3, v3, p3 := path.Query(cms, []byte("block")) + c3, v3, p3 := path.QueryMultiStore(cms, []byte("block")) require.Equal(t, uint32(0), c3) require.Equal(t, []byte("chain"), v3) @@ -70,13 +70,13 @@ func TestStore(t *testing.T) { root = commit(cms) - c1, v1, p1 = path.Query(cms, []byte("12345")) + c1, v1, p1 = path.QueryMultiStore(cms, []byte("12345")) require.Equal(t, uint32(0), c1) require.Equal(t, []byte("67890"), v1) - c2, v2, p2 = path.Query(cms, []byte("qwerty")) + c2, v2, p2 = path.QueryMultiStore(cms, []byte("qwerty")) require.Equal(t, uint32(0), c2) require.Equal(t, []byte("zxcv"), v2) - c3, v3, p3 = path.Query(cms, []byte("hello")) + c3, v3, p3 = path.QueryMultiStore(cms, []byte("hello")) require.Equal(t, uint32(0), c3) require.Equal(t, []byte("dlrow"), v3) diff --git a/x/ibc/23-commitment/merkle/utils.go b/x/ibc/23-commitment/merkle/utils.go index d2eabdaed99d..f122b0cd5383 100644 --- a/x/ibc/23-commitment/merkle/utils.go +++ b/x/ibc/23-commitment/merkle/utils.go @@ -7,22 +7,29 @@ import ( ) func (path Path) RequestQuery(key []byte) abci.RequestQuery { - pathstr := "" - for _, inter := range path.KeyPath { - pathstr = pathstr + "/" + string(inter) - } - pathstr = pathstr + "/key" - - data := append(path.KeyPrefix, key...) + req := path.RequestQueryMultiStore(key) + req.Path = "/store" + req.Path + return req +} - return abci.RequestQuery{Path: pathstr, Data: data, Prove: true} +func (path Path) RequestQueryMultiStore(key []byte) abci.RequestQuery { + return abci.RequestQuery{Path: path.Path() + "/key", Data: path.Key(key), Prove: true} } -func (path Path) Query(cms types.CommitMultiStore, key []byte) (uint32, []byte, Proof) { - qres := cms.(types.Queryable).Query(path.RequestQuery(key)) +func (path Path) QueryMultiStore(cms types.CommitMultiStore, key []byte) (uint32, []byte, Proof) { + qres := cms.(types.Queryable).Query(path.RequestQueryMultiStore(key)) return qres.Code, qres.Value, Proof{Key: key, Proof: qres.Proof} } func (path Path) Key(key []byte) []byte { return append(path.KeyPrefix, key...) // XXX: cloneAppend } + +func (path Path) Path() string { + pathstr := "" + for _, inter := range path.KeyPath { + pathstr = pathstr + "/" + string(inter) + } + + return pathstr +} From 67b1d73575e788bbcef119a8472cd18d573b0cb0 Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 26 Jun 2019 15:58:28 +0200 Subject: [PATCH 040/166] separate testing from tendermint --- .../tendermint/tests/tendermint_test.go | 58 ++++++++ .../{types_test.go => tests/types.go} | 125 ++++++------------ .../{valset_test.go => tests/valset.go} | 0 3 files changed, 98 insertions(+), 85 deletions(-) create mode 100644 x/ibc/02-client/tendermint/tests/tendermint_test.go rename x/ibc/02-client/tendermint/{types_test.go => tests/types.go} (57%) rename x/ibc/02-client/tendermint/{valset_test.go => tests/valset.go} (100%) diff --git a/x/ibc/02-client/tendermint/tests/tendermint_test.go b/x/ibc/02-client/tendermint/tests/tendermint_test.go new file mode 100644 index 000000000000..29581e718e4c --- /dev/null +++ b/x/ibc/02-client/tendermint/tests/tendermint_test.go @@ -0,0 +1,58 @@ +package tendermint + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" +) + +func newRoot() merkle.Root { + return merkle.NewRoot(nil, [][]byte{[]byte("test")}, []byte{0x12, 0x34}) +} + +func testUpdate(t *testing.T, interval int, ok bool) { + node := NewNode(NewMockValidators(100, 10)) + + node.Commit() + + verifier := node.LastStateVerifier(newRoot()) + + for i := 0; i < 100; i++ { + header := node.Commit() + + if i%interval == 0 { + err := verifier.Validate(header, node.PrevValset, node.Valset) + if ok { + require.NoError(t, err) + } else { + require.Error(t, err) + } + } + } +} + +func TestEveryBlockUpdate(t *testing.T) { + testUpdate(t, 1, true) +} + +func TestEvenBlockUpdate(t *testing.T) { + testUpdate(t, 2, true) +} + +func TestSixthBlockUpdate(t *testing.T) { + testUpdate(t, 6, true) +} + +/* +// This should fail, since the amount of mutation is so large +// Commented out because it sometimes success +func TestTenthBlockUpdate(t *testing.T) { + testUpdate(t, 10, false) +} +*/ + +func TestProofs(t *testing.T) { + testProof(t) +} diff --git a/x/ibc/02-client/tendermint/types_test.go b/x/ibc/02-client/tendermint/tests/types.go similarity index 57% rename from x/ibc/02-client/tendermint/types_test.go rename to x/ibc/02-client/tendermint/tests/types.go index 2b89f9be11c8..f32cae8efc9d 100644 --- a/x/ibc/02-client/tendermint/types_test.go +++ b/x/ibc/02-client/tendermint/tests/types.go @@ -17,6 +17,8 @@ import ( stypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/tendermint" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) @@ -37,44 +39,44 @@ func defaultComponents() (sdk.StoreKey, sdk.Context, stypes.CommitMultiStore, *c return key, ctx, cms, cdc } -type node struct { - prevvalset MockValidators - valset MockValidators +type Node struct { + PrevValset MockValidators + Valset MockValidators - cms sdk.CommitMultiStore - store sdk.KVStore + Cms sdk.CommitMultiStore + Store sdk.KVStore - commits []tmtypes.SignedHeader + Commits []tmtypes.SignedHeader } -func NewNode(valset MockValidators) *node { +func NewNode(valset MockValidators) *Node { key, ctx, cms, _ := defaultComponents() - return &node{ - valset: valset, - cms: cms, - store: ctx.KVStore(key), - commits: nil, + return &Node{ + Valset: valset, + Cms: cms, + Store: ctx.KVStore(key), + Commits: nil, } } -func (node *node) last() tmtypes.SignedHeader { - if len(node.commits) == 0 { +func (node *Node) Last() tmtypes.SignedHeader { + if len(node.Commits) == 0 { return tmtypes.SignedHeader{} } - return node.commits[len(node.commits)-1] + return node.Commits[len(node.Commits)-1] } -func (node *node) Commit() tmtypes.SignedHeader { - valsethash := node.valset.ValidatorSet().Hash() - nextvalset := node.valset.Mutate() +func (node *Node) Commit() tmtypes.SignedHeader { + valsethash := node.Valset.ValidatorSet().Hash() + nextvalset := node.Valset.Mutate() nextvalsethash := nextvalset.ValidatorSet().Hash() - commitid := node.cms.Commit() + commitid := node.Cms.Commit() header := tmtypes.Header{ ChainID: chainid, - Height: int64(len(node.commits) + 1), + Height: int64(len(node.Commits) + 1), LastBlockID: tmtypes.BlockID{ - Hash: node.last().Header.Hash(), + Hash: node.Last().Header.Hash(), }, ValidatorsHash: valsethash, @@ -82,22 +84,26 @@ func (node *node) Commit() tmtypes.SignedHeader { AppHash: commitid.Hash, } - commit := node.valset.Sign(header) + commit := node.Valset.Sign(header) - node.prevvalset = node.valset - node.valset = nextvalset - node.commits = append(node.commits, commit) + node.PrevValset = node.Valset + node.Valset = nextvalset + node.Commits = append(node.Commits, commit) return commit } +func (node *Node) LastStateVerifier(root merkle.Root) *Verifier { + return NewVerifier(node.Last(), node.Valset, root) +} + type Verifier struct { - ConsensusState + client.ConsensusState } func NewVerifier(header tmtypes.SignedHeader, nextvalset MockValidators, root merkle.Root) *Verifier { return &Verifier{ - ConsensusState{ + tendermint.ConsensusState{ ChainID: chainid, Height: uint64(header.Height), Root: root.Update(header.AppHash), @@ -108,7 +114,7 @@ func NewVerifier(header tmtypes.SignedHeader, nextvalset MockValidators, root me func (v *Verifier) Validate(header tmtypes.SignedHeader, valset, nextvalset MockValidators) error { newcs, err := v.ConsensusState.Validate( - Header{ + tendermint.Header{ SignedHeader: header, ValidatorSet: valset.ValidatorSet(), NextValidatorSet: nextvalset.ValidatorSet(), @@ -117,66 +123,19 @@ func (v *Verifier) Validate(header tmtypes.SignedHeader, valset, nextvalset Mock if err != nil { return err } - v.ConsensusState = newcs.(ConsensusState) + v.ConsensusState = newcs.(tendermint.ConsensusState) return nil } -func newRoot() merkle.Root { - return merkle.NewRoot(nil, [][]byte{[]byte("test")}, []byte{0x12, 0x34}) -} - -func testUpdate(t *testing.T, interval int, ok bool) { - node := NewNode(NewMockValidators(100, 10)) - - node.Commit() - - root := newRoot() - - verifier := NewVerifier(node.last(), node.valset, root) - - for i := 0; i < 100; i++ { - header := node.Commit() - - if i%interval == 0 { - err := verifier.Validate(header, node.prevvalset, node.valset) - if ok { - require.NoError(t, err) - } else { - require.Error(t, err) - } - } - } -} - -func TestEveryBlockUpdate(t *testing.T) { - testUpdate(t, 1, true) -} - -func TestEvenBlockUpdate(t *testing.T) { - testUpdate(t, 2, true) -} - -func TestSixthBlockUpdate(t *testing.T) { - testUpdate(t, 6, true) -} - -/* -// This should fail, since the amount of mutation is so large -// Commented out because it sometimes success -func TestTenthBlockUpdate(t *testing.T) { - testUpdate(t, 10, false) -} -*/ - -func (node *node) query(t *testing.T, root merkle.Root, k []byte) ([]byte, commitment.Proof) { - code, value, proof := root.QueryMultiStore(node.cms, k) +func (node *Node) Query(t *testing.T, root merkle.Root, k []byte) ([]byte, commitment.Proof) { + code, value, proof := root.QueryMultiStore(node.Cms, k) require.Equal(t, uint32(0), code) return value, proof } -func (node *node) Set(k, value []byte) { - node.store.Set(newRoot().Key(k), value) +func (node *Node) Set(k, value []byte) { + node.Store.Set(newRoot().Key(k), value) } func testProof(t *testing.T) { @@ -198,7 +157,7 @@ func testProof(t *testing.T) { proofs := []commitment.Proof{} root := newRoot().Update(header.AppHash) for _, kvp := range kvps { - v, p := node.query(t, root.(merkle.Root), []byte(kvp.Key)) + v, p := node.Query(t, root.(merkle.Root), []byte(kvp.Key)) require.Equal(t, kvp.Value, v) proofs = append(proofs, p) } @@ -210,7 +169,3 @@ func testProof(t *testing.T) { } } } - -func TestProofs(t *testing.T) { - testProof(t) -} diff --git a/x/ibc/02-client/tendermint/valset_test.go b/x/ibc/02-client/tendermint/tests/valset.go similarity index 100% rename from x/ibc/02-client/tendermint/valset_test.go rename to x/ibc/02-client/tendermint/tests/valset.go From ee95a839d35e5c4f62f5647b3bc64f3d5434c74c Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 26 Jun 2019 16:10:21 +0200 Subject: [PATCH 041/166] add key to node --- x/ibc/02-client/tendermint/tests/types.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x/ibc/02-client/tendermint/tests/types.go b/x/ibc/02-client/tendermint/tests/types.go index f32cae8efc9d..cc6d73592504 100644 --- a/x/ibc/02-client/tendermint/tests/types.go +++ b/x/ibc/02-client/tendermint/tests/types.go @@ -44,9 +44,12 @@ type Node struct { Valset MockValidators Cms sdk.CommitMultiStore + Key sdk.StoreKey Store sdk.KVStore Commits []tmtypes.SignedHeader + + Root merkle.Root } func NewNode(valset MockValidators) *Node { @@ -54,6 +57,7 @@ func NewNode(valset MockValidators) *Node { return &Node{ Valset: valset, Cms: cms, + Key: key, Store: ctx.KVStore(key), Commits: nil, } From b6865aa9490cbb0bb24cf2fb771d09b0ef534e8e Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 26 Jun 2019 18:58:29 +0200 Subject: [PATCH 042/166] add root and storekey to tests/node, add codec --- x/ibc/02-client/codec.go | 10 ++++++++++ x/ibc/02-client/tendermint/codec.go | 11 +++++++++++ x/ibc/02-client/tendermint/tests/types.go | 8 ++++++-- x/ibc/02-client/tendermint/tests/valset.go | 1 + 4 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 x/ibc/02-client/codec.go create mode 100644 x/ibc/02-client/tendermint/codec.go diff --git a/x/ibc/02-client/codec.go b/x/ibc/02-client/codec.go new file mode 100644 index 000000000000..fa194562e4f1 --- /dev/null +++ b/x/ibc/02-client/codec.go @@ -0,0 +1,10 @@ +package client + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterInterface((*ConsensusState)(nil), nil) + cdc.RegisterInterface((*Header)(nil), nil) +} diff --git a/x/ibc/02-client/tendermint/codec.go b/x/ibc/02-client/tendermint/codec.go new file mode 100644 index 000000000000..d8308a3e7ec8 --- /dev/null +++ b/x/ibc/02-client/tendermint/codec.go @@ -0,0 +1,11 @@ +package tendermint + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterConcrete(ConsensusState{}, "ibc/client/tendermint/ConsensusState", nil) + cdc.RegisterConcrete(Header{}, "ibc/client/tendermint/Header", nil) + +} diff --git a/x/ibc/02-client/tendermint/tests/types.go b/x/ibc/02-client/tendermint/tests/types.go index cc6d73592504..88a27fd69f51 100644 --- a/x/ibc/02-client/tendermint/tests/types.go +++ b/x/ibc/02-client/tendermint/tests/types.go @@ -101,6 +101,10 @@ func (node *Node) LastStateVerifier(root merkle.Root) *Verifier { return NewVerifier(node.Last(), node.Valset, root) } +func (node *Node) Context() sdk.Context { + return sdk.NewContext(node.Cms, abci.Header{}, false, log.NewNopLogger()) +} + type Verifier struct { client.ConsensusState } @@ -139,7 +143,7 @@ func (node *Node) Query(t *testing.T, root merkle.Root, k []byte) ([]byte, commi } func (node *Node) Set(k, value []byte) { - node.Store.Set(newRoot().Key(k), value) + node.Store.Set(node.Root.Key(k), value) } func testProof(t *testing.T) { @@ -159,7 +163,7 @@ func testProof(t *testing.T) { } header := node.Commit() proofs := []commitment.Proof{} - root := newRoot().Update(header.AppHash) + root := node.Root.Update(header.AppHash) for _, kvp := range kvps { v, p := node.Query(t, root.(merkle.Root), []byte(kvp.Key)) require.Equal(t, kvp.Value, v) diff --git a/x/ibc/02-client/tendermint/tests/valset.go b/x/ibc/02-client/tendermint/tests/valset.go index 5649a3533273..0d97a5427c9b 100644 --- a/x/ibc/02-client/tendermint/tests/valset.go +++ b/x/ibc/02-client/tendermint/tests/valset.go @@ -84,6 +84,7 @@ type MockValidators []MockValidator var _ sort.Interface = MockValidators{} +// TODO: differenciate power between the vals func NewMockValidators(num int, power int64) MockValidators { res := make(MockValidators, num) for i := range res { From 631b72d0a13a66fd05cc0d5c45df240423452d99 Mon Sep 17 00:00:00 2001 From: mossid Date: Sun, 7 Jul 2019 12:22:46 +0200 Subject: [PATCH 043/166] rm cli/query.go --- x/ibc/client/cli/query.go | 216 -------------------------------------- 1 file changed, 216 deletions(-) delete mode 100644 x/ibc/client/cli/query.go diff --git a/x/ibc/client/cli/query.go b/x/ibc/client/cli/query.go deleted file mode 100644 index ca979151c499..000000000000 --- a/x/ibc/client/cli/query.go +++ /dev/null @@ -1,216 +0,0 @@ -package cli - -import ( - "fmt" - - "github.com/spf13/cobra" - - tmtypes "github.com/tendermint/tendermint/types" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" - - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/tendermint" - ibc "github.com/cosmos/cosmos-sdk/x/ibc/keeper" -) - -func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { - ibcQueryCmd := &cobra.Command{ - Use: "ibc", - Short: "IBC query subcommands", - DisableFlagParsing: true, - SuggestionsMinimumDistance: 2, - } - - ibcQueryCmd.AddCommand(client.GetCommands( - GetCmdQueryConsensusState(cdc), - GetCmdQueryHeader(cdc), - GetCmdQueryClient(cdc), - GetCmdQueryConnection(cdc), - GetCmdQueryChannel(cdc), - )...) - return ibcQueryCmd -} - -func GetCmdQueryClient(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ - Use: "client", - Short: "Query stored client", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.NewCLIContext().WithCodec(cdc) - - keeper := ibc.DummyKeeper() - - var state ibc.ConsensusState - statebz, _, err := query(ctx, keeper.Client.Object(args[0]).Key()) - if err != nil { - return err - } - cdc.MustUnmarshalBinaryBare(statebz, &state) - - fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, state)) - - return nil - }, - } -} - -func GetCmdQueryConsensusState(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ - Use: "consensus-state", - Short: "Query the latest consensus state of the running chain", - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.NewCLIContext().WithCodec(cdc) - - node, err := ctx.GetNode() - if err != nil { - return err - } - - info, err := node.ABCIInfo() - if err != nil { - return err - } - - height := info.Response.LastBlockHeight - prevheight := height - 1 - - commit, err := node.Commit(&height) - if err != nil { - return err - } - - validators, err := node.Validators(&prevheight) - if err != nil { - return err - } - - state := tendermint.ConsensusState{ - ChainID: commit.ChainID, - Height: uint64(commit.Height), - Root: []byte(commit.AppHash), - NextValidatorSet: tmtypes.NewValidatorSet(validators.Validators), - } - - fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, state)) - - return nil - }, - } -} - -func GetCmdQueryHeader(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ - Use: "header", - Short: "Query the latest header of the running chain", - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.NewCLIContext().WithCodec(cdc) - - node, err := ctx.GetNode() - if err != nil { - return err - } - - info, err := node.ABCIInfo() - if err != nil { - return err - } - - height := info.Response.LastBlockHeight - prevheight := height - 1 - - commit, err := node.Commit(&height) - if err != nil { - return err - } - - validators, err := node.Validators(&prevheight) - if err != nil { - return err - } - - nextvalidators, err := node.Validators(&height) - if err != nil { - return err - } - - header := tendermint.Header{ - SignedHeader: commit.SignedHeader, - ValidatorSet: tmtypes.NewValidatorSet(validators.Validators), - NextValidatorSet: tmtypes.NewValidatorSet(nextvalidators.Validators), - } - - fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, header)) - - return nil - }, - } -} - -func GetCmdQueryConnection(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ - Use: "connection", - Short: "Query an existing connection", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.NewCLIContext().WithCodec(cdc) - - keeper := ibc.DummyKeeper() - - var conn ibc.Connection - connbz, _, err := query(ctx, keeper.Connection.Object(args[0]).Key()) - if err != nil { - return err - } - cdc.MustUnmarshalBinaryBare(connbz, &conn) - - fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, conn)) - - return nil - }, - } -} - -func GetCmdQueryChannel(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ - Use: "channel", - Short: "Query an existing channel", - Args: cobra.ExactArgs(2), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.NewCLIContext().WithCodec(cdc) - - keeper := ibc.DummyKeeper() - - var conn ibc.Channel - connbz, _, err := query(ctx, keeper.Channel.Object(args[0], args[1]).Key()) - if err != nil { - return err - } - cdc.MustUnmarshalBinaryBare(connbz, &conn) - - fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, conn)) - - return nil - }, - } -} - -func GetCmdQuerySendSequence(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ - Use: "send-sequence", - Short: "Query the send sequence of a channel", - Args: cobra.ExactArgs(), - RunE: func(cmd *cobra.Command, args []string) error { - - }, - } -} - -func GetCmdQueryReceiveSequence(cdc *codec.Codec) *cobra.Command { - -} - -func GetCmdQueryPacket(cdc *codec.Codec) *cobra.Command { -} From b9d89296eab4dfb18a1e8bf050fe6527e32b8687 Mon Sep 17 00:00:00 2001 From: mossid Date: Sun, 7 Jul 2019 18:21:20 +0200 Subject: [PATCH 044/166] fix test --- x/ibc/02-client/tendermint/tests/tendermint_test.go | 2 +- x/ibc/02-client/tendermint/tests/types.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/x/ibc/02-client/tendermint/tests/tendermint_test.go b/x/ibc/02-client/tendermint/tests/tendermint_test.go index 29581e718e4c..caf866337c47 100644 --- a/x/ibc/02-client/tendermint/tests/tendermint_test.go +++ b/x/ibc/02-client/tendermint/tests/tendermint_test.go @@ -13,7 +13,7 @@ func newRoot() merkle.Root { } func testUpdate(t *testing.T, interval int, ok bool) { - node := NewNode(NewMockValidators(100, 10)) + node := NewNode(NewMockValidators(100, 10), newRoot()) node.Commit() diff --git a/x/ibc/02-client/tendermint/tests/types.go b/x/ibc/02-client/tendermint/tests/types.go index 88a27fd69f51..d0b7b488db39 100644 --- a/x/ibc/02-client/tendermint/tests/types.go +++ b/x/ibc/02-client/tendermint/tests/types.go @@ -52,7 +52,7 @@ type Node struct { Root merkle.Root } -func NewNode(valset MockValidators) *Node { +func NewNode(valset MockValidators, root merkle.Root) *Node { key, ctx, cms, _ := defaultComponents() return &Node{ Valset: valset, @@ -60,6 +60,7 @@ func NewNode(valset MockValidators) *Node { Key: key, Store: ctx.KVStore(key), Commits: nil, + Root: root, } } @@ -147,7 +148,7 @@ func (node *Node) Set(k, value []byte) { } func testProof(t *testing.T) { - node := NewNode(NewMockValidators(100, 10)) + node := NewNode(NewMockValidators(100, 10), newRoot()) node.Commit() From dbd6b8349713abf9f6851bb5a03848495b2b4ea1 Mon Sep 17 00:00:00 2001 From: mossid Date: Sun, 7 Jul 2019 18:26:23 +0200 Subject: [PATCH 045/166] fix lint --- x/ibc/02-client/tendermint/tests/valset.go | 2 +- x/ibc/02-client/tendermint/types.go | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/x/ibc/02-client/tendermint/tests/valset.go b/x/ibc/02-client/tendermint/tests/valset.go index 0d97a5427c9b..1b5f88aa949b 100644 --- a/x/ibc/02-client/tendermint/tests/valset.go +++ b/x/ibc/02-client/tendermint/tests/valset.go @@ -84,7 +84,7 @@ type MockValidators []MockValidator var _ sort.Interface = MockValidators{} -// TODO: differenciate power between the vals +// TODO: differentiate power between the vals func NewMockValidators(num int, power int64) MockValidators { res := make(MockValidators, num) for i := range res { diff --git a/x/ibc/02-client/tendermint/types.go b/x/ibc/02-client/tendermint/types.go index 26a4c35370e6..6bb1bb1f18dd 100644 --- a/x/ibc/02-client/tendermint/types.go +++ b/x/ibc/02-client/tendermint/types.go @@ -48,11 +48,8 @@ func (cs ConsensusState) Validate(cheader client.Header) (client.ConsensusState, return nil, errors.New("invalid type") } - nextvalset := cs.NextValidatorSet - nexthash := nextvalset.Hash() - if cs.Height == uint64(header.Height-1) { - nexthash = cs.NextValidatorSet.Hash() + nexthash := cs.NextValidatorSet.Hash() if !bytes.Equal(header.ValidatorsHash, nexthash) { return nil, lerr.ErrUnexpectedValidators(header.ValidatorsHash, nexthash) } From f6e5f9011ce5da4e6a44c9bbdc383eec7fa533b8 Mon Sep 17 00:00:00 2001 From: mossid Date: Sun, 7 Jul 2019 19:12:36 +0200 Subject: [PATCH 046/166] fix lint --- .../tendermint/tests/tendermint_test.go | 6 ------ x/ibc/02-client/tendermint/tests/types.go | 16 ++++++++++++---- x/ibc/02-client/tendermint/tests/valset.go | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/x/ibc/02-client/tendermint/tests/tendermint_test.go b/x/ibc/02-client/tendermint/tests/tendermint_test.go index caf866337c47..8a1c764f50f2 100644 --- a/x/ibc/02-client/tendermint/tests/tendermint_test.go +++ b/x/ibc/02-client/tendermint/tests/tendermint_test.go @@ -4,14 +4,8 @@ import ( "testing" "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) -func newRoot() merkle.Root { - return merkle.NewRoot(nil, [][]byte{[]byte("test")}, []byte{0x12, 0x34}) -} - func testUpdate(t *testing.T, interval int, ok bool) { node := NewNode(NewMockValidators(100, 10), newRoot()) diff --git a/x/ibc/02-client/tendermint/tests/types.go b/x/ibc/02-client/tendermint/tests/types.go index d0b7b488db39..5e0af44f3806 100644 --- a/x/ibc/02-client/tendermint/tests/types.go +++ b/x/ibc/02-client/tendermint/tests/types.go @@ -1,7 +1,7 @@ package tendermint import ( - "math/rand" + "crypto/rand" "testing" "github.com/stretchr/testify/require" @@ -23,6 +23,11 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) +// nolint: unused +func newRoot() merkle.Root { + return merkle.NewRoot(nil, [][]byte{[]byte("test")}, []byte{0x12, 0x34}) +} + const chainid = "testchain" func defaultComponents() (sdk.StoreKey, sdk.Context, stypes.CommitMultiStore, *codec.Codec) { @@ -147,6 +152,7 @@ func (node *Node) Set(k, value []byte) { node.Store.Set(node.Root.Key(k), value) } +// nolint:deadcode,unused func testProof(t *testing.T) { node := NewNode(NewMockValidators(100, 10), newRoot()) @@ -157,8 +163,10 @@ func testProof(t *testing.T) { for i := 0; i < 100; i++ { k := make([]byte, 32) v := make([]byte, 32) - rand.Read(k) - rand.Read(v) + _, err := rand.Read(k) + require.NoError(t, err) + _, err = rand.Read(v) + require.NoError(t, err) kvps = append(kvps, cmn.KVPair{Key: k, Value: v}) node.Set(k, v) } @@ -166,7 +174,7 @@ func testProof(t *testing.T) { proofs := []commitment.Proof{} root := node.Root.Update(header.AppHash) for _, kvp := range kvps { - v, p := node.Query(t, root.(merkle.Root), []byte(kvp.Key)) + v, p := node.Query(t, root.(merkle.Root), kvp.Key) require.Equal(t, kvp.Value, v) proofs = append(proofs, p) } diff --git a/x/ibc/02-client/tendermint/tests/valset.go b/x/ibc/02-client/tendermint/tests/valset.go index 1b5f88aa949b..30bd194682c8 100644 --- a/x/ibc/02-client/tendermint/tests/valset.go +++ b/x/ibc/02-client/tendermint/tests/valset.go @@ -131,7 +131,7 @@ func (vals MockValidators) Sign(header tmtypes.Header) tmtypes.SignedHeader { Height: header.Height, Type: tmtypes.PrecommitType, } - val.MockPV.SignVote(chainid, vote) + _ = val.MockPV.SignVote(chainid, vote) precommits[i] = vote.CommitSig() } From 892a12028f88b97c0f8c62594ae642bb585006a3 Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 9 Jul 2019 23:53:46 +0900 Subject: [PATCH 047/166] add handler/msgs/client --- store/state/base.go | 12 +- x/ibc/02-client/cli.go | 9 +- x/ibc/02-client/client/cli/query.go | 159 ++++++++++++++++++++++++ x/ibc/02-client/client/cli/tx.go | 144 +++++++++++++++++++++ x/ibc/02-client/client/utils/receive.go | 94 ++++++++++++++ x/ibc/02-client/codec.go | 9 ++ x/ibc/02-client/handler.go | 29 +++++ x/ibc/02-client/manager.go | 15 +-- x/ibc/02-client/msgs.go | 69 ++++++++++ x/ibc/02-client/tendermint/codec.go | 7 +- 10 files changed, 526 insertions(+), 21 deletions(-) create mode 100644 x/ibc/02-client/client/cli/query.go create mode 100644 x/ibc/02-client/client/cli/tx.go create mode 100644 x/ibc/02-client/client/utils/receive.go create mode 100644 x/ibc/02-client/handler.go create mode 100644 x/ibc/02-client/msgs.go diff --git a/store/state/base.go b/store/state/base.go index f163c18b98a0..a6f429053248 100644 --- a/store/state/base.go +++ b/store/state/base.go @@ -15,13 +15,19 @@ type Base struct { } func EmptyBase() Base { - return NewBase(nil, nil) + return NewBase(nil, nil, nil) } -func NewBase(cdc *codec.Codec, key sdk.StoreKey) Base { +func NewBase(cdc *codec.Codec, key sdk.StoreKey, rootkey []byte) Base { + if len(rootkey) == 0 { + return Base{ + cdc: cdc, + storefn: func(ctx Context) KVStore { return ctx.KVStore(key) }, + } + } return Base{ cdc: cdc, - storefn: func(ctx Context) KVStore { return ctx.KVStore(key) }, + storefn: func(ctx Context) KVStore { return prefix.NewStore(ctx.KVStore(key), rootkey) }, } } diff --git a/x/ibc/02-client/cli.go b/x/ibc/02-client/cli.go index 2592e697573c..45f2c9672a4d 100644 --- a/x/ibc/02-client/cli.go +++ b/x/ibc/02-client/cli.go @@ -17,9 +17,10 @@ type CLIObject struct { Cdc *codec.Codec } -func (obj Object) CLI(root merkle.Root) CLIObject { +func (man Manager) CLIObject(root merkle.Root, id string) CLIObject { + obj := man.object(id) return CLIObject{ - ID: obj.id, + ID: id, ConsensusStateKey: obj.consensusState.Key(), FrozenKey: obj.frozen.Key(), @@ -42,12 +43,12 @@ func (obj CLIObject) query(ctx context.CLIContext, key []byte, ptr interface{}) } -func (obj CLIObject) ConsensusState(ctx context.CLIContext, root merkle.Root) (res ConsensusState, proof merkle.Proof, err error) { +func (obj CLIObject) ConsensusState(ctx context.CLIContext) (res ConsensusState, proof merkle.Proof, err error) { proof, err = obj.query(ctx, obj.ConsensusStateKey, &res) return } -func (obj CLIObject) Frozen(ctx context.CLIContext, root merkle.Root) (res bool, proof merkle.Proof, err error) { +func (obj CLIObject) Frozen(ctx context.CLIContext) (res bool, proof merkle.Proof, err error) { proof, err = obj.query(ctx, obj.FrozenKey, &res) return } diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go new file mode 100644 index 000000000000..2506ed8dd905 --- /dev/null +++ b/x/ibc/02-client/client/cli/query.go @@ -0,0 +1,159 @@ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + + tmtypes "github.com/tendermint/tendermint/types" + + cli "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/state" + + "github.com/cosmos/cosmos-sdk/x/ibc/02-client" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/tendermint" + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" +) + +func defaultRoot(storeKey string, root []byte) merkle.Root { + return merkle.NewRoot(root, [][]byte{[]byte(storeKey)}, []byte("protocol")) +} + +func defaultBase(cdc *codec.Codec) (state.Base, state.Base) { + protocol := state.NewBase(cdc, nil, []byte("protocol")) + free := state.NewBase(cdc, nil, []byte("free")) + return protocol, free +} + +func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { + ibcQueryCmd := &cobra.Command{ + Use: "ibc", + Short: "IBC query subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + } + + ibcQueryCmd.AddCommand(cli.GetCommands( + GetCmdQueryConsensusState(storeKey, cdc), + GetCmdQueryHeader(cdc), + GetCmdQueryClient(storeKey, cdc), + )...) + return ibcQueryCmd +} + +func GetCmdQueryClient(storeKey string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "client", + Short: "Query stored client", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.NewCLIContext().WithCodec(cdc) + man := client.NewManager(defaultBase(cdc)) + root := defaultRoot(storeKey, nil) + id := args[0] + + state, _, err := man.CLIObject(root, id).ConsensusState(ctx) + if err != nil { + return err + } + + fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, state)) + + return nil + }, + } +} + +func GetCmdQueryConsensusState(storeKey string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "consensus-state", + Short: "Query the latest consensus state of the running chain", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.NewCLIContext().WithCodec(cdc) + + node, err := ctx.GetNode() + if err != nil { + return err + } + + info, err := node.ABCIInfo() + if err != nil { + return err + } + + height := info.Response.LastBlockHeight + prevheight := height - 1 + + commit, err := node.Commit(&height) + if err != nil { + return err + } + + validators, err := node.Validators(&prevheight) + if err != nil { + return err + } + + state := tendermint.ConsensusState{ + ChainID: commit.ChainID, + Height: uint64(commit.Height), + Root: defaultRoot(storeKey, []byte(commit.AppHash)), + NextValidatorSet: tmtypes.NewValidatorSet(validators.Validators), + } + + fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, state)) + + return nil + }, + } +} + +func GetCmdQueryHeader(cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "header", + Short: "Query the latest header of the running chain", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.NewCLIContext().WithCodec(cdc) + + node, err := ctx.GetNode() + if err != nil { + return err + } + + info, err := node.ABCIInfo() + if err != nil { + return err + } + + height := info.Response.LastBlockHeight + prevheight := height - 1 + + commit, err := node.Commit(&height) + if err != nil { + return err + } + + validators, err := node.Validators(&prevheight) + if err != nil { + return err + } + + nextvalidators, err := node.Validators(&height) + if err != nil { + return err + } + + header := tendermint.Header{ + SignedHeader: commit.SignedHeader, + ValidatorSet: tmtypes.NewValidatorSet(validators.Validators), + NextValidatorSet: tmtypes.NewValidatorSet(nextvalidators.Validators), + } + + fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, header)) + + return nil + }, + } +} diff --git a/x/ibc/02-client/client/cli/tx.go b/x/ibc/02-client/client/cli/tx.go new file mode 100644 index 000000000000..e654d76e7c3d --- /dev/null +++ b/x/ibc/02-client/client/cli/tx.go @@ -0,0 +1,144 @@ +package cli + +import ( + "errors" + "io/ioutil" + // "os" + + "github.com/spf13/cobra" + + // "github.com/tendermint/tendermint/libs/log" + rpcclient "github.com/tendermint/tendermint/rpc/client" + + cli "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + // "github.com/cosmos/cosmos-sdk/store/state" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + + "github.com/cosmos/cosmos-sdk/x/ibc/02-client" + // "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" +) + +const ( + FlagStatePath = "state" + FlagClientID = "client-id" + FlagConnectionID = "connection-id" + FlagChannelID = "channel-id" + FlagCounterpartyID = "counterparty-id" + FlagCounterpartyClientID = "counterparty-client-id" + FlagSourceNode = "source-node" +) + +func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { + ibcTxCmd := &cobra.Command{ + Use: "ibc", + Short: "IBC transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + } + + ibcTxCmd.AddCommand(cli.PostCommands( + GetCmdCreateClient(cdc), + GetCmdUpdateClient(cdc), + )...) + + return ibcTxCmd +} + +func GetCmdCreateClient(cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "create-client", + Short: "create new client with a consensus state", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContext(). + WithCodec(cdc) + + contents, err := ioutil.ReadFile(args[1]) + if err != nil { + return err + } + + var state client.ConsensusState + if err := cdc.UnmarshalJSON(contents, &state); err != nil { + return err + } + + msg := client.MsgCreateClient{ + ClientID: args[0], + ConsensusState: state, + Signer: cliCtx.GetFromAddress(), + } + + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + }, + } + + return cmd +} + +func GetCmdUpdateClient(cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "update-client", + Short: "update existing client with a header", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContext(). + WithCodec(cdc) + + contents, err := ioutil.ReadFile(args[1]) + if err != nil { + return err + } + + var header client.Header + if err := cdc.UnmarshalJSON(contents, &header); err != nil { + return err + } + + msg := client.MsgUpdateClient{ + ClientID: args[0], + Header: header, + Signer: cliCtx.GetFromAddress(), + } + + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + }, + } + + return cmd +} + +// Copied from client/context/query.go +func query(ctx context.CLIContext, key []byte) ([]byte, merkle.Proof, error) { + node, err := ctx.GetNode() + if err != nil { + return nil, merkle.Proof{}, err + } + + opts := rpcclient.ABCIQueryOptions{ + Height: ctx.Height, + Prove: true, + } + + result, err := node.ABCIQueryWithOptions("/store/ibc/key", key, opts) + if err != nil { + return nil, merkle.Proof{}, err + } + + resp := result.Response + if !resp.IsOK() { + return nil, merkle.Proof{}, errors.New(resp.Log) + } + + return resp.Value, merkle.Proof{ + Key: key, + Proof: resp.Proof, + }, nil +} diff --git a/x/ibc/02-client/client/utils/receive.go b/x/ibc/02-client/client/utils/receive.go new file mode 100644 index 000000000000..7a8df21189fb --- /dev/null +++ b/x/ibc/02-client/client/utils/receive.go @@ -0,0 +1,94 @@ +package cli + +import ( + "errors" + + "github.com/spf13/viper" + + rpcclient "github.com/tendermint/tendermint/rpc/client" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/store/state" + + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" + ibc "github.com/cosmos/cosmos-sdk/x/ibc/keeper" +) + +const ( + FlagStatePath = "state" + FlagClientID = "client-id" + FlagConnectionID = "connection-id" + FlagChannelID = "channel-id" + FlagCounterpartyID = "counterparty-id" + FlagCounterpartyClientID = "counterparty-client-id" + FlagSourceNode = "source-node" +) + +// Copied from client/context/query.go +func query(ctx context.CLIContext, key []byte) ([]byte, merkle.Proof, error) { + node, err := ctx.GetNode() + if err != nil { + return nil, merkle.Proof{}, err + } + + opts := rpcclient.ABCIQueryOptions{ + Height: ctx.Height, + Prove: true, + } + + result, err := node.ABCIQueryWithOptions("/store/ibc/key", key, opts) + if err != nil { + return nil, merkle.Proof{}, err + } + + resp := result.Response + if !resp.IsOK() { + return nil, merkle.Proof{}, errors.New(resp.Log) + } + + return resp.Value, merkle.Proof{ + Key: key, + Proof: resp.Proof, + }, nil +} + +func GetRelayPacket(cliCtxSource, cliCtx context.CLIContext) (ibc.Packet, ibc.Proof, error) { + keeper := ibc.DummyKeeper() + cdc := cliCtx.Codec + + connid := viper.GetString(FlagConnectionID) + chanid := viper.GetString(FlagChannelID) + + obj := keeper.Channel.Object(connid, chanid) + + seqbz, _, err := query(cliCtx, obj.Seqrecv.Key()) + if err != nil { + return nil, nil, err + } + seq, err := state.DecodeInt(seqbz, state.Dec) + if err != nil { + return nil, nil, err + } + + sentbz, _, err := query(cliCtxSource, obj.Seqsend.Key()) + if err != nil { + return nil, nil, err + } + sent, err := state.DecodeInt(sentbz, state.Dec) + if err != nil { + return nil, nil, err + } + + if seq == sent { + return nil, nil, errors.New("no packet detected") + } + + var packet ibc.Packet + packetbz, proof, err := query(cliCtxSource, obj.Packets.Value(seq).Key()) + if err != nil { + return nil, nil, err + } + cdc.MustUnmarshalBinaryBare(packetbz, &packet) + + return packet, proof, nil +} diff --git a/x/ibc/02-client/codec.go b/x/ibc/02-client/codec.go index fa194562e4f1..ece7e750dc98 100644 --- a/x/ibc/02-client/codec.go +++ b/x/ibc/02-client/codec.go @@ -4,7 +4,16 @@ import ( "github.com/cosmos/cosmos-sdk/codec" ) +var MsgCdc = codec.New() + +func init() { + RegisterCodec(MsgCdc) +} + func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*ConsensusState)(nil), nil) cdc.RegisterInterface((*Header)(nil), nil) + + cdc.RegisterConcrete(MsgCreateClient{}, "ibc/client/MsgCreateClient", nil) + cdc.RegisterConcrete(MsgUpdateClient{}, "ibc/client/MsgUpdateClient", nil) } diff --git a/x/ibc/02-client/handler.go b/x/ibc/02-client/handler.go new file mode 100644 index 000000000000..dfab0725b8c4 --- /dev/null +++ b/x/ibc/02-client/handler.go @@ -0,0 +1,29 @@ +package client + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func HandleMsgCreateClient(ctx sdk.Context, msg MsgCreateClient, man Manager) sdk.Result { + _, err := man.Create(ctx, msg.ClientID, msg.ConsensusState) + if err != nil { + return sdk.NewError(sdk.CodespaceType("ibc"), sdk.CodeType(100), err.Error()).Result() + } + + // TODO: events + return sdk.Result{} +} + +func HandleMsgUpdateClient(ctx sdk.Context, msg MsgUpdateClient, man Manager) sdk.Result { + obj, err := man.Query(ctx, msg.ClientID) + if err != nil { + return sdk.NewError(sdk.CodespaceType("ibc"), sdk.CodeType(200), err.Error()).Result() + } + err = obj.Update(ctx, msg.Header) + if err != nil { + return sdk.NewError(sdk.CodespaceType("ibc"), sdk.CodeType(300), err.Error()).Result() + } + + // TODO: events + return sdk.Result{} +} diff --git a/x/ibc/02-client/manager.go b/x/ibc/02-client/manager.go index e94d14702b72..67d43a4325eb 100644 --- a/x/ibc/02-client/manager.go +++ b/x/ibc/02-client/manager.go @@ -2,7 +2,6 @@ package client import ( "errors" - "strconv" "github.com/cosmos/cosmos-sdk/store/state" sdk "github.com/cosmos/cosmos-sdk/types" @@ -12,25 +11,16 @@ import ( // XXX: implement spec: ClientState.verifiedRoots -type IDGenerator func(sdk.Context /*Header,*/, state.Value) string - -func IntegerIDGenerator(ctx sdk.Context, v state.Value) string { - id := state.NewInteger(v, state.Dec).Incr(ctx) - return strconv.FormatUint(id, 10) -} - type Manager struct { protocol state.Mapping idval state.Value - idgen IDGenerator } -func NewManager(protocol, free state.Base, idgen IDGenerator) Manager { +func NewManager(protocol, free state.Base) Manager { return Manager{ protocol: state.NewMapping(protocol, []byte("/client")), idval: state.NewValue(free, []byte("/client/id")), - idgen: idgen, } } @@ -61,8 +51,7 @@ func (man Manager) object(id string) Object { } } -func (man Manager) Create(ctx sdk.Context, cs ConsensusState) (Object, error) { - id := man.idgen(ctx, man.idval) +func (man Manager) Create(ctx sdk.Context, id string, cs ConsensusState) (Object, error) { obj := man.object(id) if obj.exists(ctx) { return Object{}, errors.New("Create client on already existing id") diff --git a/x/ibc/02-client/msgs.go b/x/ibc/02-client/msgs.go new file mode 100644 index 000000000000..7f515c388c8f --- /dev/null +++ b/x/ibc/02-client/msgs.go @@ -0,0 +1,69 @@ +package client + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type MsgCreateClient struct { + ClientID string + ConsensusState ConsensusState + Signer sdk.AccAddress +} + +var _ sdk.Msg = MsgCreateClient{} + +func (msg MsgCreateClient) Route() string { + return "ibc" +} + +func (msg MsgCreateClient) Type() string { + return "create-client" +} + +func (msg MsgCreateClient) ValidateBasic() sdk.Error { + if msg.Signer.Empty() { + return sdk.ErrInvalidAddress("empty address") + } + return nil +} + +func (msg MsgCreateClient) GetSignBytes() []byte { + bz := MsgCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg MsgCreateClient) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Signer} +} + +type MsgUpdateClient struct { + ClientID string + Header Header + Signer sdk.AccAddress +} + +var _ sdk.Msg = MsgUpdateClient{} + +func (msg MsgUpdateClient) Route() string { + return "ibc" +} + +func (msg MsgUpdateClient) Type() string { + return "update-client" +} + +func (msg MsgUpdateClient) ValidateBasic() sdk.Error { + if msg.Signer.Empty() { + return sdk.ErrInvalidAddress("empty address") + } + return nil +} + +func (msg MsgUpdateClient) GetSignBytes() []byte { + bz := MsgCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg MsgUpdateClient) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Signer} +} diff --git a/x/ibc/02-client/tendermint/codec.go b/x/ibc/02-client/tendermint/codec.go index d8308a3e7ec8..eca3b38917fa 100644 --- a/x/ibc/02-client/tendermint/codec.go +++ b/x/ibc/02-client/tendermint/codec.go @@ -2,10 +2,15 @@ package tendermint import ( "github.com/cosmos/cosmos-sdk/codec" + + "github.com/cosmos/cosmos-sdk/x/ibc/02-client" ) +func init() { + RegisterCodec(client.MsgCdc) +} + func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(ConsensusState{}, "ibc/client/tendermint/ConsensusState", nil) cdc.RegisterConcrete(Header{}, "ibc/client/tendermint/Header", nil) - } From dce114840b5933e839819e30b194c9a7c5ea47f6 Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 9 Jul 2019 23:54:32 +0900 Subject: [PATCH 048/166] rm relay --- x/ibc/02-client/client/utils/receive.go | 94 ------------------------- 1 file changed, 94 deletions(-) delete mode 100644 x/ibc/02-client/client/utils/receive.go diff --git a/x/ibc/02-client/client/utils/receive.go b/x/ibc/02-client/client/utils/receive.go deleted file mode 100644 index 7a8df21189fb..000000000000 --- a/x/ibc/02-client/client/utils/receive.go +++ /dev/null @@ -1,94 +0,0 @@ -package cli - -import ( - "errors" - - "github.com/spf13/viper" - - rpcclient "github.com/tendermint/tendermint/rpc/client" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/store/state" - - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" - ibc "github.com/cosmos/cosmos-sdk/x/ibc/keeper" -) - -const ( - FlagStatePath = "state" - FlagClientID = "client-id" - FlagConnectionID = "connection-id" - FlagChannelID = "channel-id" - FlagCounterpartyID = "counterparty-id" - FlagCounterpartyClientID = "counterparty-client-id" - FlagSourceNode = "source-node" -) - -// Copied from client/context/query.go -func query(ctx context.CLIContext, key []byte) ([]byte, merkle.Proof, error) { - node, err := ctx.GetNode() - if err != nil { - return nil, merkle.Proof{}, err - } - - opts := rpcclient.ABCIQueryOptions{ - Height: ctx.Height, - Prove: true, - } - - result, err := node.ABCIQueryWithOptions("/store/ibc/key", key, opts) - if err != nil { - return nil, merkle.Proof{}, err - } - - resp := result.Response - if !resp.IsOK() { - return nil, merkle.Proof{}, errors.New(resp.Log) - } - - return resp.Value, merkle.Proof{ - Key: key, - Proof: resp.Proof, - }, nil -} - -func GetRelayPacket(cliCtxSource, cliCtx context.CLIContext) (ibc.Packet, ibc.Proof, error) { - keeper := ibc.DummyKeeper() - cdc := cliCtx.Codec - - connid := viper.GetString(FlagConnectionID) - chanid := viper.GetString(FlagChannelID) - - obj := keeper.Channel.Object(connid, chanid) - - seqbz, _, err := query(cliCtx, obj.Seqrecv.Key()) - if err != nil { - return nil, nil, err - } - seq, err := state.DecodeInt(seqbz, state.Dec) - if err != nil { - return nil, nil, err - } - - sentbz, _, err := query(cliCtxSource, obj.Seqsend.Key()) - if err != nil { - return nil, nil, err - } - sent, err := state.DecodeInt(sentbz, state.Dec) - if err != nil { - return nil, nil, err - } - - if seq == sent { - return nil, nil, errors.New("no packet detected") - } - - var packet ibc.Packet - packetbz, proof, err := query(cliCtxSource, obj.Packets.Value(seq).Key()) - if err != nil { - return nil, nil, err - } - cdc.MustUnmarshalBinaryBare(packetbz, &packet) - - return packet, proof, nil -} From 26e80388c4238d40c5d261f4cf932bd2a151ad8a Mon Sep 17 00:00:00 2001 From: mossid Date: Fri, 12 Jul 2019 01:33:18 +0900 Subject: [PATCH 049/166] finalize rebase on 23 root/path sep --- x/ibc/02-client/cli.go | 8 +++--- .../tendermint/tests/tendermint_test.go | 8 +++--- x/ibc/02-client/tendermint/tests/types.go | 26 +++++++++---------- x/ibc/02-client/tendermint/types.go | 3 ++- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/x/ibc/02-client/cli.go b/x/ibc/02-client/cli.go index 45f2c9672a4d..382b1d2d4aea 100644 --- a/x/ibc/02-client/cli.go +++ b/x/ibc/02-client/cli.go @@ -13,24 +13,24 @@ type CLIObject struct { ConsensusStateKey []byte FrozenKey []byte - Root merkle.Root + Path merkle.Path Cdc *codec.Codec } -func (man Manager) CLIObject(root merkle.Root, id string) CLIObject { +func (man Manager) CLIObject(path merkle.Path, id string) CLIObject { obj := man.object(id) return CLIObject{ ID: id, ConsensusStateKey: obj.consensusState.Key(), FrozenKey: obj.frozen.Key(), - Root: root, + Path: path, Cdc: obj.consensusState.Cdc(), } } func (obj CLIObject) query(ctx context.CLIContext, key []byte, ptr interface{}) (merkle.Proof, error) { - resp, err := ctx.QueryABCI(obj.Root.RequestQuery(key)) + resp, err := ctx.QueryABCI(obj.Path.RequestQuery(key)) if err != nil { return merkle.Proof{}, err } diff --git a/x/ibc/02-client/tendermint/tests/tendermint_test.go b/x/ibc/02-client/tendermint/tests/tendermint_test.go index 8a1c764f50f2..61a0944fbaa0 100644 --- a/x/ibc/02-client/tendermint/tests/tendermint_test.go +++ b/x/ibc/02-client/tendermint/tests/tendermint_test.go @@ -4,14 +4,16 @@ import ( "testing" "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) func testUpdate(t *testing.T, interval int, ok bool) { - node := NewNode(NewMockValidators(100, 10), newRoot()) + node := NewNode(NewMockValidators(100, 10), newPath()) - node.Commit() + header := node.Commit() - verifier := node.LastStateVerifier(newRoot()) + verifier := node.LastStateVerifier(merkle.NewRoot(header.AppHash)) for i := 0; i < 100; i++ { header := node.Commit() diff --git a/x/ibc/02-client/tendermint/tests/types.go b/x/ibc/02-client/tendermint/tests/types.go index 5e0af44f3806..f9fd9fc9f0c8 100644 --- a/x/ibc/02-client/tendermint/tests/types.go +++ b/x/ibc/02-client/tendermint/tests/types.go @@ -24,8 +24,8 @@ import ( ) // nolint: unused -func newRoot() merkle.Root { - return merkle.NewRoot(nil, [][]byte{[]byte("test")}, []byte{0x12, 0x34}) +func newPath() merkle.Path { + return merkle.NewPath([][]byte{[]byte("test")}, []byte{0x12, 0x34}) } const chainid = "testchain" @@ -54,10 +54,10 @@ type Node struct { Commits []tmtypes.SignedHeader - Root merkle.Root + Path merkle.Path } -func NewNode(valset MockValidators, root merkle.Root) *Node { +func NewNode(valset MockValidators, path merkle.Path) *Node { key, ctx, cms, _ := defaultComponents() return &Node{ Valset: valset, @@ -65,7 +65,7 @@ func NewNode(valset MockValidators, root merkle.Root) *Node { Key: key, Store: ctx.KVStore(key), Commits: nil, - Root: root, + Path: path, } } @@ -120,7 +120,7 @@ func NewVerifier(header tmtypes.SignedHeader, nextvalset MockValidators, root me tendermint.ConsensusState{ ChainID: chainid, Height: uint64(header.Height), - Root: root.Update(header.AppHash), + Root: merkle.NewRoot(header.AppHash), NextValidatorSet: nextvalset.ValidatorSet(), }, } @@ -142,19 +142,19 @@ func (v *Verifier) Validate(header tmtypes.SignedHeader, valset, nextvalset Mock return nil } -func (node *Node) Query(t *testing.T, root merkle.Root, k []byte) ([]byte, commitment.Proof) { - code, value, proof := root.QueryMultiStore(node.Cms, k) +func (node *Node) Query(t *testing.T, path merkle.Path, k []byte) ([]byte, commitment.Proof) { + code, value, proof := path.QueryMultiStore(node.Cms, k) require.Equal(t, uint32(0), code) return value, proof } func (node *Node) Set(k, value []byte) { - node.Store.Set(node.Root.Key(k), value) + node.Store.Set(node.Path.Key(k), value) } // nolint:deadcode,unused func testProof(t *testing.T) { - node := NewNode(NewMockValidators(100, 10), newRoot()) + node := NewNode(NewMockValidators(100, 10), newPath()) node.Commit() @@ -172,13 +172,13 @@ func testProof(t *testing.T) { } header := node.Commit() proofs := []commitment.Proof{} - root := node.Root.Update(header.AppHash) + root := merkle.NewRoot(header.AppHash) for _, kvp := range kvps { - v, p := node.Query(t, root.(merkle.Root), kvp.Key) + v, p := node.Query(t, node.Path, kvp.Key) require.Equal(t, kvp.Value, v) proofs = append(proofs, p) } - cstore, err := commitment.NewStore(root, proofs) + cstore, err := commitment.NewStore(root, node.Path, proofs) require.NoError(t, err) for _, kvp := range kvps { diff --git a/x/ibc/02-client/tendermint/types.go b/x/ibc/02-client/tendermint/types.go index 6bb1bb1f18dd..ca5da8c1cae5 100644 --- a/x/ibc/02-client/tendermint/types.go +++ b/x/ibc/02-client/tendermint/types.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) var _ client.ConsensusState = ConsensusState{} @@ -37,7 +38,7 @@ func (cs ConsensusState) update(header Header) ConsensusState { return ConsensusState{ ChainID: cs.ChainID, Height: uint64(header.Height), - Root: cs.GetRoot().Update(header.AppHash), + Root: merkle.NewRoot(header.AppHash), NextValidatorSet: header.NextValidatorSet, } } From 812bea4a62b62263866b87b3e716939a65b200b2 Mon Sep 17 00:00:00 2001 From: mossid Date: Fri, 12 Jul 2019 01:35:54 +0900 Subject: [PATCH 050/166] fix lint, fix syntax --- x/ibc/02-client/client/cli/query.go | 10 +++++----- x/ibc/02-client/client/cli/tx.go | 31 ----------------------------- 2 files changed, 5 insertions(+), 36 deletions(-) diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index 2506ed8dd905..9de2231427bb 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -17,8 +17,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) -func defaultRoot(storeKey string, root []byte) merkle.Root { - return merkle.NewRoot(root, [][]byte{[]byte(storeKey)}, []byte("protocol")) +func defaultPath(storeKey string) merkle.Path { + return merkle.NewPath([][]byte{[]byte(storeKey)}, []byte("protocol")) } func defaultBase(cdc *codec.Codec) (state.Base, state.Base) { @@ -51,10 +51,10 @@ func GetCmdQueryClient(storeKey string, cdc *codec.Codec) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { ctx := context.NewCLIContext().WithCodec(cdc) man := client.NewManager(defaultBase(cdc)) - root := defaultRoot(storeKey, nil) + path := defaultPath(storeKey) id := args[0] - state, _, err := man.CLIObject(root, id).ConsensusState(ctx) + state, _, err := man.CLIObject(path, id).ConsensusState(ctx) if err != nil { return err } @@ -99,7 +99,7 @@ func GetCmdQueryConsensusState(storeKey string, cdc *codec.Codec) *cobra.Command state := tendermint.ConsensusState{ ChainID: commit.ChainID, Height: uint64(commit.Height), - Root: defaultRoot(storeKey, []byte(commit.AppHash)), + Root: merkle.NewRoot(commit.AppHash), NextValidatorSet: tmtypes.NewValidatorSet(validators.Validators), } diff --git a/x/ibc/02-client/client/cli/tx.go b/x/ibc/02-client/client/cli/tx.go index e654d76e7c3d..1b18dbe34783 100644 --- a/x/ibc/02-client/client/cli/tx.go +++ b/x/ibc/02-client/client/cli/tx.go @@ -1,14 +1,12 @@ package cli import ( - "errors" "io/ioutil" // "os" "github.com/spf13/cobra" // "github.com/tendermint/tendermint/libs/log" - rpcclient "github.com/tendermint/tendermint/rpc/client" cli "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" @@ -20,7 +18,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client" // "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) const ( @@ -114,31 +111,3 @@ func GetCmdUpdateClient(cdc *codec.Codec) *cobra.Command { return cmd } - -// Copied from client/context/query.go -func query(ctx context.CLIContext, key []byte) ([]byte, merkle.Proof, error) { - node, err := ctx.GetNode() - if err != nil { - return nil, merkle.Proof{}, err - } - - opts := rpcclient.ABCIQueryOptions{ - Height: ctx.Height, - Prove: true, - } - - result, err := node.ABCIQueryWithOptions("/store/ibc/key", key, opts) - if err != nil { - return nil, merkle.Proof{}, err - } - - resp := result.Response - if !resp.IsOK() { - return nil, merkle.Proof{}, errors.New(resp.Log) - } - - return resp.Value, merkle.Proof{ - Key: key, - Proof: resp.Proof, - }, nil -} From dbd47aa9abcb66e59626a8aa5c51926798b39396 Mon Sep 17 00:00:00 2001 From: mossid Date: Mon, 15 Jul 2019 17:52:19 +0900 Subject: [PATCH 051/166] rm freebase, reformat query --- x/ibc/02-client/client/cli/query.go | 24 ++++++++++++------------ x/ibc/02-client/manager.go | 5 +---- x/ibc/version.go | 3 +++ 3 files changed, 16 insertions(+), 16 deletions(-) create mode 100644 x/ibc/version.go diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index 9de2231427bb..fb2c7f73da0e 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -2,6 +2,7 @@ package cli import ( "fmt" + "strconv" "github.com/spf13/cobra" @@ -11,26 +12,25 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/state" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc" "github.com/cosmos/cosmos-sdk/x/ibc/02-client" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/tendermint" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) -func defaultPath(storeKey string) merkle.Path { - return merkle.NewPath([][]byte{[]byte(storeKey)}, []byte("protocol")) -} - -func defaultBase(cdc *codec.Codec) (state.Base, state.Base) { - protocol := state.NewBase(cdc, nil, []byte("protocol")) - free := state.NewBase(cdc, nil, []byte("free")) - return protocol, free +func components(cdc *codec.Codec, storeKey string, version int64) (path merkle.Path, base state.Base) { + prefix := []byte(strconv.FormatInt(version, 10) + "/") + path = merkle.NewPath([][]byte{[]byte(storeKey)}, prefix) + base = state.NewBase(cdc, sdk.NewKVStoreKey(storeKey), prefix) + return } func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { ibcQueryCmd := &cobra.Command{ - Use: "ibc", - Short: "IBC query subcommands", + Use: "client", + Short: "IBC client query subcommands", DisableFlagParsing: true, SuggestionsMinimumDistance: 2, } @@ -50,8 +50,8 @@ func GetCmdQueryClient(storeKey string, cdc *codec.Codec) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := context.NewCLIContext().WithCodec(cdc) - man := client.NewManager(defaultBase(cdc)) - path := defaultPath(storeKey) + path, base := components(cdc, storeKey, ibc.Version) + man := client.NewManager(base) id := args[0] state, _, err := man.CLIObject(path, id).ConsensusState(ctx) diff --git a/x/ibc/02-client/manager.go b/x/ibc/02-client/manager.go index 67d43a4325eb..4f5195bc1850 100644 --- a/x/ibc/02-client/manager.go +++ b/x/ibc/02-client/manager.go @@ -13,14 +13,11 @@ import ( type Manager struct { protocol state.Mapping - - idval state.Value } -func NewManager(protocol, free state.Base) Manager { +func NewManager(protocol state.Base) Manager { return Manager{ protocol: state.NewMapping(protocol, []byte("/client")), - idval: state.NewValue(free, []byte("/client/id")), } } diff --git a/x/ibc/version.go b/x/ibc/version.go new file mode 100644 index 000000000000..11bd8ee5b3d3 --- /dev/null +++ b/x/ibc/version.go @@ -0,0 +1,3 @@ +package ibc + +const Version int64 = 1 From f60924903d6531df2be76e6f698f4c2341041043 Mon Sep 17 00:00:00 2001 From: mossid Date: Thu, 25 Jul 2019 01:20:19 +0900 Subject: [PATCH 052/166] add docs in progre --- store/state/base.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/store/state/base.go b/store/state/base.go index 25760b77a947..280ae70e7ad9 100644 --- a/store/state/base.go +++ b/store/state/base.go @@ -8,6 +8,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +// Base is a state accessor base layer, consists of Codec, KVStore getter function, and key prefix. +// type Base struct { cdc *codec.Codec storefn func(Context) KVStore From 7da69c124e97a99a042a5f201ed4642774097a58 Mon Sep 17 00:00:00 2001 From: mossid Date: Fri, 26 Jul 2019 00:47:41 +0900 Subject: [PATCH 053/166] add comments --- store/state/base.go | 48 ++++++++++++++++++++--------- store/state/boolean.go | 2 ++ store/state/enum.go | 5 +++ store/state/errors.go | 56 +++++++++++++++++++++++++++++++++ store/state/indexer.go | 25 +++++---------- store/state/integer.go | 27 +++++----------- store/state/mapping.go | 16 +++------- store/state/string.go | 25 +++++++++++++++ store/state/value.go | 70 ++++++++++-------------------------------- 9 files changed, 158 insertions(+), 116 deletions(-) create mode 100644 store/state/errors.go create mode 100644 store/state/string.go diff --git a/store/state/base.go b/store/state/base.go index 280ae70e7ad9..19d85ffb16f4 100644 --- a/store/state/base.go +++ b/store/state/base.go @@ -8,27 +8,29 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// Base is a state accessor base layer, consists of Codec, KVStore getter function, and key prefix. +// Base is a state accessor base layer, consists of Codec, StoreKey, and prefix. +// StoreKey is used to get the KVStore, cdc is used to marshal/unmarshal the interfaces, +// and the prefix is prefixed to the key. // +// Base has practically the same capability with the storeKey. +// It should not be passed to an untrusted actor. type Base struct { - cdc *codec.Codec - storefn func(Context) KVStore - prefix []byte + storeKey sdk.StoreKey + cdc *codec.Codec + prefix []byte } -func EmptyBase() Base { - return NewBase(nil, nil) -} - -func NewBase(cdc *codec.Codec, key sdk.StoreKey) Base { +// NewBase() is the constructor for Base() +func NewBase(cdc *codec.Codec, key sdk.StoreKey, rootkey []byte) Base { return Base{ - cdc: cdc, - storefn: func(ctx Context) KVStore { return ctx.KVStore(key) }, + storeKey: key, + cdc: cdc, + prefix: rootkey, } } func (base Base) store(ctx Context) KVStore { - return prefix.NewStore(base.storefn(ctx), base.prefix) + return prefix.NewStore(ctx.KVStore(base.storeKey), base.prefix) } func join(a, b []byte) (res []byte) { @@ -38,15 +40,17 @@ func join(a, b []byte) (res []byte) { return } +// Prefix() returns a copy of the Base with the updated prefix. func (base Base) Prefix(prefix []byte) (res Base) { res = Base{ - cdc: base.cdc, - storefn: base.storefn, - prefix: join(base.prefix, prefix), + storeKey: base.storeKey, + cdc: base.cdc, + prefix: join(base.prefix, prefix), } return } +// Cdc() returns the codec of the base. It is safe to expose the codec. func (base Base) Cdc() *codec.Codec { return base.cdc } @@ -54,3 +58,17 @@ func (base Base) Cdc() *codec.Codec { func (base Base) key(key []byte) []byte { return join(base.prefix, key) } + +// StoreName() returns the name of the storeKey. It is safe to expose the store name. +// Used by the CLI side query operations. +func (base Base) StoreName() string { + return base.storeKey.Name() +} + +// PrefixBytes() returns the prefix bytes. It is safe to expsoe the prefix bytes. +// Used by the CLI side query operations. +func (base Base) PrefixBytes() (res []byte) { + res = make([]byte, len(base.prefix)) + copy(res, base.prefix) + return +} diff --git a/store/state/boolean.go b/store/state/boolean.go index 2fb3701680be..dab4d5bbf67e 100644 --- a/store/state/boolean.go +++ b/store/state/boolean.go @@ -1,5 +1,7 @@ package state +// Boolean is a bool typed wrapper for Value. +// Except for the type checking, it does not alter the behaviour. type Boolean struct { Value } diff --git a/store/state/enum.go b/store/state/enum.go index 8598f10324b7..57fbdc9f3e92 100644 --- a/store/state/enum.go +++ b/store/state/enum.go @@ -1,5 +1,7 @@ package state +// Enum is a byte typed wrapper for Value. +// Except for the type checking, it does not alter the behaviour. type Enum struct { Value } @@ -22,12 +24,15 @@ func (v Enum) Set(ctx Context, value byte) { v.Value.Set(ctx, value) } +// Incr() increments the stored value, and returns the updated value. func (v Enum) Incr(ctx Context) (res byte) { res = v.Get(ctx) + 1 v.Set(ctx, res) return } +// Transit() checks whether the stored value matching with the "from" argument. +// If it matches, it stores the "to" argument to the state and returns true. func (v Enum) Transit(ctx Context, from, to byte) bool { if v.Get(ctx) != from { return false diff --git a/store/state/errors.go b/store/state/errors.go new file mode 100644 index 000000000000..936066b0ac29 --- /dev/null +++ b/store/state/errors.go @@ -0,0 +1,56 @@ +package state + +import ( + "fmt" +) + +type GetSafeErrorType byte + +const ( + ErrTypeEmptyValue GetSafeErrorType = iota + ErrTypeUnmarshal +) + +func (ty GetSafeErrorType) Format(msg string) (res string) { + switch ty { + case ErrTypeEmptyValue: + res = fmt.Sprintf("Empty Value found") + case ErrTypeUnmarshal: + res = fmt.Sprintf("Error while unmarshal") + default: + panic("Unknown error type") + } + + if msg != "" { + res = fmt.Sprintf("%s: %s", res, msg) + } + + return +} + +type GetSafeError struct { + ty GetSafeErrorType + inner error +} + +var _ error = (*GetSafeError)(nil) // TODO: sdk.Error + +func (err *GetSafeError) Error() string { + if err.inner == nil { + return err.ty.Format("") + } + return err.ty.Format(err.inner.Error()) +} + +func ErrEmptyValue() *GetSafeError { + return &GetSafeError{ + ty: ErrTypeEmptyValue, + } +} + +func ErrUnmarshal(err error) *GetSafeError { + return &GetSafeError{ + ty: ErrTypeUnmarshal, + inner: err, + } +} diff --git a/store/state/indexer.go b/store/state/indexer.go index 281dea761fd5..72be902b3ddc 100644 --- a/store/state/indexer.go +++ b/store/state/indexer.go @@ -3,7 +3,6 @@ package state import ( "encoding/binary" "fmt" - "strconv" ) type IntEncoding byte @@ -14,12 +13,16 @@ const ( Bin ) +// Indexer is a integer typed key wrapper for Mapping. +// Except for the type checking, it does not alter the behaviour. +// All keys are encoded depending on the IntEncoding type Indexer struct { m Mapping enc IntEncoding } +// NewIndexer() constructs the Indexer with a predetermined prefix and IntEncoding func NewIndexer(base Base, prefix []byte, enc IntEncoding) Indexer { return Indexer{ m: NewMapping(base, prefix), @@ -31,10 +34,13 @@ func NewIndexer(base Base, prefix []byte, enc IntEncoding) Indexer { func EncodeInt(index uint64, enc IntEncoding) (res []byte) { switch enc { case Dec: + // Returns decimal number index, 20-length 0 padded return []byte(fmt.Sprintf("%020d", index)) case Hex: + // Returns hexadecimal number index, 20-length 0 padded return []byte(fmt.Sprintf("%020x", index)) case Bin: + // Returns bigendian encoded number index, 8-length res = make([]byte, 8) binary.BigEndian.PutUint64(res, index) return @@ -43,19 +49,6 @@ func EncodeInt(index uint64, enc IntEncoding) (res []byte) { } } -func DecodeInt(bz []byte, enc IntEncoding) (res uint64, err error) { - switch enc { - case Dec: - return strconv.ParseUint(string(bz), 10, 64) - case Hex: - return strconv.ParseUint(string(bz), 16, 64) - case Bin: - return binary.BigEndian.Uint64(bz), nil - default: - panic("invalid IntEncoding") - } -} - func (ix Indexer) Value(index uint64) Value { return ix.m.Value(EncodeInt(index, ix.enc)) } @@ -80,10 +73,6 @@ func (ix Indexer) Delete(ctx Context, index uint64) { ix.Value(index).Delete(ctx) } -func (ix Indexer) IsEmpty(ctx Context) bool { - return ix.m.IsEmpty(ctx) -} - func (ix Indexer) Prefix(prefix []byte) Indexer { return Indexer{ m: ix.m.Prefix(prefix), diff --git a/store/state/integer.go b/store/state/integer.go index 9dd00cf45d1d..0ca86572aba2 100644 --- a/store/state/integer.go +++ b/store/state/integer.go @@ -1,5 +1,7 @@ package state +// Integer is a uint64 types wrapper for Value. +// Except for the type checking, it does not alter the behaviour. type Integer struct { Value @@ -14,33 +16,20 @@ func NewInteger(v Value, enc IntEncoding) Integer { } func (v Integer) Get(ctx Context) (res uint64) { - bz := v.GetRaw(ctx) - if bz == nil { - return 0 - } - res, err := DecodeInt(bz, v.enc) - if err != nil { - panic(err) - } + v.Value.Get(ctx, &res) return res } -func (v Integer) GetSafe(ctx Context) (uint64, error) { - bz := v.GetRaw(ctx) - if bz == nil { - return 0, &GetSafeError{} - } - res, err := DecodeInt(bz, v.enc) - if err != nil { - panic(err) - } - return res, nil +func (v Integer) GetSafe(ctx Context) (res uint64, err error) { + err = v.Value.GetSafe(ctx, &res) + return } func (v Integer) Set(ctx Context, value uint64) { - v.SetRaw(ctx, EncodeInt(value, v.enc)) + v.Value.Set(ctx, value) } +// Incr() increments the stored value, and returns the updated value. func (v Integer) Incr(ctx Context) (res uint64) { res = v.Get(ctx) + 1 v.Set(ctx, res) diff --git a/store/state/mapping.go b/store/state/mapping.go index 127f5a0afb7c..91565ea2c2af 100644 --- a/store/state/mapping.go +++ b/store/state/mapping.go @@ -1,15 +1,14 @@ package state +// Mapping is key []byte -> value []byte mapping using a base(possibly prefixed) type Mapping struct { - base Base - start, end []byte + base Base } +// NewMapping() constructs a Mapping with a provided prefix func NewMapping(base Base, prefix []byte) Mapping { return Mapping{ - base: base.Prefix(prefix), - start: []byte{}, // preventing nil key access in store.Last - end: nil, + base: base.Prefix(prefix), } } @@ -17,6 +16,7 @@ func (m Mapping) store(ctx Context) KVStore { return m.base.store(ctx) } +// Value() returns the Value corresponding to the provided key func (m Mapping) Value(key []byte) Value { return NewValue(m.base, key) } @@ -45,12 +45,6 @@ func (m Mapping) Delete(ctx Context, key []byte) { m.Value(key).Delete(ctx) } -func (m Mapping) IsEmpty(ctx Context) bool { - iter := m.store(ctx).Iterator(nil, nil) - defer iter.Close() - return iter.Valid() -} - func (m Mapping) Prefix(prefix []byte) Mapping { return NewMapping(m.base, prefix) } diff --git a/store/state/string.go b/store/state/string.go new file mode 100644 index 000000000000..7198f7869a85 --- /dev/null +++ b/store/state/string.go @@ -0,0 +1,25 @@ +package state + +// String is a string types wrapper for Value. +// Except for the type checking, it does not alter the behaviour. +type String struct { + Value +} + +func NewString(v Value) String { + return String{v} +} + +func (v String) Get(ctx Context) (res string) { + v.Value.Get(ctx, &res) + return +} + +func (v String) GetSafe(ctx Context) (res string, err error) { + err = v.Value.GetSafe(ctx, &res) + return +} + +func (v String) Set(ctx Context, value string) { + v.Value.Set(ctx, value) +} diff --git a/store/state/value.go b/store/state/value.go index a087db6064ce..87bbeec9f805 100644 --- a/store/state/value.go +++ b/store/state/value.go @@ -1,16 +1,18 @@ package state import ( - "fmt" - "github.com/cosmos/cosmos-sdk/codec" ) +// Value is a capability for reading and writing on a specific key-value point in the state. +// Value consists of Base and key []byte. +// An actor holding a Value has a full access right on that state point. type Value struct { base Base key []byte } +// NewValue() constructs a Value func NewValue(base Base, key []byte) Value { return Value{ base: base, @@ -22,10 +24,13 @@ func (v Value) store(ctx Context) KVStore { return v.base.store(ctx) } +// Cdc() returns the codec that the value is using to marshal/unmarshal func (v Value) Cdc() *codec.Codec { return v.base.Cdc() } +// Get() unmarshales and sets the stored value to the pointer if it exists. +// It will panic if the value exists but not unmarshalable. func (v Value) Get(ctx Context, ptr interface{}) { bz := v.store(ctx).Get(v.key) if bz != nil { @@ -33,6 +38,8 @@ func (v Value) Get(ctx Context, ptr interface{}) { } } +// GetSafe() unmarshales and sets the stored value to the pointer. +// It will return an error if the value does not exist or unmarshalable. func (v Value) GetSafe(ctx Context, ptr interface{}) error { bz := v.store(ctx).Get(v.key) if bz == nil { @@ -45,77 +52,34 @@ func (v Value) GetSafe(ctx Context, ptr interface{}) error { return nil } +// GetRaw() returns the raw bytes that is stored in the state. func (v Value) GetRaw(ctx Context) []byte { return v.store(ctx).Get(v.key) } +// Set() marshales sets the "o" argument to the state. func (v Value) Set(ctx Context, o interface{}) { v.store(ctx).Set(v.key, v.base.cdc.MustMarshalBinaryBare(o)) } +// SetRaw() sets the raw bytes to the state. func (v Value) SetRaw(ctx Context, bz []byte) { v.store(ctx).Set(v.key, bz) } +// Exists() returns true if the stored value is not nil. +// Calles KVStore.Has() internally func (v Value) Exists(ctx Context) bool { return v.store(ctx).Has(v.key) } +// Delete() deletes the stored value. +// Calles KVStore.Delete() internally func (v Value) Delete(ctx Context) { v.store(ctx).Delete(v.key) } +// Key() returns the prefixed key that the Value is providing to the KVStore func (v Value) Key() []byte { return v.base.key(v.key) } - -type GetSafeErrorType byte - -const ( - ErrTypeEmptyValue GetSafeErrorType = iota - ErrTypeUnmarshal -) - -func (ty GetSafeErrorType) Format(msg string) (res string) { - switch ty { - case ErrTypeEmptyValue: - res = fmt.Sprintf("Empty Value found") - case ErrTypeUnmarshal: - res = fmt.Sprintf("Error while unmarshal") - default: - panic("Unknown error type") - } - - if msg != "" { - res = fmt.Sprintf("%s: %s", res, msg) - } - - return -} - -type GetSafeError struct { - ty GetSafeErrorType - inner error -} - -var _ error = (*GetSafeError)(nil) // TODO: sdk.Error - -func (err *GetSafeError) Error() string { - if err.inner == nil { - return err.ty.Format("") - } - return err.ty.Format(err.inner.Error()) -} - -func ErrEmptyValue() *GetSafeError { - return &GetSafeError{ - ty: ErrTypeEmptyValue, - } -} - -func ErrUnmarshal(err error) *GetSafeError { - return &GetSafeError{ - ty: ErrTypeUnmarshal, - inner: err, - } -} From 600445aabe9c5e555eb2794c7c63cedd63f44407 Mon Sep 17 00:00:00 2001 From: mossid Date: Fri, 26 Jul 2019 01:06:51 +0900 Subject: [PATCH 054/166] add comments --- store/state/base.go | 2 -- store/state/mapping.go | 4 ---- 2 files changed, 6 deletions(-) diff --git a/store/state/base.go b/store/state/base.go index 19d85ffb16f4..7049732feaf8 100644 --- a/store/state/base.go +++ b/store/state/base.go @@ -1,8 +1,6 @@ package state import ( - // "github.com/tendermint/tendermint/crypto/merkle" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" diff --git a/store/state/mapping.go b/store/state/mapping.go index 91565ea2c2af..9db242e867de 100644 --- a/store/state/mapping.go +++ b/store/state/mapping.go @@ -12,10 +12,6 @@ func NewMapping(base Base, prefix []byte) Mapping { } } -func (m Mapping) store(ctx Context) KVStore { - return m.base.store(ctx) -} - // Value() returns the Value corresponding to the provided key func (m Mapping) Value(key []byte) Value { return NewValue(m.base, key) From 98720bbe37aa878c1e75b1c6192b75e3fdc9f2ed Mon Sep 17 00:00:00 2001 From: Joon Date: Fri, 26 Jul 2019 01:27:40 +0900 Subject: [PATCH 055/166] Apply suggestions from code review Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> --- x/ibc/23-commitment/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/ibc/23-commitment/README.md b/x/ibc/23-commitment/README.md index 759128b0c6de..256e77f0b95d 100644 --- a/x/ibc/23-commitment/README.md +++ b/x/ibc/23-commitment/README.md @@ -11,14 +11,14 @@ type verifyMembership = (root: CommitmentRoot, proof: CommitmentProof, key: Key, type verifyNonMembership = (root: CommitmentRoot, proof: CommitmentProof, key: Key) => bool ``` -## Impl +## Implementation ### types.go `type Proof` implements `spec: type CommitmentProof`. CommitmentProof is an arbitrary object which can be used as an argument for `spec: verifyMembership` / `spec: verifyNonMembership`, constructed with `spec: createMembershipProof` / `spec: createNonMembershipProof`. The implementation type `Proof` defines `spec: verify(Non)Membership` as its method -`Verify(Root, []byte) error`, which takes the commitment root and the value bytes argument. The method acts as +`Verify(Root, []byte) error`, which takes the commitment root and the value bytes as arguments. The method acts as `spec: verifyMembership` when the value bytes is not nil, and `spec: verifyNonMembership` if it is nil. `type Root` implements `spec: type CommitmentRoot`. From cdee396a4152c7912cf80a6a870de1f7864eda48 Mon Sep 17 00:00:00 2001 From: mossid Date: Fri, 26 Jul 2019 01:35:03 +0900 Subject: [PATCH 056/166] add comments in progress --- x/ibc/23-commitment/merkle/merkle.go | 6 ++---- x/ibc/23-commitment/merkle/merkle_test.go | 2 ++ x/ibc/23-commitment/merkle/utils.go | 6 +++++- x/ibc/23-commitment/utils.go | 8 ++++++++ x/ibc/23-commitment/value.go | 7 ------- 5 files changed, 17 insertions(+), 12 deletions(-) create mode 100644 x/ibc/23-commitment/utils.go diff --git a/x/ibc/23-commitment/merkle/merkle.go b/x/ibc/23-commitment/merkle/merkle.go index 9fb11c7f38cb..a4a00a47db7b 100644 --- a/x/ibc/23-commitment/merkle/merkle.go +++ b/x/ibc/23-commitment/merkle/merkle.go @@ -21,9 +21,7 @@ type Root struct { } func NewRoot(hash []byte) Root { - return Root{ - Hash: hash, - } + return Root{hash} } func (Root) CommitmentKind() string { @@ -80,7 +78,7 @@ func (proof Proof) Verify(croot commitment.Root, cpath commitment.Path, value [] } keypath = keypath.AppendKey(append(path.KeyPrefix, proof.Key...), merkle.KeyEncodingHex) - // Hard coded for now + // TODO: Hard coded for now, proof runtime should be extensible for other proof types runtime := rootmulti.DefaultProofRuntime() if value != nil { diff --git a/x/ibc/23-commitment/merkle/merkle_test.go b/x/ibc/23-commitment/merkle/merkle_test.go index f93f64943609..e17c80ddf74f 100644 --- a/x/ibc/23-commitment/merkle/merkle_test.go +++ b/x/ibc/23-commitment/merkle/merkle_test.go @@ -36,6 +36,8 @@ func commit(cms types.CommitMultiStore) Root { return NewRoot(cid.Hash) } +// TestStore tests Merkle proof on the commitment.Store +// Sets/upates key-value pairs and prove with the query result proofs func TestStore(t *testing.T) { k, ctx, cms, _ := defaultComponents() kvstore := ctx.KVStore(k) diff --git a/x/ibc/23-commitment/merkle/utils.go b/x/ibc/23-commitment/merkle/utils.go index d2eabdaed99d..801651f09aa6 100644 --- a/x/ibc/23-commitment/merkle/utils.go +++ b/x/ibc/23-commitment/merkle/utils.go @@ -19,7 +19,11 @@ func (path Path) RequestQuery(key []byte) abci.RequestQuery { } func (path Path) Query(cms types.CommitMultiStore, key []byte) (uint32, []byte, Proof) { - qres := cms.(types.Queryable).Query(path.RequestQuery(key)) + queryable, ok := cms.(types.Queryable) + if !ok { + panic("CommitMultiStore not queryable") + } + qres := queryable.Query(path.RequestQuery(key)) return qres.Code, qres.Value, Proof{Key: key, Proof: qres.Proof} } diff --git a/x/ibc/23-commitment/utils.go b/x/ibc/23-commitment/utils.go new file mode 100644 index 000000000000..e49dc15319a0 --- /dev/null +++ b/x/ibc/23-commitment/utils.go @@ -0,0 +1,8 @@ +package commitment + +func join(a, b []byte) (res []byte) { + res = make([]byte, len(a)+len(b)) + copy(res, a) + copy(res[len(a):], b) + return +} diff --git a/x/ibc/23-commitment/value.go b/x/ibc/23-commitment/value.go index 70b8502a44e6..87d41251d111 100644 --- a/x/ibc/23-commitment/value.go +++ b/x/ibc/23-commitment/value.go @@ -21,13 +21,6 @@ func (base Base) Store(ctx sdk.Context) Store { return NewPrefix(GetStore(ctx), base.prefix) } -func join(a, b []byte) (res []byte) { - res = make([]byte, len(a)+len(b)) - copy(res, a) - copy(res[len(a):], b) - return -} - func (base Base) Prefix(prefix []byte) Base { return Base{ cdc: base.cdc, From 8d9e77904cbabf78ed1e05e2e2e7e9a5870121b4 Mon Sep 17 00:00:00 2001 From: mossid Date: Fri, 26 Jul 2019 20:07:46 +0900 Subject: [PATCH 057/166] add comments --- store/state/boolean.go | 6 ++++++ store/state/enum.go | 6 ++++++ store/state/indexer.go | 9 +++++++++ store/state/integer.go | 11 +++++++---- store/state/mapping.go | 11 ++++++++++- store/state/string.go | 6 ++++++ store/state/value.go | 2 +- 7 files changed, 45 insertions(+), 6 deletions(-) diff --git a/store/state/boolean.go b/store/state/boolean.go index dab4d5bbf67e..efc142dd1a1d 100644 --- a/store/state/boolean.go +++ b/store/state/boolean.go @@ -6,20 +6,26 @@ type Boolean struct { Value } +// NewBoolean() wraps the argument Value as Boolean func NewBoolean(v Value) Boolean { return Boolean{v} } +// Get() unmarshales and returns the stored boolean value if it exists. +// It will panic if the value exists but is not boolean type. func (v Boolean) Get(ctx Context) (res bool) { v.Value.Get(ctx, &res) return } +// GetSafe() unmarshales and returns the stored boolean value. +// It will return an error if the value does not exist or not boolean. func (v Boolean) GetSafe(ctx Context) (res bool, err error) { err = v.Value.GetSafe(ctx, &res) return } +// Set() marshales and sets the boolean argument to the state. func (v Boolean) Set(ctx Context, value bool) { v.Value.Set(ctx, value) } diff --git a/store/state/enum.go b/store/state/enum.go index 57fbdc9f3e92..617af5b3e446 100644 --- a/store/state/enum.go +++ b/store/state/enum.go @@ -6,20 +6,26 @@ type Enum struct { Value } +// NewEnum() wraps the argument value as Enum func NewEnum(v Value) Enum { return Enum{v} } +// Get() unmarshales and returns the stored byte value if it exists. +// It will panic if the value exists but is not byte type. func (v Enum) Get(ctx Context) (res byte) { v.Value.Get(ctx, &res) return } +// GetSafe() unmarshales and returns the stored byte value. +// It will returns an error if the value does not exists or not byte. func (v Enum) GetSafe(ctx Context) (res byte, err error) { err = v.Value.GetSafe(ctx, &res) return } +// Set() marshales and sets the byte argument to the state. func (v Enum) Set(ctx Context, value byte) { v.Value.Set(ctx, value) } diff --git a/store/state/indexer.go b/store/state/indexer.go index 72be902b3ddc..d60eba60d5db 100644 --- a/store/state/indexer.go +++ b/store/state/indexer.go @@ -49,30 +49,39 @@ func EncodeInt(index uint64, enc IntEncoding) (res []byte) { } } +// Value() returns the Value corresponding to the provided index func (ix Indexer) Value(index uint64) Value { return ix.m.Value(EncodeInt(index, ix.enc)) } +// Get() unmarshales and sets the stored value to the pointer if it exists. +// It will panic if the value exists but not unmarshalable. func (ix Indexer) Get(ctx Context, index uint64, ptr interface{}) { ix.Value(index).Get(ctx, ptr) } +// GetSafe() unmarshales and sets the stored value to the pointer. +// It will return an error if the value does not exist or unmarshalable. func (ix Indexer) GetSafe(ctx Context, index uint64, ptr interface{}) error { return ix.Value(index).GetSafe(ctx, ptr) } +// Set() marshales and sets the argument to the state. func (ix Indexer) Set(ctx Context, index uint64, o interface{}) { ix.Value(index).Set(ctx, o) } +// Has() returns true if the stored value is not nil func (ix Indexer) Has(ctx Context, index uint64) bool { return ix.Value(index).Exists(ctx) } +// Delete() delets the stored value. func (ix Indexer) Delete(ctx Context, index uint64) { ix.Value(index).Delete(ctx) } +// Prefix() returns a new Indexer with the updated prefix func (ix Indexer) Prefix(prefix []byte) Indexer { return Indexer{ m: ix.m.Prefix(prefix), diff --git a/store/state/integer.go b/store/state/integer.go index 0ca86572aba2..ec3dcb21ed9f 100644 --- a/store/state/integer.go +++ b/store/state/integer.go @@ -4,27 +4,30 @@ package state // Except for the type checking, it does not alter the behaviour. type Integer struct { Value - - enc IntEncoding } -func NewInteger(v Value, enc IntEncoding) Integer { +// NewInteger() wraps the argument value as Integer +func NewInteger(v Value) Integer { return Integer{ Value: v, - enc: enc, } } +// Get() unmarshales and returns the stored uint64 value if it exists. +// If will panic if the value exists but is not uint64 type. func (v Integer) Get(ctx Context) (res uint64) { v.Value.Get(ctx, &res) return res } +// GetSafe() unmarshales and returns the stored uint64 value. +// It will return an error if the value does not exist or not uint64. func (v Integer) GetSafe(ctx Context) (res uint64, err error) { err = v.Value.GetSafe(ctx, &res) return } +// Set() marshales and sets the uint64 argument to the state. func (v Integer) Set(ctx Context, value uint64) { v.Value.Set(ctx, value) } diff --git a/store/state/mapping.go b/store/state/mapping.go index 9db242e867de..46d578b50613 100644 --- a/store/state/mapping.go +++ b/store/state/mapping.go @@ -1,6 +1,7 @@ package state -// Mapping is key []byte -> value []byte mapping using a base(possibly prefixed) +// Mapping is key []byte -> value []byte mapping using a base(possibly prefixed). +// All store accessing operations are redirected to the Value corresponding to the key argument type Mapping struct { base Base } @@ -17,14 +18,19 @@ func (m Mapping) Value(key []byte) Value { return NewValue(m.base, key) } +// Get() unmarshales and sets the stored value to the pointer if it exists. +// It will panic if the value exists but not unmarshalable. func (m Mapping) Get(ctx Context, key []byte, ptr interface{}) { m.Value(key).Get(ctx, ptr) } +// GetSafe() unmarshales and sets the stored value to the pointer. +// It will return an error if the value does not exists or unmarshalable. func (m Mapping) GetSafe(ctx Context, key []byte, ptr interface{}) error { return m.Value(key).GetSafe(ctx, ptr) } +// Set() marshales and sets the argument to the state. func (m Mapping) Set(ctx Context, key []byte, o interface{}) { if o == nil { m.Delete(ctx, key) @@ -33,14 +39,17 @@ func (m Mapping) Set(ctx Context, key []byte, o interface{}) { m.Value(key).Set(ctx, o) } +// Has() returns true if the stored value is not nil func (m Mapping) Has(ctx Context, key []byte) bool { return m.Value(key).Exists(ctx) } +// Delete() deletes the stored value. func (m Mapping) Delete(ctx Context, key []byte) { m.Value(key).Delete(ctx) } +// Prefix() returns a new mapping with the updated prefix. func (m Mapping) Prefix(prefix []byte) Mapping { return NewMapping(m.base, prefix) } diff --git a/store/state/string.go b/store/state/string.go index 7198f7869a85..8d7d6f9fdb4f 100644 --- a/store/state/string.go +++ b/store/state/string.go @@ -6,20 +6,26 @@ type String struct { Value } +// NewString() wraps the argument value as String func NewString(v Value) String { return String{v} } +// Get() unmarshales and returns the stored string value if it exists. +// It will panic if the value exists but is not strin type. func (v String) Get(ctx Context) (res string) { v.Value.Get(ctx, &res) return } +// GetSafe() unmarshales and returns the stored string value. +// It will return an error if the value does not exist or not string func (v String) GetSafe(ctx Context) (res string, err error) { err = v.Value.GetSafe(ctx, &res) return } +// Set() marshales and sets the string argument to the state. func (v String) Set(ctx Context, value string) { v.Value.Set(ctx, value) } diff --git a/store/state/value.go b/store/state/value.go index 87bbeec9f805..ae7d572ee40b 100644 --- a/store/state/value.go +++ b/store/state/value.go @@ -57,7 +57,7 @@ func (v Value) GetRaw(ctx Context) []byte { return v.store(ctx).Get(v.key) } -// Set() marshales sets the "o" argument to the state. +// Set() marshales and sets the argument to the state. func (v Value) Set(ctx Context, o interface{}) { v.store(ctx).Set(v.key, v.base.cdc.MustMarshalBinaryBare(o)) } From 92d0fb497c7345027b685846f3df18d49ebaec00 Mon Sep 17 00:00:00 2001 From: mossid Date: Fri, 26 Jul 2019 20:13:12 +0900 Subject: [PATCH 058/166] fix comment --- store/state/mapping.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/store/state/mapping.go b/store/state/mapping.go index 46d578b50613..2943e690d673 100644 --- a/store/state/mapping.go +++ b/store/state/mapping.go @@ -25,12 +25,13 @@ func (m Mapping) Get(ctx Context, key []byte, ptr interface{}) { } // GetSafe() unmarshales and sets the stored value to the pointer. -// It will return an error if the value does not exists or unmarshalable. +// It will return an error if the value does not exist or unmarshalable. func (m Mapping) GetSafe(ctx Context, key []byte, ptr interface{}) error { return m.Value(key).GetSafe(ctx, ptr) } // Set() marshales and sets the argument to the state. +// Calls Delete() if the argument is nil. func (m Mapping) Set(ctx Context, key []byte, o interface{}) { if o == nil { m.Delete(ctx, key) From f70d3bdb8d03c44b313978c706000e30aa6eb0cf Mon Sep 17 00:00:00 2001 From: mossid Date: Sat, 27 Jul 2019 21:06:29 +0900 Subject: [PATCH 059/166] add comments in progress --- x/ibc/23-commitment/merkle/utils.go | 11 +++++++++++ x/ibc/23-commitment/value.go | 17 +++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/x/ibc/23-commitment/merkle/utils.go b/x/ibc/23-commitment/merkle/utils.go index 801651f09aa6..86c6d6d18f3e 100644 --- a/x/ibc/23-commitment/merkle/utils.go +++ b/x/ibc/23-commitment/merkle/utils.go @@ -6,11 +6,22 @@ import ( "github.com/cosmos/cosmos-sdk/store/types" ) +// RequestQuery() constructs the abci.RequestQuery. +// +// RequestQuery.Path is a slash separated key list, ending with "/key" +// +// RequestQuery.Data is the concatanation of path.KeyPrefix and key argument +// +// RequestQuery.Prove is set to true func (path Path) RequestQuery(key []byte) abci.RequestQuery { pathstr := "" for _, inter := range path.KeyPath { + // The Queryable() stores uses slash-separated keypath format for querying pathstr = pathstr + "/" + string(inter) } + // Suffixing pathstr with "/key". + // iavl.Store.Query() switches over the last path element, + // and performs key-value query only if it is "/key" pathstr = pathstr + "/key" data := append(path.KeyPrefix, key...) diff --git a/x/ibc/23-commitment/value.go b/x/ibc/23-commitment/value.go index 87d41251d111..6e361fa75188 100644 --- a/x/ibc/23-commitment/value.go +++ b/x/ibc/23-commitment/value.go @@ -6,6 +6,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +// Base is a proof store accessor, consists of Codec and prefix. +// The base uses the commitment store which is expected to be filled with the proofs. type Base struct { cdc *codec.Codec prefix []byte @@ -38,6 +40,8 @@ func NewMapping(base Base, prefix []byte) Mapping { } } +// Value is for proving commitment proof on a speicifc key-value point in the other state +// using the already initialized commitment store. type Value struct { base Base key []byte @@ -47,34 +51,39 @@ func NewValue(base Base, key []byte) Value { return Value{base, key} } +// Is() proves the proof with the Value's key and the provided value. func (v Value) Is(ctx sdk.Context, value interface{}) bool { return v.base.Store(ctx).Prove(v.key, v.base.cdc.MustMarshalBinaryBare(value)) } +// IsRaw() proves the proof with the Value's key and the provided raw value bytes. func (v Value) IsRaw(ctx sdk.Context, value []byte) bool { return v.base.Store(ctx).Prove(v.key, value) } +// Enum is a byte typed wrapper for Value. +// Except for the type checking, it does not alter the behaviour. type Enum struct { Value } +// NewEnum() wraps the argument Value as Enum func NewEnum(v Value) Enum { return Enum{v} } +// Is() proves the proof with the Enum's key and the provided value func (v Enum) Is(ctx sdk.Context, value byte) bool { return v.Value.IsRaw(ctx, []byte{value}) } +// Integer is a uint64 types wrapper for Value. type Integer struct { Value - - enc state.IntEncoding } -func NewInteger(v Value, enc state.IntEncoding) Integer { - return Integer{v, enc} +func NewInteger(v Value) Integer { + return Integer{v} } func (v Integer) Is(ctx sdk.Context, value uint64) bool { From 5d913b7bbdb1e3b013d4d708dd5a7ddef31f4304 Mon Sep 17 00:00:00 2001 From: mossid Date: Mon, 29 Jul 2019 16:07:45 +0900 Subject: [PATCH 060/166] recover IntEncoding scheme for integer --- store/state/indexer.go | 27 +++++++++++++++++++++++++-- store/state/integer.go | 32 +++++++++++++++++++++++++------- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/store/state/indexer.go b/store/state/indexer.go index d60eba60d5db..fab6db992858 100644 --- a/store/state/indexer.go +++ b/store/state/indexer.go @@ -3,13 +3,22 @@ package state import ( "encoding/binary" "fmt" + "strconv" ) +// IntEncoding is an enum type defining the integer serialization scheme. +// All encoding schemes preserves order. type IntEncoding byte const ( + // Dec is human readable decimal encoding scheme. + // Has fixed length of 20 bytes. Dec IntEncoding = iota + // Hex is human readable hexadecimal encoding scheme + // Has fixed length of 16 bytes. Hex + // Bin is machine readable big endian encoding scheme + // Has fixed length of 8 bytes Bin ) @@ -30,7 +39,7 @@ func NewIndexer(base Base, prefix []byte, enc IntEncoding) Indexer { } } -// Identical length independent from the index, ensure ordering +// Order preserving integer encoding function. func EncodeInt(index uint64, enc IntEncoding) (res []byte) { switch enc { case Dec: @@ -38,7 +47,7 @@ func EncodeInt(index uint64, enc IntEncoding) (res []byte) { return []byte(fmt.Sprintf("%020d", index)) case Hex: // Returns hexadecimal number index, 20-length 0 padded - return []byte(fmt.Sprintf("%020x", index)) + return []byte(fmt.Sprintf("%016x", index)) case Bin: // Returns bigendian encoded number index, 8-length res = make([]byte, 8) @@ -49,6 +58,20 @@ func EncodeInt(index uint64, enc IntEncoding) (res []byte) { } } +// Integer decoding function, inversion of EncodeInt +func DecodeInt(bz []byte, enc IntEncoding) (res uint64, err error) { + switch enc { + case Dec: + return strconv.ParseUint(string(bz), 10, 64) + case Hex: + return strconv.ParseUint(string(bz), 16, 64) + case Bin: + return binary.BigEndian.Uint64(bz), nil + default: + panic("invalid IntEncoding") + } +} + // Value() returns the Value corresponding to the provided index func (ix Indexer) Value(index uint64) Value { return ix.m.Value(EncodeInt(index, ix.enc)) diff --git a/store/state/integer.go b/store/state/integer.go index ec3dcb21ed9f..6c2695b8fd86 100644 --- a/store/state/integer.go +++ b/store/state/integer.go @@ -1,35 +1,53 @@ package state // Integer is a uint64 types wrapper for Value. -// Except for the type checking, it does not alter the behaviour. +// The serialization follows the @IntEncoding@ format provided to the NewInteger. type Integer struct { Value + + enc IntEncoding } // NewInteger() wraps the argument value as Integer -func NewInteger(v Value) Integer { +func NewInteger(v Value, enc IntEncoding) Integer { return Integer{ Value: v, + + enc: enc, } } // Get() unmarshales and returns the stored uint64 value if it exists. -// If will panic if the value exists but is not uint64 type. -func (v Integer) Get(ctx Context) (res uint64) { - v.Value.Get(ctx, &res) +// If will panic if the value exists but not decodable. +func (v Integer) Get(ctx Context) uint64 { + bz := v.Value.GetRaw(ctx) + if bz == nil { + return 0 + } + res, err := DecodeInt(bz, v.enc) + if err != nil { + panic(err) + } return res } // GetSafe() unmarshales and returns the stored uint64 value. // It will return an error if the value does not exist or not uint64. func (v Integer) GetSafe(ctx Context) (res uint64, err error) { - err = v.Value.GetSafe(ctx, &res) + bz := v.Value.GetRaw(ctx) + if bz == nil { + return 0, ErrEmptyValue() + } + res, err = DecodeInt(bz, v.enc) + if err != nil { + err = ErrUnmarshal(err) + } return } // Set() marshales and sets the uint64 argument to the state. func (v Integer) Set(ctx Context, value uint64) { - v.Value.Set(ctx, value) + v.Value.SetRaw(ctx, EncodeInt(value, v.enc)) } // Incr() increments the stored value, and returns the updated value. From 699b0ba157e24f0775746c530645781d4e845521 Mon Sep 17 00:00:00 2001 From: mossid Date: Mon, 29 Jul 2019 22:14:51 +0900 Subject: [PATCH 061/166] add uint tests, don't use codec in custom types --- store/state/base.go | 72 --------- store/state/boolean.go | 7 +- store/state/enum.go | 17 +- store/state/indexer.go | 4 +- store/state/integer.go | 9 +- store/state/mapping.go | 48 +++++- store/state/mapping_test.go | 123 +++++++++++++++ store/state/string.go | 17 +- store/state/value.go | 26 ++-- store/state/value_test.go | 300 ++++++++++++++++++++++++++++++++++++ 10 files changed, 505 insertions(+), 118 deletions(-) delete mode 100644 store/state/base.go create mode 100644 store/state/mapping_test.go create mode 100644 store/state/value_test.go diff --git a/store/state/base.go b/store/state/base.go deleted file mode 100644 index 7049732feaf8..000000000000 --- a/store/state/base.go +++ /dev/null @@ -1,72 +0,0 @@ -package state - -import ( - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/prefix" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Base is a state accessor base layer, consists of Codec, StoreKey, and prefix. -// StoreKey is used to get the KVStore, cdc is used to marshal/unmarshal the interfaces, -// and the prefix is prefixed to the key. -// -// Base has practically the same capability with the storeKey. -// It should not be passed to an untrusted actor. -type Base struct { - storeKey sdk.StoreKey - cdc *codec.Codec - prefix []byte -} - -// NewBase() is the constructor for Base() -func NewBase(cdc *codec.Codec, key sdk.StoreKey, rootkey []byte) Base { - return Base{ - storeKey: key, - cdc: cdc, - prefix: rootkey, - } -} - -func (base Base) store(ctx Context) KVStore { - return prefix.NewStore(ctx.KVStore(base.storeKey), base.prefix) -} - -func join(a, b []byte) (res []byte) { - res = make([]byte, len(a)+len(b)) - copy(res, a) - copy(res[len(a):], b) - return -} - -// Prefix() returns a copy of the Base with the updated prefix. -func (base Base) Prefix(prefix []byte) (res Base) { - res = Base{ - storeKey: base.storeKey, - cdc: base.cdc, - prefix: join(base.prefix, prefix), - } - return -} - -// Cdc() returns the codec of the base. It is safe to expose the codec. -func (base Base) Cdc() *codec.Codec { - return base.cdc -} - -func (base Base) key(key []byte) []byte { - return join(base.prefix, key) -} - -// StoreName() returns the name of the storeKey. It is safe to expose the store name. -// Used by the CLI side query operations. -func (base Base) StoreName() string { - return base.storeKey.Name() -} - -// PrefixBytes() returns the prefix bytes. It is safe to expsoe the prefix bytes. -// Used by the CLI side query operations. -func (base Base) PrefixBytes() (res []byte) { - res = make([]byte, len(base.prefix)) - copy(res, base.prefix) - return -} diff --git a/store/state/boolean.go b/store/state/boolean.go index efc142dd1a1d..5d582b6869d7 100644 --- a/store/state/boolean.go +++ b/store/state/boolean.go @@ -1,13 +1,14 @@ package state // Boolean is a bool typed wrapper for Value. -// Except for the type checking, it does not alter the behaviour. +// +// false <-> []byte{0x00} +// true <-> []byte{0x01} type Boolean struct { Value } -// NewBoolean() wraps the argument Value as Boolean -func NewBoolean(v Value) Boolean { +func (v Value) Boolean() Boolean { return Boolean{v} } diff --git a/store/state/enum.go b/store/state/enum.go index 617af5b3e446..67f18a4a2584 100644 --- a/store/state/enum.go +++ b/store/state/enum.go @@ -1,33 +1,34 @@ package state // Enum is a byte typed wrapper for Value. -// Except for the type checking, it does not alter the behaviour. +// x <-> []byte{x} type Enum struct { Value } -// NewEnum() wraps the argument value as Enum -func NewEnum(v Value) Enum { +func (v Value) Enum() Enum { return Enum{v} } // Get() unmarshales and returns the stored byte value if it exists. // It will panic if the value exists but is not byte type. func (v Enum) Get(ctx Context) (res byte) { - v.Value.Get(ctx, &res) - return + return v.Value.GetRaw(ctx)[0] } // GetSafe() unmarshales and returns the stored byte value. // It will returns an error if the value does not exists or not byte. func (v Enum) GetSafe(ctx Context) (res byte, err error) { - err = v.Value.GetSafe(ctx, &res) - return + bz := v.Value.GetRaw(ctx) + if bz == nil { + return res, ErrEmptyValue() + } + return bz[0], nil // TODO: check length } // Set() marshales and sets the byte argument to the state. func (v Enum) Set(ctx Context, value byte) { - v.Value.Set(ctx, value) + v.Value.SetRaw(ctx, []byte{value}) } // Incr() increments the stored value, and returns the updated value. diff --git a/store/state/indexer.go b/store/state/indexer.go index fab6db992858..6b837c2021a3 100644 --- a/store/state/indexer.go +++ b/store/state/indexer.go @@ -32,9 +32,9 @@ type Indexer struct { } // NewIndexer() constructs the Indexer with a predetermined prefix and IntEncoding -func NewIndexer(base Base, prefix []byte, enc IntEncoding) Indexer { +func NewIndexer(m Mapping, enc IntEncoding) Indexer { return Indexer{ - m: NewMapping(base, prefix), + m: m, enc: enc, } } diff --git a/store/state/integer.go b/store/state/integer.go index 6c2695b8fd86..187fd718d55e 100644 --- a/store/state/integer.go +++ b/store/state/integer.go @@ -8,13 +8,8 @@ type Integer struct { enc IntEncoding } -// NewInteger() wraps the argument value as Integer -func NewInteger(v Value, enc IntEncoding) Integer { - return Integer{ - Value: v, - - enc: enc, - } +func (v Value) Integer(enc IntEncoding) Integer { + return Integer{v, enc} } // Get() unmarshales and returns the stored uint64 value if it exists. diff --git a/store/state/mapping.go b/store/state/mapping.go index 2943e690d673..95a96974416b 100644 --- a/store/state/mapping.go +++ b/store/state/mapping.go @@ -1,21 +1,30 @@ package state +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + // Mapping is key []byte -> value []byte mapping using a base(possibly prefixed). // All store accessing operations are redirected to the Value corresponding to the key argument type Mapping struct { - base Base + storeKey sdk.StoreKey + cdc *codec.Codec + prefix []byte } // NewMapping() constructs a Mapping with a provided prefix -func NewMapping(base Base, prefix []byte) Mapping { +func NewMapping(storeKey sdk.StoreKey, cdc *codec.Codec, prefix []byte) Mapping { return Mapping{ - base: base.Prefix(prefix), + storeKey: storeKey, + cdc: cdc, + prefix: prefix, } } // Value() returns the Value corresponding to the provided key func (m Mapping) Value(key []byte) Value { - return NewValue(m.base, key) + return NewValue(m, key) } // Get() unmarshales and sets the stored value to the pointer if it exists. @@ -50,7 +59,36 @@ func (m Mapping) Delete(ctx Context, key []byte) { m.Value(key).Delete(ctx) } +func (m Mapping) Cdc() *codec.Codec { + return m.cdc +} + +func (m Mapping) StoreName() string { + return m.storeKey.Name() +} + +func (m Mapping) PrefixBytes() (res []byte) { + res = make([]byte, len(m.prefix)) + copy(res, m.prefix) + return +} + +func (m Mapping) KeyBytes(key []byte) (res []byte) { + return join(m.prefix, key) +} + +func join(a, b []byte) (res []byte) { + res = make([]byte, len(a)+len(b)) + copy(res, a) + copy(res[len(a):], b) + return +} + // Prefix() returns a new mapping with the updated prefix. func (m Mapping) Prefix(prefix []byte) Mapping { - return NewMapping(m.base, prefix) + return Mapping{ + storeKey: m.storeKey, + cdc: m.cdc, + prefix: join(m.prefix, prefix), + } } diff --git a/store/state/mapping_test.go b/store/state/mapping_test.go new file mode 100644 index 000000000000..0a29fead8a6e --- /dev/null +++ b/store/state/mapping_test.go @@ -0,0 +1,123 @@ +package state + +import ( + "math/rand" + "reflect" + "testing" + + "github.com/stretchr/testify/require" +) + +type mapping interface { + Get(Context, interface{}, interface{}) + GetSafe(Context, interface{}, interface{}) error + Set(Context, interface{}, interface{}) + Has(Context, interface{}) bool + Delete(Context, interface{}) + RandomKey() interface{} +} + +type mappingT struct { + Mapping +} + +var _ mapping = mappingT{} + +func newMapping() mappingT { + return mappingT{NewMapping(testkey, testcdc, nil)} +} + +func (m mappingT) Get(ctx Context, key interface{}, ptr interface{}) { + m.Mapping.Get(ctx, []byte(key.(string)), ptr) +} + +func (m mappingT) GetSafe(ctx Context, key interface{}, ptr interface{}) error { + return m.Mapping.GetSafe(ctx, []byte(key.(string)), ptr) +} + +func (m mappingT) Set(ctx Context, key interface{}, o interface{}) { + m.Mapping.Set(ctx, []byte(key.(string)), o) +} + +func (m mappingT) Has(ctx Context, key interface{}) bool { + return m.Mapping.Has(ctx, []byte(key.(string))) +} + +func (m mappingT) Delete(ctx Context, key interface{}) { + m.Mapping.Delete(ctx, []byte(key.(string))) +} + +func (m mappingT) RandomKey() interface{} { + bz := make([]byte, 64) + rand.Read(bz) + return string(bz) +} + +type indexerT struct { + Indexer +} + +var _ mapping = indexerT{} + +func newIndexer(enc IntEncoding) indexerT { + return indexerT{NewIndexer(NewMapping(testkey, testcdc, nil), enc)} +} + +func (m indexerT) Get(ctx Context, key interface{}, ptr interface{}) { + m.Indexer.Get(ctx, key.(uint64), ptr) +} + +func (m indexerT) GetSafe(ctx Context, key interface{}, ptr interface{}) error { + return m.Indexer.GetSafe(ctx, key.(uint64), ptr) +} + +func (m indexerT) Set(ctx Context, key interface{}, o interface{}) { + m.Indexer.Set(ctx, key.(uint64), o) +} + +func (m indexerT) Has(ctx Context, key interface{}) bool { + return m.Indexer.Has(ctx, key.(uint64)) +} + +func (m indexerT) Delete(ctx Context, key interface{}) { + m.Indexer.Delete(ctx, key.(uint64)) +} + +func (m indexerT) RandomKey() interface{} { + return rand.Uint64() +} + +func TestMapping(t *testing.T) { + ctx := defaultComponents() + table := []mapping{newMapping(), newIndexer(Dec), newIndexer(Hex), newIndexer(Bin)} + + for _, m := range table { + exp := make(map[interface{}]uint64) + for n := 0; n < 10e4; n++ { + k, v := m.RandomKey(), rand.Uint64() + require.False(t, m.Has(ctx, k)) + exp[k] = v + m.Set(ctx, k, v) + } + + for k, v := range exp { + ptr := new(uint64) + m.Get(ctx, k, ptr) + require.Equal(t, v, indirect(ptr)) + + ptr = new(uint64) + err := m.GetSafe(ctx, k, ptr) + require.NoError(t, err) + require.Equal(t, v, indirect(ptr)) + + require.True(t, m.Has(ctx, k)) + + m.Delete(ctx, k) + require.False(t, m.Has(ctx, k)) + ptr = new(uint64) + err = m.GetSafe(ctx, k, ptr) + require.Error(t, err) + require.Equal(t, reflect.Zero(reflect.TypeOf(ptr).Elem()).Interface(), indirect(ptr)) + } + } +} diff --git a/store/state/string.go b/store/state/string.go index 8d7d6f9fdb4f..72c85340d01f 100644 --- a/store/state/string.go +++ b/store/state/string.go @@ -1,31 +1,32 @@ package state // String is a string types wrapper for Value. -// Except for the type checking, it does not alter the behaviour. +// x <-> []byte(x) type String struct { Value } -// NewString() wraps the argument value as String -func NewString(v Value) String { +func (v Value) String() String { return String{v} } // Get() unmarshales and returns the stored string value if it exists. // It will panic if the value exists but is not strin type. func (v String) Get(ctx Context) (res string) { - v.Value.Get(ctx, &res) - return + return string(v.Value.GetRaw(ctx)) } // GetSafe() unmarshales and returns the stored string value. // It will return an error if the value does not exist or not string func (v String) GetSafe(ctx Context) (res string, err error) { - err = v.Value.GetSafe(ctx, &res) - return + bz := v.Value.GetRaw(ctx) + if bz == nil { + return res, ErrEmptyValue() + } + return string(bz), nil } // Set() marshales and sets the string argument to the state. func (v String) Set(ctx Context, value string) { - v.Value.Set(ctx, value) + v.Value.SetRaw(ctx, []byte(value)) } diff --git a/store/state/value.go b/store/state/value.go index ae7d572ee40b..7878ae2f191e 100644 --- a/store/state/value.go +++ b/store/state/value.go @@ -8,25 +8,25 @@ import ( // Value consists of Base and key []byte. // An actor holding a Value has a full access right on that state point. type Value struct { - base Base - key []byte + m Mapping + key []byte } // NewValue() constructs a Value -func NewValue(base Base, key []byte) Value { +func NewValue(m Mapping, key []byte) Value { return Value{ - base: base, - key: key, + m: m, + key: key, } } func (v Value) store(ctx Context) KVStore { - return v.base.store(ctx) + return ctx.KVStore(v.m.storeKey) } // Cdc() returns the codec that the value is using to marshal/unmarshal func (v Value) Cdc() *codec.Codec { - return v.base.Cdc() + return v.m.Cdc() } // Get() unmarshales and sets the stored value to the pointer if it exists. @@ -34,7 +34,7 @@ func (v Value) Cdc() *codec.Codec { func (v Value) Get(ctx Context, ptr interface{}) { bz := v.store(ctx).Get(v.key) if bz != nil { - v.base.cdc.MustUnmarshalBinaryBare(bz, ptr) + v.m.cdc.MustUnmarshalBinaryBare(bz, ptr) } } @@ -45,7 +45,7 @@ func (v Value) GetSafe(ctx Context, ptr interface{}) error { if bz == nil { return ErrEmptyValue() } - err := v.base.cdc.UnmarshalBinaryBare(bz, ptr) + err := v.m.cdc.UnmarshalBinaryBare(bz, ptr) if err != nil { return ErrUnmarshal(err) } @@ -59,7 +59,7 @@ func (v Value) GetRaw(ctx Context) []byte { // Set() marshales and sets the argument to the state. func (v Value) Set(ctx Context, o interface{}) { - v.store(ctx).Set(v.key, v.base.cdc.MustMarshalBinaryBare(o)) + v.store(ctx).Set(v.key, v.m.cdc.MustMarshalBinaryBare(o)) } // SetRaw() sets the raw bytes to the state. @@ -79,7 +79,7 @@ func (v Value) Delete(ctx Context) { v.store(ctx).Delete(v.key) } -// Key() returns the prefixed key that the Value is providing to the KVStore -func (v Value) Key() []byte { - return v.base.key(v.key) +// KeyBytes() returns the prefixed key that the Value is providing to the KVStore +func (v Value) KeyBytes() []byte { + return v.m.KeyBytes(v.key) } diff --git a/store/state/value_test.go b/store/state/value_test.go new file mode 100644 index 000000000000..37e42d382d95 --- /dev/null +++ b/store/state/value_test.go @@ -0,0 +1,300 @@ +package state + +import ( + "crypto/rand" + "reflect" + "testing" + + "github.com/stretchr/testify/require" + + abci "github.com/tendermint/tendermint/abci/types" + dbm "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/rootmulti" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var testcdc = codec.New() +var testkey = sdk.NewKVStoreKey("test") + +func init() { + // register +} + +func key() (res []byte) { + res = make([]byte, 64) + rand.Read(res) + return +} + +type value interface { + Get(Context, interface{}) + GetSafe(Context, interface{}) error + GetRaw(Context) []byte + Set(Context, interface{}) + SetRaw(Context, []byte) + Exists(Context) bool + Delete(Context) + Marshal(interface{}) []byte + Unmarshal([]byte, interface{}) +} + +type typeValue interface { + value + Proto() interface{} +} + +type valueT struct { + Value +} + +var _ value = valueT{} + +func (v valueT) Marshal(o interface{}) []byte { + return v.m.cdc.MustMarshalBinaryBare(o) +} + +func (v valueT) Unmarshal(bz []byte, ptr interface{}) { + v.m.cdc.MustUnmarshalBinaryBare(bz, ptr) +} + +type booleanT struct { + Boolean +} + +var _ typeValue = booleanT{} + +func newBoolean() booleanT { + return booleanT{NewMapping(testkey, testcdc, nil).Value(key()).Boolean()} +} + +func (booleanT) Proto() interface{} { + return new(bool) +} + +func (v booleanT) Get(ctx Context, ptr interface{}) { + reflect.ValueOf(ptr).Elem().SetBool(v.Boolean.Get(ctx)) +} + +func (v booleanT) GetSafe(ctx Context, ptr interface{}) error { + res, err := v.Boolean.GetSafe(ctx) + if err != nil { + return err + } + reflect.ValueOf(ptr).Elem().SetBool(res) + return nil +} + +func (v booleanT) Set(ctx Context, o interface{}) { + v.Boolean.Set(ctx, o.(bool)) +} + +func (v booleanT) Marshal(o interface{}) []byte { + switch o.(bool) { + case false: + return []byte{0x00} + case true: + return []byte{0x01} + } + panic("invalid boolean type") +} + +func (v booleanT) Unmarshal(bz []byte, ptr interface{}) { + switch bz[0] { + case 0x00: + reflect.ValueOf(ptr).Elem().SetBool(false) + case 0x01: + reflect.ValueOf(ptr).Elem().SetBool(true) + } +} + +type integerT struct { + Integer +} + +var _ typeValue = integerT{} + +func newInteger(enc IntEncoding) integerT { + return integerT{NewMapping(testkey, testcdc, nil).Value(key()).Integer(enc)} +} + +func (integerT) Proto() interface{} { + return new(uint64) +} + +func (v integerT) Get(ctx Context, ptr interface{}) { + reflect.ValueOf(ptr).Elem().SetUint(v.Integer.Get(ctx)) +} + +func (v integerT) GetSafe(ctx Context, ptr interface{}) error { + res, err := v.Integer.GetSafe(ctx) + if err != nil { + return err + } + reflect.ValueOf(ptr).Elem().SetUint(res) + return nil +} + +func (v integerT) Set(ctx Context, o interface{}) { + v.Integer.Set(ctx, o.(uint64)) +} + +func (v integerT) Marshal(o interface{}) []byte { + return EncodeInt(o.(uint64), v.enc) +} + +func (v integerT) Unmarshal(bz []byte, ptr interface{}) { + res, err := DecodeInt(bz, v.enc) + if err != nil { + panic(err) + } + reflect.ValueOf(ptr).Elem().SetUint(res) +} + +type enumT struct { + Enum +} + +var _ typeValue = enumT{} + +func newEnum() enumT { + return enumT{NewMapping(testkey, testcdc, nil).Value(key()).Enum()} +} + +func (enumT) Proto() interface{} { + return new(byte) +} + +func (v enumT) Get(ctx Context, ptr interface{}) { + reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v.Enum.Get(ctx))) +} + +func (v enumT) GetSafe(ctx Context, ptr interface{}) error { + res, err := v.Enum.GetSafe(ctx) + if err != nil { + return err + } + reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(res)) + return nil +} + +func (v enumT) Set(ctx Context, o interface{}) { + v.Enum.Set(ctx, o.(byte)) +} + +func (v enumT) Marshal(o interface{}) []byte { + return []byte{o.(byte)} +} + +func (v enumT) Unmarshal(bz []byte, ptr interface{}) { + reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(bz[0])) +} + +type stringT struct { + String +} + +var _ typeValue = stringT{} + +func newString() stringT { + return stringT{NewMapping(testkey, testcdc, nil).Value(key()).String()} +} + +func (stringT) Proto() interface{} { + return new(string) +} + +func (v stringT) Get(ctx Context, ptr interface{}) { + reflect.ValueOf(ptr).Elem().SetString(v.String.Get(ctx)) +} + +func (v stringT) GetSafe(ctx Context, ptr interface{}) error { + res, err := v.String.GetSafe(ctx) + if err != nil { + return err + } + reflect.ValueOf(ptr).Elem().SetString(res) + return nil +} + +func (v stringT) Set(ctx Context, o interface{}) { + v.String.Set(ctx, o.(string)) +} + +func (v stringT) Marshal(o interface{}) []byte { + return []byte(o.(string)) +} + +func (v stringT) Unmarshal(bz []byte, ptr interface{}) { + reflect.ValueOf(ptr).Elem().SetString(string(bz)) +} + +func defaultComponents() sdk.Context { + db := dbm.NewMemDB() + cms := rootmulti.NewStore(db) + cms.MountStoreWithDB(testkey, sdk.StoreTypeIAVL, db) + cms.LoadLatestVersion() + ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) + return ctx +} + +func indirect(ptr interface{}) interface{} { + return reflect.ValueOf(ptr).Elem().Interface() +} + +func TestTypeValue(t *testing.T) { + ctx := defaultComponents() + + var table = []struct { + ty typeValue + orig interface{} + }{ + {newBoolean(), false}, + {newBoolean(), true}, + {newInteger(Dec), uint64(1024000)}, + {newInteger(Dec), uint64(2048000)}, + {newInteger(Bin), uint64(4096000)}, + {newInteger(Bin), uint64(8192000)}, + {newInteger(Hex), uint64(16384000)}, + {newInteger(Hex), uint64(32768000)}, + {newEnum(), byte(0x00)}, + {newEnum(), byte(0x78)}, + {newEnum(), byte(0xA0)}, + {newString(), "1234567890"}, + {newString(), "asdfghjkl"}, + {newString(), "qwertyuiop"}, + } + + for i, tc := range table { + v := tc.ty + // Exists expected false + require.False(t, v.Exists(ctx)) + + // Simple get-set + v.Set(ctx, tc.orig) + ptr := v.Proto() + v.Get(ctx, ptr) + require.Equal(t, tc.orig, indirect(ptr), "Expected equal on tc %d", i) + ptr = v.Proto() + err := v.GetSafe(ctx, ptr) + require.NoError(t, err) + require.Equal(t, tc.orig, indirect(ptr), "Expected equal on tc %d", i) + + // Raw get + require.Equal(t, v.Marshal(tc.orig), v.GetRaw(ctx), "Expected equal on tc %d", i) + + // Exists expected true + require.True(t, v.Exists(ctx)) + + // After delete + v.Delete(ctx) + require.False(t, v.Exists(ctx)) + ptr = v.Proto() + err = v.GetSafe(ctx, ptr) + require.Error(t, err) + require.Equal(t, reflect.Zero(reflect.TypeOf(ptr).Elem()).Interface(), indirect(ptr)) + require.Nil(t, v.GetRaw(ctx)) + } +} From 9a077eb6ddcb1411de330b5a0eec771a0abfd7bd Mon Sep 17 00:00:00 2001 From: mossid Date: Mon, 29 Jul 2019 22:45:40 +0900 Subject: [PATCH 062/166] finalize merge --- x/ibc/23-commitment/value.go | 53 +++++++++++++++--------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/x/ibc/23-commitment/value.go b/x/ibc/23-commitment/value.go index 6e361fa75188..dd7205385616 100644 --- a/x/ibc/23-commitment/value.go +++ b/x/ibc/23-commitment/value.go @@ -6,59 +6,48 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// Base is a proof store accessor, consists of Codec and prefix. -// The base uses the commitment store which is expected to be filled with the proofs. -type Base struct { +type Mapping struct { cdc *codec.Codec prefix []byte } -func NewBase(cdc *codec.Codec) Base { - return Base{ - cdc: cdc, - } -} - -func (base Base) Store(ctx sdk.Context) Store { - return NewPrefix(GetStore(ctx), base.prefix) -} - -func (base Base) Prefix(prefix []byte) Base { - return Base{ - cdc: base.cdc, - prefix: join(base.prefix, prefix), +func NewMapping(cdc *codec.Codec, prefix []byte) Mapping { + return Mapping{ + cdc: cdc, + prefix: prefix, } } -type Mapping struct { - base Base +func (m Mapping) store(ctx sdk.Context) Store { + return NewPrefix(GetStore(ctx), m.prefix) } -func NewMapping(base Base, prefix []byte) Mapping { +func (m Mapping) Prefix(prefix []byte) Mapping { return Mapping{ - base: base.Prefix(prefix), + cdc: m.cdc, + prefix: join(m.prefix, prefix), } } // Value is for proving commitment proof on a speicifc key-value point in the other state // using the already initialized commitment store. type Value struct { - base Base - key []byte + m Mapping + key []byte } -func NewValue(base Base, key []byte) Value { - return Value{base, key} +func (m Mapping) Value(key []byte) Value { + return Value{m, key} } // Is() proves the proof with the Value's key and the provided value. func (v Value) Is(ctx sdk.Context, value interface{}) bool { - return v.base.Store(ctx).Prove(v.key, v.base.cdc.MustMarshalBinaryBare(value)) + return v.m.store(ctx).Prove(v.key, v.m.cdc.MustMarshalBinaryBare(value)) } // IsRaw() proves the proof with the Value's key and the provided raw value bytes. func (v Value) IsRaw(ctx sdk.Context, value []byte) bool { - return v.base.Store(ctx).Prove(v.key, value) + return v.m.store(ctx).Prove(v.key, value) } // Enum is a byte typed wrapper for Value. @@ -67,8 +56,8 @@ type Enum struct { Value } -// NewEnum() wraps the argument Value as Enum -func NewEnum(v Value) Enum { +// Enum() wraps the argument Value as Enum +func (v Value) Enum() Enum { return Enum{v} } @@ -80,10 +69,12 @@ func (v Enum) Is(ctx sdk.Context, value byte) bool { // Integer is a uint64 types wrapper for Value. type Integer struct { Value + + enc state.IntEncoding } -func NewInteger(v Value) Integer { - return Integer{v} +func (v Value) Integer(enc state.IntEncoding) Integer { + return Integer{v, enc} } func (v Integer) Is(ctx sdk.Context, value uint64) bool { From a4a20d64dd27352d9b8f1d6ea1754492bd7cfdba Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 30 Jul 2019 17:03:42 +0900 Subject: [PATCH 063/166] add godoc --- x/ibc/23-commitment/codec.go | 1 + x/ibc/23-commitment/context.go | 11 ++++++----- x/ibc/23-commitment/store.go | 9 ++++++++- x/ibc/23-commitment/types.go | 9 +++++++++ x/ibc/23-commitment/value.go | 7 +++++++ 5 files changed, 31 insertions(+), 6 deletions(-) diff --git a/x/ibc/23-commitment/codec.go b/x/ibc/23-commitment/codec.go index 8e0bdf49aa9a..5b6573497e86 100644 --- a/x/ibc/23-commitment/codec.go +++ b/x/ibc/23-commitment/codec.go @@ -4,6 +4,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" ) +// RegisterCodec registeres types declared in this package func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*Root)(nil), nil) cdc.RegisterInterface((*Path)(nil), nil) diff --git a/x/ibc/23-commitment/context.go b/x/ibc/23-commitment/context.go index 3680fb6b09bd..a0404e2a8927 100644 --- a/x/ibc/23-commitment/context.go +++ b/x/ibc/23-commitment/context.go @@ -4,14 +4,15 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// TODO: define Context type which embeds sdk.Context and ensures the existence of RemoteKVStore - -type ContextKeyRemoteKVStore struct{} +// ContextKeyCommitmentKVStore is a singleton type used as the key for the commitment store +type ContextKeyCommitmentKVStore struct{} +// WithStore returns the context updated with the store func WithStore(ctx sdk.Context, store Store) sdk.Context { - return ctx.WithValue(ContextKeyRemoteKVStore{}, store) + return ctx.WithValue(ContextKeyCommitmentKVStore{}, store) } +// GetStore returns the store from the context func GetStore(ctx sdk.Context) Store { - return ctx.Value(ContextKeyRemoteKVStore{}).(Store) + return ctx.Value(ContextKeyCommitmentKVStore{}).(Store) } diff --git a/x/ibc/23-commitment/store.go b/x/ibc/23-commitment/store.go index 0bae09c8c669..efb52a151a20 100644 --- a/x/ibc/23-commitment/store.go +++ b/x/ibc/23-commitment/store.go @@ -5,6 +5,8 @@ import ( "errors" ) +// Store proves key-value pairs' inclusion or non-inclusion with +// the stored commitment proofs against the commitment root. type Store interface { Prove(key, value []byte) bool } @@ -36,7 +38,9 @@ type store struct { verified map[string][]byte } -// Proofs must be provided +// NewStore constructs a new Store with the root, path, and proofs. +// The proofs are not proven immediately because proofs require value bytes to verify. +// If the kinds of the arguments don't match, returns error. func NewStore(root Root, path Path, proofs []Proof) (res store, err error) { if root.CommitmentKind() != path.CommitmentKind() { err = errors.New("path type not matching with root's") @@ -61,11 +65,13 @@ func NewStore(root Root, path Path, proofs []Proof) (res store, err error) { return } +// Get() returns the value only if it is already proven. func (store store) Get(key []byte) ([]byte, bool) { res, ok := store.verified[string(key)] return res, ok } +// Prove() proves the key-value pair with the stored proof. func (store store) Prove(key, value []byte) bool { stored, ok := store.Get(key) if ok && bytes.Equal(stored, value) { @@ -84,6 +90,7 @@ func (store store) Prove(key, value []byte) bool { return true } +// Proven() returns true if the key-value pair is already proven func (store store) Proven(key []byte) bool { _, ok := store.Get(key) return ok diff --git a/x/ibc/23-commitment/types.go b/x/ibc/23-commitment/types.go index 6453511a24e3..006e2b08c69e 100644 --- a/x/ibc/23-commitment/types.go +++ b/x/ibc/23-commitment/types.go @@ -1,13 +1,22 @@ package commitment +// Root is the interface for commitment root. +// A root is constructed from a set of key-value pairs, +// and the inclusion or non-inclusion of an arbitrary key-value pair +// can be proven with the proof. type Root interface { CommitmentKind() string } +// Path is the additional information provided to the verification function. +// Path represents the common "prefix" that a set of keys shares. type Path interface { CommitmentKind() string } +// Proof can prove whether the key-value pair is a part of the Root or not. +// Each proof has designated key-value pair it is able to prove. +// Proofs stores key but value is provided dynamically at the verification time. type Proof interface { CommitmentKind() string GetKey() []byte diff --git a/x/ibc/23-commitment/value.go b/x/ibc/23-commitment/value.go index dd7205385616..65297b91b361 100644 --- a/x/ibc/23-commitment/value.go +++ b/x/ibc/23-commitment/value.go @@ -6,11 +6,15 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +// Mapping is key []byte -> value []byte mapping, possibly prefixed. +// Proof verification should be done over Value constructed from the Mapping. type Mapping struct { cdc *codec.Codec prefix []byte } +// NewMapping() constructs a new Mapping. +// The KVStore accessor is fixed to the commitment store. func NewMapping(cdc *codec.Codec, prefix []byte) Mapping { return Mapping{ cdc: cdc, @@ -22,6 +26,7 @@ func (m Mapping) store(ctx sdk.Context) Store { return NewPrefix(GetStore(ctx), m.prefix) } +// Prefix() returns a new Mapping with the updated prefix func (m Mapping) Prefix(prefix []byte) Mapping { return Mapping{ cdc: m.cdc, @@ -73,10 +78,12 @@ type Integer struct { enc state.IntEncoding } +// Integer() wraps the argument Value as Integer func (v Value) Integer(enc state.IntEncoding) Integer { return Integer{v, enc} } +// Is() proves the proof with the Integer's key and the provided value func (v Integer) Is(ctx sdk.Context, value uint64) bool { return v.Value.IsRaw(ctx, state.EncodeInt(value, v.enc)) } From 02a9ba541cc09a127d759300ebedf497671de5ef Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 30 Jul 2019 17:46:12 +0900 Subject: [PATCH 064/166] add godoc in progress --- x/ibc/02-client/types.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/x/ibc/02-client/types.go b/x/ibc/02-client/types.go index 2a34855085b9..134837a43aa8 100644 --- a/x/ibc/02-client/types.go +++ b/x/ibc/02-client/types.go @@ -5,15 +5,24 @@ import ( ) // TODO: types in this file should be (de/)serialized with proto in the future -// currently amkno codec handles it +// currently amino codec handles it -// ConsensusState +// ConsensusState is the state of the consensus process. type ConsensusState interface { + // Kind() is the kind of the consensus algorithm. Kind() Kind GetHeight() uint64 + + // GetRoot() returns the commitment root of the consensus state, + // which is used for key-value pair verification. GetRoot() commitment.Root + + // Validate() returns the updated consensus state + // only if the header is a descendent of this consensus state. Validate(Header) (ConsensusState, error) // ValidityPredicate - Equivocation(Header, Header) bool // EquivocationPredicate + + // Equivocation checks two headers' confliction. + Equivocation(Header, Header) bool // EquivocationPredicate } /* @@ -23,8 +32,11 @@ func Equal(client1, client2 ConsensusState) bool { } */ +// Header is the consensus state update information. type Header interface { + // Kind() is the kind of the consensus algorithm. Kind() Kind + GetHeight() uint64 } From a9adb588dbdbc95099f08380f636128f9975d9c9 Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 30 Jul 2019 18:07:31 +0900 Subject: [PATCH 065/166] reformat test --- x/ibc/23-commitment/merkle/merkle_test.go | 127 ++++++++++++++-------- 1 file changed, 82 insertions(+), 45 deletions(-) diff --git a/x/ibc/23-commitment/merkle/merkle_test.go b/x/ibc/23-commitment/merkle/merkle_test.go index e17c80ddf74f..a407d17c1868 100644 --- a/x/ibc/23-commitment/merkle/merkle_test.go +++ b/x/ibc/23-commitment/merkle/merkle_test.go @@ -1,6 +1,7 @@ package merkle import ( + "crypto/rand" "testing" "github.com/stretchr/testify/require" @@ -43,49 +44,85 @@ func TestStore(t *testing.T) { kvstore := ctx.KVStore(k) path := Path{KeyPath: [][]byte{[]byte("test")}, KeyPrefix: []byte{0x01, 0x03, 0x05}} - kvstore.Set(path.Key([]byte("hello")), []byte("world")) - kvstore.Set(path.Key([]byte("merkle")), []byte("tree")) - kvstore.Set(path.Key([]byte("block")), []byte("chain")) - - root := commit(cms) - - c1, v1, p1 := path.Query(cms, []byte("hello")) - require.Equal(t, uint32(0), c1) - require.Equal(t, []byte("world"), v1) - c2, v2, p2 := path.Query(cms, []byte("merkle")) - require.Equal(t, uint32(0), c2) - require.Equal(t, []byte("tree"), v2) - c3, v3, p3 := path.Query(cms, []byte("block")) - require.Equal(t, uint32(0), c3) - require.Equal(t, []byte("chain"), v3) - - cstore, err := commitment.NewStore(root, path, []commitment.Proof{p1, p2, p3}) - require.NoError(t, err) - - require.True(t, cstore.Prove([]byte("hello"), []byte("world"))) - require.True(t, cstore.Prove([]byte("merkle"), []byte("tree"))) - require.True(t, cstore.Prove([]byte("block"), []byte("chain"))) - - kvstore.Set(path.Key([]byte("12345")), []byte("67890")) - kvstore.Set(path.Key([]byte("qwerty")), []byte("zxcv")) - kvstore.Set(path.Key([]byte("hello")), []byte("dlrow")) - - root = commit(cms) - - c1, v1, p1 = path.Query(cms, []byte("12345")) - require.Equal(t, uint32(0), c1) - require.Equal(t, []byte("67890"), v1) - c2, v2, p2 = path.Query(cms, []byte("qwerty")) - require.Equal(t, uint32(0), c2) - require.Equal(t, []byte("zxcv"), v2) - c3, v3, p3 = path.Query(cms, []byte("hello")) - require.Equal(t, uint32(0), c3) - require.Equal(t, []byte("dlrow"), v3) - - cstore, err = commitment.NewStore(root, path, []commitment.Proof{p1, p2, p3}) - require.NoError(t, err) - - require.True(t, cstore.Prove([]byte("12345"), []byte("67890"))) - require.True(t, cstore.Prove([]byte("qwerty"), []byte("zxcv"))) - require.True(t, cstore.Prove([]byte("hello"), []byte("dlrow"))) + m := make(map[string][]byte) + kvpn := 1000 + + // Repeat 100 times to test on multiple commits + for repeat := 0; repeat < 10; repeat++ { + + // Initializes random generated key-value pairs + for i := 0; i < kvpn; i++ { + k, v := make([]byte, 64), make([]byte, 64) + rand.Read(k) + rand.Read(v) + m[string(k)] = v + kvstore.Set(path.Key(k), v) + } + + // Commit store + root := commit(cms) + + // Test query, and accumulate proofs + proofs := make([]commitment.Proof, 0, kvpn) + for k, v := range m { + c, v0, p := path.Query(cms, []byte(k)) + require.Equal(t, uint32(0), c) + require.Equal(t, v, v0) + proofs = append(proofs, p) + } + + // Add some exclusion proofs + for i := 0; i < 100; i++ { + k := make([]byte, 64) + rand.Read(k) + c, v, p := path.Query(cms, k) + require.Equal(t, uint32(0), c) + require.Nil(t, v) + proofs = append(proofs, p) + m[string(k)] = []byte{} + } + + cstore, err := commitment.NewStore(root, path, proofs) + require.NoError(t, err) + + // Test commitment store + for k, v := range m { + if len(v) != 0 { + require.True(t, cstore.Prove([]byte(k), v)) + } else { + require.True(t, cstore.Prove([]byte(k), nil)) + } + } + + // Modify existing data + for k := range m { + v := make([]byte, 64) + rand.Read(v) + m[k] = v + kvstore.Set(path.Key([]byte(k)), v) + } + + root = commit(cms) + + // Test query, and accumulate proofs + proofs = make([]commitment.Proof, 0, kvpn) + for k, v := range m { + c, v0, p := path.Query(cms, []byte(k)) + require.Equal(t, uint32(0), c) + require.Equal(t, v, v0) + proofs = append(proofs, p) + } + + cstore, err = commitment.NewStore(root, path, proofs) + require.NoError(t, err) + + // Test commitment store + for k, v := range m { + if len(v) != 0 { + require.True(t, cstore.Prove([]byte(k), v)) + } else { + require.True(t, cstore.Prove([]byte(k), nil)) + } + } + } } From abb2f5131d487882c0abb2ce83daa163c91dd0b8 Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 30 Jul 2019 18:08:50 +0900 Subject: [PATCH 066/166] rm XXX --- x/ibc/23-commitment/merkle/utils.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/x/ibc/23-commitment/merkle/utils.go b/x/ibc/23-commitment/merkle/utils.go index 86c6d6d18f3e..1a0880a3659f 100644 --- a/x/ibc/23-commitment/merkle/utils.go +++ b/x/ibc/23-commitment/merkle/utils.go @@ -39,5 +39,12 @@ func (path Path) Query(cms types.CommitMultiStore, key []byte) (uint32, []byte, } func (path Path) Key(key []byte) []byte { - return append(path.KeyPrefix, key...) // XXX: cloneAppend + return join(path.KeyPrefix, key) +} + +func join(a, b []byte) (res []byte) { + res = make([]byte, len(a)+len(b)) + copy(res, a) + copy(res[len(a):], b) + return } From 361aa42a93a4290766a54c38bf7959a1de3aee23 Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 31 Jul 2019 04:01:46 +0900 Subject: [PATCH 067/166] add godoc --- x/ibc/23-commitment/merkle/merkle.go | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/x/ibc/23-commitment/merkle/merkle.go b/x/ibc/23-commitment/merkle/merkle.go index a4a00a47db7b..a5acbdd43c3a 100644 --- a/x/ibc/23-commitment/merkle/merkle.go +++ b/x/ibc/23-commitment/merkle/merkle.go @@ -16,25 +16,35 @@ const merkleKind = "merkle" // Applied on SDK-based IBC implementation var _ commitment.Root = Root{} +// Root is Merkle root hash type Root struct { Hash []byte } +// NewRoot constructs a new Root func NewRoot(hash []byte) Root { - return Root{hash} + return Root{ + Hash: hash, + } } +// Implements commitment.Root func (Root) CommitmentKind() string { return merkleKind } var _ commitment.Path = Path{} +// Path is merkle path prefixed to the key. +// The constructed key from the Path and the key will be append(Path.KeyPath, append(Path.KeyPrefix, key...)) type Path struct { - KeyPath [][]byte + // KeyPath is the list of keys prepended before the prefixed key + KeyPath [][]byte + // KeyPrefix is a byte slice prefixed before the key KeyPrefix []byte } +// NewPath() constructs new Path func NewPath(keypath [][]byte, keyprefix []byte) Path { return Path{ KeyPath: keypath, @@ -42,25 +52,30 @@ func NewPath(keypath [][]byte, keyprefix []byte) Path { } } +// Implements commitment.Path func (Path) CommitmentKind() string { return merkleKind } var _ commitment.Proof = Proof{} +// Proof is Merkle proof with the key information. type Proof struct { Proof *merkle.Proof Key []byte } +// Implements commitment.Proof func (Proof) CommitmentKind() string { return merkleKind } +// Returns the key of the proof func (proof Proof) GetKey() []byte { return proof.Key } +// Verify() proves the proof against the given root, path, and value. func (proof Proof) Verify(croot commitment.Root, cpath commitment.Path, value []byte) error { root, ok := croot.(Root) if !ok { @@ -78,7 +93,7 @@ func (proof Proof) Verify(croot commitment.Root, cpath commitment.Path, value [] } keypath = keypath.AppendKey(append(path.KeyPrefix, proof.Key...), merkle.KeyEncodingHex) - // TODO: Hard coded for now, proof runtime should be extensible for other proof types + // TODO: hard coded for now, should be extensible runtime := rootmulti.DefaultProofRuntime() if value != nil { From 54a67b0b2106522dd65a80453abefe0585eef13c Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 31 Jul 2019 17:46:32 +0900 Subject: [PATCH 068/166] modify store --- x/ibc/02-client/cli.go | 42 +++---------- x/ibc/02-client/client/cli/query.go | 10 +-- x/ibc/02-client/manager.go | 74 +++++++++++------------ x/ibc/23-commitment/merkle/merkle.go | 16 ++++- x/ibc/23-commitment/merkle/merkle_test.go | 6 +- x/ibc/23-commitment/merkle/utils.go | 26 +++++++- 6 files changed, 92 insertions(+), 82 deletions(-) diff --git a/x/ibc/02-client/cli.go b/x/ibc/02-client/cli.go index 382b1d2d4aea..d4a02a028c3a 100644 --- a/x/ibc/02-client/cli.go +++ b/x/ibc/02-client/cli.go @@ -2,53 +2,29 @@ package client import ( "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) -// CLIObject stores the key for each object fields type CLIObject struct { - ID string - ConsensusStateKey []byte - FrozenKey []byte - - Path merkle.Path - Cdc *codec.Codec + obj Object } -func (man Manager) CLIObject(path merkle.Path, id string) CLIObject { - obj := man.object(id) - return CLIObject{ - ID: id, - ConsensusStateKey: obj.consensusState.Key(), - FrozenKey: obj.frozen.Key(), - - Path: path, - Cdc: obj.consensusState.Cdc(), - } +func (obj Object) CLIObject() CLIObject { + return CLIObject{obj} } -func (obj CLIObject) query(ctx context.CLIContext, key []byte, ptr interface{}) (merkle.Proof, error) { - resp, err := ctx.QueryABCI(obj.Path.RequestQuery(key)) - if err != nil { - return merkle.Proof{}, err - } - proof := merkle.Proof{ - Key: key, - Proof: resp.Proof, - } - err = obj.Cdc.UnmarshalBinaryBare(resp.Value, ptr) - return proof, err - -} +// (path, ) func (obj CLIObject) ConsensusState(ctx context.CLIContext) (res ConsensusState, proof merkle.Proof, err error) { - proof, err = obj.query(ctx, obj.ConsensusStateKey, &res) + tmproof, err := obj.obj.ConsensusState.Query(ctx, &res) + proof = merkle.NewProofFromValue(tmproof, obj.obj.ConsensusState) return } func (obj CLIObject) Frozen(ctx context.CLIContext) (res bool, proof merkle.Proof, err error) { - proof, err = obj.query(ctx, obj.FrozenKey, &res) + res, tmproof, err := obj.obj.Frozen.Query(ctx) + proof = merkle.NewProofFromValue(tmproof, obj.obj.Frozen) return } + diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index fb2c7f73da0e..ed044444eeb8 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -15,15 +15,15 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/ibc" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client" + client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/tendermint" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) -func components(cdc *codec.Codec, storeKey string, version int64) (path merkle.Path, base state.Base) { +func components(cdc *codec.Codec, storeKey string, version int64) (path merkle.Path, mapp state.Mapping) { prefix := []byte(strconv.FormatInt(version, 10) + "/") path = merkle.NewPath([][]byte{[]byte(storeKey)}, prefix) - base = state.NewBase(cdc, sdk.NewKVStoreKey(storeKey), prefix) + mapp = state.NewMapping(sdk.NewKVStoreKey(storeKey), cdc, prefix) return } @@ -50,8 +50,8 @@ func GetCmdQueryClient(storeKey string, cdc *codec.Codec) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := context.NewCLIContext().WithCodec(cdc) - path, base := components(cdc, storeKey, ibc.Version) - man := client.NewManager(base) + path, mapp := components(cdc, storeKey, ibc.Version) + man := client.NewManager(mapp) id := args[0] state, _, err := man.CLIObject(path, id).ConsensusState(ctx) diff --git a/x/ibc/02-client/manager.go b/x/ibc/02-client/manager.go index 4f5195bc1850..fe69a59caa05 100644 --- a/x/ibc/02-client/manager.go +++ b/x/ibc/02-client/manager.go @@ -3,21 +3,21 @@ package client import ( "errors" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/state" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) -// XXX: implement spec: ClientState.verifiedRoots - +// Any actor holding the Manager can access on and modify any client information type Manager struct { protocol state.Mapping } -func NewManager(protocol state.Base) Manager { +func NewManager(base state.Mapping) Manager { return Manager{ - protocol: state.NewMapping(protocol, []byte("/client")), + protocol: base.Prefix([]byte("/client")), } } @@ -25,9 +25,9 @@ type CounterpartyManager struct { protocol commitment.Mapping } -func NewCounterpartyManager(protocol commitment.Base) CounterpartyManager { +func NewCounterpartyManager(cdc *codec.Codec) CounterpartyManager { return CounterpartyManager{ - protocol: commitment.NewMapping(protocol, []byte("/client")), + protocol: commitment.NewMapping(cdc, []byte("/client")), } } @@ -40,72 +40,69 @@ func (man Manager) RegisterKind(kind Kind, pred ValidityPredicate) Manager { return man } */ -func (man Manager) object(id string) Object { +func (man Manager) Object(id string) Object { return Object{ id: id, - consensusState: man.protocol.Value([]byte(id)), - frozen: state.NewBoolean(man.protocol.Value([]byte(id + "/freeze"))), + ConsensusState: man.protocol.Value([]byte(id)), + Frozen: man.protocol.Value([]byte(id + "/freeze")).Boolean(), } } func (man Manager) Create(ctx sdk.Context, id string, cs ConsensusState) (Object, error) { - obj := man.object(id) + obj := man.Object(id) if obj.exists(ctx) { return Object{}, errors.New("Create client on already existing id") } - obj.consensusState.Set(ctx, cs) + obj.ConsensusState.Set(ctx, cs) return obj, nil } func (man Manager) Query(ctx sdk.Context, id string) (Object, error) { - res := man.object(id) + res := man.Object(id) if !res.exists(ctx) { return Object{}, errors.New("client not exists") } return res, nil } -func (man CounterpartyManager) object(id string) CounterObject { +func (man CounterpartyManager) Object(id string) CounterObject { return CounterObject{ id: id, - consensusState: man.protocol.Value([]byte(id)), + ConsensusState: man.protocol.Value([]byte(id)), } } func (man CounterpartyManager) Query(id string) CounterObject { - return man.object(id) + return man.Object(id) } +// Any actor holding the Object can access on and modify that client information type Object struct { id string - consensusState state.Value // ConsensusState - frozen state.Boolean + ConsensusState state.Value // ConsensusState + Frozen state.Boolean } type CounterObject struct { id string - consensusState commitment.Value + ConsensusState commitment.Value } func (obj Object) ID() string { return obj.id } -func (obj Object) ConsensusState(ctx sdk.Context) (res ConsensusState) { - obj.consensusState.Get(ctx, &res) +func (obj Object) GetConsensusState(ctx sdk.Context) (res ConsensusState) { + obj.ConsensusState.Get(ctx, &res) return } -func (obj Object) Frozen(ctx sdk.Context) bool { - return obj.frozen.Get(ctx) -} - func (obj CounterObject) Is(ctx sdk.Context, client ConsensusState) bool { - return obj.consensusState.Is(ctx, client) + return obj.ConsensusState.Is(ctx, client) } func (obj Object) exists(ctx sdk.Context) bool { - return obj.consensusState.Exists(ctx) + return obj.ConsensusState.Exists(ctx) } func (obj Object) Update(ctx sdk.Context, header Header) error { @@ -113,18 +110,17 @@ func (obj Object) Update(ctx sdk.Context, header Header) error { panic("should not update nonexisting client") } - if obj.Frozen(ctx) { - return errors.New("client is frozen") + if obj.Frozen.Get(ctx) { + return errors.New("client is Frozen") } - var stored ConsensusState - obj.consensusState.Get(ctx, &stored) + stored := obj.GetConsensusState(ctx) updated, err := stored.Validate(header) if err != nil { return err } - obj.consensusState.Set(ctx, updated) + obj.ConsensusState.Set(ctx, updated) return nil } @@ -134,11 +130,11 @@ func (obj Object) Freeze(ctx sdk.Context) error { panic("should not freeze nonexisting client") } - if obj.Frozen(ctx) { - return errors.New("client is already frozen") + if obj.Frozen.Get(ctx) { + return errors.New("client is already Frozen") } - obj.frozen.Set(ctx, true) + obj.Frozen.Set(ctx, true) return nil } @@ -148,12 +144,12 @@ func (obj Object) Delete(ctx sdk.Context) error { panic("should not delete nonexisting client") } - if !obj.Frozen(ctx) { - return errors.New("client is not frozen") + if !obj.Frozen.Get(ctx) { + return errors.New("client is not Frozen") } - obj.consensusState.Delete(ctx) - obj.frozen.Delete(ctx) + obj.ConsensusState.Delete(ctx) + obj.Frozen.Delete(ctx) return nil } diff --git a/x/ibc/23-commitment/merkle/merkle.go b/x/ibc/23-commitment/merkle/merkle.go index a5acbdd43c3a..9e0b5e6b1152 100644 --- a/x/ibc/23-commitment/merkle/merkle.go +++ b/x/ibc/23-commitment/merkle/merkle.go @@ -6,8 +6,9 @@ import ( "github.com/tendermint/tendermint/crypto/merkle" "github.com/cosmos/cosmos-sdk/store/rootmulti" + "github.com/cosmos/cosmos-sdk/store/state" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) const merkleKind = "merkle" @@ -57,6 +58,10 @@ func (Path) CommitmentKind() string { return merkleKind } +func NewPathFromMapping(mapp state.Mapping) Path { + return NewPath([][]byte{[]byte(mapp.StoreName())}, mapp.PrefixBytes()) +} + var _ commitment.Proof = Proof{} // Proof is Merkle proof with the key information. @@ -101,3 +106,12 @@ func (proof Proof) Verify(croot commitment.Root, cpath commitment.Path, value [] } return runtime.VerifyAbsence(proof.Proof, root.Hash, keypath.String()) } + +type Value interface { + KeyBytes() []byte + Unmarshal([]byte, interface{}) +} + +func NewProofFromValue(proof *merkle.Proof, value Value) Proof { + return Proof{proof, value.KeyBytes()} +} diff --git a/x/ibc/23-commitment/merkle/merkle_test.go b/x/ibc/23-commitment/merkle/merkle_test.go index fab3880a06a0..4c4f49ce274a 100644 --- a/x/ibc/23-commitment/merkle/merkle_test.go +++ b/x/ibc/23-commitment/merkle/merkle_test.go @@ -65,7 +65,7 @@ func TestStore(t *testing.T) { // Test query, and accumulate proofs proofs := make([]commitment.Proof, 0, kvpn) for k, v := range m { - c, v0, p := path.Query(cms, []byte(k)) + c, v0, p := path.QueryMultiStore(cms, []byte(k)) require.Equal(t, uint32(0), c) require.Equal(t, v, v0) proofs = append(proofs, p) @@ -75,7 +75,7 @@ func TestStore(t *testing.T) { for i := 0; i < 100; i++ { k := make([]byte, 64) rand.Read(k) - c, v, p := path.Query(cms, k) + c, v, p := path.QueryMultiStore(cms, k) require.Equal(t, uint32(0), c) require.Nil(t, v) proofs = append(proofs, p) @@ -107,7 +107,7 @@ func TestStore(t *testing.T) { // Test query, and accumulate proofs proofs = make([]commitment.Proof, 0, kvpn) for k, v := range m { - c, v0, p := path.Query(cms, []byte(k)) + c, v0, p := path.QueryMultiStore(cms, []byte(k)) require.Equal(t, uint32(0), c) require.Equal(t, v, v0) proofs = append(proofs, p) diff --git a/x/ibc/23-commitment/merkle/utils.go b/x/ibc/23-commitment/merkle/utils.go index e1659d696432..b8497c757cae 100644 --- a/x/ibc/23-commitment/merkle/utils.go +++ b/x/ibc/23-commitment/merkle/utils.go @@ -1,8 +1,12 @@ package merkle import ( + "errors" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/types" ) @@ -28,12 +32,32 @@ func (path Path) RequestQueryMultiStore(key []byte) abci.RequestQuery { return abci.RequestQuery{Path: path.Path() + "/key", Data: path.Key(key), Prove: true} } +func (path Path) Query(ctx context.CLIContext, key []byte) (code uint32, value []byte, proof Proof, err error) { + resp, err := ctx.QueryABCI(path.RequestQuery(key)) + if err != nil { + return code, value, proof, err + } + if !resp.IsOK() { + return resp.Code, value, proof, errors.New(resp.Log) + } + return resp.Code, resp.Value, Proof{Key: key, Proof: resp.Proof}, nil +} + +func (path Path) QueryValue(ctx context.CLIContext, cdc *codec.Codec, key []byte, ptr interface{}) (Proof, error) { + _, value, proof, err := path.Query(ctx, key) + if err != nil { + return Proof{}, err + } + err = cdc.UnmarshalBinaryBare(value, ptr) // TODO + return proof, err +} + func (path Path) QueryMultiStore(cms types.CommitMultiStore, key []byte) (uint32, []byte, Proof) { queryable, ok := cms.(types.Queryable) if !ok { panic("CommitMultiStore not queryable") } - qres := queryable.Query(path.RequestQuery(key)) + qres := queryable.Query(path.RequestQueryMultiStore(key)) return qres.Code, qres.Value, Proof{Key: key, Proof: qres.Proof} } From 24e621a128de159bb32eb7eb5f847a63b367e51f Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 31 Jul 2019 18:58:47 +0900 Subject: [PATCH 069/166] add query --- store/state/enum.go | 5 +++++ store/state/integer.go | 9 ++++++++ store/state/string.go | 5 +++++ store/state/types.go | 5 +++++ store/state/value.go | 50 +++++++++++++++++++++++++++++++++++++++--- 5 files changed, 71 insertions(+), 3 deletions(-) diff --git a/store/state/enum.go b/store/state/enum.go index 67f18a4a2584..bc43f8c69007 100644 --- a/store/state/enum.go +++ b/store/state/enum.go @@ -47,3 +47,8 @@ func (v Enum) Transit(ctx Context, from, to byte) bool { v.Set(ctx, to) return true } + +func (v Enum) Query(ctx CLIContext) (res byte, proof *Proof, err error) { + value, proof, err := v.Value.QueryRaw(ctx) + return value[0], proof, err +} diff --git a/store/state/integer.go b/store/state/integer.go index 187fd718d55e..499fbe414ec2 100644 --- a/store/state/integer.go +++ b/store/state/integer.go @@ -51,3 +51,12 @@ func (v Integer) Incr(ctx Context) (res uint64) { v.Set(ctx, res) return } + +func (v Integer) Query(ctx CLIContext) (res uint64, proof *Proof, err error) { + value, proof, err := v.Value.QueryRaw(ctx) + if err != nil { + return + } + res, err = DecodeInt(value, v.enc) + return +} diff --git a/store/state/string.go b/store/state/string.go index 72c85340d01f..c084362d0e03 100644 --- a/store/state/string.go +++ b/store/state/string.go @@ -30,3 +30,8 @@ func (v String) GetSafe(ctx Context) (res string, err error) { func (v String) Set(ctx Context, value string) { v.Value.SetRaw(ctx, []byte(value)) } + +func (v String) Query(ctx CLIContext) (res string, proof *Proof, err error) { + value, proof, err := v.Value.QueryRaw(ctx) + return string(value), proof, err +} diff --git a/store/state/types.go b/store/state/types.go index a8d34d582a82..f5dbb4dc8648 100644 --- a/store/state/types.go +++ b/store/state/types.go @@ -1,8 +1,13 @@ package state import ( + "github.com/tendermint/tendermint/crypto/merkle" + + "github.com/cosmos/cosmos-sdk/client/context" sdk "github.com/cosmos/cosmos-sdk/types" ) type KVStore = sdk.KVStore type Context = sdk.Context +type CLIContext = context.CLIContext +type Proof = merkle.Proof diff --git a/store/state/value.go b/store/state/value.go index 7878ae2f191e..5b39818aeebb 100644 --- a/store/state/value.go +++ b/store/state/value.go @@ -1,6 +1,10 @@ package state import ( + "errors" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/codec" ) @@ -29,12 +33,24 @@ func (v Value) Cdc() *codec.Codec { return v.m.Cdc() } +func (v Value) Marshal(value interface{}) []byte { + return v.m.cdc.MustMarshalBinaryBare(value) +} + +func (v Value) Unmarshal(bz []byte, ptr interface{}) error { + return v.m.cdc.UnmarshalBinaryBare(bz, ptr) +} + +func (v Value) mustUnmarshal(bz []byte, ptr interface{}) { + v.m.cdc.MustUnmarshalBinaryBare(bz, ptr) +} + // Get() unmarshales and sets the stored value to the pointer if it exists. // It will panic if the value exists but not unmarshalable. func (v Value) Get(ctx Context, ptr interface{}) { bz := v.store(ctx).Get(v.key) if bz != nil { - v.m.cdc.MustUnmarshalBinaryBare(bz, ptr) + v.mustUnmarshal(bz, ptr) } } @@ -45,7 +61,7 @@ func (v Value) GetSafe(ctx Context, ptr interface{}) error { if bz == nil { return ErrEmptyValue() } - err := v.m.cdc.UnmarshalBinaryBare(bz, ptr) + err := v.Unmarshal(bz, ptr) if err != nil { return ErrUnmarshal(err) } @@ -59,7 +75,7 @@ func (v Value) GetRaw(ctx Context) []byte { // Set() marshales and sets the argument to the state. func (v Value) Set(ctx Context, o interface{}) { - v.store(ctx).Set(v.key, v.m.cdc.MustMarshalBinaryBare(o)) + v.store(ctx).Set(v.key, v.Marshal(o)) } // SetRaw() sets the raw bytes to the state. @@ -83,3 +99,31 @@ func (v Value) Delete(ctx Context) { func (v Value) KeyBytes() []byte { return v.m.KeyBytes(v.key) } + +func (v Value) QueryRaw(ctx CLIContext) ([]byte, *Proof, error) { + req := abci.RequestQuery{ + Path: "/store" + v.m.StoreName() + "/key", + Data: v.KeyBytes(), + Prove: true, + } + + resp, err := ctx.QueryABCI(req) + if err != nil { + return nil, nil, err + } + + if !resp.IsOK() { + return nil, nil, errors.New(resp.Log) + } + + return resp.Value, resp.Proof, nil +} + +func (v Value) Query(ctx CLIContext, ptr interface{}) (*Proof, error) { + value, proof, err := v.QueryRaw(ctx) + if err != nil { + return nil, err + } + err = v.Cdc().UnmarshalBinaryBare(value, ptr) + return proof, err +} From 64c0381ff00f489b1426f2425de2153e4b834677 Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 31 Jul 2019 19:05:53 +0900 Subject: [PATCH 070/166] update query.go --- client/context/query.go | 47 +++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/client/context/query.go b/client/context/query.go index 2dc8ab217f59..98cd30690f0c 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -49,6 +49,12 @@ func (ctx CLIContext) QueryStore(key cmn.HexBytes, storeName string) ([]byte, in return ctx.queryStore(key, storeName, "key") } +// QueryABCI performs a query to a Tendermint node with the provide RequestQuery. +// It returns the ResultQuery obtained from the query. +func (ctx CLIContext) QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { + return ctx.queryABCI(req) +} + // QuerySubspace performs a query to a Tendermint node with the provided // store name and subspace. It returns key value pair and height of the query // upon success or an error if the query fails. @@ -72,13 +78,10 @@ func (ctx CLIContext) GetFromName() string { return ctx.FromName } -// query performs a query to a Tendermint node with the provided store name -// and path. It returns the result and height of the query upon success -// or an error if the query fails. -func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, height int64, err error) { +func (ctx CLIContext) queryABCI(req abci.RequestQuery) (resp abci.ResponseQuery, err error) { node, err := ctx.GetNode() if err != nil { - return res, height, err + return } // When a client did not provide a query height, manually query for it so it can @@ -86,7 +89,7 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, height i if ctx.Height == 0 { status, err := node.Status() if err != nil { - return res, height, err + return resp, err } ctx = ctx.WithHeight(status.SyncInfo.LatestBlockHeight) } @@ -96,24 +99,40 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, height i Prove: !ctx.TrustNode, } - result, err := node.ABCIQueryWithOptions(path, key, opts) + result, err := node.ABCIQueryWithOptions(req.Path, req.Data, opts) if err != nil { - return res, height, err + return } - resp := result.Response + resp = result.Response if !resp.IsOK() { - return res, height, errors.New(resp.Log) + err = errors.New(resp.Log) + return } // data from trusted node or subspace query doesn't need verification - if ctx.TrustNode || !isQueryStoreWithProof(path) { - return resp.Value, resp.Height, nil + if ctx.TrustNode || !isQueryStoreWithProof(req.Path) { + return resp, nil } - err = ctx.verifyProof(path, resp) + err = ctx.verifyProof(req.Path, resp) if err != nil { - return res, height, err + return + } + + return +} + +// query performs a query to a Tendermint node with the provided store name +// and path. It returns the result and height of the query upon success +// or an error if the query fails. +func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, height int64, err error) { + resp, err := ctx.queryABCI(abci.RequestQuery{ + Path: path, + Data: key, + }) + if err != nil { + return } return resp.Value, resp.Height, nil From 971a642a721031faae20eea9a9cef86f4bed7215 Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 31 Jul 2019 19:13:57 +0900 Subject: [PATCH 071/166] update query.go --- client/context/query.go | 47 +++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/client/context/query.go b/client/context/query.go index 2dc8ab217f59..98cd30690f0c 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -49,6 +49,12 @@ func (ctx CLIContext) QueryStore(key cmn.HexBytes, storeName string) ([]byte, in return ctx.queryStore(key, storeName, "key") } +// QueryABCI performs a query to a Tendermint node with the provide RequestQuery. +// It returns the ResultQuery obtained from the query. +func (ctx CLIContext) QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { + return ctx.queryABCI(req) +} + // QuerySubspace performs a query to a Tendermint node with the provided // store name and subspace. It returns key value pair and height of the query // upon success or an error if the query fails. @@ -72,13 +78,10 @@ func (ctx CLIContext) GetFromName() string { return ctx.FromName } -// query performs a query to a Tendermint node with the provided store name -// and path. It returns the result and height of the query upon success -// or an error if the query fails. -func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, height int64, err error) { +func (ctx CLIContext) queryABCI(req abci.RequestQuery) (resp abci.ResponseQuery, err error) { node, err := ctx.GetNode() if err != nil { - return res, height, err + return } // When a client did not provide a query height, manually query for it so it can @@ -86,7 +89,7 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, height i if ctx.Height == 0 { status, err := node.Status() if err != nil { - return res, height, err + return resp, err } ctx = ctx.WithHeight(status.SyncInfo.LatestBlockHeight) } @@ -96,24 +99,40 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, height i Prove: !ctx.TrustNode, } - result, err := node.ABCIQueryWithOptions(path, key, opts) + result, err := node.ABCIQueryWithOptions(req.Path, req.Data, opts) if err != nil { - return res, height, err + return } - resp := result.Response + resp = result.Response if !resp.IsOK() { - return res, height, errors.New(resp.Log) + err = errors.New(resp.Log) + return } // data from trusted node or subspace query doesn't need verification - if ctx.TrustNode || !isQueryStoreWithProof(path) { - return resp.Value, resp.Height, nil + if ctx.TrustNode || !isQueryStoreWithProof(req.Path) { + return resp, nil } - err = ctx.verifyProof(path, resp) + err = ctx.verifyProof(req.Path, resp) if err != nil { - return res, height, err + return + } + + return +} + +// query performs a query to a Tendermint node with the provided store name +// and path. It returns the result and height of the query upon success +// or an error if the query fails. +func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, height int64, err error) { + resp, err := ctx.queryABCI(abci.RequestQuery{ + Path: path, + Data: key, + }) + if err != nil { + return } return resp.Value, resp.Height, nil From e7f67085a8411f0c4f10f0ab4056da321b6a145b Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 31 Jul 2019 22:42:51 +0900 Subject: [PATCH 072/166] cli refactor in progress --- x/ibc/02-client/cli.go | 2 -- x/ibc/02-client/client/cli/query.go | 10 ++++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/x/ibc/02-client/cli.go b/x/ibc/02-client/cli.go index d4a02a028c3a..acf0ecc2218c 100644 --- a/x/ibc/02-client/cli.go +++ b/x/ibc/02-client/cli.go @@ -14,8 +14,6 @@ func (obj Object) CLIObject() CLIObject { return CLIObject{obj} } -// (path, ) - func (obj CLIObject) ConsensusState(ctx context.CLIContext) (res ConsensusState, proof merkle.Proof, err error) { tmproof, err := obj.obj.ConsensusState.Query(ctx, &res) proof = merkle.NewProofFromValue(tmproof, obj.obj.ConsensusState) diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index ed044444eeb8..6232d50e2a5f 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -20,11 +20,9 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) -func components(cdc *codec.Codec, storeKey string, version int64) (path merkle.Path, mapp state.Mapping) { +func mapping(cdc *codec.Codec, storeKey string, version int64) state.Mapping { prefix := []byte(strconv.FormatInt(version, 10) + "/") - path = merkle.NewPath([][]byte{[]byte(storeKey)}, prefix) - mapp = state.NewMapping(sdk.NewKVStoreKey(storeKey), cdc, prefix) - return + return state.NewMapping(sdk.NewKVStoreKey(storeKey), cdc, prefix) } func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { @@ -50,11 +48,11 @@ func GetCmdQueryClient(storeKey string, cdc *codec.Codec) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := context.NewCLIContext().WithCodec(cdc) - path, mapp := components(cdc, storeKey, ibc.Version) + mapp := mapping(cdc, storeKey, ibc.Version) man := client.NewManager(mapp) id := args[0] - state, _, err := man.CLIObject(path, id).ConsensusState(ctx) + state, _, err := man.Object(id).CLIObject().ConsensusState(ctx) if err != nil { return err } From 0d134d5e61afad671cde87c02a1a217edd19f63c Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 31 Jul 2019 22:43:20 +0900 Subject: [PATCH 073/166] cli refactor in progress --- x/ibc/23-commitment/merkle/merkle.go | 7 ++- x/ibc/23-commitment/merkle/merkle_test.go | 32 ++++++----- x/ibc/23-commitment/merkle/utils.go | 67 ++++++++++++++--------- 3 files changed, 63 insertions(+), 43 deletions(-) diff --git a/x/ibc/23-commitment/merkle/merkle.go b/x/ibc/23-commitment/merkle/merkle.go index 9e0b5e6b1152..615b49ccadb9 100644 --- a/x/ibc/23-commitment/merkle/merkle.go +++ b/x/ibc/23-commitment/merkle/merkle.go @@ -1,6 +1,7 @@ package merkle import ( + "bytes" "errors" "github.com/tendermint/tendermint/crypto/merkle" @@ -109,9 +110,9 @@ func (proof Proof) Verify(croot commitment.Root, cpath commitment.Path, value [] type Value interface { KeyBytes() []byte - Unmarshal([]byte, interface{}) } -func NewProofFromValue(proof *merkle.Proof, value Value) Proof { - return Proof{proof, value.KeyBytes()} +func NewProofFromValue(proof *merkle.Proof, path Path, value Value) Proof { + // TODO: check HasPrefix + return Proof{proof, bytes.TrimPrefix(value.KeyBytes(), path.KeyPrefix)} } diff --git a/x/ibc/23-commitment/merkle/merkle_test.go b/x/ibc/23-commitment/merkle/merkle_test.go index 4c4f49ce274a..3c49a2dee82b 100644 --- a/x/ibc/23-commitment/merkle/merkle_test.go +++ b/x/ibc/23-commitment/merkle/merkle_test.go @@ -12,6 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" + "github.com/cosmos/cosmos-sdk/store/state" "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -40,23 +41,24 @@ func commit(cms types.CommitMultiStore) Root { // TestStore tests Merkle proof on the commitment.Store // Sets/upates key-value pairs and prove with the query result proofs func TestStore(t *testing.T) { - k, ctx, cms, _ := defaultComponents() - kvstore := ctx.KVStore(k) - path := Path{KeyPath: [][]byte{[]byte("test")}, KeyPrefix: []byte{0x01, 0x03, 0x05}} + k, ctx, cms, cdc := defaultComponents() + prefix := []byte{0x01, 0x03, 0x05, 0xAA, 0xBB} + mapp := state.NewMapping(k, cdc, prefix) + path := NewPath([][]byte{[]byte(k.Name())}, prefix) m := make(map[string][]byte) - kvpn := 1000 + kvpn := 10 - // Repeat 100 times to test on multiple commits + // Repeat to test on multiple commits for repeat := 0; repeat < 10; repeat++ { // Initializes random generated key-value pairs for i := 0; i < kvpn; i++ { - k, v := make([]byte, 64), make([]byte, 64) + k, v := make([]byte, 16), make([]byte, 16) rand.Read(k) rand.Read(v) m[string(k)] = v - kvstore.Set(path.Key(k), v) + mapp.Value(k).Set(ctx, v) } // Commit store @@ -65,18 +67,18 @@ func TestStore(t *testing.T) { // Test query, and accumulate proofs proofs := make([]commitment.Proof, 0, kvpn) for k, v := range m { - c, v0, p := path.QueryMultiStore(cms, []byte(k)) - require.Equal(t, uint32(0), c) + v0, p, err := QueryMultiStore(cms, path, []byte(k)) + require.NoError(t, err) require.Equal(t, v, v0) proofs = append(proofs, p) } // Add some exclusion proofs - for i := 0; i < 100; i++ { + for i := 0; i < 10; i++ { k := make([]byte, 64) rand.Read(k) - c, v, p := path.QueryMultiStore(cms, k) - require.Equal(t, uint32(0), c) + v, p, err := QueryMultiStore(cms, path, k) + require.NoError(t, err) require.Nil(t, v) proofs = append(proofs, p) m[string(k)] = []byte{} @@ -99,7 +101,7 @@ func TestStore(t *testing.T) { v := make([]byte, 64) rand.Read(v) m[k] = v - kvstore.Set(path.Key([]byte(k)), v) + mapp.Value([]byte(k)).Set(ctx, v) } root = commit(cms) @@ -107,8 +109,8 @@ func TestStore(t *testing.T) { // Test query, and accumulate proofs proofs = make([]commitment.Proof, 0, kvpn) for k, v := range m { - c, v0, p := path.QueryMultiStore(cms, []byte(k)) - require.Equal(t, uint32(0), c) + v0, p, err := QueryMultiStore(cms, path, []byte(k)) + require.NoError(t, err) require.Equal(t, v, v0) proofs = append(proofs, p) } diff --git a/x/ibc/23-commitment/merkle/utils.go b/x/ibc/23-commitment/merkle/utils.go index b8497c757cae..4dbd6cb292ec 100644 --- a/x/ibc/23-commitment/merkle/utils.go +++ b/x/ibc/23-commitment/merkle/utils.go @@ -1,15 +1,53 @@ package merkle import ( + "bytes" "errors" + "fmt" abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/types" ) +func QueryMultiStore(cms types.CommitMultiStore, path Path, key []byte) ([]byte, Proof, error) { + queryable, ok := cms.(types.Queryable) + if !ok { + panic("CommitMultiStore not queryable") + } + qres := queryable.Query(RequestQueryMultiStore(path, key)) + if !qres.IsOK() { + return nil, Proof{}, errors.New(qres.Log) + } + + fmt.Printf("Q: %X -> %X\n", path.Key(key), qres.Value) + return qres.Value, Proof{Key: key, Proof: qres.Proof}, nil +} + +func RequestQueryMultiStore(path Path, key []byte) abci.RequestQuery { + // Suffixing path with "/key". + // iavl.Store.Query() switches over the last path element, + // and performs key-value query only if it is "/key" + return abci.RequestQuery{ + Path: "/" + string(bytes.Join(path.KeyPath, []byte("/"))) + "/key", + Data: path.Key(key), + Prove: true, + } +} + +func (path Path) Key(key []byte) []byte { + return join(path.KeyPrefix, key) +} + +func join(a, b []byte) (res []byte) { + res = make([]byte, len(a)+len(b)) + copy(res, a) + copy(res[len(a):], b) + return +} + +/* + // RequestQuery() constructs the abci.RequestQuery. // // RequestQuery.Path is a slash separated key list, ending with "/key" @@ -25,12 +63,6 @@ func (path Path) RequestQuery(key []byte) abci.RequestQuery { return req } -func (path Path) RequestQueryMultiStore(key []byte) abci.RequestQuery { - // Suffixing path with "/key". - // iavl.Store.Query() switches over the last path element, - // and performs key-value query only if it is "/key" - return abci.RequestQuery{Path: path.Path() + "/key", Data: path.Key(key), Prove: true} -} func (path Path) Query(ctx context.CLIContext, key []byte) (code uint32, value []byte, proof Proof, err error) { resp, err := ctx.QueryABCI(path.RequestQuery(key)) @@ -52,25 +84,9 @@ func (path Path) QueryValue(ctx context.CLIContext, cdc *codec.Codec, key []byte return proof, err } -func (path Path) QueryMultiStore(cms types.CommitMultiStore, key []byte) (uint32, []byte, Proof) { - queryable, ok := cms.(types.Queryable) - if !ok { - panic("CommitMultiStore not queryable") - } - qres := queryable.Query(path.RequestQueryMultiStore(key)) - return qres.Code, qres.Value, Proof{Key: key, Proof: qres.Proof} -} -func (path Path) Key(key []byte) []byte { - return join(path.KeyPrefix, key) -} -func join(a, b []byte) (res []byte) { - res = make([]byte, len(a)+len(b)) - copy(res, a) - copy(res[len(a):], b) - return -} + func (path Path) Path() string { pathstr := "" @@ -81,3 +97,4 @@ func (path Path) Path() string { return pathstr } +*/ From fcad48756a63197911b95685e3e382014ceee207 Mon Sep 17 00:00:00 2001 From: mossid Date: Thu, 1 Aug 2019 21:02:44 +0900 Subject: [PATCH 074/166] add Query to boolean.go --- store/state/boolean.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/store/state/boolean.go b/store/state/boolean.go index 5d582b6869d7..f7873c2e775a 100644 --- a/store/state/boolean.go +++ b/store/state/boolean.go @@ -30,3 +30,8 @@ func (v Boolean) GetSafe(ctx Context) (res bool, err error) { func (v Boolean) Set(ctx Context, value bool) { v.Value.Set(ctx, value) } + +func (v Boolean) Query(ctx CLIContext) (res bool, proof *Proof, err error) { + proof, err = v.Value.Query(ctx, &res) + return +} From 6de0ed530aa2e05bb353041f4fe103b2407df410 Mon Sep 17 00:00:00 2001 From: mossid Date: Thu, 1 Aug 2019 22:34:01 +0900 Subject: [PATCH 075/166] fix key --- store/state/value.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/store/state/value.go b/store/state/value.go index 5b39818aeebb..c54445f59e9f 100644 --- a/store/state/value.go +++ b/store/state/value.go @@ -48,7 +48,7 @@ func (v Value) mustUnmarshal(bz []byte, ptr interface{}) { // Get() unmarshales and sets the stored value to the pointer if it exists. // It will panic if the value exists but not unmarshalable. func (v Value) Get(ctx Context, ptr interface{}) { - bz := v.store(ctx).Get(v.key) + bz := v.store(ctx).Get(v.KeyBytes()) if bz != nil { v.mustUnmarshal(bz, ptr) } @@ -57,7 +57,7 @@ func (v Value) Get(ctx Context, ptr interface{}) { // GetSafe() unmarshales and sets the stored value to the pointer. // It will return an error if the value does not exist or unmarshalable. func (v Value) GetSafe(ctx Context, ptr interface{}) error { - bz := v.store(ctx).Get(v.key) + bz := v.store(ctx).Get(v.KeyBytes()) if bz == nil { return ErrEmptyValue() } @@ -70,29 +70,29 @@ func (v Value) GetSafe(ctx Context, ptr interface{}) error { // GetRaw() returns the raw bytes that is stored in the state. func (v Value) GetRaw(ctx Context) []byte { - return v.store(ctx).Get(v.key) + return v.store(ctx).Get(v.KeyBytes()) } // Set() marshales and sets the argument to the state. func (v Value) Set(ctx Context, o interface{}) { - v.store(ctx).Set(v.key, v.Marshal(o)) + v.store(ctx).Set(v.KeyBytes(), v.Marshal(o)) } // SetRaw() sets the raw bytes to the state. func (v Value) SetRaw(ctx Context, bz []byte) { - v.store(ctx).Set(v.key, bz) + v.store(ctx).Set(v.KeyBytes(), bz) } // Exists() returns true if the stored value is not nil. // Calles KVStore.Has() internally func (v Value) Exists(ctx Context) bool { - return v.store(ctx).Has(v.key) + return v.store(ctx).Has(v.KeyBytes()) } // Delete() deletes the stored value. // Calles KVStore.Delete() internally func (v Value) Delete(ctx Context) { - v.store(ctx).Delete(v.key) + v.store(ctx).Delete(v.KeyBytes()) } // KeyBytes() returns the prefixed key that the Value is providing to the KVStore From 62b7d75ebe3718b9b016a1bbb7c479650c7ce68c Mon Sep 17 00:00:00 2001 From: mossid Date: Thu, 1 Aug 2019 23:41:12 +0900 Subject: [PATCH 076/166] fix cli / merkle test --- x/ibc/02-client/cli.go | 20 ++++++-------------- x/ibc/02-client/client/cli/query.go | 8 ++++---- x/ibc/02-client/tendermint/tests/types.go | 4 ++-- x/ibc/23-commitment/merkle/merkle_test.go | 8 ++++---- x/ibc/23-commitment/merkle/utils.go | 2 -- 5 files changed, 16 insertions(+), 26 deletions(-) diff --git a/x/ibc/02-client/cli.go b/x/ibc/02-client/cli.go index acf0ecc2218c..aa563f3c170b 100644 --- a/x/ibc/02-client/cli.go +++ b/x/ibc/02-client/cli.go @@ -6,23 +6,15 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) -type CLIObject struct { - obj Object -} - -func (obj Object) CLIObject() CLIObject { - return CLIObject{obj} -} - -func (obj CLIObject) ConsensusState(ctx context.CLIContext) (res ConsensusState, proof merkle.Proof, err error) { - tmproof, err := obj.obj.ConsensusState.Query(ctx, &res) - proof = merkle.NewProofFromValue(tmproof, obj.obj.ConsensusState) +func (obj Object) ConsensusStateCLI(ctx context.CLIContext, path merkle.Path) (res ConsensusState, proof merkle.Proof, err error) { + tmproof, err := obj.ConsensusState.Query(ctx, &res) + proof = merkle.NewProofFromValue(tmproof, path, obj.ConsensusState) return } -func (obj CLIObject) Frozen(ctx context.CLIContext) (res bool, proof merkle.Proof, err error) { - res, tmproof, err := obj.obj.Frozen.Query(ctx) - proof = merkle.NewProofFromValue(tmproof, obj.obj.Frozen) +func (obj Object) FrozenCLI(ctx context.CLIContext, path merkle.Path) (res bool, proof merkle.Proof, err error) { + res, tmproof, err := obj.Frozen.Query(ctx) + proof = merkle.NewProofFromValue(tmproof, path, obj.Frozen) return } diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index 6232d50e2a5f..1cf445dae389 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -20,9 +20,9 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) -func mapping(cdc *codec.Codec, storeKey string, version int64) state.Mapping { +func mapping(cdc *codec.Codec, storeKey string, version int64) (state.Mapping, merkle.Path) { prefix := []byte(strconv.FormatInt(version, 10) + "/") - return state.NewMapping(sdk.NewKVStoreKey(storeKey), cdc, prefix) + return state.NewMapping(sdk.NewKVStoreKey(storeKey), cdc, prefix), merkle.NewPath([][]byte{[]byte(storeKey)}, prefix) } func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { @@ -48,11 +48,11 @@ func GetCmdQueryClient(storeKey string, cdc *codec.Codec) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := context.NewCLIContext().WithCodec(cdc) - mapp := mapping(cdc, storeKey, ibc.Version) + mapp, path := mapping(cdc, storeKey, ibc.Version) man := client.NewManager(mapp) id := args[0] - state, _, err := man.Object(id).CLIObject().ConsensusState(ctx) + state, _, err := man.Object(id).ConsensusStateCLI(ctx, path) if err != nil { return err } diff --git a/x/ibc/02-client/tendermint/tests/types.go b/x/ibc/02-client/tendermint/tests/types.go index f9fd9fc9f0c8..40caa5d6f041 100644 --- a/x/ibc/02-client/tendermint/tests/types.go +++ b/x/ibc/02-client/tendermint/tests/types.go @@ -143,8 +143,8 @@ func (v *Verifier) Validate(header tmtypes.SignedHeader, valset, nextvalset Mock } func (node *Node) Query(t *testing.T, path merkle.Path, k []byte) ([]byte, commitment.Proof) { - code, value, proof := path.QueryMultiStore(node.Cms, k) - require.Equal(t, uint32(0), code) + value, proof, err := merkle.QueryMultiStore(node.Cms, path, k) + require.NoError(t, err) return value, proof } diff --git a/x/ibc/23-commitment/merkle/merkle_test.go b/x/ibc/23-commitment/merkle/merkle_test.go index 3c49a2dee82b..2624d54eccde 100644 --- a/x/ibc/23-commitment/merkle/merkle_test.go +++ b/x/ibc/23-commitment/merkle/merkle_test.go @@ -69,7 +69,7 @@ func TestStore(t *testing.T) { for k, v := range m { v0, p, err := QueryMultiStore(cms, path, []byte(k)) require.NoError(t, err) - require.Equal(t, v, v0) + require.Equal(t, cdc.MustMarshalBinaryBare(v), v0, "Queried value different at %d", repeat) proofs = append(proofs, p) } @@ -90,7 +90,7 @@ func TestStore(t *testing.T) { // Test commitment store for k, v := range m { if len(v) != 0 { - require.True(t, cstore.Prove([]byte(k), v)) + require.True(t, cstore.Prove([]byte(k), cdc.MustMarshalBinaryBare(v))) } else { require.True(t, cstore.Prove([]byte(k), nil)) } @@ -111,7 +111,7 @@ func TestStore(t *testing.T) { for k, v := range m { v0, p, err := QueryMultiStore(cms, path, []byte(k)) require.NoError(t, err) - require.Equal(t, v, v0) + require.Equal(t, cdc.MustMarshalBinaryBare(v), v0) proofs = append(proofs, p) } @@ -121,7 +121,7 @@ func TestStore(t *testing.T) { // Test commitment store for k, v := range m { if len(v) != 0 { - require.True(t, cstore.Prove([]byte(k), v)) + require.True(t, cstore.Prove([]byte(k), cdc.MustMarshalBinaryBare(v))) } else { require.True(t, cstore.Prove([]byte(k), nil)) } diff --git a/x/ibc/23-commitment/merkle/utils.go b/x/ibc/23-commitment/merkle/utils.go index 4dbd6cb292ec..b71d4500963b 100644 --- a/x/ibc/23-commitment/merkle/utils.go +++ b/x/ibc/23-commitment/merkle/utils.go @@ -3,7 +3,6 @@ package merkle import ( "bytes" "errors" - "fmt" abci "github.com/tendermint/tendermint/abci/types" @@ -20,7 +19,6 @@ func QueryMultiStore(cms types.CommitMultiStore, path Path, key []byte) ([]byte, return nil, Proof{}, errors.New(qres.Log) } - fmt.Printf("Q: %X -> %X\n", path.Key(key), qres.Value) return qres.Value, Proof{Key: key, Proof: qres.Proof}, nil } From 6dc912ff9d31b9ab67ace2c6cc652714094d0c2a Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 7 Aug 2019 09:18:53 -0400 Subject: [PATCH 077/166] godoc cleanup --- client/context/query.go | 2 +- store/state/boolean.go | 10 +++++----- store/state/enum.go | 14 +++++++------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/client/context/query.go b/client/context/query.go index 98cd30690f0c..1e2d8136bf07 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -81,7 +81,7 @@ func (ctx CLIContext) GetFromName() string { func (ctx CLIContext) queryABCI(req abci.RequestQuery) (resp abci.ResponseQuery, err error) { node, err := ctx.GetNode() if err != nil { - return + return resp, err } // When a client did not provide a query height, manually query for it so it can diff --git a/store/state/boolean.go b/store/state/boolean.go index 5d582b6869d7..5c9d1fd5e863 100644 --- a/store/state/boolean.go +++ b/store/state/boolean.go @@ -12,21 +12,21 @@ func (v Value) Boolean() Boolean { return Boolean{v} } -// Get() unmarshales and returns the stored boolean value if it exists. -// It will panic if the value exists but is not boolean type. +// Get decodes and returns the stored boolean value if it exists. It will panic +// if the value exists but is not boolean type. func (v Boolean) Get(ctx Context) (res bool) { v.Value.Get(ctx, &res) return } -// GetSafe() unmarshales and returns the stored boolean value. -// It will return an error if the value does not exist or not boolean. +// GetSafe decodes and returns the stored boolean value. It will return an error +// if the value does not exist or not boolean. func (v Boolean) GetSafe(ctx Context) (res bool, err error) { err = v.Value.GetSafe(ctx, &res) return } -// Set() marshales and sets the boolean argument to the state. +// Set encodes and sets the boolean argument to the state. func (v Boolean) Set(ctx Context, value bool) { v.Value.Set(ctx, value) } diff --git a/store/state/enum.go b/store/state/enum.go index bc43f8c69007..a638b854f8f0 100644 --- a/store/state/enum.go +++ b/store/state/enum.go @@ -10,14 +10,14 @@ func (v Value) Enum() Enum { return Enum{v} } -// Get() unmarshales and returns the stored byte value if it exists. -// It will panic if the value exists but is not byte type. +// Get decodes and returns the stored byte value if it exists. It will panic if +// the value exists but is not byte type. func (v Enum) Get(ctx Context) (res byte) { return v.Value.GetRaw(ctx)[0] } -// GetSafe() unmarshales and returns the stored byte value. -// It will returns an error if the value does not exists or not byte. +// GetSafe decodes and returns the stored byte value. It will returns an error +// if the value does not exists or not byte. func (v Enum) GetSafe(ctx Context) (res byte, err error) { bz := v.Value.GetRaw(ctx) if bz == nil { @@ -26,19 +26,19 @@ func (v Enum) GetSafe(ctx Context) (res byte, err error) { return bz[0], nil // TODO: check length } -// Set() marshales and sets the byte argument to the state. +// Set encodes and sets the byte argument to the state. func (v Enum) Set(ctx Context, value byte) { v.Value.SetRaw(ctx, []byte{value}) } -// Incr() increments the stored value, and returns the updated value. +// Incr increments the stored value, and returns the updated value. func (v Enum) Incr(ctx Context) (res byte) { res = v.Get(ctx) + 1 v.Set(ctx, res) return } -// Transit() checks whether the stored value matching with the "from" argument. +// Transit checks whether the stored value matching with the "from" argument. // If it matches, it stores the "to" argument to the state and returns true. func (v Enum) Transit(ctx Context, from, to byte) bool { if v.Get(ctx) != from { From 63c6f3ce2427292b55023d60db4a73bd5f5790aa Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 7 Aug 2019 09:26:58 -0400 Subject: [PATCH 078/166] godoc cleanup --- store/state/indexer.go | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/store/state/indexer.go b/store/state/indexer.go index 6b837c2021a3..8eea25e520a8 100644 --- a/store/state/indexer.go +++ b/store/state/indexer.go @@ -22,16 +22,16 @@ const ( Bin ) -// Indexer is a integer typed key wrapper for Mapping. -// Except for the type checking, it does not alter the behaviour. -// All keys are encoded depending on the IntEncoding +// Indexer is a integer typed key wrapper for Mapping. Except for the type +// checking, it does not alter the behaviour. All keys are encoded depending on +// the IntEncoding. type Indexer struct { m Mapping enc IntEncoding } -// NewIndexer() constructs the Indexer with a predetermined prefix and IntEncoding +// NewIndexer constructs the Indexer with a predetermined prefix and IntEncoding. func NewIndexer(m Mapping, enc IntEncoding) Indexer { return Indexer{ m: m, @@ -39,72 +39,78 @@ func NewIndexer(m Mapping, enc IntEncoding) Indexer { } } -// Order preserving integer encoding function. +// EncodeInt provides order preserving integer encoding function. func EncodeInt(index uint64, enc IntEncoding) (res []byte) { switch enc { case Dec: - // Returns decimal number index, 20-length 0 padded + // return decimal number index, 20-length 0 padded return []byte(fmt.Sprintf("%020d", index)) + case Hex: - // Returns hexadecimal number index, 20-length 0 padded + // return hexadecimal number index, 20-length 0 padded return []byte(fmt.Sprintf("%016x", index)) + case Bin: - // Returns bigendian encoded number index, 8-length + // return bigendian encoded number index, 8-length res = make([]byte, 8) binary.BigEndian.PutUint64(res, index) return + default: panic("invalid IntEncoding") } } -// Integer decoding function, inversion of EncodeInt +// DecodeInt provides integer decoding function, inversion of EncodeInt. func DecodeInt(bz []byte, enc IntEncoding) (res uint64, err error) { switch enc { case Dec: return strconv.ParseUint(string(bz), 10, 64) + case Hex: return strconv.ParseUint(string(bz), 16, 64) + case Bin: return binary.BigEndian.Uint64(bz), nil + default: panic("invalid IntEncoding") } } -// Value() returns the Value corresponding to the provided index +// Value returns the Value corresponding to the provided index. func (ix Indexer) Value(index uint64) Value { return ix.m.Value(EncodeInt(index, ix.enc)) } -// Get() unmarshales and sets the stored value to the pointer if it exists. -// It will panic if the value exists but not unmarshalable. +// Get decodes and sets the stored value to the pointer if it exists. It will +// panic if the value exists but not unmarshalable. func (ix Indexer) Get(ctx Context, index uint64, ptr interface{}) { ix.Value(index).Get(ctx, ptr) } -// GetSafe() unmarshales and sets the stored value to the pointer. -// It will return an error if the value does not exist or unmarshalable. +// GetSafe decodes and sets the stored value to the pointer. It will return an +// error if the value does not exist or unmarshalable. func (ix Indexer) GetSafe(ctx Context, index uint64, ptr interface{}) error { return ix.Value(index).GetSafe(ctx, ptr) } -// Set() marshales and sets the argument to the state. +// Set encodes and sets the argument to the state. func (ix Indexer) Set(ctx Context, index uint64, o interface{}) { ix.Value(index).Set(ctx, o) } -// Has() returns true if the stored value is not nil +// Has returns true if the stored value is not nil. func (ix Indexer) Has(ctx Context, index uint64) bool { return ix.Value(index).Exists(ctx) } -// Delete() delets the stored value. +// Delete removes the stored value. func (ix Indexer) Delete(ctx Context, index uint64) { ix.Value(index).Delete(ctx) } -// Prefix() returns a new Indexer with the updated prefix +// Prefix returns a new Indexer with the updated prefix func (ix Indexer) Prefix(prefix []byte) Indexer { return Indexer{ m: ix.m.Prefix(prefix), From b6441c04c525d2d9b945de280d3dc22b21618658 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 7 Aug 2019 09:30:28 -0400 Subject: [PATCH 079/166] godoc cleanup --- store/state/mapping.go | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/store/state/mapping.go b/store/state/mapping.go index 95a96974416b..ae7b3dc18151 100644 --- a/store/state/mapping.go +++ b/store/state/mapping.go @@ -6,14 +6,15 @@ import ( ) // Mapping is key []byte -> value []byte mapping using a base(possibly prefixed). -// All store accessing operations are redirected to the Value corresponding to the key argument +// All store accessing operations are redirected to the Value corresponding to +// the key argument. type Mapping struct { storeKey sdk.StoreKey cdc *codec.Codec prefix []byte } -// NewMapping() constructs a Mapping with a provided prefix +// NewMapping constructs a Mapping with a provided prefix. func NewMapping(storeKey sdk.StoreKey, cdc *codec.Codec, prefix []byte) Mapping { return Mapping{ storeKey: storeKey, @@ -22,25 +23,25 @@ func NewMapping(storeKey sdk.StoreKey, cdc *codec.Codec, prefix []byte) Mapping } } -// Value() returns the Value corresponding to the provided key +// Value returns the Value corresponding to the provided key. func (m Mapping) Value(key []byte) Value { return NewValue(m, key) } -// Get() unmarshales and sets the stored value to the pointer if it exists. -// It will panic if the value exists but not unmarshalable. +// Get decodes and sets the stored value to the pointer if it exists. It will +// panic if the value exists but not unmarshalable. func (m Mapping) Get(ctx Context, key []byte, ptr interface{}) { m.Value(key).Get(ctx, ptr) } -// GetSafe() unmarshales and sets the stored value to the pointer. -// It will return an error if the value does not exist or unmarshalable. +// GetSafe decodes and sets the stored value to the pointer. It will return an +// error if the value does not exist or unmarshalable. func (m Mapping) GetSafe(ctx Context, key []byte, ptr interface{}) error { return m.Value(key).GetSafe(ctx, ptr) } -// Set() marshales and sets the argument to the state. -// Calls Delete() if the argument is nil. +// Set encodes and sets the argument to the state. It calls Delete if the +// argument is nil. func (m Mapping) Set(ctx Context, key []byte, o interface{}) { if o == nil { m.Delete(ctx, key) @@ -49,12 +50,12 @@ func (m Mapping) Set(ctx Context, key []byte, o interface{}) { m.Value(key).Set(ctx, o) } -// Has() returns true if the stored value is not nil +// Has returns true if the stored value is not nil. func (m Mapping) Has(ctx Context, key []byte) bool { return m.Value(key).Exists(ctx) } -// Delete() deletes the stored value. +// Delete removes the stored value. func (m Mapping) Delete(ctx Context, key []byte) { m.Value(key).Delete(ctx) } @@ -63,16 +64,19 @@ func (m Mapping) Cdc() *codec.Codec { return m.cdc } +// StoreName returns the mapping's store name. func (m Mapping) StoreName() string { return m.storeKey.Name() } +// PrefixBytes returns the mapping's prefix bytes. func (m Mapping) PrefixBytes() (res []byte) { res = make([]byte, len(m.prefix)) copy(res, m.prefix) return } +// KeyBytes returns the mapping's key bytes. func (m Mapping) KeyBytes(key []byte) (res []byte) { return join(m.prefix, key) } @@ -84,7 +88,7 @@ func join(a, b []byte) (res []byte) { return } -// Prefix() returns a new mapping with the updated prefix. +// Prefix returns a new mapping with an updated prefix. func (m Mapping) Prefix(prefix []byte) Mapping { return Mapping{ storeKey: m.storeKey, From 4d723ae8484661f396f1038449b07a29a0d14bc5 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 7 Aug 2019 09:32:25 -0400 Subject: [PATCH 080/166] godoc cleanup --- store/state/string.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/store/state/string.go b/store/state/string.go index c084362d0e03..233a88adae6a 100644 --- a/store/state/string.go +++ b/store/state/string.go @@ -10,14 +10,14 @@ func (v Value) String() String { return String{v} } -// Get() unmarshales and returns the stored string value if it exists. -// It will panic if the value exists but is not strin type. +// Get decodes and returns the stored string value if it exists. It will panic +// if the value exists but is not string type. func (v String) Get(ctx Context) (res string) { return string(v.Value.GetRaw(ctx)) } -// GetSafe() unmarshales and returns the stored string value. -// It will return an error if the value does not exist or not string +// GetSafe decodes and returns the stored string value. It will return an error +// if the value does not exist or not string. func (v String) GetSafe(ctx Context) (res string, err error) { bz := v.Value.GetRaw(ctx) if bz == nil { @@ -26,7 +26,7 @@ func (v String) GetSafe(ctx Context) (res string, err error) { return string(bz), nil } -// Set() marshales and sets the string argument to the state. +// Set encodes and sets the string argument to the state. func (v String) Set(ctx Context, value string) { v.Value.SetRaw(ctx, []byte(value)) } From 244d2c256c352aa521be779bc2bf7d20a66b0dfb Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 7 Aug 2019 09:35:20 -0400 Subject: [PATCH 081/166] godoc cleanup --- store/state/value.go | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/store/state/value.go b/store/state/value.go index 5b39818aeebb..8aa591304fd4 100644 --- a/store/state/value.go +++ b/store/state/value.go @@ -8,15 +8,15 @@ import ( "github.com/cosmos/cosmos-sdk/codec" ) -// Value is a capability for reading and writing on a specific key-value point in the state. -// Value consists of Base and key []byte. -// An actor holding a Value has a full access right on that state point. +// Value is a capability for reading and writing on a specific key-value point +// in the state. Value consists of Base and key []byte. An actor holding a Value +// has a full access right on that state point. type Value struct { m Mapping key []byte } -// NewValue() constructs a Value +// NewValue constructs a Value. func NewValue(m Mapping, key []byte) Value { return Value{ m: m, @@ -45,8 +45,8 @@ func (v Value) mustUnmarshal(bz []byte, ptr interface{}) { v.m.cdc.MustUnmarshalBinaryBare(bz, ptr) } -// Get() unmarshales and sets the stored value to the pointer if it exists. -// It will panic if the value exists but not unmarshalable. +// Get decodes and sets the stored value to the pointer if it exists. It will +// panic if the value exists but not unmarshalable. func (v Value) Get(ctx Context, ptr interface{}) { bz := v.store(ctx).Get(v.key) if bz != nil { @@ -54,8 +54,8 @@ func (v Value) Get(ctx Context, ptr interface{}) { } } -// GetSafe() unmarshales and sets the stored value to the pointer. -// It will return an error if the value does not exist or unmarshalable. +// GetSafe decodes and sets the stored value to the pointer. It will return an +// error if the value does not exist or unmarshalable. func (v Value) GetSafe(ctx Context, ptr interface{}) error { bz := v.store(ctx).Get(v.key) if bz == nil { @@ -68,34 +68,33 @@ func (v Value) GetSafe(ctx Context, ptr interface{}) error { return nil } -// GetRaw() returns the raw bytes that is stored in the state. +// GetRaw returns the raw bytes that is stored in the state. func (v Value) GetRaw(ctx Context) []byte { return v.store(ctx).Get(v.key) } -// Set() marshales and sets the argument to the state. +// Set encodes and sets the argument to the state. func (v Value) Set(ctx Context, o interface{}) { v.store(ctx).Set(v.key, v.Marshal(o)) } -// SetRaw() sets the raw bytes to the state. +// SetRaw sets the raw bytes to the state. func (v Value) SetRaw(ctx Context, bz []byte) { v.store(ctx).Set(v.key, bz) } -// Exists() returns true if the stored value is not nil. -// Calles KVStore.Has() internally +// Exists returns true if the stored value is not nil. It calls KVStore.Has() +// internally. func (v Value) Exists(ctx Context) bool { return v.store(ctx).Has(v.key) } -// Delete() deletes the stored value. -// Calles KVStore.Delete() internally +// Delete removes the stored value. It calls KVStore.Delete() internally. func (v Value) Delete(ctx Context) { v.store(ctx).Delete(v.key) } -// KeyBytes() returns the prefixed key that the Value is providing to the KVStore +// KeyBytes returns the prefixed key that the Value is providing to the KVStore. func (v Value) KeyBytes() []byte { return v.m.KeyBytes(v.key) } From 7bf4d57d5e27e36074b719b8bd29ff98f2933420 Mon Sep 17 00:00:00 2001 From: mossid Date: Thu, 15 Aug 2019 20:23:38 +0900 Subject: [PATCH 082/166] merge from ics04 branch --- store/state/indexer.go | 9 +++++++-- store/state/mapping.go | 5 +++++ store/state/mapping_test.go | 2 +- store/state/types.go | 2 ++ store/state/value.go | 8 ++++++++ 5 files changed, 23 insertions(+), 3 deletions(-) diff --git a/store/state/indexer.go b/store/state/indexer.go index 6b837c2021a3..c948937306da 100644 --- a/store/state/indexer.go +++ b/store/state/indexer.go @@ -31,8 +31,9 @@ type Indexer struct { enc IntEncoding } -// NewIndexer() constructs the Indexer with a predetermined prefix and IntEncoding -func NewIndexer(m Mapping, enc IntEncoding) Indexer { + +// Indexer() constructs the Indexer with an IntEncoding +func (m Mapping) Indexer(enc IntEncoding) Indexer { return Indexer{ m: m, enc: enc, @@ -94,6 +95,10 @@ func (ix Indexer) Set(ctx Context, index uint64, o interface{}) { ix.Value(index).Set(ctx, o) } +func (ix Indexer) SetRaw(ctx Context, index uint64, value []byte) { + ix.Value(index).SetRaw(ctx, value) +} + // Has() returns true if the stored value is not nil func (ix Indexer) Has(ctx Context, index uint64) bool { return ix.Value(index).Exists(ctx) diff --git a/store/state/mapping.go b/store/state/mapping.go index 95a96974416b..8cebbf97c1d7 100644 --- a/store/state/mapping.go +++ b/store/state/mapping.go @@ -49,6 +49,10 @@ func (m Mapping) Set(ctx Context, key []byte, o interface{}) { m.Value(key).Set(ctx, o) } +func (m Mapping) SetRaw(ctx Context, key []byte, value []byte) { + m.Value(key).SetRaw(ctx, value) +} + // Has() returns true if the stored value is not nil func (m Mapping) Has(ctx Context, key []byte) bool { return m.Value(key).Exists(ctx) @@ -92,3 +96,4 @@ func (m Mapping) Prefix(prefix []byte) Mapping { prefix: join(m.prefix, prefix), } } + diff --git a/store/state/mapping_test.go b/store/state/mapping_test.go index 0a29fead8a6e..2f44b9fcccc2 100644 --- a/store/state/mapping_test.go +++ b/store/state/mapping_test.go @@ -60,7 +60,7 @@ type indexerT struct { var _ mapping = indexerT{} func newIndexer(enc IntEncoding) indexerT { - return indexerT{NewIndexer(NewMapping(testkey, testcdc, nil), enc)} + return indexerT{NewMapping(testkey, testcdc, nil).Indexer(enc)} } func (m indexerT) Get(ctx Context, key interface{}, ptr interface{}) { diff --git a/store/state/types.go b/store/state/types.go index f5dbb4dc8648..a058cf6ddf0a 100644 --- a/store/state/types.go +++ b/store/state/types.go @@ -4,6 +4,7 @@ import ( "github.com/tendermint/tendermint/crypto/merkle" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -11,3 +12,4 @@ type KVStore = sdk.KVStore type Context = sdk.Context type CLIContext = context.CLIContext type Proof = merkle.Proof +type Codec = codec.Codec diff --git a/store/state/value.go b/store/state/value.go index c54445f59e9f..eb8cd12cf76a 100644 --- a/store/state/value.go +++ b/store/state/value.go @@ -95,6 +95,14 @@ func (v Value) Delete(ctx Context) { v.store(ctx).Delete(v.KeyBytes()) } +func (v Value) StoreName() string { + return v.m.StoreName() +} + +func (v Value) PrefixBytes() []byte { + return v.m.PrefixBytes() +} + // KeyBytes() returns the prefixed key that the Value is providing to the KVStore func (v Value) KeyBytes() []byte { return v.m.KeyBytes(v.key) From 32cb0ca7900ab12481534b5ef8172aa7be8f8d17 Mon Sep 17 00:00:00 2001 From: mossid Date: Thu, 15 Aug 2019 20:29:45 +0900 Subject: [PATCH 083/166] merge from ics04 branch --- x/ibc/23-commitment/merkle/merkle.go | 15 +++- x/ibc/23-commitment/merkle/merkle_test.go | 43 +++++----- x/ibc/23-commitment/merkle/utils.go | 98 +++++++++++++++++------ x/ibc/23-commitment/store.go | 16 ++-- x/ibc/23-commitment/value.go | 43 +++++++++- 5 files changed, 159 insertions(+), 56 deletions(-) diff --git a/x/ibc/23-commitment/merkle/merkle.go b/x/ibc/23-commitment/merkle/merkle.go index a5acbdd43c3a..1606497ec4f8 100644 --- a/x/ibc/23-commitment/merkle/merkle.go +++ b/x/ibc/23-commitment/merkle/merkle.go @@ -1,13 +1,15 @@ package merkle import ( + "bytes" "errors" "github.com/tendermint/tendermint/crypto/merkle" "github.com/cosmos/cosmos-sdk/store/rootmulti" +// "github.com/cosmos/cosmos-sdk/store/state" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) const merkleKind = "merkle" @@ -91,7 +93,7 @@ func (proof Proof) Verify(croot commitment.Root, cpath commitment.Path, value [] for _, key := range path.KeyPath { keypath = keypath.AppendKey(key, merkle.KeyEncodingHex) } - keypath = keypath.AppendKey(append(path.KeyPrefix, proof.Key...), merkle.KeyEncodingHex) + keypath = keypath.AppendKey(append(path.KeyPrefix, proof.Key...), merkle.KeyEncodingHex) // TODO: hard coded for now, should be extensible runtime := rootmulti.DefaultProofRuntime() @@ -101,3 +103,12 @@ func (proof Proof) Verify(croot commitment.Root, cpath commitment.Path, value [] } return runtime.VerifyAbsence(proof.Proof, root.Hash, keypath.String()) } + +type Value interface { + KeyBytes() []byte +} + +func NewProofFromValue(proof *merkle.Proof, prefix []byte, value Value) Proof { + // TODO: check HasPrefix + return Proof{proof, bytes.TrimPrefix(value.KeyBytes(), prefix)} +} diff --git a/x/ibc/23-commitment/merkle/merkle_test.go b/x/ibc/23-commitment/merkle/merkle_test.go index a407d17c1868..222a6bf5abd9 100644 --- a/x/ibc/23-commitment/merkle/merkle_test.go +++ b/x/ibc/23-commitment/merkle/merkle_test.go @@ -12,10 +12,11 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" + "github.com/cosmos/cosmos-sdk/store/state" "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) func defaultComponents() (sdk.StoreKey, sdk.Context, types.CommitMultiStore, *codec.Codec) { @@ -40,23 +41,25 @@ func commit(cms types.CommitMultiStore) Root { // TestStore tests Merkle proof on the commitment.Store // Sets/upates key-value pairs and prove with the query result proofs func TestStore(t *testing.T) { - k, ctx, cms, _ := defaultComponents() - kvstore := ctx.KVStore(k) - path := Path{KeyPath: [][]byte{[]byte("test")}, KeyPrefix: []byte{0x01, 0x03, 0x05}} + k, ctx, cms, cdc := defaultComponents() + storeName := k.Name() + prefix := []byte{0x01, 0x03, 0x05, 0xAA, 0xBB} + mapp := state.NewMapping(k, cdc, prefix) + path := NewPath([][]byte{[]byte(storeName)}, prefix) m := make(map[string][]byte) - kvpn := 1000 + kvpn := 10 - // Repeat 100 times to test on multiple commits + // Repeat to test on multiple commits for repeat := 0; repeat < 10; repeat++ { // Initializes random generated key-value pairs for i := 0; i < kvpn; i++ { - k, v := make([]byte, 64), make([]byte, 64) + k, v := make([]byte, 16), make([]byte, 16) rand.Read(k) rand.Read(v) m[string(k)] = v - kvstore.Set(path.Key(k), v) + mapp.Value(k).Set(ctx, v) } // Commit store @@ -65,18 +68,18 @@ func TestStore(t *testing.T) { // Test query, and accumulate proofs proofs := make([]commitment.Proof, 0, kvpn) for k, v := range m { - c, v0, p := path.Query(cms, []byte(k)) - require.Equal(t, uint32(0), c) - require.Equal(t, v, v0) + v0, p, err := QueryMultiStore(cms, storeName, prefix, []byte(k)) + require.NoError(t, err) + require.Equal(t, cdc.MustMarshalBinaryBare(v), v0, "Queried value different at %d", repeat) proofs = append(proofs, p) } // Add some exclusion proofs - for i := 0; i < 100; i++ { + for i := 0; i < 10; i++ { k := make([]byte, 64) rand.Read(k) - c, v, p := path.Query(cms, k) - require.Equal(t, uint32(0), c) + v, p, err := QueryMultiStore(cms, storeName, prefix, k) + require.NoError(t, err) require.Nil(t, v) proofs = append(proofs, p) m[string(k)] = []byte{} @@ -88,7 +91,7 @@ func TestStore(t *testing.T) { // Test commitment store for k, v := range m { if len(v) != 0 { - require.True(t, cstore.Prove([]byte(k), v)) + require.True(t, cstore.Prove([]byte(k), cdc.MustMarshalBinaryBare(v))) } else { require.True(t, cstore.Prove([]byte(k), nil)) } @@ -99,7 +102,7 @@ func TestStore(t *testing.T) { v := make([]byte, 64) rand.Read(v) m[k] = v - kvstore.Set(path.Key([]byte(k)), v) + mapp.Value([]byte(k)).Set(ctx, v) } root = commit(cms) @@ -107,9 +110,9 @@ func TestStore(t *testing.T) { // Test query, and accumulate proofs proofs = make([]commitment.Proof, 0, kvpn) for k, v := range m { - c, v0, p := path.Query(cms, []byte(k)) - require.Equal(t, uint32(0), c) - require.Equal(t, v, v0) + v0, p, err := QueryMultiStore(cms, storeName, prefix, []byte(k)) + require.NoError(t, err) + require.Equal(t, cdc.MustMarshalBinaryBare(v), v0) proofs = append(proofs, p) } @@ -119,7 +122,7 @@ func TestStore(t *testing.T) { // Test commitment store for k, v := range m { if len(v) != 0 { - require.True(t, cstore.Prove([]byte(k), v)) + require.True(t, cstore.Prove([]byte(k), cdc.MustMarshalBinaryBare(v))) } else { require.True(t, cstore.Prove([]byte(k), nil)) } diff --git a/x/ibc/23-commitment/merkle/utils.go b/x/ibc/23-commitment/merkle/utils.go index 1a0880a3659f..636fae569c28 100644 --- a/x/ibc/23-commitment/merkle/utils.go +++ b/x/ibc/23-commitment/merkle/utils.go @@ -1,41 +1,35 @@ package merkle import ( + "errors" + abci "github.com/tendermint/tendermint/abci/types" "github.com/cosmos/cosmos-sdk/store/types" ) -// RequestQuery() constructs the abci.RequestQuery. -// -// RequestQuery.Path is a slash separated key list, ending with "/key" -// -// RequestQuery.Data is the concatanation of path.KeyPrefix and key argument -// -// RequestQuery.Prove is set to true -func (path Path) RequestQuery(key []byte) abci.RequestQuery { - pathstr := "" - for _, inter := range path.KeyPath { - // The Queryable() stores uses slash-separated keypath format for querying - pathstr = pathstr + "/" + string(inter) +func QueryMultiStore(cms types.CommitMultiStore, storeName string, prefix []byte, key []byte) ([]byte, Proof, error) { + queryable, ok := cms.(types.Queryable) + if !ok { + panic("CommitMultiStore not queryable") + } + qres := queryable.Query(RequestQueryMultiStore(storeName, prefix, key)) + if !qres.IsOK() { + return nil, Proof{}, errors.New(qres.Log) } - // Suffixing pathstr with "/key". - // iavl.Store.Query() switches over the last path element, - // and performs key-value query only if it is "/key" - pathstr = pathstr + "/key" - - data := append(path.KeyPrefix, key...) - return abci.RequestQuery{Path: pathstr, Data: data, Prove: true} + return qres.Value, Proof{Key: key, Proof: qres.Proof}, nil } -func (path Path) Query(cms types.CommitMultiStore, key []byte) (uint32, []byte, Proof) { - queryable, ok := cms.(types.Queryable) - if !ok { - panic("CommitMultiStore not queryable") +func RequestQueryMultiStore(storeName string, prefix []byte, key []byte) abci.RequestQuery { + // Suffixing path with "/key". + // iavl.Store.Query() switches over the last path element, + // and performs key-value query only if it is "/key" + return abci.RequestQuery{ + Path: "/" + storeName + "/key", + Data: join(prefix, key), + Prove: true, } - qres := queryable.Query(path.RequestQuery(key)) - return qres.Code, qres.Value, Proof{Key: key, Proof: qres.Proof} } func (path Path) Key(key []byte) []byte { @@ -48,3 +42,57 @@ func join(a, b []byte) (res []byte) { copy(res[len(a):], b) return } + +/* + +// RequestQuery() constructs the abci.RequestQuery. +// +// RequestQuery.Path is a slash separated key list, ending with "/key" +// +// RequestQuery.Data is the concatanation of path.KeyPrefix and key argument +// +// RequestQuery.Prove is set to true +func (path Path) RequestQuery(key []byte) abci.RequestQuery { + req := path.RequestQueryMultiStore(key) + // BaseApp switches over the first path element, + // and performs KVStore query only if it is "/store" + req.Path = "/store" + req.Path + return req +} + + + +func (path Path) Query(ctx context.CLIContext, key []byte) (code uint32, value []byte, proof Proof, err error) { + resp, err := ctx.QueryABCI(path.RequestQuery(key)) + if err != nil { + return code, value, proof, err + } + if !resp.IsOK() { + return resp.Code, value, proof, errors.New(resp.Log) + } + return resp.Code, resp.Value, Proof{Key: key, Proof: resp.Proof}, nil +} + +func (path Path) QueryValue(ctx context.CLIContext, cdc *codec.Codec, key []byte, ptr interface{}) (Proof, error) { + _, value, proof, err := path.Query(ctx, key) + if err != nil { + return Proof{}, err + } + err = cdc.UnmarshalBinaryBare(value, ptr) // TODO + return proof, err +} + + + + + +func (path Path) Path() string { + pathstr := "" + for _, inter := range path.KeyPath { + // The Queryable() stores uses slash-separated keypath format for querying + pathstr = pathstr + "/" + string(inter) + } + + return pathstr +} +*/ diff --git a/x/ibc/23-commitment/store.go b/x/ibc/23-commitment/store.go index efb52a151a20..923a065334ea 100644 --- a/x/ibc/23-commitment/store.go +++ b/x/ibc/23-commitment/store.go @@ -3,6 +3,7 @@ package commitment import ( "bytes" "errors" + "fmt" ) // Store proves key-value pairs' inclusion or non-inclusion with @@ -11,7 +12,7 @@ type Store interface { Prove(key, value []byte) bool } -var _ Store = prefix{} +var _ Store = prefix{} // TODO: pointer type prefix struct { store Store @@ -29,7 +30,7 @@ func (prefix prefix) Prove(key, value []byte) bool { return prefix.store.Prove(join(prefix.prefix, key), value) } -var _ Store = store{} +var _ Store = (*store)(nil) type store struct { root Root @@ -41,13 +42,13 @@ type store struct { // NewStore constructs a new Store with the root, path, and proofs. // The proofs are not proven immediately because proofs require value bytes to verify. // If the kinds of the arguments don't match, returns error. -func NewStore(root Root, path Path, proofs []Proof) (res store, err error) { +func NewStore(root Root, path Path, proofs []Proof) (res *store, err error) { if root.CommitmentKind() != path.CommitmentKind() { err = errors.New("path type not matching with root's") return } - res = store{ + res = &store{ root: root, path: path, proofs: make(map[string]Proof), @@ -66,19 +67,20 @@ func NewStore(root Root, path Path, proofs []Proof) (res store, err error) { } // Get() returns the value only if it is already proven. -func (store store) Get(key []byte) ([]byte, bool) { +func (store *store) Get(key []byte) ([]byte, bool) { res, ok := store.verified[string(key)] return res, ok } // Prove() proves the key-value pair with the stored proof. -func (store store) Prove(key, value []byte) bool { +func (store *store) Prove(key, value []byte) bool { stored, ok := store.Get(key) if ok && bytes.Equal(stored, value) { return true } proof, ok := store.proofs[string(key)] if !ok { + fmt.Println(111, string(key)) return false } err := proof.Verify(store.root, store.path, value) @@ -91,7 +93,7 @@ func (store store) Prove(key, value []byte) bool { } // Proven() returns true if the key-value pair is already proven -func (store store) Proven(key []byte) bool { +func (store *store) Proven(key []byte) bool { _, ok := store.Get(key) return ok } diff --git a/x/ibc/23-commitment/value.go b/x/ibc/23-commitment/value.go index 65297b91b361..947d322c06bd 100644 --- a/x/ibc/23-commitment/value.go +++ b/x/ibc/23-commitment/value.go @@ -34,8 +34,22 @@ func (m Mapping) Prefix(prefix []byte) Mapping { } } -// Value is for proving commitment proof on a speicifc key-value point in the other state -// using the already initialized commitment store. +type Indexer struct { + Mapping + enc state.IntEncoding +} + +func (m Mapping) Indexer(enc state.IntEncoding) Indexer { + return Indexer{ + Mapping: m, + enc: enc, + } +} + +func (ix Indexer) Value(index uint64) Value { + return ix.Mapping.Value(state.EncodeInt(index, ix.enc)) +} + type Value struct { m Mapping key []byte @@ -52,6 +66,7 @@ func (v Value) Is(ctx sdk.Context, value interface{}) bool { // IsRaw() proves the proof with the Value's key and the provided raw value bytes. func (v Value) IsRaw(ctx sdk.Context, value []byte) bool { + return v.m.store(ctx).Prove(v.key, value) } @@ -71,6 +86,30 @@ func (v Enum) Is(ctx sdk.Context, value byte) bool { return v.Value.IsRaw(ctx, []byte{value}) } +type String struct { + Value +} + +func (v Value) String() String { + return String{v} +} + +func (v String) Is(ctx sdk.Context, value string) bool { + return v.Value.IsRaw(ctx, []byte(value)) +} + +type Boolean struct { + Value +} + +func (v Value) Boolean() Boolean { + return Boolean{v} +} + +func (v Boolean) Is(ctx sdk.Context, value bool) bool { + return v.Value.Is(ctx, value) +} + // Integer is a uint64 types wrapper for Value. type Integer struct { Value From 17f18d735e10b733495c7fc2c495230a5ff4aae9 Mon Sep 17 00:00:00 2001 From: mossid Date: Thu, 15 Aug 2019 20:42:09 +0900 Subject: [PATCH 084/166] merge from ics04 branch --- x/ibc/02-client/cli.go | 15 ++-- x/ibc/02-client/client/cli/query.go | 8 +-- x/ibc/02-client/keys.go | 5 ++ x/ibc/02-client/manager.go | 4 +- x/ibc/02-client/tendermint/codec.go | 1 - .../tendermint/tests/tendermint_test.go | 8 +-- x/ibc/02-client/tendermint/tests/types.go | 71 +++++++++++-------- x/ibc/02-client/tendermint/tests/utils.go | 8 +++ 8 files changed, 74 insertions(+), 46 deletions(-) create mode 100644 x/ibc/02-client/keys.go create mode 100644 x/ibc/02-client/tendermint/tests/utils.go diff --git a/x/ibc/02-client/cli.go b/x/ibc/02-client/cli.go index aa563f3c170b..db03c1441329 100644 --- a/x/ibc/02-client/cli.go +++ b/x/ibc/02-client/cli.go @@ -1,20 +1,25 @@ package client import ( + "bytes" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) -func (obj Object) ConsensusStateCLI(ctx context.CLIContext, path merkle.Path) (res ConsensusState, proof merkle.Proof, err error) { +func (obj Object) prefix() []byte { + return bytes.Split(obj.ConsensusState.KeyBytes(), LocalRoot())[0] +} + +func (obj Object) ConsensusStateCLI(ctx context.CLIContext) (res ConsensusState, proof merkle.Proof, err error) { tmproof, err := obj.ConsensusState.Query(ctx, &res) - proof = merkle.NewProofFromValue(tmproof, path, obj.ConsensusState) + proof = merkle.NewProofFromValue(tmproof, obj.prefix(), obj.ConsensusState) return } -func (obj Object) FrozenCLI(ctx context.CLIContext, path merkle.Path) (res bool, proof merkle.Proof, err error) { +func (obj Object) FrozenCLI(ctx context.CLIContext) (res bool, proof merkle.Proof, err error) { res, tmproof, err := obj.Frozen.Query(ctx) - proof = merkle.NewProofFromValue(tmproof, path, obj.Frozen) + proof = merkle.NewProofFromValue(tmproof, obj.prefix(), obj.Frozen) return } - diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index 1cf445dae389..c4238dae8cdb 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -20,9 +20,9 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) -func mapping(cdc *codec.Codec, storeKey string, version int64) (state.Mapping, merkle.Path) { +func mapping(cdc *codec.Codec, storeKey string, version int64) state.Mapping { prefix := []byte(strconv.FormatInt(version, 10) + "/") - return state.NewMapping(sdk.NewKVStoreKey(storeKey), cdc, prefix), merkle.NewPath([][]byte{[]byte(storeKey)}, prefix) + return state.NewMapping(sdk.NewKVStoreKey(storeKey), cdc, prefix) } func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { @@ -48,11 +48,11 @@ func GetCmdQueryClient(storeKey string, cdc *codec.Codec) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := context.NewCLIContext().WithCodec(cdc) - mapp, path := mapping(cdc, storeKey, ibc.Version) + mapp := mapping(cdc, storeKey, ibc.Version) man := client.NewManager(mapp) id := args[0] - state, _, err := man.Object(id).ConsensusStateCLI(ctx, path) + state, _, err := man.Object(id).ConsensusStateCLI(ctx) if err != nil { return err } diff --git a/x/ibc/02-client/keys.go b/x/ibc/02-client/keys.go new file mode 100644 index 000000000000..a67761f20d36 --- /dev/null +++ b/x/ibc/02-client/keys.go @@ -0,0 +1,5 @@ +package client + +func LocalRoot() []byte { + return []byte("client/") +} diff --git a/x/ibc/02-client/manager.go b/x/ibc/02-client/manager.go index fe69a59caa05..eb9a83ff7d2e 100644 --- a/x/ibc/02-client/manager.go +++ b/x/ibc/02-client/manager.go @@ -17,7 +17,7 @@ type Manager struct { func NewManager(base state.Mapping) Manager { return Manager{ - protocol: base.Prefix([]byte("/client")), + protocol: base.Prefix(LocalRoot()), } } @@ -27,7 +27,7 @@ type CounterpartyManager struct { func NewCounterpartyManager(cdc *codec.Codec) CounterpartyManager { return CounterpartyManager{ - protocol: commitment.NewMapping(cdc, []byte("/client")), + protocol: commitment.NewMapping(cdc, LocalRoot()), } } diff --git a/x/ibc/02-client/tendermint/codec.go b/x/ibc/02-client/tendermint/codec.go index eca3b38917fa..31a25031f9fc 100644 --- a/x/ibc/02-client/tendermint/codec.go +++ b/x/ibc/02-client/tendermint/codec.go @@ -2,7 +2,6 @@ package tendermint import ( "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client" ) diff --git a/x/ibc/02-client/tendermint/tests/tendermint_test.go b/x/ibc/02-client/tendermint/tests/tendermint_test.go index 61a0944fbaa0..e7d09644126a 100644 --- a/x/ibc/02-client/tendermint/tests/tendermint_test.go +++ b/x/ibc/02-client/tendermint/tests/tendermint_test.go @@ -4,16 +4,14 @@ import ( "testing" "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) func testUpdate(t *testing.T, interval int, ok bool) { - node := NewNode(NewMockValidators(100, 10), newPath()) + node := NewNode(NewMockValidators(100, 10), "f8wib", []byte{0x98, 0x78}) - header := node.Commit() + _ = node.Commit() - verifier := node.LastStateVerifier(merkle.NewRoot(header.AppHash)) + verifier := node.LastStateVerifier() for i := 0; i < 100; i++ { header := node.Commit() diff --git a/x/ibc/02-client/tendermint/tests/types.go b/x/ibc/02-client/tendermint/tests/types.go index 40caa5d6f041..027d3a28a5f5 100644 --- a/x/ibc/02-client/tendermint/tests/types.go +++ b/x/ibc/02-client/tendermint/tests/types.go @@ -3,6 +3,7 @@ package tendermint import ( "crypto/rand" "testing" + "bytes" "github.com/stretchr/testify/require" @@ -23,15 +24,11 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) -// nolint: unused -func newPath() merkle.Path { - return merkle.NewPath([][]byte{[]byte("test")}, []byte{0x12, 0x34}) -} - const chainid = "testchain" -func defaultComponents() (sdk.StoreKey, sdk.Context, stypes.CommitMultiStore, *codec.Codec) { - key := sdk.NewKVStoreKey("test") +func defaultComponents(storename string) (sdk.StoreKey, sdk.Context, stypes.CommitMultiStore, *codec.Codec) { + key := sdk.NewKVStoreKey(storename) + db := dbm.NewMemDB() cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) @@ -54,21 +51,28 @@ type Node struct { Commits []tmtypes.SignedHeader - Path merkle.Path + StoreName string + Prefix []byte } -func NewNode(valset MockValidators, path merkle.Path) *Node { - key, ctx, cms, _ := defaultComponents() +func NewNode(valset MockValidators, storeName string, prefix []byte) *Node { + key, ctx, cms, _ := defaultComponents(storeName) + return &Node{ Valset: valset, Cms: cms, Key: key, Store: ctx.KVStore(key), Commits: nil, - Path: path, + StoreName: storeName, + Prefix: prefix, } } +func (node *Node) Path() merkle.Path { + return merkle.NewPath([][]byte{[]byte(node.StoreName)}, node.Prefix) +} + func (node *Node) Last() tmtypes.SignedHeader { if len(node.Commits) == 0 { return tmtypes.SignedHeader{} @@ -76,7 +80,7 @@ func (node *Node) Last() tmtypes.SignedHeader { return node.Commits[len(node.Commits)-1] } -func (node *Node) Commit() tmtypes.SignedHeader { +func (node *Node) Commit() tendermint.Header { valsethash := node.Valset.ValidatorSet().Hash() nextvalset := node.Valset.Mutate() nextvalsethash := nextvalset.ValidatorSet().Hash() @@ -100,11 +104,20 @@ func (node *Node) Commit() tmtypes.SignedHeader { node.Valset = nextvalset node.Commits = append(node.Commits, commit) - return commit + return tendermint.Header{ + SignedHeader: commit, + ValidatorSet: node.PrevValset.ValidatorSet(), + NextValidatorSet: node.Valset.ValidatorSet(), + } +} + +func (node *Node) LastStateVerifier() *Verifier { + return NewVerifier(node.Last(), node.Valset, node.Root()) } -func (node *Node) LastStateVerifier(root merkle.Root) *Verifier { - return NewVerifier(node.Last(), node.Valset, root) +func (node *Node) Root() merkle.Root { + return merkle.NewRoot(node.Last().AppHash) + } func (node *Node) Context() sdk.Context { @@ -126,14 +139,8 @@ func NewVerifier(header tmtypes.SignedHeader, nextvalset MockValidators, root me } } -func (v *Verifier) Validate(header tmtypes.SignedHeader, valset, nextvalset MockValidators) error { - newcs, err := v.ConsensusState.Validate( - tendermint.Header{ - SignedHeader: header, - ValidatorSet: valset.ValidatorSet(), - NextValidatorSet: nextvalset.ValidatorSet(), - }, - ) +func (v *Verifier) Validate(header tendermint.Header, valset, nextvalset MockValidators) error { + newcs, err := v.ConsensusState.Validate(header) if err != nil { return err } @@ -142,19 +149,23 @@ func (v *Verifier) Validate(header tmtypes.SignedHeader, valset, nextvalset Mock return nil } -func (node *Node) Query(t *testing.T, path merkle.Path, k []byte) ([]byte, commitment.Proof) { - value, proof, err := merkle.QueryMultiStore(node.Cms, path, k) + +func (node *Node) Query(t *testing.T, k []byte) ([]byte, commitment.Proof) { + if bytes.HasPrefix(k, node.Prefix) { + k = bytes.TrimPrefix(k, node.Prefix) + } + value, proof, err := merkle.QueryMultiStore(node.Cms, node.StoreName, node.Prefix, k) require.NoError(t, err) return value, proof } func (node *Node) Set(k, value []byte) { - node.Store.Set(node.Path.Key(k), value) + node.Store.Set(join(node.Prefix, k), value) } // nolint:deadcode,unused func testProof(t *testing.T) { - node := NewNode(NewMockValidators(100, 10), newPath()) + node := NewNode(NewMockValidators(100, 10), "1", []byte{0x00, 0x01}) node.Commit() @@ -170,15 +181,17 @@ func testProof(t *testing.T) { kvps = append(kvps, cmn.KVPair{Key: k, Value: v}) node.Set(k, v) } + header := node.Commit() proofs := []commitment.Proof{} root := merkle.NewRoot(header.AppHash) for _, kvp := range kvps { - v, p := node.Query(t, node.Path, kvp.Key) + v, p := node.Query(t, kvp.Key) + require.Equal(t, kvp.Value, v) proofs = append(proofs, p) } - cstore, err := commitment.NewStore(root, node.Path, proofs) + cstore, err := commitment.NewStore(root, node.Path(), proofs) require.NoError(t, err) for _, kvp := range kvps { diff --git a/x/ibc/02-client/tendermint/tests/utils.go b/x/ibc/02-client/tendermint/tests/utils.go new file mode 100644 index 000000000000..d5262a14a29b --- /dev/null +++ b/x/ibc/02-client/tendermint/tests/utils.go @@ -0,0 +1,8 @@ +package tendermint + +func join(a, b []byte) (res []byte) { + res = make([]byte, len(a)+len(b)) + copy(res, a) + copy(res[len(a):], b) + return +} From 71d2620c073342166c0b65c934cf29629859b68a Mon Sep 17 00:00:00 2001 From: mossid Date: Thu, 15 Aug 2019 20:50:54 +0900 Subject: [PATCH 085/166] fix lint --- x/ibc/23-commitment/codec.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/ibc/23-commitment/codec.go b/x/ibc/23-commitment/codec.go index 5b6573497e86..e2639b78b4f9 100644 --- a/x/ibc/23-commitment/codec.go +++ b/x/ibc/23-commitment/codec.go @@ -4,7 +4,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" ) -// RegisterCodec registeres types declared in this package +// RegisterCodec registers types declared in this package func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*Root)(nil), nil) cdc.RegisterInterface((*Path)(nil), nil) From 10d1c46b52e44cc0a0f9bbbd276165093cfda3c6 Mon Sep 17 00:00:00 2001 From: Joon Date: Tue, 10 Sep 2019 23:06:52 +0200 Subject: [PATCH 086/166] Apply suggestions from code review Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> --- store/state/integer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/store/state/integer.go b/store/state/integer.go index 499fbe414ec2..b83b96abff60 100644 --- a/store/state/integer.go +++ b/store/state/integer.go @@ -45,8 +45,8 @@ func (v Integer) Set(ctx Context, value uint64) { v.Value.SetRaw(ctx, EncodeInt(value, v.enc)) } -// Incr() increments the stored value, and returns the updated value. -func (v Integer) Incr(ctx Context) (res uint64) { +// Increment increments the stored value and returns it. +func (v Integer) Increment(ctx Context) (res uint64) { res = v.Get(ctx) + 1 v.Set(ctx, res) return From 09e6600cc6a9d91697fc26a72db02b499846c53d Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 11 Sep 2019 16:21:38 +0200 Subject: [PATCH 087/166] applying review in progress --- store/state/enum.go | 7 ++++++- store/state/errors.go | 20 ++++++++++++++------ store/state/integer.go | 1 + store/state/string.go | 1 + 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/store/state/enum.go b/store/state/enum.go index a638b854f8f0..e61dfb3bb33b 100644 --- a/store/state/enum.go +++ b/store/state/enum.go @@ -1,5 +1,7 @@ package state +import "errors" + // Enum is a byte typed wrapper for Value. // x <-> []byte{x} type Enum struct { @@ -23,7 +25,10 @@ func (v Enum) GetSafe(ctx Context) (res byte, err error) { if bz == nil { return res, ErrEmptyValue() } - return bz[0], nil // TODO: check length + if len(bz) != 1 { + return res, ErrUnmarshal(errors.New("stored byte slice length is not 1")) + } + return bz[0], nil } // Set encodes and sets the byte argument to the state. diff --git a/store/state/errors.go b/store/state/errors.go index 936066b0ac29..303584c68bff 100644 --- a/store/state/errors.go +++ b/store/state/errors.go @@ -4,13 +4,17 @@ import ( "fmt" ) +// GetSafeErrorType is enum for indicating the type of error type GetSafeErrorType byte const ( + // ErrTypeEmptyValue is used for nil byteslice values ErrTypeEmptyValue GetSafeErrorType = iota + // ErrTypeUnmarshal is used for undeserializable values ErrTypeUnmarshal ) +// Implements Formatter func (ty GetSafeErrorType) Format(msg string) (res string) { switch ty { case ErrTypeEmptyValue: @@ -28,28 +32,32 @@ func (ty GetSafeErrorType) Format(msg string) (res string) { return } +// GetSafeError is error type for GetSafe method type GetSafeError struct { ty GetSafeErrorType inner error } -var _ error = (*GetSafeError)(nil) // TODO: sdk.Error +var _ error = GetSafeError{} -func (err *GetSafeError) Error() string { +// Implements error +func (err GetSafeError) Error() string { if err.inner == nil { return err.ty.Format("") } return err.ty.Format(err.inner.Error()) } -func ErrEmptyValue() *GetSafeError { - return &GetSafeError{ +// ErrEmptyValue constructs GetSafeError with ErrTypeEmptyValue +func ErrEmptyValue() GetSafeError { + return GetSafeError{ ty: ErrTypeEmptyValue, } } -func ErrUnmarshal(err error) *GetSafeError { - return &GetSafeError{ +// ErrUnmarshal constructs GetSafeError with ErrTypeUnmarshal +func ErrUnmarshal(err error) GetSafeError { + return GetSafeError{ ty: ErrTypeUnmarshal, inner: err, } diff --git a/store/state/integer.go b/store/state/integer.go index 499fbe414ec2..f76b199dfd51 100644 --- a/store/state/integer.go +++ b/store/state/integer.go @@ -52,6 +52,7 @@ func (v Integer) Incr(ctx Context) (res uint64) { return } +// Query() retrives state value and proof from a queryable reference func (v Integer) Query(ctx CLIContext) (res uint64, proof *Proof, err error) { value, proof, err := v.Value.QueryRaw(ctx) if err != nil { diff --git a/store/state/string.go b/store/state/string.go index 233a88adae6a..905ac38b0e41 100644 --- a/store/state/string.go +++ b/store/state/string.go @@ -31,6 +31,7 @@ func (v String) Set(ctx Context, value string) { v.Value.SetRaw(ctx, []byte(value)) } +// Query() retrives state value and proof from a queryable reference func (v String) Query(ctx CLIContext) (res string, proof *Proof, err error) { value, proof, err := v.Value.QueryRaw(ctx) return string(value), proof, err From 4116bb78e4307bfee66e88e8bc45154e5f03b183 Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 11 Sep 2019 21:36:41 +0200 Subject: [PATCH 088/166] apply review - make querier interface --- store/state/boolean.go | 5 +++-- store/state/enum.go | 5 +++-- store/state/integer.go | 4 ++-- store/state/string.go | 4 ++-- store/state/types.go | 7 +++++-- store/state/value.go | 8 ++++---- 6 files changed, 19 insertions(+), 14 deletions(-) diff --git a/store/state/boolean.go b/store/state/boolean.go index 092d3dfb03e0..669b7ae85e8d 100644 --- a/store/state/boolean.go +++ b/store/state/boolean.go @@ -31,7 +31,8 @@ func (v Boolean) Set(ctx Context, value bool) { v.Value.Set(ctx, value) } -func (v Boolean) Query(ctx CLIContext) (res bool, proof *Proof, err error) { - proof, err = v.Value.Query(ctx, &res) +// Query() retrives state value and proof from a queryable reference +func (v Boolean) Query(q ABCIQuerier) (res bool, proof *Proof, err error) { + proof, err = v.Value.Query(q, &res) return } diff --git a/store/state/enum.go b/store/state/enum.go index e61dfb3bb33b..c580dca614aa 100644 --- a/store/state/enum.go +++ b/store/state/enum.go @@ -53,7 +53,8 @@ func (v Enum) Transit(ctx Context, from, to byte) bool { return true } -func (v Enum) Query(ctx CLIContext) (res byte, proof *Proof, err error) { - value, proof, err := v.Value.QueryRaw(ctx) +// Query() retrives state value and proof from a queryable reference +func (v Enum) Query(q ABCIQuerier) (res byte, proof *Proof, err error) { + value, proof, err := v.Value.QueryRaw(q) return value[0], proof, err } diff --git a/store/state/integer.go b/store/state/integer.go index 70121de033f4..6758d73cc479 100644 --- a/store/state/integer.go +++ b/store/state/integer.go @@ -53,8 +53,8 @@ func (v Integer) Increment(ctx Context) (res uint64) { } // Query() retrives state value and proof from a queryable reference -func (v Integer) Query(ctx CLIContext) (res uint64, proof *Proof, err error) { - value, proof, err := v.Value.QueryRaw(ctx) +func (v Integer) Query(q ABCIQuerier) (res uint64, proof *Proof, err error) { + value, proof, err := v.Value.QueryRaw(q) if err != nil { return } diff --git a/store/state/string.go b/store/state/string.go index 905ac38b0e41..a67b6b8939fb 100644 --- a/store/state/string.go +++ b/store/state/string.go @@ -32,7 +32,7 @@ func (v String) Set(ctx Context, value string) { } // Query() retrives state value and proof from a queryable reference -func (v String) Query(ctx CLIContext) (res string, proof *Proof, err error) { - value, proof, err := v.Value.QueryRaw(ctx) +func (v String) Query(q ABCIQuerier) (res string, proof *Proof, err error) { + value, proof, err := v.Value.QueryRaw(q) return string(value), proof, err } diff --git a/store/state/types.go b/store/state/types.go index a058cf6ddf0a..9251c808db88 100644 --- a/store/state/types.go +++ b/store/state/types.go @@ -1,15 +1,18 @@ package state import ( + abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/merkle" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" ) type KVStore = sdk.KVStore type Context = sdk.Context -type CLIContext = context.CLIContext type Proof = merkle.Proof type Codec = codec.Codec + +type ABCIQuerier interface { + QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) +} diff --git a/store/state/value.go b/store/state/value.go index c4c1a29a217c..60df0c9718d6 100644 --- a/store/state/value.go +++ b/store/state/value.go @@ -107,14 +107,14 @@ func (v Value) KeyBytes() []byte { return v.m.KeyBytes(v.key) } -func (v Value) QueryRaw(ctx CLIContext) ([]byte, *Proof, error) { +func (v Value) QueryRaw(q ABCIQuerier) ([]byte, *Proof, error) { req := abci.RequestQuery{ Path: "/store" + v.m.StoreName() + "/key", Data: v.KeyBytes(), Prove: true, } - resp, err := ctx.QueryABCI(req) + resp, err := q.QueryABCI(req) if err != nil { return nil, nil, err } @@ -126,8 +126,8 @@ func (v Value) QueryRaw(ctx CLIContext) ([]byte, *Proof, error) { return resp.Value, resp.Proof, nil } -func (v Value) Query(ctx CLIContext, ptr interface{}) (*Proof, error) { - value, proof, err := v.QueryRaw(ctx) +func (v Value) Query(q ABCIQuerier, ptr interface{}) (*Proof, error) { + value, proof, err := v.QueryRaw(q) if err != nil { return nil, err } From 9b63a6b4a70c14d15b913b8267f6ce2b93c5d2c0 Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 17 Sep 2019 17:32:48 +0200 Subject: [PATCH 089/166] fix dependency --- x/ibc/23-commitment/merkle/merkle_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/ibc/23-commitment/merkle/merkle_test.go b/x/ibc/23-commitment/merkle/merkle_test.go index 222a6bf5abd9..efa384a6c317 100644 --- a/x/ibc/23-commitment/merkle/merkle_test.go +++ b/x/ibc/23-commitment/merkle/merkle_test.go @@ -7,8 +7,8 @@ import ( "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" From acaaaf299546fb8f1032e71229ec6e4da36ad4e4 Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 17 Sep 2019 17:33:50 +0200 Subject: [PATCH 090/166] fix dependency --- x/ibc/02-client/tendermint/tests/types.go | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/x/ibc/02-client/tendermint/tests/types.go b/x/ibc/02-client/tendermint/tests/types.go index 027d3a28a5f5..26255358f483 100644 --- a/x/ibc/02-client/tendermint/tests/types.go +++ b/x/ibc/02-client/tendermint/tests/types.go @@ -1,26 +1,26 @@ package tendermint import ( + "bytes" "crypto/rand" "testing" - "bytes" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" tmtypes "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" stypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client" + client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/tendermint" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) @@ -52,20 +52,20 @@ type Node struct { Commits []tmtypes.SignedHeader StoreName string - Prefix []byte + Prefix []byte } func NewNode(valset MockValidators, storeName string, prefix []byte) *Node { key, ctx, cms, _ := defaultComponents(storeName) return &Node{ - Valset: valset, - Cms: cms, - Key: key, - Store: ctx.KVStore(key), - Commits: nil, + Valset: valset, + Cms: cms, + Key: key, + Store: ctx.KVStore(key), + Commits: nil, StoreName: storeName, - Prefix: prefix, + Prefix: prefix, } } @@ -149,7 +149,6 @@ func (v *Verifier) Validate(header tendermint.Header, valset, nextvalset MockVal return nil } - func (node *Node) Query(t *testing.T, k []byte) ([]byte, commitment.Proof) { if bytes.HasPrefix(k, node.Prefix) { k = bytes.TrimPrefix(k, node.Prefix) From 828badd845c0afeceb929f3c9758e6ab40f3a7b5 Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 18 Sep 2019 17:51:40 +0200 Subject: [PATCH 091/166] revise querier interface to work both on cli & store --- store/state/mapping_test.go | 2 +- store/state/types.go | 45 +++++++++++++++++++++++++++- store/state/value.go | 18 +++-------- store/state/value_test.go | 59 +++++++++++++++++++++++++++++++++++-- 4 files changed, 105 insertions(+), 19 deletions(-) diff --git a/store/state/mapping_test.go b/store/state/mapping_test.go index 2f44b9fcccc2..3d4110fdf672 100644 --- a/store/state/mapping_test.go +++ b/store/state/mapping_test.go @@ -88,7 +88,7 @@ func (m indexerT) RandomKey() interface{} { } func TestMapping(t *testing.T) { - ctx := defaultComponents() + ctx, _ := defaultComponents() table := []mapping{newMapping(), newIndexer(Dec), newIndexer(Hex), newIndexer(Bin)} for _, m := range table { diff --git a/store/state/types.go b/store/state/types.go index 9251c808db88..66ffb1f5255f 100644 --- a/store/state/types.go +++ b/store/state/types.go @@ -4,7 +4,9 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/merkle" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" + stypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -14,5 +16,46 @@ type Proof = merkle.Proof type Codec = codec.Codec type ABCIQuerier interface { - QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) + Query(storeName string, key []byte) (abci.ResponseQuery, error) +} + +var _ ABCIQuerier = CLIQuerier{} + +type CLIQuerier struct { + ctx context.CLIContext +} + +func NewCLIQuerier(ctx context.CLIContext) CLIQuerier { + return CLIQuerier{ctx} +} + +func (q CLIQuerier) Query(storeName string, key []byte) (abci.ResponseQuery, error) { + req := abci.RequestQuery{ + Path: "/store/" + storeName + "/key", + Data: key, + Prove: true, + } + + return q.ctx.QueryABCI(req) +} + +var _ ABCIQuerier = StoreQuerier{} + +type StoreQuerier struct { + store stypes.Queryable +} + +func NewStoreQuerier(store stypes.Queryable) StoreQuerier { + return StoreQuerier{store} +} + +func (q StoreQuerier) Query(storeName string, key []byte) (abci.ResponseQuery, error) { + req := abci.RequestQuery{ + Path: "/" + storeName + "/key", + Data: key, + Prove: true, + } + + return q.store.Query(req), nil + } diff --git a/store/state/value.go b/store/state/value.go index 60df0c9718d6..1d467b0ab380 100644 --- a/store/state/value.go +++ b/store/state/value.go @@ -1,11 +1,7 @@ package state import ( - "errors" - - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" ) // Value is a capability for reading and writing on a specific key-value point @@ -29,7 +25,7 @@ func (v Value) store(ctx Context) KVStore { } // Cdc() returns the codec that the value is using to marshal/unmarshal -func (v Value) Cdc() *codec.Codec { +func (v Value) Cdc() *Codec { return v.m.Cdc() } @@ -108,19 +104,13 @@ func (v Value) KeyBytes() []byte { } func (v Value) QueryRaw(q ABCIQuerier) ([]byte, *Proof, error) { - req := abci.RequestQuery{ - Path: "/store" + v.m.StoreName() + "/key", - Data: v.KeyBytes(), - Prove: true, - } - - resp, err := q.QueryABCI(req) + resp, err := q.Query(v.m.StoreName(), v.KeyBytes()) if err != nil { return nil, nil, err } if !resp.IsOK() { - return nil, nil, errors.New(resp.Log) + return nil, nil, sdk.NewError(sdk.CodespaceRoot, sdk.CodeType(resp.Code), resp.Log) } return resp.Value, resp.Proof, nil diff --git a/store/state/value_test.go b/store/state/value_test.go index 88f75c32bdbb..38888890bbdf 100644 --- a/store/state/value_test.go +++ b/store/state/value_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" @@ -30,6 +31,7 @@ func key() (res []byte) { } type value interface { + KeyBytes() []byte Get(Context, interface{}) GetSafe(Context, interface{}) error GetRaw(Context) []byte @@ -37,6 +39,7 @@ type value interface { SetRaw(Context, []byte) Exists(Context) bool Delete(Context) + Query(ABCIQuerier, interface{}) (*Proof, error) Marshal(interface{}) []byte Unmarshal([]byte, interface{}) } @@ -110,6 +113,15 @@ func (v booleanT) Unmarshal(bz []byte, ptr interface{}) { } } +func (v booleanT) Query(q ABCIQuerier, ptr interface{}) (proof *Proof, err error) { + res, proof, err := v.Boolean.Query(q) + if err != nil { + return + } + reflect.ValueOf(ptr).Elem().SetBool(res) + return +} + type integerT struct { Integer } @@ -153,6 +165,15 @@ func (v integerT) Unmarshal(bz []byte, ptr interface{}) { reflect.ValueOf(ptr).Elem().SetUint(res) } +func (v integerT) Query(q ABCIQuerier, ptr interface{}) (proof *Proof, err error) { + res, proof, err := v.Integer.Query(q) + if err != nil { + return + } + reflect.ValueOf(ptr).Elem().SetUint(res) + return +} + type enumT struct { Enum } @@ -192,6 +213,15 @@ func (v enumT) Unmarshal(bz []byte, ptr interface{}) { reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(bz[0])) } +func (v enumT) Query(q ABCIQuerier, ptr interface{}) (proof *Proof, err error) { + res, proof, err := v.Enum.Query(q) + if err != nil { + return + } + reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(res)) + return +} + type stringT struct { String } @@ -231,13 +261,22 @@ func (v stringT) Unmarshal(bz []byte, ptr interface{}) { reflect.ValueOf(ptr).Elem().SetString(string(bz)) } -func defaultComponents() sdk.Context { +func (v stringT) Query(q ABCIQuerier, ptr interface{}) (proof *Proof, err error) { + res, proof, err := v.String.Query(q) + if err != nil { + return + } + reflect.ValueOf(ptr).Elem().SetString(res) + return +} + +func defaultComponents() (sdk.Context, *rootmulti.Store) { db := dbm.NewMemDB() cms := rootmulti.NewStore(db) cms.MountStoreWithDB(testkey, sdk.StoreTypeIAVL, db) cms.LoadLatestVersion() ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) - return ctx + return ctx, cms } func indirect(ptr interface{}) interface{} { @@ -245,7 +284,7 @@ func indirect(ptr interface{}) interface{} { } func TestTypeValue(t *testing.T) { - ctx := defaultComponents() + ctx, cms := defaultComponents() var table = []struct { ty typeValue @@ -296,5 +335,19 @@ func TestTypeValue(t *testing.T) { require.Error(t, err) require.Equal(t, reflect.Zero(reflect.TypeOf(ptr).Elem()).Interface(), indirect(ptr)) require.Nil(t, v.GetRaw(ctx)) + + // Set again and test abci query + v.Set(ctx, tc.orig) + cid := cms.Commit() + ptr = v.Proto() + q := NewStoreQuerier(cms) + proof, err := v.Query(q, ptr) + require.NoError(t, err) + require.Equal(t, tc.orig, indirect(ptr), "Expected equal on tc %d", i) + prt := rootmulti.DefaultProofRuntime() + kp := merkle.KeyPath{}. + AppendKey([]byte(testkey.Name()), merkle.KeyEncodingHex). + AppendKey(v.KeyBytes(), merkle.KeyEncodingHex) + require.NoError(t, prt.VerifyValue(proof, cid.Hash, kp.String(), v.GetRaw(ctx))) } } From 1e64e92e625029fbc7bd8e6de55cdfc3ef63e9f6 Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 18 Sep 2019 17:51:40 +0200 Subject: [PATCH 092/166] revise querier interface to work both on cli & store --- store/state/mapping_test.go | 2 +- store/state/types.go | 45 +++++++++++++++++++++++++++- store/state/value.go | 18 +++-------- store/state/value_test.go | 59 +++++++++++++++++++++++++++++++++++-- 4 files changed, 105 insertions(+), 19 deletions(-) diff --git a/store/state/mapping_test.go b/store/state/mapping_test.go index 2f44b9fcccc2..3d4110fdf672 100644 --- a/store/state/mapping_test.go +++ b/store/state/mapping_test.go @@ -88,7 +88,7 @@ func (m indexerT) RandomKey() interface{} { } func TestMapping(t *testing.T) { - ctx := defaultComponents() + ctx, _ := defaultComponents() table := []mapping{newMapping(), newIndexer(Dec), newIndexer(Hex), newIndexer(Bin)} for _, m := range table { diff --git a/store/state/types.go b/store/state/types.go index 9251c808db88..66ffb1f5255f 100644 --- a/store/state/types.go +++ b/store/state/types.go @@ -4,7 +4,9 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/merkle" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" + stypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -14,5 +16,46 @@ type Proof = merkle.Proof type Codec = codec.Codec type ABCIQuerier interface { - QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) + Query(storeName string, key []byte) (abci.ResponseQuery, error) +} + +var _ ABCIQuerier = CLIQuerier{} + +type CLIQuerier struct { + ctx context.CLIContext +} + +func NewCLIQuerier(ctx context.CLIContext) CLIQuerier { + return CLIQuerier{ctx} +} + +func (q CLIQuerier) Query(storeName string, key []byte) (abci.ResponseQuery, error) { + req := abci.RequestQuery{ + Path: "/store/" + storeName + "/key", + Data: key, + Prove: true, + } + + return q.ctx.QueryABCI(req) +} + +var _ ABCIQuerier = StoreQuerier{} + +type StoreQuerier struct { + store stypes.Queryable +} + +func NewStoreQuerier(store stypes.Queryable) StoreQuerier { + return StoreQuerier{store} +} + +func (q StoreQuerier) Query(storeName string, key []byte) (abci.ResponseQuery, error) { + req := abci.RequestQuery{ + Path: "/" + storeName + "/key", + Data: key, + Prove: true, + } + + return q.store.Query(req), nil + } diff --git a/store/state/value.go b/store/state/value.go index 60df0c9718d6..1d467b0ab380 100644 --- a/store/state/value.go +++ b/store/state/value.go @@ -1,11 +1,7 @@ package state import ( - "errors" - - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" ) // Value is a capability for reading and writing on a specific key-value point @@ -29,7 +25,7 @@ func (v Value) store(ctx Context) KVStore { } // Cdc() returns the codec that the value is using to marshal/unmarshal -func (v Value) Cdc() *codec.Codec { +func (v Value) Cdc() *Codec { return v.m.Cdc() } @@ -108,19 +104,13 @@ func (v Value) KeyBytes() []byte { } func (v Value) QueryRaw(q ABCIQuerier) ([]byte, *Proof, error) { - req := abci.RequestQuery{ - Path: "/store" + v.m.StoreName() + "/key", - Data: v.KeyBytes(), - Prove: true, - } - - resp, err := q.QueryABCI(req) + resp, err := q.Query(v.m.StoreName(), v.KeyBytes()) if err != nil { return nil, nil, err } if !resp.IsOK() { - return nil, nil, errors.New(resp.Log) + return nil, nil, sdk.NewError(sdk.CodespaceRoot, sdk.CodeType(resp.Code), resp.Log) } return resp.Value, resp.Proof, nil diff --git a/store/state/value_test.go b/store/state/value_test.go index 88f75c32bdbb..38888890bbdf 100644 --- a/store/state/value_test.go +++ b/store/state/value_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" @@ -30,6 +31,7 @@ func key() (res []byte) { } type value interface { + KeyBytes() []byte Get(Context, interface{}) GetSafe(Context, interface{}) error GetRaw(Context) []byte @@ -37,6 +39,7 @@ type value interface { SetRaw(Context, []byte) Exists(Context) bool Delete(Context) + Query(ABCIQuerier, interface{}) (*Proof, error) Marshal(interface{}) []byte Unmarshal([]byte, interface{}) } @@ -110,6 +113,15 @@ func (v booleanT) Unmarshal(bz []byte, ptr interface{}) { } } +func (v booleanT) Query(q ABCIQuerier, ptr interface{}) (proof *Proof, err error) { + res, proof, err := v.Boolean.Query(q) + if err != nil { + return + } + reflect.ValueOf(ptr).Elem().SetBool(res) + return +} + type integerT struct { Integer } @@ -153,6 +165,15 @@ func (v integerT) Unmarshal(bz []byte, ptr interface{}) { reflect.ValueOf(ptr).Elem().SetUint(res) } +func (v integerT) Query(q ABCIQuerier, ptr interface{}) (proof *Proof, err error) { + res, proof, err := v.Integer.Query(q) + if err != nil { + return + } + reflect.ValueOf(ptr).Elem().SetUint(res) + return +} + type enumT struct { Enum } @@ -192,6 +213,15 @@ func (v enumT) Unmarshal(bz []byte, ptr interface{}) { reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(bz[0])) } +func (v enumT) Query(q ABCIQuerier, ptr interface{}) (proof *Proof, err error) { + res, proof, err := v.Enum.Query(q) + if err != nil { + return + } + reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(res)) + return +} + type stringT struct { String } @@ -231,13 +261,22 @@ func (v stringT) Unmarshal(bz []byte, ptr interface{}) { reflect.ValueOf(ptr).Elem().SetString(string(bz)) } -func defaultComponents() sdk.Context { +func (v stringT) Query(q ABCIQuerier, ptr interface{}) (proof *Proof, err error) { + res, proof, err := v.String.Query(q) + if err != nil { + return + } + reflect.ValueOf(ptr).Elem().SetString(res) + return +} + +func defaultComponents() (sdk.Context, *rootmulti.Store) { db := dbm.NewMemDB() cms := rootmulti.NewStore(db) cms.MountStoreWithDB(testkey, sdk.StoreTypeIAVL, db) cms.LoadLatestVersion() ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) - return ctx + return ctx, cms } func indirect(ptr interface{}) interface{} { @@ -245,7 +284,7 @@ func indirect(ptr interface{}) interface{} { } func TestTypeValue(t *testing.T) { - ctx := defaultComponents() + ctx, cms := defaultComponents() var table = []struct { ty typeValue @@ -296,5 +335,19 @@ func TestTypeValue(t *testing.T) { require.Error(t, err) require.Equal(t, reflect.Zero(reflect.TypeOf(ptr).Elem()).Interface(), indirect(ptr)) require.Nil(t, v.GetRaw(ctx)) + + // Set again and test abci query + v.Set(ctx, tc.orig) + cid := cms.Commit() + ptr = v.Proto() + q := NewStoreQuerier(cms) + proof, err := v.Query(q, ptr) + require.NoError(t, err) + require.Equal(t, tc.orig, indirect(ptr), "Expected equal on tc %d", i) + prt := rootmulti.DefaultProofRuntime() + kp := merkle.KeyPath{}. + AppendKey([]byte(testkey.Name()), merkle.KeyEncodingHex). + AppendKey(v.KeyBytes(), merkle.KeyEncodingHex) + require.NoError(t, prt.VerifyValue(proof, cid.Hash, kp.String(), v.GetRaw(ctx))) } } From b4d74913b49a3d1b5d81481df1ec4ad8a27b4228 Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 18 Sep 2019 19:33:20 +0200 Subject: [PATCH 093/166] reflect downstream change --- x/ibc/02-client/cli.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/x/ibc/02-client/cli.go b/x/ibc/02-client/cli.go index db03c1441329..67740d181748 100644 --- a/x/ibc/02-client/cli.go +++ b/x/ibc/02-client/cli.go @@ -3,7 +3,7 @@ package client import ( "bytes" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/store/state" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) @@ -12,14 +12,14 @@ func (obj Object) prefix() []byte { return bytes.Split(obj.ConsensusState.KeyBytes(), LocalRoot())[0] } -func (obj Object) ConsensusStateCLI(ctx context.CLIContext) (res ConsensusState, proof merkle.Proof, err error) { - tmproof, err := obj.ConsensusState.Query(ctx, &res) +func (obj Object) ConsensusStateCLI(q state.ABCIQuerier) (res ConsensusState, proof merkle.Proof, err error) { + tmproof, err := obj.ConsensusState.Query(q, &res) proof = merkle.NewProofFromValue(tmproof, obj.prefix(), obj.ConsensusState) return } -func (obj Object) FrozenCLI(ctx context.CLIContext) (res bool, proof merkle.Proof, err error) { - res, tmproof, err := obj.Frozen.Query(ctx) +func (obj Object) FrozenCLI(q state.ABCIQuerier) (res bool, proof merkle.Proof, err error) { + res, tmproof, err := obj.Frozen.Query(q) proof = merkle.NewProofFromValue(tmproof, obj.prefix(), obj.Frozen) return } From de1a0454c4ad97d06a05785a90628f5b1e598df8 Mon Sep 17 00:00:00 2001 From: mossid Date: Wed, 18 Sep 2019 19:37:22 +0200 Subject: [PATCH 094/166] fix cli --- x/ibc/02-client/client/cli/query.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index c4238dae8cdb..624cfd7af9ff 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -48,11 +48,12 @@ func GetCmdQueryClient(storeKey string, cdc *codec.Codec) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := context.NewCLIContext().WithCodec(cdc) + q := state.NewCLIQuerier(ctx) mapp := mapping(cdc, storeKey, ibc.Version) man := client.NewManager(mapp) id := args[0] - state, _, err := man.Object(id).ConsensusStateCLI(ctx) + state, _, err := man.Object(id).ConsensusStateCLI(q) if err != nil { return err } From 9f636291508ee2eab0e04265deeca04941a026f3 Mon Sep 17 00:00:00 2001 From: mossid Date: Sat, 28 Sep 2019 10:39:16 -0700 Subject: [PATCH 095/166] rm commented lines --- x/ibc/23-commitment/merkle/utils.go | 42 ----------------------------- 1 file changed, 42 deletions(-) diff --git a/x/ibc/23-commitment/merkle/utils.go b/x/ibc/23-commitment/merkle/utils.go index 636fae569c28..954085e3a88d 100644 --- a/x/ibc/23-commitment/merkle/utils.go +++ b/x/ibc/23-commitment/merkle/utils.go @@ -43,48 +43,6 @@ func join(a, b []byte) (res []byte) { return } -/* - -// RequestQuery() constructs the abci.RequestQuery. -// -// RequestQuery.Path is a slash separated key list, ending with "/key" -// -// RequestQuery.Data is the concatanation of path.KeyPrefix and key argument -// -// RequestQuery.Prove is set to true -func (path Path) RequestQuery(key []byte) abci.RequestQuery { - req := path.RequestQueryMultiStore(key) - // BaseApp switches over the first path element, - // and performs KVStore query only if it is "/store" - req.Path = "/store" + req.Path - return req -} - - - -func (path Path) Query(ctx context.CLIContext, key []byte) (code uint32, value []byte, proof Proof, err error) { - resp, err := ctx.QueryABCI(path.RequestQuery(key)) - if err != nil { - return code, value, proof, err - } - if !resp.IsOK() { - return resp.Code, value, proof, errors.New(resp.Log) - } - return resp.Code, resp.Value, Proof{Key: key, Proof: resp.Proof}, nil -} - -func (path Path) QueryValue(ctx context.CLIContext, cdc *codec.Codec, key []byte, ptr interface{}) (Proof, error) { - _, value, proof, err := path.Query(ctx, key) - if err != nil { - return Proof{}, err - } - err = cdc.UnmarshalBinaryBare(value, ptr) // TODO - return proof, err -} - - - - func (path Path) Path() string { pathstr := "" From b4e71e1b80014fea1d099f84c9565c58909a21ac Mon Sep 17 00:00:00 2001 From: mossid Date: Sat, 28 Sep 2019 10:59:02 -0700 Subject: [PATCH 096/166] address review in progress --- x/ibc/02-client/manager.go | 31 +---------------------- x/ibc/02-client/tendermint/tests/types.go | 2 +- x/ibc/02-client/tendermint/types.go | 12 ++++----- x/ibc/02-client/types.go | 22 ++++++++-------- 4 files changed, 18 insertions(+), 49 deletions(-) diff --git a/x/ibc/02-client/manager.go b/x/ibc/02-client/manager.go index eb9a83ff7d2e..5c14323cb633 100644 --- a/x/ibc/02-client/manager.go +++ b/x/ibc/02-client/manager.go @@ -115,7 +115,7 @@ func (obj Object) Update(ctx sdk.Context, header Header) error { } stored := obj.GetConsensusState(ctx) - updated, err := stored.Validate(header) + updated, err := stored.CheckValidityAndUpdateState(header) if err != nil { return err } @@ -124,32 +124,3 @@ func (obj Object) Update(ctx sdk.Context, header Header) error { return nil } - -func (obj Object) Freeze(ctx sdk.Context) error { - if !obj.exists(ctx) { - panic("should not freeze nonexisting client") - } - - if obj.Frozen.Get(ctx) { - return errors.New("client is already Frozen") - } - - obj.Frozen.Set(ctx, true) - - return nil -} - -func (obj Object) Delete(ctx sdk.Context) error { - if !obj.exists(ctx) { - panic("should not delete nonexisting client") - } - - if !obj.Frozen.Get(ctx) { - return errors.New("client is not Frozen") - } - - obj.ConsensusState.Delete(ctx) - obj.Frozen.Delete(ctx) - - return nil -} diff --git a/x/ibc/02-client/tendermint/tests/types.go b/x/ibc/02-client/tendermint/tests/types.go index 26255358f483..51f56a50fbb1 100644 --- a/x/ibc/02-client/tendermint/tests/types.go +++ b/x/ibc/02-client/tendermint/tests/types.go @@ -140,7 +140,7 @@ func NewVerifier(header tmtypes.SignedHeader, nextvalset MockValidators, root me } func (v *Verifier) Validate(header tendermint.Header, valset, nextvalset MockValidators) error { - newcs, err := v.ConsensusState.Validate(header) + newcs, err := v.ConsensusState.CheckValidityAndUpdateState(header) if err != nil { return err } diff --git a/x/ibc/02-client/tendermint/types.go b/x/ibc/02-client/tendermint/types.go index ca5da8c1cae5..8d2f23509175 100644 --- a/x/ibc/02-client/tendermint/types.go +++ b/x/ibc/02-client/tendermint/types.go @@ -7,8 +7,8 @@ import ( lerr "github.com/tendermint/tendermint/lite/errors" "github.com/tendermint/tendermint/types" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) @@ -43,7 +43,7 @@ func (cs ConsensusState) update(header Header) ConsensusState { } } -func (cs ConsensusState) Validate(cheader client.Header) (client.ConsensusState, error) { +func (cs ConsensusState) CheckValidityAndUpdateState(cheader client.Header) (client.ConsensusState, error) { header, ok := cheader.(Header) if !ok { return nil, errors.New("invalid type") @@ -73,14 +73,14 @@ func (cs ConsensusState) Validate(cheader client.Header) (client.ConsensusState, return cs.update(header), nil } -func (cs ConsensusState) Equivocation(header1, header2 client.Header) bool { - return false // XXX +func (cs ConsensusState) CheckMisbehaviourAndUpdateState(mb client.Misbehaviour) bool { + return false // TODO: implement } var _ client.Header = Header{} type Header struct { - // XXX: don't take the entire struct + // TODO: define Tendermint header type manually, don't use tmtypes types.SignedHeader ValidatorSet *types.ValidatorSet NextValidatorSet *types.ValidatorSet diff --git a/x/ibc/02-client/types.go b/x/ibc/02-client/types.go index 134837a43aa8..fa18c5095b9b 100644 --- a/x/ibc/02-client/types.go +++ b/x/ibc/02-client/types.go @@ -1,7 +1,7 @@ package client import ( - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) // TODO: types in this file should be (de/)serialized with proto in the future @@ -17,20 +17,20 @@ type ConsensusState interface { // which is used for key-value pair verification. GetRoot() commitment.Root - // Validate() returns the updated consensus state + // CheckValidityAndUpdateState() returns the updated consensus state // only if the header is a descendent of this consensus state. - Validate(Header) (ConsensusState, error) // ValidityPredicate + CheckValidityAndUpdateState(Header) (ConsensusState, error) - // Equivocation checks two headers' confliction. - Equivocation(Header, Header) bool // EquivocationPredicate + // CheckMisbehaviourAndUpdateState() checks any misbehaviour evidence + // depending on the state type. + CheckMisbehaviourAndUpdateState(Misbehaviour) bool } -/* -func Equal(client1, client2 ConsensusState) bool { - return client1.Kind() == client2.Kind() && - client1.GetBase().Equal(client2.GetBase()) +type Misbehaviour interface { + Kind() Kind + // TODO: embed Evidence interface + // evidence.Evidence } -*/ // Header is the consensus state update information. type Header interface { @@ -40,8 +40,6 @@ type Header interface { GetHeight() uint64 } -// XXX: Kind should be enum? - type Kind byte const ( From 6b966a37d5bfaffb047bcfd41f3483fd9b67420a Mon Sep 17 00:00:00 2001 From: mossid Date: Mon, 30 Sep 2019 23:19:36 -0700 Subject: [PATCH 097/166] rename Path -> Prefix --- x/ibc/23-commitment/codec.go | 2 +- x/ibc/23-commitment/merkle/codec.go | 2 +- x/ibc/23-commitment/merkle/merkle.go | 24 +++++++++++----------- x/ibc/23-commitment/merkle/utils.go | 10 ++++------ x/ibc/23-commitment/store.go | 28 +++++++++++--------------- x/ibc/23-commitment/types.go | 8 ++++---- x/ibc/23-commitment/value.go | 30 ++++++++++++++-------------- 7 files changed, 49 insertions(+), 55 deletions(-) diff --git a/x/ibc/23-commitment/codec.go b/x/ibc/23-commitment/codec.go index e2639b78b4f9..943f8ede8b7e 100644 --- a/x/ibc/23-commitment/codec.go +++ b/x/ibc/23-commitment/codec.go @@ -7,6 +7,6 @@ import ( // RegisterCodec registers types declared in this package func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*Root)(nil), nil) - cdc.RegisterInterface((*Path)(nil), nil) + cdc.RegisterInterface((*Prefix)(nil), nil) cdc.RegisterInterface((*Proof)(nil), nil) } diff --git a/x/ibc/23-commitment/merkle/codec.go b/x/ibc/23-commitment/merkle/codec.go index 993c46603a3e..bbb83afa87a4 100644 --- a/x/ibc/23-commitment/merkle/codec.go +++ b/x/ibc/23-commitment/merkle/codec.go @@ -6,6 +6,6 @@ import ( func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(Root{}, "ibc/commitment/merkle/Root", nil) - cdc.RegisterConcrete(Path{}, "ibc/commitment/merkle/Path", nil) + cdc.RegisterConcrete(Prefix{}, "ibc/commitment/merkle/Prefix", nil) cdc.RegisterConcrete(Proof{}, "ibc/commitment/merkle/Proof", nil) } diff --git a/x/ibc/23-commitment/merkle/merkle.go b/x/ibc/23-commitment/merkle/merkle.go index 1606497ec4f8..190f73cbd2fe 100644 --- a/x/ibc/23-commitment/merkle/merkle.go +++ b/x/ibc/23-commitment/merkle/merkle.go @@ -7,7 +7,7 @@ import ( "github.com/tendermint/tendermint/crypto/merkle" "github.com/cosmos/cosmos-sdk/store/rootmulti" -// "github.com/cosmos/cosmos-sdk/store/state" + // "github.com/cosmos/cosmos-sdk/store/state" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -35,27 +35,27 @@ func (Root) CommitmentKind() string { return merkleKind } -var _ commitment.Path = Path{} +var _ commitment.Prefix = Prefix{} -// Path is merkle path prefixed to the key. +// Prefix is merkle path prefixed to the key. // The constructed key from the Path and the key will be append(Path.KeyPath, append(Path.KeyPrefix, key...)) -type Path struct { +type Prefix struct { // KeyPath is the list of keys prepended before the prefixed key KeyPath [][]byte // KeyPrefix is a byte slice prefixed before the key KeyPrefix []byte } -// NewPath() constructs new Path -func NewPath(keypath [][]byte, keyprefix []byte) Path { - return Path{ +// NewPath() constructs new Prefix +func NewPath(keypath [][]byte, keyprefix []byte) Prefix { + return Prefix{ KeyPath: keypath, KeyPrefix: keyprefix, } } -// Implements commitment.Path -func (Path) CommitmentKind() string { +// Implements commitment.Prefix +func (Prefix) CommitmentKind() string { return merkleKind } @@ -78,13 +78,13 @@ func (proof Proof) GetKey() []byte { } // Verify() proves the proof against the given root, path, and value. -func (proof Proof) Verify(croot commitment.Root, cpath commitment.Path, value []byte) error { +func (proof Proof) Verify(croot commitment.Root, cpath commitment.Prefix, value []byte) error { root, ok := croot.(Root) if !ok { return errors.New("invalid commitment root type") } - path, ok := cpath.(Path) + path, ok := cpath.(Prefix) if !ok { return errors.New("invalid commitment path type") } @@ -93,7 +93,7 @@ func (proof Proof) Verify(croot commitment.Root, cpath commitment.Path, value [] for _, key := range path.KeyPath { keypath = keypath.AppendKey(key, merkle.KeyEncodingHex) } - keypath = keypath.AppendKey(append(path.KeyPrefix, proof.Key...), merkle.KeyEncodingHex) + keypath = keypath.AppendKey(append(path.KeyPrefix, proof.Key...), merkle.KeyEncodingHex) // TODO: hard coded for now, should be extensible runtime := rootmulti.DefaultProofRuntime() diff --git a/x/ibc/23-commitment/merkle/utils.go b/x/ibc/23-commitment/merkle/utils.go index 954085e3a88d..2f8fea5ca026 100644 --- a/x/ibc/23-commitment/merkle/utils.go +++ b/x/ibc/23-commitment/merkle/utils.go @@ -32,8 +32,8 @@ func RequestQueryMultiStore(storeName string, prefix []byte, key []byte) abci.Re } } -func (path Path) Key(key []byte) []byte { - return join(path.KeyPrefix, key) +func (prefix Prefix) Key(key []byte) []byte { + return join(prefix.KeyPrefix, key) } func join(a, b []byte) (res []byte) { @@ -43,14 +43,12 @@ func join(a, b []byte) (res []byte) { return } - -func (path Path) Path() string { +func (prefix Prefix) Path() string { pathstr := "" - for _, inter := range path.KeyPath { + for _, inter := range prefix.KeyPath { // The Queryable() stores uses slash-separated keypath format for querying pathstr = pathstr + "/" + string(inter) } return pathstr } -*/ diff --git a/x/ibc/23-commitment/store.go b/x/ibc/23-commitment/store.go index 923a065334ea..fab1564eccde 100644 --- a/x/ibc/23-commitment/store.go +++ b/x/ibc/23-commitment/store.go @@ -3,7 +3,6 @@ package commitment import ( "bytes" "errors" - "fmt" ) // Store proves key-value pairs' inclusion or non-inclusion with @@ -12,15 +11,15 @@ type Store interface { Prove(key, value []byte) bool } -var _ Store = prefix{} // TODO: pointer +var _ Store = (*prefix)(nil) // TODO: pointer type prefix struct { store Store prefix []byte } -func NewPrefix(store Store, pref []byte) prefix { - return prefix{ +func NewPrefix(store Store, pref []byte) Store { + return &prefix{ store: store, prefix: pref, } @@ -34,7 +33,7 @@ var _ Store = (*store)(nil) type store struct { root Root - path Path + prefix Prefix proofs map[string]Proof verified map[string][]byte } @@ -42,28 +41,26 @@ type store struct { // NewStore constructs a new Store with the root, path, and proofs. // The proofs are not proven immediately because proofs require value bytes to verify. // If the kinds of the arguments don't match, returns error. -func NewStore(root Root, path Path, proofs []Proof) (res *store, err error) { - if root.CommitmentKind() != path.CommitmentKind() { - err = errors.New("path type not matching with root's") - return +func NewStore(root Root, prefix Prefix, proofs []Proof) (Store, error) { + if root.CommitmentKind() != prefix.CommitmentKind() { + return nil, errors.New("prefix type not matching with root's") } - res = &store{ + res := &store{ root: root, - path: path, + prefix: prefix, proofs: make(map[string]Proof), verified: make(map[string][]byte), } for _, proof := range proofs { if proof.CommitmentKind() != root.CommitmentKind() { - err = errors.New("proof type not matching with root's") - return + return nil, errors.New("proof type not matching with root's") } res.proofs[string(proof.GetKey())] = proof } - return + return res, nil } // Get() returns the value only if it is already proven. @@ -80,10 +77,9 @@ func (store *store) Prove(key, value []byte) bool { } proof, ok := store.proofs[string(key)] if !ok { - fmt.Println(111, string(key)) return false } - err := proof.Verify(store.root, store.path, value) + err := proof.Verify(store.root, store.prefix, value) if err != nil { return false } diff --git a/x/ibc/23-commitment/types.go b/x/ibc/23-commitment/types.go index 006e2b08c69e..6d09c7c6a404 100644 --- a/x/ibc/23-commitment/types.go +++ b/x/ibc/23-commitment/types.go @@ -8,9 +8,9 @@ type Root interface { CommitmentKind() string } -// Path is the additional information provided to the verification function. -// Path represents the common "prefix" that a set of keys shares. -type Path interface { +// Prefix is the additional information provided to the verification function. +// Prefix represents the common "prefix" that a set of keys shares. +type Prefix interface { CommitmentKind() string } @@ -20,5 +20,5 @@ type Path interface { type Proof interface { CommitmentKind() string GetKey() []byte - Verify(Root, Path, []byte) error + Verify(Root, Prefix, []byte) error } diff --git a/x/ibc/23-commitment/value.go b/x/ibc/23-commitment/value.go index 947d322c06bd..1c05a8b9e1b1 100644 --- a/x/ibc/23-commitment/value.go +++ b/x/ibc/23-commitment/value.go @@ -26,7 +26,7 @@ func (m Mapping) store(ctx sdk.Context) Store { return NewPrefix(GetStore(ctx), m.prefix) } -// Prefix() returns a new Mapping with the updated prefix +// Prefix() returns a new Mapping with the updated key prefix func (m Mapping) Prefix(prefix []byte) Mapping { return Mapping{ cdc: m.cdc, @@ -59,13 +59,13 @@ func (m Mapping) Value(key []byte) Value { return Value{m, key} } -// Is() proves the proof with the Value's key and the provided value. -func (v Value) Is(ctx sdk.Context, value interface{}) bool { +// Verify() proves the proof with the Value's key and the provided value. +func (v Value) Verify(ctx sdk.Context, value interface{}) bool { return v.m.store(ctx).Prove(v.key, v.m.cdc.MustMarshalBinaryBare(value)) } -// IsRaw() proves the proof with the Value's key and the provided raw value bytes. -func (v Value) IsRaw(ctx sdk.Context, value []byte) bool { +// VerifyRaw() proves the proof with the Value's key and the provided raw value bytes. +func (v Value) VerifyRaw(ctx sdk.Context, value []byte) bool { return v.m.store(ctx).Prove(v.key, value) } @@ -81,9 +81,9 @@ func (v Value) Enum() Enum { return Enum{v} } -// Is() proves the proof with the Enum's key and the provided value -func (v Enum) Is(ctx sdk.Context, value byte) bool { - return v.Value.IsRaw(ctx, []byte{value}) +// Verify() proves the proof with the Enum's key and the provided value +func (v Enum) Verify(ctx sdk.Context, value byte) bool { + return v.Value.VerifyRaw(ctx, []byte{value}) } type String struct { @@ -94,8 +94,8 @@ func (v Value) String() String { return String{v} } -func (v String) Is(ctx sdk.Context, value string) bool { - return v.Value.IsRaw(ctx, []byte(value)) +func (v String) Verify(ctx sdk.Context, value string) bool { + return v.Value.VerifyRaw(ctx, []byte(value)) } type Boolean struct { @@ -106,8 +106,8 @@ func (v Value) Boolean() Boolean { return Boolean{v} } -func (v Boolean) Is(ctx sdk.Context, value bool) bool { - return v.Value.Is(ctx, value) +func (v Boolean) Verify(ctx sdk.Context, value bool) bool { + return v.Value.Verify(ctx, value) } // Integer is a uint64 types wrapper for Value. @@ -122,7 +122,7 @@ func (v Value) Integer(enc state.IntEncoding) Integer { return Integer{v, enc} } -// Is() proves the proof with the Integer's key and the provided value -func (v Integer) Is(ctx sdk.Context, value uint64) bool { - return v.Value.IsRaw(ctx, state.EncodeInt(value, v.enc)) +// Verify() proves the proof with the Integer's key and the provided value +func (v Integer) Verify(ctx sdk.Context, value uint64) bool { + return v.Value.VerifyRaw(ctx, state.EncodeInt(value, v.enc)) } From 21bd8d0ce4334cbd03f4542ddd2c0f46d7f01561 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Tue, 1 Oct 2019 13:12:35 +0200 Subject: [PATCH 098/166] Store accessor upstream changes (#5119) * Store accessor upstream changes (#5119) --- store/state/enum.go | 3 +++ store/state/integer.go | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/store/state/enum.go b/store/state/enum.go index c580dca614aa..18937ce18bf7 100644 --- a/store/state/enum.go +++ b/store/state/enum.go @@ -56,5 +56,8 @@ func (v Enum) Transit(ctx Context, from, to byte) bool { // Query() retrives state value and proof from a queryable reference func (v Enum) Query(q ABCIQuerier) (res byte, proof *Proof, err error) { value, proof, err := v.Value.QueryRaw(q) + if err != nil { + return + } return value[0], proof, err } diff --git a/store/state/integer.go b/store/state/integer.go index 6758d73cc479..938b83fd1f46 100644 --- a/store/state/integer.go +++ b/store/state/integer.go @@ -58,6 +58,10 @@ func (v Integer) Query(q ABCIQuerier) (res uint64, proof *Proof, err error) { if err != nil { return } + if value == nil { + res = 0 + return + } res, err = DecodeInt(value, v.enc) return } From df5b21ab5638a64ae998ccf5cc85fbd2333a1378 Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 1 Oct 2019 20:16:54 +0900 Subject: [PATCH 099/166] add comments, reformat merkle querier --- x/ibc/23-commitment/README.md | 50 --------------------- x/ibc/23-commitment/merkle/merkle.go | 5 +++ x/ibc/23-commitment/merkle/merkle_test.go | 15 ++++--- x/ibc/23-commitment/merkle/utils.go | 33 +------------- x/ibc/23-commitment/store.go | 53 ++++++++++++----------- x/ibc/23-commitment/types.go | 13 +++++- x/ibc/23-commitment/value.go | 48 ++++++++++++++++---- 7 files changed, 94 insertions(+), 123 deletions(-) delete mode 100644 x/ibc/23-commitment/README.md diff --git a/x/ibc/23-commitment/README.md b/x/ibc/23-commitment/README.md deleted file mode 100644 index 256e77f0b95d..000000000000 --- a/x/ibc/23-commitment/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# ICS 23: Commitment - -Package `commitment` defines types and methods to verify other chain's state. The main type is `Store`, containing -proofs that can be verified when the correct value is provided. The spec functions those are directly related to -verification are: - -## Spec - -```typescript -type verifyMembership = (root: CommitmentRoot, proof: CommitmentProof, key: Key, value: Value) => bool -type verifyNonMembership = (root: CommitmentRoot, proof: CommitmentProof, key: Key) => bool -``` - -## Implementation - -### types.go - -`type Proof` implements `spec: type CommitmentProof`. CommitmentProof is an arbitrary object which can be used as -an argument for `spec: verifyMembership` / `spec: verifyNonMembership`, constructed with `spec: createMembershipProof` / -`spec: createNonMembershipProof`. The implementation type `Proof` defines `spec: verify(Non)Membership` as its method -`Verify(Root, []byte) error`, which takes the commitment root and the value bytes as arguments. The method acts as -`spec: verifyMembership` when the value bytes is not nil, and `spec: verifyNonMembership` if it is nil. - -`type Root` implements `spec: type CommitmentRoot`. - -In Cosmos-SDK implementation, `Root` will be the `AppHash []byte`, and `Proof` will be `merkle.Proof`, which consists -of `SimpleProof` and `IAVLValueProof`. Defined in `merkle/` - -### store.go - -`Store` assumes that the keys are already known at the time when the transaction is included, so the type `Proof` has -the method `Key() []byte`. The values should also have to be provided in order to verify the proof, but to reduce the -size of the transaction, they are excluded from `Proof` and provided by the application on runtime. - -`NewStore` takes `[]Proof` as its argument, without verifying, since the values are yet unknown. They are stored in -`store.proofs`. - -Proofs can be verified with `store.Prove()` method which takes the key of the proof it will verify and the value -that will be given to the `proof.Verify()`. Verified proofs are stored in `store.verified`. - -### context.go - -All of the ICS internals that requires verification on other chains' state are expected to take `ctx sdk.Context` -argument initialized by `WithStore()`. `WithStore()` sets the `Store` that contains the proofs for the other chain -in the context. Any attept to verify other chain's state without setting `Store` will lead to panic. - -### value.go - -Types in `value.go` is a replication of `store/mapping/*.go`, but only with a single method -`Is(ctx sdk.Context, value T) bool`, which access on the underlying `Store` and performs verification. diff --git a/x/ibc/23-commitment/merkle/merkle.go b/x/ibc/23-commitment/merkle/merkle.go index 190f73cbd2fe..36563f8f3344 100644 --- a/x/ibc/23-commitment/merkle/merkle.go +++ b/x/ibc/23-commitment/merkle/merkle.go @@ -12,6 +12,10 @@ import ( commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) +// ICS 023 Merkle Types Implementation +// +// This file defines Merkle commitment types that implements ICS 023. + const merkleKind = "merkle" // merkle.Proof implementation of Proof @@ -19,6 +23,7 @@ const merkleKind = "merkle" var _ commitment.Root = Root{} // Root is Merkle root hash +// In Cosmos-SDK, the AppHash of the Header becomes Root. type Root struct { Hash []byte } diff --git a/x/ibc/23-commitment/merkle/merkle_test.go b/x/ibc/23-commitment/merkle/merkle_test.go index efa384a6c317..c652289caf34 100644 --- a/x/ibc/23-commitment/merkle/merkle_test.go +++ b/x/ibc/23-commitment/merkle/merkle_test.go @@ -68,20 +68,22 @@ func TestStore(t *testing.T) { // Test query, and accumulate proofs proofs := make([]commitment.Proof, 0, kvpn) for k, v := range m { - v0, p, err := QueryMultiStore(cms, storeName, prefix, []byte(k)) + q := state.NewStoreQuerier(cms.(types.Queryable)) + v0, p, err := mapp.Value([]byte(k)).QueryRaw(q) require.NoError(t, err) require.Equal(t, cdc.MustMarshalBinaryBare(v), v0, "Queried value different at %d", repeat) - proofs = append(proofs, p) + proofs = append(proofs, Proof{Key: []byte(k), Proof: p}) } // Add some exclusion proofs for i := 0; i < 10; i++ { k := make([]byte, 64) rand.Read(k) - v, p, err := QueryMultiStore(cms, storeName, prefix, k) + q := state.NewStoreQuerier(cms.(types.Queryable)) + v, p, err := mapp.Value([]byte(k)).QueryRaw(q) require.NoError(t, err) require.Nil(t, v) - proofs = append(proofs, p) + proofs = append(proofs, Proof{Key: []byte(k), Proof: p}) m[string(k)] = []byte{} } @@ -110,10 +112,11 @@ func TestStore(t *testing.T) { // Test query, and accumulate proofs proofs = make([]commitment.Proof, 0, kvpn) for k, v := range m { - v0, p, err := QueryMultiStore(cms, storeName, prefix, []byte(k)) + q := state.NewStoreQuerier(cms.(types.Queryable)) + v0, p, err := mapp.Value([]byte(k)).QueryRaw(q) require.NoError(t, err) require.Equal(t, cdc.MustMarshalBinaryBare(v), v0) - proofs = append(proofs, p) + proofs = append(proofs, Proof{Key: []byte(k), Proof: p}) } cstore, err = commitment.NewStore(root, path, proofs) diff --git a/x/ibc/23-commitment/merkle/utils.go b/x/ibc/23-commitment/merkle/utils.go index 2f8fea5ca026..5e908af9783c 100644 --- a/x/ibc/23-commitment/merkle/utils.go +++ b/x/ibc/23-commitment/merkle/utils.go @@ -1,36 +1,6 @@ package merkle -import ( - "errors" - - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/store/types" -) - -func QueryMultiStore(cms types.CommitMultiStore, storeName string, prefix []byte, key []byte) ([]byte, Proof, error) { - queryable, ok := cms.(types.Queryable) - if !ok { - panic("CommitMultiStore not queryable") - } - qres := queryable.Query(RequestQueryMultiStore(storeName, prefix, key)) - if !qres.IsOK() { - return nil, Proof{}, errors.New(qres.Log) - } - - return qres.Value, Proof{Key: key, Proof: qres.Proof}, nil -} - -func RequestQueryMultiStore(storeName string, prefix []byte, key []byte) abci.RequestQuery { - // Suffixing path with "/key". - // iavl.Store.Query() switches over the last path element, - // and performs key-value query only if it is "/key" - return abci.RequestQuery{ - Path: "/" + storeName + "/key", - Data: join(prefix, key), - Prove: true, - } -} +/* func (prefix Prefix) Key(key []byte) []byte { return join(prefix.KeyPrefix, key) @@ -52,3 +22,4 @@ func (prefix Prefix) Path() string { return pathstr } +*/ diff --git a/x/ibc/23-commitment/store.go b/x/ibc/23-commitment/store.go index fab1564eccde..4c4ae37ab167 100644 --- a/x/ibc/23-commitment/store.go +++ b/x/ibc/23-commitment/store.go @@ -5,19 +5,28 @@ import ( "errors" ) -// Store proves key-value pairs' inclusion or non-inclusion with -// the stored commitment proofs against the commitment root. +// ICS 023 Function Implementation +// +// This file includes functions defined under +// https://github.com/cosmos/ics/tree/master/spec/ics-023-vector-commitments + +// Store partially implements spec:verifyMembership and spec:verifyNonMembership. +// Store holds Root, Prefix, and list of Proofs that will be verified. +// Proofs incldues their respective Paths. Values are provided at the verification time. type Store interface { - Prove(key, value []byte) bool + Prove(path, value []byte) bool } -var _ Store = (*prefix)(nil) // TODO: pointer +var _ Store = (*prefix)(nil) type prefix struct { store Store prefix []byte } +// NewPrefix returns a prefixed store given base store and prefix. +// Prefix store for commitment proofs is used for similar path bytestring +// prefixing UX with local KVStore. func NewPrefix(store Store, pref []byte) Store { return &prefix{ store: store, @@ -25,8 +34,9 @@ func NewPrefix(store Store, pref []byte) Store { } } -func (prefix prefix) Prove(key, value []byte) bool { - return prefix.store.Prove(join(prefix.prefix, key), value) +// Prove implements Store. +func (prefix prefix) Prove(path, value []byte) bool { + return prefix.store.Prove(join(prefix.prefix, path), value) } var _ Store = (*store)(nil) @@ -39,8 +49,8 @@ type store struct { } // NewStore constructs a new Store with the root, path, and proofs. -// The proofs are not proven immediately because proofs require value bytes to verify. -// If the kinds of the arguments don't match, returns error. +// The result store will be stored in the context and used by the +// commitment.Value types. func NewStore(root Root, prefix Prefix, proofs []Proof) (Store, error) { if root.CommitmentKind() != prefix.CommitmentKind() { return nil, errors.New("prefix type not matching with root's") @@ -63,19 +73,18 @@ func NewStore(root Root, prefix Prefix, proofs []Proof) (Store, error) { return res, nil } -// Get() returns the value only if it is already proven. -func (store *store) Get(key []byte) ([]byte, bool) { - res, ok := store.verified[string(key)] - return res, ok -} - -// Prove() proves the key-value pair with the stored proof. -func (store *store) Prove(key, value []byte) bool { - stored, ok := store.Get(key) +// Prove implements spec:verifyMembership and spec:verifyNonMembership. +// The path should be one of the path format defined under +// https://github.com/cosmos/ics/tree/master/spec/ics-024-host-requirements +// Prove retrieves the matching proof with the provided path from the internal map +// and call Verify method on it with internal Root and Prefix. +// Prove acts as verifyMembership if value is not nil, and verifyNonMembership if nil. +func (store *store) Prove(path, value []byte) bool { + stored, ok := store.verified[string(path)] if ok && bytes.Equal(stored, value) { return true } - proof, ok := store.proofs[string(key)] + proof, ok := store.proofs[string(path)] if !ok { return false } @@ -83,13 +92,7 @@ func (store *store) Prove(key, value []byte) bool { if err != nil { return false } - store.verified[string(key)] = value + store.verified[string(path)] = value return true } - -// Proven() returns true if the key-value pair is already proven -func (store *store) Proven(key []byte) bool { - _, ok := store.Get(key) - return ok -} diff --git a/x/ibc/23-commitment/types.go b/x/ibc/23-commitment/types.go index 6d09c7c6a404..0e8a5db04d07 100644 --- a/x/ibc/23-commitment/types.go +++ b/x/ibc/23-commitment/types.go @@ -1,6 +1,13 @@ package commitment -// Root is the interface for commitment root. +// ICS 023 Types Implementation +// +// This file includes types defined under +// https://github.com/cosmos/ics/tree/master/spec/ics-023-vector-commitments + +// spec:Path and spec:Value are defined as bytestring + +// Root implements spec:CommitmentRoot. // A root is constructed from a set of key-value pairs, // and the inclusion or non-inclusion of an arbitrary key-value pair // can be proven with the proof. @@ -8,15 +15,17 @@ type Root interface { CommitmentKind() string } +// Prefix implements spec:CommitmentPrefix. // Prefix is the additional information provided to the verification function. // Prefix represents the common "prefix" that a set of keys shares. type Prefix interface { CommitmentKind() string } +// Proof implements spec:CommitmentProof. // Proof can prove whether the key-value pair is a part of the Root or not. // Each proof has designated key-value pair it is able to prove. -// Proofs stores key but value is provided dynamically at the verification time. +// Proofs includes key but value is provided dynamically at the verification time. type Proof interface { CommitmentKind() string GetKey() []byte diff --git a/x/ibc/23-commitment/value.go b/x/ibc/23-commitment/value.go index 1c05a8b9e1b1..4ea74ae9e8ed 100644 --- a/x/ibc/23-commitment/value.go +++ b/x/ibc/23-commitment/value.go @@ -6,6 +6,27 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +// Remote State Accessors +// +// This file defines the state accessor type for remote chains. +// The accessors defined here, unlike normal accessors, cannot +// mutate the state. The accessors are for storing the state +// struct, which means, given correct proofs, the remote state +// accessors reflects the behaviour of the normal state accessors. +// +// Exmaple +// +// On chain A, the following state mutation has happened +// v := state.NewMapping(sdk.NewStoreKey(name), cdc, preprefix+prefix).Value(key) +// v.Set(ctx, value) +// +// Given a correct membership proof, the following function returns true +// v := commitment.NewMapping(cdc, prefix).Value(key) +// v.Verify(ctx, value) +// +// The storeKey name and preprefix information is embedded in the store as +// merkle.Prefix{[][]byte{name}, preprefix} + // Mapping is key []byte -> value []byte mapping, possibly prefixed. // Proof verification should be done over Value constructed from the Mapping. type Mapping struct { @@ -13,7 +34,7 @@ type Mapping struct { prefix []byte } -// NewMapping() constructs a new Mapping. +// NewMapping constructs a new Mapping. // The KVStore accessor is fixed to the commitment store. func NewMapping(cdc *codec.Codec, prefix []byte) Mapping { return Mapping{ @@ -26,7 +47,7 @@ func (m Mapping) store(ctx sdk.Context) Store { return NewPrefix(GetStore(ctx), m.prefix) } -// Prefix() returns a new Mapping with the updated key prefix +// Prefix returns a new Mapping with the updated key prefix func (m Mapping) Prefix(prefix []byte) Mapping { return Mapping{ cdc: m.cdc, @@ -50,23 +71,26 @@ func (ix Indexer) Value(index uint64) Value { return ix.Mapping.Value(state.EncodeInt(index, ix.enc)) } +// Value is a reference for a key-value point on a remote state. +// Value only contains the information of the key string information +// which is used by the commitment proof verification type Value struct { m Mapping key []byte } +// Value constructs a Value with the provided key func (m Mapping) Value(key []byte) Value { return Value{m, key} } -// Verify() proves the proof with the Value's key and the provided value. +// Verify proves the proof with the Value's key and the provided value. func (v Value) Verify(ctx sdk.Context, value interface{}) bool { return v.m.store(ctx).Prove(v.key, v.m.cdc.MustMarshalBinaryBare(value)) } -// VerifyRaw() proves the proof with the Value's key and the provided raw value bytes. +// VerifyRaw proves the proof with the Value's key and the provided raw value bytes. func (v Value) VerifyRaw(ctx sdk.Context, value []byte) bool { - return v.m.store(ctx).Prove(v.key, value) } @@ -76,36 +100,42 @@ type Enum struct { Value } -// Enum() wraps the argument Value as Enum +// Enum wraps the argument Value as Enum func (v Value) Enum() Enum { return Enum{v} } -// Verify() proves the proof with the Enum's key and the provided value +// Verify proves the proof with the Enum's key and the provided value func (v Enum) Verify(ctx sdk.Context, value byte) bool { return v.Value.VerifyRaw(ctx, []byte{value}) } +// String is a string types wrapper for Value. type String struct { Value } +// String wraps the argument Value as String func (v Value) String() String { return String{v} } +// Verify proves the proof with the String's key and the provided value func (v String) Verify(ctx sdk.Context, value string) bool { return v.Value.VerifyRaw(ctx, []byte(value)) } +// Boolean is a bool types wrapper for Value. type Boolean struct { Value } +// Boolean wraps the argument Value as Boolean func (v Value) Boolean() Boolean { return Boolean{v} } +// Verify proves the proof with the Boolean's key and the provided value func (v Boolean) Verify(ctx sdk.Context, value bool) bool { return v.Value.Verify(ctx, value) } @@ -117,12 +147,12 @@ type Integer struct { enc state.IntEncoding } -// Integer() wraps the argument Value as Integer +// Integer wraps the argument Value as Integer func (v Value) Integer(enc state.IntEncoding) Integer { return Integer{v, enc} } -// Verify() proves the proof with the Integer's key and the provided value +// Verify proves the proof with the Integer's key and the provided value func (v Integer) Verify(ctx sdk.Context, value uint64) bool { return v.Value.VerifyRaw(ctx, state.EncodeInt(value, v.enc)) } From 1ac982c602a178e7c109726291b52b90434a7ec5 Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 1 Oct 2019 20:21:34 +0900 Subject: [PATCH 100/166] rm merkle/utils --- x/ibc/23-commitment/merkle/utils.go | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 x/ibc/23-commitment/merkle/utils.go diff --git a/x/ibc/23-commitment/merkle/utils.go b/x/ibc/23-commitment/merkle/utils.go deleted file mode 100644 index 5e908af9783c..000000000000 --- a/x/ibc/23-commitment/merkle/utils.go +++ /dev/null @@ -1,25 +0,0 @@ -package merkle - -/* - -func (prefix Prefix) Key(key []byte) []byte { - return join(prefix.KeyPrefix, key) -} - -func join(a, b []byte) (res []byte) { - res = make([]byte, len(a)+len(b)) - copy(res, a) - copy(res[len(a):], b) - return -} - -func (prefix Prefix) Path() string { - pathstr := "" - for _, inter := range prefix.KeyPath { - // The Queryable() stores uses slash-separated keypath format for querying - pathstr = pathstr + "/" + string(inter) - } - - return pathstr -} -*/ From fa0441997b15e05950b54c09b3ba5b8b83004976 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Tue, 1 Oct 2019 14:02:42 +0200 Subject: [PATCH 101/166] ICS 23 upstream changes (#5120) * ICS 23 upstream changes (#5120) --- x/ibc/23-commitment/merkle/merkle.go | 19 ++++++----- x/ibc/23-commitment/merkle/merkle_test.go | 2 +- x/ibc/23-commitment/merkle/utils.go | 40 +++++++++++++++++++++++ x/ibc/23-commitment/store.go | 2 +- 4 files changed, 53 insertions(+), 10 deletions(-) create mode 100644 x/ibc/23-commitment/merkle/utils.go diff --git a/x/ibc/23-commitment/merkle/merkle.go b/x/ibc/23-commitment/merkle/merkle.go index 36563f8f3344..f310ac948151 100644 --- a/x/ibc/23-commitment/merkle/merkle.go +++ b/x/ibc/23-commitment/merkle/merkle.go @@ -25,7 +25,7 @@ var _ commitment.Root = Root{} // Root is Merkle root hash // In Cosmos-SDK, the AppHash of the Header becomes Root. type Root struct { - Hash []byte + Hash []byte `json:"hash"` } // NewRoot constructs a new Root @@ -46,13 +46,13 @@ var _ commitment.Prefix = Prefix{} // The constructed key from the Path and the key will be append(Path.KeyPath, append(Path.KeyPrefix, key...)) type Prefix struct { // KeyPath is the list of keys prepended before the prefixed key - KeyPath [][]byte + KeyPath [][]byte `json:"key_path"` // KeyPrefix is a byte slice prefixed before the key - KeyPrefix []byte + KeyPrefix []byte `json:"key_prefix"` } -// NewPath() constructs new Prefix -func NewPath(keypath [][]byte, keyprefix []byte) Prefix { +// NewPrefix constructs new Prefix instance +func NewPrefix(keypath [][]byte, keyprefix []byte) Prefix { return Prefix{ KeyPath: keypath, KeyPrefix: keyprefix, @@ -64,12 +64,16 @@ func (Prefix) CommitmentKind() string { return merkleKind } +func (prefix Prefix) Key(key []byte) []byte { + return join(prefix.KeyPrefix, key) +} + var _ commitment.Proof = Proof{} // Proof is Merkle proof with the key information. type Proof struct { - Proof *merkle.Proof - Key []byte + Proof *merkle.Proof `json:"proof"` + Key []byte `json:"key"` } // Implements commitment.Proof @@ -114,6 +118,5 @@ type Value interface { } func NewProofFromValue(proof *merkle.Proof, prefix []byte, value Value) Proof { - // TODO: check HasPrefix return Proof{proof, bytes.TrimPrefix(value.KeyBytes(), prefix)} } diff --git a/x/ibc/23-commitment/merkle/merkle_test.go b/x/ibc/23-commitment/merkle/merkle_test.go index c652289caf34..fd550d43da53 100644 --- a/x/ibc/23-commitment/merkle/merkle_test.go +++ b/x/ibc/23-commitment/merkle/merkle_test.go @@ -45,7 +45,7 @@ func TestStore(t *testing.T) { storeName := k.Name() prefix := []byte{0x01, 0x03, 0x05, 0xAA, 0xBB} mapp := state.NewMapping(k, cdc, prefix) - path := NewPath([][]byte{[]byte(storeName)}, prefix) + path := NewPrefix([][]byte{[]byte(storeName)}, prefix) m := make(map[string][]byte) kvpn := 10 diff --git a/x/ibc/23-commitment/merkle/utils.go b/x/ibc/23-commitment/merkle/utils.go new file mode 100644 index 000000000000..ec933af09748 --- /dev/null +++ b/x/ibc/23-commitment/merkle/utils.go @@ -0,0 +1,40 @@ +package merkle + +import ( + "errors" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/store/types" +) + +func QueryMultiStore(cms types.CommitMultiStore, storeName string, prefix []byte, key []byte) ([]byte, Proof, error) { + queryable, ok := cms.(types.Queryable) + if !ok { + panic("CommitMultiStore not queryable") + } + qres := queryable.Query(RequestQueryMultiStore(storeName, prefix, key)) + if !qres.IsOK() { + return nil, Proof{}, errors.New(qres.Log) + } + + return qres.Value, Proof{Key: key, Proof: qres.Proof}, nil +} + +func RequestQueryMultiStore(storeName string, prefix []byte, key []byte) abci.RequestQuery { + // Suffixing path with "/key". + // iavl.Store.Query() switches over the last path element, + // and performs key-value query only if it is "/key" + return abci.RequestQuery{ + Path: "/" + storeName + "/key", + Data: join(prefix, key), + Prove: true, + } +} + +func join(a, b []byte) (res []byte) { + res = make([]byte, len(a)+len(b)) + copy(res, a) + copy(res[len(a):], b) + return +} diff --git a/x/ibc/23-commitment/store.go b/x/ibc/23-commitment/store.go index 4c4ae37ab167..1ced699258fd 100644 --- a/x/ibc/23-commitment/store.go +++ b/x/ibc/23-commitment/store.go @@ -17,7 +17,7 @@ type Store interface { Prove(path, value []byte) bool } -var _ Store = (*prefix)(nil) +var _ Store = prefix{} type prefix struct { store Store From db8d3820916f4ce754e1af7ac1c0c565f992f104 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 1 Oct 2019 15:54:09 +0200 Subject: [PATCH 102/166] update Value --- x/ibc/23-commitment/value.go | 69 ++++++++++-------------------------- 1 file changed, 19 insertions(+), 50 deletions(-) diff --git a/x/ibc/23-commitment/value.go b/x/ibc/23-commitment/value.go index 4ea74ae9e8ed..5797e1cd13c9 100644 --- a/x/ibc/23-commitment/value.go +++ b/x/ibc/23-commitment/value.go @@ -6,27 +6,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// Remote State Accessors -// -// This file defines the state accessor type for remote chains. -// The accessors defined here, unlike normal accessors, cannot -// mutate the state. The accessors are for storing the state -// struct, which means, given correct proofs, the remote state -// accessors reflects the behaviour of the normal state accessors. -// -// Exmaple -// -// On chain A, the following state mutation has happened -// v := state.NewMapping(sdk.NewStoreKey(name), cdc, preprefix+prefix).Value(key) -// v.Set(ctx, value) -// -// Given a correct membership proof, the following function returns true -// v := commitment.NewMapping(cdc, prefix).Value(key) -// v.Verify(ctx, value) -// -// The storeKey name and preprefix information is embedded in the store as -// merkle.Prefix{[][]byte{name}, preprefix} - // Mapping is key []byte -> value []byte mapping, possibly prefixed. // Proof verification should be done over Value constructed from the Mapping. type Mapping struct { @@ -34,7 +13,7 @@ type Mapping struct { prefix []byte } -// NewMapping constructs a new Mapping. +// NewMapping() constructs a new Mapping. // The KVStore accessor is fixed to the commitment store. func NewMapping(cdc *codec.Codec, prefix []byte) Mapping { return Mapping{ @@ -47,7 +26,7 @@ func (m Mapping) store(ctx sdk.Context) Store { return NewPrefix(GetStore(ctx), m.prefix) } -// Prefix returns a new Mapping with the updated key prefix +// Prefix() returns a new Mapping with the updated prefix func (m Mapping) Prefix(prefix []byte) Mapping { return Mapping{ cdc: m.cdc, @@ -71,26 +50,22 @@ func (ix Indexer) Value(index uint64) Value { return ix.Mapping.Value(state.EncodeInt(index, ix.enc)) } -// Value is a reference for a key-value point on a remote state. -// Value only contains the information of the key string information -// which is used by the commitment proof verification type Value struct { m Mapping key []byte } -// Value constructs a Value with the provided key func (m Mapping) Value(key []byte) Value { return Value{m, key} } -// Verify proves the proof with the Value's key and the provided value. -func (v Value) Verify(ctx sdk.Context, value interface{}) bool { +// Is() proves the proof with the Value's key and the provided value. +func (v Value) Is(ctx sdk.Context, value interface{}) bool { return v.m.store(ctx).Prove(v.key, v.m.cdc.MustMarshalBinaryBare(value)) } -// VerifyRaw proves the proof with the Value's key and the provided raw value bytes. -func (v Value) VerifyRaw(ctx sdk.Context, value []byte) bool { +// IsRaw() proves the proof with the Value's key and the provided raw value bytes. +func (v Value) IsRaw(ctx sdk.Context, value []byte) bool { return v.m.store(ctx).Prove(v.key, value) } @@ -100,44 +75,38 @@ type Enum struct { Value } -// Enum wraps the argument Value as Enum +// Enum() wraps the argument Value as Enum func (v Value) Enum() Enum { return Enum{v} } -// Verify proves the proof with the Enum's key and the provided value -func (v Enum) Verify(ctx sdk.Context, value byte) bool { - return v.Value.VerifyRaw(ctx, []byte{value}) +// Is() proves the proof with the Enum's key and the provided value +func (v Enum) Is(ctx sdk.Context, value byte) bool { + return v.Value.IsRaw(ctx, []byte{value}) } -// String is a string types wrapper for Value. type String struct { Value } -// String wraps the argument Value as String func (v Value) String() String { return String{v} } -// Verify proves the proof with the String's key and the provided value -func (v String) Verify(ctx sdk.Context, value string) bool { - return v.Value.VerifyRaw(ctx, []byte(value)) +func (v String) Is(ctx sdk.Context, value string) bool { + return v.Value.IsRaw(ctx, []byte(value)) } -// Boolean is a bool types wrapper for Value. type Boolean struct { Value } -// Boolean wraps the argument Value as Boolean func (v Value) Boolean() Boolean { return Boolean{v} } -// Verify proves the proof with the Boolean's key and the provided value -func (v Boolean) Verify(ctx sdk.Context, value bool) bool { - return v.Value.Verify(ctx, value) +func (v Boolean) Is(ctx sdk.Context, value bool) bool { + return v.Value.Is(ctx, value) } // Integer is a uint64 types wrapper for Value. @@ -147,12 +116,12 @@ type Integer struct { enc state.IntEncoding } -// Integer wraps the argument Value as Integer +// Integer() wraps the argument Value as Integer func (v Value) Integer(enc state.IntEncoding) Integer { return Integer{v, enc} } -// Verify proves the proof with the Integer's key and the provided value -func (v Integer) Verify(ctx sdk.Context, value uint64) bool { - return v.Value.VerifyRaw(ctx, state.EncodeInt(value, v.enc)) -} +// Is() proves the proof with the Integer's key and the provided value +func (v Integer) Is(ctx sdk.Context, value uint64) bool { + return v.Value.IsRaw(ctx, state.EncodeInt(value, v.enc)) +} \ No newline at end of file From 4d518f2ed06e5ee4c6a1054350b099d9330da8c6 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 1 Oct 2019 16:03:29 +0200 Subject: [PATCH 103/166] update test --- x/ibc/02-client/tendermint/tests/types.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x/ibc/02-client/tendermint/tests/types.go b/x/ibc/02-client/tendermint/tests/types.go index 51f56a50fbb1..20cb6ddfdd19 100644 --- a/x/ibc/02-client/tendermint/tests/types.go +++ b/x/ibc/02-client/tendermint/tests/types.go @@ -52,7 +52,7 @@ type Node struct { Commits []tmtypes.SignedHeader StoreName string - Prefix []byte + KeyPrefix []byte } func NewNode(valset MockValidators, storeName string, prefix []byte) *Node { @@ -65,12 +65,12 @@ func NewNode(valset MockValidators, storeName string, prefix []byte) *Node { Store: ctx.KVStore(key), Commits: nil, StoreName: storeName, - Prefix: prefix, + KeyPrefix: prefix, } } -func (node *Node) Path() merkle.Path { - return merkle.NewPath([][]byte{[]byte(node.StoreName)}, node.Prefix) +func (node *Node) Prefix() merkle.Prefix { + return merkle.NewPrefix([][]byte{[]byte(node.StoreName)}, node.KeyPrefix) } func (node *Node) Last() tmtypes.SignedHeader { From 8a3e5ea57ce19c420e45204f188b466b99f02f2e Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 1 Oct 2019 16:06:13 +0200 Subject: [PATCH 104/166] fix --- x/ibc/02-client/tendermint/tests/types.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/x/ibc/02-client/tendermint/tests/types.go b/x/ibc/02-client/tendermint/tests/types.go index 20cb6ddfdd19..b9b238c74126 100644 --- a/x/ibc/02-client/tendermint/tests/types.go +++ b/x/ibc/02-client/tendermint/tests/types.go @@ -150,16 +150,16 @@ func (v *Verifier) Validate(header tendermint.Header, valset, nextvalset MockVal } func (node *Node) Query(t *testing.T, k []byte) ([]byte, commitment.Proof) { - if bytes.HasPrefix(k, node.Prefix) { - k = bytes.TrimPrefix(k, node.Prefix) + if bytes.HasPrefix(k, node.KeyPrefix) { + k = bytes.TrimPrefix(k, node.KeyPrefix) } - value, proof, err := merkle.QueryMultiStore(node.Cms, node.StoreName, node.Prefix, k) + value, proof, err := merkle.QueryMultiStore(node.Cms, node.StoreName, node.KeyPrefix, k) require.NoError(t, err) return value, proof } func (node *Node) Set(k, value []byte) { - node.Store.Set(join(node.Prefix, k), value) + node.Store.Set(join(node.KeyPrefix, k), value) } // nolint:deadcode,unused @@ -190,7 +190,7 @@ func testProof(t *testing.T) { require.Equal(t, kvp.Value, v) proofs = append(proofs, p) } - cstore, err := commitment.NewStore(root, node.Path(), proofs) + cstore, err := commitment.NewStore(root, node.Prefix(), proofs) require.NoError(t, err) for _, kvp := range kvps { From 9975a1d2a84dac1ac3274113e5543ef829446e79 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Tue, 1 Oct 2019 16:45:02 +0200 Subject: [PATCH 105/166] ICS 02 upstream changes (#5122) * ICS 02 upstream changes (#5122) --- x/ibc/02-client/README.md | 2 +- x/ibc/02-client/cli.go | 14 ++++-- x/ibc/02-client/client/cli/query.go | 52 +++++++++++++++++-- x/ibc/02-client/client/cli/tx.go | 8 +-- x/ibc/02-client/codec.go | 10 ++-- x/ibc/02-client/manager.go | 78 +++++++++++++++++++++-------- x/ibc/02-client/tendermint/codec.go | 5 -- x/ibc/version.go | 3 -- x/ibc/version/version.go | 13 +++++ 9 files changed, 139 insertions(+), 46 deletions(-) delete mode 100644 x/ibc/version.go create mode 100644 x/ibc/version/version.go diff --git a/x/ibc/02-client/README.md b/x/ibc/02-client/README.md index a9bd6892771b..f8b3fc98d086 100644 --- a/x/ibc/02-client/README.md +++ b/x/ibc/02-client/README.md @@ -46,4 +46,4 @@ each corresponds to `spec: Header.{height, proof, state, root}`. ### manager.go -`spec: interface ClientState` is implemented by `type Object`. // TODO +`spec: interface ClientState` is implemented by `type State`. // TODO diff --git a/x/ibc/02-client/cli.go b/x/ibc/02-client/cli.go index 67740d181748..0895bbcc2c38 100644 --- a/x/ibc/02-client/cli.go +++ b/x/ibc/02-client/cli.go @@ -5,20 +5,28 @@ import ( "github.com/cosmos/cosmos-sdk/store/state" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) -func (obj Object) prefix() []byte { +func (obj State) prefix() []byte { return bytes.Split(obj.ConsensusState.KeyBytes(), LocalRoot())[0] } -func (obj Object) ConsensusStateCLI(q state.ABCIQuerier) (res ConsensusState, proof merkle.Proof, err error) { +func (obj State) RootCLI(q state.ABCIQuerier, height uint64) (res commitment.Root, proof merkle.Proof, err error) { + root := obj.Roots.Value(height) + tmproof, err := root.Query(q, &res) + proof = merkle.NewProofFromValue(tmproof, obj.prefix(), root) + return +} + +func (obj State) ConsensusStateCLI(q state.ABCIQuerier) (res ConsensusState, proof merkle.Proof, err error) { tmproof, err := obj.ConsensusState.Query(q, &res) proof = merkle.NewProofFromValue(tmproof, obj.prefix(), obj.ConsensusState) return } -func (obj Object) FrozenCLI(q state.ABCIQuerier) (res bool, proof merkle.Proof, err error) { +func (obj State) FrozenCLI(q state.ABCIQuerier) (res bool, proof merkle.Proof, err error) { res, tmproof, err := obj.Frozen.Query(q) proof = merkle.NewProofFromValue(tmproof, obj.prefix(), obj.Frozen) return diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index 624cfd7af9ff..ca3382814356 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -14,14 +14,14 @@ import ( "github.com/cosmos/cosmos-sdk/store/state" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/ibc" client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/tendermint" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" + "github.com/cosmos/cosmos-sdk/x/ibc/version" ) -func mapping(cdc *codec.Codec, storeKey string, version int64) state.Mapping { - prefix := []byte(strconv.FormatInt(version, 10) + "/") +func mapping(cdc *codec.Codec, storeKey string, v int64) state.Mapping { + prefix := version.Prefix(v) return state.NewMapping(sdk.NewKVStoreKey(storeKey), cdc, prefix) } @@ -35,8 +35,10 @@ func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { ibcQueryCmd.AddCommand(cli.GetCommands( GetCmdQueryConsensusState(storeKey, cdc), + GetCmdQueryPath(storeKey, cdc), GetCmdQueryHeader(cdc), GetCmdQueryClient(storeKey, cdc), + GetCmdQueryRoot(storeKey, cdc), )...) return ibcQueryCmd } @@ -49,11 +51,11 @@ func GetCmdQueryClient(storeKey string, cdc *codec.Codec) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { ctx := context.NewCLIContext().WithCodec(cdc) q := state.NewCLIQuerier(ctx) - mapp := mapping(cdc, storeKey, ibc.Version) + mapp := mapping(cdc, storeKey, version.Version) man := client.NewManager(mapp) id := args[0] - state, _, err := man.Object(id).ConsensusStateCLI(q) + state, _, err := man.State(id).ConsensusStateCLI(q) if err != nil { return err } @@ -65,6 +67,33 @@ func GetCmdQueryClient(storeKey string, cdc *codec.Codec) *cobra.Command { } } +func GetCmdQueryRoot(storeKey string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "root", + Short: "Query stored root", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.NewCLIContext().WithCodec(cdc) + q := state.NewCLIQuerier(ctx) + mapp := mapping(cdc, storeKey, version.Version) + man := client.NewManager(mapp) + id := args[0] + height, err := strconv.ParseUint(args[1], 10, 64) + if err != nil { + return err + } + + root, _, err := man.State(id).RootCLI(q, height) + if err != nil { + return err + } + + fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, root)) + + return nil + }, + } +} func GetCmdQueryConsensusState(storeKey string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "consensus-state", @@ -109,6 +138,19 @@ func GetCmdQueryConsensusState(storeKey string, cdc *codec.Codec) *cobra.Command } } +func GetCmdQueryPath(storeName string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "path", + Short: "Query the commitment path of the running chain", + RunE: func(cmd *cobra.Command, args []string) error { + mapp := mapping(cdc, storeName, version.Version) + path := merkle.NewPrefix([][]byte{[]byte(storeName)}, mapp.PrefixBytes()) + fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, path)) + return nil + }, + } +} + func GetCmdQueryHeader(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "header", diff --git a/x/ibc/02-client/client/cli/tx.go b/x/ibc/02-client/client/cli/tx.go index 1b18dbe34783..d79bbbcf70e2 100644 --- a/x/ibc/02-client/client/cli/tx.go +++ b/x/ibc/02-client/client/cli/tx.go @@ -32,8 +32,8 @@ const ( func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { ibcTxCmd := &cobra.Command{ - Use: "ibc", - Short: "IBC transaction subcommands", + Use: "client", + Short: "Client transaction subcommands", DisableFlagParsing: true, SuggestionsMinimumDistance: 2, } @@ -48,7 +48,7 @@ func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { func GetCmdCreateClient(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: "create-client", + Use: "create", Short: "create new client with a consensus state", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { @@ -81,7 +81,7 @@ func GetCmdCreateClient(cdc *codec.Codec) *cobra.Command { func GetCmdUpdateClient(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: "update-client", + Use: "update", Short: "update existing client with a header", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { diff --git a/x/ibc/02-client/codec.go b/x/ibc/02-client/codec.go index ece7e750dc98..18dd6da686e8 100644 --- a/x/ibc/02-client/codec.go +++ b/x/ibc/02-client/codec.go @@ -4,11 +4,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" ) -var MsgCdc = codec.New() - -func init() { - RegisterCodec(MsgCdc) -} +var MsgCdc *codec.Codec func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*ConsensusState)(nil), nil) @@ -17,3 +13,7 @@ func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(MsgCreateClient{}, "ibc/client/MsgCreateClient", nil) cdc.RegisterConcrete(MsgUpdateClient{}, "ibc/client/MsgUpdateClient", nil) } + +func SetMsgCodec(cdc *codec.Codec) { + MsgCdc = cdc +} diff --git a/x/ibc/02-client/manager.go b/x/ibc/02-client/manager.go index 5c14323cb633..8fc389420558 100644 --- a/x/ibc/02-client/manager.go +++ b/x/ibc/02-client/manager.go @@ -40,72 +40,80 @@ func (man Manager) RegisterKind(kind Kind, pred ValidityPredicate) Manager { return man } */ -func (man Manager) Object(id string) Object { - return Object{ +func (man Manager) State(id string) State { + return State{ id: id, + Roots: man.protocol.Prefix([]byte(id + "/roots/")).Indexer(state.Dec), ConsensusState: man.protocol.Value([]byte(id)), Frozen: man.protocol.Value([]byte(id + "/freeze")).Boolean(), } } -func (man Manager) Create(ctx sdk.Context, id string, cs ConsensusState) (Object, error) { - obj := man.Object(id) +func (man Manager) Create(ctx sdk.Context, id string, cs ConsensusState) (State, error) { + obj := man.State(id) if obj.exists(ctx) { - return Object{}, errors.New("Create client on already existing id") + return State{}, errors.New("Create client on already existing id") } + obj.Roots.Set(ctx, cs.GetHeight(), cs.GetRoot()) obj.ConsensusState.Set(ctx, cs) return obj, nil } -func (man Manager) Query(ctx sdk.Context, id string) (Object, error) { - res := man.Object(id) +func (man Manager) Query(ctx sdk.Context, id string) (State, error) { + res := man.State(id) if !res.exists(ctx) { - return Object{}, errors.New("client not exists") + return State{}, errors.New("client not exists") } return res, nil } -func (man CounterpartyManager) Object(id string) CounterObject { - return CounterObject{ +func (man CounterpartyManager) State(id string) CounterState { + return CounterState{ id: id, ConsensusState: man.protocol.Value([]byte(id)), } } -func (man CounterpartyManager) Query(id string) CounterObject { - return man.Object(id) +func (man CounterpartyManager) Query(id string) CounterState { + return man.State(id) } -// Any actor holding the Object can access on and modify that client information -type Object struct { +// Any actor holding the Stage can access on and modify that client information +type State struct { id string + Roots state.Indexer ConsensusState state.Value // ConsensusState Frozen state.Boolean } -type CounterObject struct { +type CounterState struct { id string ConsensusState commitment.Value } -func (obj Object) ID() string { +func (obj State) ID() string { return obj.id } -func (obj Object) GetConsensusState(ctx sdk.Context) (res ConsensusState) { +func (obj State) GetConsensusState(ctx sdk.Context) (res ConsensusState) { obj.ConsensusState.Get(ctx, &res) return } -func (obj CounterObject) Is(ctx sdk.Context, client ConsensusState) bool { +func (obj State) GetRoot(ctx sdk.Context, height uint64) (res commitment.Root, err error) { + err = obj.Roots.GetSafe(ctx, height, &res) + return +} + +func (obj CounterState) Is(ctx sdk.Context, client ConsensusState) bool { return obj.ConsensusState.Is(ctx, client) } -func (obj Object) exists(ctx sdk.Context) bool { +func (obj State) exists(ctx sdk.Context) bool { return obj.ConsensusState.Exists(ctx) } -func (obj Object) Update(ctx sdk.Context, header Header) error { +func (obj State) Update(ctx sdk.Context, header Header) error { if !obj.exists(ctx) { panic("should not update nonexisting client") } @@ -121,6 +129,36 @@ func (obj Object) Update(ctx sdk.Context, header Header) error { } obj.ConsensusState.Set(ctx, updated) + obj.Roots.Set(ctx, updated.GetHeight(), updated.GetRoot()) + + return nil +} + +func (obj State) Freeze(ctx sdk.Context) error { + if !obj.exists(ctx) { + panic("should not freeze nonexisting client") + } + + if obj.Frozen.Get(ctx) { + return errors.New("client is already Frozen") + } + + obj.Frozen.Set(ctx, true) + + return nil +} + +func (obj State) Delete(ctx sdk.Context) error { + if !obj.exists(ctx) { + panic("should not delete nonexisting client") + } + + if !obj.Frozen.Get(ctx) { + return errors.New("client is not Frozen") + } + + obj.ConsensusState.Delete(ctx) + obj.Frozen.Delete(ctx) return nil } diff --git a/x/ibc/02-client/tendermint/codec.go b/x/ibc/02-client/tendermint/codec.go index 31a25031f9fc..1c149c0a3fe3 100644 --- a/x/ibc/02-client/tendermint/codec.go +++ b/x/ibc/02-client/tendermint/codec.go @@ -2,13 +2,8 @@ package tendermint import ( "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client" ) -func init() { - RegisterCodec(client.MsgCdc) -} - func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(ConsensusState{}, "ibc/client/tendermint/ConsensusState", nil) cdc.RegisterConcrete(Header{}, "ibc/client/tendermint/Header", nil) diff --git a/x/ibc/version.go b/x/ibc/version.go deleted file mode 100644 index 11bd8ee5b3d3..000000000000 --- a/x/ibc/version.go +++ /dev/null @@ -1,3 +0,0 @@ -package ibc - -const Version int64 = 1 diff --git a/x/ibc/version/version.go b/x/ibc/version/version.go new file mode 100644 index 000000000000..a7d3275fae5b --- /dev/null +++ b/x/ibc/version/version.go @@ -0,0 +1,13 @@ +package version + +import "strconv" + +const Version int64 = 1 + +func DefaultPrefix() []byte { + return Prefix(Version) +} + +func Prefix(version int64) []byte { + return []byte("v" + strconv.FormatInt(version, 10) + "/") +} From cc404a03f769205d36f4b9855d3f43dc39a4a874 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 2 Oct 2019 12:37:17 +0200 Subject: [PATCH 106/166] cleanup types and submodule --- x/ibc/02-client/cli.go | 33 --- x/ibc/02-client/client/cli/query.go | 12 +- x/ibc/02-client/client/cli/tx.go | 44 ++-- x/ibc/02-client/codec.go | 19 -- x/ibc/02-client/{README.md => doc.go} | 29 ++- x/ibc/02-client/exported/exported.go | 53 +++++ x/ibc/02-client/handler.go | 33 ++- x/ibc/02-client/manager.go | 164 --------------- x/ibc/02-client/tendermint/codec.go | 10 - x/ibc/02-client/tendermint/types.go | 95 --------- x/ibc/02-client/types.go | 47 ----- x/ibc/02-client/types/codec.go | 23 +++ x/ibc/02-client/{ => types}/keys.go | 3 +- x/ibc/02-client/types/manager.go | 192 ++++++++++++++++++ x/ibc/02-client/{ => types}/msgs.go | 42 ++-- .../02-client/types/tendermint/tendermint.go | 101 +++++++++ .../tendermint/tests/tendermint_test.go | 0 .../{ => types}/tendermint/tests/types.go | 6 +- .../{ => types}/tendermint/tests/utils.go | 0 .../{ => types}/tendermint/tests/valset.go | 0 x/ibc/types/types.go | 15 ++ 21 files changed, 493 insertions(+), 428 deletions(-) delete mode 100644 x/ibc/02-client/cli.go delete mode 100644 x/ibc/02-client/codec.go rename x/ibc/02-client/{README.md => doc.go} (64%) create mode 100644 x/ibc/02-client/exported/exported.go delete mode 100644 x/ibc/02-client/manager.go delete mode 100644 x/ibc/02-client/tendermint/codec.go delete mode 100644 x/ibc/02-client/tendermint/types.go delete mode 100644 x/ibc/02-client/types.go create mode 100644 x/ibc/02-client/types/codec.go rename x/ibc/02-client/{ => types}/keys.go (57%) create mode 100644 x/ibc/02-client/types/manager.go rename x/ibc/02-client/{ => types}/msgs.go (55%) create mode 100644 x/ibc/02-client/types/tendermint/tendermint.go rename x/ibc/02-client/{ => types}/tendermint/tests/tendermint_test.go (100%) rename x/ibc/02-client/{ => types}/tendermint/tests/types.go (97%) rename x/ibc/02-client/{ => types}/tendermint/tests/utils.go (100%) rename x/ibc/02-client/{ => types}/tendermint/tests/valset.go (100%) create mode 100644 x/ibc/types/types.go diff --git a/x/ibc/02-client/cli.go b/x/ibc/02-client/cli.go deleted file mode 100644 index 0895bbcc2c38..000000000000 --- a/x/ibc/02-client/cli.go +++ /dev/null @@ -1,33 +0,0 @@ -package client - -import ( - "bytes" - - "github.com/cosmos/cosmos-sdk/store/state" - - commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" -) - -func (obj State) prefix() []byte { - return bytes.Split(obj.ConsensusState.KeyBytes(), LocalRoot())[0] -} - -func (obj State) RootCLI(q state.ABCIQuerier, height uint64) (res commitment.Root, proof merkle.Proof, err error) { - root := obj.Roots.Value(height) - tmproof, err := root.Query(q, &res) - proof = merkle.NewProofFromValue(tmproof, obj.prefix(), root) - return -} - -func (obj State) ConsensusStateCLI(q state.ABCIQuerier) (res ConsensusState, proof merkle.Proof, err error) { - tmproof, err := obj.ConsensusState.Query(q, &res) - proof = merkle.NewProofFromValue(tmproof, obj.prefix(), obj.ConsensusState) - return -} - -func (obj State) FrozenCLI(q state.ABCIQuerier) (res bool, proof merkle.Proof, err error) { - res, tmproof, err := obj.Frozen.Query(q) - proof = merkle.NewProofFromValue(tmproof, obj.prefix(), obj.Frozen) - return -} diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index ca3382814356..73baefcfcd5a 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -14,8 +14,8 @@ import ( "github.com/cosmos/cosmos-sdk/store/state" sdk "github.com/cosmos/cosmos-sdk/types" - client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/tendermint" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" "github.com/cosmos/cosmos-sdk/x/ibc/version" ) @@ -52,10 +52,10 @@ func GetCmdQueryClient(storeKey string, cdc *codec.Codec) *cobra.Command { ctx := context.NewCLIContext().WithCodec(cdc) q := state.NewCLIQuerier(ctx) mapp := mapping(cdc, storeKey, version.Version) - man := client.NewManager(mapp) + manager := types.NewManager(mapp) id := args[0] - state, _, err := man.State(id).ConsensusStateCLI(q) + state, _, err := manager.State(id).ConsensusStateCLI(q) if err != nil { return err } @@ -76,14 +76,14 @@ func GetCmdQueryRoot(storeKey string, cdc *codec.Codec) *cobra.Command { ctx := context.NewCLIContext().WithCodec(cdc) q := state.NewCLIQuerier(ctx) mapp := mapping(cdc, storeKey, version.Version) - man := client.NewManager(mapp) + manager := types.NewManager(mapp) id := args[0] height, err := strconv.ParseUint(args[1], 10, 64) if err != nil { return err } - root, _, err := man.State(id).RootCLI(q, height) + root, _, err := manager.State(id).RootCLI(q, height) if err != nil { return err } diff --git a/x/ibc/02-client/client/cli/tx.go b/x/ibc/02-client/client/cli/tx.go index d79bbbcf70e2..34e7c79683cc 100644 --- a/x/ibc/02-client/client/cli/tx.go +++ b/x/ibc/02-client/client/cli/tx.go @@ -2,24 +2,21 @@ package cli import ( "io/ioutil" - // "os" + "strings" "github.com/spf13/cobra" - // "github.com/tendermint/tendermint/libs/log" - cli "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" - // "github.com/cosmos/cosmos-sdk/store/state" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" - - "github.com/cosmos/cosmos-sdk/x/ibc/02-client" - // "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" ) +// ICS02 Client CLI flags const ( FlagStatePath = "state" FlagClientID = "client-id" @@ -30,6 +27,7 @@ const ( FlagSourceNode = "source-node" ) +// GetTxCmd returns the transaction commands for IBC Clients func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { ibcTxCmd := &cobra.Command{ Use: "client", @@ -46,27 +44,32 @@ func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { return ibcTxCmd } +// GetCmdCreateClient defines the command to create a new IBC Client as defined +// in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#create func GetCmdCreateClient(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "create", Short: "create new client with a consensus state", - Args: cobra.ExactArgs(2), + Long: strings.TrimSpace(`create new client with a specified identifier and consensus state: + + $ tx ibc client create $CLIENTID ./state.json --from node0 --home ../node0/cli --chain-id $CID + `), + Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). - WithCodec(cdc) + cliCtx := context.NewCLIContext().WithCodec(cdc) contents, err := ioutil.ReadFile(args[1]) if err != nil { return err } - var state client.ConsensusState + var state exported.ConsensusState if err := cdc.UnmarshalJSON(contents, &state); err != nil { return err } - msg := client.MsgCreateClient{ + msg := types.MsgCreateClient{ ClientID: args[0], ConsensusState: state, Signer: cliCtx.GetFromAddress(), @@ -75,10 +78,11 @@ func GetCmdCreateClient(cdc *codec.Codec) *cobra.Command { return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) }, } - return cmd } +// GetCmdUpdateClient defines the command to update a client as defined in +// https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#update func GetCmdUpdateClient(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "update", @@ -86,20 +90,23 @@ func GetCmdUpdateClient(cdc *codec.Codec) *cobra.Command { Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). - WithCodec(cdc) + cliCtx := context.NewCLIContext().WithCodec(cdc) contents, err := ioutil.ReadFile(args[1]) if err != nil { return err } - var header client.Header + var header exported.Header if err := cdc.UnmarshalJSON(contents, &header); err != nil { return err } - msg := client.MsgUpdateClient{ + if err := header.ValidateBasic(txBldr.ChainID()); err != nil { + return sdk.ErrInternal(err.Error()) // TODO: create error on types + } + + msg := types.MsgUpdateClient{ ClientID: args[0], Header: header, Signer: cliCtx.GetFromAddress(), @@ -108,6 +115,5 @@ func GetCmdUpdateClient(cdc *codec.Codec) *cobra.Command { return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) }, } - return cmd } diff --git a/x/ibc/02-client/codec.go b/x/ibc/02-client/codec.go deleted file mode 100644 index 18dd6da686e8..000000000000 --- a/x/ibc/02-client/codec.go +++ /dev/null @@ -1,19 +0,0 @@ -package client - -import ( - "github.com/cosmos/cosmos-sdk/codec" -) - -var MsgCdc *codec.Codec - -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterInterface((*ConsensusState)(nil), nil) - cdc.RegisterInterface((*Header)(nil), nil) - - cdc.RegisterConcrete(MsgCreateClient{}, "ibc/client/MsgCreateClient", nil) - cdc.RegisterConcrete(MsgUpdateClient{}, "ibc/client/MsgUpdateClient", nil) -} - -func SetMsgCodec(cdc *codec.Codec) { - MsgCdc = cdc -} diff --git a/x/ibc/02-client/README.md b/x/ibc/02-client/doc.go similarity index 64% rename from x/ibc/02-client/README.md rename to x/ibc/02-client/doc.go index f8b3fc98d086..40c1e138d3d9 100644 --- a/x/ibc/02-client/README.md +++ b/x/ibc/02-client/doc.go @@ -1,10 +1,13 @@ -# ICS 02: Client +/* +Package ics02 implements the ICS 02 - Client Semenatics specification +https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics. This +concrete implementations defines types and method to store and update light +clients which tracks on other chain's state. -Package `client` defines types and method to store and update light clients which tracks on other chain's state. -The main type is `Client`, which provides `commitment.Root` to verify state proofs and `ConsensusState` to +The main type is `Client`, which provides `commitment.Root` to verify state proofs and `ConsensusState` to verify header proofs. -## Spec +Specification ```typescript interface ConsensusState { @@ -32,18 +35,22 @@ type ValidityPredicate = (ConsensusState, Header) => Error | ConsensusState type EquivocationPredicate = (ConsensusState, Header, Header) => bool ``` -## Impl +Implementation -### types.go +1. Types `spec: interface ConsensusState` is implemented by `type ConsensusState`. `ConsensusState.{GetHeight(), GetRoot(), -Validate(), Equivocation()}` each corresponds to `spec: ConsensusState.{height, root, validityPredicate, -equivocationPredicate}`. `ConsensusState.Kind()` returns `Kind`, which is an enum indicating the type of the +Validate(), Equivocation()}` each corresponds to `spec: ConsensusState.{height, root, validityPredicate, +equivocationPredicate}`. `ConsensusState.Kind()` returns `Kind`, which is an enum indicating the type of the consensus algorithm. -`spec: interface Header` is implemented by `type Header`. `Header{GetHeight(), Proof(), State(), GetRoot()}` +`spec: interface Header` is implemented by `type Header`. `Header{GetHeight(), Proof(), State(), GetRoot()}` each corresponds to `spec: Header.{height, proof, state, root}`. -### manager.go +2. Manager -`spec: interface ClientState` is implemented by `type State`. // TODO +`spec: interface ClientState` is implemented by `type State`. + + +*/ +package ics02 diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go new file mode 100644 index 000000000000..6ddab9625be8 --- /dev/null +++ b/x/ibc/02-client/exported/exported.go @@ -0,0 +1,53 @@ +package exported + +import ( + ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" +) + +// Blockchain is consensus algorithm which generates valid Headers. It generates +// a unique list of headers starting from a genesis ConsensusState with arbitrary messages. +// This interface is implemented as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#blockchain. +type Blockchain interface { + Genesis() ConsensusState // Consensus state defined in the genesis + Consensus() Header // Header generating funciton +} + +// ConsensusState is the state of the consensus process +type ConsensusState interface { + Kind() Kind // Consensus kind + GetHeight() uint64 + + // GetRoot returns the commitment root of the consensus state, + // which is used for key-value pair verification. + GetRoot() ics23.Root + + // CheckValidityAndUpdateState returns the updated consensus state + // only if the header is a descendent of this consensus state. + CheckValidityAndUpdateState(Header) (ConsensusState, error) + + // CheckMisbehaviourAndUpdateState checks any misbehaviour evidence + // depending on the state type. + CheckMisbehaviourAndUpdateState(Misbehaviour) bool +} + +// Misbehaviour defines the evidence +type Misbehaviour interface { + Kind() Kind + // TODO: embed Evidence interface + // evidence.Evidence +} + +// Header is the consensus state update information +type Header interface { + Kind() Kind + GetHeight() uint64 + ValidateBasic(chainID string) error // NOTE: added for msg validation +} + +// Kind defines the type of the consensus algorithm +type Kind byte + +// Registered consensus types +const ( + Tendermint Kind = iota +) diff --git a/x/ibc/02-client/handler.go b/x/ibc/02-client/handler.go index dfab0725b8c4..3ed9a8bde551 100644 --- a/x/ibc/02-client/handler.go +++ b/x/ibc/02-client/handler.go @@ -1,11 +1,34 @@ -package client +package ics02 import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" ) -func HandleMsgCreateClient(ctx sdk.Context, msg MsgCreateClient, man Manager) sdk.Result { - _, err := man.Create(ctx, msg.ClientID, msg.ConsensusState) +// NewHandler creates a new Handler instance for IBC client +// transactions +func NewHandler(manager types.Manager) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + ctx = ctx.WithEventManager(sdk.NewEventManager()) + + switch msg := msg.(type) { + case types.MsgCreateClient: + return handleMsgCreateClient(ctx, manager, msg) + + case types.MsgUpdateClient: + return handleMsgUpdateClient(ctx, manager, msg) + + default: + errMsg := fmt.Sprintf("unrecognized IBC Client message type: %T", msg) + return sdk.ErrUnknownRequest(errMsg).Result() + } + } +} + +func handleMsgCreateClient(ctx sdk.Context, manager types.Manager, msg types.MsgCreateClient) sdk.Result { + _, err := manager.Create(ctx, msg.ClientID, msg.ConsensusState) if err != nil { return sdk.NewError(sdk.CodespaceType("ibc"), sdk.CodeType(100), err.Error()).Result() } @@ -14,8 +37,8 @@ func HandleMsgCreateClient(ctx sdk.Context, msg MsgCreateClient, man Manager) sd return sdk.Result{} } -func HandleMsgUpdateClient(ctx sdk.Context, msg MsgUpdateClient, man Manager) sdk.Result { - obj, err := man.Query(ctx, msg.ClientID) +func handleMsgUpdateClient(ctx sdk.Context, manager types.Manager, msg types.MsgUpdateClient) sdk.Result { + obj, err := manager.Query(ctx, msg.ClientID) if err != nil { return sdk.NewError(sdk.CodespaceType("ibc"), sdk.CodeType(200), err.Error()).Result() } diff --git a/x/ibc/02-client/manager.go b/x/ibc/02-client/manager.go deleted file mode 100644 index 8fc389420558..000000000000 --- a/x/ibc/02-client/manager.go +++ /dev/null @@ -1,164 +0,0 @@ -package client - -import ( - "errors" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/state" - sdk "github.com/cosmos/cosmos-sdk/types" - - commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" -) - -// Any actor holding the Manager can access on and modify any client information -type Manager struct { - protocol state.Mapping -} - -func NewManager(base state.Mapping) Manager { - return Manager{ - protocol: base.Prefix(LocalRoot()), - } -} - -type CounterpartyManager struct { - protocol commitment.Mapping -} - -func NewCounterpartyManager(cdc *codec.Codec) CounterpartyManager { - return CounterpartyManager{ - protocol: commitment.NewMapping(cdc, LocalRoot()), - } -} - -/* -func (man Manager) RegisterKind(kind Kind, pred ValidityPredicate) Manager { - if _, ok := man.pred[kind]; ok { - panic("Kind already registered") - } - man.pred[kind] = pred - return man -} -*/ -func (man Manager) State(id string) State { - return State{ - id: id, - Roots: man.protocol.Prefix([]byte(id + "/roots/")).Indexer(state.Dec), - ConsensusState: man.protocol.Value([]byte(id)), - Frozen: man.protocol.Value([]byte(id + "/freeze")).Boolean(), - } -} - -func (man Manager) Create(ctx sdk.Context, id string, cs ConsensusState) (State, error) { - obj := man.State(id) - if obj.exists(ctx) { - return State{}, errors.New("Create client on already existing id") - } - obj.Roots.Set(ctx, cs.GetHeight(), cs.GetRoot()) - obj.ConsensusState.Set(ctx, cs) - return obj, nil -} - -func (man Manager) Query(ctx sdk.Context, id string) (State, error) { - res := man.State(id) - if !res.exists(ctx) { - return State{}, errors.New("client not exists") - } - return res, nil -} - -func (man CounterpartyManager) State(id string) CounterState { - return CounterState{ - id: id, - ConsensusState: man.protocol.Value([]byte(id)), - } -} - -func (man CounterpartyManager) Query(id string) CounterState { - return man.State(id) -} - -// Any actor holding the Stage can access on and modify that client information -type State struct { - id string - Roots state.Indexer - ConsensusState state.Value // ConsensusState - Frozen state.Boolean -} - -type CounterState struct { - id string - ConsensusState commitment.Value -} - -func (obj State) ID() string { - return obj.id -} - -func (obj State) GetConsensusState(ctx sdk.Context) (res ConsensusState) { - obj.ConsensusState.Get(ctx, &res) - return -} - -func (obj State) GetRoot(ctx sdk.Context, height uint64) (res commitment.Root, err error) { - err = obj.Roots.GetSafe(ctx, height, &res) - return -} - -func (obj CounterState) Is(ctx sdk.Context, client ConsensusState) bool { - return obj.ConsensusState.Is(ctx, client) -} - -func (obj State) exists(ctx sdk.Context) bool { - return obj.ConsensusState.Exists(ctx) -} - -func (obj State) Update(ctx sdk.Context, header Header) error { - if !obj.exists(ctx) { - panic("should not update nonexisting client") - } - - if obj.Frozen.Get(ctx) { - return errors.New("client is Frozen") - } - - stored := obj.GetConsensusState(ctx) - updated, err := stored.CheckValidityAndUpdateState(header) - if err != nil { - return err - } - - obj.ConsensusState.Set(ctx, updated) - obj.Roots.Set(ctx, updated.GetHeight(), updated.GetRoot()) - - return nil -} - -func (obj State) Freeze(ctx sdk.Context) error { - if !obj.exists(ctx) { - panic("should not freeze nonexisting client") - } - - if obj.Frozen.Get(ctx) { - return errors.New("client is already Frozen") - } - - obj.Frozen.Set(ctx, true) - - return nil -} - -func (obj State) Delete(ctx sdk.Context) error { - if !obj.exists(ctx) { - panic("should not delete nonexisting client") - } - - if !obj.Frozen.Get(ctx) { - return errors.New("client is not Frozen") - } - - obj.ConsensusState.Delete(ctx) - obj.Frozen.Delete(ctx) - - return nil -} diff --git a/x/ibc/02-client/tendermint/codec.go b/x/ibc/02-client/tendermint/codec.go deleted file mode 100644 index 1c149c0a3fe3..000000000000 --- a/x/ibc/02-client/tendermint/codec.go +++ /dev/null @@ -1,10 +0,0 @@ -package tendermint - -import ( - "github.com/cosmos/cosmos-sdk/codec" -) - -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(ConsensusState{}, "ibc/client/tendermint/ConsensusState", nil) - cdc.RegisterConcrete(Header{}, "ibc/client/tendermint/Header", nil) -} diff --git a/x/ibc/02-client/tendermint/types.go b/x/ibc/02-client/tendermint/types.go deleted file mode 100644 index 8d2f23509175..000000000000 --- a/x/ibc/02-client/tendermint/types.go +++ /dev/null @@ -1,95 +0,0 @@ -package tendermint - -import ( - "bytes" - "errors" - - lerr "github.com/tendermint/tendermint/lite/errors" - "github.com/tendermint/tendermint/types" - - client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" - commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" -) - -var _ client.ConsensusState = ConsensusState{} - -// Ref tendermint/lite/base_verifier.go -type ConsensusState struct { - ChainID string - Height uint64 - Root commitment.Root - NextValidatorSet *types.ValidatorSet -} - -func (ConsensusState) Kind() client.Kind { - return client.Tendermint -} - -func (cs ConsensusState) GetHeight() uint64 { - return cs.Height -} - -func (cs ConsensusState) GetRoot() commitment.Root { - return cs.Root -} - -func (cs ConsensusState) update(header Header) ConsensusState { - return ConsensusState{ - ChainID: cs.ChainID, - Height: uint64(header.Height), - Root: merkle.NewRoot(header.AppHash), - NextValidatorSet: header.NextValidatorSet, - } -} - -func (cs ConsensusState) CheckValidityAndUpdateState(cheader client.Header) (client.ConsensusState, error) { - header, ok := cheader.(Header) - if !ok { - return nil, errors.New("invalid type") - } - - if cs.Height == uint64(header.Height-1) { - nexthash := cs.NextValidatorSet.Hash() - if !bytes.Equal(header.ValidatorsHash, nexthash) { - return nil, lerr.ErrUnexpectedValidators(header.ValidatorsHash, nexthash) - } - } - - if !bytes.Equal(header.NextValidatorsHash, header.NextValidatorSet.Hash()) { - return nil, lerr.ErrUnexpectedValidators(header.NextValidatorsHash, header.NextValidatorSet.Hash()) - } - - err := header.ValidateBasic(cs.ChainID) - if err != nil { - return nil, err - } - - err = cs.NextValidatorSet.VerifyFutureCommit(header.ValidatorSet, cs.ChainID, header.Commit.BlockID, header.Height, header.Commit) - if err != nil { - return nil, err - } - - return cs.update(header), nil -} - -func (cs ConsensusState) CheckMisbehaviourAndUpdateState(mb client.Misbehaviour) bool { - return false // TODO: implement -} - -var _ client.Header = Header{} - -type Header struct { - // TODO: define Tendermint header type manually, don't use tmtypes - types.SignedHeader - ValidatorSet *types.ValidatorSet - NextValidatorSet *types.ValidatorSet -} - -func (header Header) Kind() client.Kind { - return client.Tendermint -} - -func (header Header) GetHeight() uint64 { - return uint64(header.Height) -} diff --git a/x/ibc/02-client/types.go b/x/ibc/02-client/types.go deleted file mode 100644 index fa18c5095b9b..000000000000 --- a/x/ibc/02-client/types.go +++ /dev/null @@ -1,47 +0,0 @@ -package client - -import ( - commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" -) - -// TODO: types in this file should be (de/)serialized with proto in the future -// currently amino codec handles it - -// ConsensusState is the state of the consensus process. -type ConsensusState interface { - // Kind() is the kind of the consensus algorithm. - Kind() Kind - GetHeight() uint64 - - // GetRoot() returns the commitment root of the consensus state, - // which is used for key-value pair verification. - GetRoot() commitment.Root - - // CheckValidityAndUpdateState() returns the updated consensus state - // only if the header is a descendent of this consensus state. - CheckValidityAndUpdateState(Header) (ConsensusState, error) - - // CheckMisbehaviourAndUpdateState() checks any misbehaviour evidence - // depending on the state type. - CheckMisbehaviourAndUpdateState(Misbehaviour) bool -} - -type Misbehaviour interface { - Kind() Kind - // TODO: embed Evidence interface - // evidence.Evidence -} - -// Header is the consensus state update information. -type Header interface { - // Kind() is the kind of the consensus algorithm. - Kind() Kind - - GetHeight() uint64 -} - -type Kind byte - -const ( - Tendermint Kind = iota -) diff --git a/x/ibc/02-client/types/codec.go b/x/ibc/02-client/types/codec.go new file mode 100644 index 000000000000..c556850a0ca2 --- /dev/null +++ b/x/ibc/02-client/types/codec.go @@ -0,0 +1,23 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" +) + +var SubModuleCdc *codec.Codec + +// RegisterCodec registers the IBC client interfaces and types +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterInterface((*exported.Blockchain)(nil), nil) + cdc.RegisterInterface((*exported.ConsensusState)(nil), nil) + cdc.RegisterInterface((*exported.Header)(nil), nil) + cdc.RegisterInterface((*exported.Misbehaviour)(nil), nil) + + cdc.RegisterConcrete(MsgCreateClient{}, "ibc/client/MsgCreateClient", nil) + cdc.RegisterConcrete(MsgUpdateClient{}, "ibc/client/MsgUpdateClient", nil) + + cdc.RegisterConcrete(tendermint.ConsensusState{}, "ibc/client/tendermint/ConsensusState", nil) + cdc.RegisterConcrete(tendermint.Header{}, "ibc/client/tendermint/Header", nil) +} diff --git a/x/ibc/02-client/keys.go b/x/ibc/02-client/types/keys.go similarity index 57% rename from x/ibc/02-client/keys.go rename to x/ibc/02-client/types/keys.go index a67761f20d36..c3d97c364a16 100644 --- a/x/ibc/02-client/keys.go +++ b/x/ibc/02-client/types/keys.go @@ -1,5 +1,6 @@ -package client +package types +// IDK what is this for... func LocalRoot() []byte { return []byte("client/") } diff --git a/x/ibc/02-client/types/manager.go b/x/ibc/02-client/types/manager.go new file mode 100644 index 000000000000..7aae2fe6827d --- /dev/null +++ b/x/ibc/02-client/types/manager.go @@ -0,0 +1,192 @@ +package types + +import ( + "bytes" + "errors" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/state" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" +) + +// Any actor holding the Manager can access on and modify any client information +type Manager struct { + protocol state.Mapping +} + +// NewManager creates a new Manager instance +func NewManager(base state.Mapping) Manager { + return Manager{ + protocol: base.Prefix(LocalRoot()), + } +} + +type CounterpartyManager struct { + protocol ics23.Mapping +} + +// NewCounterpartyManager creates a new CounterpartyManager instance +func NewCounterpartyManager(cdc *codec.Codec) CounterpartyManager { + return CounterpartyManager{ + protocol: ics23.NewMapping(cdc, LocalRoot()), + } +} + +/* +func (m Manager) RegisterKind(kind Kind, pred ValidityPredicate) Manager { + if _, ok := m.pred[kind]; ok { + panic("Kind already registered") + } + m.pred[kind] = pred + return m +} +*/ + +func (m Manager) State(id string) State { + return State{ + id: id, + Roots: m.protocol.Prefix([]byte(id + "/roots/")).Indexer(state.Dec), + ConsensusState: m.protocol.Value([]byte(id)), + Frozen: m.protocol.Value([]byte(id + "/freeze")).Boolean(), + } +} + +func (m Manager) Create(ctx sdk.Context, id string, cs exported.ConsensusState) (State, error) { + state := m.State(id) + if state.exists(ctx) { + return State{}, errors.New("cannot create client on an existing id") + } + state.Roots.Set(ctx, cs.GetHeight(), cs.GetRoot()) + state.ConsensusState.Set(ctx, cs) + return state, nil +} + +func (m Manager) Query(ctx sdk.Context, id string) (State, error) { + res := m.State(id) + if !res.exists(ctx) { + return State{}, errors.New("client doesn't exist") + } + return res, nil +} + +func (m CounterpartyManager) State(id string) CounterState { + return CounterState{ + id: id, + ConsensusState: m.protocol.Value([]byte(id)), + } +} + +func (m CounterpartyManager) Query(id string) CounterState { + return m.State(id) +} + +// Any actor holding the Stage can access on and modify that client information +type State struct { + id string + Roots state.Indexer + ConsensusState state.Value // ConsensusState + Frozen state.Boolean +} + +type CounterState struct { + id string + ConsensusState ics23.Value +} + +func (state State) ID() string { + return state.id +} + +func (state State) GetConsensusState(ctx sdk.Context) (res exported.ConsensusState) { + state.ConsensusState.Get(ctx, &res) + return +} + +func (state State) GetRoot(ctx sdk.Context, height uint64) (res ics23.Root, err error) { + err = state.Roots.GetSafe(ctx, height, &res) + return +} + +func (state CounterState) Is(ctx sdk.Context, client exported.ConsensusState) bool { + return state.ConsensusState.Is(ctx, client) +} + +func (state State) exists(ctx sdk.Context) bool { + return state.ConsensusState.Exists(ctx) +} + +func (state State) Update(ctx sdk.Context, header exported.Header) error { + if !state.exists(ctx) { + panic("should not update nonexisting client") + } + + if state.Frozen.Get(ctx) { + return errors.New("client is Frozen") + } + + stored := state.GetConsensusState(ctx) + updated, err := stored.CheckValidityAndUpdateState(header) + if err != nil { + return err + } + + state.ConsensusState.Set(ctx, updated) + state.Roots.Set(ctx, updated.GetHeight(), updated.GetRoot()) + + return nil +} + +func (state State) Freeze(ctx sdk.Context) error { + if !state.exists(ctx) { + panic("should not freeze nonexisting client") + } + + if state.Frozen.Get(ctx) { + return errors.New("client is already Frozen") + } + + state.Frozen.Set(ctx, true) + + return nil +} + +func (state State) Delete(ctx sdk.Context) error { + if !state.exists(ctx) { + panic("should not delete nonexisting client") + } + + if !state.Frozen.Get(ctx) { + return errors.New("client is not Frozen") + } + + state.ConsensusState.Delete(ctx) + state.Frozen.Delete(ctx) + + return nil +} + +func (state State) prefix() []byte { + return bytes.Split(state.ConsensusState.KeyBytes(), LocalRoot())[0] +} + +func (state State) RootCLI(q state.ABCIQuerier, height uint64) (res ics23.Root, proof merkle.Proof, err error) { + root := state.Roots.Value(height) + tmproof, err := root.Query(q, &res) + proof = merkle.NewProofFromValue(tmproof, state.prefix(), root) + return +} + +func (state State) ConsensusStateCLI(q state.ABCIQuerier) (res exported.ConsensusState, proof merkle.Proof, err error) { + tmproof, err := state.ConsensusState.Query(q, &res) + proof = merkle.NewProofFromValue(tmproof, state.prefix(), state.ConsensusState) + return +} + +func (state State) FrozenCLI(q state.ABCIQuerier) (res bool, proof merkle.Proof, err error) { + res, tmproof, err := state.Frozen.Query(q) + proof = merkle.NewProofFromValue(tmproof, state.prefix(), state.Frozen) + return +} diff --git a/x/ibc/02-client/msgs.go b/x/ibc/02-client/types/msgs.go similarity index 55% rename from x/ibc/02-client/msgs.go rename to x/ibc/02-client/types/msgs.go index 7f515c388c8f..ec9a7f122284 100644 --- a/x/ibc/02-client/msgs.go +++ b/x/ibc/02-client/types/msgs.go @@ -1,25 +1,31 @@ -package client +package types import ( sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) +var _ sdk.Msg = MsgCreateClient{} + +// MsgCreateClient defines a message to create an IBC client type MsgCreateClient struct { ClientID string - ConsensusState ConsensusState + ConsensusState exported.ConsensusState Signer sdk.AccAddress } -var _ sdk.Msg = MsgCreateClient{} - +// Route implements sdk.Msg func (msg MsgCreateClient) Route() string { - return "ibc" + return ibctypes.RouterKey } +// Type implements sdk.Msg func (msg MsgCreateClient) Type() string { - return "create-client" + return "create_client" } +// ValidateBasic implements sdk.Msg func (msg MsgCreateClient) ValidateBasic() sdk.Error { if msg.Signer.Empty() { return sdk.ErrInvalidAddress("empty address") @@ -27,31 +33,36 @@ func (msg MsgCreateClient) ValidateBasic() sdk.Error { return nil } +// GetSignBytes implements sdk.Msg func (msg MsgCreateClient) GetSignBytes() []byte { - bz := MsgCdc.MustMarshalJSON(msg) - return sdk.MustSortJSON(bz) + return sdk.MustSortJSON(SubModuleCdc.MustMarshalJSON(msg)) } +// GetSigners implements sdk.Msg func (msg MsgCreateClient) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Signer} } +var _ sdk.Msg = MsgUpdateClient{} + +// MsgUpdateClient defines a message to update an IBC client type MsgUpdateClient struct { ClientID string - Header Header + Header exported.Header Signer sdk.AccAddress } -var _ sdk.Msg = MsgUpdateClient{} - +// Route implements sdk.Msg func (msg MsgUpdateClient) Route() string { - return "ibc" + return ibctypes.RouterKey } +// Type implements sdk.Msg func (msg MsgUpdateClient) Type() string { - return "update-client" + return "update_client" } +// ValidateBasic implements sdk.Msg func (msg MsgUpdateClient) ValidateBasic() sdk.Error { if msg.Signer.Empty() { return sdk.ErrInvalidAddress("empty address") @@ -59,11 +70,12 @@ func (msg MsgUpdateClient) ValidateBasic() sdk.Error { return nil } +// GetSignBytes implements sdk.Msg func (msg MsgUpdateClient) GetSignBytes() []byte { - bz := MsgCdc.MustMarshalJSON(msg) - return sdk.MustSortJSON(bz) + return sdk.MustSortJSON(SubModuleCdc.MustMarshalJSON(msg)) } +// GetSigners implements sdk.Msg func (msg MsgUpdateClient) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Signer} } diff --git a/x/ibc/02-client/types/tendermint/tendermint.go b/x/ibc/02-client/types/tendermint/tendermint.go new file mode 100644 index 000000000000..bff49e738f7b --- /dev/null +++ b/x/ibc/02-client/types/tendermint/tendermint.go @@ -0,0 +1,101 @@ +package tendermint + +import ( + "bytes" + "errors" + + lerr "github.com/tendermint/tendermint/lite/errors" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" +) + +var _ exported.ConsensusState = ConsensusState{} + +// ConsensusState defines a Tendermint consensus state +type ConsensusState struct { + ChainID string + Height uint64 + Root ics23.Root + NextValidatorSet *tmtypes.ValidatorSet +} + +func (ConsensusState) Kind() exported.Kind { + return exported.Tendermint +} + +func (cs ConsensusState) GetHeight() uint64 { + return cs.Height +} + +func (cs ConsensusState) GetRoot() ics23.Root { + return cs.Root +} + +func (cs ConsensusState) update(header Header) ConsensusState { + return ConsensusState{ + ChainID: cs.ChainID, + Height: uint64(header.Height), + Root: merkle.NewRoot(header.AppHash), + NextValidatorSet: header.NextValidatorSet, + } +} + +// CheckValidityAndUpdateState +func (cs ConsensusState) CheckValidityAndUpdateState(header exported.Header) (exported.ConsensusState, error) { + tmHeader, ok := header.(Header) + if !ok { + return nil, errors.New("header is not from a tendermint consensus") // TODO: create concrete error + } + + if cs.Height == uint64(tmHeader.Height-1) { + nexthash := cs.NextValidatorSet.Hash() + if !bytes.Equal(tmHeader.ValidatorsHash, nexthash) { + return nil, lerr.ErrUnexpectedValidators(tmHeader.ValidatorsHash, nexthash) + } + } + + if !bytes.Equal(tmHeader.NextValidatorsHash, tmHeader.NextValidatorSet.Hash()) { + return nil, lerr.ErrUnexpectedValidators(tmHeader.NextValidatorsHash, tmHeader.NextValidatorSet.Hash()) + } + + err := tmHeader.ValidateBasic(cs.ChainID) + if err != nil { + return nil, err + } + + err = cs.NextValidatorSet.VerifyFutureCommit(tmHeader.ValidatorSet, cs.ChainID, tmHeader.Commit.BlockID, tmHeader.Height, tmHeader.Commit) + if err != nil { + return nil, err + } + + return cs.update(tmHeader), nil +} + +// CheckMisbehaviourAndUpdateState - not implemented +func (cs ConsensusState) CheckMisbehaviourAndUpdateState(mb exported.Misbehaviour) bool { + // TODO: implement + return false +} + +var _ exported.Header = Header{} + +// Header defines the Tendermint consensus Header +type Header struct { + // TODO: define Tendermint header type manually, don't use tmtypes + tmtypes.SignedHeader + ValidatorSet *tmtypes.ValidatorSet + NextValidatorSet *tmtypes.ValidatorSet +} + +// Kind defines that the Header is a Tendermint consensus algorithm +func (header Header) Kind() exported.Kind { + return exported.Tendermint +} + +// GetHeight returns the current height +func (header Header) GetHeight() uint64 { + return uint64(header.Height) +} diff --git a/x/ibc/02-client/tendermint/tests/tendermint_test.go b/x/ibc/02-client/types/tendermint/tests/tendermint_test.go similarity index 100% rename from x/ibc/02-client/tendermint/tests/tendermint_test.go rename to x/ibc/02-client/types/tendermint/tests/tendermint_test.go diff --git a/x/ibc/02-client/tendermint/tests/types.go b/x/ibc/02-client/types/tendermint/tests/types.go similarity index 97% rename from x/ibc/02-client/tendermint/tests/types.go rename to x/ibc/02-client/types/tendermint/tests/types.go index b9b238c74126..2094de08abc6 100644 --- a/x/ibc/02-client/tendermint/tests/types.go +++ b/x/ibc/02-client/types/tendermint/tests/types.go @@ -18,8 +18,8 @@ import ( stypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/tendermint" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) @@ -125,7 +125,7 @@ func (node *Node) Context() sdk.Context { } type Verifier struct { - client.ConsensusState + exported.ConsensusState } func NewVerifier(header tmtypes.SignedHeader, nextvalset MockValidators, root merkle.Root) *Verifier { diff --git a/x/ibc/02-client/tendermint/tests/utils.go b/x/ibc/02-client/types/tendermint/tests/utils.go similarity index 100% rename from x/ibc/02-client/tendermint/tests/utils.go rename to x/ibc/02-client/types/tendermint/tests/utils.go diff --git a/x/ibc/02-client/tendermint/tests/valset.go b/x/ibc/02-client/types/tendermint/tests/valset.go similarity index 100% rename from x/ibc/02-client/tendermint/tests/valset.go rename to x/ibc/02-client/types/tendermint/tests/valset.go diff --git a/x/ibc/types/types.go b/x/ibc/types/types.go new file mode 100644 index 000000000000..535a8f2461e5 --- /dev/null +++ b/x/ibc/types/types.go @@ -0,0 +1,15 @@ +package types + +const ( + // ModuleName is the name of the IBC module + ModuleName = "ibc" + + // StoreKey is the string store representation + StoreKey = ModuleName + + // QuerierRoute is the querier route for the IBC module + QuerierRoute = ModuleName + + // RouterKey is the msg router key for the IBC module + RouterKey = ModuleName +) From d93a0ae027cfdb7764c768197084925dcfdb84d3 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 2 Oct 2019 13:56:34 +0200 Subject: [PATCH 107/166] more cleanup and godocs --- x/ibc/02-client/client/cli/query.go | 50 ++++-- x/ibc/02-client/doc.go | 2 - x/ibc/02-client/exported/exported.go | 2 +- x/ibc/02-client/handler.go | 7 +- x/ibc/02-client/types/errors.go | 3 + x/ibc/02-client/types/manager.go | 164 ++++-------------- x/ibc/02-client/types/state.go | 118 +++++++++++++ .../02-client/types/tendermint/tests/types.go | 4 +- 8 files changed, 195 insertions(+), 155 deletions(-) create mode 100644 x/ibc/02-client/types/errors.go create mode 100644 x/ibc/02-client/types/state.go diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index 73baefcfcd5a..ddcd2d73e30b 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -3,6 +3,7 @@ package cli import ( "fmt" "strconv" + "strings" "github.com/spf13/cobra" @@ -25,6 +26,7 @@ func mapping(cdc *codec.Codec, storeKey string, v int64) state.Mapping { return state.NewMapping(sdk.NewKVStoreKey(storeKey), cdc, prefix) } +// GetQueryCmd returns the query commands for IBC clients func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { ibcQueryCmd := &cobra.Command{ Use: "client", @@ -37,17 +39,23 @@ func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { GetCmdQueryConsensusState(storeKey, cdc), GetCmdQueryPath(storeKey, cdc), GetCmdQueryHeader(cdc), - GetCmdQueryClient(storeKey, cdc), + GetCmdQueryClientState(storeKey, cdc), GetCmdQueryRoot(storeKey, cdc), )...) return ibcQueryCmd } -func GetCmdQueryClient(storeKey string, cdc *codec.Codec) *cobra.Command { +// GetCmdQueryClientState defines the command to query the state of a client with +// a given id as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#query +func GetCmdQueryClientState(storeKey string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ - Use: "client", - Short: "Query stored client", - Args: cobra.ExactArgs(1), + Use: "state", + Short: "Query a client state", + Long: strings.TrimSpace(`Query stored client + +$ cli query ibc client state [id] + `), + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := context.NewCLIContext().WithCodec(cdc) q := state.NewCLIQuerier(ctx) @@ -61,17 +69,21 @@ func GetCmdQueryClient(storeKey string, cdc *codec.Codec) *cobra.Command { } fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, state)) - return nil }, } } +// GetCmdQueryRoot defines the command to query func GetCmdQueryRoot(storeKey string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "root", Short: "Query stored root", - Args: cobra.ExactArgs(2), + Long: strings.TrimSpace(`Query stored client + +$ cli query ibc client root [id] [height] + `), + Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { ctx := context.NewCLIContext().WithCodec(cdc) q := state.NewCLIQuerier(ctx) @@ -89,15 +101,21 @@ func GetCmdQueryRoot(storeKey string, cdc *codec.Codec) *cobra.Command { } fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, root)) - return nil }, } } + +// GetCmdQueryConsensusState defines the command to query the consensus state of +// the chain as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#query func GetCmdQueryConsensusState(storeKey string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "consensus-state", Short: "Query the latest consensus state of the running chain", + Long: strings.TrimSpace(`Query consensus state + +$ cli query ibc client consensus-state + `), RunE: func(cmd *cobra.Command, args []string) error { ctx := context.NewCLIContext().WithCodec(cdc) @@ -132,16 +150,20 @@ func GetCmdQueryConsensusState(storeKey string, cdc *codec.Codec) *cobra.Command } fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, state)) - return nil }, } } +// GetCmdQueryPath defines the command to query the commitment path func GetCmdQueryPath(storeName string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "path", Short: "Query the commitment path of the running chain", + Long: strings.TrimSpace(`Query the commitment path + +$ cli query ibc client path + `), RunE: func(cmd *cobra.Command, args []string) error { mapp := mapping(cdc, storeName, version.Version) path := merkle.NewPrefix([][]byte{[]byte(storeName)}, mapp.PrefixBytes()) @@ -151,10 +173,15 @@ func GetCmdQueryPath(storeName string, cdc *codec.Codec) *cobra.Command { } } +// GetCmdQueryHeader defines the command to query the latest header on the chain func GetCmdQueryHeader(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "header", Short: "Query the latest header of the running chain", + Long: strings.TrimSpace(`Query the latest header + +$ cli query ibc client header + `), RunE: func(cmd *cobra.Command, args []string) error { ctx := context.NewCLIContext().WithCodec(cdc) @@ -181,7 +208,7 @@ func GetCmdQueryHeader(cdc *codec.Codec) *cobra.Command { return err } - nextvalidators, err := node.Validators(&height) + nextValidators, err := node.Validators(&height) if err != nil { return err } @@ -189,11 +216,10 @@ func GetCmdQueryHeader(cdc *codec.Codec) *cobra.Command { header := tendermint.Header{ SignedHeader: commit.SignedHeader, ValidatorSet: tmtypes.NewValidatorSet(validators.Validators), - NextValidatorSet: tmtypes.NewValidatorSet(nextvalidators.Validators), + NextValidatorSet: tmtypes.NewValidatorSet(nextValidators.Validators), } fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, header)) - return nil }, } diff --git a/x/ibc/02-client/doc.go b/x/ibc/02-client/doc.go index 40c1e138d3d9..c2ce8f7729e6 100644 --- a/x/ibc/02-client/doc.go +++ b/x/ibc/02-client/doc.go @@ -50,7 +50,5 @@ each corresponds to `spec: Header.{height, proof, state, root}`. 2. Manager `spec: interface ClientState` is implemented by `type State`. - - */ package ics02 diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index 6ddab9625be8..3fbad3b79e9f 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -9,7 +9,7 @@ import ( // This interface is implemented as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#blockchain. type Blockchain interface { Genesis() ConsensusState // Consensus state defined in the genesis - Consensus() Header // Header generating funciton + Consensus() Header // Header generating function } // ConsensusState is the state of the consensus process diff --git a/x/ibc/02-client/handler.go b/x/ibc/02-client/handler.go index 3ed9a8bde551..a367ce36d6ca 100644 --- a/x/ibc/02-client/handler.go +++ b/x/ibc/02-client/handler.go @@ -28,7 +28,7 @@ func NewHandler(manager types.Manager) sdk.Handler { } func handleMsgCreateClient(ctx sdk.Context, manager types.Manager, msg types.MsgCreateClient) sdk.Result { - _, err := manager.Create(ctx, msg.ClientID, msg.ConsensusState) + _, err := manager.CreateClient(ctx, msg.ClientID, msg.ConsensusState) if err != nil { return sdk.NewError(sdk.CodespaceType("ibc"), sdk.CodeType(100), err.Error()).Result() } @@ -38,11 +38,12 @@ func handleMsgCreateClient(ctx sdk.Context, manager types.Manager, msg types.Msg } func handleMsgUpdateClient(ctx sdk.Context, manager types.Manager, msg types.MsgUpdateClient) sdk.Result { - obj, err := manager.Query(ctx, msg.ClientID) + state, err := manager.Query(ctx, msg.ClientID) if err != nil { return sdk.NewError(sdk.CodespaceType("ibc"), sdk.CodeType(200), err.Error()).Result() } - err = obj.Update(ctx, msg.Header) + + err = state.Update(ctx, msg.Header) if err != nil { return sdk.NewError(sdk.CodespaceType("ibc"), sdk.CodeType(300), err.Error()).Result() } diff --git a/x/ibc/02-client/types/errors.go b/x/ibc/02-client/types/errors.go new file mode 100644 index 000000000000..d8fa0171da5d --- /dev/null +++ b/x/ibc/02-client/types/errors.go @@ -0,0 +1,3 @@ +package types + +// TODO: diff --git a/x/ibc/02-client/types/manager.go b/x/ibc/02-client/types/manager.go index 7aae2fe6827d..039774fc44b7 100644 --- a/x/ibc/02-client/types/manager.go +++ b/x/ibc/02-client/types/manager.go @@ -1,7 +1,6 @@ package types import ( - "bytes" "errors" "github.com/cosmos/cosmos-sdk/codec" @@ -9,29 +8,18 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) -// Any actor holding the Manager can access on and modify any client information +// Manager represents a type that grants read and write permissions to any client +// state information type Manager struct { protocol state.Mapping } // NewManager creates a new Manager instance -func NewManager(base state.Mapping) Manager { +func NewManager(mapping state.Mapping) Manager { return Manager{ - protocol: base.Prefix(LocalRoot()), - } -} - -type CounterpartyManager struct { - protocol ics23.Mapping -} - -// NewCounterpartyManager creates a new CounterpartyManager instance -func NewCounterpartyManager(cdc *codec.Codec) CounterpartyManager { - return CounterpartyManager{ - protocol: ics23.NewMapping(cdc, LocalRoot()), + protocol: mapping.Prefix(LocalRoot()), } } @@ -45,25 +33,30 @@ func (m Manager) RegisterKind(kind Kind, pred ValidityPredicate) Manager { } */ -func (m Manager) State(id string) State { - return State{ - id: id, - Roots: m.protocol.Prefix([]byte(id + "/roots/")).Indexer(state.Dec), - ConsensusState: m.protocol.Value([]byte(id)), - Frozen: m.protocol.Value([]byte(id + "/freeze")).Boolean(), - } -} - -func (m Manager) Create(ctx sdk.Context, id string, cs exported.ConsensusState) (State, error) { +// CreateClient creates a new client state and populates it with a given consensus state +func (m Manager) CreateClient(ctx sdk.Context, id string, cs exported.ConsensusState) (State, error) { state := m.State(id) if state.exists(ctx) { return State{}, errors.New("cannot create client on an existing id") } + + // set the most recent state root and consensus state state.Roots.Set(ctx, cs.GetHeight(), cs.GetRoot()) state.ConsensusState.Set(ctx, cs) return state, nil } +// State returnts a new client state with a given id +func (m Manager) State(id string) State { + return State{ + id: id, + Roots: m.protocol.Prefix([]byte(id + "/roots/")).Indexer(state.Dec), + ConsensusState: m.protocol.Value([]byte(id)), + Frozen: m.protocol.Value([]byte(id + "/freeze")).Boolean(), + } +} + +// Query returns a client state that matches a given ID func (m Manager) Query(ctx sdk.Context, id string) (State, error) { res := m.State(id) if !res.exists(ctx) { @@ -72,6 +65,17 @@ func (m Manager) Query(ctx sdk.Context, id string) (State, error) { return res, nil } +type CounterpartyManager struct { + protocol ics23.Mapping +} + +// NewCounterpartyManager creates a new CounterpartyManager instance +func NewCounterpartyManager(cdc *codec.Codec) CounterpartyManager { + return CounterpartyManager{ + protocol: ics23.NewMapping(cdc, LocalRoot()), + } +} + func (m CounterpartyManager) State(id string) CounterState { return CounterState{ id: id, @@ -82,111 +86,3 @@ func (m CounterpartyManager) State(id string) CounterState { func (m CounterpartyManager) Query(id string) CounterState { return m.State(id) } - -// Any actor holding the Stage can access on and modify that client information -type State struct { - id string - Roots state.Indexer - ConsensusState state.Value // ConsensusState - Frozen state.Boolean -} - -type CounterState struct { - id string - ConsensusState ics23.Value -} - -func (state State) ID() string { - return state.id -} - -func (state State) GetConsensusState(ctx sdk.Context) (res exported.ConsensusState) { - state.ConsensusState.Get(ctx, &res) - return -} - -func (state State) GetRoot(ctx sdk.Context, height uint64) (res ics23.Root, err error) { - err = state.Roots.GetSafe(ctx, height, &res) - return -} - -func (state CounterState) Is(ctx sdk.Context, client exported.ConsensusState) bool { - return state.ConsensusState.Is(ctx, client) -} - -func (state State) exists(ctx sdk.Context) bool { - return state.ConsensusState.Exists(ctx) -} - -func (state State) Update(ctx sdk.Context, header exported.Header) error { - if !state.exists(ctx) { - panic("should not update nonexisting client") - } - - if state.Frozen.Get(ctx) { - return errors.New("client is Frozen") - } - - stored := state.GetConsensusState(ctx) - updated, err := stored.CheckValidityAndUpdateState(header) - if err != nil { - return err - } - - state.ConsensusState.Set(ctx, updated) - state.Roots.Set(ctx, updated.GetHeight(), updated.GetRoot()) - - return nil -} - -func (state State) Freeze(ctx sdk.Context) error { - if !state.exists(ctx) { - panic("should not freeze nonexisting client") - } - - if state.Frozen.Get(ctx) { - return errors.New("client is already Frozen") - } - - state.Frozen.Set(ctx, true) - - return nil -} - -func (state State) Delete(ctx sdk.Context) error { - if !state.exists(ctx) { - panic("should not delete nonexisting client") - } - - if !state.Frozen.Get(ctx) { - return errors.New("client is not Frozen") - } - - state.ConsensusState.Delete(ctx) - state.Frozen.Delete(ctx) - - return nil -} - -func (state State) prefix() []byte { - return bytes.Split(state.ConsensusState.KeyBytes(), LocalRoot())[0] -} - -func (state State) RootCLI(q state.ABCIQuerier, height uint64) (res ics23.Root, proof merkle.Proof, err error) { - root := state.Roots.Value(height) - tmproof, err := root.Query(q, &res) - proof = merkle.NewProofFromValue(tmproof, state.prefix(), root) - return -} - -func (state State) ConsensusStateCLI(q state.ABCIQuerier) (res exported.ConsensusState, proof merkle.Proof, err error) { - tmproof, err := state.ConsensusState.Query(q, &res) - proof = merkle.NewProofFromValue(tmproof, state.prefix(), state.ConsensusState) - return -} - -func (state State) FrozenCLI(q state.ABCIQuerier) (res bool, proof merkle.Proof, err error) { - res, tmproof, err := state.Frozen.Query(q) - proof = merkle.NewProofFromValue(tmproof, state.prefix(), state.Frozen) - return -} diff --git a/x/ibc/02-client/types/state.go b/x/ibc/02-client/types/state.go new file mode 100644 index 000000000000..0abbd74da179 --- /dev/null +++ b/x/ibc/02-client/types/state.go @@ -0,0 +1,118 @@ +package types + +import ( + "bytes" + "errors" + + "github.com/cosmos/cosmos-sdk/store/state" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" +) + +// State is a type that represents the state of a client. +// Any actor holding the Stage can access on and modify that client information. +type State struct { + // Client ID + id string + // Past state roots required to avoid race conditions between client updates + // and proof-carrying transactions as defined in + // https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#utilising-past-roots + Roots state.Indexer + // Consensus state bytes + ConsensusState state.Value + // Boolean that states if the client is frozen when a misbehaviour proof is + // submitted in the event of an equivocation. + Frozen state.Boolean +} + +// ID returns the client identifier +func (state State) ID() string { + return state.id +} + +// GetConsensusState returns the consensus state +func (state State) GetConsensusState(ctx sdk.Context) (cs exported.ConsensusState) { + state.ConsensusState.Get(ctx, &cs) + return +} + +// GetRoot returns the commitment root of the client at a given height +func (state State) GetRoot(ctx sdk.Context, height uint64) (root ics23.Root, err error) { + err = state.Roots.GetSafe(ctx, height, &root) + return +} + +// Update updates the consensus state and the state root from a provided header +func (state State) Update(ctx sdk.Context, header exported.Header) error { + if !state.exists(ctx) { + panic("should not update nonexisting client") + } + + if state.Frozen.Get(ctx) { + return errors.New("client is frozen due to misbehaviour") + } + + stored := state.GetConsensusState(ctx) + updated, err := stored.CheckValidityAndUpdateState(header) + if err != nil { + return err + } + + state.ConsensusState.Set(ctx, updated) + state.Roots.Set(ctx, updated.GetHeight(), updated.GetRoot()) + + return nil +} + +// Freeze updates the state of the client in the event of a misbehaviour +func (state State) Freeze(ctx sdk.Context) error { + if !state.exists(ctx) { + panic("should not freeze nonexisting client") + } + + if state.Frozen.Get(ctx) { + return errors.New("cannot freeze an already frozen client") + } + + state.Frozen.Set(ctx, true) + return nil +} + +func (state State) RootCLI(q state.ABCIQuerier, height uint64) (res ics23.Root, proof merkle.Proof, err error) { + root := state.Roots.Value(height) + tmProof, err := root.Query(q, &res) + proof = merkle.NewProofFromValue(tmProof, state.prefix(), root) + return +} + +func (state State) ConsensusStateCLI(q state.ABCIQuerier) (res exported.ConsensusState, proof merkle.Proof, err error) { + tmproof, err := state.ConsensusState.Query(q, &res) + proof = merkle.NewProofFromValue(tmproof, state.prefix(), state.ConsensusState) + return +} + +func (state State) FrozenCLI(q state.ABCIQuerier) (res bool, proof merkle.Proof, err error) { + res, tmproof, err := state.Frozen.Query(q) + proof = merkle.NewProofFromValue(tmproof, state.prefix(), state.Frozen) + return +} + +// exists verifies if the client exists or not +func (state State) exists(ctx sdk.Context) bool { + return state.ConsensusState.Exists(ctx) +} + +func (state State) prefix() []byte { + return bytes.Split(state.ConsensusState.KeyBytes(), LocalRoot())[0] +} + +type CounterState struct { + id string + ConsensusState ics23.Value +} + +func (counterState CounterState) Is(ctx sdk.Context, client exported.ConsensusState) bool { + return counterState.ConsensusState.Is(ctx, client) +} diff --git a/x/ibc/02-client/types/tendermint/tests/types.go b/x/ibc/02-client/types/tendermint/tests/types.go index 2094de08abc6..532b01c635ee 100644 --- a/x/ibc/02-client/types/tendermint/tests/types.go +++ b/x/ibc/02-client/types/tendermint/tests/types.go @@ -150,9 +150,7 @@ func (v *Verifier) Validate(header tendermint.Header, valset, nextvalset MockVal } func (node *Node) Query(t *testing.T, k []byte) ([]byte, commitment.Proof) { - if bytes.HasPrefix(k, node.KeyPrefix) { - k = bytes.TrimPrefix(k, node.KeyPrefix) - } + k = bytes.TrimPrefix(k, node.KeyPrefix) value, proof, err := merkle.QueryMultiStore(node.Cms, node.StoreName, node.KeyPrefix, k) require.NoError(t, err) return value, proof From 9b6afe920da2e1fabaeedf55680ec72d584c7eaf Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 2 Oct 2019 14:13:45 +0200 Subject: [PATCH 108/166] remove counterPartyManager/State and cleanup --- x/ibc/02-client/client/cli/tx.go | 4 --- x/ibc/02-client/exported/exported.go | 1 - x/ibc/02-client/types/manager.go | 34 ------------------- x/ibc/02-client/types/msgs.go | 21 ++++++++---- x/ibc/02-client/types/state.go | 15 ++------ .../02-client/types/tendermint/tendermint.go | 34 +++++++++++-------- 6 files changed, 37 insertions(+), 72 deletions(-) diff --git a/x/ibc/02-client/client/cli/tx.go b/x/ibc/02-client/client/cli/tx.go index 34e7c79683cc..f02ac62c496b 100644 --- a/x/ibc/02-client/client/cli/tx.go +++ b/x/ibc/02-client/client/cli/tx.go @@ -102,10 +102,6 @@ func GetCmdUpdateClient(cdc *codec.Codec) *cobra.Command { return err } - if err := header.ValidateBasic(txBldr.ChainID()); err != nil { - return sdk.ErrInternal(err.Error()) // TODO: create error on types - } - msg := types.MsgUpdateClient{ ClientID: args[0], Header: header, diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index 3fbad3b79e9f..576e06440386 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -41,7 +41,6 @@ type Misbehaviour interface { type Header interface { Kind() Kind GetHeight() uint64 - ValidateBasic(chainID string) error // NOTE: added for msg validation } // Kind defines the type of the consensus algorithm diff --git a/x/ibc/02-client/types/manager.go b/x/ibc/02-client/types/manager.go index 039774fc44b7..8af23d87c895 100644 --- a/x/ibc/02-client/types/manager.go +++ b/x/ibc/02-client/types/manager.go @@ -3,11 +3,9 @@ package types import ( "errors" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/state" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) // Manager represents a type that grants read and write permissions to any client @@ -23,16 +21,6 @@ func NewManager(mapping state.Mapping) Manager { } } -/* -func (m Manager) RegisterKind(kind Kind, pred ValidityPredicate) Manager { - if _, ok := m.pred[kind]; ok { - panic("Kind already registered") - } - m.pred[kind] = pred - return m -} -*/ - // CreateClient creates a new client state and populates it with a given consensus state func (m Manager) CreateClient(ctx sdk.Context, id string, cs exported.ConsensusState) (State, error) { state := m.State(id) @@ -64,25 +52,3 @@ func (m Manager) Query(ctx sdk.Context, id string) (State, error) { } return res, nil } - -type CounterpartyManager struct { - protocol ics23.Mapping -} - -// NewCounterpartyManager creates a new CounterpartyManager instance -func NewCounterpartyManager(cdc *codec.Codec) CounterpartyManager { - return CounterpartyManager{ - protocol: ics23.NewMapping(cdc, LocalRoot()), - } -} - -func (m CounterpartyManager) State(id string) CounterState { - return CounterState{ - id: id, - ConsensusState: m.protocol.Value([]byte(id)), - } -} - -func (m CounterpartyManager) Query(id string) CounterState { - return m.State(id) -} diff --git a/x/ibc/02-client/types/msgs.go b/x/ibc/02-client/types/msgs.go index ec9a7f122284..e1f983f0368b 100644 --- a/x/ibc/02-client/types/msgs.go +++ b/x/ibc/02-client/types/msgs.go @@ -10,9 +10,18 @@ var _ sdk.Msg = MsgCreateClient{} // MsgCreateClient defines a message to create an IBC client type MsgCreateClient struct { - ClientID string - ConsensusState exported.ConsensusState - Signer sdk.AccAddress + ClientID string `json:"id" yaml:"id"` + ConsensusState exported.ConsensusState `json:"consensus_state" yaml:"consensus_address"` + Signer sdk.AccAddress `json:"address" yaml:"address"` +} + +// NewMsgCreateClient creates a new MsgCreateClient instance +func NewMsgCreateClient(ID string, consensusState exported.ConsensusState, signer sdk.AccAddress) MsgCreateClient { + return MsgCreateClient{ + ClientID: ID, + ConsensusState: consensusState, + Signer: signer, + } } // Route implements sdk.Msg @@ -47,9 +56,9 @@ var _ sdk.Msg = MsgUpdateClient{} // MsgUpdateClient defines a message to update an IBC client type MsgUpdateClient struct { - ClientID string - Header exported.Header - Signer sdk.AccAddress + ClientID string `json:"id" yaml:"id"` + Header exported.Header `json:"header" yaml:"header"` + Signer sdk.AccAddress `json:"address" yaml:"address"` } // Route implements sdk.Msg diff --git a/x/ibc/02-client/types/state.go b/x/ibc/02-client/types/state.go index 0abbd74da179..5e968b969583 100644 --- a/x/ibc/02-client/types/state.go +++ b/x/ibc/02-client/types/state.go @@ -19,12 +19,12 @@ type State struct { // Past state roots required to avoid race conditions between client updates // and proof-carrying transactions as defined in // https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#utilising-past-roots - Roots state.Indexer + Roots state.Indexer `json:"roots" yaml:"roots"` // Consensus state bytes - ConsensusState state.Value + ConsensusState state.Value `json:"consensus_state" yaml:"consensus_state"` // Boolean that states if the client is frozen when a misbehaviour proof is // submitted in the event of an equivocation. - Frozen state.Boolean + Frozen state.Boolean `json:"frozen" yaml:"frozen"` } // ID returns the client identifier @@ -107,12 +107,3 @@ func (state State) exists(ctx sdk.Context) bool { func (state State) prefix() []byte { return bytes.Split(state.ConsensusState.KeyBytes(), LocalRoot())[0] } - -type CounterState struct { - id string - ConsensusState ics23.Value -} - -func (counterState CounterState) Is(ctx sdk.Context, client exported.ConsensusState) bool { - return counterState.ConsensusState.Is(ctx, client) -} diff --git a/x/ibc/02-client/types/tendermint/tendermint.go b/x/ibc/02-client/types/tendermint/tendermint.go index bff49e738f7b..5ae497307d73 100644 --- a/x/ibc/02-client/types/tendermint/tendermint.go +++ b/x/ibc/02-client/types/tendermint/tendermint.go @@ -16,33 +16,27 @@ var _ exported.ConsensusState = ConsensusState{} // ConsensusState defines a Tendermint consensus state type ConsensusState struct { - ChainID string - Height uint64 - Root ics23.Root - NextValidatorSet *tmtypes.ValidatorSet + ChainID string `json:"chain_id" yaml:"chain_id"` + Height uint64 `json:"height" yaml:"height"` + Root ics23.Root `json:"root" yaml:"root"` + NextValidatorSet *tmtypes.ValidatorSet `json:"next_validator_set" yaml:"next_validator_set"` } +// Kind returns Tendermint func (ConsensusState) Kind() exported.Kind { return exported.Tendermint } +// GetHeight returns the ConsensusState height func (cs ConsensusState) GetHeight() uint64 { return cs.Height } +// GetRoot returns the commitment Root func (cs ConsensusState) GetRoot() ics23.Root { return cs.Root } -func (cs ConsensusState) update(header Header) ConsensusState { - return ConsensusState{ - ChainID: cs.ChainID, - Height: uint64(header.Height), - Root: merkle.NewRoot(header.AppHash), - NextValidatorSet: header.NextValidatorSet, - } -} - // CheckValidityAndUpdateState func (cs ConsensusState) CheckValidityAndUpdateState(header exported.Header) (exported.ConsensusState, error) { tmHeader, ok := header.(Header) @@ -80,14 +74,24 @@ func (cs ConsensusState) CheckMisbehaviourAndUpdateState(mb exported.Misbehaviou return false } +// update updates the consensus state from a new header +func (cs ConsensusState) update(header Header) ConsensusState { + return ConsensusState{ + ChainID: cs.ChainID, + Height: uint64(header.Height), + Root: merkle.NewRoot(header.AppHash), + NextValidatorSet: header.NextValidatorSet, + } +} + var _ exported.Header = Header{} // Header defines the Tendermint consensus Header type Header struct { // TODO: define Tendermint header type manually, don't use tmtypes tmtypes.SignedHeader - ValidatorSet *tmtypes.ValidatorSet - NextValidatorSet *tmtypes.ValidatorSet + ValidatorSet *tmtypes.ValidatorSet `json:"validator_set" yaml:"validator_set"` + NextValidatorSet *tmtypes.ValidatorSet `json:"next_validator_set" yaml:"next_validator_set"` } // Kind defines that the Header is a Tendermint consensus algorithm From c8a0ab41c16ffb19b546b7abc63e07a8b36b95d3 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 2 Oct 2019 19:11:07 +0200 Subject: [PATCH 109/166] implement SubmitMisbehaviour and refactor --- x/ibc/02-client/client/cli/query.go | 2 + x/ibc/02-client/client/cli/tx.go | 50 ++++++++++- x/ibc/02-client/exported/exported.go | 14 ++-- x/ibc/02-client/handler.go | 33 ++++++-- x/ibc/02-client/keeper/keeper.go | 84 +++++++++++++++++++ x/ibc/02-client/types/keys.go | 8 +- x/ibc/02-client/types/manager.go | 54 ------------ x/ibc/02-client/types/msgs.go | 57 ++++++++++++- x/ibc/02-client/types/state.go | 26 ++++-- .../{tendermint.go => consensus_state.go} | 59 ++++--------- x/ibc/02-client/types/tendermint/doc.go | 5 ++ x/ibc/02-client/types/tendermint/header.go | 46 ++++++++++ .../types/tendermint/misbehaviour.go | 18 ++++ 13 files changed, 330 insertions(+), 126 deletions(-) create mode 100644 x/ibc/02-client/keeper/keeper.go delete mode 100644 x/ibc/02-client/types/manager.go rename x/ibc/02-client/types/tendermint/{tendermint.go => consensus_state.go} (54%) create mode 100644 x/ibc/02-client/types/tendermint/doc.go create mode 100644 x/ibc/02-client/types/tendermint/header.go create mode 100644 x/ibc/02-client/types/tendermint/misbehaviour.go diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index ddcd2d73e30b..d65559d5ac9f 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -21,6 +21,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/version" ) +// TODO: use Queriers + func mapping(cdc *codec.Codec, storeKey string, v int64) state.Mapping { prefix := version.Prefix(v) return state.NewMapping(sdk.NewKVStoreKey(storeKey), cdc, prefix) diff --git a/x/ibc/02-client/client/cli/tx.go b/x/ibc/02-client/client/cli/tx.go index f02ac62c496b..56fa776098f6 100644 --- a/x/ibc/02-client/client/cli/tx.go +++ b/x/ibc/02-client/client/cli/tx.go @@ -52,7 +52,7 @@ func GetCmdCreateClient(cdc *codec.Codec) *cobra.Command { Short: "create new client with a consensus state", Long: strings.TrimSpace(`create new client with a specified identifier and consensus state: - $ tx ibc client create $CLIENTID ./state.json --from node0 --home ../node0/cli --chain-id $CID +$ tx ibc client create [client-id] [path/to/consensus_state.json] --from node0 --home ../node0/cli --chain-id $CID `), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { @@ -87,18 +87,22 @@ func GetCmdUpdateClient(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "update", Short: "update existing client with a header", - Args: cobra.ExactArgs(2), + Long: strings.TrimSpace(`update existing client with a header: + +$ tx ibc client create [client-id] [path/to/header.json] --from node0 --home ../node0/cli --chain-id $CID + `), + Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) cliCtx := context.NewCLIContext().WithCodec(cdc) - contents, err := ioutil.ReadFile(args[1]) + bz, err := ioutil.ReadFile(args[1]) if err != nil { return err } var header exported.Header - if err := cdc.UnmarshalJSON(contents, &header); err != nil { + if err := cdc.UnmarshalJSON(bz, &header); err != nil { return err } @@ -113,3 +117,41 @@ func GetCmdUpdateClient(cdc *codec.Codec) *cobra.Command { } return cmd } + +// GetCmdSubmitMisbehaviour defines the command to submit a misbehaviour to invalidate +// previous state roots and prevent future updates as defined in +// https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#misbehaviour +func GetCmdSubmitMisbehaviour(cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "misbehaviour", + Short: "submit a client misbehaviour", + Long: strings.TrimSpace(`submit a client misbehaviour to invalidate to invalidate previous state roots and prevent future updates: + +$ tx ibc client misbehaviour [client-id] [path/to/evidence.json] --from node0 --home ../node0/cli --chain-id $CID + `), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContext().WithCodec(cdc) + + bz, err := ioutil.ReadFile(args[1]) + if err != nil { + return err + } + + var evidence exported.Evidence + if err := cdc.UnmarshalJSON(bz, &evidence); err != nil { + return err + } + + msg := types.MsgSubmitMisbehaviour{ + ClientID: args[0], + Evidence: evidence, + Signer: cliCtx.GetFromAddress(), + } + + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + }, + } + return cmd +} diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index 576e06440386..636a99a94361 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -24,17 +24,19 @@ type ConsensusState interface { // CheckValidityAndUpdateState returns the updated consensus state // only if the header is a descendent of this consensus state. CheckValidityAndUpdateState(Header) (ConsensusState, error) +} - // CheckMisbehaviourAndUpdateState checks any misbehaviour evidence - // depending on the state type. - CheckMisbehaviourAndUpdateState(Misbehaviour) bool +// Evidence contains two disctict headers used to submit client equivocation +// TODO: use evidence module type +type Evidence interface { + H1() Header + H2() Header } -// Misbehaviour defines the evidence +// Misbehaviour defines a specific consensus kind and an evidence type Misbehaviour interface { Kind() Kind - // TODO: embed Evidence interface - // evidence.Evidence + Evidence() Evidence } // Header is the consensus state update information diff --git a/x/ibc/02-client/handler.go b/x/ibc/02-client/handler.go index a367ce36d6ca..56a664657c1e 100644 --- a/x/ibc/02-client/handler.go +++ b/x/ibc/02-client/handler.go @@ -4,21 +4,25 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/keeper" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" ) // NewHandler creates a new Handler instance for IBC client // transactions -func NewHandler(manager types.Manager) sdk.Handler { +func NewHandler(k keeper.Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { ctx = ctx.WithEventManager(sdk.NewEventManager()) switch msg := msg.(type) { case types.MsgCreateClient: - return handleMsgCreateClient(ctx, manager, msg) + return handleMsgCreateClient(ctx, k, msg) case types.MsgUpdateClient: - return handleMsgUpdateClient(ctx, manager, msg) + return handleMsgUpdateClient(ctx, k, msg) + + case types.MsgSubmitMisbehaviour: + return handleMsgSubmitMisbehaviour(ctx, k, msg) default: errMsg := fmt.Sprintf("unrecognized IBC Client message type: %T", msg) @@ -27,8 +31,8 @@ func NewHandler(manager types.Manager) sdk.Handler { } } -func handleMsgCreateClient(ctx sdk.Context, manager types.Manager, msg types.MsgCreateClient) sdk.Result { - _, err := manager.CreateClient(ctx, msg.ClientID, msg.ConsensusState) +func handleMsgCreateClient(ctx sdk.Context, k keeper.Keeper, msg types.MsgCreateClient) sdk.Result { + _, err := k.CreateClient(ctx, msg.ClientID, msg.ConsensusState) if err != nil { return sdk.NewError(sdk.CodespaceType("ibc"), sdk.CodeType(100), err.Error()).Result() } @@ -37,8 +41,8 @@ func handleMsgCreateClient(ctx sdk.Context, manager types.Manager, msg types.Msg return sdk.Result{} } -func handleMsgUpdateClient(ctx sdk.Context, manager types.Manager, msg types.MsgUpdateClient) sdk.Result { - state, err := manager.Query(ctx, msg.ClientID) +func handleMsgUpdateClient(ctx sdk.Context, k keeper.Keeper, msg types.MsgUpdateClient) sdk.Result { + state, err := k.Query(ctx, msg.ClientID) if err != nil { return sdk.NewError(sdk.CodespaceType("ibc"), sdk.CodeType(200), err.Error()).Result() } @@ -51,3 +55,18 @@ func handleMsgUpdateClient(ctx sdk.Context, manager types.Manager, msg types.Msg // TODO: events return sdk.Result{} } + +func handleMsgSubmitMisbehaviour(ctx sdk.Context, k keeper.Keeper, msg types.MsgSubmitMisbehaviour) sdk.Result { + state, err := k.Query(ctx, msg.ClientID) + if err != nil { + return sdk.NewError(sdk.CodespaceType("ibc"), sdk.CodeType(200), err.Error()).Result() + } + + err = k.CheckMisbehaviourAndUpdateState(ctx, state, msg.Evidence) + if err != nil { + return sdk.NewError(sdk.CodespaceType("ibc"), sdk.CodeType(200), err.Error()).Result() + } + + // TODO: events + return sdk.Result{} +} diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go new file mode 100644 index 000000000000..1c0f86f35b95 --- /dev/null +++ b/x/ibc/02-client/keeper/keeper.go @@ -0,0 +1,84 @@ +package keeper + +import ( + "errors" + "fmt" + + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/store/state" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" +) + +// Keeper represents a type that grants read and write permissions to any client +// state information +type Keeper struct { + mapping state.Mapping + codespace sdk.CodespaceType +} + +// NewKeeper creates a new NewKeeper instance +func NewKeeper(mapping state.Mapping) Keeper { + return Keeper{ + mapping: mapping.Prefix([]byte(types.SubModuleName + "/")), + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s/%s", ibctypes.ModuleName, types.SubModuleName)) +} + +// CreateClient creates a new client state and populates it with a given consensus state +func (k Keeper) CreateClient(ctx sdk.Context, id string, cs exported.ConsensusState) (types.State, error) { + state, err := k.Query(ctx, id) + if err == nil { + return types.State{}, errors.New("cannot create client on an existing id") + } + + // set the most recent state root and consensus state + state.Roots.Set(ctx, cs.GetHeight(), cs.GetRoot()) + state.ConsensusState.Set(ctx, cs) + return state, nil +} + +// State returnts a new client state with a given id +func (k Keeper) State(id string) types.State { + return types.NewState( + id, // client ID + k.mapping.Prefix([]byte(id+"/roots/")).Indexer(state.Dec), // commitment roots + k.mapping.Value([]byte(id)), // consensus state + k.mapping.Value([]byte(id+"/freeze")).Boolean(), // client frozen + ) +} + +// Query returns a client state that matches a given ID +func (k Keeper) Query(ctx sdk.Context, id string) (types.State, error) { + state := k.State(id) + if !state.Exists(ctx) { + return types.State{}, errors.New("client doesn't exist") + } + return state, nil +} + +// CheckMisbehaviourAndUpdateState checks for client misbehaviour and freezes the +// client if so. +func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, state types.State, evidence exported.Evidence) error { + var err error + switch evidence.H1().Kind() { + case exported.Tendermint: + err = tendermint.CheckMisbehaviour(evidence) + default: + panic("unregistered consensus type") + } + + if err != nil { + return err + } + + return state.Freeze(ctx) +} diff --git a/x/ibc/02-client/types/keys.go b/x/ibc/02-client/types/keys.go index c3d97c364a16..bef9f661d8ad 100644 --- a/x/ibc/02-client/types/keys.go +++ b/x/ibc/02-client/types/keys.go @@ -1,6 +1,6 @@ package types -// IDK what is this for... -func LocalRoot() []byte { - return []byte("client/") -} +const ( + // SubModuleName defines the IBC client name + SubModuleName string = "client" +) diff --git a/x/ibc/02-client/types/manager.go b/x/ibc/02-client/types/manager.go deleted file mode 100644 index 8af23d87c895..000000000000 --- a/x/ibc/02-client/types/manager.go +++ /dev/null @@ -1,54 +0,0 @@ -package types - -import ( - "errors" - - "github.com/cosmos/cosmos-sdk/store/state" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" -) - -// Manager represents a type that grants read and write permissions to any client -// state information -type Manager struct { - protocol state.Mapping -} - -// NewManager creates a new Manager instance -func NewManager(mapping state.Mapping) Manager { - return Manager{ - protocol: mapping.Prefix(LocalRoot()), - } -} - -// CreateClient creates a new client state and populates it with a given consensus state -func (m Manager) CreateClient(ctx sdk.Context, id string, cs exported.ConsensusState) (State, error) { - state := m.State(id) - if state.exists(ctx) { - return State{}, errors.New("cannot create client on an existing id") - } - - // set the most recent state root and consensus state - state.Roots.Set(ctx, cs.GetHeight(), cs.GetRoot()) - state.ConsensusState.Set(ctx, cs) - return state, nil -} - -// State returnts a new client state with a given id -func (m Manager) State(id string) State { - return State{ - id: id, - Roots: m.protocol.Prefix([]byte(id + "/roots/")).Indexer(state.Dec), - ConsensusState: m.protocol.Value([]byte(id)), - Frozen: m.protocol.Value([]byte(id + "/freeze")).Boolean(), - } -} - -// Query returns a client state that matches a given ID -func (m Manager) Query(ctx sdk.Context, id string) (State, error) { - res := m.State(id) - if !res.exists(ctx) { - return State{}, errors.New("client doesn't exist") - } - return res, nil -} diff --git a/x/ibc/02-client/types/msgs.go b/x/ibc/02-client/types/msgs.go index e1f983f0368b..5f9c7286b153 100644 --- a/x/ibc/02-client/types/msgs.go +++ b/x/ibc/02-client/types/msgs.go @@ -16,9 +16,9 @@ type MsgCreateClient struct { } // NewMsgCreateClient creates a new MsgCreateClient instance -func NewMsgCreateClient(ID string, consensusState exported.ConsensusState, signer sdk.AccAddress) MsgCreateClient { +func NewMsgCreateClient(id string, consensusState exported.ConsensusState, signer sdk.AccAddress) MsgCreateClient { return MsgCreateClient{ - ClientID: ID, + ClientID: id, ConsensusState: consensusState, Signer: signer, } @@ -61,6 +61,15 @@ type MsgUpdateClient struct { Signer sdk.AccAddress `json:"address" yaml:"address"` } +// NewMsgUpdateClient creates a new MsgUpdateClient instance +func NewMsgUpdateClient(id string, header exported.Header, signer sdk.AccAddress) MsgUpdateClient { + return MsgUpdateClient{ + ClientID: id, + Header: header, + Signer: signer, + } +} + // Route implements sdk.Msg func (msg MsgUpdateClient) Route() string { return ibctypes.RouterKey @@ -88,3 +97,47 @@ func (msg MsgUpdateClient) GetSignBytes() []byte { func (msg MsgUpdateClient) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Signer} } + +// MsgSubmitMisbehaviour defines a message to update an IBC client +type MsgSubmitMisbehaviour struct { + ClientID string `json:"id" yaml:"id"` + Evidence exported.Evidence `json:"evidence" yaml:"evidence"` + Signer sdk.AccAddress `json:"address" yaml:"address"` +} + +// NewMsgSubmitMisbehaviour creates a new MsgSubmitMisbehaviour instance +func NewMsgSubmitMisbehaviour(id string, evidence exported.Evidence, signer sdk.AccAddress) MsgSubmitMisbehaviour { + return MsgSubmitMisbehaviour{ + ClientID: id, + Evidence: evidence, + Signer: signer, + } +} + +// Route implements sdk.Msg +func (msg MsgSubmitMisbehaviour) Route() string { + return ibctypes.RouterKey +} + +// Type implements sdk.Msg +func (msg MsgSubmitMisbehaviour) Type() string { + return "submit_misbehaviour" +} + +// ValidateBasic implements sdk.Msg +func (msg MsgSubmitMisbehaviour) ValidateBasic() sdk.Error { + if msg.Signer.Empty() { + return sdk.ErrInvalidAddress("empty address") + } + return nil +} + +// GetSignBytes implements sdk.Msg +func (msg MsgSubmitMisbehaviour) GetSignBytes() []byte { + return sdk.MustSortJSON(SubModuleCdc.MustMarshalJSON(msg)) +} + +// GetSigners implements sdk.Msg +func (msg MsgSubmitMisbehaviour) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Signer} +} diff --git a/x/ibc/02-client/types/state.go b/x/ibc/02-client/types/state.go index 5e968b969583..85c0d934de80 100644 --- a/x/ibc/02-client/types/state.go +++ b/x/ibc/02-client/types/state.go @@ -27,6 +27,16 @@ type State struct { Frozen state.Boolean `json:"frozen" yaml:"frozen"` } +// NewState creates a new State instance +func NewState(id string, roots state.Indexer, consensusState state.Value, frozen state.Boolean) State { + return State{ + id: id, + Roots: roots, + ConsensusState: consensusState, + Frozen: frozen, + } +} + // ID returns the client identifier func (state State) ID() string { return state.id @@ -44,9 +54,14 @@ func (state State) GetRoot(ctx sdk.Context, height uint64) (root ics23.Root, err return } +// Exists verifies if the client exists or not +func (state State) Exists(ctx sdk.Context) bool { + return state.ConsensusState.Exists(ctx) +} + // Update updates the consensus state and the state root from a provided header func (state State) Update(ctx sdk.Context, header exported.Header) error { - if !state.exists(ctx) { + if !state.Exists(ctx) { panic("should not update nonexisting client") } @@ -68,7 +83,7 @@ func (state State) Update(ctx sdk.Context, header exported.Header) error { // Freeze updates the state of the client in the event of a misbehaviour func (state State) Freeze(ctx sdk.Context) error { - if !state.exists(ctx) { + if !state.Exists(ctx) { panic("should not freeze nonexisting client") } @@ -99,11 +114,6 @@ func (state State) FrozenCLI(q state.ABCIQuerier) (res bool, proof merkle.Proof, return } -// exists verifies if the client exists or not -func (state State) exists(ctx sdk.Context) bool { - return state.ConsensusState.Exists(ctx) -} - func (state State) prefix() []byte { - return bytes.Split(state.ConsensusState.KeyBytes(), LocalRoot())[0] + return bytes.Split(state.ConsensusState.KeyBytes(), []byte(SubModuleName+"/"))[0] } diff --git a/x/ibc/02-client/types/tendermint/tendermint.go b/x/ibc/02-client/types/tendermint/consensus_state.go similarity index 54% rename from x/ibc/02-client/types/tendermint/tendermint.go rename to x/ibc/02-client/types/tendermint/consensus_state.go index 5ae497307d73..65a51f9b4e4f 100644 --- a/x/ibc/02-client/types/tendermint/tendermint.go +++ b/x/ibc/02-client/types/tendermint/consensus_state.go @@ -37,41 +37,38 @@ func (cs ConsensusState) GetRoot() ics23.Root { return cs.Root } -// CheckValidityAndUpdateState +// CheckValidityAndUpdateState checks if the provided header is valid and updates +// the consensus state if appropiate func (cs ConsensusState) CheckValidityAndUpdateState(header exported.Header) (exported.ConsensusState, error) { tmHeader, ok := header.(Header) if !ok { return nil, errors.New("header is not from a tendermint consensus") // TODO: create concrete error } - if cs.Height == uint64(tmHeader.Height-1) { - nexthash := cs.NextValidatorSet.Hash() - if !bytes.Equal(tmHeader.ValidatorsHash, nexthash) { - return nil, lerr.ErrUnexpectedValidators(tmHeader.ValidatorsHash, nexthash) - } + if err := cs.checkValidity(tmHeader); err != nil { + return nil, err } - if !bytes.Equal(tmHeader.NextValidatorsHash, tmHeader.NextValidatorSet.Hash()) { - return nil, lerr.ErrUnexpectedValidators(tmHeader.NextValidatorsHash, tmHeader.NextValidatorSet.Hash()) - } + return cs.update(tmHeader), nil +} - err := tmHeader.ValidateBasic(cs.ChainID) - if err != nil { - return nil, err +// checkValidity checks if the Tendermint header is valid +func (cs ConsensusState) checkValidity(header Header) error { + nextHash := cs.NextValidatorSet.Hash() + if cs.Height == uint64(header.Height-1) && + !bytes.Equal(header.ValidatorsHash, nextHash) { + return lerr.ErrUnexpectedValidators(header.ValidatorsHash, nextHash) } - err = cs.NextValidatorSet.VerifyFutureCommit(tmHeader.ValidatorSet, cs.ChainID, tmHeader.Commit.BlockID, tmHeader.Height, tmHeader.Commit) - if err != nil { - return nil, err + if !bytes.Equal(header.NextValidatorsHash, nextHash) { + return lerr.ErrUnexpectedValidators(header.NextValidatorsHash, nextHash) } - return cs.update(tmHeader), nil -} + if err := header.ValidateBasic(cs.ChainID); err != nil { + return err + } -// CheckMisbehaviourAndUpdateState - not implemented -func (cs ConsensusState) CheckMisbehaviourAndUpdateState(mb exported.Misbehaviour) bool { - // TODO: implement - return false + return cs.NextValidatorSet.VerifyFutureCommit(header.ValidatorSet, cs.ChainID, header.Commit.BlockID, header.Height, header.Commit) } // update updates the consensus state from a new header @@ -83,23 +80,3 @@ func (cs ConsensusState) update(header Header) ConsensusState { NextValidatorSet: header.NextValidatorSet, } } - -var _ exported.Header = Header{} - -// Header defines the Tendermint consensus Header -type Header struct { - // TODO: define Tendermint header type manually, don't use tmtypes - tmtypes.SignedHeader - ValidatorSet *tmtypes.ValidatorSet `json:"validator_set" yaml:"validator_set"` - NextValidatorSet *tmtypes.ValidatorSet `json:"next_validator_set" yaml:"next_validator_set"` -} - -// Kind defines that the Header is a Tendermint consensus algorithm -func (header Header) Kind() exported.Kind { - return exported.Tendermint -} - -// GetHeight returns the current height -func (header Header) GetHeight() uint64 { - return uint64(header.Height) -} diff --git a/x/ibc/02-client/types/tendermint/doc.go b/x/ibc/02-client/types/tendermint/doc.go new file mode 100644 index 000000000000..f0e27c7696a7 --- /dev/null +++ b/x/ibc/02-client/types/tendermint/doc.go @@ -0,0 +1,5 @@ +/* +Package tendermint implements a concrete `ConsensusState`, `Header` and `Equivocation` +for the Tendermint consensus algorithm. +*/ +package tendermint diff --git a/x/ibc/02-client/types/tendermint/header.go b/x/ibc/02-client/types/tendermint/header.go new file mode 100644 index 000000000000..fe790ac5da73 --- /dev/null +++ b/x/ibc/02-client/types/tendermint/header.go @@ -0,0 +1,46 @@ +package tendermint + +import ( + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" +) + +var _ exported.Header = Header{} + +// Header defines the Tendermint consensus Header +type Header struct { + // TODO: define Tendermint header type manually, don't use tmtypes + tmtypes.SignedHeader + ValidatorSet *tmtypes.ValidatorSet `json:"validator_set" yaml:"validator_set"` + NextValidatorSet *tmtypes.ValidatorSet `json:"next_validator_set" yaml:"next_validator_set"` +} + +// Kind defines that the Header is a Tendermint consensus algorithm +func (header Header) Kind() exported.Kind { + return exported.Tendermint +} + +// GetHeight returns the current height +func (header Header) GetHeight() uint64 { + return uint64(header.Height) +} + +var _ exported.Evidence = Evidence{} + +// Evidence defines two disctinct Tendermint headers used to submit a client misbehaviour +// TODO: use evidence module's types +type Evidence struct { + Header1 Header `json:"header_one" yaml:"header_one"` + Header2 Header `json:"header_two" yaml:"header_two"` +} + +// H1 returns the first header +func (e Evidence) H1() exported.Header { + return e.Header1 +} + +// H2 returns the second header +func (e Evidence) H2() exported.Header { + return e.Header2 +} diff --git a/x/ibc/02-client/types/tendermint/misbehaviour.go b/x/ibc/02-client/types/tendermint/misbehaviour.go new file mode 100644 index 000000000000..86bc198dd9c3 --- /dev/null +++ b/x/ibc/02-client/types/tendermint/misbehaviour.go @@ -0,0 +1,18 @@ +package tendermint + +import ( + "errors" + + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" +) + +// CheckMisbehaviour checks if the evidence provided is a misbehaviour +func CheckMisbehaviour(evidence exported.Evidence) error { + _, ok := evidence.(Evidence) + if !ok { + return errors.New("header is not from a tendermint consensus") // TODO: create concrete error + } + + // TODO: check evidence + return nil +} From 4483182b33c4deccac5f8691dfb1df8348675eeb Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Fri, 4 Oct 2019 13:50:09 +0200 Subject: [PATCH 110/166] errors --- x/ibc/02-client/handler.go | 12 ++-- x/ibc/02-client/keeper/keeper.go | 57 ++++++++++++++++--- x/ibc/02-client/types/errors.go | 35 +++++++++++- x/ibc/02-client/types/keys.go | 2 +- x/ibc/02-client/types/state.go | 37 ------------ .../types/tendermint/consensus_state.go | 13 +++-- .../types/tendermint/misbehaviour.go | 9 +-- 7 files changed, 100 insertions(+), 65 deletions(-) diff --git a/x/ibc/02-client/handler.go b/x/ibc/02-client/handler.go index 56a664657c1e..e25aca6f1ca2 100644 --- a/x/ibc/02-client/handler.go +++ b/x/ibc/02-client/handler.go @@ -34,7 +34,7 @@ func NewHandler(k keeper.Keeper) sdk.Handler { func handleMsgCreateClient(ctx sdk.Context, k keeper.Keeper, msg types.MsgCreateClient) sdk.Result { _, err := k.CreateClient(ctx, msg.ClientID, msg.ConsensusState) if err != nil { - return sdk.NewError(sdk.CodespaceType("ibc"), sdk.CodeType(100), err.Error()).Result() + return sdk.ResultFromError(err) } // TODO: events @@ -44,12 +44,12 @@ func handleMsgCreateClient(ctx sdk.Context, k keeper.Keeper, msg types.MsgCreate func handleMsgUpdateClient(ctx sdk.Context, k keeper.Keeper, msg types.MsgUpdateClient) sdk.Result { state, err := k.Query(ctx, msg.ClientID) if err != nil { - return sdk.NewError(sdk.CodespaceType("ibc"), sdk.CodeType(200), err.Error()).Result() + return sdk.ResultFromError(err) } - err = state.Update(ctx, msg.Header) + err = k.Update(ctx, state, msg.Header) if err != nil { - return sdk.NewError(sdk.CodespaceType("ibc"), sdk.CodeType(300), err.Error()).Result() + return sdk.ResultFromError(err) } // TODO: events @@ -59,12 +59,12 @@ func handleMsgUpdateClient(ctx sdk.Context, k keeper.Keeper, msg types.MsgUpdate func handleMsgSubmitMisbehaviour(ctx sdk.Context, k keeper.Keeper, msg types.MsgSubmitMisbehaviour) sdk.Result { state, err := k.Query(ctx, msg.ClientID) if err != nil { - return sdk.NewError(sdk.CodespaceType("ibc"), sdk.CodeType(200), err.Error()).Result() + return sdk.ResultFromError(err) } err = k.CheckMisbehaviourAndUpdateState(ctx, state, msg.Evidence) if err != nil { - return sdk.NewError(sdk.CodespaceType("ibc"), sdk.CodeType(200), err.Error()).Result() + return sdk.ResultFromError(err) } // TODO: events diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index 1c0f86f35b95..7257427eeb4c 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -1,13 +1,13 @@ package keeper import ( - "errors" "fmt" "github.com/tendermint/tendermint/libs/log" "github.com/cosmos/cosmos-sdk/store/state" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" @@ -22,9 +22,10 @@ type Keeper struct { } // NewKeeper creates a new NewKeeper instance -func NewKeeper(mapping state.Mapping) Keeper { +func NewKeeper(mapping state.Mapping, codespace sdk.CodespaceType) Keeper { return Keeper{ - mapping: mapping.Prefix([]byte(types.SubModuleName + "/")), + mapping: mapping.Prefix([]byte(types.SubModuleName + "/")), // "client/" + codespace: sdk.CodespaceType(fmt.Sprintf("%s/%s", codespace, types.DefaultCodespace)), // "ibc/client" } } @@ -37,7 +38,7 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { func (k Keeper) CreateClient(ctx sdk.Context, id string, cs exported.ConsensusState) (types.State, error) { state, err := k.Query(ctx, id) if err == nil { - return types.State{}, errors.New("cannot create client on an existing id") + return types.State{}, sdkerrors.Wrap(err, "cannot create client") } // set the most recent state root and consensus state @@ -46,7 +47,7 @@ func (k Keeper) CreateClient(ctx sdk.Context, id string, cs exported.ConsensusSt return state, nil } -// State returnts a new client state with a given id +// State returns a new client state with a given id func (k Keeper) State(id string) types.State { return types.NewState( id, // client ID @@ -60,7 +61,7 @@ func (k Keeper) State(id string) types.State { func (k Keeper) Query(ctx sdk.Context, id string) (types.State, error) { state := k.State(id) if !state.Exists(ctx) { - return types.State{}, errors.New("client doesn't exist") + return types.State{}, types.ErrClientExists(k.codespace) } return state, nil } @@ -71,7 +72,12 @@ func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, state types.Sta var err error switch evidence.H1().Kind() { case exported.Tendermint: - err = tendermint.CheckMisbehaviour(evidence) + var tmEvidence tendermint.Evidence + _, ok := evidence.(tendermint.Evidence) + if !ok { + return sdkerrors.Wrap(types.ErrInvalidConsensus(k.codespace), "consensus is not Tendermint") + } + err = tendermint.CheckMisbehaviour(tmEvidence) default: panic("unregistered consensus type") } @@ -80,5 +86,40 @@ func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, state types.Sta return err } - return state.Freeze(ctx) + return k.Freeze(ctx, state) +} + +// Update updates the consensus state and the state root from a provided header +func (k Keeper) Update(ctx sdk.Context, state types.State, header exported.Header) error { + if !state.Exists(ctx) { + panic("should not update nonexisting client") + } + + if state.Frozen.Get(ctx) { + return sdkerrors.Wrap(types.ErrClientFrozen(k.codespace), "cannot update client") + } + + consensusState := state.GetConsensusState(ctx) + consensusState, err := consensusState.CheckValidityAndUpdateState(header) + if err != nil { + return sdkerrors.Wrap(err, "cannot update client") + } + + state.ConsensusState.Set(ctx, consensusState) + state.Roots.Set(ctx, consensusState.GetHeight(), consensusState.GetRoot()) + return nil +} + +// Freeze updates the state of the client in the event of a misbehaviour +func (k Keeper) Freeze(ctx sdk.Context, state types.State) error { + if !state.Exists(ctx) { + panic("should not freeze nonexisting client") + } + + if state.Frozen.Get(ctx) { + return sdkerrors.Wrap(types.ErrClientFrozen(k.codespace), "already frozen") + } + + state.Frozen.Set(ctx, true) + return nil } diff --git a/x/ibc/02-client/types/errors.go b/x/ibc/02-client/types/errors.go index d8fa0171da5d..4c77d2dffe0a 100644 --- a/x/ibc/02-client/types/errors.go +++ b/x/ibc/02-client/types/errors.go @@ -1,3 +1,36 @@ package types -// TODO: +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// client error codes +const ( + DefaultCodespace sdk.CodespaceType = SubModuleName + + CodeClientExists sdk.CodeType = 101 + CodeClientNotFound sdk.CodeType = 102 + CodeClientFrozen sdk.CodeType = 103 + CodeInvalidConsensus sdk.CodeType = 104 + CodeValidatorJailed sdk.CodeType = 104 +) + +// ErrClientExists implements sdk.Error +func ErrClientExists(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeClientExists, "client already exists") +} + +// ErrClientNotFound implements sdk.Error +func ErrClientNotFound(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeClientNotFound, "client not found") +} + +// ErrClientFrozen implements sdk.Error +func ErrClientFrozen(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeClientFrozen, "client is frozen due to misbehaviour") +} + +// ErrInvalidConsensus implements sdk.Error +func ErrInvalidConsensus(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidConsensus, "invalid consensus algorithm type") +} diff --git a/x/ibc/02-client/types/keys.go b/x/ibc/02-client/types/keys.go index bef9f661d8ad..2e1f26ae3a26 100644 --- a/x/ibc/02-client/types/keys.go +++ b/x/ibc/02-client/types/keys.go @@ -2,5 +2,5 @@ package types const ( // SubModuleName defines the IBC client name - SubModuleName string = "client" + SubModuleName = "client" ) diff --git a/x/ibc/02-client/types/state.go b/x/ibc/02-client/types/state.go index 85c0d934de80..1141c3f281b0 100644 --- a/x/ibc/02-client/types/state.go +++ b/x/ibc/02-client/types/state.go @@ -2,7 +2,6 @@ package types import ( "bytes" - "errors" "github.com/cosmos/cosmos-sdk/store/state" sdk "github.com/cosmos/cosmos-sdk/types" @@ -59,42 +58,6 @@ func (state State) Exists(ctx sdk.Context) bool { return state.ConsensusState.Exists(ctx) } -// Update updates the consensus state and the state root from a provided header -func (state State) Update(ctx sdk.Context, header exported.Header) error { - if !state.Exists(ctx) { - panic("should not update nonexisting client") - } - - if state.Frozen.Get(ctx) { - return errors.New("client is frozen due to misbehaviour") - } - - stored := state.GetConsensusState(ctx) - updated, err := stored.CheckValidityAndUpdateState(header) - if err != nil { - return err - } - - state.ConsensusState.Set(ctx, updated) - state.Roots.Set(ctx, updated.GetHeight(), updated.GetRoot()) - - return nil -} - -// Freeze updates the state of the client in the event of a misbehaviour -func (state State) Freeze(ctx sdk.Context) error { - if !state.Exists(ctx) { - panic("should not freeze nonexisting client") - } - - if state.Frozen.Get(ctx) { - return errors.New("cannot freeze an already frozen client") - } - - state.Frozen.Set(ctx, true) - return nil -} - func (state State) RootCLI(q state.ABCIQuerier, height uint64) (res ics23.Root, proof merkle.Proof, err error) { root := state.Roots.Value(height) tmProof, err := root.Query(q, &res) diff --git a/x/ibc/02-client/types/tendermint/consensus_state.go b/x/ibc/02-client/types/tendermint/consensus_state.go index 65a51f9b4e4f..ed166d581142 100644 --- a/x/ibc/02-client/types/tendermint/consensus_state.go +++ b/x/ibc/02-client/types/tendermint/consensus_state.go @@ -17,9 +17,9 @@ var _ exported.ConsensusState = ConsensusState{} // ConsensusState defines a Tendermint consensus state type ConsensusState struct { ChainID string `json:"chain_id" yaml:"chain_id"` - Height uint64 `json:"height" yaml:"height"` + Height uint64 `json:"height" yaml:"height"` // NOTE: defined as 'sequence' in the spec Root ics23.Root `json:"root" yaml:"root"` - NextValidatorSet *tmtypes.ValidatorSet `json:"next_validator_set" yaml:"next_validator_set"` + NextValidatorSet *tmtypes.ValidatorSet `json:"next_validator_set" yaml:"next_validator_set"` // contains the PublicKey } // Kind returns Tendermint @@ -42,7 +42,7 @@ func (cs ConsensusState) GetRoot() ics23.Root { func (cs ConsensusState) CheckValidityAndUpdateState(header exported.Header) (exported.ConsensusState, error) { tmHeader, ok := header.(Header) if !ok { - return nil, errors.New("header is not from a tendermint consensus") // TODO: create concrete error + return nil, errors.New("header is not from a tendermint consensus") } if err := cs.checkValidity(tmHeader); err != nil { @@ -54,6 +54,7 @@ func (cs ConsensusState) CheckValidityAndUpdateState(header exported.Header) (ex // checkValidity checks if the Tendermint header is valid func (cs ConsensusState) checkValidity(header Header) error { + // TODO: shouldn't we check that header.Height > cs.Height? nextHash := cs.NextValidatorSet.Hash() if cs.Height == uint64(header.Height-1) && !bytes.Equal(header.ValidatorsHash, nextHash) { @@ -68,10 +69,12 @@ func (cs ConsensusState) checkValidity(header Header) error { return err } - return cs.NextValidatorSet.VerifyFutureCommit(header.ValidatorSet, cs.ChainID, header.Commit.BlockID, header.Height, header.Commit) + return cs.NextValidatorSet.VerifyFutureCommit( + header.ValidatorSet, cs.ChainID, header.Commit.BlockID, header.Height, header.Commit, + ) } -// update updates the consensus state from a new header +// update the consensus state from a new header func (cs ConsensusState) update(header Header) ConsensusState { return ConsensusState{ ChainID: cs.ChainID, diff --git a/x/ibc/02-client/types/tendermint/misbehaviour.go b/x/ibc/02-client/types/tendermint/misbehaviour.go index 86bc198dd9c3..53fcbcca6bd0 100644 --- a/x/ibc/02-client/types/tendermint/misbehaviour.go +++ b/x/ibc/02-client/types/tendermint/misbehaviour.go @@ -1,17 +1,12 @@ package tendermint import ( - "errors" - + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" ) // CheckMisbehaviour checks if the evidence provided is a misbehaviour -func CheckMisbehaviour(evidence exported.Evidence) error { - _, ok := evidence.(Evidence) - if !ok { - return errors.New("header is not from a tendermint consensus") // TODO: create concrete error - } +func CheckMisbehaviour(evidence exported.Evidence) sdk.Error { // TODO: check evidence return nil From 23fbb5d56a0ffc31499e39739718600cdd84e9f6 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Fri, 4 Oct 2019 14:08:40 +0200 Subject: [PATCH 111/166] events --- x/ibc/02-client/client/cli/query.go | 202 ++++++++++++++-------------- x/ibc/02-client/handler.go | 44 +++++- x/ibc/02-client/types/events.go | 21 +++ 3 files changed, 158 insertions(+), 109 deletions(-) create mode 100644 x/ibc/02-client/types/events.go diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index d65559d5ac9f..b5a4d6ffbe73 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -2,21 +2,15 @@ package cli import ( "fmt" - "strconv" "strings" "github.com/spf13/cobra" - tmtypes "github.com/tendermint/tendermint/types" - cli "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/state" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" "github.com/cosmos/cosmos-sdk/x/ibc/version" ) @@ -59,18 +53,18 @@ $ cli query ibc client state [id] `), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.NewCLIContext().WithCodec(cdc) - q := state.NewCLIQuerier(ctx) - mapp := mapping(cdc, storeKey, version.Version) - manager := types.NewManager(mapp) - id := args[0] - - state, _, err := manager.State(id).ConsensusStateCLI(q) - if err != nil { - return err - } - - fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, state)) + // ctx := context.NewCLIContext().WithCodec(cdc) + // q := state.NewCLIQuerier(ctx) + // mapp := mapping(cdc, storeKey, version.Version) + // manager := types.NewManager(mapp) + // id := args[0] + + // state, _, err := manager.State(id).ConsensusStateCLI(q) + // if err != nil { + // return err + // } + + // fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, state)) return nil }, } @@ -87,22 +81,22 @@ $ cli query ibc client root [id] [height] `), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.NewCLIContext().WithCodec(cdc) - q := state.NewCLIQuerier(ctx) - mapp := mapping(cdc, storeKey, version.Version) - manager := types.NewManager(mapp) - id := args[0] - height, err := strconv.ParseUint(args[1], 10, 64) - if err != nil { - return err - } - - root, _, err := manager.State(id).RootCLI(q, height) - if err != nil { - return err - } - - fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, root)) + // ctx := context.NewCLIContext().WithCodec(cdc) + // q := state.NewCLIQuerier(ctx) + // mapp := mapping(cdc, storeKey, version.Version) + // manager := types.NewManager(mapp) + // id := args[0] + // height, err := strconv.ParseUint(args[1], 10, 64) + // if err != nil { + // return err + // } + + // root, _, err := manager.State(id).RootCLI(q, height) + // if err != nil { + // return err + // } + + // fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, root)) return nil }, } @@ -119,39 +113,39 @@ func GetCmdQueryConsensusState(storeKey string, cdc *codec.Codec) *cobra.Command $ cli query ibc client consensus-state `), RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.NewCLIContext().WithCodec(cdc) - - node, err := ctx.GetNode() - if err != nil { - return err - } - - info, err := node.ABCIInfo() - if err != nil { - return err - } - - height := info.Response.LastBlockHeight - prevheight := height - 1 - - commit, err := node.Commit(&height) - if err != nil { - return err - } - - validators, err := node.Validators(&prevheight) - if err != nil { - return err - } - - state := tendermint.ConsensusState{ - ChainID: commit.ChainID, - Height: uint64(commit.Height), - Root: merkle.NewRoot(commit.AppHash), - NextValidatorSet: tmtypes.NewValidatorSet(validators.Validators), - } - - fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, state)) + // ctx := context.NewCLIContext().WithCodec(cdc) + + // node, err := ctx.GetNode() + // if err != nil { + // return err + // } + + // info, err := node.ABCIInfo() + // if err != nil { + // return err + // } + + // height := info.Response.LastBlockHeight + // prevheight := height - 1 + + // commit, err := node.Commit(&height) + // if err != nil { + // return err + // } + + // validators, err := node.Validators(&prevheight) + // if err != nil { + // return err + // } + + // state := tendermint.ConsensusState{ + // ChainID: commit.ChainID, + // Height: uint64(commit.Height), + // Root: merkle.NewRoot(commit.AppHash), + // NextValidatorSet: tmtypes.NewValidatorSet(validators.Validators), + // } + + // fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, state)) return nil }, } @@ -185,43 +179,43 @@ func GetCmdQueryHeader(cdc *codec.Codec) *cobra.Command { $ cli query ibc client header `), RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.NewCLIContext().WithCodec(cdc) - - node, err := ctx.GetNode() - if err != nil { - return err - } - - info, err := node.ABCIInfo() - if err != nil { - return err - } - - height := info.Response.LastBlockHeight - prevheight := height - 1 - - commit, err := node.Commit(&height) - if err != nil { - return err - } - - validators, err := node.Validators(&prevheight) - if err != nil { - return err - } - - nextValidators, err := node.Validators(&height) - if err != nil { - return err - } - - header := tendermint.Header{ - SignedHeader: commit.SignedHeader, - ValidatorSet: tmtypes.NewValidatorSet(validators.Validators), - NextValidatorSet: tmtypes.NewValidatorSet(nextValidators.Validators), - } - - fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, header)) + // ctx := context.NewCLIContext().WithCodec(cdc) + + // node, err := ctx.GetNode() + // if err != nil { + // return err + // } + + // info, err := node.ABCIInfo() + // if err != nil { + // return err + // } + + // height := info.Response.LastBlockHeight + // prevheight := height - 1 + + // commit, err := node.Commit(&height) + // if err != nil { + // return err + // } + + // validators, err := node.Validators(&prevheight) + // if err != nil { + // return err + // } + + // nextValidators, err := node.Validators(&height) + // if err != nil { + // return err + // } + + // header := tendermint.Header{ + // SignedHeader: commit.SignedHeader, + // ValidatorSet: tmtypes.NewValidatorSet(validators.Validators), + // NextValidatorSet: tmtypes.NewValidatorSet(nextValidators.Validators), + // } + + // fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, header)) return nil }, } diff --git a/x/ibc/02-client/handler.go b/x/ibc/02-client/handler.go index e25aca6f1ca2..dfc2f0eabb67 100644 --- a/x/ibc/02-client/handler.go +++ b/x/ibc/02-client/handler.go @@ -37,8 +37,19 @@ func handleMsgCreateClient(ctx sdk.Context, k keeper.Keeper, msg types.MsgCreate return sdk.ResultFromError(err) } - // TODO: events - return sdk.Result{} + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeCreateClient, + sdk.NewAttribute(types.AttributeKeyClientID, msg.ClientID), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Signer.String()), + ), + }) + + return sdk.Result{Events: ctx.EventManager().Events()} } func handleMsgUpdateClient(ctx sdk.Context, k keeper.Keeper, msg types.MsgUpdateClient) sdk.Result { @@ -52,8 +63,20 @@ func handleMsgUpdateClient(ctx sdk.Context, k keeper.Keeper, msg types.MsgUpdate return sdk.ResultFromError(err) } + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeUpdateClient, + sdk.NewAttribute(types.AttributeKeyClientID, msg.ClientID), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Signer.String()), + ), + }) + // TODO: events - return sdk.Result{} + return sdk.Result{Events: ctx.EventManager().Events()} } func handleMsgSubmitMisbehaviour(ctx sdk.Context, k keeper.Keeper, msg types.MsgSubmitMisbehaviour) sdk.Result { @@ -67,6 +90,17 @@ func handleMsgSubmitMisbehaviour(ctx sdk.Context, k keeper.Keeper, msg types.Msg return sdk.ResultFromError(err) } - // TODO: events - return sdk.Result{} + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeSubmitMisbehaviour, + sdk.NewAttribute(types.AttributeKeyClientID, msg.ClientID), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Signer.String()), + ), + }) + + return sdk.Result{Events: ctx.EventManager().Events()} } diff --git a/x/ibc/02-client/types/events.go b/x/ibc/02-client/types/events.go new file mode 100644 index 000000000000..6258a7e328d9 --- /dev/null +++ b/x/ibc/02-client/types/events.go @@ -0,0 +1,21 @@ +package types + +import ( + "fmt" + + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" +) + +// IBC client events +const ( + EventTypeCreateClient = "create_client" + EventTypeUpdateClient = "update_client" + EventTypeSubmitMisbehaviour = "submit_misbehaviour" + + AttributeKeyClientID = "client_id" +) + +// IBC client events vars +var ( + AttributeValueCategory = fmt.Sprintf("%s_%s", ibctypes.ModuleName, SubModuleName) +) From ebecbce1ee843e07ba86cd04ca6436d7502e1535 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Fri, 4 Oct 2019 14:13:48 +0200 Subject: [PATCH 112/166] fix test --- x/ibc/02-client/types/tendermint/consensus_state.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x/ibc/02-client/types/tendermint/consensus_state.go b/x/ibc/02-client/types/tendermint/consensus_state.go index ed166d581142..0fd3966fa65d 100644 --- a/x/ibc/02-client/types/tendermint/consensus_state.go +++ b/x/ibc/02-client/types/tendermint/consensus_state.go @@ -32,7 +32,7 @@ func (cs ConsensusState) GetHeight() uint64 { return cs.Height } -// GetRoot returns the commitment Root +// GetRoot returns the commitment Rootgit func (cs ConsensusState) GetRoot() ics23.Root { return cs.Root } @@ -61,6 +61,7 @@ func (cs ConsensusState) checkValidity(header Header) error { return lerr.ErrUnexpectedValidators(header.ValidatorsHash, nextHash) } + nextHash = header.NextValidatorSet.Hash() if !bytes.Equal(header.NextValidatorsHash, nextHash) { return lerr.ErrUnexpectedValidators(header.NextValidatorsHash, nextHash) } From 0e85ed041893aa201dfaadc3c68c88430c7a70ba Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Fri, 4 Oct 2019 16:28:06 +0200 Subject: [PATCH 113/166] refactors --- x/ibc/02-client/client/cli/query.go | 229 +++++++----------- x/ibc/02-client/exported/exported.go | 12 +- x/ibc/02-client/keeper/keeper.go | 4 +- x/ibc/02-client/keeper/querier.go | 134 ++++++++++ x/ibc/02-client/types/querier.go | 38 +++ .../types/tendermint/consensus_state.go | 4 +- x/ibc/02-client/types/tendermint/header.go | 4 +- 7 files changed, 277 insertions(+), 148 deletions(-) create mode 100644 x/ibc/02-client/keeper/querier.go create mode 100644 x/ibc/02-client/types/querier.go diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index b5a4d6ffbe73..b092f224f31a 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -2,26 +2,18 @@ package cli import ( "fmt" + "strconv" "strings" "github.com/spf13/cobra" cli "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/state" - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" - "github.com/cosmos/cosmos-sdk/x/ibc/version" + "github.com/cosmos/cosmos-sdk/ibc/02-client/types" + "github.com/cosmos/cosmos-sdk/version" ) -// TODO: use Queriers - -func mapping(cdc *codec.Codec, storeKey string, v int64) state.Mapping { - prefix := version.Prefix(v) - return state.NewMapping(sdk.NewKVStoreKey(storeKey), cdc, prefix) -} - // GetQueryCmd returns the query commands for IBC clients func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { ibcQueryCmd := &cobra.Command{ @@ -47,25 +39,30 @@ func GetCmdQueryClientState(storeKey string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "state", Short: "Query a client state", - Long: strings.TrimSpace(`Query stored client - -$ cli query ibc client state [id] - `), + Long: strings.TrimSpace( + fmt.Sprintf(`Query stored client + +Example: +$ %s query ibc client state [id] + `, version.ClientName), + ), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - // ctx := context.NewCLIContext().WithCodec(cdc) - // q := state.NewCLIQuerier(ctx) - // mapp := mapping(cdc, storeKey, version.Version) - // manager := types.NewManager(mapp) - // id := args[0] - - // state, _, err := manager.State(id).ConsensusStateCLI(q) - // if err != nil { - // return err - // } - - // fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, state)) - return nil + cliCtx := context.NewCLIContext().WithCodec(cdc) + id := args[0] + + bz, err := cdc.MarshalJSON(types.NewQueryClientStateParams(id)) + if err != nil { + return err + } + + req := abci.RequestQuery{ + Path: "/store/" + storeKey + "/key", + Data: bz, + Prove: true, + } + + return cliCtx.PrintOutput() }, } } @@ -75,29 +72,28 @@ func GetCmdQueryRoot(storeKey string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "root", Short: "Query stored root", - Long: strings.TrimSpace(`Query stored client - -$ cli query ibc client root [id] [height] - `), + Long: strings.TrimSpace( + fmt.Sprintf(`Query stored client + +Example: +$ %s query ibc client root [id] [height] +`, version.ClientName), + ), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - // ctx := context.NewCLIContext().WithCodec(cdc) - // q := state.NewCLIQuerier(ctx) - // mapp := mapping(cdc, storeKey, version.Version) - // manager := types.NewManager(mapp) - // id := args[0] - // height, err := strconv.ParseUint(args[1], 10, 64) - // if err != nil { - // return err - // } - - // root, _, err := manager.State(id).RootCLI(q, height) - // if err != nil { - // return err - // } - - // fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, root)) - return nil + cliCtx := context.NewCLIContext().WithCodec(cdc) + id := args[0] + height, err := strconv.ParseUint(args[1], 10, 64) + if err != nil { + return err + } + + bz, err := cdc.MarshalJSON(types.NewQueryCommitmentRootParams(id, height)) + if err != nil { + return err + } + + return cliCtx.PrintOutput() }, } } @@ -108,45 +104,38 @@ func GetCmdQueryConsensusState(storeKey string, cdc *codec.Codec) *cobra.Command return &cobra.Command{ Use: "consensus-state", Short: "Query the latest consensus state of the running chain", - Long: strings.TrimSpace(`Query consensus state - -$ cli query ibc client consensus-state - `), + Long: strings.TrimSpace( + fmt.Sprintf(`Query consensus state + +Example: +$ %s query ibc client consensus-state + `, version.ClientName), + ), RunE: func(cmd *cobra.Command, args []string) error { - // ctx := context.NewCLIContext().WithCodec(cdc) - - // node, err := ctx.GetNode() - // if err != nil { - // return err - // } - - // info, err := node.ABCIInfo() - // if err != nil { - // return err - // } - - // height := info.Response.LastBlockHeight - // prevheight := height - 1 - - // commit, err := node.Commit(&height) - // if err != nil { - // return err - // } - - // validators, err := node.Validators(&prevheight) - // if err != nil { - // return err - // } - - // state := tendermint.ConsensusState{ - // ChainID: commit.ChainID, - // Height: uint64(commit.Height), - // Root: merkle.NewRoot(commit.AppHash), - // NextValidatorSet: tmtypes.NewValidatorSet(validators.Validators), - // } - - // fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, state)) - return nil + cliCtx := context.NewCLIContext().WithCodec(cdc) + node, err := cliCtx.GetNode() + if err != nil { + return err + } + + info, err := node.ABCIInfo() + if err != nil { + return err + } + + height := info.Response.LastBlockHeight + prevheight := height - 1 + commit, err := node.Commit(&height) + if err != nil { + return err + } + + validators, err := node.Validators(&prevheight) + if err != nil { + return err + } + + return cliCtx.PrintOutput() }, } } @@ -156,15 +145,16 @@ func GetCmdQueryPath(storeName string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "path", Short: "Query the commitment path of the running chain", - Long: strings.TrimSpace(`Query the commitment path + Long: strings.TrimSpace(fmt.Sprintf(`Query the commitment path -$ cli query ibc client path - `), +Example: +$ %s query ibc client path + `, version.ClientName), + ), RunE: func(cmd *cobra.Command, args []string) error { - mapp := mapping(cdc, storeName, version.Version) - path := merkle.NewPrefix([][]byte{[]byte(storeName)}, mapp.PrefixBytes()) - fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, path)) - return nil + cliCtx := context.NewCLIContext().WithCodec(cdc) + + return cliCtx.PrintOutput() }, } } @@ -174,49 +164,16 @@ func GetCmdQueryHeader(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "header", Short: "Query the latest header of the running chain", - Long: strings.TrimSpace(`Query the latest header + Long: strings.TrimSpace(fmt.Sprintf(`Query the latest header -$ cli query ibc client header - `), +Example: +$ %s query ibc client header + `, version.ClientName), + ), RunE: func(cmd *cobra.Command, args []string) error { - // ctx := context.NewCLIContext().WithCodec(cdc) - - // node, err := ctx.GetNode() - // if err != nil { - // return err - // } - - // info, err := node.ABCIInfo() - // if err != nil { - // return err - // } - - // height := info.Response.LastBlockHeight - // prevheight := height - 1 - - // commit, err := node.Commit(&height) - // if err != nil { - // return err - // } - - // validators, err := node.Validators(&prevheight) - // if err != nil { - // return err - // } - - // nextValidators, err := node.Validators(&height) - // if err != nil { - // return err - // } - - // header := tendermint.Header{ - // SignedHeader: commit.SignedHeader, - // ValidatorSet: tmtypes.NewValidatorSet(validators.Validators), - // NextValidatorSet: tmtypes.NewValidatorSet(nextValidators.Validators), - // } - - // fmt.Printf("%s\n", codec.MustMarshalJSONIndent(cdc, header)) - return nil + cliCtx := context.NewCLIContext().WithCodec(cdc) + + return cliCtx.PrintOutput() }, } } diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index 636a99a94361..54d42f15bd9a 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -14,7 +14,7 @@ type Blockchain interface { // ConsensusState is the state of the consensus process type ConsensusState interface { - Kind() Kind // Consensus kind + ClientType() ClientType // Consensus kind GetHeight() uint64 // GetRoot returns the commitment root of the consensus state, @@ -35,20 +35,20 @@ type Evidence interface { // Misbehaviour defines a specific consensus kind and an evidence type Misbehaviour interface { - Kind() Kind + ClientType() ClientType Evidence() Evidence } // Header is the consensus state update information type Header interface { - Kind() Kind + ClientType() ClientType GetHeight() uint64 } -// Kind defines the type of the consensus algorithm -type Kind byte +// ClientType defines the type of the consensus algorithm +type ClientType byte // Registered consensus types const ( - Tendermint Kind = iota + Tendermint ClientType = iota ) diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index 7257427eeb4c..b1fb846eb672 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -48,7 +48,7 @@ func (k Keeper) CreateClient(ctx sdk.Context, id string, cs exported.ConsensusSt } // State returns a new client state with a given id -func (k Keeper) State(id string) types.State { +func (k Keeper) ClientState(id string) types.State { return types.NewState( id, // client ID k.mapping.Prefix([]byte(id+"/roots/")).Indexer(state.Dec), // commitment roots @@ -59,7 +59,7 @@ func (k Keeper) State(id string) types.State { // Query returns a client state that matches a given ID func (k Keeper) Query(ctx sdk.Context, id string) (types.State, error) { - state := k.State(id) + state := k.ClientState(id) if !state.Exists(ctx) { return types.State{}, types.ErrClientExists(k.codespace) } diff --git a/x/ibc/02-client/keeper/querier.go b/x/ibc/02-client/keeper/querier.go new file mode 100644 index 000000000000..260cfc63bc7e --- /dev/null +++ b/x/ibc/02-client/keeper/querier.go @@ -0,0 +1,134 @@ +package keeper + +import ( + "fmt" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" +) + +// NewQuerier creates a querier for the IBC client +func NewQuerier(k Keeper) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + switch path[0] { + case types.QueryClientState: + return queryClientState(ctx, req, k) + case types.QueryConsensusState: + return queryConsensusState(ctx) + case types.QueryCommitmentPath: + return queryCommitmentPath(ctx, req, k) + case types.QueryCommitmentRoot: + return queryCommitmentRoot(ctx, req, k) + case types.QueryHeader: + return queryHeader(ctx, req, k) + default: + return nil, sdk.ErrUnknownRequest("unknown IBC client query endpoint") + } + } +} + +func queryClientState(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params types.QueryClientStateParams + + err := types.SubModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) + } + + + // mapp := mapping(cdc, storeKey, version.Version) + // state, _, err := k.State(id).ConsensusStateCLI(q) + // if err != nil { + // return err + // } + + return res, nil +} + +func queryConsensusState(ctx sdk.Context) ([]byte, sdk.Error) { + + state := tendermint.ConsensusState{ + ChainID: commit.ChainID, + Height: uint64(commit.Height), + Root: merkle.NewRoot(commit.AppHash), + NextValidatorSet: tmtypes.NewValidatorSet(validators.Validators), + } + + return res, nil +} + +func queryCommitmentPath(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + + path := merkle.NewPrefix([][]byte{[]byte(k.mapping.storeName)}, k.mapping.PrefixBytes()) + + return res, nil +} + +func queryCommitmentRoot(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params types.QueryCommitmentRoot + + err := types.SubModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) + } + + root, _, err := k.ClientState(params.ID).RootCLI(q, params.Height) + if err != nil { + return nil, err + } + + res, err := codec.MarshalJSONIndent(types.SubModuleCdc, root) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + + return res, nil +} + +func queryHeader(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + // node, err := ctx.GetNode() + // if err != nil { + // return err + // } + + // info, err := node.ABCIInfo() + // if err != nil { + // return err + // } + + // height := info.Response.LastBlockHeight + // prevheight := height - 1 + + // commit, err := node.Commit(&height) + // if err != nil { + // return err + // } + + // validators, err := node.Validators(&prevheight) + // if err != nil { + // return err + // } + + // nextValidators, err := node.Validators(&height) + // if err != nil { + // return err + // } + + // header := tendermint.Header{ + // SignedHeader: commit.SignedHeader, + // ValidatorSet: tmtypes.NewValidatorSet(validators.Validators), + // NextValidatorSet: tmtypes.NewValidatorSet(nextValidators.Validators), + // } + + res, err := codec.MarshalJSONIndent(types.SubModuleCdc, header) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to marshal result to JSON", err.Error())) + } + + return res, nil +} diff --git a/x/ibc/02-client/types/querier.go b/x/ibc/02-client/types/querier.go new file mode 100644 index 000000000000..3e10883481c4 --- /dev/null +++ b/x/ibc/02-client/types/querier.go @@ -0,0 +1,38 @@ +package types + +// query routes supported by the IBC client Querier +const ( + QueryClientState = "clientState" + QueryConsensusState = "consensusState" + QueryCommitmentPath = "commitmentPath" + QueryCommitmentRoot = "commitmentRoot" + QueryHeader = "header" +) + +// QueryClientStateParams defines the params for the following queries: +// - 'custom/ibc/client/clientState' +type QueryClientStateParams struct { + ID string +} + +// NewQueryClientStateParams creates a new QueryClientStateParams instance +func NewQueryClientStateParams(id string) QueryClientStateParams { + return QueryClientStateParams{ + ID: id, + } +} + +// QueryCommitmentRootParams defines the params for the following queries: +// - 'custom/ibc/client/commitmentRoot' +type QueryCommitmentRootParams struct { + ID string + Height uint64 +} + +// NewQueryCommitmentRootParams creates a new QueryCommitmentRootParams instance +func NewQueryCommitmentRootParams(id string, height uint64) QueryCommitmentRootParams { + return QueryCommitmentRootParams{ + ID: id, + Height: height, + } +} diff --git a/x/ibc/02-client/types/tendermint/consensus_state.go b/x/ibc/02-client/types/tendermint/consensus_state.go index 0fd3966fa65d..5ebb973719bc 100644 --- a/x/ibc/02-client/types/tendermint/consensus_state.go +++ b/x/ibc/02-client/types/tendermint/consensus_state.go @@ -22,8 +22,8 @@ type ConsensusState struct { NextValidatorSet *tmtypes.ValidatorSet `json:"next_validator_set" yaml:"next_validator_set"` // contains the PublicKey } -// Kind returns Tendermint -func (ConsensusState) Kind() exported.Kind { +// ClientType returns Tendermint +func (ConsensusState) ClientType() exported.ClientType { return exported.Tendermint } diff --git a/x/ibc/02-client/types/tendermint/header.go b/x/ibc/02-client/types/tendermint/header.go index fe790ac5da73..408aaed40d28 100644 --- a/x/ibc/02-client/types/tendermint/header.go +++ b/x/ibc/02-client/types/tendermint/header.go @@ -16,8 +16,8 @@ type Header struct { NextValidatorSet *tmtypes.ValidatorSet `json:"next_validator_set" yaml:"next_validator_set"` } -// Kind defines that the Header is a Tendermint consensus algorithm -func (header Header) Kind() exported.Kind { +// ClientType defines that the Header is a Tendermint consensus algorithm +func (header Header) ClientType() exported.ClientType { return exported.Tendermint } From a7d2d987f74ab570ddf4ac7251db6cad84670ef9 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Mon, 7 Oct 2019 18:13:20 +0200 Subject: [PATCH 114/166] remove Mapping --- x/ibc/23-commitment/codec.go | 2 +- x/ibc/23-commitment/context.go | 2 +- x/ibc/23-commitment/merkle/merkle.go | 28 ++--- x/ibc/23-commitment/merkle/merkle_test.go | 12 +- x/ibc/23-commitment/merkle/utils.go | 2 +- x/ibc/23-commitment/store.go | 2 +- x/ibc/23-commitment/types.go | 2 +- x/ibc/23-commitment/utils.go | 8 -- x/ibc/23-commitment/value.go | 127 ---------------------- 9 files changed, 20 insertions(+), 165 deletions(-) delete mode 100644 x/ibc/23-commitment/utils.go delete mode 100644 x/ibc/23-commitment/value.go diff --git a/x/ibc/23-commitment/codec.go b/x/ibc/23-commitment/codec.go index 943f8ede8b7e..7312c5942d85 100644 --- a/x/ibc/23-commitment/codec.go +++ b/x/ibc/23-commitment/codec.go @@ -1,4 +1,4 @@ -package commitment +package ics23 import ( "github.com/cosmos/cosmos-sdk/codec" diff --git a/x/ibc/23-commitment/context.go b/x/ibc/23-commitment/context.go index a0404e2a8927..af3f300ef9f2 100644 --- a/x/ibc/23-commitment/context.go +++ b/x/ibc/23-commitment/context.go @@ -1,4 +1,4 @@ -package commitment +package ics23 import ( sdk "github.com/cosmos/cosmos-sdk/types" diff --git a/x/ibc/23-commitment/merkle/merkle.go b/x/ibc/23-commitment/merkle/merkle.go index f310ac948151..bef5e7c6951b 100644 --- a/x/ibc/23-commitment/merkle/merkle.go +++ b/x/ibc/23-commitment/merkle/merkle.go @@ -1,15 +1,13 @@ package merkle import ( - "bytes" "errors" "github.com/tendermint/tendermint/crypto/merkle" "github.com/cosmos/cosmos-sdk/store/rootmulti" - // "github.com/cosmos/cosmos-sdk/store/state" - commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) // ICS 023 Merkle Types Implementation @@ -20,7 +18,7 @@ const merkleKind = "merkle" // merkle.Proof implementation of Proof // Applied on SDK-based IBC implementation -var _ commitment.Root = Root{} +var _ ics23.Root = Root{} // Root is Merkle root hash // In Cosmos-SDK, the AppHash of the Header becomes Root. @@ -35,12 +33,12 @@ func NewRoot(hash []byte) Root { } } -// Implements commitment.Root +// Implements ics23.Root func (Root) CommitmentKind() string { return merkleKind } -var _ commitment.Prefix = Prefix{} +var _ ics23.Prefix = Prefix{} // Prefix is merkle path prefixed to the key. // The constructed key from the Path and the key will be append(Path.KeyPath, append(Path.KeyPrefix, key...)) @@ -59,7 +57,7 @@ func NewPrefix(keypath [][]byte, keyprefix []byte) Prefix { } } -// Implements commitment.Prefix +// Implements ics23.Prefix func (Prefix) CommitmentKind() string { return merkleKind } @@ -68,7 +66,7 @@ func (prefix Prefix) Key(key []byte) []byte { return join(prefix.KeyPrefix, key) } -var _ commitment.Proof = Proof{} +var _ ics23.Proof = Proof{} // Proof is Merkle proof with the key information. type Proof struct { @@ -76,7 +74,7 @@ type Proof struct { Key []byte `json:"key"` } -// Implements commitment.Proof +// Implements ics23.Proof func (Proof) CommitmentKind() string { return merkleKind } @@ -86,8 +84,8 @@ func (proof Proof) GetKey() []byte { return proof.Key } -// Verify() proves the proof against the given root, path, and value. -func (proof Proof) Verify(croot commitment.Root, cpath commitment.Prefix, value []byte) error { +// Verify proves the proof against the given root, path, and value. +func (proof Proof) Verify(croot ics23.Root, cpath ics23.Prefix, value []byte) error { root, ok := croot.(Root) if !ok { return errors.New("invalid commitment root type") @@ -112,11 +110,3 @@ func (proof Proof) Verify(croot commitment.Root, cpath commitment.Prefix, value } return runtime.VerifyAbsence(proof.Proof, root.Hash, keypath.String()) } - -type Value interface { - KeyBytes() []byte -} - -func NewProofFromValue(proof *merkle.Proof, prefix []byte, value Value) Proof { - return Proof{proof, bytes.TrimPrefix(value.KeyBytes(), prefix)} -} diff --git a/x/ibc/23-commitment/merkle/merkle_test.go b/x/ibc/23-commitment/merkle/merkle_test.go index fd550d43da53..0c73e7676ce8 100644 --- a/x/ibc/23-commitment/merkle/merkle_test.go +++ b/x/ibc/23-commitment/merkle/merkle_test.go @@ -16,7 +16,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) func defaultComponents() (sdk.StoreKey, sdk.Context, types.CommitMultiStore, *codec.Codec) { @@ -38,7 +38,7 @@ func commit(cms types.CommitMultiStore) Root { return NewRoot(cid.Hash) } -// TestStore tests Merkle proof on the commitment.Store +// TestStore tests Merkle proof on the ics23.Store // Sets/upates key-value pairs and prove with the query result proofs func TestStore(t *testing.T) { k, ctx, cms, cdc := defaultComponents() @@ -66,7 +66,7 @@ func TestStore(t *testing.T) { root := commit(cms) // Test query, and accumulate proofs - proofs := make([]commitment.Proof, 0, kvpn) + proofs := make([]ics23.Proof, 0, kvpn) for k, v := range m { q := state.NewStoreQuerier(cms.(types.Queryable)) v0, p, err := mapp.Value([]byte(k)).QueryRaw(q) @@ -87,7 +87,7 @@ func TestStore(t *testing.T) { m[string(k)] = []byte{} } - cstore, err := commitment.NewStore(root, path, proofs) + cstore, err := ics23.NewStore(root, path, proofs) require.NoError(t, err) // Test commitment store @@ -110,7 +110,7 @@ func TestStore(t *testing.T) { root = commit(cms) // Test query, and accumulate proofs - proofs = make([]commitment.Proof, 0, kvpn) + proofs = make([]ics23.Proof, 0, kvpn) for k, v := range m { q := state.NewStoreQuerier(cms.(types.Queryable)) v0, p, err := mapp.Value([]byte(k)).QueryRaw(q) @@ -119,7 +119,7 @@ func TestStore(t *testing.T) { proofs = append(proofs, Proof{Key: []byte(k), Proof: p}) } - cstore, err = commitment.NewStore(root, path, proofs) + cstore, err = ics23.NewStore(root, path, proofs) require.NoError(t, err) // Test commitment store diff --git a/x/ibc/23-commitment/merkle/utils.go b/x/ibc/23-commitment/merkle/utils.go index ec933af09748..64baf2d7c67c 100644 --- a/x/ibc/23-commitment/merkle/utils.go +++ b/x/ibc/23-commitment/merkle/utils.go @@ -11,7 +11,7 @@ import ( func QueryMultiStore(cms types.CommitMultiStore, storeName string, prefix []byte, key []byte) ([]byte, Proof, error) { queryable, ok := cms.(types.Queryable) if !ok { - panic("CommitMultiStore not queryable") + panic("commitMultiStore not queryable") } qres := queryable.Query(RequestQueryMultiStore(storeName, prefix, key)) if !qres.IsOK() { diff --git a/x/ibc/23-commitment/store.go b/x/ibc/23-commitment/store.go index 1ced699258fd..633ded2cbc20 100644 --- a/x/ibc/23-commitment/store.go +++ b/x/ibc/23-commitment/store.go @@ -1,4 +1,4 @@ -package commitment +package ics23 import ( "bytes" diff --git a/x/ibc/23-commitment/types.go b/x/ibc/23-commitment/types.go index 0e8a5db04d07..bba7711bb32f 100644 --- a/x/ibc/23-commitment/types.go +++ b/x/ibc/23-commitment/types.go @@ -1,4 +1,4 @@ -package commitment +package ics23 // ICS 023 Types Implementation // diff --git a/x/ibc/23-commitment/utils.go b/x/ibc/23-commitment/utils.go deleted file mode 100644 index e49dc15319a0..000000000000 --- a/x/ibc/23-commitment/utils.go +++ /dev/null @@ -1,8 +0,0 @@ -package commitment - -func join(a, b []byte) (res []byte) { - res = make([]byte, len(a)+len(b)) - copy(res, a) - copy(res[len(a):], b) - return -} diff --git a/x/ibc/23-commitment/value.go b/x/ibc/23-commitment/value.go deleted file mode 100644 index 5797e1cd13c9..000000000000 --- a/x/ibc/23-commitment/value.go +++ /dev/null @@ -1,127 +0,0 @@ -package commitment - -import ( - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/state" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Mapping is key []byte -> value []byte mapping, possibly prefixed. -// Proof verification should be done over Value constructed from the Mapping. -type Mapping struct { - cdc *codec.Codec - prefix []byte -} - -// NewMapping() constructs a new Mapping. -// The KVStore accessor is fixed to the commitment store. -func NewMapping(cdc *codec.Codec, prefix []byte) Mapping { - return Mapping{ - cdc: cdc, - prefix: prefix, - } -} - -func (m Mapping) store(ctx sdk.Context) Store { - return NewPrefix(GetStore(ctx), m.prefix) -} - -// Prefix() returns a new Mapping with the updated prefix -func (m Mapping) Prefix(prefix []byte) Mapping { - return Mapping{ - cdc: m.cdc, - prefix: join(m.prefix, prefix), - } -} - -type Indexer struct { - Mapping - enc state.IntEncoding -} - -func (m Mapping) Indexer(enc state.IntEncoding) Indexer { - return Indexer{ - Mapping: m, - enc: enc, - } -} - -func (ix Indexer) Value(index uint64) Value { - return ix.Mapping.Value(state.EncodeInt(index, ix.enc)) -} - -type Value struct { - m Mapping - key []byte -} - -func (m Mapping) Value(key []byte) Value { - return Value{m, key} -} - -// Is() proves the proof with the Value's key and the provided value. -func (v Value) Is(ctx sdk.Context, value interface{}) bool { - return v.m.store(ctx).Prove(v.key, v.m.cdc.MustMarshalBinaryBare(value)) -} - -// IsRaw() proves the proof with the Value's key and the provided raw value bytes. -func (v Value) IsRaw(ctx sdk.Context, value []byte) bool { - return v.m.store(ctx).Prove(v.key, value) -} - -// Enum is a byte typed wrapper for Value. -// Except for the type checking, it does not alter the behaviour. -type Enum struct { - Value -} - -// Enum() wraps the argument Value as Enum -func (v Value) Enum() Enum { - return Enum{v} -} - -// Is() proves the proof with the Enum's key and the provided value -func (v Enum) Is(ctx sdk.Context, value byte) bool { - return v.Value.IsRaw(ctx, []byte{value}) -} - -type String struct { - Value -} - -func (v Value) String() String { - return String{v} -} - -func (v String) Is(ctx sdk.Context, value string) bool { - return v.Value.IsRaw(ctx, []byte(value)) -} - -type Boolean struct { - Value -} - -func (v Value) Boolean() Boolean { - return Boolean{v} -} - -func (v Boolean) Is(ctx sdk.Context, value bool) bool { - return v.Value.Is(ctx, value) -} - -// Integer is a uint64 types wrapper for Value. -type Integer struct { - Value - - enc state.IntEncoding -} - -// Integer() wraps the argument Value as Integer -func (v Value) Integer(enc state.IntEncoding) Integer { - return Integer{v, enc} -} - -// Is() proves the proof with the Integer's key and the provided value -func (v Integer) Is(ctx sdk.Context, value uint64) bool { - return v.Value.IsRaw(ctx, state.EncodeInt(value, v.enc)) -} \ No newline at end of file From 1b7e4660b8f7bbaa4d8441bfaacbbd702f88cb9d Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Mon, 7 Oct 2019 18:15:15 +0200 Subject: [PATCH 115/166] remove store accessors --- store/state/boolean.go | 38 --- store/state/enum.go | 63 ----- store/state/errors.go | 64 ----- store/state/indexer.go | 125 ---------- store/state/integer.go | 67 ----- store/state/mapping.go | 103 -------- store/state/mapping_test.go | 123 ---------- store/state/string.go | 38 --- store/state/types.go | 61 ----- store/state/value.go | 126 ---------- store/state/value_test.go | 353 --------------------------- x/ibc/23-commitment/merkle/merkle.go | 2 +- x/ibc/23-commitment/merkle/utils.go | 10 +- x/ibc/23-commitment/store.go | 9 +- 14 files changed, 11 insertions(+), 1171 deletions(-) delete mode 100644 store/state/boolean.go delete mode 100644 store/state/enum.go delete mode 100644 store/state/errors.go delete mode 100644 store/state/indexer.go delete mode 100644 store/state/integer.go delete mode 100644 store/state/mapping.go delete mode 100644 store/state/mapping_test.go delete mode 100644 store/state/string.go delete mode 100644 store/state/types.go delete mode 100644 store/state/value.go delete mode 100644 store/state/value_test.go diff --git a/store/state/boolean.go b/store/state/boolean.go deleted file mode 100644 index 669b7ae85e8d..000000000000 --- a/store/state/boolean.go +++ /dev/null @@ -1,38 +0,0 @@ -package state - -// Boolean is a bool typed wrapper for Value. -// -// false <-> []byte{0x00} -// true <-> []byte{0x01} -type Boolean struct { - Value -} - -func (v Value) Boolean() Boolean { - return Boolean{v} -} - -// Get decodes and returns the stored boolean value if it exists. It will panic -// if the value exists but is not boolean type. -func (v Boolean) Get(ctx Context) (res bool) { - v.Value.Get(ctx, &res) - return -} - -// GetSafe decodes and returns the stored boolean value. It will return an error -// if the value does not exist or not boolean. -func (v Boolean) GetSafe(ctx Context) (res bool, err error) { - err = v.Value.GetSafe(ctx, &res) - return -} - -// Set encodes and sets the boolean argument to the state. -func (v Boolean) Set(ctx Context, value bool) { - v.Value.Set(ctx, value) -} - -// Query() retrives state value and proof from a queryable reference -func (v Boolean) Query(q ABCIQuerier) (res bool, proof *Proof, err error) { - proof, err = v.Value.Query(q, &res) - return -} diff --git a/store/state/enum.go b/store/state/enum.go deleted file mode 100644 index 18937ce18bf7..000000000000 --- a/store/state/enum.go +++ /dev/null @@ -1,63 +0,0 @@ -package state - -import "errors" - -// Enum is a byte typed wrapper for Value. -// x <-> []byte{x} -type Enum struct { - Value -} - -func (v Value) Enum() Enum { - return Enum{v} -} - -// Get decodes and returns the stored byte value if it exists. It will panic if -// the value exists but is not byte type. -func (v Enum) Get(ctx Context) (res byte) { - return v.Value.GetRaw(ctx)[0] -} - -// GetSafe decodes and returns the stored byte value. It will returns an error -// if the value does not exists or not byte. -func (v Enum) GetSafe(ctx Context) (res byte, err error) { - bz := v.Value.GetRaw(ctx) - if bz == nil { - return res, ErrEmptyValue() - } - if len(bz) != 1 { - return res, ErrUnmarshal(errors.New("stored byte slice length is not 1")) - } - return bz[0], nil -} - -// Set encodes and sets the byte argument to the state. -func (v Enum) Set(ctx Context, value byte) { - v.Value.SetRaw(ctx, []byte{value}) -} - -// Incr increments the stored value, and returns the updated value. -func (v Enum) Incr(ctx Context) (res byte) { - res = v.Get(ctx) + 1 - v.Set(ctx, res) - return -} - -// Transit checks whether the stored value matching with the "from" argument. -// If it matches, it stores the "to" argument to the state and returns true. -func (v Enum) Transit(ctx Context, from, to byte) bool { - if v.Get(ctx) != from { - return false - } - v.Set(ctx, to) - return true -} - -// Query() retrives state value and proof from a queryable reference -func (v Enum) Query(q ABCIQuerier) (res byte, proof *Proof, err error) { - value, proof, err := v.Value.QueryRaw(q) - if err != nil { - return - } - return value[0], proof, err -} diff --git a/store/state/errors.go b/store/state/errors.go deleted file mode 100644 index 303584c68bff..000000000000 --- a/store/state/errors.go +++ /dev/null @@ -1,64 +0,0 @@ -package state - -import ( - "fmt" -) - -// GetSafeErrorType is enum for indicating the type of error -type GetSafeErrorType byte - -const ( - // ErrTypeEmptyValue is used for nil byteslice values - ErrTypeEmptyValue GetSafeErrorType = iota - // ErrTypeUnmarshal is used for undeserializable values - ErrTypeUnmarshal -) - -// Implements Formatter -func (ty GetSafeErrorType) Format(msg string) (res string) { - switch ty { - case ErrTypeEmptyValue: - res = fmt.Sprintf("Empty Value found") - case ErrTypeUnmarshal: - res = fmt.Sprintf("Error while unmarshal") - default: - panic("Unknown error type") - } - - if msg != "" { - res = fmt.Sprintf("%s: %s", res, msg) - } - - return -} - -// GetSafeError is error type for GetSafe method -type GetSafeError struct { - ty GetSafeErrorType - inner error -} - -var _ error = GetSafeError{} - -// Implements error -func (err GetSafeError) Error() string { - if err.inner == nil { - return err.ty.Format("") - } - return err.ty.Format(err.inner.Error()) -} - -// ErrEmptyValue constructs GetSafeError with ErrTypeEmptyValue -func ErrEmptyValue() GetSafeError { - return GetSafeError{ - ty: ErrTypeEmptyValue, - } -} - -// ErrUnmarshal constructs GetSafeError with ErrTypeUnmarshal -func ErrUnmarshal(err error) GetSafeError { - return GetSafeError{ - ty: ErrTypeUnmarshal, - inner: err, - } -} diff --git a/store/state/indexer.go b/store/state/indexer.go deleted file mode 100644 index 3988e9503fe0..000000000000 --- a/store/state/indexer.go +++ /dev/null @@ -1,125 +0,0 @@ -package state - -import ( - "encoding/binary" - "fmt" - "strconv" -) - -// IntEncoding is an enum type defining the integer serialization scheme. -// All encoding schemes preserves order. -type IntEncoding byte - -const ( - // Dec is human readable decimal encoding scheme. - // Has fixed length of 20 bytes. - Dec IntEncoding = iota - // Hex is human readable hexadecimal encoding scheme - // Has fixed length of 16 bytes. - Hex - // Bin is machine readable big endian encoding scheme - // Has fixed length of 8 bytes - Bin -) - -// Indexer is a integer typed key wrapper for Mapping. Except for the type -// checking, it does not alter the behaviour. All keys are encoded depending on -// the IntEncoding. -type Indexer struct { - m Mapping - - enc IntEncoding -} - -// Indexer() constructs the Indexer with an IntEncoding -func (m Mapping) Indexer(enc IntEncoding) Indexer { - return Indexer{ - m: m, - enc: enc, - } -} - -// EncodeInt provides order preserving integer encoding function. -func EncodeInt(index uint64, enc IntEncoding) (res []byte) { - switch enc { - case Dec: - // return decimal number index, 20-length 0 padded - return []byte(fmt.Sprintf("%020d", index)) - - case Hex: - // return hexadecimal number index, 20-length 0 padded - return []byte(fmt.Sprintf("%016x", index)) - - case Bin: - // return bigendian encoded number index, 8-length - res = make([]byte, 8) - binary.BigEndian.PutUint64(res, index) - return - - default: - panic("invalid IntEncoding") - } -} - -// DecodeInt provides integer decoding function, inversion of EncodeInt. -func DecodeInt(bz []byte, enc IntEncoding) (res uint64, err error) { - switch enc { - case Dec: - return strconv.ParseUint(string(bz), 10, 64) - - case Hex: - return strconv.ParseUint(string(bz), 16, 64) - - case Bin: - return binary.BigEndian.Uint64(bz), nil - - default: - panic("invalid IntEncoding") - } -} - -// Value returns the Value corresponding to the provided index. -func (ix Indexer) Value(index uint64) Value { - return ix.m.Value(EncodeInt(index, ix.enc)) -} - -// Get decodes and sets the stored value to the pointer if it exists. It will -// panic if the value exists but not unmarshalable. -func (ix Indexer) Get(ctx Context, index uint64, ptr interface{}) { - ix.Value(index).Get(ctx, ptr) -} - -// GetSafe decodes and sets the stored value to the pointer. It will return an -// error if the value does not exist or unmarshalable. -func (ix Indexer) GetSafe(ctx Context, index uint64, ptr interface{}) error { - return ix.Value(index).GetSafe(ctx, ptr) -} - -// Set encodes and sets the argument to the state. -func (ix Indexer) Set(ctx Context, index uint64, o interface{}) { - ix.Value(index).Set(ctx, o) -} - -// SetRaw sets the raw bytestring argument to the state -func (ix Indexer) SetRaw(ctx Context, index uint64, value []byte) { - ix.Value(index).SetRaw(ctx, value) -} - -// Has returns true if the stored value is not nil. -func (ix Indexer) Has(ctx Context, index uint64) bool { - return ix.Value(index).Exists(ctx) -} - -// Delete removes the stored value. -func (ix Indexer) Delete(ctx Context, index uint64) { - ix.Value(index).Delete(ctx) -} - -// Prefix returns a new Indexer with the updated prefix -func (ix Indexer) Prefix(prefix []byte) Indexer { - return Indexer{ - m: ix.m.Prefix(prefix), - - enc: ix.enc, - } -} diff --git a/store/state/integer.go b/store/state/integer.go deleted file mode 100644 index 938b83fd1f46..000000000000 --- a/store/state/integer.go +++ /dev/null @@ -1,67 +0,0 @@ -package state - -// Integer is a uint64 types wrapper for Value. -// The serialization follows the @IntEncoding@ format provided to the NewInteger. -type Integer struct { - Value - - enc IntEncoding -} - -func (v Value) Integer(enc IntEncoding) Integer { - return Integer{v, enc} -} - -// Get() unmarshales and returns the stored uint64 value if it exists. -// If will panic if the value exists but not decodable. -func (v Integer) Get(ctx Context) uint64 { - bz := v.Value.GetRaw(ctx) - if bz == nil { - return 0 - } - res, err := DecodeInt(bz, v.enc) - if err != nil { - panic(err) - } - return res -} - -// GetSafe() unmarshales and returns the stored uint64 value. -// It will return an error if the value does not exist or not uint64. -func (v Integer) GetSafe(ctx Context) (res uint64, err error) { - bz := v.Value.GetRaw(ctx) - if bz == nil { - return 0, ErrEmptyValue() - } - res, err = DecodeInt(bz, v.enc) - if err != nil { - err = ErrUnmarshal(err) - } - return -} - -// Set() marshales and sets the uint64 argument to the state. -func (v Integer) Set(ctx Context, value uint64) { - v.Value.SetRaw(ctx, EncodeInt(value, v.enc)) -} - -// Increment increments the stored value and returns it. -func (v Integer) Increment(ctx Context) (res uint64) { - res = v.Get(ctx) + 1 - v.Set(ctx, res) - return -} - -// Query() retrives state value and proof from a queryable reference -func (v Integer) Query(q ABCIQuerier) (res uint64, proof *Proof, err error) { - value, proof, err := v.Value.QueryRaw(q) - if err != nil { - return - } - if value == nil { - res = 0 - return - } - res, err = DecodeInt(value, v.enc) - return -} diff --git a/store/state/mapping.go b/store/state/mapping.go deleted file mode 100644 index 3b81d2323efe..000000000000 --- a/store/state/mapping.go +++ /dev/null @@ -1,103 +0,0 @@ -package state - -import ( - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Mapping is key []byte -> value []byte mapping using a base(possibly prefixed). -// All store accessing operations are redirected to the Value corresponding to -// the key argument. -type Mapping struct { - storeKey sdk.StoreKey - cdc *codec.Codec - prefix []byte -} - -// NewMapping constructs a Mapping with a provided prefix. -func NewMapping(storeKey sdk.StoreKey, cdc *codec.Codec, prefix []byte) Mapping { - return Mapping{ - storeKey: storeKey, - cdc: cdc, - prefix: prefix, - } -} - -// Value returns the Value corresponding to the provided key. -func (m Mapping) Value(key []byte) Value { - return NewValue(m, key) -} - -// Get decodes and sets the stored value to the pointer if it exists. It will -// panic if the value exists but not unmarshalable. -func (m Mapping) Get(ctx Context, key []byte, ptr interface{}) { - m.Value(key).Get(ctx, ptr) -} - -// GetSafe decodes and sets the stored value to the pointer. It will return an -// error if the value does not exist or unmarshalable. -func (m Mapping) GetSafe(ctx Context, key []byte, ptr interface{}) error { - return m.Value(key).GetSafe(ctx, ptr) -} - -// Set encodes and sets the argument to the state. It calls Delete if the -// argument is nil. -func (m Mapping) Set(ctx Context, key []byte, o interface{}) { - if o == nil { - m.Delete(ctx, key) - return - } - m.Value(key).Set(ctx, o) -} - -// SetRaw sets the raw bytestring argument to the state. -func (m Mapping) SetRaw(ctx Context, key []byte, value []byte) { - m.Value(key).SetRaw(ctx, value) -} - -// Has returns true if the stored value is not nil. -func (m Mapping) Has(ctx Context, key []byte) bool { - return m.Value(key).Exists(ctx) -} - -// Delete removes the stored value. -func (m Mapping) Delete(ctx Context, key []byte) { - m.Value(key).Delete(ctx) -} - -func (m Mapping) Cdc() *codec.Codec { - return m.cdc -} - -// StoreName returns the mapping's store name. -func (m Mapping) StoreName() string { - return m.storeKey.Name() -} - -// PrefixBytes returns the mapping's prefix bytes. -func (m Mapping) PrefixBytes() (res []byte) { - res = make([]byte, len(m.prefix)) - copy(res, m.prefix) - return -} - -// KeyBytes returns the mapping's key bytes. -func (m Mapping) KeyBytes(key []byte) (res []byte) { - return join(m.prefix, key) -} - -func join(a, b []byte) (res []byte) { - res = make([]byte, len(a)+len(b)) - copy(res, a) - copy(res[len(a):], b) - return -} - -// Prefix returns a new mapping with an updated prefix. -func (m Mapping) Prefix(prefix []byte) Mapping { - return Mapping{ - storeKey: m.storeKey, - cdc: m.cdc, - prefix: join(m.prefix, prefix), - } -} diff --git a/store/state/mapping_test.go b/store/state/mapping_test.go deleted file mode 100644 index 3d4110fdf672..000000000000 --- a/store/state/mapping_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package state - -import ( - "math/rand" - "reflect" - "testing" - - "github.com/stretchr/testify/require" -) - -type mapping interface { - Get(Context, interface{}, interface{}) - GetSafe(Context, interface{}, interface{}) error - Set(Context, interface{}, interface{}) - Has(Context, interface{}) bool - Delete(Context, interface{}) - RandomKey() interface{} -} - -type mappingT struct { - Mapping -} - -var _ mapping = mappingT{} - -func newMapping() mappingT { - return mappingT{NewMapping(testkey, testcdc, nil)} -} - -func (m mappingT) Get(ctx Context, key interface{}, ptr interface{}) { - m.Mapping.Get(ctx, []byte(key.(string)), ptr) -} - -func (m mappingT) GetSafe(ctx Context, key interface{}, ptr interface{}) error { - return m.Mapping.GetSafe(ctx, []byte(key.(string)), ptr) -} - -func (m mappingT) Set(ctx Context, key interface{}, o interface{}) { - m.Mapping.Set(ctx, []byte(key.(string)), o) -} - -func (m mappingT) Has(ctx Context, key interface{}) bool { - return m.Mapping.Has(ctx, []byte(key.(string))) -} - -func (m mappingT) Delete(ctx Context, key interface{}) { - m.Mapping.Delete(ctx, []byte(key.(string))) -} - -func (m mappingT) RandomKey() interface{} { - bz := make([]byte, 64) - rand.Read(bz) - return string(bz) -} - -type indexerT struct { - Indexer -} - -var _ mapping = indexerT{} - -func newIndexer(enc IntEncoding) indexerT { - return indexerT{NewMapping(testkey, testcdc, nil).Indexer(enc)} -} - -func (m indexerT) Get(ctx Context, key interface{}, ptr interface{}) { - m.Indexer.Get(ctx, key.(uint64), ptr) -} - -func (m indexerT) GetSafe(ctx Context, key interface{}, ptr interface{}) error { - return m.Indexer.GetSafe(ctx, key.(uint64), ptr) -} - -func (m indexerT) Set(ctx Context, key interface{}, o interface{}) { - m.Indexer.Set(ctx, key.(uint64), o) -} - -func (m indexerT) Has(ctx Context, key interface{}) bool { - return m.Indexer.Has(ctx, key.(uint64)) -} - -func (m indexerT) Delete(ctx Context, key interface{}) { - m.Indexer.Delete(ctx, key.(uint64)) -} - -func (m indexerT) RandomKey() interface{} { - return rand.Uint64() -} - -func TestMapping(t *testing.T) { - ctx, _ := defaultComponents() - table := []mapping{newMapping(), newIndexer(Dec), newIndexer(Hex), newIndexer(Bin)} - - for _, m := range table { - exp := make(map[interface{}]uint64) - for n := 0; n < 10e4; n++ { - k, v := m.RandomKey(), rand.Uint64() - require.False(t, m.Has(ctx, k)) - exp[k] = v - m.Set(ctx, k, v) - } - - for k, v := range exp { - ptr := new(uint64) - m.Get(ctx, k, ptr) - require.Equal(t, v, indirect(ptr)) - - ptr = new(uint64) - err := m.GetSafe(ctx, k, ptr) - require.NoError(t, err) - require.Equal(t, v, indirect(ptr)) - - require.True(t, m.Has(ctx, k)) - - m.Delete(ctx, k) - require.False(t, m.Has(ctx, k)) - ptr = new(uint64) - err = m.GetSafe(ctx, k, ptr) - require.Error(t, err) - require.Equal(t, reflect.Zero(reflect.TypeOf(ptr).Elem()).Interface(), indirect(ptr)) - } - } -} diff --git a/store/state/string.go b/store/state/string.go deleted file mode 100644 index a67b6b8939fb..000000000000 --- a/store/state/string.go +++ /dev/null @@ -1,38 +0,0 @@ -package state - -// String is a string types wrapper for Value. -// x <-> []byte(x) -type String struct { - Value -} - -func (v Value) String() String { - return String{v} -} - -// Get decodes and returns the stored string value if it exists. It will panic -// if the value exists but is not string type. -func (v String) Get(ctx Context) (res string) { - return string(v.Value.GetRaw(ctx)) -} - -// GetSafe decodes and returns the stored string value. It will return an error -// if the value does not exist or not string. -func (v String) GetSafe(ctx Context) (res string, err error) { - bz := v.Value.GetRaw(ctx) - if bz == nil { - return res, ErrEmptyValue() - } - return string(bz), nil -} - -// Set encodes and sets the string argument to the state. -func (v String) Set(ctx Context, value string) { - v.Value.SetRaw(ctx, []byte(value)) -} - -// Query() retrives state value and proof from a queryable reference -func (v String) Query(q ABCIQuerier) (res string, proof *Proof, err error) { - value, proof, err := v.Value.QueryRaw(q) - return string(value), proof, err -} diff --git a/store/state/types.go b/store/state/types.go deleted file mode 100644 index 66ffb1f5255f..000000000000 --- a/store/state/types.go +++ /dev/null @@ -1,61 +0,0 @@ -package state - -import ( - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/merkle" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" - stypes "github.com/cosmos/cosmos-sdk/store/types" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -type KVStore = sdk.KVStore -type Context = sdk.Context -type Proof = merkle.Proof -type Codec = codec.Codec - -type ABCIQuerier interface { - Query(storeName string, key []byte) (abci.ResponseQuery, error) -} - -var _ ABCIQuerier = CLIQuerier{} - -type CLIQuerier struct { - ctx context.CLIContext -} - -func NewCLIQuerier(ctx context.CLIContext) CLIQuerier { - return CLIQuerier{ctx} -} - -func (q CLIQuerier) Query(storeName string, key []byte) (abci.ResponseQuery, error) { - req := abci.RequestQuery{ - Path: "/store/" + storeName + "/key", - Data: key, - Prove: true, - } - - return q.ctx.QueryABCI(req) -} - -var _ ABCIQuerier = StoreQuerier{} - -type StoreQuerier struct { - store stypes.Queryable -} - -func NewStoreQuerier(store stypes.Queryable) StoreQuerier { - return StoreQuerier{store} -} - -func (q StoreQuerier) Query(storeName string, key []byte) (abci.ResponseQuery, error) { - req := abci.RequestQuery{ - Path: "/" + storeName + "/key", - Data: key, - Prove: true, - } - - return q.store.Query(req), nil - -} diff --git a/store/state/value.go b/store/state/value.go deleted file mode 100644 index 1d467b0ab380..000000000000 --- a/store/state/value.go +++ /dev/null @@ -1,126 +0,0 @@ -package state - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Value is a capability for reading and writing on a specific key-value point -// in the state. Value consists of Base and key []byte. An actor holding a Value -// has a full access right on that state point. -type Value struct { - m Mapping - key []byte -} - -// NewValue constructs a Value. -func NewValue(m Mapping, key []byte) Value { - return Value{ - m: m, - key: key, - } -} - -func (v Value) store(ctx Context) KVStore { - return ctx.KVStore(v.m.storeKey) -} - -// Cdc() returns the codec that the value is using to marshal/unmarshal -func (v Value) Cdc() *Codec { - return v.m.Cdc() -} - -func (v Value) Marshal(value interface{}) []byte { - return v.m.cdc.MustMarshalBinaryBare(value) -} - -func (v Value) Unmarshal(bz []byte, ptr interface{}) error { - return v.m.cdc.UnmarshalBinaryBare(bz, ptr) -} - -func (v Value) mustUnmarshal(bz []byte, ptr interface{}) { - v.m.cdc.MustUnmarshalBinaryBare(bz, ptr) -} - -// Get decodes and sets the stored value to the pointer if it exists. It will -// panic if the value exists but not unmarshalable. -func (v Value) Get(ctx Context, ptr interface{}) { - bz := v.store(ctx).Get(v.KeyBytes()) - if bz != nil { - v.mustUnmarshal(bz, ptr) - } -} - -// GetSafe decodes and sets the stored value to the pointer. It will return an -// error if the value does not exist or unmarshalable. -func (v Value) GetSafe(ctx Context, ptr interface{}) error { - bz := v.store(ctx).Get(v.KeyBytes()) - if bz == nil { - return ErrEmptyValue() - } - err := v.Unmarshal(bz, ptr) - if err != nil { - return ErrUnmarshal(err) - } - return nil -} - -// GetRaw returns the raw bytes that is stored in the state. -func (v Value) GetRaw(ctx Context) []byte { - return v.store(ctx).Get(v.KeyBytes()) -} - -// Set encodes and sets the argument to the state. -func (v Value) Set(ctx Context, o interface{}) { - v.store(ctx).Set(v.KeyBytes(), v.Marshal(o)) -} - -// SetRaw sets the raw bytes to the state. -func (v Value) SetRaw(ctx Context, bz []byte) { - v.store(ctx).Set(v.KeyBytes(), bz) -} - -// Exists returns true if the stored value is not nil. It calls KVStore.Has() -// internally. -func (v Value) Exists(ctx Context) bool { - return v.store(ctx).Has(v.KeyBytes()) -} - -// Delete removes the stored value. It calls KVStore.Delete() internally. -func (v Value) Delete(ctx Context) { - v.store(ctx).Delete(v.KeyBytes()) -} - -func (v Value) StoreName() string { - return v.m.StoreName() -} - -func (v Value) PrefixBytes() []byte { - return v.m.PrefixBytes() -} - -// KeyBytes returns the prefixed key that the Value is providing to the KVStore. -func (v Value) KeyBytes() []byte { - return v.m.KeyBytes(v.key) -} - -func (v Value) QueryRaw(q ABCIQuerier) ([]byte, *Proof, error) { - resp, err := q.Query(v.m.StoreName(), v.KeyBytes()) - if err != nil { - return nil, nil, err - } - - if !resp.IsOK() { - return nil, nil, sdk.NewError(sdk.CodespaceRoot, sdk.CodeType(resp.Code), resp.Log) - } - - return resp.Value, resp.Proof, nil -} - -func (v Value) Query(q ABCIQuerier, ptr interface{}) (*Proof, error) { - value, proof, err := v.QueryRaw(q) - if err != nil { - return nil, err - } - err = v.Cdc().UnmarshalBinaryBare(value, ptr) - return proof, err -} diff --git a/store/state/value_test.go b/store/state/value_test.go deleted file mode 100644 index 38888890bbdf..000000000000 --- a/store/state/value_test.go +++ /dev/null @@ -1,353 +0,0 @@ -package state - -import ( - "crypto/rand" - "reflect" - "testing" - - "github.com/stretchr/testify/require" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/merkle" - "github.com/tendermint/tendermint/libs/log" - dbm "github.com/tendermint/tm-db" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/rootmulti" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -var testcdc = codec.New() -var testkey = sdk.NewKVStoreKey("test") - -func init() { - // register -} - -func key() (res []byte) { - res = make([]byte, 64) - rand.Read(res) - return -} - -type value interface { - KeyBytes() []byte - Get(Context, interface{}) - GetSafe(Context, interface{}) error - GetRaw(Context) []byte - Set(Context, interface{}) - SetRaw(Context, []byte) - Exists(Context) bool - Delete(Context) - Query(ABCIQuerier, interface{}) (*Proof, error) - Marshal(interface{}) []byte - Unmarshal([]byte, interface{}) -} - -type typeValue interface { - value - Proto() interface{} -} - -type valueT struct { - Value -} - -var _ value = valueT{} - -func (v valueT) Marshal(o interface{}) []byte { - return v.m.cdc.MustMarshalBinaryBare(o) -} - -func (v valueT) Unmarshal(bz []byte, ptr interface{}) { - v.m.cdc.MustUnmarshalBinaryBare(bz, ptr) -} - -type booleanT struct { - Boolean -} - -var _ typeValue = booleanT{} - -func newBoolean() booleanT { - return booleanT{NewMapping(testkey, testcdc, nil).Value(key()).Boolean()} -} - -func (booleanT) Proto() interface{} { - return new(bool) -} - -func (v booleanT) Get(ctx Context, ptr interface{}) { - reflect.ValueOf(ptr).Elem().SetBool(v.Boolean.Get(ctx)) -} - -func (v booleanT) GetSafe(ctx Context, ptr interface{}) error { - res, err := v.Boolean.GetSafe(ctx) - if err != nil { - return err - } - reflect.ValueOf(ptr).Elem().SetBool(res) - return nil -} - -func (v booleanT) Set(ctx Context, o interface{}) { - v.Boolean.Set(ctx, o.(bool)) -} - -func (v booleanT) Marshal(o interface{}) []byte { - switch o.(bool) { - case false: - return []byte{0x00} - case true: - return []byte{0x01} - } - panic("invalid boolean type") -} - -func (v booleanT) Unmarshal(bz []byte, ptr interface{}) { - switch bz[0] { - case 0x00: - reflect.ValueOf(ptr).Elem().SetBool(false) - case 0x01: - reflect.ValueOf(ptr).Elem().SetBool(true) - } -} - -func (v booleanT) Query(q ABCIQuerier, ptr interface{}) (proof *Proof, err error) { - res, proof, err := v.Boolean.Query(q) - if err != nil { - return - } - reflect.ValueOf(ptr).Elem().SetBool(res) - return -} - -type integerT struct { - Integer -} - -var _ typeValue = integerT{} - -func newInteger(enc IntEncoding) integerT { - return integerT{NewMapping(testkey, testcdc, nil).Value(key()).Integer(enc)} -} - -func (integerT) Proto() interface{} { - return new(uint64) -} - -func (v integerT) Get(ctx Context, ptr interface{}) { - reflect.ValueOf(ptr).Elem().SetUint(v.Integer.Get(ctx)) -} - -func (v integerT) GetSafe(ctx Context, ptr interface{}) error { - res, err := v.Integer.GetSafe(ctx) - if err != nil { - return err - } - reflect.ValueOf(ptr).Elem().SetUint(res) - return nil -} - -func (v integerT) Set(ctx Context, o interface{}) { - v.Integer.Set(ctx, o.(uint64)) -} - -func (v integerT) Marshal(o interface{}) []byte { - return EncodeInt(o.(uint64), v.enc) -} - -func (v integerT) Unmarshal(bz []byte, ptr interface{}) { - res, err := DecodeInt(bz, v.enc) - if err != nil { - panic(err) - } - reflect.ValueOf(ptr).Elem().SetUint(res) -} - -func (v integerT) Query(q ABCIQuerier, ptr interface{}) (proof *Proof, err error) { - res, proof, err := v.Integer.Query(q) - if err != nil { - return - } - reflect.ValueOf(ptr).Elem().SetUint(res) - return -} - -type enumT struct { - Enum -} - -var _ typeValue = enumT{} - -func newEnum() enumT { - return enumT{NewMapping(testkey, testcdc, nil).Value(key()).Enum()} -} - -func (enumT) Proto() interface{} { - return new(byte) -} - -func (v enumT) Get(ctx Context, ptr interface{}) { - reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v.Enum.Get(ctx))) -} - -func (v enumT) GetSafe(ctx Context, ptr interface{}) error { - res, err := v.Enum.GetSafe(ctx) - if err != nil { - return err - } - reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(res)) - return nil -} - -func (v enumT) Set(ctx Context, o interface{}) { - v.Enum.Set(ctx, o.(byte)) -} - -func (v enumT) Marshal(o interface{}) []byte { - return []byte{o.(byte)} -} - -func (v enumT) Unmarshal(bz []byte, ptr interface{}) { - reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(bz[0])) -} - -func (v enumT) Query(q ABCIQuerier, ptr interface{}) (proof *Proof, err error) { - res, proof, err := v.Enum.Query(q) - if err != nil { - return - } - reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(res)) - return -} - -type stringT struct { - String -} - -var _ typeValue = stringT{} - -func newString() stringT { - return stringT{NewMapping(testkey, testcdc, nil).Value(key()).String()} -} - -func (stringT) Proto() interface{} { - return new(string) -} - -func (v stringT) Get(ctx Context, ptr interface{}) { - reflect.ValueOf(ptr).Elem().SetString(v.String.Get(ctx)) -} - -func (v stringT) GetSafe(ctx Context, ptr interface{}) error { - res, err := v.String.GetSafe(ctx) - if err != nil { - return err - } - reflect.ValueOf(ptr).Elem().SetString(res) - return nil -} - -func (v stringT) Set(ctx Context, o interface{}) { - v.String.Set(ctx, o.(string)) -} - -func (v stringT) Marshal(o interface{}) []byte { - return []byte(o.(string)) -} - -func (v stringT) Unmarshal(bz []byte, ptr interface{}) { - reflect.ValueOf(ptr).Elem().SetString(string(bz)) -} - -func (v stringT) Query(q ABCIQuerier, ptr interface{}) (proof *Proof, err error) { - res, proof, err := v.String.Query(q) - if err != nil { - return - } - reflect.ValueOf(ptr).Elem().SetString(res) - return -} - -func defaultComponents() (sdk.Context, *rootmulti.Store) { - db := dbm.NewMemDB() - cms := rootmulti.NewStore(db) - cms.MountStoreWithDB(testkey, sdk.StoreTypeIAVL, db) - cms.LoadLatestVersion() - ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) - return ctx, cms -} - -func indirect(ptr interface{}) interface{} { - return reflect.ValueOf(ptr).Elem().Interface() -} - -func TestTypeValue(t *testing.T) { - ctx, cms := defaultComponents() - - var table = []struct { - ty typeValue - orig interface{} - }{ - {newBoolean(), false}, - {newBoolean(), true}, - {newInteger(Dec), uint64(1024000)}, - {newInteger(Dec), uint64(2048000)}, - {newInteger(Bin), uint64(4096000)}, - {newInteger(Bin), uint64(8192000)}, - {newInteger(Hex), uint64(16384000)}, - {newInteger(Hex), uint64(32768000)}, - {newEnum(), byte(0x00)}, - {newEnum(), byte(0x78)}, - {newEnum(), byte(0xA0)}, - {newString(), "1234567890"}, - {newString(), "asdfghjkl"}, - {newString(), "qwertyuiop"}, - } - - for i, tc := range table { - v := tc.ty - // Exists expected false - require.False(t, v.Exists(ctx)) - - // Simple get-set - v.Set(ctx, tc.orig) - ptr := v.Proto() - v.Get(ctx, ptr) - require.Equal(t, tc.orig, indirect(ptr), "Expected equal on tc %d", i) - ptr = v.Proto() - err := v.GetSafe(ctx, ptr) - require.NoError(t, err) - require.Equal(t, tc.orig, indirect(ptr), "Expected equal on tc %d", i) - - // Raw get - require.Equal(t, v.Marshal(tc.orig), v.GetRaw(ctx), "Expected equal on tc %d", i) - - // Exists expected true - require.True(t, v.Exists(ctx)) - - // After delete - v.Delete(ctx) - require.False(t, v.Exists(ctx)) - ptr = v.Proto() - err = v.GetSafe(ctx, ptr) - require.Error(t, err) - require.Equal(t, reflect.Zero(reflect.TypeOf(ptr).Elem()).Interface(), indirect(ptr)) - require.Nil(t, v.GetRaw(ctx)) - - // Set again and test abci query - v.Set(ctx, tc.orig) - cid := cms.Commit() - ptr = v.Proto() - q := NewStoreQuerier(cms) - proof, err := v.Query(q, ptr) - require.NoError(t, err) - require.Equal(t, tc.orig, indirect(ptr), "Expected equal on tc %d", i) - prt := rootmulti.DefaultProofRuntime() - kp := merkle.KeyPath{}. - AppendKey([]byte(testkey.Name()), merkle.KeyEncodingHex). - AppendKey(v.KeyBytes(), merkle.KeyEncodingHex) - require.NoError(t, prt.VerifyValue(proof, cid.Hash, kp.String(), v.GetRaw(ctx))) - } -} diff --git a/x/ibc/23-commitment/merkle/merkle.go b/x/ibc/23-commitment/merkle/merkle.go index bef5e7c6951b..798b41d840ad 100644 --- a/x/ibc/23-commitment/merkle/merkle.go +++ b/x/ibc/23-commitment/merkle/merkle.go @@ -63,7 +63,7 @@ func (Prefix) CommitmentKind() string { } func (prefix Prefix) Key(key []byte) []byte { - return join(prefix.KeyPrefix, key) + return ics23.Join(prefix.KeyPrefix, key) } var _ ics23.Proof = Proof{} diff --git a/x/ibc/23-commitment/merkle/utils.go b/x/ibc/23-commitment/merkle/utils.go index 64baf2d7c67c..a2813762b7b6 100644 --- a/x/ibc/23-commitment/merkle/utils.go +++ b/x/ibc/23-commitment/merkle/utils.go @@ -6,6 +6,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/cosmos/cosmos-sdk/store/types" + ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) func QueryMultiStore(cms types.CommitMultiStore, storeName string, prefix []byte, key []byte) ([]byte, Proof, error) { @@ -27,14 +28,7 @@ func RequestQueryMultiStore(storeName string, prefix []byte, key []byte) abci.Re // and performs key-value query only if it is "/key" return abci.RequestQuery{ Path: "/" + storeName + "/key", - Data: join(prefix, key), + Data: ics23.Join(prefix, key), Prove: true, } } - -func join(a, b []byte) (res []byte) { - res = make([]byte, len(a)+len(b)) - copy(res, a) - copy(res[len(a):], b) - return -} diff --git a/x/ibc/23-commitment/store.go b/x/ibc/23-commitment/store.go index 633ded2cbc20..941adaf230a0 100644 --- a/x/ibc/23-commitment/store.go +++ b/x/ibc/23-commitment/store.go @@ -36,7 +36,7 @@ func NewPrefix(store Store, pref []byte) Store { // Prove implements Store. func (prefix prefix) Prove(path, value []byte) bool { - return prefix.store.Prove(join(prefix.prefix, path), value) + return prefix.store.Prove(Join(prefix.prefix, path), value) } var _ Store = (*store)(nil) @@ -96,3 +96,10 @@ func (store *store) Prove(path, value []byte) bool { return true } + +func Join(a, b []byte) (res []byte) { + res = make([]byte, len(a)+len(b)) + copy(res, a) + copy(res[len(a):], b) + return +} From 02bd6f78e684d0007dc2df4175a884634f48a010 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Mon, 7 Oct 2019 18:46:12 +0200 Subject: [PATCH 116/166] proposed refactor --- x/ibc/02-client/keeper/keeper.go | 38 ++++++++++++++++++++++++++------ x/ibc/02-client/types/state.go | 6 ++--- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index b1fb846eb672..b23969926763 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -5,6 +5,8 @@ import ( "github.com/tendermint/tendermint/libs/log" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" "github.com/cosmos/cosmos-sdk/store/state" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -17,15 +19,19 @@ import ( // Keeper represents a type that grants read and write permissions to any client // state information type Keeper struct { - mapping state.Mapping + storeKey sdk.StoreKey + cdc *codec.Codec codespace sdk.CodespaceType + prefix []byte // prefix bytes for accessing the store } // NewKeeper creates a new NewKeeper instance -func NewKeeper(mapping state.Mapping, codespace sdk.CodespaceType) Keeper { +func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, codespace sdk.CodespaceType) Keeper { return Keeper{ - mapping: mapping.Prefix([]byte(types.SubModuleName + "/")), // "client/" - codespace: sdk.CodespaceType(fmt.Sprintf("%s/%s", codespace, types.DefaultCodespace)), // "ibc/client" + storeKey: key, + cdc: cdc, + codespace: sdk.CodespaceType(fmt.Sprintf("%s/%s", codespace, types.DefaultCodespace)), // "ibc/client", + prefix: []byte(types.SubModuleName + "/"), // "client/" } } @@ -34,11 +40,29 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s/%s", ibctypes.ModuleName, types.SubModuleName)) } +// GetClient creates a new client state and populates it with a given consensus state +func (k Keeper) GetClient(ctx sdk.Context, id string) (state types.State, found bool) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) + bz := store.Get(KeyClientState(id)) + if bz == nil { + return types.State{}, false + } + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &state) + return state, true +} + +// SetClient creates a new client state and populates it with a given consensus state +func (k Keeper) SetClient(ctx sdk.Context, clientState types.State) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(clientState) + store.Set(KeyClientState(id), bz) +} + // CreateClient creates a new client state and populates it with a given consensus state func (k Keeper) CreateClient(ctx sdk.Context, id string, cs exported.ConsensusState) (types.State, error) { - state, err := k.Query(ctx, id) - if err == nil { - return types.State{}, sdkerrors.Wrap(err, "cannot create client") + state, found := k.GetClient(ctx, id) + if found { + return types.State{}, types.ErrClientExists(k.codespace) } // set the most recent state root and consensus state diff --git a/x/ibc/02-client/types/state.go b/x/ibc/02-client/types/state.go index 1141c3f281b0..aa17a79863a6 100644 --- a/x/ibc/02-client/types/state.go +++ b/x/ibc/02-client/types/state.go @@ -23,16 +23,16 @@ type State struct { ConsensusState state.Value `json:"consensus_state" yaml:"consensus_state"` // Boolean that states if the client is frozen when a misbehaviour proof is // submitted in the event of an equivocation. - Frozen state.Boolean `json:"frozen" yaml:"frozen"` + Frozen bool `json:"frozen" yaml:"frozen"` } // NewState creates a new State instance -func NewState(id string, roots state.Indexer, consensusState state.Value, frozen state.Boolean) State { +func NewState(id string, roots state.Indexer, consensusState state.Value) State { return State{ id: id, Roots: roots, ConsensusState: consensusState, - Frozen: frozen, + Frozen: false, } } From 0e37d757fbdbe9204f093d7100590f8460b3099a Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Mon, 7 Oct 2019 22:45:19 +0200 Subject: [PATCH 117/166] remove store accessors from ICS02 --- x/ibc/02-client/exported/exported.go | 2 +- x/ibc/02-client/keeper/keeper.go | 186 +++++++++++++++++++-------- x/ibc/02-client/types/errors.go | 29 ++++- x/ibc/02-client/types/keys.go | 54 +++++++- x/ibc/02-client/types/state.go | 94 +++++--------- 5 files changed, 243 insertions(+), 122 deletions(-) diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index 54d42f15bd9a..72fbe579200e 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -50,5 +50,5 @@ type ClientType byte // Registered consensus types const ( - Tendermint ClientType = iota + Tendermint ClientType = iota + 1 // 1 ) diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index b23969926763..fa712c1b9165 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -7,12 +7,12 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" - "github.com/cosmos/cosmos-sdk/store/state" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" + ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -40,66 +40,124 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s/%s", ibctypes.ModuleName, types.SubModuleName)) } -// GetClient creates a new client state and populates it with a given consensus state -func (k Keeper) GetClient(ctx sdk.Context, id string) (state types.State, found bool) { +// GetClientState gets a particular client from the +func (k Keeper) GetClientState(ctx sdk.Context, clientID string) (types.ClientState, bool) { store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) - bz := store.Get(KeyClientState(id)) + bz := store.Get(types.KeyClientState(clientID)) if bz == nil { - return types.State{}, false + return types.ClientState{}, false } - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &state) - return state, true + + var clientState types.ClientState + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &clientState) + return clientState, true } -// SetClient creates a new client state and populates it with a given consensus state -func (k Keeper) SetClient(ctx sdk.Context, clientState types.State) { +// SetClient sets a particular Client to the store +func (k Keeper) SetClient(ctx sdk.Context, clientState types.ClientState) { store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) bz := k.cdc.MustMarshalBinaryLengthPrefixed(clientState) - store.Set(KeyClientState(id), bz) + store.Set(types.KeyClientState(clientState.ID()), bz) } -// CreateClient creates a new client state and populates it with a given consensus state -func (k Keeper) CreateClient(ctx sdk.Context, id string, cs exported.ConsensusState) (types.State, error) { - state, found := k.GetClient(ctx, id) - if found { - return types.State{}, types.ErrClientExists(k.codespace) +// GetClientType gets the consensus type for a specific client +func (k Keeper) GetClientType(ctx sdk.Context, clientID string) (exported.ClientType, bool) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) + bz := store.Get(types.KeyClientType(clientID)) + if bz == nil { + return 0, false + } + + return exported.ClientType(bz[0]), true +} + +// SetClientType sets the specific client consensus type to the provable store +func (k Keeper) SetClientType(ctx sdk.Context, clientID string, clientType exported.ClientType) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) + store.Set(types.KeyClientType(clientID), []byte{byte(clientType)}) +} + +// GetConsensusState creates a new client state and populates it with a given consensus state +func (k Keeper) GetConsensusState(ctx sdk.Context, clientID string) (exported.ConsensusState, bool) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) + bz := store.Get(types.KeyClientState(clientID)) + if bz == nil { + return nil, false + } + + var consensusState exported.ConsensusState + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &consensusState) + return consensusState, true +} + +// SetConsensusState sets a ConsensusState to a particular Client +func (k Keeper) SetConsensusState(ctx sdk.Context, clientID string, consensusState exported.ConsensusState) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(consensusState) + store.Set(types.KeyClientState(clientID), bz) +} + +// GetCommitmentRoot gets a commitment Root from a particular height to a client +func (k Keeper) GetCommitmentRoot(ctx sdk.Context, clientID string, height uint64) (ics23.Root, bool) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) + bz := store.Get(types.KeyRoot(clientID, height)) + if bz == nil { + return nil, false } - // set the most recent state root and consensus state - state.Roots.Set(ctx, cs.GetHeight(), cs.GetRoot()) - state.ConsensusState.Set(ctx, cs) - return state, nil + var root ics23.Root + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &root) + return root, true } -// State returns a new client state with a given id -func (k Keeper) ClientState(id string) types.State { - return types.NewState( - id, // client ID - k.mapping.Prefix([]byte(id+"/roots/")).Indexer(state.Dec), // commitment roots - k.mapping.Value([]byte(id)), // consensus state - k.mapping.Value([]byte(id+"/freeze")).Boolean(), // client frozen - ) +// SetCommitmentRoot sets a commitment Root from a particular height to a client +func (k Keeper) SetCommitmentRoot(ctx sdk.Context, clientID string, height uint64, root ics23.Root) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(root) + store.Set(types.KeyRoot(clientID, height), bz) } -// Query returns a client state that matches a given ID -func (k Keeper) Query(ctx sdk.Context, id string) (types.State, error) { - state := k.ClientState(id) - if !state.Exists(ctx) { - return types.State{}, types.ErrClientExists(k.codespace) +// CreateClient creates a new client state and populates it with a given consensus +// state as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#create +func (k Keeper) CreateClient( + ctx sdk.Context, clientID string, + clientType exported.ClientType, consensusState exported.ConsensusState, +) (types.ClientState, error) { + clientState, found := k.GetClientState(ctx, clientID) + if found { + return types.ClientState{}, types.ErrClientExists(k.codespace) + } + + clientType, found = k.GetClientType(ctx, clientID) + if found { + panic(fmt.Sprintf("consensus type is already defined for client %s", clientID)) } - return state, nil + + clientState = k.initialize(ctx, clientID, consensusState) + k.SetCommitmentRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) + k.SetClient(ctx, clientState) + k.SetClientType(ctx, clientID, clientType) + return clientState, nil +} + +// ClientState returns a new client state with a given id as defined in +// https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#example-implementation +func (k Keeper) initialize(ctx sdk.Context, clientID string, consensusState exported.ConsensusState) types.ClientState { + clientState := types.NewClientState(clientID) + k.SetConsensusState(ctx, clientID, consensusState) + return clientState } // CheckMisbehaviourAndUpdateState checks for client misbehaviour and freezes the // client if so. -func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, state types.State, evidence exported.Evidence) error { +func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, clientState types.ClientState, evidence exported.Evidence) error { var err error - switch evidence.H1().Kind() { + switch evidence.H1().ClientType() { case exported.Tendermint: var tmEvidence tendermint.Evidence _, ok := evidence.(tendermint.Evidence) if !ok { - return sdkerrors.Wrap(types.ErrInvalidConsensus(k.codespace), "consensus is not Tendermint") + return sdkerrors.Wrap(types.ErrInvalidClientType(k.codespace), "consensus type is not Tendermint") } err = tendermint.CheckMisbehaviour(tmEvidence) default: @@ -110,40 +168,58 @@ func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, state types.Sta return err } - return k.Freeze(ctx, state) + clientState, err = k.freeze(ctx, clientState) + if err != nil { + return err + } + + k.SetClient(ctx, clientState) + return nil } -// Update updates the consensus state and the state root from a provided header -func (k Keeper) Update(ctx sdk.Context, state types.State, header exported.Header) error { - if !state.Exists(ctx) { - panic("should not update nonexisting client") +// UpdateClient updates the consensus state and the state root from a provided header +func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.Header) error { + clientType, found := k.GetClientType(ctx, clientID) + if !found { + return sdkerrors.Wrap(types.ErrClientTypeNotFound(k.codespace), "cannot update client") + } + + // check that the header consensus matches the client one + if header.ClientType() != clientType { + return sdkerrors.Wrap(types.ErrInvalidConsensus(k.codespace), "cannot update client") + } + + clientState, found := k.GetClientState(ctx, clientID) + if !found { + return sdkerrors.Wrap(types.ErrClientNotFound(k.codespace), "cannot update client") } - if state.Frozen.Get(ctx) { + if clientState.Frozen { return sdkerrors.Wrap(types.ErrClientFrozen(k.codespace), "cannot update client") } - consensusState := state.GetConsensusState(ctx) + consensusState, found := k.GetConsensusState(ctx, clientID) + if !found { + return sdkerrors.Wrap(types.ErrConsensusStateNotFound(k.codespace), "cannot update client") + panic(fmt.Sprintf("consensus state not found for client %s", clientID)) + } + consensusState, err := consensusState.CheckValidityAndUpdateState(header) if err != nil { return sdkerrors.Wrap(err, "cannot update client") } - state.ConsensusState.Set(ctx, consensusState) - state.Roots.Set(ctx, consensusState.GetHeight(), consensusState.GetRoot()) + k.SetConsensusState(ctx, clientID, consensusState) + k.SetCommitmentRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) return nil } -// Freeze updates the state of the client in the event of a misbehaviour -func (k Keeper) Freeze(ctx sdk.Context, state types.State) error { - if !state.Exists(ctx) { - panic("should not freeze nonexisting client") +// freeze updates the state of the client in the event of a misbehaviour +func (k Keeper) freeze(ctx sdk.Context, clientState types.ClientState) (types.ClientState, error) { + if clientState.Frozen { + return types.ClientState{}, sdkerrors.Wrap(types.ErrClientFrozen(k.codespace), "already frozen") } - if state.Frozen.Get(ctx) { - return sdkerrors.Wrap(types.ErrClientFrozen(k.codespace), "already frozen") - } - - state.Frozen.Set(ctx, true) - return nil + clientState.Frozen = true + return clientState, nil } diff --git a/x/ibc/02-client/types/errors.go b/x/ibc/02-client/types/errors.go index 4c77d2dffe0a..2657ff7ea56c 100644 --- a/x/ibc/02-client/types/errors.go +++ b/x/ibc/02-client/types/errors.go @@ -8,11 +8,13 @@ import ( const ( DefaultCodespace sdk.CodespaceType = SubModuleName - CodeClientExists sdk.CodeType = 101 - CodeClientNotFound sdk.CodeType = 102 - CodeClientFrozen sdk.CodeType = 103 - CodeInvalidConsensus sdk.CodeType = 104 - CodeValidatorJailed sdk.CodeType = 104 + CodeClientExists sdk.CodeType = 101 + CodeClientNotFound sdk.CodeType = 102 + CodeClientFrozen sdk.CodeType = 103 + CodeConsensusStateNotFound sdk.CodeType = 104 + CodeInvalidConsensusState sdk.CodeType = 105 + CodeClientTypeNotFound sdk.CodeType = 106 + CodeInvalidClientType sdk.CodeType = 107 ) // ErrClientExists implements sdk.Error @@ -30,7 +32,22 @@ func ErrClientFrozen(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeClientFrozen, "client is frozen due to misbehaviour") } +// ErrConsensusStateNotFound implements sdk.Error +func ErrConsensusStateNotFound(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeConsensusStateNotFound, "consensus state not found") +} + // ErrInvalidConsensus implements sdk.Error func ErrInvalidConsensus(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeInvalidConsensus, "invalid consensus algorithm type") + return sdk.NewError(codespace, CodeInvalidConsensusState, "invalid consensus state") +} + +// ErrClientTypeNotFound implements sdk.Error +func ErrClientTypeNotFound(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeClientTypeNotFound, "client type not found") +} + +// ErrInvalidClientType implements sdk.Error +func ErrInvalidClientType(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidClientType, "client type mismatch") } diff --git a/x/ibc/02-client/types/keys.go b/x/ibc/02-client/types/keys.go index 2e1f26ae3a26..5869852a741f 100644 --- a/x/ibc/02-client/types/keys.go +++ b/x/ibc/02-client/types/keys.go @@ -1,6 +1,58 @@ package types +import ( + "fmt" +) + const ( // SubModuleName defines the IBC client name - SubModuleName = "client" + SubModuleName = "clients" ) + +// The following paths are the keys to the store as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#path-space + +// clientStatePath takes an Identifier and returns a Path under which to store a +// particular client state +func clientStatePath(clientID string) string { + return fmt.Sprintf("clients/%s/state", clientID) +} + +// clientTypePath takes an Identifier and returns Path under which to store the +// type of a particular client. +func clientTypePath(clientID string) string { + return fmt.Sprintf("clients/%s/type", clientID) +} + +// consensusStatePath takes an Identifier and returns a Path under which to +// store the consensus state of a client. +func consensusStatePath(clientID string) string { + return fmt.Sprintf("clients/%s/consensusState", clientID) +} + +// consensusStatePath takes an Identifier and returns a Path under which to +// store the consensus state of a client. +func rootPath(clientID string, height uint64) string { + return fmt.Sprintf("clients/%s/roots/%d", clientID, height) +} + +// KeyClientState returns the store key for a particular client state +func KeyClientState(clientID string) []byte { + return []byte(clientStatePath(clientID)) +} + +// KeyClientType returns the store key for type of a particular client +func KeyClientType(clientID string) []byte { + return []byte(clientTypePath(clientID)) +} + +// KeyConsensusState returns the store key for the consensus state of a particular +// client +func KeyConsensusState(clientID string) []byte { + return []byte(consensusStatePath(clientID)) +} + +// KeyRoot returns the store key for a commitment root of a particular +// client at a given height +func KeyRoot(clientID string, height uint64) []byte { + return []byte(rootPath(clientID, height)) +} diff --git a/x/ibc/02-client/types/state.go b/x/ibc/02-client/types/state.go index aa17a79863a6..873b3d9ab8aa 100644 --- a/x/ibc/02-client/types/state.go +++ b/x/ibc/02-client/types/state.go @@ -1,82 +1,58 @@ package types import ( - "bytes" - - "github.com/cosmos/cosmos-sdk/store/state" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) -// State is a type that represents the state of a client. +// ClientState is a type that represents the state of a client. // Any actor holding the Stage can access on and modify that client information. -type State struct { +type ClientState struct { // Client ID id string // Past state roots required to avoid race conditions between client updates // and proof-carrying transactions as defined in // https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#utilising-past-roots - Roots state.Indexer `json:"roots" yaml:"roots"` + Roots []ics23.Root `json:"roots" yaml:"roots"` // Consensus state bytes - ConsensusState state.Value `json:"consensus_state" yaml:"consensus_state"` + ConsensusState exported.ConsensusState `json:"consensus_state" yaml:"consensus_state"` // Boolean that states if the client is frozen when a misbehaviour proof is // submitted in the event of an equivocation. Frozen bool `json:"frozen" yaml:"frozen"` } -// NewState creates a new State instance -func NewState(id string, roots state.Indexer, consensusState state.Value) State { - return State{ - id: id, - Roots: roots, - ConsensusState: consensusState, - Frozen: false, +// NewClientState creates a new ClientState instance +func NewClientState(id string) ClientState { + return ClientState{ + id: id, + Frozen: false, } } // ID returns the client identifier -func (state State) ID() string { - return state.id -} - -// GetConsensusState returns the consensus state -func (state State) GetConsensusState(ctx sdk.Context) (cs exported.ConsensusState) { - state.ConsensusState.Get(ctx, &cs) - return -} - -// GetRoot returns the commitment root of the client at a given height -func (state State) GetRoot(ctx sdk.Context, height uint64) (root ics23.Root, err error) { - err = state.Roots.GetSafe(ctx, height, &root) - return -} - -// Exists verifies if the client exists or not -func (state State) Exists(ctx sdk.Context) bool { - return state.ConsensusState.Exists(ctx) -} - -func (state State) RootCLI(q state.ABCIQuerier, height uint64) (res ics23.Root, proof merkle.Proof, err error) { - root := state.Roots.Value(height) - tmProof, err := root.Query(q, &res) - proof = merkle.NewProofFromValue(tmProof, state.prefix(), root) - return -} - -func (state State) ConsensusStateCLI(q state.ABCIQuerier) (res exported.ConsensusState, proof merkle.Proof, err error) { - tmproof, err := state.ConsensusState.Query(q, &res) - proof = merkle.NewProofFromValue(tmproof, state.prefix(), state.ConsensusState) - return -} - -func (state State) FrozenCLI(q state.ABCIQuerier) (res bool, proof merkle.Proof, err error) { - res, tmproof, err := state.Frozen.Query(q) - proof = merkle.NewProofFromValue(tmproof, state.prefix(), state.Frozen) - return -} - -func (state State) prefix() []byte { - return bytes.Split(state.ConsensusState.KeyBytes(), []byte(SubModuleName+"/"))[0] -} +func (cs ClientState) ID() string { + return cs.id +} + +// func (state State) RootCLI(q cs.ABCIQuerier, height uint64) (res ics23.Root, proof merkle.Proof, err error) { +// root := cs.Roots.Value(height) +// tmProof, err := root.Query(q, &res) +// proof = merkle.NewProofFromValue(tmProof, cs.prefix(), root) +// return +// } + +// func (state State) ConsensusStateCLI(q cs.ABCIQuerier) (res exported.ConsensusState, proof merkle.Proof, err error) { +// tmproof, err := cs.ConsensusState.Query(q, &res) +// proof = merkle.NewProofFromValue(tmproof, cs.prefix(), cs.ConsensusState) +// return +// } + +// func (state State) FrozenCLI(q cs.ABCIQuerier) (res bool, proof merkle.Proof, err error) { +// res, tmproof, err := cs.Frozen.Query(q) +// proof = merkle.NewProofFromValue(tmproof, cs.prefix(), cs.Frozen) +// return +// } + +// func (state State) prefix() []byte { +// return bytes.Split(cs.ConsensusState.KeyBytes(), []byte(SubModuleName+"/"))[0] +// } From 8dec96f40186e81cec99955bcf43c5c9d0960809 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 8 Oct 2019 13:37:08 +0200 Subject: [PATCH 118/166] refactor queriers, handler and clean keeper --- x/ibc/02-client/client/cli/query.go | 108 +++++++++++---- x/ibc/02-client/exported/exported.go | 33 +++++ x/ibc/02-client/handler.go | 16 +-- x/ibc/02-client/keeper/keeper.go | 107 +++++++++------ x/ibc/02-client/keeper/querier.go | 129 ++++++++---------- x/ibc/02-client/types/errors.go | 6 + x/ibc/02-client/types/keys.go | 33 +++-- x/ibc/02-client/types/msgs.go | 11 +- x/ibc/02-client/types/querier.go | 20 +-- x/ibc/02-client/types/state.go | 34 ----- .../types/tendermint/consensus_state.go | 26 ++-- x/ibc/02-client/types/tendermint/header.go | 8 +- .../types/tendermint/misbehaviour.go | 4 +- 13 files changed, 311 insertions(+), 224 deletions(-) diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index b092f224f31a..8d873f29df9d 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -7,11 +7,17 @@ import ( "github.com/spf13/cobra" + tmtypes "github.com/tendermint/tendermint/types" + cli "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" + ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) // GetQueryCmd returns the query commands for IBC clients @@ -43,7 +49,7 @@ func GetCmdQueryClientState(storeKey string, cdc *codec.Codec) *cobra.Command { fmt.Sprintf(`Query stored client Example: -$ %s query ibc client state [id] +$ %s query ibc client state [client-id] `, version.ClientName), ), Args: cobra.ExactArgs(1), @@ -56,13 +62,17 @@ $ %s query ibc client state [id] return err } - req := abci.RequestQuery{ - Path: "/store/" + storeKey + "/key", - Data: bz, - Prove: true, + res, _, err := cliCtx.QueryWithData(types.ClientStatePath(id), bz) + if err != nil { + return err + } + + var clientState types.ClientState + if err := cdc.UnmarshalJSON(res, &clientState); err != nil { + return err } - return cliCtx.PrintOutput() + return cliCtx.PrintOutput(clientState) }, } } @@ -76,7 +86,7 @@ func GetCmdQueryRoot(storeKey string, cdc *codec.Codec) *cobra.Command { fmt.Sprintf(`Query stored client Example: -$ %s query ibc client root [id] [height] +$ %s query ibc client root [client-id] [height] `, version.ClientName), ), Args: cobra.ExactArgs(2), @@ -93,7 +103,17 @@ $ %s query ibc client root [id] [height] return err } - return cliCtx.PrintOutput() + res, _, err := cliCtx.QueryWithData(types.RootPath(id, height), bz) + if err != nil { + return err + } + + var root ics23.Root + if err := cdc.UnmarshalJSON(res, &root); err != nil { + return err + } + + return cliCtx.PrintOutput(root) }, } } @@ -108,34 +128,30 @@ func GetCmdQueryConsensusState(storeKey string, cdc *codec.Codec) *cobra.Command fmt.Sprintf(`Query consensus state Example: -$ %s query ibc client consensus-state +$ %s query ibc client consensus-state [client-id] `, version.ClientName), ), + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - node, err := cliCtx.GetNode() - if err != nil { - return err - } + id := args[0] - info, err := node.ABCIInfo() + bz, err := cdc.MarshalJSON(types.NewQueryClientStateParams(id)) if err != nil { return err } - height := info.Response.LastBlockHeight - prevheight := height - 1 - commit, err := node.Commit(&height) + res, _, err := cliCtx.QueryWithData(types.ConsensusStatePath(id), bz) if err != nil { return err } - validators, err := node.Validators(&prevheight) - if err != nil { + var consensusState exported.ConsensusState + if err := cdc.UnmarshalJSON(res, &consensusState); err != nil { return err } - return cliCtx.PrintOutput() + return cliCtx.PrintOutput(consensusState) }, } } @@ -154,12 +170,24 @@ $ %s query ibc client path RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - return cliCtx.PrintOutput() + // TODO: + res, _, err := cliCtx.Query("") + if err != nil { + return err + } + + var path merkle.Prefix + if err := cdc.UnmarshalJSON(res, &path); err != nil { + return err + } + + return cliCtx.PrintOutput(path) }, } } // GetCmdQueryHeader defines the command to query the latest header on the chain +// TODO: do we really need this cmd ?? func GetCmdQueryHeader(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "header", @@ -173,7 +201,41 @@ $ %s query ibc client header RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - return cliCtx.PrintOutput() + node, err := cliCtx.GetNode() + if err != nil { + return err + } + + info, err := node.ABCIInfo() + if err != nil { + return err + } + + height := info.Response.LastBlockHeight + prevHeight := height - 1 + + commit, err := node.Commit(&height) + if err != nil { + return err + } + + validators, err := node.Validators(&prevHeight) + if err != nil { + return err + } + + nextValidators, err := node.Validators(&height) + if err != nil { + return err + } + + header := tendermint.Header{ + SignedHeader: commit.SignedHeader, + ValidatorSet: tmtypes.NewValidatorSet(validators.Validators), + NextValidatorSet: tmtypes.NewValidatorSet(nextValidators.Validators), + } + + return cliCtx.PrintOutput(header) }, } } diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index 72fbe579200e..8e97b68b6f6f 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -1,6 +1,8 @@ package exported import ( + "fmt" + ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -45,6 +47,11 @@ type Header interface { GetHeight() uint64 } +// Client types +const ( + ClientTypeTendermint string = "Tendermint" +) + // ClientType defines the type of the consensus algorithm type ClientType byte @@ -52,3 +59,29 @@ type ClientType byte const ( Tendermint ClientType = iota + 1 // 1 ) + +var validClientTypes = map[string]struct{}{ + ClientTypeTendermint: {}, +} + +// RegisterClientType registers a client type. It will panic if the type is +// already registered. +func RegisterClientType(ty string) { + if _, ok := validClientTypes[ty]; ok { + panic(fmt.Sprintf("already registered client type: %s", ty)) + } + + validClientTypes[ty] = struct{}{} +} + +// ClientTypeFromStr returns a byte that corresponds to the registered client +// type. It returns 0 if the type is not found/registered. +func ClientTypeFromStr(ty string) ClientType { + switch ty { + case ClientTypeTendermint: + return Tendermint + + default: + return 0 + } +} diff --git a/x/ibc/02-client/handler.go b/x/ibc/02-client/handler.go index dfc2f0eabb67..6dab4bc41ba9 100644 --- a/x/ibc/02-client/handler.go +++ b/x/ibc/02-client/handler.go @@ -32,7 +32,7 @@ func NewHandler(k keeper.Keeper) sdk.Handler { } func handleMsgCreateClient(ctx sdk.Context, k keeper.Keeper, msg types.MsgCreateClient) sdk.Result { - _, err := k.CreateClient(ctx, msg.ClientID, msg.ConsensusState) + _, err := k.CreateClient(ctx, msg.ClientID, msg.ClientType, msg.ConsensusState) if err != nil { return sdk.ResultFromError(err) } @@ -53,12 +53,7 @@ func handleMsgCreateClient(ctx sdk.Context, k keeper.Keeper, msg types.MsgCreate } func handleMsgUpdateClient(ctx sdk.Context, k keeper.Keeper, msg types.MsgUpdateClient) sdk.Result { - state, err := k.Query(ctx, msg.ClientID) - if err != nil { - return sdk.ResultFromError(err) - } - - err = k.Update(ctx, state, msg.Header) + err := k.UpdateClient(ctx, msg.ClientID, msg.Header) if err != nil { return sdk.ResultFromError(err) } @@ -80,12 +75,7 @@ func handleMsgUpdateClient(ctx sdk.Context, k keeper.Keeper, msg types.MsgUpdate } func handleMsgSubmitMisbehaviour(ctx sdk.Context, k keeper.Keeper, msg types.MsgSubmitMisbehaviour) sdk.Result { - state, err := k.Query(ctx, msg.ClientID) - if err != nil { - return sdk.ResultFromError(err) - } - - err = k.CheckMisbehaviourAndUpdateState(ctx, state, msg.Evidence) + err := k.CheckMisbehaviourAndUpdateState(ctx, msg.ClientID, msg.Evidence) if err != nil { return sdk.ResultFromError(err) } diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index fa712c1b9165..cc6c24ac3702 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -13,6 +13,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -40,6 +41,11 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s/%s", ibctypes.ModuleName, types.SubModuleName)) } +// GetCommitmentPath returns the commitment path of the client +func (k Keeper) GetCommitmentPath() merkle.Prefix { + return merkle.NewPrefix([][]byte{[]byte(k.storeKey.Name())}, k.prefix) +} + // GetClientState gets a particular client from the func (k Keeper) GetClientState(ctx sdk.Context, clientID string) (types.ClientState, bool) { store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) @@ -121,18 +127,23 @@ func (k Keeper) SetCommitmentRoot(ctx sdk.Context, clientID string, height uint6 // state as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#create func (k Keeper) CreateClient( ctx sdk.Context, clientID string, - clientType exported.ClientType, consensusState exported.ConsensusState, + clientTypeStr string, consensusState exported.ConsensusState, ) (types.ClientState, error) { clientState, found := k.GetClientState(ctx, clientID) if found { return types.ClientState{}, types.ErrClientExists(k.codespace) } - clientType, found = k.GetClientType(ctx, clientID) + _, found = k.GetClientType(ctx, clientID) if found { panic(fmt.Sprintf("consensus type is already defined for client %s", clientID)) } + clientType := exported.ClientTypeFromStr(clientTypeStr) + if clientType == 0 { + return types.ClientState{}, types.ErrInvalidClientType(k.codespace) + } + clientState = k.initialize(ctx, clientID, consensusState) k.SetCommitmentRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) k.SetClient(ctx, clientState) @@ -140,43 +151,6 @@ func (k Keeper) CreateClient( return clientState, nil } -// ClientState returns a new client state with a given id as defined in -// https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#example-implementation -func (k Keeper) initialize(ctx sdk.Context, clientID string, consensusState exported.ConsensusState) types.ClientState { - clientState := types.NewClientState(clientID) - k.SetConsensusState(ctx, clientID, consensusState) - return clientState -} - -// CheckMisbehaviourAndUpdateState checks for client misbehaviour and freezes the -// client if so. -func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, clientState types.ClientState, evidence exported.Evidence) error { - var err error - switch evidence.H1().ClientType() { - case exported.Tendermint: - var tmEvidence tendermint.Evidence - _, ok := evidence.(tendermint.Evidence) - if !ok { - return sdkerrors.Wrap(types.ErrInvalidClientType(k.codespace), "consensus type is not Tendermint") - } - err = tendermint.CheckMisbehaviour(tmEvidence) - default: - panic("unregistered consensus type") - } - - if err != nil { - return err - } - - clientState, err = k.freeze(ctx, clientState) - if err != nil { - return err - } - - k.SetClient(ctx, clientState) - return nil -} - // UpdateClient updates the consensus state and the state root from a provided header func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.Header) error { clientType, found := k.GetClientType(ctx, clientID) @@ -201,7 +175,15 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H consensusState, found := k.GetConsensusState(ctx, clientID) if !found { return sdkerrors.Wrap(types.ErrConsensusStateNotFound(k.codespace), "cannot update client") - panic(fmt.Sprintf("consensus state not found for client %s", clientID)) + } + + if header.GetHeight() < consensusState.GetHeight() { + return sdkerrors.Wrap( + sdk.ErrInvalidSequence( + fmt.Sprintf("header height < consensus height (%d < %d)", header.GetHeight(), consensusState.GetHeight()), + ), + "cannot update client", + ) } consensusState, err := consensusState.CheckValidityAndUpdateState(header) @@ -214,6 +196,51 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H return nil } +// CheckMisbehaviourAndUpdateState checks for client misbehaviour and freezes the +// client if so. +func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, clientID string, evidence exported.Evidence) error { + clientState, found := k.GetClientState(ctx, clientID) + if !found { + sdk.ResultFromError(types.ErrClientNotFound(k.codespace)) + } + + err := k.checkMisbehaviour(ctx, evidence) + if err != nil { + return err + } + + clientState, err = k.freeze(ctx, clientState) + if err != nil { + return err + } + + k.SetClient(ctx, clientState) + return nil +} + +// ClientState returns a new client state with a given id as defined in +// https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#example-implementation +func (k Keeper) initialize(ctx sdk.Context, clientID string, consensusState exported.ConsensusState) types.ClientState { + clientState := types.NewClientState(clientID) + k.SetConsensusState(ctx, clientID, consensusState) + return clientState +} + +func (k Keeper) checkMisbehaviour(ctx sdk.Context, evidence exported.Evidence) error { + switch evidence.H1().ClientType() { + case exported.Tendermint: + var tmEvidence tendermint.Evidence + _, ok := evidence.(tendermint.Evidence) + if !ok { + return sdkerrors.Wrap(types.ErrInvalidClientType(k.codespace), "consensus type is not Tendermint") + } + // TODO: pass past consensus states + return tendermint.CheckMisbehaviour(tmEvidence) + default: + panic("unregistered consensus type") + } +} + // freeze updates the state of the client in the event of a misbehaviour func (k Keeper) freeze(ctx sdk.Context, clientState types.ClientState) (types.ClientState, error) { if clientState.Frozen { diff --git a/x/ibc/02-client/keeper/querier.go b/x/ibc/02-client/keeper/querier.go index 260cfc63bc7e..85ea53be9e4b 100644 --- a/x/ibc/02-client/keeper/querier.go +++ b/x/ibc/02-client/keeper/querier.go @@ -5,27 +5,26 @@ import ( abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" ) +// TODO: return proof + // NewQuerier creates a querier for the IBC client func NewQuerier(k Keeper) sdk.Querier { return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { switch path[0] { case types.QueryClientState: - return queryClientState(ctx, req, k) + return queryClientState(ctx, req, k) // TODO: return proof case types.QueryConsensusState: - return queryConsensusState(ctx) + return queryConsensusState(ctx, req, k) // TODO: return proof case types.QueryCommitmentPath: - return queryCommitmentPath(ctx, req, k) + return queryCommitmentPath(k) case types.QueryCommitmentRoot: - return queryCommitmentRoot(ctx, req, k) - case types.QueryHeader: - return queryHeader(ctx, req, k) + return queryCommitmentRoot(ctx, req, k) // TODO: return proof + // case types.QueryHeader: + // return queryHeader(ctx, req, k) default: return nil, sdk.ErrUnknownRequest("unknown IBC client query endpoint") } @@ -40,95 +39,81 @@ func queryClientState(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) } + // NOTE: clientID won't be exported as it's declared as private + // TODO: should we create a custom ExportedClientState to make it public ? + clientState, found := k.GetClientState(ctx, params.ClientID) + if !found { + return nil, types.ErrClientTypeNotFound(k.codespace) + } - // mapp := mapping(cdc, storeKey, version.Version) - // state, _, err := k.State(id).ConsensusStateCLI(q) - // if err != nil { - // return err - // } + bz, err := types.SubModuleCdc.MarshalJSON(clientState) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } - return res, nil + return bz, nil } -func queryConsensusState(ctx sdk.Context) ([]byte, sdk.Error) { +func queryConsensusState(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params types.QueryClientStateParams + + err := types.SubModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) + } - state := tendermint.ConsensusState{ - ChainID: commit.ChainID, - Height: uint64(commit.Height), - Root: merkle.NewRoot(commit.AppHash), - NextValidatorSet: tmtypes.NewValidatorSet(validators.Validators), + consensusState, found := k.GetConsensusState(ctx, params.ClientID) + if !found { + return nil, types.ErrConsensusStateNotFound(k.codespace) } - return res, nil + bz, err := types.SubModuleCdc.MarshalJSON(consensusState) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + + return bz, nil } -func queryCommitmentPath(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { +func queryCommitmentPath(k Keeper) ([]byte, sdk.Error) { + path := k.GetCommitmentPath() - path := merkle.NewPrefix([][]byte{[]byte(k.mapping.storeName)}, k.mapping.PrefixBytes()) + bz, err := types.SubModuleCdc.MarshalJSON(path) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } - return res, nil + return bz, nil } func queryCommitmentRoot(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { - var params types.QueryCommitmentRoot + var params types.QueryCommitmentRootParams err := types.SubModuleCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) } - root, _, err := k.ClientState(params.ID).RootCLI(q, params.Height) - if err != nil { - return nil, err + root, found := k.GetCommitmentRoot(ctx, params.ClientID, params.Height) + if !found { + return nil, types.ErrRootNotFound(k.codespace) } - res, err := codec.MarshalJSONIndent(types.SubModuleCdc, root) + bz, err := types.SubModuleCdc.MarshalJSON(root) if err != nil { return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) } - return res, nil + return bz, nil } -func queryHeader(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { - // node, err := ctx.GetNode() - // if err != nil { - // return err - // } - - // info, err := node.ABCIInfo() - // if err != nil { - // return err - // } - - // height := info.Response.LastBlockHeight - // prevheight := height - 1 - - // commit, err := node.Commit(&height) - // if err != nil { - // return err - // } - - // validators, err := node.Validators(&prevheight) - // if err != nil { - // return err - // } - - // nextValidators, err := node.Validators(&height) - // if err != nil { - // return err - // } - - // header := tendermint.Header{ - // SignedHeader: commit.SignedHeader, - // ValidatorSet: tmtypes.NewValidatorSet(validators.Validators), - // NextValidatorSet: tmtypes.NewValidatorSet(nextValidators.Validators), - // } - - res, err := codec.MarshalJSONIndent(types.SubModuleCdc, header) - if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to marshal result to JSON", err.Error())) - } +// TODO: this is implented directly on the client +// func queryHeader(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { - return res, nil -} +// res, err := codec.MarshalJSONIndent(types.SubModuleCdc, header) +// if err != nil { +// return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to marshal result to JSON", err.Error())) +// } + +// return res, nil +// } diff --git a/x/ibc/02-client/types/errors.go b/x/ibc/02-client/types/errors.go index 2657ff7ea56c..90707e84d815 100644 --- a/x/ibc/02-client/types/errors.go +++ b/x/ibc/02-client/types/errors.go @@ -15,6 +15,7 @@ const ( CodeInvalidConsensusState sdk.CodeType = 105 CodeClientTypeNotFound sdk.CodeType = 106 CodeInvalidClientType sdk.CodeType = 107 + CodeRootNotFound sdk.CodeType = 108 ) // ErrClientExists implements sdk.Error @@ -51,3 +52,8 @@ func ErrClientTypeNotFound(codespace sdk.CodespaceType) sdk.Error { func ErrInvalidClientType(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidClientType, "client type mismatch") } + +// ErrRootNotFound implements sdk.Error +func ErrRootNotFound(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeRootNotFound, "commitment root not found") +} diff --git a/x/ibc/02-client/types/keys.go b/x/ibc/02-client/types/keys.go index 5869852a741f..edc91fe2c4a3 100644 --- a/x/ibc/02-client/types/keys.go +++ b/x/ibc/02-client/types/keys.go @@ -7,52 +7,61 @@ import ( const ( // SubModuleName defines the IBC client name SubModuleName = "clients" + + // StoreKey is the store key string for IBC client + StoreKey = SubModuleName + + // RouterKey is the message route for IBC client + RouterKey = SubModuleName + + // QuerierRoute is the querier route for IBC client + QuerierRoute = SubModuleName ) // The following paths are the keys to the store as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#path-space -// clientStatePath takes an Identifier and returns a Path under which to store a +// ClientStatePath takes an Identifier and returns a Path under which to store a // particular client state -func clientStatePath(clientID string) string { +func ClientStatePath(clientID string) string { return fmt.Sprintf("clients/%s/state", clientID) } -// clientTypePath takes an Identifier and returns Path under which to store the +// ClientTypePath takes an Identifier and returns Path under which to store the // type of a particular client. -func clientTypePath(clientID string) string { +func ClientTypePath(clientID string) string { return fmt.Sprintf("clients/%s/type", clientID) } -// consensusStatePath takes an Identifier and returns a Path under which to +// ConsensusStatePath takes an Identifier and returns a Path under which to // store the consensus state of a client. -func consensusStatePath(clientID string) string { +func ConsensusStatePath(clientID string) string { return fmt.Sprintf("clients/%s/consensusState", clientID) } -// consensusStatePath takes an Identifier and returns a Path under which to +// RootPath takes an Identifier and returns a Path under which to // store the consensus state of a client. -func rootPath(clientID string, height uint64) string { +func RootPath(clientID string, height uint64) string { return fmt.Sprintf("clients/%s/roots/%d", clientID, height) } // KeyClientState returns the store key for a particular client state func KeyClientState(clientID string) []byte { - return []byte(clientStatePath(clientID)) + return []byte(ClientStatePath(clientID)) } // KeyClientType returns the store key for type of a particular client func KeyClientType(clientID string) []byte { - return []byte(clientTypePath(clientID)) + return []byte(ClientTypePath(clientID)) } // KeyConsensusState returns the store key for the consensus state of a particular // client func KeyConsensusState(clientID string) []byte { - return []byte(consensusStatePath(clientID)) + return []byte(ConsensusStatePath(clientID)) } // KeyRoot returns the store key for a commitment root of a particular // client at a given height func KeyRoot(clientID string, height uint64) []byte { - return []byte(rootPath(clientID, height)) + return []byte(RootPath(clientID, height)) } diff --git a/x/ibc/02-client/types/msgs.go b/x/ibc/02-client/types/msgs.go index 5f9c7286b153..17e1848cd577 100644 --- a/x/ibc/02-client/types/msgs.go +++ b/x/ibc/02-client/types/msgs.go @@ -10,15 +10,17 @@ var _ sdk.Msg = MsgCreateClient{} // MsgCreateClient defines a message to create an IBC client type MsgCreateClient struct { - ClientID string `json:"id" yaml:"id"` + ClientID string `json:"client_id" yaml:"client_id"` + ClientType string `json:"client_type" yaml:"client_type"` ConsensusState exported.ConsensusState `json:"consensus_state" yaml:"consensus_address"` Signer sdk.AccAddress `json:"address" yaml:"address"` } // NewMsgCreateClient creates a new MsgCreateClient instance -func NewMsgCreateClient(id string, consensusState exported.ConsensusState, signer sdk.AccAddress) MsgCreateClient { +func NewMsgCreateClient(id, clientType string, consensusState exported.ConsensusState, signer sdk.AccAddress) MsgCreateClient { return MsgCreateClient{ ClientID: id, + ClientType: clientType, ConsensusState: consensusState, Signer: signer, } @@ -39,6 +41,7 @@ func (msg MsgCreateClient) ValidateBasic() sdk.Error { if msg.Signer.Empty() { return sdk.ErrInvalidAddress("empty address") } + // TODO: validate client type and ID return nil } @@ -56,7 +59,7 @@ var _ sdk.Msg = MsgUpdateClient{} // MsgUpdateClient defines a message to update an IBC client type MsgUpdateClient struct { - ClientID string `json:"id" yaml:"id"` + ClientID string `json:"client_id" yaml:"client_id"` Header exported.Header `json:"header" yaml:"header"` Signer sdk.AccAddress `json:"address" yaml:"address"` } @@ -85,6 +88,7 @@ func (msg MsgUpdateClient) ValidateBasic() sdk.Error { if msg.Signer.Empty() { return sdk.ErrInvalidAddress("empty address") } + // TODO: validate client ID return nil } @@ -129,6 +133,7 @@ func (msg MsgSubmitMisbehaviour) ValidateBasic() sdk.Error { if msg.Signer.Empty() { return sdk.ErrInvalidAddress("empty address") } + // TODO: validate client ID return nil } diff --git a/x/ibc/02-client/types/querier.go b/x/ibc/02-client/types/querier.go index 3e10883481c4..17c41a01b40a 100644 --- a/x/ibc/02-client/types/querier.go +++ b/x/ibc/02-client/types/querier.go @@ -5,34 +5,34 @@ const ( QueryClientState = "clientState" QueryConsensusState = "consensusState" QueryCommitmentPath = "commitmentPath" - QueryCommitmentRoot = "commitmentRoot" - QueryHeader = "header" + QueryCommitmentRoot = "roots" ) // QueryClientStateParams defines the params for the following queries: -// - 'custom/ibc/client/clientState' +// - 'custom/ibc/clients//clientState' +// - 'custom/ibc/clients//consensusState' type QueryClientStateParams struct { - ID string + ClientID string } // NewQueryClientStateParams creates a new QueryClientStateParams instance func NewQueryClientStateParams(id string) QueryClientStateParams { return QueryClientStateParams{ - ID: id, + ClientID: id, } } // QueryCommitmentRootParams defines the params for the following queries: -// - 'custom/ibc/client/commitmentRoot' +// - 'custom/ibc/clients//roots/' type QueryCommitmentRootParams struct { - ID string - Height uint64 + ClientID string + Height uint64 } // NewQueryCommitmentRootParams creates a new QueryCommitmentRootParams instance func NewQueryCommitmentRootParams(id string, height uint64) QueryCommitmentRootParams { return QueryCommitmentRootParams{ - ID: id, - Height: height, + ClientID: id, + Height: height, } } diff --git a/x/ibc/02-client/types/state.go b/x/ibc/02-client/types/state.go index 873b3d9ab8aa..d900908b0018 100644 --- a/x/ibc/02-client/types/state.go +++ b/x/ibc/02-client/types/state.go @@ -1,21 +1,10 @@ package types -import ( - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" -) - // ClientState is a type that represents the state of a client. // Any actor holding the Stage can access on and modify that client information. type ClientState struct { // Client ID id string - // Past state roots required to avoid race conditions between client updates - // and proof-carrying transactions as defined in - // https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#utilising-past-roots - Roots []ics23.Root `json:"roots" yaml:"roots"` - // Consensus state bytes - ConsensusState exported.ConsensusState `json:"consensus_state" yaml:"consensus_state"` // Boolean that states if the client is frozen when a misbehaviour proof is // submitted in the event of an equivocation. Frozen bool `json:"frozen" yaml:"frozen"` @@ -33,26 +22,3 @@ func NewClientState(id string) ClientState { func (cs ClientState) ID() string { return cs.id } - -// func (state State) RootCLI(q cs.ABCIQuerier, height uint64) (res ics23.Root, proof merkle.Proof, err error) { -// root := cs.Roots.Value(height) -// tmProof, err := root.Query(q, &res) -// proof = merkle.NewProofFromValue(tmProof, cs.prefix(), root) -// return -// } - -// func (state State) ConsensusStateCLI(q cs.ABCIQuerier) (res exported.ConsensusState, proof merkle.Proof, err error) { -// tmproof, err := cs.ConsensusState.Query(q, &res) -// proof = merkle.NewProofFromValue(tmproof, cs.prefix(), cs.ConsensusState) -// return -// } - -// func (state State) FrozenCLI(q cs.ABCIQuerier) (res bool, proof merkle.Proof, err error) { -// res, tmproof, err := cs.Frozen.Query(q) -// proof = merkle.NewProofFromValue(tmproof, cs.prefix(), cs.Frozen) -// return -// } - -// func (state State) prefix() []byte { -// return bytes.Split(cs.ConsensusState.KeyBytes(), []byte(SubModuleName+"/"))[0] -// } diff --git a/x/ibc/02-client/types/tendermint/consensus_state.go b/x/ibc/02-client/types/tendermint/consensus_state.go index 5ebb973719bc..dd9b2413a422 100644 --- a/x/ibc/02-client/types/tendermint/consensus_state.go +++ b/x/ibc/02-client/types/tendermint/consensus_state.go @@ -32,13 +32,13 @@ func (cs ConsensusState) GetHeight() uint64 { return cs.Height } -// GetRoot returns the commitment Rootgit +// GetRoot returns the commitment Root for the specific func (cs ConsensusState) GetRoot() ics23.Root { return cs.Root } // CheckValidityAndUpdateState checks if the provided header is valid and updates -// the consensus state if appropiate +// the consensus state if appropriate func (cs ConsensusState) CheckValidityAndUpdateState(header exported.Header) (exported.ConsensusState, error) { tmHeader, ok := header.(Header) if !ok { @@ -53,23 +53,29 @@ func (cs ConsensusState) CheckValidityAndUpdateState(header exported.Header) (ex } // checkValidity checks if the Tendermint header is valid +// +// CONTRACT: assumes header.Height > consensusState.Height func (cs ConsensusState) checkValidity(header Header) error { - // TODO: shouldn't we check that header.Height > cs.Height? + // check if the hash from the consensus set and header + // matches nextHash := cs.NextValidatorSet.Hash() if cs.Height == uint64(header.Height-1) && - !bytes.Equal(header.ValidatorsHash, nextHash) { - return lerr.ErrUnexpectedValidators(header.ValidatorsHash, nextHash) + !bytes.Equal(nextHash, header.ValidatorsHash) { + return lerr.ErrUnexpectedValidators(nextHash, header.ValidatorsHash) } + // validate the next validator set hash from the header nextHash = header.NextValidatorSet.Hash() if !bytes.Equal(header.NextValidatorsHash, nextHash) { return lerr.ErrUnexpectedValidators(header.NextValidatorsHash, nextHash) } + // basic consistency check if err := header.ValidateBasic(cs.ChainID); err != nil { return err } + // abortTransactionUnless(consensusState.publicKey.verify(header.signature)) return cs.NextValidatorSet.VerifyFutureCommit( header.ValidatorSet, cs.ChainID, header.Commit.BlockID, header.Height, header.Commit, ) @@ -77,10 +83,8 @@ func (cs ConsensusState) checkValidity(header Header) error { // update the consensus state from a new header func (cs ConsensusState) update(header Header) ConsensusState { - return ConsensusState{ - ChainID: cs.ChainID, - Height: uint64(header.Height), - Root: merkle.NewRoot(header.AppHash), - NextValidatorSet: header.NextValidatorSet, - } + cs.Height = header.GetHeight() + cs.Root = merkle.NewRoot(header.AppHash) + cs.NextValidatorSet = header.NextValidatorSet + return cs } diff --git a/x/ibc/02-client/types/tendermint/header.go b/x/ibc/02-client/types/tendermint/header.go index 408aaed40d28..9d6178cc2d7f 100644 --- a/x/ibc/02-client/types/tendermint/header.go +++ b/x/ibc/02-client/types/tendermint/header.go @@ -17,13 +17,15 @@ type Header struct { } // ClientType defines that the Header is a Tendermint consensus algorithm -func (header Header) ClientType() exported.ClientType { +func (h Header) ClientType() exported.ClientType { return exported.Tendermint } // GetHeight returns the current height -func (header Header) GetHeight() uint64 { - return uint64(header.Height) +// +// NOTE: also referred as `sequence` +func (h Header) GetHeight() uint64 { + return uint64(h.Height) } var _ exported.Evidence = Evidence{} diff --git a/x/ibc/02-client/types/tendermint/misbehaviour.go b/x/ibc/02-client/types/tendermint/misbehaviour.go index 53fcbcca6bd0..15787450e4ca 100644 --- a/x/ibc/02-client/types/tendermint/misbehaviour.go +++ b/x/ibc/02-client/types/tendermint/misbehaviour.go @@ -2,12 +2,10 @@ package tendermint import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" ) // CheckMisbehaviour checks if the evidence provided is a misbehaviour -func CheckMisbehaviour(evidence exported.Evidence) sdk.Error { - +func CheckMisbehaviour(evidence Evidence) sdk.Error { // TODO: check evidence return nil } From 83f4c1bf7818bdc19470332715af07d48d942572 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 8 Oct 2019 16:41:27 +0200 Subject: [PATCH 119/166] logger and tx long description --- x/ibc/02-client/client/cli/query.go | 10 ++++----- x/ibc/02-client/client/cli/tx.go | 26 +++++++++++++++-------- x/ibc/02-client/keeper/keeper.go | 2 ++ x/ibc/02-client/keeper/querier.go | 21 +++--------------- x/ibc/23-commitment/merkle/merkle_test.go | 1 - 5 files changed, 27 insertions(+), 33 deletions(-) diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index 8d873f29df9d..862cfc25619e 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -46,7 +46,7 @@ func GetCmdQueryClientState(storeKey string, cdc *codec.Codec) *cobra.Command { Use: "state", Short: "Query a client state", Long: strings.TrimSpace( - fmt.Sprintf(`Query stored client + fmt.Sprintf(`Query stored client state Example: $ %s query ibc client state [client-id] @@ -83,7 +83,7 @@ func GetCmdQueryRoot(storeKey string, cdc *codec.Codec) *cobra.Command { Use: "root", Short: "Query stored root", Long: strings.TrimSpace( - fmt.Sprintf(`Query stored client + fmt.Sprintf(`Query a stored commitment root at a specific height for a particular client Example: $ %s query ibc client root [client-id] [height] @@ -123,9 +123,9 @@ $ %s query ibc client root [client-id] [height] func GetCmdQueryConsensusState(storeKey string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "consensus-state", - Short: "Query the latest consensus state of the running chain", + Short: "Query the latest consensus state of the client", Long: strings.TrimSpace( - fmt.Sprintf(`Query consensus state + fmt.Sprintf(`Query the consensus state for a particular client Example: $ %s query ibc client consensus-state [client-id] @@ -192,7 +192,7 @@ func GetCmdQueryHeader(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "header", Short: "Query the latest header of the running chain", - Long: strings.TrimSpace(fmt.Sprintf(`Query the latest header + Long: strings.TrimSpace(fmt.Sprintf(`Query the latest Tendermint header Example: $ %s query ibc client header diff --git a/x/ibc/02-client/client/cli/tx.go b/x/ibc/02-client/client/cli/tx.go index 56fa776098f6..a7304166268d 100644 --- a/x/ibc/02-client/client/cli/tx.go +++ b/x/ibc/02-client/client/cli/tx.go @@ -1,6 +1,7 @@ package cli import ( + "fmt" "io/ioutil" "strings" @@ -10,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth/client/utils" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" @@ -50,10 +52,12 @@ func GetCmdCreateClient(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "create", Short: "create new client with a consensus state", - Long: strings.TrimSpace(`create new client with a specified identifier and consensus state: + Long: strings.TrimSpace(fmt.Sprintf(`create new client with a specified identifier and consensus state: -$ tx ibc client create [client-id] [path/to/consensus_state.json] --from node0 --home ../node0/cli --chain-id $CID - `), +Example: +$ %s tx ibc client create [client-id] [path/to/consensus_state.json] --from node0 --home ../node0/cli --chain-id $CID + `, version.ClientName), + ), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) @@ -87,10 +91,12 @@ func GetCmdUpdateClient(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "update", Short: "update existing client with a header", - Long: strings.TrimSpace(`update existing client with a header: + Long: strings.TrimSpace(fmt.Sprintf(`update existing client with a header: -$ tx ibc client create [client-id] [path/to/header.json] --from node0 --home ../node0/cli --chain-id $CID - `), +Example: +$ %s tx ibc client create [client-id] [path/to/header.json] --from node0 --home ../node0/cli --chain-id $CID + `, version.ClientName), + ), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) @@ -125,10 +131,12 @@ func GetCmdSubmitMisbehaviour(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "misbehaviour", Short: "submit a client misbehaviour", - Long: strings.TrimSpace(`submit a client misbehaviour to invalidate to invalidate previous state roots and prevent future updates: + Long: strings.TrimSpace(fmt.Sprintf(`submit a client misbehaviour to invalidate to invalidate previous state roots and prevent future updates: -$ tx ibc client misbehaviour [client-id] [path/to/evidence.json] --from node0 --home ../node0/cli --chain-id $CID - `), +Example: +$ %s tx ibc client misbehaviour [client-id] [path/to/evidence.json] --from node0 --home ../node0/cli --chain-id $CID + `, version.ClientName), + ), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index cc6c24ac3702..37cf07b2db8e 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -193,6 +193,7 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H k.SetConsensusState(ctx, clientID, consensusState) k.SetCommitmentRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) + k.Logger(ctx).Info(fmt.Sprintf("client %s updated at height %d", clientID, consensusState.GetHeight())) return nil } @@ -215,6 +216,7 @@ func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, clientID string } k.SetClient(ctx, clientState) + k.Logger(ctx).Info(fmt.Sprintf("client %s frozen due to misbehaviour", clientID)) return nil } diff --git a/x/ibc/02-client/keeper/querier.go b/x/ibc/02-client/keeper/querier.go index 85ea53be9e4b..3b014d916ac3 100644 --- a/x/ibc/02-client/keeper/querier.go +++ b/x/ibc/02-client/keeper/querier.go @@ -9,22 +9,18 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" ) -// TODO: return proof - // NewQuerier creates a querier for the IBC client func NewQuerier(k Keeper) sdk.Querier { return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { switch path[0] { case types.QueryClientState: - return queryClientState(ctx, req, k) // TODO: return proof + return queryClientState(ctx, req, k) case types.QueryConsensusState: - return queryConsensusState(ctx, req, k) // TODO: return proof + return queryConsensusState(ctx, req, k) case types.QueryCommitmentPath: return queryCommitmentPath(k) case types.QueryCommitmentRoot: - return queryCommitmentRoot(ctx, req, k) // TODO: return proof - // case types.QueryHeader: - // return queryHeader(ctx, req, k) + return queryCommitmentRoot(ctx, req, k) default: return nil, sdk.ErrUnknownRequest("unknown IBC client query endpoint") } @@ -106,14 +102,3 @@ func queryCommitmentRoot(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]by return bz, nil } - -// TODO: this is implented directly on the client -// func queryHeader(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { - -// res, err := codec.MarshalJSONIndent(types.SubModuleCdc, header) -// if err != nil { -// return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to marshal result to JSON", err.Error())) -// } - -// return res, nil -// } diff --git a/x/ibc/23-commitment/merkle/merkle_test.go b/x/ibc/23-commitment/merkle/merkle_test.go index 0c73e7676ce8..0026ac0d0123 100644 --- a/x/ibc/23-commitment/merkle/merkle_test.go +++ b/x/ibc/23-commitment/merkle/merkle_test.go @@ -12,7 +12,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" - "github.com/cosmos/cosmos-sdk/store/state" "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" From 178912f2fd4ae7c977defd2a7dadee1d8ac41d67 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 8 Oct 2019 16:49:42 +0200 Subject: [PATCH 120/166] ineffassign --- x/ibc/02-client/keeper/keeper.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index 37cf07b2db8e..d468e4de653d 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -129,7 +129,7 @@ func (k Keeper) CreateClient( ctx sdk.Context, clientID string, clientTypeStr string, consensusState exported.ConsensusState, ) (types.ClientState, error) { - clientState, found := k.GetClientState(ctx, clientID) + _, found := k.GetClientState(ctx, clientID) if found { return types.ClientState{}, types.ErrClientExists(k.codespace) } @@ -144,7 +144,7 @@ func (k Keeper) CreateClient( return types.ClientState{}, types.ErrInvalidClientType(k.codespace) } - clientState = k.initialize(ctx, clientID, consensusState) + clientState := k.initialize(ctx, clientID, consensusState) k.SetCommitmentRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) k.SetClient(ctx, clientState) k.SetClientType(ctx, clientID, clientType) From 9a225e9fce9c6d60ce13792a2fc93dfc57a78bd2 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Tue, 8 Oct 2019 17:17:04 +0200 Subject: [PATCH 121/166] Apply suggestions from code review Co-Authored-By: Jack Zampolin --- x/ibc/02-client/client/cli/query.go | 6 +++--- x/ibc/02-client/client/cli/tx.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index 862cfc25619e..108d77233d55 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -43,7 +43,7 @@ func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { // a given id as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#query func GetCmdQueryClientState(storeKey string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ - Use: "state", + Use: "state [client-id]", Short: "Query a client state", Long: strings.TrimSpace( fmt.Sprintf(`Query stored client state @@ -80,7 +80,7 @@ $ %s query ibc client state [client-id] // GetCmdQueryRoot defines the command to query func GetCmdQueryRoot(storeKey string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ - Use: "root", + Use: "root [client-id] [height]", Short: "Query stored root", Long: strings.TrimSpace( fmt.Sprintf(`Query a stored commitment root at a specific height for a particular client @@ -122,7 +122,7 @@ $ %s query ibc client root [client-id] [height] // the chain as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#query func GetCmdQueryConsensusState(storeKey string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ - Use: "consensus-state", + Use: "consensus-state [client-id]", Short: "Query the latest consensus state of the client", Long: strings.TrimSpace( fmt.Sprintf(`Query the consensus state for a particular client diff --git a/x/ibc/02-client/client/cli/tx.go b/x/ibc/02-client/client/cli/tx.go index a7304166268d..180a713d5615 100644 --- a/x/ibc/02-client/client/cli/tx.go +++ b/x/ibc/02-client/client/cli/tx.go @@ -50,7 +50,7 @@ func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { // in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#create func GetCmdCreateClient(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: "create", + Use: "create [client-id] [path/to/consensus_state.json]", Short: "create new client with a consensus state", Long: strings.TrimSpace(fmt.Sprintf(`create new client with a specified identifier and consensus state: From fe7da5f48f20a4e84843b273b8a25b2a5f3b572a Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Tue, 8 Oct 2019 17:17:36 +0200 Subject: [PATCH 122/166] Apply suggestions from code review Co-Authored-By: Jack Zampolin --- x/ibc/02-client/client/cli/tx.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/ibc/02-client/client/cli/tx.go b/x/ibc/02-client/client/cli/tx.go index 180a713d5615..c9aacb3747fd 100644 --- a/x/ibc/02-client/client/cli/tx.go +++ b/x/ibc/02-client/client/cli/tx.go @@ -89,7 +89,7 @@ $ %s tx ibc client create [client-id] [path/to/consensus_state.json] --from node // https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#update func GetCmdUpdateClient(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: "update", + Use: "update [client-id] [path/to/header.json]", Short: "update existing client with a header", Long: strings.TrimSpace(fmt.Sprintf(`update existing client with a header: @@ -129,7 +129,7 @@ $ %s tx ibc client create [client-id] [path/to/header.json] --from node0 --home // https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#misbehaviour func GetCmdSubmitMisbehaviour(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: "misbehaviour", + Use: "misbehaviour [client-it] [path/to/evidence.json]", Short: "submit a client misbehaviour", Long: strings.TrimSpace(fmt.Sprintf(`submit a client misbehaviour to invalidate to invalidate previous state roots and prevent future updates: From 21f7d2cb92e830d36e440c9a89c5004ed7fcb094 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 9 Oct 2019 16:06:20 +0200 Subject: [PATCH 123/166] add verification functions --- x/ibc/02-client/keeper/client.go | 107 +++++++++++++++++++++ x/ibc/02-client/keeper/keeper.go | 156 +++++++++++-------------------- 2 files changed, 162 insertions(+), 101 deletions(-) create mode 100644 x/ibc/02-client/keeper/client.go diff --git a/x/ibc/02-client/keeper/client.go b/x/ibc/02-client/keeper/client.go new file mode 100644 index 000000000000..f0645b49b5e4 --- /dev/null +++ b/x/ibc/02-client/keeper/client.go @@ -0,0 +1,107 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" +) + +// CreateClient creates a new client state and populates it with a given consensus +// state as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#create +func (k Keeper) CreateClient( + ctx sdk.Context, clientID string, + clientTypeStr string, consensusState exported.ConsensusState, +) (types.ClientState, error) { + _, found := k.GetClientState(ctx, clientID) + if found { + return types.ClientState{}, types.ErrClientExists(k.codespace) + } + + _, found = k.GetClientType(ctx, clientID) + if found { + panic(fmt.Sprintf("consensus type is already defined for client %s", clientID)) + } + + clientType := exported.ClientTypeFromStr(clientTypeStr) + if clientType == 0 { + return types.ClientState{}, types.ErrInvalidClientType(k.codespace) + } + + clientState := k.initialize(ctx, clientID, consensusState) + k.SetCommitmentRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) + k.SetClientState(ctx, clientState) + k.SetClientType(ctx, clientID, clientType) + return clientState, nil +} + +// UpdateClient updates the consensus state and the state root from a provided header +func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.Header) error { + clientType, found := k.GetClientType(ctx, clientID) + if !found { + return sdkerrors.Wrap(types.ErrClientTypeNotFound(k.codespace), "cannot update client") + } + + // check that the header consensus matches the client one + if header.ClientType() != clientType { + return sdkerrors.Wrap(types.ErrInvalidConsensus(k.codespace), "cannot update client") + } + + clientState, found := k.GetClientState(ctx, clientID) + if !found { + return sdkerrors.Wrap(types.ErrClientNotFound(k.codespace), "cannot update client") + } + + if clientState.Frozen { + return sdkerrors.Wrap(types.ErrClientFrozen(k.codespace), "cannot update client") + } + + consensusState, found := k.GetConsensusState(ctx, clientID) + if !found { + return sdkerrors.Wrap(types.ErrConsensusStateNotFound(k.codespace), "cannot update client") + } + + if header.GetHeight() < consensusState.GetHeight() { + return sdkerrors.Wrap( + sdk.ErrInvalidSequence( + fmt.Sprintf("header height < consensus height (%d < %d)", header.GetHeight(), consensusState.GetHeight()), + ), + "cannot update client", + ) + } + + consensusState, err := consensusState.CheckValidityAndUpdateState(header) + if err != nil { + return sdkerrors.Wrap(err, "cannot update client") + } + + k.SetConsensusState(ctx, clientID, consensusState) + k.SetCommitmentRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) + k.Logger(ctx).Info(fmt.Sprintf("client %s updated at height %d", clientID, consensusState.GetHeight())) + return nil +} + +// CheckMisbehaviourAndUpdateState checks for client misbehaviour and freezes the +// client if so. +func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, clientID string, evidence exported.Evidence) error { + clientState, found := k.GetClientState(ctx, clientID) + if !found { + sdk.ResultFromError(types.ErrClientNotFound(k.codespace)) + } + + err := k.checkMisbehaviour(ctx, evidence) + if err != nil { + return err + } + + clientState, err = k.freeze(ctx, clientState) + if err != nil { + return err + } + + k.SetClientState(ctx, clientState) + k.Logger(ctx).Info(fmt.Sprintf("client %s frozen due to misbehaviour", clientID)) + return nil +} diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index d468e4de653d..d0f83ce19d31 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -46,7 +46,7 @@ func (k Keeper) GetCommitmentPath() merkle.Prefix { return merkle.NewPrefix([][]byte{[]byte(k.storeKey.Name())}, k.prefix) } -// GetClientState gets a particular client from the +// GetClientState gets a particular client from the store func (k Keeper) GetClientState(ctx sdk.Context, clientID string) (types.ClientState, bool) { store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) bz := store.Get(types.KeyClientState(clientID)) @@ -59,8 +59,8 @@ func (k Keeper) GetClientState(ctx sdk.Context, clientID string) (types.ClientSt return clientState, true } -// SetClient sets a particular Client to the store -func (k Keeper) SetClient(ctx sdk.Context, clientState types.ClientState) { +// SetClientState sets a particular Client to the store +func (k Keeper) SetClientState(ctx sdk.Context, clientState types.ClientState) { store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) bz := k.cdc.MustMarshalBinaryLengthPrefixed(clientState) store.Set(types.KeyClientState(clientState.ID()), bz) @@ -96,7 +96,7 @@ func (k Keeper) GetConsensusState(ctx sdk.Context, clientID string) (exported.Co return consensusState, true } -// SetConsensusState sets a ConsensusState to a particular Client +// SetConsensusState sets a ConsensusState to a particular client func (k Keeper) SetConsensusState(ctx sdk.Context, clientID string, consensusState exported.ConsensusState) { store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) bz := k.cdc.MustMarshalBinaryLengthPrefixed(consensusState) @@ -123,103 +123,6 @@ func (k Keeper) SetCommitmentRoot(ctx sdk.Context, clientID string, height uint6 store.Set(types.KeyRoot(clientID, height), bz) } -// CreateClient creates a new client state and populates it with a given consensus -// state as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#create -func (k Keeper) CreateClient( - ctx sdk.Context, clientID string, - clientTypeStr string, consensusState exported.ConsensusState, -) (types.ClientState, error) { - _, found := k.GetClientState(ctx, clientID) - if found { - return types.ClientState{}, types.ErrClientExists(k.codespace) - } - - _, found = k.GetClientType(ctx, clientID) - if found { - panic(fmt.Sprintf("consensus type is already defined for client %s", clientID)) - } - - clientType := exported.ClientTypeFromStr(clientTypeStr) - if clientType == 0 { - return types.ClientState{}, types.ErrInvalidClientType(k.codespace) - } - - clientState := k.initialize(ctx, clientID, consensusState) - k.SetCommitmentRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) - k.SetClient(ctx, clientState) - k.SetClientType(ctx, clientID, clientType) - return clientState, nil -} - -// UpdateClient updates the consensus state and the state root from a provided header -func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.Header) error { - clientType, found := k.GetClientType(ctx, clientID) - if !found { - return sdkerrors.Wrap(types.ErrClientTypeNotFound(k.codespace), "cannot update client") - } - - // check that the header consensus matches the client one - if header.ClientType() != clientType { - return sdkerrors.Wrap(types.ErrInvalidConsensus(k.codespace), "cannot update client") - } - - clientState, found := k.GetClientState(ctx, clientID) - if !found { - return sdkerrors.Wrap(types.ErrClientNotFound(k.codespace), "cannot update client") - } - - if clientState.Frozen { - return sdkerrors.Wrap(types.ErrClientFrozen(k.codespace), "cannot update client") - } - - consensusState, found := k.GetConsensusState(ctx, clientID) - if !found { - return sdkerrors.Wrap(types.ErrConsensusStateNotFound(k.codespace), "cannot update client") - } - - if header.GetHeight() < consensusState.GetHeight() { - return sdkerrors.Wrap( - sdk.ErrInvalidSequence( - fmt.Sprintf("header height < consensus height (%d < %d)", header.GetHeight(), consensusState.GetHeight()), - ), - "cannot update client", - ) - } - - consensusState, err := consensusState.CheckValidityAndUpdateState(header) - if err != nil { - return sdkerrors.Wrap(err, "cannot update client") - } - - k.SetConsensusState(ctx, clientID, consensusState) - k.SetCommitmentRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) - k.Logger(ctx).Info(fmt.Sprintf("client %s updated at height %d", clientID, consensusState.GetHeight())) - return nil -} - -// CheckMisbehaviourAndUpdateState checks for client misbehaviour and freezes the -// client if so. -func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, clientID string, evidence exported.Evidence) error { - clientState, found := k.GetClientState(ctx, clientID) - if !found { - sdk.ResultFromError(types.ErrClientNotFound(k.codespace)) - } - - err := k.checkMisbehaviour(ctx, evidence) - if err != nil { - return err - } - - clientState, err = k.freeze(ctx, clientState) - if err != nil { - return err - } - - k.SetClient(ctx, clientState) - k.Logger(ctx).Info(fmt.Sprintf("client %s frozen due to misbehaviour", clientID)) - return nil -} - // ClientState returns a new client state with a given id as defined in // https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#example-implementation func (k Keeper) initialize(ctx sdk.Context, clientID string, consensusState exported.ConsensusState) types.ClientState { @@ -252,3 +155,54 @@ func (k Keeper) freeze(ctx sdk.Context, clientState types.ClientState) (types.Cl clientState.Frozen = true return clientState, nil } + +// VerifyMembership state membership verification function defined by the client type +func (k Keeper) VerifyMembership( + ctx sdk.Context, + clientState types.ClientState, + height uint64, // sequence + proof ics23.Proof, + path string, + value []byte, +) bool { + if clientState.Frozen { + return false + } + + root, found := k.GetCommitmentRoot(ctx, clientState.ID(), height) + if !found { + return false + } + + prefix := merkle.NewPrefix([][]byte{[]byte(path)}, nil) // TODO: keyprefix? + if err := proof.Verify(root, prefix, value); err != nil { + return false + } + + return true +} + +// VerifyNonMembership state non-membership function defined by the client type +func (k Keeper) VerifyNonMembership( + ctx sdk.Context, + clientState types.ClientState, + height uint64, // sequence + proof ics23.Proof, + path string, +) bool { + if clientState.Frozen { + return false + } + + root, found := k.GetCommitmentRoot(ctx, clientState.ID(), height) + if !found { + return false + } + + prefix := merkle.NewPrefix([][]byte{[]byte(path)}, nil) // TODO: keyprefix? + if err := proof.Verify(root, prefix, nil); err != nil { + return false + } + + return true +} From cf347bf00482031b02fa4dfc5d9cd4c7419f333b Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 9 Oct 2019 22:29:36 +0200 Subject: [PATCH 124/166] ICS02 module.go --- x/ibc/02-client/alias.go | 78 +++++++++++++++++++++++++++++ x/ibc/02-client/client/cli/query.go | 20 ++++---- x/ibc/02-client/client/cli/tx.go | 22 +++----- x/ibc/02-client/handler.go | 34 ++++++------- x/ibc/02-client/module.go | 33 ++++++++++++ 5 files changed, 143 insertions(+), 44 deletions(-) create mode 100644 x/ibc/02-client/alias.go create mode 100644 x/ibc/02-client/module.go diff --git a/x/ibc/02-client/alias.go b/x/ibc/02-client/alias.go new file mode 100644 index 000000000000..f66d835dfa0e --- /dev/null +++ b/x/ibc/02-client/alias.go @@ -0,0 +1,78 @@ +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/ibc/02-client/keeper +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/ibc/02-client/types +package ics02 + +import ( + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/keeper" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" +) + +const ( + DefaultCodespace = types.DefaultCodespace + CodeClientExists = types.CodeClientExists + CodeClientNotFound = types.CodeClientNotFound + CodeClientFrozen = types.CodeClientFrozen + CodeConsensusStateNotFound = types.CodeConsensusStateNotFound + CodeInvalidConsensusState = types.CodeInvalidConsensusState + CodeClientTypeNotFound = types.CodeClientTypeNotFound + CodeInvalidClientType = types.CodeInvalidClientType + CodeRootNotFound = types.CodeRootNotFound + EventTypeCreateClient = types.EventTypeCreateClient + EventTypeUpdateClient = types.EventTypeUpdateClient + EventTypeSubmitMisbehaviour = types.EventTypeSubmitMisbehaviour + AttributeKeyClientID = types.AttributeKeyClientID + SubModuleName = types.SubModuleName + StoreKey = types.StoreKey + RouterKey = types.RouterKey + QuerierRoute = types.QuerierRoute + QueryClientState = types.QueryClientState + QueryConsensusState = types.QueryConsensusState + QueryCommitmentPath = types.QueryCommitmentPath + QueryCommitmentRoot = types.QueryCommitmentRoot +) + +var ( + // functions aliases + NewKeeper = keeper.NewKeeper + NewQuerier = keeper.NewQuerier + RegisterCodec = types.RegisterCodec + ErrClientExists = types.ErrClientExists + ErrClientNotFound = types.ErrClientNotFound + ErrClientFrozen = types.ErrClientFrozen + ErrConsensusStateNotFound = types.ErrConsensusStateNotFound + ErrInvalidConsensus = types.ErrInvalidConsensus + ErrClientTypeNotFound = types.ErrClientTypeNotFound + ErrInvalidClientType = types.ErrInvalidClientType + ErrRootNotFound = types.ErrRootNotFound + ClientStatePath = types.ClientStatePath + ClientTypePath = types.ClientTypePath + ConsensusStatePath = types.ConsensusStatePath + RootPath = types.RootPath + KeyClientState = types.KeyClientState + KeyClientType = types.KeyClientType + KeyConsensusState = types.KeyConsensusState + KeyRoot = types.KeyRoot + NewMsgCreateClient = types.NewMsgCreateClient + NewMsgUpdateClient = types.NewMsgUpdateClient + NewMsgSubmitMisbehaviour = types.NewMsgSubmitMisbehaviour + NewQueryClientStateParams = types.NewQueryClientStateParams + NewQueryCommitmentRootParams = types.NewQueryCommitmentRootParams + NewClientState = types.NewClientState + + // variable aliases + SubModuleCdc = types.SubModuleCdc + AttributeValueCategory = types.AttributeValueCategory +) + +type ( + Keeper = keeper.Keeper + MsgCreateClient = types.MsgCreateClient + MsgUpdateClient = types.MsgUpdateClient + MsgSubmitMisbehaviour = types.MsgSubmitMisbehaviour + QueryClientStateParams = types.QueryClientStateParams + QueryCommitmentRootParams = types.QueryCommitmentRootParams + ClientState = types.ClientState +) diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index 108d77233d55..a3ce9f401418 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -9,7 +9,7 @@ import ( tmtypes "github.com/tendermint/tendermint/types" - cli "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/version" @@ -21,22 +21,22 @@ import ( ) // GetQueryCmd returns the query commands for IBC clients -func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { - ibcQueryCmd := &cobra.Command{ +func GetQueryCmd(queryRouter string, cdc *codec.Codec) *cobra.Command { + ics02ClientQueryCmd := &cobra.Command{ Use: "client", Short: "IBC client query subcommands", DisableFlagParsing: true, SuggestionsMinimumDistance: 2, } - ibcQueryCmd.AddCommand(cli.GetCommands( - GetCmdQueryConsensusState(storeKey, cdc), - GetCmdQueryPath(storeKey, cdc), + ics02ClientQueryCmd.AddCommand(client.GetCommands( + GetCmdQueryConsensusState(queryRouter, cdc), + GetCmdQueryPath(queryRouter, cdc), GetCmdQueryHeader(cdc), - GetCmdQueryClientState(storeKey, cdc), - GetCmdQueryRoot(storeKey, cdc), + GetCmdQueryClientState(queryRouter, cdc), + GetCmdQueryRoot(queryRouter, cdc), )...) - return ibcQueryCmd + return ics02ClientQueryCmd } // GetCmdQueryClientState defines the command to query the state of a client with @@ -170,7 +170,7 @@ $ %s query ibc client path RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - // TODO: + // TODO: get right path res, _, err := cliCtx.Query("") if err != nil { return err diff --git a/x/ibc/02-client/client/cli/tx.go b/x/ibc/02-client/client/cli/tx.go index c9aacb3747fd..ef9b0674882c 100644 --- a/x/ibc/02-client/client/cli/tx.go +++ b/x/ibc/02-client/client/cli/tx.go @@ -7,7 +7,7 @@ import ( "github.com/spf13/cobra" - cli "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -18,32 +18,21 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" ) -// ICS02 Client CLI flags -const ( - FlagStatePath = "state" - FlagClientID = "client-id" - FlagConnectionID = "connection-id" - FlagChannelID = "channel-id" - FlagCounterpartyID = "counterparty-id" - FlagCounterpartyClientID = "counterparty-client-id" - FlagSourceNode = "source-node" -) - // GetTxCmd returns the transaction commands for IBC Clients func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { - ibcTxCmd := &cobra.Command{ + ics02ClientTxCmd := &cobra.Command{ Use: "client", Short: "Client transaction subcommands", DisableFlagParsing: true, SuggestionsMinimumDistance: 2, } - ibcTxCmd.AddCommand(cli.PostCommands( + ics02ClientTxCmd.AddCommand(client.PostCommands( GetCmdCreateClient(cdc), GetCmdUpdateClient(cdc), )...) - return ibcTxCmd + return ics02ClientTxCmd } // GetCmdCreateClient defines the command to create a new IBC Client as defined @@ -74,7 +63,8 @@ $ %s tx ibc client create [client-id] [path/to/consensus_state.json] --from node } msg := types.MsgCreateClient{ - ClientID: args[0], + ClientID: args[0], + ConsensusState: state, Signer: cliCtx.GetFromAddress(), } diff --git a/x/ibc/02-client/handler.go b/x/ibc/02-client/handler.go index 6dab4bc41ba9..f2da176b4771 100644 --- a/x/ibc/02-client/handler.go +++ b/x/ibc/02-client/handler.go @@ -4,24 +4,22 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/keeper" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" ) // NewHandler creates a new Handler instance for IBC client // transactions -func NewHandler(k keeper.Keeper) sdk.Handler { +func NewHandler(k Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { ctx = ctx.WithEventManager(sdk.NewEventManager()) switch msg := msg.(type) { - case types.MsgCreateClient: + case MsgCreateClient: return handleMsgCreateClient(ctx, k, msg) - case types.MsgUpdateClient: + case MsgUpdateClient: return handleMsgUpdateClient(ctx, k, msg) - case types.MsgSubmitMisbehaviour: + case MsgSubmitMisbehaviour: return handleMsgSubmitMisbehaviour(ctx, k, msg) default: @@ -31,7 +29,7 @@ func NewHandler(k keeper.Keeper) sdk.Handler { } } -func handleMsgCreateClient(ctx sdk.Context, k keeper.Keeper, msg types.MsgCreateClient) sdk.Result { +func handleMsgCreateClient(ctx sdk.Context, k Keeper, msg MsgCreateClient) sdk.Result { _, err := k.CreateClient(ctx, msg.ClientID, msg.ClientType, msg.ConsensusState) if err != nil { return sdk.ResultFromError(err) @@ -39,12 +37,12 @@ func handleMsgCreateClient(ctx sdk.Context, k keeper.Keeper, msg types.MsgCreate ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( - types.EventTypeCreateClient, - sdk.NewAttribute(types.AttributeKeyClientID, msg.ClientID), + EventTypeCreateClient, + sdk.NewAttribute(AttributeKeyClientID, msg.ClientID), ), sdk.NewEvent( sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyModule, AttributeValueCategory), sdk.NewAttribute(sdk.AttributeKeySender, msg.Signer.String()), ), }) @@ -52,7 +50,7 @@ func handleMsgCreateClient(ctx sdk.Context, k keeper.Keeper, msg types.MsgCreate return sdk.Result{Events: ctx.EventManager().Events()} } -func handleMsgUpdateClient(ctx sdk.Context, k keeper.Keeper, msg types.MsgUpdateClient) sdk.Result { +func handleMsgUpdateClient(ctx sdk.Context, k Keeper, msg MsgUpdateClient) sdk.Result { err := k.UpdateClient(ctx, msg.ClientID, msg.Header) if err != nil { return sdk.ResultFromError(err) @@ -60,12 +58,12 @@ func handleMsgUpdateClient(ctx sdk.Context, k keeper.Keeper, msg types.MsgUpdate ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( - types.EventTypeUpdateClient, - sdk.NewAttribute(types.AttributeKeyClientID, msg.ClientID), + EventTypeUpdateClient, + sdk.NewAttribute(AttributeKeyClientID, msg.ClientID), ), sdk.NewEvent( sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyModule, AttributeValueCategory), sdk.NewAttribute(sdk.AttributeKeySender, msg.Signer.String()), ), }) @@ -74,7 +72,7 @@ func handleMsgUpdateClient(ctx sdk.Context, k keeper.Keeper, msg types.MsgUpdate return sdk.Result{Events: ctx.EventManager().Events()} } -func handleMsgSubmitMisbehaviour(ctx sdk.Context, k keeper.Keeper, msg types.MsgSubmitMisbehaviour) sdk.Result { +func handleMsgSubmitMisbehaviour(ctx sdk.Context, k Keeper, msg MsgSubmitMisbehaviour) sdk.Result { err := k.CheckMisbehaviourAndUpdateState(ctx, msg.ClientID, msg.Evidence) if err != nil { return sdk.ResultFromError(err) @@ -82,12 +80,12 @@ func handleMsgSubmitMisbehaviour(ctx sdk.Context, k keeper.Keeper, msg types.Msg ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( - types.EventTypeSubmitMisbehaviour, - sdk.NewAttribute(types.AttributeKeyClientID, msg.ClientID), + EventTypeSubmitMisbehaviour, + sdk.NewAttribute(AttributeKeyClientID, msg.ClientID), ), sdk.NewEvent( sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyModule, AttributeValueCategory), sdk.NewAttribute(sdk.AttributeKeySender, msg.Signer.String()), ), }) diff --git a/x/ibc/02-client/module.go b/x/ibc/02-client/module.go new file mode 100644 index 000000000000..c839e262a101 --- /dev/null +++ b/x/ibc/02-client/module.go @@ -0,0 +1,33 @@ +package ics02 + +import ( + "fmt" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/client/cli" +) + +// Name returns the staking module's name +func Name() string { + return SubModuleName +} + +// RegisterRESTRoutes registers the REST routes for the staking module. +func RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { + // TODO: + // rest.RegisterRoutes(ctx, rtr) +} + +// GetTxCmd returns the root tx command for the staking module. +func GetTxCmd(cdc *codec.Codec, storeKey string) *cobra.Command { + return cli.GetTxCmd(fmt.Sprintf("%s/%s", storeKey, SubModuleName), cdc) +} + +// GetQueryCmd returns no root query command for the staking module. +func GetQueryCmd(cdc *codec.Codec, queryRoute string) *cobra.Command { + return cli.GetQueryCmd(fmt.Sprintf("%s/%s", queryRoute, SubModuleName), cdc) +} From 5514e9e3cd8e96af7349c96ddc6172554bae3021 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 9 Oct 2019 22:30:40 +0200 Subject: [PATCH 125/166] top level x/ibc structure --- x/ibc/alias.go | 27 ++++++++ x/ibc/client/cli/cli.go | 43 +++++++++++++ x/ibc/handler.go | 13 ++++ x/ibc/keeper/keeper.go | 19 ++++++ x/ibc/module.go | 133 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 235 insertions(+) create mode 100644 x/ibc/alias.go create mode 100644 x/ibc/client/cli/cli.go create mode 100644 x/ibc/handler.go create mode 100644 x/ibc/keeper/keeper.go create mode 100644 x/ibc/module.go diff --git a/x/ibc/alias.go b/x/ibc/alias.go new file mode 100644 index 000000000000..34587f7580e8 --- /dev/null +++ b/x/ibc/alias.go @@ -0,0 +1,27 @@ +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/ibc/keeper +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/ibc/types +package ibc + +import ( + "github.com/cosmos/cosmos-sdk/x/ibc/keeper" + "github.com/cosmos/cosmos-sdk/x/ibc/types" +) + +const ( + ModuleName = types.ModuleName + StoreKey = types.StoreKey + QuerierRoute = types.QuerierRoute + RouterKey = types.RouterKey +) + +var ( + // functions aliases + NewKeeper = keeper.NewKeeper +) + +type ( + Keeper = keeper.Keeper +) diff --git a/x/ibc/client/cli/cli.go b/x/ibc/client/cli/cli.go new file mode 100644 index 000000000000..640d4b3f580c --- /dev/null +++ b/x/ibc/client/cli/cli.go @@ -0,0 +1,43 @@ +package cli + +import ( + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + ics02 "github.com/cosmos/cosmos-sdk/x/ibc/02-client" + "github.com/cosmos/cosmos-sdk/x/ibc/types" +) + +// GetTxCmd returns the transaction commands for this module +func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { + ibcTxCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "IBC transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + ibcTxCmd.AddCommand( + ics02.GetTxCmd(cdc, storeKey), + ) + return ibcTxCmd +} + +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { + // Group ibc queries under a subcommand + ibcQueryCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the IBC module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + ibcQueryCmd.AddCommand( + ics02.GetQueryCmd(cdc, queryRoute), + ) + return ibcQueryCmd +} diff --git a/x/ibc/handler.go b/x/ibc/handler.go new file mode 100644 index 000000000000..5420e956fb4f --- /dev/null +++ b/x/ibc/handler.go @@ -0,0 +1,13 @@ +package ibc + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// NewHandler defines the IBC handler +func NewHandler(k Keeper) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + // TODO: + return sdk.Result{} + } +} diff --git a/x/ibc/keeper/keeper.go b/x/ibc/keeper/keeper.go new file mode 100644 index 000000000000..1dd8abef5596 --- /dev/null +++ b/x/ibc/keeper/keeper.go @@ -0,0 +1,19 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + ics02 "github.com/cosmos/cosmos-sdk/x/ibc/02-client" +) + +// Keeper defines each ICS keeper for IBC +type Keeper struct { + ClientKeeper ics02.Keeper +} + +// NewKeeper creates a new ibc Keeper +func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, codespace sdk.CodespaceType) Keeper { + return Keeper{ + ClientKeeper: ics02.NewKeeper(cdc, key, codespace), + } +} diff --git a/x/ibc/module.go b/x/ibc/module.go new file mode 100644 index 000000000000..950ab8938c14 --- /dev/null +++ b/x/ibc/module.go @@ -0,0 +1,133 @@ +package ibc + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + ics02 "github.com/cosmos/cosmos-sdk/x/ibc/02-client" + ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + "github.com/cosmos/cosmos-sdk/x/ibc/client/cli" + "github.com/cosmos/cosmos-sdk/x/ibc/types" +) + +// TODO: AppModuleSimulation +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// AppModuleBasic defines the basic application module used by the staking module. +type AppModuleBasic struct{} + +var _ module.AppModuleBasic = AppModuleBasic{} + +// Name returns the staking module's name. +func (AppModuleBasic) Name() string { + return types.ModuleName +} + +// RegisterCodec registers the staking module's types for the given codec. +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + ics02.RegisterCodec(cdc) + ics23.RegisterCodec(cdc) +} + +// DefaultGenesis returns default genesis state as raw bytes for the staking +// module. +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + return nil +} + +// ValidateGenesis performs genesis state validation for the staking module. +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + return nil +} + +// RegisterRESTRoutes registers the REST routes for the staking module. +func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { + //noop +} + +// GetTxCmd returns the root tx command for the staking module. +func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetTxCmd(StoreKey, cdc) +} + +// GetQueryCmd returns no root query command for the staking module. +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetQueryCmd(QuerierRoute, cdc) +} + +// AppModule implements an application module for the staking module. +type AppModule struct { + AppModuleBasic + keeper Keeper +} + +// NewAppModule creates a new AppModule object +func NewAppModule(k Keeper) AppModule { + return AppModule{ + keeper: k, + } +} + +// Name returns the staking module's name. +func (AppModule) Name() string { + return ModuleName +} + +// RegisterInvariants registers the staking module invariants. +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + // TODO: +} + +// Route returns the message routing key for the staking module. +func (AppModule) Route() string { + return RouterKey +} + +// NewHandler returns an sdk.Handler for the staking module. +func (am AppModule) NewHandler() sdk.Handler { + return NewHandler(am.keeper) +} + +// QuerierRoute returns the staking module's querier route name. +func (AppModule) QuerierRoute() string { + return QuerierRoute +} + +// NewQuerierHandler returns the staking module sdk.Querier. +func (am AppModule) NewQuerierHandler() sdk.Querier { + // return NewQuerier(am.keeper + return nil +} + +// InitGenesis performs genesis initialization for the staking module. It returns +// no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the exported genesis state as raw bytes for the staking +// module. +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + return nil +} + +// BeginBlock returns the begin blocker for the staking module. +func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { +} + +// EndBlock returns the end blocker for the staking module. It returns no validator +// updates. +func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} From a8ceb4585044cd5d9b81e3095a115f656cf7c833 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Tue, 15 Oct 2019 11:24:41 +0200 Subject: [PATCH 126/166] Update x/ibc/02-client/client/cli/query.go Co-Authored-By: Jack Zampolin --- x/ibc/02-client/client/cli/query.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index a3ce9f401418..f1a0674c8b96 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -95,7 +95,7 @@ $ %s query ibc client root [client-id] [height] id := args[0] height, err := strconv.ParseUint(args[1], 10, 64) if err != nil { - return err + return fmt.Errorf("expected integer height, got: %v\n", args[1]) } bz, err := cdc.MarshalJSON(types.NewQueryCommitmentRootParams(id, height)) From 956eb5cfa44016982018b7054cb9c842ec1da803 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Tue, 15 Oct 2019 11:32:36 +0200 Subject: [PATCH 127/166] Update x/ibc/02-client/types/tendermint/consensus_state.go Co-Authored-By: Jack Zampolin --- x/ibc/02-client/types/tendermint/consensus_state.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/ibc/02-client/types/tendermint/consensus_state.go b/x/ibc/02-client/types/tendermint/consensus_state.go index dd9b2413a422..0a52c598a9ec 100644 --- a/x/ibc/02-client/types/tendermint/consensus_state.go +++ b/x/ibc/02-client/types/tendermint/consensus_state.go @@ -42,7 +42,7 @@ func (cs ConsensusState) GetRoot() ics23.Root { func (cs ConsensusState) CheckValidityAndUpdateState(header exported.Header) (exported.ConsensusState, error) { tmHeader, ok := header.(Header) if !ok { - return nil, errors.New("header is not from a tendermint consensus") + return nil, errors.New("header not a valid tendermint header") } if err := cs.checkValidity(tmHeader); err != nil { From c573d5760bd1353f024a6f91d9c4459321dc4493 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 15 Oct 2019 12:49:23 +0200 Subject: [PATCH 128/166] address some of the review comments --- x/ibc/02-client/alias.go | 49 ++++++++++++++-------------- x/ibc/02-client/client/cli/query.go | 31 ++++++++++++------ x/ibc/02-client/doc.go | 4 +-- x/ibc/02-client/exported/exported.go | 2 +- x/ibc/02-client/handler.go | 37 ++++----------------- x/ibc/02-client/keeper/client.go | 2 +- x/ibc/02-client/module.go | 2 +- x/ibc/02-client/types/events.go | 8 ++--- x/ibc/02-client/types/keys.go | 2 +- x/ibc/02-client/types/querier.go | 10 +++--- x/ibc/alias.go | 3 +- x/ibc/handler.go | 21 ++++++++++-- x/ibc/module.go | 4 +-- 13 files changed, 91 insertions(+), 84 deletions(-) diff --git a/x/ibc/02-client/alias.go b/x/ibc/02-client/alias.go index f66d835dfa0e..a93de15094b7 100644 --- a/x/ibc/02-client/alias.go +++ b/x/ibc/02-client/alias.go @@ -1,9 +1,10 @@ +package client + // nolint // autogenerated code using github.com/rigelrozanski/multitool // aliases generated for the following subdirectories: // ALIASGEN: github.com/cosmos/cosmos-sdk/x/ibc/02-client/keeper // ALIASGEN: github.com/cosmos/cosmos-sdk/x/ibc/02-client/types -package ics02 import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/keeper" @@ -11,27 +12,24 @@ import ( ) const ( - DefaultCodespace = types.DefaultCodespace - CodeClientExists = types.CodeClientExists - CodeClientNotFound = types.CodeClientNotFound - CodeClientFrozen = types.CodeClientFrozen - CodeConsensusStateNotFound = types.CodeConsensusStateNotFound - CodeInvalidConsensusState = types.CodeInvalidConsensusState - CodeClientTypeNotFound = types.CodeClientTypeNotFound - CodeInvalidClientType = types.CodeInvalidClientType - CodeRootNotFound = types.CodeRootNotFound - EventTypeCreateClient = types.EventTypeCreateClient - EventTypeUpdateClient = types.EventTypeUpdateClient - EventTypeSubmitMisbehaviour = types.EventTypeSubmitMisbehaviour - AttributeKeyClientID = types.AttributeKeyClientID - SubModuleName = types.SubModuleName - StoreKey = types.StoreKey - RouterKey = types.RouterKey - QuerierRoute = types.QuerierRoute - QueryClientState = types.QueryClientState - QueryConsensusState = types.QueryConsensusState - QueryCommitmentPath = types.QueryCommitmentPath - QueryCommitmentRoot = types.QueryCommitmentRoot + DefaultCodespace = types.DefaultCodespace + CodeClientExists = types.CodeClientExists + CodeClientNotFound = types.CodeClientNotFound + CodeClientFrozen = types.CodeClientFrozen + CodeConsensusStateNotFound = types.CodeConsensusStateNotFound + CodeInvalidConsensusState = types.CodeInvalidConsensusState + CodeClientTypeNotFound = types.CodeClientTypeNotFound + CodeInvalidClientType = types.CodeInvalidClientType + CodeRootNotFound = types.CodeRootNotFound + AttributeKeyClientID = types.AttributeKeyClientID + SubModuleName = types.SubModuleName + StoreKey = types.StoreKey + RouterKey = types.RouterKey + QuerierRoute = types.QuerierRoute + QueryClientState = types.QueryClientState + QueryConsensusState = types.QueryConsensusState + QueryCommitmentPath = types.QueryCommitmentPath + QueryCommitmentRoot = types.QueryCommitmentRoot ) var ( @@ -63,8 +61,11 @@ var ( NewClientState = types.NewClientState // variable aliases - SubModuleCdc = types.SubModuleCdc - AttributeValueCategory = types.AttributeValueCategory + SubModuleCdc = types.SubModuleCdc + EventTypeCreateClient = types.EventTypeCreateClient + EventTypeUpdateClient = types.EventTypeUpdateClient + EventTypeSubmitMisbehaviour = types.EventTypeSubmitMisbehaviour + AttributeValueCategory = types.AttributeValueCategory ) type ( diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index f1a0674c8b96..fbef4a4e8181 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -1,6 +1,7 @@ package cli import ( + "errors" "fmt" "strconv" "strings" @@ -55,14 +56,17 @@ $ %s query ibc client state [client-id] Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - id := args[0] + clientID := args[0] + if strings.TrimSpace(clientID) == "" { + return errors.New("client ID can't be blank") + } - bz, err := cdc.MarshalJSON(types.NewQueryClientStateParams(id)) + bz, err := cdc.MarshalJSON(types.NewQueryClientStateParams(clientID)) if err != nil { return err } - res, _, err := cliCtx.QueryWithData(types.ClientStatePath(id), bz) + res, _, err := cliCtx.QueryWithData(types.ClientStatePath(clientID), bz) if err != nil { return err } @@ -92,18 +96,22 @@ $ %s query ibc client root [client-id] [height] Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - id := args[0] + clientID := args[0] + if strings.TrimSpace(clientID) == "" { + return errors.New("client ID can't be blank") + } + height, err := strconv.ParseUint(args[1], 10, 64) if err != nil { - return fmt.Errorf("expected integer height, got: %v\n", args[1]) + return fmt.Errorf("expected integer height, got: %v", args[1]) } - bz, err := cdc.MarshalJSON(types.NewQueryCommitmentRootParams(id, height)) + bz, err := cdc.MarshalJSON(types.NewQueryCommitmentRootParams(clientID, height)) if err != nil { return err } - res, _, err := cliCtx.QueryWithData(types.RootPath(id, height), bz) + res, _, err := cliCtx.QueryWithData(types.RootPath(clientID, height), bz) if err != nil { return err } @@ -134,14 +142,17 @@ $ %s query ibc client consensus-state [client-id] Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - id := args[0] + clientID := args[0] + if strings.TrimSpace(clientID) == "" { + return errors.New("client ID can't be blank") + } - bz, err := cdc.MarshalJSON(types.NewQueryClientStateParams(id)) + bz, err := cdc.MarshalJSON(types.NewQueryClientStateParams(clientID)) if err != nil { return err } - res, _, err := cliCtx.QueryWithData(types.ConsensusStatePath(id), bz) + res, _, err := cliCtx.QueryWithData(types.ConsensusStatePath(clientID), bz) if err != nil { return err } diff --git a/x/ibc/02-client/doc.go b/x/ibc/02-client/doc.go index c2ce8f7729e6..061e160d9739 100644 --- a/x/ibc/02-client/doc.go +++ b/x/ibc/02-client/doc.go @@ -1,5 +1,5 @@ /* -Package ics02 implements the ICS 02 - Client Semenatics specification +Package client implements the ICS 02 - Client Semenatics specification https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics. This concrete implementations defines types and method to store and update light clients which tracks on other chain's state. @@ -51,4 +51,4 @@ each corresponds to `spec: Header.{height, proof, state, root}`. `spec: interface ClientState` is implemented by `type State`. */ -package ics02 +package client diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index 8e97b68b6f6f..074dfff0024e 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -49,7 +49,7 @@ type Header interface { // Client types const ( - ClientTypeTendermint string = "Tendermint" + ClientTypeTendermint string = "tendermint" ) // ClientType defines the type of the consensus algorithm diff --git a/x/ibc/02-client/handler.go b/x/ibc/02-client/handler.go index f2da176b4771..b7394edf0e29 100644 --- a/x/ibc/02-client/handler.go +++ b/x/ibc/02-client/handler.go @@ -1,35 +1,11 @@ -package ics02 +package client import ( - "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" ) -// NewHandler creates a new Handler instance for IBC client -// transactions -func NewHandler(k Keeper) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { - ctx = ctx.WithEventManager(sdk.NewEventManager()) - - switch msg := msg.(type) { - case MsgCreateClient: - return handleMsgCreateClient(ctx, k, msg) - - case MsgUpdateClient: - return handleMsgUpdateClient(ctx, k, msg) - - case MsgSubmitMisbehaviour: - return handleMsgSubmitMisbehaviour(ctx, k, msg) - - default: - errMsg := fmt.Sprintf("unrecognized IBC Client message type: %T", msg) - return sdk.ErrUnknownRequest(errMsg).Result() - } - } -} - -func handleMsgCreateClient(ctx sdk.Context, k Keeper, msg MsgCreateClient) sdk.Result { +// HandleMsgCreateClient defines the sdk.Handler for MsgCreateClient +func HandleMsgCreateClient(ctx sdk.Context, k Keeper, msg MsgCreateClient) sdk.Result { _, err := k.CreateClient(ctx, msg.ClientID, msg.ClientType, msg.ConsensusState) if err != nil { return sdk.ResultFromError(err) @@ -50,7 +26,8 @@ func handleMsgCreateClient(ctx sdk.Context, k Keeper, msg MsgCreateClient) sdk.R return sdk.Result{Events: ctx.EventManager().Events()} } -func handleMsgUpdateClient(ctx sdk.Context, k Keeper, msg MsgUpdateClient) sdk.Result { +// HandleMsgUpdateClient defines the sdk.Handler for MsgUpdateClient +func HandleMsgUpdateClient(ctx sdk.Context, k Keeper, msg MsgUpdateClient) sdk.Result { err := k.UpdateClient(ctx, msg.ClientID, msg.Header) if err != nil { return sdk.ResultFromError(err) @@ -68,11 +45,11 @@ func handleMsgUpdateClient(ctx sdk.Context, k Keeper, msg MsgUpdateClient) sdk.R ), }) - // TODO: events return sdk.Result{Events: ctx.EventManager().Events()} } -func handleMsgSubmitMisbehaviour(ctx sdk.Context, k Keeper, msg MsgSubmitMisbehaviour) sdk.Result { +// HandleMsgSubmitMisbehaviour defines the sdk.Handler for MsgSubmitMisbehaviour +func HandleMsgSubmitMisbehaviour(ctx sdk.Context, k Keeper, msg MsgSubmitMisbehaviour) sdk.Result { err := k.CheckMisbehaviourAndUpdateState(ctx, msg.ClientID, msg.Evidence) if err != nil { return sdk.ResultFromError(err) diff --git a/x/ibc/02-client/keeper/client.go b/x/ibc/02-client/keeper/client.go index f0645b49b5e4..df3aaf53d23b 100644 --- a/x/ibc/02-client/keeper/client.go +++ b/x/ibc/02-client/keeper/client.go @@ -79,7 +79,7 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H k.SetConsensusState(ctx, clientID, consensusState) k.SetCommitmentRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) - k.Logger(ctx).Info(fmt.Sprintf("client %s updated at height %d", clientID, consensusState.GetHeight())) + k.Logger(ctx).Info(fmt.Sprintf("client %s updated to height %d", clientID, consensusState.GetHeight())) return nil } diff --git a/x/ibc/02-client/module.go b/x/ibc/02-client/module.go index c839e262a101..a4586961b08a 100644 --- a/x/ibc/02-client/module.go +++ b/x/ibc/02-client/module.go @@ -1,4 +1,4 @@ -package ics02 +package client import ( "fmt" diff --git a/x/ibc/02-client/types/events.go b/x/ibc/02-client/types/events.go index 6258a7e328d9..7e7f9a508cee 100644 --- a/x/ibc/02-client/types/events.go +++ b/x/ibc/02-client/types/events.go @@ -8,14 +8,14 @@ import ( // IBC client events const ( - EventTypeCreateClient = "create_client" - EventTypeUpdateClient = "update_client" - EventTypeSubmitMisbehaviour = "submit_misbehaviour" - AttributeKeyClientID = "client_id" ) // IBC client events vars var ( + EventTypeCreateClient = MsgCreateClient{}.Type() + EventTypeUpdateClient = MsgUpdateClient{}.Type() + EventTypeSubmitMisbehaviour = MsgSubmitMisbehaviour{}.Type() + AttributeValueCategory = fmt.Sprintf("%s_%s", ibctypes.ModuleName, SubModuleName) ) diff --git a/x/ibc/02-client/types/keys.go b/x/ibc/02-client/types/keys.go index edc91fe2c4a3..8d97ca893324 100644 --- a/x/ibc/02-client/types/keys.go +++ b/x/ibc/02-client/types/keys.go @@ -6,7 +6,7 @@ import ( const ( // SubModuleName defines the IBC client name - SubModuleName = "clients" + SubModuleName = "client" // StoreKey is the store key string for IBC client StoreKey = SubModuleName diff --git a/x/ibc/02-client/types/querier.go b/x/ibc/02-client/types/querier.go index 17c41a01b40a..6e58b6c00f57 100644 --- a/x/ibc/02-client/types/querier.go +++ b/x/ibc/02-client/types/querier.go @@ -2,15 +2,15 @@ package types // query routes supported by the IBC client Querier const ( - QueryClientState = "clientState" - QueryConsensusState = "consensusState" - QueryCommitmentPath = "commitmentPath" + QueryClientState = "client_state" + QueryConsensusState = "consensus_state" + QueryCommitmentPath = "commitment_path" QueryCommitmentRoot = "roots" ) // QueryClientStateParams defines the params for the following queries: -// - 'custom/ibc/clients//clientState' -// - 'custom/ibc/clients//consensusState' +// - 'custom/ibc/clients//client_state' +// - 'custom/ibc/clients//consensus_state' type QueryClientStateParams struct { ClientID string } diff --git a/x/ibc/alias.go b/x/ibc/alias.go index 34587f7580e8..d71d90640ab2 100644 --- a/x/ibc/alias.go +++ b/x/ibc/alias.go @@ -1,9 +1,10 @@ +package ibc + // nolint // autogenerated code using github.com/rigelrozanski/multitool // aliases generated for the following subdirectories: // ALIASGEN: github.com/cosmos/cosmos-sdk/x/ibc/keeper // ALIASGEN: github.com/cosmos/cosmos-sdk/x/ibc/types -package ibc import ( "github.com/cosmos/cosmos-sdk/x/ibc/keeper" diff --git a/x/ibc/handler.go b/x/ibc/handler.go index 5420e956fb4f..5dc802b6f063 100644 --- a/x/ibc/handler.go +++ b/x/ibc/handler.go @@ -1,13 +1,30 @@ package ibc import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" ) // NewHandler defines the IBC handler func NewHandler(k Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { - // TODO: - return sdk.Result{} + ctx = ctx.WithEventManager(sdk.NewEventManager()) + + switch msg := msg.(type) { + case client.MsgCreateClient: + return client.HandleMsgCreateClient(ctx, k.ClientKeeper, msg) + + case client.MsgUpdateClient: + return client.HandleMsgUpdateClient(ctx, k.ClientKeeper, msg) + + case client.MsgSubmitMisbehaviour: + return client.HandleMsgSubmitMisbehaviour(ctx, k.ClientKeeper, msg) + + default: + errMsg := fmt.Sprintf("unrecognized IBC Client message type: %T", msg) + return sdk.ErrUnknownRequest(errMsg).Result() + } } } diff --git a/x/ibc/module.go b/x/ibc/module.go index 950ab8938c14..5a56db96877f 100644 --- a/x/ibc/module.go +++ b/x/ibc/module.go @@ -12,7 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - ics02 "github.com/cosmos/cosmos-sdk/x/ibc/02-client" + client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" "github.com/cosmos/cosmos-sdk/x/ibc/client/cli" "github.com/cosmos/cosmos-sdk/x/ibc/types" @@ -36,7 +36,7 @@ func (AppModuleBasic) Name() string { // RegisterCodec registers the staking module's types for the given codec. func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { - ics02.RegisterCodec(cdc) + client.RegisterCodec(cdc) ics23.RegisterCodec(cdc) } From 6af2ce7a0bf8d08217d1be3d620d27637040734f Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 16 Oct 2019 15:53:30 +0200 Subject: [PATCH 129/166] minor UX improvements --- x/ibc/02-client/client/cli/tx.go | 77 +++++++++++++++++----------- x/ibc/02-client/exported/exported.go | 15 +++++- x/ibc/02-client/types/errors.go | 14 ++--- 3 files changed, 67 insertions(+), 39 deletions(-) diff --git a/x/ibc/02-client/client/cli/tx.go b/x/ibc/02-client/client/cli/tx.go index ef9b0674882c..a9efc1162461 100644 --- a/x/ibc/02-client/client/cli/tx.go +++ b/x/ibc/02-client/client/cli/tx.go @@ -3,6 +3,7 @@ package cli import ( "fmt" "io/ioutil" + "os" "strings" "github.com/spf13/cobra" @@ -52,21 +53,27 @@ $ %s tx ibc client create [client-id] [path/to/consensus_state.json] --from node txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) cliCtx := context.NewCLIContext().WithCodec(cdc) - contents, err := ioutil.ReadFile(args[1]) - if err != nil { - return err - } + clientID := args[0] var state exported.ConsensusState - if err := cdc.UnmarshalJSON(contents, &state); err != nil { - return err + if err := cdc.UnmarshalJSON([]byte(args[1]), &state); err != nil { + fmt.Fprintf(os.Stderr, "failed to unmarshall input into struct, checking for file...") + + contents, err := ioutil.ReadFile(args[1]) + if err != nil { + return fmt.Errorf("error opening proof file: %v", err) + } + if err := cdc.UnmarshalJSON(contents, &state); err != nil { + return fmt.Errorf("error unmarshalling consensus state file: %v", err) + } } - msg := types.MsgCreateClient{ - ClientID: args[0], - - ConsensusState: state, - Signer: cliCtx.GetFromAddress(), + msg := types.NewMsgCreateClient( + clientID, exported.ClientTypeToString(state.ClientType()), state, + cliCtx.GetFromAddress(), + ) + if err := msg.ValidateBasic(); err != nil { + return err } return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) @@ -92,20 +99,24 @@ $ %s tx ibc client create [client-id] [path/to/header.json] --from node0 --home txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) cliCtx := context.NewCLIContext().WithCodec(cdc) - bz, err := ioutil.ReadFile(args[1]) - if err != nil { - return err - } + clientID := args[0] var header exported.Header - if err := cdc.UnmarshalJSON(bz, &header); err != nil { - return err + if err := cdc.UnmarshalJSON([]byte(args[1]), &header); err != nil { + fmt.Fprintf(os.Stderr, "failed to unmarshall input into struct, checking for file...") + + contents, err := ioutil.ReadFile(args[1]) + if err != nil { + return fmt.Errorf("error opening proof file: %v", err) + } + if err := cdc.UnmarshalJSON(contents, &header); err != nil { + return fmt.Errorf("error unmarshalling header file: %v", err) + } } - msg := types.MsgUpdateClient{ - ClientID: args[0], - Header: header, - Signer: cliCtx.GetFromAddress(), + msg := types.NewMsgUpdateClient(clientID, header, cliCtx.GetFromAddress()) + if err := msg.ValidateBasic(); err != nil { + return err } return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) @@ -132,20 +143,24 @@ $ %s tx ibc client misbehaviour [client-id] [path/to/evidence.json] --from node0 txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) cliCtx := context.NewCLIContext().WithCodec(cdc) - bz, err := ioutil.ReadFile(args[1]) - if err != nil { - return err - } + clientID := args[0] var evidence exported.Evidence - if err := cdc.UnmarshalJSON(bz, &evidence); err != nil { - return err + if err := cdc.UnmarshalJSON([]byte(args[1]), &evidence); err != nil { + fmt.Fprintf(os.Stderr, "failed to unmarshall input into struct, checking for file...") + + contents, err := ioutil.ReadFile(args[1]) + if err != nil { + return fmt.Errorf("error opening proof file: %v", err) + } + if err := cdc.UnmarshalJSON(contents, &evidence); err != nil { + return fmt.Errorf("error unmarshalling evidence file: %v", err) + } } - msg := types.MsgSubmitMisbehaviour{ - ClientID: args[0], - Evidence: evidence, - Signer: cliCtx.GetFromAddress(), + msg := types.NewMsgSubmitMisbehaviour(clientID, evidence, cliCtx.GetFromAddress()) + if err := msg.ValidateBasic(); err != nil { + return err } return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index 074dfff0024e..ce0f51af84af 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -76,8 +76,8 @@ func RegisterClientType(ty string) { // ClientTypeFromStr returns a byte that corresponds to the registered client // type. It returns 0 if the type is not found/registered. -func ClientTypeFromStr(ty string) ClientType { - switch ty { +func ClientTypeFromStr(clientType string) ClientType { + switch clientType { case ClientTypeTendermint: return Tendermint @@ -85,3 +85,14 @@ func ClientTypeFromStr(ty string) ClientType { return 0 } } + +// ClientTypeToString returns the string representation of a client type +func ClientTypeToString(clientType ClientType) string { + switch clientType { + case Tendermint: + return ClientTypeTendermint + + default: + return "" + } +} diff --git a/x/ibc/02-client/types/errors.go b/x/ibc/02-client/types/errors.go index 90707e84d815..282e697d3bce 100644 --- a/x/ibc/02-client/types/errors.go +++ b/x/ibc/02-client/types/errors.go @@ -1,6 +1,8 @@ package types import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -19,18 +21,18 @@ const ( ) // ErrClientExists implements sdk.Error -func ErrClientExists(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeClientExists, "client already exists") +func ErrClientExists(codespace sdk.CodespaceType, clientID string) sdk.Error { + return sdk.NewError(codespace, CodeClientExists, fmt.Sprintf("client with ID %s already exists", clientID)) } // ErrClientNotFound implements sdk.Error -func ErrClientNotFound(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeClientNotFound, "client not found") +func ErrClientNotFound(codespace sdk.CodespaceType, clientID string) sdk.Error { + return sdk.NewError(codespace, CodeClientNotFound, fmt.Sprintf("client with ID %s not found", clientID)) } // ErrClientFrozen implements sdk.Error -func ErrClientFrozen(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeClientFrozen, "client is frozen due to misbehaviour") +func ErrClientFrozen(codespace sdk.CodespaceType, clientID string) sdk.Error { + return sdk.NewError(codespace, CodeClientFrozen, fmt.Sprintf("client with ID %s is frozen due to misbehaviour", clientID)) } // ErrConsensusStateNotFound implements sdk.Error From 8be3af78e5c3a0d3b5d564d1d75a29e63d3a5e99 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 16 Oct 2019 15:56:22 +0200 Subject: [PATCH 130/166] rename pkg --- x/ibc/client/cli/cli.go | 6 +++--- x/ibc/keeper/keeper.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/x/ibc/client/cli/cli.go b/x/ibc/client/cli/cli.go index 640d4b3f580c..f2fa0fa9030a 100644 --- a/x/ibc/client/cli/cli.go +++ b/x/ibc/client/cli/cli.go @@ -5,7 +5,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" - ics02 "github.com/cosmos/cosmos-sdk/x/ibc/02-client" + ibcclient "github.com/cosmos/cosmos-sdk/x/ibc/02-client" "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -20,7 +20,7 @@ func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { } ibcTxCmd.AddCommand( - ics02.GetTxCmd(cdc, storeKey), + ibcclient.GetTxCmd(cdc, storeKey), ) return ibcTxCmd } @@ -37,7 +37,7 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { } ibcQueryCmd.AddCommand( - ics02.GetQueryCmd(cdc, queryRoute), + ibcclient.GetQueryCmd(cdc, queryRoute), ) return ibcQueryCmd } diff --git a/x/ibc/keeper/keeper.go b/x/ibc/keeper/keeper.go index 1dd8abef5596..fce91fb353bd 100644 --- a/x/ibc/keeper/keeper.go +++ b/x/ibc/keeper/keeper.go @@ -3,17 +3,17 @@ package keeper import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - ics02 "github.com/cosmos/cosmos-sdk/x/ibc/02-client" + client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" ) // Keeper defines each ICS keeper for IBC type Keeper struct { - ClientKeeper ics02.Keeper + ClientKeeper client.Keeper } // NewKeeper creates a new ibc Keeper func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, codespace sdk.CodespaceType) Keeper { return Keeper{ - ClientKeeper: ics02.NewKeeper(cdc, key, codespace), + ClientKeeper: client.NewKeeper(cdc, key, codespace), } } From 53ab47db24733eeaaaf2ee1ca52cda4518d8a2cc Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 16 Oct 2019 16:01:18 +0200 Subject: [PATCH 131/166] fixes --- x/ibc/02-client/keeper/client.go | 8 ++++---- x/ibc/02-client/keeper/keeper.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x/ibc/02-client/keeper/client.go b/x/ibc/02-client/keeper/client.go index df3aaf53d23b..de26aa6c6591 100644 --- a/x/ibc/02-client/keeper/client.go +++ b/x/ibc/02-client/keeper/client.go @@ -17,7 +17,7 @@ func (k Keeper) CreateClient( ) (types.ClientState, error) { _, found := k.GetClientState(ctx, clientID) if found { - return types.ClientState{}, types.ErrClientExists(k.codespace) + return types.ClientState{}, types.ErrClientExists(k.codespace, clientID) } _, found = k.GetClientType(ctx, clientID) @@ -51,11 +51,11 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H clientState, found := k.GetClientState(ctx, clientID) if !found { - return sdkerrors.Wrap(types.ErrClientNotFound(k.codespace), "cannot update client") + return sdkerrors.Wrap(types.ErrClientNotFound(k.codespace, clientID), "cannot update client") } if clientState.Frozen { - return sdkerrors.Wrap(types.ErrClientFrozen(k.codespace), "cannot update client") + return sdkerrors.Wrap(types.ErrClientFrozen(k.codespace, clientID), "cannot update client") } consensusState, found := k.GetConsensusState(ctx, clientID) @@ -88,7 +88,7 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, clientID string, evidence exported.Evidence) error { clientState, found := k.GetClientState(ctx, clientID) if !found { - sdk.ResultFromError(types.ErrClientNotFound(k.codespace)) + sdk.ResultFromError(types.ErrClientNotFound(k.codespace, clientID)) } err := k.checkMisbehaviour(ctx, evidence) diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index d0f83ce19d31..660fdc450f24 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -149,7 +149,7 @@ func (k Keeper) checkMisbehaviour(ctx sdk.Context, evidence exported.Evidence) e // freeze updates the state of the client in the event of a misbehaviour func (k Keeper) freeze(ctx sdk.Context, clientState types.ClientState) (types.ClientState, error) { if clientState.Frozen { - return types.ClientState{}, sdkerrors.Wrap(types.ErrClientFrozen(k.codespace), "already frozen") + return types.ClientState{}, sdkerrors.Wrap(types.ErrClientFrozen(k.codespace, clientState.ID()), "already frozen") } clientState.Frozen = true From 499abb4b1dee900bc6958ac980323ae2ca91b093 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Fri, 18 Oct 2019 11:54:02 +0200 Subject: [PATCH 132/166] refactor ICS23 --- x/ibc/23-commitment/codec.go | 12 -- x/ibc/23-commitment/context.go | 18 --- .../{types.go => exported/exported.go} | 24 +-- x/ibc/23-commitment/keeper/keeper.go | 81 ++++++++++ x/ibc/23-commitment/merkle/merkle.go | 112 ------------- x/ibc/23-commitment/merkle/merkle_test.go | 134 --------------- x/ibc/23-commitment/merkle/utils.go | 34 ---- x/ibc/23-commitment/store.go | 105 ------------ .../23-commitment/{merkle => types}/codec.go | 8 +- x/ibc/23-commitment/types/merkle.go | 153 ++++++++++++++++++ x/ibc/23-commitment/types/state.go | 24 +++ x/ibc/23-commitment/types/utils.go | 8 + 12 files changed, 286 insertions(+), 427 deletions(-) delete mode 100644 x/ibc/23-commitment/codec.go delete mode 100644 x/ibc/23-commitment/context.go rename x/ibc/23-commitment/{types.go => exported/exported.go} (66%) create mode 100644 x/ibc/23-commitment/keeper/keeper.go delete mode 100644 x/ibc/23-commitment/merkle/merkle.go delete mode 100644 x/ibc/23-commitment/merkle/merkle_test.go delete mode 100644 x/ibc/23-commitment/merkle/utils.go delete mode 100644 x/ibc/23-commitment/store.go rename x/ibc/23-commitment/{merkle => types}/codec.go (50%) create mode 100644 x/ibc/23-commitment/types/merkle.go create mode 100644 x/ibc/23-commitment/types/state.go create mode 100644 x/ibc/23-commitment/types/utils.go diff --git a/x/ibc/23-commitment/codec.go b/x/ibc/23-commitment/codec.go deleted file mode 100644 index 7312c5942d85..000000000000 --- a/x/ibc/23-commitment/codec.go +++ /dev/null @@ -1,12 +0,0 @@ -package ics23 - -import ( - "github.com/cosmos/cosmos-sdk/codec" -) - -// RegisterCodec registers types declared in this package -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterInterface((*Root)(nil), nil) - cdc.RegisterInterface((*Prefix)(nil), nil) - cdc.RegisterInterface((*Proof)(nil), nil) -} diff --git a/x/ibc/23-commitment/context.go b/x/ibc/23-commitment/context.go deleted file mode 100644 index af3f300ef9f2..000000000000 --- a/x/ibc/23-commitment/context.go +++ /dev/null @@ -1,18 +0,0 @@ -package ics23 - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// ContextKeyCommitmentKVStore is a singleton type used as the key for the commitment store -type ContextKeyCommitmentKVStore struct{} - -// WithStore returns the context updated with the store -func WithStore(ctx sdk.Context, store Store) sdk.Context { - return ctx.WithValue(ContextKeyCommitmentKVStore{}, store) -} - -// GetStore returns the store from the context -func GetStore(ctx sdk.Context) Store { - return ctx.Value(ContextKeyCommitmentKVStore{}).(Store) -} diff --git a/x/ibc/23-commitment/types.go b/x/ibc/23-commitment/exported/exported.go similarity index 66% rename from x/ibc/23-commitment/types.go rename to x/ibc/23-commitment/exported/exported.go index bba7711bb32f..c63af1b385f2 100644 --- a/x/ibc/23-commitment/types.go +++ b/x/ibc/23-commitment/exported/exported.go @@ -1,4 +1,4 @@ -package ics23 +package exported // ICS 023 Types Implementation // @@ -7,27 +7,29 @@ package ics23 // spec:Path and spec:Value are defined as bytestring -// Root implements spec:CommitmentRoot. +// RootI implements spec:CommitmentRoot. // A root is constructed from a set of key-value pairs, // and the inclusion or non-inclusion of an arbitrary key-value pair // can be proven with the proof. -type Root interface { - CommitmentKind() string +type RootI interface { + CommitmentType() string + Bytes() []byte } -// Prefix implements spec:CommitmentPrefix. +// PrefixI implements spec:CommitmentPrefix. // Prefix is the additional information provided to the verification function. // Prefix represents the common "prefix" that a set of keys shares. -type Prefix interface { - CommitmentKind() string +type PrefixI interface { + CommitmentType() string } -// Proof implements spec:CommitmentProof. +// ProofI implements spec:CommitmentProof. // Proof can prove whether the key-value pair is a part of the Root or not. // Each proof has designated key-value pair it is able to prove. // Proofs includes key but value is provided dynamically at the verification time. -type Proof interface { - CommitmentKind() string +type ProofI interface { + CommitmentType() string GetKey() []byte - Verify(Root, Prefix, []byte) error + VerifyMembership(RootI, PrefixI, []byte) bool + VerifyAbsence(RootI, PrefixI) bool } diff --git a/x/ibc/23-commitment/keeper/keeper.go b/x/ibc/23-commitment/keeper/keeper.go new file mode 100644 index 000000000000..31fa76d5ee38 --- /dev/null +++ b/x/ibc/23-commitment/keeper/keeper.go @@ -0,0 +1,81 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" +) + +// Keeper defines the IBC commitment keeper (i.e the vector commitment manager). +// A vector commitment manager has the ability to add or remove items from the +// commitment state as defined in https://github.com/cosmos/ics/tree/master/spec/ics-023-vector-commitments#definitions. +type Keeper struct { + prefix exported.PrefixI + proofs map[string]exported.ProofI + verified map[string][]byte +} + +// NewKeeper returns a prefixed store given base store and prefix. +func NewKeeper(prefix exported.PrefixI, proofs []exported.ProofI) Keeper { + return Keeper{ + prefix: prefix, + proofs: make(map[string]exported.ProofI), + verified: make(map[string][]byte), + } +} + +// GetRoot returns the application Hash at the curretn block height as a commitment +// root for proof verification. +func (k Keeper) GetRoot(ctx sdk.Context) exported.RootI { + return types.NewRoot(ctx.BlockHeader().AppHash) +} + +// // NewStore constructs a new Store with the root, path, and proofs. +// // The result store will be stored in the context and used by the +// // commitment.Value types. +// func NewStore(root RootI, prefix PrefixI, proofs []ProofI) (StoreI, error) { +// if root.CommitmentType() != prefix.CommitmentType() { +// return nil, errors.New("prefix type not matching with root's") +// } + +// res := &store{ +// root: root, +// prefix: prefix, +// proofs: make(map[string]ProofI), +// verified: make(map[string][]byte), +// } + +// for _, proof := range proofs { +// if proof.CommitmentType() != root.CommitmentType() { +// return nil, errors.New("proof type not matching with root's") +// } +// res.proofs[string(proof.GetKey())] = proof +// } + +// return res, nil +// } + +// // Prove implements spec:verifyMembership and spec:verifyNonMembership. +// // The path should be one of the path format defined under +// // https://github.com/cosmos/ics/tree/master/spec/ics-024-host-requirements +// // Prove retrieves the matching proof with the provided path from the internal map +// // and call Verify method on it with internal Root and Prefix. +// // Prove acts as verifyMembership if value is not nil, and verifyNonMembership if nil. +// func (store *store) Prove(path, value []byte) bool { +// stored, ok := store.verified[string(path)] +// if ok && bytes.Equal(stored, value) { +// return true +// } +// proof, ok := store.proofs[string(path)] +// if !ok { +// return false +// } + +// err := proof.Verify(store.root, store.prefix, value) +// if err != nil { +// return false +// } +// store.verified[string(path)] = value + +// return true +// } diff --git a/x/ibc/23-commitment/merkle/merkle.go b/x/ibc/23-commitment/merkle/merkle.go deleted file mode 100644 index 798b41d840ad..000000000000 --- a/x/ibc/23-commitment/merkle/merkle.go +++ /dev/null @@ -1,112 +0,0 @@ -package merkle - -import ( - "errors" - - "github.com/tendermint/tendermint/crypto/merkle" - - "github.com/cosmos/cosmos-sdk/store/rootmulti" - - ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" -) - -// ICS 023 Merkle Types Implementation -// -// This file defines Merkle commitment types that implements ICS 023. - -const merkleKind = "merkle" - -// merkle.Proof implementation of Proof -// Applied on SDK-based IBC implementation -var _ ics23.Root = Root{} - -// Root is Merkle root hash -// In Cosmos-SDK, the AppHash of the Header becomes Root. -type Root struct { - Hash []byte `json:"hash"` -} - -// NewRoot constructs a new Root -func NewRoot(hash []byte) Root { - return Root{ - Hash: hash, - } -} - -// Implements ics23.Root -func (Root) CommitmentKind() string { - return merkleKind -} - -var _ ics23.Prefix = Prefix{} - -// Prefix is merkle path prefixed to the key. -// The constructed key from the Path and the key will be append(Path.KeyPath, append(Path.KeyPrefix, key...)) -type Prefix struct { - // KeyPath is the list of keys prepended before the prefixed key - KeyPath [][]byte `json:"key_path"` - // KeyPrefix is a byte slice prefixed before the key - KeyPrefix []byte `json:"key_prefix"` -} - -// NewPrefix constructs new Prefix instance -func NewPrefix(keypath [][]byte, keyprefix []byte) Prefix { - return Prefix{ - KeyPath: keypath, - KeyPrefix: keyprefix, - } -} - -// Implements ics23.Prefix -func (Prefix) CommitmentKind() string { - return merkleKind -} - -func (prefix Prefix) Key(key []byte) []byte { - return ics23.Join(prefix.KeyPrefix, key) -} - -var _ ics23.Proof = Proof{} - -// Proof is Merkle proof with the key information. -type Proof struct { - Proof *merkle.Proof `json:"proof"` - Key []byte `json:"key"` -} - -// Implements ics23.Proof -func (Proof) CommitmentKind() string { - return merkleKind -} - -// Returns the key of the proof -func (proof Proof) GetKey() []byte { - return proof.Key -} - -// Verify proves the proof against the given root, path, and value. -func (proof Proof) Verify(croot ics23.Root, cpath ics23.Prefix, value []byte) error { - root, ok := croot.(Root) - if !ok { - return errors.New("invalid commitment root type") - } - - path, ok := cpath.(Prefix) - if !ok { - return errors.New("invalid commitment path type") - } - - keypath := merkle.KeyPath{} - for _, key := range path.KeyPath { - keypath = keypath.AppendKey(key, merkle.KeyEncodingHex) - } - keypath = keypath.AppendKey(append(path.KeyPrefix, proof.Key...), merkle.KeyEncodingHex) - - // TODO: hard coded for now, should be extensible - runtime := rootmulti.DefaultProofRuntime() - - if value != nil { - return runtime.VerifyValue(proof.Proof, root.Hash, keypath.String(), value) - } - return runtime.VerifyAbsence(proof.Proof, root.Hash, keypath.String()) -} diff --git a/x/ibc/23-commitment/merkle/merkle_test.go b/x/ibc/23-commitment/merkle/merkle_test.go deleted file mode 100644 index 0c73e7676ce8..000000000000 --- a/x/ibc/23-commitment/merkle/merkle_test.go +++ /dev/null @@ -1,134 +0,0 @@ -package merkle - -import ( - "crypto/rand" - "testing" - - "github.com/stretchr/testify/require" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/libs/log" - dbm "github.com/tendermint/tm-db" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - "github.com/cosmos/cosmos-sdk/store/state" - "github.com/cosmos/cosmos-sdk/store/types" - sdk "github.com/cosmos/cosmos-sdk/types" - - ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" -) - -func defaultComponents() (sdk.StoreKey, sdk.Context, types.CommitMultiStore, *codec.Codec) { - key := sdk.NewKVStoreKey("test") - db := dbm.NewMemDB() - cms := store.NewCommitMultiStore(db) - cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) - err := cms.LoadLatestVersion() - if err != nil { - panic(err) - } - ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) - cdc := codec.New() - return key, ctx, cms, cdc -} - -func commit(cms types.CommitMultiStore) Root { - cid := cms.Commit() - return NewRoot(cid.Hash) -} - -// TestStore tests Merkle proof on the ics23.Store -// Sets/upates key-value pairs and prove with the query result proofs -func TestStore(t *testing.T) { - k, ctx, cms, cdc := defaultComponents() - storeName := k.Name() - prefix := []byte{0x01, 0x03, 0x05, 0xAA, 0xBB} - mapp := state.NewMapping(k, cdc, prefix) - path := NewPrefix([][]byte{[]byte(storeName)}, prefix) - - m := make(map[string][]byte) - kvpn := 10 - - // Repeat to test on multiple commits - for repeat := 0; repeat < 10; repeat++ { - - // Initializes random generated key-value pairs - for i := 0; i < kvpn; i++ { - k, v := make([]byte, 16), make([]byte, 16) - rand.Read(k) - rand.Read(v) - m[string(k)] = v - mapp.Value(k).Set(ctx, v) - } - - // Commit store - root := commit(cms) - - // Test query, and accumulate proofs - proofs := make([]ics23.Proof, 0, kvpn) - for k, v := range m { - q := state.NewStoreQuerier(cms.(types.Queryable)) - v0, p, err := mapp.Value([]byte(k)).QueryRaw(q) - require.NoError(t, err) - require.Equal(t, cdc.MustMarshalBinaryBare(v), v0, "Queried value different at %d", repeat) - proofs = append(proofs, Proof{Key: []byte(k), Proof: p}) - } - - // Add some exclusion proofs - for i := 0; i < 10; i++ { - k := make([]byte, 64) - rand.Read(k) - q := state.NewStoreQuerier(cms.(types.Queryable)) - v, p, err := mapp.Value([]byte(k)).QueryRaw(q) - require.NoError(t, err) - require.Nil(t, v) - proofs = append(proofs, Proof{Key: []byte(k), Proof: p}) - m[string(k)] = []byte{} - } - - cstore, err := ics23.NewStore(root, path, proofs) - require.NoError(t, err) - - // Test commitment store - for k, v := range m { - if len(v) != 0 { - require.True(t, cstore.Prove([]byte(k), cdc.MustMarshalBinaryBare(v))) - } else { - require.True(t, cstore.Prove([]byte(k), nil)) - } - } - - // Modify existing data - for k := range m { - v := make([]byte, 64) - rand.Read(v) - m[k] = v - mapp.Value([]byte(k)).Set(ctx, v) - } - - root = commit(cms) - - // Test query, and accumulate proofs - proofs = make([]ics23.Proof, 0, kvpn) - for k, v := range m { - q := state.NewStoreQuerier(cms.(types.Queryable)) - v0, p, err := mapp.Value([]byte(k)).QueryRaw(q) - require.NoError(t, err) - require.Equal(t, cdc.MustMarshalBinaryBare(v), v0) - proofs = append(proofs, Proof{Key: []byte(k), Proof: p}) - } - - cstore, err = ics23.NewStore(root, path, proofs) - require.NoError(t, err) - - // Test commitment store - for k, v := range m { - if len(v) != 0 { - require.True(t, cstore.Prove([]byte(k), cdc.MustMarshalBinaryBare(v))) - } else { - require.True(t, cstore.Prove([]byte(k), nil)) - } - } - } -} diff --git a/x/ibc/23-commitment/merkle/utils.go b/x/ibc/23-commitment/merkle/utils.go deleted file mode 100644 index a2813762b7b6..000000000000 --- a/x/ibc/23-commitment/merkle/utils.go +++ /dev/null @@ -1,34 +0,0 @@ -package merkle - -import ( - "errors" - - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/store/types" - ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" -) - -func QueryMultiStore(cms types.CommitMultiStore, storeName string, prefix []byte, key []byte) ([]byte, Proof, error) { - queryable, ok := cms.(types.Queryable) - if !ok { - panic("commitMultiStore not queryable") - } - qres := queryable.Query(RequestQueryMultiStore(storeName, prefix, key)) - if !qres.IsOK() { - return nil, Proof{}, errors.New(qres.Log) - } - - return qres.Value, Proof{Key: key, Proof: qres.Proof}, nil -} - -func RequestQueryMultiStore(storeName string, prefix []byte, key []byte) abci.RequestQuery { - // Suffixing path with "/key". - // iavl.Store.Query() switches over the last path element, - // and performs key-value query only if it is "/key" - return abci.RequestQuery{ - Path: "/" + storeName + "/key", - Data: ics23.Join(prefix, key), - Prove: true, - } -} diff --git a/x/ibc/23-commitment/store.go b/x/ibc/23-commitment/store.go deleted file mode 100644 index 941adaf230a0..000000000000 --- a/x/ibc/23-commitment/store.go +++ /dev/null @@ -1,105 +0,0 @@ -package ics23 - -import ( - "bytes" - "errors" -) - -// ICS 023 Function Implementation -// -// This file includes functions defined under -// https://github.com/cosmos/ics/tree/master/spec/ics-023-vector-commitments - -// Store partially implements spec:verifyMembership and spec:verifyNonMembership. -// Store holds Root, Prefix, and list of Proofs that will be verified. -// Proofs incldues their respective Paths. Values are provided at the verification time. -type Store interface { - Prove(path, value []byte) bool -} - -var _ Store = prefix{} - -type prefix struct { - store Store - prefix []byte -} - -// NewPrefix returns a prefixed store given base store and prefix. -// Prefix store for commitment proofs is used for similar path bytestring -// prefixing UX with local KVStore. -func NewPrefix(store Store, pref []byte) Store { - return &prefix{ - store: store, - prefix: pref, - } -} - -// Prove implements Store. -func (prefix prefix) Prove(path, value []byte) bool { - return prefix.store.Prove(Join(prefix.prefix, path), value) -} - -var _ Store = (*store)(nil) - -type store struct { - root Root - prefix Prefix - proofs map[string]Proof - verified map[string][]byte -} - -// NewStore constructs a new Store with the root, path, and proofs. -// The result store will be stored in the context and used by the -// commitment.Value types. -func NewStore(root Root, prefix Prefix, proofs []Proof) (Store, error) { - if root.CommitmentKind() != prefix.CommitmentKind() { - return nil, errors.New("prefix type not matching with root's") - } - - res := &store{ - root: root, - prefix: prefix, - proofs: make(map[string]Proof), - verified: make(map[string][]byte), - } - - for _, proof := range proofs { - if proof.CommitmentKind() != root.CommitmentKind() { - return nil, errors.New("proof type not matching with root's") - } - res.proofs[string(proof.GetKey())] = proof - } - - return res, nil -} - -// Prove implements spec:verifyMembership and spec:verifyNonMembership. -// The path should be one of the path format defined under -// https://github.com/cosmos/ics/tree/master/spec/ics-024-host-requirements -// Prove retrieves the matching proof with the provided path from the internal map -// and call Verify method on it with internal Root and Prefix. -// Prove acts as verifyMembership if value is not nil, and verifyNonMembership if nil. -func (store *store) Prove(path, value []byte) bool { - stored, ok := store.verified[string(path)] - if ok && bytes.Equal(stored, value) { - return true - } - proof, ok := store.proofs[string(path)] - if !ok { - return false - } - err := proof.Verify(store.root, store.prefix, value) - if err != nil { - return false - } - store.verified[string(path)] = value - - return true -} - -func Join(a, b []byte) (res []byte) { - res = make([]byte, len(a)+len(b)) - copy(res, a) - copy(res[len(a):], b) - return -} diff --git a/x/ibc/23-commitment/merkle/codec.go b/x/ibc/23-commitment/types/codec.go similarity index 50% rename from x/ibc/23-commitment/merkle/codec.go rename to x/ibc/23-commitment/types/codec.go index bbb83afa87a4..b5a21331309d 100644 --- a/x/ibc/23-commitment/merkle/codec.go +++ b/x/ibc/23-commitment/types/codec.go @@ -1,10 +1,16 @@ -package merkle +package types import ( "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" ) +// RegisterCodec registers types declared in this package func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterInterface((*exported.RootI)(nil), nil) + cdc.RegisterInterface((*exported.PrefixI)(nil), nil) + cdc.RegisterInterface((*exported.ProofI)(nil), nil) + cdc.RegisterConcrete(Root{}, "ibc/commitment/merkle/Root", nil) cdc.RegisterConcrete(Prefix{}, "ibc/commitment/merkle/Prefix", nil) cdc.RegisterConcrete(Proof{}, "ibc/commitment/merkle/Proof", nil) diff --git a/x/ibc/23-commitment/types/merkle.go b/x/ibc/23-commitment/types/merkle.go new file mode 100644 index 000000000000..94b723e203e1 --- /dev/null +++ b/x/ibc/23-commitment/types/merkle.go @@ -0,0 +1,153 @@ +package types + +import ( + "github.com/tendermint/tendermint/crypto/merkle" + + "github.com/cosmos/cosmos-sdk/store/rootmulti" + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" +) + +// ICS 023 Merkle Types Implementation +// +// This file defines Merkle commitment types that implements ICS 023. + +const merkleKind = "merkle" + +// merkle.Proof implementation of Proof +// Applied on SDK-based IBC implementation +var _ exported.RootI = Root{} + +// Root defines a merkle root hash. +// In the Cosmos SDK, the AppHash of a block header becomes the Root. +type Root struct { + Hash []byte `json:"hash" yaml:"hash"` +} + +// NewRoot constructs a new Root +func NewRoot(hash []byte) Root { + return Root{ + Hash: hash, + } +} + +// CommitmentType implements RootI interface +func (Root) CommitmentType() string { + return merkleKind +} + +// Bytes implements RootI interface +func (r Root) Bytes() []byte { + return r.Hash +} + +var _ exported.PrefixI = Prefix{} + +// TODO: applyPrefix() + +// Prefix is merkle path prefixed to the key. +// The constructed key from the Path and the key will be append(Path.KeyPath, append(Path.KeyPrefix, key...)) +type Prefix struct { + // KeyPath is the list of keys prepended before the prefixed key + KeyPath [][]byte `json:"key_path" yaml:"key_path"` + // KeyPrefix is a byte slice prefixed before the key + KeyPrefix []byte `json:"key_prefix" yaml:"key_prefix"` +} + +// NewPrefix constructs new Prefix instance +func NewPrefix(keypath [][]byte, keyprefix []byte) Prefix { + return Prefix{ + KeyPath: keypath, + KeyPrefix: keyprefix, + } +} + +// CommitmentType implements PrefixI +func (Prefix) CommitmentType() string { + return merkleKind +} + +// Key returns the full commitment prefix key +func (prefix Prefix) Key(key []byte) []byte { + return join(prefix.KeyPrefix, key) +} + +var _ exported.ProofI = Proof{} + +// Proof is a wrapper type that contains a merkle proof and the key used to verify . +// It demonstrates membership or non-membership for an element or set of elements, +// verifiable in conjunction with a known commitment root. Proofs should be +// succinct. +type Proof struct { + Proof *merkle.Proof `json:"proof" yaml:"proof"` + Key []byte `json:"key" yaml:"key"` +} + +// CommitmentType implements ProofI +func (Proof) CommitmentType() string { + return merkleKind +} + +// GetKey returns the key of the commitment proof +func (proof Proof) GetKey() []byte { + return proof.Key +} + +// GetRawProof returns the raw merkle proof +func (proof Proof) GetRawProof() *merkle.Proof { + return proof.Proof +} + +// VerifyMembership proves the proof against the given root, path, and value. +func (proof Proof) VerifyMembership(commitmentRoot exported.RootI, commitmentPrefix exported.PrefixI, value []byte) bool { + root, ok := commitmentRoot.(Root) + if !ok { + return false + } + + path, ok := commitmentPrefix.(Prefix) + if !ok { + return false + } + + keypath := merkle.KeyPath{} + for _, key := range path.KeyPath { + keypath = keypath.AppendKey(key, merkle.KeyEncodingHex) + } + keypath = keypath.AppendKey(append(path.KeyPrefix, proof.Key...), merkle.KeyEncodingHex) + + runtime := rootmulti.DefaultProofRuntime() + + err := runtime.VerifyValue(proof.Proof, root.Hash, keypath.String(), value) + if err != nil { + return false + } + + return true +} + +// VerifyAbsence verifies the absence of a proof against the given root, path. +func (proof Proof) VerifyAbsence(commitmentRoot exported.RootI, commitmentPrefix exported.PrefixI) bool { + root, ok := commitmentRoot.(Root) + if !ok { + return false + } + + path, ok := commitmentPrefix.(Prefix) + if !ok { + return false + } + + keypath := merkle.KeyPath{} + for _, key := range path.KeyPath { + keypath = keypath.AppendKey(key, merkle.KeyEncodingHex) + } + keypath = keypath.AppendKey(append(path.KeyPrefix, proof.Key...), merkle.KeyEncodingHex) + + runtime := rootmulti.DefaultProofRuntime() + + err := runtime.VerifyAbsence(proof.Proof, root.Hash, keypath.String()) + if err != nil { + return false + } + return true +} diff --git a/x/ibc/23-commitment/types/state.go b/x/ibc/23-commitment/types/state.go new file mode 100644 index 000000000000..8d0a4fb4abfd --- /dev/null +++ b/x/ibc/23-commitment/types/state.go @@ -0,0 +1,24 @@ +package types + +// State implements a commitment state as defined in https://github.com/cosmos/ics/tree/master/spec/ics-023-vector-commitments#commitment-state. +// It represents full state of the commitment, which will be stored by the manager. +type State struct { + vectorCommitment map[string][]byte +} + +// NewState creates a new commitment State instance +func NewState(vectorCommitment map[string][]byte) State { + return State{ + vectorCommitment: vectorCommitment, + } +} + +// Set adds a mapping from a path -> value to the vector commitment. +func (s *State) Set(path string, commitment []byte) { + s.vectorCommitment[path] = commitment +} + +// Remove removes a commitment under a specific path. +func (s *State) Remove(path string) { + delete(s.vectorCommitment, path) +} diff --git a/x/ibc/23-commitment/types/utils.go b/x/ibc/23-commitment/types/utils.go new file mode 100644 index 000000000000..a53ca19812bd --- /dev/null +++ b/x/ibc/23-commitment/types/utils.go @@ -0,0 +1,8 @@ +package types + +func join(a, b []byte) (res []byte) { + res = make([]byte, len(a)+len(b)) + copy(res, a) + copy(res[len(a):], b) + return +} From a925d88056c8614469d36a56e4b81229e9558572 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Fri, 18 Oct 2019 17:06:08 +0200 Subject: [PATCH 133/166] cleanup types --- x/ibc/23-commitment/exported/exported.go | 16 ++- x/ibc/23-commitment/types/codec.go | 2 + x/ibc/23-commitment/types/merkle.go | 129 +++++++++++------------ x/ibc/23-commitment/types/state.go | 24 ----- 4 files changed, 76 insertions(+), 95 deletions(-) delete mode 100644 x/ibc/23-commitment/types/state.go diff --git a/x/ibc/23-commitment/exported/exported.go b/x/ibc/23-commitment/exported/exported.go index c63af1b385f2..77a78f89ee10 100644 --- a/x/ibc/23-commitment/exported/exported.go +++ b/x/ibc/23-commitment/exported/exported.go @@ -13,14 +13,21 @@ package exported // can be proven with the proof. type RootI interface { CommitmentType() string - Bytes() []byte + GetHash() []byte } // PrefixI implements spec:CommitmentPrefix. -// Prefix is the additional information provided to the verification function. // Prefix represents the common "prefix" that a set of keys shares. type PrefixI interface { CommitmentType() string + Bytes() []byte +} + +// PathI implements spec:CommitmentPath. +// A path is the additional information provided to the verification function. +type PathI interface { + CommitmentType() string + String() string } // ProofI implements spec:CommitmentProof. @@ -29,7 +36,6 @@ type PrefixI interface { // Proofs includes key but value is provided dynamically at the verification time. type ProofI interface { CommitmentType() string - GetKey() []byte - VerifyMembership(RootI, PrefixI, []byte) bool - VerifyAbsence(RootI, PrefixI) bool + VerifyMembership(RootI, PathI, []byte) bool + VerifyAbsence(RootI, PathI) bool } diff --git a/x/ibc/23-commitment/types/codec.go b/x/ibc/23-commitment/types/codec.go index b5a21331309d..c2b96c48ad3f 100644 --- a/x/ibc/23-commitment/types/codec.go +++ b/x/ibc/23-commitment/types/codec.go @@ -9,9 +9,11 @@ import ( func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*exported.RootI)(nil), nil) cdc.RegisterInterface((*exported.PrefixI)(nil), nil) + cdc.RegisterInterface((*exported.PathI)(nil), nil) cdc.RegisterInterface((*exported.ProofI)(nil), nil) cdc.RegisterConcrete(Root{}, "ibc/commitment/merkle/Root", nil) cdc.RegisterConcrete(Prefix{}, "ibc/commitment/merkle/Prefix", nil) + cdc.RegisterConcrete(Path{}, "ibc/commitment/merkle/Path", nil) cdc.RegisterConcrete(Proof{}, "ibc/commitment/merkle/Proof", nil) } diff --git a/x/ibc/23-commitment/types/merkle.go b/x/ibc/23-commitment/types/merkle.go index 94b723e203e1..7eadf8b2bdd6 100644 --- a/x/ibc/23-commitment/types/merkle.go +++ b/x/ibc/23-commitment/types/merkle.go @@ -1,6 +1,8 @@ package types import ( + "strings" + "github.com/tendermint/tendermint/crypto/merkle" "github.com/cosmos/cosmos-sdk/store/rootmulti" @@ -11,6 +13,7 @@ import ( // // This file defines Merkle commitment types that implements ICS 023. +// TODO: use iota const merkleKind = "merkle" // merkle.Proof implementation of Proof @@ -35,29 +38,23 @@ func (Root) CommitmentType() string { return merkleKind } -// Bytes implements RootI interface -func (r Root) Bytes() []byte { +// GetHash implements RootI interface +func (r Root) GetHash() []byte { return r.Hash } var _ exported.PrefixI = Prefix{} -// TODO: applyPrefix() - // Prefix is merkle path prefixed to the key. // The constructed key from the Path and the key will be append(Path.KeyPath, append(Path.KeyPrefix, key...)) type Prefix struct { - // KeyPath is the list of keys prepended before the prefixed key - KeyPath [][]byte `json:"key_path" yaml:"key_path"` - // KeyPrefix is a byte slice prefixed before the key - KeyPrefix []byte `json:"key_prefix" yaml:"key_prefix"` + KeyPrefix []byte `json:"key_prefix" yaml:"key_prefix"` // byte slice prefixed before the key } // NewPrefix constructs new Prefix instance -func NewPrefix(keypath [][]byte, keyprefix []byte) Prefix { +func NewPrefix(keyPrefix []byte) Prefix { return Prefix{ - KeyPath: keypath, - KeyPrefix: keyprefix, + KeyPrefix: keyPrefix, } } @@ -66,20 +63,63 @@ func (Prefix) CommitmentType() string { return merkleKind } -// Key returns the full commitment prefix key -func (prefix Prefix) Key(key []byte) []byte { - return join(prefix.KeyPrefix, key) +// Bytes returns the key prefix bytes +func (p Prefix) Bytes() []byte { + return p.KeyPrefix +} + +var _ exported.PathI = Path{} + +// Path is the path used to verify commitment proofs, which can be an arbitrary +// structured object (defined by a commitment type). +type Path struct { + KeyPath merkle.KeyPath `json:"key_path" yaml:"key_path"` // byte slice prefixed before the key +} + +// NewPath creates a new CommitmentPath instance +func NewPath(keyPathStr []string) Path { + merkleKeyPath := merkle.KeyPath{} + for _, keyStr := range keyPathStr { + merkleKeyPath = merkleKeyPath.AppendKey([]byte(keyStr), merkle.KeyEncodingHex) + } + + return Path{ + KeyPath: merkleKeyPath, + } +} + +// CommitmentType implements PathI +func (Path) CommitmentType() string { + return merkleKind +} + +// String implements fmt.Stringer +func (p Path) String() string { + return p.KeyPath.String() +} + +// ApplyPrefix constructs a new commitment path from the arguments. It interprets +// the path argument in the context of the prefix argument. +// +// CONTRACT: provided path string MUST be a well formated path. See ICS24 for +// reference. +func ApplyPrefix(prefix exported.PrefixI, path string) Path { + // Split paths by the separator + pathSlice := strings.Split(path, "/") + commitmentPath := NewPath(pathSlice) + + commitmentPath.KeyPath = commitmentPath.KeyPath.AppendKey(prefix.Bytes(), merkle.KeyEncodingHex) + return commitmentPath } var _ exported.ProofI = Proof{} -// Proof is a wrapper type that contains a merkle proof and the key used to verify . +// Proof is a wrapper type that contains a merkle proof. // It demonstrates membership or non-membership for an element or set of elements, // verifiable in conjunction with a known commitment root. Proofs should be // succinct. type Proof struct { Proof *merkle.Proof `json:"proof" yaml:"proof"` - Key []byte `json:"key" yaml:"key"` } // CommitmentType implements ProofI @@ -87,37 +127,10 @@ func (Proof) CommitmentType() string { return merkleKind } -// GetKey returns the key of the commitment proof -func (proof Proof) GetKey() []byte { - return proof.Key -} - -// GetRawProof returns the raw merkle proof -func (proof Proof) GetRawProof() *merkle.Proof { - return proof.Proof -} - -// VerifyMembership proves the proof against the given root, path, and value. -func (proof Proof) VerifyMembership(commitmentRoot exported.RootI, commitmentPrefix exported.PrefixI, value []byte) bool { - root, ok := commitmentRoot.(Root) - if !ok { - return false - } - - path, ok := commitmentPrefix.(Prefix) - if !ok { - return false - } - - keypath := merkle.KeyPath{} - for _, key := range path.KeyPath { - keypath = keypath.AppendKey(key, merkle.KeyEncodingHex) - } - keypath = keypath.AppendKey(append(path.KeyPrefix, proof.Key...), merkle.KeyEncodingHex) - +// VerifyMembership verifies the membership pf a merkle proof against the given root, path, and value. +func (proof Proof) VerifyMembership(root exported.RootI, path exported.PathI, value []byte) bool { runtime := rootmulti.DefaultProofRuntime() - - err := runtime.VerifyValue(proof.Proof, root.Hash, keypath.String(), value) + err := runtime.VerifyValue(proof.Proof, root.GetHash(), path.String(), value) if err != nil { return false } @@ -125,29 +138,13 @@ func (proof Proof) VerifyMembership(commitmentRoot exported.RootI, commitmentPre return true } -// VerifyAbsence verifies the absence of a proof against the given root, path. -func (proof Proof) VerifyAbsence(commitmentRoot exported.RootI, commitmentPrefix exported.PrefixI) bool { - root, ok := commitmentRoot.(Root) - if !ok { - return false - } - - path, ok := commitmentPrefix.(Prefix) - if !ok { - return false - } - - keypath := merkle.KeyPath{} - for _, key := range path.KeyPath { - keypath = keypath.AppendKey(key, merkle.KeyEncodingHex) - } - keypath = keypath.AppendKey(append(path.KeyPrefix, proof.Key...), merkle.KeyEncodingHex) - +// VerifyAbsence verifies the absence of a merkle proof against the given root and path. +func (proof Proof) VerifyAbsence(root exported.RootI, path exported.PathI) bool { runtime := rootmulti.DefaultProofRuntime() - - err := runtime.VerifyAbsence(proof.Proof, root.Hash, keypath.String()) + err := runtime.VerifyAbsence(proof.Proof, root.GetHash(), path.String()) if err != nil { return false } + return true } diff --git a/x/ibc/23-commitment/types/state.go b/x/ibc/23-commitment/types/state.go deleted file mode 100644 index 8d0a4fb4abfd..000000000000 --- a/x/ibc/23-commitment/types/state.go +++ /dev/null @@ -1,24 +0,0 @@ -package types - -// State implements a commitment state as defined in https://github.com/cosmos/ics/tree/master/spec/ics-023-vector-commitments#commitment-state. -// It represents full state of the commitment, which will be stored by the manager. -type State struct { - vectorCommitment map[string][]byte -} - -// NewState creates a new commitment State instance -func NewState(vectorCommitment map[string][]byte) State { - return State{ - vectorCommitment: vectorCommitment, - } -} - -// Set adds a mapping from a path -> value to the vector commitment. -func (s *State) Set(path string, commitment []byte) { - s.vectorCommitment[path] = commitment -} - -// Remove removes a commitment under a specific path. -func (s *State) Remove(path string) { - delete(s.vectorCommitment, path) -} From c12c2980c57a9914360d6d8451b136e13a1de685 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Mon, 21 Oct 2019 17:40:22 +0200 Subject: [PATCH 134/166] implement batch verification --- x/ibc/23-commitment/exported/exported.go | 2 +- x/ibc/23-commitment/keeper/keeper.go | 107 ++++++++++++----------- x/ibc/23-commitment/types/merkle.go | 4 +- 3 files changed, 57 insertions(+), 56 deletions(-) diff --git a/x/ibc/23-commitment/exported/exported.go b/x/ibc/23-commitment/exported/exported.go index 77a78f89ee10..9e0d8d0ef776 100644 --- a/x/ibc/23-commitment/exported/exported.go +++ b/x/ibc/23-commitment/exported/exported.go @@ -37,5 +37,5 @@ type PathI interface { type ProofI interface { CommitmentType() string VerifyMembership(RootI, PathI, []byte) bool - VerifyAbsence(RootI, PathI) bool + VerifyNonMembership(RootI, PathI) bool } diff --git a/x/ibc/23-commitment/keeper/keeper.go b/x/ibc/23-commitment/keeper/keeper.go index 31fa76d5ee38..62790e6b83cb 100644 --- a/x/ibc/23-commitment/keeper/keeper.go +++ b/x/ibc/23-commitment/keeper/keeper.go @@ -1,6 +1,8 @@ package keeper import ( + "bytes" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" @@ -10,72 +12,71 @@ import ( // A vector commitment manager has the ability to add or remove items from the // commitment state as defined in https://github.com/cosmos/ics/tree/master/spec/ics-023-vector-commitments#definitions. type Keeper struct { - prefix exported.PrefixI - proofs map[string]exported.ProofI - verified map[string][]byte + prefix exported.PrefixI + verifiedMemberships map[string][]byte // lookup map for returning already verified membership proofs + verifiedAbsences map[string]bool // lookup map for returning already verified absences } -// NewKeeper returns a prefixed store given base store and prefix. -func NewKeeper(prefix exported.PrefixI, proofs []exported.ProofI) Keeper { +// NewKeeper returns a new Keeper +func NewKeeper(prefix exported.PrefixI) Keeper { return Keeper{ - prefix: prefix, - proofs: make(map[string]exported.ProofI), - verified: make(map[string][]byte), + prefix: prefix, + verifiedMemberships: make(map[string][]byte), + verifiedAbsences: make(map[string]bool), } } -// GetRoot returns the application Hash at the curretn block height as a commitment +// CalculateRoot returns the application Hash at the curretn block height as a commitment // root for proof verification. -func (k Keeper) GetRoot(ctx sdk.Context) exported.RootI { +func (k Keeper) CalculateRoot(ctx sdk.Context) exported.RootI { return types.NewRoot(ctx.BlockHeader().AppHash) } -// // NewStore constructs a new Store with the root, path, and proofs. -// // The result store will be stored in the context and used by the -// // commitment.Value types. -// func NewStore(root RootI, prefix PrefixI, proofs []ProofI) (StoreI, error) { -// if root.CommitmentType() != prefix.CommitmentType() { -// return nil, errors.New("prefix type not matching with root's") -// } +// BatchVerifyMembership verifies a proof that many paths have been set to +// specific values in a commitment. It calls the proof's VerifyMembership method +// with the calculated root and the provided paths. +// Returns false on the first failed membership verification. +func (k Keeper) BatchVerifyMembership(ctx sdk.Context, proof exported.ProofI, items map[string][]byte) bool { + root := k.CalculateRoot(ctx) + + for pathStr, value := range items { + storedValue, ok := k.verifiedMemberships[pathStr] + if ok && bytes.Equal(storedValue, value) { + continue + } -// res := &store{ -// root: root, -// prefix: prefix, -// proofs: make(map[string]ProofI), -// verified: make(map[string][]byte), -// } + path := types.ApplyPrefix(k.prefix, pathStr) + ok = proof.VerifyMembership(root, path, value) + if !ok { + return false + } -// for _, proof := range proofs { -// if proof.CommitmentType() != root.CommitmentType() { -// return nil, errors.New("proof type not matching with root's") -// } -// res.proofs[string(proof.GetKey())] = proof -// } + k.verifiedMemberships[pathStr] = value + } -// return res, nil -// } + return true +} -// // Prove implements spec:verifyMembership and spec:verifyNonMembership. -// // The path should be one of the path format defined under -// // https://github.com/cosmos/ics/tree/master/spec/ics-024-host-requirements -// // Prove retrieves the matching proof with the provided path from the internal map -// // and call Verify method on it with internal Root and Prefix. -// // Prove acts as verifyMembership if value is not nil, and verifyNonMembership if nil. -// func (store *store) Prove(path, value []byte) bool { -// stored, ok := store.verified[string(path)] -// if ok && bytes.Equal(stored, value) { -// return true -// } -// proof, ok := store.proofs[string(path)] -// if !ok { -// return false -// } +// BatchVerifyNonMembership verifies a proof that many paths have not been set +// to any value in a commitment. It calls the proof's VerifyNonMembership method +// with the calculated root and the provided paths. +// Returns false on the first failed non-membership verification. +func (k Keeper) BatchVerifyNonMembership(ctx sdk.Context, proof exported.ProofI, paths []string) bool { + root := k.CalculateRoot(ctx) + for _, pathStr := range paths { + ok := k.verifiedAbsences[pathStr] + if ok { + continue + } -// err := proof.Verify(store.root, store.prefix, value) -// if err != nil { -// return false -// } -// store.verified[string(path)] = value + path := types.ApplyPrefix(k.prefix, pathStr) + ok = proof.VerifyNonMembership(root, path) + if !ok { + return false + } -// return true -// } + k.verifiedAbsences[pathStr] = true + } + + return true +} diff --git a/x/ibc/23-commitment/types/merkle.go b/x/ibc/23-commitment/types/merkle.go index 7eadf8b2bdd6..0a89c58e682b 100644 --- a/x/ibc/23-commitment/types/merkle.go +++ b/x/ibc/23-commitment/types/merkle.go @@ -138,8 +138,8 @@ func (proof Proof) VerifyMembership(root exported.RootI, path exported.PathI, va return true } -// VerifyAbsence verifies the absence of a merkle proof against the given root and path. -func (proof Proof) VerifyAbsence(root exported.RootI, path exported.PathI) bool { +// VerifyNonMembership verifies the absence of a merkle proof against the given root and path. +func (proof Proof) VerifyNonMembership(root exported.RootI, path exported.PathI) bool { runtime := rootmulti.DefaultProofRuntime() err := runtime.VerifyAbsence(proof.Proof, root.GetHash(), path.String()) if err != nil { From a34b7411549b185866766f0acdfd5915b6c873f2 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Mon, 21 Oct 2019 17:56:15 +0200 Subject: [PATCH 135/166] gosimple suggestion --- x/ibc/23-commitment/types/merkle.go | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/x/ibc/23-commitment/types/merkle.go b/x/ibc/23-commitment/types/merkle.go index 0a89c58e682b..0a61a997213b 100644 --- a/x/ibc/23-commitment/types/merkle.go +++ b/x/ibc/23-commitment/types/merkle.go @@ -131,20 +131,12 @@ func (Proof) CommitmentType() string { func (proof Proof) VerifyMembership(root exported.RootI, path exported.PathI, value []byte) bool { runtime := rootmulti.DefaultProofRuntime() err := runtime.VerifyValue(proof.Proof, root.GetHash(), path.String(), value) - if err != nil { - return false - } - - return true + return err == nil } // VerifyNonMembership verifies the absence of a merkle proof against the given root and path. func (proof Proof) VerifyNonMembership(root exported.RootI, path exported.PathI) bool { runtime := rootmulti.DefaultProofRuntime() err := runtime.VerifyAbsence(proof.Proof, root.GetHash(), path.String()) - if err != nil { - return false - } - - return true + return err == nil } From 9b7727566361ed82fc6b869f7d6c313a57676d96 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 22 Oct 2019 14:38:14 +0200 Subject: [PATCH 136/166] various fixes; remove legacy tests; remove commitment path query --- x/ibc/02-client/client/cli/query.go | 36 +--- x/ibc/02-client/exported/exported.go | 4 +- x/ibc/02-client/keeper/keeper.go | 39 ++-- x/ibc/02-client/keeper/querier.go | 13 -- x/ibc/02-client/types/querier.go | 1 - .../types/tendermint/consensus_state.go | 16 +- .../types/tendermint/tests/tendermint_test.go | 52 ----- .../02-client/types/tendermint/tests/types.go | 198 ------------------ .../02-client/types/tendermint/tests/utils.go | 8 - .../types/tendermint/tests/valset.go | 182 ---------------- x/ibc/module.go | 6 +- 11 files changed, 30 insertions(+), 525 deletions(-) delete mode 100644 x/ibc/02-client/types/tendermint/tests/tendermint_test.go delete mode 100644 x/ibc/02-client/types/tendermint/tests/types.go delete mode 100644 x/ibc/02-client/types/tendermint/tests/utils.go delete mode 100644 x/ibc/02-client/types/tendermint/tests/valset.go diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index fbef4a4e8181..1e2e4f1ef5b2 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -17,8 +17,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" - ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" + commitmentexported "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" ) // GetQueryCmd returns the query commands for IBC clients @@ -32,7 +31,6 @@ func GetQueryCmd(queryRouter string, cdc *codec.Codec) *cobra.Command { ics02ClientQueryCmd.AddCommand(client.GetCommands( GetCmdQueryConsensusState(queryRouter, cdc), - GetCmdQueryPath(queryRouter, cdc), GetCmdQueryHeader(cdc), GetCmdQueryClientState(queryRouter, cdc), GetCmdQueryRoot(queryRouter, cdc), @@ -116,7 +114,7 @@ $ %s query ibc client root [client-id] [height] return err } - var root ics23.Root + var root commitmentexported.RootI if err := cdc.UnmarshalJSON(res, &root); err != nil { return err } @@ -167,36 +165,6 @@ $ %s query ibc client consensus-state [client-id] } } -// GetCmdQueryPath defines the command to query the commitment path -func GetCmdQueryPath(storeName string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ - Use: "path", - Short: "Query the commitment path of the running chain", - Long: strings.TrimSpace(fmt.Sprintf(`Query the commitment path - -Example: -$ %s query ibc client path - `, version.ClientName), - ), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - // TODO: get right path - res, _, err := cliCtx.Query("") - if err != nil { - return err - } - - var path merkle.Prefix - if err := cdc.UnmarshalJSON(res, &path); err != nil { - return err - } - - return cliCtx.PrintOutput(path) - }, - } -} - // GetCmdQueryHeader defines the command to query the latest header on the chain // TODO: do we really need this cmd ?? func GetCmdQueryHeader(cdc *codec.Codec) *cobra.Command { diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index ce0f51af84af..05db3902130f 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -3,7 +3,7 @@ package exported import ( "fmt" - ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + commitmentexported "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" ) // Blockchain is consensus algorithm which generates valid Headers. It generates @@ -21,7 +21,7 @@ type ConsensusState interface { // GetRoot returns the commitment root of the consensus state, // which is used for key-value pair verification. - GetRoot() ics23.Root + GetRoot() commitmentexported.RootI // CheckValidityAndUpdateState returns the updated consensus state // only if the header is a descendent of this consensus state. diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index 660fdc450f24..feef68f3a7f3 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -12,8 +12,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" - ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" + commitmentexported "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -41,11 +41,6 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s/%s", ibctypes.ModuleName, types.SubModuleName)) } -// GetCommitmentPath returns the commitment path of the client -func (k Keeper) GetCommitmentPath() merkle.Prefix { - return merkle.NewPrefix([][]byte{[]byte(k.storeKey.Name())}, k.prefix) -} - // GetClientState gets a particular client from the store func (k Keeper) GetClientState(ctx sdk.Context, clientID string) (types.ClientState, bool) { store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) @@ -104,20 +99,20 @@ func (k Keeper) SetConsensusState(ctx sdk.Context, clientID string, consensusSta } // GetCommitmentRoot gets a commitment Root from a particular height to a client -func (k Keeper) GetCommitmentRoot(ctx sdk.Context, clientID string, height uint64) (ics23.Root, bool) { +func (k Keeper) GetCommitmentRoot(ctx sdk.Context, clientID string, height uint64) (commitmentexported.RootI, bool) { store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) bz := store.Get(types.KeyRoot(clientID, height)) if bz == nil { return nil, false } - var root ics23.Root + var root commitmentexported.RootI k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &root) return root, true } // SetCommitmentRoot sets a commitment Root from a particular height to a client -func (k Keeper) SetCommitmentRoot(ctx sdk.Context, clientID string, height uint64, root ics23.Root) { +func (k Keeper) SetCommitmentRoot(ctx sdk.Context, clientID string, height uint64, root commitmentexported.RootI) { store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) bz := k.cdc.MustMarshalBinaryLengthPrefixed(root) store.Set(types.KeyRoot(clientID, height), bz) @@ -161,8 +156,8 @@ func (k Keeper) VerifyMembership( ctx sdk.Context, clientState types.ClientState, height uint64, // sequence - proof ics23.Proof, - path string, + proof commitmentexported.ProofI, + pathStr string, value []byte, ) bool { if clientState.Frozen { @@ -174,12 +169,10 @@ func (k Keeper) VerifyMembership( return false } - prefix := merkle.NewPrefix([][]byte{[]byte(path)}, nil) // TODO: keyprefix? - if err := proof.Verify(root, prefix, value); err != nil { - return false - } + prefix := commitmenttypes.NewPrefix(k.prefix) + path := commitmenttypes.ApplyPrefix(prefix, pathStr) - return true + return proof.VerifyMembership(root, path, value) } // VerifyNonMembership state non-membership function defined by the client type @@ -187,8 +180,8 @@ func (k Keeper) VerifyNonMembership( ctx sdk.Context, clientState types.ClientState, height uint64, // sequence - proof ics23.Proof, - path string, + proof commitmentexported.ProofI, + pathStr string, ) bool { if clientState.Frozen { return false @@ -199,10 +192,8 @@ func (k Keeper) VerifyNonMembership( return false } - prefix := merkle.NewPrefix([][]byte{[]byte(path)}, nil) // TODO: keyprefix? - if err := proof.Verify(root, prefix, nil); err != nil { - return false - } + prefix := commitmenttypes.NewPrefix(k.prefix) + path := commitmenttypes.ApplyPrefix(prefix, pathStr) - return true + return proof.VerifyNonMembership(root, path) } diff --git a/x/ibc/02-client/keeper/querier.go b/x/ibc/02-client/keeper/querier.go index 3b014d916ac3..dc52cdd20f4e 100644 --- a/x/ibc/02-client/keeper/querier.go +++ b/x/ibc/02-client/keeper/querier.go @@ -17,8 +17,6 @@ func NewQuerier(k Keeper) sdk.Querier { return queryClientState(ctx, req, k) case types.QueryConsensusState: return queryConsensusState(ctx, req, k) - case types.QueryCommitmentPath: - return queryCommitmentPath(k) case types.QueryCommitmentRoot: return queryCommitmentRoot(ctx, req, k) default: @@ -71,17 +69,6 @@ func queryConsensusState(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]by return bz, nil } -func queryCommitmentPath(k Keeper) ([]byte, sdk.Error) { - path := k.GetCommitmentPath() - - bz, err := types.SubModuleCdc.MarshalJSON(path) - if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) - } - - return bz, nil -} - func queryCommitmentRoot(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { var params types.QueryCommitmentRootParams diff --git a/x/ibc/02-client/types/querier.go b/x/ibc/02-client/types/querier.go index 6e58b6c00f57..eff5bce07b67 100644 --- a/x/ibc/02-client/types/querier.go +++ b/x/ibc/02-client/types/querier.go @@ -4,7 +4,6 @@ package types const ( QueryClientState = "client_state" QueryConsensusState = "consensus_state" - QueryCommitmentPath = "commitment_path" QueryCommitmentRoot = "roots" ) diff --git a/x/ibc/02-client/types/tendermint/consensus_state.go b/x/ibc/02-client/types/tendermint/consensus_state.go index 0a52c598a9ec..6a369433c9f5 100644 --- a/x/ibc/02-client/types/tendermint/consensus_state.go +++ b/x/ibc/02-client/types/tendermint/consensus_state.go @@ -8,18 +8,18 @@ import ( tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" + commitmentexported "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" ) var _ exported.ConsensusState = ConsensusState{} // ConsensusState defines a Tendermint consensus state type ConsensusState struct { - ChainID string `json:"chain_id" yaml:"chain_id"` - Height uint64 `json:"height" yaml:"height"` // NOTE: defined as 'sequence' in the spec - Root ics23.Root `json:"root" yaml:"root"` - NextValidatorSet *tmtypes.ValidatorSet `json:"next_validator_set" yaml:"next_validator_set"` // contains the PublicKey + ChainID string `json:"chain_id" yaml:"chain_id"` + Height uint64 `json:"height" yaml:"height"` // NOTE: defined as 'sequence' in the spec + Root commitmentexported.RootI `json:"root" yaml:"root"` + NextValidatorSet *tmtypes.ValidatorSet `json:"next_validator_set" yaml:"next_validator_set"` // contains the PublicKey } // ClientType returns Tendermint @@ -33,7 +33,7 @@ func (cs ConsensusState) GetHeight() uint64 { } // GetRoot returns the commitment Root for the specific -func (cs ConsensusState) GetRoot() ics23.Root { +func (cs ConsensusState) GetRoot() commitmentexported.RootI { return cs.Root } @@ -84,7 +84,7 @@ func (cs ConsensusState) checkValidity(header Header) error { // update the consensus state from a new header func (cs ConsensusState) update(header Header) ConsensusState { cs.Height = header.GetHeight() - cs.Root = merkle.NewRoot(header.AppHash) + cs.Root = commitmenttypes.NewRoot(header.AppHash) cs.NextValidatorSet = header.NextValidatorSet return cs } diff --git a/x/ibc/02-client/types/tendermint/tests/tendermint_test.go b/x/ibc/02-client/types/tendermint/tests/tendermint_test.go deleted file mode 100644 index e7d09644126a..000000000000 --- a/x/ibc/02-client/types/tendermint/tests/tendermint_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package tendermint - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func testUpdate(t *testing.T, interval int, ok bool) { - node := NewNode(NewMockValidators(100, 10), "f8wib", []byte{0x98, 0x78}) - - _ = node.Commit() - - verifier := node.LastStateVerifier() - - for i := 0; i < 100; i++ { - header := node.Commit() - - if i%interval == 0 { - err := verifier.Validate(header, node.PrevValset, node.Valset) - if ok { - require.NoError(t, err) - } else { - require.Error(t, err) - } - } - } -} - -func TestEveryBlockUpdate(t *testing.T) { - testUpdate(t, 1, true) -} - -func TestEvenBlockUpdate(t *testing.T) { - testUpdate(t, 2, true) -} - -func TestSixthBlockUpdate(t *testing.T) { - testUpdate(t, 6, true) -} - -/* -// This should fail, since the amount of mutation is so large -// Commented out because it sometimes success -func TestTenthBlockUpdate(t *testing.T) { - testUpdate(t, 10, false) -} -*/ - -func TestProofs(t *testing.T) { - testProof(t) -} diff --git a/x/ibc/02-client/types/tendermint/tests/types.go b/x/ibc/02-client/types/tendermint/tests/types.go deleted file mode 100644 index 532b01c635ee..000000000000 --- a/x/ibc/02-client/types/tendermint/tests/types.go +++ /dev/null @@ -1,198 +0,0 @@ -package tendermint - -import ( - "bytes" - "crypto/rand" - "testing" - - "github.com/stretchr/testify/require" - - abci "github.com/tendermint/tendermint/abci/types" - cmn "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/libs/log" - tmtypes "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - stypes "github.com/cosmos/cosmos-sdk/store/types" - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" - commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" -) - -const chainid = "testchain" - -func defaultComponents(storename string) (sdk.StoreKey, sdk.Context, stypes.CommitMultiStore, *codec.Codec) { - key := sdk.NewKVStoreKey(storename) - - db := dbm.NewMemDB() - cms := store.NewCommitMultiStore(db) - cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) - err := cms.LoadLatestVersion() - if err != nil { - panic(err) - } - ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) - cdc := codec.New() - return key, ctx, cms, cdc -} - -type Node struct { - PrevValset MockValidators - Valset MockValidators - - Cms sdk.CommitMultiStore - Key sdk.StoreKey - Store sdk.KVStore - - Commits []tmtypes.SignedHeader - - StoreName string - KeyPrefix []byte -} - -func NewNode(valset MockValidators, storeName string, prefix []byte) *Node { - key, ctx, cms, _ := defaultComponents(storeName) - - return &Node{ - Valset: valset, - Cms: cms, - Key: key, - Store: ctx.KVStore(key), - Commits: nil, - StoreName: storeName, - KeyPrefix: prefix, - } -} - -func (node *Node) Prefix() merkle.Prefix { - return merkle.NewPrefix([][]byte{[]byte(node.StoreName)}, node.KeyPrefix) -} - -func (node *Node) Last() tmtypes.SignedHeader { - if len(node.Commits) == 0 { - return tmtypes.SignedHeader{} - } - return node.Commits[len(node.Commits)-1] -} - -func (node *Node) Commit() tendermint.Header { - valsethash := node.Valset.ValidatorSet().Hash() - nextvalset := node.Valset.Mutate() - nextvalsethash := nextvalset.ValidatorSet().Hash() - commitid := node.Cms.Commit() - - header := tmtypes.Header{ - ChainID: chainid, - Height: int64(len(node.Commits) + 1), - LastBlockID: tmtypes.BlockID{ - Hash: node.Last().Header.Hash(), - }, - - ValidatorsHash: valsethash, - NextValidatorsHash: nextvalsethash, - AppHash: commitid.Hash, - } - - commit := node.Valset.Sign(header) - - node.PrevValset = node.Valset - node.Valset = nextvalset - node.Commits = append(node.Commits, commit) - - return tendermint.Header{ - SignedHeader: commit, - ValidatorSet: node.PrevValset.ValidatorSet(), - NextValidatorSet: node.Valset.ValidatorSet(), - } -} - -func (node *Node) LastStateVerifier() *Verifier { - return NewVerifier(node.Last(), node.Valset, node.Root()) -} - -func (node *Node) Root() merkle.Root { - return merkle.NewRoot(node.Last().AppHash) - -} - -func (node *Node) Context() sdk.Context { - return sdk.NewContext(node.Cms, abci.Header{}, false, log.NewNopLogger()) -} - -type Verifier struct { - exported.ConsensusState -} - -func NewVerifier(header tmtypes.SignedHeader, nextvalset MockValidators, root merkle.Root) *Verifier { - return &Verifier{ - tendermint.ConsensusState{ - ChainID: chainid, - Height: uint64(header.Height), - Root: merkle.NewRoot(header.AppHash), - NextValidatorSet: nextvalset.ValidatorSet(), - }, - } -} - -func (v *Verifier) Validate(header tendermint.Header, valset, nextvalset MockValidators) error { - newcs, err := v.ConsensusState.CheckValidityAndUpdateState(header) - if err != nil { - return err - } - v.ConsensusState = newcs.(tendermint.ConsensusState) - - return nil -} - -func (node *Node) Query(t *testing.T, k []byte) ([]byte, commitment.Proof) { - k = bytes.TrimPrefix(k, node.KeyPrefix) - value, proof, err := merkle.QueryMultiStore(node.Cms, node.StoreName, node.KeyPrefix, k) - require.NoError(t, err) - return value, proof -} - -func (node *Node) Set(k, value []byte) { - node.Store.Set(join(node.KeyPrefix, k), value) -} - -// nolint:deadcode,unused -func testProof(t *testing.T) { - node := NewNode(NewMockValidators(100, 10), "1", []byte{0x00, 0x01}) - - node.Commit() - - kvps := cmn.KVPairs{} - for h := 0; h < 20; h++ { - for i := 0; i < 100; i++ { - k := make([]byte, 32) - v := make([]byte, 32) - _, err := rand.Read(k) - require.NoError(t, err) - _, err = rand.Read(v) - require.NoError(t, err) - kvps = append(kvps, cmn.KVPair{Key: k, Value: v}) - node.Set(k, v) - } - - header := node.Commit() - proofs := []commitment.Proof{} - root := merkle.NewRoot(header.AppHash) - for _, kvp := range kvps { - v, p := node.Query(t, kvp.Key) - - require.Equal(t, kvp.Value, v) - proofs = append(proofs, p) - } - cstore, err := commitment.NewStore(root, node.Prefix(), proofs) - require.NoError(t, err) - - for _, kvp := range kvps { - require.True(t, cstore.Prove(kvp.Key, kvp.Value)) - } - } -} diff --git a/x/ibc/02-client/types/tendermint/tests/utils.go b/x/ibc/02-client/types/tendermint/tests/utils.go deleted file mode 100644 index d5262a14a29b..000000000000 --- a/x/ibc/02-client/types/tendermint/tests/utils.go +++ /dev/null @@ -1,8 +0,0 @@ -package tendermint - -func join(a, b []byte) (res []byte) { - res = make([]byte, len(a)+len(b)) - copy(res, a) - copy(res[len(a):], b) - return -} diff --git a/x/ibc/02-client/types/tendermint/tests/valset.go b/x/ibc/02-client/types/tendermint/tests/valset.go deleted file mode 100644 index 30bd194682c8..000000000000 --- a/x/ibc/02-client/types/tendermint/tests/valset.go +++ /dev/null @@ -1,182 +0,0 @@ -package tendermint - -import ( - "bytes" - "sort" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - tmtypes "github.com/tendermint/tendermint/types" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// reimplementing tmtypes.MockPV to make it marshallable -type mockPV struct { - PrivKey crypto.PrivKey -} - -var _ tmtypes.PrivValidator = (*mockPV)(nil) - -func newMockPV() *mockPV { - return &mockPV{ed25519.GenPrivKey()} -} - -func (pv *mockPV) GetAddress() tmtypes.Address { - return pv.PrivKey.PubKey().Address() -} - -func (pv *mockPV) GetPubKey() crypto.PubKey { - return pv.PrivKey.PubKey() -} - -func (pv *mockPV) SignVote(chainID string, vote *tmtypes.Vote) error { - signBytes := vote.SignBytes(chainID) - sig, err := pv.PrivKey.Sign(signBytes) - if err != nil { - return err - } - vote.Signature = sig - return nil -} - -func (pv *mockPV) SignProposal(string, *tmtypes.Proposal) error { - panic("not needed") -} - -// MockValset -type MockValidator struct { - MockPV *mockPV - Power sdk.Dec -} - -func NewMockValidator(power sdk.Dec) MockValidator { - return MockValidator{ - MockPV: newMockPV(), - Power: power, - } -} - -func (val MockValidator) GetOperator() sdk.ValAddress { - return sdk.ValAddress(val.MockPV.GetAddress()) -} - -func (val MockValidator) GetConsAddr() sdk.ConsAddress { - return sdk.GetConsAddress(val.MockPV.GetPubKey()) -} - -func (val MockValidator) GetConsPubKey() crypto.PubKey { - return val.MockPV.GetPubKey() -} - -func (val MockValidator) GetPower() sdk.Dec { - return val.Power -} - -func (val MockValidator) Validator() *tmtypes.Validator { - return tmtypes.NewValidator( - val.GetConsPubKey(), - val.GetPower().RoundInt64(), - ) -} - -type MockValidators []MockValidator - -var _ sort.Interface = MockValidators{} - -// TODO: differentiate power between the vals -func NewMockValidators(num int, power int64) MockValidators { - res := make(MockValidators, num) - for i := range res { - res[i] = NewMockValidator(sdk.NewDec(power)) - } - - sort.Sort(res) - - return res -} - -func (vals MockValidators) Len() int { - return len(vals) -} - -func (vals MockValidators) Less(i, j int) bool { - return bytes.Compare([]byte(vals[i].GetConsAddr()), []byte(vals[j].GetConsAddr())) == -1 -} - -func (vals MockValidators) Swap(i, j int) { - it := vals[j] - vals[j] = vals[i] - vals[i] = it -} - -func (vals MockValidators) TotalPower() sdk.Dec { - res := sdk.ZeroDec() - for _, val := range vals { - res = res.Add(val.Power) - } - return res -} - -func (vals MockValidators) Sign(header tmtypes.Header) tmtypes.SignedHeader { - - precommits := make([]*tmtypes.CommitSig, len(vals)) - for i, val := range vals { - vote := &tmtypes.Vote{ - BlockID: tmtypes.BlockID{ - Hash: header.Hash(), - }, - ValidatorAddress: val.MockPV.GetAddress(), - ValidatorIndex: i, - Height: header.Height, - Type: tmtypes.PrecommitType, - } - _ = val.MockPV.SignVote(chainid, vote) - precommits[i] = vote.CommitSig() - } - - return tmtypes.SignedHeader{ - Header: &header, - Commit: &tmtypes.Commit{ - BlockID: tmtypes.BlockID{ - Hash: header.Hash(), - }, - Precommits: precommits, - }, - } -} - -// Mutate valset -func (vals MockValidators) Mutate() MockValidators { - num := len(vals) / 20 // 5% change each block - - res := make(MockValidators, len(vals)) - - for i := 0; i < len(vals)-num; i++ { - res[i] = vals[num:][i] - } - - for i := len(vals) - num; i < len(vals); i++ { - res[i] = NewMockValidator(vals[0].Power) - } - - sort.Sort(res) - - for i, val := range vals { - if val != res[i] { - return res - } - } - - panic("not mutated") -} - -func (vals MockValidators) ValidatorSet() *tmtypes.ValidatorSet { - tmvals := make([]*tmtypes.Validator, len(vals)) - - for i, val := range vals { - tmvals[i] = val.Validator() - } - - return tmtypes.NewValidatorSet(tmvals) -} diff --git a/x/ibc/module.go b/x/ibc/module.go index 5a56db96877f..b250e9c355ef 100644 --- a/x/ibc/module.go +++ b/x/ibc/module.go @@ -13,7 +13,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" - ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" "github.com/cosmos/cosmos-sdk/x/ibc/client/cli" "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -37,7 +37,7 @@ func (AppModuleBasic) Name() string { // RegisterCodec registers the staking module's types for the given codec. func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { client.RegisterCodec(cdc) - ics23.RegisterCodec(cdc) + commitment.RegisterCodec(cdc) } // DefaultGenesis returns default genesis state as raw bytes for the staking @@ -53,7 +53,7 @@ func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { // RegisterRESTRoutes registers the REST routes for the staking module. func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { - //noop + /// TODO: } // GetTxCmd returns the root tx command for the staking module. From 40fca1a82f8ede9c586ebb757ed697010d55fcf2 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 22 Oct 2019 14:59:39 +0200 Subject: [PATCH 137/166] alias --- x/ibc/23-commitment/alias.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 x/ibc/23-commitment/alias.go diff --git a/x/ibc/23-commitment/alias.go b/x/ibc/23-commitment/alias.go new file mode 100644 index 000000000000..4c40bbfed20b --- /dev/null +++ b/x/ibc/23-commitment/alias.go @@ -0,0 +1,30 @@ +package commitment + +import ( + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/keeper" + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" +) + +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/keeper +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types + +var ( + // functions aliases + NewKeeper = keeper.NewKeeper + RegisterCodec = types.RegisterCodec + NewRoot = types.NewRoot + NewPrefix = types.NewPrefix + NewPath = types.NewPath + ApplyPrefix = types.ApplyPrefix +) + +type ( + Keeper = keeper.Keeper + Root = types.Root + Prefix = types.Prefix + Path = types.Path + Proof = types.Proof +) From deceeb8633d77322de3887e724e1e27826a80ccc Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 22 Oct 2019 15:05:35 +0200 Subject: [PATCH 138/166] minor updates from ICS23 --- x/ibc/02-client/alias.go | 1 - x/ibc/02-client/keeper/keeper.go | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/x/ibc/02-client/alias.go b/x/ibc/02-client/alias.go index a93de15094b7..b282922159d2 100644 --- a/x/ibc/02-client/alias.go +++ b/x/ibc/02-client/alias.go @@ -28,7 +28,6 @@ const ( QuerierRoute = types.QuerierRoute QueryClientState = types.QueryClientState QueryConsensusState = types.QueryConsensusState - QueryCommitmentPath = types.QueryCommitmentPath QueryCommitmentRoot = types.QueryCommitmentRoot ) diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index feef68f3a7f3..6b31b58e98ee 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -12,8 +12,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" commitmentexported "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" - commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -169,8 +169,8 @@ func (k Keeper) VerifyMembership( return false } - prefix := commitmenttypes.NewPrefix(k.prefix) - path := commitmenttypes.ApplyPrefix(prefix, pathStr) + prefix := commitment.NewPrefix(k.prefix) + path := commitment.ApplyPrefix(prefix, pathStr) return proof.VerifyMembership(root, path, value) } @@ -192,8 +192,8 @@ func (k Keeper) VerifyNonMembership( return false } - prefix := commitmenttypes.NewPrefix(k.prefix) - path := commitmenttypes.ApplyPrefix(prefix, pathStr) + prefix := commitment.NewPrefix(k.prefix) + path := commitment.ApplyPrefix(prefix, pathStr) return proof.VerifyNonMembership(root, path) } From 2a1a14426748489d742ae88d8bf05a8126d34a07 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 22 Oct 2019 15:06:06 +0200 Subject: [PATCH 139/166] renaming --- x/ibc/02-client/types/tendermint/consensus_state.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/ibc/02-client/types/tendermint/consensus_state.go b/x/ibc/02-client/types/tendermint/consensus_state.go index 6a369433c9f5..2f8ce8abbc84 100644 --- a/x/ibc/02-client/types/tendermint/consensus_state.go +++ b/x/ibc/02-client/types/tendermint/consensus_state.go @@ -8,8 +8,8 @@ import ( tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" commitmentexported "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" - commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" ) var _ exported.ConsensusState = ConsensusState{} @@ -84,7 +84,7 @@ func (cs ConsensusState) checkValidity(header Header) error { // update the consensus state from a new header func (cs ConsensusState) update(header Header) ConsensusState { cs.Height = header.GetHeight() - cs.Root = commitmenttypes.NewRoot(header.AppHash) + cs.Root = commitment.NewRoot(header.AppHash) cs.NextValidatorSet = header.NextValidatorSet return cs } From 714e6e2ab9150843984dc0ee704ff75c03dfd5aa Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 22 Oct 2019 16:23:22 +0200 Subject: [PATCH 140/166] update verification and rename root funcs --- x/ibc/02-client/client/cli/query.go | 4 ++-- x/ibc/02-client/keeper/client.go | 4 ++-- x/ibc/02-client/keeper/keeper.go | 25 ++++++++++--------------- x/ibc/02-client/keeper/querier.go | 2 +- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index 1e2e4f1ef5b2..75d20c41ccd7 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -83,9 +83,9 @@ $ %s query ibc client state [client-id] func GetCmdQueryRoot(storeKey string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "root [client-id] [height]", - Short: "Query stored root", + Short: "Query a verified commitment root", Long: strings.TrimSpace( - fmt.Sprintf(`Query a stored commitment root at a specific height for a particular client + fmt.Sprintf(`Query an already verified commitment root at a specific height for a particular client Example: $ %s query ibc client root [client-id] [height] diff --git a/x/ibc/02-client/keeper/client.go b/x/ibc/02-client/keeper/client.go index de26aa6c6591..71d3bb3eb195 100644 --- a/x/ibc/02-client/keeper/client.go +++ b/x/ibc/02-client/keeper/client.go @@ -31,7 +31,7 @@ func (k Keeper) CreateClient( } clientState := k.initialize(ctx, clientID, consensusState) - k.SetCommitmentRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) + k.SetVerifiedRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) k.SetClientState(ctx, clientState) k.SetClientType(ctx, clientID, clientType) return clientState, nil @@ -78,7 +78,7 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H } k.SetConsensusState(ctx, clientID, consensusState) - k.SetCommitmentRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) + k.SetVerifiedRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) k.Logger(ctx).Info(fmt.Sprintf("client %s updated to height %d", clientID, consensusState.GetHeight())) return nil } diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index 6b31b58e98ee..1469544ab530 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -12,7 +12,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" - commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" commitmentexported "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -98,8 +97,9 @@ func (k Keeper) SetConsensusState(ctx sdk.Context, clientID string, consensusSta store.Set(types.KeyClientState(clientID), bz) } -// GetCommitmentRoot gets a commitment Root from a particular height to a client -func (k Keeper) GetCommitmentRoot(ctx sdk.Context, clientID string, height uint64) (commitmentexported.RootI, bool) { +// GetVerifiedRoot gets a verified commitment Root from a particular height to +// a client +func (k Keeper) GetVerifiedRoot(ctx sdk.Context, clientID string, height uint64) (commitmentexported.RootI, bool) { store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) bz := store.Get(types.KeyRoot(clientID, height)) if bz == nil { @@ -111,8 +111,9 @@ func (k Keeper) GetCommitmentRoot(ctx sdk.Context, clientID string, height uint6 return root, true } -// SetCommitmentRoot sets a commitment Root from a particular height to a client -func (k Keeper) SetCommitmentRoot(ctx sdk.Context, clientID string, height uint64, root commitmentexported.RootI) { +// SetVerifiedRoot sets a verified commitment Root from a particular height to +// a client +func (k Keeper) SetVerifiedRoot(ctx sdk.Context, clientID string, height uint64, root commitmentexported.RootI) { store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) bz := k.cdc.MustMarshalBinaryLengthPrefixed(root) store.Set(types.KeyRoot(clientID, height), bz) @@ -157,21 +158,18 @@ func (k Keeper) VerifyMembership( clientState types.ClientState, height uint64, // sequence proof commitmentexported.ProofI, - pathStr string, + path commitmentexported.PathI, value []byte, ) bool { if clientState.Frozen { return false } - root, found := k.GetCommitmentRoot(ctx, clientState.ID(), height) + root, found := k.GetVerifiedRoot(ctx, clientState.ID(), height) if !found { return false } - prefix := commitment.NewPrefix(k.prefix) - path := commitment.ApplyPrefix(prefix, pathStr) - return proof.VerifyMembership(root, path, value) } @@ -181,19 +179,16 @@ func (k Keeper) VerifyNonMembership( clientState types.ClientState, height uint64, // sequence proof commitmentexported.ProofI, - pathStr string, + path commitmentexported.PathI, ) bool { if clientState.Frozen { return false } - root, found := k.GetCommitmentRoot(ctx, clientState.ID(), height) + root, found := k.GetVerifiedRoot(ctx, clientState.ID(), height) if !found { return false } - prefix := commitment.NewPrefix(k.prefix) - path := commitment.ApplyPrefix(prefix, pathStr) - return proof.VerifyNonMembership(root, path) } diff --git a/x/ibc/02-client/keeper/querier.go b/x/ibc/02-client/keeper/querier.go index dc52cdd20f4e..24172c087f43 100644 --- a/x/ibc/02-client/keeper/querier.go +++ b/x/ibc/02-client/keeper/querier.go @@ -77,7 +77,7 @@ func queryCommitmentRoot(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]by return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) } - root, found := k.GetCommitmentRoot(ctx, params.ClientID, params.Height) + root, found := k.GetVerifiedRoot(ctx, params.ClientID, params.Height) if !found { return nil, types.ErrRootNotFound(k.codespace) } From de13f5acd4416fcf6add8367c9b74c155f4ca3b7 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 22 Oct 2019 18:25:08 +0200 Subject: [PATCH 141/166] move querier to x/ibc --- x/ibc/02-client/alias.go | 6 ++++-- x/ibc/02-client/keeper/querier.go | 27 ++++++--------------------- x/ibc/02-client/types/querier.go | 2 +- x/ibc/alias.go | 3 ++- x/ibc/keeper/querier.go | 24 ++++++++++++++++++++++++ x/ibc/module.go | 3 +-- 6 files changed, 38 insertions(+), 27 deletions(-) create mode 100644 x/ibc/keeper/querier.go diff --git a/x/ibc/02-client/alias.go b/x/ibc/02-client/alias.go index b282922159d2..44eef2a19e8e 100644 --- a/x/ibc/02-client/alias.go +++ b/x/ibc/02-client/alias.go @@ -28,13 +28,15 @@ const ( QuerierRoute = types.QuerierRoute QueryClientState = types.QueryClientState QueryConsensusState = types.QueryConsensusState - QueryCommitmentRoot = types.QueryCommitmentRoot + QueryVerifiedRoot = types.QueryVerifiedRoot ) var ( // functions aliases NewKeeper = keeper.NewKeeper - NewQuerier = keeper.NewQuerier + QuerierClientState = keeper.QuerierClientState + QuerierConsensusState = keeper.QuerierConsensusState + QuerierVerifiedRoot = keeper.QuerierVerifiedRoot RegisterCodec = types.RegisterCodec ErrClientExists = types.ErrClientExists ErrClientNotFound = types.ErrClientNotFound diff --git a/x/ibc/02-client/keeper/querier.go b/x/ibc/02-client/keeper/querier.go index 24172c087f43..8c9ce5a263ef 100644 --- a/x/ibc/02-client/keeper/querier.go +++ b/x/ibc/02-client/keeper/querier.go @@ -9,23 +9,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" ) -// NewQuerier creates a querier for the IBC client -func NewQuerier(k Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { - switch path[0] { - case types.QueryClientState: - return queryClientState(ctx, req, k) - case types.QueryConsensusState: - return queryConsensusState(ctx, req, k) - case types.QueryCommitmentRoot: - return queryCommitmentRoot(ctx, req, k) - default: - return nil, sdk.ErrUnknownRequest("unknown IBC client query endpoint") - } - } -} - -func queryClientState(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { +// QuerierClientState defines the sdk.Querier to query the IBC client state +func QuerierClientState(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { var params types.QueryClientStateParams err := types.SubModuleCdc.UnmarshalJSON(req.Data, ¶ms) @@ -33,8 +18,6 @@ func queryClientState(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) } - // NOTE: clientID won't be exported as it's declared as private - // TODO: should we create a custom ExportedClientState to make it public ? clientState, found := k.GetClientState(ctx, params.ClientID) if !found { return nil, types.ErrClientTypeNotFound(k.codespace) @@ -48,7 +31,8 @@ func queryClientState(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, return bz, nil } -func queryConsensusState(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { +// QuerierConsensusState defines the sdk.Querier to query a consensus state +func QuerierConsensusState(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { var params types.QueryClientStateParams err := types.SubModuleCdc.UnmarshalJSON(req.Data, ¶ms) @@ -69,7 +53,8 @@ func queryConsensusState(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]by return bz, nil } -func queryCommitmentRoot(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { +// QuerierVerifiedRoot defines the sdk.Querier to query a verified commitment root +func QuerierVerifiedRoot(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { var params types.QueryCommitmentRootParams err := types.SubModuleCdc.UnmarshalJSON(req.Data, ¶ms) diff --git a/x/ibc/02-client/types/querier.go b/x/ibc/02-client/types/querier.go index eff5bce07b67..2f5de5af0572 100644 --- a/x/ibc/02-client/types/querier.go +++ b/x/ibc/02-client/types/querier.go @@ -4,7 +4,7 @@ package types const ( QueryClientState = "client_state" QueryConsensusState = "consensus_state" - QueryCommitmentRoot = "roots" + QueryVerifiedRoot = "roots" ) // QueryClientStateParams defines the params for the following queries: diff --git a/x/ibc/alias.go b/x/ibc/alias.go index d71d90640ab2..7c1ad02349e2 100644 --- a/x/ibc/alias.go +++ b/x/ibc/alias.go @@ -20,7 +20,8 @@ const ( var ( // functions aliases - NewKeeper = keeper.NewKeeper + NewKeeper = keeper.NewKeeper + NewQuerier = keeper.NewQuerier ) type ( diff --git a/x/ibc/keeper/querier.go b/x/ibc/keeper/querier.go new file mode 100644 index 000000000000..7cbd4ec9c499 --- /dev/null +++ b/x/ibc/keeper/querier.go @@ -0,0 +1,24 @@ +package keeper + +import ( + abci "github.com/tendermint/tendermint/abci/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" +) + +// NewQuerier creates a querier for the IBC module +func NewQuerier(k Keeper) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + switch path[0] { + case client.QueryClientState: + return client.QuerierClientState(ctx, req, k.ClientKeeper) + case client.QueryConsensusState: + return client.QuerierConsensusState(ctx, req, k.ClientKeeper) + case client.QueryVerifiedRoot: + return client.QuerierVerifiedRoot(ctx, req, k.ClientKeeper) + default: + return nil, sdk.ErrUnknownRequest("unknown IBC client query endpoint") + } + } +} diff --git a/x/ibc/module.go b/x/ibc/module.go index b250e9c355ef..675b18aa1ddf 100644 --- a/x/ibc/module.go +++ b/x/ibc/module.go @@ -106,8 +106,7 @@ func (AppModule) QuerierRoute() string { // NewQuerierHandler returns the staking module sdk.Querier. func (am AppModule) NewQuerierHandler() sdk.Querier { - // return NewQuerier(am.keeper - return nil + return NewQuerier(am.keeper) } // InitGenesis performs genesis initialization for the staking module. It returns From aae117f5ecbfd9da52c273d729d2944da542c1f6 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 22 Oct 2019 18:37:31 +0200 Subject: [PATCH 142/166] update query.go to use 'custom/...' query path --- x/ibc/02-client/alias.go | 2 +- x/ibc/02-client/client/cli/query.go | 22 +++++++++++----------- x/ibc/02-client/keeper/client.go | 6 +++--- x/ibc/02-client/keeper/keeper.go | 20 ++++++++++---------- x/ibc/02-client/types/state.go | 12 ++++++------ 5 files changed, 31 insertions(+), 31 deletions(-) diff --git a/x/ibc/02-client/alias.go b/x/ibc/02-client/alias.go index 44eef2a19e8e..1d82de808c50 100644 --- a/x/ibc/02-client/alias.go +++ b/x/ibc/02-client/alias.go @@ -76,5 +76,5 @@ type ( MsgSubmitMisbehaviour = types.MsgSubmitMisbehaviour QueryClientStateParams = types.QueryClientStateParams QueryCommitmentRootParams = types.QueryCommitmentRootParams - ClientState = types.ClientState + State = types.State ) diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index 75d20c41ccd7..9fbecbd2aebd 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -21,7 +21,7 @@ import ( ) // GetQueryCmd returns the query commands for IBC clients -func GetQueryCmd(queryRouter string, cdc *codec.Codec) *cobra.Command { +func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { ics02ClientQueryCmd := &cobra.Command{ Use: "client", Short: "IBC client query subcommands", @@ -30,17 +30,17 @@ func GetQueryCmd(queryRouter string, cdc *codec.Codec) *cobra.Command { } ics02ClientQueryCmd.AddCommand(client.GetCommands( - GetCmdQueryConsensusState(queryRouter, cdc), + GetCmdQueryConsensusState(queryRoute, cdc), GetCmdQueryHeader(cdc), - GetCmdQueryClientState(queryRouter, cdc), - GetCmdQueryRoot(queryRouter, cdc), + GetCmdQueryClientState(queryRoute, cdc), + GetCmdQueryRoot(queryRoute, cdc), )...) return ics02ClientQueryCmd } // GetCmdQueryClientState defines the command to query the state of a client with // a given id as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#query -func GetCmdQueryClientState(storeKey string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryClientState(queryRoute string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "state [client-id]", Short: "Query a client state", @@ -64,12 +64,12 @@ $ %s query ibc client state [client-id] return err } - res, _, err := cliCtx.QueryWithData(types.ClientStatePath(clientID), bz) + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryClientState), bz) if err != nil { return err } - var clientState types.ClientState + var clientState types.State if err := cdc.UnmarshalJSON(res, &clientState); err != nil { return err } @@ -80,7 +80,7 @@ $ %s query ibc client state [client-id] } // GetCmdQueryRoot defines the command to query -func GetCmdQueryRoot(storeKey string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryRoot(queryRoute string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "root [client-id] [height]", Short: "Query a verified commitment root", @@ -109,7 +109,7 @@ $ %s query ibc client root [client-id] [height] return err } - res, _, err := cliCtx.QueryWithData(types.RootPath(clientID, height), bz) + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryVerifiedRoot), bz) if err != nil { return err } @@ -126,7 +126,7 @@ $ %s query ibc client root [client-id] [height] // GetCmdQueryConsensusState defines the command to query the consensus state of // the chain as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#query -func GetCmdQueryConsensusState(storeKey string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryConsensusState(queryRoute string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "consensus-state [client-id]", Short: "Query the latest consensus state of the client", @@ -150,7 +150,7 @@ $ %s query ibc client consensus-state [client-id] return err } - res, _, err := cliCtx.QueryWithData(types.ConsensusStatePath(clientID), bz) + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryConsensusState), bz) if err != nil { return err } diff --git a/x/ibc/02-client/keeper/client.go b/x/ibc/02-client/keeper/client.go index 71d3bb3eb195..26a0469b1038 100644 --- a/x/ibc/02-client/keeper/client.go +++ b/x/ibc/02-client/keeper/client.go @@ -14,10 +14,10 @@ import ( func (k Keeper) CreateClient( ctx sdk.Context, clientID string, clientTypeStr string, consensusState exported.ConsensusState, -) (types.ClientState, error) { +) (types.State, error) { _, found := k.GetClientState(ctx, clientID) if found { - return types.ClientState{}, types.ErrClientExists(k.codespace, clientID) + return types.State{}, types.ErrClientExists(k.codespace, clientID) } _, found = k.GetClientType(ctx, clientID) @@ -27,7 +27,7 @@ func (k Keeper) CreateClient( clientType := exported.ClientTypeFromStr(clientTypeStr) if clientType == 0 { - return types.ClientState{}, types.ErrInvalidClientType(k.codespace) + return types.State{}, types.ErrInvalidClientType(k.codespace) } clientState := k.initialize(ctx, clientID, consensusState) diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index 1469544ab530..6c5c4f1d1c78 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -41,20 +41,20 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { } // GetClientState gets a particular client from the store -func (k Keeper) GetClientState(ctx sdk.Context, clientID string) (types.ClientState, bool) { +func (k Keeper) GetClientState(ctx sdk.Context, clientID string) (types.State, bool) { store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) bz := store.Get(types.KeyClientState(clientID)) if bz == nil { - return types.ClientState{}, false + return types.State{}, false } - var clientState types.ClientState + var clientState types.State k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &clientState) return clientState, true } // SetClientState sets a particular Client to the store -func (k Keeper) SetClientState(ctx sdk.Context, clientState types.ClientState) { +func (k Keeper) SetClientState(ctx sdk.Context, clientState types.State) { store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) bz := k.cdc.MustMarshalBinaryLengthPrefixed(clientState) store.Set(types.KeyClientState(clientState.ID()), bz) @@ -119,9 +119,9 @@ func (k Keeper) SetVerifiedRoot(ctx sdk.Context, clientID string, height uint64, store.Set(types.KeyRoot(clientID, height), bz) } -// ClientState returns a new client state with a given id as defined in +// State returns a new client state with a given id as defined in // https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#example-implementation -func (k Keeper) initialize(ctx sdk.Context, clientID string, consensusState exported.ConsensusState) types.ClientState { +func (k Keeper) initialize(ctx sdk.Context, clientID string, consensusState exported.ConsensusState) types.State { clientState := types.NewClientState(clientID) k.SetConsensusState(ctx, clientID, consensusState) return clientState @@ -143,9 +143,9 @@ func (k Keeper) checkMisbehaviour(ctx sdk.Context, evidence exported.Evidence) e } // freeze updates the state of the client in the event of a misbehaviour -func (k Keeper) freeze(ctx sdk.Context, clientState types.ClientState) (types.ClientState, error) { +func (k Keeper) freeze(ctx sdk.Context, clientState types.State) (types.State, error) { if clientState.Frozen { - return types.ClientState{}, sdkerrors.Wrap(types.ErrClientFrozen(k.codespace, clientState.ID()), "already frozen") + return types.State{}, sdkerrors.Wrap(types.ErrClientFrozen(k.codespace, clientState.ID()), "already frozen") } clientState.Frozen = true @@ -155,7 +155,7 @@ func (k Keeper) freeze(ctx sdk.Context, clientState types.ClientState) (types.Cl // VerifyMembership state membership verification function defined by the client type func (k Keeper) VerifyMembership( ctx sdk.Context, - clientState types.ClientState, + clientState types.State, height uint64, // sequence proof commitmentexported.ProofI, path commitmentexported.PathI, @@ -176,7 +176,7 @@ func (k Keeper) VerifyMembership( // VerifyNonMembership state non-membership function defined by the client type func (k Keeper) VerifyNonMembership( ctx sdk.Context, - clientState types.ClientState, + clientState types.State, height uint64, // sequence proof commitmentexported.ProofI, path commitmentexported.PathI, diff --git a/x/ibc/02-client/types/state.go b/x/ibc/02-client/types/state.go index d900908b0018..8c60260df0a2 100644 --- a/x/ibc/02-client/types/state.go +++ b/x/ibc/02-client/types/state.go @@ -1,8 +1,8 @@ package types -// ClientState is a type that represents the state of a client. +// State is a type that represents the state of a client. // Any actor holding the Stage can access on and modify that client information. -type ClientState struct { +type State struct { // Client ID id string // Boolean that states if the client is frozen when a misbehaviour proof is @@ -11,14 +11,14 @@ type ClientState struct { } // NewClientState creates a new ClientState instance -func NewClientState(id string) ClientState { - return ClientState{ +func NewClientState(id string) State { + return State{ id: id, Frozen: false, } } // ID returns the client identifier -func (cs ClientState) ID() string { - return cs.id +func (s State) ID() string { + return s.id } From f8fd580040fe3635d936d529107e7a80afd8be5f Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Tue, 22 Oct 2019 11:31:52 -0700 Subject: [PATCH 143/166] add tests --- x/ibc/23-commitment/keeper/keeper_test.go | 1 + x/ibc/23-commitment/types/merkle_test.go | 40 +++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 x/ibc/23-commitment/keeper/keeper_test.go create mode 100644 x/ibc/23-commitment/types/merkle_test.go diff --git a/x/ibc/23-commitment/keeper/keeper_test.go b/x/ibc/23-commitment/keeper/keeper_test.go new file mode 100644 index 000000000000..9429264902a9 --- /dev/null +++ b/x/ibc/23-commitment/keeper/keeper_test.go @@ -0,0 +1 @@ +package keeper_test diff --git a/x/ibc/23-commitment/types/merkle_test.go b/x/ibc/23-commitment/types/merkle_test.go new file mode 100644 index 000000000000..1b69f05e5b1e --- /dev/null +++ b/x/ibc/23-commitment/types/merkle_test.go @@ -0,0 +1,40 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/store/iavl" + "github.com/cosmos/cosmos-sdk/store/rootmulti" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" + + abci "github.com/tendermint/tendermint/abci/types" + dbm "github.com/tendermint/tm-db" +) + +func TestVerifyMembership(t *testing.T) { + db := dbm.NewMemDB() + store := rootmulti.NewStore(db) + + iavlStoreKey := storetypes.NewKVStoreKey("iavlStoreKey") + + store.MountStoreWithDB(iavlStoreKey, storetypes.StoreTypeIAVL, nil) + store.LoadVersion(0) + + iavlStore := store.GetCommitStore(iavlStoreKey).(*iavl.Store) + iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) + + res := store.Query(abci.RequestQuery{ + Path: "/iavlStoreKey/key", // required path to get key/value+proof + Data: []byte("MYKEY"), + Prove: true, + }) + require.NotNil(t, res.Proof) + + proof := types.Proof{ + Proof: res.Proof, + } + +} From cbc11061006de1cbdf4929eb104036af7e31991e Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 22 Oct 2019 17:42:22 -0400 Subject: [PATCH 144/166] ICS 24 Implementation (#5229) * add validation functions * validate path in ics-23 * address @fede comments * move errors into host package --- types/errors/errors.go | 6 +++ x/ibc/23-commitment/keeper/keeper.go | 10 +++- x/ibc/23-commitment/types/merkle.go | 9 +++- x/ibc/24-host/errors.go | 16 ++++++ x/ibc/24-host/validate.go | 74 ++++++++++++++++++++++++++++ 5 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 x/ibc/24-host/errors.go create mode 100644 x/ibc/24-host/validate.go diff --git a/types/errors/errors.go b/types/errors/errors.go index 642c433d90b2..370e6c591469 100644 --- a/types/errors/errors.go +++ b/types/errors/errors.go @@ -65,6 +65,12 @@ var ( // ErrNoSignatures to doc ErrNoSignatures = Register(RootCodespace, 16, "no signatures supplied") + // ErrInvalidId to doc + ErrInvalidID = Register(RootCodespace, 17, "invalid identifier") + + // ErrInvalidPath to doc + ErrInvalidPath = Register(RootCodespace, 18, "invalid path") + // ErrPanic is only set when we recover from a panic, so we know to // redact potentially sensitive system info ErrPanic = Register(UndefinedCodespace, 111222, "panic") diff --git a/x/ibc/23-commitment/keeper/keeper.go b/x/ibc/23-commitment/keeper/keeper.go index 62790e6b83cb..eb1a2dfb8c60 100644 --- a/x/ibc/23-commitment/keeper/keeper.go +++ b/x/ibc/23-commitment/keeper/keeper.go @@ -45,7 +45,10 @@ func (k Keeper) BatchVerifyMembership(ctx sdk.Context, proof exported.ProofI, it continue } - path := types.ApplyPrefix(k.prefix, pathStr) + path, err := types.ApplyPrefix(k.prefix, pathStr) + if err != nil { + return false + } ok = proof.VerifyMembership(root, path, value) if !ok { return false @@ -69,7 +72,10 @@ func (k Keeper) BatchVerifyNonMembership(ctx sdk.Context, proof exported.ProofI, continue } - path := types.ApplyPrefix(k.prefix, pathStr) + path, err := types.ApplyPrefix(k.prefix, pathStr) + if err != nil { + return false + } ok = proof.VerifyNonMembership(root, path) if !ok { return false diff --git a/x/ibc/23-commitment/types/merkle.go b/x/ibc/23-commitment/types/merkle.go index 0a61a997213b..95ac011f9936 100644 --- a/x/ibc/23-commitment/types/merkle.go +++ b/x/ibc/23-commitment/types/merkle.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/rootmulti" "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" + host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" ) // ICS 023 Merkle Types Implementation @@ -103,13 +104,17 @@ func (p Path) String() string { // // CONTRACT: provided path string MUST be a well formated path. See ICS24 for // reference. -func ApplyPrefix(prefix exported.PrefixI, path string) Path { +func ApplyPrefix(prefix exported.PrefixI, path string) (Path, error) { + err := host.DefaultPathValidator(path) + if err != nil { + return Path{}, err + } // Split paths by the separator pathSlice := strings.Split(path, "/") commitmentPath := NewPath(pathSlice) commitmentPath.KeyPath = commitmentPath.KeyPath.AppendKey(prefix.Bytes(), merkle.KeyEncodingHex) - return commitmentPath + return commitmentPath, nil } var _ exported.ProofI = Proof{} diff --git a/x/ibc/24-host/errors.go b/x/ibc/24-host/errors.go new file mode 100644 index 000000000000..e1fa5e0e7a08 --- /dev/null +++ b/x/ibc/24-host/errors.go @@ -0,0 +1,16 @@ +package host + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// IBCCodeSpace is the codespace for all errors defined in the ibc module +const IBCCodeSpace = "ibc" + +var ( + // ErrInvalidID is returned if identifier string is invalid + ErrInvalidID = sdkerrors.Register(IBCCodeSpace, 1, "invalid identifier") + + // ErrInvalidPath is returned if path string is invalid + ErrInvalidPath = sdkerrors.Register(IBCCodeSpace, 2, "invalid path") +) diff --git a/x/ibc/24-host/validate.go b/x/ibc/24-host/validate.go new file mode 100644 index 000000000000..00dd5f8ba99e --- /dev/null +++ b/x/ibc/24-host/validate.go @@ -0,0 +1,74 @@ +package host + +import ( + "regexp" + "strings" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// ICS 024 Identifier and Path Validation Implementation +// +// This file defines ValidateFn to validate identifier and path strings +// The spec for ICS 024 can be located here: +// https://github.com/cosmos/ics/tree/master/spec/ics-024-host-requirements + +// regular expression to check string is lowercase alphabetic characters only +var isAlphaLower = regexp.MustCompile(`^[a-z]+$`).MatchString + +// regular expression to check string is alphanumeric +var isAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString + +// ValidateFn function type to validate path and identifier bytestrings +type ValidateFn func(string) error + +// Default validator function for Client, Connection, and Channel +// identifiers +// Valid Identifier must be between 10-20 characters and only +// contain lowercase alphabetic characters +func DefaultIdentifierValidator(id string) error { + // valid id MUST NOT contain "/" separator + if strings.Contains(id, "/") { + return sdkerrors.Wrap(ErrInvalidID, "identifier cannot contain separator: /") + } + // valid id must be between 10 and 20 characters + if len(id) < 10 || len(id) > 20 { + return sdkerrors.Wrapf(ErrInvalidID, "identifier has invalid length: %d, must be between 10-20 characters", len(id)) + } + // valid id must contain only lower alphabetic characters + if !isAlphaLower(id) { + return sdkerrors.Wrap(ErrInvalidID, "identifier must contain only lowercase alphabetic characters") + } + return nil +} + +// NewPathValidator takes in a Identifier Validator function and returns +// a Path Validator function which requires path only has valid identifiers +// alphanumeric character strings, and "/" separators +func NewPathValidator(idValidator ValidateFn) ValidateFn { + return func(path string) error { + pathArr := strings.Split(path, "/") + for _, p := range pathArr { + // Each path element must either be valid identifier or alphanumeric + err := idValidator(p) + if err != nil && !isAlphaNumeric(p) { + return sdkerrors.Wrapf(ErrInvalidPath, "path contains invalid identifier or non-alphanumeric path element: %s", p) + } + } + return nil + } +} + +// Default Path Validator takes in path string and validates +// with default identifier rules. This is optimized by simply +// checking that all path elements are alphanumeric +func DefaultPathValidator(path string) error { + pathArr := strings.Split(path, "/") + for _, p := range pathArr { + // Each path element must either be alphanumeric + if !isAlphaNumeric(p) { + return sdkerrors.Wrapf(ErrInvalidPath, "invalid path element containing non-alphanumeric characters: %s", p) + } + } + return nil +} From 23ae99489ab18b2dbb7dff983e3f557660c2f9c6 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 23 Oct 2019 12:27:12 +0200 Subject: [PATCH 145/166] flatten ICS23 structure --- x/ibc/23-commitment/alias.go | 30 ------- x/ibc/23-commitment/{types => }/codec.go | 11 ++- x/ibc/23-commitment/keeper/keeper.go | 88 ------------------- x/ibc/23-commitment/{types => }/merkle.go | 46 +++++----- .../{exported/exported.go => types.go} | 34 +++++-- x/ibc/23-commitment/types/utils.go | 8 -- x/ibc/23-commitment/verify.go | 62 +++++++++++++ x/ibc/24-host/validate.go | 10 +-- 8 files changed, 122 insertions(+), 167 deletions(-) delete mode 100644 x/ibc/23-commitment/alias.go rename x/ibc/23-commitment/{types => }/codec.go (59%) delete mode 100644 x/ibc/23-commitment/keeper/keeper.go rename x/ibc/23-commitment/{types => }/merkle.go (77%) rename x/ibc/23-commitment/{exported/exported.go => types.go} (69%) delete mode 100644 x/ibc/23-commitment/types/utils.go create mode 100644 x/ibc/23-commitment/verify.go diff --git a/x/ibc/23-commitment/alias.go b/x/ibc/23-commitment/alias.go deleted file mode 100644 index 4c40bbfed20b..000000000000 --- a/x/ibc/23-commitment/alias.go +++ /dev/null @@ -1,30 +0,0 @@ -package commitment - -import ( - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/keeper" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" -) - -// nolint -// autogenerated code using github.com/rigelrozanski/multitool -// aliases generated for the following subdirectories: -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/keeper -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types - -var ( - // functions aliases - NewKeeper = keeper.NewKeeper - RegisterCodec = types.RegisterCodec - NewRoot = types.NewRoot - NewPrefix = types.NewPrefix - NewPath = types.NewPath - ApplyPrefix = types.ApplyPrefix -) - -type ( - Keeper = keeper.Keeper - Root = types.Root - Prefix = types.Prefix - Path = types.Path - Proof = types.Proof -) diff --git a/x/ibc/23-commitment/types/codec.go b/x/ibc/23-commitment/codec.go similarity index 59% rename from x/ibc/23-commitment/types/codec.go rename to x/ibc/23-commitment/codec.go index c2b96c48ad3f..7e90e8cac69e 100644 --- a/x/ibc/23-commitment/types/codec.go +++ b/x/ibc/23-commitment/codec.go @@ -1,16 +1,15 @@ -package types +package commitment import ( "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" ) // RegisterCodec registers types declared in this package func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterInterface((*exported.RootI)(nil), nil) - cdc.RegisterInterface((*exported.PrefixI)(nil), nil) - cdc.RegisterInterface((*exported.PathI)(nil), nil) - cdc.RegisterInterface((*exported.ProofI)(nil), nil) + cdc.RegisterInterface((*RootI)(nil), nil) + cdc.RegisterInterface((*PrefixI)(nil), nil) + cdc.RegisterInterface((*PathI)(nil), nil) + cdc.RegisterInterface((*ProofI)(nil), nil) cdc.RegisterConcrete(Root{}, "ibc/commitment/merkle/Root", nil) cdc.RegisterConcrete(Prefix{}, "ibc/commitment/merkle/Prefix", nil) diff --git a/x/ibc/23-commitment/keeper/keeper.go b/x/ibc/23-commitment/keeper/keeper.go deleted file mode 100644 index eb1a2dfb8c60..000000000000 --- a/x/ibc/23-commitment/keeper/keeper.go +++ /dev/null @@ -1,88 +0,0 @@ -package keeper - -import ( - "bytes" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" -) - -// Keeper defines the IBC commitment keeper (i.e the vector commitment manager). -// A vector commitment manager has the ability to add or remove items from the -// commitment state as defined in https://github.com/cosmos/ics/tree/master/spec/ics-023-vector-commitments#definitions. -type Keeper struct { - prefix exported.PrefixI - verifiedMemberships map[string][]byte // lookup map for returning already verified membership proofs - verifiedAbsences map[string]bool // lookup map for returning already verified absences -} - -// NewKeeper returns a new Keeper -func NewKeeper(prefix exported.PrefixI) Keeper { - return Keeper{ - prefix: prefix, - verifiedMemberships: make(map[string][]byte), - verifiedAbsences: make(map[string]bool), - } -} - -// CalculateRoot returns the application Hash at the curretn block height as a commitment -// root for proof verification. -func (k Keeper) CalculateRoot(ctx sdk.Context) exported.RootI { - return types.NewRoot(ctx.BlockHeader().AppHash) -} - -// BatchVerifyMembership verifies a proof that many paths have been set to -// specific values in a commitment. It calls the proof's VerifyMembership method -// with the calculated root and the provided paths. -// Returns false on the first failed membership verification. -func (k Keeper) BatchVerifyMembership(ctx sdk.Context, proof exported.ProofI, items map[string][]byte) bool { - root := k.CalculateRoot(ctx) - - for pathStr, value := range items { - storedValue, ok := k.verifiedMemberships[pathStr] - if ok && bytes.Equal(storedValue, value) { - continue - } - - path, err := types.ApplyPrefix(k.prefix, pathStr) - if err != nil { - return false - } - ok = proof.VerifyMembership(root, path, value) - if !ok { - return false - } - - k.verifiedMemberships[pathStr] = value - } - - return true -} - -// BatchVerifyNonMembership verifies a proof that many paths have not been set -// to any value in a commitment. It calls the proof's VerifyNonMembership method -// with the calculated root and the provided paths. -// Returns false on the first failed non-membership verification. -func (k Keeper) BatchVerifyNonMembership(ctx sdk.Context, proof exported.ProofI, paths []string) bool { - root := k.CalculateRoot(ctx) - for _, pathStr := range paths { - ok := k.verifiedAbsences[pathStr] - if ok { - continue - } - - path, err := types.ApplyPrefix(k.prefix, pathStr) - if err != nil { - return false - } - ok = proof.VerifyNonMembership(root, path) - if !ok { - return false - } - - k.verifiedAbsences[pathStr] = true - } - - return true -} diff --git a/x/ibc/23-commitment/types/merkle.go b/x/ibc/23-commitment/merkle.go similarity index 77% rename from x/ibc/23-commitment/types/merkle.go rename to x/ibc/23-commitment/merkle.go index 95ac011f9936..980fe0e4247c 100644 --- a/x/ibc/23-commitment/types/merkle.go +++ b/x/ibc/23-commitment/merkle.go @@ -1,4 +1,4 @@ -package types +package commitment import ( "strings" @@ -6,7 +6,6 @@ import ( "github.com/tendermint/tendermint/crypto/merkle" "github.com/cosmos/cosmos-sdk/store/rootmulti" - "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" ) @@ -14,12 +13,9 @@ import ( // // This file defines Merkle commitment types that implements ICS 023. -// TODO: use iota -const merkleKind = "merkle" - -// merkle.Proof implementation of Proof +// Merkle proof implementation of the Proof interface // Applied on SDK-based IBC implementation -var _ exported.RootI = Root{} +var _ RootI = Root{} // Root defines a merkle root hash. // In the Cosmos SDK, the AppHash of a block header becomes the Root. @@ -34,9 +30,9 @@ func NewRoot(hash []byte) Root { } } -// CommitmentType implements RootI interface -func (Root) CommitmentType() string { - return merkleKind +// GetCommitmentType implements RootI interface +func (Root) GetCommitmentType() Type { + return Merkle } // GetHash implements RootI interface @@ -44,7 +40,7 @@ func (r Root) GetHash() []byte { return r.Hash } -var _ exported.PrefixI = Prefix{} +var _ PrefixI = Prefix{} // Prefix is merkle path prefixed to the key. // The constructed key from the Path and the key will be append(Path.KeyPath, append(Path.KeyPrefix, key...)) @@ -59,9 +55,9 @@ func NewPrefix(keyPrefix []byte) Prefix { } } -// CommitmentType implements PrefixI -func (Prefix) CommitmentType() string { - return merkleKind +// GetCommitmentType implements PrefixI +func (Prefix) GetCommitmentType() Type { + return Merkle } // Bytes returns the key prefix bytes @@ -69,7 +65,7 @@ func (p Prefix) Bytes() []byte { return p.KeyPrefix } -var _ exported.PathI = Path{} +var _ PathI = Path{} // Path is the path used to verify commitment proofs, which can be an arbitrary // structured object (defined by a commitment type). @@ -89,9 +85,9 @@ func NewPath(keyPathStr []string) Path { } } -// CommitmentType implements PathI -func (Path) CommitmentType() string { - return merkleKind +// GetCommitmentType implements PathI +func (Path) GetCommitmentType() Type { + return Merkle } // String implements fmt.Stringer @@ -104,7 +100,7 @@ func (p Path) String() string { // // CONTRACT: provided path string MUST be a well formated path. See ICS24 for // reference. -func ApplyPrefix(prefix exported.PrefixI, path string) (Path, error) { +func ApplyPrefix(prefix PrefixI, path string) (Path, error) { err := host.DefaultPathValidator(path) if err != nil { return Path{}, err @@ -117,7 +113,7 @@ func ApplyPrefix(prefix exported.PrefixI, path string) (Path, error) { return commitmentPath, nil } -var _ exported.ProofI = Proof{} +var _ ProofI = Proof{} // Proof is a wrapper type that contains a merkle proof. // It demonstrates membership or non-membership for an element or set of elements, @@ -127,20 +123,20 @@ type Proof struct { Proof *merkle.Proof `json:"proof" yaml:"proof"` } -// CommitmentType implements ProofI -func (Proof) CommitmentType() string { - return merkleKind +// GetCommitmentType implements ProofI +func (Proof) GetCommitmentType() Type { + return Merkle } // VerifyMembership verifies the membership pf a merkle proof against the given root, path, and value. -func (proof Proof) VerifyMembership(root exported.RootI, path exported.PathI, value []byte) bool { +func (proof Proof) VerifyMembership(root RootI, path PathI, value []byte) bool { runtime := rootmulti.DefaultProofRuntime() err := runtime.VerifyValue(proof.Proof, root.GetHash(), path.String(), value) return err == nil } // VerifyNonMembership verifies the absence of a merkle proof against the given root and path. -func (proof Proof) VerifyNonMembership(root exported.RootI, path exported.PathI) bool { +func (proof Proof) VerifyNonMembership(root RootI, path PathI) bool { runtime := rootmulti.DefaultProofRuntime() err := runtime.VerifyAbsence(proof.Proof, root.GetHash(), path.String()) return err == nil diff --git a/x/ibc/23-commitment/exported/exported.go b/x/ibc/23-commitment/types.go similarity index 69% rename from x/ibc/23-commitment/exported/exported.go rename to x/ibc/23-commitment/types.go index 9e0d8d0ef776..af33cd0b7f99 100644 --- a/x/ibc/23-commitment/exported/exported.go +++ b/x/ibc/23-commitment/types.go @@ -1,4 +1,4 @@ -package exported +package commitment // ICS 023 Types Implementation // @@ -12,21 +12,21 @@ package exported // and the inclusion or non-inclusion of an arbitrary key-value pair // can be proven with the proof. type RootI interface { - CommitmentType() string + GetCommitmentType() Type GetHash() []byte } // PrefixI implements spec:CommitmentPrefix. // Prefix represents the common "prefix" that a set of keys shares. type PrefixI interface { - CommitmentType() string + GetCommitmentType() Type Bytes() []byte } // PathI implements spec:CommitmentPath. // A path is the additional information provided to the verification function. type PathI interface { - CommitmentType() string + GetCommitmentType() Type String() string } @@ -35,7 +35,31 @@ type PathI interface { // Each proof has designated key-value pair it is able to prove. // Proofs includes key but value is provided dynamically at the verification time. type ProofI interface { - CommitmentType() string + GetCommitmentType() Type VerifyMembership(RootI, PathI, []byte) bool VerifyNonMembership(RootI, PathI) bool } + +// Type defines the type of the commitment +type Type byte + +// Registered commitment types +const ( + Merkle Type = iota + 1 // 1 +) + +// Client types +const ( + TypeMerkle string = "merkle" +) + +// TypeToString returns the string representation of a client type +func TypeToString(commitmentType Type) string { + switch commitmentType { + case Merkle: + return TypeMerkle + + default: + return "" + } +} diff --git a/x/ibc/23-commitment/types/utils.go b/x/ibc/23-commitment/types/utils.go deleted file mode 100644 index a53ca19812bd..000000000000 --- a/x/ibc/23-commitment/types/utils.go +++ /dev/null @@ -1,8 +0,0 @@ -package types - -func join(a, b []byte) (res []byte) { - res = make([]byte, len(a)+len(b)) - copy(res, a) - copy(res[len(a):], b) - return -} diff --git a/x/ibc/23-commitment/verify.go b/x/ibc/23-commitment/verify.go new file mode 100644 index 000000000000..7c620c2b5a90 --- /dev/null +++ b/x/ibc/23-commitment/verify.go @@ -0,0 +1,62 @@ +package commitment + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// CalculateRoot returns the application Hash at the curretn block height as a commitment +// root for proof verification. +func CalculateRoot(ctx sdk.Context) RootI { + return NewRoot(ctx.BlockHeader().AppHash) +} + +// BatchVerifyMembership verifies a proof that many paths have been set to +// specific values in a commitment. It calls the proof's VerifyMembership method +// with the calculated root and the provided paths. +// Returns false on the first failed membership verification. +func BatchVerifyMembership( + ctx sdk.Context, + proof ProofI, + prefix PrefixI, + items map[string][]byte, +) bool { + root := CalculateRoot(ctx) + + for pathStr, value := range items { + path, err := ApplyPrefix(prefix, pathStr) + if err != nil { + return false + } + + if ok := proof.VerifyMembership(root, path, value); !ok { + return false + } + } + + return true +} + +// BatchVerifyNonMembership verifies a proof that many paths have not been set +// to any value in a commitment. It calls the proof's VerifyNonMembership method +// with the calculated root and the provided paths. +// Returns false on the first failed non-membership verification. +func BatchVerifyNonMembership( + ctx sdk.Context, + proof ProofI, + prefix PrefixI, + paths []string, +) bool { + root := CalculateRoot(ctx) + for _, pathStr := range paths { + path, err := ApplyPrefix(prefix, pathStr) + if err != nil { + return false + } + + if ok := proof.VerifyNonMembership(root, path); !ok { + return false + } + } + + return true +} diff --git a/x/ibc/24-host/validate.go b/x/ibc/24-host/validate.go index 00dd5f8ba99e..38128de234ae 100644 --- a/x/ibc/24-host/validate.go +++ b/x/ibc/24-host/validate.go @@ -22,10 +22,10 @@ var isAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString // ValidateFn function type to validate path and identifier bytestrings type ValidateFn func(string) error -// Default validator function for Client, Connection, and Channel -// identifiers -// Valid Identifier must be between 10-20 characters and only -// contain lowercase alphabetic characters +// DefaultIdentifierValidator is the default validator function for Client, +// Connection and Channel identifiers. +// A valid Identifier must be between 10-20 characters and only contain lowercase +// alphabetic characters, func DefaultIdentifierValidator(id string) error { // valid id MUST NOT contain "/" separator if strings.Contains(id, "/") { @@ -59,7 +59,7 @@ func NewPathValidator(idValidator ValidateFn) ValidateFn { } } -// Default Path Validator takes in path string and validates +// DefaultPathValidator takes in path string and validates // with default identifier rules. This is optimized by simply // checking that all path elements are alphanumeric func DefaultPathValidator(path string) error { From e1fa5f8b884f0d1e3897f977076e13fc0f12bade Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 23 Oct 2019 12:34:14 +0200 Subject: [PATCH 146/166] fix ApplyPrefix --- x/ibc/23-commitment/merkle.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x/ibc/23-commitment/merkle.go b/x/ibc/23-commitment/merkle.go index 980fe0e4247c..0f161f01ba7f 100644 --- a/x/ibc/23-commitment/merkle.go +++ b/x/ibc/23-commitment/merkle.go @@ -107,9 +107,11 @@ func ApplyPrefix(prefix PrefixI, path string) (Path, error) { } // Split paths by the separator pathSlice := strings.Split(path, "/") + keyPath := merkle.KeyPath{} commitmentPath := NewPath(pathSlice) - commitmentPath.KeyPath = commitmentPath.KeyPath.AppendKey(prefix.Bytes(), merkle.KeyEncodingHex) + keyPath = keyPath.AppendKey(prefix.Bytes(), merkle.KeyEncodingHex) + commitmentPath.KeyPath = append(keyPath, commitmentPath.KeyPath...) return commitmentPath, nil } From 4f896026fb5ed0fbee701c4bdeb50a788d440861 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 23 Oct 2019 16:48:55 +0200 Subject: [PATCH 147/166] updates from ICS23 and ICS24 --- x/ibc/02-client/client/cli/query.go | 4 ++-- x/ibc/02-client/exported/exported.go | 4 ++-- x/ibc/02-client/keeper/keeper.go | 16 ++++++++-------- .../types/tendermint/consensus_state.go | 11 +++++------ 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index 9fbecbd2aebd..9bd992b5d939 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -17,7 +17,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" - commitmentexported "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) // GetQueryCmd returns the query commands for IBC clients @@ -114,7 +114,7 @@ $ %s query ibc client root [client-id] [height] return err } - var root commitmentexported.RootI + var root commitment.RootI if err := cdc.UnmarshalJSON(res, &root); err != nil { return err } diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index 05db3902130f..dcab60459283 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -3,7 +3,7 @@ package exported import ( "fmt" - commitmentexported "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) // Blockchain is consensus algorithm which generates valid Headers. It generates @@ -21,7 +21,7 @@ type ConsensusState interface { // GetRoot returns the commitment root of the consensus state, // which is used for key-value pair verification. - GetRoot() commitmentexported.RootI + GetRoot() commitment.RootI // CheckValidityAndUpdateState returns the updated consensus state // only if the header is a descendent of this consensus state. diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index 6c5c4f1d1c78..c9495758cbfc 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -12,7 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" - commitmentexported "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -99,21 +99,21 @@ func (k Keeper) SetConsensusState(ctx sdk.Context, clientID string, consensusSta // GetVerifiedRoot gets a verified commitment Root from a particular height to // a client -func (k Keeper) GetVerifiedRoot(ctx sdk.Context, clientID string, height uint64) (commitmentexported.RootI, bool) { +func (k Keeper) GetVerifiedRoot(ctx sdk.Context, clientID string, height uint64) (commitment.RootI, bool) { store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) bz := store.Get(types.KeyRoot(clientID, height)) if bz == nil { return nil, false } - var root commitmentexported.RootI + var root commitment.RootI k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &root) return root, true } // SetVerifiedRoot sets a verified commitment Root from a particular height to // a client -func (k Keeper) SetVerifiedRoot(ctx sdk.Context, clientID string, height uint64, root commitmentexported.RootI) { +func (k Keeper) SetVerifiedRoot(ctx sdk.Context, clientID string, height uint64, root commitment.RootI) { store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) bz := k.cdc.MustMarshalBinaryLengthPrefixed(root) store.Set(types.KeyRoot(clientID, height), bz) @@ -157,8 +157,8 @@ func (k Keeper) VerifyMembership( ctx sdk.Context, clientState types.State, height uint64, // sequence - proof commitmentexported.ProofI, - path commitmentexported.PathI, + proof commitment.ProofI, + path commitment.PathI, value []byte, ) bool { if clientState.Frozen { @@ -178,8 +178,8 @@ func (k Keeper) VerifyNonMembership( ctx sdk.Context, clientState types.State, height uint64, // sequence - proof commitmentexported.ProofI, - path commitmentexported.PathI, + proof commitment.ProofI, + path commitment.PathI, ) bool { if clientState.Frozen { return false diff --git a/x/ibc/02-client/types/tendermint/consensus_state.go b/x/ibc/02-client/types/tendermint/consensus_state.go index 2f8ce8abbc84..74e8a9f42dfa 100644 --- a/x/ibc/02-client/types/tendermint/consensus_state.go +++ b/x/ibc/02-client/types/tendermint/consensus_state.go @@ -9,17 +9,16 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" - commitmentexported "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported" ) var _ exported.ConsensusState = ConsensusState{} // ConsensusState defines a Tendermint consensus state type ConsensusState struct { - ChainID string `json:"chain_id" yaml:"chain_id"` - Height uint64 `json:"height" yaml:"height"` // NOTE: defined as 'sequence' in the spec - Root commitmentexported.RootI `json:"root" yaml:"root"` - NextValidatorSet *tmtypes.ValidatorSet `json:"next_validator_set" yaml:"next_validator_set"` // contains the PublicKey + ChainID string `json:"chain_id" yaml:"chain_id"` + Height uint64 `json:"height" yaml:"height"` // NOTE: defined as 'sequence' in the spec + Root commitment.RootI `json:"root" yaml:"root"` + NextValidatorSet *tmtypes.ValidatorSet `json:"next_validator_set" yaml:"next_validator_set"` // contains the PublicKey } // ClientType returns Tendermint @@ -33,7 +32,7 @@ func (cs ConsensusState) GetHeight() uint64 { } // GetRoot returns the commitment Root for the specific -func (cs ConsensusState) GetRoot() commitmentexported.RootI { +func (cs ConsensusState) GetRoot() commitment.RootI { return cs.Root } From 22f874a8b9e6cdbda4f8fc0ca143d2719f398462 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Wed, 23 Oct 2019 18:18:23 +0200 Subject: [PATCH 148/166] msg.ValidateBasic and ADR09 evidence interface --- x/ibc/02-client/exported/exported.go | 39 +++++++++++-------- x/ibc/02-client/keeper/keeper.go | 26 ++++++------- x/ibc/02-client/types/codec.go | 1 + x/ibc/02-client/types/errors.go | 12 ++++++ x/ibc/02-client/types/msgs.go | 27 +++++++++++-- x/ibc/02-client/types/tendermint/header.go | 19 --------- .../types/tendermint/misbehaviour.go | 3 +- 7 files changed, 74 insertions(+), 53 deletions(-) diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index dcab60459283..d482a54553d8 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -1,7 +1,7 @@ package exported import ( - "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) @@ -28,11 +28,25 @@ type ConsensusState interface { CheckValidityAndUpdateState(Header) (ConsensusState, error) } -// Evidence contains two disctict headers used to submit client equivocation -// TODO: use evidence module type +// Evidence from ADR 009: Evidence Module +// TODO: use evidence module interface once merged type Evidence interface { - H1() Header - H2() Header + Route() string + Type() string + String() string + ValidateBasic() sdk.Error + + // The consensus address of the malicious validator at time of infraction + GetConsensusAddress() sdk.ConsAddress + + // Height at which the infraction occurred + GetHeight() int64 + + // The total power of the malicious validator at time of infraction + GetValidatorPower() int64 + + // The total validator set power at time of infraction + GetTotalPower() int64 } // Misbehaviour defines a specific consensus kind and an evidence @@ -60,18 +74,9 @@ const ( Tendermint ClientType = iota + 1 // 1 ) -var validClientTypes = map[string]struct{}{ - ClientTypeTendermint: {}, -} - -// RegisterClientType registers a client type. It will panic if the type is -// already registered. -func RegisterClientType(ty string) { - if _, ok := validClientTypes[ty]; ok { - panic(fmt.Sprintf("already registered client type: %s", ty)) - } - - validClientTypes[ty] = struct{}{} +// ValidClientTypes returns the registerd client types for this chain +var ValidClientTypes = map[string]bool{ + ClientTypeTendermint: true, } // ClientTypeFromStr returns a byte that corresponds to the registered client diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index c9495758cbfc..d0c45f0a6ffd 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -11,7 +11,6 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -128,18 +127,19 @@ func (k Keeper) initialize(ctx sdk.Context, clientID string, consensusState expo } func (k Keeper) checkMisbehaviour(ctx sdk.Context, evidence exported.Evidence) error { - switch evidence.H1().ClientType() { - case exported.Tendermint: - var tmEvidence tendermint.Evidence - _, ok := evidence.(tendermint.Evidence) - if !ok { - return sdkerrors.Wrap(types.ErrInvalidClientType(k.codespace), "consensus type is not Tendermint") - } - // TODO: pass past consensus states - return tendermint.CheckMisbehaviour(tmEvidence) - default: - panic("unregistered consensus type") - } + // switch evidence.H1().ClientType() { + // case exported.Tendermint: + // var tmEvidence tendermint.Evidence + // _, ok := evidence.(tendermint.Evidence) + // if !ok { + // return sdkerrors.Wrap(types.ErrInvalidClientType(k.codespace), "consensus type is not Tendermint") + // } + // // TODO: pass past consensus states + // return tendermint.CheckMisbehaviour(tmEvidence) + // default: + // panic("unregistered consensus type") + // } + return nil } // freeze updates the state of the client in the event of a misbehaviour diff --git a/x/ibc/02-client/types/codec.go b/x/ibc/02-client/types/codec.go index c556850a0ca2..eae86ec2b53d 100644 --- a/x/ibc/02-client/types/codec.go +++ b/x/ibc/02-client/types/codec.go @@ -12,6 +12,7 @@ var SubModuleCdc *codec.Codec func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*exported.Blockchain)(nil), nil) cdc.RegisterInterface((*exported.ConsensusState)(nil), nil) + cdc.RegisterInterface((*exported.Evidence)(nil), nil) cdc.RegisterInterface((*exported.Header)(nil), nil) cdc.RegisterInterface((*exported.Misbehaviour)(nil), nil) diff --git a/x/ibc/02-client/types/errors.go b/x/ibc/02-client/types/errors.go index 282e697d3bce..b6f8a84c9aa9 100644 --- a/x/ibc/02-client/types/errors.go +++ b/x/ibc/02-client/types/errors.go @@ -18,6 +18,8 @@ const ( CodeClientTypeNotFound sdk.CodeType = 106 CodeInvalidClientType sdk.CodeType = 107 CodeRootNotFound sdk.CodeType = 108 + CodeInvalidHeader sdk.CodeType = 109 + CodeInvalidEvidence sdk.CodeType = 110 ) // ErrClientExists implements sdk.Error @@ -59,3 +61,13 @@ func ErrInvalidClientType(codespace sdk.CodespaceType) sdk.Error { func ErrRootNotFound(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeRootNotFound, "commitment root not found") } + +// ErrInvalidHeader implements sdk.Error +func ErrInvalidHeader(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidHeader, "invalid header") +} + +// ErrInvalidEvidence implements sdk.Error +func ErrInvalidEvidence(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidEvidence, "invalid evidence") +} diff --git a/x/ibc/02-client/types/msgs.go b/x/ibc/02-client/types/msgs.go index 17e1848cd577..450b7f99c86e 100644 --- a/x/ibc/02-client/types/msgs.go +++ b/x/ibc/02-client/types/msgs.go @@ -1,8 +1,11 @@ package types import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -41,7 +44,15 @@ func (msg MsgCreateClient) ValidateBasic() sdk.Error { if msg.Signer.Empty() { return sdk.ErrInvalidAddress("empty address") } - // TODO: validate client type and ID + if err := host.DefaultIdentifierValidator(msg.ClientID); err != nil { + return sdk.NewError(host.IBCCodeSpace, 1, fmt.Sprintf("invalid client ID: %s", err.Error())) + } + if !exported.ValidClientTypes[msg.ClientType] { + return ErrInvalidClientType(DefaultCodespace) + } + if msg.ConsensusState == nil { + return ErrInvalidConsensus(DefaultCodespace) + } return nil } @@ -88,7 +99,12 @@ func (msg MsgUpdateClient) ValidateBasic() sdk.Error { if msg.Signer.Empty() { return sdk.ErrInvalidAddress("empty address") } - // TODO: validate client ID + if err := host.DefaultIdentifierValidator(msg.ClientID); err != nil { + return sdk.NewError(host.IBCCodeSpace, 1, fmt.Sprintf("invalid client ID: %s", err.Error())) + } + if msg.Header == nil { + return ErrInvalidHeader(DefaultCodespace) + } return nil } @@ -133,7 +149,12 @@ func (msg MsgSubmitMisbehaviour) ValidateBasic() sdk.Error { if msg.Signer.Empty() { return sdk.ErrInvalidAddress("empty address") } - // TODO: validate client ID + if err := host.DefaultIdentifierValidator(msg.ClientID); err != nil { + return sdk.NewError(host.IBCCodeSpace, 1, fmt.Sprintf("invalid client ID: %s", err.Error())) + } + if msg.Evidence == nil { + return ErrInvalidEvidence(DefaultCodespace) + } return nil } diff --git a/x/ibc/02-client/types/tendermint/header.go b/x/ibc/02-client/types/tendermint/header.go index 9d6178cc2d7f..62202b1e6b02 100644 --- a/x/ibc/02-client/types/tendermint/header.go +++ b/x/ibc/02-client/types/tendermint/header.go @@ -27,22 +27,3 @@ func (h Header) ClientType() exported.ClientType { func (h Header) GetHeight() uint64 { return uint64(h.Height) } - -var _ exported.Evidence = Evidence{} - -// Evidence defines two disctinct Tendermint headers used to submit a client misbehaviour -// TODO: use evidence module's types -type Evidence struct { - Header1 Header `json:"header_one" yaml:"header_one"` - Header2 Header `json:"header_two" yaml:"header_two"` -} - -// H1 returns the first header -func (e Evidence) H1() exported.Header { - return e.Header1 -} - -// H2 returns the second header -func (e Evidence) H2() exported.Header { - return e.Header2 -} diff --git a/x/ibc/02-client/types/tendermint/misbehaviour.go b/x/ibc/02-client/types/tendermint/misbehaviour.go index 15787450e4ca..229a0ab36967 100644 --- a/x/ibc/02-client/types/tendermint/misbehaviour.go +++ b/x/ibc/02-client/types/tendermint/misbehaviour.go @@ -2,10 +2,11 @@ package tendermint import ( sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" ) // CheckMisbehaviour checks if the evidence provided is a misbehaviour -func CheckMisbehaviour(evidence Evidence) sdk.Error { +func CheckMisbehaviour(evidence exported.Evidence) sdk.Error { // TODO: check evidence return nil } From 2edb03fdee729e8af413288dedef62eecfc0cec9 Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Wed, 23 Oct 2019 11:55:00 -0700 Subject: [PATCH 149/166] complete types testing --- x/ibc/23-commitment/types/merkle.go | 17 +++-- x/ibc/23-commitment/types/merkle_test.go | 92 ++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 4 deletions(-) diff --git a/x/ibc/23-commitment/types/merkle.go b/x/ibc/23-commitment/types/merkle.go index 0a89c58e682b..7b0fe28e8d35 100644 --- a/x/ibc/23-commitment/types/merkle.go +++ b/x/ibc/23-commitment/types/merkle.go @@ -80,7 +80,7 @@ type Path struct { func NewPath(keyPathStr []string) Path { merkleKeyPath := merkle.KeyPath{} for _, keyStr := range keyPathStr { - merkleKeyPath = merkleKeyPath.AppendKey([]byte(keyStr), merkle.KeyEncodingHex) + merkleKeyPath = merkleKeyPath.AppendKey([]byte(keyStr), merkle.KeyEncodingURL) } return Path{ @@ -106,10 +106,19 @@ func (p Path) String() string { func ApplyPrefix(prefix exported.PrefixI, path string) Path { // Split paths by the separator pathSlice := strings.Split(path, "/") - commitmentPath := NewPath(pathSlice) + merkleKeyPath := merkle.KeyPath{} + + // append prefix first + merkleKeyPath = merkleKeyPath.AppendKey(prefix.Bytes(), merkle.KeyEncodingURL) + + // append all path elements after + for _, keyStr := range pathSlice { + merkleKeyPath = merkleKeyPath.AppendKey([]byte(keyStr), merkle.KeyEncodingURL) + } - commitmentPath.KeyPath = commitmentPath.KeyPath.AppendKey(prefix.Bytes(), merkle.KeyEncodingHex) - return commitmentPath + return Path{ + KeyPath: merkleKeyPath, + } } var _ exported.ProofI = Proof{} diff --git a/x/ibc/23-commitment/types/merkle_test.go b/x/ibc/23-commitment/types/merkle_test.go index 1b69f05e5b1e..10e8158bffce 100644 --- a/x/ibc/23-commitment/types/merkle_test.go +++ b/x/ibc/23-commitment/types/merkle_test.go @@ -25,6 +25,7 @@ func TestVerifyMembership(t *testing.T) { iavlStore := store.GetCommitStore(iavlStoreKey).(*iavl.Store) iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) + cid := store.Commit() res := store.Query(abci.RequestQuery{ Path: "/iavlStoreKey/key", // required path to get key/value+proof @@ -37,4 +38,95 @@ func TestVerifyMembership(t *testing.T) { Proof: res.Proof, } + cases := []struct { + root []byte + pathArr []string + value []byte + shouldPass bool + errString string + }{ + {cid.Hash, []string{"iavlStoreKey", "MYKEY"}, []byte("MYVALUE"), true, "valid membership proof failed"}, // valid proof + {cid.Hash, []string{"iavlStoreKey", "MYKEY"}, []byte("WRONGVALUE"), false, "invalid membership proof with wrong value passed"}, // invalid proof with wrong value + {cid.Hash, []string{"iavlStoreKey", "MYKEY"}, []byte(nil), false, "invalid membership proof with wrong value passed"}, // invalid proof with nil value + {cid.Hash, []string{"iavlStoreKey", "NOTMYKEY"}, []byte("MYVALUE"), false, "invalid membership proof with wrong key passed"}, // invalid proof with wrong key + {cid.Hash, []string{"iavlStoreKey", "MYKEY", "MYKEY"}, []byte("MYVALUE"), false, "invalid membership proof with wrong path passed"}, // invalid proof with wrong path + {cid.Hash, []string{"iavlStoreKey"}, []byte("MYVALUE"), false, "invalid membership proof with wrong path passed"}, // invalid proof with wrong path + {cid.Hash, []string{"MYKEY"}, []byte("MYVALUE"), false, "invalid membership proof with wrong path passed"}, // invalid proof with wrong path + {cid.Hash, []string{"otherStoreKey", "MYKEY"}, []byte("MYVALUE"), false, "invalid membership proof with wrong store prefix passed"}, // invalid proof with wrong store prefix + {[]byte("WRONGROOT"), []string{"iavlStoreKey", "MYKEY"}, []byte("MYVALUE"), false, "invalid membership proof with wrong root passed"}, // invalid proof with wrong root + {[]byte(nil), []string{"iavlStoreKey", "MYKEY"}, []byte("MYVALUE"), false, "invalid membership proof with nil root passed"}, // invalid proof with nil root + } + + for i, tc := range cases { + root := types.NewRoot(tc.root) + path := types.NewPath(tc.pathArr) + + ok := proof.VerifyMembership(root, path, tc.value) + + require.True(t, ok == tc.shouldPass, "Test case %d failed: %s", i, tc.errString) + } + +} + +func TestVerifyNonMembership(t *testing.T) { + db := dbm.NewMemDB() + store := rootmulti.NewStore(db) + + iavlStoreKey := storetypes.NewKVStoreKey("iavlStoreKey") + + store.MountStoreWithDB(iavlStoreKey, storetypes.StoreTypeIAVL, nil) + store.LoadVersion(0) + + iavlStore := store.GetCommitStore(iavlStoreKey).(*iavl.Store) + iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) + cid := store.Commit() + + // Get Proof + res := store.Query(abci.RequestQuery{ + Path: "/iavlStoreKey/key", // required path to get key/value+proof + Data: []byte("MYABSENTKEY"), + Prove: true, + }) + require.NotNil(t, res.Proof) + + proof := types.Proof{ + Proof: res.Proof, + } + + cases := []struct { + root []byte + pathArr []string + shouldPass bool + errString string + }{ + {cid.Hash, []string{"iavlStoreKey", "MYABSENTKEY"}, true, "valid non-membership proof failed"}, // valid proof + {cid.Hash, []string{"iavlStoreKey", "MYKEY"}, false, "invalid non-membership proof with wrong key passed"}, // invalid proof with existent key + {cid.Hash, []string{"iavlStoreKey", "MYKEY", "MYABSENTKEY"}, false, "invalid non-membership proof with wrong path passed"}, // invalid proof with wrong path + {cid.Hash, []string{"iavlStoreKey", "MYABSENTKEY", "MYKEY"}, false, "invalid non-membership proof with wrong path passed"}, // invalid proof with wrong path + {cid.Hash, []string{"iavlStoreKey"}, false, "invalid non-membership proof with wrong path passed"}, // invalid proof with wrong path + {cid.Hash, []string{"MYABSENTKEY"}, false, "invalid non-membership proof with wrong path passed"}, // invalid proof with wrong path + {cid.Hash, []string{"otherStoreKey", "MYABSENTKEY"}, false, "invalid non-membership proof with wrong store prefix passed"}, // invalid proof with wrong store prefix + {[]byte("WRONGROOT"), []string{"iavlStoreKey", "MYABSENTKEY"}, false, "invalid non-membership proof with wrong root passed"}, // invalid proof with wrong root + {[]byte(nil), []string{"iavlStoreKey", "MYABSENTKEY"}, false, "invalid non-membership proof with nil root passed"}, // invalid proof with nil root + } + + for i, tc := range cases { + root := types.NewRoot(tc.root) + path := types.NewPath(tc.pathArr) + + ok := proof.VerifyNonMembership(root, path) + + require.True(t, ok == tc.shouldPass, "Test case %d failed: %s", i, tc.errString) + } + +} + +func TestApplyPrefix(t *testing.T) { + prefix := types.NewPrefix([]byte("storePrefixKey")) + + pathStr := "path1/path2/path3/key" + + prefixedPath := types.ApplyPrefix(prefix, pathStr) + + require.Equal(t, "/storePrefixKey/path1/path2/path3/key", prefixedPath.String(), "Prefixed path incorrect") } From 696db2e9d3847caffdf58f5479e0a36b7136708b Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Wed, 23 Oct 2019 12:42:54 -0700 Subject: [PATCH 150/166] delete empty test file --- x/ibc/23-commitment/keeper/keeper_test.go | 1 - 1 file changed, 1 deletion(-) delete mode 100644 x/ibc/23-commitment/keeper/keeper_test.go diff --git a/x/ibc/23-commitment/keeper/keeper_test.go b/x/ibc/23-commitment/keeper/keeper_test.go deleted file mode 100644 index 9429264902a9..000000000000 --- a/x/ibc/23-commitment/keeper/keeper_test.go +++ /dev/null @@ -1 +0,0 @@ -package keeper_test From 69db1f2902ff3a1a1272bef392caf794d8e8b681 Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Wed, 23 Oct 2019 12:44:29 -0700 Subject: [PATCH 151/166] remove ibc errors from core error package --- types/errors/errors.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/types/errors/errors.go b/types/errors/errors.go index 370e6c591469..642c433d90b2 100644 --- a/types/errors/errors.go +++ b/types/errors/errors.go @@ -65,12 +65,6 @@ var ( // ErrNoSignatures to doc ErrNoSignatures = Register(RootCodespace, 16, "no signatures supplied") - // ErrInvalidId to doc - ErrInvalidID = Register(RootCodespace, 17, "invalid identifier") - - // ErrInvalidPath to doc - ErrInvalidPath = Register(RootCodespace, 18, "invalid path") - // ErrPanic is only set when we recover from a panic, so we know to // redact potentially sensitive system info ErrPanic = Register(UndefinedCodespace, 111222, "panic") From 102ab3dff568ff478c40fc32e925591bd2013600 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 24 Oct 2019 15:17:25 +0200 Subject: [PATCH 152/166] custom JSON marshaling --- x/ibc/02-client/client/cli/tx.go | 2 +- x/ibc/02-client/doc.go | 44 --------------- x/ibc/02-client/exported/exported.go | 62 ++++++++++++++-------- x/ibc/02-client/handler.go | 9 +++- x/ibc/02-client/keeper/client.go | 7 +-- x/ibc/02-client/module.go | 9 ++-- x/ibc/02-client/types/errors.go | 4 +- x/ibc/02-client/types/msgs.go | 22 ++++---- x/ibc/02-client/types/tendermint/header.go | 1 - 9 files changed, 67 insertions(+), 93 deletions(-) diff --git a/x/ibc/02-client/client/cli/tx.go b/x/ibc/02-client/client/cli/tx.go index a9efc1162461..92e37b15c9b5 100644 --- a/x/ibc/02-client/client/cli/tx.go +++ b/x/ibc/02-client/client/cli/tx.go @@ -69,7 +69,7 @@ $ %s tx ibc client create [client-id] [path/to/consensus_state.json] --from node } msg := types.NewMsgCreateClient( - clientID, exported.ClientTypeToString(state.ClientType()), state, + clientID, state.ClientType().String(), state, cliCtx.GetFromAddress(), ) if err := msg.ValidateBasic(); err != nil { diff --git a/x/ibc/02-client/doc.go b/x/ibc/02-client/doc.go index 061e160d9739..2b6952ba97eb 100644 --- a/x/ibc/02-client/doc.go +++ b/x/ibc/02-client/doc.go @@ -6,49 +6,5 @@ clients which tracks on other chain's state. The main type is `Client`, which provides `commitment.Root` to verify state proofs and `ConsensusState` to verify header proofs. - -Specification - -```typescript -interface ConsensusState { - height: uint64 - root: CommitmentRoot - validityPredicate: ValidityPredicate - eqivocationPredicate: EquivocationPredicate -} - -interface ClientState { - consensusState: ConsensusState - verifiedRoots: Map - frozen: bool -} - -interface Header { - height: uint64 - proof: HeaderProof - state: Maybe[ConsensusState] - root: CommitmentRoot -} - -type ValidityPredicate = (ConsensusState, Header) => Error | ConsensusState - -type EquivocationPredicate = (ConsensusState, Header, Header) => bool -``` - -Implementation - -1. Types - -`spec: interface ConsensusState` is implemented by `type ConsensusState`. `ConsensusState.{GetHeight(), GetRoot(), -Validate(), Equivocation()}` each corresponds to `spec: ConsensusState.{height, root, validityPredicate, -equivocationPredicate}`. `ConsensusState.Kind()` returns `Kind`, which is an enum indicating the type of the -consensus algorithm. - -`spec: interface Header` is implemented by `type Header`. `Header{GetHeight(), Proof(), State(), GetRoot()}` -each corresponds to `spec: Header.{height, proof, state, root}`. - -2. Manager - -`spec: interface ClientState` is implemented by `type State`. */ package client diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index d482a54553d8..ff5f586a75cb 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -1,6 +1,9 @@ package exported import ( + "encoding/json" + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" @@ -61,43 +64,58 @@ type Header interface { GetHeight() uint64 } -// Client types -const ( - ClientTypeTendermint string = "tendermint" -) - // ClientType defines the type of the consensus algorithm type ClientType byte -// Registered consensus types +// available client types const ( Tendermint ClientType = iota + 1 // 1 ) -// ValidClientTypes returns the registerd client types for this chain -var ValidClientTypes = map[string]bool{ - ClientTypeTendermint: true, +// string representation of the client types +const ( + ClientTypeTendermint string = "tendermint" +) + +func (ct ClientType) String() string { + switch ct { + case Tendermint: + return ClientTypeTendermint + default: + return "" + } } -// ClientTypeFromStr returns a byte that corresponds to the registered client -// type. It returns 0 if the type is not found/registered. -func ClientTypeFromStr(clientType string) ClientType { - switch clientType { - case ClientTypeTendermint: - return Tendermint +// MarshalJSON marshal to JSON using string. +func (ct ClientType) MarshalJSON() ([]byte, error) { + return json.Marshal(ct.String()) +} - default: - return 0 +// UnmarshalJSON decodes from JSON. +func (ct *ClientType) UnmarshalJSON(data []byte) error { + var s string + err := json.Unmarshal(data, &s) + if err != nil { + return err + } + + bz2, err := ClientTypeFromString(s) + if err != nil { + return err } + + *ct = bz2 + return nil } -// ClientTypeToString returns the string representation of a client type -func ClientTypeToString(clientType ClientType) string { +// ClientTypeFromString returns a byte that corresponds to the registered client +// type. It returns 0 if the type is not found/registered. +func ClientTypeFromString(clientType string) (ClientType, error) { switch clientType { - case Tendermint: - return ClientTypeTendermint + case ClientTypeTendermint: + return Tendermint, nil default: - return "" + return 0, fmt.Errorf("'%s' is not a valid client type", clientType) } } diff --git a/x/ibc/02-client/handler.go b/x/ibc/02-client/handler.go index b7394edf0e29..1b1e1807b54a 100644 --- a/x/ibc/02-client/handler.go +++ b/x/ibc/02-client/handler.go @@ -2,11 +2,18 @@ package client import ( sdk "github.com/cosmos/cosmos-sdk/types" + exported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" ) // HandleMsgCreateClient defines the sdk.Handler for MsgCreateClient func HandleMsgCreateClient(ctx sdk.Context, k Keeper, msg MsgCreateClient) sdk.Result { - _, err := k.CreateClient(ctx, msg.ClientID, msg.ClientType, msg.ConsensusState) + clientType, err := exported.ClientTypeFromString(msg.ClientType) + if err != nil { + return sdk.ResultFromError(ErrInvalidClientType(DefaultCodespace, err.Error())) + } + + // TODO: should we create an event with the new client state id ? + _, err = k.CreateClient(ctx, msg.ClientID, clientType, msg.ConsensusState) if err != nil { return sdk.ResultFromError(err) } diff --git a/x/ibc/02-client/keeper/client.go b/x/ibc/02-client/keeper/client.go index 26a0469b1038..e180919dec7c 100644 --- a/x/ibc/02-client/keeper/client.go +++ b/x/ibc/02-client/keeper/client.go @@ -13,7 +13,7 @@ import ( // state as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#create func (k Keeper) CreateClient( ctx sdk.Context, clientID string, - clientTypeStr string, consensusState exported.ConsensusState, + clientType exported.ClientType, consensusState exported.ConsensusState, ) (types.State, error) { _, found := k.GetClientState(ctx, clientID) if found { @@ -25,11 +25,6 @@ func (k Keeper) CreateClient( panic(fmt.Sprintf("consensus type is already defined for client %s", clientID)) } - clientType := exported.ClientTypeFromStr(clientTypeStr) - if clientType == 0 { - return types.State{}, types.ErrInvalidClientType(k.codespace) - } - clientState := k.initialize(ctx, clientID, consensusState) k.SetVerifiedRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) k.SetClientState(ctx, clientState) diff --git a/x/ibc/02-client/module.go b/x/ibc/02-client/module.go index a4586961b08a..3df05cdfbdf2 100644 --- a/x/ibc/02-client/module.go +++ b/x/ibc/02-client/module.go @@ -11,23 +11,22 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/client/cli" ) -// Name returns the staking module's name +// Name returns the IBC client name func Name() string { return SubModuleName } -// RegisterRESTRoutes registers the REST routes for the staking module. +// RegisterRESTRoutes registers the REST routes for the IBC client func RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { // TODO: - // rest.RegisterRoutes(ctx, rtr) } -// GetTxCmd returns the root tx command for the staking module. +// GetTxCmd returns the root tx command for the IBC client func GetTxCmd(cdc *codec.Codec, storeKey string) *cobra.Command { return cli.GetTxCmd(fmt.Sprintf("%s/%s", storeKey, SubModuleName), cdc) } -// GetQueryCmd returns no root query command for the staking module. +// GetQueryCmd returns no root query command for the IBC client func GetQueryCmd(cdc *codec.Codec, queryRoute string) *cobra.Command { return cli.GetQueryCmd(fmt.Sprintf("%s/%s", queryRoute, SubModuleName), cdc) } diff --git a/x/ibc/02-client/types/errors.go b/x/ibc/02-client/types/errors.go index b6f8a84c9aa9..d4e6e9bb5558 100644 --- a/x/ibc/02-client/types/errors.go +++ b/x/ibc/02-client/types/errors.go @@ -53,8 +53,8 @@ func ErrClientTypeNotFound(codespace sdk.CodespaceType) sdk.Error { } // ErrInvalidClientType implements sdk.Error -func ErrInvalidClientType(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeInvalidClientType, "client type mismatch") +func ErrInvalidClientType(codespace sdk.CodespaceType, msg string) sdk.Error { + return sdk.NewError(codespace, CodeInvalidClientType, msg) } // ErrRootNotFound implements sdk.Error diff --git a/x/ibc/02-client/types/msgs.go b/x/ibc/02-client/types/msgs.go index 450b7f99c86e..c9d46cafa4db 100644 --- a/x/ibc/02-client/types/msgs.go +++ b/x/ibc/02-client/types/msgs.go @@ -41,18 +41,18 @@ func (msg MsgCreateClient) Type() string { // ValidateBasic implements sdk.Msg func (msg MsgCreateClient) ValidateBasic() sdk.Error { - if msg.Signer.Empty() { - return sdk.ErrInvalidAddress("empty address") - } if err := host.DefaultIdentifierValidator(msg.ClientID); err != nil { return sdk.NewError(host.IBCCodeSpace, 1, fmt.Sprintf("invalid client ID: %s", err.Error())) } - if !exported.ValidClientTypes[msg.ClientType] { - return ErrInvalidClientType(DefaultCodespace) + if _, err := exported.ClientTypeFromString(msg.ClientType); err != nil { + return ErrInvalidClientType(DefaultCodespace, err.Error()) } if msg.ConsensusState == nil { return ErrInvalidConsensus(DefaultCodespace) } + if msg.Signer.Empty() { + return sdk.ErrInvalidAddress("empty address") + } return nil } @@ -96,15 +96,15 @@ func (msg MsgUpdateClient) Type() string { // ValidateBasic implements sdk.Msg func (msg MsgUpdateClient) ValidateBasic() sdk.Error { - if msg.Signer.Empty() { - return sdk.ErrInvalidAddress("empty address") - } if err := host.DefaultIdentifierValidator(msg.ClientID); err != nil { return sdk.NewError(host.IBCCodeSpace, 1, fmt.Sprintf("invalid client ID: %s", err.Error())) } if msg.Header == nil { return ErrInvalidHeader(DefaultCodespace) } + if msg.Signer.Empty() { + return sdk.ErrInvalidAddress("empty address") + } return nil } @@ -146,15 +146,15 @@ func (msg MsgSubmitMisbehaviour) Type() string { // ValidateBasic implements sdk.Msg func (msg MsgSubmitMisbehaviour) ValidateBasic() sdk.Error { - if msg.Signer.Empty() { - return sdk.ErrInvalidAddress("empty address") - } if err := host.DefaultIdentifierValidator(msg.ClientID); err != nil { return sdk.NewError(host.IBCCodeSpace, 1, fmt.Sprintf("invalid client ID: %s", err.Error())) } if msg.Evidence == nil { return ErrInvalidEvidence(DefaultCodespace) } + if msg.Signer.Empty() { + return sdk.ErrInvalidAddress("empty address") + } return nil } diff --git a/x/ibc/02-client/types/tendermint/header.go b/x/ibc/02-client/types/tendermint/header.go index 62202b1e6b02..c2d9cfebc9c5 100644 --- a/x/ibc/02-client/types/tendermint/header.go +++ b/x/ibc/02-client/types/tendermint/header.go @@ -10,7 +10,6 @@ var _ exported.Header = Header{} // Header defines the Tendermint consensus Header type Header struct { - // TODO: define Tendermint header type manually, don't use tmtypes tmtypes.SignedHeader ValidatorSet *tmtypes.ValidatorSet `json:"validator_set" yaml:"validator_set"` NextValidatorSet *tmtypes.ValidatorSet `json:"next_validator_set" yaml:"next_validator_set"` From 33312c2dccc3e90c565ee95813af8646128ef4c0 Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Thu, 24 Oct 2019 13:41:10 -0700 Subject: [PATCH 153/166] start batch-verify tests --- x/ibc/23-commitment/verify_test.go | 74 ++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 x/ibc/23-commitment/verify_test.go diff --git a/x/ibc/23-commitment/verify_test.go b/x/ibc/23-commitment/verify_test.go new file mode 100644 index 000000000000..5b94160dfd1c --- /dev/null +++ b/x/ibc/23-commitment/verify_test.go @@ -0,0 +1,74 @@ +package commitment_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/cosmos/cosmos-sdk/store/iavl" + "github.com/cosmos/cosmos-sdk/store/rootmulti" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + "github.com/stretchr/testify/require" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" +) + +func TestBatchVerifyMembership(t *testing.T) { + db := dbm.NewMemDB() + store := rootmulti.NewStore(db) + + iavlStoreKey := storetypes.NewKVStoreKey("iavlStoreKey") + + store.MountStoreWithDB(iavlStoreKey, storetypes.StoreTypeIAVL, nil) + store.LoadVersion(0) + + iavlStore := store.GetCommitStore(iavlStoreKey).(*iavl.Store) + for i := 0; i < 5; i++ { + iavlStore.Set([]byte(fmt.Sprintf("KEY:%d", i)), []byte(fmt.Sprintf("VAL:%d", i))) + } + cid := store.Commit() + + header := abci.Header{AppHash: cid.Hash} + ctx := sdk.NewContext(store, header, false, log.NewNopLogger()) + + res := store.Query(abci.RequestQuery{ + Path: "/iavlStoreKey/subspace", + Data: []byte("KEY:"), + Height: cid.Version, + Prove: true, + }) + require.NotNil(t, res.Proof) + + proof := commitment.Proof{ + Proof: res.Proof, + } + prefix := commitment.NewPrefix([]byte("iavlStoreKey")) + + keyBatches := [][]string{ + {"KEY:1"}, // batch verify one key + {"KEY:1", "KEY:2"}, // batch verify first 2 keys in subspace + {"KEY:2", "KEY:3", "KEY:4"}, // batch verify middle 3 keys in subspace + {"KEY:4", "KEY:5"}, // batch verify last 2 keys in subspace + {"KEY:3", "KEY:2"}, // batch verify keys in reverse order + {"KEY:4", "KEY:1"}, // batch verify non-contingous keys + {"KEY:2", "KEY:3", "KEY:4", "KEY:1", "KEY:5"}, // batch verify all keys in random order + } + + for i, batch := range keyBatches { + items := make(map[string][]byte) + + for _, key := range batch { + // key-pair must have form KEY:{str} => VAL:{str} + splitKey := strings.Split(key, ":") + items[key] = []byte(fmt.Sprintf("VAL:%s", splitKey[1])) + } + + ok := commitment.BatchVerifyMembership(ctx, proof, prefix, items) + + require.True(t, ok, "Test case %d failed on batch verify", i) + } +} From 523972041d97f300d331367909e68d99c194f584 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Fri, 25 Oct 2019 12:05:00 +0200 Subject: [PATCH 154/166] minor changes on commitment types --- x/ibc/23-commitment/merkle.go | 6 ++++++ x/ibc/23-commitment/types.go | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/x/ibc/23-commitment/merkle.go b/x/ibc/23-commitment/merkle.go index 206f921d85af..8754823e0683 100644 --- a/x/ibc/23-commitment/merkle.go +++ b/x/ibc/23-commitment/merkle.go @@ -1,6 +1,7 @@ package commitment import ( + "errors" "strings" "github.com/tendermint/tendermint/crypto/merkle" @@ -105,6 +106,11 @@ func ApplyPrefix(prefix PrefixI, path string) (Path, error) { if err != nil { return Path{}, err } + + if prefix == nil || len(prefix.Bytes()) == 0 { + return Path{}, errors.New("prefix can't be empty") + } + // Split paths by the separator pathSlice := strings.Split(path, "/") keyPath := merkle.KeyPath{} diff --git a/x/ibc/23-commitment/types.go b/x/ibc/23-commitment/types.go index af33cd0b7f99..1b70f4f04d9c 100644 --- a/x/ibc/23-commitment/types.go +++ b/x/ibc/23-commitment/types.go @@ -48,14 +48,14 @@ const ( Merkle Type = iota + 1 // 1 ) -// Client types +// string representation of the commitment types const ( TypeMerkle string = "merkle" ) -// TypeToString returns the string representation of a client type -func TypeToString(commitmentType Type) string { - switch commitmentType { +// String implements the Stringer interface +func (ct Type) String() string { + switch ct { case Merkle: return TypeMerkle From e048a6c6522b0cba6956d708070acd7f11c7e9cb Mon Sep 17 00:00:00 2001 From: vincent Date: Fri, 25 Oct 2019 18:53:20 +0800 Subject: [PATCH 155/166] R4R - Store consensus state correctly (#5242) * store consensus state correctly * fix client example --- x/ibc/02-client/client/cli/tx.go | 2 +- x/ibc/02-client/keeper/keeper.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x/ibc/02-client/client/cli/tx.go b/x/ibc/02-client/client/cli/tx.go index 92e37b15c9b5..fc92efb874ba 100644 --- a/x/ibc/02-client/client/cli/tx.go +++ b/x/ibc/02-client/client/cli/tx.go @@ -91,7 +91,7 @@ func GetCmdUpdateClient(cdc *codec.Codec) *cobra.Command { Long: strings.TrimSpace(fmt.Sprintf(`update existing client with a header: Example: -$ %s tx ibc client create [client-id] [path/to/header.json] --from node0 --home ../node0/cli --chain-id $CID +$ %s tx ibc client update [client-id] [path/to/header.json] --from node0 --home ../node0/cli --chain-id $CID `, version.ClientName), ), Args: cobra.ExactArgs(2), diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index d0c45f0a6ffd..c9f7fbe1c9f7 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -79,7 +79,7 @@ func (k Keeper) SetClientType(ctx sdk.Context, clientID string, clientType expor // GetConsensusState creates a new client state and populates it with a given consensus state func (k Keeper) GetConsensusState(ctx sdk.Context, clientID string) (exported.ConsensusState, bool) { store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) - bz := store.Get(types.KeyClientState(clientID)) + bz := store.Get(types.KeyConsensusState(clientID)) if bz == nil { return nil, false } @@ -93,7 +93,7 @@ func (k Keeper) GetConsensusState(ctx sdk.Context, clientID string) (exported.Co func (k Keeper) SetConsensusState(ctx sdk.Context, clientID string, consensusState exported.ConsensusState) { store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) bz := k.cdc.MustMarshalBinaryLengthPrefixed(consensusState) - store.Set(types.KeyClientState(clientID), bz) + store.Set(types.KeyConsensusState(clientID), bz) } // GetVerifiedRoot gets a verified commitment Root from a particular height to From 0c187bf7b1d3de81a2cf8a607395206e8b27fbf3 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Fri, 25 Oct 2019 12:55:44 +0200 Subject: [PATCH 156/166] update alias --- x/ibc/02-client/alias.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x/ibc/02-client/alias.go b/x/ibc/02-client/alias.go index 1d82de808c50..aedc47bfdc76 100644 --- a/x/ibc/02-client/alias.go +++ b/x/ibc/02-client/alias.go @@ -21,6 +21,8 @@ const ( CodeClientTypeNotFound = types.CodeClientTypeNotFound CodeInvalidClientType = types.CodeInvalidClientType CodeRootNotFound = types.CodeRootNotFound + CodeInvalidHeader = types.CodeInvalidHeader + CodeInvalidEvidence = types.CodeInvalidEvidence AttributeKeyClientID = types.AttributeKeyClientID SubModuleName = types.SubModuleName StoreKey = types.StoreKey @@ -46,6 +48,8 @@ var ( ErrClientTypeNotFound = types.ErrClientTypeNotFound ErrInvalidClientType = types.ErrInvalidClientType ErrRootNotFound = types.ErrRootNotFound + ErrInvalidHeader = types.ErrInvalidHeader + ErrInvalidEvidence = types.ErrInvalidEvidence ClientStatePath = types.ClientStatePath ClientTypePath = types.ClientTypePath ConsensusStatePath = types.ConsensusStatePath From 0a461b524fe44ffd564d6f079509055538eccab2 Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Wed, 30 Oct 2019 14:52:42 -0700 Subject: [PATCH 157/166] use testsuite --- x/ibc/23-commitment/commitment_test.go | 37 +++++++++ x/ibc/23-commitment/merkle_test.go | 109 +++++++++++-------------- x/ibc/23-commitment/verify_test.go | 74 ----------------- 3 files changed, 83 insertions(+), 137 deletions(-) create mode 100644 x/ibc/23-commitment/commitment_test.go delete mode 100644 x/ibc/23-commitment/verify_test.go diff --git a/x/ibc/23-commitment/commitment_test.go b/x/ibc/23-commitment/commitment_test.go new file mode 100644 index 000000000000..2535f5705e29 --- /dev/null +++ b/x/ibc/23-commitment/commitment_test.go @@ -0,0 +1,37 @@ +package commitment_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/store/iavl" + "github.com/cosmos/cosmos-sdk/store/rootmulti" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + + dbm "github.com/tendermint/tm-db" +) + +type MerkleTestSuite struct { + suite.Suite + + store *rootmulti.Store + storeKey *storetypes.KVStoreKey + iavlStore *iavl.Store +} + +func (suite *MerkleTestSuite) SetupTest() { + db := dbm.NewMemDB() + suite.store = rootmulti.NewStore(db) + + suite.storeKey = storetypes.NewKVStoreKey("iavlStoreKey") + + suite.store.MountStoreWithDB(suite.storeKey, storetypes.StoreTypeIAVL, nil) + suite.store.LoadVersion(0) + + suite.iavlStore = suite.store.GetCommitStore(suite.storeKey).(*iavl.Store) +} + +func TestMerkleTestSuite(t *testing.T) { + suite.Run(t, new(MerkleTestSuite)) +} diff --git a/x/ibc/23-commitment/merkle_test.go b/x/ibc/23-commitment/merkle_test.go index 492169bcfca5..bfd2dc5a485c 100644 --- a/x/ibc/23-commitment/merkle_test.go +++ b/x/ibc/23-commitment/merkle_test.go @@ -1,122 +1,105 @@ package commitment_test import ( + "fmt" "testing" "github.com/stretchr/testify/require" - "github.com/cosmos/cosmos-sdk/store/iavl" - "github.com/cosmos/cosmos-sdk/store/rootmulti" - storetypes "github.com/cosmos/cosmos-sdk/store/types" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" abci "github.com/tendermint/tendermint/abci/types" - dbm "github.com/tendermint/tm-db" ) -func TestVerifyMembership(t *testing.T) { - db := dbm.NewMemDB() - store := rootmulti.NewStore(db) +func (suite *MerkleTestSuite) TestVerifyMembership() { + suite.iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) + cid := suite.store.Commit() - iavlStoreKey := storetypes.NewKVStoreKey("iavlStoreKey") - - store.MountStoreWithDB(iavlStoreKey, storetypes.StoreTypeIAVL, nil) - store.LoadVersion(0) - - iavlStore := store.GetCommitStore(iavlStoreKey).(*iavl.Store) - iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) - cid := store.Commit() - - res := store.Query(abci.RequestQuery{ - Path: "/iavlStoreKey/key", // required path to get key/value+proof + res := suite.store.Query(abci.RequestQuery{ + Path: fmt.Sprintf("/%s/key", suite.storeKey.Name()), // required path to get key/value+proof Data: []byte("MYKEY"), Prove: true, }) - require.NotNil(t, res.Proof) + require.NotNil(suite.T(), res.Proof) proof := commitment.Proof{ Proof: res.Proof, } cases := []struct { + name string root []byte pathArr []string value []byte shouldPass bool - errString string }{ - {cid.Hash, []string{"iavlStoreKey", "MYKEY"}, []byte("MYVALUE"), true, "valid membership proof failed"}, // valid proof - {cid.Hash, []string{"iavlStoreKey", "MYKEY"}, []byte("WRONGVALUE"), false, "invalid membership proof with wrong value passed"}, // invalid proof with wrong value - {cid.Hash, []string{"iavlStoreKey", "MYKEY"}, []byte(nil), false, "invalid membership proof with wrong value passed"}, // invalid proof with nil value - {cid.Hash, []string{"iavlStoreKey", "NOTMYKEY"}, []byte("MYVALUE"), false, "invalid membership proof with wrong key passed"}, // invalid proof with wrong key - {cid.Hash, []string{"iavlStoreKey", "MYKEY", "MYKEY"}, []byte("MYVALUE"), false, "invalid membership proof with wrong path passed"}, // invalid proof with wrong path - {cid.Hash, []string{"iavlStoreKey"}, []byte("MYVALUE"), false, "invalid membership proof with wrong path passed"}, // invalid proof with wrong path - {cid.Hash, []string{"MYKEY"}, []byte("MYVALUE"), false, "invalid membership proof with wrong path passed"}, // invalid proof with wrong path - {cid.Hash, []string{"otherStoreKey", "MYKEY"}, []byte("MYVALUE"), false, "invalid membership proof with wrong store prefix passed"}, // invalid proof with wrong store prefix - {[]byte("WRONGROOT"), []string{"iavlStoreKey", "MYKEY"}, []byte("MYVALUE"), false, "invalid membership proof with wrong root passed"}, // invalid proof with wrong root - {[]byte(nil), []string{"iavlStoreKey", "MYKEY"}, []byte("MYVALUE"), false, "invalid membership proof with nil root passed"}, // invalid proof with nil root + {"valid proof", cid.Hash, []string{suite.storeKey.Name(), "MYKEY"}, []byte("MYVALUE"), true}, // valid proof + {"wrong value", cid.Hash, []string{suite.storeKey.Name(), "MYKEY"}, []byte("WRONGVALUE"), false}, // invalid proof with wrong value + {"nil value", cid.Hash, []string{suite.storeKey.Name(), "MYKEY"}, []byte(nil), false}, // invalid proof with nil value + {"wrong key", cid.Hash, []string{suite.storeKey.Name(), "NOTMYKEY"}, []byte("MYVALUE"), false}, // invalid proof with wrong key + {"wrong path 1", cid.Hash, []string{suite.storeKey.Name(), "MYKEY", "MYKEY"}, []byte("MYVALUE"), false}, // invalid proof with wrong path + {"wrong path 2", cid.Hash, []string{suite.storeKey.Name()}, []byte("MYVALUE"), false}, // invalid proof with wrong path + {"wrong path 3", cid.Hash, []string{"MYKEY"}, []byte("MYVALUE"), false}, // invalid proof with wrong path + {"wrong storekey", cid.Hash, []string{"otherStoreKey", "MYKEY"}, []byte("MYVALUE"), false}, // invalid proof with wrong store prefix + {"wrong root", []byte("WRONGROOT"), []string{suite.storeKey.Name(), "MYKEY"}, []byte("MYVALUE"), false}, // invalid proof with wrong root + {"nil root", []byte(nil), []string{suite.storeKey.Name(), "MYKEY"}, []byte("MYVALUE"), false}, // invalid proof with nil root } for i, tc := range cases { - root := commitment.NewRoot(tc.root) - path := commitment.NewPath(tc.pathArr) + suite.Run(tc.name, func() { + root := commitment.NewRoot(tc.root) + path := commitment.NewPath(tc.pathArr) - ok := proof.VerifyMembership(root, path, tc.value) + ok := proof.VerifyMembership(root, path, tc.value) - require.True(t, ok == tc.shouldPass, "Test case %d failed: %s", i, tc.errString) + require.True(suite.T(), ok == tc.shouldPass, "Test case %d failed", i) + }) } } -func TestVerifyNonMembership(t *testing.T) { - db := dbm.NewMemDB() - store := rootmulti.NewStore(db) - - iavlStoreKey := storetypes.NewKVStoreKey("iavlStoreKey") - - store.MountStoreWithDB(iavlStoreKey, storetypes.StoreTypeIAVL, nil) - store.LoadVersion(0) - - iavlStore := store.GetCommitStore(iavlStoreKey).(*iavl.Store) - iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) - cid := store.Commit() +func (suite *MerkleTestSuite) TestVerifyNonMembership() { + suite.iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) + cid := suite.store.Commit() // Get Proof - res := store.Query(abci.RequestQuery{ - Path: "/iavlStoreKey/key", // required path to get key/value+proof + res := suite.store.Query(abci.RequestQuery{ + Path: fmt.Sprintf("/%s/key", suite.storeKey.Name()), // required path to get key/value+proof Data: []byte("MYABSENTKEY"), Prove: true, }) - require.NotNil(t, res.Proof) + require.NotNil(suite.T(), res.Proof) proof := commitment.Proof{ Proof: res.Proof, } cases := []struct { + name string root []byte pathArr []string shouldPass bool - errString string }{ - {cid.Hash, []string{"iavlStoreKey", "MYABSENTKEY"}, true, "valid non-membership proof failed"}, // valid proof - {cid.Hash, []string{"iavlStoreKey", "MYKEY"}, false, "invalid non-membership proof with wrong key passed"}, // invalid proof with existent key - {cid.Hash, []string{"iavlStoreKey", "MYKEY", "MYABSENTKEY"}, false, "invalid non-membership proof with wrong path passed"}, // invalid proof with wrong path - {cid.Hash, []string{"iavlStoreKey", "MYABSENTKEY", "MYKEY"}, false, "invalid non-membership proof with wrong path passed"}, // invalid proof with wrong path - {cid.Hash, []string{"iavlStoreKey"}, false, "invalid non-membership proof with wrong path passed"}, // invalid proof with wrong path - {cid.Hash, []string{"MYABSENTKEY"}, false, "invalid non-membership proof with wrong path passed"}, // invalid proof with wrong path - {cid.Hash, []string{"otherStoreKey", "MYABSENTKEY"}, false, "invalid non-membership proof with wrong store prefix passed"}, // invalid proof with wrong store prefix - {[]byte("WRONGROOT"), []string{"iavlStoreKey", "MYABSENTKEY"}, false, "invalid non-membership proof with wrong root passed"}, // invalid proof with wrong root - {[]byte(nil), []string{"iavlStoreKey", "MYABSENTKEY"}, false, "invalid non-membership proof with nil root passed"}, // invalid proof with nil root + {"valid proof", cid.Hash, []string{suite.storeKey.Name(), "MYABSENTKEY"}, true}, // valid proof + {"wrong key", cid.Hash, []string{suite.storeKey.Name(), "MYKEY"}, false}, // invalid proof with existent key + {"wrong path 1", cid.Hash, []string{suite.storeKey.Name(), "MYKEY", "MYABSENTKEY"}, false}, // invalid proof with wrong path + {"wrong path 2", cid.Hash, []string{suite.storeKey.Name(), "MYABSENTKEY", "MYKEY"}, false}, // invalid proof with wrong path + {"wrong path 3", cid.Hash, []string{suite.storeKey.Name()}, false}, // invalid proof with wrong path + {"wrong path 4", cid.Hash, []string{"MYABSENTKEY"}, false}, // invalid proof with wrong path + {"wrong storeKey", cid.Hash, []string{"otherStoreKey", "MYABSENTKEY"}, false}, // invalid proof with wrong store prefix + {"wrong root", []byte("WRONGROOT"), []string{suite.storeKey.Name(), "MYABSENTKEY"}, false}, // invalid proof with wrong root + {"nil root", []byte(nil), []string{suite.storeKey.Name(), "MYABSENTKEY"}, false}, // invalid proof with nil root } for i, tc := range cases { - root := commitment.NewRoot(tc.root) - path := commitment.NewPath(tc.pathArr) + suite.Run(tc.name, func() { + root := commitment.NewRoot(tc.root) + path := commitment.NewPath(tc.pathArr) - ok := proof.VerifyNonMembership(root, path) + ok := proof.VerifyNonMembership(root, path) - require.True(t, ok == tc.shouldPass, "Test case %d failed: %s", i, tc.errString) + require.True(suite.T(), ok == tc.shouldPass, "Test case %d failed", i) + }) } } diff --git a/x/ibc/23-commitment/verify_test.go b/x/ibc/23-commitment/verify_test.go deleted file mode 100644 index 5b94160dfd1c..000000000000 --- a/x/ibc/23-commitment/verify_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package commitment_test - -import ( - "fmt" - "strings" - "testing" - - "github.com/cosmos/cosmos-sdk/store/iavl" - "github.com/cosmos/cosmos-sdk/store/rootmulti" - storetypes "github.com/cosmos/cosmos-sdk/store/types" - sdk "github.com/cosmos/cosmos-sdk/types" - commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" - "github.com/stretchr/testify/require" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/libs/log" - dbm "github.com/tendermint/tm-db" -) - -func TestBatchVerifyMembership(t *testing.T) { - db := dbm.NewMemDB() - store := rootmulti.NewStore(db) - - iavlStoreKey := storetypes.NewKVStoreKey("iavlStoreKey") - - store.MountStoreWithDB(iavlStoreKey, storetypes.StoreTypeIAVL, nil) - store.LoadVersion(0) - - iavlStore := store.GetCommitStore(iavlStoreKey).(*iavl.Store) - for i := 0; i < 5; i++ { - iavlStore.Set([]byte(fmt.Sprintf("KEY:%d", i)), []byte(fmt.Sprintf("VAL:%d", i))) - } - cid := store.Commit() - - header := abci.Header{AppHash: cid.Hash} - ctx := sdk.NewContext(store, header, false, log.NewNopLogger()) - - res := store.Query(abci.RequestQuery{ - Path: "/iavlStoreKey/subspace", - Data: []byte("KEY:"), - Height: cid.Version, - Prove: true, - }) - require.NotNil(t, res.Proof) - - proof := commitment.Proof{ - Proof: res.Proof, - } - prefix := commitment.NewPrefix([]byte("iavlStoreKey")) - - keyBatches := [][]string{ - {"KEY:1"}, // batch verify one key - {"KEY:1", "KEY:2"}, // batch verify first 2 keys in subspace - {"KEY:2", "KEY:3", "KEY:4"}, // batch verify middle 3 keys in subspace - {"KEY:4", "KEY:5"}, // batch verify last 2 keys in subspace - {"KEY:3", "KEY:2"}, // batch verify keys in reverse order - {"KEY:4", "KEY:1"}, // batch verify non-contingous keys - {"KEY:2", "KEY:3", "KEY:4", "KEY:1", "KEY:5"}, // batch verify all keys in random order - } - - for i, batch := range keyBatches { - items := make(map[string][]byte) - - for _, key := range batch { - // key-pair must have form KEY:{str} => VAL:{str} - splitKey := strings.Split(key, ":") - items[key] = []byte(fmt.Sprintf("VAL:%s", splitKey[1])) - } - - ok := commitment.BatchVerifyMembership(ctx, proof, prefix, items) - - require.True(t, ok, "Test case %d failed on batch verify", i) - } -} From 7f9c166b3a77945f12ac1ed2b9da17199053f924 Mon Sep 17 00:00:00 2001 From: Aditya Date: Mon, 4 Nov 2019 08:50:07 -0500 Subject: [PATCH 158/166] Integrate Evidence Implementation into ICS-02 (#5258) * implement evidence in ics-02 * fix build errors and import cycles * address fede comments * remove unnecessary pubkey and fix init * add tests --- x/ibc/02-client/alias.go | 43 +++---- x/ibc/02-client/exported/exported.go | 2 + x/ibc/02-client/keeper/client.go | 15 +-- x/ibc/02-client/keeper/keeper.go | 34 +++--- x/ibc/02-client/keeper/querier.go | 7 +- x/ibc/02-client/types/codec.go | 7 +- x/ibc/02-client/types/{ => errors}/errors.go | 8 +- x/ibc/02-client/types/msgs.go | 12 +- x/ibc/02-client/types/tendermint/codec.go | 18 +++ .../types/tendermint/misbehaviour.go | 90 ++++++++++++++- .../types/tendermint/misbehaviour_test.go | 107 ++++++++++++++++++ x/ibc/24-host/errors.go | 3 + 12 files changed, 288 insertions(+), 58 deletions(-) rename x/ibc/02-client/types/{ => errors}/errors.go (94%) create mode 100644 x/ibc/02-client/types/tendermint/codec.go create mode 100644 x/ibc/02-client/types/tendermint/misbehaviour_test.go diff --git a/x/ibc/02-client/alias.go b/x/ibc/02-client/alias.go index aedc47bfdc76..00519d9da512 100644 --- a/x/ibc/02-client/alias.go +++ b/x/ibc/02-client/alias.go @@ -9,20 +9,21 @@ package client import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/keeper" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" ) const ( - DefaultCodespace = types.DefaultCodespace - CodeClientExists = types.CodeClientExists - CodeClientNotFound = types.CodeClientNotFound - CodeClientFrozen = types.CodeClientFrozen - CodeConsensusStateNotFound = types.CodeConsensusStateNotFound - CodeInvalidConsensusState = types.CodeInvalidConsensusState - CodeClientTypeNotFound = types.CodeClientTypeNotFound - CodeInvalidClientType = types.CodeInvalidClientType - CodeRootNotFound = types.CodeRootNotFound - CodeInvalidHeader = types.CodeInvalidHeader - CodeInvalidEvidence = types.CodeInvalidEvidence + DefaultCodespace = errors.DefaultCodespace + CodeClientExists = errors.CodeClientExists + CodeClientNotFound = errors.CodeClientNotFound + CodeClientFrozen = errors.CodeClientFrozen + CodeConsensusStateNotFound = errors.CodeConsensusStateNotFound + CodeInvalidConsensusState = errors.CodeInvalidConsensusState + CodeClientTypeNotFound = errors.CodeClientTypeNotFound + CodeInvalidClientType = errors.CodeInvalidClientType + CodeRootNotFound = errors.CodeRootNotFound + CodeInvalidHeader = errors.CodeInvalidHeader + CodeInvalidEvidence = errors.CodeInvalidEvidence AttributeKeyClientID = types.AttributeKeyClientID SubModuleName = types.SubModuleName StoreKey = types.StoreKey @@ -40,16 +41,16 @@ var ( QuerierConsensusState = keeper.QuerierConsensusState QuerierVerifiedRoot = keeper.QuerierVerifiedRoot RegisterCodec = types.RegisterCodec - ErrClientExists = types.ErrClientExists - ErrClientNotFound = types.ErrClientNotFound - ErrClientFrozen = types.ErrClientFrozen - ErrConsensusStateNotFound = types.ErrConsensusStateNotFound - ErrInvalidConsensus = types.ErrInvalidConsensus - ErrClientTypeNotFound = types.ErrClientTypeNotFound - ErrInvalidClientType = types.ErrInvalidClientType - ErrRootNotFound = types.ErrRootNotFound - ErrInvalidHeader = types.ErrInvalidHeader - ErrInvalidEvidence = types.ErrInvalidEvidence + ErrClientExists = errors.ErrClientExists + ErrClientNotFound = errors.ErrClientNotFound + ErrClientFrozen = errors.ErrClientFrozen + ErrConsensusStateNotFound = errors.ErrConsensusStateNotFound + ErrInvalidConsensus = errors.ErrInvalidConsensus + ErrClientTypeNotFound = errors.ErrClientTypeNotFound + ErrInvalidClientType = errors.ErrInvalidClientType + ErrRootNotFound = errors.ErrRootNotFound + ErrInvalidHeader = errors.ErrInvalidHeader + ErrInvalidEvidence = errors.ErrInvalidEvidence ClientStatePath = types.ClientStatePath ClientTypePath = types.ClientTypePath ConsensusStatePath = types.ConsensusStatePath diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index ff5f586a75cb..e7d1ac5c5411 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + cmn "github.com/tendermint/tendermint/libs/common" ) // Blockchain is consensus algorithm which generates valid Headers. It generates @@ -37,6 +38,7 @@ type Evidence interface { Route() string Type() string String() string + Hash() cmn.HexBytes ValidateBasic() sdk.Error // The consensus address of the malicious validator at time of infraction diff --git a/x/ibc/02-client/keeper/client.go b/x/ibc/02-client/keeper/client.go index e180919dec7c..aa1faf956625 100644 --- a/x/ibc/02-client/keeper/client.go +++ b/x/ibc/02-client/keeper/client.go @@ -7,6 +7,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" ) // CreateClient creates a new client state and populates it with a given consensus @@ -17,7 +18,7 @@ func (k Keeper) CreateClient( ) (types.State, error) { _, found := k.GetClientState(ctx, clientID) if found { - return types.State{}, types.ErrClientExists(k.codespace, clientID) + return types.State{}, errors.ErrClientExists(k.codespace, clientID) } _, found = k.GetClientType(ctx, clientID) @@ -36,26 +37,26 @@ func (k Keeper) CreateClient( func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.Header) error { clientType, found := k.GetClientType(ctx, clientID) if !found { - return sdkerrors.Wrap(types.ErrClientTypeNotFound(k.codespace), "cannot update client") + return sdkerrors.Wrap(errors.ErrClientTypeNotFound(k.codespace), "cannot update client") } // check that the header consensus matches the client one if header.ClientType() != clientType { - return sdkerrors.Wrap(types.ErrInvalidConsensus(k.codespace), "cannot update client") + return sdkerrors.Wrap(errors.ErrInvalidConsensus(k.codespace), "cannot update client") } clientState, found := k.GetClientState(ctx, clientID) if !found { - return sdkerrors.Wrap(types.ErrClientNotFound(k.codespace, clientID), "cannot update client") + return sdkerrors.Wrap(errors.ErrClientNotFound(k.codespace, clientID), "cannot update client") } if clientState.Frozen { - return sdkerrors.Wrap(types.ErrClientFrozen(k.codespace, clientID), "cannot update client") + return sdkerrors.Wrap(errors.ErrClientFrozen(k.codespace, clientID), "cannot update client") } consensusState, found := k.GetConsensusState(ctx, clientID) if !found { - return sdkerrors.Wrap(types.ErrConsensusStateNotFound(k.codespace), "cannot update client") + return sdkerrors.Wrap(errors.ErrConsensusStateNotFound(k.codespace), "cannot update client") } if header.GetHeight() < consensusState.GetHeight() { @@ -83,7 +84,7 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, clientID string, evidence exported.Evidence) error { clientState, found := k.GetClientState(ctx, clientID) if !found { - sdk.ResultFromError(types.ErrClientNotFound(k.codespace, clientID)) + sdk.ResultFromError(errors.ErrClientNotFound(k.codespace, clientID)) } err := k.checkMisbehaviour(ctx, evidence) diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index c9f7fbe1c9f7..f753f88656d5 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -11,6 +11,8 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -29,8 +31,8 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, codespace sdk.CodespaceType) return Keeper{ storeKey: key, cdc: cdc, - codespace: sdk.CodespaceType(fmt.Sprintf("%s/%s", codespace, types.DefaultCodespace)), // "ibc/client", - prefix: []byte(types.SubModuleName + "/"), // "client/" + codespace: sdk.CodespaceType(fmt.Sprintf("%s/%s", codespace, errors.DefaultCodespace)), // "ibc/client", + prefix: []byte(types.SubModuleName + "/"), // "client/" } } @@ -127,25 +129,27 @@ func (k Keeper) initialize(ctx sdk.Context, clientID string, consensusState expo } func (k Keeper) checkMisbehaviour(ctx sdk.Context, evidence exported.Evidence) error { - // switch evidence.H1().ClientType() { - // case exported.Tendermint: - // var tmEvidence tendermint.Evidence - // _, ok := evidence.(tendermint.Evidence) - // if !ok { - // return sdkerrors.Wrap(types.ErrInvalidClientType(k.codespace), "consensus type is not Tendermint") - // } - // // TODO: pass past consensus states - // return tendermint.CheckMisbehaviour(tmEvidence) - // default: - // panic("unregistered consensus type") - // } + switch evidence.Type() { + case exported.ClientTypeTendermint: + var tmEvidence tendermint.Evidence + _, ok := evidence.(tendermint.Evidence) + if !ok { + return errors.ErrInvalidClientType(k.codespace, "consensus type is not Tendermint") + } + err := tendermint.CheckMisbehaviour(tmEvidence) + if err != nil { + return errors.ErrInvalidEvidence(k.codespace, err.Error()) + } + default: + panic(fmt.Sprintf("unregistered evidence type: %s", evidence.Type())) + } return nil } // freeze updates the state of the client in the event of a misbehaviour func (k Keeper) freeze(ctx sdk.Context, clientState types.State) (types.State, error) { if clientState.Frozen { - return types.State{}, sdkerrors.Wrap(types.ErrClientFrozen(k.codespace, clientState.ID()), "already frozen") + return types.State{}, sdkerrors.Wrap(errors.ErrClientFrozen(k.codespace, clientState.ID()), "already frozen") } clientState.Frozen = true diff --git a/x/ibc/02-client/keeper/querier.go b/x/ibc/02-client/keeper/querier.go index 8c9ce5a263ef..5f56b8d6b01a 100644 --- a/x/ibc/02-client/keeper/querier.go +++ b/x/ibc/02-client/keeper/querier.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" ) // QuerierClientState defines the sdk.Querier to query the IBC client state @@ -20,7 +21,7 @@ func QuerierClientState(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byt clientState, found := k.GetClientState(ctx, params.ClientID) if !found { - return nil, types.ErrClientTypeNotFound(k.codespace) + return nil, errors.ErrClientTypeNotFound(k.codespace) } bz, err := types.SubModuleCdc.MarshalJSON(clientState) @@ -42,7 +43,7 @@ func QuerierConsensusState(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([] consensusState, found := k.GetConsensusState(ctx, params.ClientID) if !found { - return nil, types.ErrConsensusStateNotFound(k.codespace) + return nil, errors.ErrConsensusStateNotFound(k.codespace) } bz, err := types.SubModuleCdc.MarshalJSON(consensusState) @@ -64,7 +65,7 @@ func QuerierVerifiedRoot(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]by root, found := k.GetVerifiedRoot(ctx, params.ClientID, params.Height) if !found { - return nil, types.ErrRootNotFound(k.codespace) + return nil, errors.ErrRootNotFound(k.codespace) } bz, err := types.SubModuleCdc.MarshalJSON(root) diff --git a/x/ibc/02-client/types/codec.go b/x/ibc/02-client/types/codec.go index eae86ec2b53d..69985c6044b4 100644 --- a/x/ibc/02-client/types/codec.go +++ b/x/ibc/02-client/types/codec.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" ) -var SubModuleCdc *codec.Codec +var SubModuleCdc = codec.New() // RegisterCodec registers the IBC client interfaces and types func RegisterCodec(cdc *codec.Codec) { @@ -21,4 +21,9 @@ func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(tendermint.ConsensusState{}, "ibc/client/tendermint/ConsensusState", nil) cdc.RegisterConcrete(tendermint.Header{}, "ibc/client/tendermint/Header", nil) + cdc.RegisterConcrete(tendermint.Evidence{}, "ibc/client/tendermint/Evidence", nil) +} + +func init() { + RegisterCodec(SubModuleCdc) } diff --git a/x/ibc/02-client/types/errors.go b/x/ibc/02-client/types/errors/errors.go similarity index 94% rename from x/ibc/02-client/types/errors.go rename to x/ibc/02-client/types/errors/errors.go index d4e6e9bb5558..c133bcfef808 100644 --- a/x/ibc/02-client/types/errors.go +++ b/x/ibc/02-client/types/errors/errors.go @@ -1,4 +1,4 @@ -package types +package errors import ( "fmt" @@ -8,7 +8,7 @@ import ( // client error codes const ( - DefaultCodespace sdk.CodespaceType = SubModuleName + DefaultCodespace sdk.CodespaceType = "client" CodeClientExists sdk.CodeType = 101 CodeClientNotFound sdk.CodeType = 102 @@ -68,6 +68,6 @@ func ErrInvalidHeader(codespace sdk.CodespaceType) sdk.Error { } // ErrInvalidEvidence implements sdk.Error -func ErrInvalidEvidence(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeInvalidEvidence, "invalid evidence") +func ErrInvalidEvidence(codespace sdk.CodespaceType, msg string) sdk.Error { + return sdk.NewError(codespace, CodeInvalidEvidence, "invalid evidence: %s", msg) } diff --git a/x/ibc/02-client/types/msgs.go b/x/ibc/02-client/types/msgs.go index c9d46cafa4db..17bd0786a91f 100644 --- a/x/ibc/02-client/types/msgs.go +++ b/x/ibc/02-client/types/msgs.go @@ -5,6 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -45,10 +46,10 @@ func (msg MsgCreateClient) ValidateBasic() sdk.Error { return sdk.NewError(host.IBCCodeSpace, 1, fmt.Sprintf("invalid client ID: %s", err.Error())) } if _, err := exported.ClientTypeFromString(msg.ClientType); err != nil { - return ErrInvalidClientType(DefaultCodespace, err.Error()) + return errors.ErrInvalidClientType(errors.DefaultCodespace, err.Error()) } if msg.ConsensusState == nil { - return ErrInvalidConsensus(DefaultCodespace) + return errors.ErrInvalidConsensus(errors.DefaultCodespace) } if msg.Signer.Empty() { return sdk.ErrInvalidAddress("empty address") @@ -100,7 +101,7 @@ func (msg MsgUpdateClient) ValidateBasic() sdk.Error { return sdk.NewError(host.IBCCodeSpace, 1, fmt.Sprintf("invalid client ID: %s", err.Error())) } if msg.Header == nil { - return ErrInvalidHeader(DefaultCodespace) + return errors.ErrInvalidHeader(errors.DefaultCodespace) } if msg.Signer.Empty() { return sdk.ErrInvalidAddress("empty address") @@ -150,7 +151,10 @@ func (msg MsgSubmitMisbehaviour) ValidateBasic() sdk.Error { return sdk.NewError(host.IBCCodeSpace, 1, fmt.Sprintf("invalid client ID: %s", err.Error())) } if msg.Evidence == nil { - return ErrInvalidEvidence(DefaultCodespace) + return errors.ErrInvalidEvidence(errors.DefaultCodespace, "evidence is nil") + } + if err := msg.Evidence.ValidateBasic(); err != nil { + return errors.ErrInvalidEvidence(errors.DefaultCodespace, err.Error()) } if msg.Signer.Empty() { return sdk.ErrInvalidAddress("empty address") diff --git a/x/ibc/02-client/types/tendermint/codec.go b/x/ibc/02-client/types/tendermint/codec.go new file mode 100644 index 000000000000..46814fec2479 --- /dev/null +++ b/x/ibc/02-client/types/tendermint/codec.go @@ -0,0 +1,18 @@ +package tendermint + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +var SubModuleCdc = codec.New() + +// RegisterCodec registers the Tendermint types +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterConcrete(ConsensusState{}, "ibc/client/tendermint/ConsensusState", nil) + cdc.RegisterConcrete(Header{}, "ibc/client/tendermint/Header", nil) + cdc.RegisterConcrete(Evidence{}, "ibc/client/tendermint/Evidence", nil) +} + +func init() { + RegisterCodec(SubModuleCdc) +} diff --git a/x/ibc/02-client/types/tendermint/misbehaviour.go b/x/ibc/02-client/types/tendermint/misbehaviour.go index 229a0ab36967..512bb0f72161 100644 --- a/x/ibc/02-client/types/tendermint/misbehaviour.go +++ b/x/ibc/02-client/types/tendermint/misbehaviour.go @@ -1,12 +1,96 @@ package tendermint import ( + "fmt" + + yaml "gopkg.in/yaml.v2" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + + "github.com/tendermint/tendermint/crypto/tmhash" + cmn "github.com/tendermint/tendermint/libs/common" + tmtypes "github.com/tendermint/tendermint/types" ) -// CheckMisbehaviour checks if the evidence provided is a misbehaviour -func CheckMisbehaviour(evidence exported.Evidence) sdk.Error { - // TODO: check evidence +var _ exported.Evidence = Evidence{} + +// Evidence is a wrapper over tendermint's DuplicateVoteEvidence +// that implements Evidence interface expected by ICS-02 +type Evidence struct { + *tmtypes.DuplicateVoteEvidence + ChainID string `json:"chain_id" yaml:"chain_id"` + ValidatorPower int64 `json:"val_power" yaml:"val_power"` + TotalPower int64 `json:"total_power" yaml:"total_power"` +} + +// Type implements exported.Evidence interface +func (ev Evidence) Route() string { + return exported.ClientTypeTendermint +} + +// Type implements exported.Evidence interface +func (ev Evidence) Type() string { + return exported.ClientTypeTendermint +} + +// String implements exported.Evidence interface +func (ev Evidence) String() string { + bz, err := yaml.Marshal(ev) + if err != nil { + panic(err) + } + return string(bz) +} + +// Hash implements exported.Evidence interface +func (ev Evidence) Hash() cmn.HexBytes { + return tmhash.Sum(SubModuleCdc.MustMarshalBinaryBare(ev)) +} + +// ValidateBasic implements exported.Evidence interface +func (ev Evidence) ValidateBasic() sdk.Error { + if ev.DuplicateVoteEvidence == nil { + return errors.ErrInvalidEvidence(errors.DefaultCodespace, "duplicate evidence is nil") + } + err := ev.DuplicateVoteEvidence.ValidateBasic() + if err != nil { + return errors.ErrInvalidEvidence(errors.DefaultCodespace, err.Error()) + } + if ev.ChainID == "" { + return errors.ErrInvalidEvidence(errors.DefaultCodespace, "chainID is empty") + } + if ev.ValidatorPower <= 0 { + return errors.ErrInvalidEvidence(errors.DefaultCodespace, fmt.Sprintf("Invalid Validator Power: %d", ev.ValidatorPower)) + } + if ev.TotalPower < ev.ValidatorPower { + return errors.ErrInvalidEvidence(errors.DefaultCodespace, fmt.Sprintf("Invalid Total Power: %d", ev.TotalPower)) + } return nil } + +// GetConsensusAddress implements exported.Evidence interface +func (ev Evidence) GetConsensusAddress() sdk.ConsAddress { + return sdk.ConsAddress(ev.DuplicateVoteEvidence.Address()) +} + +// GetHeight implements exported.Evidence interface +func (ev Evidence) GetHeight() int64 { + return ev.DuplicateVoteEvidence.Height() +} + +// GetValidatorPower implements exported.Evidence interface +func (ev Evidence) GetValidatorPower() int64 { + return ev.ValidatorPower +} + +// GetTotalPower implements exported.Evidence interface +func (ev Evidence) GetTotalPower() int64 { + return ev.TotalPower +} + +// CheckMisbehaviour checks if the evidence provided is a misbehaviour +func CheckMisbehaviour(evidence Evidence) error { + return evidence.DuplicateVoteEvidence.Verify(evidence.ChainID, evidence.DuplicateVoteEvidence.PubKey) +} diff --git a/x/ibc/02-client/types/tendermint/misbehaviour_test.go b/x/ibc/02-client/types/tendermint/misbehaviour_test.go new file mode 100644 index 000000000000..fb432e32bcea --- /dev/null +++ b/x/ibc/02-client/types/tendermint/misbehaviour_test.go @@ -0,0 +1,107 @@ +package tendermint + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/tendermint/tendermint/crypto/tmhash" + tmtypes "github.com/tendermint/tendermint/types" + yaml "gopkg.in/yaml.v2" +) + +// Copied unimported test functions from tmtypes to use them here +func makeBlockID(hash []byte, partSetSize int, partSetHash []byte) tmtypes.BlockID { + return tmtypes.BlockID{ + Hash: hash, + PartsHeader: tmtypes.PartSetHeader{ + Total: partSetSize, + Hash: partSetHash, + }, + } + +} + +func makeVote(val tmtypes.PrivValidator, chainID string, valIndex int, height int64, round, step int, blockID tmtypes.BlockID) *tmtypes.Vote { + addr := val.GetPubKey().Address() + v := &tmtypes.Vote{ + ValidatorAddress: addr, + ValidatorIndex: valIndex, + Height: height, + Round: round, + Type: tmtypes.SignedMsgType(step), + BlockID: blockID, + } + err := val.SignVote(chainID, v) + if err != nil { + panic(err) + } + return v +} + +func randomDuplicatedVoteEvidence() *tmtypes.DuplicateVoteEvidence { + val := tmtypes.NewMockPV() + blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), 1000, tmhash.Sum([]byte("partshash"))) + blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), 1000, tmhash.Sum([]byte("partshash"))) + const chainID = "mychain" + return &tmtypes.DuplicateVoteEvidence{ + PubKey: val.GetPubKey(), + VoteA: makeVote(val, chainID, 0, 10, 2, 1, blockID), + VoteB: makeVote(val, chainID, 0, 10, 2, 1, blockID2), + } +} + +func TestString(t *testing.T) { + dupEv := randomDuplicatedVoteEvidence() + ev := Evidence{ + DuplicateVoteEvidence: dupEv, + ChainID: "mychain", + ValidatorPower: 10, + TotalPower: 50, + } + + byteStr, err := yaml.Marshal(ev) + require.Nil(t, err) + require.Equal(t, string(byteStr), ev.String(), "Evidence String method does not work as expected") + +} + +func TestValidateBasic(t *testing.T) { + dupEv := randomDuplicatedVoteEvidence() + + // good evidence + ev := Evidence{ + DuplicateVoteEvidence: dupEv, + ChainID: "mychain", + ValidatorPower: 10, + TotalPower: 50, + } + + err := ev.ValidateBasic() + require.Nil(t, err, "good evidence failed on ValidateBasic: %v", err) + + // invalid duplicate evidence + ev.DuplicateVoteEvidence.VoteA = nil + err = ev.ValidateBasic() + require.NotNil(t, err, "invalid duplicate evidence passed on ValidateBasic") + + // reset duplicate evidence to be valid, and set empty chainID + dupEv = randomDuplicatedVoteEvidence() + ev.DuplicateVoteEvidence = dupEv + ev.ChainID = "" + err = ev.ValidateBasic() + require.NotNil(t, err, "invalid chain-id passed on ValidateBasic") + + // reset chainID and set 0 validator power + ev.ChainID = "mychain" + ev.ValidatorPower = 0 + err = ev.ValidateBasic() + require.NotNil(t, err, "invalid validator power passed on ValidateBasic") + + // reset validator power and set invalid total power + ev.ValidatorPower = 10 + ev.TotalPower = 9 + err = ev.ValidateBasic() + require.NotNil(t, err, "invalid total power passed on ValidateBasic") + +} diff --git a/x/ibc/24-host/errors.go b/x/ibc/24-host/errors.go index e1fa5e0e7a08..1468e29d5725 100644 --- a/x/ibc/24-host/errors.go +++ b/x/ibc/24-host/errors.go @@ -13,4 +13,7 @@ var ( // ErrInvalidPath is returned if path string is invalid ErrInvalidPath = sdkerrors.Register(IBCCodeSpace, 2, "invalid path") + + // ErrInvalidEvidence is returned if evidence is invalid + ErrInvalidEvidence = sdkerrors.Register(IBCCodeSpace, 3, "invalid evidence") ) From 6d580aa1429b569de333cb08c83d8366c940dd6c Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Mon, 4 Nov 2019 16:10:22 -0800 Subject: [PATCH 159/166] finish tendermint tests --- .../types/tendermint/consensus_state_test.go | 52 +++++++++++ .../types/tendermint/tendermint_test.go | 87 +++++++++++++++++++ .../02-client/types/tendermint/test_utils.go | 16 ++++ 3 files changed, 155 insertions(+) create mode 100644 x/ibc/02-client/types/tendermint/consensus_state_test.go create mode 100644 x/ibc/02-client/types/tendermint/tendermint_test.go create mode 100644 x/ibc/02-client/types/tendermint/test_utils.go diff --git a/x/ibc/02-client/types/tendermint/consensus_state_test.go b/x/ibc/02-client/types/tendermint/consensus_state_test.go new file mode 100644 index 000000000000..2f4129226228 --- /dev/null +++ b/x/ibc/02-client/types/tendermint/consensus_state_test.go @@ -0,0 +1,52 @@ +package tendermint + +import ( + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/tmhash" + tmtypes "github.com/tendermint/tendermint/types" +) + +func (suite *TendermintTestSuite) TestCheckValidity() { + // valid header + err := suite.cs.checkValidity(suite.header) + require.Nil(suite.T(), err, "validity failed") + + // switch out header ValidatorsHash + suite.header.ValidatorsHash = tmhash.Sum([]byte("hello")) + err = suite.cs.checkValidity(suite.header) + require.NotNil(suite.T(), err, "validator hash is wrong") + + // reset suite and make header.NextValidatorSet different + // from NextValidatorSetHash + suite.SetupTest() + privVal := tmtypes.NewMockPV() + val := tmtypes.NewValidator(privVal.GetPubKey(), 5) + suite.header.NextValidatorSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) + err = suite.cs.checkValidity(suite.header) + require.NotNil(suite.T(), err, "header's next validator set is not consistent with hash") + + // reset and make header fail validatebasic + suite.SetupTest() + suite.header.ChainID = "not_mychain" + err = suite.cs.checkValidity(suite.header) + require.NotNil(suite.T(), err, "invalid header should fail ValidateBasic") +} + +func (suite *TendermintTestSuite) TestCheckUpdate() { + // valid header should successfully update consensus state + cs, err := suite.cs.CheckValidityAndUpdateState(suite.header) + + require.Nil(suite.T(), err, "valid update failed") + require.Equal(suite.T(), suite.header.GetHeight(), cs.GetHeight(), "height not updated") + require.Equal(suite.T(), suite.header.AppHash.Bytes(), cs.GetRoot().GetHash(), "root not updated") + tmCS, _ := cs.(ConsensusState) + require.Equal(suite.T(), suite.header.NextValidatorSet, tmCS.NextValidatorSet, "validator set did not update") + + // make header invalid so update should be unsuccessful + suite.SetupTest() + suite.header.ChainID = "not_mychain" + + cs, err = suite.cs.CheckValidityAndUpdateState(suite.header) + require.NotNil(suite.T(), err) + require.Nil(suite.T(), cs) +} diff --git a/x/ibc/02-client/types/tendermint/tendermint_test.go b/x/ibc/02-client/types/tendermint/tendermint_test.go new file mode 100644 index 000000000000..11817f73e9ff --- /dev/null +++ b/x/ibc/02-client/types/tendermint/tendermint_test.go @@ -0,0 +1,87 @@ +package tendermint + +import ( + "math" + "testing" + "time" + + "github.com/stretchr/testify/suite" + + "github.com/tendermint/tendermint/crypto/tmhash" + tmtypes "github.com/tendermint/tendermint/types" + "github.com/tendermint/tendermint/version" + + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" +) + +type TendermintTestSuite struct { + suite.Suite + + privVal tmtypes.PrivValidator + valSet *tmtypes.ValidatorSet + header Header + cs ConsensusState +} + +func (suite *TendermintTestSuite) SetupTest() { + privVal := tmtypes.NewMockPV() + val := tmtypes.NewValidator(privVal.GetPubKey(), 10) + valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) + vsetHash := valSet.Hash() + timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) + tmHeader := tmtypes.Header{ + Version: version.Consensus{Block: 2, App: 2}, + ChainID: "mychain", + Height: 3, + Time: timestamp, + NumTxs: 100, + TotalTxs: 1000, + LastBlockID: makeBlockID(make([]byte, tmhash.Size), math.MaxInt64, make([]byte, tmhash.Size)), + LastCommitHash: tmhash.Sum([]byte("last_commit_hash")), + DataHash: tmhash.Sum([]byte("data_hash")), + ValidatorsHash: vsetHash, + NextValidatorsHash: vsetHash, + 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: privVal.GetPubKey().Address(), + } + hhash := tmHeader.Hash() + blockID := makeBlockID(hhash, 3, tmhash.Sum([]byte("part_set"))) + voteSet := tmtypes.NewVoteSet("mychain", 3, 1, tmtypes.PrecommitType, valSet) + commit, err := tmtypes.MakeCommit(blockID, 3, 1, voteSet, []tmtypes.PrivValidator{privVal}) + if err != nil { + panic(err) + } + + signedHeader := tmtypes.SignedHeader{ + Header: &tmHeader, + Commit: commit, + } + + header := Header{ + SignedHeader: signedHeader, + ValidatorSet: valSet, + NextValidatorSet: valSet, + } + + root := commitment.NewRoot(tmhash.Sum([]byte("my root"))) + + cs := ConsensusState{ + ChainID: "mychain", + Height: 3, + Root: root, + NextValidatorSet: valSet, + } + + // set fields in suite + suite.privVal = privVal + suite.valSet = valSet + suite.header = header + suite.cs = cs +} + +func TestTendermintTestSuite(t *testing.T) { + suite.Run(t, new(TendermintTestSuite)) +} diff --git a/x/ibc/02-client/types/tendermint/test_utils.go b/x/ibc/02-client/types/tendermint/test_utils.go new file mode 100644 index 000000000000..c9f9b89f493e --- /dev/null +++ b/x/ibc/02-client/types/tendermint/test_utils.go @@ -0,0 +1,16 @@ +package tendermint + +import ( + tmtypes "github.com/tendermint/tendermint/types" +) + +func makeBlockID(hash []byte, partSetSize int, partSetHash []byte) tmtypes.BlockID { + return tmtypes.BlockID{ + Hash: hash, + PartsHeader: tmtypes.PartSetHeader{ + Total: partSetSize, + Hash: partSetHash, + }, + } + +} From e875ca76a33586b7c7d1e35bf656a5e23e0feb52 Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Mon, 4 Nov 2019 16:12:44 -0800 Subject: [PATCH 160/166] complete merge --- .../types/tendermint/misbehaviour_test.go | 43 ------------------- .../02-client/types/tendermint/test_utils.go | 31 +++++++++++++ 2 files changed, 31 insertions(+), 43 deletions(-) diff --git a/x/ibc/02-client/types/tendermint/misbehaviour_test.go b/x/ibc/02-client/types/tendermint/misbehaviour_test.go index fb432e32bcea..f5e75e12f785 100644 --- a/x/ibc/02-client/types/tendermint/misbehaviour_test.go +++ b/x/ibc/02-client/types/tendermint/misbehaviour_test.go @@ -5,52 +5,9 @@ import ( "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/tmhash" - tmtypes "github.com/tendermint/tendermint/types" yaml "gopkg.in/yaml.v2" ) -// Copied unimported test functions from tmtypes to use them here -func makeBlockID(hash []byte, partSetSize int, partSetHash []byte) tmtypes.BlockID { - return tmtypes.BlockID{ - Hash: hash, - PartsHeader: tmtypes.PartSetHeader{ - Total: partSetSize, - Hash: partSetHash, - }, - } - -} - -func makeVote(val tmtypes.PrivValidator, chainID string, valIndex int, height int64, round, step int, blockID tmtypes.BlockID) *tmtypes.Vote { - addr := val.GetPubKey().Address() - v := &tmtypes.Vote{ - ValidatorAddress: addr, - ValidatorIndex: valIndex, - Height: height, - Round: round, - Type: tmtypes.SignedMsgType(step), - BlockID: blockID, - } - err := val.SignVote(chainID, v) - if err != nil { - panic(err) - } - return v -} - -func randomDuplicatedVoteEvidence() *tmtypes.DuplicateVoteEvidence { - val := tmtypes.NewMockPV() - blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), 1000, tmhash.Sum([]byte("partshash"))) - blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), 1000, tmhash.Sum([]byte("partshash"))) - const chainID = "mychain" - return &tmtypes.DuplicateVoteEvidence{ - PubKey: val.GetPubKey(), - VoteA: makeVote(val, chainID, 0, 10, 2, 1, blockID), - VoteB: makeVote(val, chainID, 0, 10, 2, 1, blockID2), - } -} - func TestString(t *testing.T) { dupEv := randomDuplicatedVoteEvidence() ev := Evidence{ diff --git a/x/ibc/02-client/types/tendermint/test_utils.go b/x/ibc/02-client/types/tendermint/test_utils.go index c9f9b89f493e..a88bd13d3b1c 100644 --- a/x/ibc/02-client/types/tendermint/test_utils.go +++ b/x/ibc/02-client/types/tendermint/test_utils.go @@ -1,9 +1,11 @@ package tendermint import ( + "github.com/tendermint/tendermint/crypto/tmhash" tmtypes "github.com/tendermint/tendermint/types" ) +// Copied unimported test functions from tmtypes to use them here func makeBlockID(hash []byte, partSetSize int, partSetHash []byte) tmtypes.BlockID { return tmtypes.BlockID{ Hash: hash, @@ -14,3 +16,32 @@ func makeBlockID(hash []byte, partSetSize int, partSetHash []byte) tmtypes.Block } } + +func makeVote(val tmtypes.PrivValidator, chainID string, valIndex int, height int64, round, step int, blockID tmtypes.BlockID) *tmtypes.Vote { + addr := val.GetPubKey().Address() + v := &tmtypes.Vote{ + ValidatorAddress: addr, + ValidatorIndex: valIndex, + Height: height, + Round: round, + Type: tmtypes.SignedMsgType(step), + BlockID: blockID, + } + err := val.SignVote(chainID, v) + if err != nil { + panic(err) + } + return v +} + +func randomDuplicatedVoteEvidence() *tmtypes.DuplicateVoteEvidence { + val := tmtypes.NewMockPV() + blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), 1000, tmhash.Sum([]byte("partshash"))) + blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), 1000, tmhash.Sum([]byte("partshash"))) + const chainID = "mychain" + return &tmtypes.DuplicateVoteEvidence{ + PubKey: val.GetPubKey(), + VoteA: makeVote(val, chainID, 0, 10, 2, 1, blockID), + VoteB: makeVote(val, chainID, 0, 10, 2, 1, blockID2), + } +} From cd0aa22bfc963fa7413126de2d1c069e632cdf98 Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Mon, 4 Nov 2019 19:23:35 -0800 Subject: [PATCH 161/166] Add tests for msgs --- x/ibc/02-client/types/msgs_test.go | 138 +++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 x/ibc/02-client/types/msgs_test.go diff --git a/x/ibc/02-client/types/msgs_test.go b/x/ibc/02-client/types/msgs_test.go new file mode 100644 index 000000000000..8dc9a15d21c5 --- /dev/null +++ b/x/ibc/02-client/types/msgs_test.go @@ -0,0 +1,138 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" + "github.com/tendermint/tendermint/crypto/secp256k1" + cmn "github.com/tendermint/tendermint/libs/common" +) + +func TestMsgCreateClientValidateBasic(t *testing.T) { + cs := tendermint.ConsensusState{} + privKey := secp256k1.GenPrivKey() + signer := sdk.AccAddress(privKey.PubKey().Address()) + testMsgs := []MsgCreateClient{ + NewMsgCreateClient(exported.ClientTypeTendermint, exported.ClientTypeTendermint, cs, signer), // valid msg + NewMsgCreateClient("badClient", exported.ClientTypeTendermint, cs, signer), // invalid client id + NewMsgCreateClient("goodChain", "bad_type", cs, signer), // invalid client type + NewMsgCreateClient("goodChain", exported.ClientTypeTendermint, nil, signer), // nil Consensus State + NewMsgCreateClient("goodChain", exported.ClientTypeTendermint, cs, sdk.AccAddress{}), // empty signer + } + + cases := []struct { + msg MsgCreateClient + expPass bool + errMsg string + }{ + {testMsgs[0], true, ""}, + {testMsgs[1], false, "invalid client id passed"}, + {testMsgs[2], false, "unregistered client type passed"}, + {testMsgs[3], false, "Nil Consensus State in msg passed"}, + {testMsgs[4], false, "Empty address passed"}, + } + + for i, tc := range cases { + err := tc.msg.ValidateBasic() + if tc.expPass { + require.Nil(t, err, "Msg %d failed: %v", i, err) + } else { + require.NotNil(t, err, "Invalid Msg %d passed: %s", i, tc.errMsg) + } + } +} + +func TestMsgUpdateClient(t *testing.T) { + privKey := secp256k1.GenPrivKey() + signer := sdk.AccAddress(privKey.PubKey().Address()) + testMsgs := []MsgUpdateClient{ + NewMsgUpdateClient(exported.ClientTypeTendermint, tendermint.Header{}, signer), // valid msg + NewMsgUpdateClient("badClient", tendermint.Header{}, signer), // bad client id + NewMsgUpdateClient(exported.ClientTypeTendermint, nil, signer), // nil Header + NewMsgUpdateClient(exported.ClientTypeTendermint, tendermint.Header{}, sdk.AccAddress{}), // empty address + } + + cases := []struct { + msg MsgUpdateClient + expPass bool + errMsg string + }{ + {testMsgs[0], true, ""}, + {testMsgs[1], false, "invalid client id passed"}, + {testMsgs[2], false, "Nil Header passed"}, + {testMsgs[3], false, "Empty address passed"}, + } + + for i, tc := range cases { + err := tc.msg.ValidateBasic() + if tc.expPass { + require.Nil(t, err, "Msg %d failed: %v", i, err) + } else { + require.NotNil(t, err, "Invalid Msg %d passed: %s", i, tc.errMsg) + } + } +} + +var _ exported.Evidence = mockEvidence{} + +// mock GoodEvidence +type mockEvidence struct{} + +// Implement Evidence interface +func (me mockEvidence) Route() string { return "mock" } +func (me mockEvidence) Type() string { return "mock" } +func (me mockEvidence) String() string { return "mock" } +func (me mockEvidence) Hash() cmn.HexBytes { return cmn.HexBytes([]byte("mock")) } +func (me mockEvidence) ValidateBasic() sdk.Error { return nil } +func (me mockEvidence) GetConsensusAddress() sdk.ConsAddress { return sdk.ConsAddress{} } +func (me mockEvidence) GetHeight() int64 { return 3 } +func (me mockEvidence) GetValidatorPower() int64 { return 3 } +func (me mockEvidence) GetTotalPower() int64 { return 5 } + +// mock bad evidence +type mockBadEvidence struct { + mockEvidence +} + +// Override ValidateBasic +func (mbe mockBadEvidence) ValidateBasic() sdk.Error { + return errors.ErrInvalidEvidence(errors.DefaultCodespace, "invalid evidence") +} + +func TestMsgSubmitMisbehaviour(t *testing.T) { + privKey := secp256k1.GenPrivKey() + signer := sdk.AccAddress(privKey.PubKey().Address()) + testMsgs := []MsgSubmitMisbehaviour{ + NewMsgSubmitMisbehaviour(exported.ClientTypeTendermint, mockEvidence{}, signer), // valid msg + NewMsgSubmitMisbehaviour("badClient", mockEvidence{}, signer), // bad client id + NewMsgSubmitMisbehaviour(exported.ClientTypeTendermint, nil, signer), // nil evidence + NewMsgSubmitMisbehaviour(exported.ClientTypeTendermint, mockBadEvidence{}, signer), // invalid evidence + NewMsgSubmitMisbehaviour(exported.ClientTypeTendermint, mockEvidence{}, sdk.AccAddress{}), // empty signer + } + + cases := []struct { + msg MsgSubmitMisbehaviour + expPass bool + errMsg string + }{ + {testMsgs[0], true, ""}, + {testMsgs[1], false, "invalid client id passed"}, + {testMsgs[2], false, "Nil Evidence passed"}, + {testMsgs[3], false, "Invalid Evidence passed"}, + {testMsgs[4], false, "Empty address passed"}, + } + + for i, tc := range cases { + err := tc.msg.ValidateBasic() + if tc.expPass { + require.Nil(t, err, "Msg %d failed: %v", i, err) + } else { + require.NotNil(t, err, "Invalid Msg %d passed: %s", i, tc.errMsg) + } + } +} From 0c8b759156dac6ce0510329c1b4e16802df719b4 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 5 Nov 2019 12:29:16 +0100 Subject: [PATCH 162/166] upstream changes --- x/ibc/02-client/client/cli/query.go | 59 +++++++++++++++++---- x/ibc/02-client/client/cli/tx.go | 4 +- x/ibc/02-client/client/utils/utils.go | 75 +++++++++++++++++++++++++++ x/ibc/02-client/keeper/client.go | 1 + x/ibc/02-client/keeper/keeper.go | 63 +++++++++++----------- x/ibc/02-client/types/codec.go | 1 + x/ibc/02-client/types/querier.go | 29 +++++++++++ 7 files changed, 191 insertions(+), 41 deletions(-) create mode 100644 x/ibc/02-client/client/utils/utils.go diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index 9bd992b5d939..2f7166333899 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -14,6 +14,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/client/utils" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" @@ -34,6 +35,8 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { GetCmdQueryHeader(cdc), GetCmdQueryClientState(queryRoute, cdc), GetCmdQueryRoot(queryRoute, cdc), + GetCmdNodeConsensusState(queryRoute, cdc), + GetCmdQueryPath(queryRoute, cdc), )...) return ics02ClientQueryCmd } @@ -166,7 +169,6 @@ $ %s query ibc client consensus-state [client-id] } // GetCmdQueryHeader defines the command to query the latest header on the chain -// TODO: do we really need this cmd ?? func GetCmdQueryHeader(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "header", @@ -180,6 +182,33 @@ $ %s query ibc client header RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) + header, err := utils.GetTendermintHeader(cliCtx) + if err != nil { + return err + } + + return cliCtx.PrintOutput(header) + }, + } +} + +// GetCmdNodeConsensusState defines the command to query the latest consensus state of a node +// The result is feed to client creation +func GetCmdNodeConsensusState(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "node-state", + Short: "Query a node consensus state", + Long: strings.TrimSpace( + fmt.Sprintf(`Query a node consensus state. This result is feed to the client creation transaction. + +Example: +$ %s query ibc client node-state + `, version.ClientName), + ), + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + node, err := cliCtx.GetNode() if err != nil { return err @@ -203,18 +232,28 @@ $ %s query ibc client header return err } - nextValidators, err := node.Validators(&height) - if err != nil { - return err + var state exported.ConsensusState + state = tendermint.ConsensusState{ + ChainID: commit.ChainID, + Height: uint64(commit.Height), + Root: commitment.NewRoot(commit.AppHash), + NextValidatorSet: tmtypes.NewValidatorSet(validators.Validators), } - header := tendermint.Header{ - SignedHeader: commit.SignedHeader, - ValidatorSet: tmtypes.NewValidatorSet(validators.Validators), - NextValidatorSet: tmtypes.NewValidatorSet(nextValidators.Validators), - } + return cliCtx.PrintOutput(state) + }, + } +} - return cliCtx.PrintOutput(header) +// GetCmdQueryPath defines the command to query the commitment path. +func GetCmdQueryPath(storeName string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "path", + Short: "Query the commitment path of the running chain", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.NewCLIContext().WithCodec(cdc) + path := commitment.NewPrefix([]byte("ibc")) + return ctx.PrintOutput(path) }, } } diff --git a/x/ibc/02-client/client/cli/tx.go b/x/ibc/02-client/client/cli/tx.go index fc92efb874ba..4cbc97b39842 100644 --- a/x/ibc/02-client/client/cli/tx.go +++ b/x/ibc/02-client/client/cli/tx.go @@ -10,6 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" @@ -51,7 +52,7 @@ $ %s tx ibc client create [client-id] [path/to/consensus_state.json] --from node Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext().WithCodec(cdc) + cliCtx := context.NewCLIContext().WithCodec(cdc).WithBroadcastMode(flags.BroadcastBlock) clientID := args[0] @@ -72,6 +73,7 @@ $ %s tx ibc client create [client-id] [path/to/consensus_state.json] --from node clientID, state.ClientType().String(), state, cliCtx.GetFromAddress(), ) + if err := msg.ValidateBasic(); err != nil { return err } diff --git a/x/ibc/02-client/client/utils/utils.go b/x/ibc/02-client/client/utils/utils.go new file mode 100644 index 000000000000..5d0786267373 --- /dev/null +++ b/x/ibc/02-client/client/utils/utils.go @@ -0,0 +1,75 @@ +package utils + +import ( + "fmt" + + abci "github.com/tendermint/tendermint/abci/types" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" +) + +// QueryConsensusStateProof queries the store to get the consensus state and a +// merkle proof. +func QueryConsensusStateProof(cliCtx client.CLIContext, clientID string) (types.ConsensusStateResponse, error) { + var conStateRes types.ConsensusStateResponse + req := abci.RequestQuery{ + Path: "store/ibc/key", + Data: []byte(fmt.Sprintf("clients/%s/consensusState", clientID)), + Prove: true, + } + + res, err := cliCtx.QueryABCI(req) + if err != nil { + return conStateRes, err + } + + var cs tendermint.ConsensusState + if err := cliCtx.Codec.UnmarshalBinaryLengthPrefixed(res.Value, &cs); err != nil { + return conStateRes, err + } + return types.NewConsensusStateResponse(clientID, cs, res.Proof, res.Height), nil +} + +// GetTendermintHeader takes a client context and returns the appropriate +// tendermint header +func GetTendermintHeader(cliCtx context.CLIContext) (tendermint.Header, error) { + node, err := cliCtx.GetNode() + if err != nil { + return tendermint.Header{}, err + } + + info, err := node.ABCIInfo() + if err != nil { + return tendermint.Header{}, err + } + + height := info.Response.LastBlockHeight + prevheight := height - 1 + + commit, err := node.Commit(&height) + if err != nil { + return tendermint.Header{}, err + } + + validators, err := node.Validators(&prevheight) + if err != nil { + return tendermint.Header{}, err + } + + nextvalidators, err := node.Validators(&height) + if err != nil { + return tendermint.Header{}, err + } + + header := tendermint.Header{ + SignedHeader: commit.SignedHeader, + ValidatorSet: tmtypes.NewValidatorSet(validators.Validators), + NextValidatorSet: tmtypes.NewValidatorSet(nextvalidators.Validators), + } + + return header, nil +} diff --git a/x/ibc/02-client/keeper/client.go b/x/ibc/02-client/keeper/client.go index aa1faf956625..b75f8174939a 100644 --- a/x/ibc/02-client/keeper/client.go +++ b/x/ibc/02-client/keeper/client.go @@ -30,6 +30,7 @@ func (k Keeper) CreateClient( k.SetVerifiedRoot(ctx, clientID, consensusState.GetHeight(), consensusState.GetRoot()) k.SetClientState(ctx, clientState) k.SetClientType(ctx, clientID, clientType) + k.Logger(ctx).Info(fmt.Sprintf("client %s created at height %d", clientID, consensusState.GetHeight())) return clientState, nil } diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index f753f88656d5..2dc106d72d33 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -11,8 +11,6 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" - "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -31,8 +29,9 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, codespace sdk.CodespaceType) return Keeper{ storeKey: key, cdc: cdc, - codespace: sdk.CodespaceType(fmt.Sprintf("%s/%s", codespace, errors.DefaultCodespace)), // "ibc/client", - prefix: []byte(types.SubModuleName + "/"), // "client/" + codespace: sdk.CodespaceType(fmt.Sprintf("%s/%s", codespace, types.DefaultCodespace)), // "ibc/client", + prefix: []byte{}, + // prefix: []byte(types.SubModuleName + "/"), // "client/" } } @@ -102,6 +101,7 @@ func (k Keeper) SetConsensusState(ctx sdk.Context, clientID string, consensusSta // a client func (k Keeper) GetVerifiedRoot(ctx sdk.Context, clientID string, height uint64) (commitment.RootI, bool) { store := prefix.NewStore(ctx.KVStore(k.storeKey), k.prefix) + bz := store.Get(types.KeyRoot(clientID, height)) if bz == nil { return nil, false @@ -129,27 +129,25 @@ func (k Keeper) initialize(ctx sdk.Context, clientID string, consensusState expo } func (k Keeper) checkMisbehaviour(ctx sdk.Context, evidence exported.Evidence) error { - switch evidence.Type() { - case exported.ClientTypeTendermint: - var tmEvidence tendermint.Evidence - _, ok := evidence.(tendermint.Evidence) - if !ok { - return errors.ErrInvalidClientType(k.codespace, "consensus type is not Tendermint") - } - err := tendermint.CheckMisbehaviour(tmEvidence) - if err != nil { - return errors.ErrInvalidEvidence(k.codespace, err.Error()) - } - default: - panic(fmt.Sprintf("unregistered evidence type: %s", evidence.Type())) - } + // switch evidence.H1().ClientType() { + // case exported.Tendermint: + // var tmEvidence tendermint.Evidence + // _, ok := evidence.(tendermint.Evidence) + // if !ok { + // return sdkerrors.Wrap(types.ErrInvalidClientType(k.codespace), "consensus type is not Tendermint") + // } + // // TODO: pass past consensus states + // return tendermint.CheckMisbehaviour(tmEvidence) + // default: + // panic("unregistered consensus type") + // } return nil } // freeze updates the state of the client in the event of a misbehaviour func (k Keeper) freeze(ctx sdk.Context, clientState types.State) (types.State, error) { if clientState.Frozen { - return types.State{}, sdkerrors.Wrap(errors.ErrClientFrozen(k.codespace, clientState.ID()), "already frozen") + return types.State{}, sdkerrors.Wrap(types.ErrClientFrozen(k.codespace, clientState.ID()), "already frozen") } clientState.Frozen = true @@ -159,17 +157,20 @@ func (k Keeper) freeze(ctx sdk.Context, clientState types.State) (types.State, e // VerifyMembership state membership verification function defined by the client type func (k Keeper) VerifyMembership( ctx sdk.Context, - clientState types.State, + clientID string, height uint64, // sequence proof commitment.ProofI, path commitment.PathI, value []byte, ) bool { - if clientState.Frozen { - return false - } + // XXX: commented out for demo + /* + if clientState.Frozen { + return false + } + */ - root, found := k.GetVerifiedRoot(ctx, clientState.ID(), height) + root, found := k.GetVerifiedRoot(ctx, clientID, height) if !found { return false } @@ -180,16 +181,18 @@ func (k Keeper) VerifyMembership( // VerifyNonMembership state non-membership function defined by the client type func (k Keeper) VerifyNonMembership( ctx sdk.Context, - clientState types.State, + clientID string, height uint64, // sequence proof commitment.ProofI, path commitment.PathI, ) bool { - if clientState.Frozen { - return false - } - - root, found := k.GetVerifiedRoot(ctx, clientState.ID(), height) + // XXX: commented out for demo + /* + if clientState.Frozen { + return false + } + */ + root, found := k.GetVerifiedRoot(ctx, clientID, height) if !found { return false } diff --git a/x/ibc/02-client/types/codec.go b/x/ibc/02-client/types/codec.go index 69985c6044b4..7a141e811a0a 100644 --- a/x/ibc/02-client/types/codec.go +++ b/x/ibc/02-client/types/codec.go @@ -6,6 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" ) +// SubModuleCdc defines the IBC client codec. var SubModuleCdc = codec.New() // RegisterCodec registers the IBC client interfaces and types diff --git a/x/ibc/02-client/types/querier.go b/x/ibc/02-client/types/querier.go index 2f5de5af0572..7638c0200882 100644 --- a/x/ibc/02-client/types/querier.go +++ b/x/ibc/02-client/types/querier.go @@ -1,5 +1,13 @@ package types +import ( + "strings" + + tmtypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + "github.com/tendermint/tendermint/crypto/merkle" +) + // query routes supported by the IBC client Querier const ( QueryClientState = "client_state" @@ -35,3 +43,24 @@ func NewQueryCommitmentRootParams(id string, height uint64) QueryCommitmentRootP Height: height, } } + +// ConsensusStateResponse defines the client response for a Consensus state query. +// It includes the commitment proof and the height of the proof. +type ConsensusStateResponse struct { + ConsensusState tmtypes.ConsensusState + Proof commitment.Proof `json:"proof,omitempty" yaml:"proof,omitempty"` + ProofPath commitment.Path `json:"proof_path,omitempty" yaml:"proof_path,omitempty"` + ProofHeight uint64 `json:"proof_height,omitempty" yaml:"proof_height,omitempty"` +} + +// NewConsensusStateResponse creates a new ConsensusStateResponse instance. +func NewConsensusStateResponse( + clientID string, cs tmtypes.ConsensusState, proof *merkle.Proof, height int64, +) ConsensusStateResponse { + return ConsensusStateResponse{ + ConsensusState: cs, + Proof: commitment.Proof{Proof: proof}, + ProofPath: commitment.NewPath(strings.Split(ConsensusStatePath(clientID), "/")), + ProofHeight: uint64(height), + } +} From c6fa07be197cefe0967706968c6ad58b6ae77a38 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 5 Nov 2019 12:35:53 +0100 Subject: [PATCH 163/166] fix --- x/ibc/02-client/keeper/keeper.go | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index 2dc106d72d33..ce0d54e99782 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -11,6 +11,8 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -29,7 +31,7 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, codespace sdk.CodespaceType) return Keeper{ storeKey: key, cdc: cdc, - codespace: sdk.CodespaceType(fmt.Sprintf("%s/%s", codespace, types.DefaultCodespace)), // "ibc/client", + codespace: sdk.CodespaceType(fmt.Sprintf("%s/%s", codespace, errors.DefaultCodespace)), // "ibc/client", prefix: []byte{}, // prefix: []byte(types.SubModuleName + "/"), // "client/" } @@ -129,25 +131,27 @@ func (k Keeper) initialize(ctx sdk.Context, clientID string, consensusState expo } func (k Keeper) checkMisbehaviour(ctx sdk.Context, evidence exported.Evidence) error { - // switch evidence.H1().ClientType() { - // case exported.Tendermint: - // var tmEvidence tendermint.Evidence - // _, ok := evidence.(tendermint.Evidence) - // if !ok { - // return sdkerrors.Wrap(types.ErrInvalidClientType(k.codespace), "consensus type is not Tendermint") - // } - // // TODO: pass past consensus states - // return tendermint.CheckMisbehaviour(tmEvidence) - // default: - // panic("unregistered consensus type") - // } + switch evidence.Type() { + case exported.ClientTypeTendermint: + var tmEvidence tendermint.Evidence + _, ok := evidence.(tendermint.Evidence) + if !ok { + return errors.ErrInvalidClientType(k.codespace, "consensus type is not Tendermint") + } + err := tendermint.CheckMisbehaviour(tmEvidence) + if err != nil { + return errors.ErrInvalidEvidence(k.codespace, err.Error()) + } + default: + panic(fmt.Sprintf("unregistered evidence type: %s", evidence.Type())) + } return nil } // freeze updates the state of the client in the event of a misbehaviour func (k Keeper) freeze(ctx sdk.Context, clientState types.State) (types.State, error) { if clientState.Frozen { - return types.State{}, sdkerrors.Wrap(types.ErrClientFrozen(k.codespace, clientState.ID()), "already frozen") + return types.State{}, sdkerrors.Wrap(errors.ErrClientFrozen(k.codespace, clientState.ID()), "already frozen") } clientState.Frozen = true From 820482f030deb87f766ff0cf3f54928b68a38dd9 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 5 Nov 2019 12:42:37 +0100 Subject: [PATCH 164/166] upstream changes --- x/ibc/23-commitment/merkle.go | 10 +-------- x/ibc/24-host/errors.go | 3 +++ x/ibc/24-host/utils.go | 11 ++++++++++ x/ibc/24-host/validate.go | 38 ++++++++++++++++++++++++++++------- 4 files changed, 46 insertions(+), 16 deletions(-) create mode 100644 x/ibc/24-host/utils.go diff --git a/x/ibc/23-commitment/merkle.go b/x/ibc/23-commitment/merkle.go index 8754823e0683..ab428eeaf737 100644 --- a/x/ibc/23-commitment/merkle.go +++ b/x/ibc/23-commitment/merkle.go @@ -2,7 +2,6 @@ package commitment import ( "errors" - "strings" "github.com/tendermint/tendermint/crypto/merkle" @@ -111,14 +110,7 @@ func ApplyPrefix(prefix PrefixI, path string) (Path, error) { return Path{}, errors.New("prefix can't be empty") } - // Split paths by the separator - pathSlice := strings.Split(path, "/") - keyPath := merkle.KeyPath{} - commitmentPath := NewPath(pathSlice) - - keyPath = keyPath.AppendKey(prefix.Bytes(), merkle.KeyEncodingURL) - commitmentPath.KeyPath = append(keyPath, commitmentPath.KeyPath...) - return commitmentPath, nil + return NewPath([]string{string(prefix.Bytes()), path}), nil } var _ ProofI = Proof{} diff --git a/x/ibc/24-host/errors.go b/x/ibc/24-host/errors.go index e1fa5e0e7a08..afc1c26ea303 100644 --- a/x/ibc/24-host/errors.go +++ b/x/ibc/24-host/errors.go @@ -13,4 +13,7 @@ var ( // ErrInvalidPath is returned if path string is invalid ErrInvalidPath = sdkerrors.Register(IBCCodeSpace, 2, "invalid path") + + // ErrInvalidPacket is returned if packets embedded in msg are invalid + ErrInvalidPacket = sdkerrors.Register(IBCCodeSpace, 3, "invalid packet extracted from msg") ) diff --git a/x/ibc/24-host/utils.go b/x/ibc/24-host/utils.go new file mode 100644 index 000000000000..c75f356561f6 --- /dev/null +++ b/x/ibc/24-host/utils.go @@ -0,0 +1,11 @@ +package host + +// RemovePath is an util function to remove a path from a set. +func RemovePath(paths []string, path string) ([]string, bool) { + for i, p := range paths { + if p == path { + return append(paths[:i], paths[i+1:]...), true + } + } + return paths, false +} diff --git a/x/ibc/24-host/validate.go b/x/ibc/24-host/validate.go index 38128de234ae..6632d27c25c1 100644 --- a/x/ibc/24-host/validate.go +++ b/x/ibc/24-host/validate.go @@ -22,18 +22,14 @@ var isAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString // ValidateFn function type to validate path and identifier bytestrings type ValidateFn func(string) error -// DefaultIdentifierValidator is the default validator function for Client, -// Connection and Channel identifiers. -// A valid Identifier must be between 10-20 characters and only contain lowercase -// alphabetic characters, -func DefaultIdentifierValidator(id string) error { +func defaultIdentifierValidator(id string, min, max int) error { // valid id MUST NOT contain "/" separator if strings.Contains(id, "/") { return sdkerrors.Wrap(ErrInvalidID, "identifier cannot contain separator: /") } // valid id must be between 10 and 20 characters - if len(id) < 10 || len(id) > 20 { - return sdkerrors.Wrapf(ErrInvalidID, "identifier has invalid length: %d, must be between 10-20 characters", len(id)) + if len(id) < min || len(id) > max { + return sdkerrors.Wrapf(ErrInvalidID, "identifier has invalid length: %d, must be between %d-%d characters", len(id), min, max) } // valid id must contain only lower alphabetic characters if !isAlphaLower(id) { @@ -42,6 +38,34 @@ func DefaultIdentifierValidator(id string) error { return nil } +// DefaultClientIdentifierValidator is the default validator function for Client identifiers +// A valid Identifier must be between 10-20 characters and only contain lowercase +// alphabetic characters, +func DefaultClientIdentifierValidator(id string) error { + return defaultIdentifierValidator(id, 10, 20) +} + +// DefaultConnectionIdentifierValidator is the default validator function for Connection identifiers +// A valid Identifier must be between 10-20 characters and only contain lowercase +// alphabetic characters, +func DefaultConnectionIdentifierValidator(id string) error { + return defaultIdentifierValidator(id, 10, 20) +} + +// DefaultChannelIdentifierValidator is the default validator function for Channel identifiers +// A valid Identifier must be between 10-20 characters and only contain lowercase +// alphabetic characters, +func DefaultChannelIdentifierValidator(id string) error { + return defaultIdentifierValidator(id, 10, 20) +} + +// DefaultPortIdentifierValidator is the default validator function for Port identifiers +// A valid Identifier must be between 2-20 characters and only contain lowercase +// alphabetic characters, +func DefaultPortIdentifierValidator(id string) error { + return defaultIdentifierValidator(id, 2, 20) +} + // NewPathValidator takes in a Identifier Validator function and returns // a Path Validator function which requires path only has valid identifiers // alphanumeric character strings, and "/" separators From b5442d341164bfcbc81245a68a775adfdf29068e Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 5 Nov 2019 12:52:34 +0100 Subject: [PATCH 165/166] fix cons state --- x/ibc/02-client/types/tendermint/consensus_state.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/ibc/02-client/types/tendermint/consensus_state.go b/x/ibc/02-client/types/tendermint/consensus_state.go index 74e8a9f42dfa..3ad0892a9ea8 100644 --- a/x/ibc/02-client/types/tendermint/consensus_state.go +++ b/x/ibc/02-client/types/tendermint/consensus_state.go @@ -75,8 +75,8 @@ func (cs ConsensusState) checkValidity(header Header) error { } // abortTransactionUnless(consensusState.publicKey.verify(header.signature)) - return cs.NextValidatorSet.VerifyFutureCommit( - header.ValidatorSet, cs.ChainID, header.Commit.BlockID, header.Height, header.Commit, + return header.ValidatorSet.VerifyFutureCommit( + cs.NextValidatorSet, cs.ChainID, header.Commit.BlockID, header.Height, header.Commit, ) } From 7c67236659ba14ae13b50dd22f655cd9161066b9 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 5 Nov 2019 12:55:29 +0100 Subject: [PATCH 166/166] context changes --- client/context/context.go | 47 ++++++++++++++++++++++++++++++++++ client/context/query.go | 53 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/client/context/context.go b/client/context/context.go index 2fd6ff66f8be..07168ecdf6ca 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -99,6 +99,53 @@ func NewCLIContextWithFrom(from string) CLIContext { return ctx.WithVerifier(verifier) } +// NewCLIContextIBC takes additional arguements +func NewCLIContextIBC(from string, chainID string, nodeURI string) CLIContext { + var rpc rpcclient.Client + + genOnly := viper.GetBool(flags.FlagGenerateOnly) + fromAddress, fromName, err := GetFromFields(from, genOnly) + if err != nil { + fmt.Printf("failed to get from fields: %v", err) + os.Exit(1) + } + + if !genOnly { + if nodeURI != "" { + rpc = rpcclient.NewHTTP(nodeURI, "/websocket") + } + } + + ctx := CLIContext{ + Client: rpc, + ChainID: chainID, + Output: os.Stdout, + NodeURI: nodeURI, + From: from, + OutputFormat: viper.GetString(cli.OutputFlag), + Height: viper.GetInt64(flags.FlagHeight), + HomeDir: viper.GetString(flags.FlagHome), + TrustNode: viper.GetBool(flags.FlagTrustNode), + UseLedger: viper.GetBool(flags.FlagUseLedger), + BroadcastMode: viper.GetString(flags.FlagBroadcastMode), + Simulate: viper.GetBool(flags.FlagDryRun), + GenerateOnly: genOnly, + FromAddress: fromAddress, + FromName: fromName, + Indent: viper.GetBool(flags.FlagIndentResponse), + SkipConfirm: viper.GetBool(flags.FlagSkipConfirmation), + } + + // create a verifier for the specific chain ID and RPC client + verifier, err := CreateVerifier(ctx, DefaultVerifierCacheSize) + if err != nil && viper.IsSet(flags.FlagTrustNode) { + fmt.Printf("failed to create verifier: %s\n", err) + os.Exit(1) + } + + return ctx.WithVerifier(verifier) +} + // NewCLIContext returns a new initialized CLIContext with parameters from the // command line using Viper. func NewCLIContext() CLIContext { return NewCLIContextWithFrom(viper.GetString(flags.FlagFrom)) } diff --git a/client/context/query.go b/client/context/query.go index 81917a1c493c..2a01a7577a0c 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -3,6 +3,7 @@ package context import ( "fmt" "strings" + "time" "github.com/pkg/errors" @@ -12,6 +13,7 @@ import ( tmliteErr "github.com/tendermint/tendermint/lite/errors" tmliteProxy "github.com/tendermint/tendermint/lite/proxy" rpcclient "github.com/tendermint/tendermint/rpc/client" + ctypes "github.com/tendermint/tendermint/rpc/core/types" tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/store/rootmulti" @@ -28,6 +30,55 @@ func (ctx CLIContext) GetNode() (rpcclient.Client, error) { return ctx.Client, nil } +// WaitForNBlocks blocks until the node defined on the context has advanced N blocks +func (ctx CLIContext) WaitForNBlocks(n int64) { + node, err := ctx.GetNode() + if err != nil { + panic(err) + } + + resBlock, err := node.Block(nil) + var height int64 + if err != nil || resBlock.Block == nil { + // wait for the first block to exist + ctx.waitForHeight(1) + height = 1 + n + } else { + height = resBlock.Block.Height + n + } + ctx.waitForHeight(height) +} + +func (ctx CLIContext) waitForHeight(height int64) { + node, err := ctx.GetNode() + if err != nil { + panic(err) + } + + for { + // get url, try a few times + var resBlock *ctypes.ResultBlock + var err error + INNER: + for i := 0; i < 5; i++ { + resBlock, err = node.Block(nil) + if err == nil { + break INNER + } + time.Sleep(time.Millisecond * 200) + } + if err != nil { + panic(err) + } + + if resBlock.Block != nil && resBlock.Block.Height >= height { + return + } + + time.Sleep(time.Millisecond * 100) + } +} + // Query performs a query to a Tendermint node with the provided path. // It returns the result and height of the query upon success or an error if // the query fails. @@ -97,7 +148,7 @@ func (ctx CLIContext) queryABCI(req abci.RequestQuery) (resp abci.ResponseQuery, opts := rpcclient.ABCIQueryOptions{ Height: ctx.Height, - Prove: !ctx.TrustNode, + Prove: req.Prove || !ctx.TrustNode, } result, err := node.ABCIQueryWithOptions(req.Path, req.Data, opts)