From aff0e228a3adbae1a634ec47d92ebc69b3d9e57c Mon Sep 17 00:00:00 2001 From: David Terpay <35130517+davidterpay@users.noreply.github.com> Date: Tue, 17 Oct 2023 16:50:28 -0400 Subject: [PATCH] fix(compare): Adding Sequence Number check on Compare Priority (#159) * adding seq num check on compare * nit * adding debug logging --- README.md | 2 + abci/abci.go | 4 +- abci/abci_test.go | 270 +++++++++++++++++++++++ block/base/handlers.go | 6 +- block/base/mempool.go | 49 ++++- block/lane.go | 2 +- block/proposals/proposals.go | 10 +- block/proposals/proposals_test.go | 21 +- block/proposals/update.go | 12 + lanes/base/abci_test.go | 351 ++++++++++++++++++++++++++++++ lanes/base/mempool_test.go | 113 ++++++++++ lanes/mev/abci_test.go | 39 ++-- lanes/terminator/lane.go | 4 +- 13 files changed, 842 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index a96f58c..f38f63e 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,8 @@ ### 🤔 What is the Block SDK? +> **Note**: The Block SDK is midway through an audit. Please use at your own risk. Timeline for audit completion is early November. + **🌐 The Block SDK is a toolkit for building customized blocks**. The Block SDK is a set of Cosmos SDK and ABCI++ primitives that allow chains to fully customize blocks to specific use cases. It turns your chain's blocks into a **`highway`** consisting of individual **`lanes`** with their own special functionality. diff --git a/abci/abci.go b/abci/abci.go index 5a7e2c2..90fd019 100644 --- a/abci/abci.go +++ b/abci/abci.go @@ -75,7 +75,7 @@ func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler { ) // Fill the proposal with transactions from each lane. - finalProposal, err := h.prepareLanesHandler(ctx, proposals.NewProposalWithContext(ctx, h.txEncoder)) + finalProposal, err := h.prepareLanesHandler(ctx, proposals.NewProposalWithContext(h.logger, ctx, h.txEncoder)) if err != nil { h.logger.Error("failed to prepare proposal", "err", err) return &abci.ResponsePrepareProposal{Txs: make([][]byte, 0)}, err @@ -141,7 +141,7 @@ func (h *ProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHandler { // Build handler that will verify the partial proposals according to each lane's verification logic. processLanesHandler := ChainProcessLanes(partialProposals, h.mempool.Registry()) - finalProposal, err := processLanesHandler(ctx, proposals.NewProposalWithContext(ctx, h.txEncoder)) + finalProposal, err := processLanesHandler(ctx, proposals.NewProposalWithContext(h.logger, ctx, h.txEncoder)) if err != nil { h.logger.Error("failed to validate the proposal", "err", err) return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}, err diff --git a/abci/abci_test.go b/abci/abci_test.go index a257ee7..a66e8e8 100644 --- a/abci/abci_test.go +++ b/abci/abci_test.go @@ -1,6 +1,7 @@ package abci_test import ( + "context" "math/rand" "testing" @@ -862,6 +863,54 @@ func (s *ProposalsTestSuite) TestProcessProposal() { s.Require().Equal(&cometabci.ResponseProcessProposal{Status: cometabci.ResponseProcessProposal_ACCEPT}, resp) }) + s.Run("can accept proposal where txs are broadcasted with different sequence numbers", func() { + tx1, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 0, + 0, + 0, + 1, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1)), + ) + s.Require().NoError(err) + + tx2, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 1, + 0, + 0, + 1, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(2)), + ) + s.Require().NoError(err) + + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ + tx1: true, + tx2: true, + }) + + defaultLane.Insert(sdk.Context{}, tx1) + defaultLane.Insert(sdk.Context{}, tx2) + + txs := [][]sdk.Tx{} + + for iterator := defaultLane.Select(context.Background(), nil); iterator != nil; iterator = iterator.Next() { + txs = append(txs, []sdk.Tx{iterator.Tx()}) + } + + s.Require().Equal(2, len(txs)) + + proposal := s.createProposal(map[string]uint64{defaultLane.Name(): 2}, tx1, tx2) + + proposalHandler := s.setUpProposalHandlers([]block.Lane{defaultLane}).ProcessProposalHandler() + resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal, Height: 2}) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Require().Equal(&cometabci.ResponseProcessProposal{Status: cometabci.ResponseProcessProposal_ACCEPT}, resp) + }) + s.Run("can process a valid proposal with a single tx", func() { // Create a random transaction that will be inserted into the default lane tx, err := testutils.CreateRandomTx( @@ -1271,6 +1320,227 @@ func (s *ProposalsTestSuite) TestProcessProposal() { }) } +func (s *ProposalsTestSuite) TestPrepareProcessParity() { + // Define a large enough block size and gas limit to ensure that the proposal is accepted + s.setBlockParams(1000000000000, 1000000000000) + + // Create a random transaction that will be inserted into the default/free lane + numTxsPerAccount := uint64(25) + numAccounts := 25 + accounts := testutils.RandomAccounts(s.random, numAccounts) + + // Create a bunch of transactions to insert into the default lane + txsToInsert := []sdk.Tx{} + validationMap := make(map[sdk.Tx]bool) + for _, account := range accounts { + for nonce := uint64(0); nonce < numTxsPerAccount; nonce++ { + // create a random fee amount + feeAmount := math.NewInt(int64(rand.Intn(100000))) + tx, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + account, + nonce, + 1, + 0, + 1, + sdk.NewCoin(s.gasTokenDenom, feeAmount), + ) + s.Require().NoError(err) + + txsToInsert = append(txsToInsert, tx) + validationMap[tx] = true + } + } + + // Set up the default lane with the transactions + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), validationMap) + for _, tx := range txsToInsert { + s.Require().NoError(defaultLane.Insert(s.ctx, tx)) + } + + // Create a bunch of transactions to insert into the free lane + freeTxsToInsert := []sdk.Tx{} + freeValidationMap := make(map[sdk.Tx]bool) + for _, account := range accounts { + for nonce := uint64(0); nonce < numTxsPerAccount; nonce++ { + // create a random fee amount + feeAmount := math.NewInt(int64(rand.Intn(100000))) + tx, err := testutils.CreateFreeTx( + s.encodingConfig.TxConfig, + account, + nonce, + 1, + "test", + sdk.NewCoin(s.gasTokenDenom, feeAmount), + sdk.NewCoin(s.gasTokenDenom, feeAmount), + ) + s.Require().NoError(err) + + freeTxsToInsert = append(freeTxsToInsert, tx) + freeValidationMap[tx] = true + } + } + + freelane := s.setUpFreeLane(math.LegacyMustNewDecFromStr("0.0"), freeValidationMap) + for _, tx := range freeTxsToInsert { + s.Require().NoError(freelane.Insert(s.ctx, tx)) + } + + // Retrieve the transactions from the default lane in the same way the prepare function would. + retrievedTxs := []sdk.Tx{} + for iterator := defaultLane.Select(context.Background(), nil); iterator != nil; iterator = iterator.Next() { + retrievedTxs = append(retrievedTxs, iterator.Tx()) + } + s.Require().Equal(len(txsToInsert), len(retrievedTxs)) + + // Retrieve the transactions from the free lane in the same way the prepare function would. + freeRetrievedTxs := []sdk.Tx{} + for iterator := freelane.Select(context.Background(), nil); iterator != nil; iterator = iterator.Next() { + freeRetrievedTxs = append(freeRetrievedTxs, iterator.Tx()) + } + s.Require().Equal(len(freeTxsToInsert), len(freeRetrievedTxs)) + + numTxsPerLane := numTxsPerAccount * uint64(numAccounts) + s.Require().Equal(numTxsPerLane, uint64(len(retrievedTxs))) + s.Require().Equal(numTxsPerLane, uint64(len(freeRetrievedTxs))) + + // Create a proposal with the retrieved transactions + // Set up the default lane with no transactions + proposalHandler := s.setUpProposalHandlers([]block.Lane{freelane, defaultLane}).PrepareProposalHandler() + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{Height: 2}) + s.Require().NoError(err) + s.Require().NotNil(resp) + + info := s.getProposalInfo(resp.Txs[0]) + s.Require().NotNil(info) + s.Require().Equal(2, len(info.TxsByLane)) + s.Require().Equal(numTxsPerLane, info.TxsByLane[defaultLane.Name()]) + s.Require().Equal(numTxsPerLane, info.TxsByLane[freelane.Name()]) + s.Require().Equal(numTxsPerLane*2, uint64(len(resp.Txs)-1)) + + // Ensure the transactions are in the correct order for the free lane + for i := 0; i < int(numTxsPerLane); i++ { + bz, err := s.encodingConfig.TxConfig.TxEncoder()(freeRetrievedTxs[i]) + s.Require().NoError(err) + s.Require().Equal(bz, resp.Txs[i+1]) + } + + // Ensure the transactions are in the correct order for the default lane + for i := 0; i < int(numTxsPerLane); i++ { + bz, err := s.encodingConfig.TxConfig.TxEncoder()(retrievedTxs[i]) + s.Require().NoError(err) + s.Require().Equal(bz, resp.Txs[i+1+int(numTxsPerLane)]) + } + + proposal := s.createProposal( + map[string]uint64{defaultLane.Name(): numTxsPerLane, freelane.Name(): numTxsPerLane}, + append(freeRetrievedTxs, retrievedTxs...)..., + ) + + // Validate the proposal + processHandler := s.setUpProposalHandlers([]block.Lane{freelane, defaultLane}).ProcessProposalHandler() + processResp, err := processHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal, Height: 2}) + s.Require().NotNil(processResp) + s.Require().NoError(err) +} + +func (s *ProposalsTestSuite) TestIterateMempoolAndProcessProposalParity() { + // Define a large enough block size and gas limit to ensure that the proposal is accepted + s.setBlockParams(1000000000000, 1000000000000) + + // Create a random transaction that will be inserted into the default/free lane + numTxsPerAccount := uint64(25) + numAccounts := 25 + accounts := testutils.RandomAccounts(s.random, numAccounts) + + // Create a bunch of transactions to insert into the default lane + txsToInsert := []sdk.Tx{} + validationMap := make(map[sdk.Tx]bool) + for _, account := range accounts { + for nonce := uint64(0); nonce < numTxsPerAccount; nonce++ { + // create a random fee amount + feeAmount := math.NewInt(int64(rand.Intn(100000))) + tx, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + account, + nonce, + 1, + 0, + 1, + sdk.NewCoin(s.gasTokenDenom, feeAmount), + ) + s.Require().NoError(err) + + txsToInsert = append(txsToInsert, tx) + validationMap[tx] = true + } + } + + // Set up the default lane with the transactions + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), validationMap) + for _, tx := range txsToInsert { + s.Require().NoError(defaultLane.Insert(s.ctx, tx)) + } + + // Create a bunch of transactions to insert into the free lane + freeTxsToInsert := []sdk.Tx{} + freeValidationMap := make(map[sdk.Tx]bool) + for _, account := range accounts { + for nonce := uint64(0); nonce < numTxsPerAccount; nonce++ { + // create a random fee amount + feeAmount := math.NewInt(int64(rand.Intn(100000))) + tx, err := testutils.CreateFreeTx( + s.encodingConfig.TxConfig, + account, + nonce, + 1, + "test", + sdk.NewCoin(s.gasTokenDenom, feeAmount), + sdk.NewCoin(s.gasTokenDenom, feeAmount), + ) + s.Require().NoError(err) + + freeTxsToInsert = append(freeTxsToInsert, tx) + freeValidationMap[tx] = true + } + } + + freelane := s.setUpFreeLane(math.LegacyMustNewDecFromStr("0.0"), freeValidationMap) + for _, tx := range freeTxsToInsert { + s.Require().NoError(freelane.Insert(s.ctx, tx)) + } + + // Retrieve the transactions from the default lane in the same way the prepare function would. + retrievedTxs := []sdk.Tx{} + for iterator := defaultLane.Select(context.Background(), nil); iterator != nil; iterator = iterator.Next() { + retrievedTxs = append(retrievedTxs, iterator.Tx()) + } + s.Require().Equal(len(txsToInsert), len(retrievedTxs)) + + // Retrieve the transactions from the free lane in the same way the prepare function would. + freeRetrievedTxs := []sdk.Tx{} + for iterator := freelane.Select(context.Background(), nil); iterator != nil; iterator = iterator.Next() { + freeRetrievedTxs = append(freeRetrievedTxs, iterator.Tx()) + } + s.Require().Equal(len(freeTxsToInsert), len(freeRetrievedTxs)) + + // Create a proposal with the retrieved transactions + numTxsPerLane := numTxsPerAccount * uint64(numAccounts) + s.Require().Equal(numTxsPerLane, uint64(len(retrievedTxs))) + s.Require().Equal(numTxsPerLane, uint64(len(freeRetrievedTxs))) + + proposal := s.createProposal( + map[string]uint64{defaultLane.Name(): numTxsPerLane, freelane.Name(): numTxsPerLane}, + append(freeRetrievedTxs, retrievedTxs...)..., + ) + + // Validate the proposal + proposalHandler := s.setUpProposalHandlers([]block.Lane{freelane, defaultLane}).ProcessProposalHandler() + resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal, Height: 2}) + s.Require().NotNil(resp) + s.Require().NoError(err) +} + func (s *ProposalsTestSuite) TestValidateBasic() { // Set up the default lane with no transactions mevlane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), nil) diff --git a/block/base/handlers.go b/block/base/handlers.go index e072057..167753f 100644 --- a/block/base/handlers.go +++ b/block/base/handlers.go @@ -124,8 +124,10 @@ func (l *BaseLane) DefaultProcessLaneHandler() ProcessLaneHandler { // If the transactions do not respect the priority defined by the mempool, we consider the proposal // to be invalid - if index > 0 && l.Compare(ctx, partialProposal[index-1], tx) == -1 { - return fmt.Errorf("transaction at index %d has a higher priority than %d", index, index-1) + if index > 0 { + if v, err := l.Compare(ctx, partialProposal[index-1], tx); v == -1 || err != nil { + return fmt.Errorf("transaction at index %d has a higher priority than %d", index, index-1) + } } if err := l.VerifyTx(ctx, tx, false); err != nil { diff --git a/block/base/mempool.go b/block/base/mempool.go index 5e1f30b..0ec5267 100644 --- a/block/base/mempool.go +++ b/block/base/mempool.go @@ -22,6 +22,10 @@ type ( // index defines an index of transactions. index sdkmempool.Mempool + // signerExtractor defines the signer extraction adapter that allows us to + // extract the signer from a transaction. + extractor signer_extraction.Adapter + // txPriority defines the transaction priority function. It is used to // retrieve the priority of a given transaction and to compare the priority // of two transactions. The index utilizes this struct to order transactions @@ -91,6 +95,7 @@ func NewMempool[C comparable](txPriority TxPriority[C], txEncoder sdk.TxEncoder, }, extractor, ), + extractor: extractor, txPriority: txPriority, txEncoder: txEncoder, txCache: make(map[string]struct{}), @@ -155,8 +160,48 @@ func (cm *Mempool[C]) Contains(tx sdk.Tx) bool { } // Compare determines the relative priority of two transactions belonging in the same lane. -func (cm *Mempool[C]) Compare(ctx sdk.Context, this sdk.Tx, other sdk.Tx) int { +// There are two cases to consider: +// 1. The transactions have the same signer. In this case, we compare the sequence numbers. +// 2. The transactions have different signers. In this case, we compare the priorities of the +// transactions. +// +// Compare will return -1 if this transaction has a lower priority than the other transaction, 0 if +// they have the same priority, and 1 if this transaction has a higher priority than the other transaction. +func (cm *Mempool[C]) Compare(ctx sdk.Context, this sdk.Tx, other sdk.Tx) (int, error) { + signers, err := cm.extractor.GetSigners(this) + if err != nil { + return 0, err + } + if len(signers) == 0 { + return 0, fmt.Errorf("expected one signer for the first transaction") + } + // The priority nonce mempool uses the first tx signer so this is a safe operation. + thisSignerInfo := signers[0] + + signers, err = cm.extractor.GetSigners(other) + if err != nil { + return 0, err + } + if len(signers) == 0 { + return 0, fmt.Errorf("expected one signer for the second transaction") + } + otherSignerInfo := signers[0] + + // If the signers are the same, we compare the sequence numbers. + if thisSignerInfo.Signer.Equals(otherSignerInfo.Signer) { + switch { + case thisSignerInfo.Sequence < otherSignerInfo.Sequence: + return 1, nil + case thisSignerInfo.Sequence > otherSignerInfo.Sequence: + return -1, nil + default: + // This case should never happen but we add in the case for completeness. + return 0, fmt.Errorf("the two transactions have the same sequence number") + } + } + + // Determine the priority and compare the priorities. firstPriority := cm.txPriority.GetTxPriority(ctx, this) secondPriority := cm.txPriority.GetTxPriority(ctx, other) - return cm.txPriority.Compare(firstPriority, secondPriority) + return cm.txPriority.Compare(firstPriority, secondPriority), nil } diff --git a/block/lane.go b/block/lane.go index 1b09203..c6d3ac1 100644 --- a/block/lane.go +++ b/block/lane.go @@ -18,7 +18,7 @@ type LaneMempool interface { // Compare determines the relative priority of two transactions belonging in the same lane. Compare // will return -1 if this transaction has a lower priority than the other transaction, 0 if they have // the same priority, and 1 if this transaction has a higher priority than the other transaction. - Compare(ctx sdk.Context, this, other sdk.Tx) int + Compare(ctx sdk.Context, this, other sdk.Tx) (int, error) // Contains returns true if the transaction is contained in the mempool. Contains(tx sdk.Tx) bool diff --git a/block/proposals/proposals.go b/block/proposals/proposals.go index 0701ffb..06f26c9 100644 --- a/block/proposals/proposals.go +++ b/block/proposals/proposals.go @@ -1,6 +1,7 @@ package proposals import ( + "cosmossdk.io/log" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/skip-mev/block-sdk/block/proposals/types" @@ -9,6 +10,8 @@ import ( type ( // Proposal defines a block proposal type. Proposal struct { + Logger log.Logger + // Txs is the list of transactions in the proposal. Txs [][]byte // Cache is a cache of the selected transactions in the proposal. @@ -21,15 +24,16 @@ type ( ) // NewProposalWithContext returns a new empty proposal. -func NewProposalWithContext(ctx sdk.Context, txEncoder sdk.TxEncoder) Proposal { +func NewProposalWithContext(logger log.Logger, ctx sdk.Context, txEncoder sdk.TxEncoder) Proposal { maxBlockSize, maxGasLimit := GetBlockLimits(ctx) - return NewProposal(txEncoder, maxBlockSize, maxGasLimit) + return NewProposal(logger, txEncoder, maxBlockSize, maxGasLimit) } // NewProposal returns a new empty proposal. Any transactions added to the proposal // will be subject to the given max block size and max gas limit. -func NewProposal(txEncoder sdk.TxEncoder, maxBlockSize int64, maxGasLimit uint64) Proposal { +func NewProposal(logger log.Logger, txEncoder sdk.TxEncoder, maxBlockSize int64, maxGasLimit uint64) Proposal { return Proposal{ + Logger: logger, TxEncoder: txEncoder, Txs: make([][]byte, 0), Cache: make(map[string]struct{}), diff --git a/block/proposals/proposals_test.go b/block/proposals/proposals_test.go index 92c2331..bd0e5e7 100644 --- a/block/proposals/proposals_test.go +++ b/block/proposals/proposals_test.go @@ -4,6 +4,7 @@ import ( "math/rand" "testing" + "cosmossdk.io/log" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/skip-mev/block-sdk/block/mocks" @@ -27,7 +28,7 @@ func TestUpdateProposal(t *testing.T) { lane.On("GetMaxBlockSpace").Return(math.LegacyNewDec(1)).Maybe() t.Run("can update with no transactions", func(t *testing.T) { - proposal := proposals.NewProposal(nil, 100, 100) + proposal := proposals.NewProposal(log.NewTestLogger(t), nil, 100, 100) err := proposal.UpdateProposal(lane, nil) require.NoError(t, err) @@ -59,7 +60,7 @@ func TestUpdateProposal(t *testing.T) { size := len(txBzs[0]) gasLimit := 100 - proposal := proposals.NewProposal(encodingConfig.TxConfig.TxEncoder(), int64(size), uint64(gasLimit)) + proposal := proposals.NewProposal(log.NewTestLogger(t), encodingConfig.TxConfig.TxEncoder(), int64(size), uint64(gasLimit)) err = proposal.UpdateProposal(lane, []sdk.Tx{tx}) require.NoError(t, err) @@ -105,7 +106,7 @@ func TestUpdateProposal(t *testing.T) { gasLimit += 100 } - proposal := proposals.NewProposal(encodingConfig.TxConfig.TxEncoder(), int64(size), gasLimit) + proposal := proposals.NewProposal(log.NewTestLogger(t), encodingConfig.TxConfig.TxEncoder(), int64(size), gasLimit) err = proposal.UpdateProposal(lane, txs) require.NoError(t, err) @@ -142,7 +143,7 @@ func TestUpdateProposal(t *testing.T) { size := int64(len(txBzs[0])) gasLimit := uint64(100) - proposal := proposals.NewProposal(encodingConfig.TxConfig.TxEncoder(), size, gasLimit) + proposal := proposals.NewProposal(log.NewTestLogger(t), encodingConfig.TxConfig.TxEncoder(), size, gasLimit) err = proposal.UpdateProposal(lane, []sdk.Tx{tx}) require.NoError(t, err) @@ -202,7 +203,7 @@ func TestUpdateProposal(t *testing.T) { size := len(txBzs[0]) + len(txBzs[1]) gasLimit := 200 - proposal := proposals.NewProposal(encodingConfig.TxConfig.TxEncoder(), int64(size), uint64(gasLimit)) + proposal := proposals.NewProposal(log.NewTestLogger(t), encodingConfig.TxConfig.TxEncoder(), int64(size), uint64(gasLimit)) err = proposal.UpdateProposal(lane, []sdk.Tx{tx}) require.NoError(t, err) @@ -240,7 +241,7 @@ func TestUpdateProposal(t *testing.T) { size := len(txBzs[0]) gasLimit := 100 - proposal := proposals.NewProposal(encodingConfig.TxConfig.TxEncoder(), int64(size), uint64(gasLimit)) + proposal := proposals.NewProposal(log.NewTestLogger(t), encodingConfig.TxConfig.TxEncoder(), int64(size), uint64(gasLimit)) lane := mocks.NewLane(t) @@ -278,7 +279,7 @@ func TestUpdateProposal(t *testing.T) { size := len(txBzs[0]) gasLimit := 100 - proposal := proposals.NewProposal(encodingConfig.TxConfig.TxEncoder(), int64(size), uint64(gasLimit)) + proposal := proposals.NewProposal(log.NewTestLogger(t), encodingConfig.TxConfig.TxEncoder(), int64(size), uint64(gasLimit)) lane := mocks.NewLane(t) @@ -316,7 +317,7 @@ func TestUpdateProposal(t *testing.T) { size := len(txBzs[0]) gasLimit := 100 - proposal := proposals.NewProposal(encodingConfig.TxConfig.TxEncoder(), int64(size)-1, uint64(gasLimit)) + proposal := proposals.NewProposal(log.NewTestLogger(t), encodingConfig.TxConfig.TxEncoder(), int64(size)-1, uint64(gasLimit)) err = proposal.UpdateProposal(lane, []sdk.Tx{tx}) require.Error(t, err) @@ -349,7 +350,7 @@ func TestUpdateProposal(t *testing.T) { size := len(txBzs[0]) gasLimit := 100 - proposal := proposals.NewProposal(encodingConfig.TxConfig.TxEncoder(), int64(size), uint64(gasLimit)-1) + proposal := proposals.NewProposal(log.NewTestLogger(t), encodingConfig.TxConfig.TxEncoder(), int64(size), uint64(gasLimit)-1) err = proposal.UpdateProposal(lane, []sdk.Tx{tx}) require.Error(t, err) @@ -390,7 +391,7 @@ func TestUpdateProposal(t *testing.T) { txBzs, err := utils.GetEncodedTxs(encodingConfig.TxConfig.TxEncoder(), []sdk.Tx{tx, tx2}) require.NoError(t, err) - proposal := proposals.NewProposal(encodingConfig.TxConfig.TxEncoder(), 10000, 10000) + proposal := proposals.NewProposal(log.NewTestLogger(t), encodingConfig.TxConfig.TxEncoder(), 10000, 10000) err = proposal.UpdateProposal(lane, []sdk.Tx{tx}) require.NoError(t, err) diff --git a/block/proposals/update.go b/block/proposals/update.go index 4d19897..51a447e 100644 --- a/block/proposals/update.go +++ b/block/proposals/update.go @@ -1,6 +1,7 @@ package proposals import ( + "encoding/base64" "fmt" "cosmossdk.io/math" @@ -46,6 +47,17 @@ func (p *Proposal) UpdateProposal(lane Lane, partialProposal []sdk.Tx) error { return fmt.Errorf("err retrieving transaction info: %s", err) } + p.Logger.Debug( + "updating proposal with tx", + "index", index, + "lane", lane.Name(), + "tx_hash", txInfo.Hash, + "tx_size", txInfo.Size, + "tx_gas_limit", txInfo.GasLimit, + "tx_bytes", txInfo.TxBytes, + "raw_tx", base64.StdEncoding.EncodeToString(txInfo.TxBytes), + ) + // invariant check: Ensure that the transaction is not already in the proposal. if _, ok := p.Cache[txInfo.Hash]; ok { return fmt.Errorf("transaction %s is already in the proposal", txInfo.Hash) diff --git a/lanes/base/abci_test.go b/lanes/base/abci_test.go index 020b4ee..dc71783 100644 --- a/lanes/base/abci_test.go +++ b/lanes/base/abci_test.go @@ -1,9 +1,11 @@ package base_test import ( + "context" "crypto/sha256" "encoding/hex" "fmt" + "math/rand" "cosmossdk.io/log" "cosmossdk.io/math" @@ -43,6 +45,7 @@ func (s *BaseTestSuite) TestPrepareLane() { s.Require().NoError(err) emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), int64(len(txBz)), 1, @@ -86,6 +89,7 @@ func (s *BaseTestSuite) TestPrepareLane() { MaxGasLimit: 10, } emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), limit.MaxTxBytes, limit.MaxGasLimit, @@ -130,6 +134,7 @@ func (s *BaseTestSuite) TestPrepareLane() { MaxGasLimit: 10, } emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), limit.MaxTxBytes, limit.MaxGasLimit, @@ -173,6 +178,7 @@ func (s *BaseTestSuite) TestPrepareLane() { MaxGasLimit: 10, } emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), limit.MaxTxBytes, limit.MaxGasLimit, @@ -213,6 +219,7 @@ func (s *BaseTestSuite) TestPrepareLane() { s.Require().NoError(err) emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), int64(len(txBz)), 10, @@ -272,6 +279,7 @@ func (s *BaseTestSuite) TestPrepareLane() { size := int64(len(txBz1)) + int64(len(txBz2)) gasLimit := uint64(20) emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), size, gasLimit, @@ -328,6 +336,7 @@ func (s *BaseTestSuite) TestPrepareLane() { size := int64(len(txBz1)) + int64(len(txBz2)) gasLimit := uint64(2) emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), size, gasLimit, @@ -387,6 +396,7 @@ func (s *BaseTestSuite) TestPrepareLane() { size := int64(len(txBz1)) + int64(len(txBz2)) - 1 gasLimit := uint64(3) emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), size, gasLimit, @@ -446,6 +456,7 @@ func (s *BaseTestSuite) TestPrepareLane() { size := int64(len(txBz1)) + int64(len(txBz2)) - 1 gasLimit := uint64(1) emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), size, gasLimit, @@ -487,6 +498,7 @@ func (s *BaseTestSuite) TestPrepareLane() { s.Require().NoError(err) emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), int64(len(txBz))*10, 1000000, @@ -512,6 +524,195 @@ func (s *BaseTestSuite) TestPrepareLane() { } func (s *BaseTestSuite) TestProcessLane() { + s.Run("should accept a proposal where transaction fees are not in order bc of sequence numbers", func() { + tx1, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 0, + 1, + 0, + 1, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1)), + ) + s.Require().NoError(err) + + tx2, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 1, + 0, + 0, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(2)), + ) + s.Require().NoError(err) + + proposal := []sdk.Tx{ + tx1, + tx2, // This transaction has a higher sequence number and higher fees + } + + lane := s.initLane( + math.LegacyOneDec(), + map[sdk.Tx]bool{ + tx1: true, + tx2: true, + }, + ) + + partialProposal, err := utils.GetEncodedTxs(s.encodingConfig.TxConfig.TxEncoder(), proposal) + s.Require().NoError(err) + + emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), + s.encodingConfig.TxConfig.TxEncoder(), + 100000, + 100000, + ) + + _, err = lane.ProcessLane(s.ctx, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) + s.Require().NoError(err) + }) + + s.Run("should accept a proposal where transaction fees are not in order bc of sequence numbers with other txs", func() { + tx1, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 0, + 1, + 0, + 1, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(10)), + ) + s.Require().NoError(err) + + tx2, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 1, + 0, + 0, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(20)), + ) + s.Require().NoError(err) + + tx3, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[1], + 0, + 1, + 0, + 1, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(3)), + ) + s.Require().NoError(err) + + proposal := []sdk.Tx{ + tx1, + tx2, // This transaction has a higher sequence number and higher fees + tx3, + } + + lane := s.initLane( + math.LegacyOneDec(), + map[sdk.Tx]bool{ + tx1: true, + tx2: true, + tx3: true, + }, + ) + + partialProposal, err := utils.GetEncodedTxs(s.encodingConfig.TxConfig.TxEncoder(), proposal) + s.Require().NoError(err) + + emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), + s.encodingConfig.TxConfig.TxEncoder(), + 100000, + 100000, + ) + + _, err = lane.ProcessLane(s.ctx, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) + s.Require().NoError(err) + }) + + s.Run("accepts proposal with multiple senders and seq nums", func() { + tx1, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 0, + 1, + 0, + 1, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(10)), + ) + s.Require().NoError(err) + + tx2, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 1, + 0, + 0, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(20)), + ) + s.Require().NoError(err) + + tx3, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[1], + 0, + 1, + 0, + 1, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(9)), + ) + s.Require().NoError(err) + + tx4, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[1], + 1, + 1, + 0, + 1, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(11)), + ) + s.Require().NoError(err) + + proposal := []sdk.Tx{ + tx1, + tx2, + tx3, + tx4, + } + + lane := s.initLane( + math.LegacyOneDec(), + map[sdk.Tx]bool{ + tx1: true, + tx2: true, + tx3: true, + tx4: true, + }, + ) + + partialProposal, err := utils.GetEncodedTxs(s.encodingConfig.TxConfig.TxEncoder(), proposal) + s.Require().NoError(err) + + emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), + s.encodingConfig.TxConfig.TxEncoder(), + 100000, + 100000, + ) + + _, err = lane.ProcessLane(s.ctx, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) + s.Require().NoError(err) + }) + s.Run("should accept a proposal with valid transactions", func() { tx1, err := testutils.CreateRandomTx( s.encodingConfig.TxConfig, @@ -538,6 +739,7 @@ func (s *BaseTestSuite) TestProcessLane() { s.Require().NoError(err) emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), 100000, 100000, @@ -573,6 +775,7 @@ func (s *BaseTestSuite) TestProcessLane() { s.Require().NoError(err) emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), 100000, 100000, @@ -631,6 +834,7 @@ func (s *BaseTestSuite) TestProcessLane() { s.Require().NoError(err) emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), 100000, 100000, @@ -679,6 +883,7 @@ func (s *BaseTestSuite) TestProcessLane() { s.Require().NoError(err) emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), 100000, 100000, @@ -727,6 +932,7 @@ func (s *BaseTestSuite) TestProcessLane() { s.Require().NoError(err) emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), 100000, 100000, @@ -778,6 +984,7 @@ func (s *BaseTestSuite) TestProcessLane() { s.Require().NoError(err) emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), 100000, 100000, @@ -813,6 +1020,7 @@ func (s *BaseTestSuite) TestProcessLane() { s.Require().NoError(err) emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), maxSize, 1000000, @@ -849,6 +1057,7 @@ func (s *BaseTestSuite) TestProcessLane() { s.Require().NoError(err) emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), maxSize, 9, @@ -896,6 +1105,7 @@ func (s *BaseTestSuite) TestProcessLane() { s.Require().NoError(err) emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), maxSize, 19, @@ -944,6 +1154,7 @@ func (s *BaseTestSuite) TestProcessLane() { s.Require().NoError(err) emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), maxSize, 20, @@ -954,6 +1165,146 @@ func (s *BaseTestSuite) TestProcessLane() { }) } +func (s *BaseTestSuite) TestPrepareProcessParity() { + txsToInsert := []sdk.Tx{} + validationMap := make(map[sdk.Tx]bool) + numTxsPerAccount := uint64(50) + accounts := testutils.RandomAccounts(s.random, 50) + + for _, account := range accounts { + for nonce := uint64(0); nonce < numTxsPerAccount; nonce++ { + // create a random fee amount + feeAmount := math.NewInt(int64(rand.Intn(100000))) + tx, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + account, + nonce, + 1, + 0, + 1, + sdk.NewCoin(s.gasTokenDenom, feeAmount), + ) + s.Require().NoError(err) + + txsToInsert = append(txsToInsert, tx) + validationMap[tx] = true + } + } + + // Add the transactions to the lane + lane := s.initLane(math.LegacyOneDec(), validationMap) + for _, tx := range txsToInsert { + s.Require().NoError(lane.Insert(s.ctx, tx)) + } + + // Retrieve the transactions from the lane in the same way the prepare function would. + retrievedTxs := []sdk.Tx{} + for iterator := lane.Select(context.Background(), nil); iterator != nil; iterator = iterator.Next() { + retrievedTxs = append(retrievedTxs, iterator.Tx()) + } + s.Require().Equal(len(txsToInsert), len(retrievedTxs)) + + // Construct a block proposal with the transactions in the mempool + emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), + s.encodingConfig.TxConfig.TxEncoder(), + 1000000000000000, + 1000000000000000, + ) + proposal, err := lane.PrepareLane(s.ctx, emptyProposal, block.NoOpPrepareLanesHandler()) + s.Require().NoError(err) + s.Require().Equal(len(txsToInsert), len(proposal.Txs)) + + // Ensure that the transactions are in the same order + for i := 0; i < len(retrievedTxs); i++ { + bz, err := s.encodingConfig.TxConfig.TxEncoder()(retrievedTxs[i]) + s.Require().NoError(err) + s.Require().Equal(bz, proposal.Txs[i]) + } + + // Verify the same proposal with the process lanes handler + emptyProposal = proposals.NewProposal( + log.NewTestLogger(s.T()), + s.encodingConfig.TxConfig.TxEncoder(), + 1000000000000000, + 1000000000000000, + ) + proposal, err = lane.ProcessLane(s.ctx, emptyProposal, proposal.Txs, block.NoOpProcessLanesHandler()) + s.Require().NoError(err) + s.Require().Equal(len(txsToInsert), len(proposal.Txs)) + s.T().Logf("proposal num txs: %d", len(proposal.Txs)) + + // Ensure that the transactions are in the same order + for i := 0; i < len(retrievedTxs); i++ { + bz, err := s.encodingConfig.TxConfig.TxEncoder()(retrievedTxs[i]) + s.Require().NoError(err) + s.Require().Equal(bz, proposal.Txs[i]) + } +} + +func (s *BaseTestSuite) TestIterateMempoolAndProcessProposalParity() { + txsToInsert := []sdk.Tx{} + validationMap := make(map[sdk.Tx]bool) + numTxsPerAccount := uint64(200) + accounts := testutils.RandomAccounts(s.random, 50) + + for _, account := range accounts { + for nonce := uint64(0); nonce < numTxsPerAccount; nonce++ { + // create a random fee amount + feeAmount := math.NewInt(int64(rand.Intn(100000))) + tx, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + account, + nonce, + 1, + 0, + 1, + sdk.NewCoin(s.gasTokenDenom, feeAmount), + ) + s.Require().NoError(err) + + txsToInsert = append(txsToInsert, tx) + validationMap[tx] = true + } + } + + // Add the transactions to the lane + lane := s.initLane(math.LegacyOneDec(), validationMap) + for _, tx := range txsToInsert { + s.Require().NoError(lane.Insert(s.ctx, tx)) + } + + // Retrieve the transactions from the lane in the same way the prepare function would. + retrievedTxs := []sdk.Tx{} + for iterator := lane.Select(context.Background(), nil); iterator != nil; iterator = iterator.Next() { + retrievedTxs = append(retrievedTxs, iterator.Tx()) + } + + s.Require().Equal(len(txsToInsert), len(retrievedTxs)) + + partialProposal, err := utils.GetEncodedTxs(s.encodingConfig.TxConfig.TxEncoder(), retrievedTxs) + s.Require().NoError(err) + + emptyProposal := proposals.NewProposal( + log.NewTestLogger(s.T()), + s.encodingConfig.TxConfig.TxEncoder(), + 1000000000000000, + 1000000000000000, + ) + + proposal, err := lane.ProcessLane(s.ctx, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) + s.Require().NoError(err) + s.Require().Equal(len(txsToInsert), len(proposal.Txs)) + s.T().Logf("proposal num txs: %d", len(proposal.Txs)) + + // Ensure that the transactions are in the same order + for i := 0; i < len(retrievedTxs); i++ { + bz, err := s.encodingConfig.TxConfig.TxEncoder()(retrievedTxs[i]) + s.Require().NoError(err) + s.Require().Equal(bz, proposal.Txs[i]) + } +} + func (s *BaseTestSuite) initLane( maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool, diff --git a/lanes/base/mempool_test.go b/lanes/base/mempool_test.go index 341b289..3667a1c 100644 --- a/lanes/base/mempool_test.go +++ b/lanes/base/mempool_test.go @@ -86,6 +86,119 @@ func (s *BaseTestSuite) TestCompareTxPriority() { b := sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)).String() s.Require().Equal(0, txPriority.Compare(a, b)) }) + + lane := s.initLane(math.LegacyOneDec(), nil) + + s.Run("should return -1 when signers are the same but the first tx has a higher sequence", func() { + tx1, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 1, + 0, + 0, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + ) + s.Require().NoError(err) + + tx2, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 0, + 0, + 0, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + ) + s.Require().NoError(err) + + cmp, err := lane.Compare(sdk.Context{}, tx1, tx2) + s.Require().NoError(err) + s.Require().Equal(-1, cmp) + }) + + s.Run("should return 1 when signers are the same but the second tx has a higher sequence", func() { + tx1, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 0, + 0, + 0, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + ) + s.Require().NoError(err) + + tx2, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 1, + 0, + 0, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + ) + s.Require().NoError(err) + + cmp, err := lane.Compare(sdk.Context{}, tx1, tx2) + s.Require().NoError(err) + s.Require().Equal(1, cmp) + }) + + s.Run("should return 0 when signers are the same and the sequence is the same", func() { + tx1, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 1, + 0, + 0, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + ) + s.Require().NoError(err) + + tx2, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 1, + 0, + 0, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + ) + s.Require().NoError(err) + + _, err = lane.Compare(sdk.Context{}, tx1, tx2) + s.Require().Error(err) + }) + + s.Run("should return 1 when the first tx has a higher priority", func() { + tx1, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 0, + 0, + 0, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(200)), + ) + s.Require().NoError(err) + + tx2, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[1], + 0, + 0, + 0, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)), + ) + s.Require().NoError(err) + + cmp, err := lane.Compare(sdk.Context{}, tx1, tx2) + s.Require().NoError(err) + s.Require().Equal(1, cmp) + }) } func (s *BaseTestSuite) TestInsert() { diff --git a/lanes/mev/abci_test.go b/lanes/mev/abci_test.go index d1c8bec..21fa9cc 100644 --- a/lanes/mev/abci_test.go +++ b/lanes/mev/abci_test.go @@ -1,6 +1,7 @@ package mev_test import ( + log "cosmossdk.io/log" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/skip-mev/block-sdk/block" @@ -14,7 +15,7 @@ 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 := proposals.NewProposal(log.NewTestLogger(s.T()), s.encCfg.TxConfig.TxEncoder(), 200, 100) proposal, err := lane.PrepareLane(s.ctx, proposal, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) @@ -40,7 +41,7 @@ func (s *MEVTestSuite) TestPrepareLane() { 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 := proposals.NewProposal(log.NewTestLogger(s.T()), s.encCfg.TxConfig.TxEncoder(), 200, 100) proposal, err = lane.PrepareLane(s.ctx, proposal, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) @@ -82,7 +83,7 @@ func (s *MEVTestSuite) TestPrepareLane() { 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 := proposals.NewProposal(log.NewTestLogger(s.T()), s.encCfg.TxConfig.TxEncoder(), 20000, 100000) proposal, err = lane.PrepareLane(s.ctx, proposal, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) @@ -124,7 +125,7 @@ func (s *MEVTestSuite) TestPrepareLane() { 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 := proposals.NewProposal(log.NewTestLogger(s.T()), s.encCfg.TxConfig.TxEncoder(), 20000, 100000) proposal, err = lane.PrepareLane(s.ctx, proposal, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) @@ -154,7 +155,7 @@ func (s *MEVTestSuite) TestPrepareLane() { 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 := proposals.NewProposal(log.NewTestLogger(s.T()), s.encCfg.TxConfig.TxEncoder(), 20000, 100000) proposal, err = lane.PrepareLane(s.ctx, proposal, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) @@ -185,7 +186,7 @@ func (s *MEVTestSuite) TestPrepareLane() { 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 := proposals.NewProposal(log.NewTestLogger(s.T()), s.encCfg.TxConfig.TxEncoder(), s.getTxSize(bidTx), 100000) proposal, err = lane.PrepareLane(s.ctx, proposal, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) @@ -210,7 +211,7 @@ func (s *MEVTestSuite) TestPrepareLane() { 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 := proposals.NewProposal(log.NewTestLogger(s.T()), s.encCfg.TxConfig.TxEncoder(), s.getTxSize(bidTx), 99) proposal, err = lane.PrepareLane(s.ctx, proposal, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) @@ -226,7 +227,7 @@ 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 := proposals.NewProposal(log.NewTestLogger(s.T()), s.encCfg.TxConfig.TxEncoder(), 200, 100) proposal, err := lane.ProcessLane(s.ctx, proposal, nil, block.NoOpProcessLanesHandler()) s.Require().NoError(err) @@ -241,7 +242,7 @@ func (s *MEVTestSuite) TestProcessLane() { s.Require().NoError(err) lane := s.initLane(math.LegacyOneDec(), nil) - proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 200, 100) + proposal := proposals.NewProposal(log.NewTestLogger(s.T()), s.encCfg.TxConfig.TxEncoder(), 200, 100) _, err = lane.ProcessLane(s.ctx, proposal, [][]byte{txBz}, block.NoOpProcessLanesHandler()) s.Require().Error(err) @@ -263,7 +264,7 @@ func (s *MEVTestSuite) TestProcessLane() { s.Require().NoError(err) lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx: false}) - proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 200000, 1000000) + proposal := proposals.NewProposal(log.NewTestLogger(s.T()), s.encCfg.TxConfig.TxEncoder(), 200000, 1000000) _, err = lane.ProcessLane(s.ctx, proposal, partialProposal, block.NoOpProcessLanesHandler()) s.Require().Error(err) @@ -285,7 +286,7 @@ func (s *MEVTestSuite) TestProcessLane() { 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) + proposal := proposals.NewProposal(log.NewTestLogger(s.T()), s.encCfg.TxConfig.TxEncoder(), 200000, 1000000) _, err = lane.ProcessLane(s.ctx, proposal, partialProposal, block.NoOpProcessLanesHandler()) s.Require().Error(err) @@ -307,7 +308,7 @@ func (s *MEVTestSuite) TestProcessLane() { 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) + proposal := proposals.NewProposal(log.NewTestLogger(s.T()), s.encCfg.TxConfig.TxEncoder(), 200000, 1000000) _, err = lane.ProcessLane(s.ctx, proposal, partialProposal, block.NoOpProcessLanesHandler()) s.Require().Error(err) @@ -329,7 +330,7 @@ func (s *MEVTestSuite) TestProcessLane() { 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) + proposal := proposals.NewProposal(log.NewTestLogger(s.T()), s.encCfg.TxConfig.TxEncoder(), 200000, 1000000) _, err = lane.ProcessLane(s.ctx, proposal, partialProposal, block.NoOpProcessLanesHandler()) s.Require().Error(err) @@ -351,7 +352,7 @@ func (s *MEVTestSuite) TestProcessLane() { 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) + proposal := proposals.NewProposal(log.NewTestLogger(s.T()), s.encCfg.TxConfig.TxEncoder(), 200000, 1000000) _, err = lane.ProcessLane(s.ctx, proposal, partialProposal, block.NoOpProcessLanesHandler()) s.Require().NoError(err) @@ -373,7 +374,7 @@ func (s *MEVTestSuite) TestProcessLane() { s.Require().NoError(err) lane := s.initLane(math.LegacyOneDec(), map[sdk.Tx]bool{bidTx: true}) - proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 200000, 1000000) + proposal := proposals.NewProposal(log.NewTestLogger(s.T()), s.encCfg.TxConfig.TxEncoder(), 200000, 1000000) _, err = lane.ProcessLane(s.ctx, proposal, partialProposal, block.NoOpProcessLanesHandler()) s.Require().NoError(err) @@ -395,7 +396,7 @@ func (s *MEVTestSuite) TestProcessLane() { 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) + proposal := proposals.NewProposal(log.NewTestLogger(s.T()), s.encCfg.TxConfig.TxEncoder(), 20000, 99) _, err = lane.ProcessLane(s.ctx, proposal, partialProposal, block.NoOpProcessLanesHandler()) s.Require().Error(err) @@ -417,7 +418,7 @@ func (s *MEVTestSuite) TestProcessLane() { 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) + proposal := proposals.NewProposal(log.NewTestLogger(s.T()), s.encCfg.TxConfig.TxEncoder(), 200, 100) _, err = lane.ProcessLane(s.ctx, proposal, partialProposal, block.NoOpProcessLanesHandler()) s.Require().Error(err) @@ -426,7 +427,7 @@ func (s *MEVTestSuite) TestProcessLane() { func (s *MEVTestSuite) TestVerifyBidBasic() { lane := s.initLane(math.LegacyOneDec(), nil) - proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 200, 100) + proposal := proposals.NewProposal(log.NewTestLogger(s.T()), s.encCfg.TxConfig.TxEncoder(), 200, 100) limits := proposal.GetLaneLimits(lane.GetMaxBlockSpace()) s.Run("can verify a bid with no bundled txs", func() { @@ -490,7 +491,7 @@ func (s *MEVTestSuite) TestVerifyBidBasic() { s.Require().NoError(err) size := s.getTxSize(bidTx) - proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), size-1, 100) + proposal := proposals.NewProposal(log.NewTestLogger(s.T()), s.encCfg.TxConfig.TxEncoder(), size-1, 100) limits := proposal.GetLaneLimits(lane.GetMaxBlockSpace()) _, err = lane.VerifyBidBasic(bidTx, proposal, limits) diff --git a/lanes/terminator/lane.go b/lanes/terminator/lane.go index c2ec64d..c125dae 100644 --- a/lanes/terminator/lane.go +++ b/lanes/terminator/lane.go @@ -102,6 +102,6 @@ func (t Terminator) Select(context.Context, [][]byte) sdkmempool.Iterator { } // HasHigherPriority is a no-op -func (t Terminator) Compare(sdk.Context, sdk.Tx, sdk.Tx) int { - return 0 +func (t Terminator) Compare(sdk.Context, sdk.Tx, sdk.Tx) (int, error) { + return 0, nil }