From 4f1322f64b9bead3d271605c428d12aeb7e089ed Mon Sep 17 00:00:00 2001 From: Josh Klopfenstein Date: Mon, 9 Dec 2024 19:09:57 -0600 Subject: [PATCH] Add test for antehandler --- e2e/stack_test.go | 109 ++++--------------------------- testutils/utils.go | 85 ++++++++++++++++++++++++ x/rollup/tx/helpers/ante.go | 5 +- x/rollup/tx/helpers/ante_test.go | 71 ++++++++++++++++++++ x/rollup/tx/l1_data.go | 8 ++- 5 files changed, 176 insertions(+), 102 deletions(-) create mode 100644 x/rollup/tx/helpers/ante_test.go diff --git a/e2e/stack_test.go b/e2e/stack_test.go index 7ff244b8..27f89971 100644 --- a/e2e/stack_test.go +++ b/e2e/stack_test.go @@ -8,17 +8,10 @@ import ( "time" authv1beta1 "cosmossdk.io/api/cosmos/auth/v1beta1" - txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1" "cosmossdk.io/math" abcitypes "github.com/cometbft/cometbft/abci/types" cometcore "github.com/cometbft/cometbft/rpc/core/types" bfttypes "github.com/cometbft/cometbft/types" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - cosmossecp256k1 "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - sdk "github.com/cosmos/cosmos-sdk/types" - sdktx "github.com/cosmos/cosmos-sdk/types/tx" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - "github.com/decred/dcrd/dcrec/secp256k1/v4" opbindings "github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/receipts" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" @@ -34,9 +27,9 @@ import ( protov1 "github.com/golang/protobuf/proto" //nolint:staticcheck "github.com/polymerdao/monomer" "github.com/polymerdao/monomer/e2e" + "github.com/polymerdao/monomer/testutils" rolluptypes "github.com/polymerdao/monomer/x/rollup/types" "github.com/stretchr/testify/require" - "google.golang.org/protobuf/proto" ) func checkForRollbacks(t *testing.T, stack *e2e.StackConfig) { @@ -262,18 +255,17 @@ func ethRollupFlow(t *testing.T, stack *e2e.StackConfig) { l2ChainID, err := stack.MonomerClient.ChainID(stack.Ctx) require.NoError(t, err) - withdrawalTxResult, err := stack.L2Client.BroadcastTxAsync( - stack.Ctx, - buildTx(t, l2ChainID.String(), baseAccount.Sequence, baseAccount.AccountNumber, userPrivKey, []protov1.Message{ - &rolluptypes.MsgInitiateWithdrawal{ - Sender: userCosmosAddr, - Target: withdrawalTx.Target.String(), - Value: math.NewIntFromBigInt(withdrawalTx.Value), - GasLimit: withdrawalTx.GasLimit.Bytes(), - Data: []byte{}, - }, - }), - ) + l2WithdrawalTxBytes, err := testutils.BuildSDKTx(t, l2ChainID.String(), baseAccount.Sequence, baseAccount.AccountNumber, userPrivKey, []protov1.Message{ + &rolluptypes.MsgInitiateWithdrawal{ + Sender: userCosmosAddr, + Target: withdrawalTx.Target.String(), + Value: math.NewIntFromBigInt(withdrawalTx.Value), + GasLimit: withdrawalTx.GasLimit.Bytes(), + Data: []byte{}, + }, + }).Marshal() + require.NoError(t, err) + withdrawalTxResult, err := stack.L2Client.BroadcastTxAsync(stack.Ctx, l2WithdrawalTxBytes) require.NoError(t, err) require.Equalf(t, abcitypes.CodeTypeOK, withdrawalTxResult.Code, "log: "+withdrawalTxResult.Log) @@ -380,83 +372,6 @@ func ethRollupFlow(t *testing.T, stack *e2e.StackConfig) { t.Log("Monomer can initiate withdrawals on L2 and can generate proofs for verifying the withdrawal on L1") } -func convertPrivKey(ecdsaPrivKey *ecdsa.PrivateKey) *secp256k1.PrivateKey { - privKeyBytes := ecdsaPrivKey.D.Bytes() - var key secp256k1.ModNScalar - if len(privKeyBytes) > 32 || key.SetByteSlice(privKeyBytes) { - panic("overflow") - } - if key.IsZero() { - panic("private keys must not be 0") - } - return secp256k1.NewPrivateKey(&key) -} - -func buildTx(t *testing.T, chainID string, seqNum, accNum uint64, ethPrivKey *ecdsa.PrivateKey, msgs []protov1.Message) bfttypes.Tx { - cosmosPrivKey := &cosmossecp256k1.PrivKey{ - Key: convertPrivKey(ethPrivKey).Serialize(), - } - - var msgAnys []*codectypes.Any - for _, msg := range msgs { - msgAny, err := codectypes.NewAnyWithValue(msg) - require.NoError(t, err) - msgAnys = append(msgAnys, msgAny) - } - - pubKeyAny, err := codectypes.NewAnyWithValue(cosmosPrivKey.PubKey()) - require.NoError(t, err) - - tx := &sdktx.Tx{ - Body: &sdktx.TxBody{ - Messages: msgAnys, - }, - AuthInfo: &sdktx.AuthInfo{ - SignerInfos: []*sdktx.SignerInfo{ - { - PublicKey: pubKeyAny, - ModeInfo: &sdktx.ModeInfo{ - Sum: &sdktx.ModeInfo_Single_{ - Single: &sdktx.ModeInfo_Single{ - Mode: signing.SignMode_SIGN_MODE_DIRECT, - }, - }, - }, - Sequence: seqNum, - }, - }, - Fee: &sdktx.Fee{ - Amount: sdk.NewCoins(sdk.NewCoin(rolluptypes.WEI, math.NewInt(100000000))), - GasLimit: 1000000, - }, - }, - } - - bodyBytes, err := protov1.Marshal(tx.Body) - require.NoError(t, err) - authInfoBytes, err := protov1.Marshal(tx.AuthInfo) - require.NoError(t, err) - - signBytes, err := (proto.MarshalOptions{Deterministic: true}).Marshal(&txv1beta1.SignDoc{ - BodyBytes: bodyBytes, - AuthInfoBytes: authInfoBytes, - ChainId: chainID, - AccountNumber: accNum, - }) - require.NoError(t, err) - - signature, err := cosmosPrivKey.Sign(signBytes) - require.NoError(t, err) - - require.True(t, cosmosPrivKey.PubKey().VerifySignature(signBytes, signature)) - - tx.Signatures = [][]byte{signature} - - txBytes, err := tx.Marshal() - require.NoError(t, err) - return txBytes -} - func erc20RollupFlow(t *testing.T, stack *e2e.StackConfig) { l1Client := stack.L1Client diff --git a/testutils/utils.go b/testutils/utils.go index 08bc211b..f475f64d 100644 --- a/testutils/utils.go +++ b/testutils/utils.go @@ -1,16 +1,25 @@ package testutils import ( + "crypto/ecdsa" "math/big" "math/rand" "strings" "testing" + txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1" + "cosmossdk.io/math" "github.com/cockroachdb/pebble" "github.com/cockroachdb/pebble/vfs" cometdb "github.com/cometbft/cometbft-db" bfttypes "github.com/cometbft/cometbft/types" dbm "github.com/cosmos/cosmos-db" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cosmossecp256k1 "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + sdktypes "github.com/cosmos/cosmos-sdk/types" + sdktx "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/decred/dcrd/dcrec/secp256k1/v4" opbindings "github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain" "github.com/ethereum-optimism/optimism/op-node/rollup" @@ -24,9 +33,12 @@ import ( "github.com/ethereum/go-ethereum/core/state" gethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/trie" + protov1 "github.com/golang/protobuf/proto" //nolint:staticcheck "github.com/polymerdao/monomer" "github.com/polymerdao/monomer/monomerdb/localdb" + rolluptypes "github.com/polymerdao/monomer/x/rollup/types" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" ) func NewCometMemDB(t *testing.T) cometdb.DB { @@ -209,3 +221,76 @@ func GenerateL1Block() *gethtypes.Block { Time: uint64(0), }, nil, nil, nil, trie.NewStackTrie(nil)) } + +func convertPrivKey(ecdsaPrivKey *ecdsa.PrivateKey) *secp256k1.PrivateKey { + privKeyBytes := ecdsaPrivKey.D.Bytes() + var key secp256k1.ModNScalar + if len(privKeyBytes) > 32 || key.SetByteSlice(privKeyBytes) { + panic("overflow") + } + if key.IsZero() { + panic("private keys must not be 0") + } + return secp256k1.NewPrivateKey(&key) +} + +func BuildSDKTx(t *testing.T, chainID string, seqNum, accNum uint64, ethPrivKey *ecdsa.PrivateKey, msgs []protov1.Message) *sdktx.Tx { + cosmosPrivKey := &cosmossecp256k1.PrivKey{ + Key: convertPrivKey(ethPrivKey).Serialize(), + } + + var msgAnys []*codectypes.Any + for _, msg := range msgs { + msgAny, err := codectypes.NewAnyWithValue(msg) + require.NoError(t, err) + msgAnys = append(msgAnys, msgAny) + } + + pubKeyAny, err := codectypes.NewAnyWithValue(cosmosPrivKey.PubKey()) + require.NoError(t, err) + + tx := &sdktx.Tx{ + Body: &sdktx.TxBody{ + Messages: msgAnys, + }, + AuthInfo: &sdktx.AuthInfo{ + SignerInfos: []*sdktx.SignerInfo{ + { + PublicKey: pubKeyAny, + ModeInfo: &sdktx.ModeInfo{ + Sum: &sdktx.ModeInfo_Single_{ + Single: &sdktx.ModeInfo_Single{ + Mode: signing.SignMode_SIGN_MODE_DIRECT, + }, + }, + }, + Sequence: seqNum, + }, + }, + Fee: &sdktx.Fee{ + Amount: sdktypes.NewCoins(sdktypes.NewCoin(rolluptypes.WEI, math.NewInt(100000000))), + GasLimit: 1000000, + }, + }, + } + + bodyBytes, err := protov1.Marshal(tx.Body) + require.NoError(t, err) + authInfoBytes, err := protov1.Marshal(tx.AuthInfo) + require.NoError(t, err) + + signBytes, err := (proto.MarshalOptions{Deterministic: true}).Marshal(&txv1beta1.SignDoc{ + BodyBytes: bodyBytes, + AuthInfoBytes: authInfoBytes, + ChainId: chainID, + AccountNumber: accNum, + }) + require.NoError(t, err) + + signature, err := cosmosPrivKey.Sign(signBytes) + require.NoError(t, err) + + tx.Signatures = [][]byte{signature} + + return tx +} diff --git a/x/rollup/tx/helpers/ante.go b/x/rollup/tx/helpers/ante.go index 29f87cc4..18b1033f 100644 --- a/x/rollup/tx/helpers/ante.go +++ b/x/rollup/tx/helpers/ante.go @@ -6,19 +6,18 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" authante "github.com/cosmos/cosmos-sdk/x/auth/ante" - rollupkeeper "github.com/polymerdao/monomer/x/rollup/keeper" rolluptx "github.com/polymerdao/monomer/x/rollup/tx" rolluptypes "github.com/polymerdao/monomer/x/rollup/types" ) type AnteHandler struct { authAnteHandler sdktypes.AnteHandler - rollupKeeper *rollupkeeper.Keeper + rollupKeeper rolluptx.RollupKeeper } func NewAnteHandler( options authante.HandlerOptions, //nolint:gocritic // hugeParam - rollupKeeper *rollupkeeper.Keeper, + rollupKeeper rolluptx.RollupKeeper, ) (*AnteHandler, error) { authAnteHandler, err := authante.NewAnteHandler(options) if err != nil { diff --git a/x/rollup/tx/helpers/ante_test.go b/x/rollup/tx/helpers/ante_test.go new file mode 100644 index 00000000..cda1ee8b --- /dev/null +++ b/x/rollup/tx/helpers/ante_test.go @@ -0,0 +1,71 @@ +package helpers_test + +import ( + "testing" + + storetypes "cosmossdk.io/store/types" + "cosmossdk.io/x/tx/signing" + "cosmossdk.io/x/tx/signing/direct" + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + sdktx "github.com/cosmos/cosmos-sdk/types/tx" + authante "github.com/cosmos/cosmos-sdk/x/auth/ante" + authantetestutil "github.com/cosmos/cosmos-sdk/x/auth/ante/testutil" + authtestutil "github.com/cosmos/cosmos-sdk/x/auth/testutil" + "github.com/ethereum/go-ethereum/crypto" + "github.com/golang/mock/gomock" + protov1 "github.com/golang/protobuf/proto" //nolint:staticcheck + "github.com/polymerdao/monomer/testutils" + "github.com/polymerdao/monomer/x/rollup/tx/helpers" + rolluptypes "github.com/polymerdao/monomer/x/rollup/types" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/reflect/protoreflect" +) + +type mockRollupKeeper struct{} + +func (mockRollupKeeper) GetL1BlockInfo(_ sdk.Context) (*rolluptypes.L1BlockInfo, error) { //nolint:gocritic // hugeParam + return &rolluptypes.L1BlockInfo{}, nil +} + +type mockTx struct { + tx *sdktx.Tx +} + +var _ sdk.Tx = (*mockTx)(nil) + +func (tx *mockTx) GetMsgs() []sdk.Msg { + return tx.tx.GetMsgs() +} + +func (tx *mockTx) GetMsgsV2() ([]protoreflect.ProtoMessage, error) { + msgsV1 := tx.GetMsgs() + msgs := make([]protoreflect.ProtoMessage, 0, len(msgsV1)) + for _, msgV1 := range msgsV1 { + msgs = append(msgs, protov1.MessageV2(msgV1)) + } + return msgs, nil +} + +func TestUserTransactionCannotContainDepositMesssage(t *testing.T) { + sdkCtx := testutil.DefaultContextWithDB( + t, + storetypes.NewKVStoreKey(rolluptypes.StoreKey), + storetypes.NewTransientStoreKey("transient_test"), + ).Ctx + anteHandler, err := helpers.NewAnteHandler(authante.HandlerOptions{ + AccountKeeper: authantetestutil.NewMockAccountKeeper(gomock.NewController(t)), + BankKeeper: authtestutil.NewMockBankKeeper(gomock.NewController(t)), + SignModeHandler: signing.NewHandlerMap(direct.SignModeHandler{}), + }, mockRollupKeeper{}) + require.NoError(t, err) + + privKey, err := crypto.GenerateKey() + require.NoError(t, err) + for _, depositMsg := range []protov1.Message{&rolluptypes.MsgSetL1Attributes{}, &rolluptypes.MsgApplyUserDeposit{}} { + _, err := anteHandler.AnteHandle(sdkCtx, &mockTx{ + tx: testutils.BuildSDKTx(t, "chainID", 0, 0, privKey, []protov1.Message{depositMsg}), + }, false) + require.ErrorContains(t, err, "transaction contains deposit message") + } +} diff --git a/x/rollup/tx/l1_data.go b/x/rollup/tx/l1_data.go index cce90ab5..e5f4bddf 100644 --- a/x/rollup/tx/l1_data.go +++ b/x/rollup/tx/l1_data.go @@ -8,13 +8,17 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/ethereum/go-ethereum/rlp" - rollupkeeper "github.com/polymerdao/monomer/x/rollup/keeper" + rolluptypes "github.com/polymerdao/monomer/x/rollup/types" ) +type RollupKeeper interface { + GetL1BlockInfo(ctx sdk.Context) (*rolluptypes.L1BlockInfo, error) +} + // L1DataAnteHandler will consume gas to compensate the sequencer for posting // the transaction to Ethereum. The gas cost is calculated based on the Ecotone // upgrade and the sequencer is expected to post the transaction using blobs. -func L1DataAnteHandler(ctx sdk.Context, tx sdk.Tx, rollupKeeper *rollupkeeper.Keeper) (sdk.Context, error) { //nolint:gocritic // hugeparam +func L1DataAnteHandler(ctx sdk.Context, tx sdk.Tx, rollupKeeper RollupKeeper) (sdk.Context, error) { //nolint:gocritic // hugeparam if rollupKeeper == nil { return ctx, errorsmod.Wrap(sdkerrors.ErrLogic, "rollup keeper is required for l1 data ante handler") }