2018-09-18 04:16:20 -07:00
|
|
|
package store
|
2018-08-31 02:03:43 -07:00
|
|
|
|
|
|
|
import (
|
|
|
|
"reflect"
|
|
|
|
|
|
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
|
|
)
|
|
|
|
|
2018-10-06 06:50:58 -07:00
|
|
|
// Additional capicity to be allocated for Store.space
|
|
|
|
// So we don't have to allocate extra space each time appending to the key
|
|
|
|
const extraKeyCap = 20
|
|
|
|
|
2018-08-31 02:03:43 -07:00
|
|
|
// Individual parameter store for each keeper
|
2018-09-27 11:12:52 -07:00
|
|
|
// Transient store persists for a block, so we use it for
|
|
|
|
// recording whether the parameter has been changed or not
|
2018-09-18 04:16:20 -07:00
|
|
|
type Store struct {
|
2018-08-31 02:03:43 -07:00
|
|
|
cdc *codec.Codec
|
2018-09-27 11:12:52 -07:00
|
|
|
key sdk.StoreKey // []byte -> []byte, stores parameter
|
|
|
|
tkey sdk.StoreKey // []byte -> bool, stores parameter change
|
2018-08-31 02:03:43 -07:00
|
|
|
|
2018-09-18 10:16:51 -07:00
|
|
|
space []byte
|
2018-10-06 08:32:41 -07:00
|
|
|
|
|
|
|
table Table
|
2018-08-31 02:03:43 -07:00
|
|
|
}
|
|
|
|
|
2018-09-18 04:16:20 -07:00
|
|
|
// NewStore constructs a store with namestore
|
2018-10-06 08:32:41 -07:00
|
|
|
func NewStore(cdc *codec.Codec, key sdk.StoreKey, tkey sdk.StoreKey, space string, table Table) (res Store) {
|
2018-10-06 06:50:58 -07:00
|
|
|
res = Store{
|
2018-08-31 02:03:43 -07:00
|
|
|
cdc: cdc,
|
|
|
|
key: key,
|
|
|
|
tkey: tkey,
|
2018-10-06 08:32:41 -07:00
|
|
|
|
|
|
|
table: table,
|
2018-08-31 02:03:43 -07:00
|
|
|
}
|
2018-10-06 06:50:58 -07:00
|
|
|
|
|
|
|
spacebz := []byte(space)
|
|
|
|
res.space = make([]byte, len(spacebz), len(spacebz)+extraKeyCap)
|
|
|
|
copy(res.space, spacebz)
|
|
|
|
return
|
2018-08-31 02:03:43 -07:00
|
|
|
}
|
|
|
|
|
2018-10-06 06:50:58 -07:00
|
|
|
// Returns a KVStore identical with ctx.KVStore(s.key).Prefix()
|
2018-09-18 10:16:51 -07:00
|
|
|
func (s Store) kvStore(ctx sdk.Context) sdk.KVStore {
|
2018-10-06 06:50:58 -07:00
|
|
|
// append here is safe, appends within a function won't cause
|
|
|
|
// weird side effects when its singlethreaded
|
2018-09-18 10:22:42 -07:00
|
|
|
return ctx.KVStore(s.key).Prefix(append(s.space, '/'))
|
2018-09-18 10:16:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a KVStore identical with ctx.TransientStore(s.tkey).Prefix()
|
|
|
|
func (s Store) transientStore(ctx sdk.Context) sdk.KVStore {
|
2018-10-06 06:50:58 -07:00
|
|
|
// append here is safe, appends within a function won't cause
|
|
|
|
// weird side effects when its singlethreaded
|
2018-09-18 10:22:42 -07:00
|
|
|
return ctx.TransientStore(s.tkey).Prefix(append(s.space, '/'))
|
2018-09-18 10:16:51 -07:00
|
|
|
}
|
|
|
|
|
2018-08-31 02:03:43 -07:00
|
|
|
// Get parameter from store
|
2018-09-27 11:52:29 -07:00
|
|
|
func (s Store) Get(ctx sdk.Context, key []byte, ptr interface{}) {
|
2018-09-18 10:16:51 -07:00
|
|
|
store := s.kvStore(ctx)
|
2018-09-27 11:52:29 -07:00
|
|
|
bz := store.Get(key)
|
2018-08-31 02:03:43 -07:00
|
|
|
err := s.cdc.UnmarshalJSON(bz, ptr)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetIfExists do not modify ptr if the stored parameter is nil
|
2018-09-27 11:52:29 -07:00
|
|
|
func (s Store) GetIfExists(ctx sdk.Context, key []byte, ptr interface{}) {
|
2018-09-18 10:16:51 -07:00
|
|
|
store := s.kvStore(ctx)
|
2018-09-27 11:52:29 -07:00
|
|
|
bz := store.Get(key)
|
2018-08-31 02:03:43 -07:00
|
|
|
if bz == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err := s.cdc.UnmarshalJSON(bz, ptr)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get raw bytes of parameter from store
|
2018-09-27 11:52:29 -07:00
|
|
|
func (s Store) GetRaw(ctx sdk.Context, key []byte) []byte {
|
2018-09-18 10:16:51 -07:00
|
|
|
store := s.kvStore(ctx)
|
2018-10-06 08:32:41 -07:00
|
|
|
return store.Get(key)
|
2018-08-31 02:03:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the parameter is set in the store
|
2018-09-27 11:52:29 -07:00
|
|
|
func (s Store) Has(ctx sdk.Context, key []byte) bool {
|
2018-09-18 10:16:51 -07:00
|
|
|
store := s.kvStore(ctx)
|
2018-09-27 11:52:29 -07:00
|
|
|
return store.Has(key)
|
2018-08-31 02:03:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns true if the parameter is set in the block
|
2018-09-27 11:52:29 -07:00
|
|
|
func (s Store) Modified(ctx sdk.Context, key []byte) bool {
|
2018-09-18 10:16:51 -07:00
|
|
|
tstore := s.transientStore(ctx)
|
2018-09-27 11:52:29 -07:00
|
|
|
return tstore.Has(key)
|
2018-08-31 02:03:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set parameter, return error if stored parameter has different type from input
|
2018-09-26 11:30:57 -07:00
|
|
|
// Also set to the transient store to record change
|
2018-09-27 11:52:29 -07:00
|
|
|
func (s Store) Set(ctx sdk.Context, key []byte, param interface{}) {
|
2018-09-18 10:16:51 -07:00
|
|
|
store := s.kvStore(ctx)
|
2018-08-31 02:03:43 -07:00
|
|
|
|
2018-10-06 09:08:49 -07:00
|
|
|
ty, ok := s.table[string(key)]
|
2018-10-06 08:32:41 -07:00
|
|
|
if !ok {
|
|
|
|
panic("Parameter not registered")
|
|
|
|
}
|
2018-09-26 11:30:57 -07:00
|
|
|
|
2018-10-06 08:32:41 -07:00
|
|
|
pty := reflect.TypeOf(param)
|
|
|
|
if pty.Kind() == reflect.Ptr {
|
|
|
|
pty = pty.Elem()
|
|
|
|
}
|
2018-08-31 02:03:43 -07:00
|
|
|
|
2018-10-06 08:32:41 -07:00
|
|
|
if pty != ty {
|
|
|
|
panic("Type mismatch with registered table")
|
2018-08-31 02:03:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bz, err := s.cdc.MarshalJSON(param)
|
|
|
|
if err != nil {
|
2018-09-10 05:10:06 -07:00
|
|
|
panic(err)
|
2018-08-31 02:03:43 -07:00
|
|
|
}
|
2018-09-27 11:52:29 -07:00
|
|
|
store.Set(key, bz)
|
2018-08-31 02:03:43 -07:00
|
|
|
|
2018-09-18 10:16:51 -07:00
|
|
|
tstore := s.transientStore(ctx)
|
2018-09-27 11:52:29 -07:00
|
|
|
tstore.Set(key, []byte{})
|
2018-08-31 02:03:43 -07:00
|
|
|
}
|
|
|
|
|
2018-09-18 09:27:54 -07:00
|
|
|
// Get to ParamStruct
|
|
|
|
func (s Store) GetStruct(ctx sdk.Context, ps ParamStruct) {
|
2018-10-06 06:50:58 -07:00
|
|
|
for _, pair := range ps.KeyValuePairs() {
|
2018-10-06 08:32:41 -07:00
|
|
|
s.Get(ctx, pair.Key, pair.Value)
|
2018-09-18 09:27:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-17 08:28:13 -07:00
|
|
|
// Set from ParamStruct
|
2018-09-18 09:27:54 -07:00
|
|
|
func (s Store) SetStruct(ctx sdk.Context, ps ParamStruct) {
|
2018-10-06 06:50:58 -07:00
|
|
|
for _, pair := range ps.KeyValuePairs() {
|
2018-09-18 10:16:51 -07:00
|
|
|
// pair.Field is a pointer to the field, so indirecting the ptr.
|
|
|
|
// go-amino automatically handles it but just for sure,
|
|
|
|
// since SetStruct is meant to be used in InitGenesis
|
|
|
|
// so this method will not be called frequently
|
2018-10-06 08:32:41 -07:00
|
|
|
v := reflect.Indirect(reflect.ValueOf(pair.Value)).Interface()
|
2018-09-18 10:16:51 -07:00
|
|
|
s.Set(ctx, pair.Key, v)
|
2018-09-17 08:28:13 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-18 10:16:51 -07:00
|
|
|
// Returns internal namespace
|
|
|
|
func (s Store) Space() string {
|
|
|
|
return string(s.space)
|
2018-08-31 02:03:43 -07:00
|
|
|
}
|
|
|
|
|
2018-09-18 04:16:20 -07:00
|
|
|
// Wrapper of Store, provides immutable functions only
|
|
|
|
type ReadOnlyStore struct {
|
|
|
|
s Store
|
2018-08-31 02:03:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Exposes Get
|
2018-09-27 11:52:29 -07:00
|
|
|
func (ros ReadOnlyStore) Get(ctx sdk.Context, key []byte, ptr interface{}) {
|
2018-08-31 02:03:43 -07:00
|
|
|
ros.s.Get(ctx, key, ptr)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Exposes GetRaw
|
2018-09-27 11:52:29 -07:00
|
|
|
func (ros ReadOnlyStore) GetRaw(ctx sdk.Context, key []byte) []byte {
|
2018-08-31 02:03:43 -07:00
|
|
|
return ros.s.GetRaw(ctx, key)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Exposes Has
|
2018-09-27 11:52:29 -07:00
|
|
|
func (ros ReadOnlyStore) Has(ctx sdk.Context, key []byte) bool {
|
2018-08-31 02:03:43 -07:00
|
|
|
return ros.s.Has(ctx, key)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Exposes Modified
|
2018-09-27 11:52:29 -07:00
|
|
|
func (ros ReadOnlyStore) Modified(ctx sdk.Context, key []byte) bool {
|
2018-08-31 02:03:43 -07:00
|
|
|
return ros.s.Modified(ctx, key)
|
|
|
|
}
|
2018-09-18 10:16:51 -07:00
|
|
|
|
|
|
|
// Exposes Space
|
|
|
|
func (ros ReadOnlyStore) Space() string {
|
|
|
|
return ros.s.Space()
|
|
|
|
}
|