feat(v1): [ENG-790] Implementing vote extension handlers (#103)
Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>
This commit is contained in:
parent
14827d56f8
commit
07c76b8330
264
abci/abci_test.go
Normal file
264
abci/abci_test.go
Normal file
@ -0,0 +1,264 @@
|
||||
package abci_test
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cometbft/cometbft/libs/log"
|
||||
storetypes "github.com/cosmos/cosmos-sdk/store/types"
|
||||
"github.com/cosmos/cosmos-sdk/testutil"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/skip-mev/pob/abci"
|
||||
"github.com/skip-mev/pob/mempool"
|
||||
testutils "github.com/skip-mev/pob/testutils"
|
||||
"github.com/skip-mev/pob/x/builder/ante"
|
||||
"github.com/skip-mev/pob/x/builder/keeper"
|
||||
buildertypes "github.com/skip-mev/pob/x/builder/types"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type ABCITestSuite struct {
|
||||
suite.Suite
|
||||
ctx sdk.Context
|
||||
|
||||
// mempool setup
|
||||
mempool *mempool.AuctionMempool
|
||||
logger log.Logger
|
||||
encodingConfig testutils.EncodingConfig
|
||||
proposalHandler *abci.ProposalHandler
|
||||
voteExtensionHandler *abci.VoteExtensionHandler
|
||||
config mempool.Config
|
||||
txs map[string]struct{}
|
||||
|
||||
// auction bid setup
|
||||
auctionBidAmount sdk.Coin
|
||||
minBidIncrement sdk.Coin
|
||||
|
||||
// builder setup
|
||||
builderKeeper keeper.Keeper
|
||||
bankKeeper *testutils.MockBankKeeper
|
||||
accountKeeper *testutils.MockAccountKeeper
|
||||
distrKeeper *testutils.MockDistributionKeeper
|
||||
stakingKeeper *testutils.MockStakingKeeper
|
||||
builderDecorator ante.BuilderDecorator
|
||||
key *storetypes.KVStoreKey
|
||||
authorityAccount sdk.AccAddress
|
||||
|
||||
// account set up
|
||||
accounts []testutils.Account
|
||||
balances sdk.Coins
|
||||
random *rand.Rand
|
||||
nonces map[string]uint64
|
||||
}
|
||||
|
||||
func TestABCISuite(t *testing.T) {
|
||||
suite.Run(t, new(ABCITestSuite))
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) SetupTest() {
|
||||
// General config
|
||||
suite.encodingConfig = testutils.CreateTestEncodingConfig()
|
||||
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
|
||||
|
||||
// Mempool set up
|
||||
suite.config = mempool.NewDefaultConfig(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))
|
||||
suite.minBidIncrement = sdk.NewCoin("foo", sdk.NewInt(1000))
|
||||
|
||||
// Mock keepers set up
|
||||
ctrl := gomock.NewController(suite.T())
|
||||
suite.accountKeeper = testutils.NewMockAccountKeeper(ctrl)
|
||||
suite.accountKeeper.EXPECT().GetModuleAddress(buildertypes.ModuleName).Return(sdk.AccAddress{}).AnyTimes()
|
||||
suite.bankKeeper = testutils.NewMockBankKeeper(ctrl)
|
||||
suite.distrKeeper = testutils.NewMockDistributionKeeper(ctrl)
|
||||
suite.stakingKeeper = testutils.NewMockStakingKeeper(ctrl)
|
||||
suite.authorityAccount = sdk.AccAddress([]byte("authority"))
|
||||
|
||||
// Builder keeper / decorator set up
|
||||
suite.builderKeeper = keeper.NewKeeper(
|
||||
suite.encodingConfig.Codec,
|
||||
suite.key,
|
||||
suite.accountKeeper,
|
||||
suite.bankKeeper,
|
||||
suite.distrKeeper,
|
||||
suite.stakingKeeper,
|
||||
suite.authorityAccount.String(),
|
||||
)
|
||||
err := suite.builderKeeper.SetParams(suite.ctx, buildertypes.DefaultParams())
|
||||
suite.Require().NoError(err)
|
||||
suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), suite.mempool)
|
||||
|
||||
// Accounts set up
|
||||
suite.accounts = testutils.RandomAccounts(suite.random, 1)
|
||||
suite.balances = sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(1000000000000000000)))
|
||||
suite.nonces = make(map[string]uint64)
|
||||
for _, acc := range suite.accounts {
|
||||
suite.nonces[acc.Address.String()] = 0
|
||||
}
|
||||
|
||||
// Proposal handler set up
|
||||
suite.logger = log.NewNopLogger()
|
||||
suite.proposalHandler = abci.NewProposalHandler(suite.mempool, suite.logger, suite.anteHandler, suite.encodingConfig.TxConfig.TxEncoder(), suite.encodingConfig.TxConfig.TxDecoder())
|
||||
suite.voteExtensionHandler = abci.NewVoteExtensionHandler(suite.mempool, suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), suite.anteHandler)
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) anteHandler(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
|
||||
signer := tx.GetMsgs()[0].GetSigners()[0]
|
||||
suite.bankKeeper.EXPECT().GetAllBalances(ctx, signer).AnyTimes().Return(suite.balances)
|
||||
|
||||
next := func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
ctx, err := suite.builderDecorator.AnteHandle(ctx, tx, false, next)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
bz, err := suite.encodingConfig.TxConfig.TxEncoder()(tx)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
if !simulate {
|
||||
hash := sha256.Sum256(bz)
|
||||
txHash := hex.EncodeToString(hash[:])
|
||||
if _, ok := suite.txs[txHash]; ok {
|
||||
return ctx, fmt.Errorf("tx already in mempool")
|
||||
}
|
||||
suite.txs[txHash] = struct{}{}
|
||||
}
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs int, insertRefTxs bool) int {
|
||||
suite.mempool = mempool.NewAuctionMempool(suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), 0, suite.config)
|
||||
|
||||
// Insert a bunch of normal transactions into the global mempool
|
||||
for i := 0; i < numNormalTxs; i++ {
|
||||
// randomly select an account to create the tx
|
||||
randomIndex := suite.random.Intn(len(suite.accounts))
|
||||
acc := suite.accounts[randomIndex]
|
||||
|
||||
// create a few random msgs
|
||||
randomMsgs := testutils.CreateRandomMsgs(acc.Address, 3)
|
||||
|
||||
nonce := suite.nonces[acc.Address.String()]
|
||||
randomTx, err := testutils.CreateTx(suite.encodingConfig.TxConfig, acc, nonce, 1000, randomMsgs)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.nonces[acc.Address.String()]++
|
||||
priority := suite.random.Int63n(100) + 1
|
||||
suite.Require().NoError(suite.mempool.Insert(suite.ctx.WithPriority(priority), randomTx))
|
||||
}
|
||||
|
||||
suite.Require().Equal(numNormalTxs, suite.mempool.CountTx())
|
||||
suite.Require().Equal(0, suite.mempool.CountAuctionTx())
|
||||
|
||||
// Insert a bunch of auction transactions into the global mempool and auction mempool
|
||||
for i := 0; i < numAuctionTxs; i++ {
|
||||
// randomly select a bidder to create the tx
|
||||
randomIndex := suite.random.Intn(len(suite.accounts))
|
||||
acc := suite.accounts[randomIndex]
|
||||
|
||||
// create a new auction bid msg with numBundledTxs bundled transactions
|
||||
nonce := suite.nonces[acc.Address.String()]
|
||||
bidMsg, err := testutils.CreateMsgAuctionBid(suite.encodingConfig.TxConfig, acc, suite.auctionBidAmount, nonce, numBundledTxs)
|
||||
suite.nonces[acc.Address.String()] += uint64(numBundledTxs)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// create the auction tx
|
||||
nonce = suite.nonces[acc.Address.String()]
|
||||
auctionTx, err := testutils.CreateTx(suite.encodingConfig.TxConfig, acc, nonce, 1000, []sdk.Msg{bidMsg})
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// insert the auction tx into the global mempool
|
||||
priority := suite.random.Int63n(100) + 1
|
||||
suite.Require().NoError(suite.mempool.Insert(suite.ctx.WithPriority(priority), auctionTx))
|
||||
suite.nonces[acc.Address.String()]++
|
||||
|
||||
if insertRefTxs {
|
||||
for _, refRawTx := range bidMsg.GetTransactions() {
|
||||
refTx, err := suite.encodingConfig.TxConfig.TxDecoder()(refRawTx)
|
||||
suite.Require().NoError(err)
|
||||
priority := suite.random.Int63n(100) + 1
|
||||
suite.Require().NoError(suite.mempool.Insert(suite.ctx.WithPriority(priority), refTx))
|
||||
}
|
||||
}
|
||||
|
||||
// decrement the bid amount for the next auction tx
|
||||
suite.auctionBidAmount = suite.auctionBidAmount.Sub(suite.minBidIncrement)
|
||||
}
|
||||
|
||||
numSeenGlobalTxs := 0
|
||||
for iterator := suite.mempool.Select(suite.ctx, nil); iterator != nil; iterator = iterator.Next() {
|
||||
numSeenGlobalTxs++
|
||||
}
|
||||
|
||||
numSeenAuctionTxs := 0
|
||||
for iterator := suite.mempool.AuctionBidSelect(suite.ctx); iterator != nil; iterator = iterator.Next() {
|
||||
numSeenAuctionTxs++
|
||||
}
|
||||
|
||||
var totalNumTxs int
|
||||
suite.Require().Equal(numAuctionTxs, suite.mempool.CountAuctionTx())
|
||||
if insertRefTxs {
|
||||
totalNumTxs = numNormalTxs + numAuctionTxs*(numBundledTxs)
|
||||
suite.Require().Equal(totalNumTxs, suite.mempool.CountTx())
|
||||
suite.Require().Equal(totalNumTxs, numSeenGlobalTxs)
|
||||
} else {
|
||||
totalNumTxs = numNormalTxs
|
||||
suite.Require().Equal(totalNumTxs, suite.mempool.CountTx())
|
||||
suite.Require().Equal(totalNumTxs, numSeenGlobalTxs)
|
||||
}
|
||||
|
||||
suite.Require().Equal(numAuctionTxs, numSeenAuctionTxs)
|
||||
|
||||
return totalNumTxs
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) exportMempool(exportRefTxs bool) [][]byte {
|
||||
txs := make([][]byte, 0)
|
||||
seenTxs := make(map[string]bool)
|
||||
|
||||
auctionIterator := suite.mempool.AuctionBidSelect(suite.ctx)
|
||||
for ; auctionIterator != nil; auctionIterator = auctionIterator.Next() {
|
||||
auctionTx := auctionIterator.Tx()
|
||||
txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(auctionTx)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
txs = append(txs, txBz)
|
||||
|
||||
if exportRefTxs {
|
||||
for _, refRawTx := range auctionTx.GetMsgs()[0].(*buildertypes.MsgAuctionBid).GetTransactions() {
|
||||
txs = append(txs, refRawTx)
|
||||
seenTxs[string(refRawTx)] = true
|
||||
}
|
||||
}
|
||||
|
||||
seenTxs[string(txBz)] = true
|
||||
}
|
||||
|
||||
iterator := suite.mempool.Select(suite.ctx, nil)
|
||||
for ; iterator != nil; iterator = iterator.Next() {
|
||||
txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(iterator.Tx())
|
||||
suite.Require().NoError(err)
|
||||
|
||||
if !seenTxs[string(txBz)] {
|
||||
txs = append(txs, txBz)
|
||||
}
|
||||
}
|
||||
|
||||
return txs
|
||||
}
|
||||
@ -2,264 +2,15 @@ package abci_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
abcitypes "github.com/cometbft/cometbft/abci/types"
|
||||
"github.com/cometbft/cometbft/libs/log"
|
||||
storetypes "github.com/cosmos/cosmos-sdk/store/types"
|
||||
"github.com/cosmos/cosmos-sdk/testutil"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/skip-mev/pob/abci"
|
||||
"github.com/skip-mev/pob/mempool"
|
||||
testutils "github.com/skip-mev/pob/testutils"
|
||||
"github.com/skip-mev/pob/x/builder/ante"
|
||||
"github.com/skip-mev/pob/x/builder/keeper"
|
||||
buildertypes "github.com/skip-mev/pob/x/builder/types"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type ABCITestSuite struct {
|
||||
suite.Suite
|
||||
ctx sdk.Context
|
||||
|
||||
// mempool setup
|
||||
mempool *mempool.AuctionMempool
|
||||
logger log.Logger
|
||||
encodingConfig testutils.EncodingConfig
|
||||
proposalHandler *abci.ProposalHandler
|
||||
txs map[string]struct{}
|
||||
|
||||
// auction bid setup
|
||||
auctionBidAmount sdk.Coin
|
||||
minBidIncrement sdk.Coin
|
||||
|
||||
// builder setup
|
||||
builderKeeper keeper.Keeper
|
||||
bankKeeper *testutils.MockBankKeeper
|
||||
accountKeeper *testutils.MockAccountKeeper
|
||||
distrKeeper *testutils.MockDistributionKeeper
|
||||
stakingKeeper *testutils.MockStakingKeeper
|
||||
builderDecorator ante.BuilderDecorator
|
||||
key *storetypes.KVStoreKey
|
||||
authorityAccount sdk.AccAddress
|
||||
|
||||
// account set up
|
||||
accounts []testutils.Account
|
||||
balances sdk.Coins
|
||||
random *rand.Rand
|
||||
nonces map[string]uint64
|
||||
}
|
||||
|
||||
func TestABCISuite(t *testing.T) {
|
||||
suite.Run(t, new(ABCITestSuite))
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) SetupTest() {
|
||||
// General config
|
||||
suite.encodingConfig = testutils.CreateTestEncodingConfig()
|
||||
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
|
||||
|
||||
// Mempool set up
|
||||
config := mempool.NewDefaultConfig(suite.encodingConfig.TxConfig.TxDecoder())
|
||||
suite.mempool = mempool.NewAuctionMempool(suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), 0, config)
|
||||
suite.txs = make(map[string]struct{})
|
||||
suite.auctionBidAmount = sdk.NewCoin("foo", sdk.NewInt(1000000000))
|
||||
suite.minBidIncrement = sdk.NewCoin("foo", sdk.NewInt(1000))
|
||||
|
||||
// Mock keepers set up
|
||||
ctrl := gomock.NewController(suite.T())
|
||||
suite.accountKeeper = testutils.NewMockAccountKeeper(ctrl)
|
||||
suite.accountKeeper.EXPECT().GetModuleAddress(buildertypes.ModuleName).Return(sdk.AccAddress{}).AnyTimes()
|
||||
suite.bankKeeper = testutils.NewMockBankKeeper(ctrl)
|
||||
suite.distrKeeper = testutils.NewMockDistributionKeeper(ctrl)
|
||||
suite.stakingKeeper = testutils.NewMockStakingKeeper(ctrl)
|
||||
suite.authorityAccount = sdk.AccAddress([]byte("authority"))
|
||||
|
||||
// Builder keeper / decorator set up
|
||||
suite.builderKeeper = keeper.NewKeeper(
|
||||
suite.encodingConfig.Codec,
|
||||
suite.key,
|
||||
suite.accountKeeper,
|
||||
suite.bankKeeper,
|
||||
suite.distrKeeper,
|
||||
suite.stakingKeeper,
|
||||
suite.authorityAccount.String(),
|
||||
)
|
||||
err := suite.builderKeeper.SetParams(suite.ctx, buildertypes.DefaultParams())
|
||||
suite.Require().NoError(err)
|
||||
suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), suite.mempool)
|
||||
|
||||
// Accounts set up
|
||||
suite.accounts = testutils.RandomAccounts(suite.random, 1)
|
||||
suite.balances = sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(1000000000000000000)))
|
||||
suite.nonces = make(map[string]uint64)
|
||||
for _, acc := range suite.accounts {
|
||||
suite.nonces[acc.Address.String()] = 0
|
||||
}
|
||||
|
||||
// Proposal handler set up
|
||||
suite.logger = log.NewNopLogger()
|
||||
suite.proposalHandler = abci.NewProposalHandler(suite.mempool, suite.logger, suite.anteHandler, suite.encodingConfig.TxConfig.TxEncoder(), suite.encodingConfig.TxConfig.TxDecoder())
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) anteHandler(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
|
||||
signer := tx.GetMsgs()[0].GetSigners()[0]
|
||||
suite.bankKeeper.EXPECT().GetAllBalances(ctx, signer).AnyTimes().Return(suite.balances)
|
||||
|
||||
next := func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
ctx, err := suite.builderDecorator.AnteHandle(ctx, tx, false, next)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
bz, err := suite.encodingConfig.TxConfig.TxEncoder()(tx)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
if !simulate {
|
||||
hash := sha256.Sum256(bz)
|
||||
txHash := hex.EncodeToString(hash[:])
|
||||
if _, ok := suite.txs[txHash]; ok {
|
||||
return ctx, fmt.Errorf("tx already in mempool")
|
||||
}
|
||||
suite.txs[txHash] = struct{}{}
|
||||
}
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs int, insertRefTxs bool) int {
|
||||
// Insert a bunch of normal transactions into the global mempool
|
||||
for i := 0; i < numNormalTxs; i++ {
|
||||
// randomly select an account to create the tx
|
||||
randomIndex := suite.random.Intn(len(suite.accounts))
|
||||
acc := suite.accounts[randomIndex]
|
||||
|
||||
// create a few random msgs
|
||||
randomMsgs := testutils.CreateRandomMsgs(acc.Address, 3)
|
||||
|
||||
nonce := suite.nonces[acc.Address.String()]
|
||||
randomTx, err := testutils.CreateTx(suite.encodingConfig.TxConfig, acc, nonce, 1000, randomMsgs)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.nonces[acc.Address.String()]++
|
||||
priority := suite.random.Int63n(100) + 1
|
||||
suite.Require().NoError(suite.mempool.Insert(suite.ctx.WithPriority(priority), randomTx))
|
||||
}
|
||||
|
||||
suite.Require().Equal(numNormalTxs, suite.mempool.CountTx())
|
||||
suite.Require().Equal(0, suite.mempool.CountAuctionTx())
|
||||
|
||||
// Insert a bunch of auction transactions into the global mempool and auction mempool
|
||||
for i := 0; i < numAuctionTxs; i++ {
|
||||
// randomly select a bidder to create the tx
|
||||
randomIndex := suite.random.Intn(len(suite.accounts))
|
||||
acc := suite.accounts[randomIndex]
|
||||
|
||||
// create a new auction bid msg with numBundledTxs bundled transactions
|
||||
nonce := suite.nonces[acc.Address.String()]
|
||||
bidMsg, err := testutils.CreateMsgAuctionBid(suite.encodingConfig.TxConfig, acc, suite.auctionBidAmount, nonce, numBundledTxs)
|
||||
suite.nonces[acc.Address.String()] += uint64(numBundledTxs)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// create the auction tx
|
||||
nonce = suite.nonces[acc.Address.String()]
|
||||
auctionTx, err := testutils.CreateTx(suite.encodingConfig.TxConfig, acc, nonce, 1000, []sdk.Msg{bidMsg})
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// insert the auction tx into the global mempool
|
||||
priority := suite.random.Int63n(100) + 1
|
||||
suite.Require().NoError(suite.mempool.Insert(suite.ctx.WithPriority(priority), auctionTx))
|
||||
suite.nonces[acc.Address.String()]++
|
||||
|
||||
if insertRefTxs {
|
||||
for _, refRawTx := range bidMsg.GetTransactions() {
|
||||
refTx, err := suite.encodingConfig.TxConfig.TxDecoder()(refRawTx)
|
||||
suite.Require().NoError(err)
|
||||
priority := suite.random.Int63n(100) + 1
|
||||
suite.Require().NoError(suite.mempool.Insert(suite.ctx.WithPriority(priority), refTx))
|
||||
}
|
||||
}
|
||||
|
||||
// decrement the bid amount for the next auction tx
|
||||
suite.auctionBidAmount = suite.auctionBidAmount.Sub(suite.minBidIncrement)
|
||||
}
|
||||
|
||||
numSeenGlobalTxs := 0
|
||||
for iterator := suite.mempool.Select(suite.ctx, nil); iterator != nil; iterator = iterator.Next() {
|
||||
numSeenGlobalTxs++
|
||||
}
|
||||
|
||||
numSeenAuctionTxs := 0
|
||||
for iterator := suite.mempool.AuctionBidSelect(suite.ctx); iterator != nil; iterator = iterator.Next() {
|
||||
numSeenAuctionTxs++
|
||||
}
|
||||
|
||||
var totalNumTxs int
|
||||
suite.Require().Equal(numAuctionTxs, suite.mempool.CountAuctionTx())
|
||||
if insertRefTxs {
|
||||
totalNumTxs = numNormalTxs + numAuctionTxs*(numBundledTxs)
|
||||
suite.Require().Equal(totalNumTxs, suite.mempool.CountTx())
|
||||
suite.Require().Equal(totalNumTxs, numSeenGlobalTxs)
|
||||
} else {
|
||||
totalNumTxs = numNormalTxs
|
||||
suite.Require().Equal(totalNumTxs, suite.mempool.CountTx())
|
||||
suite.Require().Equal(totalNumTxs, numSeenGlobalTxs)
|
||||
}
|
||||
|
||||
suite.Require().Equal(numAuctionTxs, numSeenAuctionTxs)
|
||||
|
||||
return totalNumTxs
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) exportMempool(exportRefTxs bool) [][]byte {
|
||||
txs := make([][]byte, 0)
|
||||
seenTxs := make(map[string]bool)
|
||||
|
||||
auctionIterator := suite.mempool.AuctionBidSelect(suite.ctx)
|
||||
for ; auctionIterator != nil; auctionIterator = auctionIterator.Next() {
|
||||
auctionTx := auctionIterator.Tx()
|
||||
txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(auctionTx)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
txs = append(txs, txBz)
|
||||
|
||||
if exportRefTxs {
|
||||
for _, refRawTx := range auctionTx.GetMsgs()[0].(*buildertypes.MsgAuctionBid).GetTransactions() {
|
||||
txs = append(txs, refRawTx)
|
||||
seenTxs[string(refRawTx)] = true
|
||||
}
|
||||
}
|
||||
|
||||
seenTxs[string(txBz)] = true
|
||||
}
|
||||
|
||||
iterator := suite.mempool.Select(suite.ctx, nil)
|
||||
for ; iterator != nil; iterator = iterator.Next() {
|
||||
txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(iterator.Tx())
|
||||
suite.Require().NoError(err)
|
||||
|
||||
if !seenTxs[string(txBz)] {
|
||||
txs = append(txs, txBz)
|
||||
}
|
||||
}
|
||||
|
||||
return txs
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) TestPrepareProposal() {
|
||||
var (
|
||||
// the modified transactions cannot exceed this size
|
||||
@ -490,6 +241,9 @@ func (suite *ABCITestSuite) TestPrepareProposal() {
|
||||
suite.builderKeeper.SetParams(suite.ctx, params)
|
||||
suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), suite.mempool)
|
||||
|
||||
// reset the proposal handler with the new mempool
|
||||
suite.proposalHandler = abci.NewProposalHandler(suite.mempool, suite.logger, suite.anteHandler, suite.encodingConfig.TxConfig.TxEncoder(), suite.encodingConfig.TxConfig.TxDecoder())
|
||||
|
||||
handler := suite.proposalHandler.PrepareProposalHandler()
|
||||
res := handler(suite.ctx, abcitypes.RequestPrepareProposal{
|
||||
MaxTxBytes: maxTxBytes,
|
||||
@ -723,6 +477,9 @@ func (suite *ABCITestSuite) TestProcessProposal() {
|
||||
|
||||
suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs)
|
||||
|
||||
// reset the proposal handler with the new mempool
|
||||
suite.proposalHandler = abci.NewProposalHandler(suite.mempool, suite.logger, suite.anteHandler, suite.encodingConfig.TxConfig.TxEncoder(), suite.encodingConfig.TxConfig.TxDecoder())
|
||||
|
||||
if frontRunningTx != nil {
|
||||
suite.Require().NoError(suite.mempool.Insert(suite.ctx, frontRunningTx))
|
||||
}
|
||||
|
||||
@ -2,6 +2,9 @@ package abci
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool"
|
||||
@ -14,15 +17,30 @@ type (
|
||||
Remove(tx sdk.Tx) error
|
||||
AuctionBidSelect(ctx context.Context) sdkmempool.Iterator
|
||||
IsAuctionTx(tx sdk.Tx) (bool, error)
|
||||
GetBundledTransactions(tx sdk.Tx) ([][]byte, error)
|
||||
WrapBundleTransaction(tx []byte) (sdk.Tx, error)
|
||||
}
|
||||
|
||||
// VoteExtensionHandler contains the functionality and handlers required to
|
||||
// process, validate and build vote extensions.
|
||||
VoteExtensionHandler struct {
|
||||
mempool VoteExtensionMempool
|
||||
txDecoder sdk.TxDecoder
|
||||
txEncoder sdk.TxEncoder
|
||||
mempool VoteExtensionMempool
|
||||
|
||||
// txDecoder is used to decode the top bidding auction transaction
|
||||
txDecoder sdk.TxDecoder
|
||||
|
||||
// txEncoder is used to encode the top bidding auction transaction
|
||||
txEncoder sdk.TxEncoder
|
||||
|
||||
// anteHandler is used to validate the vote extension
|
||||
anteHandler sdk.AnteHandler
|
||||
|
||||
// cache is used to store the results of the vote extension verification
|
||||
// for a given block height.
|
||||
cache map[string]error
|
||||
|
||||
// currentHeight is the block height the cache is valid for.
|
||||
currentHeight int64
|
||||
}
|
||||
)
|
||||
|
||||
@ -32,10 +50,12 @@ func NewVoteExtensionHandler(mp VoteExtensionMempool, txDecoder sdk.TxDecoder,
|
||||
txEncoder sdk.TxEncoder, ah sdk.AnteHandler,
|
||||
) *VoteExtensionHandler {
|
||||
return &VoteExtensionHandler{
|
||||
mempool: mp,
|
||||
txDecoder: txDecoder,
|
||||
txEncoder: txEncoder,
|
||||
anteHandler: ah,
|
||||
mempool: mp,
|
||||
txDecoder: txDecoder,
|
||||
txEncoder: txEncoder,
|
||||
anteHandler: ah,
|
||||
cache: make(map[string]error),
|
||||
currentHeight: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,7 +64,22 @@ func NewVoteExtensionHandler(mp VoteExtensionMempool, txDecoder sdk.TxDecoder,
|
||||
// returns it in its vote extension.
|
||||
func (h *VoteExtensionHandler) ExtendVoteHandler() ExtendVoteHandler {
|
||||
return func(ctx sdk.Context, req *RequestExtendVote) (*ResponseExtendVote, error) {
|
||||
panic("implement me")
|
||||
// Iterate through auction bids until we find a valid one
|
||||
auctionIterator := h.mempool.AuctionBidSelect(ctx)
|
||||
|
||||
for ; auctionIterator != nil; auctionIterator = auctionIterator.Next() {
|
||||
bidTx := auctionIterator.Tx()
|
||||
|
||||
// Verify the bid tx can be encoded and included in vote extension
|
||||
if bidBz, err := h.txEncoder(bidTx); err == nil {
|
||||
// Validate the auction transaction
|
||||
if err := h.verifyAuctionTx(ctx, bidTx); err == nil {
|
||||
return &ResponseExtendVote{VoteExtension: bidBz}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &ResponseExtendVote{VoteExtension: nil}, nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,6 +88,93 @@ func (h *VoteExtensionHandler) ExtendVoteHandler() ExtendVoteHandler {
|
||||
// In particular, it verifies that the vote extension is a valid auction transaction.
|
||||
func (h *VoteExtensionHandler) VerifyVoteExtensionHandler() VerifyVoteExtensionHandler {
|
||||
return func(ctx sdk.Context, req *RequestVerifyVoteExtension) (*ResponseVerifyVoteExtension, error) {
|
||||
panic("implement me")
|
||||
txBz := req.VoteExtension
|
||||
if len(txBz) == 0 {
|
||||
return &ResponseVerifyVoteExtension{Status: ResponseVerifyVoteExtension_ACCEPT}, nil
|
||||
}
|
||||
|
||||
// Reset the cache if necessary
|
||||
h.resetCache(ctx.BlockHeight())
|
||||
|
||||
hashBz := sha256.Sum256(txBz)
|
||||
hash := hex.EncodeToString(hashBz[:])
|
||||
|
||||
// Short circuit if we have already verified this vote extension
|
||||
if err, ok := h.cache[hash]; ok {
|
||||
if err != nil {
|
||||
return &ResponseVerifyVoteExtension{Status: ResponseVerifyVoteExtension_REJECT}, err
|
||||
}
|
||||
|
||||
return &ResponseVerifyVoteExtension{Status: ResponseVerifyVoteExtension_ACCEPT}, nil
|
||||
}
|
||||
|
||||
// Decode the vote extension which should be a valid auction transaction
|
||||
bidTx, err := h.txDecoder(txBz)
|
||||
if err != nil {
|
||||
h.cache[hash] = err
|
||||
return &ResponseVerifyVoteExtension{Status: ResponseVerifyVoteExtension_REJECT}, err
|
||||
}
|
||||
|
||||
// Verify the auction transaction and cache the result
|
||||
if err = h.verifyAuctionTx(ctx, bidTx); err != nil {
|
||||
h.cache[hash] = err
|
||||
return &ResponseVerifyVoteExtension{Status: ResponseVerifyVoteExtension_REJECT}, err
|
||||
}
|
||||
|
||||
h.cache[hash] = nil
|
||||
|
||||
return &ResponseVerifyVoteExtension{Status: ResponseVerifyVoteExtension_ACCEPT}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// checkStaleCache checks if the current height differs than the previous height at which
|
||||
// the vote extensions were verified in. If so, it resets the cache to allow transactions to be
|
||||
// reverified.
|
||||
func (h *VoteExtensionHandler) resetCache(blockHeight int64) {
|
||||
if h.currentHeight != blockHeight {
|
||||
h.cache = make(map[string]error)
|
||||
h.currentHeight = blockHeight
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isAuctionTx {
|
||||
return fmt.Errorf("vote extension is not a valid auction transaction")
|
||||
}
|
||||
|
||||
if h.anteHandler == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cache context is used to avoid state changes
|
||||
cache, _ := ctx.CacheContext()
|
||||
if _, err := h.anteHandler(cache, bidTx, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bundledTxs, err := h.mempool.GetBundledTransactions(bidTx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Verify all bundled transactions
|
||||
for _, tx := range bundledTxs {
|
||||
wrappedTx, err := h.mempool.WrapBundleTransaction(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := h.anteHandler(cache, wrappedTx, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1 +1,371 @@
|
||||
package abci_test
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/skip-mev/pob/abci"
|
||||
"github.com/skip-mev/pob/mempool"
|
||||
testutils "github.com/skip-mev/pob/testutils"
|
||||
"github.com/skip-mev/pob/x/builder/types"
|
||||
)
|
||||
|
||||
func (suite *ABCITestSuite) TestExtendVoteExtensionHandler() {
|
||||
params := types.Params{
|
||||
MaxBundleSize: 5,
|
||||
ReserveFee: sdk.NewCoin("foo", sdk.NewInt(10)),
|
||||
MinBuyInFee: sdk.NewCoin("foo", sdk.NewInt(10)),
|
||||
FrontRunningProtection: true,
|
||||
MinBidIncrement: suite.minBidIncrement,
|
||||
}
|
||||
|
||||
err := suite.builderKeeper.SetParams(suite.ctx, params)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
getExpectedVE func() []byte
|
||||
}{
|
||||
{
|
||||
"empty mempool",
|
||||
func() []byte {
|
||||
suite.createFilledMempool(0, 0, 0, false)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
"filled mempool with no auction transactions",
|
||||
func() []byte {
|
||||
suite.createFilledMempool(100, 0, 0, false)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
"mempool with invalid auction transaction (too many bundled transactions)",
|
||||
func() []byte {
|
||||
suite.createFilledMempool(0, 1, int(params.MaxBundleSize)+1, true)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
"mempool with invalid auction transaction (invalid bid)",
|
||||
func() []byte {
|
||||
bidder := suite.accounts[0]
|
||||
bid := params.ReserveFee.Sub(sdk.NewCoin("foo", sdk.NewInt(1)))
|
||||
signers := []testutils.Account{bidder}
|
||||
timeout := 1
|
||||
|
||||
bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, 0, uint64(timeout), signers)
|
||||
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 only invalid auction bids (bid is too low)",
|
||||
func() []byte {
|
||||
params.ReserveFee = suite.auctionBidAmount
|
||||
err := suite.builderKeeper.SetParams(suite.ctx, params)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// this way all of the bids will be too small
|
||||
suite.auctionBidAmount = params.ReserveFee.Sub(sdk.NewCoin("foo", sdk.NewInt(1)))
|
||||
|
||||
suite.createFilledMempool(100, 100, 2, true)
|
||||
|
||||
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 {
|
||||
bidder := suite.accounts[0]
|
||||
bid := params.ReserveFee
|
||||
signers := []testutils.Account{bidder}
|
||||
timeout := 0
|
||||
|
||||
bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, 0, uint64(timeout), signers)
|
||||
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
|
||||
},
|
||||
},
|
||||
{
|
||||
"top bid is invalid but next best is valid",
|
||||
func() []byte {
|
||||
bidder := suite.accounts[0]
|
||||
bid := suite.auctionBidAmount.Add(suite.minBidIncrement)
|
||||
signers := []testutils.Account{bidder}
|
||||
timeout := 0
|
||||
|
||||
bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, 0, uint64(timeout), signers)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.createFilledMempool(100, 100, 2, true)
|
||||
|
||||
topBidTx := suite.mempool.GetTopAuctionTx(suite.ctx)
|
||||
|
||||
err = suite.mempool.Insert(suite.ctx, bidTx)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
bz, err := suite.encodingConfig.TxConfig.TxEncoder()(topBidTx)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
return bz
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
expectedVE := tc.getExpectedVE()
|
||||
|
||||
// Reset the handler with the new mempool
|
||||
suite.voteExtensionHandler = abci.NewVoteExtensionHandler(suite.mempool, suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), suite.anteHandler)
|
||||
|
||||
handler := suite.voteExtensionHandler.ExtendVoteHandler()
|
||||
resp, err := handler(suite.ctx, nil)
|
||||
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(expectedVE, resp.VoteExtension)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) TestVerifyVoteExtensionHandler() {
|
||||
params := types.Params{
|
||||
MaxBundleSize: 5,
|
||||
ReserveFee: sdk.NewCoin("foo", sdk.NewInt(100)),
|
||||
MinBuyInFee: sdk.NewCoin("foo", sdk.NewInt(100)),
|
||||
FrontRunningProtection: true,
|
||||
MinBidIncrement: sdk.NewCoin("foo", sdk.NewInt(10)), // can't be tested atm
|
||||
}
|
||||
|
||||
err := suite.builderKeeper.SetParams(suite.ctx, params)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
req func() *abci.RequestVerifyVoteExtension
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
"invalid vote extension bytes",
|
||||
func() *abci.RequestVerifyVoteExtension {
|
||||
return &abci.RequestVerifyVoteExtension{
|
||||
VoteExtension: []byte("invalid vote extension"),
|
||||
}
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"empty vote extension bytes",
|
||||
func() *abci.RequestVerifyVoteExtension {
|
||||
return &abci.RequestVerifyVoteExtension{
|
||||
VoteExtension: []byte{},
|
||||
}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"nil vote extension bytes",
|
||||
func() *abci.RequestVerifyVoteExtension {
|
||||
return &abci.RequestVerifyVoteExtension{
|
||||
VoteExtension: nil,
|
||||
}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid extension with bid tx with bad timeout",
|
||||
func() *abci.RequestVerifyVoteExtension {
|
||||
bidder := suite.accounts[0]
|
||||
bid := sdk.NewCoin("foo", sdk.NewInt(10))
|
||||
signers := []testutils.Account{bidder}
|
||||
timeout := 0
|
||||
|
||||
bz := suite.createAuctionTxBz(bidder, bid, signers, timeout)
|
||||
return &abci.RequestVerifyVoteExtension{
|
||||
VoteExtension: bz,
|
||||
}
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"invalid vote extension with bid tx with bad bid",
|
||||
func() *abci.RequestVerifyVoteExtension {
|
||||
bidder := suite.accounts[0]
|
||||
bid := sdk.NewCoin("foo", sdk.NewInt(0))
|
||||
signers := []testutils.Account{bidder}
|
||||
timeout := 10
|
||||
|
||||
bz := suite.createAuctionTxBz(bidder, bid, signers, timeout)
|
||||
return &abci.RequestVerifyVoteExtension{
|
||||
VoteExtension: bz,
|
||||
}
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"valid vote extension",
|
||||
func() *abci.RequestVerifyVoteExtension {
|
||||
bidder := suite.accounts[0]
|
||||
bid := params.ReserveFee
|
||||
signers := []testutils.Account{bidder}
|
||||
timeout := 10
|
||||
|
||||
bz := suite.createAuctionTxBz(bidder, bid, signers, timeout)
|
||||
return &abci.RequestVerifyVoteExtension{
|
||||
VoteExtension: bz,
|
||||
}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid vote extension with front running bid tx",
|
||||
func() *abci.RequestVerifyVoteExtension {
|
||||
bidder := suite.accounts[0]
|
||||
bid := params.ReserveFee
|
||||
timeout := 10
|
||||
|
||||
bundlee := testutils.RandomAccounts(suite.random, 1)[0]
|
||||
signers := []testutils.Account{bidder, bundlee}
|
||||
|
||||
bz := suite.createAuctionTxBz(bidder, bid, signers, timeout)
|
||||
return &abci.RequestVerifyVoteExtension{
|
||||
VoteExtension: bz,
|
||||
}
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"invalid vote extension with too many bundle txs",
|
||||
func() *abci.RequestVerifyVoteExtension {
|
||||
// disable front running protection
|
||||
params.FrontRunningProtection = false
|
||||
err := suite.builderKeeper.SetParams(suite.ctx, params)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
bidder := suite.accounts[0]
|
||||
bid := params.ReserveFee
|
||||
signers := testutils.RandomAccounts(suite.random, int(params.MaxBundleSize)+1)
|
||||
timeout := 10
|
||||
|
||||
bz := suite.createAuctionTxBz(bidder, bid, signers, timeout)
|
||||
return &abci.RequestVerifyVoteExtension{
|
||||
VoteExtension: bz,
|
||||
}
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"invalid vote extension with a failing bundle tx",
|
||||
func() *abci.RequestVerifyVoteExtension {
|
||||
bidder := suite.accounts[0]
|
||||
bid := params.ReserveFee
|
||||
|
||||
msgAuctionBid, err := testutils.CreateMsgAuctionBid(suite.encodingConfig.TxConfig, bidder, bid, 0, 0)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Create a failing tx
|
||||
msgAuctionBid.Transactions = [][]byte{{0x01}}
|
||||
|
||||
bidTx, err := testutils.CreateTx(suite.encodingConfig.TxConfig, suite.accounts[0], 0, 1, []sdk.Msg{msgAuctionBid})
|
||||
suite.Require().NoError(err)
|
||||
|
||||
bz, err := suite.encodingConfig.TxConfig.TxEncoder()(bidTx)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
return &abci.RequestVerifyVoteExtension{
|
||||
VoteExtension: bz,
|
||||
}
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"valid vote extension + no comparison to local mempool",
|
||||
func() *abci.RequestVerifyVoteExtension {
|
||||
bidder := suite.accounts[0]
|
||||
bid := params.ReserveFee
|
||||
signers := []testutils.Account{bidder}
|
||||
timeout := 10
|
||||
|
||||
bz := suite.createAuctionTxBz(bidder, bid, signers, timeout)
|
||||
|
||||
// Add a bid to the mempool that is greater than the one in the vote extension
|
||||
bid = bid.Add(params.MinBidIncrement)
|
||||
bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, 10, 1, signers)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
err = suite.mempool.Insert(suite.ctx, bidTx)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
tx := suite.mempool.GetTopAuctionTx(suite.ctx)
|
||||
suite.Require().NotNil(tx)
|
||||
|
||||
return &abci.RequestVerifyVoteExtension{
|
||||
VoteExtension: bz,
|
||||
}
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
req := tc.req()
|
||||
|
||||
handler := suite.voteExtensionHandler.VerifyVoteExtensionHandler()
|
||||
_, err := handler(suite.ctx, req)
|
||||
|
||||
if tc.expectedErr {
|
||||
suite.Require().Error(err)
|
||||
} else {
|
||||
suite.Require().NoError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) createAuctionTxBz(bidder testutils.Account, bid sdk.Coin, signers []testutils.Account, timeout int) []byte {
|
||||
auctionTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, 0, uint64(timeout), signers)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(auctionTx)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
return txBz
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user