feat(BB): Initialization of lane constructor code (#244)
This commit is contained in:
parent
1ed1adc856
commit
10688927ce
@ -1,6 +1,8 @@
|
||||
package abci
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
abci "github.com/cometbft/cometbft/abci/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
@ -20,13 +22,14 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
// NewProposalHandler returns a new abci++ proposal handler.
|
||||
func NewProposalHandler(logger log.Logger, txDecoder sdk.TxDecoder, mempool blockbuster.Mempool) *ProposalHandler {
|
||||
// NewProposalHandler returns a new abci++ proposal handler. This proposal handler will
|
||||
// iteratively call each of the lanes in the chain to prepare and process the proposal.
|
||||
func NewProposalHandler(logger log.Logger, txDecoder sdk.TxDecoder, lanes []blockbuster.Lane) *ProposalHandler {
|
||||
return &ProposalHandler{
|
||||
logger: logger,
|
||||
txDecoder: txDecoder,
|
||||
prepareLanesHandler: ChainPrepareLanes(mempool.Registry()...),
|
||||
processLanesHandler: ChainProcessLanes(mempool.Registry()...),
|
||||
prepareLanesHandler: ChainPrepareLanes(lanes...),
|
||||
processLanesHandler: ChainProcessLanes(lanes...),
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,6 +58,7 @@ func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler {
|
||||
"prepared proposal",
|
||||
"num_txs", proposal.GetNumTxs(),
|
||||
"total_tx_bytes", proposal.GetTotalTxBytes(),
|
||||
"height", req.Height,
|
||||
)
|
||||
|
||||
return &abci.ResponsePrepareProposal{
|
||||
@ -71,9 +75,11 @@ func (h *ProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHandler {
|
||||
return func(ctx sdk.Context, req *abci.RequestProcessProposal) (resp *abci.ResponseProcessProposal, err error) {
|
||||
// In the case where any of the lanes panic, we recover here and return a reject status.
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
h.logger.Error("failed to process proposal", "err", err)
|
||||
if rec := recover(); rec != nil {
|
||||
h.logger.Error("failed to process proposal", "recover_err", rec)
|
||||
|
||||
resp = &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}
|
||||
err = fmt.Errorf("failed to process proposal: %v", rec)
|
||||
}
|
||||
}()
|
||||
|
||||
@ -125,9 +131,14 @@ func ChainPrepareLanes(chain ...blockbuster.Lane) blockbuster.PrepareLanesHandle
|
||||
// Cache the context in the case where any of the lanes fail to prepare the proposal.
|
||||
cacheCtx, write := ctx.CacheContext()
|
||||
|
||||
// We utilize a recover to handle any panics or errors that occur during the preparation
|
||||
// of a lane's transactions. This defer will first check if there was a panic or error
|
||||
// thrown from the lane's preparation logic. If there was, we log the error, skip the lane,
|
||||
// and call the next lane in the chain to the prepare the proposal.
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil || err != nil {
|
||||
lane.Logger().Error("failed to prepare lane", "lane", lane.Name(), "err", err, "recover_error", rec)
|
||||
lane.Logger().Info("skipping lane", "lane", lane.Name())
|
||||
|
||||
lanesRemaining := len(chain)
|
||||
switch {
|
||||
@ -141,22 +152,13 @@ func ChainPrepareLanes(chain ...blockbuster.Lane) blockbuster.PrepareLanesHandle
|
||||
// is the lane that failed to prepare the proposal but the second lane in the
|
||||
// chain is not the terminator lane so there could potentially be more transactions
|
||||
// added to the proposal
|
||||
maxTxBytesForLane := utils.GetMaxTxBytesForLane(
|
||||
partialProposal.GetMaxTxBytes(),
|
||||
partialProposal.GetTotalTxBytes(),
|
||||
chain[1].GetMaxBlockSpace(),
|
||||
)
|
||||
|
||||
finalProposal, err = chain[1].PrepareLane(
|
||||
ctx,
|
||||
partialProposal,
|
||||
maxTxBytesForLane,
|
||||
ChainPrepareLanes(chain[2:]...),
|
||||
)
|
||||
finalProposal, err = ChainPrepareLanes(chain[1:]...)(ctx, partialProposal)
|
||||
}
|
||||
} else {
|
||||
// Write the cache to the context since we know that the lane successfully prepared
|
||||
// the partial proposal.
|
||||
// the partial proposal. State is written to in a backwards, cascading fashion. This means
|
||||
// that the final context will only be updated after all other lanes have successfully
|
||||
// prepared the partial proposal.
|
||||
write()
|
||||
}
|
||||
}()
|
||||
@ -198,7 +200,7 @@ func ChainProcessLanes(chain ...blockbuster.Lane) blockbuster.ProcessLanesHandle
|
||||
|
||||
chain[0].Logger().Info("processing lane", "lane", chain[0].Name())
|
||||
|
||||
if err := chain[0].ProcessLaneBasic(ctx, proposalTxs); err != nil {
|
||||
if err := chain[0].CheckOrder(ctx, proposalTxs); err != nil {
|
||||
chain[0].Logger().Error("failed to process lane", "lane", chain[0].Name(), "err", err)
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,123 +0,0 @@
|
||||
package blockbuster
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/math"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool"
|
||||
)
|
||||
|
||||
type (
|
||||
// PrepareLanesHandler wraps all of the lanes Prepare function into a single chained
|
||||
// function. You can think of it like an AnteHandler, but for preparing proposals in the
|
||||
// context of lanes instead of modules.
|
||||
PrepareLanesHandler func(ctx sdk.Context, proposal BlockProposal) (BlockProposal, error)
|
||||
|
||||
// ProcessLanesHandler wraps all of the lanes Process functions into a single chained
|
||||
// function. You can think of it like an AnteHandler, but for processing proposals in the
|
||||
// context of lanes instead of modules.
|
||||
ProcessLanesHandler func(ctx sdk.Context, txs []sdk.Tx) (sdk.Context, error)
|
||||
|
||||
// BaseLaneConfig defines the basic functionality needed for a lane.
|
||||
BaseLaneConfig struct {
|
||||
Logger log.Logger
|
||||
TxEncoder sdk.TxEncoder
|
||||
TxDecoder sdk.TxDecoder
|
||||
AnteHandler sdk.AnteHandler
|
||||
|
||||
// MaxBlockSpace defines the relative percentage of block space that can be
|
||||
// used by this lane. NOTE: If this is set to zero, then there is no limit
|
||||
// on the number of transactions that can be included in the block for this
|
||||
// lane (up to maxTxBytes as provided by the request). This is useful for the default lane.
|
||||
MaxBlockSpace math.LegacyDec
|
||||
|
||||
// IgnoreList defines the list of lanes to ignore when processing transactions. This
|
||||
// is useful for when you want lanes to exist after the default lane. For example,
|
||||
// say there are two lanes: default and free. The free lane should be processed after
|
||||
// the default lane. In this case, the free lane should be added to the ignore list
|
||||
// of the default lane. Otherwise, the transactions that belong to the free lane
|
||||
// will be processed by the default lane.
|
||||
IgnoreList []Lane
|
||||
}
|
||||
|
||||
// Lane defines an interface used for block construction
|
||||
Lane interface {
|
||||
sdkmempool.Mempool
|
||||
|
||||
// Name returns the name of the lane.
|
||||
Name() string
|
||||
|
||||
// Match determines if a transaction belongs to this lane.
|
||||
Match(ctx sdk.Context, tx sdk.Tx) bool
|
||||
|
||||
// VerifyTx verifies the transaction belonging to this lane.
|
||||
VerifyTx(ctx sdk.Context, tx sdk.Tx) error
|
||||
|
||||
// Contains returns true if the mempool/lane contains the given transaction.
|
||||
Contains(tx sdk.Tx) bool
|
||||
|
||||
// PrepareLane builds a portion of the block. It inputs the maxTxBytes that can be
|
||||
// included in the proposal for the given lane, the partial proposal, and a function
|
||||
// to call the next lane in the chain. The next lane in the chain will be called with
|
||||
// the updated proposal and context.
|
||||
PrepareLane(ctx sdk.Context, proposal BlockProposal, maxTxBytes int64, next PrepareLanesHandler) (BlockProposal, error)
|
||||
|
||||
// ProcessLaneBasic validates that transactions belonging to this lane are not misplaced
|
||||
// in the block proposal.
|
||||
ProcessLaneBasic(ctx sdk.Context, txs []sdk.Tx) error
|
||||
|
||||
// ProcessLane verifies this lane's portion of a proposed block. It inputs the transactions
|
||||
// that may belong to this lane and a function to call the next lane in the chain. The next
|
||||
// lane in the chain will be called with the updated context and filtered down transactions.
|
||||
ProcessLane(ctx sdk.Context, proposalTxs []sdk.Tx, next ProcessLanesHandler) (sdk.Context, error)
|
||||
|
||||
// SetAnteHandler sets the lane's antehandler.
|
||||
SetAnteHandler(antehander sdk.AnteHandler)
|
||||
|
||||
// Logger returns the lane's logger.
|
||||
Logger() log.Logger
|
||||
|
||||
// GetMaxBlockSpace returns the max block space for the lane as a relative percentage.
|
||||
GetMaxBlockSpace() math.LegacyDec
|
||||
}
|
||||
)
|
||||
|
||||
// NewLaneConfig returns a new LaneConfig. This will be embedded in a lane.
|
||||
func NewBaseLaneConfig(
|
||||
logger log.Logger,
|
||||
txEncoder sdk.TxEncoder,
|
||||
txDecoder sdk.TxDecoder,
|
||||
anteHandler sdk.AnteHandler,
|
||||
maxBlockSpace math.LegacyDec,
|
||||
) BaseLaneConfig {
|
||||
return BaseLaneConfig{
|
||||
Logger: logger,
|
||||
TxEncoder: txEncoder,
|
||||
TxDecoder: txDecoder,
|
||||
AnteHandler: anteHandler,
|
||||
MaxBlockSpace: maxBlockSpace,
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateBasic validates the lane configuration.
|
||||
func (c *BaseLaneConfig) ValidateBasic() error {
|
||||
if c.Logger == nil {
|
||||
return fmt.Errorf("logger cannot be nil")
|
||||
}
|
||||
|
||||
if c.TxEncoder == nil {
|
||||
return fmt.Errorf("tx encoder cannot be nil")
|
||||
}
|
||||
|
||||
if c.TxDecoder == nil {
|
||||
return fmt.Errorf("tx decoder cannot be nil")
|
||||
}
|
||||
|
||||
if c.MaxBlockSpace.IsNil() || c.MaxBlockSpace.IsNegative() || c.MaxBlockSpace.GT(math.LegacyOneDec()) {
|
||||
return fmt.Errorf("max block space must be set to a value between 0 and 1")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
67
blockbuster/lane_abci.go
Normal file
67
blockbuster/lane_abci.go
Normal file
@ -0,0 +1,67 @@
|
||||
package blockbuster
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/skip-mev/pob/blockbuster/utils"
|
||||
)
|
||||
|
||||
// PrepareLane will prepare a partial proposal for the lane. It will select transactions from the
|
||||
// lane respecting the selection logic of the prepareLaneHandler. It will then update the partial
|
||||
// proposal with the selected transactions. If the proposal is unable to be updated, we return an
|
||||
// error. The proposal will only be modified if it passes all of the invarient checks.
|
||||
func (l *LaneConstructor) PrepareLane(
|
||||
ctx sdk.Context,
|
||||
proposal BlockProposal,
|
||||
maxTxBytes int64,
|
||||
next PrepareLanesHandler,
|
||||
) (BlockProposal, error) {
|
||||
txs, txsToRemove, err := l.prepareLaneHandler(ctx, proposal, maxTxBytes)
|
||||
if err != nil {
|
||||
return proposal, err
|
||||
}
|
||||
|
||||
// Remove all transactions that were invalid during the creation of the partial proposal.
|
||||
if err := utils.RemoveTxsFromLane(txsToRemove, l); err != nil {
|
||||
l.Logger().Error(
|
||||
"failed to remove transactions from lane",
|
||||
"lane", l.Name(),
|
||||
"err", err,
|
||||
)
|
||||
}
|
||||
|
||||
// Update the proposal with the selected transactions.
|
||||
if err := proposal.UpdateProposal(l, txs); err != nil {
|
||||
return proposal, err
|
||||
}
|
||||
|
||||
return next(ctx, proposal)
|
||||
}
|
||||
|
||||
// CheckOrder checks that the ordering logic of the lane is respected given the set of transactions
|
||||
// in the block proposal. If the ordering logic is not respected, we return an error.
|
||||
func (l *LaneConstructor) CheckOrder(ctx sdk.Context, txs []sdk.Tx) error {
|
||||
return l.checkOrderHandler(ctx, txs)
|
||||
}
|
||||
|
||||
// ProcessLane verifies that the transactions included in the block proposal are valid respecting
|
||||
// the verification logic of the lane (processLaneHandler). If the transactions are valid, we
|
||||
// return the transactions that do not belong to this lane to the next lane. If the transactions
|
||||
// are invalid, we return an error.
|
||||
func (l *LaneConstructor) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next ProcessLanesHandler) (sdk.Context, error) {
|
||||
remainingTxs, err := l.processLaneHandler(ctx, txs)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
return next(ctx, remainingTxs)
|
||||
}
|
||||
|
||||
// AnteVerifyTx verifies that the transaction is valid respecting the ante verification logic of
|
||||
// of the antehandler chain.
|
||||
func (l *LaneConstructor) AnteVerifyTx(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
|
||||
if l.cfg.AnteHandler != nil {
|
||||
return l.cfg.AnteHandler(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
198
blockbuster/lane_constructor.go
Normal file
198
blockbuster/lane_constructor.go
Normal file
@ -0,0 +1,198 @@
|
||||
package blockbuster
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/math"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
var _ Lane = (*LaneConstructor)(nil)
|
||||
|
||||
// LaneConstructor is a generic implementation of a lane. It is meant to be used
|
||||
// as a base for other lanes to be built on top of. It provides a default
|
||||
// implementation of the MatchHandler, PrepareLaneHandler, ProcessLaneHandler,
|
||||
// and CheckOrderHandler. To extend this lane, you must either utilize the default
|
||||
// handlers or construct your own that you pass into the constructor/setters.
|
||||
type LaneConstructor struct {
|
||||
// cfg stores functionality required to encode/decode transactions, maintains how
|
||||
// many transactions are allowed in this lane's mempool, and the amount of block
|
||||
// space this lane is allowed to consume.
|
||||
cfg LaneConfig
|
||||
|
||||
// laneName is the name of the lane.
|
||||
laneName string
|
||||
|
||||
// LaneMempool is the mempool that is responsible for storing transactions
|
||||
// that are waiting to be processed.
|
||||
LaneMempool
|
||||
|
||||
// matchHandler is the function that determines whether or not a transaction
|
||||
// should be processed by this lane.
|
||||
matchHandler MatchHandler
|
||||
|
||||
// prepareLaneHandler is the function that is called when a new proposal is being
|
||||
// requested and the lane needs to submit transactions it wants included in the block.
|
||||
prepareLaneHandler PrepareLaneHandler
|
||||
|
||||
// checkOrderHandler is the function that is called when a new proposal is being
|
||||
// verified and the lane needs to verify that the transactions included in the proposal
|
||||
// respect the ordering rules of the lane and does not interleave transactions from other lanes.
|
||||
checkOrderHandler CheckOrderHandler
|
||||
|
||||
// processLaneHandler is the function that is called when a new proposal is being
|
||||
// verified and the lane needs to verify that the transactions included in the proposal
|
||||
// are valid respecting the verification logic of the lane.
|
||||
processLaneHandler ProcessLaneHandler
|
||||
}
|
||||
|
||||
// NewLaneConstructor returns a new lane constructor. When creating this lane, the type
|
||||
// of the lane must be specified. The type of the lane is directly associated with the
|
||||
// type of the mempool that is used to store transactions that are waiting to be processed.
|
||||
func NewLaneConstructor(
|
||||
cfg LaneConfig,
|
||||
laneName string,
|
||||
laneMempool LaneMempool,
|
||||
matchHandlerFn MatchHandler,
|
||||
) *LaneConstructor {
|
||||
lane := &LaneConstructor{
|
||||
cfg: cfg,
|
||||
laneName: laneName,
|
||||
LaneMempool: laneMempool,
|
||||
matchHandler: matchHandlerFn,
|
||||
}
|
||||
|
||||
if err := lane.ValidateBasic(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return lane
|
||||
}
|
||||
|
||||
// ValidateBasic ensures that the lane was constructed properly. In the case that
|
||||
// the lane was not constructed with proper handlers, default handlers are set.
|
||||
func (l *LaneConstructor) ValidateBasic() error {
|
||||
if err := l.cfg.ValidateBasic(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if l.laneName == "" {
|
||||
return fmt.Errorf("lane name cannot be empty")
|
||||
}
|
||||
|
||||
if l.LaneMempool == nil {
|
||||
return fmt.Errorf("lane mempool cannot be nil")
|
||||
}
|
||||
|
||||
if l.matchHandler == nil {
|
||||
return fmt.Errorf("match handler cannot be nil")
|
||||
}
|
||||
|
||||
if l.prepareLaneHandler == nil {
|
||||
l.prepareLaneHandler = l.DefaultPrepareLaneHandler()
|
||||
}
|
||||
|
||||
if l.processLaneHandler == nil {
|
||||
l.processLaneHandler = l.DefaultProcessLaneHandler()
|
||||
}
|
||||
|
||||
if l.checkOrderHandler == nil {
|
||||
l.checkOrderHandler = l.DefaultCheckOrderHandler()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetPrepareLaneHandler sets the prepare lane handler for the lane. This handler
|
||||
// is called when a new proposal is being requested and the lane needs to submit
|
||||
// transactions it wants included in the block.
|
||||
func (l *LaneConstructor) SetPrepareLaneHandler(prepareLaneHandler PrepareLaneHandler) {
|
||||
if prepareLaneHandler == nil {
|
||||
panic("prepare lane handler cannot be nil")
|
||||
}
|
||||
|
||||
l.prepareLaneHandler = prepareLaneHandler
|
||||
}
|
||||
|
||||
// SetProcessLaneHandler sets the process lane handler for the lane. This handler
|
||||
// is called when a new proposal is being verified and the lane needs to verify
|
||||
// that the transactions included in the proposal are valid respecting the verification
|
||||
// logic of the lane.
|
||||
func (l *LaneConstructor) SetProcessLaneHandler(processLaneHandler ProcessLaneHandler) {
|
||||
if processLaneHandler == nil {
|
||||
panic("process lane handler cannot be nil")
|
||||
}
|
||||
|
||||
l.processLaneHandler = processLaneHandler
|
||||
}
|
||||
|
||||
// SetCheckOrderHandler sets the check order handler for the lane. This handler
|
||||
// is called when a new proposal is being verified and the lane needs to verify
|
||||
// that the transactions included in the proposal respect the ordering rules of
|
||||
// the lane and does not include transactions from other lanes.
|
||||
func (l *LaneConstructor) SetCheckOrderHandler(checkOrderHandler CheckOrderHandler) {
|
||||
if checkOrderHandler == nil {
|
||||
panic("check order handler cannot be nil")
|
||||
}
|
||||
|
||||
l.checkOrderHandler = checkOrderHandler
|
||||
}
|
||||
|
||||
// Match returns true if the transaction should be processed by this lane. This
|
||||
// function first determines if the transaction matches the lane and then checks
|
||||
// if the transaction is on the ignore list. If the transaction is on the ignore
|
||||
// list, it returns false.
|
||||
func (l *LaneConstructor) Match(ctx sdk.Context, tx sdk.Tx) bool {
|
||||
return l.matchHandler(ctx, tx) && !l.CheckIgnoreList(ctx, tx)
|
||||
}
|
||||
|
||||
// CheckIgnoreList returns true if the transaction is on the ignore list. The ignore
|
||||
// list is utilized to prevent transactions that should be considered in other lanes
|
||||
// from being considered from this lane.
|
||||
func (l *LaneConstructor) CheckIgnoreList(ctx sdk.Context, tx sdk.Tx) bool {
|
||||
for _, lane := range l.cfg.IgnoreList {
|
||||
if lane.Match(ctx, tx) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Name returns the name of the lane.
|
||||
func (l *LaneConstructor) Name() string {
|
||||
return l.laneName
|
||||
}
|
||||
|
||||
// SetIgnoreList sets the ignore list for the lane. The ignore list is a list
|
||||
// of lanes that the lane should ignore when processing transactions.
|
||||
func (l *LaneConstructor) SetIgnoreList(lanes []Lane) {
|
||||
l.cfg.IgnoreList = lanes
|
||||
}
|
||||
|
||||
// SetAnteHandler sets the ante handler for the lane.
|
||||
func (l *LaneConstructor) SetAnteHandler(anteHandler sdk.AnteHandler) {
|
||||
l.cfg.AnteHandler = anteHandler
|
||||
}
|
||||
|
||||
// Logger returns the logger for the lane.
|
||||
func (l *LaneConstructor) Logger() log.Logger {
|
||||
return l.cfg.Logger
|
||||
}
|
||||
|
||||
// TxDecoder returns the tx decoder for the lane.
|
||||
func (l *LaneConstructor) TxDecoder() sdk.TxDecoder {
|
||||
return l.cfg.TxDecoder
|
||||
}
|
||||
|
||||
// TxEncoder returns the tx encoder for the lane.
|
||||
func (l *LaneConstructor) TxEncoder() sdk.TxEncoder {
|
||||
return l.cfg.TxEncoder
|
||||
}
|
||||
|
||||
// GetMaxBlockSpace returns the maximum amount of block space that the lane is
|
||||
// allowed to consume as a percentage of the total block space.
|
||||
func (l *LaneConstructor) GetMaxBlockSpace() math.LegacyDec {
|
||||
return l.cfg.MaxBlockSpace
|
||||
}
|
||||
155
blockbuster/lane_handlers.go
Normal file
155
blockbuster/lane_handlers.go
Normal file
@ -0,0 +1,155 @@
|
||||
package blockbuster
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/skip-mev/pob/blockbuster/utils"
|
||||
)
|
||||
|
||||
// DefaultPrepareLaneHandler returns a default implementation of the PrepareLaneHandler. It
|
||||
// selects all transactions in the mempool that are valid and not already in the partial
|
||||
// proposal. It will continue to reap transactions until the maximum block space for this
|
||||
// lane has been reached. Additionally, any transactions that are invalid will be returned.
|
||||
func (l *LaneConstructor) DefaultPrepareLaneHandler() PrepareLaneHandler {
|
||||
return func(ctx sdk.Context, proposal BlockProposal, maxTxBytes int64) ([][]byte, []sdk.Tx, error) {
|
||||
var (
|
||||
totalSize int64
|
||||
txs [][]byte
|
||||
txsToRemove []sdk.Tx
|
||||
)
|
||||
|
||||
// Select all transactions in the mempool that are valid and not already in the
|
||||
// partial proposal.
|
||||
for iterator := l.Select(ctx, nil); iterator != nil; iterator = iterator.Next() {
|
||||
tx := iterator.Tx()
|
||||
|
||||
txBytes, hash, err := utils.GetTxHashStr(l.TxEncoder(), tx)
|
||||
if err != nil {
|
||||
l.Logger().Info("failed to get hash of tx", "err", err)
|
||||
|
||||
txsToRemove = append(txsToRemove, tx)
|
||||
continue
|
||||
}
|
||||
|
||||
// Double check that the transaction belongs to this lane.
|
||||
if !l.Match(ctx, tx) {
|
||||
l.Logger().Info(
|
||||
"failed to select tx for lane; tx does not belong to lane",
|
||||
"tx_hash", hash,
|
||||
"lane", l.Name(),
|
||||
)
|
||||
|
||||
txsToRemove = append(txsToRemove, tx)
|
||||
continue
|
||||
}
|
||||
|
||||
// if the transaction is already in the (partial) block proposal, we skip it.
|
||||
if proposal.Contains(txBytes) {
|
||||
l.Logger().Info(
|
||||
"failed to select tx for lane; tx is already in proposal",
|
||||
"tx_hash", hash,
|
||||
"lane", l.Name(),
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// If the transaction is too large, we break and do not attempt to include more txs.
|
||||
txSize := int64(len(txBytes))
|
||||
if updatedSize := totalSize + txSize; updatedSize > maxTxBytes {
|
||||
l.Logger().Info(
|
||||
"tx bytes above the maximum allowed",
|
||||
"lane", l.Name(),
|
||||
"tx_size", txSize,
|
||||
"total_size", totalSize,
|
||||
"max_tx_bytes", maxTxBytes,
|
||||
"tx_hash", hash,
|
||||
)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
// Verify the transaction.
|
||||
if ctx, err = l.AnteVerifyTx(ctx, tx, false); err != nil {
|
||||
l.Logger().Info(
|
||||
"failed to verify tx",
|
||||
"tx_hash", hash,
|
||||
"err", err,
|
||||
)
|
||||
|
||||
txsToRemove = append(txsToRemove, tx)
|
||||
continue
|
||||
}
|
||||
|
||||
totalSize += txSize
|
||||
txs = append(txs, txBytes)
|
||||
}
|
||||
|
||||
return txs, txsToRemove, nil
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultProcessLaneHandler returns a default implementation of the ProcessLaneHandler. It
|
||||
// verifies all transactions in the lane that matches to the lane. If any transaction
|
||||
// fails to verify, the entire proposal is rejected. If the handler comes across a transaction
|
||||
// that does not match the lane's matcher, it will return the remaining transactions in the
|
||||
// proposal.
|
||||
func (l *LaneConstructor) DefaultProcessLaneHandler() ProcessLaneHandler {
|
||||
return func(ctx sdk.Context, txs []sdk.Tx) ([]sdk.Tx, error) {
|
||||
var err error
|
||||
|
||||
// Process all transactions that match the lane's matcher.
|
||||
for index, tx := range txs {
|
||||
if l.Match(ctx, tx) {
|
||||
if ctx, err = l.AnteVerifyTx(ctx, tx, false); err != nil {
|
||||
return nil, fmt.Errorf("failed to verify tx: %w", err)
|
||||
}
|
||||
} else {
|
||||
return txs[index:], nil
|
||||
}
|
||||
}
|
||||
|
||||
// This means we have processed all transactions in the proposal.
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultCheckOrderHandler returns a default implementation of the CheckOrderHandler. It
|
||||
// ensures the following invariants:
|
||||
//
|
||||
// 1. All transactions that belong to this lane respect the ordering logic defined by the
|
||||
// lane.
|
||||
// 2. Transactions that belong to other lanes cannot be interleaved with transactions that
|
||||
// belong to this lane.
|
||||
func (l *LaneConstructor) DefaultCheckOrderHandler() CheckOrderHandler {
|
||||
return func(ctx sdk.Context, txs []sdk.Tx) error {
|
||||
seenOtherLaneTx := false
|
||||
|
||||
for index, tx := range txs {
|
||||
if l.Match(ctx, tx) {
|
||||
if seenOtherLaneTx {
|
||||
return fmt.Errorf("the %s lane contains a transaction that belongs to another lane", l.Name())
|
||||
}
|
||||
|
||||
// If the transactions do not respect the priority defined by the mempool, we consider the proposal
|
||||
// to be invalid
|
||||
if index > 0 && l.Compare(ctx, txs[index-1], tx) == -1 {
|
||||
return fmt.Errorf("transaction at index %d has a higher priority than %d", index, index-1)
|
||||
}
|
||||
} else {
|
||||
seenOtherLaneTx = true
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultMatchHandler returns a default implementation of the MatchHandler. It matches all
|
||||
// transactions.
|
||||
func DefaultMatchHandler() MatchHandler {
|
||||
return func(ctx sdk.Context, tx sdk.Tx) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
71
blockbuster/lane_interface.go
Normal file
71
blockbuster/lane_interface.go
Normal file
@ -0,0 +1,71 @@
|
||||
package blockbuster
|
||||
|
||||
import (
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/math"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool"
|
||||
)
|
||||
|
||||
// LaneMempool defines the interface a lane's mempool should implement. The basic API
|
||||
// is the same as the sdk.Mempool, but it also includes a Compare function that is used
|
||||
// to determine the relative priority of two transactions belonging in the same lane.
|
||||
//
|
||||
//go:generate mockery --name LaneMempool --output ./utils/mocks --outpkg mocks --case underscore
|
||||
type LaneMempool interface {
|
||||
sdkmempool.Mempool
|
||||
|
||||
// Compare determines the relative priority of two transactions belonging in the same lane. Compare
|
||||
// will return -1 if this transaction has a lower priority than the other transaction, 0 if they have
|
||||
// the same priority, and 1 if this transaction has a higher priority than the other transaction.
|
||||
Compare(ctx sdk.Context, this, other sdk.Tx) int
|
||||
|
||||
// Contains returns true if the transaction is contained in the mempool.
|
||||
Contains(tx sdk.Tx) bool
|
||||
}
|
||||
|
||||
// Lane defines an interface used for matching transactions to lanes, storing transactions,
|
||||
// and constructing partial blocks.
|
||||
//
|
||||
//go:generate mockery --name Lane --output ./utils/mocks --outpkg mocks --case underscore
|
||||
type Lane interface {
|
||||
LaneMempool
|
||||
|
||||
// PrepareLane builds a portion of the block. It inputs the maxTxBytes that can be
|
||||
// included in the proposal for the given lane, the partial proposal, and a function
|
||||
// to call the next lane in the chain. The next lane in the chain will be called with
|
||||
// the updated proposal and context.
|
||||
PrepareLane(
|
||||
ctx sdk.Context,
|
||||
proposal BlockProposal,
|
||||
maxTxBytes int64,
|
||||
next PrepareLanesHandler,
|
||||
) (BlockProposal, error)
|
||||
|
||||
// CheckOrder validates that transactions belonging to this lane are not misplaced
|
||||
// in the block proposal and respect the ordering rules of the lane.
|
||||
CheckOrder(ctx sdk.Context, txs []sdk.Tx) error
|
||||
|
||||
// ProcessLane verifies this lane's portion of a proposed block. It inputs the transactions
|
||||
// that may belong to this lane and a function to call the next lane in the chain. The next
|
||||
// lane in the chain will be called with the updated context and filtered down transactions.
|
||||
ProcessLane(ctx sdk.Context, proposalTxs []sdk.Tx, next ProcessLanesHandler) (sdk.Context, error)
|
||||
|
||||
// GetMaxBlockSpace returns the max block space for the lane as a relative percentage.
|
||||
GetMaxBlockSpace() math.LegacyDec
|
||||
|
||||
// Logger returns the lane's logger.
|
||||
Logger() log.Logger
|
||||
|
||||
// Name returns the name of the lane.
|
||||
Name() string
|
||||
|
||||
// SetAnteHandler sets the lane's antehandler.
|
||||
SetAnteHandler(antehander sdk.AnteHandler)
|
||||
|
||||
// SetIgnoreList sets the lanes that should be ignored by this lane.
|
||||
SetIgnoreList(ignoreList []Lane)
|
||||
|
||||
// Match determines if a transaction belongs to this lane.
|
||||
Match(ctx sdk.Context, tx sdk.Tx) bool
|
||||
}
|
||||
159
blockbuster/lane_mempool.go
Normal file
159
blockbuster/lane_mempool.go
Normal file
@ -0,0 +1,159 @@
|
||||
package blockbuster
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool"
|
||||
"github.com/skip-mev/pob/blockbuster/utils"
|
||||
)
|
||||
|
||||
type (
|
||||
// ConstructorMempool defines a mempool that orders transactions based on the
|
||||
// txPriority. The mempool is a wrapper on top of the SDK's Priority Nonce mempool.
|
||||
// It include's additional helper functions that allow users to determine if a
|
||||
// transaction is already in the mempool and to compare the priority of two
|
||||
// transactions.
|
||||
ConstructorMempool[C comparable] struct {
|
||||
// index defines an index of transactions.
|
||||
index sdkmempool.Mempool
|
||||
|
||||
// txPriority defines the transaction priority function. It is used to
|
||||
// retrieve the priority of a given transaction and to compare the priority
|
||||
// of two transactions. The index utilizes this struct to order transactions
|
||||
// in the mempool.
|
||||
txPriority TxPriority[C]
|
||||
|
||||
// txEncoder defines the sdk.Tx encoder that allows us to encode transactions
|
||||
// to bytes.
|
||||
txEncoder sdk.TxEncoder
|
||||
|
||||
// txCache is a map of all transactions in the mempool. It is used
|
||||
// to quickly check if a transaction is already in the mempool.
|
||||
txCache map[string]struct{}
|
||||
}
|
||||
)
|
||||
|
||||
// DefaultTxPriority returns a default implementation of the TxPriority. It prioritizes
|
||||
// transactions by their fee.
|
||||
func DefaultTxPriority() TxPriority[string] {
|
||||
return TxPriority[string]{
|
||||
GetTxPriority: func(goCtx context.Context, tx sdk.Tx) string {
|
||||
feeTx, ok := tx.(sdk.FeeTx)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
return feeTx.GetFee().String()
|
||||
},
|
||||
Compare: func(a, b string) int {
|
||||
aCoins, _ := sdk.ParseCoinsNormalized(a)
|
||||
bCoins, _ := sdk.ParseCoinsNormalized(b)
|
||||
|
||||
switch {
|
||||
case aCoins == nil && bCoins == nil:
|
||||
return 0
|
||||
|
||||
case aCoins == nil:
|
||||
return -1
|
||||
|
||||
case bCoins == nil:
|
||||
return 1
|
||||
|
||||
default:
|
||||
switch {
|
||||
case aCoins.IsAllGT(bCoins):
|
||||
return 1
|
||||
|
||||
case aCoins.IsAllLT(bCoins):
|
||||
return -1
|
||||
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
},
|
||||
MinValue: "",
|
||||
}
|
||||
}
|
||||
|
||||
// NewConstructorMempool returns a new ConstructorMempool.
|
||||
func NewConstructorMempool[C comparable](txPriority TxPriority[C], txEncoder sdk.TxEncoder, maxTx int) *ConstructorMempool[C] {
|
||||
return &ConstructorMempool[C]{
|
||||
index: NewPriorityMempool(
|
||||
PriorityNonceMempoolConfig[C]{
|
||||
TxPriority: txPriority,
|
||||
MaxTx: maxTx,
|
||||
},
|
||||
),
|
||||
txPriority: txPriority,
|
||||
txEncoder: txEncoder,
|
||||
txCache: make(map[string]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Insert inserts a transaction into the mempool.
|
||||
func (cm *ConstructorMempool[C]) Insert(ctx context.Context, tx sdk.Tx) error {
|
||||
if err := cm.index.Insert(ctx, tx); err != nil {
|
||||
return fmt.Errorf("failed to insert tx into auction index: %w", err)
|
||||
}
|
||||
|
||||
_, txHashStr, err := utils.GetTxHashStr(cm.txEncoder, tx)
|
||||
if err != nil {
|
||||
cm.Remove(tx)
|
||||
return err
|
||||
}
|
||||
|
||||
cm.txCache[txHashStr] = struct{}{}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes a transaction from the mempool.
|
||||
func (cm *ConstructorMempool[C]) Remove(tx sdk.Tx) error {
|
||||
if err := cm.index.Remove(tx); err != nil && !errors.Is(err, sdkmempool.ErrTxNotFound) {
|
||||
return fmt.Errorf("failed to remove transaction from the mempool: %w", err)
|
||||
}
|
||||
|
||||
_, txHashStr, err := utils.GetTxHashStr(cm.txEncoder, tx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get tx hash string: %w", err)
|
||||
}
|
||||
|
||||
delete(cm.txCache, txHashStr)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Select returns an iterator of all transactions in the mempool. NOTE: If you
|
||||
// remove a transaction from the mempool while iterating over the transactions,
|
||||
// the iterator will not be aware of the removal and will continue to iterate
|
||||
// over the removed transaction. Be sure to reset the iterator if you remove a transaction.
|
||||
func (cm *ConstructorMempool[C]) Select(ctx context.Context, txs [][]byte) sdkmempool.Iterator {
|
||||
return cm.index.Select(ctx, txs)
|
||||
}
|
||||
|
||||
// CountTx returns the number of transactions in the mempool.
|
||||
func (cm *ConstructorMempool[C]) CountTx() int {
|
||||
return cm.index.CountTx()
|
||||
}
|
||||
|
||||
// Contains returns true if the transaction is contained in the mempool.
|
||||
func (cm *ConstructorMempool[C]) Contains(tx sdk.Tx) bool {
|
||||
_, txHashStr, err := utils.GetTxHashStr(cm.txEncoder, tx)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
_, ok := cm.txCache[txHashStr]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Compare determines the relative priority of two transactions belonging in the same lane.
|
||||
func (cm *ConstructorMempool[C]) Compare(ctx sdk.Context, this sdk.Tx, other sdk.Tx) int {
|
||||
firstPriority := cm.txPriority.GetTxPriority(ctx, this)
|
||||
secondPriority := cm.txPriority.GetTxPriority(ctx, other)
|
||||
return cm.txPriority.Compare(firstPriority, secondPriority)
|
||||
}
|
||||
@ -7,257 +7,247 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/skip-mev/pob/blockbuster"
|
||||
"github.com/skip-mev/pob/blockbuster/utils"
|
||||
"github.com/skip-mev/pob/x/builder/types"
|
||||
)
|
||||
|
||||
// PrepareLane will attempt to select the highest bid transaction that is valid
|
||||
// PrepareLaneHandler will attempt to select the highest bid transaction that is valid
|
||||
// and whose bundled transactions are valid and include them in the proposal. It
|
||||
// will return an empty partial proposal if no valid bids are found.
|
||||
func (l *TOBLane) PrepareLane(
|
||||
ctx sdk.Context,
|
||||
proposal blockbuster.BlockProposal,
|
||||
maxTxBytes int64,
|
||||
next blockbuster.PrepareLanesHandler,
|
||||
) (blockbuster.BlockProposal, error) {
|
||||
// Define all of the info we need to select transactions for the partial proposal.
|
||||
var (
|
||||
txs [][]byte
|
||||
txsToRemove = make(map[sdk.Tx]struct{}, 0)
|
||||
)
|
||||
// will return no transactions if no valid bids are found. If any of the bids are invalid,
|
||||
// it will return them and will only remove the bids and not the bundled transactions.
|
||||
func (l *TOBLane) PrepareLaneHandler() blockbuster.PrepareLaneHandler {
|
||||
return func(ctx sdk.Context, proposal blockbuster.BlockProposal, maxTxBytes int64) ([][]byte, []sdk.Tx, error) {
|
||||
// Define all of the info we need to select transactions for the partial proposal.
|
||||
var (
|
||||
txs [][]byte
|
||||
txsToRemove []sdk.Tx
|
||||
)
|
||||
|
||||
// Attempt to select the highest bid transaction that is valid and whose
|
||||
// bundled transactions are valid.
|
||||
bidTxIterator := l.Select(ctx, nil)
|
||||
selectBidTxLoop:
|
||||
for ; bidTxIterator != nil; bidTxIterator = bidTxIterator.Next() {
|
||||
cacheCtx, write := ctx.CacheContext()
|
||||
tmpBidTx := bidTxIterator.Tx()
|
||||
// Attempt to select the highest bid transaction that is valid and whose
|
||||
// bundled transactions are valid.
|
||||
bidTxIterator := l.Select(ctx, nil)
|
||||
selectBidTxLoop:
|
||||
for ; bidTxIterator != nil; bidTxIterator = bidTxIterator.Next() {
|
||||
cacheCtx, write := ctx.CacheContext()
|
||||
tmpBidTx := bidTxIterator.Tx()
|
||||
|
||||
bidTxBz, hash, err := utils.GetTxHashStr(l.Cfg.TxEncoder, tmpBidTx)
|
||||
if err != nil {
|
||||
l.Logger().Info("failed to get hash of auction bid tx", "err", err)
|
||||
bidTxBz, hash, err := utils.GetTxHashStr(l.TxEncoder(), tmpBidTx)
|
||||
if err != nil {
|
||||
l.Logger().Info("failed to get hash of auction bid tx", "err", err)
|
||||
|
||||
txsToRemove[tmpBidTx] = struct{}{}
|
||||
continue selectBidTxLoop
|
||||
}
|
||||
txsToRemove = append(txsToRemove, tmpBidTx)
|
||||
continue selectBidTxLoop
|
||||
}
|
||||
|
||||
// if the transaction is already in the (partial) block proposal, we skip it.
|
||||
if proposal.Contains(bidTxBz) {
|
||||
l.Logger().Info(
|
||||
"failed to select auction bid tx for lane; tx is already in proposal",
|
||||
"tx_hash", hash,
|
||||
)
|
||||
|
||||
continue selectBidTxLoop
|
||||
}
|
||||
|
||||
bidTxSize := int64(len(bidTxBz))
|
||||
if bidTxSize <= maxTxBytes {
|
||||
// Build the partial proposal by selecting the bid transaction and all of
|
||||
// its bundled transactions.
|
||||
bidInfo, err := l.GetAuctionBidInfo(tmpBidTx)
|
||||
if err != nil {
|
||||
l.Logger().Info(
|
||||
"failed to get auction bid info",
|
||||
"tx_hash", hash,
|
||||
"err", err,
|
||||
)
|
||||
|
||||
// Some transactions in the bundle may be malformed or invalid, so we
|
||||
// remove the bid transaction and try the next top bid.
|
||||
txsToRemove = append(txsToRemove, tmpBidTx)
|
||||
continue selectBidTxLoop
|
||||
}
|
||||
|
||||
// Verify the bid transaction and all of its bundled transactions.
|
||||
if err := l.VerifyTx(cacheCtx, tmpBidTx, bidInfo); err != nil {
|
||||
l.Logger().Info(
|
||||
"failed to verify auction bid tx",
|
||||
"tx_hash", hash,
|
||||
"err", err,
|
||||
)
|
||||
|
||||
txsToRemove = append(txsToRemove, tmpBidTx)
|
||||
continue selectBidTxLoop
|
||||
}
|
||||
|
||||
// store the bytes of each ref tx as sdk.Tx bytes in order to build a valid proposal
|
||||
bundledTxBz := make([][]byte, len(bidInfo.Transactions))
|
||||
for index, rawRefTx := range bidInfo.Transactions {
|
||||
sdkTx, err := l.WrapBundleTransaction(rawRefTx)
|
||||
if err != nil {
|
||||
l.Logger().Info(
|
||||
"failed to wrap bundled tx",
|
||||
"tx_hash", hash,
|
||||
"err", err,
|
||||
)
|
||||
|
||||
txsToRemove = append(txsToRemove, tmpBidTx)
|
||||
continue selectBidTxLoop
|
||||
}
|
||||
|
||||
sdkTxBz, _, err := utils.GetTxHashStr(l.TxEncoder(), sdkTx)
|
||||
if err != nil {
|
||||
l.Logger().Info(
|
||||
"failed to get hash of bundled tx",
|
||||
"tx_hash", hash,
|
||||
"err", err,
|
||||
)
|
||||
|
||||
txsToRemove = append(txsToRemove, tmpBidTx)
|
||||
continue selectBidTxLoop
|
||||
}
|
||||
|
||||
// if the transaction is already in the (partial) block proposal, we skip it.
|
||||
if proposal.Contains(sdkTxBz) {
|
||||
l.Logger().Info(
|
||||
"failed to select auction bid tx for lane; tx is already in proposal",
|
||||
"tx_hash", hash,
|
||||
)
|
||||
|
||||
continue selectBidTxLoop
|
||||
}
|
||||
|
||||
bundleTxBz := make([]byte, len(sdkTxBz))
|
||||
copy(bundleTxBz, sdkTxBz)
|
||||
bundledTxBz[index] = sdkTxBz
|
||||
}
|
||||
|
||||
// At this point, both the bid transaction itself and all the bundled
|
||||
// transactions are valid. So we select the bid transaction along with
|
||||
// all the bundled transactions. We also mark these transactions as seen and
|
||||
// update the total size selected thus far.
|
||||
txs = append(txs, bidTxBz)
|
||||
txs = append(txs, bundledTxBz...)
|
||||
|
||||
// Write the cache context to the original context when we know we have a
|
||||
// valid top of block bundle.
|
||||
write()
|
||||
|
||||
break selectBidTxLoop
|
||||
}
|
||||
|
||||
// if the transaction is already in the (partial) block proposal, we skip it.
|
||||
if proposal.Contains(bidTxBz) {
|
||||
l.Logger().Info(
|
||||
"failed to select auction bid tx for lane; tx is already in proposal",
|
||||
"tx_hash", hash,
|
||||
"failed to select auction bid tx for lane; tx size is too large",
|
||||
"tx_size", bidTxSize,
|
||||
"max_size", maxTxBytes,
|
||||
)
|
||||
|
||||
continue selectBidTxLoop
|
||||
}
|
||||
|
||||
bidTxSize := int64(len(bidTxBz))
|
||||
if bidTxSize <= maxTxBytes {
|
||||
// Verify the bid transaction and all of its bundled transactions.
|
||||
if err := l.VerifyTx(cacheCtx, tmpBidTx); err != nil {
|
||||
l.Logger().Info(
|
||||
"failed to verify auction bid tx",
|
||||
"tx_hash", hash,
|
||||
"err", err,
|
||||
)
|
||||
|
||||
txsToRemove[tmpBidTx] = struct{}{}
|
||||
continue selectBidTxLoop
|
||||
}
|
||||
|
||||
// Build the partial proposal by selecting the bid transaction and all of
|
||||
// its bundled transactions.
|
||||
bidInfo, err := l.GetAuctionBidInfo(tmpBidTx)
|
||||
if bidInfo == nil || err != nil {
|
||||
l.Logger().Info(
|
||||
"failed to get auction bid info",
|
||||
"tx_hash", hash,
|
||||
"err", err,
|
||||
)
|
||||
|
||||
// Some transactions in the bundle may be malformed or invalid, so we
|
||||
// remove the bid transaction and try the next top bid.
|
||||
txsToRemove[tmpBidTx] = struct{}{}
|
||||
continue selectBidTxLoop
|
||||
}
|
||||
|
||||
// store the bytes of each ref tx as sdk.Tx bytes in order to build a valid proposal
|
||||
bundledTxBz := make([][]byte, len(bidInfo.Transactions))
|
||||
for index, rawRefTx := range bidInfo.Transactions {
|
||||
sdkTx, err := l.WrapBundleTransaction(rawRefTx)
|
||||
if err != nil {
|
||||
l.Logger().Info(
|
||||
"failed to wrap bundled tx",
|
||||
"tx_hash", hash,
|
||||
"err", err,
|
||||
)
|
||||
|
||||
txsToRemove[tmpBidTx] = struct{}{}
|
||||
continue selectBidTxLoop
|
||||
}
|
||||
|
||||
sdkTxBz, _, err := utils.GetTxHashStr(l.Cfg.TxEncoder, sdkTx)
|
||||
if err != nil {
|
||||
l.Logger().Info(
|
||||
"failed to get hash of bundled tx",
|
||||
"tx_hash", hash,
|
||||
"err", err,
|
||||
)
|
||||
|
||||
txsToRemove[tmpBidTx] = struct{}{}
|
||||
continue selectBidTxLoop
|
||||
}
|
||||
|
||||
// if the transaction is already in the (partial) block proposal, we skip it.
|
||||
if proposal.Contains(sdkTxBz) {
|
||||
l.Logger().Info(
|
||||
"failed to select auction bid tx for lane; tx is already in proposal",
|
||||
"tx_hash", hash,
|
||||
)
|
||||
|
||||
continue selectBidTxLoop
|
||||
}
|
||||
|
||||
bundleTxBz := make([]byte, len(sdkTxBz))
|
||||
copy(bundleTxBz, sdkTxBz)
|
||||
bundledTxBz[index] = sdkTxBz
|
||||
}
|
||||
|
||||
// At this point, both the bid transaction itself and all the bundled
|
||||
// transactions are valid. So we select the bid transaction along with
|
||||
// all the bundled transactions. We also mark these transactions as seen and
|
||||
// update the total size selected thus far.
|
||||
txs = append(txs, bidTxBz)
|
||||
txs = append(txs, bundledTxBz...)
|
||||
|
||||
// Write the cache context to the original context when we know we have a
|
||||
// valid top of block bundle.
|
||||
write()
|
||||
|
||||
break selectBidTxLoop
|
||||
}
|
||||
|
||||
l.Logger().Info(
|
||||
"failed to select auction bid tx for lane; tx size is too large",
|
||||
"tx_size", bidTxSize,
|
||||
"max_size", maxTxBytes,
|
||||
)
|
||||
return txs, txsToRemove, nil
|
||||
}
|
||||
|
||||
// Remove all transactions that were invalid during the creation of the partial proposal.
|
||||
if err := utils.RemoveTxsFromLane(txsToRemove, l.Mempool); err != nil {
|
||||
l.Logger().Error(
|
||||
"failed to remove transactions from lane",
|
||||
"lane", l.Name(),
|
||||
"err", err,
|
||||
)
|
||||
|
||||
return proposal, err
|
||||
}
|
||||
|
||||
// Update the proposal with the selected transactions. This will only return an error
|
||||
// if the invarient checks are not passed. In the case when this errors, the original proposal
|
||||
// will be returned (without the selected transactions from this lane).
|
||||
if err := proposal.UpdateProposal(l, txs); err != nil {
|
||||
return proposal, err
|
||||
}
|
||||
|
||||
return next(ctx, proposal)
|
||||
}
|
||||
|
||||
// ProcessLane will ensure that block proposals that include transactions from
|
||||
// ProcessLaneHandler will ensure that block proposals that include transactions from
|
||||
// the top-of-block auction lane are valid.
|
||||
func (l *TOBLane) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next blockbuster.ProcessLanesHandler) (sdk.Context, error) {
|
||||
if len(txs) == 0 {
|
||||
return next(ctx, txs)
|
||||
}
|
||||
func (l *TOBLane) ProcessLaneHandler() blockbuster.ProcessLaneHandler {
|
||||
return func(ctx sdk.Context, txs []sdk.Tx) ([]sdk.Tx, error) {
|
||||
if len(txs) == 0 {
|
||||
return txs, nil
|
||||
}
|
||||
|
||||
bidTx := txs[0]
|
||||
if !l.Match(ctx, bidTx) {
|
||||
return next(ctx, txs)
|
||||
}
|
||||
bidTx := txs[0]
|
||||
if !l.Match(ctx, bidTx) {
|
||||
return txs, nil
|
||||
}
|
||||
|
||||
bidInfo, err := l.GetAuctionBidInfo(bidTx)
|
||||
if err != nil {
|
||||
return ctx, fmt.Errorf("failed to get bid info for lane %s: %w", l.Name(), err)
|
||||
}
|
||||
bidInfo, err := l.GetAuctionBidInfo(bidTx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get bid info for lane %s: %w", l.Name(), err)
|
||||
}
|
||||
|
||||
if err := l.VerifyTx(ctx, bidTx); err != nil {
|
||||
return ctx, fmt.Errorf("invalid bid tx: %w", err)
|
||||
}
|
||||
if err := l.VerifyTx(ctx, bidTx, bidInfo); err != nil {
|
||||
return nil, fmt.Errorf("invalid bid tx: %w", err)
|
||||
}
|
||||
|
||||
return next(ctx, txs[len(bidInfo.Transactions)+1:])
|
||||
return txs[len(bidInfo.Transactions)+1:], nil
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessLaneBasic ensures that if a bid transaction is present in a proposal,
|
||||
// CheckOrderHandler ensures that if a bid transaction is present in a proposal,
|
||||
// - it is the first transaction in the partial proposal
|
||||
// - all of the bundled transactions are included after the bid transaction in the order
|
||||
// they were included in the bid transaction.
|
||||
// - there are no other bid transactions in the proposal
|
||||
func (l *TOBLane) ProcessLaneBasic(ctx sdk.Context, txs []sdk.Tx) error {
|
||||
if len(txs) == 0 {
|
||||
return nil
|
||||
}
|
||||
// - transactions from other lanes are not interleaved with transactions from the bid
|
||||
// transaction.
|
||||
func (l *TOBLane) CheckOrderHandler() blockbuster.CheckOrderHandler {
|
||||
return func(ctx sdk.Context, txs []sdk.Tx) error {
|
||||
if len(txs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
bidTx := txs[0]
|
||||
bidTx := txs[0]
|
||||
|
||||
// If there is a bid transaction, it must be the first transaction in the block proposal.
|
||||
if !l.Match(ctx, bidTx) {
|
||||
for _, tx := range txs[1:] {
|
||||
// If there is a bid transaction, it must be the first transaction in the block proposal.
|
||||
if !l.Match(ctx, bidTx) {
|
||||
for _, tx := range txs[1:] {
|
||||
if l.Match(ctx, tx) {
|
||||
return fmt.Errorf("misplaced bid transactions in lane %s", l.Name())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
bidInfo, err := l.GetAuctionBidInfo(bidTx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get bid info for lane %s: %w", l.Name(), err)
|
||||
}
|
||||
|
||||
if len(txs) < len(bidInfo.Transactions)+1 {
|
||||
return fmt.Errorf(
|
||||
"invalid number of transactions in lane %s; expected at least %d, got %d",
|
||||
l.Name(),
|
||||
len(bidInfo.Transactions)+1,
|
||||
len(txs),
|
||||
)
|
||||
}
|
||||
|
||||
// Ensure that the order of transactions in the bundle is preserved.
|
||||
for i, bundleTx := range txs[1 : len(bidInfo.Transactions)+1] {
|
||||
if l.Match(ctx, bundleTx) {
|
||||
return fmt.Errorf("multiple bid transactions in lane %s", l.Name())
|
||||
}
|
||||
|
||||
txBz, err := l.TxEncoder()(bundleTx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encode bundled tx in lane %s: %w", l.Name(), err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(txBz, bidInfo.Transactions[i]) {
|
||||
return fmt.Errorf("invalid order of transactions in lane %s", l.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that there are no more bid transactions in the block proposal.
|
||||
for _, tx := range txs[len(bidInfo.Transactions)+1:] {
|
||||
if l.Match(ctx, tx) {
|
||||
return fmt.Errorf("misplaced bid transactions in lane %s", l.Name())
|
||||
return fmt.Errorf("multiple bid transactions in lane %s", l.Name())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
bidInfo, err := l.GetAuctionBidInfo(bidTx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get bid info for lane %s: %w", l.Name(), err)
|
||||
}
|
||||
|
||||
if len(txs) < len(bidInfo.Transactions)+1 {
|
||||
return fmt.Errorf("invalid number of transactions in lane %s; expected at least %d, got %d", l.Name(), len(bidInfo.Transactions)+1, len(txs))
|
||||
}
|
||||
|
||||
// Ensure that the order of transactions in the bundle is preserved.
|
||||
for i, bundleTx := range txs[1 : len(bidInfo.Transactions)+1] {
|
||||
if l.Match(ctx, bundleTx) {
|
||||
return fmt.Errorf("multiple bid transactions in lane %s", l.Name())
|
||||
}
|
||||
|
||||
txBz, err := l.Cfg.TxEncoder(bundleTx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encode bundled tx in lane %s: %w", l.Name(), err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(txBz, bidInfo.Transactions[i]) {
|
||||
return fmt.Errorf("invalid order of transactions in lane %s", l.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that there are no more bid transactions in the block proposal.
|
||||
for _, tx := range txs[len(bidInfo.Transactions)+1:] {
|
||||
if l.Match(ctx, tx) {
|
||||
return fmt.Errorf("multiple bid transactions in lane %s", l.Name())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyTx will verify that the bid transaction and all of its bundled
|
||||
// transactions are valid. It will return an error if any of the transactions
|
||||
// are invalid.
|
||||
func (l *TOBLane) VerifyTx(ctx sdk.Context, bidTx sdk.Tx) error {
|
||||
bidInfo, err := l.GetAuctionBidInfo(bidTx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get auction bid info: %w", err)
|
||||
func (l *TOBLane) VerifyTx(ctx sdk.Context, bidTx sdk.Tx, bidInfo *types.BidInfo) (err error) {
|
||||
if bidInfo == nil {
|
||||
return fmt.Errorf("bid info is nil")
|
||||
}
|
||||
|
||||
// verify the top-level bid transaction
|
||||
ctx, err = l.verifyTx(ctx, bidTx)
|
||||
if err != nil {
|
||||
if ctx, err = l.AnteVerifyTx(ctx, bidTx, false); err != nil {
|
||||
return fmt.Errorf("invalid bid tx; failed to execute ante handler: %w", err)
|
||||
}
|
||||
|
||||
@ -268,27 +258,10 @@ func (l *TOBLane) VerifyTx(ctx sdk.Context, bidTx sdk.Tx) error {
|
||||
return fmt.Errorf("invalid bid tx; failed to decode bundled tx: %w", err)
|
||||
}
|
||||
|
||||
// bid txs cannot be included in bundled txs
|
||||
bidInfo, _ := l.GetAuctionBidInfo(bundledTx)
|
||||
if bidInfo != nil {
|
||||
return fmt.Errorf("invalid bid tx; bundled tx cannot be a bid tx")
|
||||
}
|
||||
|
||||
if ctx, err = l.verifyTx(ctx, bundledTx); err != nil {
|
||||
if ctx, err = l.AnteVerifyTx(ctx, bundledTx, false); err != nil {
|
||||
return fmt.Errorf("invalid bid tx; failed to execute bundled transaction: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyTx will execute the ante handler on the transaction and return the
|
||||
// resulting context and error.
|
||||
func (l *TOBLane) verifyTx(ctx sdk.Context, tx sdk.Tx) (sdk.Context, error) {
|
||||
if l.Cfg.AnteHandler != nil {
|
||||
newCtx, err := l.Cfg.AnteHandler(ctx, tx, false)
|
||||
return newCtx, err
|
||||
}
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
@ -18,7 +18,6 @@ type IntegrationTestSuite struct {
|
||||
|
||||
encCfg testutils.EncodingConfig
|
||||
config auction.Factory
|
||||
mempool auction.Mempool
|
||||
ctx sdk.Context
|
||||
random *rand.Rand
|
||||
accounts []testutils.Account
|
||||
@ -33,7 +32,6 @@ func (suite *IntegrationTestSuite) SetupTest() {
|
||||
// Mempool setup
|
||||
suite.encCfg = testutils.CreateTestEncodingConfig()
|
||||
suite.config = auction.NewDefaultAuctionFactory(suite.encCfg.TxConfig.TxDecoder())
|
||||
suite.mempool = auction.NewMempool(suite.encCfg.TxConfig.TxEncoder(), 0, suite.config)
|
||||
suite.ctx = sdk.NewContext(nil, cmtproto.Header{}, false, log.NewTestLogger(suite.T()))
|
||||
|
||||
// Init accounts
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package abci
|
||||
package auction
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
log "cosmossdk.io/log"
|
||||
@ -29,7 +28,7 @@ type (
|
||||
|
||||
// TOBLane is utilized to retrieve the bid info of a transaction and to
|
||||
// insert a bid transaction into the application-side mempool.
|
||||
tobLane TOBLane
|
||||
tobLane TOBLaneI
|
||||
|
||||
// anteHandler is utilized to verify the bid transaction against the latest
|
||||
// committed state.
|
||||
@ -40,20 +39,6 @@ type (
|
||||
// transaction.
|
||||
CheckTx func(req *cometabci.RequestCheckTx) (*cometabci.ResponseCheckTx, error)
|
||||
|
||||
// TOBLane is the interface that defines all of the dependencies that
|
||||
// are required to interact with the top of block lane.
|
||||
TOBLane interface {
|
||||
// GetAuctionBidInfo is utilized to retrieve the bid info of a transaction.
|
||||
GetAuctionBidInfo(tx sdk.Tx) (*types.BidInfo, 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 {
|
||||
@ -82,7 +67,7 @@ type (
|
||||
func NewCheckTxHandler(
|
||||
baseApp BaseApp,
|
||||
txDecoder sdk.TxDecoder,
|
||||
tobLane TOBLane,
|
||||
tobLane TOBLaneI,
|
||||
anteHandler sdk.AnteHandler,
|
||||
) *CheckTxHandler {
|
||||
return &CheckTxHandler{
|
||||
@ -5,6 +5,7 @@ import (
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
"github.com/skip-mev/pob/blockbuster"
|
||||
"github.com/skip-mev/pob/x/builder/types"
|
||||
)
|
||||
|
||||
@ -21,6 +22,9 @@ type (
|
||||
|
||||
// GetAuctionBidInfo defines a function that returns the bid info from an auction transaction.
|
||||
GetAuctionBidInfo(tx sdk.Tx) (*types.BidInfo, error)
|
||||
|
||||
// MatchHandler defines a function that checks if a transaction matches the auction lane.
|
||||
MatchHandler() blockbuster.MatchHandler
|
||||
}
|
||||
|
||||
// DefaultAuctionFactory defines a default implmentation for the auction factory interface for processing auction transactions.
|
||||
@ -91,6 +95,13 @@ func (config *DefaultAuctionFactory) GetAuctionBidInfo(tx sdk.Tx) (*types.BidInf
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (config *DefaultAuctionFactory) MatchHandler() blockbuster.MatchHandler {
|
||||
return func(ctx sdk.Context, tx sdk.Tx) bool {
|
||||
bidInfo, err := config.GetAuctionBidInfo(tx)
|
||||
return bidInfo != nil && err == nil
|
||||
}
|
||||
}
|
||||
|
||||
// getBundleSigners defines a default function that returns the signers of all transactions in
|
||||
// a bundle. In the default case, each bundle transaction will be an sdk.Tx and the
|
||||
// signers are the signers of each sdk.Msg in the transaction.
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
package auction
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/skip-mev/pob/blockbuster"
|
||||
"github.com/skip-mev/pob/blockbuster/lanes/base"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -11,10 +12,7 @@ const (
|
||||
LaneName = "top-of-block"
|
||||
)
|
||||
|
||||
var (
|
||||
_ blockbuster.Lane = (*TOBLane)(nil)
|
||||
_ Factory = (*TOBLane)(nil)
|
||||
)
|
||||
var _ TOBLaneI = (*TOBLane)(nil)
|
||||
|
||||
// TOBLane defines a top-of-block auction lane. The top of block auction lane
|
||||
// hosts transactions that want to bid for inclusion at the top of the next block.
|
||||
@ -22,43 +20,53 @@ var (
|
||||
// their bid price. The highest valid bid transaction is selected for inclusion in the
|
||||
// next block. The bundled transactions of the selected bid transaction are also
|
||||
// included in the next block.
|
||||
type TOBLane struct {
|
||||
// Mempool defines the mempool for the lane.
|
||||
Mempool
|
||||
type (
|
||||
// TOBLaneI defines the interface for the top-of-block auction lane. This interface
|
||||
// is utilized by both the x/builder module and the checkTx handler.
|
||||
TOBLaneI interface {
|
||||
blockbuster.Lane
|
||||
Factory
|
||||
GetTopAuctionTx(ctx context.Context) sdk.Tx
|
||||
}
|
||||
|
||||
// LaneConfig defines the base lane configuration.
|
||||
*base.DefaultLane
|
||||
TOBLane struct {
|
||||
// LaneConfig defines the base lane configuration.
|
||||
*blockbuster.LaneConstructor
|
||||
|
||||
// Factory defines the API/functionality which is responsible for determining
|
||||
// if a transaction is a bid transaction and how to extract relevant
|
||||
// information from the transaction (bid, timeout, bidder, etc.).
|
||||
Factory
|
||||
}
|
||||
// Factory defines the API/functionality which is responsible for determining
|
||||
// if a transaction is a bid transaction and how to extract relevant
|
||||
// information from the transaction (bid, timeout, bidder, etc.).
|
||||
Factory
|
||||
}
|
||||
)
|
||||
|
||||
// NewTOBLane returns a new TOB lane.
|
||||
func NewTOBLane(
|
||||
cfg blockbuster.BaseLaneConfig,
|
||||
maxTx int,
|
||||
af Factory,
|
||||
cfg blockbuster.LaneConfig,
|
||||
factory Factory,
|
||||
) *TOBLane {
|
||||
if err := cfg.ValidateBasic(); err != nil {
|
||||
panic(err)
|
||||
lane := &TOBLane{
|
||||
LaneConstructor: blockbuster.NewLaneConstructor(
|
||||
cfg,
|
||||
LaneName,
|
||||
blockbuster.NewConstructorMempool[string](
|
||||
TxPriority(factory),
|
||||
cfg.TxEncoder,
|
||||
cfg.MaxTxs,
|
||||
),
|
||||
factory.MatchHandler(),
|
||||
),
|
||||
Factory: factory,
|
||||
}
|
||||
|
||||
return &TOBLane{
|
||||
Mempool: NewMempool(cfg.TxEncoder, maxTx, af),
|
||||
DefaultLane: base.NewDefaultLane(cfg).WithName(LaneName),
|
||||
Factory: af,
|
||||
}
|
||||
}
|
||||
|
||||
// Match returns true if the transaction is a bid transaction. This is determined
|
||||
// by the AuctionFactory.
|
||||
func (l *TOBLane) Match(ctx sdk.Context, tx sdk.Tx) bool {
|
||||
if l.MatchIgnoreList(ctx, tx) {
|
||||
return false
|
||||
}
|
||||
|
||||
bidInfo, err := l.GetAuctionBidInfo(tx)
|
||||
return bidInfo != nil && err == nil
|
||||
// Set the prepare lane handler to the TOB one
|
||||
lane.SetPrepareLaneHandler(lane.PrepareLaneHandler())
|
||||
|
||||
// Set the process lane handler to the TOB one
|
||||
lane.SetProcessLaneHandler(lane.ProcessLaneHandler())
|
||||
|
||||
// Set the check order handler to the TOB one
|
||||
lane.SetCheckOrderHandler(lane.CheckOrderHandler())
|
||||
|
||||
return lane
|
||||
}
|
||||
|
||||
@ -2,48 +2,9 @@ package auction
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool"
|
||||
"github.com/skip-mev/pob/blockbuster"
|
||||
"github.com/skip-mev/pob/blockbuster/utils"
|
||||
)
|
||||
|
||||
var _ Mempool = (*TOBMempool)(nil)
|
||||
|
||||
type (
|
||||
// Mempool defines the interface of the auction mempool.
|
||||
Mempool interface {
|
||||
sdkmempool.Mempool
|
||||
|
||||
// GetTopAuctionTx returns the highest bidding transaction in the auction mempool.
|
||||
GetTopAuctionTx(ctx context.Context) sdk.Tx
|
||||
|
||||
// Contains returns true if the transaction is contained in the mempool.
|
||||
Contains(tx sdk.Tx) bool
|
||||
}
|
||||
|
||||
// TOBMempool defines an auction mempool. It can be seen as an extension of
|
||||
// an SDK PriorityNonceMempool, i.e. a mempool that supports <sender, nonce>
|
||||
// two-dimensional priority ordering, with the additional support of prioritizing
|
||||
// and indexing auction bids.
|
||||
TOBMempool struct {
|
||||
// index defines an index of auction bids.
|
||||
index sdkmempool.Mempool
|
||||
|
||||
// txEncoder defines the sdk.Tx encoder that allows us to encode transactions
|
||||
// to bytes.
|
||||
txEncoder sdk.TxEncoder
|
||||
|
||||
// txIndex is a map of all transactions in the mempool. It is used
|
||||
// to quickly check if a transaction is already in the mempool.
|
||||
txIndex map[string]struct{}
|
||||
|
||||
// Factory implements the functionality required to process auction transactions.
|
||||
Factory
|
||||
}
|
||||
)
|
||||
|
||||
// TxPriority returns a TxPriority over auction bid transactions only. It
|
||||
@ -89,78 +50,13 @@ func TxPriority(config Factory) blockbuster.TxPriority[string] {
|
||||
}
|
||||
}
|
||||
|
||||
// NewMempool returns a new auction mempool.
|
||||
func NewMempool(txEncoder sdk.TxEncoder, maxTx int, config Factory) *TOBMempool {
|
||||
return &TOBMempool{
|
||||
index: blockbuster.NewPriorityMempool(
|
||||
blockbuster.PriorityNonceMempoolConfig[string]{
|
||||
TxPriority: TxPriority(config),
|
||||
MaxTx: maxTx,
|
||||
},
|
||||
),
|
||||
txEncoder: txEncoder,
|
||||
txIndex: make(map[string]struct{}),
|
||||
Factory: config,
|
||||
}
|
||||
}
|
||||
|
||||
// Insert inserts a transaction into the auction mempool.
|
||||
func (am *TOBMempool) Insert(ctx context.Context, tx sdk.Tx) error {
|
||||
if err := am.index.Insert(ctx, tx); err != nil {
|
||||
return fmt.Errorf("failed to insert tx into auction index: %w", err)
|
||||
}
|
||||
|
||||
_, txHashStr, err := utils.GetTxHashStr(am.txEncoder, tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
am.txIndex[txHashStr] = struct{}{}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes a transaction from the mempool based.
|
||||
func (am *TOBMempool) Remove(tx sdk.Tx) error {
|
||||
if err := am.index.Remove(tx); err != nil && !errors.Is(err, sdkmempool.ErrTxNotFound) {
|
||||
return fmt.Errorf("failed to remove invalid transaction from the mempool: %w", err)
|
||||
}
|
||||
|
||||
_, txHashStr, err := utils.GetTxHashStr(am.txEncoder, tx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get tx hash string: %w", err)
|
||||
}
|
||||
|
||||
delete(am.txIndex, txHashStr)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTopAuctionTx returns the highest bidding transaction in the auction mempool.
|
||||
func (am *TOBMempool) GetTopAuctionTx(ctx context.Context) sdk.Tx {
|
||||
iterator := am.index.Select(ctx, nil)
|
||||
// This is primarily a helper function for the x/builder module.
|
||||
func (l *TOBLane) GetTopAuctionTx(ctx context.Context) sdk.Tx {
|
||||
iterator := l.Select(ctx, nil)
|
||||
if iterator == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return iterator.Tx()
|
||||
}
|
||||
|
||||
func (am *TOBMempool) Select(ctx context.Context, txs [][]byte) sdkmempool.Iterator {
|
||||
return am.index.Select(ctx, txs)
|
||||
}
|
||||
|
||||
func (am *TOBMempool) CountTx() int {
|
||||
return am.index.CountTx()
|
||||
}
|
||||
|
||||
// Contains returns true if the transaction is contained in the mempool.
|
||||
func (am *TOBMempool) Contains(tx sdk.Tx) bool {
|
||||
_, txHashStr, err := utils.GetTxHashStr(am.txEncoder, tx)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
_, ok := am.txIndex[txHashStr]
|
||||
return ok
|
||||
}
|
||||
|
||||
@ -1,142 +0,0 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/skip-mev/pob/blockbuster"
|
||||
"github.com/skip-mev/pob/blockbuster/utils"
|
||||
)
|
||||
|
||||
// PrepareLane will prepare a partial proposal for the default lane. It will select and include
|
||||
// all valid transactions in the mempool that are not already in the partial proposal.
|
||||
// The default lane orders transactions by the sdk.Context priority.
|
||||
func (l *DefaultLane) PrepareLane(
|
||||
ctx sdk.Context,
|
||||
proposal blockbuster.BlockProposal,
|
||||
maxTxBytes int64,
|
||||
next blockbuster.PrepareLanesHandler,
|
||||
) (blockbuster.BlockProposal, error) {
|
||||
// Define all of the info we need to select transactions for the partial proposal.
|
||||
var (
|
||||
totalSize int64
|
||||
txs [][]byte
|
||||
txsToRemove = make(map[sdk.Tx]struct{}, 0)
|
||||
)
|
||||
|
||||
// Select all transactions in the mempool that are valid and not already in the
|
||||
// partial proposal.
|
||||
for iterator := l.Mempool.Select(ctx, nil); iterator != nil; iterator = iterator.Next() {
|
||||
tx := iterator.Tx()
|
||||
|
||||
txBytes, hash, err := utils.GetTxHashStr(l.Cfg.TxEncoder, tx)
|
||||
if err != nil {
|
||||
l.Logger().Info("failed to get hash of tx", "err", err)
|
||||
|
||||
txsToRemove[tx] = struct{}{}
|
||||
continue
|
||||
}
|
||||
|
||||
// if the transaction is already in the (partial) block proposal, we skip it.
|
||||
if proposal.Contains(txBytes) {
|
||||
l.Logger().Info(
|
||||
"failed to select tx for lane; tx is already in proposal",
|
||||
"tx_hash", hash,
|
||||
"lane", l.Name(),
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// If the transaction is too large, we break and do not attempt to include more txs.
|
||||
txSize := int64(len(txBytes))
|
||||
if updatedSize := totalSize + txSize; updatedSize > maxTxBytes {
|
||||
break
|
||||
}
|
||||
|
||||
// Verify the transaction.
|
||||
if err := l.VerifyTx(ctx, tx); err != nil {
|
||||
l.Logger().Info(
|
||||
"failed to verify tx",
|
||||
"tx_hash", hash,
|
||||
"err", err,
|
||||
)
|
||||
|
||||
txsToRemove[tx] = struct{}{}
|
||||
continue
|
||||
}
|
||||
|
||||
totalSize += txSize
|
||||
txs = append(txs, txBytes)
|
||||
}
|
||||
|
||||
// Remove all transactions that were invalid during the creation of the partial proposal.
|
||||
if err := utils.RemoveTxsFromLane(txsToRemove, l.Mempool); err != nil {
|
||||
l.Logger().Error(
|
||||
"failed to remove transactions from lane",
|
||||
"err", err,
|
||||
)
|
||||
|
||||
return proposal, err
|
||||
}
|
||||
|
||||
// Update the partial proposal with the selected transactions. If the proposal is unable to
|
||||
// be updated, we return an error. The proposal will only be modified if it passes all
|
||||
// of the invarient checks.
|
||||
if err := proposal.UpdateProposal(l, txs); err != nil {
|
||||
return proposal, err
|
||||
}
|
||||
|
||||
return next(ctx, proposal)
|
||||
}
|
||||
|
||||
// ProcessLane verifies the default lane's portion of a block proposal. Since the default lane's
|
||||
// ProcessLaneBasic function ensures that all of the default transactions are in the correct order,
|
||||
// we only need to verify the contiguous set of transactions that match to the default lane.
|
||||
func (l *DefaultLane) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next blockbuster.ProcessLanesHandler) (sdk.Context, error) {
|
||||
for index, tx := range txs {
|
||||
if l.Match(ctx, tx) {
|
||||
if err := l.VerifyTx(ctx, tx); err != nil {
|
||||
return ctx, fmt.Errorf("failed to verify tx: %w", err)
|
||||
}
|
||||
} else {
|
||||
return next(ctx, txs[index:])
|
||||
}
|
||||
}
|
||||
|
||||
// This means we have processed all transactions in the proposal.
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// transactions that belong to this lane are not misplaced in the block proposal i.e.
|
||||
// the proposal only contains contiguous transactions that belong to this lane - there
|
||||
// can be no interleaving of transactions from other lanes.
|
||||
func (l *DefaultLane) ProcessLaneBasic(ctx sdk.Context, txs []sdk.Tx) error {
|
||||
seenOtherLaneTx := false
|
||||
lastSeenIndex := 0
|
||||
|
||||
for _, tx := range txs {
|
||||
if l.Match(ctx, tx) {
|
||||
if seenOtherLaneTx {
|
||||
return fmt.Errorf("the %s lane contains a transaction that belongs to another lane", l.Name())
|
||||
}
|
||||
|
||||
lastSeenIndex++
|
||||
continue
|
||||
}
|
||||
|
||||
seenOtherLaneTx = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyTx does basic verification of the transaction using the ante handler.
|
||||
func (l *DefaultLane) VerifyTx(ctx sdk.Context, tx sdk.Tx) error {
|
||||
if l.Cfg.AnteHandler != nil {
|
||||
_, err := l.Cfg.AnteHandler(ctx, tx, false)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
547
blockbuster/lanes/base/abci_test.go
Normal file
547
blockbuster/lanes/base/abci_test.go
Normal file
@ -0,0 +1,547 @@
|
||||
package base_test
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/math"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/skip-mev/pob/blockbuster"
|
||||
"github.com/skip-mev/pob/blockbuster/lanes/base"
|
||||
"github.com/skip-mev/pob/blockbuster/utils/mocks"
|
||||
testutils "github.com/skip-mev/pob/testutils"
|
||||
)
|
||||
|
||||
func (s *BaseTestSuite) TestPrepareLane() {
|
||||
s.Run("should not build a proposal when amount configured to lane is too small", func() {
|
||||
// Create a basic transaction that should not in the proposal
|
||||
tx, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(1)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Create a lane with a max block space of 1 but a proposal that is smaller than the tx
|
||||
expectedExecution := map[sdk.Tx]bool{
|
||||
tx: true,
|
||||
}
|
||||
lane := s.initLane(math.LegacyMustNewDecFromStr("1"), expectedExecution)
|
||||
|
||||
// Insert the transaction into the lane
|
||||
s.Require().NoError(lane.Insert(sdk.Context{}, tx))
|
||||
|
||||
txBz, err := s.encodingConfig.TxConfig.TxEncoder()(tx)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Create a proposal
|
||||
maxTxBytes := int64(len(txBz) - 1)
|
||||
proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler())
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Ensure the proposal is empty
|
||||
s.Require().Equal(0, proposal.GetNumTxs())
|
||||
s.Require().Equal(int64(0), proposal.GetTotalTxBytes())
|
||||
})
|
||||
|
||||
s.Run("should not build a proposal when box space configured to lane is too small", func() {
|
||||
// Create a basic transaction that should not in the proposal
|
||||
tx, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(1)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Create a lane with a max block space of 1 but a proposal that is smaller than the tx
|
||||
expectedExecution := map[sdk.Tx]bool{
|
||||
tx: true,
|
||||
}
|
||||
lane := s.initLane(math.LegacyMustNewDecFromStr("0.000001"), expectedExecution)
|
||||
|
||||
// Insert the transaction into the lane
|
||||
s.Require().NoError(lane.Insert(sdk.Context{}, tx))
|
||||
|
||||
txBz, err := s.encodingConfig.TxConfig.TxEncoder()(tx)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Create a proposal
|
||||
maxTxBytes := int64(len(txBz))
|
||||
proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler())
|
||||
s.Require().Error(err)
|
||||
|
||||
// Ensure the proposal is empty
|
||||
s.Require().Equal(0, proposal.GetNumTxs())
|
||||
s.Require().Equal(int64(0), proposal.GetTotalTxBytes())
|
||||
})
|
||||
|
||||
s.Run("should be able to build a proposal with a tx that just fits in", func() {
|
||||
// Create a basic transaction that should not in the proposal
|
||||
tx, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(1)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Create a lane with a max block space of 1 but a proposal that is smaller than the tx
|
||||
expectedExecution := map[sdk.Tx]bool{
|
||||
tx: true,
|
||||
}
|
||||
lane := s.initLane(math.LegacyMustNewDecFromStr("1"), expectedExecution)
|
||||
|
||||
// Insert the transaction into the lane
|
||||
s.Require().NoError(lane.Insert(sdk.Context{}, tx))
|
||||
|
||||
txBz, err := s.encodingConfig.TxConfig.TxEncoder()(tx)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Create a proposal
|
||||
maxTxBytes := int64(len(txBz))
|
||||
proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler())
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Ensure the proposal is not empty and contains the transaction
|
||||
s.Require().Equal(1, proposal.GetNumTxs())
|
||||
s.Require().Equal(maxTxBytes, proposal.GetTotalTxBytes())
|
||||
s.Require().Equal(txBz, proposal.GetTxs()[0])
|
||||
})
|
||||
|
||||
s.Run("should not build a proposal with a that fails verify tx", func() {
|
||||
// Create a basic transaction that should not in the proposal
|
||||
tx, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(1)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Create a lane with a max block space of 1 but a proposal that is smaller than the tx
|
||||
expectedExecution := map[sdk.Tx]bool{
|
||||
tx: false,
|
||||
}
|
||||
lane := s.initLane(math.LegacyMustNewDecFromStr("1"), expectedExecution)
|
||||
|
||||
// Insert the transaction into the lane
|
||||
s.Require().NoError(lane.Insert(sdk.Context{}, tx))
|
||||
|
||||
// Create a proposal
|
||||
txBz, err := s.encodingConfig.TxConfig.TxEncoder()(tx)
|
||||
s.Require().NoError(err)
|
||||
|
||||
maxTxBytes := int64(len(txBz))
|
||||
proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler())
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Ensure the proposal is empty
|
||||
s.Require().Equal(0, proposal.GetNumTxs())
|
||||
s.Require().Equal(int64(0), proposal.GetTotalTxBytes())
|
||||
|
||||
// Ensure the transaction is removed from the lane
|
||||
s.Require().False(lane.Contains(tx))
|
||||
s.Require().Equal(0, lane.CountTx())
|
||||
})
|
||||
|
||||
s.Run("should order transactions correctly in the proposal", func() {
|
||||
// Create a basic transaction that should not in the proposal
|
||||
tx1, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(2)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
tx2, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[1],
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(1)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Create a lane with a max block space of 1 but a proposal that is smaller than the tx
|
||||
expectedExecution := map[sdk.Tx]bool{
|
||||
tx1: true,
|
||||
tx2: true,
|
||||
}
|
||||
lane := s.initLane(math.LegacyMustNewDecFromStr("1"), expectedExecution)
|
||||
|
||||
// Insert the transaction into the lane
|
||||
s.Require().NoError(lane.Insert(sdk.Context{}, tx1))
|
||||
s.Require().NoError(lane.Insert(sdk.Context{}, tx2))
|
||||
|
||||
txBz1, err := s.encodingConfig.TxConfig.TxEncoder()(tx1)
|
||||
s.Require().NoError(err)
|
||||
|
||||
txBz2, err := s.encodingConfig.TxConfig.TxEncoder()(tx2)
|
||||
s.Require().NoError(err)
|
||||
|
||||
maxTxBytes := int64(len(txBz1)) + int64(len(txBz2))
|
||||
proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler())
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Ensure the proposal is ordered correctly
|
||||
s.Require().Equal(2, proposal.GetNumTxs())
|
||||
s.Require().Equal(maxTxBytes, proposal.GetTotalTxBytes())
|
||||
s.Require().Equal([][]byte{txBz1, txBz2}, proposal.GetTxs())
|
||||
})
|
||||
|
||||
s.Run("should order transactions correctly in the proposal (with different insertion)", func() {
|
||||
// Create a basic transaction that should not in the proposal
|
||||
tx1, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(1)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
tx2, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[1],
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(2)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Create a lane with a max block space of 1 but a proposal that is smaller than the tx
|
||||
expectedExecution := map[sdk.Tx]bool{
|
||||
tx1: true,
|
||||
tx2: true,
|
||||
}
|
||||
lane := s.initLane(math.LegacyMustNewDecFromStr("1"), expectedExecution)
|
||||
|
||||
// Insert the transaction into the lane
|
||||
s.Require().NoError(lane.Insert(sdk.Context{}, tx1))
|
||||
s.Require().NoError(lane.Insert(sdk.Context{}, tx2))
|
||||
|
||||
txBz1, err := s.encodingConfig.TxConfig.TxEncoder()(tx1)
|
||||
s.Require().NoError(err)
|
||||
|
||||
txBz2, err := s.encodingConfig.TxConfig.TxEncoder()(tx2)
|
||||
s.Require().NoError(err)
|
||||
|
||||
maxTxBytes := int64(len(txBz1)) + int64(len(txBz2))
|
||||
proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler())
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Ensure the proposal is ordered correctly
|
||||
s.Require().Equal(2, proposal.GetNumTxs())
|
||||
s.Require().Equal(maxTxBytes, proposal.GetTotalTxBytes())
|
||||
s.Require().Equal([][]byte{txBz2, txBz1}, proposal.GetTxs())
|
||||
})
|
||||
|
||||
s.Run("should include tx that fits in proposal when other does not", func() {
|
||||
// Create a basic transaction that should not in the proposal
|
||||
tx1, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(1)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
tx2, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[1],
|
||||
0,
|
||||
10, // This tx is too large to fit in the proposal
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(1)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Create a lane with a max block space of 1 but a proposal that is smaller than the tx
|
||||
expectedExecution := map[sdk.Tx]bool{
|
||||
tx1: true,
|
||||
tx2: true,
|
||||
}
|
||||
lane := s.initLane(math.LegacyMustNewDecFromStr("1"), expectedExecution)
|
||||
|
||||
// Insert the transaction into the lane
|
||||
s.Require().NoError(lane.Insert(sdk.Context{}.WithPriority(10), tx1))
|
||||
s.Require().NoError(lane.Insert(sdk.Context{}.WithPriority(5), tx2))
|
||||
|
||||
txBz1, err := s.encodingConfig.TxConfig.TxEncoder()(tx1)
|
||||
s.Require().NoError(err)
|
||||
|
||||
txBz2, err := s.encodingConfig.TxConfig.TxEncoder()(tx2)
|
||||
s.Require().NoError(err)
|
||||
|
||||
maxTxBytes := int64(len(txBz1)) + int64(len(txBz2)) - 1
|
||||
proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler())
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Ensure the proposal is ordered correctly
|
||||
s.Require().Equal(1, proposal.GetNumTxs())
|
||||
s.Require().Equal(int64(len(txBz1)), proposal.GetTotalTxBytes())
|
||||
s.Require().Equal([][]byte{txBz1}, proposal.GetTxs())
|
||||
})
|
||||
}
|
||||
|
||||
func (s *BaseTestSuite) TestProcessLane() {
|
||||
s.Run("should accept a proposal with valid transactions", func() {
|
||||
tx1, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
proposal := []sdk.Tx{
|
||||
tx1,
|
||||
}
|
||||
|
||||
lane := s.initLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{
|
||||
tx1: true,
|
||||
})
|
||||
|
||||
_, err = lane.ProcessLane(sdk.Context{}, proposal, blockbuster.NoOpProcessLanesHandler())
|
||||
s.Require().NoError(err)
|
||||
})
|
||||
|
||||
s.Run("should not accept a proposal with invalid transactions", func() {
|
||||
tx1, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
proposal := []sdk.Tx{
|
||||
tx1,
|
||||
}
|
||||
|
||||
lane := s.initLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{
|
||||
tx1: false,
|
||||
})
|
||||
|
||||
_, err = lane.ProcessLane(sdk.Context{}, proposal, blockbuster.NoOpProcessLanesHandler())
|
||||
s.Require().Error(err)
|
||||
})
|
||||
|
||||
s.Run("should not accept a proposal with some invalid transactions", func() {
|
||||
tx1, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
tx2, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[1],
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
tx3, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[2],
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
proposal := []sdk.Tx{
|
||||
tx1,
|
||||
tx2,
|
||||
tx3,
|
||||
}
|
||||
|
||||
lane := s.initLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{
|
||||
tx1: true,
|
||||
tx2: false,
|
||||
tx3: true,
|
||||
})
|
||||
|
||||
_, err = lane.ProcessLane(sdk.Context{}, proposal, blockbuster.NoOpProcessLanesHandler())
|
||||
s.Require().Error(err)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *BaseTestSuite) TestCheckOrder() {
|
||||
s.Run("should accept proposal with transactions in correct order", func() {
|
||||
tx1, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(2)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
tx2, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[1],
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(1)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
proposal := []sdk.Tx{
|
||||
tx1,
|
||||
tx2,
|
||||
}
|
||||
|
||||
lane := s.initLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{
|
||||
tx1: true,
|
||||
tx2: true,
|
||||
})
|
||||
s.Require().NoError(lane.CheckOrder(sdk.Context{}, proposal))
|
||||
})
|
||||
|
||||
s.Run("should not accept a proposal with transactions that are not in the correct order", func() {
|
||||
tx1, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(1)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
tx2, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[1],
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(2)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
proposal := []sdk.Tx{
|
||||
tx1,
|
||||
tx2,
|
||||
}
|
||||
|
||||
lane := s.initLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{
|
||||
tx1: true,
|
||||
tx2: true,
|
||||
})
|
||||
s.Require().Error(lane.CheckOrder(sdk.Context{}, proposal))
|
||||
})
|
||||
|
||||
s.Run("should not accept a proposal where transactions are out of order relative to other lanes", func() {
|
||||
tx1, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(1)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
tx2, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[1],
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(2)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
mocklane := mocks.NewLane(s.T())
|
||||
mocklane.On("Match", sdk.Context{}, tx1).Return(true)
|
||||
mocklane.On("Match", sdk.Context{}, tx2).Return(false)
|
||||
|
||||
lane := s.initLane(math.LegacyMustNewDecFromStr("1"), nil)
|
||||
lane.SetIgnoreList([]blockbuster.Lane{mocklane})
|
||||
|
||||
proposal := []sdk.Tx{
|
||||
tx1,
|
||||
tx2,
|
||||
}
|
||||
|
||||
s.Require().Error(lane.CheckOrder(sdk.Context{}, proposal))
|
||||
})
|
||||
}
|
||||
|
||||
func (s *BaseTestSuite) initLane(
|
||||
maxBlockSpace math.LegacyDec,
|
||||
expectedExecution map[sdk.Tx]bool,
|
||||
) *base.DefaultLane {
|
||||
config := blockbuster.NewBaseLaneConfig(
|
||||
log.NewTestLogger(s.T()),
|
||||
s.encodingConfig.TxConfig.TxEncoder(),
|
||||
s.encodingConfig.TxConfig.TxDecoder(),
|
||||
s.setUpAnteHandler(expectedExecution),
|
||||
maxBlockSpace,
|
||||
)
|
||||
|
||||
return base.NewDefaultLane(config)
|
||||
}
|
||||
|
||||
func (s *BaseTestSuite) setUpAnteHandler(expectedExecution map[sdk.Tx]bool) sdk.AnteHandler {
|
||||
txCache := make(map[string]bool)
|
||||
for tx, pass := range expectedExecution {
|
||||
bz, err := s.encodingConfig.TxConfig.TxEncoder()(tx)
|
||||
s.Require().NoError(err)
|
||||
|
||||
hash := sha256.Sum256(bz)
|
||||
hashStr := hex.EncodeToString(hash[:])
|
||||
txCache[hashStr] = pass
|
||||
}
|
||||
|
||||
anteHandler := func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) {
|
||||
bz, err := s.encodingConfig.TxConfig.TxEncoder()(tx)
|
||||
s.Require().NoError(err)
|
||||
|
||||
hash := sha256.Sum256(bz)
|
||||
hashStr := hex.EncodeToString(hash[:])
|
||||
|
||||
pass, found := txCache[hashStr]
|
||||
if !found {
|
||||
return ctx, fmt.Errorf("tx not found")
|
||||
}
|
||||
|
||||
if pass {
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
return ctx, fmt.Errorf("tx failed")
|
||||
}
|
||||
|
||||
return anteHandler
|
||||
}
|
||||
32
blockbuster/lanes/base/base_test.go
Normal file
32
blockbuster/lanes/base/base_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
package base_test
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
testutils "github.com/skip-mev/pob/testutils"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type BaseTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
encodingConfig testutils.EncodingConfig
|
||||
random *rand.Rand
|
||||
accounts []testutils.Account
|
||||
gasTokenDenom string
|
||||
}
|
||||
|
||||
func TestBaseTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(BaseTestSuite))
|
||||
}
|
||||
|
||||
func (s *BaseTestSuite) SetupTest() {
|
||||
// Set up basic TX encoding config.
|
||||
s.encodingConfig = testutils.CreateTestEncodingConfig()
|
||||
|
||||
// Create a few random accounts
|
||||
s.random = rand.New(rand.NewSource(1))
|
||||
s.accounts = testutils.RandomAccounts(s.random, 5)
|
||||
s.gasTokenDenom = "stake"
|
||||
}
|
||||
@ -1,9 +1,6 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/math"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/skip-mev/pob/blockbuster"
|
||||
)
|
||||
|
||||
@ -15,83 +12,29 @@ const (
|
||||
var _ blockbuster.Lane = (*DefaultLane)(nil)
|
||||
|
||||
// DefaultLane defines a default lane implementation. The default lane orders
|
||||
// transactions by the sdk.Context priority. The default lane will accept any
|
||||
// transaction that is not a part of the lane's IgnoreList. By default, the IgnoreList
|
||||
// is empty and the default lane will accept any transaction. The default lane on its
|
||||
// own implements the same functionality as the pre v0.47.0 tendermint mempool and proposal
|
||||
// handlers.
|
||||
// transactions by the transaction fees. The default lane accepts any transaction
|
||||
// that is should not be ignored (as defined by the IgnoreList in the LaneConfig).
|
||||
// The default lane builds and verifies blocks in a similar fashion to how the
|
||||
// CometBFT/Tendermint consensus engine builds and verifies blocks pre SDK version
|
||||
// 0.47.0.
|
||||
type DefaultLane struct {
|
||||
// Mempool defines the mempool for the lane.
|
||||
Mempool
|
||||
|
||||
// LaneConfig defines the base lane configuration.
|
||||
Cfg blockbuster.BaseLaneConfig
|
||||
|
||||
// Name defines the name of the lane.
|
||||
laneName string
|
||||
*blockbuster.LaneConstructor
|
||||
}
|
||||
|
||||
// NewDefaultLane returns a new default lane.
|
||||
func NewDefaultLane(cfg blockbuster.BaseLaneConfig) *DefaultLane {
|
||||
if err := cfg.ValidateBasic(); err != nil {
|
||||
panic(err)
|
||||
func NewDefaultLane(cfg blockbuster.LaneConfig) *DefaultLane {
|
||||
lane := blockbuster.NewLaneConstructor(
|
||||
cfg,
|
||||
LaneName,
|
||||
blockbuster.NewConstructorMempool[string](
|
||||
blockbuster.DefaultTxPriority(),
|
||||
cfg.TxEncoder,
|
||||
cfg.MaxTxs,
|
||||
),
|
||||
blockbuster.DefaultMatchHandler(),
|
||||
)
|
||||
|
||||
return &DefaultLane{
|
||||
LaneConstructor: lane,
|
||||
}
|
||||
|
||||
lane := &DefaultLane{
|
||||
Mempool: NewDefaultMempool(cfg.TxEncoder),
|
||||
Cfg: cfg,
|
||||
laneName: LaneName,
|
||||
}
|
||||
|
||||
return lane
|
||||
}
|
||||
|
||||
// WithName returns a lane option that sets the lane's name.
|
||||
func (l *DefaultLane) WithName(name string) *DefaultLane {
|
||||
l.laneName = name
|
||||
return l
|
||||
}
|
||||
|
||||
// Match returns true if the transaction belongs to this lane. Since
|
||||
// this is the default lane, it always returns true except for transactions
|
||||
// that belong to lanes in the ignore list.
|
||||
func (l *DefaultLane) Match(ctx sdk.Context, tx sdk.Tx) bool {
|
||||
return !l.MatchIgnoreList(ctx, tx)
|
||||
}
|
||||
|
||||
// MatchIgnoreList returns true if any of the lanes that are in the ignore list
|
||||
// match the current transaction.
|
||||
func (l *DefaultLane) MatchIgnoreList(ctx sdk.Context, tx sdk.Tx) bool {
|
||||
for _, lane := range l.Cfg.IgnoreList {
|
||||
if lane.Match(ctx, tx) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Name returns the name of the lane.
|
||||
func (l *DefaultLane) Name() string {
|
||||
return l.laneName
|
||||
}
|
||||
|
||||
// Logger returns the lane's logger.
|
||||
func (l *DefaultLane) Logger() log.Logger {
|
||||
return l.Cfg.Logger
|
||||
}
|
||||
|
||||
// SetAnteHandler sets the lane's antehandler.
|
||||
func (l *DefaultLane) SetAnteHandler(anteHandler sdk.AnteHandler) {
|
||||
l.Cfg.AnteHandler = anteHandler
|
||||
}
|
||||
|
||||
// GetMaxBlockSpace returns the maximum block space for the lane as a relative percentage.
|
||||
func (l *DefaultLane) GetMaxBlockSpace() math.LegacyDec {
|
||||
return l.Cfg.MaxBlockSpace
|
||||
}
|
||||
|
||||
// GetIgnoreList returns the lane's ignore list.
|
||||
func (l *DefaultLane) GetIgnoreList() []blockbuster.Lane {
|
||||
return l.Cfg.IgnoreList
|
||||
}
|
||||
|
||||
@ -1,105 +0,0 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool"
|
||||
"github.com/skip-mev/pob/blockbuster"
|
||||
"github.com/skip-mev/pob/blockbuster/utils"
|
||||
)
|
||||
|
||||
var _ sdkmempool.Mempool = (*DefaultMempool)(nil)
|
||||
|
||||
type (
|
||||
// Mempool defines the interface of the default mempool.
|
||||
Mempool interface {
|
||||
sdkmempool.Mempool
|
||||
|
||||
// Contains returns true if the transaction is contained in the mempool.
|
||||
Contains(tx sdk.Tx) bool
|
||||
}
|
||||
|
||||
// DefaultMempool defines the most basic mempool. It can be seen as an extension of
|
||||
// an SDK PriorityNonceMempool, i.e. a mempool that supports <sender, nonce>
|
||||
// two-dimensional priority ordering, with the additional support of prioritizing
|
||||
// and indexing auction bids.
|
||||
DefaultMempool struct {
|
||||
// index defines an index transactions.
|
||||
index sdkmempool.Mempool
|
||||
|
||||
// txEncoder defines the sdk.Tx encoder that allows us to encode transactions
|
||||
// to bytes.
|
||||
txEncoder sdk.TxEncoder
|
||||
|
||||
// txIndex is a map of all transactions in the mempool. It is used
|
||||
// to quickly check if a transaction is already in the mempool.
|
||||
txIndex map[string]struct{}
|
||||
}
|
||||
)
|
||||
|
||||
// NewDefaultMempool returns a new default mempool instance. The default mempool
|
||||
// orders transactions by the sdk.Context priority.
|
||||
func NewDefaultMempool(txEncoder sdk.TxEncoder) *DefaultMempool {
|
||||
return &DefaultMempool{
|
||||
index: blockbuster.NewPriorityMempool(
|
||||
blockbuster.DefaultPriorityNonceMempoolConfig(),
|
||||
),
|
||||
txEncoder: txEncoder,
|
||||
txIndex: make(map[string]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Insert inserts a transaction into the mempool based on the transaction type (normal or auction).
|
||||
func (am *DefaultMempool) Insert(ctx context.Context, tx sdk.Tx) error {
|
||||
if err := am.index.Insert(ctx, tx); err != nil {
|
||||
return fmt.Errorf("failed to insert tx into auction index: %w", err)
|
||||
}
|
||||
|
||||
_, txHashStr, err := utils.GetTxHashStr(am.txEncoder, tx)
|
||||
if err != nil {
|
||||
am.Remove(tx)
|
||||
return err
|
||||
}
|
||||
|
||||
am.txIndex[txHashStr] = struct{}{}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes a transaction from the mempool based on the transaction type (normal or auction).
|
||||
func (am *DefaultMempool) Remove(tx sdk.Tx) error {
|
||||
if err := am.index.Remove(tx); err != nil && !errors.Is(err, sdkmempool.ErrTxNotFound) {
|
||||
return fmt.Errorf("failed to remove transaction from the mempool: %w", err)
|
||||
}
|
||||
|
||||
_, txHashStr, err := utils.GetTxHashStr(am.txEncoder, tx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get tx hash string: %w", err)
|
||||
}
|
||||
|
||||
delete(am.txIndex, txHashStr)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (am *DefaultMempool) Select(ctx context.Context, txs [][]byte) sdkmempool.Iterator {
|
||||
return am.index.Select(ctx, txs)
|
||||
}
|
||||
|
||||
func (am *DefaultMempool) CountTx() int {
|
||||
return am.index.CountTx()
|
||||
}
|
||||
|
||||
// Contains returns true if the transaction is contained in the mempool.
|
||||
func (am *DefaultMempool) Contains(tx sdk.Tx) bool {
|
||||
_, txHashStr, err := utils.GetTxHashStr(am.txEncoder, tx)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
_, ok := am.txIndex[txHashStr]
|
||||
return ok
|
||||
}
|
||||
240
blockbuster/lanes/base/mempool_test.go
Normal file
240
blockbuster/lanes/base/mempool_test.go
Normal file
@ -0,0 +1,240 @@
|
||||
package base_test
|
||||
|
||||
import (
|
||||
"cosmossdk.io/math"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/skip-mev/pob/blockbuster"
|
||||
testutils "github.com/skip-mev/pob/testutils"
|
||||
)
|
||||
|
||||
func (s *BaseTestSuite) TestGetTxPriority() {
|
||||
txPriority := blockbuster.DefaultTxPriority()
|
||||
|
||||
s.Run("should be able to get the priority off a normal transaction with fees", func() {
|
||||
tx, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
priority := txPriority.GetTxPriority(sdk.Context{}, tx)
|
||||
s.Require().Equal(sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)).String(), priority)
|
||||
})
|
||||
|
||||
s.Run("should not get a priority when the transaction does not have a fee", func() {
|
||||
tx, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
priority := txPriority.GetTxPriority(sdk.Context{}, tx)
|
||||
s.Require().Equal("", priority)
|
||||
})
|
||||
|
||||
s.Run("should get a priority when the gas token is different", func() {
|
||||
tx, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
sdk.NewCoin("random", math.NewInt(100)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
priority := txPriority.GetTxPriority(sdk.Context{}, tx)
|
||||
s.Require().Equal(sdk.NewCoin("random", math.NewInt(100)).String(), priority)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *BaseTestSuite) TestCompareTxPriority() {
|
||||
txPriority := blockbuster.DefaultTxPriority()
|
||||
|
||||
s.Run("should return 0 when both priorities are nil", func() {
|
||||
a := sdk.NewCoin(s.gasTokenDenom, math.NewInt(0)).String()
|
||||
b := sdk.NewCoin(s.gasTokenDenom, math.NewInt(0)).String()
|
||||
s.Require().Equal(0, txPriority.Compare(a, b))
|
||||
})
|
||||
|
||||
s.Run("should return 1 when the first priority is greater", func() {
|
||||
a := sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)).String()
|
||||
b := sdk.NewCoin(s.gasTokenDenom, math.NewInt(1)).String()
|
||||
s.Require().Equal(1, txPriority.Compare(a, b))
|
||||
})
|
||||
|
||||
s.Run("should return -1 when the second priority is greater", func() {
|
||||
a := sdk.NewCoin(s.gasTokenDenom, math.NewInt(1)).String()
|
||||
b := sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)).String()
|
||||
s.Require().Equal(-1, txPriority.Compare(a, b))
|
||||
})
|
||||
|
||||
s.Run("should return 0 when both priorities are equal", func() {
|
||||
a := sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)).String()
|
||||
b := sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)).String()
|
||||
s.Require().Equal(0, txPriority.Compare(a, b))
|
||||
})
|
||||
}
|
||||
|
||||
func (s *BaseTestSuite) TestInsert() {
|
||||
mempool := blockbuster.NewConstructorMempool[string](blockbuster.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3)
|
||||
|
||||
s.Run("should be able to insert a transaction", func() {
|
||||
tx, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = mempool.Insert(sdk.Context{}, tx)
|
||||
s.Require().NoError(err)
|
||||
s.Require().True(mempool.Contains(tx))
|
||||
})
|
||||
|
||||
s.Run("cannot insert more transactions than the max", func() {
|
||||
for i := 0; i < 3; i++ {
|
||||
tx, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
uint64(i),
|
||||
0,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(int64(100*i))),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = mempool.Insert(sdk.Context{}, tx)
|
||||
s.Require().NoError(err)
|
||||
s.Require().True(mempool.Contains(tx))
|
||||
}
|
||||
|
||||
tx, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
10,
|
||||
0,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = mempool.Insert(sdk.Context{}, tx)
|
||||
s.Require().Error(err)
|
||||
s.Require().False(mempool.Contains(tx))
|
||||
})
|
||||
}
|
||||
|
||||
func (s *BaseTestSuite) TestRemove() {
|
||||
mempool := blockbuster.NewConstructorMempool[string](blockbuster.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3)
|
||||
|
||||
s.Run("should be able to remove a transaction", func() {
|
||||
tx, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = mempool.Insert(sdk.Context{}, tx)
|
||||
s.Require().NoError(err)
|
||||
s.Require().True(mempool.Contains(tx))
|
||||
|
||||
mempool.Remove(tx)
|
||||
s.Require().False(mempool.Contains(tx))
|
||||
})
|
||||
|
||||
s.Run("should not error when removing a transaction that does not exist", func() {
|
||||
tx, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
mempool.Remove(tx)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *BaseTestSuite) TestSelect() {
|
||||
s.Run("should be able to select transactions in the correct order", func() {
|
||||
mempool := blockbuster.NewConstructorMempool[string](blockbuster.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3)
|
||||
|
||||
tx1, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
tx2, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[1],
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(200)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Insert the transactions into the mempool
|
||||
s.Require().NoError(mempool.Insert(sdk.Context{}, tx1))
|
||||
s.Require().NoError(mempool.Insert(sdk.Context{}, tx2))
|
||||
s.Require().Equal(2, mempool.CountTx())
|
||||
|
||||
// Check that the transactions are in the correct order
|
||||
iterator := mempool.Select(sdk.Context{}, nil)
|
||||
s.Require().NotNil(iterator)
|
||||
s.Require().Equal(tx2, iterator.Tx())
|
||||
|
||||
// Check the second transaction
|
||||
iterator = iterator.Next()
|
||||
s.Require().NotNil(iterator)
|
||||
s.Require().Equal(tx1, iterator.Tx())
|
||||
})
|
||||
|
||||
s.Run("should be able to select a single transaction", func() {
|
||||
mempool := blockbuster.NewConstructorMempool[string](blockbuster.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3)
|
||||
|
||||
tx1, err := testutils.CreateRandomTx(
|
||||
s.encodingConfig.TxConfig,
|
||||
s.accounts[0],
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
sdk.NewCoin(s.gasTokenDenom, math.NewInt(100)),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Insert the transactions into the mempool
|
||||
s.Require().NoError(mempool.Insert(sdk.Context{}, tx1))
|
||||
s.Require().Equal(1, mempool.CountTx())
|
||||
|
||||
// Check that the transactions are in the correct order
|
||||
iterator := mempool.Select(sdk.Context{}, nil)
|
||||
s.Require().NotNil(iterator)
|
||||
s.Require().Equal(tx1, iterator.Tx())
|
||||
|
||||
iterator = iterator.Next()
|
||||
s.Require().Nil(iterator)
|
||||
})
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
package free
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
)
|
||||
|
||||
type (
|
||||
// Factory defines the interface for processing free transactions. It is
|
||||
// a wrapper around all of the functionality that each application chain must implement
|
||||
// in order for free processing to work.
|
||||
Factory interface {
|
||||
// IsFreeTx defines a function that checks if a transaction qualifies as free.
|
||||
IsFreeTx(tx sdk.Tx) bool
|
||||
}
|
||||
|
||||
// DefaultFreeFactory defines a default implmentation for the free factory interface for processing free transactions.
|
||||
DefaultFreeFactory struct {
|
||||
txDecoder sdk.TxDecoder
|
||||
}
|
||||
)
|
||||
|
||||
var _ Factory = (*DefaultFreeFactory)(nil)
|
||||
|
||||
// NewDefaultFreeFactory returns a default free factory interface implementation.
|
||||
func NewDefaultFreeFactory(txDecoder sdk.TxDecoder) Factory {
|
||||
return &DefaultFreeFactory{
|
||||
txDecoder: txDecoder,
|
||||
}
|
||||
}
|
||||
|
||||
// IsFreeTx defines a default function that checks if a transaction is free. In this case,
|
||||
// any transaction that is a delegation/redelegation transaction is free.
|
||||
func (config *DefaultFreeFactory) IsFreeTx(tx sdk.Tx) bool {
|
||||
for _, msg := range tx.GetMsgs() {
|
||||
switch msg.(type) {
|
||||
case *types.MsgDelegate:
|
||||
return true
|
||||
case *types.MsgBeginRedelegate:
|
||||
return true
|
||||
case *types.MsgCancelUnbondingDelegation:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
@ -2,8 +2,8 @@ package free
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
"github.com/skip-mev/pob/blockbuster"
|
||||
"github.com/skip-mev/pob/blockbuster/lanes/base"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -11,31 +11,52 @@ const (
|
||||
LaneName = "free"
|
||||
)
|
||||
|
||||
var _ blockbuster.Lane = (*Lane)(nil)
|
||||
var _ blockbuster.Lane = (*FreeLane)(nil)
|
||||
|
||||
// FreeLane defines the lane that is responsible for processing free transactions.
|
||||
type Lane struct {
|
||||
*base.DefaultLane
|
||||
Factory
|
||||
// By default, transactions that are staking related are considered free.
|
||||
type FreeLane struct { //nolint
|
||||
*blockbuster.LaneConstructor
|
||||
}
|
||||
|
||||
// NewFreeLane returns a new free lane.
|
||||
func NewFreeLane(cfg blockbuster.BaseLaneConfig, factory Factory) *Lane {
|
||||
if err := cfg.ValidateBasic(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
func NewFreeLane(
|
||||
cfg blockbuster.LaneConfig,
|
||||
txPriority blockbuster.TxPriority[string],
|
||||
matchFn blockbuster.MatchHandler,
|
||||
) *FreeLane {
|
||||
lane := blockbuster.NewLaneConstructor(
|
||||
cfg,
|
||||
LaneName,
|
||||
blockbuster.NewConstructorMempool[string](
|
||||
txPriority,
|
||||
cfg.TxEncoder,
|
||||
cfg.MaxTxs,
|
||||
),
|
||||
matchFn,
|
||||
)
|
||||
|
||||
return &Lane{
|
||||
DefaultLane: base.NewDefaultLane(cfg).WithName(LaneName),
|
||||
Factory: factory,
|
||||
return &FreeLane{
|
||||
LaneConstructor: lane,
|
||||
}
|
||||
}
|
||||
|
||||
// Match returns true if the transaction is a free transaction.
|
||||
func (l *Lane) Match(ctx sdk.Context, tx sdk.Tx) bool {
|
||||
if l.MatchIgnoreList(ctx, tx) {
|
||||
// DefaultMatchHandler returns the default match handler for the free lane. The
|
||||
// default implementation matches transactions that are staking related. In particular,
|
||||
// any transaction that is a MsgDelegate, MsgBeginRedelegate, or MsgCancelUnbondingDelegation.
|
||||
func DefaultMatchHandler() blockbuster.MatchHandler {
|
||||
return func(ctx sdk.Context, tx sdk.Tx) bool {
|
||||
for _, msg := range tx.GetMsgs() {
|
||||
switch msg.(type) {
|
||||
case *types.MsgDelegate:
|
||||
return true
|
||||
case *types.MsgBeginRedelegate:
|
||||
return true
|
||||
case *types.MsgCancelUnbondingDelegation:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return l.IsFreeTx(tx)
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ package terminator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/math"
|
||||
@ -11,6 +10,10 @@ import (
|
||||
"github.com/skip-mev/pob/blockbuster"
|
||||
)
|
||||
|
||||
const (
|
||||
LaneName = "Terminator"
|
||||
)
|
||||
|
||||
// Terminator Lane will get added to the chain to simplify chaining code so that we
|
||||
// don't need to check if next == nil further up the chain
|
||||
//
|
||||
@ -40,26 +43,42 @@ func (t Terminator) PrepareLane(_ sdk.Context, proposal blockbuster.BlockProposa
|
||||
return proposal, nil
|
||||
}
|
||||
|
||||
// ValidateLaneBasic is a no-op
|
||||
func (t Terminator) CheckOrder(sdk.Context, []sdk.Tx) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProcessLane is a no-op
|
||||
func (t Terminator) ProcessLane(ctx sdk.Context, _ []sdk.Tx, _ blockbuster.ProcessLanesHandler) (sdk.Context, error) {
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// GetMaxBlockSpace is a no-op
|
||||
func (t Terminator) GetMaxBlockSpace() math.LegacyDec {
|
||||
return math.LegacyZeroDec()
|
||||
}
|
||||
|
||||
// Logger is a no-op
|
||||
func (t Terminator) Logger() log.Logger {
|
||||
return log.NewNopLogger()
|
||||
}
|
||||
|
||||
// Name returns the name of the lane
|
||||
func (t Terminator) Name() string {
|
||||
return "Terminator"
|
||||
return LaneName
|
||||
}
|
||||
|
||||
// SetAnteHandler is a no-op
|
||||
func (t Terminator) SetAnteHandler(sdk.AnteHandler) {}
|
||||
|
||||
// SetIgnoreList is a no-op
|
||||
func (t Terminator) SetIgnoreList([]blockbuster.Lane) {}
|
||||
|
||||
// Match is a no-op
|
||||
func (t Terminator) Match(sdk.Context, sdk.Tx) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// VerifyTx is a no-op
|
||||
func (t Terminator) VerifyTx(sdk.Context, sdk.Tx) error {
|
||||
return fmt.Errorf("Terminator lane should not be called")
|
||||
}
|
||||
|
||||
// Contains is a no-op
|
||||
func (t Terminator) Contains(sdk.Tx) bool {
|
||||
return false
|
||||
@ -85,20 +104,7 @@ func (t Terminator) Select(context.Context, [][]byte) sdkmempool.Iterator {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateLaneBasic is a no-op
|
||||
func (t Terminator) ProcessLaneBasic(sdk.Context, []sdk.Tx) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetLaneConfig is a no-op
|
||||
func (t Terminator) SetAnteHandler(sdk.AnteHandler) {}
|
||||
|
||||
// Logger is a no-op
|
||||
func (t Terminator) Logger() log.Logger {
|
||||
return log.NewNopLogger()
|
||||
}
|
||||
|
||||
// GetMaxBlockSpace is a no-op
|
||||
func (t Terminator) GetMaxBlockSpace() math.LegacyDec {
|
||||
return math.LegacyZeroDec()
|
||||
// HasHigherPriority is a no-op
|
||||
func (t Terminator) Compare(sdk.Context, sdk.Tx, sdk.Tx) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -31,11 +31,15 @@ type (
|
||||
GetLane(name string) (Lane, error)
|
||||
}
|
||||
|
||||
// Mempool defines the Blockbuster mempool implement. It contains a registry
|
||||
// BBMempool defines the Blockbuster mempool implementation. It contains a registry
|
||||
// of lanes, which allows for customizable block proposal construction.
|
||||
BBMempool struct {
|
||||
logger log.Logger
|
||||
|
||||
// registry contains the lanes in the mempool. The lanes are ordered
|
||||
// according to their priority. The first lane in the registry has the
|
||||
// highest priority and the last lane has the lowest priority.
|
||||
registry []Lane
|
||||
logger log.Logger
|
||||
}
|
||||
)
|
||||
|
||||
@ -47,8 +51,10 @@ type (
|
||||
// registry. Each transaction should only belong in one lane but this is NOT enforced.
|
||||
// To enforce that each transaction belong to a single lane, you must configure the
|
||||
// ignore list of each lane to include all preceding lanes. Basic mempool API will
|
||||
// attempt to insert, remove transactions from all lanes it belongs to.
|
||||
func NewMempool(logger log.Logger, lanes ...Lane) *BBMempool {
|
||||
// attempt to insert, remove transactions from all lanes it belongs to. It is recommended,
|
||||
// that mutex is set to true when creating the mempool. This will ensure that each
|
||||
// transaction cannot be inserted into the lanes before it.
|
||||
func NewMempool(logger log.Logger, mutex bool, lanes ...Lane) *BBMempool {
|
||||
mempool := &BBMempool{
|
||||
logger: logger,
|
||||
registry: lanes,
|
||||
@ -58,6 +64,16 @@ func NewMempool(logger log.Logger, lanes ...Lane) *BBMempool {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Set the ignore list for each lane
|
||||
if mutex {
|
||||
registry := mempool.registry
|
||||
for index, lane := range mempool.registry {
|
||||
if index > 0 {
|
||||
lane.SetIgnoreList(registry[:index])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mempool
|
||||
}
|
||||
|
||||
@ -85,7 +101,14 @@ func (m *BBMempool) GetTxDistribution() map[string]int {
|
||||
|
||||
// Insert will insert a transaction into the mempool. It inserts the transaction
|
||||
// into the first lane that it matches.
|
||||
func (m *BBMempool) Insert(ctx context.Context, tx sdk.Tx) error {
|
||||
func (m *BBMempool) Insert(ctx context.Context, tx sdk.Tx) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
m.logger.Error("panic in Insert", "err", r)
|
||||
err = fmt.Errorf("panic in Insert: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
var errors []string
|
||||
|
||||
unwrappedCtx := sdk.UnwrapSDKContext(ctx)
|
||||
@ -118,7 +141,14 @@ func (m *BBMempool) Select(_ context.Context, _ [][]byte) sdkmempool.Iterator {
|
||||
}
|
||||
|
||||
// Remove removes a transaction from all of the lanes it is currently in.
|
||||
func (m *BBMempool) Remove(tx sdk.Tx) error {
|
||||
func (m *BBMempool) Remove(tx sdk.Tx) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
m.logger.Error("panic in Remove", "err", r)
|
||||
err = fmt.Errorf("panic in Remove: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
var errors []string
|
||||
|
||||
for _, lane := range m.registry {
|
||||
@ -149,7 +179,14 @@ func (m *BBMempool) Remove(tx sdk.Tx) error {
|
||||
}
|
||||
|
||||
// Contains returns true if the transaction is contained in any of the lanes.
|
||||
func (m *BBMempool) Contains(tx sdk.Tx) bool {
|
||||
func (m *BBMempool) Contains(tx sdk.Tx) (contains bool) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
m.logger.Error("panic in Contains", "err", r)
|
||||
contains = false
|
||||
}
|
||||
}()
|
||||
|
||||
for _, lane := range m.registry {
|
||||
if lane.Contains(tx) {
|
||||
return true
|
||||
@ -164,7 +201,10 @@ func (m *BBMempool) Registry() []Lane {
|
||||
return m.registry
|
||||
}
|
||||
|
||||
// ValidateBasic validates the mempools configuration.
|
||||
// ValidateBasic validates the mempools configuration. ValidateBasic ensures
|
||||
// the following:
|
||||
// - The sum of the lane max block space percentages is less than or equal to 1.
|
||||
// - There is no unused block space.
|
||||
func (m *BBMempool) ValidateBasic() error {
|
||||
sum := math.LegacyZeroDec()
|
||||
seenZeroMaxBlockSpace := false
|
||||
|
||||
@ -27,9 +27,10 @@ type BlockBusterTestSuite struct {
|
||||
encodingConfig testutils.EncodingConfig
|
||||
|
||||
// Define all of the lanes utilized in the test suite
|
||||
tobLane *auction.TOBLane
|
||||
baseLane *base.DefaultLane
|
||||
freeLane *free.Lane
|
||||
tobLane *auction.TOBLane
|
||||
baseLane *base.DefaultLane
|
||||
freeLane *free.FreeLane
|
||||
gasTokenDenom string
|
||||
|
||||
lanes []blockbuster.Lane
|
||||
mempool blockbuster.Mempool
|
||||
@ -55,7 +56,8 @@ func (suite *BlockBusterTestSuite) SetupTest() {
|
||||
// Lanes configuration
|
||||
//
|
||||
// TOB lane set up
|
||||
tobConfig := blockbuster.BaseLaneConfig{
|
||||
suite.gasTokenDenom = "stake"
|
||||
tobConfig := blockbuster.LaneConfig{
|
||||
Logger: log.NewNopLogger(),
|
||||
TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(),
|
||||
TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(),
|
||||
@ -64,37 +66,30 @@ func (suite *BlockBusterTestSuite) SetupTest() {
|
||||
}
|
||||
suite.tobLane = auction.NewTOBLane(
|
||||
tobConfig,
|
||||
0, // No bound on the number of transactions in the lane
|
||||
auction.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder()),
|
||||
)
|
||||
|
||||
// Free lane set up
|
||||
freeConfig := blockbuster.BaseLaneConfig{
|
||||
freeConfig := blockbuster.LaneConfig{
|
||||
Logger: log.NewNopLogger(),
|
||||
TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(),
|
||||
TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(),
|
||||
AnteHandler: nil,
|
||||
MaxBlockSpace: math.LegacyZeroDec(),
|
||||
IgnoreList: []blockbuster.Lane{
|
||||
suite.tobLane,
|
||||
},
|
||||
}
|
||||
suite.freeLane = free.NewFreeLane(
|
||||
freeConfig,
|
||||
free.NewDefaultFreeFactory(suite.encodingConfig.TxConfig.TxDecoder()),
|
||||
blockbuster.DefaultTxPriority(),
|
||||
free.DefaultMatchHandler(),
|
||||
)
|
||||
|
||||
// Base lane set up
|
||||
baseConfig := blockbuster.BaseLaneConfig{
|
||||
baseConfig := blockbuster.LaneConfig{
|
||||
Logger: log.NewNopLogger(),
|
||||
TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(),
|
||||
TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(),
|
||||
AnteHandler: nil,
|
||||
MaxBlockSpace: math.LegacyZeroDec(),
|
||||
IgnoreList: []blockbuster.Lane{
|
||||
suite.tobLane,
|
||||
suite.freeLane,
|
||||
},
|
||||
}
|
||||
suite.baseLane = base.NewDefaultLane(
|
||||
baseConfig,
|
||||
@ -102,7 +97,7 @@ func (suite *BlockBusterTestSuite) SetupTest() {
|
||||
|
||||
// Mempool set up
|
||||
suite.lanes = []blockbuster.Lane{suite.tobLane, suite.freeLane, suite.baseLane}
|
||||
suite.mempool = blockbuster.NewMempool(log.NewTestLogger(suite.T()), suite.lanes...)
|
||||
suite.mempool = blockbuster.NewMempool(log.NewTestLogger(suite.T()), true, suite.lanes...)
|
||||
|
||||
// Accounts set up
|
||||
suite.accounts = testutils.RandomAccounts(suite.random, 10)
|
||||
@ -328,12 +323,12 @@ func (suite *BlockBusterTestSuite) fillBaseLane(numTxs int) {
|
||||
// create a few random msgs and construct the tx
|
||||
nonce := suite.nonces[acc.Address.String()]
|
||||
randomMsgs := testutils.CreateRandomMsgs(acc.Address, 3)
|
||||
tx, err := testutils.CreateTx(suite.encodingConfig.TxConfig, acc, nonce, 1000, randomMsgs)
|
||||
priority := suite.random.Int63n(100) + 1
|
||||
tx, err := testutils.CreateTx(suite.encodingConfig.TxConfig, acc, nonce, 1000, randomMsgs, sdk.NewCoin(suite.gasTokenDenom, math.NewInt(priority)))
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// insert the tx into the lane and update the account
|
||||
suite.nonces[acc.Address.String()]++
|
||||
priority := suite.random.Int63n(100) + 1
|
||||
suite.Require().NoError(suite.mempool.Insert(suite.ctx.WithPriority(priority), tx))
|
||||
}
|
||||
}
|
||||
@ -348,7 +343,7 @@ func (suite *BlockBusterTestSuite) fillTOBLane(numTxs int) {
|
||||
// create a randomized auction transaction
|
||||
nonce := suite.nonces[acc.Address.String()]
|
||||
bidAmount := math.NewInt(int64(suite.random.Intn(1000) + 1))
|
||||
bid := sdk.NewCoin("stake", bidAmount)
|
||||
bid := sdk.NewCoin(suite.gasTokenDenom, bidAmount)
|
||||
tx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, acc, bid, nonce, 1000, nil)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
@ -367,7 +362,7 @@ func (suite *BlockBusterTestSuite) fillFreeLane(numTxs int) {
|
||||
|
||||
// create a few random msgs and construct the tx
|
||||
nonce := suite.nonces[acc.Address.String()]
|
||||
tx, err := testutils.CreateFreeTx(suite.encodingConfig.TxConfig, acc, nonce, 1000, "val1", sdk.NewCoin("stake", math.NewInt(100)))
|
||||
tx, err := testutils.CreateFreeTx(suite.encodingConfig.TxConfig, acc, nonce, 1000, "val1", sdk.NewCoin(suite.gasTokenDenom, math.NewInt(100)), sdk.NewCoin(suite.gasTokenDenom, math.NewInt(100)))
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// insert the tx into the lane and update the account
|
||||
|
||||
@ -313,14 +313,16 @@ func (i *PriorityNonceIterator[C]) Next() sdkmempool.Iterator {
|
||||
|
||||
// We've reached a transaction with a priority lower than the next highest
|
||||
// priority in the pool.
|
||||
if i.mempool.cfg.TxPriority.Compare(key.priority, i.nextPriority) < 0 {
|
||||
return i.iteratePriority()
|
||||
} else if i.mempool.cfg.TxPriority.Compare(key.priority, i.nextPriority) == 0 {
|
||||
// Weight is incorporated into the priority index key only (not sender index)
|
||||
// so we must fetch it here from the scores map.
|
||||
weight := i.mempool.scores[txMeta[C]{nonce: key.nonce, sender: key.sender}].weight
|
||||
if i.mempool.cfg.TxPriority.Compare(weight, i.priorityNode.Next().Key().(txMeta[C]).weight) < 0 {
|
||||
if i.priorityNode.Next() != nil {
|
||||
if i.mempool.cfg.TxPriority.Compare(key.priority, i.nextPriority) < 0 {
|
||||
return i.iteratePriority()
|
||||
} else if i.mempool.cfg.TxPriority.Compare(key.priority, i.nextPriority) == 0 {
|
||||
// Weight is incorporated into the priority index key only (not sender index)
|
||||
// so we must fetch it here from the scores map.
|
||||
weight := i.mempool.scores[txMeta[C]{nonce: key.nonce, sender: key.sender}].weight
|
||||
if i.mempool.cfg.TxPriority.Compare(weight, i.priorityNode.Next().Key().(txMeta[C]).weight) < 0 {
|
||||
return i.iteratePriority()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -131,14 +131,6 @@ func (p *Proposal) UpdateProposal(lane LaneProposal, partialProposalTxs [][]byte
|
||||
}
|
||||
p.totalTxBytes = updatedSize
|
||||
|
||||
lane.Logger().Info(
|
||||
"adding transactions to proposal",
|
||||
"lane", lane.Name(),
|
||||
"num_txs", len(partialProposalTxs),
|
||||
"total_tx_bytes", partialProposalSize,
|
||||
"cumulative_size", updatedSize,
|
||||
)
|
||||
|
||||
p.txs = append(p.txs, partialProposalTxs...)
|
||||
|
||||
for _, tx := range partialProposalTxs {
|
||||
@ -146,8 +138,23 @@ func (p *Proposal) UpdateProposal(lane LaneProposal, partialProposalTxs [][]byte
|
||||
txHashStr := hex.EncodeToString(txHash[:])
|
||||
|
||||
p.cache[txHashStr] = struct{}{}
|
||||
|
||||
lane.Logger().Info(
|
||||
"added transaction to proposal",
|
||||
"lane", lane.Name(),
|
||||
"tx_hash", txHashStr,
|
||||
"tx_bytes", len(tx),
|
||||
)
|
||||
}
|
||||
|
||||
lane.Logger().Info(
|
||||
"added transactions to proposal",
|
||||
"lane", lane.Name(),
|
||||
"num_txs", len(partialProposalTxs),
|
||||
"total_tx_bytes", partialProposalSize,
|
||||
"cumulative_size", updatedSize,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
164
blockbuster/types.go
Normal file
164
blockbuster/types.go
Normal file
@ -0,0 +1,164 @@
|
||||
package blockbuster
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/math"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
type (
|
||||
// MatchHandler is utilized to determine if a transaction should be included in the lane. This
|
||||
// function can be a stateless or stateful check on the transaction.
|
||||
MatchHandler func(ctx sdk.Context, tx sdk.Tx) bool
|
||||
|
||||
// PrepareLaneHandler is responsible for preparing transactions to be included in the block from a
|
||||
// given lane. Given a lane, this function should return the transactions to include in the block,
|
||||
// the transactions that must be removed from the lane, and an error if one occurred.
|
||||
PrepareLaneHandler func(
|
||||
ctx sdk.Context,
|
||||
proposal BlockProposal,
|
||||
maxTxBytes int64,
|
||||
) (txsToInclude [][]byte, txsToRemove []sdk.Tx, err error)
|
||||
|
||||
// ProcessLaneHandler is responsible for processing transactions that are included in a block and
|
||||
// belong to a given lane. ProcessLaneHandler is executed after CheckOrderHandler so the transactions
|
||||
// passed into this function SHOULD already be in order respecting the ordering rules of the lane and
|
||||
// respecting the ordering rules of mempool relative to the lanes it has.
|
||||
ProcessLaneHandler func(ctx sdk.Context, txs []sdk.Tx) ([]sdk.Tx, error)
|
||||
|
||||
// CheckOrderHandler is responsible for checking the order of transactions that belong to a given
|
||||
// lane. This handler should be used to verify that the ordering of transactions passed into the
|
||||
// function respect the ordering logic of the lane (if any transactions from the lane are included).
|
||||
// This function should also ensure that transactions that belong to this lane are contiguous and do
|
||||
// not have any transactions from other lanes in between them.
|
||||
CheckOrderHandler func(ctx sdk.Context, txs []sdk.Tx) error
|
||||
|
||||
// PrepareLanesHandler wraps all of the lanes' PrepareLane function into a single chained
|
||||
// function. You can think of it like an AnteHandler, but for preparing proposals in the
|
||||
// context of lanes instead of modules.
|
||||
PrepareLanesHandler func(ctx sdk.Context, proposal BlockProposal) (BlockProposal, error)
|
||||
|
||||
// ProcessLanesHandler wraps all of the lanes' ProcessLane functions into a single chained
|
||||
// function. You can think of it like an AnteHandler, but for processing proposals in the
|
||||
// context of lanes instead of modules.
|
||||
ProcessLanesHandler func(ctx sdk.Context, txs []sdk.Tx) (sdk.Context, error)
|
||||
|
||||
// LaneConfig defines the basic functionality needed for a lane.
|
||||
LaneConfig struct {
|
||||
Logger log.Logger
|
||||
TxEncoder sdk.TxEncoder
|
||||
TxDecoder sdk.TxDecoder
|
||||
AnteHandler sdk.AnteHandler
|
||||
|
||||
// MaxBlockSpace defines the relative percentage of block space that can be
|
||||
// used by this lane. NOTE: If this is set to zero, then there is no limit
|
||||
// on the number of transactions that can be included in the block for this
|
||||
// lane (up to maxTxBytes as provided by the request). This is useful for the default lane.
|
||||
MaxBlockSpace math.LegacyDec
|
||||
|
||||
// IgnoreList defines the list of lanes to ignore when processing transactions. This
|
||||
// is useful for when you want lanes to exist after the default lane. For example,
|
||||
// say there are two lanes: default and free. The free lane should be processed after
|
||||
// the default lane. In this case, the free lane should be added to the ignore list
|
||||
// of the default lane. Otherwise, the transactions that belong to the free lane
|
||||
// will be processed by the default lane (which accepts all transactions by default).
|
||||
IgnoreList []Lane
|
||||
|
||||
// MaxTxs sets the maximum number of transactions allowed in the mempool with
|
||||
// the semantics:
|
||||
// - if MaxTx == 0, there is no cap on the number of transactions in the mempool
|
||||
// - if MaxTx > 0, the mempool will cap the number of transactions it stores,
|
||||
// and will prioritize transactions by their priority and sender-nonce
|
||||
// (sequence number) when evicting transactions.
|
||||
// - if MaxTx < 0, `Insert` is a no-op.
|
||||
MaxTxs int
|
||||
}
|
||||
)
|
||||
|
||||
// NewLaneConfig returns a new LaneConfig. This will be embedded in a lane.
|
||||
func NewBaseLaneConfig(
|
||||
logger log.Logger,
|
||||
txEncoder sdk.TxEncoder,
|
||||
txDecoder sdk.TxDecoder,
|
||||
anteHandler sdk.AnteHandler,
|
||||
maxBlockSpace math.LegacyDec,
|
||||
) LaneConfig {
|
||||
return LaneConfig{
|
||||
Logger: logger,
|
||||
TxEncoder: txEncoder,
|
||||
TxDecoder: txDecoder,
|
||||
AnteHandler: anteHandler,
|
||||
MaxBlockSpace: maxBlockSpace,
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateBasic validates the lane configuration.
|
||||
func (c *LaneConfig) ValidateBasic() error {
|
||||
if c.Logger == nil {
|
||||
return fmt.Errorf("logger cannot be nil")
|
||||
}
|
||||
|
||||
if c.TxEncoder == nil {
|
||||
return fmt.Errorf("tx encoder cannot be nil")
|
||||
}
|
||||
|
||||
if c.TxDecoder == nil {
|
||||
return fmt.Errorf("tx decoder cannot be nil")
|
||||
}
|
||||
|
||||
if c.MaxBlockSpace.IsNil() || c.MaxBlockSpace.IsNegative() || c.MaxBlockSpace.GT(math.LegacyOneDec()) {
|
||||
return fmt.Errorf("max block space must be set to a value between 0 and 1")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NoOpPrepareLanesHandler returns a no-op prepare lanes handler.
|
||||
// This should only be used for testing.
|
||||
func NoOpPrepareLanesHandler() PrepareLanesHandler {
|
||||
return func(ctx sdk.Context, proposal BlockProposal) (BlockProposal, error) {
|
||||
return proposal, nil
|
||||
}
|
||||
}
|
||||
|
||||
// NoOpPrepareLaneHandler returns a no-op prepare lane handler.
|
||||
// This should only be used for testing.
|
||||
func NoOpPrepareLaneHandler() PrepareLaneHandler {
|
||||
return func(ctx sdk.Context, proposal BlockProposal, maxTxBytes int64) (txsToInclude [][]byte, txsToRemove []sdk.Tx, err error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// PanicPrepareLaneHandler returns a prepare lane handler that panics.
|
||||
// This should only be used for testing.
|
||||
func PanicPrepareLaneHandler() PrepareLaneHandler {
|
||||
return func(sdk.Context, BlockProposal, int64) (txsToInclude [][]byte, txsToRemove []sdk.Tx, err error) {
|
||||
panic("panic prepare lanes handler")
|
||||
}
|
||||
}
|
||||
|
||||
// NoOpProcessLanesHandler returns a no-op process lanes handler.
|
||||
// This should only be used for testing.
|
||||
func NoOpProcessLanesHandler() ProcessLanesHandler {
|
||||
return func(ctx sdk.Context, txs []sdk.Tx) (sdk.Context, error) {
|
||||
return ctx, nil
|
||||
}
|
||||
}
|
||||
|
||||
// NoOpProcessLaneHandler returns a no-op process lane handler.
|
||||
// This should only be used for testing.
|
||||
func NoOpProcessLaneHandler() ProcessLaneHandler {
|
||||
return func(ctx sdk.Context, txs []sdk.Tx) ([]sdk.Tx, error) {
|
||||
return txs, nil
|
||||
}
|
||||
}
|
||||
|
||||
// PanicProcessLanesHandler returns a process lanes handler that panics.
|
||||
// This should only be used for testing.
|
||||
func PanicProcessLaneHandler() ProcessLaneHandler {
|
||||
return func(sdk.Context, []sdk.Tx) ([]sdk.Tx, error) {
|
||||
panic("panic process lanes handler")
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
// Lane defines the required functionality for a lane. The ignore decorator
|
||||
// Lane defines the required API dependencies for the IgnoreDecorator. The ignore decorator
|
||||
// will check if a transaction belongs to a lane by calling the Match function.
|
||||
Lane interface {
|
||||
Match(ctx sdk.Context, tx sdk.Tx) bool
|
||||
|
||||
256
blockbuster/utils/mocks/lane.go
Normal file
256
blockbuster/utils/mocks/lane.go
Normal file
@ -0,0 +1,256 @@
|
||||
// Code generated by mockery v2.30.1. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
blockbuster "github.com/skip-mev/pob/blockbuster"
|
||||
|
||||
log "cosmossdk.io/log"
|
||||
|
||||
math "cosmossdk.io/math"
|
||||
|
||||
mempool "github.com/cosmos/cosmos-sdk/types/mempool"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
types "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Lane is an autogenerated mock type for the Lane type
|
||||
type Lane struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// CheckOrder provides a mock function with given fields: ctx, txs
|
||||
func (_m *Lane) CheckOrder(ctx types.Context, txs []types.Tx) error {
|
||||
ret := _m.Called(ctx, txs)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(types.Context, []types.Tx) error); ok {
|
||||
r0 = rf(ctx, txs)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Compare provides a mock function with given fields: ctx, this, other
|
||||
func (_m *Lane) Compare(ctx types.Context, this types.Tx, other types.Tx) int {
|
||||
ret := _m.Called(ctx, this, other)
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func(types.Context, types.Tx, types.Tx) int); ok {
|
||||
r0 = rf(ctx, this, other)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Contains provides a mock function with given fields: tx
|
||||
func (_m *Lane) Contains(tx types.Tx) bool {
|
||||
ret := _m.Called(tx)
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func(types.Tx) bool); ok {
|
||||
r0 = rf(tx)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// CountTx provides a mock function with given fields:
|
||||
func (_m *Lane) CountTx() int {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func() int); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// GetMaxBlockSpace provides a mock function with given fields:
|
||||
func (_m *Lane) GetMaxBlockSpace() math.LegacyDec {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 math.LegacyDec
|
||||
if rf, ok := ret.Get(0).(func() math.LegacyDec); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(math.LegacyDec)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Insert provides a mock function with given fields: _a0, _a1
|
||||
func (_m *Lane) Insert(_a0 context.Context, _a1 types.Tx) error {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, types.Tx) error); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Logger provides a mock function with given fields:
|
||||
func (_m *Lane) Logger() log.Logger {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 log.Logger
|
||||
if rf, ok := ret.Get(0).(func() log.Logger); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(log.Logger)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Match provides a mock function with given fields: ctx, tx
|
||||
func (_m *Lane) Match(ctx types.Context, tx types.Tx) bool {
|
||||
ret := _m.Called(ctx, tx)
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func(types.Context, types.Tx) bool); ok {
|
||||
r0 = rf(ctx, tx)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Name provides a mock function with given fields:
|
||||
func (_m *Lane) Name() string {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func() string); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// PrepareLane provides a mock function with given fields: ctx, proposal, maxTxBytes, next
|
||||
func (_m *Lane) PrepareLane(ctx types.Context, proposal blockbuster.BlockProposal, maxTxBytes int64, next blockbuster.PrepareLanesHandler) (blockbuster.BlockProposal, error) {
|
||||
ret := _m.Called(ctx, proposal, maxTxBytes, next)
|
||||
|
||||
var r0 blockbuster.BlockProposal
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(types.Context, blockbuster.BlockProposal, int64, blockbuster.PrepareLanesHandler) (blockbuster.BlockProposal, error)); ok {
|
||||
return rf(ctx, proposal, maxTxBytes, next)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(types.Context, blockbuster.BlockProposal, int64, blockbuster.PrepareLanesHandler) blockbuster.BlockProposal); ok {
|
||||
r0 = rf(ctx, proposal, maxTxBytes, next)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(blockbuster.BlockProposal)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(types.Context, blockbuster.BlockProposal, int64, blockbuster.PrepareLanesHandler) error); ok {
|
||||
r1 = rf(ctx, proposal, maxTxBytes, next)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// ProcessLane provides a mock function with given fields: ctx, proposalTxs, next
|
||||
func (_m *Lane) ProcessLane(ctx types.Context, proposalTxs []types.Tx, next blockbuster.ProcessLanesHandler) (types.Context, error) {
|
||||
ret := _m.Called(ctx, proposalTxs, next)
|
||||
|
||||
var r0 types.Context
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(types.Context, []types.Tx, blockbuster.ProcessLanesHandler) (types.Context, error)); ok {
|
||||
return rf(ctx, proposalTxs, next)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(types.Context, []types.Tx, blockbuster.ProcessLanesHandler) types.Context); ok {
|
||||
r0 = rf(ctx, proposalTxs, next)
|
||||
} else {
|
||||
r0 = ret.Get(0).(types.Context)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(types.Context, []types.Tx, blockbuster.ProcessLanesHandler) error); ok {
|
||||
r1 = rf(ctx, proposalTxs, next)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Remove provides a mock function with given fields: _a0
|
||||
func (_m *Lane) Remove(_a0 types.Tx) error {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(types.Tx) error); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Select provides a mock function with given fields: _a0, _a1
|
||||
func (_m *Lane) Select(_a0 context.Context, _a1 [][]byte) mempool.Iterator {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 mempool.Iterator
|
||||
if rf, ok := ret.Get(0).(func(context.Context, [][]byte) mempool.Iterator); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(mempool.Iterator)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SetAnteHandler provides a mock function with given fields: antehander
|
||||
func (_m *Lane) SetAnteHandler(antehander types.AnteHandler) {
|
||||
_m.Called(antehander)
|
||||
}
|
||||
|
||||
// SetIgnoreList provides a mock function with given fields: ignoreList
|
||||
func (_m *Lane) SetIgnoreList(ignoreList []blockbuster.Lane) {
|
||||
_m.Called(ignoreList)
|
||||
}
|
||||
|
||||
// NewLane creates a new instance of Lane. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewLane(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Lane {
|
||||
mock := &Lane{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
117
blockbuster/utils/mocks/lane_mempool.go
Normal file
117
blockbuster/utils/mocks/lane_mempool.go
Normal file
@ -0,0 +1,117 @@
|
||||
// Code generated by mockery v2.30.1. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
mempool "github.com/cosmos/cosmos-sdk/types/mempool"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
types "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// LaneMempool is an autogenerated mock type for the LaneMempool type
|
||||
type LaneMempool struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Compare provides a mock function with given fields: ctx, this, other
|
||||
func (_m *LaneMempool) Compare(ctx types.Context, this types.Tx, other types.Tx) int {
|
||||
ret := _m.Called(ctx, this, other)
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func(types.Context, types.Tx, types.Tx) int); ok {
|
||||
r0 = rf(ctx, this, other)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Contains provides a mock function with given fields: tx
|
||||
func (_m *LaneMempool) Contains(tx types.Tx) bool {
|
||||
ret := _m.Called(tx)
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func(types.Tx) bool); ok {
|
||||
r0 = rf(tx)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// CountTx provides a mock function with given fields:
|
||||
func (_m *LaneMempool) CountTx() int {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func() int); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Insert provides a mock function with given fields: _a0, _a1
|
||||
func (_m *LaneMempool) Insert(_a0 context.Context, _a1 types.Tx) error {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, types.Tx) error); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Remove provides a mock function with given fields: _a0
|
||||
func (_m *LaneMempool) Remove(_a0 types.Tx) error {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(types.Tx) error); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Select provides a mock function with given fields: _a0, _a1
|
||||
func (_m *LaneMempool) Select(_a0 context.Context, _a1 [][]byte) mempool.Iterator {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 mempool.Iterator
|
||||
if rf, ok := ret.Get(0).(func(context.Context, [][]byte) mempool.Iterator); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(mempool.Iterator)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewLaneMempool creates a new instance of LaneMempool. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewLaneMempool(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *LaneMempool {
|
||||
mock := &LaneMempool{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
@ -40,8 +40,8 @@ func GetDecodedTxs(txDecoder sdk.TxDecoder, txs [][]byte) ([]sdk.Tx, error) {
|
||||
}
|
||||
|
||||
// RemoveTxsFromLane removes the transactions from the given lane's mempool.
|
||||
func RemoveTxsFromLane(txs map[sdk.Tx]struct{}, mempool sdkmempool.Mempool) error {
|
||||
for tx := range txs {
|
||||
func RemoveTxsFromLane(txs []sdk.Tx, mempool sdkmempool.Mempool) error {
|
||||
for _, tx := range txs {
|
||||
if err := mempool.Remove(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
23
go.mod
23
go.mod
@ -20,14 +20,12 @@ require (
|
||||
github.com/cosmos/cosmos-db v1.0.0
|
||||
github.com/cosmos/cosmos-proto v1.0.0-beta.3
|
||||
github.com/cosmos/cosmos-sdk v0.50.0-beta.0
|
||||
github.com/cosmos/go-bip39 v1.0.0
|
||||
github.com/cosmos/gogoproto v1.4.10
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/golang/protobuf v1.5.3
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0
|
||||
github.com/huandu/skiplist v1.2.0
|
||||
github.com/ory/dockertest/v3 v3.10.0
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.16.0
|
||||
@ -47,10 +45,7 @@ require (
|
||||
filippo.io/edwards25519 v1.0.0 // indirect
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
|
||||
github.com/99designs/keyring v1.2.1 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/DataDog/zstd v1.5.5 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
||||
github.com/aws/aws-sdk-go v1.44.224 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
||||
@ -68,8 +63,8 @@ require (
|
||||
github.com/cockroachdb/redact v1.1.5 // indirect
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20230613231145-182959a1fad6 // indirect
|
||||
github.com/cometbft/cometbft-db v0.8.0 // indirect
|
||||
github.com/containerd/continuity v0.3.0 // indirect
|
||||
github.com/cosmos/btcutil v1.0.5 // indirect
|
||||
github.com/cosmos/go-bip39 v1.0.0 // indirect
|
||||
github.com/cosmos/gogogateway v1.2.0 // indirect
|
||||
github.com/cosmos/iavl v1.0.0-beta.2 // indirect
|
||||
github.com/cosmos/ics23/go v0.10.0 // indirect
|
||||
@ -83,10 +78,6 @@ require (
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
|
||||
github.com/docker/cli v23.0.1+incompatible // indirect
|
||||
github.com/docker/docker v23.0.1+incompatible // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
|
||||
github.com/emicklei/dot v1.5.0 // indirect
|
||||
@ -107,7 +98,6 @@ require (
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/orderedcode v0.0.1 // indirect
|
||||
github.com/google/s2a-go v0.1.4 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.11.0 // indirect
|
||||
@ -128,7 +118,6 @@ require (
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/hdevalence/ed25519consensus v0.1.0 // indirect
|
||||
github.com/iancoleman/strcase v0.3.0 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/improbable-eng/grpc-web v0.15.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
@ -148,12 +137,9 @@ require (
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
|
||||
github.com/mtibben/percent v0.2.1 // indirect
|
||||
github.com/oasisprotocol/curve25519-voi v0.0.0-20230110094441-db37f07504ce // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2 // indirect
|
||||
github.com/opencontainers/runc v1.1.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.9 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20230518223814-80aa455d8761 // indirect
|
||||
@ -168,32 +154,27 @@ require (
|
||||
github.com/rs/cors v1.8.3 // indirect
|
||||
github.com/rs/zerolog v1.30.0 // indirect
|
||||
github.com/sasha-s/go-deadlock v0.3.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/spf13/afero v1.9.5 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/subosito/gotenv v1.4.2 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
||||
github.com/tendermint/go-amino v0.16.0 // indirect
|
||||
github.com/tidwall/btree v1.6.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.11 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/zondax/hid v0.9.1 // indirect
|
||||
github.com/zondax/ledger-go v0.14.1 // indirect
|
||||
go.etcd.io/bbolt v1.3.7 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
golang.org/x/crypto v0.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb // indirect
|
||||
golang.org/x/mod v0.11.0 // indirect
|
||||
golang.org/x/net v0.12.0 // indirect
|
||||
golang.org/x/oauth2 v0.8.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/sys v0.10.0 // indirect
|
||||
golang.org/x/term v0.10.0 // indirect
|
||||
golang.org/x/text v0.11.0 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/api v0.126.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
|
||||
34
go.sum
34
go.sum
@ -221,7 +221,6 @@ filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5E
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
@ -229,9 +228,7 @@ github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=
|
||||
github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
|
||||
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
@ -334,7 +331,6 @@ github.com/cometbft/cometbft-db v0.8.0 h1:vUMDaH3ApkX8m0KZvOFFy9b5DZHBAjsnEuo9AK
|
||||
github.com/cometbft/cometbft-db v0.8.0/go.mod h1:6ASCP4pfhmrCBpfk01/9E1SI29nD3HfVHrY4PG8x5c0=
|
||||
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
|
||||
github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
|
||||
github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
@ -374,7 +370,6 @@ github.com/creachadair/tomledit v0.0.24 h1:5Xjr25R2esu1rKCbQEmjZYlrhFkDspoAbAKb6
|
||||
github.com/creachadair/tomledit v0.0.24/go.mod h1:9qHbShRWQzSCcn617cMzg4eab1vbLCOjOshAWSzWr8U=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
|
||||
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
|
||||
@ -395,15 +390,9 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/docker/cli v23.0.1+incompatible h1:LRyWITpGzl2C9e9uGxzisptnxAn1zfZKXy13Ul2Q5oM=
|
||||
github.com/docker/cli v23.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/docker v23.0.1+incompatible h1:vjgvJZxprTTE1A37nm+CLNAdwu6xZekyoiVlUZEINcY=
|
||||
github.com/docker/docker v23.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
@ -475,7 +464,6 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
@ -590,8 +578,6 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
|
||||
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
@ -693,8 +679,6 @@ github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSAS
|
||||
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||
github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ=
|
||||
github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
@ -794,8 +778,6 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
|
||||
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
|
||||
github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@ -837,9 +819,7 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
|
||||
github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ=
|
||||
github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs=
|
||||
github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
|
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
@ -853,8 +833,6 @@ github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJ
|
||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA=
|
||||
github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4=
|
||||
github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg=
|
||||
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
@ -943,7 +921,6 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
@ -1014,13 +991,6 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@ -1117,7 +1087,6 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
|
||||
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -1321,7 +1290,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -1417,7 +1385,6 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@ -1691,7 +1658,6 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
|
||||
|
||||
@ -139,7 +139,7 @@ type TestApp struct {
|
||||
FeeGrantKeeper feegrantkeeper.Keeper
|
||||
|
||||
// custom checkTx handler
|
||||
checkTxHandler abci.CheckTx
|
||||
checkTxHandler auction.CheckTx
|
||||
}
|
||||
|
||||
func init() {
|
||||
@ -262,43 +262,39 @@ func New(
|
||||
// NOTE: The lanes are ordered by priority. The first lane is the highest priority
|
||||
// lane and the last lane is the lowest priority lane.
|
||||
// Top of block lane allows transactions to bid for inclusion at the top of the next block.
|
||||
tobConfig := blockbuster.BaseLaneConfig{
|
||||
tobConfig := blockbuster.LaneConfig{
|
||||
Logger: app.Logger(),
|
||||
TxEncoder: app.txConfig.TxEncoder(),
|
||||
TxDecoder: app.txConfig.TxDecoder(),
|
||||
MaxBlockSpace: math.LegacyZeroDec(),
|
||||
MaxBlockSpace: math.LegacyZeroDec(), // This means the lane has no limit on block space.
|
||||
MaxTxs: 0, // This means the lane has no limit on the number of transactions it can store.
|
||||
}
|
||||
tobLane := auction.NewTOBLane(
|
||||
tobConfig,
|
||||
0,
|
||||
auction.NewDefaultAuctionFactory(app.txConfig.TxDecoder()),
|
||||
)
|
||||
|
||||
// Free lane allows transactions to be included in the next block for free.
|
||||
freeConfig := blockbuster.BaseLaneConfig{
|
||||
freeConfig := blockbuster.LaneConfig{
|
||||
Logger: app.Logger(),
|
||||
TxEncoder: app.txConfig.TxEncoder(),
|
||||
TxDecoder: app.txConfig.TxDecoder(),
|
||||
MaxBlockSpace: math.LegacyZeroDec(),
|
||||
IgnoreList: []blockbuster.Lane{
|
||||
tobLane,
|
||||
},
|
||||
MaxTxs: 0,
|
||||
}
|
||||
freeLane := free.NewFreeLane(
|
||||
freeConfig,
|
||||
free.NewDefaultFreeFactory(app.txConfig.TxDecoder()),
|
||||
blockbuster.DefaultTxPriority(),
|
||||
free.DefaultMatchHandler(),
|
||||
)
|
||||
|
||||
// Default lane accepts all other transactions.
|
||||
defaultConfig := blockbuster.BaseLaneConfig{
|
||||
defaultConfig := blockbuster.LaneConfig{
|
||||
Logger: app.Logger(),
|
||||
TxEncoder: app.txConfig.TxEncoder(),
|
||||
TxDecoder: app.txConfig.TxDecoder(),
|
||||
MaxBlockSpace: math.LegacyZeroDec(),
|
||||
IgnoreList: []blockbuster.Lane{
|
||||
tobLane,
|
||||
freeLane,
|
||||
},
|
||||
MaxTxs: 0,
|
||||
}
|
||||
defaultLane := base.NewDefaultLane(defaultConfig)
|
||||
|
||||
@ -308,7 +304,7 @@ func New(
|
||||
freeLane,
|
||||
defaultLane,
|
||||
}
|
||||
mempool := blockbuster.NewMempool(app.Logger(), lanes...)
|
||||
mempool := blockbuster.NewMempool(app.Logger(), true, lanes...)
|
||||
app.App.SetMempool(mempool)
|
||||
|
||||
// Create a global ante handler that will be called on each transaction when
|
||||
@ -337,17 +333,17 @@ func New(
|
||||
}
|
||||
app.App.SetAnteHandler(anteHandler)
|
||||
|
||||
// Set the proposal handlers on base app
|
||||
// Set the abci handlers on base app
|
||||
proposalHandler := abci.NewProposalHandler(
|
||||
app.Logger(),
|
||||
app.TxConfig().TxDecoder(),
|
||||
mempool,
|
||||
lanes,
|
||||
)
|
||||
app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler())
|
||||
app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler())
|
||||
|
||||
// Set the custom CheckTx handler on BaseApp.
|
||||
checkTxHandler := abci.NewCheckTxHandler(
|
||||
checkTxHandler := auction.NewCheckTxHandler(
|
||||
app.App,
|
||||
app.txConfig.TxDecoder(),
|
||||
tobLane,
|
||||
@ -396,7 +392,7 @@ func (app *TestApp) CheckTx(req *cometabci.RequestCheckTx) (*cometabci.ResponseC
|
||||
}
|
||||
|
||||
// SetCheckTx sets the checkTxHandler for the app.
|
||||
func (app *TestApp) SetCheckTx(handler abci.CheckTx) {
|
||||
func (app *TestApp) SetCheckTx(handler auction.CheckTx) {
|
||||
app.checkTxHandler = handler
|
||||
}
|
||||
|
||||
|
||||
@ -189,7 +189,6 @@ func BroadcastTxs(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain,
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -5,9 +5,9 @@ import (
|
||||
"testing"
|
||||
|
||||
testutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
|
||||
"github.com/skip-mev/pob/tests/integration"
|
||||
buildertypes "github.com/skip-mev/pob/x/builder/types"
|
||||
"github.com/strangelove-ventures/interchaintest/v7"
|
||||
"github.com/skip-mev/pob/tests/integration"
|
||||
"github.com/strangelove-ventures/interchaintest/v7/chain/cosmos"
|
||||
"github.com/strangelove-ventures/interchaintest/v7/ibc"
|
||||
"github.com/stretchr/testify/suite"
|
||||
@ -16,7 +16,7 @@ import (
|
||||
var (
|
||||
// config params
|
||||
numValidators = 4
|
||||
numFullNodes = 0
|
||||
numFullNodes = 0
|
||||
denom = "stake"
|
||||
|
||||
image = ibc.DockerImage{
|
||||
|
||||
@ -84,7 +84,7 @@ func RandomAccounts(r *rand.Rand, n int) []Account {
|
||||
return accs
|
||||
}
|
||||
|
||||
func CreateTx(txCfg client.TxConfig, account Account, nonce, timeout uint64, msgs []sdk.Msg) (authsigning.Tx, error) {
|
||||
func CreateTx(txCfg client.TxConfig, account Account, nonce, timeout uint64, msgs []sdk.Msg, fees ...sdk.Coin) (authsigning.Tx, error) {
|
||||
txBuilder := txCfg.NewTxBuilder()
|
||||
if err := txBuilder.SetMsgs(msgs...); err != nil {
|
||||
return nil, err
|
||||
@ -104,10 +104,12 @@ func CreateTx(txCfg client.TxConfig, account Account, nonce, timeout uint64, msg
|
||||
|
||||
txBuilder.SetTimeoutHeight(timeout)
|
||||
|
||||
txBuilder.SetFeeAmount(fees)
|
||||
|
||||
return txBuilder.GetTx(), nil
|
||||
}
|
||||
|
||||
func CreateFreeTx(txCfg client.TxConfig, account Account, nonce, timeout uint64, validator string, amount sdk.Coin) (authsigning.Tx, error) {
|
||||
func CreateFreeTx(txCfg client.TxConfig, account Account, nonce, timeout uint64, validator string, amount sdk.Coin, fees ...sdk.Coin) (authsigning.Tx, error) {
|
||||
msgs := []sdk.Msg{
|
||||
&stakingtypes.MsgDelegate{
|
||||
DelegatorAddress: account.Address.String(),
|
||||
@ -116,10 +118,10 @@ func CreateFreeTx(txCfg client.TxConfig, account Account, nonce, timeout uint64,
|
||||
},
|
||||
}
|
||||
|
||||
return CreateTx(txCfg, account, nonce, timeout, msgs)
|
||||
return CreateTx(txCfg, account, nonce, timeout, msgs, fees...)
|
||||
}
|
||||
|
||||
func CreateRandomTx(txCfg client.TxConfig, account Account, nonce, numberMsgs, timeout uint64) (authsigning.Tx, error) {
|
||||
func CreateRandomTx(txCfg client.TxConfig, account Account, nonce, numberMsgs, timeout uint64, fees ...sdk.Coin) (authsigning.Tx, error) {
|
||||
msgs := make([]sdk.Msg, numberMsgs)
|
||||
for i := 0; i < int(numberMsgs); i++ {
|
||||
msgs[i] = &banktypes.MsgSend{
|
||||
@ -147,6 +149,8 @@ func CreateRandomTx(txCfg client.TxConfig, account Account, nonce, numberMsgs, t
|
||||
|
||||
txBuilder.SetTimeoutHeight(timeout)
|
||||
|
||||
txBuilder.SetFeeAmount(fees)
|
||||
|
||||
return txBuilder.GetTx(), nil
|
||||
}
|
||||
|
||||
@ -189,6 +193,53 @@ func CreateTxWithSigners(txCfg client.TxConfig, nonce, timeout uint64, signers [
|
||||
return txBuilder.GetTx(), nil
|
||||
}
|
||||
|
||||
func CreateAuctionTx(txCfg client.TxConfig, bidder Account, bid sdk.Coin, nonce, timeout uint64, signers []Account) (authsigning.Tx, []authsigning.Tx, error) {
|
||||
bidMsg := &buildertypes.MsgAuctionBid{
|
||||
Bidder: bidder.Address.String(),
|
||||
Bid: bid,
|
||||
Transactions: make([][]byte, len(signers)),
|
||||
}
|
||||
|
||||
txs := []authsigning.Tx{}
|
||||
|
||||
for i := 0; i < len(signers); i++ {
|
||||
randomMsg := CreateRandomMsgs(signers[i].Address, 1)
|
||||
randomTx, err := CreateTx(txCfg, signers[i], 0, timeout, randomMsg)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
bz, err := txCfg.TxEncoder()(randomTx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
bidMsg.Transactions[i] = bz
|
||||
txs = append(txs, randomTx)
|
||||
}
|
||||
|
||||
txBuilder := txCfg.NewTxBuilder()
|
||||
if err := txBuilder.SetMsgs(bidMsg); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
sigV2 := signing.SignatureV2{
|
||||
PubKey: bidder.PrivKey.PubKey(),
|
||||
Data: &signing.SingleSignatureData{
|
||||
SignMode: signing.SignMode_SIGN_MODE_DIRECT,
|
||||
Signature: nil,
|
||||
},
|
||||
Sequence: nonce,
|
||||
}
|
||||
if err := txBuilder.SetSignatures(sigV2); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
txBuilder.SetTimeoutHeight(timeout)
|
||||
|
||||
return txBuilder.GetTx(), txs, nil
|
||||
}
|
||||
|
||||
func CreateAuctionTxWithSigners(txCfg client.TxConfig, bidder Account, bid sdk.Coin, nonce, timeout uint64, signers []Account) (authsigning.Tx, error) {
|
||||
bidMsg := &buildertypes.MsgAuctionBid{
|
||||
Bidder: bidder.Address.String(),
|
||||
|
||||
@ -83,7 +83,7 @@ func (suite *AnteTestSuite) SetupTest() {
|
||||
// Lanes configuration
|
||||
//
|
||||
// TOB lane set up
|
||||
tobConfig := blockbuster.BaseLaneConfig{
|
||||
tobConfig := blockbuster.LaneConfig{
|
||||
Logger: suite.ctx.Logger(),
|
||||
TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(),
|
||||
TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(),
|
||||
@ -92,12 +92,11 @@ func (suite *AnteTestSuite) SetupTest() {
|
||||
}
|
||||
suite.tobLane = auction.NewTOBLane(
|
||||
tobConfig,
|
||||
0, // No bound on the number of transactions in the lane
|
||||
auction.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder()),
|
||||
)
|
||||
|
||||
// Base lane set up
|
||||
baseConfig := blockbuster.BaseLaneConfig{
|
||||
baseConfig := blockbuster.LaneConfig{
|
||||
Logger: suite.ctx.Logger(),
|
||||
TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(),
|
||||
TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(),
|
||||
@ -109,7 +108,7 @@ func (suite *AnteTestSuite) SetupTest() {
|
||||
|
||||
// Mempool set up
|
||||
suite.lanes = []blockbuster.Lane{suite.tobLane, suite.baseLane}
|
||||
suite.mempool = blockbuster.NewMempool(log.NewTestLogger(suite.T()), suite.lanes...)
|
||||
suite.mempool = blockbuster.NewMempool(log.NewTestLogger(suite.T()), true, suite.lanes...)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) anteHandler(ctx sdk.Context, tx sdk.Tx, _ bool) (sdk.Context, error) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user