diff --git a/CHANGELOG.md b/CHANGELOG.md index a32f2e23..a85eb0b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## HEAD +- use external crud package - make controllers API nicer - enhance executors tests - allow open domain transfers diff --git a/go.mod b/go.mod index 757fd1bb..b6da657c 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/golang/mock v1.3.1 // indirect github.com/gorilla/mux v1.7.4 github.com/gorilla/websocket v1.4.2 + github.com/iov-one/cosmos-sdk-crud v0.0.0-20200804183153-2b7470a92e52 github.com/jinzhu/gorm v1.9.14 github.com/lib/pq v1.1.1 github.com/onsi/ginkgo v1.8.0 // indirect diff --git a/go.sum b/go.sum index bc4fe32c..d919c578 100644 --- a/go.sum +++ b/go.sum @@ -243,6 +243,10 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d h1:/WZQPMZNsjZ7IlCpsLGdQBINg5bxKQ1K1sh6awxLtkA= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/iov-one/cosmos-sdk-crud v0.0.0-20200804182707-29e3147b6e2e h1:KM8wM8RVerY5QaUTDsjxY9Ousw8Vn32vHOklnoOBbrA= +github.com/iov-one/cosmos-sdk-crud v0.0.0-20200804182707-29e3147b6e2e/go.mod h1:wfvPz9CNFOERo+9wnq2jUeDKH6NHSrFtJcNA2oB6UdQ= +github.com/iov-one/cosmos-sdk-crud v0.0.0-20200804183153-2b7470a92e52 h1:fCi/QCX1F8J61LrRsSmWBXpTVIdDFAi3Enf+6FQkd7g= +github.com/iov-one/cosmos-sdk-crud v0.0.0-20200804183153-2b7470a92e52/go.mod h1:wfvPz9CNFOERo+9wnq2jUeDKH6NHSrFtJcNA2oB6UdQ= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/gorm v1.9.14 h1:Kg3ShyTPcM6nzVo148fRrcMO6MNKuqtOUwnzqMgVniM= github.com/jinzhu/gorm v1.9.14/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= diff --git a/pkg/crud/aliasing.go b/pkg/crud/aliasing.go deleted file mode 100644 index 9ea4115e..00000000 --- a/pkg/crud/aliasing.go +++ /dev/null @@ -1,14 +0,0 @@ -package crud - -import ( - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/iov-one/iovns/pkg/crud/internal/store" - "github.com/iov-one/iovns/pkg/crud/types" -) - -// NewStore returns a new CRUD key value store -func NewStore(ctx sdk.Context, key sdk.StoreKey, cdc *codec.Codec, uniquePrefix []byte) types.Store { - return store.NewStore(ctx, key, cdc, uniquePrefix) -} - diff --git a/pkg/crud/internal/filter/filter.go b/pkg/crud/internal/filter/filter.go deleted file mode 100644 index 31cab44f..00000000 --- a/pkg/crud/internal/filter/filter.go +++ /dev/null @@ -1,71 +0,0 @@ -package filter - -import ( - "bytes" - "fmt" - "github.com/iov-one/iovns/pkg/crud/types" - "sort" -) - -type store interface { - Read(key types.PrimaryKey, o types.Object) bool - Delete(key types.PrimaryKey) - Update(o types.Object) -} - -type Filtered struct { - counter int - nKeys int - primaryKeys []types.PrimaryKey - store store -} - -func NewFiltered(keys []types.PrimaryKey, store store) *Filtered { - // sort deterministically - sort.Slice(keys, func(i, j int) bool { - return bytes.Compare(keys[i].Key(), keys[j].Key()) < 0 - }) - return &Filtered{ - counter: 0, - nKeys: len(keys), - primaryKeys: keys, - store: store, - } -} - -func (f *Filtered) Read(o types.Object) { - ok := f.store.Read(f.currKey(), o) - if !ok { - panic(fmt.Sprintf("can't find object using primary key: %s", f.currKey())) - } -} - -func (f *Filtered) Update(o types.Object) { - if !bytes.Equal(o.PrimaryKey().Key(), f.currKey().Key()) { - panic("trying to update objects with unmatching primary keys") - } - f.store.Update(o) -} - -func (f *Filtered) Delete() { - if !f.Valid() { - return - } - f.store.Delete(f.currKey()) -} - -func (f *Filtered) currKey() types.PrimaryKey { - if f.counter == 0 && f.nKeys == 0 { - panic("iterating an empty filter is not valid") - } - return f.primaryKeys[f.counter] -} - -func (f *Filtered) Next() { - f.counter++ -} - -func (f *Filtered) Valid() bool { - return f.counter < f.nKeys -} - diff --git a/pkg/crud/internal/store/index.go b/pkg/crud/internal/store/index.go deleted file mode 100644 index b6b8d6ee..00000000 --- a/pkg/crud/internal/store/index.go +++ /dev/null @@ -1,119 +0,0 @@ -package store - -import ( - "bytes" - "fmt" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/prefix" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/iov-one/iovns/pkg/crud/types" - "sort" -) - -// _indexes is the indexes store, it simply uses a store in which it saves -// pointers to primary keys given an index with a fixed prefix -// and a pointer to the whole list of indexes in order to delete them when -// required, during object updates and deletions -type _indexes struct { - pointers sdk.KVStore // pointers is the store which contains the pointers to primary keys - list sdk.KVStore // list contains the list of indexes - cdc *codec.Codec -} - -// newIndexes is the _indexes constructor -func newIndexes(cdc *codec.Codec, store sdk.KVStore) indexes { - return _indexes{ - pointers: prefix.NewStore(store, indexPrefix), - list: prefix.NewStore(store, indexListPrefix), - cdc: cdc, - } -} - -// storeFromSecondaryKey returns the prefixed key value store for the given secondary key -func (s _indexes) storeFromSecondaryKey(sk types.SecondaryKey) sdk.KVStore { - indexPrefix := prefix.NewStore(s.pointers, []byte{sk.Prefix()}) - return prefix.NewStore(indexPrefix, sk.Key()) -} - -// create creates indexes for the given object -func (s _indexes) create(o types.Object) { - sks := o.SecondaryKeys() - pk := o.PrimaryKey() - for _, sk := range sks { - store := s.storeFromSecondaryKey(sk) - store.Set(pk.Key(), []byte{}) - } - s.createIndexList(pk, sks) -} - -// delete deletes the indexes that point to the given primary key -func (s _indexes) delete(pk types.PrimaryKey) { - secondaryKeys := s.getSecondaryKeysFromPrimary(pk) - for _, sk := range secondaryKeys { - store := s.storeFromSecondaryKey(sk) - store.Delete(pk.Key()) - } - // remove indexes - s.deleteIndexList(pk) -} - -// iterate finds all the primary keys to which the given secondary key points to -func (s _indexes) iterate(sk types.SecondaryKey, do func(pk types.PrimaryKey) bool) { - store := s.storeFromSecondaryKey(sk) - it := store.Iterator(nil, nil) - defer it.Close() - for ; it.Valid(); it.Next() { - primaryKey := types.NewPrimaryKey(it.Key()) - if !do(primaryKey) { - break - } - } -} - -// getSecondaryKeysFromPrimary returns all the secondary keys associated with an object with the given primary key -func (s _indexes) getSecondaryKeysFromPrimary(pk types.PrimaryKey) []types.SecondaryKey { - v := s.list.Get(pk.Key()) - if v == nil { - panic(fmt.Sprintf("no index exists for given key: %x", pk)) - } - indexes := new(marshalledIndexes) - s.cdc.MustUnmarshalBinaryBare(v, indexes) - secondaryKeys := make([]types.SecondaryKey, len(indexes.Indexes)) - for i, index := range indexes.Indexes { - sk := types.NewSecondaryKeyFromBytes(index) - secondaryKeys[i] = sk - } - return secondaryKeys -} - -// createIndexList creates the pointer to the whole list of secondary keys associated with an object's primary key -func (s _indexes) createIndexList(pk types.PrimaryKey, sks []types.SecondaryKey) { - indexes := make([][]byte, len(sks)) - for i, sk := range sks { - key := sk.Marshal() - indexes[i] = key - } - // order it - sort.Slice(indexes, func(i, j int) bool { - return bytes.Compare(indexes[i], indexes[j]) < 0 - }) - // check if create/delete flow is correctly applied - if s.list.Has(pk.Key()) { - panic(fmt.Sprintf("index list for primary key %x should have been deleted and then reset", pk.Key())) - } - // set them - s.list.Set(pk.Key(), s.cdc.MustMarshalBinaryBare(marshalledIndexes{Indexes: indexes})) -} - -// deleteIndexList deletes the pointer of the list of secondary keys associated with an object's primary key -func (s _indexes) deleteIndexList(pk types.PrimaryKey) { - if !s.list.Has(pk.Key()) { - panic(fmt.Sprintf("cannot remove index list because it does not exist for key: %x", pk)) - } - s.list.Delete(pk.Key()) -} - -// marshalledIndexes is the byte array containing -type marshalledIndexes struct { - Indexes [][]byte -} \ No newline at end of file diff --git a/pkg/crud/internal/store/keyset.go b/pkg/crud/internal/store/keyset.go deleted file mode 100644 index ee5099fd..00000000 --- a/pkg/crud/internal/store/keyset.go +++ /dev/null @@ -1,81 +0,0 @@ -package store - -import ( - "github.com/iov-one/iovns/pkg/crud/types" - "sort" -) - -type hash string - -// TODO this is an inefficient implementation that should be changed asap. -type keySet map[hash]struct{} - -func (k keySet) Insert(b types.PrimaryKey) { - key := b.Key() - k[hash(key)] = struct{}{} -} - -func (k keySet) Has(b types.PrimaryKey) bool { - key := b.Key() - _, ok := k[hash(key)] - return ok -} - -func (k keySet) Keys() []types.PrimaryKey { - primaryKeys := make([]types.PrimaryKey, 0, len(k)) - for key := range k { - primaryKeys = append(primaryKeys, types.NewPrimaryKeyFromString(string(key))) - } - return primaryKeys -} - -func (k keySet) Len() int { - return len(k) -} - -type set interface { - Has(b types.PrimaryKey) bool - Keys() []types.PrimaryKey - Len() int -} - -func primaryKeysFromSets(sets []set) []types.PrimaryKey { - if len(sets) == 0 { - return nil - } - if len(sets) == 1 { - return sets[0].Keys() - } - // determine the smallest set, as the final filter - // will have, at best, all the keys in the smallest one - sort.Slice(sets, func(i, j int) bool { - return sets[i].Len() < sets[j].Len() - }) - smallestSet := sets[0] - smallerLen := smallestSet.Len() - // if smallest is zero then return nothing - if smallerLen == 0 { - return nil - } - // nice now start filtering - primaryKeys := make([]types.PrimaryKey, 0, smallestSet.Len()) - for _, key := range smallestSet.Keys() { - if !isInAll(key, sets) { - continue - } - primaryKeys = append(primaryKeys, key) - } - // success - return primaryKeys -} - -// isInAll verifies that the given primary key is present in all sets -func isInAll(key types.PrimaryKey, sets []set) bool { - for _, set := range sets { - if !set.Has(key) { - return false - } - } - // key is in all sets - return true -} diff --git a/pkg/crud/internal/store/keyset_test.go b/pkg/crud/internal/store/keyset_test.go deleted file mode 100644 index d255f886..00000000 --- a/pkg/crud/internal/store/keyset_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package store - -import ( - "github.com/iov-one/iovns/pkg/crud/types" - "reflect" - "testing" -) - -func Test_filter(t *testing.T) { - set1 := make(keySet) - set1.Insert(types.NewPrimaryKeyFromString("1")) - set1.Insert(types.NewPrimaryKeyFromString("2")) - set1.Insert(types.NewPrimaryKeyFromString("5")) - set2 := make(keySet) - set2.Insert(types.NewPrimaryKeyFromString("2")) - set2.Insert(types.NewPrimaryKeyFromString("3")) - set3 := make(keySet) - set3.Insert(types.NewPrimaryKeyFromString("5")) - set3.Insert(types.NewPrimaryKeyFromString("2")) - expected := []types.PrimaryKey{types.NewPrimaryKeyFromString("2")} - result := primaryKeysFromSets([]set{set1, set2, set3}) - if !reflect.DeepEqual(expected, result) { - t.Fatalf("unexpected result got: %#v", result) - } -} diff --git a/pkg/crud/internal/store/main_test.go b/pkg/crud/internal/store/main_test.go deleted file mode 100644 index 76e752aa..00000000 --- a/pkg/crud/internal/store/main_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package store - -import ( - "fmt" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" - tmtypes "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/libs/log" - db "github.com/tendermint/tm-db" - "os" - "testing" - "time" -) - -var testCtx sdk.Context -var testKey = sdk.NewKVStoreKey("test") -var testCdc *codec.Codec - -func newTest() error { - testCdc = codec.New() - mdb := db.NewMemDB() - ms := store.NewCommitMultiStore(mdb) - ms.MountStoreWithDB(testKey, sdk.StoreTypeIAVL, mdb) - err := ms.LoadLatestVersion() - if err != nil { - return err - } - testCtx = sdk.NewContext(ms, tmtypes.Header{Time: time.Now()}, true, log.NewNopLogger()) - return nil -} - -func TestMain(m *testing.M) { - err := newTest() - if err != nil { - fmt.Println(err) - os.Exit(1) - } - os.Exit(m.Run()) -} diff --git a/pkg/crud/internal/store/objects.go b/pkg/crud/internal/store/objects.go deleted file mode 100644 index aa7c1510..00000000 --- a/pkg/crud/internal/store/objects.go +++ /dev/null @@ -1,89 +0,0 @@ -package store - -import ( - "fmt" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/prefix" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/iov-one/iovns/pkg/crud/types" -) - -// newObjectsStore builds the object store -func newObjectsStore(cdc *codec.Codec, kv sdk.KVStore) _objects { - return _objects{cdc: cdc, store: prefix.NewStore(kv, objectPrefix)} -} - -// _objects is the objects store -type _objects struct { - cdc *codec.Codec - store sdk.KVStore -} - -// create creates an object in the store -func (s _objects) create(o types.Object) { - pk := o.PrimaryKey() - if s.store.Has(pk.Key()) { - panic(fmt.Errorf("cannot re-create an existing object with primary key: %x", pk.Key())) - } - s.store.Set(pk.Key(), s.encode(o)) -} - -// delete deletes an object from the store -func (s _objects) delete(pk types.PrimaryKey) { - if !s.store.Has(pk.Key()) { - panic(fmt.Errorf("cannot delete non existing object with primary key: %x", pk.Key())) - } - s.store.Delete(pk.Key()) -} - -// update updates the object in the store -func (s _objects) update(o types.Object) { - pk := o.PrimaryKey() - if !s.store.Has(pk.Key()) { - panic(fmt.Errorf("cannot update non existing object with primary key: %x", pk.Key())) - } - s.store.Set(o.PrimaryKey().Key(), s.encode(o)) -} - -// read reads to the given target using the provided primary key -func (s _objects) read(pk types.PrimaryKey, target types.Object) bool { - v := s.store.Get(pk.Key()) - if v == nil { - return false - } - s.decode(v, target) - return true -} - -// iterate iterates -func (s _objects) iterate(do func(pk types.PrimaryKey) bool) { - it := s.store.Iterator(nil, nil) - defer it.Close() - for ; it.Valid(); it.Next() { - key := types.NewPrimaryKey(it.Key()) - if !do(key) { - break - } - } -} - -func (s _objects) encode(o interface{}) []byte { - if e, ok := o.(encoder); ok { - o = e.MarshalCRUD() - } - return s.cdc.MustMarshalBinaryBare(o) -} - -func (s _objects) decode(b []byte, o interface{}) { - e, ok := o.(encoder) - if !ok { - s.cdc.MustUnmarshalBinaryBare(b, o) - return - } - e.UnmarshalCRUD(s.cdc, b) -} - -type encoder interface { - MarshalCRUD() interface{} - UnmarshalCRUD(cdc *codec.Codec, b []byte) -} \ No newline at end of file diff --git a/pkg/crud/internal/store/store.go b/pkg/crud/internal/store/store.go deleted file mode 100644 index bf80f2a2..00000000 --- a/pkg/crud/internal/store/store.go +++ /dev/null @@ -1,130 +0,0 @@ -package store - -import ( - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/prefix" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/iov-one/iovns/pkg/crud/internal/filter" - "github.com/iov-one/iovns/pkg/crud/types" -) - -var indexListPrefix = []byte{0x2} -var indexPrefix = []byte{0x01} -var objectPrefix = []byte{0x00} - -type objects interface { - create(o types.Object) - read(key types.PrimaryKey, o types.Object) bool - delete(key types.PrimaryKey) - update(o types.Object) - iterate(do func(pk types.PrimaryKey) bool) -} - -type indexes interface { - create(o types.Object) - delete(pk types.PrimaryKey) - iterate(sk types.SecondaryKey, do func(pk types.PrimaryKey) bool) -} - -// Store defines a crud object store -// the store creates two sub-stores -// using prefixing, one is used to store objects -// the other one is used to store the indexes of -// the object. -type Store struct { - objects objects - indexes indexes - raw sdk.KVStore -} - -// NewStore generates a new crud.Store given a context, a store key, the codec and a unique prefix -// that can be specified as nil if not required, the prefix generally serves the purpose of splitting -// a store into different stores in case different objects have to coexist in the same store. -func NewStore(ctx sdk.Context, key sdk.StoreKey, cdc *codec.Codec, uniquePrefix []byte) Store { - store := ctx.KVStore(key) - if len(uniquePrefix) != 0 { - store = prefix.NewStore(store, uniquePrefix) - } - return Store{ - indexes: newIndexes(cdc, store), - objects: newObjectsStore(cdc, store), - raw: store, - } -} - -func (s Store) Filter(fltr types.Object) types.Filter { - // if the primary key is specified then return that - var primaryKeys []types.PrimaryKey - pk := fltr.PrimaryKey() - // if primary key exists then just use that - if pk != nil && len(pk.Key()) != 0 { - primaryKeys = append(primaryKeys, pk) - return filter.NewFiltered(primaryKeys, s) - } - primaryKeys = append(primaryKeys, s.getPrimaryKeys(fltr.SecondaryKeys())...) - return filter.NewFiltered(primaryKeys, s) -} - -// Create creates a new object in the object store and writes its indexes -func (s Store) Create(o types.Object) { - primaryKey := o.PrimaryKey() - // TODO this in the future needs to be autogenerated to decouple - // the need for a primary key for objects that do not need or have it - // and rely on indexes for filtering of different objects - if len(primaryKey.Key()) == 0 { - panic("empty primary key provided") - } - // create object - s.objects.create(o) - // generate indexes - s.indexes.create(o) -} - -// Read reads in the object store and returns false if the object is not found -// if it is found then the binary is unmarshalled into the Object. -// CONTRACT: Object must be a pointer for the unmarshalling to take effect. -func (s Store) Read(key types.PrimaryKey, o types.Object) (ok bool) { - return s.objects.read(key, o) -} - -func (s Store) IterateKeys(do func(pk types.PrimaryKey) bool) { - s.objects.iterate(do) -} - -// Update updates the given Object in the objects store -// after clearing the indexes and reapplying them based on the -// new update. -// To achieve so a zeroed copy of Object is created which is used to -// unmarshal the old object contents which is necessary for the un-indexing. -func (s Store) Update(newObject types.Object) { - pk := newObject.PrimaryKey() - // remove old indexes - s.indexes.delete(pk) - // set new object - s.objects.update(newObject) - // index new object - s.indexes.create(newObject) -} - -// Delete deletes an object from the object store after -// clearing its indexes, the object is required to provide -// a kind to clone in order to remove indexes related -func (s Store) Delete(pk types.PrimaryKey) { - // remove indexes - s.indexes.delete(pk) - // remove key - s.objects.delete(pk) -} - -func (s Store) getPrimaryKeys(filters []types.SecondaryKey) []types.PrimaryKey { - sets := make([]set, 0, len(filters)) - for _, fltr := range filters { - set := make(keySet) - s.indexes.iterate(fltr, func(key types.PrimaryKey) bool { - set.Insert(key) - return true - }) - sets = append(sets, set) - } - return primaryKeysFromSets(sets) -} diff --git a/pkg/crud/internal/store/store_test.go b/pkg/crud/internal/store/store_test.go deleted file mode 100644 index 508f04b5..00000000 --- a/pkg/crud/internal/store/store_test.go +++ /dev/null @@ -1,150 +0,0 @@ -package store - -import ( - "crypto/rand" - "fmt" - "github.com/cosmos/cosmos-sdk/store/prefix" - "github.com/iov-one/iovns/pkg/crud/types" - "reflect" - "testing" -) - -func TestNewStore(t *testing.T) { - _ = NewStore(testCtx, testKey, testCdc, []byte{0x0}) -} - -type testStoreObject struct { - Key string - Index1 string - Index2 string -} - -func (t testStoreObject) PrimaryKey() types.PrimaryKey { - return types.NewPrimaryKey([]byte(t.Key)) -} - -func (t testStoreObject) SecondaryKeys() []types.SecondaryKey { - var sk []types.SecondaryKey - if t.Index1 != "" { - sk = append(sk, types.NewSecondaryKey(0x1, []byte(t.Index1))) - } - if t.Index2 != "" { - sk = append(sk, types.NewSecondaryKey(0x2, []byte(t.Index2))) - - } - return sk -} - -func newTestStoreObject() *testStoreObject { - key := make([]byte, 8) - index1 := make([]byte, 8) - index2 := make([]byte, 8) - _, _ = rand.Read(key) - _, _ = rand.Read(index1) - _, _ = rand.Read(index2) - return &testStoreObject{ - Key: fmt.Sprintf("%x", key), - Index1: fmt.Sprintf("%x", index1), - Index2: fmt.Sprintf("%x", index2), - } -} - -func TestStore(t *testing.T) { - store := NewStore(testCtx, testKey, testCdc, []byte{0x0}) - obj := newTestStoreObject() - obj.Index2 = string([]byte{types.ReservedSeparator, 0x2, 0x3}) // put reserved separator in - store.Create(obj) - cpy := new(testStoreObject) - if !store.Read(obj.PrimaryKey(), cpy) { - t.Fatal("object not found") - } - if !reflect.DeepEqual(cpy, obj) { - t.Fatal("objects do not match") - } - // update object - oldIndex := obj.Index2 - obj.Index2 = "updated" - store.Update(obj) - if !store.Read(obj.PrimaryKey(), cpy) { - t.Fatal("object deleted after update") - } - // check if indexes were updated - filter := store.Filter(&testStoreObject{Index2: "updated"}) - if !filter.Valid() { - t.Fatal("index was not updated") - } - filter.Read(cpy) - if !reflect.DeepEqual(cpy, obj) { - t.Fatal("objects do not match") - } - // try read from deleted index - filter = store.Filter(&testStoreObject{Index2: oldIndex}) - if filter.Valid() { - t.Fatal("old index was not removed") - } - // delete object - store.Delete(obj.PrimaryKey()) - // check if anything was left in the store - it := store.raw.Iterator(nil, nil) - defer it.Close() - if it.Valid() { - t.Fatal("nothing should be in the store") - } -} - -func TestFilterAndIterateKeys(t *testing.T) { - x := 5 - objs := make([]*testStoreObject, x) - for i := 0; i < x; i++ { - objs[i] = newTestStoreObject() - objs[i].Index1 = objs[0].Index1 // set same index - } - // create objects - store := NewStore(testCtx, testKey, testCdc, []byte{0x0}) - // create objects - for _, obj := range objs { - store.Create(obj) - } - // check if object number is correct - primaryKeys := make([]types.PrimaryKey, 0, x) - store.IterateKeys(func(pk types.PrimaryKey) bool { - primaryKeys = append(primaryKeys, pk) - return true - }) - if len(primaryKeys) != x { - t.Fatal("unexpected number of keys", len(primaryKeys), x) - } - // delete based on primaryKeysFromSets - filter := store.Filter(&testStoreObject{Index1: objs[0].Index1}) // delete based on same index - for ; filter.Valid(); filter.Next() { - filter.Delete() - } - // try to read primary keys and check if they're deleted - for i, obj := range objs { - if store.objects.(_objects).store.Has(obj.PrimaryKey().Key()) { - t.Fatalf("key not deleted %d %#v", i, obj) - } - } - // now try to iterate indexes and check if index keys were removed - for _, obj := range objs { - for _, sk := range obj.SecondaryKeys() { - if prefix.NewStore(store.indexes.(_indexes).pointers, []byte{sk.Prefix()}).Has(sk.Key()) { - t.Fatal("index not removed") - } - } - } - // try to filter again - filter = store.Filter(&testStoreObject{Index1: objs[0].Index1}) - if filter.Valid() { - t.Fatal("no valid keys should exist") - } - // try to filter by secondary index - filter = store.Filter(&testStoreObject{Index2: objs[0].Index2}) - if filter.Valid() { - t.Fatalf("no valid keys should exist") - } - iterator := store.raw.Iterator(nil, nil) - for ; iterator.Valid(); iterator.Next() { - t.Logf("%s", iterator.Key()) - } -} diff --git a/pkg/crud/types/primary_key.go b/pkg/crud/types/primary_key.go deleted file mode 100644 index 7342f9b1..00000000 --- a/pkg/crud/types/primary_key.go +++ /dev/null @@ -1,33 +0,0 @@ -package types - -// PrimaryKey defines a primary key, which is a secondary key, under the hood, but with a fixed 0x0 prefix -type PrimaryKey interface { - // KeyCopy makes a copy of the primary key - KeyCopy() []byte - // Key returns the primary key - Key() []byte -} - -type primaryKey []byte - -func (p primaryKey) KeyCopy() []byte { - cpy := make([]byte, len(p)) - copy(cpy, p) - return cpy -} - -func (p primaryKey) Key() []byte { - return p -} - - -func NewPrimaryKey(value []byte) PrimaryKey { - v := make(primaryKey, len(value)) - copy(v, value) - return v -} - -func NewPrimaryKeyFromString(s string) PrimaryKey { - return primaryKey(s) -} - diff --git a/pkg/crud/types/primary_key_test.go b/pkg/crud/types/primary_key_test.go deleted file mode 100644 index 8c7bf43e..00000000 --- a/pkg/crud/types/primary_key_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package types - -import ( - "bytes" - "testing" -) - -func TestNewPrimaryKey(t *testing.T) { - e := []byte("test") - x := NewPrimaryKey(e) - if !bytes.Equal(x.Key(), e) { - t.Fatal("mismatch") - } -} diff --git a/pkg/crud/types/secondary_key.go b/pkg/crud/types/secondary_key.go deleted file mode 100644 index 54599aca..00000000 --- a/pkg/crud/types/secondary_key.go +++ /dev/null @@ -1,81 +0,0 @@ -package types - -import ( - "bytes" - "encoding/base64" -) - -const ReservedSeparator = 0xFF - -// SecondaryKey defines the secondary key behaviour -type SecondaryKey interface { - Marshal() []byte - Unmarshal(b []byte) - Key() []byte - Prefix() byte -} - - -func NewSecondaryKey(storePrefix byte, key []byte) SecondaryKey { - return &secondaryKey{ - key: fixKey(key), - storePrefix: storePrefix, - } -} - -func NewSecondaryKeyFromBytes(b []byte) SecondaryKey { - sk := &secondaryKey{} - sk.Unmarshal(b) - return sk -} - -// SecondaryKey defines a secondary key for the object -type secondaryKey struct { - // key is the byte key which identifies the byte key prefix used to iterate of the index of the secondary key - key []byte - // storePrefix is the prefix of the index, necessary to divide one index from another - storePrefix byte -} - -func (s secondaryKey) Marshal() []byte { - result := make([]byte, 0, 1 + len(s.key)) - result = append(result, s.storePrefix) - result = append(result, s.key...) - return result -} - -func (s *secondaryKey) Unmarshal(b []byte) { - // at least three bytes define an index - // store prefix + index key + separator - if len(b) < 3 { - panic("cannot unmarshal invalid length byte slice") - } - s.storePrefix = b[0] - s.key = b[1:] -} - -func (s *secondaryKey) Prefix() byte { - return s.storePrefix -} - -func (s *secondaryKey) Key() []byte { - cpy := make([]byte, len(s.key)) - copy(cpy, s.key) - return cpy -} - -// fixKey encodes key value which contains the reserved separator by base64-encoding them -// this is necessary because we're dealing with a prefixed KVStore which, if we iterate, is going to -// iterate over bytes contained in a key, so if we assume we have: -// KeyA = [0x1] -// KeyB = [0x1, 0x2] -// during iteration, in case we wanted to iterate over KeyA only we'd end up in KeyB domain too because -// KeyB starts with KeyA, so to avoid this we put a full stop separator which we know other keys can not contain -func fixKey(b []byte) []byte { - if bytes.Contains(b, []byte{ReservedSeparator}) { - dst := make([]byte, base64.RawStdEncoding.EncodedLen(len(b))) - base64.RawStdEncoding.Encode(dst, b) - return append(dst, ReservedSeparator) - } - return append(b, ReservedSeparator) -} diff --git a/pkg/crud/types/types.go b/pkg/crud/types/types.go deleted file mode 100644 index 27a5a8b8..00000000 --- a/pkg/crud/types/types.go +++ /dev/null @@ -1,35 +0,0 @@ -package types - -// Object defines an object in which we can do crud operations -type Object interface { - // PrimaryKey returns the unique key of the object - PrimaryKey() PrimaryKey - // SecondaryKeys returns the secondary keys used to index the object - SecondaryKeys() []SecondaryKey -} - -// Store defines the crud store behaviour -// interface -type Store interface { - // Create creates a new object and its indexes - Create(o Object) - // Read reads to the given target using the provided primary key - // returns false if no object exists - Read(key PrimaryKey, target Object) bool - // Update updates the object and its indexes, it will panic if the object does not exist - Update(o Object) - // Delete deletes the object given its primary key and remove its indexes, - // it will panic if the provided primary key does not exist in the kv store - Delete(key PrimaryKey) - // Filter returns a filter given an object whose fields are filters - Filter(filter Object) Filter - IterateKeys(func (pk PrimaryKey) bool) -} - -// Filter defines the behaviour of the store filter -type Filter interface { - Read(target Object) - Valid() bool - Next() - Delete() -} \ No newline at end of file diff --git a/x/starname/controllers/account/account.go b/x/starname/controllers/account/account.go index 1b8abd33..94c4b6fd 100644 --- a/x/starname/controllers/account/account.go +++ b/x/starname/controllers/account/account.go @@ -2,7 +2,7 @@ package account import ( "bytes" - crud "github.com/iov-one/iovns/pkg/crud/types" + crud "github.com/iov-one/cosmos-sdk-crud/pkg/crud" "github.com/iov-one/iovns/pkg/utils" "regexp" "time" diff --git a/x/starname/controllers/domain/domain.go b/x/starname/controllers/domain/domain.go index 3620dc39..52d48a55 100644 --- a/x/starname/controllers/domain/domain.go +++ b/x/starname/controllers/domain/domain.go @@ -1,7 +1,7 @@ package domain import ( - crud "github.com/iov-one/iovns/pkg/crud/types" + crud "github.com/iov-one/cosmos-sdk-crud/pkg/crud" "github.com/iov-one/iovns/pkg/utils" "regexp" "time" diff --git a/x/starname/genesis.go b/x/starname/genesis.go index df51fab0..277576ad 100644 --- a/x/starname/genesis.go +++ b/x/starname/genesis.go @@ -3,7 +3,7 @@ package starname import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" - crud "github.com/iov-one/iovns/pkg/crud/types" + crud "github.com/iov-one/cosmos-sdk-crud/pkg/crud/types" "github.com/iov-one/iovns/x/starname/types" ) diff --git a/x/starname/keeper/executor/account.go b/x/starname/keeper/executor/account.go index 669abdf1..1cdecaf9 100644 --- a/x/starname/keeper/executor/account.go +++ b/x/starname/keeper/executor/account.go @@ -2,7 +2,7 @@ package executor import ( sdk "github.com/cosmos/cosmos-sdk/types" - crud "github.com/iov-one/iovns/pkg/crud/types" + crud "github.com/iov-one/cosmos-sdk-crud/pkg/crud" "github.com/iov-one/iovns/pkg/utils" "github.com/iov-one/iovns/x/starname/keeper" "github.com/iov-one/iovns/x/starname/types" diff --git a/x/starname/keeper/executor/domain.go b/x/starname/keeper/executor/domain.go index da7cc49f..df0de4a8 100644 --- a/x/starname/keeper/executor/domain.go +++ b/x/starname/keeper/executor/domain.go @@ -2,7 +2,7 @@ package executor import ( sdk "github.com/cosmos/cosmos-sdk/types" - crud "github.com/iov-one/iovns/pkg/crud/types" + crud "github.com/iov-one/cosmos-sdk-crud/pkg/crud" "github.com/iov-one/iovns/pkg/utils" "github.com/iov-one/iovns/x/starname/keeper" "github.com/iov-one/iovns/x/starname/types" diff --git a/x/starname/keeper/keeper.go b/x/starname/keeper/keeper.go index dc9a8846..7527055d 100644 --- a/x/starname/keeper/keeper.go +++ b/x/starname/keeper/keeper.go @@ -2,8 +2,7 @@ package keeper import ( "fmt" - "github.com/iov-one/iovns/pkg/crud" - crud_types "github.com/iov-one/iovns/pkg/crud/types" + "github.com/iov-one/cosmos-sdk-crud/pkg/crud" "time" "github.com/cosmos/cosmos-sdk/codec" @@ -67,12 +66,12 @@ func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, configKeeper Configurati return keeper } -func (k Keeper) AccountStore(ctx sdk.Context) crud_types.Store { +func (k Keeper) AccountStore(ctx sdk.Context) crud.Store { store := crud.NewStore(ctx, k.StoreKey, k.Cdc, []byte{0x1}) return store } -func (k Keeper) DomainStore(ctx sdk.Context) crud_types.Store { +func (k Keeper) DomainStore(ctx sdk.Context) crud.Store { return crud.NewStore(ctx, k.StoreKey, k.Cdc, []byte{0x2}) } diff --git a/x/starname/keeper/queries.go b/x/starname/keeper/queries.go index 109d9de3..39228534 100644 --- a/x/starname/keeper/queries.go +++ b/x/starname/keeper/queries.go @@ -2,7 +2,7 @@ package keeper import ( "fmt" - crud "github.com/iov-one/iovns/pkg/crud/types" + crud "github.com/iov-one/cosmos-sdk-crud/pkg/crud" "github.com/iov-one/iovns/pkg/queries" "github.com/iov-one/iovns/pkg/utils" "strings" diff --git a/x/starname/types/types.go b/x/starname/types/types.go index c1a4b92b..e0f0ca68 100644 --- a/x/starname/types/types.go +++ b/x/starname/types/types.go @@ -4,7 +4,7 @@ import ( "strings" "github.com/cosmos/cosmos-sdk/codec" - crud "github.com/iov-one/iovns/pkg/crud/types" + crud "github.com/iov-one/cosmos-sdk-crud/pkg/crud/types" "github.com/iov-one/iovns/pkg/utils" "github.com/cosmos/cosmos-sdk/types/errors"