diff --git a/a3p-integration/proposals/a:upgrade-next/ante-fees.test.js b/a3p-integration/proposals/a:upgrade-next/ante-fees.test.js new file mode 100644 index 00000000000..b32a938a664 --- /dev/null +++ b/a3p-integration/proposals/a:upgrade-next/ante-fees.test.js @@ -0,0 +1,74 @@ +import test from 'ava'; + +import { CHAINID, GOV1ADDR, GOV2ADDR, agd } from '@agoric/synthetic-chain'; + +test(`ante handler sends fee only to vbank/reserve`, async t => { + const [feeCollector, vbankReserve] = await Promise.all( + // Look up addresses for fee collector and reserve accounts. + ['fee_collector', 'vbank/reserve'].map(async name => { + const { + account: { + '@type': moduleAcct, + base_account: { address }, + }, + } = await agd.query('auth', 'module-account', name); + + t.is( + moduleAcct, + '/cosmos.auth.v1beta1.ModuleAccount', + `${name} is a module account`, + ); + return address; + }), + ); + + const getBalances = addresses => + Promise.all( + addresses.map(async address => { + const { balances } = await agd.query('bank', 'balances', address); + return balances; + }), + ); + + const [feeCollectorStartBalances, vbankReserveStartBalances] = + await getBalances([feeCollector, vbankReserve]); + + // Send a transaction with a known fee. + const feeAmount = 999n; + const feeDenom = 'uist'; + const result = await agd.tx( + `bank send ${GOV1ADDR} ${GOV2ADDR} 1234ubld --fees=${feeAmount}${feeDenom} \ + --from=${GOV1ADDR} --chain-id=${CHAINID} --keyring-backend=test --yes`, + ); + t.like(result, { code: 0 }); + + const [feeCollectorEndBalances, vbankReserveEndBalances] = await getBalances([ + feeCollector, + vbankReserve, + ]); + t.deepEqual(feeCollectorEndBalances, feeCollectorStartBalances); + + // The reserve balances should have increased by exactly the fee (possibly + // from zero, in which case start balances wouldn't include its denomination). + const feeDenomIndex = vbankReserveStartBalances.findIndex( + ({ denom }) => denom === feeDenom, + ); + const preFeeAmount = + feeDenomIndex < 0 + ? 0n + : BigInt(vbankReserveStartBalances[feeDenomIndex].amount); + const beforeCount = + feeDenomIndex < 0 ? vbankReserveStartBalances.length : feeDenomIndex; + + const vbankReserveExpectedBalances = [ + ...vbankReserveStartBalances.slice(0, beforeCount), + { amount: String(preFeeAmount + feeAmount), denom: feeDenom }, + ...vbankReserveStartBalances.slice(beforeCount + 1), + ]; + + t.deepEqual( + vbankReserveEndBalances, + vbankReserveExpectedBalances, + 'vbank/reserve should receive the fee', + ); +}); diff --git a/golang/cosmos/ante/ante.go b/golang/cosmos/ante/ante.go index 4ac88298745..afd84eec92d 100644 --- a/golang/cosmos/ante/ante.go +++ b/golang/cosmos/ante/ante.go @@ -45,15 +45,12 @@ func NewAnteHandler(opts HandlerOptions) (sdk.AnteHandler, error) { anteDecorators := []sdk.AnteDecorator{ ante.NewSetUpContextDecorator(), ante.NewExtensionOptionsDecorator(nil), // reject all extensions - // former ante.NewMempoolFeeDecorator() - // replaced as in https://github.com/provenance-io/provenance/pull/1016 - ante.NewDeductFeeDecorator(opts.AccountKeeper, opts.BankKeeper, opts.FeegrantKeeper, nil), ante.NewValidateBasicDecorator(), ante.NewTxTimeoutHeightDecorator(), ante.NewValidateMemoDecorator(opts.AccountKeeper), ante.NewConsumeGasForTxSizeDecorator(opts.AccountKeeper), NewInboundDecorator(opts.SwingsetKeeper), - NewDeductFeeDecorator(opts.AccountKeeper, opts.BankKeeper, opts.FeegrantKeeper, opts.FeeCollectorName), + ante.NewDeductFeeDecoratorWithName(opts.AccountKeeper, opts.BankKeeper, opts.FeegrantKeeper, nil, opts.FeeCollectorName), // SetPubKeyDecorator must be called before all signature verification decorators ante.NewSetPubKeyDecorator(opts.AccountKeeper), ante.NewValidateSigCountDecorator(opts.AccountKeeper), diff --git a/golang/cosmos/ante/fee.go b/golang/cosmos/ante/fee.go deleted file mode 100644 index b8dedfd2398..00000000000 --- a/golang/cosmos/ante/fee.go +++ /dev/null @@ -1,97 +0,0 @@ -package ante - -import ( - "fmt" - - sdkioerrors "cosmossdk.io/errors" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/auth/types" -) - -// DeductFeeDecorator deducts fees from the first signer of the tx -// If the first signer does not have the funds to pay for the fees, return with InsufficientFunds error -// Call next AnteHandler if fees successfully deducted -// CONTRACT: Tx must implement FeeTx interface to use DeductFeeDecorator -type DeductFeeDecorator struct { - ak AccountKeeper - bankKeeper types.BankKeeper - feegrantKeeper FeegrantKeeper - feeCollectorName string -} - -func NewDeductFeeDecorator(ak AccountKeeper, bk types.BankKeeper, fk FeegrantKeeper, feeCollectorName string) DeductFeeDecorator { - return DeductFeeDecorator{ - ak: ak, - bankKeeper: bk, - feegrantKeeper: fk, - feeCollectorName: feeCollectorName, - } -} - -func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - feeTx, ok := tx.(sdk.FeeTx) - if !ok { - return ctx, sdkioerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") - } - - if addr := dfd.ak.GetModuleAddress(dfd.feeCollectorName); addr == nil { - return ctx, fmt.Errorf("fee collector module account (%s) has not been set", dfd.feeCollectorName) - } - - fee := feeTx.GetFee() - feePayer := feeTx.FeePayer() - feeGranter := feeTx.FeeGranter() - - deductFeesFrom := feePayer - - // if feegranter set deduct fee from feegranter account. - // this works with only when feegrant enabled. - if feeGranter != nil { - if dfd.feegrantKeeper == nil { - return ctx, sdkioerrors.Wrap(sdkerrors.ErrInvalidRequest, "fee grants are not enabled") - } else if !feeGranter.Equals(feePayer) { - err := dfd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, fee, tx.GetMsgs()) - - if err != nil { - return ctx, sdkioerrors.Wrapf(err, "%s not allowed to pay fees from %s", feeGranter, feePayer) - } - } - - deductFeesFrom = feeGranter - } - - deductFeesFromAcc := dfd.ak.GetAccount(ctx, deductFeesFrom) - if deductFeesFromAcc == nil { - return ctx, sdkioerrors.Wrapf(sdkerrors.ErrUnknownAddress, "fee payer address: %s does not exist", deductFeesFrom) - } - - // deduct the fees - if !feeTx.GetFee().IsZero() { - err = DeductFees(dfd.bankKeeper, ctx, deductFeesFromAcc, feeTx.GetFee(), dfd.feeCollectorName) - if err != nil { - return ctx, err - } - } - - events := sdk.Events{sdk.NewEvent(sdk.EventTypeTx, - sdk.NewAttribute(sdk.AttributeKeyFee, feeTx.GetFee().String()), - )} - ctx.EventManager().EmitEvents(events) - - return next(ctx, tx, simulate) -} - -// DeductFees deducts fees from the given account. -func DeductFees(bankKeeper types.BankKeeper, ctx sdk.Context, acc types.AccountI, fees sdk.Coins, feeCollectorName string) error { - if !fees.IsValid() { - return sdkioerrors.Wrapf(sdkerrors.ErrInsufficientFee, "invalid fee amount: %s", fees) - } - - err := bankKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), feeCollectorName, fees) - if err != nil { - return sdkioerrors.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error()) - } - - return nil -} diff --git a/golang/cosmos/go.mod b/golang/cosmos/go.mod index 5ef54f5f2d8..1933f7a8c0d 100644 --- a/golang/cosmos/go.mod +++ b/golang/cosmos/go.mod @@ -187,7 +187,7 @@ replace ( // Agoric-specific replacements: replace ( // We need a fork of cosmos-sdk until all of the differences are merged. - github.com/cosmos/cosmos-sdk => github.com/agoric-labs/cosmos-sdk v0.46.16-alpha.agoric.2 + github.com/cosmos/cosmos-sdk => github.com/agoric-labs/cosmos-sdk v0.46.16-alpha.agoric.2.1 // Async version negotiation github.com/cosmos/ibc-go/v6 => github.com/agoric-labs/ibc-go/v6 v6.2.1-alpha.agoric.3 diff --git a/golang/cosmos/go.sum b/golang/cosmos/go.sum index 7562b04991a..bc5aa648309 100644 --- a/golang/cosmos/go.sum +++ b/golang/cosmos/go.sum @@ -232,8 +232,8 @@ github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBA github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/agoric-labs/cometbft v0.34.30-alpha.agoric.1 h1:tqCNL72pQXdUmBzgv1md5SN2U3K/PaYQ4qZ5pFv8v6w= github.com/agoric-labs/cometbft v0.34.30-alpha.agoric.1/go.mod h1:myvkihZD8eg9jKE3WFaugkNoL5nvEqlP7Jbjg98pCek= -github.com/agoric-labs/cosmos-sdk v0.46.16-alpha.agoric.2 h1:iHHqpYC0JzMbH4UYnQrcwVjLyHJuQphB0ogHbuLz44c= -github.com/agoric-labs/cosmos-sdk v0.46.16-alpha.agoric.2/go.mod h1:zUe5lsg/X7SeSO1nGkzOh9EGKO295szfrxIxYmeLYic= +github.com/agoric-labs/cosmos-sdk v0.46.16-alpha.agoric.2.1 h1:VZFX9Mogwt4cVTnkdt9zA6UJue4XYXdBURNhlTWw71Q= +github.com/agoric-labs/cosmos-sdk v0.46.16-alpha.agoric.2.1/go.mod h1:zUe5lsg/X7SeSO1nGkzOh9EGKO295szfrxIxYmeLYic= github.com/agoric-labs/cosmos-sdk/ics23/go v0.8.0-alpha.agoric.1 h1:2jvHI/2d+psWAZy6FQ0vXJCHUtfU3ZbbW+pQFL04arQ= github.com/agoric-labs/cosmos-sdk/ics23/go v0.8.0-alpha.agoric.1/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= github.com/agoric-labs/ibc-go/v6 v6.2.1-alpha.agoric.3 h1:YqvVwK+Lg/ZsuwyVm9UbPs8K55fg00R3Y9KnmaTBdgc= diff --git a/scripts/generate-a3p-submission-dir.sh b/scripts/generate-a3p-submission-dir.sh index c5636bb7056..cd7266b4f70 100755 --- a/scripts/generate-a3p-submission-dir.sh +++ b/scripts/generate-a3p-submission-dir.sh @@ -1,12 +1,12 @@ #!/bin/bash set -ueo pipefail -SCRIPT_DIR=$( cd ${0%/*} && pwd -P ) +SCRIPT_DIR=$( cd "${0%/*}" && pwd -P ) for proposal in ./proposals/?:* do - cd $proposal - args=`jq -r < package.json '.agoricProposal["sdk-generate"][0]'` - $SCRIPT_DIR/generate-a3p-submission.sh $proposal $args + cd "$proposal" + args=$(jq -r < package.json '.agoricProposal["sdk-generate"][0]') + "$SCRIPT_DIR/generate-a3p-submission.sh" "$proposal" $args cd - done