feat(MEV): Updating MEV Lane with Testing + Cleaner Implementation (#134)

* updating mev lane with cleaner impl

* nit

* lint
This commit is contained in:
David Terpay
2023-10-04 18:23:36 -04:00
committed by GitHub
parent b9d6761776
commit d495b38954
10 changed files with 1034 additions and 416 deletions
+125 -163
View File
@@ -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() {
for iterator := l.Select(ctx, nil); iterator != nil; iterator = iterator.Next() {
bidTx := iterator.Tx()
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
}
bundle, err := l.VerifyBidBasic(bidTx, proposal, limit)
if err != nil {
l.Logger().Info(
"failed to select auction bid tx for lane; tx is invalid",
"err", err,
)
txsToRemove = append(txsToRemove, bidTx)
continue
}
cacheCtx, write := ctx.CacheContext()
bidTx := bidTxIterator.Tx()
txInfo, err := utils.GetTxInfo(l.TxEncoder(), bidTx)
if err != nil {
l.Logger().Info("failed to get hash of auction bid tx", "err", err)
txsToRemove = append(txsToRemove, bidTx)
continue selectBidTxLoop
}
// 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) {
if err := l.VerifyBidTx(cacheCtx, bidTx, bundle); err != nil {
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)
if err != nil {
l.Logger().Info(
"failed to get auction bid info",
"tx_hash", txInfo.Hash,
"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
}
// Verify the bid transaction and all of its bundled transactions.
if err := l.VerifyTx(cacheCtx, bidTx, bidInfo); 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 {
+598
View File
@@ -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))
})
}
+129 -129
View File
@@ -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)
}
}
})
+88 -18
View File
@@ -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"
}
suite.nonces = make(map[string]uint64)
for _, acc := range suite.accounts {
suite.nonces[acc.Address.String()] = 0
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)
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)
}