Some checks failed
CodeQL / Analyze (push) Waiting to run
golangci-lint / lint (push) Waiting to run
Tests / Code Coverage / build (amd64) (push) Waiting to run
Tests / Code Coverage / build (arm64) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[additional-args:-tags="test_e2e" name:e2e path:./e2e]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:08-wasm path:./modules/light-clients/08-wasm]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:ibc-go path:.]) (push) Waiting to run
Docker Build & Push Simapp (main) / docker-build (push) Has been cancelled
326 lines
10 KiB
Go
326 lines
10 KiB
Go
package keeper
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"cosmossdk.io/core/address"
|
|
corestore "cosmossdk.io/core/store"
|
|
"cosmossdk.io/log"
|
|
sdkmath "cosmossdk.io/math"
|
|
"cosmossdk.io/store/prefix"
|
|
storetypes "cosmossdk.io/store/types"
|
|
|
|
"git.cw.tr/mukan-network/mukan-sdk/codec"
|
|
"git.cw.tr/mukan-network/mukan-sdk/runtime"
|
|
sdk "git.cw.tr/mukan-network/mukan-sdk/types"
|
|
authcodec "git.cw.tr/mukan-network/mukan-sdk/x/auth/codec"
|
|
banktypes "git.cw.tr/mukan-network/mukan-sdk/x/bank/types"
|
|
|
|
cmtbytes "git.cw.tr/mukan-network/mukan-consensus/libs/bytes"
|
|
|
|
"git.cw.tr/mukan-network/mukan-ibc/modules/apps/transfer/types"
|
|
porttypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/05-port/types"
|
|
"git.cw.tr/mukan-network/mukan-ibc/modules/core/exported"
|
|
)
|
|
|
|
// Keeper defines the IBC fungible transfer keeper
|
|
type Keeper struct {
|
|
storeService corestore.KVStoreService
|
|
cdc codec.BinaryCodec
|
|
legacySubspace types.ParamSubspace
|
|
addressCodec address.Codec
|
|
|
|
ics4Wrapper porttypes.ICS4Wrapper
|
|
channelKeeper types.ChannelKeeper
|
|
msgRouter types.MessageRouter
|
|
AuthKeeper types.AccountKeeper
|
|
BankKeeper types.BankKeeper
|
|
|
|
// the address capable of executing a MsgUpdateParams message. Typically, this
|
|
// should be the x/gov module account.
|
|
authority string
|
|
}
|
|
|
|
// NewKeeper creates a new IBC transfer Keeper instance
|
|
func NewKeeper(
|
|
cdc codec.BinaryCodec,
|
|
storeService corestore.KVStoreService,
|
|
legacySubspace types.ParamSubspace,
|
|
ics4Wrapper porttypes.ICS4Wrapper,
|
|
channelKeeper types.ChannelKeeper,
|
|
msgRouter types.MessageRouter,
|
|
authKeeper types.AccountKeeper,
|
|
bankKeeper types.BankKeeper,
|
|
authority string,
|
|
) Keeper {
|
|
// ensure ibc transfer module account is set
|
|
if addr := authKeeper.GetModuleAddress(types.ModuleName); addr == nil {
|
|
panic(errors.New("the IBC transfer module account has not been set"))
|
|
}
|
|
|
|
if strings.TrimSpace(authority) == "" {
|
|
panic(errors.New("authority must be non-empty"))
|
|
}
|
|
|
|
addressCodec := authcodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix())
|
|
|
|
return Keeper{
|
|
cdc: cdc,
|
|
storeService: storeService,
|
|
legacySubspace: legacySubspace,
|
|
addressCodec: addressCodec,
|
|
ics4Wrapper: ics4Wrapper,
|
|
channelKeeper: channelKeeper,
|
|
msgRouter: msgRouter,
|
|
AuthKeeper: authKeeper,
|
|
BankKeeper: bankKeeper,
|
|
authority: authority,
|
|
}
|
|
}
|
|
|
|
// WithICS4Wrapper sets the ICS4Wrapper. This function may be used after
|
|
// the keepers creation to set the middleware which is above this module
|
|
// in the IBC application stack.
|
|
func (k *Keeper) WithICS4Wrapper(wrapper porttypes.ICS4Wrapper) {
|
|
k.ics4Wrapper = wrapper
|
|
}
|
|
|
|
// GetICS4Wrapper returns the ICS4Wrapper.
|
|
func (k Keeper) GetICS4Wrapper() porttypes.ICS4Wrapper {
|
|
return k.ics4Wrapper
|
|
}
|
|
|
|
// GetAuthority returns the transfer module's authority.
|
|
func (k Keeper) GetAuthority() string {
|
|
return k.authority
|
|
}
|
|
|
|
// SetAddressCodec sets the address codec used by the keeper.
|
|
func (k *Keeper) SetAddressCodec(addressCodec address.Codec) {
|
|
k.addressCodec = addressCodec
|
|
}
|
|
|
|
// GetAddressCodec returns the address codec used by the keeper.
|
|
func (k *Keeper) GetAddressCodec() address.Codec {
|
|
return k.addressCodec
|
|
}
|
|
|
|
// Logger returns a module-specific logger.
|
|
func (Keeper) Logger(ctx sdk.Context) log.Logger {
|
|
return ctx.Logger().With("module", "x/"+exported.ModuleName+"-"+types.ModuleName)
|
|
}
|
|
|
|
// GetPort returns the portID for the transfer module. Used in ExportGenesis
|
|
func (k Keeper) GetPort(ctx sdk.Context) string {
|
|
store := k.storeService.OpenKVStore(ctx)
|
|
bz, err := store.Get(types.PortKey)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return string(bz)
|
|
}
|
|
|
|
// SetPort sets the portID for the transfer module. Used in InitGenesis
|
|
func (k Keeper) SetPort(ctx sdk.Context, portID string) {
|
|
store := k.storeService.OpenKVStore(ctx)
|
|
if err := store.Set(types.PortKey, []byte(portID)); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// GetParams returns the current transfer module parameters.
|
|
func (k Keeper) GetParams(ctx sdk.Context) types.Params {
|
|
store := k.storeService.OpenKVStore(ctx)
|
|
bz, err := store.Get([]byte(types.ParamsKey))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if bz == nil { // only panic on unset params and not on empty params
|
|
panic(errors.New("transfer params are not set in store"))
|
|
}
|
|
|
|
var params types.Params
|
|
k.cdc.MustUnmarshal(bz, ¶ms)
|
|
return params
|
|
}
|
|
|
|
// SetParams sets the transfer module parameters.
|
|
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
|
|
store := k.storeService.OpenKVStore(ctx)
|
|
bz := k.cdc.MustMarshal(¶ms)
|
|
if err := store.Set([]byte(types.ParamsKey), bz); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// GetDenom retrieves the denom from store given the hash of the denom.
|
|
func (k Keeper) GetDenom(ctx sdk.Context, denomHash cmtbytes.HexBytes) (types.Denom, bool) {
|
|
store := prefix.NewStore(runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)), types.DenomKey)
|
|
bz := store.Get(denomHash)
|
|
if len(bz) == 0 {
|
|
return types.Denom{}, false
|
|
}
|
|
|
|
var denom types.Denom
|
|
k.cdc.MustUnmarshal(bz, &denom)
|
|
|
|
return denom, true
|
|
}
|
|
|
|
// HasDenom checks if a the key with the given denomination hash exists on the store.
|
|
func (k Keeper) HasDenom(ctx sdk.Context, denomHash cmtbytes.HexBytes) bool {
|
|
store := prefix.NewStore(runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)), types.DenomKey)
|
|
return store.Has(denomHash)
|
|
}
|
|
|
|
// SetDenom sets a new {denom hash -> denom } pair to the store.
|
|
// This allows for reverse lookup of the denom given the hash.
|
|
func (k Keeper) SetDenom(ctx sdk.Context, denom types.Denom) {
|
|
store := prefix.NewStore(runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)), types.DenomKey)
|
|
bz := k.cdc.MustMarshal(&denom)
|
|
store.Set(denom.Hash(), bz)
|
|
}
|
|
|
|
// GetAllDenoms returns all the denominations.
|
|
func (k Keeper) GetAllDenoms(ctx sdk.Context) types.Denoms {
|
|
denoms := types.Denoms{}
|
|
k.IterateDenoms(ctx, func(denom types.Denom) bool {
|
|
denoms = append(denoms, denom)
|
|
return false
|
|
})
|
|
|
|
return denoms.Sort()
|
|
}
|
|
|
|
// IterateDenoms iterates over the denominations in the store and performs a callback function.
|
|
func (k Keeper) IterateDenoms(ctx sdk.Context, cb func(denom types.Denom) bool) {
|
|
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
|
|
iterator := storetypes.KVStorePrefixIterator(store, types.DenomKey)
|
|
|
|
defer sdk.LogDeferred(k.Logger(ctx), func() error { return iterator.Close() })
|
|
for ; iterator.Valid(); iterator.Next() {
|
|
var denom types.Denom
|
|
k.cdc.MustUnmarshal(iterator.Value(), &denom)
|
|
|
|
if cb(denom) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// SetDenomMetadata sets an IBC token's denomination metadata
|
|
func (k Keeper) SetDenomMetadata(ctx sdk.Context, denom types.Denom) {
|
|
metadata := banktypes.Metadata{
|
|
Description: fmt.Sprintf("IBC token from %s", denom.Path()),
|
|
DenomUnits: []*banktypes.DenomUnit{
|
|
{
|
|
Denom: denom.Base,
|
|
Exponent: 0,
|
|
},
|
|
},
|
|
// Setting base as IBC hash denom since bank keepers's SetDenomMetadata uses
|
|
// Base as key path and the IBC hash is what gives this token uniqueness
|
|
// on the executing chain
|
|
Base: denom.IBCDenom(),
|
|
Display: denom.Path(),
|
|
Name: fmt.Sprintf("%s IBC token", denom.Path()),
|
|
Symbol: strings.ToUpper(denom.Base),
|
|
}
|
|
|
|
k.BankKeeper.SetDenomMetaData(ctx, metadata)
|
|
}
|
|
|
|
// GetTotalEscrowForDenom gets the total amount of source chain tokens that
|
|
// are in escrow, keyed by the denomination.
|
|
//
|
|
// NOTE: if there is no value stored in state for the provided denom then a new Coin is returned for the denom with an initial value of zero.
|
|
// This accommodates callers to simply call `Add()` on the returned Coin as an empty Coin literal (e.g. sdk.Coin{}) will trigger a panic due to the absence of a denom.
|
|
func (k Keeper) GetTotalEscrowForDenom(ctx sdk.Context, denom string) sdk.Coin {
|
|
store := k.storeService.OpenKVStore(ctx)
|
|
bz, err := store.Get(types.TotalEscrowForDenomKey(denom))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if len(bz) == 0 {
|
|
return sdk.NewCoin(denom, sdkmath.ZeroInt())
|
|
}
|
|
|
|
amount := sdk.IntProto{}
|
|
k.cdc.MustUnmarshal(bz, &amount)
|
|
|
|
return sdk.NewCoin(denom, amount.Int)
|
|
}
|
|
|
|
// SetTotalEscrowForDenom stores the total amount of source chain tokens that are in escrow.
|
|
// Amount is stored in state if and only if it is not equal to zero. The function will panic
|
|
// if the amount is negative.
|
|
func (k Keeper) SetTotalEscrowForDenom(ctx sdk.Context, coin sdk.Coin) {
|
|
if coin.Amount.IsNegative() {
|
|
panic(fmt.Errorf("amount cannot be negative: %s", coin.Amount))
|
|
}
|
|
|
|
store := k.storeService.OpenKVStore(ctx)
|
|
key := types.TotalEscrowForDenomKey(coin.Denom)
|
|
|
|
if coin.Amount.IsZero() {
|
|
if err := store.Delete(key); err != nil { // delete the key since Cosmos SDK x/bank module will prune any non-zero balances
|
|
panic(err)
|
|
}
|
|
return
|
|
}
|
|
|
|
bz := k.cdc.MustMarshal(&sdk.IntProto{Int: coin.Amount})
|
|
if err := store.Set(key, bz); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// GetAllTotalEscrowed returns the escrow information for all the denominations.
|
|
func (k Keeper) GetAllTotalEscrowed(ctx sdk.Context) sdk.Coins {
|
|
var escrows sdk.Coins
|
|
k.IterateTokensInEscrow(ctx, []byte(types.KeyTotalEscrowPrefix), func(denomEscrow sdk.Coin) bool {
|
|
escrows = escrows.Add(denomEscrow)
|
|
return false
|
|
})
|
|
|
|
return escrows
|
|
}
|
|
|
|
// IterateTokensInEscrow iterates over the denomination escrows in the store
|
|
// and performs a callback function. Denominations for which an invalid value
|
|
// (i.e. not integer) is stored, will be skipped.
|
|
func (k Keeper) IterateTokensInEscrow(ctx sdk.Context, storeprefix []byte, cb func(denomEscrow sdk.Coin) bool) {
|
|
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
|
|
iterator := storetypes.KVStorePrefixIterator(store, storeprefix)
|
|
|
|
defer sdk.LogDeferred(k.Logger(ctx), func() error { return iterator.Close() })
|
|
for ; iterator.Valid(); iterator.Next() {
|
|
denom := strings.TrimPrefix(string(iterator.Key()), fmt.Sprintf("%s/", types.KeyTotalEscrowPrefix))
|
|
if strings.TrimSpace(denom) == "" {
|
|
continue // denom is empty
|
|
}
|
|
|
|
amount := sdk.IntProto{}
|
|
if err := k.cdc.Unmarshal(iterator.Value(), &amount); err != nil {
|
|
continue // total escrow amount cannot be unmarshalled to integer
|
|
}
|
|
|
|
denomEscrow := sdk.NewCoin(denom, amount.Int)
|
|
if cb(denomEscrow) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// IsBlockedAddr checks if the given address is allowed to send or receive tokens.
|
|
// The module account is always allowed to send and receive tokens.
|
|
func (k Keeper) IsBlockedAddr(addr sdk.AccAddress) bool {
|
|
moduleAddr := k.AuthKeeper.GetModuleAddress(types.ModuleName)
|
|
if addr.Equals(moduleAddr) {
|
|
return false
|
|
}
|
|
|
|
return k.BankKeeper.BlockedAddr(addr)
|
|
}
|