Skip to content

Commit

Permalink
Add native associate tx type (#1694)
Browse files Browse the repository at this point in the history
  • Loading branch information
codchen authored May 24, 2024
1 parent 59dcce1 commit de00a65
Show file tree
Hide file tree
Showing 13 changed files with 614 additions and 66 deletions.
2 changes: 1 addition & 1 deletion app/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func NewAnteHandlerAndDepGenerator(options HandlerOptions) (sdk.AnteHandler, sdk

anteDecorators := []sdk.AnteFullDecorator{
sdk.DefaultWrappedAnteDecorator(ante.NewSetUpContextDecorator(antedecorators.GetGasMeterSetter(options.ParamsKeeper.(paramskeeper.Keeper)))), // outermost AnteDecorator. SetUpContext must be called first
antedecorators.NewGaslessDecorator([]sdk.AnteFullDecorator{ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.ParamsKeeper.(paramskeeper.Keeper), options.TxFeeChecker)}, *options.OracleKeeper),
antedecorators.NewGaslessDecorator([]sdk.AnteFullDecorator{ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.ParamsKeeper.(paramskeeper.Keeper), options.TxFeeChecker)}, *options.OracleKeeper, options.EVMKeeper),
sdk.DefaultWrappedAnteDecorator(wasmkeeper.NewLimitSimulationGasDecorator(options.WasmConfig.SimulationGasLimit, antedecorators.GetGasMeterSetter(options.ParamsKeeper.(paramskeeper.Keeper)))), // after setup context to enforce limits early
sdk.DefaultWrappedAnteDecorator(ante.NewRejectExtensionOptionsDecorator()),
oracle.NewSpammingPreventionDecorator(*options.OracleKeeper),
Expand Down
1 change: 0 additions & 1 deletion app/antedecorators/accesscontrol_wasm_dependency.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ func NewACLWasmDependencyDecorator(aclKeeper aclkeeper.Keeper, wasmKeeper wasm.K
}

func (ad ACLWasmDependencyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {

for _, msg := range tx.GetMsgs() {
switch m := msg.(type) {
case *acltypes.MsgRegisterWasmDependency:
Expand Down
22 changes: 18 additions & 4 deletions app/antedecorators/gasless.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,28 @@ import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
dextypes "github.com/sei-protocol/sei-chain/x/dex/types"
evmkeeper "github.com/sei-protocol/sei-chain/x/evm/keeper"
evmtypes "github.com/sei-protocol/sei-chain/x/evm/types"
oraclekeeper "github.com/sei-protocol/sei-chain/x/oracle/keeper"
oracletypes "github.com/sei-protocol/sei-chain/x/oracle/types"
)

type GaslessDecorator struct {
wrapped []sdk.AnteFullDecorator
oracleKeeper oraclekeeper.Keeper
evmKeeper *evmkeeper.Keeper
}

func NewGaslessDecorator(wrapped []sdk.AnteFullDecorator, oracleKeeper oraclekeeper.Keeper) GaslessDecorator {
return GaslessDecorator{wrapped: wrapped, oracleKeeper: oracleKeeper}
func NewGaslessDecorator(wrapped []sdk.AnteFullDecorator, oracleKeeper oraclekeeper.Keeper, evmKeeper *evmkeeper.Keeper) GaslessDecorator {
return GaslessDecorator{wrapped: wrapped, oracleKeeper: oracleKeeper, evmKeeper: evmKeeper}
}

func (gd GaslessDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
originalGasMeter := ctx.GasMeter()
// eagerly set infinite gas meter so that queries performed by IsTxGasless will not incur gas cost
ctx = ctx.WithGasMeter(storetypes.NewNoConsumptionInfiniteGasMeter())

isGasless, err := IsTxGasless(tx, ctx, gd.oracleKeeper)
isGasless, err := IsTxGasless(tx, ctx, gd.oracleKeeper, gd.evmKeeper)
if err != nil {
return ctx, err
}
Expand Down Expand Up @@ -108,7 +111,7 @@ func (gd GaslessDecorator) AnteDeps(txDeps []sdkacltypes.AccessOperation, tx sdk
return next(append(txDeps, deps...), tx, txIndex)
}

func IsTxGasless(tx sdk.Tx, ctx sdk.Context, oracleKeeper oraclekeeper.Keeper) (bool, error) {
func IsTxGasless(tx sdk.Tx, ctx sdk.Context, oracleKeeper oraclekeeper.Keeper, evmKeeper *evmkeeper.Keeper) (bool, error) {
if len(tx.GetMsgs()) == 0 {
// empty TX shouldn't be gasless
return false, nil
Expand All @@ -129,6 +132,10 @@ func IsTxGasless(tx sdk.Tx, ctx sdk.Context, oracleKeeper oraclekeeper.Keeper) (
if err != nil || !isGasless {
return false, err
}
case *evmtypes.MsgAssociate:
if !evmAssociateIsGasless(m, ctx, evmKeeper) {
return false, nil
}
default:
return false, nil
}
Expand Down Expand Up @@ -190,3 +197,10 @@ func oracleVoteIsGasless(msg *oracletypes.MsgAggregateExchangeRateVote, ctx sdk.
// otherwise we allow it
return true, nil
}

func evmAssociateIsGasless(msg *evmtypes.MsgAssociate, ctx sdk.Context, keeper *evmkeeper.Keeper) bool {
// not gasless if already associated
seiAddr := sdk.MustAccAddressFromBech32(msg.Sender)
_, associated := keeper.GetEVMAddress(ctx, seiAddr)
return !associated
}
15 changes: 8 additions & 7 deletions app/antedecorators/gasless_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/sei-protocol/sei-chain/app/antedecorators"
"github.com/sei-protocol/sei-chain/x/dex/types"
evmkeeper "github.com/sei-protocol/sei-chain/x/evm/keeper"
oraclekeeper "github.com/sei-protocol/sei-chain/x/oracle/keeper"
oracletypes "github.com/sei-protocol/sei-chain/x/oracle/types"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -91,9 +92,9 @@ func (t FakeTx) FeeGranter() sdk.AccAddress {
return nil
}

func CallGaslessDecoratorWithMsg(ctx sdk.Context, msg sdk.Msg, oracleKeeper oraclekeeper.Keeper) error {
func CallGaslessDecoratorWithMsg(ctx sdk.Context, msg sdk.Msg, oracleKeeper oraclekeeper.Keeper, evmKeeper *evmkeeper.Keeper) error {
anteDecorators := []sdk.AnteFullDecorator{
antedecorators.NewGaslessDecorator([]sdk.AnteFullDecorator{sdk.DefaultWrappedAnteDecorator(FakeAnteDecoratorGasReqd{})}, oracleKeeper),
antedecorators.NewGaslessDecorator([]sdk.AnteFullDecorator{sdk.DefaultWrappedAnteDecorator(FakeAnteDecoratorGasReqd{})}, oracleKeeper, evmKeeper),
}
chainedHandler, depGen := sdk.ChainAnteDecorators(anteDecorators...)
fakeTx := FakeTx{
Expand Down Expand Up @@ -140,12 +141,12 @@ func TestOracleVoteGasless(t *testing.T) {
}

// reset gasless
err = CallGaslessDecoratorWithMsg(ctx, &vote1, input.OracleKeeper)
err = CallGaslessDecoratorWithMsg(ctx, &vote1, input.OracleKeeper, nil)
require.Error(t, err)

// reset gasless
gasless = true
err = CallGaslessDecoratorWithMsg(ctx, &vote2, input.OracleKeeper)
err = CallGaslessDecoratorWithMsg(ctx, &vote2, input.OracleKeeper, nil)
require.NoError(t, err)
require.True(t, gasless)
}
Expand All @@ -167,14 +168,14 @@ func TestDexCancelOrderGasless(t *testing.T) {
// not whitelisted
// reset gasless
gasless = true
err := CallGaslessDecoratorWithMsg(sdk.NewContext(nil, tmproto.Header{}, false, nil).WithIsCheckTx(true), &cancelMsg1, oraclekeeper.Keeper{})
err := CallGaslessDecoratorWithMsg(sdk.NewContext(nil, tmproto.Header{}, false, nil).WithIsCheckTx(true), &cancelMsg1, oraclekeeper.Keeper{}, nil)
require.NoError(t, err)
require.False(t, gasless)

// whitelisted
// reset gasless
gasless = true
err = CallGaslessDecoratorWithMsg(sdk.NewContext(nil, tmproto.Header{}, false, nil).WithIsCheckTx(true), &cancelMsg2, oraclekeeper.Keeper{})
err = CallGaslessDecoratorWithMsg(sdk.NewContext(nil, tmproto.Header{}, false, nil).WithIsCheckTx(true), &cancelMsg2, oraclekeeper.Keeper{}, nil)
require.NoError(t, err)
require.True(t, gasless)
}
Expand All @@ -183,7 +184,7 @@ func TestNonGaslessMsg(t *testing.T) {
// this needs to be updated if its changed from constant true
// reset gasless
gasless = true
err := CallGaslessDecoratorWithMsg(sdk.NewContext(nil, tmproto.Header{}, false, nil).WithIsCheckTx(true), &types.MsgRegisterContract{}, oraclekeeper.Keeper{})
err := CallGaslessDecoratorWithMsg(sdk.NewContext(nil, tmproto.Header{}, false, nil).WithIsCheckTx(true), &types.MsgRegisterContract{}, oraclekeeper.Keeper{}, nil)
require.NoError(t, err)
require.False(t, gasless)
}
2 changes: 1 addition & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -1786,7 +1786,7 @@ func (app *App) checkTotalBlockGasWanted(ctx sdk.Context, txs [][]byte) bool {
// such tx will not be processed and thus won't consume gas. Skipping
continue
}
isGasless, err := antedecorators.IsTxGasless(decoded, ctx, app.OracleKeeper)
isGasless, err := antedecorators.IsTxGasless(decoded, ctx, app.OracleKeeper, &app.EvmKeeper)
if err != nil {
ctx.Logger().Error("error checking if tx is gasless", "error", err)
continue
Expand Down
10 changes: 9 additions & 1 deletion proto/evm/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ service Msg {
rpc Send(MsgSend) returns (MsgSendResponse);
rpc RegisterPointer(MsgRegisterPointer) returns (MsgRegisterPointerResponse);
rpc AssociateContractAddress(MsgAssociateContractAddress) returns (MsgAssociateContractAddressResponse);
rpc Associate(MsgAssociate) returns (MsgAssociateResponse);
}

message MsgEVMTransaction {
Expand Down Expand Up @@ -72,4 +73,11 @@ message MsgAssociateContractAddress {
string address = 2;
}

message MsgAssociateContractAddressResponse {}
message MsgAssociateContractAddressResponse {}

message MsgAssociate {
string sender = 1;
string custom_message = 2;
}

message MsgAssociateResponse {}
6 changes: 6 additions & 0 deletions x/evm/ante/preprocess.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,12 @@ func (p *EVMAddressDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo
ctx.Logger().Error(fmt.Sprintf("failed to migrate EVM address balance (%s) %s", evmAddr.Hex(), err))
return ctx, err
}
if evmtypes.IsTxMsgAssociate(tx) {
// check if there is non-zero balance
if !p.evmKeeper.BankKeeper().GetBalance(ctx, signer, sdk.MustGetBaseDenom()).IsPositive() && !p.evmKeeper.BankKeeper().GetWeiBalance(ctx, signer).IsPositive() {
return ctx, sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, "account needs to have at least 1 wei to force association")
}
}
}
return next(ctx, tx, simulate)
}
25 changes: 25 additions & 0 deletions x/evm/client/cli/native_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,28 @@ func AssociateContractAddressCmd() *cobra.Command {

return cmd
}

func NativeAssociateCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "native-associate [custom msg]",
Short: `Set address association for the sender.`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

msg := types.NewMsgAssociate(clientCtx.GetFromAddress(), args[0])
if err := msg.ValidateBasic(); err != nil {
return err
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}
1 change: 1 addition & 0 deletions x/evm/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func GetTxCmd() *cobra.Command {
cmd.AddCommand(NewAddCWERC20PointerProposalTxCmd())
cmd.AddCommand(NewAddCWERC721PointerProposalTxCmd())
cmd.AddCommand(AssociateContractAddressCmd())
cmd.AddCommand(NativeAssociateCmd())

return cmd
}
Expand Down
4 changes: 4 additions & 0 deletions x/evm/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,3 +343,7 @@ func (server msgServer) AssociateContractAddress(goCtx context.Context, msg *typ
server.SetAddressMapping(ctx, addr, evmAddr)
return &types.MsgAssociateContractAddressResponse{}, nil
}

func (server msgServer) Associate(context.Context, *types.MsgAssociate) (*types.MsgAssociateResponse, error) {
return &types.MsgAssociateResponse{}, nil
}
56 changes: 56 additions & 0 deletions x/evm/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper_test

import (
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
"math/big"
Expand All @@ -11,13 +12,16 @@ import (
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/sei-protocol/sei-chain/example/contracts/echo"
"github.com/sei-protocol/sei-chain/example/contracts/sendall"
"github.com/sei-protocol/sei-chain/example/contracts/simplestorage"
testkeeper "github.com/sei-protocol/sei-chain/testutil/keeper"
dexcache "github.com/sei-protocol/sei-chain/x/dex/cache"
dexutils "github.com/sei-protocol/sei-chain/x/dex/utils"
"github.com/sei-protocol/sei-chain/x/evm/ante"
"github.com/sei-protocol/sei-chain/x/evm/artifacts/erc20"
"github.com/sei-protocol/sei-chain/x/evm/artifacts/erc721"
Expand Down Expand Up @@ -708,3 +712,55 @@ func TestAssociateContractAddress(t *testing.T) {
require.NotNil(t, err)
require.Contains(t, err.Error(), "no wasm contract found at the given address")
}

func TestAssociate(t *testing.T) {
ctx := testkeeper.EVMTestApp.GetContextForDeliverTx([]byte{}).WithChainID("sei-test").WithBlockHeight(1)
ctx = ctx.WithContext(
context.WithValue(ctx.Context(), dexutils.DexMemStateContextKey, &dexcache.MemState{}),
)
privKey := testkeeper.MockPrivateKey()
seiAddr, evmAddr := testkeeper.PrivateKeyToAddresses(privKey)
acc := testkeeper.EVMTestApp.AccountKeeper.NewAccountWithAddress(ctx, seiAddr)
testkeeper.EVMTestApp.AccountKeeper.SetAccount(ctx, acc)
msg := types.NewMsgAssociate(seiAddr, "test")
tb := testkeeper.EVMTestApp.GetTxConfig().NewTxBuilder()
tb.SetMsgs(msg)
tb.SetSignatures(signing.SignatureV2{
PubKey: privKey.PubKey(),
Data: &signing.SingleSignatureData{
SignMode: testkeeper.EVMTestApp.GetTxConfig().SignModeHandler().DefaultMode(),
Signature: nil,
},
Sequence: acc.GetSequence(),
})
signerData := authsigning.SignerData{
ChainID: "sei-test",
AccountNumber: acc.GetAccountNumber(),
Sequence: acc.GetSequence(),
}
signBytes, err := testkeeper.EVMTestApp.GetTxConfig().SignModeHandler().GetSignBytes(testkeeper.EVMTestApp.GetTxConfig().SignModeHandler().DefaultMode(), signerData, tb.GetTx())
require.Nil(t, err)
sig, err := privKey.Sign(signBytes)
require.Nil(t, err)
sigs := make([]signing.SignatureV2, 1)
sigs[0] = signing.SignatureV2{
PubKey: privKey.PubKey(),
Data: &signing.SingleSignatureData{
SignMode: testkeeper.EVMTestApp.GetTxConfig().SignModeHandler().DefaultMode(),
Signature: sig,
},
Sequence: acc.GetSequence(),
}
require.Nil(t, tb.SetSignatures(sigs...))
sdktx := tb.GetTx()
txbz, err := testkeeper.EVMTestApp.GetTxConfig().TxEncoder()(sdktx)
require.Nil(t, err)

res := testkeeper.EVMTestApp.DeliverTx(ctx, abci.RequestDeliverTx{Tx: txbz}, sdktx, sha256.Sum256(txbz))
require.NotEqual(t, uint32(0), res.Code) // not enough balance

require.Nil(t, testkeeper.EVMTestApp.BankKeeper.AddWei(ctx, sdk.AccAddress(evmAddr[:]), sdk.OneInt()))

res = testkeeper.EVMTestApp.DeliverTx(ctx, abci.RequestDeliverTx{Tx: txbz}, sdktx, sha256.Sum256(txbz))
require.Equal(t, uint32(0), res.Code)
}
54 changes: 54 additions & 0 deletions x/evm/types/message_associate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package types

import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

const TypeMsgAssociate = "evm_associate"

var (
_ sdk.Msg = &MsgAssociate{}
)

func NewMsgAssociate(sender sdk.AccAddress, customMsg string) *MsgAssociate {
return &MsgAssociate{Sender: sender.String(), CustomMessage: customMsg}
}

func (msg *MsgAssociate) Route() string {
return RouterKey
}

func (msg *MsgAssociate) Type() string {
return TypeMsgAssociate
}

func (msg *MsgAssociate) GetSigners() []sdk.AccAddress {
from, err := sdk.AccAddressFromBech32(msg.Sender)
if err != nil {
panic(err)
}
return []sdk.AccAddress{from}
}

func (msg *MsgAssociate) GetSignBytes() []byte {
return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg))
}

func (msg *MsgAssociate) ValidateBasic() error {
_, err := sdk.AccAddressFromBech32(msg.Sender)
if err != nil {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err)
}

return nil
}

func IsTxMsgAssociate(tx sdk.Tx) bool {
msgs := tx.GetMsgs()
if len(msgs) != 1 {
return false
}
_, ok := msgs[0].(*MsgAssociate)
return ok
}
Loading

0 comments on commit de00a65

Please sign in to comment.