mirror of
https://github.com/planetmint/planetmint-go.git
synced 2025-03-30 15:08:28 +00:00
wip: trying to add antehandler to e2e test setup, jump to last commit for working TestAttestMachine w/o ante handler in place
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
This commit is contained in:
parent
a67ba84b70
commit
1a144ee5de
77
app/ante/ante.go
Normal file
77
app/ante/ante.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package ante
|
||||||
|
|
||||||
|
import (
|
||||||
|
errorsmod "cosmossdk.io/errors"
|
||||||
|
storetypes "cosmossdk.io/store/types"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||||
|
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||||
|
ante "github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||||
|
|
||||||
|
// authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||||
|
authsigning "cosmossdk.io/x/tx/signing"
|
||||||
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HandlerOptions are the options required for constructing a default SDK AnteHandler.
|
||||||
|
type HandlerOptions struct {
|
||||||
|
AccountKeeper AccountKeeper
|
||||||
|
BankKeeper BankKeeper
|
||||||
|
ExtensionOptionChecker ante.ExtensionOptionChecker
|
||||||
|
FeegrantKeeper FeegrantKeeper
|
||||||
|
SignModeHandler authsigning.SignModeHandler
|
||||||
|
SigGasConsumer func(meter storetypes.GasMeter, sig signing.SignatureV2, params authtypes.Params) error
|
||||||
|
TxFeeChecker TxFeeChecker
|
||||||
|
MachineKeeper MachineKeeper
|
||||||
|
// DaoKeeper DaoKeeper
|
||||||
|
StakingKeeper StakingKeeper
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAnteHandler returns an AnteHandler that checks and increments sequence
|
||||||
|
// numbers, checks signatures & account numbers, and deducts fees from the first
|
||||||
|
// signer.
|
||||||
|
func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
|
||||||
|
if options.AccountKeeper == nil {
|
||||||
|
return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "account keeper is required for ante builder")
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.BankKeeper == nil {
|
||||||
|
return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "bank keeper is required for ante builder")
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.SignModeHandler == nil {
|
||||||
|
return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "sign mode handler is required for ante builder")
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.MachineKeeper == nil {
|
||||||
|
return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "machine keeper is required for ante builder")
|
||||||
|
}
|
||||||
|
// if options.DaoKeeper == nil {
|
||||||
|
// return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "dao keeper is required for ante builder")
|
||||||
|
// }
|
||||||
|
if options.StakingKeeper == nil {
|
||||||
|
return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "staking keeper is required for ante builder")
|
||||||
|
}
|
||||||
|
|
||||||
|
anteDecorators := []sdk.AnteDecorator{
|
||||||
|
// NewSetUpContextDecorator(options.DaoKeeper), // outermost AnteDecorator. SetUpContext must be called first
|
||||||
|
NewSetUpContextDecorator(),
|
||||||
|
ante.NewExtensionOptionsDecorator(options.ExtensionOptionChecker),
|
||||||
|
NewGasKVCostDecorator(options.StakingKeeper),
|
||||||
|
ante.NewValidateBasicDecorator(),
|
||||||
|
ante.NewTxTimeoutHeightDecorator(),
|
||||||
|
ante.NewValidateMemoDecorator(options.AccountKeeper),
|
||||||
|
NewCheckValidatorDecorator(options.StakingKeeper),
|
||||||
|
NewCheckMachineDecorator(options.MachineKeeper),
|
||||||
|
// NewCheckMintAddressDecorator(options.DaoKeeper),
|
||||||
|
ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper),
|
||||||
|
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),
|
||||||
|
ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler),
|
||||||
|
ante.NewIncrementSequenceDecorator(options.AccountKeeper),
|
||||||
|
}
|
||||||
|
|
||||||
|
return sdk.ChainAnteDecorators(anteDecorators...), nil
|
||||||
|
}
|
80
app/ante/check_machine_decorator.go
Normal file
80
app/ante/check_machine_decorator.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package ante
|
||||||
|
|
||||||
|
import (
|
||||||
|
errorsmod "cosmossdk.io/errors"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
|
// assettypes "github.com/planetmint/planetmint-go/x/asset/types"
|
||||||
|
// daotypes "github.com/planetmint/planetmint-go/x/dao/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, err error) {
|
||||||
|
for _, msg := range tx.GetMsgs() {
|
||||||
|
switch sdk.MsgTypeURL(msg) {
|
||||||
|
// case "/planetmintgo.asset.MsgNotarizeAsset":
|
||||||
|
// notarizeMsg, ok := msg.(*assettypes.MsgNotarizeAsset)
|
||||||
|
// if ok {
|
||||||
|
// ctx, err = cm.handleNotarizeAsset(ctx, notarizeMsg)
|
||||||
|
// }
|
||||||
|
case "/planetmintgo.machine.MsgAttestMachine":
|
||||||
|
attestMsg, ok := msg.(*machinetypes.MsgAttestMachine)
|
||||||
|
if ok {
|
||||||
|
ctx, err = cm.handleAttestMachine(ctx, attestMsg)
|
||||||
|
}
|
||||||
|
// case "planetmintgo.dao.MsgReportPoPResult":
|
||||||
|
// popMsg, ok := msg.(*daotypes.MsgReportPopResult)
|
||||||
|
// if ok {
|
||||||
|
// ctx, err = cm.handlePopResult(ctx, popMsg)
|
||||||
|
// }
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ctx, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return next(ctx, tx, simulate)
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (cm CheckMachineDecorator) handleNotarizeAsset(ctx sdk.Context, notarizeMsg *assettypes.MsgNotarizeAsset) (sdk.Context, error) {
|
||||||
|
// _, found := cm.mk.GetMachineIndexByAddress(ctx, notarizeMsg.GetCreator())
|
||||||
|
// if !found {
|
||||||
|
// return ctx, errorsmod.Wrapf(machinetypes.ErrMachineNotFound, ErrorAnteContext)
|
||||||
|
// }
|
||||||
|
// return ctx, nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (cm CheckMachineDecorator) handleAttestMachine(ctx sdk.Context, attestMsg *machinetypes.MsgAttestMachine) (sdk.Context, error) {
|
||||||
|
if attestMsg.GetCreator() != attestMsg.Machine.GetAddress() {
|
||||||
|
return ctx, errorsmod.Wrapf(machinetypes.ErrMachineIsNotCreator, ErrorAnteContext)
|
||||||
|
}
|
||||||
|
_, activated, found := cm.mk.GetTrustAnchor(ctx, attestMsg.Machine.MachineId)
|
||||||
|
if !found {
|
||||||
|
return ctx, errorsmod.Wrapf(machinetypes.ErrTrustAnchorNotFound, ErrorAnteContext)
|
||||||
|
}
|
||||||
|
if activated {
|
||||||
|
return ctx, errorsmod.Wrapf(machinetypes.ErrTrustAnchorAlreadyInUse, ErrorAnteContext)
|
||||||
|
}
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (cm CheckMachineDecorator) handlePopResult(ctx sdk.Context, popMsg *daotypes.MsgReportPopResult) (sdk.Context, error) {
|
||||||
|
// _, found := cm.mk.GetMachineIndexByAddress(ctx, popMsg.GetCreator())
|
||||||
|
// if !found {
|
||||||
|
// return ctx, errorsmod.Wrapf(machinetypes.ErrMachineNotFound, ErrorAnteContext)
|
||||||
|
// }
|
||||||
|
// return ctx, nil
|
||||||
|
// }
|
38
app/ante/check_mint_address_decorator.go
Normal file
38
app/ante/check_mint_address_decorator.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package ante
|
||||||
|
|
||||||
|
// import (
|
||||||
|
// errorsmod "cosmossdk.io/errors"
|
||||||
|
// sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
// daotypes "github.com/planetmint/planetmint-go/x/dao/types"
|
||||||
|
// )
|
||||||
|
|
||||||
|
// type CheckMintAddressDecorator struct {
|
||||||
|
// dk DaoKeeper
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func NewCheckMintAddressDecorator(dk DaoKeeper) CheckMintAddressDecorator {
|
||||||
|
// return CheckMintAddressDecorator{
|
||||||
|
// dk: dk,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (cmad CheckMintAddressDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
|
||||||
|
// for _, msg := range tx.GetMsgs() {
|
||||||
|
// if sdk.MsgTypeURL(msg) != "/planetmintgo.dao.MsgMintToken" {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// mintMsg, ok := msg.(*daotypes.MsgMintToken)
|
||||||
|
// if !ok {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// if mintMsg.Creator != cmad.dk.GetMintAddress(ctx) {
|
||||||
|
// return ctx, errorsmod.Wrapf(daotypes.ErrInvalidMintAddress, "expected: %s; got: %s", cmad.dk.GetMintAddress(ctx), mintMsg.Creator)
|
||||||
|
// }
|
||||||
|
// _, found := cmad.dk.GetMintRequestByHash(ctx, mintMsg.GetMintRequest().GetLiquidTxHash())
|
||||||
|
// if found {
|
||||||
|
// return ctx, errorsmod.Wrapf(daotypes.ErrAlreadyMinted, "liquid tx hash %s has already been minted", mintMsg.GetMintRequest().GetLiquidTxHash())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return next(ctx, tx, simulate)
|
||||||
|
// }
|
64
app/ante/check_validator_decorator.go
Normal file
64
app/ante/check_validator_decorator.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package ante
|
||||||
|
|
||||||
|
import (
|
||||||
|
errorsmod "cosmossdk.io/errors"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
// "github.com/planetmint/planetmint-go/x/dao/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CheckValidatorDecorator struct {
|
||||||
|
sk StakingKeeper
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCheckValidatorDecorator(sk StakingKeeper) CheckValidatorDecorator {
|
||||||
|
return CheckValidatorDecorator{sk: sk}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cv CheckValidatorDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (_ sdk.Context, err error) {
|
||||||
|
if simulate || ctx.BlockHeight() == 0 {
|
||||||
|
return next(ctx, tx, simulate)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, msg := range tx.GetMsgs() {
|
||||||
|
switch sdk.MsgTypeURL(msg) {
|
||||||
|
case "/planetmintgo.dao.MsgInitPop":
|
||||||
|
fallthrough
|
||||||
|
case "/planetmintgo.dao.MsgDistributionRequest":
|
||||||
|
fallthrough
|
||||||
|
case "/planetmintgo.dao.MsgDistributionResult":
|
||||||
|
fallthrough
|
||||||
|
case "/planetmintgo.dao.MsgReissueRDDLProposal":
|
||||||
|
fallthrough
|
||||||
|
case "/planetmintgo.dao.MsgReissueRDDLResult":
|
||||||
|
fallthrough
|
||||||
|
case "/planetmintgo.dao.MsgUpdateRedeemClaim":
|
||||||
|
fallthrough
|
||||||
|
case "/planetmintgo.machine.MsgNotarizeLiquidAsset":
|
||||||
|
fallthrough
|
||||||
|
case "/planetmintgo.machine.MsgRegisterTrustAnchor":
|
||||||
|
ctx, err = cv.handleMsg(ctx, msg)
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ctx, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return next(ctx, tx, simulate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cv CheckValidatorDecorator) handleMsg(ctx sdk.Context, msg sdk.Msg) (_ sdk.Context, err error) {
|
||||||
|
signatureMsg, ok := msg.(sdk.Signature)
|
||||||
|
if !ok {
|
||||||
|
return ctx, errorsmod.Error{}
|
||||||
|
}
|
||||||
|
signer := signatureMsg.GetPubKey().Address()
|
||||||
|
_, found := cv.sk.GetValidator(ctx, sdk.ValAddress(signer))
|
||||||
|
if !found {
|
||||||
|
return ctx, errorsmod.Error{}
|
||||||
|
// return ctx, errorsmod.Wrapf(types.ErrRestrictedMsg, ErrorAnteContext)
|
||||||
|
}
|
||||||
|
return ctx, nil
|
||||||
|
}
|
175
app/ante/deduct_fee_decorator.go
Normal file
175
app/ante/deduct_fee_decorator.go
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
package ante
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
errorsmod "cosmossdk.io/errors"
|
||||||
|
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, ErrorTxFeeTx)
|
||||||
|
}
|
||||||
|
|
||||||
|
feeCoins := feeTx.GetFee()
|
||||||
|
|
||||||
|
if !ctx.IsCheckTx() {
|
||||||
|
return feeCoins, nil
|
||||||
|
}
|
||||||
|
minGasPrices := ctx.MinGasPrices()
|
||||||
|
if minGasPrices.IsZero() {
|
||||||
|
return feeCoins, nil
|
||||||
|
}
|
||||||
|
feeDenoms := feeCoins.Denoms()
|
||||||
|
if len(feeDenoms) != 1 {
|
||||||
|
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidCoins, "fee must be exactly one coin; got: %s", feeDenoms)
|
||||||
|
}
|
||||||
|
|
||||||
|
gasDenom := minGasPrices.GetDenomByIndex(0)
|
||||||
|
if slices.Contains(feeDenoms, gasDenom) {
|
||||||
|
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidCoins, "received wrong fee denom; got: %s required: %s", feeDenoms[0], gasDenom)
|
||||||
|
}
|
||||||
|
|
||||||
|
requiredFees := sdk.Coins{sdk.NewInt64Coin(gasDenom, 1)}
|
||||||
|
|
||||||
|
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, ErrorTxFeeTx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !simulate && ctx.BlockHeight() > 0 && feeTx.GetGas() == 0 {
|
||||||
|
return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidGasLimit, "must provide positive gas")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
msgs := tx.GetMsgs()
|
||||||
|
if len(msgs) == 1 && sdk.MsgTypeURL(msgs[0]) == "/planetmintgo.machine.MsgAttestMachine" {
|
||||||
|
return next(ctx, tx, simulate)
|
||||||
|
}
|
||||||
|
|
||||||
|
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, ErrorTxFeeTx)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 !bytes.Equal(feeGranter, 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, sdk.AccAddress(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := bankKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), authtypes.FeeCollectorName, fees)
|
||||||
|
if err != nil {
|
||||||
|
return errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
6
app/ante/error.go
Normal file
6
app/ante/error.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package ante
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrorAnteContext = "error during CheckTx or ReCheckTx"
|
||||||
|
ErrorTxFeeTx = "Tx must be a FeeTx"
|
||||||
|
)
|
51
app/ante/expected_keepers.go
Normal file
51
app/ante/expected_keepers.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package ante
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/planetmint/planetmint-go/x/machine/types"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||||
|
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||||
|
// daotypes "github.com/planetmint/planetmint-go/x/dao/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MachineKeeper interface {
|
||||||
|
GetMachineIndexByPubKey(ctx sdk.Context, pubKey string) (val types.MachineIndex, found bool)
|
||||||
|
GetMachineIndexByAddress(ctx sdk.Context, address string) (val types.MachineIndex, found bool)
|
||||||
|
GetTrustAnchor(ctx sdk.Context, pubKey string) (val types.TrustAnchor, activated bool, found bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountKeeper defines the contract needed for AccountKeeper related APIs.
|
||||||
|
// Interface provides support to use non-sdk AccountKeeper for AnteHandler's decorators.
|
||||||
|
type AccountKeeper interface {
|
||||||
|
GetParams(ctx sdk.Context) (params authtypes.Params)
|
||||||
|
GetAccount(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI
|
||||||
|
SetAccount(ctx sdk.Context, acc authtypes.AccountI)
|
||||||
|
GetModuleAddress(moduleName string) sdk.AccAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
// FeegrantKeeper defines the expected feegrant keeper.
|
||||||
|
type FeegrantKeeper interface {
|
||||||
|
UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins, msgs []sdk.Msg) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type BankKeeper interface {
|
||||||
|
IsSendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error
|
||||||
|
SendCoins(ctx sdk.Context, from, to sdk.AccAddress, amt sdk.Coins) error
|
||||||
|
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
|
||||||
|
GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
|
||||||
|
}
|
||||||
|
|
||||||
|
// type DaoKeeper interface {
|
||||||
|
// GetMintRequestByHash(ctx sdk.Context, hash string) (val daotypes.MintRequest, found bool)
|
||||||
|
// GetMintAddress(ctx sdk.Context) (mintAddress string)
|
||||||
|
// GetTxGasLimit(ctx sdk.Context) (txGasLimit uint64)
|
||||||
|
// GetClaimAddress(ctx sdk.Context) (claimAddress string)
|
||||||
|
// IsValidReissuanceProposal(ctx sdk.Context, msg *daotypes.MsgReissueRDDLProposal) (isValid bool)
|
||||||
|
// GetRedeemClaim(ctx sdk.Context, benficiary string, id uint64) (val daotypes.RedeemClaim, found bool)
|
||||||
|
// GetParams(ctx sdk.Context) (params daotypes.Params)
|
||||||
|
// }
|
||||||
|
|
||||||
|
type StakingKeeper interface {
|
||||||
|
GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator stakingtypes.Validator, found bool)
|
||||||
|
}
|
50
app/ante/gaskv_cost_decorator.go
Normal file
50
app/ante/gaskv_cost_decorator.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package ante
|
||||||
|
|
||||||
|
import (
|
||||||
|
errorsmod "cosmossdk.io/errors"
|
||||||
|
storetypes "cosmossdk.io/store/types"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GasKVCostDecorator struct {
|
||||||
|
sk StakingKeeper
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGasKVCostDecorator(sk StakingKeeper) GasKVCostDecorator {
|
||||||
|
return GasKVCostDecorator{sk: sk}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gc GasKVCostDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (_ sdk.Context, err error) {
|
||||||
|
if simulate || ctx.BlockHeight() == 0 {
|
||||||
|
return next(ctx, tx, simulate)
|
||||||
|
}
|
||||||
|
|
||||||
|
msgs := tx.GetMsgs()
|
||||||
|
|
||||||
|
signatureMsg, ok := msgs[0].(sdk.Signature)
|
||||||
|
if !ok {
|
||||||
|
return ctx, errorsmod.Error{}
|
||||||
|
}
|
||||||
|
signer := signatureMsg.GetPubKey().Address()
|
||||||
|
// signers := msgs[0].GetSigners()
|
||||||
|
// signer := signers[0]
|
||||||
|
|
||||||
|
valAddr := sdk.ValAddress(signer)
|
||||||
|
_, found := gc.sk.GetValidator(ctx, valAddr)
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return next(ctx, tx, simulate)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = ctx.WithKVGasConfig(storetypes.GasConfig{
|
||||||
|
HasCost: 0,
|
||||||
|
DeleteCost: 0,
|
||||||
|
ReadCostFlat: 0,
|
||||||
|
ReadCostPerByte: 0,
|
||||||
|
WriteCostFlat: 0,
|
||||||
|
WriteCostPerByte: 0,
|
||||||
|
IterNextCostFlat: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
return next(ctx, tx, simulate)
|
||||||
|
}
|
92
app/ante/setup_context_decorator.go
Normal file
92
app/ante/setup_context_decorator.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package ante
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
errorsmod "cosmossdk.io/errors"
|
||||||
|
storetypes "cosmossdk.io/store/types"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GasTx defines a Tx with a GetGas() method which is needed to use SetUpContextDecorator
|
||||||
|
type GasTx interface {
|
||||||
|
sdk.Tx
|
||||||
|
GetGas() uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUpContextDecorator sets the GasMeter in the Context and wraps the next AnteHandler with a defer clause
|
||||||
|
// to recover from any downstream OutOfGas panics in the AnteHandler chain to return an error with information
|
||||||
|
// on gas provided and gas used.
|
||||||
|
// CONTRACT: Must be first decorator in the chain
|
||||||
|
// CONTRACT: Tx must implement GasTx interface
|
||||||
|
type SetUpContextDecorator struct {
|
||||||
|
// dk DaoKeeper
|
||||||
|
}
|
||||||
|
|
||||||
|
// func NewSetUpContextDecorator(dk DaoKeeper) SetUpContextDecorator {
|
||||||
|
func NewSetUpContextDecorator() SetUpContextDecorator {
|
||||||
|
return SetUpContextDecorator{
|
||||||
|
// dk: dk,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sud SetUpContextDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
|
||||||
|
// all transactions must implement GasTx
|
||||||
|
gasTx, ok := tx.(GasTx)
|
||||||
|
if !ok {
|
||||||
|
// Set a gas meter with limit 0 as to prevent an infinite gas meter attack
|
||||||
|
// during runTx.
|
||||||
|
newCtx = SetGasMeter(simulate, ctx, 0)
|
||||||
|
return newCtx, errorsmod.Wrapf(sdkerrors.ErrTxDecode, "Tx must be GasTx")
|
||||||
|
}
|
||||||
|
|
||||||
|
// gasLimit := sud.dk.GetTxGasLimit(ctx)
|
||||||
|
// if gasLimit == 0 {
|
||||||
|
// gasLimit = gasTx.GetGas()
|
||||||
|
// }
|
||||||
|
gasLimit := gasTx.GetGas()
|
||||||
|
|
||||||
|
newCtx = SetGasMeter(simulate, ctx, gasLimit)
|
||||||
|
|
||||||
|
if cp := ctx.ConsensusParams(); cp != nil && cp.Block != nil {
|
||||||
|
// If there exists a maximum block gas limit, we must ensure that the tx
|
||||||
|
// does not exceed it.
|
||||||
|
if cp.Block.MaxGas > 0 && gasTx.GetGas() > uint64(cp.Block.MaxGas) {
|
||||||
|
return newCtx, errorsmod.Wrapf(sdkerrors.ErrInvalidGasLimit, "tx gas limit %d exceeds block max gas %d", gasTx.GetGas(), cp.Block.MaxGas)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decorator will catch an OutOfGasPanic caused in the next antehandler
|
||||||
|
// AnteHandlers must have their own defer/recover in order for the BaseApp
|
||||||
|
// to know how much gas was used! This is because the GasMeter is created in
|
||||||
|
// the AnteHandler, but if it panics the context won't be set properly in
|
||||||
|
// runTx's recover call.
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
switch rType := r.(type) {
|
||||||
|
case storetypes.ErrorOutOfGas:
|
||||||
|
log := fmt.Sprintf(
|
||||||
|
"out of gas in location: %v; gasWanted: %d, gasUsed: %d",
|
||||||
|
rType.Descriptor, gasTx.GetGas(), newCtx.GasMeter().GasConsumed())
|
||||||
|
|
||||||
|
err = errorsmod.Wrapf(sdkerrors.ErrOutOfGas, log)
|
||||||
|
default:
|
||||||
|
panic(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return next(newCtx, tx, simulate)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGasMeter returns a new context with a gas meter set from a given context.
|
||||||
|
func SetGasMeter(simulate bool, ctx sdk.Context, gasLimit uint64) sdk.Context {
|
||||||
|
// In various cases such as simulation and during the genesis block, we do not
|
||||||
|
// meter any gas utilization.
|
||||||
|
if simulate || ctx.BlockHeight() == 0 {
|
||||||
|
return ctx.WithGasMeter(storetypes.NewInfiniteGasMeter())
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.WithGasMeter(storetypes.NewGasMeter(gasLimit))
|
||||||
|
}
|
@ -241,8 +241,10 @@ var (
|
|||||||
Config: appconfig.WrapAny(¶msmodulev1.Module{}),
|
Config: appconfig.WrapAny(¶msmodulev1.Module{}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "tx",
|
Name: "tx",
|
||||||
Config: appconfig.WrapAny(&txconfigv1.Config{}),
|
Config: appconfig.WrapAny(&txconfigv1.Config{
|
||||||
|
SkipAnteHandler: true,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: genutiltypes.ModuleName,
|
Name: genutiltypes.ModuleName,
|
||||||
|
@ -3,17 +3,72 @@ package machine
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"cosmossdk.io/depinject"
|
||||||
|
dbm "github.com/cosmos/cosmos-db"
|
||||||
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||||
|
"github.com/cosmos/cosmos-sdk/runtime"
|
||||||
"github.com/cosmos/cosmos-sdk/testutil/network"
|
"github.com/cosmos/cosmos-sdk/testutil/network"
|
||||||
|
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||||
"github.com/planetmint/planetmint-go/app"
|
"github.com/planetmint/planetmint-go/app"
|
||||||
|
"github.com/planetmint/planetmint-go/app/ante"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
|
pruningtypes "cosmossdk.io/store/pruning/types"
|
||||||
|
servertypes "github.com/cosmos/cosmos-sdk/server/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
cfg network.Config
|
||||||
|
appConfig depinject.Config
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestE2EMachineTestSuite(t *testing.T) {
|
func TestE2EMachineTestSuite(t *testing.T) {
|
||||||
cfg, err := network.DefaultConfigWithAppConfig(app.AppConfig())
|
appConfig = app.AppConfig()
|
||||||
|
var err error
|
||||||
|
cfg, err = network.DefaultConfigWithAppConfig(appConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("error while setting up application config")
|
panic("error while setting up application config")
|
||||||
}
|
}
|
||||||
cfg.NumValidators = 3
|
cfg.NumValidators = 3
|
||||||
cfg.MinGasPrices = "0.000003stake"
|
cfg.MinGasPrices = "0.000003stake"
|
||||||
|
|
||||||
|
cfg.AppConstructor = appConstructor
|
||||||
|
|
||||||
suite.Run(t, NewE2ETestSuite(cfg))
|
suite.Run(t, NewE2ETestSuite(cfg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func appConstructor(val network.ValidatorI) servertypes.Application {
|
||||||
|
// we build a unique app instance for every validator here
|
||||||
|
var appBuilder *runtime.AppBuilder
|
||||||
|
if err := depinject.Inject(
|
||||||
|
depinject.Configs(
|
||||||
|
appConfig,
|
||||||
|
depinject.Supply(val.GetCtx().Logger),
|
||||||
|
),
|
||||||
|
&appBuilder); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
app := appBuilder.Build(
|
||||||
|
dbm.NewMemDB(),
|
||||||
|
nil,
|
||||||
|
baseapp.SetPruning(pruningtypes.NewPruningOptionsFromString(val.GetAppConfig().Pruning)),
|
||||||
|
baseapp.SetMinGasPrices(val.GetAppConfig().MinGasPrices),
|
||||||
|
baseapp.SetChainID(cfg.ChainID),
|
||||||
|
)
|
||||||
|
|
||||||
|
testdata.RegisterQueryServer(app.GRPCQueryRouter(), testdata.QueryImpl{})
|
||||||
|
|
||||||
|
if err := app.Load(true); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
anteOpts := ante.HandlerOptions{}
|
||||||
|
anteHandler, err := ante.NewAnteHandler(anteOpts)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.SetAnteHandler(anteHandler)
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user