Skip to content

Commit

Permalink
Create account if not found and skip fees for IBC relayer messages
Browse files Browse the repository at this point in the history
  • Loading branch information
zale144 committed Sep 13, 2024
1 parent 3dcee4e commit 412cd54
Show file tree
Hide file tree
Showing 8 changed files with 512 additions and 87 deletions.
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)

Check warning

Code scanning / CodeQL

Useless assignment to local variable Warning

This definition of acc is never used.
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

0 comments on commit 412cd54

Please sign in to comment.