github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/x/params/subspace/subspace.go (about)

     1  package subspace
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"reflect"
     7  	"strconv"
     8  
     9  	"github.com/valyala/fastjson"
    10  
    11  	"encoding/json"
    12  
    13  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec"
    14  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    15  	"github.com/tendermint/go-amino"
    16  
    17  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/prefix"
    18  )
    19  
    20  const (
    21  	// StoreKey is the string store key for the param store
    22  	StoreKey = "params"
    23  
    24  	// TStoreKey is the string store key for the param transient store
    25  	TStoreKey = "transient_params"
    26  )
    27  
    28  // Individual parameter store for each keeper
    29  // Transient store persists for a block, so we use it for
    30  // recording whether the parameter has been changed or not
    31  type Subspace struct {
    32  	cdc   *codec.Codec
    33  	key   sdk.StoreKey // []byte -> []byte, stores parameter
    34  	tkey  sdk.StoreKey // []byte -> bool, stores parameter change
    35  	name  []byte
    36  	cName []byte
    37  	table KeyTable
    38  }
    39  
    40  // NewSubspace constructs a store with namestore
    41  func NewSubspace(cdc *codec.Codec, key sdk.StoreKey, tkey sdk.StoreKey, name string) Subspace {
    42  	return Subspace{
    43  		cdc:   cdc,
    44  		key:   key,
    45  		tkey:  tkey,
    46  		name:  []byte(name),
    47  		cName: []byte("custom/" + name + "/"),
    48  		table: NewKeyTable(),
    49  	}
    50  }
    51  
    52  // HasKeyTable returns if the Subspace has a KeyTable registered.
    53  func (s Subspace) HasKeyTable() bool {
    54  	return len(s.table.m) > 0
    55  }
    56  
    57  // WithKeyTable initializes KeyTable and returns modified Subspace
    58  func (s Subspace) WithKeyTable(table KeyTable) Subspace {
    59  	if table.m == nil {
    60  		panic("SetKeyTable() called with nil KeyTable")
    61  	}
    62  	if len(s.table.m) != 0 {
    63  		panic("SetKeyTable() called on already initialized Subspace")
    64  	}
    65  	if s.table.m == nil {
    66  		s.table.m = make(map[string]attribute)
    67  	}
    68  
    69  	for k, v := range table.m {
    70  		s.table.m[k] = v
    71  	}
    72  
    73  	// Allocate additional capacity for Subspace.name
    74  	// So we don't have to allocate extra space each time appending to the key
    75  	name := s.name
    76  	s.name = make([]byte, len(name), len(name)+table.maxKeyLength())
    77  	copy(s.name, name)
    78  
    79  	return s
    80  }
    81  
    82  func (s Subspace) LazyWithKeyTable(table KeyTable) Subspace {
    83  	if table.m == nil {
    84  		panic("SetKeyTable() called with nil KeyTable")
    85  	}
    86  	if len(s.table.m) == 0 {
    87  		panic("SetKeyTable() should call on already initialized Subspace")
    88  	}
    89  
    90  	for k, v := range table.m {
    91  		s.table.m[k] = v
    92  	}
    93  
    94  	// Allocate additional capacity for Subspace.name
    95  	// So we don't have to allocate extra space each time appending to the key
    96  	name := s.name
    97  	s.name = make([]byte, len(name), len(name)+table.maxKeyLength())
    98  	copy(s.name, name)
    99  
   100  	return s
   101  }
   102  
   103  // Returns a KVStore identical with ctx.KVStore(s.key).Prefix()
   104  func (s Subspace) kvStore(ctx sdk.Context) sdk.KVStore {
   105  	// append here is safe, appends within a function won't cause
   106  	// weird side effects when its singlethreaded
   107  	return prefix.NewStore(ctx.KVStore(s.key), append(s.name, '/'))
   108  }
   109  
   110  // Returns a KVStore identical with ctx.KVStore(s.key).Prefix()
   111  func (s Subspace) CustomKVStore(ctx sdk.Context) sdk.KVStore {
   112  	// append here is safe, appends within a function won't cause
   113  	// weird side effects when its singlethreaded
   114  	return prefix.NewStore(ctx.KVStore(s.key), s.cName)
   115  }
   116  
   117  // Returns a transient store for modification
   118  func (s Subspace) transientStore(ctx sdk.Context) sdk.KVStore {
   119  	// append here is safe, appends within a function won't cause
   120  	// weird side effects when its singlethreaded
   121  	return prefix.NewStore(ctx.TransientStore(s.tkey), append(s.name, '/'))
   122  }
   123  
   124  // Validate attempts to validate a parameter value by its key. If the key is not
   125  // registered or if the validation of the value fails, an error is returned.
   126  func (s Subspace) Validate(ctx sdk.Context, key []byte, value interface{}) error {
   127  	attr, ok := s.table.m[string(key)]
   128  	if !ok {
   129  		return fmt.Errorf("parameter %s not registered", string(key))
   130  	}
   131  
   132  	if err := attr.vfn(value); err != nil {
   133  		return fmt.Errorf("invalid parameter value: %s", err)
   134  	}
   135  
   136  	return nil
   137  }
   138  
   139  // Get queries for a parameter by key from the Subspace's KVStore and sets the
   140  // value to the provided pointer. If the value does not exist, it will panic.
   141  func (s Subspace) Get(ctx sdk.Context, key []byte, ptr interface{}) {
   142  	store := s.kvStore(ctx)
   143  	bz := store.Get(key)
   144  
   145  	if err := tryParseJSON(bz, ptr, s.cdc); err != nil {
   146  		panic(err)
   147  	}
   148  }
   149  
   150  func isJsonString(bz []byte) bool {
   151  	return len(bz) >= 2 && bz[0] == '"' && bz[len(bz)-1] == '"'
   152  }
   153  
   154  func getString(rawbz []byte, tptr *string) bool {
   155  	ok := true
   156  	for _, c := range rawbz {
   157  		if c < 0x20 || c == '\\' {
   158  			ok = false
   159  			break
   160  		}
   161  	}
   162  	if !ok {
   163  		return false
   164  	}
   165  	*tptr = string(rawbz)
   166  	return true
   167  }
   168  
   169  func getUint64(rawbz []byte, tptr *uint64) bool {
   170  	ok := true
   171  	for _, c := range rawbz {
   172  		if c < '0' || c > '9' {
   173  			ok = false
   174  			break
   175  		}
   176  	}
   177  	if !ok {
   178  		return false
   179  	}
   180  	ui, err := strconv.ParseUint(amino.BytesToStr(rawbz), 10, 64)
   181  	if err == nil {
   182  		*tptr = ui
   183  		return true
   184  	}
   185  	return false
   186  }
   187  
   188  func getInt64(rawbz []byte, tptr *int64) bool {
   189  	ok := true
   190  	rawbz2 := rawbz
   191  	if rawbz2[0] == '-' {
   192  		rawbz2 = rawbz[1:]
   193  	}
   194  	for _, c := range rawbz2 {
   195  		if c < '0' || c > '9' {
   196  			ok = false
   197  			break
   198  		}
   199  	}
   200  	if !ok {
   201  		return false
   202  	}
   203  	ui, err := strconv.ParseInt(amino.BytesToStr(rawbz), 10, 64)
   204  	if err == nil {
   205  		*tptr = ui
   206  		return true
   207  	}
   208  	return false
   209  }
   210  
   211  func getDec(rawbz []byte, tptr *sdk.Dec) bool {
   212  	ok := true
   213  	for _, c := range rawbz {
   214  		if c < 0x20 || c == '\\' {
   215  			ok = false
   216  			break
   217  		}
   218  	}
   219  	if !ok {
   220  		return false
   221  	}
   222  	nd, err := sdk.NewDecFromStr(amino.BytesToStr(rawbz))
   223  	if err == nil {
   224  		tptr.Int = nd.Int
   225  		return true
   226  	}
   227  	return false
   228  }
   229  
   230  func getDecCoin(bz []byte, tptr *sdk.DecCoin) bool {
   231  	if amino.BytesToStr(bz) == "null" {
   232  		return false
   233  	}
   234  	v, err := fastjson.Parse(amino.BytesToStr(bz))
   235  	if err == nil {
   236  		tptr.Denom = string(v.GetStringBytes("denom"))
   237  		amount := v.GetStringBytes("amount")
   238  		newDec, err := sdk.NewDecFromStr(amino.BytesToStr(amount))
   239  		if err == nil {
   240  			tptr.Amount.Int = newDec.Int
   241  			return true
   242  		}
   243  	}
   244  	return false
   245  }
   246  
   247  func tryParseJSON(bz []byte, ptr interface{}, cdc *amino.Codec) error {
   248  	if len(bz) < 2 {
   249  		return cdc.UnmarshalJSON(bz, ptr)
   250  	}
   251  	if isJsonString(bz) {
   252  		rawbz := bz[1 : len(bz)-1] // trim leading & trailing "
   253  		switch tptr := ptr.(type) {
   254  		case *string:
   255  			if getString(rawbz, tptr) {
   256  				return nil
   257  			}
   258  			return json.Unmarshal(bz, ptr)
   259  		case *uint64:
   260  			if getUint64(rawbz, tptr) {
   261  				return nil
   262  			}
   263  			return json.Unmarshal(rawbz, ptr)
   264  		case *int64:
   265  			if getInt64(rawbz, tptr) {
   266  				return nil
   267  			}
   268  			return json.Unmarshal(rawbz, ptr)
   269  		case *sdk.Dec:
   270  			if getDec(rawbz, tptr) {
   271  				return nil
   272  			}
   273  			return tptr.UnmarshalJSON(bz)
   274  		}
   275  	} else {
   276  		switch tptr := ptr.(type) {
   277  		case *sdk.DecCoin:
   278  			if getDecCoin(bz, tptr) {
   279  				return nil
   280  			}
   281  		case *bool:
   282  			switch amino.BytesToStr(bz) {
   283  			case "true":
   284  				*tptr = true
   285  				return nil
   286  			case "false":
   287  				*tptr = false
   288  				return nil
   289  			}
   290  		case *[]int:
   291  			if amino.BytesToStr(bz) == "null" {
   292  				*tptr = nil
   293  				return nil
   294  			}
   295  		}
   296  	}
   297  
   298  	return cdc.UnmarshalJSON(bz, ptr)
   299  }
   300  
   301  // GetIfExists queries for a parameter by key from the Subspace's KVStore and
   302  // sets the value to the provided pointer. If the value does not exist, it will
   303  // perform a no-op.
   304  func (s Subspace) GetIfExists(ctx sdk.Context, key []byte, ptr interface{}) {
   305  	store := s.kvStore(ctx)
   306  	bz := store.Get(key)
   307  	if bz == nil {
   308  		return
   309  	}
   310  
   311  	if err := s.cdc.UnmarshalJSON(bz, ptr); err != nil {
   312  		panic(err)
   313  	}
   314  }
   315  
   316  // GetRaw queries for the raw values bytes for a parameter by key.
   317  func (s Subspace) GetRaw(ctx sdk.Context, key []byte) []byte {
   318  	store := s.kvStore(ctx)
   319  	return store.Get(key)
   320  }
   321  
   322  // Has returns if a parameter key exists or not in the Subspace's KVStore.
   323  func (s Subspace) Has(ctx sdk.Context, key []byte) bool {
   324  	store := s.kvStore(ctx)
   325  	return store.Has(key)
   326  }
   327  
   328  // Modified returns true if the parameter key is set in the Subspace's transient
   329  // KVStore.
   330  func (s Subspace) Modified(ctx sdk.Context, key []byte) bool {
   331  	tstore := s.transientStore(ctx)
   332  	return tstore.Has(key)
   333  }
   334  
   335  // checkType verifies that the provided key and value are comptable and registered.
   336  func (s Subspace) checkType(key []byte, value interface{}) {
   337  	attr, ok := s.table.m[string(key)]
   338  	if !ok {
   339  		panic(fmt.Sprintf("parameter %s not registered", string(key)))
   340  	}
   341  
   342  	ty := attr.ty
   343  	pty := reflect.TypeOf(value)
   344  	if pty.Kind() == reflect.Ptr {
   345  		pty = pty.Elem()
   346  	}
   347  
   348  	if pty != ty {
   349  		panic("type mismatch with registered table")
   350  	}
   351  }
   352  
   353  // Set stores a value for given a parameter key assuming the parameter type has
   354  // been registered. It will panic if the parameter type has not been registered
   355  // or if the value cannot be encoded. A change record is also set in the Subspace's
   356  // transient KVStore to mark the parameter as modified.
   357  func (s Subspace) Set(ctx sdk.Context, key []byte, value interface{}) {
   358  	s.checkType(key, value)
   359  	store := s.kvStore(ctx)
   360  
   361  	bz, err := s.cdc.MarshalJSON(value)
   362  	if err != nil {
   363  		panic(err)
   364  	}
   365  
   366  	store.Set(key, bz)
   367  
   368  	tstore := s.transientStore(ctx)
   369  	tstore.Set(key, []byte{})
   370  }
   371  
   372  // Update stores an updated raw value for a given parameter key assuming the
   373  // parameter type has been registered. It will panic if the parameter type has
   374  // not been registered or if the value cannot be encoded. An error is returned
   375  // if the raw value is not compatible with the registered type for the parameter
   376  // key or if the new value is invalid as determined by the registered type's
   377  // validation function.
   378  func (s Subspace) Update(ctx sdk.Context, key, value []byte) error {
   379  	attr, ok := s.table.m[string(key)]
   380  	if !ok {
   381  		panic(fmt.Sprintf("parameter %s not registered", string(key)))
   382  	}
   383  
   384  	ty := attr.ty
   385  	dest := reflect.New(ty).Interface()
   386  	s.GetIfExists(ctx, key, dest)
   387  
   388  	if err := s.cdc.UnmarshalJSON(value, dest); err != nil {
   389  		return err
   390  	}
   391  
   392  	// destValue contains the dereferenced value of dest so validation function do
   393  	// not have to operate on pointers.
   394  	destValue := reflect.Indirect(reflect.ValueOf(dest)).Interface()
   395  	if err := s.Validate(ctx, key, destValue); err != nil {
   396  		return err
   397  	}
   398  
   399  	s.Set(ctx, key, dest)
   400  	return nil
   401  }
   402  
   403  // GetParamSet iterates through each ParamSetPair where for each pair, it will
   404  // retrieve the value and set it to the corresponding value pointer provided
   405  // in the ParamSetPair by calling Subspace#Get.
   406  func (s Subspace) GetParamSet(ctx sdk.Context, ps ParamSet) {
   407  	for _, pair := range ps.ParamSetPairs() {
   408  		s.Get(ctx, pair.Key, pair.Value)
   409  	}
   410  }
   411  
   412  // GetParamSetForInitGenesis iterates through each ParamSetPair where for each pair, it will
   413  // retrieve the value and set it to the corresponding value pointer provided, ignore the target keys for additional
   414  // in the ParamSetPair by calling Subspace#Get.
   415  func (s Subspace) GetParamSetForInitGenesis(ctx sdk.Context, ps ParamSet, ignoreList [][]byte) {
   416  	for _, pair := range ps.ParamSetPairs() {
   417  		beIgnore := false
   418  		for _, ignore := range ignoreList {
   419  			if bytes.Equal(ignore, pair.Key) {
   420  				beIgnore = true
   421  				break
   422  			}
   423  		}
   424  
   425  		if beIgnore {
   426  			continue
   427  		}
   428  		s.Get(ctx, pair.Key, pair.Value)
   429  	}
   430  }
   431  
   432  // SetParamSet iterates through each ParamSetPair and sets the value with the
   433  // corresponding parameter key in the Subspace's KVStore.
   434  func (s Subspace) SetParamSet(ctx sdk.Context, ps ParamSet) {
   435  	for _, pair := range ps.ParamSetPairs() {
   436  		// pair.Field is a pointer to the field, so indirecting the ptr.
   437  		// go-amino automatically handles it but just for sure,
   438  		// since SetStruct is meant to be used in InitGenesis
   439  		// so this method will not be called frequently
   440  		v := reflect.Indirect(reflect.ValueOf(pair.Value)).Interface()
   441  
   442  		if err := pair.ValidatorFn(v); err != nil {
   443  			panic(fmt.Sprintf("value from ParamSetPair is invalid: %s", err))
   444  		}
   445  
   446  		s.Set(ctx, pair.Key, v)
   447  	}
   448  }
   449  
   450  // SetParamSetForInitGenesis iterates through each ParamSetPair and sets the value with the
   451  // corresponding parameter key in the Subspace's KVStore, ignore the target keys for additional
   452  func (s Subspace) SetParamSetForInitGenesis(ctx sdk.Context, ps ParamSet, ignoreList [][]byte) {
   453  	for _, pair := range ps.ParamSetPairs() {
   454  		beIgnore := false
   455  		for _, ignore := range ignoreList {
   456  			if bytes.Equal(ignore, pair.Key) {
   457  				beIgnore = true
   458  				break
   459  			}
   460  		}
   461  
   462  		if beIgnore {
   463  			continue
   464  		}
   465  
   466  		// pair.Field is a pointer to the field, so indirecting the ptr.
   467  		// go-amino automatically handles it but just for sure,
   468  		// since SetStruct is meant to be used in InitGenesis
   469  		// so this method will not be called frequently
   470  		v := reflect.Indirect(reflect.ValueOf(pair.Value)).Interface()
   471  
   472  		if err := pair.ValidatorFn(v); err != nil {
   473  			panic(fmt.Sprintf("value from ParamSetPair is invalid: %s", err))
   474  		}
   475  
   476  		s.Set(ctx, pair.Key, v)
   477  	}
   478  }
   479  
   480  // Name returns the name of the Subspace.
   481  func (s Subspace) Name() string {
   482  	return string(s.name)
   483  }
   484  
   485  // Wrapper of Subspace, provides immutable functions only
   486  type ReadOnlySubspace struct {
   487  	s Subspace
   488  }
   489  
   490  // Get delegates a read-only Get call to the Subspace.
   491  func (ros ReadOnlySubspace) Get(ctx sdk.Context, key []byte, ptr interface{}) {
   492  	ros.s.Get(ctx, key, ptr)
   493  }
   494  
   495  // GetRaw delegates a read-only GetRaw call to the Subspace.
   496  func (ros ReadOnlySubspace) GetRaw(ctx sdk.Context, key []byte) []byte {
   497  	return ros.s.GetRaw(ctx, key)
   498  }
   499  
   500  // Has delegates a read-only Has call to the Subspace.
   501  func (ros ReadOnlySubspace) Has(ctx sdk.Context, key []byte) bool {
   502  	return ros.s.Has(ctx, key)
   503  }
   504  
   505  // Modified delegates a read-only Modified call to the Subspace.
   506  func (ros ReadOnlySubspace) Modified(ctx sdk.Context, key []byte) bool {
   507  	return ros.s.Modified(ctx, key)
   508  }
   509  
   510  // Name delegates a read-only Name call to the Subspace.
   511  func (ros ReadOnlySubspace) Name() string {
   512  	return ros.s.Name()
   513  }