Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem: no efficient bank api to support evm add/sub balances #239

Merged
merged 2 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Features

* (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) [#206](https://github.com/crypto-org-chain/cosmos-sdk/pull/206) Support mount object store in baseapp, add `ObjectStore` api in context..
* (baseapp) [#206](https://github.com/crypto-org-chain/cosmos-sdk/pull/206) Support mount object store in baseapp, add `ObjectStore` api in context.
* (bank) [#237](https://github.com/crypto-org-chain/cosmos-sdk/pull/237) Support virtual accounts in sending coins.
* (x/bank) [#239](https://github.com/crypto-org-chain/cosmos-sdk/pull/239) Add low level `AddBalance`,`SubBalance` APIs to bank keeper.

## [Unreleased-Upstream]

Expand Down
27 changes: 27 additions & 0 deletions x/bank/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,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's for evm integration, call at your own risk.
// 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)
yihuang marked this conversation as resolved.
Show resolved Hide resolved
}

// SubBalance 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.
// it's for evm integration, call at your own risk.
// 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)),
)
lockedCoins := k.LockedCoins(ctx, addr)
return k.subCoin(ctx, addr, coin, lockedCoins)
}

// 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.
Expand Down
63 changes: 34 additions & 29 deletions x/bank/keeper/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"cosmossdk.io/core/store"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/log"
"cosmossdk.io/math"
storetypes "cosmossdk.io/store/types"

"github.com/cosmos/cosmos-sdk/codec"
Expand Down Expand Up @@ -273,29 +272,7 @@ 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, lockedCoins); err != nil {
return err
}
}
Expand All @@ -308,6 +285,32 @@ 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, lockedCoins sdk.Coins) error {
var (
spendable sdk.Coin
err error
)
balance := k.GetBalance(ctx, addr, coin.Denom)
locked := sdk.NewCoin(coin.Denom, lockedCoins.AmountOf(coin.Denom))
if locked.IsZero() {
spendable = balance
} else {
spendable, err = balance.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 {
Expand All @@ -316,11 +319,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
}
}
Expand All @@ -334,6 +333,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() {
Expand Down
Loading