github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/wasm/types/types.go (about)

     1  package types
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  
     8  	wasmvmtypes "github.com/CosmWasm/wasmvm/types"
     9  	codectypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec/types"
    10  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    11  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
    12  	"github.com/gogo/protobuf/proto"
    13  )
    14  
    15  const (
    16  	defaultMemoryCacheSize    uint32 = 100 // in MiB
    17  	defaultSmartQueryGasLimit uint64 = 3_000_000
    18  	defaultContractDebugMode         = false
    19  
    20  	// ContractAddrLen defines a valid address length for contracts
    21  	ContractAddrLen = 32
    22  	// SDKAddrLen defines a valid address length that was used in sdk address generation
    23  	SDKAddrLen = 20
    24  )
    25  
    26  func (m Model) ValidateBasic() error {
    27  	if len(m.Key) == 0 {
    28  		return sdkerrors.Wrap(ErrEmpty, "key")
    29  	}
    30  	return nil
    31  }
    32  
    33  func (c CodeInfo) ValidateBasic() error {
    34  	if len(c.CodeHash) == 0 {
    35  		return sdkerrors.Wrap(ErrEmpty, "code hash")
    36  	}
    37  	if _, err := sdk.AccAddressFromBech32(c.Creator); err != nil {
    38  		return sdkerrors.Wrap(err, "creator")
    39  	}
    40  	if err := c.InstantiateConfig.ValidateBasic(); err != nil {
    41  		return sdkerrors.Wrap(err, "instantiate config")
    42  	}
    43  	return nil
    44  }
    45  
    46  // NewCodeInfo fills a new CodeInfo struct
    47  func NewCodeInfo(codeHash []byte, creator sdk.AccAddress, instantiatePermission AccessConfig) CodeInfo {
    48  	return CodeInfo{
    49  		CodeHash:          codeHash,
    50  		Creator:           creator.String(),
    51  		InstantiateConfig: instantiatePermission,
    52  	}
    53  }
    54  
    55  var AllCodeHistoryTypes = []ContractCodeHistoryOperationType{ContractCodeHistoryOperationTypeGenesis, ContractCodeHistoryOperationTypeInit, ContractCodeHistoryOperationTypeMigrate}
    56  
    57  // NewContractInfo creates a new instance of a given WASM contract info
    58  func NewContractInfo(codeID uint64, creator, admin sdk.AccAddress, label string, createdAt *AbsoluteTxPosition) ContractInfo {
    59  	var adminAddr string
    60  	if !admin.Empty() {
    61  		adminAddr = admin.String()
    62  	}
    63  	return ContractInfo{
    64  		CodeID:  codeID,
    65  		Creator: creator.String(),
    66  		Admin:   adminAddr,
    67  		Label:   label,
    68  		Created: createdAt,
    69  	}
    70  }
    71  
    72  // validatable is an optional interface that can be implemented by an ContractInfoExtension to enable validation
    73  type validatable interface {
    74  	ValidateBasic() error
    75  }
    76  
    77  // ValidateBasic does syntax checks on the data. If an extension is set and has the `ValidateBasic() error` method, then
    78  // the method is called as well. It is recommend to implement `ValidateBasic` so that the data is verified in the setter
    79  // but also in the genesis import process.
    80  func (c *ContractInfo) ValidateBasic() error {
    81  	if c.CodeID == 0 {
    82  		return sdkerrors.Wrap(ErrEmpty, "code id")
    83  	}
    84  	if _, err := sdk.AccAddressFromBech32(c.Creator); err != nil {
    85  		return sdkerrors.Wrap(err, "creator")
    86  	}
    87  	if len(c.Admin) != 0 {
    88  		if _, err := sdk.AccAddressFromBech32(c.Admin); err != nil {
    89  			return sdkerrors.Wrap(err, "admin")
    90  		}
    91  	}
    92  	if err := validateLabel(c.Label); err != nil {
    93  		return sdkerrors.Wrap(err, "label")
    94  	}
    95  	if c.Extension == nil {
    96  		return nil
    97  	}
    98  
    99  	e, ok := c.Extension.GetCachedValue().(validatable)
   100  	if !ok {
   101  		return nil
   102  	}
   103  	if err := e.ValidateBasic(); err != nil {
   104  		return sdkerrors.Wrap(err, "extension")
   105  	}
   106  	return nil
   107  }
   108  
   109  // SetExtension set new extension data. Calls `ValidateBasic() error` on non nil values when method is implemented by
   110  // the extension.
   111  func (c *ContractInfo) SetExtension(ext ContractInfoExtension) error {
   112  	if ext == nil {
   113  		c.Extension = nil
   114  		return nil
   115  	}
   116  	if e, ok := ext.(validatable); ok {
   117  		if err := e.ValidateBasic(); err != nil {
   118  			return err
   119  		}
   120  	}
   121  	any, err := codectypes.NewAnyWithValue(ext)
   122  	if err != nil {
   123  		return sdkerrors.Wrap(sdkerrors.ErrPackAny, err.Error())
   124  	}
   125  
   126  	c.Extension = any
   127  	return nil
   128  }
   129  
   130  // ReadExtension copies the extension value to the pointer passed as argument so that there is no need to cast
   131  // For example with a custom extension of type `MyContractDetails` it will look as following:
   132  //
   133  //	var d MyContractDetails
   134  //	if err := info.ReadExtension(&d); err != nil {
   135  //		return nil, sdkerrors.Wrap(err, "extension")
   136  //	}
   137  func (c *ContractInfo) ReadExtension(e ContractInfoExtension) error {
   138  	rv := reflect.ValueOf(e)
   139  	if rv.Kind() != reflect.Ptr || rv.IsNil() {
   140  		return sdkerrors.Wrap(sdkerrors.ErrInvalidType, "not a pointer")
   141  	}
   142  	if c.Extension == nil {
   143  		return nil
   144  	}
   145  
   146  	cached := c.Extension.GetCachedValue()
   147  	elem := reflect.ValueOf(cached).Elem()
   148  	if !elem.Type().AssignableTo(rv.Elem().Type()) {
   149  		return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "extension is of type %s but argument of %s", elem.Type(), rv.Elem().Type())
   150  	}
   151  	rv.Elem().Set(elem)
   152  	return nil
   153  }
   154  
   155  func (c ContractInfo) InitialHistory(initMsg []byte) ContractCodeHistoryEntry {
   156  	return ContractCodeHistoryEntry{
   157  		Operation: ContractCodeHistoryOperationTypeInit,
   158  		CodeID:    c.CodeID,
   159  		Updated:   c.Created,
   160  		Msg:       initMsg,
   161  	}
   162  }
   163  
   164  func (c *ContractInfo) AddMigration(ctx sdk.Context, codeID uint64, msg []byte) ContractCodeHistoryEntry {
   165  	h := ContractCodeHistoryEntry{
   166  		Operation: ContractCodeHistoryOperationTypeMigrate,
   167  		CodeID:    codeID,
   168  		Updated:   NewAbsoluteTxPosition(ctx),
   169  		Msg:       msg,
   170  	}
   171  	c.CodeID = codeID
   172  	return h
   173  }
   174  
   175  // ResetFromGenesis resets contracts timestamp and history.
   176  func (c *ContractInfo) ResetFromGenesis(ctx sdk.Context) ContractCodeHistoryEntry {
   177  	c.Created = NewAbsoluteTxPosition(ctx)
   178  	return ContractCodeHistoryEntry{
   179  		Operation: ContractCodeHistoryOperationTypeGenesis,
   180  		CodeID:    c.CodeID,
   181  		Updated:   c.Created,
   182  	}
   183  }
   184  
   185  // AdminAddr convert into sdk.AccAddress or nil when not set
   186  func (c *ContractInfo) AdminAddr() sdk.AccAddress {
   187  	if c.Admin == "" {
   188  		return nil
   189  	}
   190  	admin, err := sdk.AccAddressFromBech32(c.Admin)
   191  	if err != nil { // should never happen
   192  		panic(err.Error())
   193  	}
   194  	return admin
   195  }
   196  
   197  // ContractInfoExtension defines the extension point for custom data to be stored with a contract info
   198  type ContractInfoExtension interface {
   199  	proto.Message
   200  	String() string
   201  }
   202  
   203  var _ codectypes.UnpackInterfacesMessage = &ContractInfo{}
   204  
   205  // UnpackInterfaces implements codectypes.UnpackInterfaces
   206  func (c *ContractInfo) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
   207  	var details ContractInfoExtension
   208  	if err := unpacker.UnpackAny(c.Extension, &details); err != nil {
   209  		return err
   210  	}
   211  	return codectypes.UnpackInterfaces(details, unpacker)
   212  }
   213  
   214  // NewAbsoluteTxPosition gets a block position from the context
   215  func NewAbsoluteTxPosition(ctx sdk.Context) *AbsoluteTxPosition {
   216  	// we must safely handle nil gas meters
   217  	var index uint64
   218  	meter := ctx.BlockGasMeter()
   219  	if meter != nil {
   220  		index = meter.GasConsumed()
   221  	}
   222  	height := ctx.BlockHeight()
   223  	if height < 0 {
   224  		panic(fmt.Sprintf("unsupported height: %d", height))
   225  	}
   226  	return &AbsoluteTxPosition{
   227  		BlockHeight: uint64(height),
   228  		TxIndex:     index,
   229  	}
   230  }
   231  
   232  // LessThan can be used to sort
   233  func (a *AbsoluteTxPosition) LessThan(b *AbsoluteTxPosition) bool {
   234  	if a == nil {
   235  		return true
   236  	}
   237  	if b == nil {
   238  		return false
   239  	}
   240  	return a.BlockHeight < b.BlockHeight || (a.BlockHeight == b.BlockHeight && a.TxIndex < b.TxIndex)
   241  }
   242  
   243  // AbsoluteTxPositionLen number of elements in byte representation
   244  const AbsoluteTxPositionLen = 16
   245  
   246  // Bytes encodes the object into a 16 byte representation with big endian block height and tx index.
   247  func (a *AbsoluteTxPosition) Bytes() []byte {
   248  	if a == nil {
   249  		panic("object must not be nil")
   250  	}
   251  	r := make([]byte, AbsoluteTxPositionLen)
   252  	copy(r[0:], sdk.Uint64ToBigEndian(a.BlockHeight))
   253  	copy(r[8:], sdk.Uint64ToBigEndian(a.TxIndex))
   254  	return r
   255  }
   256  
   257  // NewEnv initializes the environment for a contract instance
   258  func NewEnv(ctx sdk.Context, contractAddr sdk.AccAddress) wasmvmtypes.Env {
   259  	// safety checks before casting below
   260  	if ctx.BlockHeight() < 0 {
   261  		panic("Block height must never be negative")
   262  	}
   263  	nano := ctx.BlockTime().UnixNano()
   264  	if nano < 1 {
   265  		panic("Block (unix) time must never be empty or negative ")
   266  	}
   267  
   268  	env := wasmvmtypes.Env{
   269  		Block: wasmvmtypes.BlockInfo{
   270  			Height:  uint64(ctx.BlockHeight()),
   271  			Time:    uint64(nano),
   272  			ChainID: ctx.ChainID(),
   273  		},
   274  		Contract: wasmvmtypes.ContractInfo{
   275  			Address: contractAddr.String(),
   276  		},
   277  	}
   278  	if txCounter, ok := TXCounter(ctx); ok {
   279  		env.Transaction = &wasmvmtypes.TransactionInfo{Index: txCounter}
   280  	}
   281  	return env
   282  }
   283  
   284  // NewInfo initializes the MessageInfo for a contract instance
   285  func NewInfo(creator sdk.AccAddress, deposit sdk.CoinAdapters) wasmvmtypes.MessageInfo {
   286  	return wasmvmtypes.MessageInfo{
   287  		Sender: creator.String(),
   288  		Funds:  NewWasmCoins(deposit),
   289  	}
   290  }
   291  
   292  // NewWasmCoins translates between Cosmos SDK coins and Wasm coins
   293  func NewWasmCoins(cosmosCoins sdk.CoinAdapters) (wasmCoins []wasmvmtypes.Coin) {
   294  	for _, coin := range cosmosCoins {
   295  		wasmCoin := wasmvmtypes.Coin{
   296  			Denom:  coin.Denom,
   297  			Amount: coin.Amount.String(),
   298  		}
   299  		wasmCoins = append(wasmCoins, wasmCoin)
   300  	}
   301  	return wasmCoins
   302  }
   303  
   304  // WasmConfig is the extra config required for wasm
   305  type WasmConfig struct {
   306  	// SimulationGasLimit is the max gas to be used in a tx simulation call.
   307  	// When not set the consensus max block gas is used instead
   308  	SimulationGasLimit *uint64
   309  	// SimulationGasLimit is the max gas to be used in a smart query contract call
   310  	SmartQueryGasLimit uint64
   311  	// MemoryCacheSize in MiB not bytes
   312  	MemoryCacheSize uint32
   313  	// ContractDebugMode log what contract print
   314  	ContractDebugMode bool
   315  }
   316  
   317  // DefaultWasmConfig returns the default settings for WasmConfig
   318  func DefaultWasmConfig() WasmConfig {
   319  	return WasmConfig{
   320  		SmartQueryGasLimit: defaultSmartQueryGasLimit,
   321  		MemoryCacheSize:    defaultMemoryCacheSize,
   322  		ContractDebugMode:  defaultContractDebugMode,
   323  	}
   324  }
   325  
   326  // VerifyAddressLen ensures that the address matches the expected length
   327  func VerifyAddressLen() func(addr []byte) error {
   328  	return func(addr []byte) error {
   329  		if len(addr) != ContractAddrLen && len(addr) != SDKAddrLen {
   330  			return sdkerrors.ErrInvalidAddress
   331  		}
   332  		return nil
   333  	}
   334  }
   335  
   336  // IsSubset will return true if the caller is the same as the superset,
   337  // or if the caller is more restrictive than the superset.
   338  func (a AccessConfig) IsSubset(superSet AccessConfig) bool {
   339  	switch superSet.Permission {
   340  	case AccessTypeEverybody:
   341  		// Everything is a subset of this
   342  		return a.Permission != AccessTypeUnspecified
   343  	case AccessTypeNobody:
   344  		// Only an exact match is a subset of this
   345  		return a.Permission == AccessTypeNobody
   346  	case AccessTypeOnlyAddress:
   347  		// A subset addrs match or nobody
   348  		if a.Permission == AccessTypeNobody {
   349  			return true
   350  		}
   351  		if a.Permission == AccessTypeOnlyAddress {
   352  			m := make(map[string]struct{})
   353  			for _, addr := range strings.Split(superSet.Address, ",") {
   354  				m[addr] = struct{}{}
   355  			}
   356  			for _, addr := range strings.Split(a.Address, ",") {
   357  				if _, ok := m[addr]; !ok {
   358  					return false
   359  				}
   360  			}
   361  			return true
   362  		}
   363  		return false
   364  	default:
   365  		return false
   366  	}
   367  }