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

feat(ante): skip fees for IBC messages #127

Merged
merged 5 commits into from
Sep 16, 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
23 changes: 8 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export HUB_RPC_ENDPOINT="http://localhost"
export HUB_RPC_PORT="36657" # default: 36657
export HUB_RPC_URL="${HUB_RPC_ENDPOINT}:${HUB_RPC_PORT}"
export HUB_CHAIN_ID="dymension_100-1"
export HUB_REST_URL="localhost:1318" # required for relayer

dymd config chain-id ${HUB_CHAIN_ID}
dymd config node ${HUB_RPC_URL}
Expand Down Expand Up @@ -140,7 +141,7 @@ NEW_NUMERIC_PART=$(echo "$NUMERIC_PART + 100000000000000000000" | bc)
# Append 'adym' back
TRANSFER_AMOUNT="${NEW_NUMERIC_PART}adym"

dymd tx bank send $HUB_KEY_WITH_FUNDS $SEQUENCER_ADDR ${TRANSFER_AMOUNT} --keyring-backend test --broadcast-mode block --fees 1dym -y --node ${HUB_RPC_URL} --chain-id ${HUB_CHAIN_ID}
dymd tx bank send $HUB_KEY_WITH_FUNDS $SEQUENCER_ADDR ${TRANSFER_AMOUNT} --keyring-backend test --fees 1dym -y --node ${HUB_RPC_URL} --chain-id ${HUB_CHAIN_ID}
```

### Generate denommetadata
Expand Down Expand Up @@ -170,22 +171,14 @@ sh scripts/settlement/register_sequencer_to_hub.sh
### Configure the rollapp

Modify `dymint.toml` in the chain directory (`~/.rollapp/config`)
set:

linux:

```shell
sed -i 's/settlement_layer.*/settlement_layer = "dymension"/' ${ROLLAPP_HOME_DIR}/config/dymint.toml
sed -i '/node_address =/c\node_address = '\"$HUB_RPC_URL\" "${ROLLAPP_HOME_DIR}/config/dymint.toml"
sed -i '/rollapp_id =/c\rollapp_id = '\"$ROLLAPP_CHAIN_ID\" "${ROLLAPP_HOME_DIR}/config/dymint.toml"
```

mac:

```shell
sed -i '' 's/settlement_layer.*/settlement_layer = "dymension"/' ${ROLLAPP_HOME_DIR}/config/dymint.toml
sed -i '' 's|node_address =.*|node_address = '\"$HUB_RPC_URL\"'|' "${ROLLAPP_HOME_DIR}/config/dymint.toml"
sed -i '' 's|rollapp_id =.*|rollapp_id = '\"$ROLLAPP_CHAIN_ID\"'|' "${ROLLAPP_HOME_DIR}/config/dymint.toml"
dasel put -f "${ROLLAPP_HOME_DIR}"/config/dymint.toml "settlement_layer" -v "dymension"
dasel put -f "${ROLLAPP_HOME_DIR}"/config/dymint.toml "node_address" -v "$HUB_RPC_URL"
dasel put -f "${ROLLAPP_HOME_DIR}"/config/dymint.toml "rollapp_id" -v "$ROLLAPP_CHAIN_ID"
dasel put -f "${ROLLAPP_HOME_DIR}"/config/dymint.toml "max_idle_time" -v "2s"
dasel put -f "${ROLLAPP_HOME_DIR}"/config/dymint.toml "max_proof_time" -v "1s"
dasel put -f "${ROLLAPP_HOME_DIR}"/config/app.toml "minimum-gas-prices" -v "1awsm"
```

### Run rollapp locally
Expand Down
5 changes: 3 additions & 2 deletions app/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,13 @@ func GetAnteDecorators(options HandlerOptions) []sdk.AnteDecorator {
ante.NewTxTimeoutHeightDecorator(),

ante.NewValidateMemoDecorator(options.AccountKeeper),
NewCreateAccountDecorator(options.AccountKeeper.(accountKeeper)),
ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper),
gasless.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TxFeeChecker, options.GaslessKeeper),
NewBypassIBCFeeDecorator(gasless.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TxFeeChecker, options.GaslessKeeper)),
ante.NewSetPubKeyDecorator(options.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators
ante.NewValidateSigCountDecorator(options.AccountKeeper),
ante.NewSigGasConsumeDecorator(options.AccountKeeper, sigGasConsumer),
ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler),
NewSigCheckDecorator(options.AccountKeeper.(accountKeeper), options.SignModeHandler),
ante.NewIncrementSequenceDecorator(options.AccountKeeper),
}

Expand Down
66 changes: 66 additions & 0 deletions app/bypass_ibc_fee_decorator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package app

import (
sdk "github.com/cosmos/cosmos-sdk/types"
clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types"
conntypes "github.com/cosmos/ibc-go/v6/modules/core/03-connection/types"
channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
)

type anteHandler interface {
AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error)
}

type BypassIBCFeeDecorator struct {
nextAnte anteHandler
}

func NewBypassIBCFeeDecorator(nextAnte anteHandler) BypassIBCFeeDecorator {
return BypassIBCFeeDecorator{nextAnte: nextAnte}
}

// SKIP FEE DEDUCT and MIN GAS PRICE Ante handlers for IBC relayer messages
func (n BypassIBCFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
// ======== HACK ================
if isIBCRelayerMsg(tx.GetMsgs()) {
return next(ctx, tx, simulate)
}
// ==============================

// If it's not an IBC Relayer transfer, proceed with the default fee handling
return n.nextAnte.AnteHandle(ctx, tx, simulate, next)
}

// isIBCRelayerMsg checks if all the messages in the transaction are IBC relayer messages
func isIBCRelayerMsg(msgs []sdk.Msg) bool {
isIBCRelayer := false

for _, msg := range msgs {
switch msg.(type) {
// IBC Client Messages
case *clienttypes.MsgCreateClient, *clienttypes.MsgUpdateClient,
*clienttypes.MsgUpgradeClient, *clienttypes.MsgSubmitMisbehaviour:
isIBCRelayer = true

// IBC Connection Messages
case *conntypes.MsgConnectionOpenInit, *conntypes.MsgConnectionOpenTry,
*conntypes.MsgConnectionOpenAck, *conntypes.MsgConnectionOpenConfirm:
isIBCRelayer = true

// IBC Channel Messages
case *channeltypes.MsgChannelOpenInit, *channeltypes.MsgChannelOpenTry,
*channeltypes.MsgChannelOpenAck, *channeltypes.MsgChannelOpenConfirm,
*channeltypes.MsgChannelCloseInit, *channeltypes.MsgChannelCloseConfirm:
isIBCRelayer = true

// IBC Packet Messages
case *channeltypes.MsgRecvPacket, *channeltypes.MsgAcknowledgement,
*channeltypes.MsgTimeout, *channeltypes.MsgTimeoutOnClose:
isIBCRelayer = true
default:
return false
}
}

return isIBCRelayer
}
67 changes: 67 additions & 0 deletions app/create_account_decorator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package app

import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/cosmos/cosmos-sdk/x/auth/types"
)

type createAccountDecorator struct {
ak accountKeeper
}

type accountKeeper interface {
authante.AccountKeeper
NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) types.AccountI
}

func NewCreateAccountDecorator(ak accountKeeper) createAccountDecorator {
return createAccountDecorator{ak: ak}
}

const newAccountCtxKeyPrefix = "new-account/"

func CtxKeyNewAccount(acc string) string {
return newAccountCtxKeyPrefix + acc
}

func (cad createAccountDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
sigTx, ok := tx.(authsigning.SigVerifiableTx)
if !ok {
return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid tx type")

Check failure on line 33 in app/create_account_decorator.go

View workflow job for this annotation

GitHub Actions / golangci-lint

SA1019: sdkerrors.Wrap is deprecated: functionality of this package has been moved to it's own module: (staticcheck)
}

pubkeys, err := sigTx.GetPubKeys()
if err != nil {
return ctx, err
}

ibcRelayerMsg := isIBCRelayerMsg(tx.GetMsgs())

for i, pk := range pubkeys {
if pk == nil {
continue
}

acc, err := authante.GetSignerAcc(ctx, cad.ak, sigTx.GetSigners()[i])

Check failure on line 48 in app/create_account_decorator.go

View workflow job for this annotation

GitHub Actions / golangci-lint

ineffectual assignment to acc (ineffassign)
Fixed Show fixed Hide fixed
if err != nil {
// ======= HACK =========================
// for IBC relayer messages, create an account if it doesn't exist
if ibcRelayerMsg {
address := sdk.AccAddress(pk.Address())
acc = cad.ak.NewAccountWithAddress(ctx, address)
// inject the new account flag into the context, in order to signal
// the account creation to the subsequent decorators (sigchecker)
ctx = ctx.WithValue(CtxKeyNewAccount(address.String()), struct{}{})
cad.ak.SetAccount(ctx, acc)
} else {
return ctx, err
}
// ======================================
}
}

return next(ctx, tx, simulate)
}
105 changes: 105 additions & 0 deletions app/sigcheck_decorator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package app

import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
)

type sigCheckDecorator struct {
ak accountKeeper
signModeHandler authsigning.SignModeHandler
}

func NewSigCheckDecorator(ak accountKeeper, signModeHandler authsigning.SignModeHandler) sigCheckDecorator {
return sigCheckDecorator{ak: ak, signModeHandler: signModeHandler}
}

// Copied from github.com/cosmos/[email protected]/x/auth/ante/sigverify.go:235
// and modified to set account number to 0 when verifying for IBC relayer messages from a new account
func (svd sigCheckDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
sigTx, ok := tx.(authsigning.SigVerifiableTx)
if !ok {
return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type")

Check failure on line 26 in app/sigcheck_decorator.go

View workflow job for this annotation

GitHub Actions / golangci-lint

SA1019: sdkerrors.Wrap is deprecated: functionality of this package has been moved to it's own module: (staticcheck)
}

// stdSigs contains the sequence number, account number, and signatures.
// When simulating, this would just be a 0-length slice.
sigs, err := sigTx.GetSignaturesV2()
if err != nil {
return ctx, err
}

signerAddrs := sigTx.GetSigners()

// check that signer length and signature length are the same
if len(sigs) != len(signerAddrs) {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "invalid number of signer; expected: %d, got %d", len(signerAddrs), len(sigs))

Check failure on line 40 in app/sigcheck_decorator.go

View workflow job for this annotation

GitHub Actions / golangci-lint

SA1019: sdkerrors.Wrapf is deprecated: functionality of this package has been moved to it's own module: (staticcheck)
}

ibcRelayerMsg := isIBCRelayerMsg(tx.GetMsgs())

for i, sig := range sigs {
acc, err := authante.GetSignerAcc(ctx, svd.ak, signerAddrs[i])
if err != nil {
return ctx, err
}

// retrieve pubkey
pubKey := acc.GetPubKey()
if !simulate && pubKey == nil {
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidPubKey, "pubkey on account is not set")

Check failure on line 54 in app/sigcheck_decorator.go

View workflow job for this annotation

GitHub Actions / golangci-lint

SA1019: sdkerrors.Wrap is deprecated: functionality of this package has been moved to it's own module: (staticcheck)
}

// Check account sequence number.
if sig.Sequence != acc.GetSequence() {
return ctx, sdkerrors.Wrapf(

Check failure on line 59 in app/sigcheck_decorator.go

View workflow job for this annotation

GitHub Actions / golangci-lint

SA1019: sdkerrors.Wrapf is deprecated: functionality of this package has been moved to it's own module: (staticcheck)
sdkerrors.ErrWrongSequence,
"account sequence mismatch, expected %d, got %d", acc.GetSequence(), sig.Sequence,
)
}

// retrieve signer data
genesis := ctx.BlockHeight() == 0
chainID := ctx.ChainID()
var accNum uint64

// ======= HACK ====================
_, isNewAcc := ctx.Value(CtxKeyNewAccount(acc.GetAddress().String())).(struct{})
isNewRelayerAcc := ibcRelayerMsg && isNewAcc
if !genesis && !isNewRelayerAcc {
accNum = acc.GetAccountNumber()
}
// =================================

signerData := authsigning.SignerData{
Address: acc.GetAddress().String(),
ChainID: chainID,
AccountNumber: accNum,
Sequence: acc.GetSequence(),
PubKey: pubKey,
}

// no need to verify signatures on recheck tx
if !simulate && !ctx.IsReCheckTx() {
err := authsigning.VerifySignature(pubKey, signerData, sig.Data, svd.signModeHandler, tx)
if err != nil {
var errMsg string
if authante.OnlyLegacyAminoSigners(sig.Data) {
// If all signers are using SIGN_MODE_LEGACY_AMINO, we rely on VerifySignature to check account sequence number,
// and therefore communicate sequence number as a potential cause of error.
errMsg = fmt.Sprintf("signature verification failed; please verify account number (%d), sequence (%d) and chain-id (%s)", accNum, acc.GetSequence(), chainID)
} else {
errMsg = fmt.Sprintf("signature verification failed; please verify account number (%d) and chain-id (%s)", accNum, chainID)
}
return ctx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, errMsg)

}
}
}

return next(ctx, tx, simulate)
}
Loading
Loading