From 0c80f4e18ba21465c3415a24199a36c6382df617 Mon Sep 17 00:00:00 2001 From: yihuang Date: Thu, 28 Mar 2024 12:02:11 +0800 Subject: [PATCH] Problem: no efficient bank api to support evm add/sub balances --- CHANGELOG.md | 1 + x/bank/keeper/keeper.go | 32 +++++++++++++++++++++ x/bank/keeper/send.go | 61 ++++++++++++++++++++++------------------- 3 files changed, 66 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 330a5499f075f..904d7e7e5a019 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (baseapp) [#205](https://github.com/crypto-org-chain/cosmos-sdk/pull/205) Add `TxExecutor` baseapp option, add `TxIndex`/`TxCount`/`MsgIndex`/`BlockGasUsed` fields to `Context, to support tx parallel execution. * (baseapp) [#205](https://github.com/crypto-org-chain/cosmos-sdk/pull/205) Support mount object store in baseapp, add `ObjectStore` api in context.. +* (x/bank) [#]() Add low level `AddBalance`,`SubBalance` APIs to bank keeper. ## [Unreleased-Upstream] diff --git a/x/bank/keeper/keeper.go b/x/bank/keeper/keeper.go index bfa45d23f64e7..bf499ac1d64a3 100644 --- a/x/bank/keeper/keeper.go +++ b/x/bank/keeper/keeper.go @@ -46,6 +46,11 @@ type Keeper interface { MintCoins(ctx context.Context, moduleName string, amt sdk.Coins) error BurnCoins(ctx context.Context, moduleName string, amt sdk.Coins) error + // it's for evm integration, call at your own risk. + AddBalance(ctx context.Context, addr sdk.AccAddress, coin sdk.Coin) error + // it's for evm integration, call at your own risk. + SubBalance(ctx context.Context, addr sdk.AccAddress, coin sdk.Coin) error + DelegateCoins(ctx context.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error UndelegateCoins(ctx context.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins) error @@ -412,6 +417,33 @@ func (k BaseKeeper) BurnCoins(ctx context.Context, moduleName string, amounts sd return nil } +// AddBalance is low level api to update balance directly, mainly used by evm integration, +// caller should make sure the total supply of the denom not changed and the account exists. +// +// it emits mint event. +func (k BaseKeeper) AddBalance(ctx context.Context, addr sdk.AccAddress, coin sdk.Coin) error { + sdkCtx := sdk.UnwrapSDKContext(ctx) + // emit mint event + sdkCtx.EventManager().EmitEvent( + types.NewCoinMintEvent(addr, sdk.NewCoins(coin)), + ) + return k.addCoin(ctx, addr, coin) +} + +// SubBalance is low level api to set balance directly, mainly used by evm integration, +// caller should make sure the total supply of the denom not changed, the account exists, +// and not a vesting account (no locked coins). +// +// it emits burn event. +func (k BaseKeeper) SubBalance(ctx context.Context, addr sdk.AccAddress, coin sdk.Coin) error { + sdkCtx := sdk.UnwrapSDKContext(ctx) + // emit burn event + sdkCtx.EventManager().EmitEvent( + types.NewCoinBurnEvent(addr, sdk.NewCoins(coin)), + ) + return k.subCoin(ctx, addr, coin, sdk.NewCoin(coin.Denom, math.ZeroInt())) +} + // setSupply sets the supply for the given coin func (k BaseKeeper) setSupply(ctx context.Context, coin sdk.Coin) { // Bank invariants and IBC requires to remove zero coins. diff --git a/x/bank/keeper/send.go b/x/bank/keeper/send.go index 3bcc6d315c331..fbce557eedc1a 100644 --- a/x/bank/keeper/send.go +++ b/x/bank/keeper/send.go @@ -8,7 +8,6 @@ import ( "cosmossdk.io/core/store" errorsmod "cosmossdk.io/errors" "cosmossdk.io/log" - "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/telemetry" @@ -262,29 +261,8 @@ func (k BaseSendKeeper) subUnlockedCoins(ctx context.Context, addr sdk.AccAddres lockedCoins := k.LockedCoins(ctx, addr) for _, coin := range amt { - balance := k.GetBalance(ctx, addr, coin.Denom) locked := sdk.NewCoin(coin.Denom, lockedCoins.AmountOf(coin.Denom)) - - spendable, hasNeg := sdk.Coins{balance}.SafeSub(locked) - if hasNeg { - return errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, - "locked amount exceeds account balance funds: %s > %s", locked, balance) - } - - if _, hasNeg := spendable.SafeSub(coin); hasNeg { - if len(spendable) == 0 { - spendable = sdk.Coins{sdk.NewCoin(coin.Denom, math.ZeroInt())} - } - return errorsmod.Wrapf( - sdkerrors.ErrInsufficientFunds, - "spendable balance %s is smaller than %s", - spendable, coin, - ) - } - - newBalance := balance.Sub(coin) - - if err := k.setBalance(ctx, addr, newBalance); err != nil { + if err := k.subCoin(ctx, addr, coin, locked); err != nil { return err } } @@ -297,6 +275,31 @@ func (k BaseSendKeeper) subUnlockedCoins(ctx context.Context, addr sdk.AccAddres return nil } +func (k BaseSendKeeper) subCoin(ctx context.Context, addr sdk.AccAddress, coin sdk.Coin, locked sdk.Coin) error { + var ( + spendable sdk.Coin + err error + ) + balance := k.GetBalance(ctx, addr, coin.Denom) + if locked.IsZero() { + spendable = balance + } else { + spendable, err = spendable.SafeSub(locked) + if err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, + "locked amount exceeds account balance funds: %s > %s", locked, balance) + } + } + if spendable.Amount.LT(coin.Amount) { + return errorsmod.Wrapf( + sdkerrors.ErrInsufficientFunds, + "spendable balance %s is smaller than %s", + spendable, coin, + ) + } + return k.setBalance(ctx, addr, balance.Sub(coin)) +} + // addCoins increase the addr balance by the given amt. Fails if the provided // amt is invalid. It emits a coin received event. func (k BaseSendKeeper) addCoins(ctx context.Context, addr sdk.AccAddress, amt sdk.Coins) error { @@ -305,11 +308,7 @@ func (k BaseSendKeeper) addCoins(ctx context.Context, addr sdk.AccAddress, amt s } for _, coin := range amt { - balance := k.GetBalance(ctx, addr, coin.Denom) - newBalance := balance.Add(coin) - - err := k.setBalance(ctx, addr, newBalance) - if err != nil { + if err := k.addCoin(ctx, addr, coin); err != nil { return err } } @@ -323,6 +322,12 @@ func (k BaseSendKeeper) addCoins(ctx context.Context, addr sdk.AccAddress, amt s return nil } +func (k BaseSendKeeper) addCoin(ctx context.Context, addr sdk.AccAddress, coin sdk.Coin) error { + balance := k.GetBalance(ctx, addr, coin.Denom) + newBalance := balance.Add(coin) + return k.setBalance(ctx, addr, newBalance) +} + // setBalance sets the coin balance for an account by address. func (k BaseSendKeeper) setBalance(ctx context.Context, addr sdk.AccAddress, balance sdk.Coin) error { if !balance.IsValid() {