diff --git a/abci/utils.go b/abci/utils.go index be859908..2923ff05 100644 --- a/abci/utils.go +++ b/abci/utils.go @@ -24,7 +24,6 @@ func ChainPrepareLanes(chain []block.Lane) block.PrepareLanesHandler { return func(ctx sdk.Context, partialProposal proposals.Proposal) (finalProposal proposals.Proposal, err error) { lane := chain[0] - lane.Logger().Info("preparing lane", "lane", lane.Name()) // Cache the context in the case where any of the lanes fail to prepare the proposal. cacheCtx, write := ctx.CacheContext() @@ -35,9 +34,6 @@ func ChainPrepareLanes(chain []block.Lane) block.PrepareLanesHandler { // and call the next lane in the chain to the prepare the proposal. defer func() { if rec := recover(); rec != nil || err != nil { - lane.Logger().Error("failed to prepare lane", "lane", lane.Name(), "err", err, "recover_error", rec) - lane.Logger().Info("skipping lane", "lane", lane.Name()) - if len(chain) <= 2 { // If there are only two lanes remaining, then the first lane in the chain // is the lane that failed to prepare the partial proposal and the second lane in the @@ -85,9 +81,6 @@ func ChainProcessLanes(partialProposals [][][]byte, chain []block.Lane) block.Pr return func(ctx sdk.Context, proposal proposals.Proposal) (proposals.Proposal, error) { lane := chain[0] partialProposal := partialProposals[0] - - lane.Logger().Info("processing lane", "lane", chain[0].Name()) - return lane.ProcessLane(ctx, proposal, partialProposal, ChainProcessLanes(partialProposals[1:], chain[1:])) } } diff --git a/block/base/abci.go b/block/base/abci.go index 49ee2270..886360b2 100644 --- a/block/base/abci.go +++ b/block/base/abci.go @@ -17,12 +17,19 @@ func (l *BaseLane) PrepareLane( proposal proposals.Proposal, next block.PrepareLanesHandler, ) (proposals.Proposal, error) { - limit := proposal.GetLaneLimits(l.cfg.MaxBlockSpace) + l.Logger().Info("preparing lane", "lane", l.Name()) // Select transactions from the lane respecting the selection logic of the lane and the // max block space for the lane. + limit := proposal.GetLaneLimits(l.cfg.MaxBlockSpace) txsToInclude, txsToRemove, err := l.prepareLaneHandler(ctx, proposal, limit) if err != nil { + l.Logger().Error( + "failed to prepare lane", + "lane", l.Name(), + "err", err, + ) + return proposal, err } @@ -68,6 +75,8 @@ func (l *BaseLane) ProcessLane( txs [][]byte, next block.ProcessLanesHandler, ) (proposals.Proposal, error) { + l.Logger().Info("processing lane", "lane", l.Name(), "num_txs_to_verify", len(txs)) + // Assume that this lane is processing sdk.Tx's and decode the transactions. decodedTxs, err := utils.GetDecodedTxs(l.TxDecoder(), txs) if err != nil { @@ -82,6 +91,13 @@ func (l *BaseLane) ProcessLane( // Verify the transactions that belong to this lane according to the verification logic of the lane. if err := l.processLaneHandler(ctx, decodedTxs); err != nil { + l.Logger().Error( + "failed to process lane", + "lane", l.Name(), + "err", err, + "num_txs_to_verify", len(decodedTxs), + ) + return proposal, err } diff --git a/block/lane.go b/block/lane.go index b4dcd1f7..1b092030 100644 --- a/block/lane.go +++ b/block/lane.go @@ -1,7 +1,6 @@ package block import ( - "cosmossdk.io/log" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool" @@ -57,9 +56,6 @@ type Lane interface { // GetMaxBlockSpace returns the max block space for the lane as a relative percentage. GetMaxBlockSpace() math.LegacyDec - // Logger returns the lane's logger. - Logger() log.Logger - // Name returns the name of the lane. Name() string diff --git a/block/mocks/lane.go b/block/mocks/lane.go index 9fc39e20..933afab2 100644 --- a/block/mocks/lane.go +++ b/block/mocks/lane.go @@ -7,8 +7,6 @@ import ( block "github.com/skip-mev/block-sdk/block" - log "cosmossdk.io/log" - math "cosmossdk.io/math" mempool "github.com/cosmos/cosmos-sdk/types/mempool" @@ -95,22 +93,6 @@ func (_m *Lane) Insert(_a0 context.Context, _a1 types.Tx) error { return r0 } -// Logger provides a mock function with given fields: -func (_m *Lane) Logger() log.Logger { - ret := _m.Called() - - var r0 log.Logger - if rf, ok := ret.Get(0).(func() log.Logger); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(log.Logger) - } - } - - return r0 -} - // Match provides a mock function with given fields: ctx, tx func (_m *Lane) Match(ctx types.Context, tx types.Tx) bool { ret := _m.Called(ctx, tx) diff --git a/lanes/mev/abci.go b/lanes/mev/abci.go index 20849602..ecea336b 100644 --- a/lanes/mev/abci.go +++ b/lanes/mev/abci.go @@ -9,7 +9,6 @@ import ( "github.com/skip-mev/block-sdk/block/base" "github.com/skip-mev/block-sdk/block/proposals" "github.com/skip-mev/block-sdk/block/utils" - "github.com/skip-mev/block-sdk/x/auction/types" ) // PrepareLaneHandler will attempt to select the highest bid transaction that is valid @@ -26,148 +25,36 @@ func (l *MEVLane) PrepareLaneHandler() base.PrepareLaneHandler { // Attempt to select the highest bid transaction that is valid and whose // bundled transactions are valid. - bidTxIterator := l.Select(ctx, nil) - selectBidTxLoop: - for ; bidTxIterator != nil; bidTxIterator = bidTxIterator.Next() { - cacheCtx, write := ctx.CacheContext() - bidTx := bidTxIterator.Tx() + for iterator := l.Select(ctx, nil); iterator != nil; iterator = iterator.Next() { + bidTx := iterator.Tx() - txInfo, err := utils.GetTxInfo(l.TxEncoder(), bidTx) - if err != nil { - l.Logger().Info("failed to get hash of auction bid tx", "err", err) + if !l.Match(ctx, bidTx) { + l.Logger().Info("failed to select auction bid tx for lane; tx does not match lane") txsToRemove = append(txsToRemove, bidTx) - continue selectBidTxLoop + continue } - // if the transaction is already in the (partial) block proposal, we skip it. - // - // TODO: Should we really be panic'ing here? - if proposal.Contains(txInfo.Hash) { - l.Logger().Info( - "failed to select auction bid tx for lane; tx is already in proposal", - "tx_hash", txInfo.Hash, - ) - - continue selectBidTxLoop - } - - if txInfo.Size > limit.MaxTxBytes { - l.Logger().Info( - "failed to select auction bid tx for lane; tx size is too large", - "tx_size", txInfo.Size, - "max_size", limit.MaxTxBytes, - "tx_hash", txInfo.Hash, - ) - - txsToRemove = append(txsToRemove, bidTx) - continue selectBidTxLoop - } - - if txInfo.GasLimit > limit.MaxGasLimit { - l.Logger().Info( - "failed to select auction bid tx for lane; tx gas limit is too large", - "tx_gas_limit", txInfo.GasLimit, - "max_gas_limit", limit.MaxGasLimit, - "tx_hash", txInfo.Hash, - ) - - txsToRemove = append(txsToRemove, bidTx) - continue selectBidTxLoop - } - - // Build the partial proposal by selecting the bid transaction and all of - // its bundled transactions. - bidInfo, err := l.GetAuctionBidInfo(bidTx) + bundle, err := l.VerifyBidBasic(bidTx, proposal, limit) if err != nil { l.Logger().Info( - "failed to get auction bid info", - "tx_hash", txInfo.Hash, + "failed to select auction bid tx for lane; tx is invalid", "err", err, ) - // Some transactions in the bundle may be malformed or invalid, so we - // remove the bid transaction and try the next top bid. txsToRemove = append(txsToRemove, bidTx) - continue selectBidTxLoop + continue } - // Verify the bid transaction and all of its bundled transactions. - if err := l.VerifyTx(cacheCtx, bidTx, bidInfo); err != nil { + cacheCtx, write := ctx.CacheContext() + if err := l.VerifyBidTx(cacheCtx, bidTx, bundle); err != nil { l.Logger().Info( - "failed to verify auction bid tx", - "tx_hash", txInfo.Hash, + "failed to select auction bid tx for lane; tx is invalid", "err", err, ) txsToRemove = append(txsToRemove, bidTx) - continue selectBidTxLoop - } - - // store the bytes of each ref tx as sdk.Tx bytes in order to build a valid proposal - gasLimitSum := txInfo.GasLimit - bundledTxs := make([]sdk.Tx, len(bidInfo.Transactions)) - for index, bundledTxBz := range bidInfo.Transactions { - bundleTx, err := l.WrapBundleTransaction(bundledTxBz) - if err != nil { - l.Logger().Info( - "failed to wrap bundled tx", - "tx_hash", txInfo.Hash, - "err", err, - ) - - txsToRemove = append(txsToRemove, bidTx) - continue selectBidTxLoop - } - - bundledTxInfo, err := utils.GetTxInfo(l.TxEncoder(), bundleTx) - if err != nil { - l.Logger().Info( - "failed to get hash of bundled tx", - "tx_hash", txInfo.Hash, - "err", err, - ) - - txsToRemove = append(txsToRemove, bidTx) - continue selectBidTxLoop - } - - // if the transaction is already in the (partial) block proposal, we skip it. - if proposal.Contains(bundledTxInfo.Hash) { - l.Logger().Info( - "failed to select auction bid tx for lane; tx is already in proposal", - "tx_hash", bundledTxInfo.Hash, - ) - - continue selectBidTxLoop - } - - // If the bundled transaction is a bid transaction, we skip it. - if l.Match(ctx, bundleTx) { - l.Logger().Info( - "failed to select auction bid tx for lane; bundled tx is another bid transaction", - "tx_hash", bundledTxInfo.Hash, - ) - - txsToRemove = append(txsToRemove, bidTx) - continue selectBidTxLoop - } - - if gasLimitSum += bundledTxInfo.GasLimit; gasLimitSum > limit.MaxGasLimit { - l.Logger().Info( - "failed to select auction bid tx for lane; tx gas limit is too large", - "tx_gas_limit", gasLimitSum, - "max_gas_limit", limit.MaxGasLimit, - "tx_hash", txInfo.Hash, - ) - - txsToRemove = append(txsToRemove, bidTx) - continue selectBidTxLoop - } - - bundleTxBz := make([]byte, bundledTxInfo.Size) - copy(bundleTxBz, bundledTxInfo.TxBytes) - bundledTxs[index] = bundleTx + continue } // At this point, both the bid transaction itself and all the bundled @@ -175,13 +62,13 @@ func (l *MEVLane) PrepareLaneHandler() base.PrepareLaneHandler { // all the bundled transactions. We also mark these transactions as seen and // update the total size selected thus far. txsToInclude = append(txsToInclude, bidTx) - txsToInclude = append(txsToInclude, bundledTxs...) + txsToInclude = append(txsToInclude, bundle...) // Write the cache context to the original context when we know we have a // valid bundle. write() - break selectBidTxLoop + break } return txsToInclude, txsToRemove, nil @@ -213,6 +100,10 @@ func (l *MEVLane) ProcessLaneHandler() base.ProcessLaneHandler { return fmt.Errorf("failed to get bid info from auction bid tx for lane %s: %w", l.Name(), err) } + if bidInfo == nil { + return fmt.Errorf("bid info is nil") + } + // Check that all bundled transactions were included. if len(bidInfo.Transactions)+1 != len(partialProposal) { return fmt.Errorf( @@ -223,46 +114,118 @@ func (l *MEVLane) ProcessLaneHandler() base.ProcessLaneHandler { ) } - // Verify the top-level bid transaction. - if ctx, err = l.AnteVerifyTx(ctx, bidTx, false); err != nil { - return fmt.Errorf("invalid bid tx; failed to execute ante handler: %w", err) - } - - // Verify all of the bundled transactions. + // Ensure the transactions in the proposal match the bundled transactions in the bid transaction. + bundle := partialProposal[1:] for index, bundledTxBz := range bidInfo.Transactions { bundledTx, err := l.WrapBundleTransaction(bundledTxBz) if err != nil { return fmt.Errorf("invalid bid tx; failed to decode bundled tx: %w", err) } - txBz, err := l.TxEncoder()(partialProposal[index+1]) + expectedTxBz, err := l.TxEncoder()(bundledTx) + if err != nil { + return fmt.Errorf("invalid bid tx; failed to encode bundled tx: %w", err) + } + + actualTxBz, err := l.TxEncoder()(bundle[index]) if err != nil { return fmt.Errorf("invalid bid tx; failed to encode tx: %w", err) } // Verify that the bundled transaction matches the transaction in the block proposal. - if !bytes.Equal(bundledTxBz, txBz) { + if !bytes.Equal(actualTxBz, expectedTxBz) { return fmt.Errorf("invalid bid tx; bundled tx does not match tx in block proposal") } + } - // Verify this is not another bid transaction. - if l.Match(ctx, bundledTx) { - return fmt.Errorf("invalid bid tx; bundled tx is another bid transaction") - } - - if ctx, err = l.AnteVerifyTx(ctx, bundledTx, false); err != nil { - return fmt.Errorf("invalid bid tx; failed to execute bundled transaction: %w", err) - } + // Verify the top-level bid transaction. + if err := l.VerifyBidTx(ctx, bidTx, bundle); err != nil { + return fmt.Errorf("invalid bid tx; failed to verify bid tx: %w", err) } return nil } } -// VerifyTx will verify that the bid transaction and all of its bundled -// transactions are valid. It will return an error if any of the transactions -// are invalid. -func (l *MEVLane) VerifyTx(ctx sdk.Context, bidTx sdk.Tx, bidInfo *types.BidInfo) (err error) { +// VerifyBidBasic will verify that the bid transaction and all of its bundled +// transactions respect the basic invariants of the lane (e.g. size, gas limit). +func (l *MEVLane) VerifyBidBasic( + bidTx sdk.Tx, + proposal proposals.Proposal, + limit proposals.LaneLimits, +) ([]sdk.Tx, error) { + // Verify the transaction is a bid transaction. + bidInfo, err := l.GetAuctionBidInfo(bidTx) + if err != nil { + return nil, fmt.Errorf("failed to get bid info from auction bid tx for lane %s: %w", l.Name(), err) + } + + if bidInfo == nil { + return nil, fmt.Errorf("bid info is nil") + } + + txInfo, err := utils.GetTxInfo(l.TxEncoder(), bidTx) + if err != nil { + return nil, fmt.Errorf("err retrieving transaction info: %s", err) + } + + // This should never happen, but we check just in case. + if proposal.Contains(txInfo.Hash) { + return nil, fmt.Errorf("invalid bid tx; bid tx is already in the proposal") + } + + totalSize := txInfo.Size + totalGasLimit := txInfo.GasLimit + bundle := make([]sdk.Tx, len(bidInfo.Transactions)) + + // Verify size and gas limit of the bundled transactions. + for index, bundledTxBz := range bidInfo.Transactions { + bundledTx, err := l.WrapBundleTransaction(bundledTxBz) + if err != nil { + return nil, fmt.Errorf("invalid bid tx; failed to decode bundled tx: %w", err) + } + + bundledTxInfo, err := utils.GetTxInfo(l.TxEncoder(), bundledTx) + if err != nil { + return nil, fmt.Errorf("err retrieving transaction info: %s", err) + } + + if proposal.Contains(bundledTxInfo.Hash) { + return nil, fmt.Errorf("invalid bid tx; bundled tx is already in the proposal") + } + + totalSize += bundledTxInfo.Size + totalGasLimit += bundledTxInfo.GasLimit + bundle[index] = bundledTx + } + + if totalSize > limit.MaxTxBytes { + return nil, fmt.Errorf( + "partial proposal is too large: %d > %d", + totalSize, + limit.MaxTxBytes, + ) + } + + if totalGasLimit > limit.MaxGasLimit { + return nil, fmt.Errorf( + "partial proposal consumes too much gas: %d > %d", + totalGasLimit, + limit.MaxGasLimit, + ) + } + + return bundle, nil +} + +// VerifyBidTx will verify that the bid transaction and all of its bundled +// transactions are valid. +func (l *MEVLane) VerifyBidTx(ctx sdk.Context, bidTx sdk.Tx, bundle []sdk.Tx) error { + bidInfo, err := l.GetAuctionBidInfo(bidTx) + if err != nil { + return fmt.Errorf("failed to get bid info from auction bid tx for lane %s: %w", l.Name(), err) + } + if bidInfo == nil { return fmt.Errorf("bid info is nil") } @@ -273,10 +236,9 @@ func (l *MEVLane) VerifyTx(ctx sdk.Context, bidTx sdk.Tx, bidInfo *types.BidInfo } // verify all of the bundled transactions - for _, tx := range bidInfo.Transactions { - bundledTx, err := l.WrapBundleTransaction(tx) - if err != nil { - return fmt.Errorf("invalid bid tx; failed to decode bundled tx: %w", err) + for _, bundledTx := range bundle { + if l.Match(ctx, bundledTx) { + return fmt.Errorf("invalid bid tx; bundled tx is another bid transaction") } if ctx, err = l.AnteVerifyTx(ctx, bundledTx, false); err != nil { diff --git a/lanes/mev/abci_test.go b/lanes/mev/abci_test.go new file mode 100644 index 00000000..29dd86a7 --- /dev/null +++ b/lanes/mev/abci_test.go @@ -0,0 +1,598 @@ +package mev_test + +import ( + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/skip-mev/block-sdk/block" + "github.com/skip-mev/block-sdk/block/proposals" + "github.com/skip-mev/block-sdk/block/utils" + testutils "github.com/skip-mev/block-sdk/testutils" +) + +func (s *MEVTestSuite) TestPrepareLane() { + s.Run("can prepare a lane with no txs in mempool", func() { + lane := s.initLane(math.LegacyOneDec(), nil) + proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 200, 100) + + proposal, err := lane.PrepareLane(s.ctx, proposal, block.NoOpPrepareLanesHandler()) + s.Require().NoError(err) + s.Require().Equal(0, len(proposal.Txs)) + s.Require().Equal(0, len(proposal.Info.TxsByLane)) + s.Require().Equal(int64(0), proposal.Info.BlockSize) + s.Require().Equal(uint64(0), proposal.Info.GasLimit) + }) + + s.Run("can prepare a lane with a single bid tx in mempool", func() { + bidTx, _, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + 0, + 0, + nil, + 100, + ) + s.Require().NoError(err) + size := s.getTxSize(bidTx) + + lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx: true}) + s.Require().NoError(lane.Insert(s.ctx, bidTx)) + + proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 200, 100) + + proposal, err = lane.PrepareLane(s.ctx, proposal, block.NoOpPrepareLanesHandler()) + s.Require().NoError(err) + s.Require().Equal(1, len(proposal.Txs)) + s.Require().Equal(1, len(proposal.Info.TxsByLane)) + s.Require().Equal(size, proposal.Info.BlockSize) + s.Require().Equal(uint64(100), proposal.Info.GasLimit) + + expectedProposal := []sdk.Tx{bidTx} + txBzs, err := utils.GetEncodedTxs(s.encCfg.TxConfig.TxEncoder(), expectedProposal) + s.Require().NoError(err) + s.Require().Equal(txBzs[0], proposal.Txs[0]) + }) + + s.Run("can prepare a lane with multiple bid txs where highest bid fails", func() { + bidTx1, _, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + 0, + 0, + nil, + 100, + ) + s.Require().NoError(err) + + bidTx2, _, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[1], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(200)), + 0, + 0, + nil, + 100, + ) + s.Require().NoError(err) + + lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx1: true, bidTx2: false}) + s.Require().NoError(lane.Insert(s.ctx, bidTx1)) + s.Require().NoError(lane.Insert(s.ctx, bidTx2)) + + proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 20000, 100000) + + proposal, err = lane.PrepareLane(s.ctx, proposal, block.NoOpPrepareLanesHandler()) + s.Require().NoError(err) + s.Require().Equal(1, len(proposal.Txs)) + s.Require().Equal(1, len(proposal.Info.TxsByLane)) + s.Require().Equal(s.getTxSize(bidTx1), proposal.Info.BlockSize) + s.Require().Equal(uint64(100), proposal.Info.GasLimit) + + expectedProposal := []sdk.Tx{bidTx1} + txBzs, err := utils.GetEncodedTxs(s.encCfg.TxConfig.TxEncoder(), expectedProposal) + s.Require().NoError(err) + s.Require().Equal(txBzs[0], proposal.Txs[0]) + }) + + s.Run("can prepare a lane with multiple bid txs where highest bid passes", func() { + bidTx1, _, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + 0, + 0, + nil, + 100, + ) + s.Require().NoError(err) + + bidTx2, _, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[1], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(200)), + 0, + 0, + nil, + 100, + ) + s.Require().NoError(err) + + lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx1: false, bidTx2: true}) + s.Require().NoError(lane.Insert(s.ctx, bidTx1)) + s.Require().NoError(lane.Insert(s.ctx, bidTx2)) + + proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 20000, 100000) + + proposal, err = lane.PrepareLane(s.ctx, proposal, block.NoOpPrepareLanesHandler()) + s.Require().NoError(err) + s.Require().Equal(1, len(proposal.Txs)) + s.Require().Equal(1, len(proposal.Info.TxsByLane)) + s.Require().Equal(s.getTxSize(bidTx2), proposal.Info.BlockSize) + s.Require().Equal(uint64(100), proposal.Info.GasLimit) + + expectedProposal := []sdk.Tx{bidTx2} + txBzs, err := utils.GetEncodedTxs(s.encCfg.TxConfig.TxEncoder(), expectedProposal) + s.Require().NoError(err) + s.Require().Equal(txBzs[0], proposal.Txs[0]) + }) + + s.Run("can build a proposal with bid tx that has a bundle", func() { + bidTx, bundle, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + 0, + 0, + s.accounts[0:2], + 100, + ) + s.Require().NoError(err) + + lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx: true, bundle[0]: true, bundle[1]: true}) + s.Require().NoError(lane.Insert(s.ctx, bidTx)) + + proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 20000, 100000) + + proposal, err = lane.PrepareLane(s.ctx, proposal, block.NoOpPrepareLanesHandler()) + s.Require().NoError(err) + s.Require().Equal(3, len(proposal.Txs)) + s.Require().Equal(1, len(proposal.Info.TxsByLane)) + s.Require().Equal(uint64(3), proposal.Info.TxsByLane[lane.Name()]) + s.Require().Equal(s.getTxSize(bidTx)+s.getTxSize(bundle[0])+s.getTxSize(bundle[1]), proposal.Info.BlockSize) + s.Require().Equal(uint64(100), proposal.Info.GasLimit) + + expectedProposal := []sdk.Tx{bidTx, bundle[0], bundle[1]} + txBzs, err := utils.GetEncodedTxs(s.encCfg.TxConfig.TxEncoder(), expectedProposal) + s.Require().NoError(err) + s.Require().Equal(txBzs, proposal.Txs) + }) + + s.Run("can reject a bid that is too large", func() { + bidTx, bundle, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(200)), + 0, + 0, + s.accounts[0:2], + 100, + ) + s.Require().NoError(err) + + lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx: true, bundle[0]: true, bundle[1]: true}) + s.Require().NoError(lane.Insert(s.ctx, bidTx)) + + proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), s.getTxSize(bidTx), 100000) + + proposal, err = lane.PrepareLane(s.ctx, proposal, block.NoOpPrepareLanesHandler()) + s.Require().NoError(err) + s.Require().Equal(0, len(proposal.Txs)) + s.Require().Equal(0, len(proposal.Info.TxsByLane)) + s.Require().Equal(int64(0), proposal.Info.BlockSize) + s.Require().Equal(uint64(0), proposal.Info.GasLimit) + }) + + s.Run("can reject a bid that is too gas intensive", func() { + bidTx, _, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(200)), + 0, + 0, + nil, + 100, + ) + s.Require().NoError(err) + + lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx: true}) + s.Require().NoError(lane.Insert(s.ctx, bidTx)) + + proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), s.getTxSize(bidTx), 99) + + proposal, err = lane.PrepareLane(s.ctx, proposal, block.NoOpPrepareLanesHandler()) + s.Require().NoError(err) + s.Require().Equal(0, len(proposal.Txs)) + s.Require().Equal(0, len(proposal.Info.TxsByLane)) + s.Require().Equal(int64(0), proposal.Info.BlockSize) + s.Require().Equal(uint64(0), proposal.Info.GasLimit) + }) +} + +func (s *MEVTestSuite) TestProcessLane() { + s.Run("can process an empty proposal", func() { + lane := s.initLane(math.LegacyOneDec(), nil) + proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 200, 100) + + proposal, err := lane.ProcessLane(s.ctx, proposal, nil, block.NoOpProcessLanesHandler()) + s.Require().NoError(err) + s.Require().Equal(0, len(proposal.Txs)) + s.Require().Equal(0, len(proposal.Info.TxsByLane)) + s.Require().Equal(int64(0), proposal.Info.BlockSize) + s.Require().Equal(uint64(0), proposal.Info.GasLimit) + }) + + s.Run("can process a proposal with tx that does not belong to this lane", func() { + txBz, err := testutils.CreateRandomTxBz(s.encCfg.TxConfig, s.accounts[0], 0, 1, 0, 100) + s.Require().NoError(err) + + lane := s.initLane(math.LegacyOneDec(), nil) + proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 200, 100) + + _, err = lane.ProcessLane(s.ctx, proposal, [][]byte{txBz}, block.NoOpProcessLanesHandler()) + s.Require().Error(err) + }) + + s.Run("can process a proposal with bad bid tx", func() { + bidTx, _, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + 0, + 0, + nil, + 100, + ) + s.Require().NoError(err) + + partialProposal, err := utils.GetEncodedTxs(s.encCfg.TxConfig.TxEncoder(), []sdk.Tx{bidTx}) + s.Require().NoError(err) + + lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx: false}) + proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 200000, 1000000) + + _, err = lane.ProcessLane(s.ctx, proposal, partialProposal, block.NoOpProcessLanesHandler()) + s.Require().Error(err) + }) + + s.Run("can process a proposal with a bad bundled tx", func() { + bidTx, bundle, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + 0, + 0, + s.accounts[0:2], + 100, + ) + s.Require().NoError(err) + + partialProposal, err := utils.GetEncodedTxs(s.encCfg.TxConfig.TxEncoder(), []sdk.Tx{bidTx, bundle[0], bundle[1]}) + s.Require().NoError(err) + + lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx: true, bundle[0]: true, bundle[1]: false}) + proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 200000, 1000000) + + _, err = lane.ProcessLane(s.ctx, proposal, partialProposal, block.NoOpProcessLanesHandler()) + s.Require().Error(err) + }) + + s.Run("can process a proposal with mismatching txs in bundle", func() { + bidTx, bundle, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + 0, + 0, + s.accounts[0:2], + 100, + ) + s.Require().NoError(err) + + partialProposal, err := utils.GetEncodedTxs(s.encCfg.TxConfig.TxEncoder(), []sdk.Tx{bidTx, bundle[1], bundle[0]}) + s.Require().NoError(err) + + lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx: true, bundle[0]: true, bundle[1]: true}) + proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 200000, 1000000) + + _, err = lane.ProcessLane(s.ctx, proposal, partialProposal, block.NoOpProcessLanesHandler()) + s.Require().Error(err) + }) + + s.Run("can process a proposal with missing bundle tx", func() { + bidTx, bundle, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + 0, + 0, + s.accounts[0:2], + 100, + ) + s.Require().NoError(err) + + partialProposal, err := utils.GetEncodedTxs(s.encCfg.TxConfig.TxEncoder(), []sdk.Tx{bidTx, bundle[0]}) + s.Require().NoError(err) + + lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx: true, bundle[0]: true}) + proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 200000, 1000000) + + _, err = lane.ProcessLane(s.ctx, proposal, partialProposal, block.NoOpProcessLanesHandler()) + s.Require().Error(err) + }) + + s.Run("can process a valid proposal", func() { + bidTx, bundle, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + 0, + 0, + s.accounts[0:2], + 100, + ) + s.Require().NoError(err) + + partialProposal, err := utils.GetEncodedTxs(s.encCfg.TxConfig.TxEncoder(), []sdk.Tx{bidTx, bundle[0], bundle[1]}) + s.Require().NoError(err) + + lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx: true, bundle[0]: true, bundle[1]: true}) + proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 200000, 1000000) + + _, err = lane.ProcessLane(s.ctx, proposal, partialProposal, block.NoOpProcessLanesHandler()) + s.Require().NoError(err) + }) + + s.Run("can process a valid proposal with a single bid with no bundle", func() { + bidTx, _, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin("stake", math.NewInt(100)), + 0, + 0, + nil, + 100, + ) + s.Require().NoError(err) + + partialProposal, err := utils.GetEncodedTxs(s.encCfg.TxConfig.TxEncoder(), []sdk.Tx{bidTx}) + s.Require().NoError(err) + + lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx: true}) + proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 200000, 1000000) + + _, err = lane.ProcessLane(s.ctx, proposal, partialProposal, block.NoOpProcessLanesHandler()) + s.Require().NoError(err) + }) + + s.Run("can reject a block proposal that exceeds its gas limit", func() { + bidTx, bundle, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin("stake", math.NewInt(100)), + 0, + 0, + s.accounts[0:2], + 100, + ) + s.Require().NoError(err) + + partialProposal, err := utils.GetEncodedTxs(s.encCfg.TxConfig.TxEncoder(), []sdk.Tx{bidTx, bundle[0], bundle[1]}) + s.Require().NoError(err) + + lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx: true, bundle[0]: true, bundle[1]: true}) + proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 20000, 99) + + _, err = lane.ProcessLane(s.ctx, proposal, partialProposal, block.NoOpProcessLanesHandler()) + s.Require().Error(err) + }) + + s.Run("can reject a block proposal that exceeds its block size", func() { + bidTx, bundle, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin("stake", math.NewInt(100)), + 0, + 0, + s.accounts[0:2], + 100, + ) + s.Require().NoError(err) + + partialProposal, err := utils.GetEncodedTxs(s.encCfg.TxConfig.TxEncoder(), []sdk.Tx{bidTx, bundle[0], bundle[1]}) + s.Require().NoError(err) + + lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx: true, bundle[0]: true, bundle[1]: true}) + proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 200, 100) + + _, err = lane.ProcessLane(s.ctx, proposal, partialProposal, block.NoOpProcessLanesHandler()) + s.Require().Error(err) + }) +} + +func (s *MEVTestSuite) TestVerifyBidBasic() { + lane := s.initLane(math.LegacyOneDec(), nil) + proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 200, 100) + limits := proposal.GetLaneLimits(lane.GetMaxBlockSpace()) + + s.Run("can verify a bid with no bundled txs", func() { + bidTx, expectedBundle, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + 0, + 0, + nil, + 100, + ) + s.Require().NoError(err) + + bundle, err := lane.VerifyBidBasic(bidTx, proposal, limits) + s.Require().NoError(err) + s.compare(bundle, expectedBundle) + }) + + s.Run("can reject a tx that is not a bid", func() { + tx, err := testutils.CreateRandomTx( + s.encCfg.TxConfig, + s.accounts[0], + 0, + 1, + 0, + 100, + ) + s.Require().NoError(err) + + _, err = lane.VerifyBidBasic(tx, proposal, limits) + s.Require().Error(err) + }) + + s.Run("can reject a bundle that is too gas intensive", func() { + bidTx, _, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + 0, + 0, + nil, + 101, + ) + s.Require().NoError(err) + + _, err = lane.VerifyBidBasic(bidTx, proposal, limits) + s.Require().Error(err) + }) + + s.Run("can reject a bundle that is too large", func() { + bidTx, _, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + 0, + 0, + s.accounts[0:2], + 100, + ) + s.Require().NoError(err) + + size := s.getTxSize(bidTx) + proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), size-1, 100) + limits := proposal.GetLaneLimits(lane.GetMaxBlockSpace()) + + _, err = lane.VerifyBidBasic(bidTx, proposal, limits) + s.Require().Error(err) + }) + + s.Run("can reject a bundle with malformed txs", func() { + bidMsg, err := testutils.CreateMsgAuctionBid( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + 0, + 3, + ) + s.Require().NoError(err) + + bidMsg.Transactions[2] = []byte("invalid") + + bidTx, err := testutils.CreateTx( + s.encCfg.TxConfig, + s.accounts[0], + 0, + 0, + []sdk.Msg{bidMsg}, + ) + s.Require().NoError(err) + + _, err = lane.VerifyBidBasic(bidTx, proposal, limits) + s.Require().Error(err) + }) +} + +func (s *MEVTestSuite) TestVerifyBidTx() { + s.Run("can verify a valid bid", func() { + bidTx, bundle, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + 0, + 0, + nil, + 100, + ) + s.Require().NoError(err) + + lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx: true}) + s.Require().NoError(lane.VerifyBidTx(s.ctx, bidTx, bundle)) + }) + + s.Run("can reject a bid transaction", func() { + bidTx, bundle, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + 0, + 0, + nil, + 100, + ) + s.Require().NoError(err) + + lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx: false}) + s.Require().Error(lane.VerifyBidTx(s.ctx, bidTx, bundle)) + }) + + s.Run("can reject a bid transaction with a bad bundle", func() { + bidTx, bundle, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + 0, + 0, + s.accounts[0:2], + 100, + ) + s.Require().NoError(err) + + lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx: true, bundle[0]: true, bundle[1]: false}) + s.Require().Error(lane.VerifyBidTx(s.ctx, bidTx, bundle)) + }) + + s.Run("can reject a bid transaction with a bundle that has another bid tx", func() { + bidTx, bundle, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + 0, + 0, + s.accounts[0:2], + 100, + ) + s.Require().NoError(err) + + otherBidTx, _, err := testutils.CreateAuctionTx( + s.encCfg.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + 0, + 0, + nil, + 100, + ) + s.Require().NoError(err) + bundle = append(bundle, otherBidTx) + + lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx: true, bundle[0]: true, bundle[1]: true, otherBidTx: true}) + s.Require().Error(lane.VerifyBidTx(s.ctx, bidTx, bundle)) + }) +} diff --git a/lanes/mev/factory_test.go b/lanes/mev/factory_test.go index 29570027..6102bc2d 100644 --- a/lanes/mev/factory_test.go +++ b/lanes/mev/factory_test.go @@ -9,7 +9,7 @@ import ( testutils "github.com/skip-mev/block-sdk/testutils" ) -func (suite *MEVTestSuite) TestIsAuctionTx() { +func (s *MEVTestSuite) TestIsAuctionTx() { testCases := []struct { name string createTx func() sdk.Tx @@ -19,8 +19,8 @@ func (suite *MEVTestSuite) TestIsAuctionTx() { { "normal sdk tx", func() sdk.Tx { - tx, err := testutils.CreateRandomTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 2, 0, 0) - suite.Require().NoError(err) + tx, err := testutils.CreateRandomTx(s.encCfg.TxConfig, s.accounts[0], 0, 2, 0, 0) + s.Require().NoError(err) return tx }, false, @@ -29,14 +29,14 @@ func (suite *MEVTestSuite) TestIsAuctionTx() { { "malformed auction bid tx", func() sdk.Tx { - msgAuctionBid, err := testutils.CreateMsgAuctionBid(suite.encCfg.TxConfig, suite.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) - suite.Require().NoError(err) + msgAuctionBid, err := testutils.CreateMsgAuctionBid(s.encCfg.TxConfig, s.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) + s.Require().NoError(err) - msgs := testutils.CreateRandomMsgs(suite.accounts[0].Address, 2) + msgs := testutils.CreateRandomMsgs(s.accounts[0].Address, 2) msgs = append(msgs, msgAuctionBid) - tx, err := testutils.CreateTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 0, msgs) - suite.Require().NoError(err) + tx, err := testutils.CreateTx(s.encCfg.TxConfig, s.accounts[0], 0, 0, msgs) + s.Require().NoError(err) return tx }, false, @@ -45,13 +45,13 @@ func (suite *MEVTestSuite) TestIsAuctionTx() { { "valid auction bid tx", func() sdk.Tx { - msgAuctionBid, err := testutils.CreateMsgAuctionBid(suite.encCfg.TxConfig, suite.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) - suite.Require().NoError(err) + msgAuctionBid, err := testutils.CreateMsgAuctionBid(s.encCfg.TxConfig, s.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) + s.Require().NoError(err) msgs := []sdk.Msg{msgAuctionBid} - tx, err := testutils.CreateTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 0, msgs) - suite.Require().NoError(err) + tx, err := testutils.CreateTx(s.encCfg.TxConfig, s.accounts[0], 0, 0, msgs) + s.Require().NoError(err) return tx }, true, @@ -60,16 +60,16 @@ func (suite *MEVTestSuite) TestIsAuctionTx() { { "tx with multiple MsgAuctionBid messages", func() sdk.Tx { - bid1, err := testutils.CreateMsgAuctionBid(suite.encCfg.TxConfig, suite.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) - suite.Require().NoError(err) + bid1, err := testutils.CreateMsgAuctionBid(s.encCfg.TxConfig, s.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) + s.Require().NoError(err) - bid2, err := testutils.CreateMsgAuctionBid(suite.encCfg.TxConfig, suite.accounts[0], sdk.NewInt64Coin("stake", 100), 1, 2) - suite.Require().NoError(err) + bid2, err := testutils.CreateMsgAuctionBid(s.encCfg.TxConfig, s.accounts[0], sdk.NewInt64Coin("stake", 100), 1, 2) + s.Require().NoError(err) msgs := []sdk.Msg{bid1, bid2} - tx, err := testutils.CreateTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 0, msgs) - suite.Require().NoError(err) + tx, err := testutils.CreateTx(s.encCfg.TxConfig, s.accounts[0], 0, 0, msgs) + s.Require().NoError(err) return tx }, false, @@ -78,22 +78,22 @@ func (suite *MEVTestSuite) TestIsAuctionTx() { } for _, tc := range testCases { - suite.Run(tc.name, func() { + s.Run(tc.name, func() { tx := tc.createTx() - bidInfo, err := suite.config.GetAuctionBidInfo(tx) + bidInfo, err := s.config.GetAuctionBidInfo(tx) - suite.Require().Equal(tc.isAuctionTx, bidInfo != nil) + s.Require().Equal(tc.isAuctionTx, bidInfo != nil) if tc.expectedError { - suite.Require().Error(err) + s.Require().Error(err) } else { - suite.Require().NoError(err) + s.Require().NoError(err) } }) } } -func (suite *MEVTestSuite) TestGetTransactionSigners() { +func (s *MEVTestSuite) TestGetTransactionSigners() { testCases := []struct { name string createTx func() sdk.Tx @@ -104,20 +104,20 @@ func (suite *MEVTestSuite) TestGetTransactionSigners() { "normal auction tx", func() sdk.Tx { tx, err := testutils.CreateAuctionTxWithSigners( - suite.encCfg.TxConfig, - suite.accounts[0], + s.encCfg.TxConfig, + s.accounts[0], sdk.NewCoin("stake", math.NewInt(100)), 1, 0, - suite.accounts[0:1], + s.accounts[0:1], ) - suite.Require().NoError(err) + s.Require().NoError(err) return tx }, []map[string]struct{}{ { - suite.accounts[0].Address.String(): {}, + s.accounts[0].Address.String(): {}, }, }, false, @@ -125,8 +125,8 @@ func (suite *MEVTestSuite) TestGetTransactionSigners() { { "normal sdk tx", func() sdk.Tx { - tx, err := testutils.CreateRandomTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 10, 0, 0) - suite.Require().NoError(err) + tx, err := testutils.CreateRandomTx(s.encCfg.TxConfig, s.accounts[0], 0, 10, 0, 0) + s.Require().NoError(err) return tx }, @@ -137,26 +137,26 @@ func (suite *MEVTestSuite) TestGetTransactionSigners() { "multiple signers on auction tx", func() sdk.Tx { tx, err := testutils.CreateAuctionTxWithSigners( - suite.encCfg.TxConfig, - suite.accounts[0], + s.encCfg.TxConfig, + s.accounts[0], sdk.NewCoin("stake", math.NewInt(100)), 1, 0, - suite.accounts[0:3], + s.accounts[0:3], ) - suite.Require().NoError(err) + s.Require().NoError(err) return tx }, []map[string]struct{}{ { - suite.accounts[0].Address.String(): {}, + s.accounts[0].Address.String(): {}, }, { - suite.accounts[1].Address.String(): {}, + s.accounts[1].Address.String(): {}, }, { - suite.accounts[2].Address.String(): {}, + s.accounts[2].Address.String(): {}, }, }, false, @@ -164,20 +164,20 @@ func (suite *MEVTestSuite) TestGetTransactionSigners() { } for _, tc := range testCases { - suite.Run(tc.name, func() { + s.Run(tc.name, func() { tx := tc.createTx() - bidInfo, _ := suite.config.GetAuctionBidInfo(tx) + bidInfo, _ := s.config.GetAuctionBidInfo(tx) if tc.expectedError { - suite.Require().Nil(bidInfo) + s.Require().Nil(bidInfo) } else { - suite.Require().Equal(tc.expectedSigners, bidInfo.Signers) + s.Require().Equal(tc.expectedSigners, bidInfo.Signers) } }) } } -func (suite *MEVTestSuite) TestWrapBundleTransaction() { +func (s *MEVTestSuite) TestWrapBundleTransaction() { testCases := []struct { name string createBundleTx func() (sdk.Tx, []byte) @@ -186,11 +186,11 @@ func (suite *MEVTestSuite) TestWrapBundleTransaction() { { "normal sdk tx", func() (sdk.Tx, []byte) { - tx, err := testutils.CreateRandomTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 1, 0, 0) - suite.Require().NoError(err) + tx, err := testutils.CreateRandomTx(s.encCfg.TxConfig, s.accounts[0], 0, 1, 0, 0) + s.Require().NoError(err) - bz, err := suite.encCfg.TxConfig.TxEncoder()(tx) - suite.Require().NoError(err) + bz, err := s.encCfg.TxConfig.TxEncoder()(tx) + s.Require().NoError(err) return tx, bz }, @@ -209,28 +209,28 @@ func (suite *MEVTestSuite) TestWrapBundleTransaction() { } for _, tc := range testCases { - suite.Run(tc.name, func() { + s.Run(tc.name, func() { tx, bz := tc.createBundleTx() - wrappedTx, err := suite.config.WrapBundleTransaction(bz) + wrappedTx, err := s.config.WrapBundleTransaction(bz) if tc.expectedError { - suite.Require().Error(err) + s.Require().Error(err) } else { - suite.Require().NoError(err) + s.Require().NoError(err) - txBytes, err := suite.encCfg.TxConfig.TxEncoder()(tx) - suite.Require().NoError(err) + txBytes, err := s.encCfg.TxConfig.TxEncoder()(tx) + s.Require().NoError(err) - wrappedTxBytes, err := suite.encCfg.TxConfig.TxEncoder()(wrappedTx) - suite.Require().NoError(err) + wrappedTxBytes, err := s.encCfg.TxConfig.TxEncoder()(wrappedTx) + s.Require().NoError(err) - suite.Require().Equal(txBytes, wrappedTxBytes) + s.Require().Equal(txBytes, wrappedTxBytes) } }) } } -func (suite *MEVTestSuite) TestGetBidder() { +func (s *MEVTestSuite) TestGetBidder() { testCases := []struct { name string createTx func() sdk.Tx @@ -241,8 +241,8 @@ func (suite *MEVTestSuite) TestGetBidder() { { "normal sdk tx", func() sdk.Tx { - tx, err := testutils.CreateRandomTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 1, 0, 0) - suite.Require().NoError(err) + tx, err := testutils.CreateRandomTx(s.encCfg.TxConfig, s.accounts[0], 0, 1, 0, 0) + s.Require().NoError(err) return tx }, @@ -253,32 +253,32 @@ func (suite *MEVTestSuite) TestGetBidder() { { "valid auction tx", func() sdk.Tx { - msgAuctionBid, err := testutils.CreateMsgAuctionBid(suite.encCfg.TxConfig, suite.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) - suite.Require().NoError(err) + msgAuctionBid, err := testutils.CreateMsgAuctionBid(s.encCfg.TxConfig, s.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) + s.Require().NoError(err) msgs := []sdk.Msg{msgAuctionBid} - tx, err := testutils.CreateTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 0, msgs) - suite.Require().NoError(err) + tx, err := testutils.CreateTx(s.encCfg.TxConfig, s.accounts[0], 0, 0, msgs) + s.Require().NoError(err) return tx }, - suite.accounts[0].Address.String(), + s.accounts[0].Address.String(), false, true, }, { "invalid auction tx", func() sdk.Tx { - msgAuctionBid, err := testutils.CreateMsgAuctionBid(suite.encCfg.TxConfig, suite.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) - suite.Require().NoError(err) + msgAuctionBid, err := testutils.CreateMsgAuctionBid(s.encCfg.TxConfig, s.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) + s.Require().NoError(err) - randomMsg := testutils.CreateRandomMsgs(suite.accounts[0].Address, 1)[0] - suite.Require().NoError(err) + randomMsg := testutils.CreateRandomMsgs(s.accounts[0].Address, 1)[0] + s.Require().NoError(err) msgs := []sdk.Msg{msgAuctionBid, randomMsg} - tx, err := testutils.CreateTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 0, msgs) - suite.Require().NoError(err) + tx, err := testutils.CreateTx(s.encCfg.TxConfig, s.accounts[0], 0, 0, msgs) + s.Require().NoError(err) return tx }, "", @@ -288,24 +288,24 @@ func (suite *MEVTestSuite) TestGetBidder() { } for _, tc := range testCases { - suite.Run(tc.name, func() { + s.Run(tc.name, func() { tx := tc.createTx() - bidInfo, err := suite.config.GetAuctionBidInfo(tx) + bidInfo, err := s.config.GetAuctionBidInfo(tx) if tc.expectedError { - suite.Require().Error(err) + s.Require().Error(err) } else { - suite.Require().NoError(err) + s.Require().NoError(err) if tc.isAuctionTx { - suite.Require().Equal(tc.expectedBidder, bidInfo.Bidder.String()) + s.Require().Equal(tc.expectedBidder, bidInfo.Bidder.String()) } } }) } } -func (suite *MEVTestSuite) TestGetBid() { +func (s *MEVTestSuite) TestGetBid() { testCases := []struct { name string createTx func() sdk.Tx @@ -316,8 +316,8 @@ func (suite *MEVTestSuite) TestGetBid() { { "normal sdk tx", func() sdk.Tx { - tx, err := testutils.CreateRandomTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 1, 0, 0) - suite.Require().NoError(err) + tx, err := testutils.CreateRandomTx(s.encCfg.TxConfig, s.accounts[0], 0, 1, 0, 0) + s.Require().NoError(err) return tx }, @@ -328,13 +328,13 @@ func (suite *MEVTestSuite) TestGetBid() { { "valid auction tx", func() sdk.Tx { - msgAuctionBid, err := testutils.CreateMsgAuctionBid(suite.encCfg.TxConfig, suite.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) - suite.Require().NoError(err) + msgAuctionBid, err := testutils.CreateMsgAuctionBid(s.encCfg.TxConfig, s.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) + s.Require().NoError(err) msgs := []sdk.Msg{msgAuctionBid} - tx, err := testutils.CreateTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 0, msgs) - suite.Require().NoError(err) + tx, err := testutils.CreateTx(s.encCfg.TxConfig, s.accounts[0], 0, 0, msgs) + s.Require().NoError(err) return tx }, sdk.NewInt64Coin("stake", 100), @@ -344,16 +344,16 @@ func (suite *MEVTestSuite) TestGetBid() { { "invalid auction tx", func() sdk.Tx { - msgAuctionBid, err := testutils.CreateMsgAuctionBid(suite.encCfg.TxConfig, suite.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) - suite.Require().NoError(err) + msgAuctionBid, err := testutils.CreateMsgAuctionBid(s.encCfg.TxConfig, s.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) + s.Require().NoError(err) - randomMsg := testutils.CreateRandomMsgs(suite.accounts[0].Address, 1)[0] - suite.Require().NoError(err) + randomMsg := testutils.CreateRandomMsgs(s.accounts[0].Address, 1)[0] + s.Require().NoError(err) msgs := []sdk.Msg{msgAuctionBid, randomMsg} - tx, err := testutils.CreateTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 0, msgs) - suite.Require().NoError(err) + tx, err := testutils.CreateTx(s.encCfg.TxConfig, s.accounts[0], 0, 0, msgs) + s.Require().NoError(err) return tx }, sdk.Coin{}, @@ -363,24 +363,24 @@ func (suite *MEVTestSuite) TestGetBid() { } for _, tc := range testCases { - suite.Run(tc.name, func() { + s.Run(tc.name, func() { tx := tc.createTx() - bidInfo, err := suite.config.GetAuctionBidInfo(tx) + bidInfo, err := s.config.GetAuctionBidInfo(tx) if tc.expectedError { - suite.Require().Error(err) + s.Require().Error(err) } else { - suite.Require().NoError(err) + s.Require().NoError(err) if tc.isAuctionTx { - suite.Require().Equal(tc.expectedBid, bidInfo.Bid) + s.Require().Equal(tc.expectedBid, bidInfo.Bid) } } }) } } -func (suite *MEVTestSuite) TestGetBundledTransactions() { +func (s *MEVTestSuite) TestGetBundledTransactions() { testCases := []struct { name string createTx func() (sdk.Tx, [][]byte) @@ -390,8 +390,8 @@ func (suite *MEVTestSuite) TestGetBundledTransactions() { { "normal sdk tx", func() (sdk.Tx, [][]byte) { - tx, err := testutils.CreateRandomTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 1, 0, 0) - suite.Require().NoError(err) + tx, err := testutils.CreateRandomTx(s.encCfg.TxConfig, s.accounts[0], 0, 1, 0, 0) + s.Require().NoError(err) return tx, nil }, @@ -401,13 +401,13 @@ func (suite *MEVTestSuite) TestGetBundledTransactions() { { "valid auction tx", func() (sdk.Tx, [][]byte) { - msgAuctionBid, err := testutils.CreateMsgAuctionBid(suite.encCfg.TxConfig, suite.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) - suite.Require().NoError(err) + msgAuctionBid, err := testutils.CreateMsgAuctionBid(s.encCfg.TxConfig, s.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) + s.Require().NoError(err) msgs := []sdk.Msg{msgAuctionBid} - tx, err := testutils.CreateTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 0, msgs) - suite.Require().NoError(err) + tx, err := testutils.CreateTx(s.encCfg.TxConfig, s.accounts[0], 0, 0, msgs) + s.Require().NoError(err) return tx, msgAuctionBid.Transactions }, false, @@ -416,16 +416,16 @@ func (suite *MEVTestSuite) TestGetBundledTransactions() { { "invalid auction tx", func() (sdk.Tx, [][]byte) { - msgAuctionBid, err := testutils.CreateMsgAuctionBid(suite.encCfg.TxConfig, suite.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) - suite.Require().NoError(err) + msgAuctionBid, err := testutils.CreateMsgAuctionBid(s.encCfg.TxConfig, s.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) + s.Require().NoError(err) - randomMsg := testutils.CreateRandomMsgs(suite.accounts[0].Address, 1)[0] - suite.Require().NoError(err) + randomMsg := testutils.CreateRandomMsgs(s.accounts[0].Address, 1)[0] + s.Require().NoError(err) msgs := []sdk.Msg{msgAuctionBid, randomMsg} - tx, err := testutils.CreateTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 0, msgs) - suite.Require().NoError(err) + tx, err := testutils.CreateTx(s.encCfg.TxConfig, s.accounts[0], 0, 0, msgs) + s.Require().NoError(err) return tx, nil }, true, @@ -434,24 +434,24 @@ func (suite *MEVTestSuite) TestGetBundledTransactions() { } for _, tc := range testCases { - suite.Run(tc.name, func() { + s.Run(tc.name, func() { tx, expectedBundledTxs := tc.createTx() - bidInfo, err := suite.config.GetAuctionBidInfo(tx) + bidInfo, err := s.config.GetAuctionBidInfo(tx) if tc.expectedError { - suite.Require().Error(err) + s.Require().Error(err) } else { - suite.Require().NoError(err) + s.Require().NoError(err) if tc.isAuctionTx { - suite.Require().Equal(expectedBundledTxs, bidInfo.Transactions) + s.Require().Equal(expectedBundledTxs, bidInfo.Transactions) } } }) } } -func (suite *MEVTestSuite) TestGetTimeout() { +func (s *MEVTestSuite) TestGetTimeout() { testCases := []struct { name string createTx func() sdk.Tx @@ -462,8 +462,8 @@ func (suite *MEVTestSuite) TestGetTimeout() { { "normal sdk tx", func() sdk.Tx { - tx, err := testutils.CreateRandomTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 1, 1, 0) - suite.Require().NoError(err) + tx, err := testutils.CreateRandomTx(s.encCfg.TxConfig, s.accounts[0], 0, 1, 1, 0) + s.Require().NoError(err) return tx }, @@ -474,13 +474,13 @@ func (suite *MEVTestSuite) TestGetTimeout() { { "valid auction tx", func() sdk.Tx { - msgAuctionBid, err := testutils.CreateMsgAuctionBid(suite.encCfg.TxConfig, suite.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) - suite.Require().NoError(err) + msgAuctionBid, err := testutils.CreateMsgAuctionBid(s.encCfg.TxConfig, s.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) + s.Require().NoError(err) msgs := []sdk.Msg{msgAuctionBid} - tx, err := testutils.CreateTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 10, msgs) - suite.Require().NoError(err) + tx, err := testutils.CreateTx(s.encCfg.TxConfig, s.accounts[0], 0, 10, msgs) + s.Require().NoError(err) return tx }, false, @@ -490,16 +490,16 @@ func (suite *MEVTestSuite) TestGetTimeout() { { "invalid auction tx", func() sdk.Tx { - msgAuctionBid, err := testutils.CreateMsgAuctionBid(suite.encCfg.TxConfig, suite.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) - suite.Require().NoError(err) + msgAuctionBid, err := testutils.CreateMsgAuctionBid(s.encCfg.TxConfig, s.accounts[0], sdk.NewInt64Coin("stake", 100), 0, 2) + s.Require().NoError(err) - randomMsg := testutils.CreateRandomMsgs(suite.accounts[0].Address, 1)[0] - suite.Require().NoError(err) + randomMsg := testutils.CreateRandomMsgs(s.accounts[0].Address, 1)[0] + s.Require().NoError(err) msgs := []sdk.Msg{msgAuctionBid, randomMsg} - tx, err := testutils.CreateTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 10, msgs) - suite.Require().NoError(err) + tx, err := testutils.CreateTx(s.encCfg.TxConfig, s.accounts[0], 0, 10, msgs) + s.Require().NoError(err) return tx }, true, @@ -509,17 +509,17 @@ func (suite *MEVTestSuite) TestGetTimeout() { } for _, tc := range testCases { - suite.Run(tc.name, func() { + s.Run(tc.name, func() { tx := tc.createTx() - bidInfo, err := suite.config.GetAuctionBidInfo(tx) + bidInfo, err := s.config.GetAuctionBidInfo(tx) if tc.expectedError { - suite.Require().Error(err) + s.Require().Error(err) } else { - suite.Require().NoError(err) + s.Require().NoError(err) if tc.isAuctionTx { - suite.Require().Equal(tc.expectedTimeout, bidInfo.Timeout) + s.Require().Equal(tc.expectedTimeout, bidInfo.Timeout) } } }) diff --git a/lanes/mev/mev_test.go b/lanes/mev/mev_test.go index 4f6a2c51..707fe782 100644 --- a/lanes/mev/mev_test.go +++ b/lanes/mev/mev_test.go @@ -1,16 +1,23 @@ package mev_test import ( + "crypto/sha256" + "encoding/hex" + "fmt" "math/rand" "testing" "time" "cosmossdk.io/log" - cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + "cosmossdk.io/math" + storetypes "cosmossdk.io/store/types" + "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" signer_extraction "github.com/skip-mev/block-sdk/adapters/signer_extraction_adapter" + "github.com/skip-mev/block-sdk/block/base" + "github.com/skip-mev/block-sdk/block/utils" "github.com/skip-mev/block-sdk/lanes/mev" testutils "github.com/skip-mev/block-sdk/testutils" ) @@ -18,30 +25,93 @@ import ( type MEVTestSuite struct { suite.Suite - encCfg testutils.EncodingConfig - config mev.Factory - ctx sdk.Context - random *rand.Rand - accounts []testutils.Account - nonces map[string]uint64 + encCfg testutils.EncodingConfig + config mev.Factory + ctx sdk.Context + accounts []testutils.Account + gasTokenDenom string } -func TestMempoolTestSuite(t *testing.T) { +func TestMEVTestSuite(t *testing.T) { suite.Run(t, new(MEVTestSuite)) } -func (suite *MEVTestSuite) SetupTest() { - // Mempool setup - suite.encCfg = testutils.CreateTestEncodingConfig() - suite.config = mev.NewDefaultAuctionFactory(suite.encCfg.TxConfig.TxDecoder(), signer_extraction.NewDefaultAdapter()) - suite.ctx = sdk.NewContext(nil, cmtproto.Header{}, false, log.NewTestLogger(suite.T())) +func (s *MEVTestSuite) SetupTest() { + // Init encoding config + s.encCfg = testutils.CreateTestEncodingConfig() + s.config = mev.NewDefaultAuctionFactory(s.encCfg.TxConfig.TxDecoder(), signer_extraction.NewDefaultAdapter()) + testCtx := testutil.DefaultContextWithDB(s.T(), storetypes.NewKVStoreKey("test"), storetypes.NewTransientStoreKey("transient_test")) + s.ctx = testCtx.Ctx.WithIsCheckTx(true) // Init accounts - suite.random = rand.New(rand.NewSource(time.Now().Unix())) - suite.accounts = testutils.RandomAccounts(suite.random, 10) + random := rand.New(rand.NewSource(time.Now().Unix())) + s.accounts = testutils.RandomAccounts(random, 10) + s.gasTokenDenom = "stake" +} + +func (s *MEVTestSuite) initLane( + maxBlockSpace math.LegacyDec, + expectedExecution map[sdk.Tx]bool, +) *mev.MEVLane { + config := base.NewLaneConfig( + log.NewTestLogger(s.T()), + s.encCfg.TxConfig.TxEncoder(), + s.encCfg.TxConfig.TxDecoder(), + s.setUpAnteHandler(expectedExecution), + signer_extraction.NewDefaultAdapter(), + maxBlockSpace, + ) + + factory := mev.NewDefaultAuctionFactory(s.encCfg.TxConfig.TxDecoder(), signer_extraction.NewDefaultAdapter()) + return mev.NewMEVLane(config, factory) +} + +func (s *MEVTestSuite) setUpAnteHandler(expectedExecution map[sdk.Tx]bool) sdk.AnteHandler { + txCache := make(map[string]bool) + for tx, pass := range expectedExecution { + bz, err := s.encCfg.TxConfig.TxEncoder()(tx) + s.Require().NoError(err) - suite.nonces = make(map[string]uint64) - for _, acc := range suite.accounts { - suite.nonces[acc.Address.String()] = 0 + hash := sha256.Sum256(bz) + hashStr := hex.EncodeToString(hash[:]) + txCache[hashStr] = pass } + + anteHandler := func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) { + bz, err := s.encCfg.TxConfig.TxEncoder()(tx) + s.Require().NoError(err) + + hash := sha256.Sum256(bz) + hashStr := hex.EncodeToString(hash[:]) + + pass, found := txCache[hashStr] + if !found { + return ctx, fmt.Errorf("tx not found") + } + + if pass { + return ctx, nil + } + + return ctx, fmt.Errorf("tx failed") + } + + return anteHandler +} + +func (s *MEVTestSuite) getTxSize(tx sdk.Tx) int64 { + txBz, err := s.encCfg.TxConfig.TxEncoder()(tx) + s.Require().NoError(err) + + return int64(len(txBz)) +} + +func (s *MEVTestSuite) compare(first, second []sdk.Tx) { + firstBytes, err := utils.GetEncodedTxs(s.encCfg.TxConfig.TxEncoder(), first) + s.Require().NoError(err) + + secondBytes, err := utils.GetEncodedTxs(s.encCfg.TxConfig.TxEncoder(), second) + s.Require().NoError(err) + + s.Require().Equal(firstBytes, secondBytes) } diff --git a/tests/integration/block_sdk_suite.go b/tests/integration/block_sdk_suite.go index f1d96786..3a0c0d6e 100644 --- a/tests/integration/block_sdk_suite.go +++ b/tests/integration/block_sdk_suite.go @@ -3,7 +3,12 @@ package integration import ( "context" + "bytes" + "cosmossdk.io/math" + rpctypes "github.com/cometbft/cometbft/rpc/core/types" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -12,10 +17,6 @@ import ( "github.com/strangelove-ventures/interchaintest/v7/ibc" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - rpctypes "github.com/cometbft/cometbft/rpc/core/types" - "bytes" ) const ( @@ -62,8 +63,8 @@ func (s *IntegrationTestSuite) WithDenom(denom string) *IntegrationTestSuite { func (s *IntegrationTestSuite) WithKeyringOptions(cdc codec.Codec, opts keyring.Option) { s.broadcasterOverrides = &KeyringOverride{ - cdc: cdc, - keyringOptions: opts, + cdc: cdc, + keyringOptions: opts, } } @@ -143,7 +144,7 @@ func (s *IntegrationTestSuite) TestValidBids() { require.NoError(s.T(), err) // broadcast + wait for the tx to be included in a block - res := s.BroadcastTxs( context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ + res := s.BroadcastTxs(context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ { User: s.user1, Msgs: []sdk.Msg{bid}, @@ -177,7 +178,7 @@ func (s *IntegrationTestSuite) TestValidBids() { // create the MsgAuctionBid bidAmt := params.ReserveFee - bid, bundledTxs := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{ + bid, bundledTxs := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{ { User: s.user1, Msgs: msgs[0:1], @@ -205,8 +206,8 @@ func (s *IntegrationTestSuite) TestValidBids() { expTxs := make(chan committedTx, 2) regular_txs := s.BroadcastTxsWithCallback( - context.Background(), - s.chain.(*cosmos.CosmosChain), + context.Background(), + s.chain.(*cosmos.CosmosChain), msgsToBcast, func(tx []byte, resp *rpctypes.ResultTx) { expTxs <- committedTx{tx, resp} @@ -217,7 +218,7 @@ func (s *IntegrationTestSuite) TestValidBids() { // get the height of the block that the bid was included in var commitHeight int64 - + tx1 := <-expTxs tx2 := <-expTxs @@ -265,10 +266,10 @@ func (s *IntegrationTestSuite) TestValidBids() { } // create bundle bidAmt := params.ReserveFee - bid, bundledTxs := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, txs) + bid, bundledTxs := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, txs) // create 2 more bundle w same txs from same user - bid2, _ := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt.Add(params.MinBidIncrement), txs) - bid3, _ := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt.Add(params.MinBidIncrement).Add(params.MinBidIncrement), txs) + bid2, _ := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt.Add(params.MinBidIncrement), txs) + bid3, _ := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt.Add(params.MinBidIncrement).Add(params.MinBidIncrement), txs) // query height height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background()) @@ -279,7 +280,7 @@ func (s *IntegrationTestSuite) TestValidBids() { height++ // broadcast all bids - broadcastedTxs := s.BroadcastTxs( context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ + broadcastedTxs := s.BroadcastTxs(context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ { User: s.user1, Msgs: []sdk.Msg{bid}, @@ -345,18 +346,18 @@ func (s *IntegrationTestSuite) TestValidBids() { // broadcast txs in the bundle to network + bundle + extra broadcastedTxs := s.BroadcastTxsWithCallback(context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ - { - User: s.user2, - Msgs: []sdk.Msg{bid}, - Height: height + 3, - }, { - User: s.user3, - Msgs: []sdk.Msg{banktypes.NewMsgSend(s.user3.Address(), s.user1.Address(), sdk.NewCoins(sdk.NewCoin(s.denom, math.NewInt(100))))}, - Height: height + 3, - }}, - func(tx []byte, resp *rpctypes.ResultTx) { - expTxs <- committedTx{tx, resp} - }) + { + User: s.user2, + Msgs: []sdk.Msg{bid}, + Height: height + 3, + }, { + User: s.user3, + Msgs: []sdk.Msg{banktypes.NewMsgSend(s.user3.Address(), s.user1.Address(), sdk.NewCoins(sdk.NewCoin(s.denom, math.NewInt(100))))}, + Height: height + 3, + }}, + func(tx []byte, resp *rpctypes.ResultTx) { + expTxs <- committedTx{tx, resp} + }) close(expTxs) var bidTxHeight int64 @@ -379,7 +380,7 @@ func (s *IntegrationTestSuite) TestValidBids() { } type committedTx struct { - tx []byte + tx []byte res *rpctypes.ResultTx } @@ -410,7 +411,7 @@ func (s *IntegrationTestSuite) TestMultipleBids() { } // create bid1 bidAmt := params.ReserveFee - bid1, bundledTxs := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) + bid1, bundledTxs := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) // create bid 2 msg2 := Tx{ @@ -419,7 +420,7 @@ func (s *IntegrationTestSuite) TestMultipleBids() { SequenceIncrement: 1, } // create bid2 w/ higher bid than bid1 - bid2, bundledTxs2 := s.CreateAuctionBidMsg( context.Background(), s.user2, s.chain.(*cosmos.CosmosChain), bidAmt.Add(params.MinBidIncrement), []Tx{msg2}) + bid2, bundledTxs2 := s.CreateAuctionBidMsg(context.Background(), s.user2, s.chain.(*cosmos.CosmosChain), bidAmt.Add(params.MinBidIncrement), []Tx{msg2}) // get chain height height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background()) require.NoError(s.T(), err) @@ -430,9 +431,9 @@ func (s *IntegrationTestSuite) TestMultipleBids() { // broadcast both bids (with ample time to be committed (instead of timing out)) txs := s.BroadcastTxsWithCallback(context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ { - User: s.user1, - Msgs: []sdk.Msg{bid1}, - Height: height + 4, + User: s.user1, + Msgs: []sdk.Msg{bid1}, + Height: height + 4, }, { User: s.user2, @@ -442,7 +443,7 @@ func (s *IntegrationTestSuite) TestMultipleBids() { }, func(tx []byte, resp *rpctypes.ResultTx) { txsCh <- committedTx{tx, resp} }) - + // check txs were committed require.Len(s.T(), txsCh, 2) close(txsCh) @@ -450,7 +451,7 @@ func (s *IntegrationTestSuite) TestMultipleBids() { tx1 := <-txsCh tx2 := <-txsCh - // query next block + // query next block block := Block(s.T(), s.chain.(*cosmos.CosmosChain), tx1.res.Height) // check bid2 was included first @@ -481,7 +482,7 @@ func (s *IntegrationTestSuite) TestMultipleBids() { } // create bid1 bidAmt := params.ReserveFee - bid1, bundledTxs := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{tx}) + bid1, bundledTxs := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{tx}) // create bid 2 tx2 := Tx{ @@ -490,7 +491,7 @@ func (s *IntegrationTestSuite) TestMultipleBids() { SequenceIncrement: 1, } // create bid2 w/ higher bid than bid1 - bid2, _ := s.CreateAuctionBidMsg( context.Background(), s.user2, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{tx2}) + bid2, _ := s.CreateAuctionBidMsg(context.Background(), s.user2, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{tx2}) // get chain height height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background()) @@ -543,7 +544,7 @@ func (s *IntegrationTestSuite) TestMultipleBids() { } // create bid1 bidAmt := params.ReserveFee - bid1, bundledTxs := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) + bid1, bundledTxs := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) // create bid 2 msg2 := Tx{ @@ -553,7 +554,7 @@ func (s *IntegrationTestSuite) TestMultipleBids() { } // create bid2 w/ higher bid than bid1 - bid2, _ := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg2}) + bid2, _ := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg2}) // get chain height height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background()) require.NoError(s.T(), err) @@ -568,10 +569,10 @@ func (s *IntegrationTestSuite) TestMultipleBids() { Height: height + 4, }, { - User: s.user1, - Msgs: []sdk.Msg{bid2}, - Height: height + 4, - ExpectFail: true, + User: s.user1, + Msgs: []sdk.Msg{bid2}, + Height: height + 4, + ExpectFail: true, }, }, func(tx []byte, resp *rpctypes.ResultTx) { expTx <- committedTx{tx, resp} @@ -605,10 +606,10 @@ func (s *IntegrationTestSuite) TestMultipleBids() { } // create bid1 bidAmt := params.ReserveFee - bid1, bundledTxs := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) + bid1, bundledTxs := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) // create bid2 w/ higher bid than bid1 - bid2, _ := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt.Add(params.MinBidIncrement), []Tx{msg}) + bid2, _ := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt.Add(params.MinBidIncrement), []Tx{msg}) // get chain height height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background()) require.NoError(s.T(), err) @@ -618,9 +619,9 @@ func (s *IntegrationTestSuite) TestMultipleBids() { // broadcast both bids txs := s.BroadcastTxsWithCallback(context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ { - User: s.user1, - Msgs: []sdk.Msg{bid1}, - Height: height + 4, + User: s.user1, + Msgs: []sdk.Msg{bid1}, + Height: height + 4, SkipInclusionCheck: true, }, { @@ -663,10 +664,10 @@ func (s *IntegrationTestSuite) TestMultipleBids() { // create bid1 bidAmt := params.ReserveFee - bid1, bundledTxs := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) + bid1, bundledTxs := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) // create bid2 w/ higher bid than bid1 - bid2, _ := s.CreateAuctionBidMsg( context.Background(), s.user2, s.chain.(*cosmos.CosmosChain), bidAmt.Add(params.MinBidIncrement), []Tx{msg}) + bid2, _ := s.CreateAuctionBidMsg(context.Background(), s.user2, s.chain.(*cosmos.CosmosChain), bidAmt.Add(params.MinBidIncrement), []Tx{msg}) // get chain height height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background()) require.NoError(s.T(), err) @@ -676,9 +677,9 @@ func (s *IntegrationTestSuite) TestMultipleBids() { // broadcast both bids txs := s.BroadcastTxsWithCallback(context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ { - User: s.user1, - Msgs: []sdk.Msg{bid1}, - Height: height + 4, + User: s.user1, + Msgs: []sdk.Msg{bid1}, + Height: height + 4, SkipInclusionCheck: true, }, { @@ -721,7 +722,7 @@ func (s *IntegrationTestSuite) TestMultipleBids() { } // create bid1 bidAmt := params.ReserveFee - bid1, bundledTxs := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) + bid1, bundledTxs := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) // create bid2 // create a second message @@ -743,9 +744,9 @@ func (s *IntegrationTestSuite) TestMultipleBids() { // broadcast both bids txs := s.BroadcastTxsWithCallback(context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ { - User: s.user1, - Msgs: []sdk.Msg{bid1}, - Height: height + 4, + User: s.user1, + Msgs: []sdk.Msg{bid1}, + Height: height + 4, }, { User: s.user2, @@ -754,7 +755,7 @@ func (s *IntegrationTestSuite) TestMultipleBids() { }, }, func(tx []byte, resp *rpctypes.ResultTx) { committedTxs <- committedTx{ - tx: tx, + tx: tx, res: resp, } }) @@ -797,11 +798,11 @@ func (s *IntegrationTestSuite) TestInvalidBids() { SequenceIncrement: 2, } bidAmt := params.ReserveFee - bid, _ := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) + bid, _ := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background()) // wrap bidTx in another tx - wrappedBid, _ := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{ + wrappedBid, _ := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{ { User: s.user1, Msgs: []sdk.Msg{bid}, @@ -813,7 +814,7 @@ func (s *IntegrationTestSuite) TestInvalidBids() { require.NoError(s.T(), err) // broadcast wrapped bid, and expect a failure - s.BroadcastTxs( context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ + s.BroadcastTxs(context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ { User: s.user1, Msgs: []sdk.Msg{wrappedBid}, @@ -831,7 +832,7 @@ func (s *IntegrationTestSuite) TestInvalidBids() { SequenceIncrement: 2, } bidAmt := sdk.NewCoin(s.denom, math.NewInt(1000000000000000000)) - bid, _ := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) + bid, _ := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background()) require.NoError(s.T(), err) @@ -858,7 +859,7 @@ func (s *IntegrationTestSuite) TestInvalidBids() { } bidAmt := params.ReserveFee - bid, _ := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg, msg2, msg3}) + bid, _ := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg, msg2, msg3}) height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background()) require.NoError(s.T(), err) @@ -875,13 +876,13 @@ func (s *IntegrationTestSuite) TestInvalidBids() { SequenceIncrement: 2, } bidAmt := params.ReserveFee - bid, _ := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) + bid, _ := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background()) require.NoError(s.T(), err) // broadcast wrapped bid, and expect a failure - s.BroadcastTxs( context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ + s.BroadcastTxs(context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ { User: s.user1, Msgs: []sdk.Msg{bid}, @@ -901,7 +902,7 @@ func (s *IntegrationTestSuite) TestInvalidBids() { // create bid smaller than reserve bidAmt := sdk.NewCoin(s.denom, math.NewInt(0)) - bid, _ := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) + bid, _ := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background()) require.NoError(s.T(), err) @@ -924,7 +925,7 @@ func (s *IntegrationTestSuite) TestInvalidBids() { // create bid smaller than reserve bidAmt := sdk.NewCoin(s.denom, math.NewInt(0)) - bid, _ := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, msgs) + bid, _ := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, msgs) height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background()) require.NoError(s.T(), err) @@ -943,7 +944,7 @@ func (s *IntegrationTestSuite) TestInvalidBids() { // create bid smaller than reserve bidAmt := sdk.NewCoin(s.denom, math.NewInt(0)) - bid, _ := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) + bid, _ := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{msg}) // broadcast wrapped bid, and expect a failure s.SimulateTx(context.Background(), s.chain.(*cosmos.CosmosChain), s.user1, 0, true, []sdk.Msg{bid}...) @@ -959,7 +960,7 @@ func (s *IntegrationTestSuite) TestInvalidBids() { // create the MsgAuctioBid (this should fail b.c same tx is repeated twice) bidAmt := params.ReserveFee - bid, _ := s.CreateAuctionBidMsg( context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{ + bid, _ := s.CreateAuctionBidMsg(context.Background(), s.user1, s.chain.(*cosmos.CosmosChain), bidAmt, []Tx{ { User: s.user2, Msgs: []sdk.Msg{ @@ -976,7 +977,7 @@ func (s *IntegrationTestSuite) TestInvalidBids() { require.NoError(s.T(), err) // broadcast + wait for the tx to be included in a block - txs := s.BroadcastTxs( context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ + txs := s.BroadcastTxs(context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ { User: s.user1, Msgs: []sdk.Msg{bid}, @@ -1022,7 +1023,7 @@ func (s *IntegrationTestSuite) TestFreeLane() { balanceBefore := QueryAccountBalance(s.T(), s.chain.(*cosmos.CosmosChain), s.user1.FormattedAddress(), s.denom) // create a free tx (MsgDelegate), broadcast and wait for commit - s.BroadcastTxs( context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ + s.BroadcastTxs(context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ { User: s.user1, Msgs: []sdk.Msg{ @@ -1085,7 +1086,7 @@ func (s *IntegrationTestSuite) TestFreeLane() { user2BalanceBefore := QueryAccountBalance(s.T(), s.chain.(*cosmos.CosmosChain), s.user2.FormattedAddress(), s.denom) // user1 submits a free-tx, user2 submits a free tx - s.BroadcastTxs( context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ + s.BroadcastTxs(context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ { User: s.user1, Msgs: []sdk.Msg{ @@ -1242,7 +1243,7 @@ func (s *IntegrationTestSuite) TestLanes() { height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background()) require.NoError(s.T(), err) - s.BroadcastTxs( context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ + s.BroadcastTxs(context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ { User: s.user1, Msgs: []sdk.Msg{bid}, @@ -1392,7 +1393,7 @@ func (s *IntegrationTestSuite) TestLanes() { height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background()) require.NoError(s.T(), err) - txs := s.BroadcastTxs( context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ + txs := s.BroadcastTxs(context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{ { User: s.user3, Msgs: []sdk.Msg{bid}, diff --git a/testutils/utils.go b/testutils/utils.go index 506bf622..b0a2ce40 100644 --- a/testutils/utils.go +++ b/testutils/utils.go @@ -196,14 +196,14 @@ func CreateTxWithSigners(txCfg client.TxConfig, nonce, timeout uint64, signers [ return txBuilder.GetTx(), nil } -func CreateAuctionTx(txCfg client.TxConfig, bidder Account, bid sdk.Coin, nonce, timeout uint64, signers []Account, gasLimit uint64) (authsigning.Tx, []authsigning.Tx, error) { +func CreateAuctionTx(txCfg client.TxConfig, bidder Account, bid sdk.Coin, nonce, timeout uint64, signers []Account, gasLimit uint64) (sdk.Tx, []sdk.Tx, error) { bidMsg := &auctiontypes.MsgAuctionBid{ Bidder: bidder.Address.String(), Bid: bid, Transactions: make([][]byte, len(signers)), } - txs := []authsigning.Tx{} + txs := []sdk.Tx{} for i := 0; i < len(signers); i++ { randomMsg := CreateRandomMsgs(signers[i].Address, 1)