[ENG-733]: Supporting generic auction transaction configurations (#66)
This commit is contained in:
+14
-35
@@ -4,22 +4,23 @@ import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/skip-mev/pob/mempool"
|
||||
)
|
||||
|
||||
// ValidateAuctionMsg validates that the MsgAuctionBid can be included in the auction.
|
||||
func (k Keeper) ValidateAuctionMsg(ctx sdk.Context, bidder sdk.AccAddress, bid, highestBid sdk.Coin, transactions []sdk.Tx) error {
|
||||
// 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 {
|
||||
// Validate the bundle size.
|
||||
maxBundleSize, err := k.GetMaxBundleSize(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if uint32(len(transactions)) > maxBundleSize {
|
||||
return fmt.Errorf("bundle size (%d) exceeds max bundle size (%d)", len(transactions), maxBundleSize)
|
||||
if uint32(len(bidInfo.Transactions)) > maxBundleSize {
|
||||
return fmt.Errorf("bundle size (%d) exceeds max bundle size (%d)", len(bidInfo.Transactions), maxBundleSize)
|
||||
}
|
||||
|
||||
// Validate the bid amount.
|
||||
if err := k.ValidateAuctionBid(ctx, bidder, bid, highestBid); err != nil {
|
||||
if err := k.ValidateAuctionBid(ctx, bidInfo.Bidder, bidInfo.Bid, highestBid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -30,7 +31,7 @@ func (k Keeper) ValidateAuctionMsg(ctx sdk.Context, bidder sdk.AccAddress, bid,
|
||||
}
|
||||
|
||||
if protectionEnabled {
|
||||
if err := k.ValidateAuctionBundle(bidder, transactions); err != nil {
|
||||
if err := k.ValidateAuctionBundle(bidInfo.Bidder, signers); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -100,27 +101,19 @@ func (k Keeper) ValidateAuctionBid(ctx sdk.Context, bidder sdk.AccAddress, bid,
|
||||
// 2. valid: [tx1, tx2, tx3, tx4] where tx1 - tx4 are signed by the bidder.
|
||||
// 3. invalid: [tx1, tx2, tx3] where tx1 and tx3 are signed by the bidder and tx2 is signed by some other signer. (possible sandwich attack)
|
||||
// 4. invalid: [tx1, tx2, tx3] where tx1 is signed by the bidder, and tx2 - tx3 are signed by some other signer. (possible front-running attack)
|
||||
func (k Keeper) ValidateAuctionBundle(bidder sdk.AccAddress, transactions []sdk.Tx) error {
|
||||
if len(transactions) <= 1 {
|
||||
func (k Keeper) ValidateAuctionBundle(bidder sdk.AccAddress, bundleSigners []map[string]struct{}) error {
|
||||
if len(bundleSigners) <= 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// prevSigners is used to track whether the signers of the current transaction overlap.
|
||||
prevSigners, err := k.getTxSigners(transactions[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
seenBidder := prevSigners[bidder.String()]
|
||||
prevSigners := bundleSigners[0]
|
||||
_, seenBidder := prevSigners[bidder.String()]
|
||||
|
||||
// Check that all subsequent transactions are signed by either
|
||||
// 1. the same party as the first transaction
|
||||
// 2. the same party for some arbitrary number of txs and then are all remaining txs are signed by the bidder.
|
||||
for _, refTx := range transactions[1:] {
|
||||
txSigners, err := k.getTxSigners(refTx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, txSigners := range bundleSigners[1:] {
|
||||
// Filter the signers to only those that signed the current transaction.
|
||||
filterSigners(prevSigners, txSigners)
|
||||
|
||||
@@ -132,7 +125,7 @@ func (k Keeper) ValidateAuctionBundle(bidder sdk.AccAddress, transactions []sdk.
|
||||
}
|
||||
|
||||
seenBidder = true
|
||||
prevSigners = map[string]bool{bidder.String(): true}
|
||||
prevSigners = map[string]struct{}{bidder.String(): {}}
|
||||
filterSigners(prevSigners, txSigners)
|
||||
|
||||
if len(prevSigners) == 0 {
|
||||
@@ -144,22 +137,8 @@ func (k Keeper) ValidateAuctionBundle(bidder sdk.AccAddress, transactions []sdk.
|
||||
return nil
|
||||
}
|
||||
|
||||
// getTxSigners returns the signers of a transaction.
|
||||
func (k Keeper) getTxSigners(tx sdk.Tx) (map[string]bool, error) {
|
||||
signers := make(map[string]bool, 0)
|
||||
for _, msg := range tx.GetMsgs() {
|
||||
for _, signer := range msg.GetSigners() {
|
||||
// TODO: check for multi-sig accounts
|
||||
// https://github.com/skip-mev/pob/issues/14
|
||||
signers[signer.String()] = true
|
||||
}
|
||||
}
|
||||
|
||||
return signers, nil
|
||||
}
|
||||
|
||||
// filterSigners removes any signers from the currentSigners map that are not in the txSigners map.
|
||||
func filterSigners(currentSigners, txSigners map[string]bool) {
|
||||
func filterSigners(currentSigners, txSigners map[string]struct{}) {
|
||||
for signer := range currentSigners {
|
||||
if _, ok := txSigners[signer]; !ok {
|
||||
delete(currentSigners, signer)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/skip-mev/pob/mempool"
|
||||
testutils "github.com/skip-mev/pob/testutils"
|
||||
"github.com/skip-mev/pob/x/builder/keeper"
|
||||
buildertypes "github.com/skip-mev/pob/x/builder/types"
|
||||
@@ -181,14 +182,26 @@ func (suite *KeeperTestSuite) TestValidateAuctionMsg() {
|
||||
suite.builderKeeper.SetParams(suite.ctx, params)
|
||||
|
||||
// Create the bundle of transactions ordered by accounts
|
||||
bundle := make([]sdk.Tx, 0)
|
||||
bundle := make([][]byte, 0)
|
||||
for _, acc := range accounts {
|
||||
tx, err := testutils.CreateRandomTx(suite.encCfg.TxConfig, acc, 0, 1, 100)
|
||||
suite.Require().NoError(err)
|
||||
bundle = append(bundle, tx)
|
||||
|
||||
txBz, err := suite.encCfg.TxConfig.TxEncoder()(tx)
|
||||
suite.Require().NoError(err)
|
||||
bundle = append(bundle, txBz)
|
||||
}
|
||||
|
||||
err := suite.builderKeeper.ValidateAuctionMsg(suite.ctx, bidder.Address, bid, highestBid, bundle)
|
||||
bidInfo := mempool.AuctionBidInfo{
|
||||
Bidder: bidder.Address,
|
||||
Bid: bid,
|
||||
Transactions: bundle,
|
||||
}
|
||||
|
||||
signers, err := suite.mempool.GetBundleSigners(bundle)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
err = suite.builderKeeper.ValidateBidInfo(suite.ctx, highestBid, bidInfo, signers)
|
||||
if tc.pass {
|
||||
suite.Require().NoError(err)
|
||||
} else {
|
||||
@@ -290,16 +303,22 @@ func (suite *KeeperTestSuite) TestValidateBundle() {
|
||||
tc.malleate()
|
||||
|
||||
// Create the bundle of transactions ordered by accounts
|
||||
bundle := make([]sdk.Tx, 0)
|
||||
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)
|
||||
bundle = append(bundle, tx)
|
||||
|
||||
txBz, err := suite.encCfg.TxConfig.TxEncoder()(tx)
|
||||
suite.Require().NoError(err)
|
||||
bundle = append(bundle, txBz)
|
||||
}
|
||||
|
||||
signers, err := suite.mempool.GetBundleSigners(bundle)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Validate the bundle
|
||||
err := suite.builderKeeper.ValidateAuctionBundle(bidder.Address, bundle)
|
||||
err = suite.builderKeeper.ValidateAuctionBundle(bidder.Address, signers)
|
||||
if tc.pass {
|
||||
suite.Require().NoError(err)
|
||||
} else {
|
||||
|
||||
@@ -64,6 +64,7 @@ func (suite *KeeperTestSuite) SetupTest() {
|
||||
err := suite.builderKeeper.SetParams(suite.ctx, types.DefaultParams())
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.mempool = mempool.NewAuctionMempool(suite.encCfg.TxConfig.TxDecoder(), suite.encCfg.TxConfig.TxEncoder(), 0)
|
||||
config := mempool.NewDefaultConfig(suite.encCfg.TxConfig.TxDecoder())
|
||||
suite.mempool = mempool.NewAuctionMempool(suite.encCfg.TxConfig.TxDecoder(), suite.encCfg.TxConfig.TxEncoder(), 0, config)
|
||||
suite.msgServer = keeper.NewMsgServerImpl(suite.builderKeeper)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user