mirror of
https://github.com/planetmint/planetmint-go.git
synced 2025-06-05 13:46:38 +00:00
100 set fees to be 1 plmnt for a tx (#114)
* replace DeductFeeDecorator and adjust e2e tests * reorganize decorators and add comments --------- Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
This commit is contained in:
parent
e80ce7e894
commit
94022e1102
@ -1,9 +1,6 @@
|
||||
package ante
|
||||
|
||||
import (
|
||||
assettypes "github.com/planetmint/planetmint-go/x/asset/types"
|
||||
machinetypes "github.com/planetmint/planetmint-go/x/machine/types"
|
||||
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
@ -13,49 +10,6 @@ import (
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
)
|
||||
|
||||
type CheckMachineDecorator struct {
|
||||
mk MachineKeeper
|
||||
}
|
||||
|
||||
func NewCheckMachineDecorator(mk MachineKeeper) CheckMachineDecorator {
|
||||
return CheckMachineDecorator{
|
||||
mk: mk,
|
||||
}
|
||||
}
|
||||
|
||||
func (cm CheckMachineDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
|
||||
for _, msg := range tx.GetMsgs() {
|
||||
switch sdk.MsgTypeURL(msg) {
|
||||
case "/planetmintgo.asset.MsgNotarizeAsset":
|
||||
notarizeMsg, ok := msg.(*assettypes.MsgNotarizeAsset)
|
||||
if ok {
|
||||
_, found := cm.mk.GetMachineIndexByAddress(ctx, notarizeMsg.GetCreator())
|
||||
if !found {
|
||||
return ctx, errorsmod.Wrapf(machinetypes.ErrMachineNotFound, "error during CheckTx or ReCheckTx")
|
||||
}
|
||||
}
|
||||
case "/planetmintgo.machine.MsgAttestMachine":
|
||||
attestMsg, ok := msg.(*machinetypes.MsgAttestMachine)
|
||||
if ok {
|
||||
if attestMsg.GetCreator() != attestMsg.Machine.GetAddress() {
|
||||
return ctx, errorsmod.Wrapf(machinetypes.ErrMachineIsNotCreator, "error during CheckTx or ReCheckTx")
|
||||
}
|
||||
_, activated, found := cm.mk.GetTrustAnchor(ctx, attestMsg.Machine.MachineId)
|
||||
if !found {
|
||||
return ctx, errorsmod.Wrapf(machinetypes.ErrTrustAnchorNotFound, "error during CheckTx or ReCheckTx")
|
||||
}
|
||||
if activated {
|
||||
return ctx, errorsmod.Wrapf(machinetypes.ErrTrustAnchorAlreadyInUse, "error during CheckTx or ReCheckTx")
|
||||
}
|
||||
}
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
// HandlerOptions are the options required for constructing a default SDK AnteHandler.
|
||||
type HandlerOptions struct {
|
||||
AccountKeeper AccountKeeper
|
||||
@ -64,7 +18,7 @@ type HandlerOptions struct {
|
||||
FeegrantKeeper FeegrantKeeper
|
||||
SignModeHandler authsigning.SignModeHandler
|
||||
SigGasConsumer func(meter sdk.GasMeter, sig signing.SignatureV2, params authtypes.Params) error
|
||||
TxFeeChecker ante.TxFeeChecker
|
||||
TxFeeChecker TxFeeChecker
|
||||
MachineKeeper MachineKeeper
|
||||
}
|
||||
|
||||
@ -96,7 +50,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
|
||||
ante.NewValidateMemoDecorator(options.AccountKeeper),
|
||||
NewCheckMachineDecorator(options.MachineKeeper),
|
||||
ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper),
|
||||
ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TxFeeChecker),
|
||||
NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TxFeeChecker),
|
||||
ante.NewSetPubKeyDecorator(options.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators
|
||||
ante.NewValidateSigCountDecorator(options.AccountKeeper),
|
||||
ante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer),
|
||||
|
51
app/ante/check_machine_decorator.go
Normal file
51
app/ante/check_machine_decorator.go
Normal file
@ -0,0 +1,51 @@
|
||||
package ante
|
||||
|
||||
import (
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
assettypes "github.com/planetmint/planetmint-go/x/asset/types"
|
||||
machinetypes "github.com/planetmint/planetmint-go/x/machine/types"
|
||||
)
|
||||
|
||||
type CheckMachineDecorator struct {
|
||||
mk MachineKeeper
|
||||
}
|
||||
|
||||
func NewCheckMachineDecorator(mk MachineKeeper) CheckMachineDecorator {
|
||||
return CheckMachineDecorator{
|
||||
mk: mk,
|
||||
}
|
||||
}
|
||||
|
||||
func (cm CheckMachineDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
|
||||
for _, msg := range tx.GetMsgs() {
|
||||
switch sdk.MsgTypeURL(msg) {
|
||||
case "/planetmintgo.asset.MsgNotarizeAsset":
|
||||
notarizeMsg, ok := msg.(*assettypes.MsgNotarizeAsset)
|
||||
if ok {
|
||||
_, found := cm.mk.GetMachineIndexByAddress(ctx, notarizeMsg.GetCreator())
|
||||
if !found {
|
||||
return ctx, errorsmod.Wrapf(machinetypes.ErrMachineNotFound, "error during CheckTx or ReCheckTx")
|
||||
}
|
||||
}
|
||||
case "/planetmintgo.machine.MsgAttestMachine":
|
||||
attestMsg, ok := msg.(*machinetypes.MsgAttestMachine)
|
||||
if ok {
|
||||
if attestMsg.GetCreator() != attestMsg.Machine.GetAddress() {
|
||||
return ctx, errorsmod.Wrapf(machinetypes.ErrMachineIsNotCreator, "error during CheckTx or ReCheckTx")
|
||||
}
|
||||
_, activated, found := cm.mk.GetTrustAnchor(ctx, attestMsg.Machine.MachineId)
|
||||
if !found {
|
||||
return ctx, errorsmod.Wrapf(machinetypes.ErrTrustAnchorNotFound, "error during CheckTx or ReCheckTx")
|
||||
}
|
||||
if activated {
|
||||
return ctx, errorsmod.Wrapf(machinetypes.ErrTrustAnchorAlreadyInUse, "error during CheckTx or ReCheckTx")
|
||||
}
|
||||
}
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
172
app/ante/deduct_fee_decorator.go
Normal file
172
app/ante/deduct_fee_decorator.go
Normal file
@ -0,0 +1,172 @@
|
||||
package ante
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
sdkmath "cosmossdk.io/math"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
)
|
||||
|
||||
// TxFeeChecker check if the provided fee is enough and returns the effective fee and tx priority,
|
||||
// the effective fee should be deducted later, and the priority should be returned in abci response.
|
||||
type TxFeeChecker func(ctx sdk.Context, tx sdk.Tx) (sdk.Coins, error)
|
||||
|
||||
// DeductFeeDecorator deducts fees from the fee payer. The fee payer is the fee granter (if specified) or first signer of the tx.
|
||||
// If the fee payer does not have the funds to pay for the fees, return an InsufficientFunds error.
|
||||
// Call next AnteHandler if fees successfully deducted.
|
||||
// CONTRACT: Tx must implement FeeTx interface to use DeductFeeDecorator
|
||||
type DeductFeeDecorator struct {
|
||||
accountKeeper AccountKeeper
|
||||
bankKeeper authtypes.BankKeeper
|
||||
feegrantKeeper FeegrantKeeper
|
||||
txFeeChecker TxFeeChecker
|
||||
}
|
||||
|
||||
func NewDeductFeeDecorator(ak AccountKeeper, bk authtypes.BankKeeper, fk FeegrantKeeper, tfc TxFeeChecker) DeductFeeDecorator {
|
||||
if tfc == nil {
|
||||
tfc = checkTxFee
|
||||
}
|
||||
|
||||
return DeductFeeDecorator{
|
||||
accountKeeper: ak,
|
||||
bankKeeper: bk,
|
||||
feegrantKeeper: fk,
|
||||
txFeeChecker: tfc,
|
||||
}
|
||||
}
|
||||
|
||||
func checkTxFee(ctx sdk.Context, tx sdk.Tx) (sdk.Coins, error) {
|
||||
feeTx, ok := tx.(sdk.FeeTx)
|
||||
if !ok {
|
||||
return nil, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
|
||||
}
|
||||
|
||||
feeCoins := feeTx.GetFee()
|
||||
gas := feeTx.GetGas()
|
||||
|
||||
if ctx.IsCheckTx() {
|
||||
// min-gas-prices is used to determine which coins are to be deducted and if they are provided as fee
|
||||
minGasPrices := ctx.MinGasPrices()
|
||||
if !minGasPrices.IsZero() {
|
||||
requiredFees := make(sdk.Coins, len(minGasPrices))
|
||||
|
||||
// Determine the required fees by multiplying each required minimum gas
|
||||
// price by the gas limit, where fee = ceil(minGasPrice * gasLimit).
|
||||
glDec := sdkmath.LegacyNewDec(int64(gas))
|
||||
for i, gp := range minGasPrices {
|
||||
fee := gp.Amount.Mul(glDec)
|
||||
requiredFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt())
|
||||
}
|
||||
|
||||
if !feeCoins.IsAnyGTE(requiredFees) {
|
||||
return nil, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, requiredFees)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return feeCoins, nil
|
||||
}
|
||||
|
||||
func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
|
||||
feeTx, ok := tx.(sdk.FeeTx)
|
||||
if !ok {
|
||||
return ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
|
||||
}
|
||||
|
||||
if !simulate && ctx.BlockHeight() > 0 && feeTx.GetGas() == 0 {
|
||||
return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidGasLimit, "must provide positive gas")
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
)
|
||||
|
||||
fee := feeTx.GetFee()
|
||||
if !simulate {
|
||||
fee, err = dfd.txFeeChecker(ctx, tx)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
}
|
||||
if err := dfd.checkDeductFee(ctx, tx, fee); err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
func (dfd DeductFeeDecorator) checkDeductFee(ctx sdk.Context, sdkTx sdk.Tx, fee sdk.Coins) error {
|
||||
feeTx, ok := sdkTx.(sdk.FeeTx)
|
||||
if !ok {
|
||||
return errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
|
||||
}
|
||||
|
||||
if addr := dfd.accountKeeper.GetModuleAddress(authtypes.FeeCollectorName); addr == nil {
|
||||
return fmt.Errorf("fee collector module account (%s) has not been set", authtypes.FeeCollectorName)
|
||||
}
|
||||
|
||||
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 sdkerrors.ErrInvalidRequest.Wrap("fee grants are not enabled")
|
||||
} else if !feeGranter.Equals(feePayer) {
|
||||
err := dfd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, fee, sdkTx.GetMsgs())
|
||||
if err != nil {
|
||||
return errorsmod.Wrapf(err, "%s does not allow to pay fees for %s", feeGranter, feePayer)
|
||||
}
|
||||
}
|
||||
|
||||
deductFeesFrom = feeGranter
|
||||
}
|
||||
|
||||
deductFeesFromAcc := dfd.accountKeeper.GetAccount(ctx, deductFeesFrom)
|
||||
if deductFeesFromAcc == nil {
|
||||
return sdkerrors.ErrUnknownAddress.Wrapf("fee payer address: %s does not exist", deductFeesFrom)
|
||||
}
|
||||
|
||||
// deduct the fees
|
||||
if !fee.IsZero() {
|
||||
err := dfd.deductFees(dfd.bankKeeper, ctx, deductFeesFromAcc, fee)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
events := sdk.Events{
|
||||
sdk.NewEvent(
|
||||
sdk.EventTypeTx,
|
||||
sdk.NewAttribute(sdk.AttributeKeyFee, fee.String()),
|
||||
sdk.NewAttribute(sdk.AttributeKeyFeePayer, deductFeesFrom.String()),
|
||||
),
|
||||
}
|
||||
ctx.EventManager().EmitEvents(events)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeductFees deducts fees from the given account.
|
||||
func (dfd DeductFeeDecorator) deductFees(bankKeeper authtypes.BankKeeper, ctx sdk.Context, acc authtypes.AccountI, fees sdk.Coins) error {
|
||||
// check if exactly one fee is provided and is greater than 0
|
||||
if !fees.IsValid() && len(fees) == 1 {
|
||||
return errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "invalid fee amount: %s", fees)
|
||||
}
|
||||
|
||||
// take exactly on token as fee no matter what is provided
|
||||
denoms := fees.Denoms()
|
||||
feesToCollect := sdk.Coins{sdk.NewCoin(denoms[0], sdk.OneInt())}
|
||||
|
||||
err := bankKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), authtypes.FeeCollectorName, feesToCollect)
|
||||
if err != nil {
|
||||
return errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -2,6 +2,7 @@ package dao
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/planetmint/planetmint-go/config"
|
||||
"github.com/planetmint/planetmint-go/testutil/network"
|
||||
"github.com/planetmint/planetmint-go/testutil/sample"
|
||||
@ -119,17 +120,18 @@ func (s *E2ETestSuite) TestDistributeCollectedFees() {
|
||||
err = s.network.WaitForNextBlock()
|
||||
s.Require().NoError(err)
|
||||
|
||||
// assert that alice has 6 of 20 paid fee tokens based on 5000 stake of 15000 total stake
|
||||
// assert that alice has 0 of 2 paid fee tokens based on 5000 stake of 15000 total stake
|
||||
out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, bank.GetBalancesCmd(), []string{
|
||||
aliceAddr.String(),
|
||||
})
|
||||
assert.Contains(s.T(), out.String(), "6")
|
||||
assert.NotContains(s.T(), out.String(), "node0token")
|
||||
s.Require().NoError(err)
|
||||
|
||||
// assert that bob has 13 of 20 paid fee tokens based on 10000 stake of 15000 total stake
|
||||
// assert that bob has 1 of 2 paid fee tokens based on 10000 stake of 15000 total stake
|
||||
out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, bank.GetBalancesCmd(), []string{
|
||||
bobAddr.String(),
|
||||
})
|
||||
assert.Contains(s.T(), out.String(), "13")
|
||||
assert.Contains(s.T(), out.String(), "1")
|
||||
assert.Contains(s.T(), out.String(), "node0token")
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ func DefaultConfig() network.Config {
|
||||
ChainID: chainID,
|
||||
NumValidators: 1,
|
||||
BondDenom: sdk.DefaultBondDenom,
|
||||
MinGasPrices: fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom),
|
||||
MinGasPrices: fmt.Sprintf("0.000003%s", sdk.DefaultBondDenom),
|
||||
AccountTokens: sdk.TokensFromConsensusPower(1000, sdk.DefaultPowerReduction),
|
||||
StakingTokens: sdk.TokensFromConsensusPower(500, sdk.DefaultPowerReduction),
|
||||
BondedTokens: sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction),
|
||||
|
@ -29,7 +29,7 @@ const Name = "machine"
|
||||
const Amount = "1000stake"
|
||||
|
||||
// Fees is the amount of fees to use in tests
|
||||
const Fees = "2stake"
|
||||
const Fees = "1stake"
|
||||
|
||||
// DefaultDerivationPath is the BIP44Prefix for PLMNT (see https://github.com/satoshilabs/slips/blob/master/slip-0044.md)
|
||||
const DefaultDerivationPath = "m/44'/8680'/0'/0/0"
|
||||
|
Loading…
x
Reference in New Issue
Block a user