From d58b36bf3da331b67567a5125986abe4a578c1cb Mon Sep 17 00:00:00 2001 From: David Terpay <35130517+davidterpay@users.noreply.github.com> Date: Thu, 4 May 2023 15:33:18 -0400 Subject: [PATCH] fix(Config): Simplifying config interface (#104) --- abci/abci_test.go | 6 +- abci/proposals.go | 30 +- abci/vote_extensions.go | 15 +- abci/vote_extensions_test.go | 29 +- mempool/auction_bid.go | 128 +++++++++ .../{config_test.go => auction_bid_test.go} | 259 ++++++++---------- mempool/config.go | 242 ---------------- mempool/mempool.go | 34 ++- mempool/mempool_test.go | 4 +- x/builder/ante/ante.go | 110 ++------ x/builder/ante/ante_test.go | 7 +- x/builder/keeper/auction.go | 4 +- x/builder/keeper/auction_test.go | 39 +-- x/builder/keeper/keeper_test.go | 2 +- 14 files changed, 345 insertions(+), 564 deletions(-) create mode 100644 mempool/auction_bid.go rename mempool/{config_test.go => auction_bid_test.go} (67%) delete mode 100644 mempool/config.go diff --git a/abci/abci_test.go b/abci/abci_test.go index 30a1364..e995ea4 100644 --- a/abci/abci_test.go +++ b/abci/abci_test.go @@ -32,7 +32,7 @@ type ABCITestSuite struct { encodingConfig testutils.EncodingConfig proposalHandler *abci.ProposalHandler voteExtensionHandler *abci.VoteExtensionHandler - config mempool.Config + config mempool.AuctionFactory txs map[string]struct{} // auction bid setup @@ -66,10 +66,10 @@ func (suite *ABCITestSuite) SetupTest() { suite.random = rand.New(rand.NewSource(time.Now().Unix())) suite.key = storetypes.NewKVStoreKey(buildertypes.StoreKey) testCtx := testutil.DefaultContextWithDB(suite.T(), suite.key, storetypes.NewTransientStoreKey("transient_test")) - suite.ctx = testCtx.Ctx + suite.ctx = testCtx.Ctx.WithBlockHeight(1) // Mempool set up - suite.config = mempool.NewDefaultConfig(suite.encodingConfig.TxConfig.TxDecoder()) + suite.config = mempool.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder()) suite.mempool = mempool.NewAuctionMempool(suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), 0, suite.config) suite.txs = make(map[string]struct{}) suite.auctionBidAmount = sdk.NewCoin("foo", sdk.NewInt(1000000000)) diff --git a/abci/proposals.go b/abci/proposals.go index d01480b..f00b016 100644 --- a/abci/proposals.go +++ b/abci/proposals.go @@ -12,6 +12,7 @@ import ( "github.com/cometbft/cometbft/libs/log" sdk "github.com/cosmos/cosmos-sdk/types" sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool" + "github.com/skip-mev/pob/mempool" ) type ( @@ -19,10 +20,14 @@ type ( // to interact with the local mempool. ProposalMempool interface { sdkmempool.Mempool + + // The AuctionFactory interface is utilized to retrieve, validate, and wrap bid + // information into the block proposal. + mempool.AuctionFactory + + // AuctionBidSelect returns an iterator that iterates over the top bid + // transactions in the mempool. AuctionBidSelect(ctx context.Context) sdkmempool.Iterator - GetBundledTransactions(tx sdk.Tx) ([][]byte, error) - WrapBundleTransaction(tx []byte) (sdk.Tx, error) - IsAuctionTx(tx sdk.Tx) (bool, error) } // ProposalHandler contains the functionality and handlers required to\ @@ -82,7 +87,7 @@ func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler { bidTxSize := int64(len(bidTxBz)) if bidTxSize <= req.MaxTxBytes { - bundledTransactions, err := h.mempool.GetBundledTransactions(tmpBidTx) + bidInfo, err := h.mempool.GetAuctionBidInfo(tmpBidTx) if err != nil { // Some transactions in the bundle may be malformatted or invalid, so // we remove the bid transaction and try the next top bid. @@ -91,6 +96,7 @@ func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler { } // store the bytes of each ref tx as sdk.Tx bytes in order to build a valid proposal + bundledTransactions := bidInfo.Transactions sdkTxBytes := make([][]byte, len(bundledTransactions)) // Ensure that the bundled transactions are valid @@ -205,23 +211,21 @@ func (h *ProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHandler { return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} } - isAuctionTx, err := h.mempool.IsAuctionTx(tx) + bidInfo, err := h.mempool.GetAuctionBidInfo(tx) if err != nil { return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} } - if isAuctionTx { - // Only the first transaction can be an auction bid tx + // If the transaction is an auction bid, then we need to ensure that it is + // the first transaction in the block proposal and that the order of + // transactions in the block proposal follows the order of transactions in + // the bid. + if bidInfo != nil { if index != 0 { return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} } - bundledTransactions, err := h.mempool.GetBundledTransactions(tx) - if err != nil { - return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} - } - - // The order of transactions in the block proposal must follow the order of transactions in the bid. + bundledTransactions := bidInfo.Transactions if len(req.Txs) < len(bundledTransactions)+1 { return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} } diff --git a/abci/vote_extensions.go b/abci/vote_extensions.go index 733ce8f..5a0c96e 100644 --- a/abci/vote_extensions.go +++ b/abci/vote_extensions.go @@ -8,6 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool" + "github.com/skip-mev/pob/mempool" ) type ( @@ -16,8 +17,7 @@ type ( VoteExtensionMempool interface { Remove(tx sdk.Tx) error AuctionBidSelect(ctx context.Context) sdkmempool.Iterator - IsAuctionTx(tx sdk.Tx) (bool, error) - GetBundledTransactions(tx sdk.Tx) ([][]byte, error) + GetAuctionBidInfo(tx sdk.Tx) (*mempool.AuctionBidInfo, error) WrapBundleTransaction(tx []byte) (sdk.Tx, error) } @@ -140,12 +140,12 @@ func (h *VoteExtensionHandler) resetCache(blockHeight int64) { // verifyAuctionTx verifies a transaction against the application's state. func (h *VoteExtensionHandler) verifyAuctionTx(ctx sdk.Context, bidTx sdk.Tx) error { // Verify the vote extension is a auction transaction - isAuctionTx, err := h.mempool.IsAuctionTx(bidTx) + bidInfo, err := h.mempool.GetAuctionBidInfo(bidTx) if err != nil { return err } - if !isAuctionTx { + if bidInfo == nil { return fmt.Errorf("vote extension is not a valid auction transaction") } @@ -159,13 +159,8 @@ func (h *VoteExtensionHandler) verifyAuctionTx(ctx sdk.Context, bidTx sdk.Tx) er return err } - bundledTxs, err := h.mempool.GetBundledTransactions(bidTx) - if err != nil { - return err - } - // Verify all bundled transactions - for _, tx := range bundledTxs { + for _, tx := range bidInfo.Transactions { wrappedTx, err := h.mempool.WrapBundleTransaction(tx) if err != nil { return err diff --git a/abci/vote_extensions_test.go b/abci/vote_extensions_test.go index dd8aba3..79f787b 100644 --- a/abci/vote_extensions_test.go +++ b/abci/vote_extensions_test.go @@ -79,31 +79,6 @@ func (suite *ABCITestSuite) TestExtendVoteExtensionHandler() { return nil }, }, - { - "mempool contains bid that has bundled txs that are invalid", - func() []byte { - params.ReserveFee = sdk.NewCoin("foo", sdk.NewInt(10)) - err := suite.builderKeeper.SetParams(suite.ctx, params) - suite.Require().NoError(err) - - bidder := suite.accounts[0] - bid := params.ReserveFee.Sub(sdk.NewCoin("foo", sdk.NewInt(1))) - - msgAuctionBid, err := testutils.CreateMsgAuctionBid(suite.encodingConfig.TxConfig, bidder, bid, 0, 0) - suite.Require().NoError(err) - msgAuctionBid.Transactions = [][]byte{[]byte("invalid tx")} - - bidTx, err := testutils.CreateTx(suite.encodingConfig.TxConfig, bidder, 0, 10, []sdk.Msg{msgAuctionBid}) - suite.Require().NoError(err) - - suite.mempool = mempool.NewAuctionMempool(suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), 0, suite.config) - err = suite.mempool.Insert(suite.ctx, bidTx) - suite.Require().NoError(err) - - // this should return nothing since the top bid is not valid - return nil - }, - }, { "mempool contains bid that has an invalid timeout", func() []byte { @@ -126,6 +101,10 @@ func (suite *ABCITestSuite) TestExtendVoteExtensionHandler() { { "top bid is invalid but next best is valid", func() []byte { + params.ReserveFee = sdk.NewCoin("foo", sdk.NewInt(100)) + err := suite.builderKeeper.SetParams(suite.ctx, params) + suite.Require().NoError(err) + bidder := suite.accounts[0] bid := suite.auctionBidAmount.Add(suite.minBidIncrement) signers := []testutils.Account{bidder} diff --git a/mempool/auction_bid.go b/mempool/auction_bid.go new file mode 100644 index 0000000..86d36d4 --- /dev/null +++ b/mempool/auction_bid.go @@ -0,0 +1,128 @@ +package mempool + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +type ( + // AuctionBidInfo defines the information about a bid to the auction house. + AuctionBidInfo struct { + Bidder sdk.AccAddress + Bid sdk.Coin + Transactions [][]byte + Timeout uint64 + Signers []map[string]struct{} + } + + // AuctionFactory defines the interface for processing auction transactions. It is + // a wrapper around all of the functionality that each application chain must implement + // in order for auction processing to work. + AuctionFactory interface { + // WrapBundleTransaction defines a function that wraps a bundle transaction into a sdk.Tx. Since + // this is a potentially expensive operation, we allow each application chain to define how + // they want to wrap the transaction such that it is only called when necessary (i.e. when the + // transaction is being considered in the proposal handlers). + WrapBundleTransaction(tx []byte) (sdk.Tx, error) + + // GetAuctionBidInfo defines a function that returns the bid info from an auction transaction. + GetAuctionBidInfo(tx sdk.Tx) (*AuctionBidInfo, error) + } + + // DefaultAuctionFactory defines a default implmentation for the auction factory interface for processing auction transactions. + DefaultAuctionFactory struct { + txDecoder sdk.TxDecoder + } + + // TxWithTimeoutHeight is used to extract timeouts from sdk.Tx transactions. In the case where, + // timeouts are explicitly set on the sdk.Tx, we can use this interface to extract the timeout. + TxWithTimeoutHeight interface { + sdk.Tx + + GetTimeoutHeight() uint64 + } +) + +var _ AuctionFactory = (*DefaultAuctionFactory)(nil) + +// NewDefaultAuctionFactory returns a default auction factory interface implementation. +func NewDefaultAuctionFactory(txDecoder sdk.TxDecoder) AuctionFactory { + return &DefaultAuctionFactory{ + txDecoder: txDecoder, + } +} + +// WrapBundleTransaction defines a default function that wraps a transaction +// that is included in the bundle into a sdk.Tx. In the default case, the transaction +// that is included in the bundle will be the raw bytes of an sdk.Tx so we can just +// decode it. +func (config *DefaultAuctionFactory) WrapBundleTransaction(tx []byte) (sdk.Tx, error) { + return config.txDecoder(tx) +} + +// GetAuctionBidInfo defines a default function that returns the auction bid info from +// an auction transaction. In the default case, the auction bid info is stored in the +// MsgAuctionBid message. +func (config *DefaultAuctionFactory) GetAuctionBidInfo(tx sdk.Tx) (*AuctionBidInfo, error) { + msg, err := GetMsgAuctionBidFromTx(tx) + if err != nil { + return nil, err + } + + if msg == nil { + return nil, nil + } + + bidder, err := sdk.AccAddressFromBech32(msg.Bidder) + if err != nil { + return nil, fmt.Errorf("invalid bidder address (%s): %w", msg.Bidder, err) + } + + timeoutTx, ok := tx.(TxWithTimeoutHeight) + if !ok { + return nil, fmt.Errorf("cannot extract timeout; transaction does not implement TxWithTimeoutHeight") + } + + signers, err := config.getBundleSigners(msg.Transactions) + if err != nil { + return nil, err + } + + return &AuctionBidInfo{ + Bid: msg.Bid, + Bidder: bidder, + Transactions: msg.Transactions, + Timeout: timeoutTx.GetTimeoutHeight(), + Signers: signers, + }, nil +} + +// getBundleSigners defines a default function that returns the signers of all transactions in +// a bundle. In the default case, each bundle transaction will be an sdk.Tx and the +// signers are the signers of each sdk.Msg in the transaction. +func (config *DefaultAuctionFactory) getBundleSigners(bundle [][]byte) ([]map[string]struct{}, error) { + signers := make([]map[string]struct{}, 0) + + for _, tx := range bundle { + sdkTx, err := config.txDecoder(tx) + if err != nil { + return nil, err + } + + sigTx, ok := sdkTx.(signing.SigVerifiableTx) + if !ok { + return nil, fmt.Errorf("transaction is not valid") + } + + txSigners := make(map[string]struct{}) + for _, signer := range sigTx.GetSigners() { + txSigners[signer.String()] = struct{}{} + } + + signers = append(signers, txSigners) + } + + return signers, nil +} diff --git a/mempool/config_test.go b/mempool/auction_bid_test.go similarity index 67% rename from mempool/config_test.go rename to mempool/auction_bid_test.go index d856e2a..314663b 100644 --- a/mempool/config_test.go +++ b/mempool/auction_bid_test.go @@ -1,6 +1,8 @@ package mempool_test import ( + "crypto/rand" + sdk "github.com/cosmos/cosmos-sdk/types" testutils "github.com/skip-mev/pob/testutils" ) @@ -77,9 +79,9 @@ func (suite *IntegrationTestSuite) TestIsAuctionTx() { suite.Run(tc.name, func() { tx := tc.createTx() - isAuctionTx, err := suite.config.IsAuctionTx(tx) + bidInfo, err := suite.config.GetAuctionBidInfo(tx) - suite.Require().Equal(tc.isAuctionTx, isAuctionTx) + suite.Require().Equal(tc.isAuctionTx, bidInfo != nil) if tc.expectedError { suite.Require().Error(err) } else { @@ -92,47 +94,70 @@ func (suite *IntegrationTestSuite) TestIsAuctionTx() { func (suite *IntegrationTestSuite) TestGetTransactionSigners() { testCases := []struct { name string - createTx func() []byte - expectedSigners []string + createTx func() sdk.Tx + expectedSigners []map[string]struct{} + expectedError bool }{ { - "normal sdk tx", - func() []byte { - tx, err := testutils.CreateRandomTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 1, 0) + "normal auction tx", + func() sdk.Tx { + tx, err := testutils.CreateAuctionTxWithSigners( + suite.encCfg.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(100)), + 1, + 0, + suite.accounts[0:1], + ) suite.Require().NoError(err) - bz, err := suite.encCfg.TxConfig.TxEncoder()(tx) - suite.Require().NoError(err) - - return bz + return tx }, - []string{suite.accounts[0].Address.String()}, + []map[string]struct{}{ + { + suite.accounts[0].Address.String(): {}, + }, + }, + false, }, { - "normal sdk tx with several messages", - func() []byte { + "normal sdk tx", + func() sdk.Tx { tx, err := testutils.CreateRandomTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 10, 0) suite.Require().NoError(err) - bz, err := suite.encCfg.TxConfig.TxEncoder()(tx) - suite.Require().NoError(err) - - return bz + return tx }, - []string{suite.accounts[0].Address.String()}, + nil, + true, }, { - "multiple signers on tx", - func() []byte { - tx, err := testutils.CreateTxWithSigners(suite.encCfg.TxConfig, 0, 0, suite.accounts[0:3]) + "multiple signers on auction tx", + func() sdk.Tx { + tx, err := testutils.CreateAuctionTxWithSigners( + suite.encCfg.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(100)), + 1, + 0, + suite.accounts[0:3], + ) suite.Require().NoError(err) - bz, err := suite.encCfg.TxConfig.TxEncoder()(tx) - suite.Require().NoError(err) - - return bz + return tx }, - []string{suite.accounts[0].Address.String(), suite.accounts[1].Address.String(), suite.accounts[2].Address.String()}, + []map[string]struct{}{ + { + suite.accounts[0].Address.String(): {}, + }, + { + suite.accounts[1].Address.String(): {}, + }, + { + suite.accounts[2].Address.String(): {}, + }, + }, + false, }, } @@ -140,103 +165,11 @@ func (suite *IntegrationTestSuite) TestGetTransactionSigners() { suite.Run(tc.name, func() { tx := tc.createTx() - signers, err := suite.config.GetTransactionSigners(tx) - suite.Require().NoError(err) - suite.Require().Equal(len(tc.expectedSigners), len(signers)) - - for _, signer := range tc.expectedSigners { - suite.Require().Contains(signers, signer) - } - }) - } -} - -func (suite *IntegrationTestSuite) TestGetBundleSigners() { - testCases := []struct { - name string - createBundle func() [][]byte - expectedSigners [][]string - }{ - { - "single bundle with one signer", - func() [][]byte { - tx, err := testutils.CreateRandomTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 1, 0) - suite.Require().NoError(err) - - bz, err := suite.encCfg.TxConfig.TxEncoder()(tx) - suite.Require().NoError(err) - - return [][]byte{bz} - }, - [][]string{{suite.accounts[0].Address.String()}}, - }, - { - "single bundle with multiple signers", - func() [][]byte { - tx, err := testutils.CreateTxWithSigners(suite.encCfg.TxConfig, 0, 0, suite.accounts[0:3]) - suite.Require().NoError(err) - - bz, err := suite.encCfg.TxConfig.TxEncoder()(tx) - suite.Require().NoError(err) - - return [][]byte{bz} - }, - [][]string{{suite.accounts[0].Address.String(), suite.accounts[1].Address.String(), suite.accounts[2].Address.String()}}, - }, - { - "multiple bundles with one signer", - func() [][]byte { - tx1, err := testutils.CreateRandomTx(suite.encCfg.TxConfig, suite.accounts[0], 0, 1, 0) - suite.Require().NoError(err) - - bz1, err := suite.encCfg.TxConfig.TxEncoder()(tx1) - suite.Require().NoError(err) - - tx2, err := testutils.CreateRandomTx(suite.encCfg.TxConfig, suite.accounts[1], 0, 1, 0) - suite.Require().NoError(err) - - bz2, err := suite.encCfg.TxConfig.TxEncoder()(tx2) - suite.Require().NoError(err) - - return [][]byte{bz1, bz2} - }, - [][]string{{suite.accounts[0].Address.String()}, {suite.accounts[1].Address.String()}}, - }, - { - "multiple bundles with multiple signers", - func() [][]byte { - tx1, err := testutils.CreateTxWithSigners(suite.encCfg.TxConfig, 0, 0, suite.accounts[0:3]) - suite.Require().NoError(err) - - bz1, err := suite.encCfg.TxConfig.TxEncoder()(tx1) - suite.Require().NoError(err) - - tx2, err := testutils.CreateTxWithSigners(suite.encCfg.TxConfig, 0, 0, suite.accounts[3:6]) - suite.Require().NoError(err) - - bz2, err := suite.encCfg.TxConfig.TxEncoder()(tx2) - suite.Require().NoError(err) - - return [][]byte{bz1, bz2} - }, - [][]string{{suite.accounts[0].Address.String(), suite.accounts[1].Address.String(), suite.accounts[2].Address.String()}, {suite.accounts[3].Address.String(), suite.accounts[4].Address.String(), suite.accounts[5].Address.String()}}, - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - bundle := tc.createBundle() - - signers, err := suite.config.GetBundleSigners(bundle) - suite.Require().NoError(err) - suite.Require().Equal(len(tc.expectedSigners), len(signers)) - - for i, bundleSigners := range tc.expectedSigners { - suite.Require().Equal(len(bundleSigners), len(signers[i])) - - for _, signer := range bundleSigners { - suite.Require().Contains(signers[i], signer) - } + bidInfo, _ := suite.config.GetAuctionBidInfo(tx) + if tc.expectedError { + suite.Require().Nil(bidInfo) + } else { + suite.Require().Equal(tc.expectedSigners, bidInfo.Signers) } }) } @@ -246,6 +179,7 @@ func (suite *IntegrationTestSuite) TestWrapBundleTransaction() { testCases := []struct { name string createBundleTx func() (sdk.Tx, []byte) + expectedError bool }{ { "normal sdk tx", @@ -258,6 +192,17 @@ func (suite *IntegrationTestSuite) TestWrapBundleTransaction() { return tx, bz }, + false, + }, + { + "random bytes with expected failure", + func() (sdk.Tx, []byte) { + bz := make([]byte, 100) + rand.Read(bz) + + return nil, bz + }, + true, }, } @@ -266,15 +211,19 @@ func (suite *IntegrationTestSuite) TestWrapBundleTransaction() { tx, bz := tc.createBundleTx() wrappedTx, err := suite.config.WrapBundleTransaction(bz) - suite.Require().NoError(err) + if tc.expectedError { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) - txBytes, err := suite.encCfg.TxConfig.TxEncoder()(tx) - suite.Require().NoError(err) + txBytes, err := suite.encCfg.TxConfig.TxEncoder()(tx) + suite.Require().NoError(err) - wrappedTxBytes, err := suite.encCfg.TxConfig.TxEncoder()(wrappedTx) - suite.Require().NoError(err) + wrappedTxBytes, err := suite.encCfg.TxConfig.TxEncoder()(wrappedTx) + suite.Require().NoError(err) - suite.Require().Equal(txBytes, wrappedTxBytes) + suite.Require().Equal(txBytes, wrappedTxBytes) + } }) } } @@ -285,6 +234,7 @@ func (suite *IntegrationTestSuite) TestGetBidder() { createTx func() sdk.Tx expectedBidder string expectedError bool + isAuctionTx bool }{ { "normal sdk tx", @@ -295,7 +245,8 @@ func (suite *IntegrationTestSuite) TestGetBidder() { return tx }, "", - true, + false, + false, }, { "valid auction tx", @@ -311,6 +262,7 @@ func (suite *IntegrationTestSuite) TestGetBidder() { }, suite.accounts[0].Address.String(), false, + true, }, { "invalid auction tx", @@ -329,6 +281,7 @@ func (suite *IntegrationTestSuite) TestGetBidder() { }, "", true, + false, }, } @@ -336,12 +289,15 @@ func (suite *IntegrationTestSuite) TestGetBidder() { suite.Run(tc.name, func() { tx := tc.createTx() - bidder, err := suite.config.GetBidder(tx) + bidInfo, err := suite.config.GetAuctionBidInfo(tx) if tc.expectedError { suite.Require().Error(err) } else { suite.Require().NoError(err) - suite.Require().Equal(tc.expectedBidder, bidder.String()) + + if tc.isAuctionTx { + suite.Require().Equal(tc.expectedBidder, bidInfo.Bidder.String()) + } } }) } @@ -353,6 +309,7 @@ func (suite *IntegrationTestSuite) TestGetBid() { createTx func() sdk.Tx expectedBid sdk.Coin expectedError bool + isAuctionTx bool }{ { "normal sdk tx", @@ -363,7 +320,8 @@ func (suite *IntegrationTestSuite) TestGetBid() { return tx }, sdk.Coin{}, - true, + false, + false, }, { "valid auction tx", @@ -379,6 +337,7 @@ func (suite *IntegrationTestSuite) TestGetBid() { }, sdk.NewInt64Coin("foo", 100), false, + true, }, { "invalid auction tx", @@ -397,6 +356,7 @@ func (suite *IntegrationTestSuite) TestGetBid() { }, sdk.Coin{}, true, + false, }, } @@ -404,12 +364,15 @@ func (suite *IntegrationTestSuite) TestGetBid() { suite.Run(tc.name, func() { tx := tc.createTx() - bid, err := suite.config.GetBid(tx) + bidInfo, err := suite.config.GetAuctionBidInfo(tx) if tc.expectedError { suite.Require().Error(err) } else { suite.Require().NoError(err) - suite.Require().Equal(tc.expectedBid, bid) + + if tc.isAuctionTx { + suite.Require().Equal(tc.expectedBid, bidInfo.Bid) + } } }) } @@ -420,6 +383,7 @@ func (suite *IntegrationTestSuite) TestGetBundledTransactions() { name string createTx func() (sdk.Tx, [][]byte) expectedError bool + isAuctionTx bool }{ { "normal sdk tx", @@ -429,7 +393,8 @@ func (suite *IntegrationTestSuite) TestGetBundledTransactions() { return tx, nil }, - true, + false, + false, }, { "valid auction tx", @@ -444,6 +409,7 @@ func (suite *IntegrationTestSuite) TestGetBundledTransactions() { return tx, msgAuctionBid.Transactions }, false, + true, }, { "invalid auction tx", @@ -461,6 +427,7 @@ func (suite *IntegrationTestSuite) TestGetBundledTransactions() { return tx, nil }, true, + false, }, } @@ -468,12 +435,15 @@ func (suite *IntegrationTestSuite) TestGetBundledTransactions() { suite.Run(tc.name, func() { tx, expectedBundledTxs := tc.createTx() - bundledTxs, err := suite.config.GetBundledTransactions(tx) + bidInfo, err := suite.config.GetAuctionBidInfo(tx) if tc.expectedError { suite.Require().Error(err) } else { suite.Require().NoError(err) - suite.Require().Equal(expectedBundledTxs, bundledTxs) + + if tc.isAuctionTx { + suite.Require().Equal(expectedBundledTxs, bidInfo.Transactions) + } } }) } @@ -484,6 +454,7 @@ func (suite *IntegrationTestSuite) TestGetTimeout() { name string createTx func() sdk.Tx expectedError bool + isAuctionTx bool expectedTimeout uint64 }{ { @@ -495,6 +466,7 @@ func (suite *IntegrationTestSuite) TestGetTimeout() { return tx }, false, + false, 1, }, { @@ -510,6 +482,7 @@ func (suite *IntegrationTestSuite) TestGetTimeout() { return tx }, false, + true, 10, }, { @@ -527,6 +500,7 @@ func (suite *IntegrationTestSuite) TestGetTimeout() { suite.Require().NoError(err) return tx }, + true, false, 10, }, @@ -536,12 +510,15 @@ func (suite *IntegrationTestSuite) TestGetTimeout() { suite.Run(tc.name, func() { tx := tc.createTx() - timeout, err := suite.config.GetTimeout(tx) + bidInfo, err := suite.config.GetAuctionBidInfo(tx) if tc.expectedError { suite.Require().Error(err) } else { suite.Require().NoError(err) - suite.Require().Equal(tc.expectedTimeout, timeout) + + if tc.isAuctionTx { + suite.Require().Equal(tc.expectedTimeout, bidInfo.Timeout) + } } }) } diff --git a/mempool/config.go b/mempool/config.go deleted file mode 100644 index 1932c12..0000000 --- a/mempool/config.go +++ /dev/null @@ -1,242 +0,0 @@ -package mempool - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/signing" -) - -type ( - // AuctionBidInfo defines the information about a bid to the auction house. - AuctionBidInfo struct { - Bidder sdk.AccAddress - Bid sdk.Coin - Transactions [][]byte - Timeout uint64 - } - - // Config defines the configuration for processing auction transactions. It is - // a wrapper around all of the functionality that each application chain must implement - // in order for auction processing to work. - Config interface { - // IsAuctionTx defines a function that returns true iff a transaction is an - // auction bid transaction. - IsAuctionTx(tx sdk.Tx) (bool, error) - - // GetTransactionSigners defines a function that returns the signers of a - // bundle transaction i.e. transaction that was included in the auction transaction's bundle. - GetTransactionSigners(tx []byte) (map[string]struct{}, error) - - // GetBundleSigners defines a function that returns the signers of every transaction in a bundle. - GetBundleSigners(tx [][]byte) ([]map[string]struct{}, error) - - // WrapBundleTransaction defines a function that wraps a bundle transaction into a sdk.Tx. - WrapBundleTransaction(tx []byte) (sdk.Tx, error) - - // GetBidder defines a function that returns the bidder of an auction transaction transaction. - GetBidder(tx sdk.Tx) (sdk.AccAddress, error) - - // GetBid defines a function that returns the bid of an auction transaction. - GetBid(tx sdk.Tx) (sdk.Coin, error) - - // GetBundledTransactions defines a function that returns the bundled transactions - // that the user wants to execute at the top of the block given an auction transaction. - GetBundledTransactions(tx sdk.Tx) ([][]byte, error) - - // GetTimeout defines a function that returns the timeout of an auction transaction. - GetTimeout(tx sdk.Tx) (uint64, error) - - // GetAuctionBidInfo defines a function that returns the bid info from an auction transaction. - GetAuctionBidInfo(tx sdk.Tx) (AuctionBidInfo, error) - } - - // DefaultConfig defines a default configuration for processing auction transactions. - DefaultConfig struct { - txDecoder sdk.TxDecoder - } - - // TxWithTimeoutHeight is used to extract timeouts from sdk.Tx transactions. In the case where, - // timeouts are explicitly set on the sdk.Tx, we can use this interface to extract the timeout. - TxWithTimeoutHeight interface { - sdk.Tx - - GetTimeoutHeight() uint64 - } -) - -var _ Config = (*DefaultConfig)(nil) - -// NewDefaultConfig returns a default transaction configuration. -func NewDefaultConfig(txDecoder sdk.TxDecoder) Config { - return &DefaultConfig{ - txDecoder: txDecoder, - } -} - -// NewDefaultIsAuctionTx defines a default function that returns true iff a transaction -// is an auction bid transaction. In the default case, the transaction must contain a single -// MsgAuctionBid message. -func (config *DefaultConfig) IsAuctionTx(tx sdk.Tx) (bool, error) { - msg, err := GetMsgAuctionBidFromTx(tx) - if err != nil { - return false, err - } - - return msg != nil, nil -} - -// GetTransactionSigners defines a default function that returns the signers -// of a transaction. In the default case, each bundle transaction will be an sdk.Tx and the -// signers are the signers of each sdk.Msg in the transaction. -func (config *DefaultConfig) GetTransactionSigners(tx []byte) (map[string]struct{}, error) { - sdkTx, err := config.txDecoder(tx) - if err != nil { - return nil, err - } - - sigTx, ok := sdkTx.(signing.SigVerifiableTx) - if !ok { - return nil, fmt.Errorf("transaction is not valid") - } - - signers := make(map[string]struct{}) - for _, signer := range sigTx.GetSigners() { - signers[signer.String()] = struct{}{} - } - - return signers, nil -} - -// GetBundleSigners defines a default function that returns the signers of every transaction -// in a bundle. In the default case, each bundle transaction will be an sdk.Tx and the -// signers are the signers of each sdk.Msg in the transaction. -func (config *DefaultConfig) GetBundleSigners(txs [][]byte) ([]map[string]struct{}, error) { - signers := make([]map[string]struct{}, len(txs)) - - for index, tx := range txs { - txSigners, err := config.GetTransactionSigners(tx) - if err != nil { - return nil, err - } - - signers[index] = txSigners - } - - return signers, nil -} - -// WrapBundleTransaction defines a default function that wraps a transaction -// that is included in the bundle into a sdk.Tx. In the default case, the transaction -// that is included in the bundle will be the raw bytes of an sdk.Tx so we can just -// decode it. -func (config *DefaultConfig) WrapBundleTransaction(tx []byte) (sdk.Tx, error) { - return config.txDecoder(tx) -} - -// GetBidder defines a default function that returns the bidder of an auction transaction. -// In the default case, the bidder is the address defined in MsgAuctionBid. -func (config *DefaultConfig) GetBidder(tx sdk.Tx) (sdk.AccAddress, error) { - isAuctionTx, err := config.IsAuctionTx(tx) - if err != nil { - return nil, err - } - - if !isAuctionTx { - return nil, fmt.Errorf("transaction is not an auction transaction") - } - - msg, err := GetMsgAuctionBidFromTx(tx) - if err != nil { - return nil, err - } - - bidder, err := sdk.AccAddressFromBech32(msg.Bidder) - if err != nil { - return nil, fmt.Errorf("invalid bidder address (%s): %w", msg.Bidder, err) - } - - return bidder, nil -} - -// GetBid defines a default function that returns the bid of an auction transaction. -// In the default case, the bid is the amount defined in MsgAuctionBid. -func (config *DefaultConfig) GetBid(tx sdk.Tx) (sdk.Coin, error) { - isAuctionTx, err := config.IsAuctionTx(tx) - if err != nil { - return sdk.Coin{}, err - } - - if !isAuctionTx { - return sdk.Coin{}, fmt.Errorf("transaction is not an auction transaction") - } - - msg, err := GetMsgAuctionBidFromTx(tx) - if err != nil { - return sdk.Coin{}, err - } - - return msg.Bid, nil -} - -// GetBundledTransactions defines a default function that returns the bundled -// transactions that the user wants to execute at the top of the block. In the default case, -// the bundled transactions will be the raw bytes of sdk.Tx's that are included in the -// MsgAuctionBid. -func (config *DefaultConfig) GetBundledTransactions(tx sdk.Tx) ([][]byte, error) { - isAuctionTx, err := config.IsAuctionTx(tx) - if err != nil { - return nil, err - } - - if !isAuctionTx { - return nil, fmt.Errorf("transaction is not an auction transaction") - } - - msg, err := GetMsgAuctionBidFromTx(tx) - if err != nil { - return nil, err - } - - return msg.Transactions, nil -} - -// GetTimeout defines a default function that returns the timeout of an auction transaction. -func (config *DefaultConfig) GetTimeout(tx sdk.Tx) (uint64, error) { - timeoutTx, ok := tx.(TxWithTimeoutHeight) - if !ok { - return 0, fmt.Errorf("transaction does not implement TxWithTimeoutHeight") - } - - return timeoutTx.GetTimeoutHeight(), nil -} - -// GetAuctionBidInfo returns the auction bid info from an auction transaction. -func (config *DefaultConfig) GetAuctionBidInfo(tx sdk.Tx) (AuctionBidInfo, error) { - bid, err := config.GetBid(tx) - if err != nil { - return AuctionBidInfo{}, err - } - - bidder, err := config.GetBidder(tx) - if err != nil { - return AuctionBidInfo{}, err - } - - bundle, err := config.GetBundledTransactions(tx) - if err != nil { - return AuctionBidInfo{}, err - } - - timeout, err := config.GetTimeout(tx) - if err != nil { - return AuctionBidInfo{}, err - } - - return AuctionBidInfo{ - Bid: bid, - Bidder: bidder, - Transactions: bundle, - Timeout: timeout, - }, nil -} diff --git a/mempool/mempool.go b/mempool/mempool.go index 14415e5..ea09872 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -38,21 +38,21 @@ type AuctionMempool struct { // to quickly check if a transaction is already in the mempool. txIndex map[string]struct{} - // Config defines the transaction configuration for processing auction transactions. - Config + // AuctionFactory implements the functionality required to process auction transactions. + AuctionFactory } // AuctionTxPriority returns a TxPriority over auction bid transactions only. It // is to be used in the auction index only. -func AuctionTxPriority(config Config) TxPriority[string] { +func AuctionTxPriority(config AuctionFactory) TxPriority[string] { return TxPriority[string]{ GetTxPriority: func(goCtx context.Context, tx sdk.Tx) string { - bid, err := config.GetBid(tx) + bidInfo, err := config.GetAuctionBidInfo(tx) if err != nil { panic(err) } - return bid.String() + return bidInfo.Bid.String() }, Compare: func(a, b string) int { aCoins, _ := sdk.ParseCoinsNormalized(a) @@ -85,7 +85,7 @@ func AuctionTxPriority(config Config) TxPriority[string] { } } -func NewAuctionMempool(txDecoder sdk.TxDecoder, txEncoder sdk.TxEncoder, maxTx int, config Config) *AuctionMempool { +func NewAuctionMempool(txDecoder sdk.TxDecoder, txEncoder sdk.TxEncoder, maxTx int, config AuctionFactory) *AuctionMempool { return &AuctionMempool{ globalIndex: NewPriorityMempool( PriorityNonceMempoolConfig[int64]{ @@ -99,27 +99,26 @@ func NewAuctionMempool(txDecoder sdk.TxDecoder, txEncoder sdk.TxEncoder, maxTx i MaxTx: maxTx, }, ), - txDecoder: txDecoder, - txEncoder: txEncoder, - txIndex: make(map[string]struct{}), - Config: config, + txDecoder: txDecoder, + txEncoder: txEncoder, + txIndex: make(map[string]struct{}), + AuctionFactory: config, } } // Insert inserts a transaction into the mempool based on the transaction type (normal or auction). func (am *AuctionMempool) Insert(ctx context.Context, tx sdk.Tx) error { - isAuctionTx, err := am.IsAuctionTx(tx) + bidInfo, err := am.GetAuctionBidInfo(tx) if err != nil { return err } // Insert the transactions into the appropriate index. - switch { - case !isAuctionTx: + if bidInfo == nil { if err := am.globalIndex.Insert(ctx, tx); err != nil { return fmt.Errorf("failed to insert tx into global index: %w", err) } - case isAuctionTx: + } else { if err := am.auctionIndex.Insert(ctx, tx); err != nil { return fmt.Errorf("failed to insert tx into auction index: %w", err) } @@ -137,16 +136,15 @@ func (am *AuctionMempool) Insert(ctx context.Context, tx sdk.Tx) error { // Remove removes a transaction from the mempool based on the transaction type (normal or auction). func (am *AuctionMempool) Remove(tx sdk.Tx) error { - isAuctionTx, err := am.IsAuctionTx(tx) + bidInfo, err := am.GetAuctionBidInfo(tx) if err != nil { return err } // Remove the transactions from the appropriate index. - switch { - case !isAuctionTx: + if bidInfo == nil { am.removeTx(am.globalIndex, tx) - case isAuctionTx: + } else { am.removeTx(am.auctionIndex, tx) } diff --git a/mempool/mempool_test.go b/mempool/mempool_test.go index 86ca9c9..0f375ee 100644 --- a/mempool/mempool_test.go +++ b/mempool/mempool_test.go @@ -19,7 +19,7 @@ type IntegrationTestSuite struct { suite.Suite encCfg testutils.EncodingConfig - config mempool.Config + config mempool.AuctionFactory mempool *mempool.AuctionMempool ctx sdk.Context random *rand.Rand @@ -34,7 +34,7 @@ func TestMempoolTestSuite(t *testing.T) { func (suite *IntegrationTestSuite) SetupTest() { // Mempool setup suite.encCfg = testutils.CreateTestEncodingConfig() - suite.config = mempool.NewDefaultConfig(suite.encCfg.TxConfig.TxDecoder()) + suite.config = mempool.NewDefaultAuctionFactory(suite.encCfg.TxConfig.TxDecoder()) suite.mempool = mempool.NewAuctionMempool(suite.encCfg.TxConfig.TxDecoder(), suite.encCfg.TxConfig.TxEncoder(), 0, suite.config) suite.ctx = sdk.NewContext(nil, cmtproto.Header{}, false, log.NewNopLogger()) diff --git a/x/builder/ante/ante.go b/x/builder/ante/ante.go index cf55eb3..d0be9bd 100644 --- a/x/builder/ante/ante.go +++ b/x/builder/ante/ante.go @@ -16,11 +16,8 @@ var _ sdk.AnteDecorator = BuilderDecorator{} type ( Mempool interface { Contains(tx sdk.Tx) (bool, error) - IsAuctionTx(tx sdk.Tx) (bool, error) - GetAuctionBidInfo(tx sdk.Tx) (mempool.AuctionBidInfo, error) - GetBundleSigners(txs [][]byte) ([]map[string]struct{}, error) + GetAuctionBidInfo(tx sdk.Tx) (*mempool.AuctionBidInfo, error) GetTopAuctionTx(ctx context.Context) sdk.Tx - GetTimeout(tx sdk.Tx) (uint64, error) } BuilderDecorator struct { @@ -55,108 +52,51 @@ func (ad BuilderDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, } } - isAuctionTx, err := ad.mempool.IsAuctionTx(tx) + bidInfo, err := ad.mempool.GetAuctionBidInfo(tx) if err != nil { return ctx, err } // Validate the auction bid if one exists. - if isAuctionTx { + if bidInfo != nil { // Auction transactions must have a timeout set to a valid block height. - if err := ad.HasValidTimeout(ctx, tx); err != nil { - return ctx, err + if int64(bidInfo.Timeout) < ctx.BlockHeight() { + return ctx, fmt.Errorf("timeout height cannot be less than the current block height") } - bidInfo, err := ad.mempool.GetAuctionBidInfo(tx) - if err != nil { - return ctx, err - } - - // If the current transaction is the highest bidding transaction, then the highest bid is empty. - topBid := sdk.Coin{} - // We only need to verify the auction bid relative to the local validator's mempool if the mode // is checkTx or recheckTx. Otherwise, the ABCI handlers (VerifyVoteExtension, ExtendVoteExtension, etc.) // will always compare the auction bid to the highest bidding transaction in the mempool leading to // poor liveness guarantees. + topBid := sdk.Coin{} if ctx.IsCheckTx() || ctx.IsReCheckTx() { - isTopBidTx, err := ad.IsTopBidTx(ctx, tx) - if err != nil { - return ctx, errors.Wrap(err, "failed to check if current transaction is highest bidding transaction") - } - - if !isTopBidTx { - // Set the top bid to the highest bidding transaction. - topBid, err = ad.GetTopAuctionBid(ctx) + if topBidTx := ad.mempool.GetTopAuctionTx(ctx); topBidTx != nil { + topBidBz, err := ad.txEncoder(topBidTx) if err != nil { - return ctx, errors.Wrap(err, "failed to get highest auction bid") + return ctx, err + } + + currentTxBz, err := ad.txEncoder(tx) + if err != nil { + return ctx, err + } + + // Compare the bytes to see if the current transaction is the highest bidding transaction. + if !bytes.Equal(topBidBz, currentTxBz) { + topBidInfo, err := ad.mempool.GetAuctionBidInfo(topBidTx) + if err != nil { + return ctx, err + } + + topBid = topBidInfo.Bid } } } - // Extract signers from bundle for verification. - signers, err := ad.mempool.GetBundleSigners(bidInfo.Transactions) - if err != nil { - return ctx, errors.Wrap(err, "failed to get bundle signers") - } - - if err := ad.builderKeeper.ValidateBidInfo(ctx, topBid, bidInfo, signers); err != nil { + if err := ad.builderKeeper.ValidateBidInfo(ctx, topBid, bidInfo); err != nil { return ctx, errors.Wrap(err, "failed to validate auction bid") } } return next(ctx, tx, simulate) } - -// GetTopAuctionBid returns the highest auction bid if one exists. -func (ad BuilderDecorator) GetTopAuctionBid(ctx sdk.Context) (sdk.Coin, error) { - auctionTx := ad.mempool.GetTopAuctionTx(ctx) - if auctionTx == nil { - return sdk.Coin{}, nil - } - - auctionBidInfo, err := ad.mempool.GetAuctionBidInfo(auctionTx) - if err != nil { - return sdk.Coin{}, err - } - - return auctionBidInfo.Bid, nil -} - -// IsTopBidTx returns true if the transaction inputted is the highest bidding auction transaction in the mempool. -func (ad BuilderDecorator) IsTopBidTx(ctx sdk.Context, tx sdk.Tx) (bool, error) { - auctionTx := ad.mempool.GetTopAuctionTx(ctx) - if auctionTx == nil { - return false, nil - } - - topBidBz, err := ad.txEncoder(auctionTx) - if err != nil { - return false, err - } - - currentTxBz, err := ad.txEncoder(tx) - if err != nil { - return false, err - } - - return bytes.Equal(topBidBz, currentTxBz), nil -} - -// HasValidTimeout returns true if the transaction has a valid timeout height. -func (ad BuilderDecorator) HasValidTimeout(ctx sdk.Context, tx sdk.Tx) error { - timeout, err := ad.mempool.GetTimeout(tx) - if err != nil { - return err - } - - if timeout == 0 { - return fmt.Errorf("timeout height cannot be zero") - } - - if timeout < uint64(ctx.BlockHeight()) { - return fmt.Errorf("timeout height cannot be less than the current block height") - } - - return nil -} diff --git a/x/builder/ante/ante_test.go b/x/builder/ante/ante_test.go index d09c435..d62d2a2 100644 --- a/x/builder/ante/ante_test.go +++ b/x/builder/ante/ante_test.go @@ -46,8 +46,7 @@ func (suite *AnteTestSuite) SetupTest() { suite.random = rand.New(rand.NewSource(time.Now().Unix())) suite.key = storetypes.NewKVStoreKey(buildertypes.StoreKey) testCtx := testutil.DefaultContextWithDB(suite.T(), suite.key, storetypes.NewTransientStoreKey("transient_test")) - suite.ctx = testCtx.Ctx - suite.ctx = suite.ctx.WithIsCheckTx(true) + suite.ctx = testCtx.Ctx.WithIsCheckTx(true) // Keepers set up ctrl := gomock.NewController(suite.T()) @@ -234,6 +233,8 @@ func (suite *AnteTestSuite) TestAnteHandler() { suite.SetupTest() tc.malleate() + suite.ctx = suite.ctx.WithBlockHeight(1) + // Set the auction params err := suite.builderKeeper.SetParams(suite.ctx, buildertypes.Params{ MaxBundleSize: maxBundleSize, @@ -245,7 +246,7 @@ func (suite *AnteTestSuite) TestAnteHandler() { suite.Require().NoError(err) // Insert the top bid into the mempool - config := mempool.NewDefaultConfig(suite.encodingConfig.TxConfig.TxDecoder()) + config := mempool.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder()) mempool := mempool.NewAuctionMempool(suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), 0, config) if insertTopBid { topAuctionTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, topBidder, topBid, 0, timeout, []testutils.Account{}) diff --git a/x/builder/keeper/auction.go b/x/builder/keeper/auction.go index ae8c4a9..d097fa6 100644 --- a/x/builder/keeper/auction.go +++ b/x/builder/keeper/auction.go @@ -8,7 +8,7 @@ import ( ) // ValidateBidInfo validates that the bid can be included in the auction. -func (k Keeper) ValidateBidInfo(ctx sdk.Context, highestBid sdk.Coin, bidInfo mempool.AuctionBidInfo, signers []map[string]struct{}) error { +func (k Keeper) ValidateBidInfo(ctx sdk.Context, highestBid sdk.Coin, bidInfo *mempool.AuctionBidInfo) error { // Validate the bundle size. maxBundleSize, err := k.GetMaxBundleSize(ctx) if err != nil { @@ -31,7 +31,7 @@ func (k Keeper) ValidateBidInfo(ctx sdk.Context, highestBid sdk.Coin, bidInfo me } if protectionEnabled { - if err := k.ValidateAuctionBundle(bidInfo.Bidder, signers); err != nil { + if err := k.ValidateAuctionBundle(bidInfo.Bidder, bidInfo.Signers); err != nil { return err } } diff --git a/x/builder/keeper/auction_test.go b/x/builder/keeper/auction_test.go index ddea532..498e96d 100644 --- a/x/builder/keeper/auction_test.go +++ b/x/builder/keeper/auction_test.go @@ -11,7 +11,7 @@ import ( buildertypes "github.com/skip-mev/pob/x/builder/types" ) -func (suite *KeeperTestSuite) TestValidateAuctionMsg() { +func (suite *KeeperTestSuite) TestValidateBidInfo() { var ( // Tx building variables accounts = []testutils.Account{} // tracks the order of signers in the bundle @@ -192,16 +192,23 @@ func (suite *KeeperTestSuite) TestValidateAuctionMsg() { bundle = append(bundle, txBz) } - bidInfo := mempool.AuctionBidInfo{ + signers := make([]map[string]struct{}, len(accounts)) + for index, acc := range accounts { + txSigners := map[string]struct{}{ + acc.Address.String(): {}, + } + + signers[index] = txSigners + } + + bidInfo := &mempool.AuctionBidInfo{ Bidder: bidder.Address, Bid: bid, Transactions: bundle, + Signers: signers, } - signers, err := suite.mempool.GetBundleSigners(bundle) - suite.Require().NoError(err) - - err = suite.builderKeeper.ValidateBidInfo(suite.ctx, highestBid, bidInfo, signers) + err := suite.builderKeeper.ValidateBidInfo(suite.ctx, highestBid, bidInfo) if tc.pass { suite.Require().NoError(err) } else { @@ -302,23 +309,17 @@ func (suite *KeeperTestSuite) TestValidateBundle() { // Malleate the test case tc.malleate() - // Create the bundle of transactions ordered by accounts - bundle := make([][]byte, 0) - for _, acc := range accounts { - // Create a random tx - tx, err := testutils.CreateRandomTx(suite.encCfg.TxConfig, acc, 0, 1, 1000) - suite.Require().NoError(err) + signers := make([]map[string]struct{}, len(accounts)) + for index, acc := range accounts { + txSigners := map[string]struct{}{ + acc.Address.String(): {}, + } - txBz, err := suite.encCfg.TxConfig.TxEncoder()(tx) - suite.Require().NoError(err) - bundle = append(bundle, txBz) + signers[index] = txSigners } - signers, err := suite.mempool.GetBundleSigners(bundle) - suite.Require().NoError(err) - // Validate the bundle - err = suite.builderKeeper.ValidateAuctionBundle(bidder.Address, signers) + err := suite.builderKeeper.ValidateAuctionBundle(bidder.Address, signers) if tc.pass { suite.Require().NoError(err) } else { diff --git a/x/builder/keeper/keeper_test.go b/x/builder/keeper/keeper_test.go index 1a0cfbf..c4d1094 100644 --- a/x/builder/keeper/keeper_test.go +++ b/x/builder/keeper/keeper_test.go @@ -64,7 +64,7 @@ func (suite *KeeperTestSuite) SetupTest() { err := suite.builderKeeper.SetParams(suite.ctx, types.DefaultParams()) suite.Require().NoError(err) - config := mempool.NewDefaultConfig(suite.encCfg.TxConfig.TxDecoder()) + config := mempool.NewDefaultAuctionFactory(suite.encCfg.TxConfig.TxDecoder()) suite.mempool = mempool.NewAuctionMempool(suite.encCfg.TxConfig.TxDecoder(), suite.encCfg.TxConfig.TxEncoder(), 0, config) suite.msgServer = keeper.NewMsgServerImpl(suite.builderKeeper) }