github.com/cosmos/cosmos-sdk@v0.50.10/x/params/types/subspace.go (about)

     1  package types
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  
     7  	"cosmossdk.io/store/prefix"
     8  	storetypes "cosmossdk.io/store/types"
     9  
    10  	"github.com/cosmos/cosmos-sdk/codec"
    11  	sdk "github.com/cosmos/cosmos-sdk/types"
    12  )
    13  
    14  const (
    15  	// StoreKey is the string store key for the param store
    16  	StoreKey = "params"
    17  
    18  	// TStoreKey is the string store key for the param transient store
    19  	TStoreKey = "transient_params"
    20  )
    21  
    22  // Individual parameter store for each keeper
    23  // Transient store persists for a block, so we use it for
    24  // recording whether the parameter has been changed or not
    25  type Subspace struct {
    26  	cdc         codec.BinaryCodec
    27  	legacyAmino *codec.LegacyAmino
    28  	key         storetypes.StoreKey // []byte -> []byte, stores parameter
    29  	tkey        storetypes.StoreKey // []byte -> bool, stores parameter change
    30  	name        []byte
    31  	table       KeyTable
    32  }
    33  
    34  // NewSubspace constructs a store with namestore
    35  func NewSubspace(cdc codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey storetypes.StoreKey, name string) Subspace {
    36  	return Subspace{
    37  		cdc:         cdc,
    38  		legacyAmino: legacyAmino,
    39  		key:         key,
    40  		tkey:        tkey,
    41  		name:        []byte(name),
    42  		table:       NewKeyTable(),
    43  	}
    44  }
    45  
    46  // HasKeyTable returns if the Subspace has a KeyTable registered.
    47  func (s Subspace) HasKeyTable() bool {
    48  	return len(s.table.m) > 0
    49  }
    50  
    51  // WithKeyTable initializes KeyTable and returns modified Subspace
    52  func (s Subspace) WithKeyTable(table KeyTable) Subspace {
    53  	if table.m == nil {
    54  		panic("WithKeyTable() called with nil KeyTable")
    55  	}
    56  	if len(s.table.m) != 0 {
    57  		panic("WithKeyTable() called on already initialized Subspace")
    58  	}
    59  
    60  	for k, v := range table.m {
    61  		s.table.m[k] = v
    62  	}
    63  
    64  	// Allocate additional capacity for Subspace.name
    65  	// So we don't have to allocate extra space each time appending to the key
    66  	name := s.name
    67  	s.name = make([]byte, len(name), len(name)+table.maxKeyLength())
    68  	copy(s.name, name)
    69  
    70  	return s
    71  }
    72  
    73  // Returns a KVStore identical with ctx.KVStore(s.key).Prefix()
    74  func (s Subspace) kvStore(ctx sdk.Context) storetypes.KVStore {
    75  	// append here is safe, appends within a function won't cause
    76  	// weird side effects when its singlethreaded
    77  	return prefix.NewStore(ctx.KVStore(s.key), append(s.name, '/'))
    78  }
    79  
    80  // Returns a transient store for modification
    81  func (s Subspace) transientStore(ctx sdk.Context) storetypes.KVStore {
    82  	// append here is safe, appends within a function won't cause
    83  	// weird side effects when its singlethreaded
    84  	return prefix.NewStore(ctx.TransientStore(s.tkey), append(s.name, '/'))
    85  }
    86  
    87  // Validate attempts to validate a parameter value by its key. If the key is not
    88  // registered or if the validation of the value fails, an error is returned.
    89  func (s Subspace) Validate(ctx sdk.Context, key []byte, value interface{}) error {
    90  	attr, ok := s.table.m[string(key)]
    91  	if !ok {
    92  		return fmt.Errorf("parameter %s not registered", key)
    93  	}
    94  
    95  	if err := attr.vfn(value); err != nil {
    96  		return fmt.Errorf("invalid parameter value: %s", err)
    97  	}
    98  
    99  	return nil
   100  }
   101  
   102  // Get queries for a parameter by key from the Subspace's KVStore and sets the
   103  // value to the provided pointer. If the value does not exist, it will panic.
   104  func (s Subspace) Get(ctx sdk.Context, key []byte, ptr interface{}) {
   105  	s.checkType(key, ptr)
   106  
   107  	store := s.kvStore(ctx)
   108  	bz := store.Get(key)
   109  
   110  	if err := s.legacyAmino.UnmarshalJSON(bz, ptr); err != nil {
   111  		panic(err)
   112  	}
   113  }
   114  
   115  // GetIfExists queries for a parameter by key from the Subspace's KVStore and
   116  // sets the value to the provided pointer. If the value does not exist, it will
   117  // perform a no-op.
   118  func (s Subspace) GetIfExists(ctx sdk.Context, key []byte, ptr interface{}) {
   119  	store := s.kvStore(ctx)
   120  	bz := store.Get(key)
   121  	if bz == nil {
   122  		return
   123  	}
   124  
   125  	s.checkType(key, ptr)
   126  
   127  	if err := s.legacyAmino.UnmarshalJSON(bz, ptr); err != nil {
   128  		panic(err)
   129  	}
   130  }
   131  
   132  // IterateKeys iterates over all the keys in the subspace and executes the
   133  // provided callback. If the callback returns true for a given key, iteration
   134  // will halt.
   135  func (s Subspace) IterateKeys(ctx sdk.Context, cb func(key []byte) bool) {
   136  	store := s.kvStore(ctx)
   137  
   138  	iter := storetypes.KVStorePrefixIterator(store, nil)
   139  	defer iter.Close()
   140  
   141  	for ; iter.Valid(); iter.Next() {
   142  		if cb(iter.Key()) {
   143  			break
   144  		}
   145  	}
   146  }
   147  
   148  // GetRaw queries for the raw values bytes for a parameter by key.
   149  func (s Subspace) GetRaw(ctx sdk.Context, key []byte) []byte {
   150  	store := s.kvStore(ctx)
   151  	return store.Get(key)
   152  }
   153  
   154  // Has returns if a parameter key exists or not in the Subspace's KVStore.
   155  func (s Subspace) Has(ctx sdk.Context, key []byte) bool {
   156  	store := s.kvStore(ctx)
   157  	return store.Has(key)
   158  }
   159  
   160  // Modified returns true if the parameter key is set in the Subspace's transient
   161  // KVStore.
   162  func (s Subspace) Modified(ctx sdk.Context, key []byte) bool {
   163  	tstore := s.transientStore(ctx)
   164  	return tstore.Has(key)
   165  }
   166  
   167  // checkType verifies that the provided key and value are comptable and registered.
   168  func (s Subspace) checkType(key []byte, value interface{}) {
   169  	attr, ok := s.table.m[string(key)]
   170  	if !ok {
   171  		panic(fmt.Sprintf("parameter %s not registered", key))
   172  	}
   173  
   174  	ty := attr.ty
   175  	pty := reflect.TypeOf(value)
   176  	if pty.Kind() == reflect.Ptr {
   177  		pty = pty.Elem()
   178  	}
   179  
   180  	if pty != ty {
   181  		panic("type mismatch with registered table")
   182  	}
   183  }
   184  
   185  // Set stores a value for given a parameter key assuming the parameter type has
   186  // been registered. It will panic if the parameter type has not been registered
   187  // or if the value cannot be encoded. A change record is also set in the Subspace's
   188  // transient KVStore to mark the parameter as modified.
   189  func (s Subspace) Set(ctx sdk.Context, key []byte, value interface{}) {
   190  	s.checkType(key, value)
   191  	store := s.kvStore(ctx)
   192  
   193  	bz, err := s.legacyAmino.MarshalJSON(value)
   194  	if err != nil {
   195  		panic(err)
   196  	}
   197  
   198  	store.Set(key, bz)
   199  
   200  	tstore := s.transientStore(ctx)
   201  	tstore.Set(key, []byte{})
   202  }
   203  
   204  // Update stores an updated raw value for a given parameter key assuming the
   205  // parameter type has been registered. It will panic if the parameter type has
   206  // not been registered or if the value cannot be encoded. An error is returned
   207  // if the raw value is not compatible with the registered type for the parameter
   208  // key or if the new value is invalid as determined by the registered type's
   209  // validation function.
   210  func (s Subspace) Update(ctx sdk.Context, key, value []byte) error {
   211  	attr, ok := s.table.m[string(key)]
   212  	if !ok {
   213  		panic(fmt.Sprintf("parameter %s not registered", key))
   214  	}
   215  
   216  	ty := attr.ty
   217  	dest := reflect.New(ty).Interface()
   218  	s.GetIfExists(ctx, key, dest)
   219  
   220  	if err := s.legacyAmino.UnmarshalJSON(value, dest); err != nil {
   221  		return err
   222  	}
   223  
   224  	// destValue contains the dereferenced value of dest so validation function do
   225  	// not have to operate on pointers.
   226  	destValue := reflect.Indirect(reflect.ValueOf(dest)).Interface()
   227  	if err := s.Validate(ctx, key, destValue); err != nil {
   228  		return err
   229  	}
   230  
   231  	s.Set(ctx, key, dest)
   232  	return nil
   233  }
   234  
   235  // GetParamSet iterates through each ParamSetPair where for each pair, it will
   236  // retrieve the value and set it to the corresponding value pointer provided
   237  // in the ParamSetPair by calling Subspace#Get.
   238  func (s Subspace) GetParamSet(ctx sdk.Context, ps ParamSet) {
   239  	for _, pair := range ps.ParamSetPairs() {
   240  		s.Get(ctx, pair.Key, pair.Value)
   241  	}
   242  }
   243  
   244  // GetParamSetIfExists iterates through each ParamSetPair where for each pair, it will
   245  // retrieve the value and set it to the corresponding value pointer provided
   246  // in the ParamSetPair by calling Subspace#GetIfExists.
   247  func (s Subspace) GetParamSetIfExists(ctx sdk.Context, ps ParamSet) {
   248  	for _, pair := range ps.ParamSetPairs() {
   249  		s.GetIfExists(ctx, pair.Key, pair.Value)
   250  	}
   251  }
   252  
   253  // SetParamSet iterates through each ParamSetPair and sets the value with the
   254  // corresponding parameter key in the Subspace's KVStore.
   255  func (s Subspace) SetParamSet(ctx sdk.Context, ps ParamSet) {
   256  	for _, pair := range ps.ParamSetPairs() {
   257  		// pair.Field is a pointer to the field, so indirecting the ptr.
   258  		// go-amino automatically handles it but just for sure,
   259  		// since SetStruct is meant to be used in InitGenesis
   260  		// so this method will not be called frequently
   261  		v := reflect.Indirect(reflect.ValueOf(pair.Value)).Interface()
   262  
   263  		if err := pair.ValidatorFn(v); err != nil {
   264  			panic(fmt.Sprintf("value from ParamSetPair is invalid: %s", err))
   265  		}
   266  
   267  		s.Set(ctx, pair.Key, v)
   268  	}
   269  }
   270  
   271  // Name returns the name of the Subspace.
   272  func (s Subspace) Name() string {
   273  	return string(s.name)
   274  }
   275  
   276  // Wrapper of Subspace, provides immutable functions only
   277  type ReadOnlySubspace struct {
   278  	s Subspace
   279  }
   280  
   281  // Get delegates a read-only Get call to the Subspace.
   282  func (ros ReadOnlySubspace) Get(ctx sdk.Context, key []byte, ptr interface{}) {
   283  	ros.s.Get(ctx, key, ptr)
   284  }
   285  
   286  // GetRaw delegates a read-only GetRaw call to the Subspace.
   287  func (ros ReadOnlySubspace) GetRaw(ctx sdk.Context, key []byte) []byte {
   288  	return ros.s.GetRaw(ctx, key)
   289  }
   290  
   291  // Has delegates a read-only Has call to the Subspace.
   292  func (ros ReadOnlySubspace) Has(ctx sdk.Context, key []byte) bool {
   293  	return ros.s.Has(ctx, key)
   294  }
   295  
   296  // Modified delegates a read-only Modified call to the Subspace.
   297  func (ros ReadOnlySubspace) Modified(ctx sdk.Context, key []byte) bool {
   298  	return ros.s.Modified(ctx, key)
   299  }
   300  
   301  // Name delegates a read-only Name call to the Subspace.
   302  func (ros ReadOnlySubspace) Name() string {
   303  	return ros.s.Name()
   304  }