fix: CheckTx Handler (#141)
This commit is contained in:
parent
af94805b48
commit
648a2f88d6
87
README.md
87
README.md
@ -90,8 +90,12 @@ $ go install github.com/skip-mev/pob
|
||||
|
||||
```go
|
||||
type App struct {
|
||||
BuilderKeeper builderkeeper.Keeper
|
||||
...
|
||||
// BuilderKeeper is the keeper that handles processing auction transactions
|
||||
BuilderKeeper builderkeeper.Keeper
|
||||
|
||||
// Custom checkTx handler
|
||||
checkTxHandler abci.CheckTx
|
||||
}
|
||||
```
|
||||
|
||||
@ -133,7 +137,7 @@ $ go install github.com/skip-mev/pob
|
||||
```
|
||||
|
||||
d. Searchers bid to have their bundles executed at the top of the block
|
||||
using `MsgAuctionBid` messages. While the builder `Keeper` is capable of
|
||||
using `MsgAuctionBid` messages (by default). While the builder `Keeper` is capable of
|
||||
tracking valid bids, it is unable to correctly sequence the auction
|
||||
transactions alongside the normal transactions without having access to the
|
||||
application’s mempool. As such, we have to instantiate POB’s custom
|
||||
@ -141,8 +145,17 @@ $ go install github.com/skip-mev/pob
|
||||
mempool - into the application. Note, this should be done after `BaseApp` is
|
||||
instantiated.
|
||||
|
||||
d.1. Application developers can choose to implement their own `AuctionFactory` implementation
|
||||
or use the default implementation provided by POB. The `AuctionFactory` is responsible
|
||||
for determining what is an auction bid transaction and how to extract the bid information
|
||||
from the transaction. The default implementation provided by POB is `DefaultAuctionFactory`
|
||||
which uses the `MsgAuctionBid` message to determine if a transaction is an auction bid
|
||||
transaction and extracts the bid information from the message.
|
||||
|
||||
```go
|
||||
mempool := mempool.NewAuctionMempool(txConfig.TxDecoder(), GetMaxMempoolSize())
|
||||
config := mempool.NewDefaultAuctionFactory(txDecoder)
|
||||
|
||||
mempool := mempool.NewAuctionMempool(txDecoder, txEncoder, maxTx, config)
|
||||
bApp.SetMempool(mempool)
|
||||
```
|
||||
|
||||
@ -153,18 +166,68 @@ $ go install github.com/skip-mev/pob
|
||||
will verify the contents of the block proposal by all validators. The
|
||||
combination of the `AuctionMempool`, `PrepareProposal` and `ProcessProposal`
|
||||
handlers allows the application to verifiably build valid blocks with
|
||||
top-of-block block space reserved for auctions.
|
||||
top-of-block block space reserved for auctions. Additionally, we override the
|
||||
`BaseApp`'s `CheckTx` handler with our own custom `CheckTx` handler that will
|
||||
be responsible for checking the validity of transactions. We override the
|
||||
`CheckTx` handler so that we can verify auction transactions before they are
|
||||
inserted into the mempool. With the POB `CheckTx`, we can verify the auction
|
||||
transaction and all of the bundled transactions before inserting the auction
|
||||
transaction into the mempool. This is important because we otherwise there may be
|
||||
discrepencies between the auction transaction and the bundled transactions
|
||||
are validated in `CheckTx` and `PrepareProposal` such that the auction can be
|
||||
griefed. All other transactions will be executed with base app's `CheckTx`.
|
||||
|
||||
```go
|
||||
// Create the entire chain of AnteDecorators for the application.
|
||||
anteDecorators := []sdk.AnteDecorator{
|
||||
auction.NewAuctionDecorator(
|
||||
app.BuilderKeeper,
|
||||
txConfig.TxEncoder(),
|
||||
mempool,
|
||||
),
|
||||
...,
|
||||
}
|
||||
|
||||
// Create the antehandler that will be used to check transactions throughout the lifecycle
|
||||
// of the application.
|
||||
anteHandler := sdk.ChainAnteDecorators(anteDecorators...)
|
||||
app.SetAnteHandler(anteHandler)
|
||||
|
||||
// Create the proposal handler that will be used to build and validate blocks.
|
||||
handler := proposalhandler.NewProposalHandler(
|
||||
mempool,
|
||||
bApp.Logger(),
|
||||
bApp,
|
||||
anteHandler,
|
||||
txConfig.TxEncoder(),
|
||||
txConfig.TxDecoder(),
|
||||
)
|
||||
bApp.SetPrepareProposal(handler.PrepareProposalHandler())
|
||||
bApp.SetProcessProposal(handler.ProcessProposalHandler())
|
||||
app.SetPrepareProposal(handler.PrepareProposalHandler())
|
||||
app.SetProcessProposal(handler.ProcessProposalHandler())
|
||||
|
||||
// Set the custom CheckTx handler on BaseApp.
|
||||
checkTxHandler := pobabci.CheckTxHandler(
|
||||
app.App,
|
||||
app.TxDecoder,
|
||||
mempool,
|
||||
anteHandler,
|
||||
chainID,
|
||||
)
|
||||
app.SetCheckTx(checkTxHandler)
|
||||
|
||||
...
|
||||
|
||||
// CheckTx will check the transaction with the provided checkTxHandler. We override the default
|
||||
// handler so that we can verify bid transactions before they are inserted into the mempool.
|
||||
// With the POB CheckTx, we can verify the bid transaction and all of the bundled transactions
|
||||
// before inserting the bid transaction into the mempool.
|
||||
func (app *TestApp) CheckTx(req cometabci.RequestCheckTx) cometabci.ResponseCheckTx {
|
||||
return app.checkTxHandler(req)
|
||||
}
|
||||
|
||||
// SetCheckTx sets the checkTxHandler for the app.
|
||||
func (app *TestApp) SetCheckTx(handler abci.CheckTx) {
|
||||
app.checkTxHandler = handler
|
||||
}
|
||||
```
|
||||
|
||||
f. Finally, update the app's `InitGenesis` order and ante-handler chain.
|
||||
@ -174,16 +237,6 @@ $ go install github.com/skip-mev/pob
|
||||
buildertypes.ModuleName,
|
||||
...,
|
||||
}
|
||||
|
||||
anteDecorators := []sdk.AnteDecorator{
|
||||
auction.NewAuctionDecorator(
|
||||
app.BuilderKeeper,
|
||||
txConfig.TxDecoder(),
|
||||
txConfig.TxEncoder(),
|
||||
mempool,
|
||||
),
|
||||
...,
|
||||
}
|
||||
```
|
||||
|
||||
## Params
|
||||
|
||||
@ -97,7 +97,7 @@ func (suite *ABCITestSuite) SetupTest() {
|
||||
)
|
||||
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)
|
||||
suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxEncoder(), suite.mempool)
|
||||
|
||||
// Accounts set up
|
||||
suite.accounts = testutils.RandomAccounts(suite.random, 1)
|
||||
@ -487,7 +487,7 @@ func (suite *ABCITestSuite) TestPrepareProposal() {
|
||||
MinBidIncrement: suite.minBidIncrement,
|
||||
}
|
||||
suite.builderKeeper.SetParams(suite.ctx, params)
|
||||
suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), suite.mempool)
|
||||
suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxEncoder(), suite.mempool)
|
||||
|
||||
handler := suite.proposalHandler.PrepareProposalHandler()
|
||||
res := handler(suite.ctx, abcitypes.RequestPrepareProposal{
|
||||
@ -733,7 +733,7 @@ func (suite *ABCITestSuite) TestProcessProposal() {
|
||||
MinBidIncrement: suite.minBidIncrement,
|
||||
}
|
||||
suite.builderKeeper.SetParams(suite.ctx, params)
|
||||
suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), suite.mempool)
|
||||
suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxEncoder(), suite.mempool)
|
||||
suite.Require().Equal(tc.isTopBidValid, suite.isTopBidValid())
|
||||
|
||||
txs := suite.exportMempool(exportRefTxs)
|
||||
|
||||
214
abci/check_tx.go
Normal file
214
abci/check_tx.go
Normal file
@ -0,0 +1,214 @@
|
||||
package abci
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
cometabci "github.com/cometbft/cometbft/abci/types"
|
||||
log "github.com/cometbft/cometbft/libs/log"
|
||||
tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/skip-mev/pob/mempool"
|
||||
)
|
||||
|
||||
type (
|
||||
// CheckTxHandler is a wrapper around baseapp's CheckTx method that allows us to
|
||||
// verify bid transactions against the latest committed state. All other transactions
|
||||
// are executed normally using base app's CheckTx. This defines all of the
|
||||
// dependencies that are required to verify a bid transaction.
|
||||
CheckTxHandler struct {
|
||||
// baseApp is utilized to retrieve the latest committed state and to call
|
||||
// baseapp's CheckTx method.
|
||||
baseApp BaseApp
|
||||
|
||||
// txDecoder is utilized to decode transactions to determine if they are
|
||||
// bid transactions.
|
||||
txDecoder sdk.TxDecoder
|
||||
|
||||
// mempool is utilized to retrieve the bid info of a transaction and to
|
||||
// insert a transaction into the application-side mempool.
|
||||
mempool CheckTxMempool
|
||||
|
||||
// anteHandler is utilized to verify the bid transaction against the latest
|
||||
// committed state.
|
||||
anteHandler sdk.AnteHandler
|
||||
|
||||
// chainID is the chain ID of the blockchain.
|
||||
chainID string
|
||||
}
|
||||
|
||||
// CheckTx is baseapp's CheckTx method that checks the validity of a
|
||||
// transaction.
|
||||
CheckTx func(cometabci.RequestCheckTx) cometabci.ResponseCheckTx
|
||||
|
||||
// CheckTxMempool is the interface that defines all of the dependencies that
|
||||
// are required to interact with the application-side mempool.
|
||||
CheckTxMempool interface {
|
||||
// GetAuctionBidInfo is utilized to retrieve the bid info of a transaction.
|
||||
GetAuctionBidInfo(tx sdk.Tx) (*mempool.AuctionBidInfo, error)
|
||||
|
||||
// Insert is utilized to insert a transaction into the application-side mempool.
|
||||
Insert(ctx context.Context, tx sdk.Tx) error
|
||||
|
||||
// WrapBundleTransaction is utilized to wrap a transaction included in a bid transaction
|
||||
// into an sdk.Tx.
|
||||
WrapBundleTransaction(tx []byte) (sdk.Tx, error)
|
||||
}
|
||||
|
||||
// BaseApp is an interface that allows us to call baseapp's CheckTx method
|
||||
// as well as retrieve the latest committed state.
|
||||
BaseApp interface {
|
||||
// CommitMultiStore is utilized to retrieve the latest committed state.
|
||||
CommitMultiStore() sdk.CommitMultiStore
|
||||
|
||||
// CheckTx is baseapp's CheckTx method that checks the validity of a
|
||||
// transaction.
|
||||
CheckTx(cometabci.RequestCheckTx) cometabci.ResponseCheckTx
|
||||
|
||||
// Logger is utilized to log errors.
|
||||
Logger() log.Logger
|
||||
|
||||
// LastBlockHeight is utilized to retrieve the latest block height.
|
||||
LastBlockHeight() int64
|
||||
|
||||
// GetConsensusParams is utilized to retrieve the consensus params.
|
||||
GetConsensusParams(ctx sdk.Context) *tmproto.ConsensusParams
|
||||
}
|
||||
)
|
||||
|
||||
// NewCheckTxHandler is a constructor for CheckTxHandler.
|
||||
func NewCheckTxHandler(baseApp BaseApp, txDecoder sdk.TxDecoder, mempool CheckTxMempool, anteHandler sdk.AnteHandler, chainID string) *CheckTxHandler {
|
||||
return &CheckTxHandler{
|
||||
baseApp: baseApp,
|
||||
txDecoder: txDecoder,
|
||||
mempool: mempool,
|
||||
anteHandler: anteHandler,
|
||||
chainID: chainID,
|
||||
}
|
||||
}
|
||||
|
||||
// CheckTxHandler is a wrapper around baseapp's CheckTx method that allows us to
|
||||
// verify bid transactions against the latest committed state. All other transactions
|
||||
// are executed normally. We must verify each bid tx and all of its bundled transactions
|
||||
// before we can insert it into the mempool against the latest commit state because
|
||||
// otherwise the auction can be griefed. No state changes are applied to the state
|
||||
// during this process.
|
||||
func (handler *CheckTxHandler) CheckTx() CheckTx {
|
||||
return func(req cometabci.RequestCheckTx) (resp cometabci.ResponseCheckTx) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
resp = sdkerrors.ResponseCheckTxWithEvents(fmt.Errorf("panic in check tx handler: %s", err), 0, 0, nil, false)
|
||||
}
|
||||
}()
|
||||
|
||||
tx, err := handler.txDecoder(req.Tx)
|
||||
if err != nil {
|
||||
return sdkerrors.ResponseCheckTxWithEvents(fmt.Errorf("failed to decode tx: %w", err), 0, 0, nil, false)
|
||||
}
|
||||
|
||||
// Attempt to get the bid info of the transaction.
|
||||
bidInfo, err := handler.mempool.GetAuctionBidInfo(tx)
|
||||
if err != nil {
|
||||
return sdkerrors.ResponseCheckTxWithEvents(fmt.Errorf("failed to get auction bid info: %w", err), 0, 0, nil, false)
|
||||
}
|
||||
|
||||
// If this is not a bid transaction, we just execute it normally.
|
||||
if bidInfo == nil {
|
||||
return handler.baseApp.CheckTx(req)
|
||||
}
|
||||
|
||||
// We attempt to get the latest committed state in order to verify transactions
|
||||
// as if they were to be executed at the top of the block. After verification, this
|
||||
// context will be discarded and will not apply any state changes.
|
||||
ctx := handler.GetContextForBidTx(req)
|
||||
|
||||
// Verify the bid transaction.
|
||||
gasInfo, err := handler.ValidateBidTx(ctx, tx, bidInfo)
|
||||
if err != nil {
|
||||
return sdkerrors.ResponseCheckTxWithEvents(fmt.Errorf("invalid bid tx: %w", err), gasInfo.GasWanted, gasInfo.GasUsed, nil, false)
|
||||
}
|
||||
|
||||
// If the bid transaction is valid, we know we can insert it into the mempool for consideration in the next block.
|
||||
if err := handler.mempool.Insert(ctx, tx); err != nil {
|
||||
return sdkerrors.ResponseCheckTxWithEvents(fmt.Errorf("invalid bid tx; failed to insert bid transaction into mempool: %w", err), gasInfo.GasWanted, gasInfo.GasUsed, nil, false)
|
||||
}
|
||||
|
||||
return cometabci.ResponseCheckTx{
|
||||
Code: cometabci.CodeTypeOK,
|
||||
GasWanted: int64(gasInfo.GasWanted),
|
||||
GasUsed: int64(gasInfo.GasUsed),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateBidTx is utilized to verify the bid transaction against the latest committed state.
|
||||
func (handler *CheckTxHandler) ValidateBidTx(ctx sdk.Context, bidTx sdk.Tx, bidInfo *mempool.AuctionBidInfo) (sdk.GasInfo, error) {
|
||||
// Verify the bid transaction.
|
||||
ctx, err := handler.anteHandler(ctx, bidTx, false)
|
||||
if err != nil {
|
||||
return sdk.GasInfo{}, fmt.Errorf("invalid bid tx; failed to execute ante handler: %w", err)
|
||||
}
|
||||
|
||||
// Store the gas info and priority of the bid transaction before applying changes with other transactions.
|
||||
gasInfo := sdk.GasInfo{
|
||||
GasWanted: ctx.GasMeter().Limit(),
|
||||
GasUsed: ctx.GasMeter().GasConsumed(),
|
||||
}
|
||||
|
||||
// Verify all of the bundled transactions.
|
||||
for _, tx := range bidInfo.Transactions {
|
||||
bundledTx, err := handler.mempool.WrapBundleTransaction(tx)
|
||||
if err != nil {
|
||||
return gasInfo, fmt.Errorf("invalid bid tx; failed to decode bundled tx: %w", err)
|
||||
}
|
||||
|
||||
bidInfo, err := handler.mempool.GetAuctionBidInfo(bundledTx)
|
||||
if err != nil {
|
||||
return gasInfo, fmt.Errorf("invalid bid tx; failed to get auction bid info: %w", err)
|
||||
}
|
||||
|
||||
// Bid txs cannot be included in bundled txs.
|
||||
if bidInfo != nil {
|
||||
return gasInfo, fmt.Errorf("invalid bid tx; bundled tx cannot be a bid tx")
|
||||
}
|
||||
|
||||
if ctx, err = handler.anteHandler(ctx, bundledTx, false); err != nil {
|
||||
return gasInfo, fmt.Errorf("invalid bid tx; failed to execute bundled transaction: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return gasInfo, nil
|
||||
}
|
||||
|
||||
// GetContextForBidTx is returns the latest committed state and sets the context given
|
||||
// the checkTx request.
|
||||
func (handler *CheckTxHandler) GetContextForBidTx(req cometabci.RequestCheckTx) sdk.Context {
|
||||
// Retrieve the commit multi-store which is used to retrieve the latest committed state.
|
||||
ms := handler.baseApp.CommitMultiStore().CacheMultiStore()
|
||||
|
||||
// Create a new context based off of the latest committed state.
|
||||
header := tmproto.Header{
|
||||
Height: handler.baseApp.LastBlockHeight(),
|
||||
ChainID: handler.chainID, // TODO: Replace with actual chain ID. This is currently not exposed by the app.
|
||||
}
|
||||
ctx, _ := sdk.NewContext(ms, header, true, handler.baseApp.Logger()).CacheContext()
|
||||
|
||||
// Set the context to the correct checking mode.
|
||||
switch req.Type {
|
||||
case cometabci.CheckTxType_New:
|
||||
ctx = ctx.WithIsCheckTx(true)
|
||||
case cometabci.CheckTxType_Recheck:
|
||||
ctx = ctx.WithIsReCheckTx(true)
|
||||
default:
|
||||
panic("unknown check tx type")
|
||||
}
|
||||
|
||||
// Set the remaining important context values.
|
||||
ctx = ctx.
|
||||
WithTxBytes(req.Tx).
|
||||
WithEventManager(sdk.NewEventManager()).
|
||||
WithConsensusParams(handler.baseApp.GetConsensusParams(ctx))
|
||||
|
||||
return ctx
|
||||
}
|
||||
@ -95,7 +95,7 @@ func (suite *ABCITestSuite) SetupTest() {
|
||||
)
|
||||
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)
|
||||
suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxEncoder(), suite.mempool)
|
||||
|
||||
// Accounts set up
|
||||
suite.accounts = testutils.RandomAccounts(suite.random, 10)
|
||||
|
||||
@ -296,7 +296,7 @@ func (suite *ABCITestSuite) TestPrepareProposal() {
|
||||
MinBidIncrement: suite.minBidIncrement,
|
||||
}
|
||||
suite.builderKeeper.SetParams(suite.ctx, params)
|
||||
suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), suite.mempool)
|
||||
suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxEncoder(), suite.mempool)
|
||||
|
||||
// Reset the proposal handler with the new mempool.
|
||||
suite.proposalHandler = v2.NewProposalHandler(suite.mempool, suite.logger, suite.anteHandler, suite.encodingConfig.TxConfig.TxEncoder(), suite.encodingConfig.TxConfig.TxDecoder())
|
||||
@ -749,7 +749,7 @@ func (suite *ABCITestSuite) TestProcessProposal() {
|
||||
MinBidIncrement: suite.minBidIncrement,
|
||||
}
|
||||
suite.builderKeeper.SetParams(suite.ctx, params)
|
||||
suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), suite.mempool)
|
||||
suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxEncoder(), suite.mempool)
|
||||
|
||||
// reset the proposal handler with the new mempool
|
||||
suite.proposalHandler = v2.NewProposalHandler(suite.mempool, suite.logger, suite.anteHandler, suite.encodingConfig.TxConfig.TxEncoder(), suite.encodingConfig.TxConfig.TxDecoder())
|
||||
|
||||
@ -48,7 +48,7 @@ func NewPOBAnteHandler(options POBHandlerOptions) sdk.AnteHandler {
|
||||
ante.NewSigGasConsumeDecorator(options.BaseOptions.AccountKeeper, options.BaseOptions.SigGasConsumer),
|
||||
ante.NewSigVerificationDecorator(options.BaseOptions.AccountKeeper, options.BaseOptions.SignModeHandler),
|
||||
ante.NewIncrementSequenceDecorator(options.BaseOptions.AccountKeeper),
|
||||
builderante.NewBuilderDecorator(options.BuilderKeeper, options.TxDecoder, options.TxEncoder, options.Mempool),
|
||||
builderante.NewBuilderDecorator(options.BuilderKeeper, options.TxEncoder, options.Mempool),
|
||||
}
|
||||
|
||||
return sdk.ChainAnteDecorators(anteDecorators...)
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
|
||||
"cosmossdk.io/depinject"
|
||||
dbm "github.com/cometbft/cometbft-db"
|
||||
cometabci "github.com/cometbft/cometbft/abci/types"
|
||||
"github.com/cometbft/cometbft/libs/log"
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
@ -72,6 +73,10 @@ import (
|
||||
builderkeeper "github.com/skip-mev/pob/x/builder/keeper"
|
||||
)
|
||||
|
||||
const (
|
||||
ChainID = "chain-id-0"
|
||||
)
|
||||
|
||||
var (
|
||||
BondDenom = sdk.DefaultBondDenom
|
||||
|
||||
@ -142,6 +147,9 @@ type TestApp struct {
|
||||
GroupKeeper groupkeeper.Keeper
|
||||
ConsensusParamsKeeper consensuskeeper.Keeper
|
||||
BuilderKeeper builderkeeper.Keeper
|
||||
|
||||
// custom checkTx handler
|
||||
checkTxHandler abci.CheckTx
|
||||
}
|
||||
|
||||
func init() {
|
||||
@ -275,7 +283,7 @@ func New(
|
||||
}
|
||||
anteHandler := NewPOBAnteHandler(options)
|
||||
|
||||
// Set the proposal handlers on the BaseApp.
|
||||
// Set the proposal handlers on the BaseApp along with the custom antehandler.
|
||||
proposalHandlers := abci.NewProposalHandler(
|
||||
mempool,
|
||||
app.App.Logger(),
|
||||
@ -285,6 +293,17 @@ func New(
|
||||
)
|
||||
app.App.SetPrepareProposal(proposalHandlers.PrepareProposalHandler())
|
||||
app.App.SetProcessProposal(proposalHandlers.ProcessProposalHandler())
|
||||
app.App.SetAnteHandler(anteHandler)
|
||||
|
||||
// Set the custom CheckTx handler on BaseApp.
|
||||
checkTxHandler := abci.NewCheckTxHandler(
|
||||
app.App,
|
||||
app.txConfig.TxDecoder(),
|
||||
mempool,
|
||||
anteHandler,
|
||||
ChainID,
|
||||
)
|
||||
app.SetCheckTx(checkTxHandler.CheckTx())
|
||||
|
||||
// load state streaming if enabled
|
||||
if _, _, err := streaming.LoadStreamingServices(app.App.BaseApp, appOpts, app.appCodec, logger, app.kvStoreKeys()); err != nil {
|
||||
@ -320,6 +339,19 @@ func New(
|
||||
return app
|
||||
}
|
||||
|
||||
// CheckTx will check the transaction with the provided checkTxHandler. We override the default
|
||||
// handler so that we can verify bid transactions before they are inserted into the mempool.
|
||||
// With the POB CheckTx, we can verify the bid transaction and all of the bundled transactions
|
||||
// before inserting the bid transaction into the mempool.
|
||||
func (app *TestApp) CheckTx(req cometabci.RequestCheckTx) cometabci.ResponseCheckTx {
|
||||
return app.checkTxHandler(req)
|
||||
}
|
||||
|
||||
// SetCheckTx sets the checkTxHandler for the app.
|
||||
func (app *TestApp) SetCheckTx(handler abci.CheckTx) {
|
||||
app.checkTxHandler = handler
|
||||
}
|
||||
|
||||
// Name returns the name of the App
|
||||
func (app *TestApp) Name() string { return app.BaseApp.Name() }
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@ import (
|
||||
|
||||
dbm "github.com/cometbft/cometbft-db"
|
||||
"github.com/cometbft/cometbft/libs/log"
|
||||
cometrand "github.com/cometbft/cometbft/libs/rand"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
||||
"github.com/skip-mev/pob/tests/app"
|
||||
@ -47,7 +46,7 @@ func newChain() (*chain, error) {
|
||||
}
|
||||
|
||||
return &chain{
|
||||
id: "chain-" + cometrand.NewRand().Str(6),
|
||||
id: app.ChainID,
|
||||
dataDir: tmpDir,
|
||||
}, nil
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,6 @@ package e2e
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@ -16,68 +15,9 @@ import (
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
"github.com/ory/dockertest/v3/docker"
|
||||
"github.com/skip-mev/pob/tests/app"
|
||||
buildertypes "github.com/skip-mev/pob/x/builder/types"
|
||||
)
|
||||
|
||||
// execAuctionBidTx executes an auction bid transaction on the given validator given the provided
|
||||
// bid, timeout, and bundle. This function returns the transaction hash. It does not wait for the
|
||||
// transaction to be committed.
|
||||
func (s *IntegrationTestSuite) execAuctionBidTx(valIdx int, bid sdk.Coin, timeout int64, bundle []string) string {
|
||||
address, err := s.chain.validators[valIdx].keyInfo.GetAddress()
|
||||
s.Require().NoError(err)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
|
||||
exec, err := s.dkrPool.Client.CreateExec(docker.CreateExecOptions{
|
||||
Context: ctx,
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
Container: s.valResources[valIdx].Container.ID,
|
||||
User: "root",
|
||||
Cmd: []string{
|
||||
"testappd",
|
||||
"tx",
|
||||
"builder",
|
||||
"auction-bid",
|
||||
address.String(), // bidder
|
||||
bid.String(), // bid
|
||||
strings.Join(bundle, ","), // bundle
|
||||
fmt.Sprintf("--%s=%d", flags.FlagTimeoutHeight, timeout), // timeout
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, s.chain.validators[valIdx].keyInfo.Name),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagChainID, s.chain.id),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoin(app.BondDenom, sdk.NewInt(1000000000)).String()),
|
||||
"--keyring-backend=test",
|
||||
"--broadcast-mode=sync",
|
||||
"-y",
|
||||
},
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
var (
|
||||
outBuf bytes.Buffer
|
||||
errBuf bytes.Buffer
|
||||
)
|
||||
|
||||
err = s.dkrPool.Client.StartExec(exec.ID, docker.StartExecOptions{
|
||||
Context: ctx,
|
||||
Detach: false,
|
||||
OutputStream: &outBuf,
|
||||
ErrorStream: &errBuf,
|
||||
})
|
||||
s.Require().NoErrorf(err, "stdout: %s, stderr: %s", outBuf.String(), errBuf.String())
|
||||
|
||||
output := outBuf.String()
|
||||
resp := strings.Split(output, ":")
|
||||
txHash := strings.TrimSpace(resp[len(resp)-1])
|
||||
|
||||
s.T().Logf(
|
||||
"broadcasted bid tx %s with bid %s timeout %d and %d bundled txs",
|
||||
txHash, bid, timeout, len(bundle),
|
||||
)
|
||||
|
||||
return txHash
|
||||
}
|
||||
|
||||
// execMsgSendTx executes a send transaction on the given validator given the provided
|
||||
// recipient and amount. This function returns the transaction hash. It does not wait for the
|
||||
// transaction to be committed.
|
||||
@ -136,12 +76,22 @@ func (s *IntegrationTestSuite) execMsgSendTx(valIdx int, to sdk.AccAddress, amou
|
||||
return txHash
|
||||
}
|
||||
|
||||
// createAuctionBidTx creates a transaction that bids on an auction given the provided bidder, bid, and transactions.
|
||||
func (s *IntegrationTestSuite) createAuctionBidTx(account TestAccount, bid sdk.Coin, transactions [][]byte, sequenceOffset, height uint64) []byte {
|
||||
msgs := []sdk.Msg{
|
||||
&buildertypes.MsgAuctionBid{
|
||||
Bidder: account.Address.String(),
|
||||
Bid: bid,
|
||||
Transactions: transactions,
|
||||
},
|
||||
}
|
||||
|
||||
return s.createTx(account, msgs, sequenceOffset, height)
|
||||
}
|
||||
|
||||
// createMsgSendTx creates a send transaction given the provided signer, recipient, amount, sequence number offset, and block height timeout.
|
||||
// This function is primarily used to create bundles of transactions.
|
||||
func (s *IntegrationTestSuite) createMsgSendTx(account TestAccount, toAddress string, amount sdk.Coins, sequenceOffset, height int) string {
|
||||
txConfig := encodingConfig.TxConfig
|
||||
txBuilder := txConfig.NewTxBuilder()
|
||||
|
||||
func (s *IntegrationTestSuite) createMsgSendTx(account TestAccount, toAddress string, amount sdk.Coins, sequenceOffset, height uint64) []byte {
|
||||
msgs := []sdk.Msg{
|
||||
&banktypes.MsgSend{
|
||||
FromAddress: account.Address.String(),
|
||||
@ -150,15 +100,23 @@ func (s *IntegrationTestSuite) createMsgSendTx(account TestAccount, toAddress st
|
||||
},
|
||||
}
|
||||
|
||||
return s.createTx(account, msgs, sequenceOffset, height)
|
||||
}
|
||||
|
||||
// createTx creates a transaction given the provided messages, sequence number offset, and block height timeout.
|
||||
func (s *IntegrationTestSuite) createTx(account TestAccount, msgs []sdk.Msg, sequenceOffset, height uint64) []byte {
|
||||
txConfig := encodingConfig.TxConfig
|
||||
txBuilder := txConfig.NewTxBuilder()
|
||||
|
||||
// Get account info of the sender to set the account number and sequence number
|
||||
baseAccount := s.queryAccount(account.Address)
|
||||
sequenceNumber := baseAccount.Sequence + uint64(sequenceOffset)
|
||||
sequenceNumber := baseAccount.Sequence + sequenceOffset
|
||||
|
||||
// Set the messages, fees, and timeout.
|
||||
txBuilder.SetMsgs(msgs...)
|
||||
txBuilder.SetGasLimit(5000000)
|
||||
txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(75000))))
|
||||
txBuilder.SetTimeoutHeight(uint64(height))
|
||||
txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(150000))))
|
||||
txBuilder.SetTimeoutHeight(height)
|
||||
|
||||
sigV2 := signing.SignatureV2{
|
||||
PubKey: account.PrivateKey.PubKey(),
|
||||
@ -191,8 +149,5 @@ func (s *IntegrationTestSuite) createMsgSendTx(account TestAccount, toAddress st
|
||||
bz, err := txConfig.TxEncoder()(txBuilder.GetTx())
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Hex encode the transaction
|
||||
hash := hex.EncodeToString(bz)
|
||||
|
||||
return hash
|
||||
return bz
|
||||
}
|
||||
|
||||
@ -116,15 +116,29 @@ func (s *IntegrationTestSuite) waitForABlock() {
|
||||
}
|
||||
|
||||
// bundleToTxHashes converts a bundle to a slice of transaction hashes.
|
||||
func (s *IntegrationTestSuite) bundleToTxHashes(bundle []string) []string {
|
||||
hashes := make([]string, len(bundle))
|
||||
func (s *IntegrationTestSuite) bundleToTxHashes(bidTx []byte, bundle [][]byte) []string {
|
||||
hashes := make([]string, len(bundle)+1)
|
||||
|
||||
for i, tx := range bundle {
|
||||
hashBz, err := hex.DecodeString(tx)
|
||||
s.Require().NoError(err)
|
||||
// encode the bid transaction into a hash
|
||||
hashBz := sha256.Sum256(bidTx)
|
||||
hash := hex.EncodeToString(hashBz[:])
|
||||
hashes[0] = hash
|
||||
|
||||
shaBz := sha256.Sum256(hashBz)
|
||||
hashes[i] = hex.EncodeToString(shaBz[:])
|
||||
for i, hash := range s.normalTxsToTxHashes(bundle) {
|
||||
hashes[i+1] = hash
|
||||
}
|
||||
|
||||
return hashes
|
||||
}
|
||||
|
||||
// normalTxsToTxHashes converts a slice of normal transactions to a slice of transaction hashes.
|
||||
func (s *IntegrationTestSuite) normalTxsToTxHashes(txs [][]byte) []string {
|
||||
hashes := make([]string, len(txs))
|
||||
|
||||
for i, tx := range txs {
|
||||
hashBz := sha256.Sum256(tx)
|
||||
hash := hex.EncodeToString(hashBz[:])
|
||||
hashes[i] = hash
|
||||
}
|
||||
|
||||
return hashes
|
||||
@ -132,13 +146,13 @@ func (s *IntegrationTestSuite) bundleToTxHashes(bundle []string) []string {
|
||||
|
||||
// verifyBlock verifies that the transactions in the block at the given height were seen
|
||||
// and executed in the order they were submitted i.e. how they are broadcasted in the bundle.
|
||||
func (s *IntegrationTestSuite) verifyBlock(height int64, bidTx string, bundle []string, expectedExecution map[string]bool) {
|
||||
func (s *IntegrationTestSuite) verifyBlock(height uint64, bundle []string, expectedExecution map[string]bool) {
|
||||
s.waitForABlock()
|
||||
s.T().Logf("Verifying block %d", height)
|
||||
|
||||
// Get the block's transactions and display the expected and actual block for debugging.
|
||||
txs := s.queryBlockTxs(height)
|
||||
s.displayBlock(txs, bidTx, bundle)
|
||||
s.displayBlock(txs, bundle)
|
||||
|
||||
// Ensure that all transactions executed as expected (i.e. landed or failed to land).
|
||||
for tx, landed := range expectedExecution {
|
||||
@ -149,28 +163,32 @@ func (s *IntegrationTestSuite) verifyBlock(height int64, bidTx string, bundle []
|
||||
|
||||
// Check that the block contains the expected transactions in the expected order
|
||||
// iff the bid transaction was expected to execute.
|
||||
if expectedExecution[bidTx] {
|
||||
hashBz := sha256.Sum256(txs[0])
|
||||
hash := hex.EncodeToString(hashBz[:])
|
||||
s.Require().Equal(strings.ToUpper(bidTx), strings.ToUpper(hash))
|
||||
if len(bundle) > 0 && expectedExecution[bundle[0]] {
|
||||
if expectedExecution[bundle[0]] {
|
||||
hashBz := sha256.Sum256(txs[0])
|
||||
hash := hex.EncodeToString(hashBz[:])
|
||||
s.Require().Equal(strings.ToUpper(bundle[0]), strings.ToUpper(hash))
|
||||
|
||||
for index, bundleTx := range bundle {
|
||||
hashBz := sha256.Sum256(txs[index+1])
|
||||
txHash := hex.EncodeToString(hashBz[:])
|
||||
for index, bundleTx := range bundle[1:] {
|
||||
hashBz := sha256.Sum256(txs[index+1])
|
||||
txHash := hex.EncodeToString(hashBz[:])
|
||||
|
||||
s.Require().Equal(strings.ToUpper(bundleTx), strings.ToUpper(txHash))
|
||||
s.Require().Equal(strings.ToUpper(bundleTx), strings.ToUpper(txHash))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// displayExpectedBlock displays the expected and actual blocks.
|
||||
func (s *IntegrationTestSuite) displayBlock(txs [][]byte, bidTx string, bundle []string) {
|
||||
expectedBlock := fmt.Sprintf("Expected block:\n\t(%d, %s)\n", 0, bidTx)
|
||||
for index, bundleTx := range bundle {
|
||||
expectedBlock += fmt.Sprintf("\t(%d, %s)\n", index+1, bundleTx)
|
||||
}
|
||||
func (s *IntegrationTestSuite) displayBlock(txs [][]byte, bundle []string) {
|
||||
if len(bundle) != 0 {
|
||||
expectedBlock := fmt.Sprintf("Expected block:\n\t(%d, %s)\n", 0, bundle[0])
|
||||
for index, bundleTx := range bundle[1:] {
|
||||
expectedBlock += fmt.Sprintf("\t(%d, %s)\n", index+1, bundleTx)
|
||||
}
|
||||
|
||||
s.T().Logf(expectedBlock)
|
||||
s.T().Logf(expectedBlock)
|
||||
}
|
||||
|
||||
// Display the actual block.
|
||||
if len(txs) == 0 {
|
||||
@ -192,15 +210,35 @@ func (s *IntegrationTestSuite) displayBlock(txs [][]byte, bidTx string, bundle [
|
||||
}
|
||||
|
||||
// displayExpectedBundle displays the expected order of the bid and bundled transactions.
|
||||
func (s *IntegrationTestSuite) displayExpectedBundle(prefix, bidTx string, bundle []string) {
|
||||
expectedBundle := fmt.Sprintf("%s expected bundle:\n\t(%d, %s)\n", prefix, 0, bidTx)
|
||||
for index, bundleTx := range s.bundleToTxHashes(bundle) {
|
||||
func (s *IntegrationTestSuite) displayExpectedBundle(prefix string, bidTx []byte, bundle [][]byte) {
|
||||
// encode the bid transaction into a hash
|
||||
hashes := s.bundleToTxHashes(bidTx, bundle)
|
||||
|
||||
expectedBundle := fmt.Sprintf("%s expected bundle:\n\t(%d, %s)\n", prefix, 0, hashes[0])
|
||||
for index, bundleTx := range hashes[1:] {
|
||||
expectedBundle += fmt.Sprintf("\t(%d, %s)\n", index+1, bundleTx)
|
||||
}
|
||||
|
||||
s.T().Logf(expectedBundle)
|
||||
}
|
||||
|
||||
// broadcastTx broadcasts a transaction to the network using the given validator.
|
||||
func (s *IntegrationTestSuite) broadcastTx(tx []byte, valIdx int) {
|
||||
node := s.valResources[valIdx]
|
||||
gRPCURI := node.GetHostPort("9090/tcp")
|
||||
|
||||
grpcConn, err := grpc.Dial(
|
||||
gRPCURI,
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
)
|
||||
|
||||
client := txtypes.NewServiceClient(grpcConn)
|
||||
|
||||
req := &txtypes.BroadcastTxRequest{TxBytes: tx, Mode: txtypes.BroadcastMode_BROADCAST_MODE_SYNC}
|
||||
_, err = client.BroadcastTx(context.Background(), req)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
// queryTx queries a transaction by its hash and returns whether there was an
|
||||
// error in including the transaction in a block.
|
||||
func (s *IntegrationTestSuite) queryTxPassed(txHash string) error {
|
||||
@ -268,21 +306,21 @@ func (s *IntegrationTestSuite) queryAccount(address sdk.AccAddress) *authtypes.B
|
||||
}
|
||||
|
||||
// queryCurrentHeight returns the current block height.
|
||||
func (s *IntegrationTestSuite) queryCurrentHeight() int64 {
|
||||
func (s *IntegrationTestSuite) queryCurrentHeight() uint64 {
|
||||
queryClient := tmclient.NewServiceClient(s.createClientContext())
|
||||
|
||||
req := &tmclient.GetLatestBlockRequest{}
|
||||
resp, err := queryClient.GetLatestBlock(context.Background(), req)
|
||||
s.Require().NoError(err)
|
||||
|
||||
return resp.SdkBlock.Header.Height
|
||||
return uint64(resp.SdkBlock.Header.Height)
|
||||
}
|
||||
|
||||
// queryBlockTxs returns the txs of the block at the given height.
|
||||
func (s *IntegrationTestSuite) queryBlockTxs(height int64) [][]byte {
|
||||
func (s *IntegrationTestSuite) queryBlockTxs(height uint64) [][]byte {
|
||||
queryClient := tmclient.NewServiceClient(s.createClientContext())
|
||||
|
||||
req := &tmclient.GetBlockByHeightRequest{Height: height}
|
||||
req := &tmclient.GetBlockByHeightRequest{Height: int64(height)}
|
||||
resp, err := queryClient.GetBlockByHeight(context.Background(), req)
|
||||
s.Require().NoError(err)
|
||||
|
||||
|
||||
@ -14,24 +14,24 @@ import (
|
||||
var _ sdk.AnteDecorator = BuilderDecorator{}
|
||||
|
||||
type (
|
||||
// Mempool is an interface that defines the methods required to interact with the application-side mempool.
|
||||
Mempool interface {
|
||||
Contains(tx sdk.Tx) (bool, error)
|
||||
GetAuctionBidInfo(tx sdk.Tx) (*mempool.AuctionBidInfo, error)
|
||||
GetTopAuctionTx(ctx context.Context) sdk.Tx
|
||||
}
|
||||
|
||||
// BuilderDecorator is an AnteDecorator that validates the auction bid and bundled transactions.
|
||||
BuilderDecorator struct {
|
||||
builderKeeper keeper.Keeper
|
||||
txDecoder sdk.TxDecoder
|
||||
txEncoder sdk.TxEncoder
|
||||
mempool Mempool
|
||||
}
|
||||
)
|
||||
|
||||
func NewBuilderDecorator(ak keeper.Keeper, txDecoder sdk.TxDecoder, txEncoder sdk.TxEncoder, mempool Mempool) BuilderDecorator {
|
||||
func NewBuilderDecorator(ak keeper.Keeper, txEncoder sdk.TxEncoder, mempool Mempool) BuilderDecorator {
|
||||
return BuilderDecorator{
|
||||
builderKeeper: ak,
|
||||
txDecoder: txDecoder,
|
||||
txEncoder: txEncoder,
|
||||
mempool: mempool,
|
||||
}
|
||||
@ -39,20 +39,20 @@ func NewBuilderDecorator(ak keeper.Keeper, txDecoder sdk.TxDecoder, txEncoder sd
|
||||
|
||||
// AnteHandle validates that the auction bid is valid if one exists. If valid it will deduct the entrance fee from the
|
||||
// bidder's account.
|
||||
func (ad BuilderDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
|
||||
func (bd BuilderDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
|
||||
// If comet is re-checking a transaction, we only need to check if the transaction is in the application-side mempool.
|
||||
if ctx.IsReCheckTx() {
|
||||
contains, err := ad.mempool.Contains(tx)
|
||||
contains, err := bd.mempool.Contains(tx)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
if !contains {
|
||||
return ctx, fmt.Errorf("transaction not found in application mempool")
|
||||
return ctx, fmt.Errorf("transaction not found in application-side mempool")
|
||||
}
|
||||
}
|
||||
|
||||
bidInfo, err := ad.mempool.GetAuctionBidInfo(tx)
|
||||
bidInfo, err := bd.mempool.GetAuctionBidInfo(tx)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
@ -60,8 +60,8 @@ func (ad BuilderDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool,
|
||||
// Validate the auction bid if one exists.
|
||||
if bidInfo != nil {
|
||||
// Auction transactions must have a timeout set to a valid block height.
|
||||
if int64(bidInfo.Timeout) < ctx.BlockHeight() {
|
||||
return ctx, fmt.Errorf("timeout height cannot be less than the current block height")
|
||||
if err := bd.ValidateTimeout(ctx, int64(bidInfo.Timeout)); err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
// We only need to verify the auction bid relative to the local validator's mempool if the mode
|
||||
@ -70,20 +70,20 @@ func (ad BuilderDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool,
|
||||
// poor liveness guarantees.
|
||||
topBid := sdk.Coin{}
|
||||
if ctx.IsCheckTx() || ctx.IsReCheckTx() {
|
||||
if topBidTx := ad.mempool.GetTopAuctionTx(ctx); topBidTx != nil {
|
||||
topBidBz, err := ad.txEncoder(topBidTx)
|
||||
if topBidTx := bd.mempool.GetTopAuctionTx(ctx); topBidTx != nil {
|
||||
topBidBz, err := bd.txEncoder(topBidTx)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
currentTxBz, err := ad.txEncoder(tx)
|
||||
currentTxBz, err := bd.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)
|
||||
topBidInfo, err := bd.mempool.GetAuctionBidInfo(topBidTx)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
@ -93,10 +93,31 @@ func (ad BuilderDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool,
|
||||
}
|
||||
}
|
||||
|
||||
if err := ad.builderKeeper.ValidateBidInfo(ctx, topBid, bidInfo); err != nil {
|
||||
if err := bd.builderKeeper.ValidateBidInfo(ctx, topBid, bidInfo); err != nil {
|
||||
return ctx, errors.Wrap(err, "failed to validate auction bid")
|
||||
}
|
||||
}
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
// ValidateTimeout validates that the timeout is greater than or equal to the expected block height
|
||||
// the bid transaction will be executed in.
|
||||
//
|
||||
// TODO: This will be deprecated in favor of the pre-commit hook once this available on the SDK
|
||||
// https://github.com/skip-mev/pob/issues/147
|
||||
func (bd BuilderDecorator) ValidateTimeout(ctx sdk.Context, timeout int64) error {
|
||||
currentBlockHeight := ctx.BlockHeight()
|
||||
|
||||
// If the mode is CheckTx or ReCheckTx, we increment the current block height by one to
|
||||
// account for the fact that the transaction will be executed in the next block.
|
||||
if ctx.IsCheckTx() || ctx.IsReCheckTx() {
|
||||
currentBlockHeight++
|
||||
}
|
||||
|
||||
if timeout < currentBlockHeight {
|
||||
return fmt.Errorf("timeout height cannot be less than the current block height")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -248,7 +248,7 @@ func (suite *AnteTestSuite) TestAnteHandler() {
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Execute the ante handler
|
||||
suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), mempool)
|
||||
suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxEncoder(), mempool)
|
||||
_, err = suite.executeAnteHandler(auctionTx, balance)
|
||||
if tc.pass {
|
||||
suite.Require().NoError(err)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user