From 427194f70c698a4a1ef1cad3a3f1fec7b8bd9597 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Mon, 14 Aug 2023 17:38:20 -0400 Subject: [PATCH 01/41] init --- blockbuster/abci.go | 67 +++++++ blockbuster/handlers.go | 199 +++++++++++++++++++ blockbuster/lane.go | 123 ------------ blockbuster/lane_constructor.go | 196 ++++++++++++++++++ blockbuster/lane_interface.go | 71 +++++++ blockbuster/lane_mempool.go | 116 +++++++++++ blockbuster/lanes/terminator/lane.go | 52 ++--- blockbuster/priority_nonce.go | 16 +- blockbuster/proposals.go | 23 ++- blockbuster/{abci => proposals}/abci.go | 42 ++-- blockbuster/{abci => proposals}/abci_test.go | 2 +- blockbuster/{abci => proposals}/check_tx.go | 2 +- blockbuster/types.go | 164 +++++++++++++++ blockbuster/utils/utils.go | 4 +- 14 files changed, 892 insertions(+), 185 deletions(-) create mode 100644 blockbuster/abci.go create mode 100644 blockbuster/handlers.go delete mode 100644 blockbuster/lane.go create mode 100644 blockbuster/lane_constructor.go create mode 100644 blockbuster/lane_interface.go create mode 100644 blockbuster/lane_mempool.go rename blockbuster/{abci => proposals}/abci.go (84%) rename blockbuster/{abci => proposals}/abci_test.go (99%) rename blockbuster/{abci => proposals}/check_tx.go (99%) create mode 100644 blockbuster/types.go diff --git a/blockbuster/abci.go b/blockbuster/abci.go new file mode 100644 index 0000000..70dd0d0 --- /dev/null +++ b/blockbuster/abci.go @@ -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[C]) 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[C]) 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[C]) 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[C]) 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 +} diff --git a/blockbuster/handlers.go b/blockbuster/handlers.go new file mode 100644 index 0000000..52fb5d7 --- /dev/null +++ b/blockbuster/handlers.go @@ -0,0 +1,199 @@ +package blockbuster + +import ( + "context" + "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[C]) 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[C]) 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[C]) 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 + } +} + +// 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: "", + } +} diff --git a/blockbuster/lane.go b/blockbuster/lane.go deleted file mode 100644 index c0d9bcc..0000000 --- a/blockbuster/lane.go +++ /dev/null @@ -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 -} diff --git a/blockbuster/lane_constructor.go b/blockbuster/lane_constructor.go new file mode 100644 index 0000000..c139978 --- /dev/null +++ b/blockbuster/lane_constructor.go @@ -0,0 +1,196 @@ +package blockbuster + +import ( + "fmt" + + "cosmossdk.io/log" + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// 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[C comparable] struct { + // cfg stores functionality requred 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[C comparable]( + cfg LaneConfig, + laneName string, + laneMempool LaneMempool, + matchHandlerFn MatchHandler, +) *LaneConstructor[C] { + lane := &LaneConstructor[C]{ + 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[C]) 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[C]) 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[C]) 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[C]) 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[C]) 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[C]) 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[C]) 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[C]) SetIgnoreList(lanes []Lane) { + l.cfg.IgnoreList = lanes +} + +// SetAnteHandler sets the ante handler for the lane. +func (l *LaneConstructor[C]) SetAnteHandler(anteHandler sdk.AnteHandler) { + l.cfg.AnteHandler = anteHandler +} + +// Logger returns the logger for the lane. +func (l *LaneConstructor[C]) Logger() log.Logger { + return l.cfg.Logger +} + +// TxDecoder returns the tx decoder for the lane. +func (l *LaneConstructor[C]) TxDecoder() sdk.TxDecoder { + return l.cfg.TxDecoder +} + +// TxEncoder returns the tx encoder for the lane. +func (l *LaneConstructor[C]) 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[C]) GetMaxBlockSpace() math.LegacyDec { + return l.cfg.MaxBlockSpace +} diff --git a/blockbuster/lane_interface.go b/blockbuster/lane_interface.go new file mode 100644 index 0000000..59ee35e --- /dev/null +++ b/blockbuster/lane_interface.go @@ -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 +} diff --git a/blockbuster/lane_mempool.go b/blockbuster/lane_mempool.go new file mode 100644 index 0000000..aa3c9d4 --- /dev/null +++ b/blockbuster/lane_mempool.go @@ -0,0 +1,116 @@ +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{} + } +) + +// 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) +} diff --git a/blockbuster/lanes/terminator/lane.go b/blockbuster/lanes/terminator/lane.go index 159d69a..35e1453 100644 --- a/blockbuster/lanes/terminator/lane.go +++ b/blockbuster/lanes/terminator/lane.go @@ -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 } diff --git a/blockbuster/priority_nonce.go b/blockbuster/priority_nonce.go index 5b55c14..b561d50 100644 --- a/blockbuster/priority_nonce.go +++ b/blockbuster/priority_nonce.go @@ -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() + } } } diff --git a/blockbuster/proposals.go b/blockbuster/proposals.go index 4ea94a1..95da151 100644 --- a/blockbuster/proposals.go +++ b/blockbuster/proposals.go @@ -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 } diff --git a/blockbuster/abci/abci.go b/blockbuster/proposals/abci.go similarity index 84% rename from blockbuster/abci/abci.go rename to blockbuster/proposals/abci.go index 50f418e..a1a0135 100644 --- a/blockbuster/abci/abci.go +++ b/blockbuster/proposals/abci.go @@ -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 a 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 } diff --git a/blockbuster/abci/abci_test.go b/blockbuster/proposals/abci_test.go similarity index 99% rename from blockbuster/abci/abci_test.go rename to blockbuster/proposals/abci_test.go index 4444d4a..b778edf 100644 --- a/blockbuster/abci/abci_test.go +++ b/blockbuster/proposals/abci_test.go @@ -1,4 +1,4 @@ -package abci_test +package proposals_test import ( "math/rand" diff --git a/blockbuster/abci/check_tx.go b/blockbuster/proposals/check_tx.go similarity index 99% rename from blockbuster/abci/check_tx.go rename to blockbuster/proposals/check_tx.go index e9deaf9..7facb9c 100644 --- a/blockbuster/abci/check_tx.go +++ b/blockbuster/proposals/check_tx.go @@ -1,4 +1,4 @@ -package abci +package proposals import ( "context" diff --git a/blockbuster/types.go b/blockbuster/types.go new file mode 100644 index 0000000..d816863 --- /dev/null +++ b/blockbuster/types.go @@ -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") + } +} diff --git a/blockbuster/utils/utils.go b/blockbuster/utils/utils.go index 6edf364..a4b2ae0 100644 --- a/blockbuster/utils/utils.go +++ b/blockbuster/utils/utils.go @@ -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 } From 9b6a86d4d98cce565cc3c7838d6d8157d13329a1 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Mon, 14 Aug 2023 17:42:02 -0400 Subject: [PATCH 02/41] nit rename of files --- blockbuster/{abci.go => lane_abci.go} | 0 blockbuster/{handlers.go => lane_handlers.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename blockbuster/{abci.go => lane_abci.go} (100%) rename blockbuster/{handlers.go => lane_handlers.go} (100%) diff --git a/blockbuster/abci.go b/blockbuster/lane_abci.go similarity index 100% rename from blockbuster/abci.go rename to blockbuster/lane_abci.go diff --git a/blockbuster/handlers.go b/blockbuster/lane_handlers.go similarity index 100% rename from blockbuster/handlers.go rename to blockbuster/lane_handlers.go From f203db880e6fb01f89d6425dd5cf69b3849820f2 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Mon, 14 Aug 2023 17:48:41 -0400 Subject: [PATCH 03/41] more renaming --- blockbuster/proposals/abci.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/blockbuster/proposals/abci.go b/blockbuster/proposals/abci.go index a1a0135..cb7e17c 100644 --- a/blockbuster/proposals/abci.go +++ b/blockbuster/proposals/abci.go @@ -1,4 +1,4 @@ -package abci +package proposals import ( "fmt" @@ -23,7 +23,7 @@ type ( ) // NewProposalHandler returns a new abci++ proposal handler. This proposal handler will -// iteratively call each of the lanes in the chain to prepare and process a proposal. +// 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, @@ -131,7 +131,7 @@ 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 + // We utilize a recover to handler 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. From 7005d4c8a35002e0e77b0b2c68e7198a8f78d563 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Mon, 14 Aug 2023 18:02:08 -0400 Subject: [PATCH 04/41] default re-factor with constructor --- blockbuster/lanes/base/abci.go | 142 ------ blockbuster/lanes/base/abci_test.go | 547 ++++++++++++++++++++++++ blockbuster/lanes/base/base_test.go | 32 ++ blockbuster/lanes/base/lane.go | 97 +---- blockbuster/lanes/base/mempool.go | 105 ----- blockbuster/lanes/base/mempool_test.go | 240 +++++++++++ blockbuster/lanes/free/factory.go | 47 -- blockbuster/lanes/free/lane.go | 55 ++- blockbuster/utils/mocks/lane.go | 256 +++++++++++ blockbuster/utils/mocks/lane_mempool.go | 117 +++++ testutils/utils.go | 59 ++- 11 files changed, 1305 insertions(+), 392 deletions(-) delete mode 100644 blockbuster/lanes/base/abci.go create mode 100644 blockbuster/lanes/base/abci_test.go create mode 100644 blockbuster/lanes/base/base_test.go delete mode 100644 blockbuster/lanes/base/mempool.go create mode 100644 blockbuster/lanes/base/mempool_test.go delete mode 100644 blockbuster/lanes/free/factory.go create mode 100644 blockbuster/utils/mocks/lane.go create mode 100644 blockbuster/utils/mocks/lane_mempool.go diff --git a/blockbuster/lanes/base/abci.go b/blockbuster/lanes/base/abci.go deleted file mode 100644 index b8ada51..0000000 --- a/blockbuster/lanes/base/abci.go +++ /dev/null @@ -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 -} diff --git a/blockbuster/lanes/base/abci_test.go b/blockbuster/lanes/base/abci_test.go new file mode 100644 index 0000000..d5c054d --- /dev/null +++ b/blockbuster/lanes/base/abci_test.go @@ -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 +} diff --git a/blockbuster/lanes/base/base_test.go b/blockbuster/lanes/base/base_test.go new file mode 100644 index 0000000..7852dac --- /dev/null +++ b/blockbuster/lanes/base/base_test.go @@ -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" +} diff --git a/blockbuster/lanes/base/lane.go b/blockbuster/lanes/base/lane.go index a1c4b9b..05b1640 100644 --- a/blockbuster/lanes/base/lane.go +++ b/blockbuster/lanes/base/lane.go @@ -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 similiar 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[string] } // 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[string]( + 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 } diff --git a/blockbuster/lanes/base/mempool.go b/blockbuster/lanes/base/mempool.go deleted file mode 100644 index 54b2388..0000000 --- a/blockbuster/lanes/base/mempool.go +++ /dev/null @@ -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 - // 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 -} diff --git a/blockbuster/lanes/base/mempool_test.go b/blockbuster/lanes/base/mempool_test.go new file mode 100644 index 0000000..4f12e73 --- /dev/null +++ b/blockbuster/lanes/base/mempool_test.go @@ -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) + }) +} diff --git a/blockbuster/lanes/free/factory.go b/blockbuster/lanes/free/factory.go deleted file mode 100644 index c9bd0fa..0000000 --- a/blockbuster/lanes/free/factory.go +++ /dev/null @@ -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 -} diff --git a/blockbuster/lanes/free/lane.go b/blockbuster/lanes/free/lane.go index 6413d1e..312c0d7 100644 --- a/blockbuster/lanes/free/lane.go +++ b/blockbuster/lanes/free/lane.go @@ -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 { + *blockbuster.LaneConstructor[string] } // 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[string]( + 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) } diff --git a/blockbuster/utils/mocks/lane.go b/blockbuster/utils/mocks/lane.go new file mode 100644 index 0000000..16faa82 --- /dev/null +++ b/blockbuster/utils/mocks/lane.go @@ -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 +} diff --git a/blockbuster/utils/mocks/lane_mempool.go b/blockbuster/utils/mocks/lane_mempool.go new file mode 100644 index 0000000..d4116e0 --- /dev/null +++ b/blockbuster/utils/mocks/lane_mempool.go @@ -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 +} diff --git a/testutils/utils.go b/testutils/utils.go index 2a77768..9dd0c5b 100644 --- a/testutils/utils.go +++ b/testutils/utils.go @@ -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(), From 605d94f201449bc328b3de695f91710bb5d256d3 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Mon, 14 Aug 2023 18:18:23 -0400 Subject: [PATCH 05/41] init --- blockbuster/lanes/auction/abci.go | 429 +++-- blockbuster/lanes/auction/auction_test.go | 2 - blockbuster/lanes/auction/factory.go | 11 + blockbuster/lanes/auction/lane.go | 78 +- blockbuster/lanes/auction/mempool.go | 110 +- blockbuster/mempool.go | 56 +- blockbuster/mempool_test.go | 35 +- blockbuster/proposals/abci_test.go | 1747 +++++++++------------ blockbuster/utils/ante.go | 2 +- tests/app/app.go | 36 +- x/builder/ante/ante_test.go | 7 +- 11 files changed, 1081 insertions(+), 1432 deletions(-) diff --git a/blockbuster/lanes/auction/abci.go b/blockbuster/lanes/auction/abci.go index 9a1f62b..0773c82 100644 --- a/blockbuster/lanes/auction/abci.go +++ b/blockbuster/lanes/auction/abci.go @@ -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 -} diff --git a/blockbuster/lanes/auction/auction_test.go b/blockbuster/lanes/auction/auction_test.go index 7e2eec4..ffe71ae 100644 --- a/blockbuster/lanes/auction/auction_test.go +++ b/blockbuster/lanes/auction/auction_test.go @@ -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 diff --git a/blockbuster/lanes/auction/factory.go b/blockbuster/lanes/auction/factory.go index 37c9738..6032aa9 100644 --- a/blockbuster/lanes/auction/factory.go +++ b/blockbuster/lanes/auction/factory.go @@ -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. diff --git a/blockbuster/lanes/auction/lane.go b/blockbuster/lanes/auction/lane.go index 15be52d..4be6982 100644 --- a/blockbuster/lanes/auction/lane.go +++ b/blockbuster/lanes/auction/lane.go @@ -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 ( @@ -12,8 +13,7 @@ const ( ) var ( - _ blockbuster.Lane = (*TOBLane)(nil) - _ Factory = (*TOBLane)(nil) + _ TOBLaneI = (*TOBLane)(nil) ) // TOBLane defines a top-of-block auction lane. The top of block auction lane @@ -22,43 +22,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[string] - // 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[string]( + 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 } diff --git a/blockbuster/lanes/auction/mempool.go b/blockbuster/lanes/auction/mempool.go index 8c71798..68a3b81 100644 --- a/blockbuster/lanes/auction/mempool.go +++ b/blockbuster/lanes/auction/mempool.go @@ -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 - // 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 -} diff --git a/blockbuster/mempool.go b/blockbuster/mempool.go index 82f997b..938d2b6 100644 --- a/blockbuster/mempool.go +++ b/blockbuster/mempool.go @@ -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 diff --git a/blockbuster/mempool_test.go b/blockbuster/mempool_test.go index 2384a1b..84e470c 100644 --- a/blockbuster/mempool_test.go +++ b/blockbuster/mempool_test.go @@ -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 diff --git a/blockbuster/proposals/abci_test.go b/blockbuster/proposals/abci_test.go index b778edf..ee62b73 100644 --- a/blockbuster/proposals/abci_test.go +++ b/blockbuster/proposals/abci_test.go @@ -1,1071 +1,802 @@ package proposals_test import ( + "crypto/sha256" + "encoding/hex" + "fmt" "math/rand" "testing" - "time" "cosmossdk.io/log" "cosmossdk.io/math" storetypes "cosmossdk.io/store/types" + cometabci "github.com/cometbft/cometbft/abci/types" "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/golang/mock/gomock" "github.com/skip-mev/pob/blockbuster" - "github.com/skip-mev/pob/blockbuster/abci" "github.com/skip-mev/pob/blockbuster/lanes/auction" "github.com/skip-mev/pob/blockbuster/lanes/base" "github.com/skip-mev/pob/blockbuster/lanes/free" + "github.com/skip-mev/pob/blockbuster/proposals" testutils "github.com/skip-mev/pob/testutils" - "github.com/skip-mev/pob/x/builder/ante" - "github.com/skip-mev/pob/x/builder/keeper" - buildertypes "github.com/skip-mev/pob/x/builder/types" "github.com/stretchr/testify/suite" - - abcitypes "github.com/cometbft/cometbft/abci/types" ) -type ABCITestSuite struct { +type ProposalsTestSuite struct { suite.Suite ctx sdk.Context + key *storetypes.KVStoreKey - // Define basic tx configuration encodingConfig testutils.EncodingConfig - - // Define all of the lanes utilized in the test suite - tobConfig blockbuster.BaseLaneConfig - tobLane *auction.TOBLane - - freeConfig blockbuster.BaseLaneConfig - freeLane *free.Lane - - baseConfig blockbuster.BaseLaneConfig - baseLane *base.DefaultLane - - lanes []blockbuster.Lane - mempool blockbuster.Mempool - - // Proposal handler set up - proposalHandler *abci.ProposalHandler - - // account set up - accounts []testutils.Account - random *rand.Rand - nonces map[string]uint64 - - // Keeper set up - builderKeeper keeper.Keeper - bankKeeper *testutils.MockBankKeeper - accountKeeper *testutils.MockAccountKeeper - distrKeeper *testutils.MockDistributionKeeper - stakingKeeper *testutils.MockStakingKeeper - builderDecorator ante.BuilderDecorator + random *rand.Rand + accounts []testutils.Account + gasTokenDenom string } func TestBlockBusterTestSuite(t *testing.T) { - suite.Run(t, new(ABCITestSuite)) + suite.Run(t, new(ProposalsTestSuite)) } -func (suite *ABCITestSuite) SetupTest() { - // General config for transactions and randomness for the test suite - suite.encodingConfig = testutils.CreateTestEncodingConfig() - suite.random = rand.New(rand.NewSource(time.Now().Unix())) - key := storetypes.NewKVStoreKey(buildertypes.StoreKey) - testCtx := testutil.DefaultContextWithDB(suite.T(), key, storetypes.NewTransientStoreKey("transient_test")) - suite.ctx = testCtx.Ctx.WithBlockHeight(1) +func (s *ProposalsTestSuite) SetupTest() { + // Set up basic TX encoding config. + s.encodingConfig = testutils.CreateTestEncodingConfig() - // Lanes configuration - // Top of block lane set up - suite.tobConfig = blockbuster.BaseLaneConfig{ - Logger: log.NewTestLogger(suite.T()), - TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), - TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), - AnteHandler: suite.anteHandler, - MaxBlockSpace: math.LegacyZeroDec(), // It can be as big as it wants (up to maxTxBytes) - } - suite.tobLane = auction.NewTOBLane( - suite.tobConfig, - 0, // No bound on the number of transactions in the lane - auction.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder()), - ) + // Create a few random accounts + s.random = rand.New(rand.NewSource(1)) + s.accounts = testutils.RandomAccounts(s.random, 5) + s.gasTokenDenom = "stake" - // Free lane set up - suite.freeConfig = blockbuster.BaseLaneConfig{ - Logger: log.NewTestLogger(suite.T()), - TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), - TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), - AnteHandler: suite.anteHandler, - MaxBlockSpace: math.LegacyZeroDec(), // It can be as big as it wants (up to maxTxBytes) - IgnoreList: []blockbuster.Lane{suite.tobLane}, - } - suite.freeLane = free.NewFreeLane( - suite.freeConfig, - free.NewDefaultFreeFactory(suite.encodingConfig.TxConfig.TxDecoder()), - ) - - // Base lane set up - suite.baseConfig = blockbuster.BaseLaneConfig{ - Logger: log.NewTestLogger(suite.T()), - TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), - TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), - AnteHandler: suite.anteHandler, - MaxBlockSpace: math.LegacyZeroDec(), // It can be as big as it wants (up to maxTxBytes) - IgnoreList: []blockbuster.Lane{suite.tobLane, suite.freeLane}, - } - suite.baseLane = base.NewDefaultLane( - suite.baseConfig, - ) - - // Mempool set up - suite.lanes = []blockbuster.Lane{suite.tobLane, suite.freeLane, suite.baseLane} - suite.mempool = blockbuster.NewMempool(log.NewTestLogger(suite.T()), suite.lanes...) - - // Accounts set up - suite.accounts = testutils.RandomAccounts(suite.random, 10) - suite.nonces = make(map[string]uint64) - for _, acc := range suite.accounts { - suite.nonces[acc.Address.String()] = 0 - } - - // Set up the keepers and decorators - // Mock keepers set up - ctrl := gomock.NewController(suite.T()) - suite.accountKeeper = testutils.NewMockAccountKeeper(ctrl) - suite.accountKeeper.EXPECT().GetModuleAddress(buildertypes.ModuleName).Return(sdk.AccAddress{}).AnyTimes() - suite.bankKeeper = testutils.NewMockBankKeeper(ctrl) - suite.distrKeeper = testutils.NewMockDistributionKeeper(ctrl) - suite.stakingKeeper = testutils.NewMockStakingKeeper(ctrl) - - // Builder keeper / decorator set up - suite.builderKeeper = keeper.NewKeeper( - suite.encodingConfig.Codec, - key, - suite.accountKeeper, - suite.bankKeeper, - suite.distrKeeper, - suite.stakingKeeper, - sdk.AccAddress([]byte("authority")).String(), - ) - - // Set the default params for the builder keeper - err := suite.builderKeeper.SetParams(suite.ctx, buildertypes.DefaultParams()) - suite.Require().NoError(err) - - // Set up the ante handler - suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxEncoder(), suite.tobLane, suite.mempool) - - // Proposal handler set up - suite.proposalHandler = abci.NewProposalHandler(log.NewTestLogger(suite.T()), suite.encodingConfig.TxConfig.TxDecoder(), suite.mempool) + s.key = storetypes.NewKVStoreKey("test") + testCtx := testutil.DefaultContextWithDB(s.T(), s.key, storetypes.NewTransientStoreKey("transient_test")) + s.ctx = testCtx.Ctx.WithIsCheckTx(true) } -func (suite *ABCITestSuite) anteHandler(ctx sdk.Context, tx sdk.Tx, _ bool) (sdk.Context, error) { - suite.bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), "stake").AnyTimes().Return( - sdk.NewCoin("stake", math.NewInt(100000000000000)), - ) +func (s *ProposalsTestSuite) TestPrepareProposal() { + s.Run("can prepare a proposal with no transactions", func() { + // Set up the default lane with no transactions + defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("1"), nil) - next := func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) { - return ctx, nil - } + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{defaultLane}).PrepareProposalHandler() - return suite.builderDecorator.AnteHandle(ctx, tx, false, next) -} + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Require().Equal(0, len(resp.Txs)) + }) -func (suite *ABCITestSuite) resetLanesWithNewConfig() { - // Top of block lane set up - suite.tobLane = auction.NewTOBLane( - suite.tobConfig, - 0, // No bound on the number of transactions in the lane - auction.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder()), - ) - - // Free lane set up - suite.freeLane = free.NewFreeLane( - suite.freeConfig, - free.NewDefaultFreeFactory(suite.encodingConfig.TxConfig.TxDecoder()), - ) - - // Base lane set up - suite.baseLane = base.NewDefaultLane( - suite.baseConfig, - ) - - suite.lanes = []blockbuster.Lane{suite.tobLane, suite.freeLane, suite.baseLane} - - suite.mempool = blockbuster.NewMempool(log.NewTestLogger(suite.T()), suite.lanes...) -} - -func (suite *ABCITestSuite) TestPrepareProposal() { - var ( - // the modified transactions cannot exceed this size - maxTxBytes int64 = 1000000000000000000 - - // mempool configuration - txs []sdk.Tx - auctionTxs []sdk.Tx - winningBidTx sdk.Tx - insertBundledTxs = false - - // auction configuration - maxBundleSize uint32 = 10 - reserveFee = sdk.NewCoin("stake", math.NewInt(1000)) - minBidIncrement = sdk.NewCoin("stake", math.NewInt(100)) - frontRunningProtection = true - ) - - cases := []struct { - name string - malleate func() - expectedNumberProposalTxs int - expectedMempoolDistribution map[string]int - }{ - { - "empty mempool", - func() { - txs = []sdk.Tx{} - auctionTxs = []sdk.Tx{} - winningBidTx = nil - insertBundledTxs = false - }, + s.Run("can build a proposal with a single tx from the lane", func() { + // Create a random transaction that will be inserted into the default lane + tx, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], 0, - map[string]int{ - base.LaneName: 0, - auction.LaneName: 0, - free.LaneName: 0, - }, - }, - { - "maxTxBytes is less than any transaction in the mempool", - func() { - // Create a tob tx - bidder := suite.accounts[0] - bid := sdk.NewCoin("stake", math.NewInt(1000)) - nonce := suite.nonces[bidder.Address.String()] - timeout := uint64(100) - signers := []testutils.Account{bidder} - bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) - - // Create a free tx - account := suite.accounts[1] - nonce = suite.nonces[account.Address.String()] - freeTx, err := testutils.CreateFreeTx(suite.encodingConfig.TxConfig, account, nonce, timeout, "val1", bid) - suite.Require().NoError(err) - - // Create a normal tx - account = suite.accounts[2] - nonce = suite.nonces[account.Address.String()] - numberMsgs := uint64(3) - normalTx, err := testutils.CreateRandomTx(suite.encodingConfig.TxConfig, account, nonce, numberMsgs, timeout) - suite.Require().NoError(err) - - txs = []sdk.Tx{freeTx, normalTx} - auctionTxs = []sdk.Tx{bidTx} - winningBidTx = nil - insertBundledTxs = false - maxTxBytes = 10 - }, - 0, - map[string]int{ - base.LaneName: 1, - auction.LaneName: 1, - free.LaneName: 1, - }, - }, - { - "valid tob tx but maxTxBytes is less for the tob lane so only the free tx should be included", - func() { - // Create a tob tx - bidder := suite.accounts[0] - bid := sdk.NewCoin("stake", math.NewInt(1000)) - nonce := suite.nonces[bidder.Address.String()] - timeout := uint64(100) - signers := []testutils.Account{suite.accounts[2], bidder} - bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) - - // Create a free tx - account := suite.accounts[1] - nonce = suite.nonces[account.Address.String()] - freeTx, err := testutils.CreateFreeTx(suite.encodingConfig.TxConfig, account, nonce, timeout, "val1", bid) - suite.Require().NoError(err) - - // Get the size of the tob tx - bidTxBytes, err := suite.encodingConfig.TxConfig.TxEncoder()(bidTx) - suite.Require().NoError(err) - tobSize := int64(len(bidTxBytes)) - - // Get the size of the free tx - freeTxBytes, err := suite.encodingConfig.TxConfig.TxEncoder()(freeTx) - suite.Require().NoError(err) - freeSize := int64(len(freeTxBytes)) - - maxTxBytes = tobSize + freeSize - suite.tobConfig.MaxBlockSpace = math.LegacyMustNewDecFromStr("0.1") - - txs = []sdk.Tx{freeTx} - auctionTxs = []sdk.Tx{bidTx} - winningBidTx = nil - insertBundledTxs = false - }, 1, - map[string]int{ - base.LaneName: 0, - auction.LaneName: 1, - free.LaneName: 1, - }, - }, - { - "valid tob tx with sufficient space for only tob tx", - func() { - // Create a tob tx - bidder := suite.accounts[0] - bid := sdk.NewCoin("stake", math.NewInt(1000)) - nonce := suite.nonces[bidder.Address.String()] - timeout := uint64(100) - signers := []testutils.Account{suite.accounts[2]} - bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) - - // Create a free tx - account := suite.accounts[1] - nonce = suite.nonces[account.Address.String()] - freeTx, err := testutils.CreateFreeTx(suite.encodingConfig.TxConfig, account, nonce, timeout, "val1", bid) - suite.Require().NoError(err) - - // Get the size of the tob tx - bidTxBytes, err := suite.encodingConfig.TxConfig.TxEncoder()(bidTx) - suite.Require().NoError(err) - tobSize := int64(len(bidTxBytes)) - - // Get the size of the free tx - freeTxBytes, err := suite.encodingConfig.TxConfig.TxEncoder()(freeTx) - suite.Require().NoError(err) - freeSize := int64(len(freeTxBytes)) - - maxTxBytes = tobSize*2 + freeSize - 1 - suite.tobConfig.MaxBlockSpace = math.LegacyZeroDec() - suite.freeConfig.MaxBlockSpace = math.LegacyMustNewDecFromStr("0.1") - - txs = []sdk.Tx{freeTx} - auctionTxs = []sdk.Tx{bidTx} - winningBidTx = bidTx - insertBundledTxs = false - }, - 2, - map[string]int{ - base.LaneName: 0, - auction.LaneName: 1, - free.LaneName: 1, - }, - }, - { - "tob, free, and normal tx but only space for tob and normal tx", - func() { - // Create a tob tx - bidder := suite.accounts[0] - bid := sdk.NewCoin("stake", math.NewInt(1000)) - nonce := suite.nonces[bidder.Address.String()] - timeout := uint64(100) - signers := []testutils.Account{suite.accounts[2], bidder} - bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) - - // Create a free tx - account := suite.accounts[1] - nonce = suite.nonces[account.Address.String()] - freeTx, err := testutils.CreateFreeTx(suite.encodingConfig.TxConfig, account, nonce, timeout, "val1", bid) - suite.Require().NoError(err) - - // Create a normal tx - account = suite.accounts[3] - nonce = suite.nonces[account.Address.String()] - numberMsgs := uint64(3) - normalTx, err := testutils.CreateRandomTx(suite.encodingConfig.TxConfig, account, nonce, numberMsgs, timeout) - suite.Require().NoError(err) - - // Get the size of the tob tx - bidTxBytes, err := suite.encodingConfig.TxConfig.TxEncoder()(bidTx) - suite.Require().NoError(err) - tobSize := int64(len(bidTxBytes)) - - // Get the size of the free tx - freeTxBytes, err := suite.encodingConfig.TxConfig.TxEncoder()(freeTx) - suite.Require().NoError(err) - freeSize := int64(len(freeTxBytes)) - - // Get the size of the normal tx - normalTxBytes, err := suite.encodingConfig.TxConfig.TxEncoder()(normalTx) - suite.Require().NoError(err) - normalSize := int64(len(normalTxBytes)) - - maxTxBytes = tobSize*2 + freeSize + normalSize + 1 - - // Tob can take up as much space as it wants - suite.tobConfig.MaxBlockSpace = math.LegacyZeroDec() - - // Free can take up less space than the tx - suite.freeConfig.MaxBlockSpace = math.LegacyMustNewDecFromStr("0.01") - - // Default can take up as much space as it wants - suite.baseConfig.MaxBlockSpace = math.LegacyZeroDec() - - txs = []sdk.Tx{freeTx, normalTx} - auctionTxs = []sdk.Tx{bidTx} - winningBidTx = bidTx - insertBundledTxs = false - }, - 4, - map[string]int{ - base.LaneName: 1, - auction.LaneName: 1, - free.LaneName: 1, - }, - }, - { - "single valid tob transaction in the mempool", - func() { - // reset the configs - suite.tobConfig.MaxBlockSpace = math.LegacyZeroDec() - suite.freeConfig.MaxBlockSpace = math.LegacyZeroDec() - suite.baseConfig.MaxBlockSpace = math.LegacyZeroDec() - - bidder := suite.accounts[0] - bid := sdk.NewCoin("stake", math.NewInt(1000)) - nonce := suite.nonces[bidder.Address.String()] - timeout := uint64(100) - signers := []testutils.Account{bidder} - bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) - - txs = []sdk.Tx{} - auctionTxs = []sdk.Tx{bidTx} - winningBidTx = bidTx - insertBundledTxs = false - maxTxBytes = 1000000000000000000 - }, - 2, - map[string]int{ - base.LaneName: 0, - auction.LaneName: 1, - free.LaneName: 0, - }, - }, - { - "single invalid tob transaction in the mempool", - func() { - bidder := suite.accounts[0] - bid := reserveFee.Sub(sdk.NewCoin("stake", math.NewInt(1))) // bid is less than the reserve fee - nonce := suite.nonces[bidder.Address.String()] - timeout := uint64(100) - signers := []testutils.Account{bidder} - bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) - - txs = []sdk.Tx{} - auctionTxs = []sdk.Tx{bidTx} - winningBidTx = nil - insertBundledTxs = false - }, 0, - map[string]int{ - base.LaneName: 0, - auction.LaneName: 0, - free.LaneName: 0, - }, - }, - { - "normal transactions in the mempool", - func() { - account := suite.accounts[0] - nonce := suite.nonces[account.Address.String()] - timeout := uint64(100) - numberMsgs := uint64(3) - normalTx, err := testutils.CreateRandomTx(suite.encodingConfig.TxConfig, account, nonce, numberMsgs, timeout) - suite.Require().NoError(err) + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1000000)), + ) + s.Require().NoError(err) - txs = []sdk.Tx{normalTx} - auctionTxs = []sdk.Tx{} - winningBidTx = nil - insertBundledTxs = false - }, + // Set up the default lane + defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx: true}) + s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) + + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{defaultLane}).PrepareProposalHandler() + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000}) + s.Require().NotNil(resp) + s.Require().NoError(err) + + proposal := s.getTxBytes(tx) + s.Require().Equal(1, len(resp.Txs)) + s.Require().Equal(proposal, resp.Txs) + }) + + s.Run("can build a proposal with multiple txs from the lane", func() { + // Create a random transaction that will be inserted into the default lane + tx1, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 0, 1, - map[string]int{ - base.LaneName: 1, - auction.LaneName: 0, - free.LaneName: 0, - }, - }, - { - "normal transactions and tob transactions in the mempool", - func() { - // Create a valid tob transaction - bidder := suite.accounts[0] - bid := sdk.NewCoin("stake", math.NewInt(1000)) - nonce := suite.nonces[bidder.Address.String()] - timeout := uint64(100) - signers := []testutils.Account{bidder} - bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1000000)), + ) + s.Require().NoError(err) - // Create a valid default transaction - account := suite.accounts[1] - nonce = suite.nonces[account.Address.String()] + 1 - numberMsgs := uint64(3) - normalTx, err := testutils.CreateRandomTx(suite.encodingConfig.TxConfig, account, nonce, numberMsgs, timeout) - suite.Require().NoError(err) + // Create a second random transaction that will be inserted into the default lane + tx2, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[1], + 1, + 1, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(200000000)), + ) + s.Require().NoError(err) - txs = []sdk.Tx{normalTx} - auctionTxs = []sdk.Tx{bidTx} - winningBidTx = bidTx - insertBundledTxs = false - }, - 3, - map[string]int{ - base.LaneName: 1, - auction.LaneName: 1, - free.LaneName: 0, - }, - }, - { - "multiple tob transactions where the first is invalid", - func() { - // Create an invalid tob transaction (frontrunning) - bidder := suite.accounts[0] - bid := sdk.NewCoin("stake", math.NewInt(1000000000)) - nonce := suite.nonces[bidder.Address.String()] - timeout := uint64(100) - signers := []testutils.Account{bidder, bidder, suite.accounts[1]} - bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) + // Set up the default lane with both transactions passing + defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx1: true, tx2: true}) + s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx1)) + s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx2)) - // Create a valid tob transaction - bidder = suite.accounts[1] - bid = sdk.NewCoin("stake", math.NewInt(1000)) - nonce = suite.nonces[bidder.Address.String()] - timeout = uint64(100) - signers = []testutils.Account{bidder} - bidTx2, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{defaultLane}).PrepareProposalHandler() + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000}) + s.Require().NotNil(resp) + s.Require().NoError(err) - txs = []sdk.Tx{} - auctionTxs = []sdk.Tx{bidTx, bidTx2} - winningBidTx = bidTx2 - insertBundledTxs = false - }, - 2, - map[string]int{ - base.LaneName: 0, - auction.LaneName: 1, - free.LaneName: 0, - }, - }, - { - "multiple tob transactions where the first is valid", - func() { - // Create an valid tob transaction - bidder := suite.accounts[0] - bid := sdk.NewCoin("stake", math.NewInt(10000000)) - nonce := suite.nonces[bidder.Address.String()] - timeout := uint64(100) - signers := []testutils.Account{suite.accounts[2], bidder} - bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) + proposal := s.getTxBytes(tx2, tx1) + s.Require().Equal(2, len(resp.Txs)) + s.Require().Equal(proposal, resp.Txs) + }) - // Create a valid tob transaction - bidder = suite.accounts[1] - bid = sdk.NewCoin("stake", math.NewInt(1000)) - nonce = suite.nonces[bidder.Address.String()] - timeout = uint64(100) - signers = []testutils.Account{bidder} - bidTx2, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) + s.Run("can build a proposal with single tx with other that fails", func() { + // Create a random transaction that will be inserted into the default lane + tx1, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 0, + 1, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1000000)), + ) + s.Require().NoError(err) - txs = []sdk.Tx{} - auctionTxs = []sdk.Tx{bidTx, bidTx2} - winningBidTx = bidTx - insertBundledTxs = false - }, - 3, - map[string]int{ - base.LaneName: 0, - auction.LaneName: 2, - free.LaneName: 0, - }, - }, - { - "multiple tob transactions where the first is valid and bundle is inserted into mempool", - func() { - frontRunningProtection = false + // Create a second random transaction that will be inserted into the default lane + tx2, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[1], + 1, + 1, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(200000000)), + ) + s.Require().NoError(err) - // Create an valid tob transaction - bidder := suite.accounts[0] - bid := sdk.NewCoin("stake", math.NewInt(10000000)) - nonce := suite.nonces[bidder.Address.String()] - timeout := uint64(100) - signers := []testutils.Account{suite.accounts[2], suite.accounts[1], bidder, suite.accounts[3], suite.accounts[4]} - bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) + // Set up the default lane with both transactions passing + defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx1: true, tx2: false}) + s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx1)) + s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx2)) - txs = []sdk.Tx{} - auctionTxs = []sdk.Tx{bidTx} - winningBidTx = bidTx - insertBundledTxs = true - }, - 6, - map[string]int{ - base.LaneName: 5, - auction.LaneName: 1, - free.LaneName: 0, - }, - }, - { - "valid tob, free, and normal tx", - func() { - // Create a tob tx - bidder := suite.accounts[0] - bid := sdk.NewCoin("stake", math.NewInt(1000)) - nonce := suite.nonces[bidder.Address.String()] - timeout := uint64(100) - signers := []testutils.Account{suite.accounts[2], bidder} - bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{defaultLane}).PrepareProposalHandler() + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000}) + s.Require().NotNil(resp) + s.Require().NoError(err) - // Create a free tx - account := suite.accounts[1] - nonce = suite.nonces[account.Address.String()] - freeTx, err := testutils.CreateFreeTx(suite.encodingConfig.TxConfig, account, nonce, timeout, "val1", bid) - suite.Require().NoError(err) + proposal := s.getTxBytes(tx1) + s.Require().Equal(1, len(resp.Txs)) + s.Require().Equal(proposal, resp.Txs) + }) - // Create a normal tx - account = suite.accounts[3] - nonce = suite.nonces[account.Address.String()] - numberMsgs := uint64(3) - normalTx, err := testutils.CreateRandomTx(suite.encodingConfig.TxConfig, account, nonce, numberMsgs, timeout) - suite.Require().NoError(err) + s.Run("can build a proposal an empty proposal with multiple lanes", func() { + tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), nil) + defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), nil) - txs = []sdk.Tx{freeTx, normalTx} - auctionTxs = []sdk.Tx{bidTx} - winningBidTx = bidTx - insertBundledTxs = false - }, - 5, - map[string]int{ - base.LaneName: 1, - auction.LaneName: 1, - free.LaneName: 1, - }, - }, - } + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, defaultLane}).PrepareProposalHandler() - for _, tc := range cases { - suite.Run(tc.name, func() { - suite.SetupTest() // reset - tc.malleate() - suite.resetLanesWithNewConfig() + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) + s.Require().NoError(err) + s.Require().NotNil(resp) - // Insert all of the normal transactions into the default lane - for _, tx := range txs { - suite.Require().NoError(suite.mempool.Insert(suite.ctx, tx)) - } + s.Require().Equal(0, len(resp.Txs)) + }) - // Insert all of the auction transactions into the TOB lane - for _, tx := range auctionTxs { - suite.Require().NoError(suite.mempool.Insert(suite.ctx, tx)) - } + s.Run("can build a proposal with transactions from a single lane given multiple lanes", func() { + // Create a bid tx that includes a single bundled tx + tx, bundleTxs, err := testutils.CreateAuctionTx( + s.encodingConfig.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1000000)), + 0, + 0, + s.accounts[0:1], + ) + s.Require().NoError(err) - // Insert all of the bundled transactions into the TOB lane if desired - if insertBundledTxs { - for _, tx := range auctionTxs { - bidInfo, err := suite.tobLane.GetAuctionBidInfo(tx) - suite.Require().NoError(err) - - for _, txBz := range bidInfo.Transactions { - tx, err := suite.encodingConfig.TxConfig.TxDecoder()(txBz) - suite.Require().NoError(err) - - suite.Require().NoError(suite.mempool.Insert(suite.ctx, tx)) - } - } - } - - // Create a new auction - params := buildertypes.Params{ - MaxBundleSize: maxBundleSize, - ReserveFee: reserveFee, - FrontRunningProtection: frontRunningProtection, - MinBidIncrement: minBidIncrement, - } - suite.builderKeeper.SetParams(suite.ctx, params) - suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxEncoder(), suite.tobLane, suite.mempool) - - for _, lane := range suite.lanes { - lane.SetAnteHandler(suite.anteHandler) - } - - // Create a new proposal handler - suite.proposalHandler = abci.NewProposalHandler(log.NewTestLogger(suite.T()), suite.encodingConfig.TxConfig.TxDecoder(), suite.mempool) - handler := suite.proposalHandler.PrepareProposalHandler() - res, err := handler(suite.ctx, &abcitypes.RequestPrepareProposal{ - MaxTxBytes: maxTxBytes, - }) - suite.Require().NoError(err) - - // -------------------- Check Invariants -------------------- // - // 1. the number of transactions in the response must be equal to the number of expected transactions - suite.Require().Equal(tc.expectedNumberProposalTxs, len(res.Txs)) - - // 2. total bytes must be less than or equal to maxTxBytes - totalBytes := int64(0) - txIndex := 0 - for txIndex < len(res.Txs) { - totalBytes += int64(len(res.Txs[txIndex])) - - tx, err := suite.encodingConfig.TxConfig.TxDecoder()(res.Txs[txIndex]) - suite.Require().NoError(err) - - suite.Require().Equal(true, suite.mempool.Contains(tx)) - - // In the case where we have a tob tx, we skip the other transactions in the bundle - // in order to not double count - switch { - case suite.tobLane.Match(suite.ctx, tx): - bidInfo, err := suite.tobLane.GetAuctionBidInfo(tx) - suite.Require().NoError(err) - - txIndex += len(bidInfo.Transactions) + 1 - default: - txIndex++ - } - } - - suite.Require().LessOrEqual(totalBytes, maxTxBytes) - - // 3. if there are auction transactions, the first transaction must be the top bid - // and the rest of the bundle must be in the response - if winningBidTx != nil { - auctionTx, err := suite.encodingConfig.TxConfig.TxDecoder()(res.Txs[0]) - suite.Require().NoError(err) - - bidInfo, err := suite.tobLane.GetAuctionBidInfo(auctionTx) - suite.Require().NoError(err) - - for index, tx := range bidInfo.Transactions { - suite.Require().Equal(tx, res.Txs[index+1]) - } - } else if len(res.Txs) > 0 { - tx, err := suite.encodingConfig.TxConfig.TxDecoder()(res.Txs[0]) - suite.Require().NoError(err) - - bidInfo, err := suite.tobLane.GetAuctionBidInfo(tx) - suite.Require().NoError(err) - suite.Require().Nil(bidInfo) - } - - // 4. All of the transactions must be unique - uniqueTxs := make(map[string]bool) - for _, tx := range res.Txs { - suite.Require().False(uniqueTxs[string(tx)]) - uniqueTxs[string(tx)] = true - } - - // 5. The number of transactions in the mempool must be correct - suite.Require().Equal(tc.expectedMempoolDistribution, suite.mempool.GetTxDistribution()) - - // 6. The ordering of transactions must respect the ordering of the lanes - laneIndex := 0 - txIndex = 0 - for txIndex < len(res.Txs) { - sdkTx, err := suite.encodingConfig.TxConfig.TxDecoder()(res.Txs[txIndex]) - suite.Require().NoError(err) - - if suite.lanes[laneIndex].Match(suite.ctx, sdkTx) { - switch suite.lanes[laneIndex].Name() { - case suite.tobLane.Name(): - bidInfo, err := suite.tobLane.GetAuctionBidInfo(sdkTx) - suite.Require().NoError(err) - - txIndex += len(bidInfo.Transactions) + 1 - default: - txIndex++ - } - } else { - laneIndex++ - } - - suite.Require().Less(laneIndex, len(suite.lanes)) - } + // Set up the TOB lane with the bid tx and the bundled tx + tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ + tx: true, + bundleTxs[0]: true, }) - } + s.Require().NoError(tobLane.Insert(sdk.Context{}, tx)) + + defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), nil) + + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, defaultLane}).PrepareProposalHandler() + + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000}) + s.Require().NoError(err) + s.Require().NotNil(resp) + + proposal := s.getTxBytes(tx, bundleTxs[0]) + s.Require().Equal(2, len(resp.Txs)) + s.Require().Equal(proposal, resp.Txs) + }) + + s.Run("can ignore txs that are already included in a proposal", func() { + // Create a bid tx that includes a single bundled tx + tx, bundleTxs, err := testutils.CreateAuctionTx( + s.encodingConfig.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1000000)), + 0, + 0, + s.accounts[0:1], + ) + s.Require().NoError(err) + + // Set up the TOB lane with the bid tx and the bundled tx + tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ + tx: true, + bundleTxs[0]: true, + }) + s.Require().NoError(tobLane.Insert(sdk.Context{}, tx)) + + // Set up the default lane with the bid tx and the bundled tx + defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ + tx: true, + bundleTxs[0]: true, + }) + s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) + s.Require().NoError(defaultLane.Insert(sdk.Context{}, bundleTxs[0])) + + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, defaultLane}).PrepareProposalHandler() + + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000}) + s.Require().NoError(err) + s.Require().NotNil(resp) + + proposal := s.getTxBytes(tx, bundleTxs[0]) + s.Require().Equal(2, len(resp.Txs)) + s.Require().Equal(proposal, resp.Txs) + }) + + s.Run("can build a proposal where first lane has too large of a tx and second lane has a valid tx", func() { + // Create a bid tx that includes a single bundled tx + tx, bundleTxs, err := testutils.CreateAuctionTx( + s.encodingConfig.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1000000)), + 0, + 0, + s.accounts[0:1], + ) + s.Require().NoError(err) + + // Set up the TOB lane with the bid tx and the bundled tx + tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ + tx: false, + bundleTxs[0]: true, + }) + s.Require().NoError(tobLane.Insert(sdk.Context{}, tx)) + + // Set up the default lane with the bid tx and the bundled tx + defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ + // Even though this passes it should not include it in the proposal because it is in the ignore list + tx: true, + bundleTxs[0]: true, + }) + s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) + s.Require().NoError(defaultLane.Insert(sdk.Context{}, bundleTxs[0])) + + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, defaultLane}).PrepareProposalHandler() + + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000}) + s.Require().NoError(err) + s.Require().NotNil(resp) + + proposal := s.getTxBytes(bundleTxs[0]) + s.Require().Equal(1, len(resp.Txs)) + s.Require().Equal(proposal, resp.Txs) + }) + + s.Run("can build a proposal where first lane cannot fit txs but second lane can", func() { + // Create a bid tx that includes a single bundled tx + tx, bundleTxs, err := testutils.CreateAuctionTx( + s.encodingConfig.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1000000)), + 0, + 0, + s.accounts[0:1], + ) + s.Require().NoError(err) + + // Set up the TOB lane with the bid tx and the bundled tx + tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ + tx: true, + bundleTxs[0]: true, + }) + s.Require().NoError(tobLane.Insert(sdk.Context{}, tx)) + + // Set up the default lane with the bid tx and the bundled tx + defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ + // Even though this passes it should not include it in the proposal because it is in the ignore list + tx: true, + bundleTxs[0]: true, + }) + s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) + s.Require().NoError(defaultLane.Insert(sdk.Context{}, bundleTxs[0])) + + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, defaultLane}).PrepareProposalHandler() + proposal := s.getTxBytes(tx, bundleTxs[0]) + size := int64(len(proposal[0]) - 1) + + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: size}) + s.Require().NoError(err) + s.Require().NotNil(resp) + + s.Require().Equal(1, len(resp.Txs)) + s.Require().Equal(proposal[1:], resp.Txs) + }) + + s.Run("can build a proposal with single tx from middle lane", func() { + tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), nil) + + freeTx, err := testutils.CreateFreeTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 0, + 0, + "test", + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1000000)), + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1000000)), + ) + s.Require().NoError(err) + + defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ + freeTx: true, + }) + s.Require().NoError(defaultLane.Insert(sdk.Context{}, freeTx)) + + freeLane := s.setUpFreeLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{ + freeTx: true, + }) + s.Require().NoError(freeLane.Insert(sdk.Context{}, freeTx)) + + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, freeLane, defaultLane}).PrepareProposalHandler() + + proposal := s.getTxBytes(freeTx) + + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 1000000}) + s.Require().NoError(err) + s.Require().NotNil(resp) + + s.Require().Equal(1, len(resp.Txs)) + s.Require().Equal(proposal, resp.Txs) + }) + + s.Run("transaction from every lane", func() { + // Create a bid tx that includes a single bundled tx + tx, bundleTxs, err := testutils.CreateAuctionTx( + s.encodingConfig.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1000000)), + 0, + 0, + s.accounts[0:4], + ) + s.Require().NoError(err) + + // Create a normal tx + normalTx, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[1], + 0, + 0, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1000000)), + ) + s.Require().NoError(err) + + // Create a free tx + freeTx, err := testutils.CreateFreeTx( + s.encodingConfig.TxConfig, + s.accounts[2], + 0, + 0, + "test", + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1000000)), + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1000000)), + ) + s.Require().NoError(err) + + tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{ + tx: true, + bundleTxs[0]: true, + bundleTxs[1]: true, + bundleTxs[2]: true, + bundleTxs[3]: true, + }) + tobLane.Insert(sdk.Context{}, tx) + + defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ + normalTx: true, + }) + defaultLane.Insert(sdk.Context{}, normalTx) + + freeLane := s.setUpFreeLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{ + freeTx: true, + }) + freeLane.Insert(sdk.Context{}, freeTx) + + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, freeLane, defaultLane}).PrepareProposalHandler() + proposal := s.getTxBytes(tx, bundleTxs[0], bundleTxs[1], bundleTxs[2], bundleTxs[3], freeTx, normalTx) + + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 1000000000}) + s.Require().NoError(err) + s.Require().NotNil(resp) + + s.Require().Equal(7, len(resp.Txs)) + s.Require().Equal(proposal, resp.Txs) + }) } -func (suite *ABCITestSuite) TestProcessProposal() { - var ( - // mempool configuration - txs []sdk.Tx - auctionTxs []sdk.Tx - insertRefTxs = false +func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() { + s.Run("can build a proposal if a lane panics first", func() { + panicLane := s.setUpPanicLane(math.LegacyMustNewDecFromStr("0.25")) - // auction configuration - maxBundleSize uint32 = 10 - reserveFee = sdk.NewCoin("stake", math.NewInt(1000)) - frontRunningProtection = true + tx, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 0, + 0, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1000000)), + ) + s.Require().NoError(err) + + defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ + tx: true, + }) + s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) + + proposalHandler := proposals.NewProposalHandler( + log.NewTestLogger(s.T()), + s.encodingConfig.TxConfig.TxDecoder(), + []blockbuster.Lane{panicLane, defaultLane}, + ).PrepareProposalHandler() + + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 1000000}) + s.Require().NoError(err) + s.Require().NotNil(resp) + + proposal := s.getTxBytes(tx) + s.Require().Equal(1, len(resp.Txs)) + s.Require().Equal(proposal, resp.Txs) + }) + + s.Run("can build a proposal if second lane panics", func() { + panicLane := s.setUpPanicLane(math.LegacyMustNewDecFromStr("0.25")) + + tx, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 0, + 0, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1000000)), + ) + s.Require().NoError(err) + + defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ + tx: true, + }) + s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) + + proposalHandler := proposals.NewProposalHandler( + log.NewTestLogger(s.T()), + s.encodingConfig.TxConfig.TxDecoder(), + []blockbuster.Lane{defaultLane, panicLane}, + ).PrepareProposalHandler() + + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 1000000}) + s.Require().NoError(err) + s.Require().NotNil(resp) + + proposal := s.getTxBytes(tx) + s.Require().Equal(1, len(resp.Txs)) + s.Require().Equal(proposal, resp.Txs) + }) + + s.Run("can build a proposal if multiple consecutive lanes panic", func() { + panicLane := s.setUpPanicLane(math.LegacyMustNewDecFromStr("0.25")) + panicLane2 := s.setUpPanicLane(math.LegacyMustNewDecFromStr("0.25")) + + tx, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 0, + 0, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1000000)), + ) + s.Require().NoError(err) + + defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ + tx: true, + }) + s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) + + proposalHandler := proposals.NewProposalHandler( + log.NewTestLogger(s.T()), + s.encodingConfig.TxConfig.TxDecoder(), + []blockbuster.Lane{panicLane, panicLane2, defaultLane}, + ).PrepareProposalHandler() + + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 1000000}) + s.Require().NoError(err) + s.Require().NotNil(resp) + + proposal := s.getTxBytes(tx) + s.Require().Equal(1, len(resp.Txs)) + s.Require().Equal(proposal, resp.Txs) + }) + + s.Run("can build a proposal if the last few lanes panic", func() { + panicLane := s.setUpPanicLane(math.LegacyMustNewDecFromStr("0.25")) + panicLane2 := s.setUpPanicLane(math.LegacyMustNewDecFromStr("0.25")) + + tx, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 0, + 0, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1000000)), + ) + s.Require().NoError(err) + + defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ + tx: true, + }) + s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) + + proposalHandler := proposals.NewProposalHandler( + log.NewTestLogger(s.T()), + s.encodingConfig.TxConfig.TxDecoder(), + []blockbuster.Lane{defaultLane, panicLane, panicLane2}, + ).PrepareProposalHandler() + + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 1000000}) + s.Require().NoError(err) + s.Require().NotNil(resp) + + proposal := s.getTxBytes(tx) + s.Require().Equal(1, len(resp.Txs)) + s.Require().Equal(proposal, resp.Txs) + }) +} + +func (s *ProposalsTestSuite) TestProcessProposal() { + s.Run("can process a valid empty proposal", func() { + tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{}) + freeLane := s.setUpFreeLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{}) + defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{}) + + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, freeLane, defaultLane}).ProcessProposalHandler() + + resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: nil}) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Require().Equal(&cometabci.ResponseProcessProposal{Status: cometabci.ResponseProcessProposal_ACCEPT}, resp) + }) + + s.Run("rejects a proposal with bad txs", func() { + tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{}) + freeLane := s.setUpFreeLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{}) + defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{}) + + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, freeLane, defaultLane}).ProcessProposalHandler() + + resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: [][]byte{{0x01, 0x02, 0x03}}}) + s.Require().Error(err) + s.Require().Equal(&cometabci.ResponseProcessProposal{Status: cometabci.ResponseProcessProposal_REJECT}, resp) + }) + + s.Run("rejects a proposal when a lane panics", func() { + tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{}) + panicLane := s.setUpPanicLane(math.LegacyMustNewDecFromStr("0.0")) + + txbz, err := testutils.CreateRandomTxBz( + s.encodingConfig.TxConfig, + s.accounts[0], + 0, + 0, + 0, + ) + s.Require().NoError(err) + + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, panicLane}).ProcessProposalHandler() + resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: [][]byte{txbz}}) + s.Require().Error(err) + s.Require().Equal(&cometabci.ResponseProcessProposal{Status: cometabci.ResponseProcessProposal_REJECT}, resp) + }) + + s.Run("can process a invalid proposal (out of order)", func() { + // Create a random transaction that will be inserted into the default lane + tx, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[0], + 0, + 1, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1000000)), + ) + s.Require().NoError(err) + + // Create a random transaction that will be inserted into the default lane + tx2, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[2], + 0, + 1, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(2000000)), + ) + s.Require().NoError(err) + + // Set up the default lane + defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx: true}) + s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) + + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{defaultLane}).ProcessProposalHandler() + resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: s.getTxBytes(tx, tx2)}) + s.Require().NotNil(resp) + s.Require().Error(err) + }) + + s.Run("can process a invalid proposal where first lane is valid second is not", func() { + bidTx, bundle, err := testutils.CreateAuctionTx( + s.encodingConfig.TxConfig, + s.accounts[0], + sdk.NewCoin(s.gasTokenDenom, math.NewInt(1000000)), + 0, + 1, + s.accounts[0:2], + ) + s.Require().NoError(err) + + normalTx, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[1], + 0, + 1, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(2000000)), + ) + s.Require().NoError(err) + + normalTx2, err := testutils.CreateRandomTx( + s.encodingConfig.TxConfig, + s.accounts[2], + 0, + 1, + 0, + sdk.NewCoin(s.gasTokenDenom, math.NewInt(3000000)), + ) + s.Require().NoError(err) + + // Set up the default lane + defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), nil) + defaultLane.SetProcessLaneHandler(blockbuster.NoOpProcessLaneHandler()) + + // Set up the TOB lane + tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), nil) + tobLane.SetProcessLaneHandler(blockbuster.NoOpProcessLaneHandler()) + + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, defaultLane}).ProcessProposalHandler() + resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: s.getTxBytes(bidTx, bundle[0], bundle[1], normalTx, normalTx2)}) + s.Require().NotNil(resp) + s.Require().Error(err) + }) +} + +func (s *ProposalsTestSuite) 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 +} + +func (s *ProposalsTestSuite) setUpDefaultLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *base.DefaultLane { + cfg := blockbuster.LaneConfig{ + Logger: log.NewTestLogger(s.T()), + TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), + TxDecoder: s.encodingConfig.TxConfig.TxDecoder(), + AnteHandler: s.setUpAnteHandler(expectedExecution), + MaxBlockSpace: maxBlockSpace, + } + + return base.NewDefaultLane(cfg) +} + +func (s *ProposalsTestSuite) setUpTOBLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *auction.TOBLane { + cfg := blockbuster.LaneConfig{ + Logger: log.NewTestLogger(s.T()), + TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), + TxDecoder: s.encodingConfig.TxConfig.TxDecoder(), + AnteHandler: s.setUpAnteHandler(expectedExecution), + MaxBlockSpace: maxBlockSpace, + } + + return auction.NewTOBLane(cfg, auction.NewDefaultAuctionFactory(cfg.TxDecoder)) +} + +func (s *ProposalsTestSuite) setUpFreeLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *free.FreeLane { + cfg := blockbuster.LaneConfig{ + Logger: log.NewTestLogger(s.T()), + TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), + TxDecoder: s.encodingConfig.TxConfig.TxDecoder(), + AnteHandler: s.setUpAnteHandler(expectedExecution), + MaxBlockSpace: maxBlockSpace, + } + + return free.NewFreeLane(cfg, blockbuster.DefaultTxPriority(), free.DefaultMatchHandler()) +} + +func (s *ProposalsTestSuite) setUpPanicLane(maxBlockSpace math.LegacyDec) *blockbuster.LaneConstructor[string] { + cfg := blockbuster.LaneConfig{ + Logger: log.NewTestLogger(s.T()), + TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), + TxDecoder: s.encodingConfig.TxConfig.TxDecoder(), + MaxBlockSpace: maxBlockSpace, + } + + lane := blockbuster.NewLaneConstructor[string]( + cfg, + "panic", + blockbuster.NewConstructorMempool[string](blockbuster.DefaultTxPriority(), cfg.TxEncoder, 0), + blockbuster.DefaultMatchHandler(), ) - cases := []struct { - name string - malleate func() - response abcitypes.ResponseProcessProposal_ProposalStatus - }{ - { - "no normal tx, no tob tx", - func() { - }, - abcitypes.ResponseProcessProposal_ACCEPT, - }, - { - "single default tx", - func() { - account := suite.accounts[0] - nonce := suite.nonces[account.Address.String()] - timeout := uint64(100) - numberMsgs := uint64(3) - normalTx, err := testutils.CreateRandomTx(suite.encodingConfig.TxConfig, account, nonce, numberMsgs, timeout) - suite.Require().NoError(err) + lane.SetPrepareLaneHandler(blockbuster.PanicPrepareLaneHandler()) + lane.SetProcessLaneHandler(blockbuster.PanicProcessLaneHandler()) - txs = []sdk.Tx{normalTx} - auctionTxs = []sdk.Tx{} - insertRefTxs = false - }, - abcitypes.ResponseProcessProposal_ACCEPT, - }, - { - "single tob tx without bundled txs in proposal", - func() { - bidder := suite.accounts[0] - bid := sdk.NewCoin("stake", math.NewInt(1000)) - nonce := suite.nonces[bidder.Address.String()] - timeout := uint64(100) - signers := []testutils.Account{bidder} - bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) - - txs = []sdk.Tx{} - auctionTxs = []sdk.Tx{bidTx} - insertRefTxs = false - }, - abcitypes.ResponseProcessProposal_REJECT, - }, - { - "single tob tx with bundled txs in proposal", - func() { - bidder := suite.accounts[0] - bid := sdk.NewCoin("stake", math.NewInt(1000)) - nonce := suite.nonces[bidder.Address.String()] - timeout := uint64(100) - signers := []testutils.Account{suite.accounts[1], bidder} - bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) - - txs = []sdk.Tx{} - auctionTxs = []sdk.Tx{bidTx} - insertRefTxs = true - }, - abcitypes.ResponseProcessProposal_ACCEPT, - }, - { - "single invalid tob tx (front-running)", - func() { - // Create an valid tob transaction - bidder := suite.accounts[0] - bid := sdk.NewCoin("stake", math.NewInt(10000000)) - nonce := suite.nonces[bidder.Address.String()] - timeout := uint64(100) - signers := []testutils.Account{suite.accounts[2], suite.accounts[1], bidder, suite.accounts[3], suite.accounts[4]} - bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) - - txs = []sdk.Tx{} - auctionTxs = []sdk.Tx{bidTx} - insertRefTxs = true - }, - abcitypes.ResponseProcessProposal_REJECT, - }, - { - "multiple tob txs in the proposal", - func() { - // Create an valid tob transaction - bidder := suite.accounts[0] - bid := sdk.NewCoin("stake", math.NewInt(10000000)) - nonce := suite.nonces[bidder.Address.String()] - timeout := uint64(100) - signers := []testutils.Account{suite.accounts[2], bidder} - bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) - - // Create a valid tob transaction - bidder = suite.accounts[1] - bid = sdk.NewCoin("stake", math.NewInt(1000)) - nonce = suite.nonces[bidder.Address.String()] - timeout = uint64(100) - signers = []testutils.Account{bidder} - bidTx2, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) - - txs = []sdk.Tx{} - auctionTxs = []sdk.Tx{bidTx, bidTx2} - insertRefTxs = true - }, - abcitypes.ResponseProcessProposal_REJECT, - }, - { - "single tob tx with front-running disabled and multiple other txs", - func() { - frontRunningProtection = false - - // Create an valid tob transaction - bidder := suite.accounts[0] - bid := sdk.NewCoin("stake", math.NewInt(10000000)) - nonce := suite.nonces[bidder.Address.String()] - timeout := uint64(100) - signers := []testutils.Account{suite.accounts[2], bidder} - bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) - - // Create a few other transactions - account := suite.accounts[1] - nonce = suite.nonces[account.Address.String()] - timeout = uint64(100) - numberMsgs := uint64(3) - normalTx, err := testutils.CreateRandomTx(suite.encodingConfig.TxConfig, account, nonce, numberMsgs, timeout) - suite.Require().NoError(err) - - account = suite.accounts[3] - nonce = suite.nonces[account.Address.String()] - timeout = uint64(100) - numberMsgs = uint64(3) - normalTx2, err := testutils.CreateRandomTx(suite.encodingConfig.TxConfig, account, nonce, numberMsgs, timeout) - suite.Require().NoError(err) - - txs = []sdk.Tx{normalTx, normalTx2} - auctionTxs = []sdk.Tx{bidTx} - insertRefTxs = true - }, - abcitypes.ResponseProcessProposal_ACCEPT, - }, - { - "tob, free, and default tx", - func() { - // Create a tob tx - bidder := suite.accounts[0] - bid := sdk.NewCoin("stake", math.NewInt(1000)) - nonce := suite.nonces[bidder.Address.String()] - timeout := uint64(100) - signers := []testutils.Account{suite.accounts[2], bidder} - bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) - - // Create a free tx - account := suite.accounts[1] - nonce = suite.nonces[account.Address.String()] - freeTx, err := testutils.CreateFreeTx(suite.encodingConfig.TxConfig, account, nonce, timeout, "val1", bid) - suite.Require().NoError(err) - - // Create a normal tx - account = suite.accounts[3] - nonce = suite.nonces[account.Address.String()] - numberMsgs := uint64(3) - normalTx, err := testutils.CreateRandomTx(suite.encodingConfig.TxConfig, account, nonce, numberMsgs, timeout) - suite.Require().NoError(err) - - txs = []sdk.Tx{freeTx, normalTx} - auctionTxs = []sdk.Tx{bidTx} - insertRefTxs = true - }, - abcitypes.ResponseProcessProposal_ACCEPT, - }, - { - "tob, free, and default tx with default and free mixed", - func() { - // Create a tob tx - bidder := suite.accounts[0] - bid := sdk.NewCoin("stake", math.NewInt(1000)) - nonce := suite.nonces[bidder.Address.String()] - timeout := uint64(100) - signers := []testutils.Account{suite.accounts[2], bidder} - bidTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, nonce, timeout, signers) - suite.Require().NoError(err) - - // Create a free tx - account := suite.accounts[1] - nonce = suite.nonces[account.Address.String()] - freeTx, err := testutils.CreateFreeTx(suite.encodingConfig.TxConfig, account, nonce, timeout, "val1", bid) - suite.Require().NoError(err) - - // Create a normal tx - account = suite.accounts[3] - nonce = suite.nonces[account.Address.String()] - numberMsgs := uint64(3) - normalTx, err := testutils.CreateRandomTx(suite.encodingConfig.TxConfig, account, nonce, numberMsgs, timeout) - suite.Require().NoError(err) - - txs = []sdk.Tx{normalTx, freeTx} - auctionTxs = []sdk.Tx{bidTx} - insertRefTxs = true - }, - abcitypes.ResponseProcessProposal_ACCEPT, - }, - } - - for _, tc := range cases { - suite.Run(tc.name, func() { - suite.SetupTest() // reset - tc.malleate() - - // Insert all of the transactions into the proposal - proposalTxs := make([][]byte, 0) - for _, tx := range auctionTxs { - txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(tx) - suite.Require().NoError(err) - - proposalTxs = append(proposalTxs, txBz) - - if insertRefTxs { - bidInfo, err := suite.tobLane.GetAuctionBidInfo(tx) - suite.Require().NoError(err) - - proposalTxs = append(proposalTxs, bidInfo.Transactions...) - } - } - - for _, tx := range txs { - txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(tx) - suite.Require().NoError(err) - - proposalTxs = append(proposalTxs, txBz) - } - - // create a new auction - params := buildertypes.Params{ - MaxBundleSize: maxBundleSize, - ReserveFee: reserveFee, - FrontRunningProtection: frontRunningProtection, - } - suite.builderKeeper.SetParams(suite.ctx, params) - suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxEncoder(), suite.tobLane, suite.mempool) - - handler := suite.proposalHandler.ProcessProposalHandler() - res, err := handler(suite.ctx, &abcitypes.RequestProcessProposal{ - Txs: proposalTxs, - }) - - // Check if the response is valid - suite.Require().Equal(tc.response, res.Status) - - if res.Status == abcitypes.ResponseProcessProposal_ACCEPT { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } + return lane +} + +func (s *ProposalsTestSuite) setUpProposalHandlers(lanes []blockbuster.Lane) *proposals.ProposalHandler { + mempool := blockbuster.NewMempool(log.NewTestLogger(s.T()), true, lanes...) + + return proposals.NewProposalHandler( + log.NewTestLogger(s.T()), + s.encodingConfig.TxConfig.TxDecoder(), + mempool.Registry(), + ) +} + +func (s *ProposalsTestSuite) getTxBytes(txs ...sdk.Tx) [][]byte { + txBytes := make([][]byte, len(txs)) + for i, tx := range txs { + bz, err := s.encodingConfig.TxConfig.TxEncoder()(tx) + s.Require().NoError(err) + + txBytes[i] = bz + } + return txBytes } diff --git a/blockbuster/utils/ante.go b/blockbuster/utils/ante.go index 418662e..fe9e12f 100644 --- a/blockbuster/utils/ante.go +++ b/blockbuster/utils/ante.go @@ -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 diff --git a/tests/app/app.go b/tests/app/app.go index ab52db1..774d592 100644 --- a/tests/app/app.go +++ b/tests/app/app.go @@ -62,10 +62,10 @@ import ( stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/skip-mev/pob/blockbuster" - "github.com/skip-mev/pob/blockbuster/abci" "github.com/skip-mev/pob/blockbuster/lanes/auction" "github.com/skip-mev/pob/blockbuster/lanes/base" "github.com/skip-mev/pob/blockbuster/lanes/free" + "github.com/skip-mev/pob/blockbuster/proposals" buildermodule "github.com/skip-mev/pob/x/builder" builderkeeper "github.com/skip-mev/pob/x/builder/keeper" ) @@ -139,7 +139,7 @@ type TestApp struct { FeeGrantKeeper feegrantkeeper.Keeper // custom checkTx handler - checkTxHandler abci.CheckTx + checkTxHandler proposals.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 @@ -338,16 +334,16 @@ func New( app.App.SetAnteHandler(anteHandler) // Set the proposal handlers on base app - proposalHandler := abci.NewProposalHandler( + proposalHandler := proposals.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 := proposals.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 proposals.CheckTx) { app.checkTxHandler = handler } diff --git a/x/builder/ante/ante_test.go b/x/builder/ante/ante_test.go index 1c93700..5608d0b 100644 --- a/x/builder/ante/ante_test.go +++ b/x/builder/ante/ante_test.go @@ -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) { From 0baa0aa951854e32d042a65fc39591b067db17a6 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Mon, 14 Aug 2023 19:07:19 -0400 Subject: [PATCH 06/41] moving stuff around n shi --- blockbuster/{proposals => abci}/abci.go | 2 +- blockbuster/{proposals => abci}/abci_test.go | 2 +- blockbuster/{proposals => abci}/check_tx.go | 2 +- blockbuster/lane_constructor.go | 2 +- blockbuster/lane_handlers.go | 44 -------------------- blockbuster/lane_mempool.go | 43 +++++++++++++++++++ 6 files changed, 47 insertions(+), 48 deletions(-) rename blockbuster/{proposals => abci}/abci.go (99%) rename blockbuster/{proposals => abci}/abci_test.go (99%) rename blockbuster/{proposals => abci}/check_tx.go (99%) diff --git a/blockbuster/proposals/abci.go b/blockbuster/abci/abci.go similarity index 99% rename from blockbuster/proposals/abci.go rename to blockbuster/abci/abci.go index cb7e17c..2e8f2f9 100644 --- a/blockbuster/proposals/abci.go +++ b/blockbuster/abci/abci.go @@ -1,4 +1,4 @@ -package proposals +package abci import ( "fmt" diff --git a/blockbuster/proposals/abci_test.go b/blockbuster/abci/abci_test.go similarity index 99% rename from blockbuster/proposals/abci_test.go rename to blockbuster/abci/abci_test.go index b778edf..4444d4a 100644 --- a/blockbuster/proposals/abci_test.go +++ b/blockbuster/abci/abci_test.go @@ -1,4 +1,4 @@ -package proposals_test +package abci_test import ( "math/rand" diff --git a/blockbuster/proposals/check_tx.go b/blockbuster/abci/check_tx.go similarity index 99% rename from blockbuster/proposals/check_tx.go rename to blockbuster/abci/check_tx.go index 7facb9c..e9deaf9 100644 --- a/blockbuster/proposals/check_tx.go +++ b/blockbuster/abci/check_tx.go @@ -1,4 +1,4 @@ -package proposals +package abci import ( "context" diff --git a/blockbuster/lane_constructor.go b/blockbuster/lane_constructor.go index c139978..ccc56ae 100644 --- a/blockbuster/lane_constructor.go +++ b/blockbuster/lane_constructor.go @@ -14,7 +14,7 @@ import ( // 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[C comparable] struct { - // cfg stores functionality requred to encode/decode transactions, maintains how + // 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 diff --git a/blockbuster/lane_handlers.go b/blockbuster/lane_handlers.go index 52fb5d7..627c198 100644 --- a/blockbuster/lane_handlers.go +++ b/blockbuster/lane_handlers.go @@ -1,7 +1,6 @@ package blockbuster import ( - "context" "fmt" sdk "github.com/cosmos/cosmos-sdk/types" @@ -154,46 +153,3 @@ func DefaultMatchHandler() MatchHandler { return true } } - -// 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: "", - } -} diff --git a/blockbuster/lane_mempool.go b/blockbuster/lane_mempool.go index aa3c9d4..e102ee8 100644 --- a/blockbuster/lane_mempool.go +++ b/blockbuster/lane_mempool.go @@ -36,6 +36,49 @@ type ( } ) +// 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]{ From 50edf777ded2e8146745bf1f47f28adb6700accd Mon Sep 17 00:00:00 2001 From: David Terpay Date: Mon, 14 Aug 2023 19:13:49 -0400 Subject: [PATCH 07/41] merging n updating n shi --- blockbuster/abci/abci_test.go | 14 +++++++------- tests/app/app.go | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/blockbuster/abci/abci_test.go b/blockbuster/abci/abci_test.go index 3724523..668a5fd 100644 --- a/blockbuster/abci/abci_test.go +++ b/blockbuster/abci/abci_test.go @@ -14,10 +14,10 @@ import ( "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/skip-mev/pob/blockbuster" + "github.com/skip-mev/pob/blockbuster/abci" "github.com/skip-mev/pob/blockbuster/lanes/auction" "github.com/skip-mev/pob/blockbuster/lanes/base" "github.com/skip-mev/pob/blockbuster/lanes/free" - "github.com/skip-mev/pob/blockbuster/proposals" testutils "github.com/skip-mev/pob/testutils" "github.com/stretchr/testify/suite" ) @@ -449,7 +449,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() { }) s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) - proposalHandler := proposals.NewProposalHandler( + proposalHandler := abci.NewProposalHandler( log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxDecoder(), []blockbuster.Lane{panicLane, defaultLane}, @@ -482,7 +482,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() { }) s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) - proposalHandler := proposals.NewProposalHandler( + proposalHandler := abci.NewProposalHandler( log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxDecoder(), []blockbuster.Lane{defaultLane, panicLane}, @@ -516,7 +516,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() { }) s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) - proposalHandler := proposals.NewProposalHandler( + proposalHandler := abci.NewProposalHandler( log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxDecoder(), []blockbuster.Lane{panicLane, panicLane2, defaultLane}, @@ -550,7 +550,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() { }) s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) - proposalHandler := proposals.NewProposalHandler( + proposalHandler := abci.NewProposalHandler( log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxDecoder(), []blockbuster.Lane{defaultLane, panicLane, panicLane2}, @@ -780,10 +780,10 @@ func (s *ProposalsTestSuite) setUpPanicLane(maxBlockSpace math.LegacyDec) *block return lane } -func (s *ProposalsTestSuite) setUpProposalHandlers(lanes []blockbuster.Lane) *proposals.ProposalHandler { +func (s *ProposalsTestSuite) setUpProposalHandlers(lanes []blockbuster.Lane) *abci.ProposalHandler { mempool := blockbuster.NewMempool(log.NewTestLogger(s.T()), true, lanes...) - return proposals.NewProposalHandler( + return abci.NewProposalHandler( log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxDecoder(), mempool.Registry(), diff --git a/tests/app/app.go b/tests/app/app.go index 774d592..ad83577 100644 --- a/tests/app/app.go +++ b/tests/app/app.go @@ -62,10 +62,10 @@ import ( stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/skip-mev/pob/blockbuster" + "github.com/skip-mev/pob/blockbuster/abci" "github.com/skip-mev/pob/blockbuster/lanes/auction" "github.com/skip-mev/pob/blockbuster/lanes/base" "github.com/skip-mev/pob/blockbuster/lanes/free" - "github.com/skip-mev/pob/blockbuster/proposals" buildermodule "github.com/skip-mev/pob/x/builder" builderkeeper "github.com/skip-mev/pob/x/builder/keeper" ) @@ -139,7 +139,7 @@ type TestApp struct { FeeGrantKeeper feegrantkeeper.Keeper // custom checkTx handler - checkTxHandler proposals.CheckTx + checkTxHandler abci.CheckTx } func init() { @@ -333,8 +333,8 @@ func New( } app.App.SetAnteHandler(anteHandler) - // Set the proposal handlers on base app - proposalHandler := proposals.NewProposalHandler( + // Set the abci handlers on base app + proposalHandler := abci.NewProposalHandler( app.Logger(), app.TxConfig().TxDecoder(), lanes, @@ -343,7 +343,7 @@ func New( app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) // Set the custom CheckTx handler on BaseApp. - checkTxHandler := proposals.NewCheckTxHandler( + checkTxHandler := abci.NewCheckTxHandler( app.App, app.txConfig.TxDecoder(), tobLane, @@ -392,7 +392,7 @@ func (app *TestApp) CheckTx(req *cometabci.RequestCheckTx) (*cometabci.ResponseC } // SetCheckTx sets the checkTxHandler for the app. -func (app *TestApp) SetCheckTx(handler proposals.CheckTx) { +func (app *TestApp) SetCheckTx(handler abci.CheckTx) { app.checkTxHandler = handler } From 9f99bb6866a4114ba4f8877b6e112784a2c138dd Mon Sep 17 00:00:00 2001 From: David Terpay Date: Mon, 14 Aug 2023 19:19:37 -0400 Subject: [PATCH 08/41] moving check tx to lane --- .../{abci => lanes/auction}/check_tx.go | 21 +++---------------- 1 file changed, 3 insertions(+), 18 deletions(-) rename blockbuster/{abci => lanes/auction}/check_tx.go (92%) diff --git a/blockbuster/abci/check_tx.go b/blockbuster/lanes/auction/check_tx.go similarity index 92% rename from blockbuster/abci/check_tx.go rename to blockbuster/lanes/auction/check_tx.go index e9deaf9..089ee99 100644 --- a/blockbuster/abci/check_tx.go +++ b/blockbuster/lanes/auction/check_tx.go @@ -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{ From e9dd8259eb4622ffbc964c92fcaa546ff4f10a57 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Mon, 14 Aug 2023 19:20:13 -0400 Subject: [PATCH 09/41] base app fix --- go.mod | 23 ++--------------------- go.sum | 34 ---------------------------------- tests/app/app.go | 6 +++--- 3 files changed, 5 insertions(+), 58 deletions(-) diff --git a/go.mod b/go.mod index 837118b..535433e 100644 --- a/go.mod +++ b/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 diff --git a/go.sum b/go.sum index f814c4d..8a43bf1 100644 --- a/go.sum +++ b/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= diff --git a/tests/app/app.go b/tests/app/app.go index ad83577..8ebc49a 100644 --- a/tests/app/app.go +++ b/tests/app/app.go @@ -139,7 +139,7 @@ type TestApp struct { FeeGrantKeeper feegrantkeeper.Keeper // custom checkTx handler - checkTxHandler abci.CheckTx + checkTxHandler auction.CheckTx } func init() { @@ -343,7 +343,7 @@ func New( app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) // Set the custom CheckTx handler on BaseApp. - checkTxHandler := abci.NewCheckTxHandler( + checkTxHandler := auction.NewCheckTxHandler( app.App, app.txConfig.TxDecoder(), tobLane, @@ -392,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 } From b2daf5acbb39dc634053c495749139274f470128 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 09:44:11 -0400 Subject: [PATCH 10/41] rename tob lane to mev lane --- README.md | 12 ++-- SPEC.md | 10 +-- blockbuster/README.md | 16 ++--- blockbuster/abci/abci_test.go | 62 ++++++++-------- blockbuster/lanes/{auction => mev}/abci.go | 14 ++-- .../lanes/{auction => mev}/check_tx.go | 18 ++--- blockbuster/lanes/{auction => mev}/factory.go | 2 +- .../lanes/{auction => mev}/factory_test.go | 16 ++--- blockbuster/lanes/{auction => mev}/lane.go | 32 ++++----- blockbuster/lanes/{auction => mev}/mempool.go | 8 +-- .../auction_test.go => mev/mev_test.go} | 14 ++-- blockbuster/lanes/{auction => mev}/utils.go | 2 +- .../lanes/{auction => mev}/utils_test.go | 10 +-- blockbuster/mempool_test.go | 70 +++++++++---------- go.work.sum | 2 + tests/app/ante.go | 4 +- tests/app/app.go | 22 +++--- tests/integration/pob_suite.go | 8 +-- x/builder/ante/ante.go | 8 +-- x/builder/ante/ante_test.go | 44 ++++++------ 20 files changed, 188 insertions(+), 186 deletions(-) rename blockbuster/lanes/{auction => mev}/abci.go (95%) rename blockbuster/lanes/{auction => mev}/check_tx.go (95%) rename blockbuster/lanes/{auction => mev}/factory.go (99%) rename blockbuster/lanes/{auction => mev}/factory_test.go (96%) rename blockbuster/lanes/{auction => mev}/lane.go (65%) rename blockbuster/lanes/{auction => mev}/mempool.go (84%) rename blockbuster/lanes/{auction/auction_test.go => mev/mev_test.go} (73%) rename blockbuster/lanes/{auction => mev}/utils.go (98%) rename blockbuster/lanes/{auction => mev}/utils_test.go (79%) diff --git a/README.md b/README.md index 7865804..ed67445 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ $ go install github.com/skip-mev/pob ... "github.com/skip-mev/pob/blockbuster" "github.com/skip-mev/pob/blockbuster/abci" - "github.com/skip-mev/pob/blockbuster/lanes/auction" + "github.com/skip-mev/pob/blockbuster/lanes/mev" "github.com/skip-mev/pob/blockbuster/lanes/base" "github.com/skip-mev/pob/blockbuster/lanes/free" buildermodule "github.com/skip-mev/pob/x/builder" @@ -82,7 +82,7 @@ $ go install github.com/skip-mev/pob 2. Add your module to the the `AppModuleBasic` manager. This manager is in charge of setting up basic, non-dependent module elements such as codec registration and genesis verification. This will register the special - `MsgAuctionBid` message. When users want to bid for top of block execution, + `MsgAuctionBid` message. When users want to bid for MEV execution, they will submit a transaction - which we call an auction transaction - that includes a single `MsgAuctionBid`. We prevent any other messages from being included in auction transaction to prevent malicious behavior - such as front @@ -99,7 +99,7 @@ $ go install github.com/skip-mev/pob ``` 3. The builder `Keeper` is POB's gateway to processing special `MsgAuctionBid` - messages that allow users to participate in the top of block auction, distribute + messages that allow users to participate in the MEV auction, distribute revenue to the auction house, and ensure the validity of auction transactions. a. First add the keeper to the app's struct definition. We also want to add POB's custom @@ -148,7 +148,7 @@ $ go install github.com/skip-mev/pob // indicates that the lane can use all available block space. MaxBlockSpace: sdk.ZeroDec(), } - tobLane := auction.NewTOBLane( + tobLane := auction.NewMEVLane( tobConfig, // the maximum number of transactions that the mempool can store. a value of 0 indicates // that the mempool can store an unlimited number of transactions. @@ -233,7 +233,7 @@ $ go install github.com/skip-mev/pob options.FreeLane, ), ... - builderante.NewBuilderDecorator(options.BuilderKeeper, options.TxEncoder, options.TOBLane, options.Mempool), + builderante.NewBuilderDecorator(options.BuilderKeeper, options.TxEncoder, options.MEVLane, options.Mempool), } anteHandler := sdk.ChainAnteDecorators(anteDecorators...) @@ -280,7 +280,7 @@ $ go install github.com/skip-mev/pob will verify the contents of the block proposal by all validators. The combination of the `BlockBuster` mempool + `PrepareProposal`/`ProcessProposal` handlers allows the application to verifiably build valid blocks with - top-of-block block space reserved for auctions and partial block for free transactions. + mev block space reserved for auctions and partial block for free transactions. Additionally, we override the `BaseApp`'s `CheckTx` handler with our own custom `CheckTx` handler that will be responsible for checking the validity of transactions. We override the `CheckTx` handler so that we can verify auction transactions before they are diff --git a/SPEC.md b/SPEC.md index d71156c..198b975 100644 --- a/SPEC.md +++ b/SPEC.md @@ -4,11 +4,11 @@ ## Abstract The `x/builder` module is a Cosmos SDK module that allows Cosmos chains to host -top-of-block auctions directly in-protocol with auction revenue (MEV) being +mev auctions directly in-protocol with auction revenue (MEV) being redistributed according to the preferences of the chain. The `x/builder` module introduces a new `MsgAuctionBid` message that allows users to submit a bid alongside an ordered list of transactions, i.e. a **bundle**, that they want -executed at top-of-block before any other transactions are executed for that +executed at mev before any other transactions are executed for that block. The `x/builder` module works alongside the `AuctionMempool` such that: * Auctions are held directly in the `AuctionMempool`, where a winner is determined @@ -169,7 +169,7 @@ Then, it will build the rest of the block by reaping and validating the transact in the global index. The second portion of block building iterates from highest to lowest priority transactions in the global index and adds them to the proposal if they are valid. If the proposer comes across a transaction that was already -included in the top of block, it will be ignored. +included in the MEV, it will be ignored. ### ProcessProposal @@ -183,7 +183,7 @@ transactions in the block in the order in which they were provided in the propos ### Ante Handler -When users want to bid for the rights for top-of-block execution they will submit +When users want to bid for the rights for mev execution they will submit a normal `sdk.Tx` transaction with a single `MsgAuctionBid`. The ante handler is responsible for verification of this transaction. The ante handler will verify that: @@ -253,7 +253,7 @@ message Params { ### MsgAuctionBid POB defines a new Cosmos SDK `Message`, `MsgAuctionBid`, that allows users to -create an auction bid and participate in a top-of-block auction. The `MsgAuctionBid` +create an auction bid and participate in a mev auction. The `MsgAuctionBid` message defines a bidder and a series of embedded transactions, i.e. the bundle. ```protobuf diff --git a/blockbuster/README.md b/blockbuster/README.md index ff80dcf..8e01116 100644 --- a/blockbuster/README.md +++ b/blockbuster/README.md @@ -29,8 +29,8 @@ together define the desired block structure of a chain. A mempool with separate `lanes` can be used for: -1. **MEV mitigation**: a top of block lane could be designed to create an -in-protocol top-of-block auction (as we are doing with POB) to recapture MEV +1. **MEV mitigation**: a MEV lane could be designed to create an +in-protocol mev auction (as we are doing with POB) to recapture MEV in a transparent and governable way. 2. **Free/reduced fee txs**: transactions with certain properties (e.g. from trusted accounts or performing encouraged actions) could leverage a @@ -63,7 +63,7 @@ desired lanes and their configurations (including lane ordering). Utilizing BlockBuster is a simple three step process: * Determine the lanes desired. Currently, POB supports three different -implementations of lanes: top of block lane, free lane, and a default lane. +implementations of lanes: MEV lane, free lane, and a default lane. 1. Top of block lane allows the top of every block to be auctioned off and constructed using logic defined by the `x/builder` module. 2. Free lane allows base app to not charge certain types of transactions @@ -111,7 +111,7 @@ three lanes: #### Preparing Proposals When the current proposer starts building a block, it will first populate the -proposal with transactions from the top of block lane, followed by free and +proposal with transactions from the MEV lane, followed by free and default lane. Each lane proposes its own set of transactions using the lane’s `PrepareLane` (analogous to `PrepareProposal`). Each lane has a limit on the relative percentage of total block space that the lane can consume. @@ -131,7 +131,7 @@ order relative to the ordering of lanes will be rejected. Following the example defined above, if a proposal contains the following transactions: 1. Default transaction (belonging to the default lane) -2. Top of block transaction (belonging to the top of block lane) +2. Top of block transaction (belonging to the MEV lane) 3. Free transaction (belonging to the free lane) It will be rejected because it does not respect the lane ordering. @@ -221,7 +221,7 @@ Transactions within a lane are ordered in a proposal respecting the ordering defined on the lane’s mempool. Developers can define their own custom ordering by implementing a custom `TxPriority` struct that allows the lane’s mempool to determine the priority of a transaction `GetTxPriority` and relatively order -two transactions given the priority `Compare`. The top of block lane includes +two transactions given the priority `Compare`. The MEV lane includes an custom `TxPriority` that orders transactions in the mempool based on their bid. @@ -323,14 +323,14 @@ func (config *DefaultFreeFactory) IsFreeTx(tx sdk.Tx) bool { Lanes must implement a `Match` interface which determines whether a transaction should be considered for a given lane. Developer’s are encouraged to utilize the same interfaces defined in the `Factory` to match transactions to lanes. For - example, developers might configure a top of block auction lane to accept + example, developers might configure a MEV auction lane to accept transactions if they contain a single `MsgAuctionBid` message in the transaction. ### 4.1. [Optional] Transaction Validation Transactions will be verified the lane’s `VerifyTx` function. This logic can be completely arbitrary. For example, the default lane verifies transactions -using base app’s `AnteHandler` while the top of block lane verifies transactions +using base app’s `AnteHandler` while the MEV lane verifies transactions by extracting all bundled transactions included in the bid transaction and then verifying the transaction iteratively given the bundle. diff --git a/blockbuster/abci/abci_test.go b/blockbuster/abci/abci_test.go index 668a5fd..6ceb3d6 100644 --- a/blockbuster/abci/abci_test.go +++ b/blockbuster/abci/abci_test.go @@ -15,9 +15,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/skip-mev/pob/blockbuster" "github.com/skip-mev/pob/blockbuster/abci" - "github.com/skip-mev/pob/blockbuster/lanes/auction" "github.com/skip-mev/pob/blockbuster/lanes/base" "github.com/skip-mev/pob/blockbuster/lanes/free" + "github.com/skip-mev/pob/blockbuster/lanes/mev" testutils "github.com/skip-mev/pob/testutils" "github.com/stretchr/testify/suite" ) @@ -167,10 +167,10 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { }) s.Run("can build a proposal an empty proposal with multiple lanes", func() { - tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), nil) + mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), nil) defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), nil) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, defaultLane}).PrepareProposalHandler() + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, defaultLane}).PrepareProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) s.Require().NoError(err) @@ -192,15 +192,15 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(err) // Set up the TOB lane with the bid tx and the bundled tx - tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ + mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ tx: true, bundleTxs[0]: true, }) - s.Require().NoError(tobLane.Insert(sdk.Context{}, tx)) + s.Require().NoError(mevLane.Insert(sdk.Context{}, tx)) defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), nil) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, defaultLane}).PrepareProposalHandler() + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, defaultLane}).PrepareProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000}) s.Require().NoError(err) @@ -224,11 +224,11 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(err) // Set up the TOB lane with the bid tx and the bundled tx - tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ + mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ tx: true, bundleTxs[0]: true, }) - s.Require().NoError(tobLane.Insert(sdk.Context{}, tx)) + s.Require().NoError(mevLane.Insert(sdk.Context{}, tx)) // Set up the default lane with the bid tx and the bundled tx defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ @@ -238,7 +238,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) s.Require().NoError(defaultLane.Insert(sdk.Context{}, bundleTxs[0])) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, defaultLane}).PrepareProposalHandler() + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, defaultLane}).PrepareProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000}) s.Require().NoError(err) @@ -262,11 +262,11 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(err) // Set up the TOB lane with the bid tx and the bundled tx - tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ + mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ tx: false, bundleTxs[0]: true, }) - s.Require().NoError(tobLane.Insert(sdk.Context{}, tx)) + s.Require().NoError(mevLane.Insert(sdk.Context{}, tx)) // Set up the default lane with the bid tx and the bundled tx defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ @@ -277,7 +277,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) s.Require().NoError(defaultLane.Insert(sdk.Context{}, bundleTxs[0])) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, defaultLane}).PrepareProposalHandler() + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, defaultLane}).PrepareProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000}) s.Require().NoError(err) @@ -301,11 +301,11 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(err) // Set up the TOB lane with the bid tx and the bundled tx - tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ + mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ tx: true, bundleTxs[0]: true, }) - s.Require().NoError(tobLane.Insert(sdk.Context{}, tx)) + s.Require().NoError(mevLane.Insert(sdk.Context{}, tx)) // Set up the default lane with the bid tx and the bundled tx defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ @@ -316,7 +316,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) s.Require().NoError(defaultLane.Insert(sdk.Context{}, bundleTxs[0])) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, defaultLane}).PrepareProposalHandler() + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, defaultLane}).PrepareProposalHandler() proposal := s.getTxBytes(tx, bundleTxs[0]) size := int64(len(proposal[0]) - 1) @@ -329,7 +329,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { }) s.Run("can build a proposal with single tx from middle lane", func() { - tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), nil) + mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), nil) freeTx, err := testutils.CreateFreeTx( s.encodingConfig.TxConfig, @@ -352,7 +352,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { }) s.Require().NoError(freeLane.Insert(sdk.Context{}, freeTx)) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, freeLane, defaultLane}).PrepareProposalHandler() + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, freeLane, defaultLane}).PrepareProposalHandler() proposal := s.getTxBytes(freeTx) @@ -399,14 +399,14 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { ) s.Require().NoError(err) - tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{ + mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{ tx: true, bundleTxs[0]: true, bundleTxs[1]: true, bundleTxs[2]: true, bundleTxs[3]: true, }) - tobLane.Insert(sdk.Context{}, tx) + mevLane.Insert(sdk.Context{}, tx) defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ normalTx: true, @@ -418,7 +418,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { }) freeLane.Insert(sdk.Context{}, freeTx) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, freeLane, defaultLane}).PrepareProposalHandler() + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, freeLane, defaultLane}).PrepareProposalHandler() proposal := s.getTxBytes(tx, bundleTxs[0], bundleTxs[1], bundleTxs[2], bundleTxs[3], freeTx, normalTx) resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 1000000000}) @@ -568,11 +568,11 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() { func (s *ProposalsTestSuite) TestProcessProposal() { s.Run("can process a valid empty proposal", func() { - tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{}) + mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{}) freeLane := s.setUpFreeLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{}) defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{}) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, freeLane, defaultLane}).ProcessProposalHandler() + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, freeLane, defaultLane}).ProcessProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: nil}) s.Require().NoError(err) @@ -581,11 +581,11 @@ func (s *ProposalsTestSuite) TestProcessProposal() { }) s.Run("rejects a proposal with bad txs", func() { - tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{}) + mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{}) freeLane := s.setUpFreeLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{}) defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{}) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, freeLane, defaultLane}).ProcessProposalHandler() + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, freeLane, defaultLane}).ProcessProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: [][]byte{{0x01, 0x02, 0x03}}}) s.Require().Error(err) @@ -593,7 +593,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { }) s.Run("rejects a proposal when a lane panics", func() { - tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{}) + mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{}) panicLane := s.setUpPanicLane(math.LegacyMustNewDecFromStr("0.0")) txbz, err := testutils.CreateRandomTxBz( @@ -605,7 +605,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { ) s.Require().NoError(err) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, panicLane}).ProcessProposalHandler() + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, panicLane}).ProcessProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: [][]byte{txbz}}) s.Require().Error(err) s.Require().Equal(&cometabci.ResponseProcessProposal{Status: cometabci.ResponseProcessProposal_REJECT}, resp) @@ -680,10 +680,10 @@ func (s *ProposalsTestSuite) TestProcessProposal() { defaultLane.SetProcessLaneHandler(blockbuster.NoOpProcessLaneHandler()) // Set up the TOB lane - tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), nil) - tobLane.SetProcessLaneHandler(blockbuster.NoOpProcessLaneHandler()) + mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), nil) + mevLane.SetProcessLaneHandler(blockbuster.NoOpProcessLaneHandler()) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, defaultLane}).ProcessProposalHandler() + proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, defaultLane}).ProcessProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: s.getTxBytes(bidTx, bundle[0], bundle[1], normalTx, normalTx2)}) s.Require().NotNil(resp) s.Require().Error(err) @@ -735,7 +735,7 @@ func (s *ProposalsTestSuite) setUpDefaultLane(maxBlockSpace math.LegacyDec, expe return base.NewDefaultLane(cfg) } -func (s *ProposalsTestSuite) setUpTOBLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *auction.TOBLane { +func (s *ProposalsTestSuite) setUpTOBLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *mev.MEVLane { cfg := blockbuster.LaneConfig{ Logger: log.NewTestLogger(s.T()), TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), @@ -744,7 +744,7 @@ func (s *ProposalsTestSuite) setUpTOBLane(maxBlockSpace math.LegacyDec, expected MaxBlockSpace: maxBlockSpace, } - return auction.NewTOBLane(cfg, auction.NewDefaultAuctionFactory(cfg.TxDecoder)) + return mev.NewMEVLane(cfg, mev.NewDefaultAuctionFactory(cfg.TxDecoder)) } func (s *ProposalsTestSuite) setUpFreeLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *free.FreeLane { diff --git a/blockbuster/lanes/auction/abci.go b/blockbuster/lanes/mev/abci.go similarity index 95% rename from blockbuster/lanes/auction/abci.go rename to blockbuster/lanes/mev/abci.go index 0773c82..412d97c 100644 --- a/blockbuster/lanes/auction/abci.go +++ b/blockbuster/lanes/mev/abci.go @@ -1,4 +1,4 @@ -package auction +package mev import ( "bytes" @@ -14,7 +14,7 @@ import ( // and whose bundled transactions are valid and include them in the proposal. It // 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 { +func (l *MEVLane) 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 ( @@ -128,7 +128,7 @@ func (l *TOBLane) PrepareLaneHandler() blockbuster.PrepareLaneHandler { txs = append(txs, bundledTxBz...) // Write the cache context to the original context when we know we have a - // valid top of block bundle. + // valid bundle. write() break selectBidTxLoop @@ -146,8 +146,8 @@ func (l *TOBLane) PrepareLaneHandler() blockbuster.PrepareLaneHandler { } // ProcessLaneHandler will ensure that block proposals that include transactions from -// the top-of-block auction lane are valid. -func (l *TOBLane) ProcessLaneHandler() blockbuster.ProcessLaneHandler { +// the mev lane are valid. +func (l *MEVLane) ProcessLaneHandler() blockbuster.ProcessLaneHandler { return func(ctx sdk.Context, txs []sdk.Tx) ([]sdk.Tx, error) { if len(txs) == 0 { return txs, nil @@ -178,7 +178,7 @@ func (l *TOBLane) ProcessLaneHandler() blockbuster.ProcessLaneHandler { // - there are no other bid transactions in the proposal // - transactions from other lanes are not interleaved with transactions from the bid // transaction. -func (l *TOBLane) CheckOrderHandler() blockbuster.CheckOrderHandler { +func (l *MEVLane) CheckOrderHandler() blockbuster.CheckOrderHandler { return func(ctx sdk.Context, txs []sdk.Tx) error { if len(txs) == 0 { return nil @@ -241,7 +241,7 @@ func (l *TOBLane) CheckOrderHandler() blockbuster.CheckOrderHandler { // 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, bidInfo *types.BidInfo) (err error) { +func (l *MEVLane) VerifyTx(ctx sdk.Context, bidTx sdk.Tx, bidInfo *types.BidInfo) (err error) { if bidInfo == nil { return fmt.Errorf("bid info is nil") } diff --git a/blockbuster/lanes/auction/check_tx.go b/blockbuster/lanes/mev/check_tx.go similarity index 95% rename from blockbuster/lanes/auction/check_tx.go rename to blockbuster/lanes/mev/check_tx.go index 089ee99..fc5cafe 100644 --- a/blockbuster/lanes/auction/check_tx.go +++ b/blockbuster/lanes/mev/check_tx.go @@ -1,4 +1,4 @@ -package auction +package mev import ( "fmt" @@ -26,9 +26,9 @@ type ( // bid transactions. txDecoder sdk.TxDecoder - // TOBLane is utilized to retrieve the bid info of a transaction and to + // MEVLane is utilized to retrieve the bid info of a transaction and to // insert a bid transaction into the application-side mempool. - tobLane TOBLaneI + mevLane MEVLaneI // anteHandler is utilized to verify the bid transaction against the latest // committed state. @@ -67,13 +67,13 @@ type ( func NewCheckTxHandler( baseApp BaseApp, txDecoder sdk.TxDecoder, - tobLane TOBLaneI, + mevLane MEVLaneI, anteHandler sdk.AnteHandler, ) *CheckTxHandler { return &CheckTxHandler{ baseApp: baseApp, txDecoder: txDecoder, - tobLane: tobLane, + mevLane: mevLane, anteHandler: anteHandler, } } @@ -121,7 +121,7 @@ func (handler *CheckTxHandler) CheckTx() CheckTx { } // Attempt to get the bid info of the transaction. - bidInfo, err := handler.tobLane.GetAuctionBidInfo(tx) + bidInfo, err := handler.mevLane.GetAuctionBidInfo(tx) if err != nil { handler.baseApp.Logger().Info( "failed to get auction bid info", @@ -173,7 +173,7 @@ func (handler *CheckTxHandler) CheckTx() CheckTx { } // If the bid transaction is valid, we know we can insert it into the mempool for consideration in the next block. - if err := handler.tobLane.Insert(ctx, tx); err != nil { + if err := handler.mevLane.Insert(ctx, tx); err != nil { handler.baseApp.Logger().Info( "invalid bid tx; failed to insert bid transaction into mempool", "err", err, @@ -212,13 +212,13 @@ func (handler *CheckTxHandler) ValidateBidTx(ctx sdk.Context, bidTx sdk.Tx, bidI // Verify all of the bundled transactions. for _, tx := range bidInfo.Transactions { - bundledTx, err := handler.tobLane.WrapBundleTransaction(tx) + bundledTx, err := handler.mevLane.WrapBundleTransaction(tx) if err != nil { return gasInfo, fmt.Errorf("invalid bid tx; failed to decode bundled tx: %w", err) } // bid txs cannot be included in bundled txs - bidInfo, _ := handler.tobLane.GetAuctionBidInfo(bundledTx) + bidInfo, _ := handler.mevLane.GetAuctionBidInfo(bundledTx) if bidInfo != nil { return gasInfo, fmt.Errorf("invalid bid tx; bundled tx cannot be a bid tx") } diff --git a/blockbuster/lanes/auction/factory.go b/blockbuster/lanes/mev/factory.go similarity index 99% rename from blockbuster/lanes/auction/factory.go rename to blockbuster/lanes/mev/factory.go index 6032aa9..7569340 100644 --- a/blockbuster/lanes/auction/factory.go +++ b/blockbuster/lanes/mev/factory.go @@ -1,4 +1,4 @@ -package auction +package mev import ( "fmt" diff --git a/blockbuster/lanes/auction/factory_test.go b/blockbuster/lanes/mev/factory_test.go similarity index 96% rename from blockbuster/lanes/auction/factory_test.go rename to blockbuster/lanes/mev/factory_test.go index 53c6ec1..b68587e 100644 --- a/blockbuster/lanes/auction/factory_test.go +++ b/blockbuster/lanes/mev/factory_test.go @@ -1,4 +1,4 @@ -package auction_test +package mev_test import ( "crypto/rand" @@ -8,7 +8,7 @@ import ( testutils "github.com/skip-mev/pob/testutils" ) -func (suite *IntegrationTestSuite) TestIsAuctionTx() { +func (suite *MEVTestSuite) TestIsAuctionTx() { testCases := []struct { name string createTx func() sdk.Tx @@ -92,7 +92,7 @@ func (suite *IntegrationTestSuite) TestIsAuctionTx() { } } -func (suite *IntegrationTestSuite) TestGetTransactionSigners() { +func (suite *MEVTestSuite) TestGetTransactionSigners() { testCases := []struct { name string createTx func() sdk.Tx @@ -176,7 +176,7 @@ func (suite *IntegrationTestSuite) TestGetTransactionSigners() { } } -func (suite *IntegrationTestSuite) TestWrapBundleTransaction() { +func (suite *MEVTestSuite) TestWrapBundleTransaction() { testCases := []struct { name string createBundleTx func() (sdk.Tx, []byte) @@ -229,7 +229,7 @@ func (suite *IntegrationTestSuite) TestWrapBundleTransaction() { } } -func (suite *IntegrationTestSuite) TestGetBidder() { +func (suite *MEVTestSuite) TestGetBidder() { testCases := []struct { name string createTx func() sdk.Tx @@ -304,7 +304,7 @@ func (suite *IntegrationTestSuite) TestGetBidder() { } } -func (suite *IntegrationTestSuite) TestGetBid() { +func (suite *MEVTestSuite) TestGetBid() { testCases := []struct { name string createTx func() sdk.Tx @@ -379,7 +379,7 @@ func (suite *IntegrationTestSuite) TestGetBid() { } } -func (suite *IntegrationTestSuite) TestGetBundledTransactions() { +func (suite *MEVTestSuite) TestGetBundledTransactions() { testCases := []struct { name string createTx func() (sdk.Tx, [][]byte) @@ -450,7 +450,7 @@ func (suite *IntegrationTestSuite) TestGetBundledTransactions() { } } -func (suite *IntegrationTestSuite) TestGetTimeout() { +func (suite *MEVTestSuite) TestGetTimeout() { testCases := []struct { name string createTx func() sdk.Tx diff --git a/blockbuster/lanes/auction/lane.go b/blockbuster/lanes/mev/lane.go similarity index 65% rename from blockbuster/lanes/auction/lane.go rename to blockbuster/lanes/mev/lane.go index 4be6982..6a8f335 100644 --- a/blockbuster/lanes/auction/lane.go +++ b/blockbuster/lanes/mev/lane.go @@ -1,4 +1,4 @@ -package auction +package mev import ( "context" @@ -8,30 +8,30 @@ import ( ) const ( - // LaneName defines the name of the top-of-block auction lane. - LaneName = "top-of-block" + // LaneName defines the name of the mev lane. + LaneName = "mev" ) var ( - _ TOBLaneI = (*TOBLane)(nil) + _ MEVLaneI = (*MEVLane)(nil) ) -// TOBLane defines a top-of-block auction lane. The top of block auction lane +// MEVLane defines a MEV (Maximal Extracted Value) auction lane. The MEV auction lane // hosts transactions that want to bid for inclusion at the top of the next block. -// The top of block auction lane stores bid transactions that are sorted by -// 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. +// The MEV auction lane stores bid transactions that are sorted by 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 ( - // TOBLaneI defines the interface for the top-of-block auction lane. This interface + // MEVLaneI defines the interface for the mev auction lane. This interface // is utilized by both the x/builder module and the checkTx handler. - TOBLaneI interface { + MEVLaneI interface { blockbuster.Lane Factory GetTopAuctionTx(ctx context.Context) sdk.Tx } - TOBLane struct { + MEVLane struct { // LaneConfig defines the base lane configuration. *blockbuster.LaneConstructor[string] @@ -42,12 +42,12 @@ type ( } ) -// NewTOBLane returns a new TOB lane. -func NewTOBLane( +// NewMEVLane returns a new TOB lane. +func NewMEVLane( cfg blockbuster.LaneConfig, factory Factory, -) *TOBLane { - lane := &TOBLane{ +) *MEVLane { + lane := &MEVLane{ LaneConstructor: blockbuster.NewLaneConstructor[string]( cfg, LaneName, diff --git a/blockbuster/lanes/auction/mempool.go b/blockbuster/lanes/mev/mempool.go similarity index 84% rename from blockbuster/lanes/auction/mempool.go rename to blockbuster/lanes/mev/mempool.go index 68a3b81..4e0caea 100644 --- a/blockbuster/lanes/auction/mempool.go +++ b/blockbuster/lanes/mev/mempool.go @@ -1,4 +1,4 @@ -package auction +package mev import ( "context" @@ -7,8 +7,8 @@ import ( "github.com/skip-mev/pob/blockbuster" ) -// TxPriority returns a TxPriority over auction bid transactions only. It -// is to be used in the auction index only. +// TxPriority returns a TxPriority over mev lane transactions only. It +// is to be used in the mev index only. func TxPriority(config Factory) blockbuster.TxPriority[string] { return blockbuster.TxPriority[string]{ GetTxPriority: func(goCtx context.Context, tx sdk.Tx) string { @@ -52,7 +52,7 @@ func TxPriority(config Factory) blockbuster.TxPriority[string] { // GetTopAuctionTx returns the highest bidding transaction in the auction mempool. // This is primarily a helper function for the x/builder module. -func (l *TOBLane) GetTopAuctionTx(ctx context.Context) sdk.Tx { +func (l *MEVLane) GetTopAuctionTx(ctx context.Context) sdk.Tx { iterator := l.Select(ctx, nil) if iterator == nil { return nil diff --git a/blockbuster/lanes/auction/auction_test.go b/blockbuster/lanes/mev/mev_test.go similarity index 73% rename from blockbuster/lanes/auction/auction_test.go rename to blockbuster/lanes/mev/mev_test.go index ffe71ae..bd6530b 100644 --- a/blockbuster/lanes/auction/auction_test.go +++ b/blockbuster/lanes/mev/mev_test.go @@ -1,4 +1,4 @@ -package auction_test +package mev_test import ( "math/rand" @@ -8,16 +8,16 @@ import ( "cosmossdk.io/log" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/skip-mev/pob/blockbuster/lanes/auction" + "github.com/skip-mev/pob/blockbuster/lanes/mev" testutils "github.com/skip-mev/pob/testutils" "github.com/stretchr/testify/suite" ) -type IntegrationTestSuite struct { +type MEVTestSuite struct { suite.Suite encCfg testutils.EncodingConfig - config auction.Factory + config mev.Factory ctx sdk.Context random *rand.Rand accounts []testutils.Account @@ -25,13 +25,13 @@ type IntegrationTestSuite struct { } func TestMempoolTestSuite(t *testing.T) { - suite.Run(t, new(IntegrationTestSuite)) + suite.Run(t, new(MEVTestSuite)) } -func (suite *IntegrationTestSuite) SetupTest() { +func (suite *MEVTestSuite) SetupTest() { // Mempool setup suite.encCfg = testutils.CreateTestEncodingConfig() - suite.config = auction.NewDefaultAuctionFactory(suite.encCfg.TxConfig.TxDecoder()) + suite.config = mev.NewDefaultAuctionFactory(suite.encCfg.TxConfig.TxDecoder()) suite.ctx = sdk.NewContext(nil, cmtproto.Header{}, false, log.NewTestLogger(suite.T())) // Init accounts diff --git a/blockbuster/lanes/auction/utils.go b/blockbuster/lanes/mev/utils.go similarity index 98% rename from blockbuster/lanes/auction/utils.go rename to blockbuster/lanes/mev/utils.go index 6d85330..4406c56 100644 --- a/blockbuster/lanes/auction/utils.go +++ b/blockbuster/lanes/mev/utils.go @@ -1,4 +1,4 @@ -package auction +package mev import ( "errors" diff --git a/blockbuster/lanes/auction/utils_test.go b/blockbuster/lanes/mev/utils_test.go similarity index 79% rename from blockbuster/lanes/auction/utils_test.go rename to blockbuster/lanes/mev/utils_test.go index 2db4b74..020dc84 100644 --- a/blockbuster/lanes/auction/utils_test.go +++ b/blockbuster/lanes/mev/utils_test.go @@ -1,10 +1,10 @@ -package auction_test +package mev_test import ( "testing" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/skip-mev/pob/blockbuster/lanes/auction" + "github.com/skip-mev/pob/blockbuster/lanes/mev" testutils "github.com/skip-mev/pob/testutils" buildertypes "github.com/skip-mev/pob/x/builder/types" "github.com/stretchr/testify/require" @@ -16,7 +16,7 @@ func TestGetMsgAuctionBidFromTx_Valid(t *testing.T) { txBuilder := encCfg.TxConfig.NewTxBuilder() txBuilder.SetMsgs(&buildertypes.MsgAuctionBid{}) - msg, err := auction.GetMsgAuctionBidFromTx(txBuilder.GetTx()) + msg, err := mev.GetMsgAuctionBidFromTx(txBuilder.GetTx()) require.NoError(t, err) require.NotNil(t, msg) } @@ -31,7 +31,7 @@ func TestGetMsgAuctionBidFromTx_MultiMsgBid(t *testing.T) { &banktypes.MsgSend{}, ) - msg, err := auction.GetMsgAuctionBidFromTx(txBuilder.GetTx()) + msg, err := mev.GetMsgAuctionBidFromTx(txBuilder.GetTx()) require.Error(t, err) require.Nil(t, msg) } @@ -42,7 +42,7 @@ func TestGetMsgAuctionBidFromTx_NoBid(t *testing.T) { txBuilder := encCfg.TxConfig.NewTxBuilder() txBuilder.SetMsgs(&banktypes.MsgSend{}) - msg, err := auction.GetMsgAuctionBidFromTx(txBuilder.GetTx()) + msg, err := mev.GetMsgAuctionBidFromTx(txBuilder.GetTx()) require.NoError(t, err) require.Nil(t, msg) } diff --git a/blockbuster/mempool_test.go b/blockbuster/mempool_test.go index 84e470c..32ee173 100644 --- a/blockbuster/mempool_test.go +++ b/blockbuster/mempool_test.go @@ -11,9 +11,9 @@ import ( "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/skip-mev/pob/blockbuster" - "github.com/skip-mev/pob/blockbuster/lanes/auction" "github.com/skip-mev/pob/blockbuster/lanes/base" "github.com/skip-mev/pob/blockbuster/lanes/free" + "github.com/skip-mev/pob/blockbuster/lanes/mev" testutils "github.com/skip-mev/pob/testutils" buildertypes "github.com/skip-mev/pob/x/builder/types" "github.com/stretchr/testify/suite" @@ -27,7 +27,7 @@ type BlockBusterTestSuite struct { encodingConfig testutils.EncodingConfig // Define all of the lanes utilized in the test suite - tobLane *auction.TOBLane + mevLane *mev.MEVLane baseLane *base.DefaultLane freeLane *free.FreeLane gasTokenDenom string @@ -57,16 +57,16 @@ func (suite *BlockBusterTestSuite) SetupTest() { // // TOB lane set up suite.gasTokenDenom = "stake" - tobConfig := blockbuster.LaneConfig{ + mevConfig := blockbuster.LaneConfig{ Logger: log.NewNopLogger(), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), AnteHandler: nil, MaxBlockSpace: math.LegacyZeroDec(), } - suite.tobLane = auction.NewTOBLane( - tobConfig, - auction.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder()), + suite.mevLane = mev.NewMEVLane( + mevConfig, + mev.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder()), ) // Free lane set up @@ -96,7 +96,7 @@ func (suite *BlockBusterTestSuite) SetupTest() { ) // Mempool set up - suite.lanes = []blockbuster.Lane{suite.tobLane, suite.freeLane, suite.baseLane} + suite.lanes = []blockbuster.Lane{suite.mevLane, suite.freeLane, suite.baseLane} suite.mempool = blockbuster.NewMempool(log.NewTestLogger(suite.T()), true, suite.lanes...) // Accounts set up @@ -113,15 +113,15 @@ func (suite *BlockBusterTestSuite) TestInsert() { insertDistribution map[string]int }{ { - "insert 1 tob tx", + "insert 1 mev tx", map[string]int{ - suite.tobLane.Name(): 1, + suite.mevLane.Name(): 1, }, }, { - "insert 10 tob txs", + "insert 10 mev txs", map[string]int{ - suite.tobLane.Name(): 10, + suite.mevLane.Name(): 10, }, }, { @@ -131,24 +131,24 @@ func (suite *BlockBusterTestSuite) TestInsert() { }, }, { - "insert 10 base txs and 10 tob txs", + "insert 10 base txs and 10 mev txs", map[string]int{ suite.baseLane.Name(): 10, - suite.tobLane.Name(): 10, + suite.mevLane.Name(): 10, }, }, { - "insert 100 base txs and 100 tob txs", + "insert 100 base txs and 100 mev txs", map[string]int{ suite.baseLane.Name(): 100, - suite.tobLane.Name(): 100, + suite.mevLane.Name(): 100, }, }, { - "insert 100 base txs, 100 tob txs, and 100 free txs", + "insert 100 base txs, 100 mev txs, and 100 free txs", map[string]int{ suite.baseLane.Name(): 100, - suite.tobLane.Name(): 100, + suite.mevLane.Name(): 100, suite.freeLane.Name(): 100, }, }, @@ -166,9 +166,9 @@ func (suite *BlockBusterTestSuite) TestInsert() { }, }, { - "insert 10 tob txs and 10 free txs", + "insert 10 mev txs and 10 free txs", map[string]int{ - suite.tobLane.Name(): 10, + suite.mevLane.Name(): 10, suite.freeLane.Name(): 10, }, }, @@ -182,7 +182,7 @@ func (suite *BlockBusterTestSuite) TestInsert() { suite.fillBaseLane(tc.insertDistribution[suite.baseLane.Name()]) // Fill the TOB lane with numTobTxs transactions - suite.fillTOBLane(tc.insertDistribution[suite.tobLane.Name()]) + suite.fillTOBLane(tc.insertDistribution[suite.mevLane.Name()]) // Fill the Free lane with numFreeTxs transactions suite.fillFreeLane(tc.insertDistribution[suite.freeLane.Name()]) @@ -196,7 +196,7 @@ func (suite *BlockBusterTestSuite) TestInsert() { suite.Require().Equal(sum, suite.mempool.CountTx()) // Validate the lanes - suite.Require().Equal(tc.insertDistribution[suite.tobLane.Name()], suite.tobLane.CountTx()) + suite.Require().Equal(tc.insertDistribution[suite.mevLane.Name()], suite.mevLane.CountTx()) suite.Require().Equal(tc.insertDistribution[suite.baseLane.Name()], suite.baseLane.CountTx()) suite.Require().Equal(tc.insertDistribution[suite.freeLane.Name()], suite.freeLane.CountTx()) @@ -204,7 +204,7 @@ func (suite *BlockBusterTestSuite) TestInsert() { laneCounts := suite.mempool.GetTxDistribution() // Ensure that the lane counts are correct - suite.Require().Equal(tc.insertDistribution[suite.tobLane.Name()], laneCounts[suite.tobLane.Name()]) + suite.Require().Equal(tc.insertDistribution[suite.mevLane.Name()], laneCounts[suite.mevLane.Name()]) suite.Require().Equal(tc.insertDistribution[suite.baseLane.Name()], laneCounts[suite.baseLane.Name()]) suite.Require().Equal(tc.insertDistribution[suite.freeLane.Name()], laneCounts[suite.freeLane.Name()]) }) @@ -218,12 +218,12 @@ func (suite *BlockBusterTestSuite) TestRemove() { numBaseTxs int }{ { - "insert 1 tob tx", + "insert 1 mev tx", 1, 0, }, { - "insert 10 tob txs", + "insert 10 mev txs", 10, 0, }, @@ -233,12 +233,12 @@ func (suite *BlockBusterTestSuite) TestRemove() { 1, }, { - "insert 10 base txs and 10 tob txs", + "insert 10 base txs and 10 mev txs", 10, 10, }, { - "insert 100 base txs and 100 tob txs", + "insert 100 base txs and 100 mev txs", 100, 100, }, @@ -255,7 +255,7 @@ func (suite *BlockBusterTestSuite) TestRemove() { suite.fillTOBLane(tc.numTobTxs) // Remove all transactions from the lanes - tobCount := tc.numTobTxs + mevCount := tc.numTobTxs baseCount := tc.numBaseTxs for iterator := suite.baseLane.Select(suite.ctx, nil); iterator != nil; { tx := iterator.Tx() @@ -277,10 +277,10 @@ func (suite *BlockBusterTestSuite) TestRemove() { } suite.Require().Equal(0, suite.baseLane.CountTx()) - suite.Require().Equal(tobCount, suite.tobLane.CountTx()) + suite.Require().Equal(mevCount, suite.mevLane.CountTx()) // Remove all transactions from the lanes - for iterator := suite.tobLane.Select(suite.ctx, nil); iterator != nil; { + for iterator := suite.mevLane.Select(suite.ctx, nil); iterator != nil; { tx := iterator.Tx() // Remove the transaction from the mempool @@ -290,16 +290,16 @@ func (suite *BlockBusterTestSuite) TestRemove() { suite.Require().Equal(false, suite.mempool.Contains(tx)) // Ensure the number of transactions in the lane is correct - tobCount-- - suite.Require().Equal(suite.tobLane.CountTx(), tobCount) + mevCount-- + suite.Require().Equal(suite.mevLane.CountTx(), mevCount) distribution := suite.mempool.GetTxDistribution() - suite.Require().Equal(distribution[suite.tobLane.Name()], tobCount) + suite.Require().Equal(distribution[suite.mevLane.Name()], mevCount) - iterator = suite.tobLane.Select(suite.ctx, nil) + iterator = suite.mevLane.Select(suite.ctx, nil) } - suite.Require().Equal(0, suite.tobLane.CountTx()) + suite.Require().Equal(0, suite.mevLane.CountTx()) suite.Require().Equal(0, suite.baseLane.CountTx()) suite.Require().Equal(0, suite.mempool.CountTx()) @@ -307,7 +307,7 @@ func (suite *BlockBusterTestSuite) TestRemove() { distribution := suite.mempool.GetTxDistribution() // Ensure that the lane counts are correct - suite.Require().Equal(distribution[suite.tobLane.Name()], 0) + suite.Require().Equal(distribution[suite.mevLane.Name()], 0) suite.Require().Equal(distribution[suite.baseLane.Name()], 0) }) } diff --git a/go.work.sum b/go.work.sum index 8109fb3..177f0a9 100644 --- a/go.work.sum +++ b/go.work.sum @@ -4,7 +4,9 @@ github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLj github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cosmos/cosmos-db v0.0.0-20221226095112-f3c38ecb5e32 h1:zlCp9n3uwQieELltZWHRmwPmPaZ8+XoL2Sj+A2YJlr8= +github.com/docker/docker v23.0.1+incompatible h1:vjgvJZxprTTE1A37nm+CLNAdwu6xZekyoiVlUZEINcY= github.com/getsentry/sentry-go v0.17.0 h1:UustVWnOoDFHBS7IJUB2QK/nB5pap748ZEp0swnQJak= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= diff --git a/tests/app/ante.go b/tests/app/ante.go index 7fdbf91..c10d80b 100644 --- a/tests/app/ante.go +++ b/tests/app/ante.go @@ -12,7 +12,7 @@ import ( type POBHandlerOptions struct { BaseOptions ante.HandlerOptions Mempool blockbuster.Mempool - TOBLane builderante.TOBLane + MEVLane builderante.MEVLane TxDecoder sdk.TxDecoder TxEncoder sdk.TxEncoder BuilderKeeper builderkeeper.Keeper @@ -54,7 +54,7 @@ func NewPOBAnteHandler(options POBHandlerOptions) sdk.AnteHandler { ante.NewSigGasConsumeDecorator(options.BaseOptions.AccountKeeper, options.BaseOptions.SigGasConsumer), ante.NewSigVerificationDecorator(options.BaseOptions.AccountKeeper, options.BaseOptions.SignModeHandler), ante.NewIncrementSequenceDecorator(options.BaseOptions.AccountKeeper), - builderante.NewBuilderDecorator(options.BuilderKeeper, options.TxEncoder, options.TOBLane, options.Mempool), + builderante.NewBuilderDecorator(options.BuilderKeeper, options.TxEncoder, options.MEVLane, options.Mempool), } return sdk.ChainAnteDecorators(anteDecorators...) diff --git a/tests/app/app.go b/tests/app/app.go index 8ebc49a..7b9f0d7 100644 --- a/tests/app/app.go +++ b/tests/app/app.go @@ -63,9 +63,9 @@ import ( "github.com/skip-mev/pob/blockbuster" "github.com/skip-mev/pob/blockbuster/abci" - "github.com/skip-mev/pob/blockbuster/lanes/auction" "github.com/skip-mev/pob/blockbuster/lanes/base" "github.com/skip-mev/pob/blockbuster/lanes/free" + "github.com/skip-mev/pob/blockbuster/lanes/mev" buildermodule "github.com/skip-mev/pob/x/builder" builderkeeper "github.com/skip-mev/pob/x/builder/keeper" ) @@ -139,7 +139,7 @@ type TestApp struct { FeeGrantKeeper feegrantkeeper.Keeper // custom checkTx handler - checkTxHandler auction.CheckTx + checkTxHandler mev.CheckTx } func init() { @@ -262,16 +262,16 @@ 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.LaneConfig{ + mevConfig := blockbuster.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), 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, - auction.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), + mevLane := mev.NewMEVLane( + mevConfig, + mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), ) // Free lane allows transactions to be included in the next block for free. @@ -300,7 +300,7 @@ func New( // Set the lanes into the mempool. lanes := []blockbuster.Lane{ - tobLane, + mevLane, freeLane, defaultLane, } @@ -322,7 +322,7 @@ func New( TxDecoder: app.txConfig.TxDecoder(), TxEncoder: app.txConfig.TxEncoder(), FreeLane: freeLane, - TOBLane: tobLane, + MEVLane: mevLane, Mempool: mempool, } anteHandler := NewPOBAnteHandler(options) @@ -343,10 +343,10 @@ func New( app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) // Set the custom CheckTx handler on BaseApp. - checkTxHandler := auction.NewCheckTxHandler( + checkTxHandler := mev.NewCheckTxHandler( app.App, app.txConfig.TxDecoder(), - tobLane, + mevLane, anteHandler, ) app.SetCheckTx(checkTxHandler.CheckTx()) @@ -392,7 +392,7 @@ func (app *TestApp) CheckTx(req *cometabci.RequestCheckTx) (*cometabci.ResponseC } // SetCheckTx sets the checkTxHandler for the app. -func (app *TestApp) SetCheckTx(handler auction.CheckTx) { +func (app *TestApp) SetCheckTx(handler mev.CheckTx) { app.checkTxHandler = handler } diff --git a/tests/integration/pob_suite.go b/tests/integration/pob_suite.go index 4f406c0..dde0672 100644 --- a/tests/integration/pob_suite.go +++ b/tests/integration/pob_suite.go @@ -1003,7 +1003,7 @@ func (s *POBIntegrationTestSuite) TestLanes() { params := QueryBuilderParams(s.T(), s.chain) - s.Run("block with tob, free, and normal tx", func() { + s.Run("block with mev, free, and normal tx", func() { user2BalanceBefore := QueryAccountBalance(s.T(), s.chain.(*cosmos.CosmosChain), s.user2.FormattedAddress(), s.denom) // create free-tx, bid-tx, and normal-tx\ @@ -1071,7 +1071,7 @@ func (s *POBIntegrationTestSuite) TestLanes() { require.Equal(s.T(), user2BalanceBefore, user2BalanceAfter+delegation.Amount.Int64()) }) - s.Run("failing top of block transaction, free, and normal tx", func() { + s.Run("failing MEV transaction, free, and normal tx", func() { user2BalanceBefore := QueryAccountBalance(s.T(), s.chain.(*cosmos.CosmosChain), s.user2.FormattedAddress(), s.denom) user1Balance := QueryAccountBalance(s.T(), s.chain.(*cosmos.CosmosChain), s.user1.FormattedAddress(), s.denom) // create free-tx, bid-tx, and normal-tx\ @@ -1151,7 +1151,7 @@ func (s *POBIntegrationTestSuite) TestLanes() { require.Equal(s.T(), user2BalanceBefore, user2BalanceAfter+delegation.Amount.Int64()) }) - s.Run("top of block transaction that includes transactions from the free lane", func() { + s.Run("MEV transaction that includes transactions from the free lane", func() { user2BalanceBefore := QueryAccountBalance(s.T(), s.chain.(*cosmos.CosmosChain), s.user2.FormattedAddress(), s.denom) delegateTx := Tx{ @@ -1212,7 +1212,7 @@ func (s *POBIntegrationTestSuite) TestLanes() { VerifyBlock(s.T(), block, 0, TxHash(txs[0]), bundledTx) }) - s.Run("top of block transaction that includes transaction from free lane + other free lane txs + normal txs", func() { + s.Run("MEV transaction that includes transaction from free lane + other free lane txs + normal txs", func() { user2BalanceBefore := QueryAccountBalance(s.T(), s.chain.(*cosmos.CosmosChain), s.user2.FormattedAddress(), s.denom) // create free-txs signed by user2 / 3 diff --git a/x/builder/ante/ante.go b/x/builder/ante/ante.go index 4c0a496..ae62909 100644 --- a/x/builder/ante/ante.go +++ b/x/builder/ante/ante.go @@ -14,9 +14,9 @@ import ( var _ sdk.AnteDecorator = BuilderDecorator{} type ( - // TOBLane is an interface that defines the methods required to interact with the top of block + // MEVLane is an interface that defines the methods required to interact with the MEV // lane. - TOBLane interface { + MEVLane interface { GetAuctionBidInfo(tx sdk.Tx) (*types.BidInfo, error) GetTopAuctionTx(ctx context.Context) sdk.Tx } @@ -30,12 +30,12 @@ type ( BuilderDecorator struct { builderKeeper keeper.Keeper txEncoder sdk.TxEncoder - lane TOBLane + lane MEVLane mempool Mempool } ) -func NewBuilderDecorator(ak keeper.Keeper, txEncoder sdk.TxEncoder, lane TOBLane, mempool Mempool) BuilderDecorator { +func NewBuilderDecorator(ak keeper.Keeper, txEncoder sdk.TxEncoder, lane MEVLane, mempool Mempool) BuilderDecorator { return BuilderDecorator{ builderKeeper: ak, txEncoder: txEncoder, diff --git a/x/builder/ante/ante_test.go b/x/builder/ante/ante_test.go index 5608d0b..a8b567c 100644 --- a/x/builder/ante/ante_test.go +++ b/x/builder/ante/ante_test.go @@ -12,8 +12,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/golang/mock/gomock" "github.com/skip-mev/pob/blockbuster" - "github.com/skip-mev/pob/blockbuster/lanes/auction" "github.com/skip-mev/pob/blockbuster/lanes/base" + "github.com/skip-mev/pob/blockbuster/lanes/mev" testutils "github.com/skip-mev/pob/testutils" "github.com/skip-mev/pob/x/builder/ante" "github.com/skip-mev/pob/x/builder/keeper" @@ -40,7 +40,7 @@ type AnteTestSuite struct { // mempool and lane set up mempool blockbuster.Mempool - tobLane *auction.TOBLane + mevLane *mev.MEVLane baseLane *base.DefaultLane lanes []blockbuster.Lane @@ -83,16 +83,16 @@ func (suite *AnteTestSuite) SetupTest() { // Lanes configuration // // TOB lane set up - tobConfig := blockbuster.LaneConfig{ + mevConfig := blockbuster.LaneConfig{ Logger: suite.ctx.Logger(), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), AnteHandler: suite.anteHandler, MaxBlockSpace: math.LegacyZeroDec(), } - suite.tobLane = auction.NewTOBLane( - tobConfig, - auction.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder()), + suite.mevLane = mev.NewMEVLane( + mevConfig, + mev.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder()), ) // Base lane set up @@ -102,12 +102,12 @@ func (suite *AnteTestSuite) SetupTest() { TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), AnteHandler: suite.anteHandler, MaxBlockSpace: math.LegacyZeroDec(), - IgnoreList: []blockbuster.Lane{suite.tobLane}, + IgnoreList: []blockbuster.Lane{suite.mevLane}, } suite.baseLane = base.NewDefaultLane(baseConfig) // Mempool set up - suite.lanes = []blockbuster.Lane{suite.tobLane, suite.baseLane} + suite.lanes = []blockbuster.Lane{suite.mevLane, suite.baseLane} suite.mempool = blockbuster.NewMempool(log.NewTestLogger(suite.T()), true, suite.lanes...) } @@ -180,7 +180,7 @@ func (suite *AnteTestSuite) TestAnteHandler() { false, }, { - "valid auction bid tx", + "valid mev bid tx", func() { balance = sdk.NewCoin("stake", math.NewInt(10000)) bid = sdk.NewCoin("stake", math.NewInt(1000)) @@ -189,14 +189,14 @@ func (suite *AnteTestSuite) TestAnteHandler() { true, }, { - "invalid auction bid tx with no timeout", + "invalid mev bid tx with no timeout", func() { timeout = 0 }, false, }, { - "auction tx is the top bidding tx", + "mev tx is the top bidding tx", func() { timeout = 1000 balance = sdk.NewCoin("stake", math.NewInt(10000)) @@ -211,7 +211,7 @@ func (suite *AnteTestSuite) TestAnteHandler() { true, }, { - "invalid frontrunning auction bid tx", + "invalid frontrunning mev bid tx", func() { randomAccount := testutils.RandomAccounts(suite.random, 2) bidder := randomAccount[0] @@ -223,7 +223,7 @@ func (suite *AnteTestSuite) TestAnteHandler() { false, }, { - "valid frontrunning auction bid tx", + "valid frontrunning mev bid tx", func() { randomAccount := testutils.RandomAccounts(suite.random, 2) bidder := randomAccount[0] @@ -235,7 +235,7 @@ func (suite *AnteTestSuite) TestAnteHandler() { true, }, { - "invalid sandwiching auction bid tx", + "invalid sandwiching mev bid tx", func() { randomAccount := testutils.RandomAccounts(suite.random, 2) bidder := randomAccount[0] @@ -247,7 +247,7 @@ func (suite *AnteTestSuite) TestAnteHandler() { false, }, { - "invalid auction bid tx with many signers", + "invalid mev bid tx with many signers", func() { signers = testutils.RandomAccounts(suite.random, 10) frontRunningProtection = true @@ -263,7 +263,7 @@ func (suite *AnteTestSuite) TestAnteHandler() { suite.ctx = suite.ctx.WithBlockHeight(1) - // Set the auction params + // Set the mev params err := suite.builderKeeper.SetParams(suite.ctx, buildertypes.Params{ MaxBundleSize: maxBundleSize, ReserveFee: reserveFee, @@ -278,24 +278,24 @@ func (suite *AnteTestSuite) TestAnteHandler() { suite.Require().NoError(err) distribution := suite.mempool.GetTxDistribution() - suite.Require().Equal(0, distribution[auction.LaneName]) + suite.Require().Equal(0, distribution[mev.LaneName]) suite.Require().Equal(0, distribution[base.LaneName]) suite.Require().NoError(suite.mempool.Insert(suite.ctx, topAuctionTx)) distribution = suite.mempool.GetTxDistribution() - suite.Require().Equal(1, distribution[auction.LaneName]) + suite.Require().Equal(1, distribution[mev.LaneName]) suite.Require().Equal(0, distribution[base.LaneName]) } - // Create the actual auction tx and insert into the mempool - auctionTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, 0, timeout, signers) + // Create the actual mev tx and insert into the mempool + mevTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, 0, timeout, signers) suite.Require().NoError(err) // Execute the ante handler suite.balance = balance - suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxEncoder(), suite.tobLane, suite.mempool) - _, err = suite.anteHandler(suite.ctx, auctionTx, false) + suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxEncoder(), suite.mevLane, suite.mempool) + _, err = suite.anteHandler(suite.ctx, mevTx, false) if tc.pass { suite.Require().NoError(err) } else { From 9d349910d1071eab5c44a35550d6033e0aecf97b Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 11:09:17 -0400 Subject: [PATCH 11/41] moving lanes to their own directory --- {blockbuster/abci => abci}/abci.go | 2 +- {blockbuster/abci => abci}/abci_test.go | 21 ++++----- .../{lane_abci.go => constructor/abci.go} | 11 ++--- .../handlers.go} | 13 +++--- .../lane.go} | 29 ++++++------ .../mempool.go} | 15 ++++--- blockbuster/{lane_interface.go => lane.go} | 0 blockbuster/mempool.go | 44 +++++++------------ blockbuster/mempool_test.go | 11 ++--- .../lanes => lanes}/base/abci_test.go | 2 +- .../lanes => lanes}/base/base_test.go | 0 {blockbuster/lanes => lanes}/base/lane.go | 11 ++--- .../lanes => lanes}/base/mempool_test.go | 14 +++--- {blockbuster/lanes => lanes}/free/lane.go | 7 +-- {blockbuster/lanes => lanes}/mev/abci.go | 0 {blockbuster/lanes => lanes}/mev/check_tx.go | 0 {blockbuster/lanes => lanes}/mev/factory.go | 0 .../lanes => lanes}/mev/factory_test.go | 0 {blockbuster/lanes => lanes}/mev/lane.go | 7 +-- {blockbuster/lanes => lanes}/mev/mempool.go | 0 {blockbuster/lanes => lanes}/mev/mev_test.go | 2 +- {blockbuster/lanes => lanes}/mev/utils.go | 0 .../lanes => lanes}/mev/utils_test.go | 2 +- .../lanes => lanes}/terminator/lane.go | 0 tests/app/app.go | 13 +++--- x/builder/ante/ante_test.go | 6 +-- 26 files changed, 103 insertions(+), 107 deletions(-) rename {blockbuster/abci => abci}/abci.go (99%) rename {blockbuster/abci => abci}/abci_test.go (97%) rename blockbuster/{lane_abci.go => constructor/abci.go} (90%) rename blockbuster/{lane_handlers.go => constructor/handlers.go} (89%) rename blockbuster/{lane_constructor.go => constructor/lane.go} (91%) rename blockbuster/{lane_mempool.go => constructor/mempool.go} (91%) rename blockbuster/{lane_interface.go => lane.go} (100%) rename {blockbuster/lanes => lanes}/base/abci_test.go (99%) rename {blockbuster/lanes => lanes}/base/base_test.go (100%) rename {blockbuster/lanes => lanes}/base/lane.go (77%) rename {blockbuster/lanes => lanes}/base/mempool_test.go (91%) rename {blockbuster/lanes => lanes}/free/lane.go (88%) rename {blockbuster/lanes => lanes}/mev/abci.go (100%) rename {blockbuster/lanes => lanes}/mev/check_tx.go (100%) rename {blockbuster/lanes => lanes}/mev/factory.go (100%) rename {blockbuster/lanes => lanes}/mev/factory_test.go (100%) rename {blockbuster/lanes => lanes}/mev/lane.go (90%) rename {blockbuster/lanes => lanes}/mev/mempool.go (100%) rename {blockbuster/lanes => lanes}/mev/mev_test.go (95%) rename {blockbuster/lanes => lanes}/mev/utils.go (100%) rename {blockbuster/lanes => lanes}/mev/utils_test.go (96%) rename {blockbuster/lanes => lanes}/terminator/lane.go (100%) diff --git a/blockbuster/abci/abci.go b/abci/abci.go similarity index 99% rename from blockbuster/abci/abci.go rename to abci/abci.go index 2e8f2f9..e7c3777 100644 --- a/blockbuster/abci/abci.go +++ b/abci/abci.go @@ -7,8 +7,8 @@ import ( abci "github.com/cometbft/cometbft/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/skip-mev/pob/blockbuster" - "github.com/skip-mev/pob/blockbuster/lanes/terminator" "github.com/skip-mev/pob/blockbuster/utils" + "github.com/skip-mev/pob/lanes/terminator" ) type ( diff --git a/blockbuster/abci/abci_test.go b/abci/abci_test.go similarity index 97% rename from blockbuster/abci/abci_test.go rename to abci/abci_test.go index 6ceb3d6..0f9d711 100644 --- a/blockbuster/abci/abci_test.go +++ b/abci/abci_test.go @@ -13,11 +13,12 @@ import ( cometabci "github.com/cometbft/cometbft/abci/types" "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/skip-mev/pob/abci" "github.com/skip-mev/pob/blockbuster" - "github.com/skip-mev/pob/blockbuster/abci" - "github.com/skip-mev/pob/blockbuster/lanes/base" - "github.com/skip-mev/pob/blockbuster/lanes/free" - "github.com/skip-mev/pob/blockbuster/lanes/mev" + "github.com/skip-mev/pob/blockbuster/constructor" + "github.com/skip-mev/pob/lanes/base" + "github.com/skip-mev/pob/lanes/free" + "github.com/skip-mev/pob/lanes/mev" testutils "github.com/skip-mev/pob/testutils" "github.com/stretchr/testify/suite" ) @@ -756,10 +757,10 @@ func (s *ProposalsTestSuite) setUpFreeLane(maxBlockSpace math.LegacyDec, expecte MaxBlockSpace: maxBlockSpace, } - return free.NewFreeLane(cfg, blockbuster.DefaultTxPriority(), free.DefaultMatchHandler()) + return free.NewFreeLane(cfg, constructor.DefaultTxPriority(), free.DefaultMatchHandler()) } -func (s *ProposalsTestSuite) setUpPanicLane(maxBlockSpace math.LegacyDec) *blockbuster.LaneConstructor[string] { +func (s *ProposalsTestSuite) setUpPanicLane(maxBlockSpace math.LegacyDec) *constructor.LaneConstructor[string] { cfg := blockbuster.LaneConfig{ Logger: log.NewTestLogger(s.T()), TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), @@ -767,11 +768,11 @@ func (s *ProposalsTestSuite) setUpPanicLane(maxBlockSpace math.LegacyDec) *block MaxBlockSpace: maxBlockSpace, } - lane := blockbuster.NewLaneConstructor[string]( + lane := constructor.NewLaneConstructor[string]( cfg, "panic", - blockbuster.NewConstructorMempool[string](blockbuster.DefaultTxPriority(), cfg.TxEncoder, 0), - blockbuster.DefaultMatchHandler(), + constructor.NewConstructorMempool[string](constructor.DefaultTxPriority(), cfg.TxEncoder, 0), + constructor.DefaultMatchHandler(), ) lane.SetPrepareLaneHandler(blockbuster.PanicPrepareLaneHandler()) @@ -781,7 +782,7 @@ func (s *ProposalsTestSuite) setUpPanicLane(maxBlockSpace math.LegacyDec) *block } func (s *ProposalsTestSuite) setUpProposalHandlers(lanes []blockbuster.Lane) *abci.ProposalHandler { - mempool := blockbuster.NewMempool(log.NewTestLogger(s.T()), true, lanes...) + mempool := blockbuster.NewLanedMempool(log.NewTestLogger(s.T()), true, lanes...) return abci.NewProposalHandler( log.NewTestLogger(s.T()), diff --git a/blockbuster/lane_abci.go b/blockbuster/constructor/abci.go similarity index 90% rename from blockbuster/lane_abci.go rename to blockbuster/constructor/abci.go index 70dd0d0..de4fe72 100644 --- a/blockbuster/lane_abci.go +++ b/blockbuster/constructor/abci.go @@ -1,7 +1,8 @@ -package blockbuster +package constructor import ( sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/skip-mev/pob/blockbuster" "github.com/skip-mev/pob/blockbuster/utils" ) @@ -11,10 +12,10 @@ import ( // error. The proposal will only be modified if it passes all of the invarient checks. func (l *LaneConstructor[C]) PrepareLane( ctx sdk.Context, - proposal BlockProposal, + proposal blockbuster.BlockProposal, maxTxBytes int64, - next PrepareLanesHandler, -) (BlockProposal, error) { + next blockbuster.PrepareLanesHandler, +) (blockbuster.BlockProposal, error) { txs, txsToRemove, err := l.prepareLaneHandler(ctx, proposal, maxTxBytes) if err != nil { return proposal, err @@ -47,7 +48,7 @@ func (l *LaneConstructor[C]) CheckOrder(ctx sdk.Context, txs []sdk.Tx) error { // 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[C]) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next ProcessLanesHandler) (sdk.Context, error) { +func (l *LaneConstructor[C]) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next blockbuster.ProcessLanesHandler) (sdk.Context, error) { remainingTxs, err := l.processLaneHandler(ctx, txs) if err != nil { return ctx, err diff --git a/blockbuster/lane_handlers.go b/blockbuster/constructor/handlers.go similarity index 89% rename from blockbuster/lane_handlers.go rename to blockbuster/constructor/handlers.go index 627c198..d5a11af 100644 --- a/blockbuster/lane_handlers.go +++ b/blockbuster/constructor/handlers.go @@ -1,9 +1,10 @@ -package blockbuster +package constructor import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/skip-mev/pob/blockbuster" "github.com/skip-mev/pob/blockbuster/utils" ) @@ -11,8 +12,8 @@ import ( // 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[C]) DefaultPrepareLaneHandler() PrepareLaneHandler { - return func(ctx sdk.Context, proposal BlockProposal, maxTxBytes int64) ([][]byte, []sdk.Tx, error) { +func (l *LaneConstructor[C]) DefaultPrepareLaneHandler() blockbuster.PrepareLaneHandler { + return func(ctx sdk.Context, proposal blockbuster.BlockProposal, maxTxBytes int64) ([][]byte, []sdk.Tx, error) { var ( totalSize int64 txs [][]byte @@ -95,7 +96,7 @@ func (l *LaneConstructor[C]) DefaultPrepareLaneHandler() PrepareLaneHandler { // 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[C]) DefaultProcessLaneHandler() ProcessLaneHandler { +func (l *LaneConstructor[C]) DefaultProcessLaneHandler() blockbuster.ProcessLaneHandler { return func(ctx sdk.Context, txs []sdk.Tx) ([]sdk.Tx, error) { var err error @@ -122,7 +123,7 @@ func (l *LaneConstructor[C]) DefaultProcessLaneHandler() ProcessLaneHandler { // lane. // 2. Transactions that belong to other lanes cannot be interleaved with transactions that // belong to this lane. -func (l *LaneConstructor[C]) DefaultCheckOrderHandler() CheckOrderHandler { +func (l *LaneConstructor[C]) DefaultCheckOrderHandler() blockbuster.CheckOrderHandler { return func(ctx sdk.Context, txs []sdk.Tx) error { seenOtherLaneTx := false @@ -148,7 +149,7 @@ func (l *LaneConstructor[C]) DefaultCheckOrderHandler() CheckOrderHandler { // DefaultMatchHandler returns a default implementation of the MatchHandler. It matches all // transactions. -func DefaultMatchHandler() MatchHandler { +func DefaultMatchHandler() blockbuster.MatchHandler { return func(ctx sdk.Context, tx sdk.Tx) bool { return true } diff --git a/blockbuster/lane_constructor.go b/blockbuster/constructor/lane.go similarity index 91% rename from blockbuster/lane_constructor.go rename to blockbuster/constructor/lane.go index ccc56ae..f801d57 100644 --- a/blockbuster/lane_constructor.go +++ b/blockbuster/constructor/lane.go @@ -1,4 +1,4 @@ -package blockbuster +package constructor import ( "fmt" @@ -6,6 +6,7 @@ import ( "cosmossdk.io/log" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/skip-mev/pob/blockbuster" ) // LaneConstructor is a generic implementation of a lane. It is meant to be used @@ -17,42 +18,42 @@ type LaneConstructor[C comparable] 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 + cfg blockbuster.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 + blockbuster.LaneMempool // matchHandler is the function that determines whether or not a transaction // should be processed by this lane. - matchHandler MatchHandler + matchHandler blockbuster.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 + prepareLaneHandler blockbuster.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 + checkOrderHandler blockbuster.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 + processLaneHandler blockbuster.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[C comparable]( - cfg LaneConfig, + cfg blockbuster.LaneConfig, laneName string, - laneMempool LaneMempool, - matchHandlerFn MatchHandler, + laneMempool blockbuster.LaneMempool, + matchHandlerFn blockbuster.MatchHandler, ) *LaneConstructor[C] { lane := &LaneConstructor[C]{ cfg: cfg, @@ -105,7 +106,7 @@ func (l *LaneConstructor[C]) ValidateBasic() error { // 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[C]) SetPrepareLaneHandler(prepareLaneHandler PrepareLaneHandler) { +func (l *LaneConstructor[C]) SetPrepareLaneHandler(prepareLaneHandler blockbuster.PrepareLaneHandler) { if prepareLaneHandler == nil { panic("prepare lane handler cannot be nil") } @@ -117,7 +118,7 @@ func (l *LaneConstructor[C]) SetPrepareLaneHandler(prepareLaneHandler PrepareLan // 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[C]) SetProcessLaneHandler(processLaneHandler ProcessLaneHandler) { +func (l *LaneConstructor[C]) SetProcessLaneHandler(processLaneHandler blockbuster.ProcessLaneHandler) { if processLaneHandler == nil { panic("process lane handler cannot be nil") } @@ -129,7 +130,7 @@ func (l *LaneConstructor[C]) SetProcessLaneHandler(processLaneHandler ProcessLan // 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[C]) SetCheckOrderHandler(checkOrderHandler CheckOrderHandler) { +func (l *LaneConstructor[C]) SetCheckOrderHandler(checkOrderHandler blockbuster.CheckOrderHandler) { if checkOrderHandler == nil { panic("check order handler cannot be nil") } @@ -165,7 +166,7 @@ func (l *LaneConstructor[C]) Name() string { // 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[C]) SetIgnoreList(lanes []Lane) { +func (l *LaneConstructor[C]) SetIgnoreList(lanes []blockbuster.Lane) { l.cfg.IgnoreList = lanes } diff --git a/blockbuster/lane_mempool.go b/blockbuster/constructor/mempool.go similarity index 91% rename from blockbuster/lane_mempool.go rename to blockbuster/constructor/mempool.go index e102ee8..463d4a0 100644 --- a/blockbuster/lane_mempool.go +++ b/blockbuster/constructor/mempool.go @@ -1,4 +1,4 @@ -package blockbuster +package constructor import ( "context" @@ -7,6 +7,7 @@ import ( 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" ) @@ -24,7 +25,7 @@ type ( // 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] + txPriority blockbuster.TxPriority[C] // txEncoder defines the sdk.Tx encoder that allows us to encode transactions // to bytes. @@ -38,8 +39,8 @@ type ( // DefaultTxPriority returns a default implementation of the TxPriority. It prioritizes // transactions by their fee. -func DefaultTxPriority() TxPriority[string] { - return TxPriority[string]{ +func DefaultTxPriority() blockbuster.TxPriority[string] { + return blockbuster.TxPriority[string]{ GetTxPriority: func(goCtx context.Context, tx sdk.Tx) string { feeTx, ok := tx.(sdk.FeeTx) if !ok { @@ -80,10 +81,10 @@ func DefaultTxPriority() TxPriority[string] { } // NewConstructorMempool returns a new ConstructorMempool. -func NewConstructorMempool[C comparable](txPriority TxPriority[C], txEncoder sdk.TxEncoder, maxTx int) *ConstructorMempool[C] { +func NewConstructorMempool[C comparable](txPriority blockbuster.TxPriority[C], txEncoder sdk.TxEncoder, maxTx int) *ConstructorMempool[C] { return &ConstructorMempool[C]{ - index: NewPriorityMempool( - PriorityNonceMempoolConfig[C]{ + index: blockbuster.NewPriorityMempool( + blockbuster.PriorityNonceMempoolConfig[C]{ TxPriority: txPriority, MaxTx: maxTx, }, diff --git a/blockbuster/lane_interface.go b/blockbuster/lane.go similarity index 100% rename from blockbuster/lane_interface.go rename to blockbuster/lane.go diff --git a/blockbuster/mempool.go b/blockbuster/mempool.go index 938d2b6..37fe45b 100644 --- a/blockbuster/mempool.go +++ b/blockbuster/mempool.go @@ -11,10 +11,10 @@ import ( sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool" ) -var _ Mempool = (*BBMempool)(nil) +var _ Mempool = (*LanedMempool)(nil) type ( - // Mempool defines the Blockbuster mempool interface. + // LanedMempool defines the Block SDK mempool interface. Mempool interface { sdkmempool.Mempool @@ -26,14 +26,11 @@ type ( // GetTxDistribution returns the number of transactions in each lane. GetTxDistribution() map[string]int - - // GetLane returns the lane with the given name. - GetLane(name string) (Lane, error) } - // BBMempool defines the Blockbuster mempool implementation. It contains a registry + // LanedMempool defines the Block SDK mempool implementation. It contains a registry // of lanes, which allows for customizable block proposal construction. - BBMempool struct { + LanedMempool struct { logger log.Logger // registry contains the lanes in the mempool. The lanes are ordered @@ -43,7 +40,7 @@ type ( } ) -// NewMempool returns a new Blockbuster mempool. The blockbuster mempool is +// NewLanedMempool returns a new Blockbuster mempool. The blockbuster mempool is // comprised of a registry of lanes. Each lane is responsible for selecting // transactions according to its own selection logic. The lanes are ordered // according to their priority. The first lane in the registry has the highest @@ -54,8 +51,8 @@ type ( // 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{ +func NewLanedMempool(logger log.Logger, mutex bool, lanes ...Lane) *LanedMempool { + mempool := &LanedMempool{ logger: logger, registry: lanes, } @@ -79,7 +76,7 @@ func NewMempool(logger log.Logger, mutex bool, lanes ...Lane) *BBMempool { // CountTx returns the total number of transactions in the mempool. This will // be the sum of the number of transactions in each lane. -func (m *BBMempool) CountTx() int { +func (m *LanedMempool) CountTx() int { var total int for _, lane := range m.registry { total += lane.CountTx() @@ -89,7 +86,7 @@ func (m *BBMempool) CountTx() int { } // GetTxDistribution returns the number of transactions in each lane. -func (m *BBMempool) GetTxDistribution() map[string]int { +func (m *LanedMempool) GetTxDistribution() map[string]int { counts := make(map[string]int, len(m.registry)) for _, lane := range m.registry { @@ -101,7 +98,7 @@ 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) (err error) { +func (m *LanedMempool) Insert(ctx context.Context, tx sdk.Tx) (err error) { defer func() { if r := recover(); r != nil { m.logger.Error("panic in Insert", "err", r) @@ -136,12 +133,12 @@ func (m *BBMempool) Insert(ctx context.Context, tx sdk.Tx) (err error) { // - Determine if it even makes sense to return an iterator. What does that even // mean in the context where you have multiple lanes? // - Perhaps consider implementing and returning a no-op iterator? -func (m *BBMempool) Select(_ context.Context, _ [][]byte) sdkmempool.Iterator { +func (m *LanedMempool) Select(_ context.Context, _ [][]byte) sdkmempool.Iterator { return nil } // Remove removes a transaction from all of the lanes it is currently in. -func (m *BBMempool) Remove(tx sdk.Tx) (err error) { +func (m *LanedMempool) Remove(tx sdk.Tx) (err error) { defer func() { if r := recover(); r != nil { m.logger.Error("panic in Remove", "err", r) @@ -179,7 +176,7 @@ func (m *BBMempool) Remove(tx sdk.Tx) (err error) { } // Contains returns true if the transaction is contained in any of the lanes. -func (m *BBMempool) Contains(tx sdk.Tx) (contains bool) { +func (m *LanedMempool) Contains(tx sdk.Tx) (contains bool) { defer func() { if r := recover(); r != nil { m.logger.Error("panic in Contains", "err", r) @@ -197,7 +194,7 @@ func (m *BBMempool) Contains(tx sdk.Tx) (contains bool) { } // Registry returns the mempool's lane registry. -func (m *BBMempool) Registry() []Lane { +func (m *LanedMempool) Registry() []Lane { return m.registry } @@ -205,7 +202,7 @@ func (m *BBMempool) Registry() []Lane { // 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 { +func (m *LanedMempool) ValidateBasic() error { sum := math.LegacyZeroDec() seenZeroMaxBlockSpace := false @@ -230,14 +227,3 @@ func (m *BBMempool) ValidateBasic() error { return nil } - -// GetLane returns the lane with the given name. -func (m *BBMempool) GetLane(name string) (Lane, error) { - for _, lane := range m.registry { - if lane.Name() == name { - return lane, nil - } - } - - return nil, fmt.Errorf("lane %s not found", name) -} diff --git a/blockbuster/mempool_test.go b/blockbuster/mempool_test.go index 32ee173..2452300 100644 --- a/blockbuster/mempool_test.go +++ b/blockbuster/mempool_test.go @@ -11,9 +11,10 @@ import ( "github.com/cosmos/cosmos-sdk/testutil" 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/lanes/free" - "github.com/skip-mev/pob/blockbuster/lanes/mev" + "github.com/skip-mev/pob/blockbuster/constructor" + "github.com/skip-mev/pob/lanes/base" + "github.com/skip-mev/pob/lanes/free" + "github.com/skip-mev/pob/lanes/mev" testutils "github.com/skip-mev/pob/testutils" buildertypes "github.com/skip-mev/pob/x/builder/types" "github.com/stretchr/testify/suite" @@ -79,7 +80,7 @@ func (suite *BlockBusterTestSuite) SetupTest() { } suite.freeLane = free.NewFreeLane( freeConfig, - blockbuster.DefaultTxPriority(), + constructor.DefaultTxPriority(), free.DefaultMatchHandler(), ) @@ -97,7 +98,7 @@ func (suite *BlockBusterTestSuite) SetupTest() { // Mempool set up suite.lanes = []blockbuster.Lane{suite.mevLane, suite.freeLane, suite.baseLane} - suite.mempool = blockbuster.NewMempool(log.NewTestLogger(suite.T()), true, suite.lanes...) + suite.mempool = blockbuster.NewLanedMempool(log.NewTestLogger(suite.T()), true, suite.lanes...) // Accounts set up suite.accounts = testutils.RandomAccounts(suite.random, 10) diff --git a/blockbuster/lanes/base/abci_test.go b/lanes/base/abci_test.go similarity index 99% rename from blockbuster/lanes/base/abci_test.go rename to lanes/base/abci_test.go index d5c054d..b4b24e2 100644 --- a/blockbuster/lanes/base/abci_test.go +++ b/lanes/base/abci_test.go @@ -9,8 +9,8 @@ import ( "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" + "github.com/skip-mev/pob/lanes/base" testutils "github.com/skip-mev/pob/testutils" ) diff --git a/blockbuster/lanes/base/base_test.go b/lanes/base/base_test.go similarity index 100% rename from blockbuster/lanes/base/base_test.go rename to lanes/base/base_test.go diff --git a/blockbuster/lanes/base/lane.go b/lanes/base/lane.go similarity index 77% rename from blockbuster/lanes/base/lane.go rename to lanes/base/lane.go index 05b1640..c842cd2 100644 --- a/blockbuster/lanes/base/lane.go +++ b/lanes/base/lane.go @@ -2,6 +2,7 @@ package base import ( "github.com/skip-mev/pob/blockbuster" + "github.com/skip-mev/pob/blockbuster/constructor" ) const ( @@ -18,20 +19,20 @@ var _ blockbuster.Lane = (*DefaultLane)(nil) // CometBFT/Tendermint consensus engine builds and verifies blocks pre SDK version // 0.47.0. type DefaultLane struct { - *blockbuster.LaneConstructor[string] + *constructor.LaneConstructor[string] } // NewDefaultLane returns a new default lane. func NewDefaultLane(cfg blockbuster.LaneConfig) *DefaultLane { - lane := blockbuster.NewLaneConstructor[string]( + lane := constructor.NewLaneConstructor[string]( cfg, LaneName, - blockbuster.NewConstructorMempool[string]( - blockbuster.DefaultTxPriority(), + constructor.NewConstructorMempool[string]( + constructor.DefaultTxPriority(), cfg.TxEncoder, cfg.MaxTxs, ), - blockbuster.DefaultMatchHandler(), + constructor.DefaultMatchHandler(), ) return &DefaultLane{ diff --git a/blockbuster/lanes/base/mempool_test.go b/lanes/base/mempool_test.go similarity index 91% rename from blockbuster/lanes/base/mempool_test.go rename to lanes/base/mempool_test.go index 4f12e73..024ee37 100644 --- a/blockbuster/lanes/base/mempool_test.go +++ b/lanes/base/mempool_test.go @@ -3,12 +3,12 @@ package base_test import ( "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/skip-mev/pob/blockbuster" + "github.com/skip-mev/pob/blockbuster/constructor" testutils "github.com/skip-mev/pob/testutils" ) func (s *BaseTestSuite) TestGetTxPriority() { - txPriority := blockbuster.DefaultTxPriority() + txPriority := constructor.DefaultTxPriority() s.Run("should be able to get the priority off a normal transaction with fees", func() { tx, err := testutils.CreateRandomTx( @@ -56,7 +56,7 @@ func (s *BaseTestSuite) TestGetTxPriority() { } func (s *BaseTestSuite) TestCompareTxPriority() { - txPriority := blockbuster.DefaultTxPriority() + txPriority := constructor.DefaultTxPriority() s.Run("should return 0 when both priorities are nil", func() { a := sdk.NewCoin(s.gasTokenDenom, math.NewInt(0)).String() @@ -84,7 +84,7 @@ func (s *BaseTestSuite) TestCompareTxPriority() { } func (s *BaseTestSuite) TestInsert() { - mempool := blockbuster.NewConstructorMempool[string](blockbuster.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) + mempool := constructor.NewConstructorMempool[string](constructor.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) s.Run("should be able to insert a transaction", func() { tx, err := testutils.CreateRandomTx( @@ -136,7 +136,7 @@ func (s *BaseTestSuite) TestInsert() { } func (s *BaseTestSuite) TestRemove() { - mempool := blockbuster.NewConstructorMempool[string](blockbuster.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) + mempool := constructor.NewConstructorMempool[string](constructor.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) s.Run("should be able to remove a transaction", func() { tx, err := testutils.CreateRandomTx( @@ -174,7 +174,7 @@ func (s *BaseTestSuite) TestRemove() { 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) + mempool := constructor.NewConstructorMempool[string](constructor.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) tx1, err := testutils.CreateRandomTx( s.encodingConfig.TxConfig, @@ -213,7 +213,7 @@ func (s *BaseTestSuite) TestSelect() { }) s.Run("should be able to select a single transaction", func() { - mempool := blockbuster.NewConstructorMempool[string](blockbuster.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) + mempool := constructor.NewConstructorMempool[string](constructor.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) tx1, err := testutils.CreateRandomTx( s.encodingConfig.TxConfig, diff --git a/blockbuster/lanes/free/lane.go b/lanes/free/lane.go similarity index 88% rename from blockbuster/lanes/free/lane.go rename to lanes/free/lane.go index 312c0d7..ba4170b 100644 --- a/blockbuster/lanes/free/lane.go +++ b/lanes/free/lane.go @@ -4,6 +4,7 @@ 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/constructor" ) const ( @@ -16,7 +17,7 @@ var _ blockbuster.Lane = (*FreeLane)(nil) // FreeLane defines the lane that is responsible for processing free transactions. // By default, transactions that are staking related are considered free. type FreeLane struct { - *blockbuster.LaneConstructor[string] + *constructor.LaneConstructor[string] } // NewFreeLane returns a new free lane. @@ -25,10 +26,10 @@ func NewFreeLane( txPriority blockbuster.TxPriority[string], matchFn blockbuster.MatchHandler, ) *FreeLane { - lane := blockbuster.NewLaneConstructor[string]( + lane := constructor.NewLaneConstructor[string]( cfg, LaneName, - blockbuster.NewConstructorMempool[string]( + constructor.NewConstructorMempool[string]( txPriority, cfg.TxEncoder, cfg.MaxTxs, diff --git a/blockbuster/lanes/mev/abci.go b/lanes/mev/abci.go similarity index 100% rename from blockbuster/lanes/mev/abci.go rename to lanes/mev/abci.go diff --git a/blockbuster/lanes/mev/check_tx.go b/lanes/mev/check_tx.go similarity index 100% rename from blockbuster/lanes/mev/check_tx.go rename to lanes/mev/check_tx.go diff --git a/blockbuster/lanes/mev/factory.go b/lanes/mev/factory.go similarity index 100% rename from blockbuster/lanes/mev/factory.go rename to lanes/mev/factory.go diff --git a/blockbuster/lanes/mev/factory_test.go b/lanes/mev/factory_test.go similarity index 100% rename from blockbuster/lanes/mev/factory_test.go rename to lanes/mev/factory_test.go diff --git a/blockbuster/lanes/mev/lane.go b/lanes/mev/lane.go similarity index 90% rename from blockbuster/lanes/mev/lane.go rename to lanes/mev/lane.go index 6a8f335..59fd600 100644 --- a/blockbuster/lanes/mev/lane.go +++ b/lanes/mev/lane.go @@ -5,6 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/skip-mev/pob/blockbuster" + "github.com/skip-mev/pob/blockbuster/constructor" ) const ( @@ -33,7 +34,7 @@ type ( MEVLane struct { // LaneConfig defines the base lane configuration. - *blockbuster.LaneConstructor[string] + *constructor.LaneConstructor[string] // Factory defines the API/functionality which is responsible for determining // if a transaction is a bid transaction and how to extract relevant @@ -48,10 +49,10 @@ func NewMEVLane( factory Factory, ) *MEVLane { lane := &MEVLane{ - LaneConstructor: blockbuster.NewLaneConstructor[string]( + LaneConstructor: constructor.NewLaneConstructor[string]( cfg, LaneName, - blockbuster.NewConstructorMempool[string]( + constructor.NewConstructorMempool[string]( TxPriority(factory), cfg.TxEncoder, cfg.MaxTxs, diff --git a/blockbuster/lanes/mev/mempool.go b/lanes/mev/mempool.go similarity index 100% rename from blockbuster/lanes/mev/mempool.go rename to lanes/mev/mempool.go diff --git a/blockbuster/lanes/mev/mev_test.go b/lanes/mev/mev_test.go similarity index 95% rename from blockbuster/lanes/mev/mev_test.go rename to lanes/mev/mev_test.go index bd6530b..e1c9e65 100644 --- a/blockbuster/lanes/mev/mev_test.go +++ b/lanes/mev/mev_test.go @@ -8,7 +8,7 @@ import ( "cosmossdk.io/log" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/skip-mev/pob/blockbuster/lanes/mev" + "github.com/skip-mev/pob/lanes/mev" testutils "github.com/skip-mev/pob/testutils" "github.com/stretchr/testify/suite" ) diff --git a/blockbuster/lanes/mev/utils.go b/lanes/mev/utils.go similarity index 100% rename from blockbuster/lanes/mev/utils.go rename to lanes/mev/utils.go diff --git a/blockbuster/lanes/mev/utils_test.go b/lanes/mev/utils_test.go similarity index 96% rename from blockbuster/lanes/mev/utils_test.go rename to lanes/mev/utils_test.go index 020dc84..7ae1dae 100644 --- a/blockbuster/lanes/mev/utils_test.go +++ b/lanes/mev/utils_test.go @@ -4,7 +4,7 @@ import ( "testing" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/skip-mev/pob/blockbuster/lanes/mev" + "github.com/skip-mev/pob/lanes/mev" testutils "github.com/skip-mev/pob/testutils" buildertypes "github.com/skip-mev/pob/x/builder/types" "github.com/stretchr/testify/require" diff --git a/blockbuster/lanes/terminator/lane.go b/lanes/terminator/lane.go similarity index 100% rename from blockbuster/lanes/terminator/lane.go rename to lanes/terminator/lane.go diff --git a/tests/app/app.go b/tests/app/app.go index 7b9f0d7..e40694b 100644 --- a/tests/app/app.go +++ b/tests/app/app.go @@ -61,11 +61,12 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/skip-mev/pob/abci" "github.com/skip-mev/pob/blockbuster" - "github.com/skip-mev/pob/blockbuster/abci" - "github.com/skip-mev/pob/blockbuster/lanes/base" - "github.com/skip-mev/pob/blockbuster/lanes/free" - "github.com/skip-mev/pob/blockbuster/lanes/mev" + "github.com/skip-mev/pob/blockbuster/constructor" + "github.com/skip-mev/pob/lanes/base" + "github.com/skip-mev/pob/lanes/free" + "github.com/skip-mev/pob/lanes/mev" buildermodule "github.com/skip-mev/pob/x/builder" builderkeeper "github.com/skip-mev/pob/x/builder/keeper" ) @@ -284,7 +285,7 @@ func New( } freeLane := free.NewFreeLane( freeConfig, - blockbuster.DefaultTxPriority(), + constructor.DefaultTxPriority(), free.DefaultMatchHandler(), ) @@ -304,7 +305,7 @@ func New( freeLane, defaultLane, } - mempool := blockbuster.NewMempool(app.Logger(), true, lanes...) + mempool := blockbuster.NewLanedMempool(app.Logger(), true, lanes...) app.App.SetMempool(mempool) // Create a global ante handler that will be called on each transaction when diff --git a/x/builder/ante/ante_test.go b/x/builder/ante/ante_test.go index a8b567c..afb0fa4 100644 --- a/x/builder/ante/ante_test.go +++ b/x/builder/ante/ante_test.go @@ -12,8 +12,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/golang/mock/gomock" "github.com/skip-mev/pob/blockbuster" - "github.com/skip-mev/pob/blockbuster/lanes/base" - "github.com/skip-mev/pob/blockbuster/lanes/mev" + "github.com/skip-mev/pob/lanes/base" + "github.com/skip-mev/pob/lanes/mev" testutils "github.com/skip-mev/pob/testutils" "github.com/skip-mev/pob/x/builder/ante" "github.com/skip-mev/pob/x/builder/keeper" @@ -108,7 +108,7 @@ func (suite *AnteTestSuite) SetupTest() { // Mempool set up suite.lanes = []blockbuster.Lane{suite.mevLane, suite.baseLane} - suite.mempool = blockbuster.NewMempool(log.NewTestLogger(suite.T()), true, suite.lanes...) + suite.mempool = blockbuster.NewLanedMempool(log.NewTestLogger(suite.T()), true, suite.lanes...) } func (suite *AnteTestSuite) anteHandler(ctx sdk.Context, tx sdk.Tx, _ bool) (sdk.Context, error) { From 4cbf8d0f1ab68acc61e2025d83e3ed1adc400698 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 11:15:34 -0400 Subject: [PATCH 12/41] deprecating blockbuster --- README.md | 42 ++++++------ abci/abci.go | 18 +++--- abci/abci_test.go | 64 +++++++++---------- {blockbuster => block}/README.md | 14 ++-- {blockbuster => block}/constructor/abci.go | 12 ++-- .../constructor/handlers.go | 14 ++-- {blockbuster => block}/constructor/lane.go | 28 ++++---- {blockbuster => block}/constructor/mempool.go | 16 ++--- {blockbuster => block}/lane.go | 2 +- {blockbuster => block}/mempool.go | 4 +- {blockbuster => block}/mempool_test.go | 20 +++--- {blockbuster => block}/priority_nonce.go | 2 +- {blockbuster => block}/proposals.go | 4 +- {blockbuster => block}/types.go | 2 +- {blockbuster => block}/utils/ante.go | 0 {blockbuster => block}/utils/mocks/lane.go | 24 +++---- .../utils/mocks/lane_mempool.go | 0 {blockbuster => block}/utils/utils.go | 0 {blockbuster => block}/utils/utils_test.go | 2 +- lanes/base/abci_test.go | 28 ++++---- lanes/base/lane.go | 8 +-- lanes/base/mempool_test.go | 2 +- lanes/free/lane.go | 14 ++-- lanes/mev/abci.go | 12 ++-- lanes/mev/factory.go | 6 +- lanes/mev/lane.go | 8 +-- lanes/mev/mempool.go | 6 +- lanes/terminator/lane.go | 10 +-- tests/app/ante.go | 8 +-- tests/app/app.go | 14 ++-- x/builder/ante/ante_test.go | 16 ++--- 31 files changed, 200 insertions(+), 200 deletions(-) rename {blockbuster => block}/README.md (97%) rename {blockbuster => block}/constructor/abci.go (89%) rename {blockbuster => block}/constructor/handlers.go (89%) rename {blockbuster => block}/constructor/lane.go (91%) rename {blockbuster => block}/constructor/mempool.go (90%) rename {blockbuster => block}/lane.go (99%) rename {blockbuster => block}/mempool.go (98%) rename {blockbuster => block}/mempool_test.go (95%) rename {blockbuster => block}/priority_nonce.go (99%) rename {blockbuster => block}/proposals.go (98%) rename {blockbuster => block}/types.go (99%) rename {blockbuster => block}/utils/ante.go (100%) rename {blockbuster => block}/utils/mocks/lane.go (80%) rename {blockbuster => block}/utils/mocks/lane_mempool.go (100%) rename {blockbuster => block}/utils/utils.go (100%) rename {blockbuster => block}/utils/utils_test.go (96%) diff --git a/README.md b/README.md index ed67445..500b3a0 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Skip's POB provides developers with a set of a few core primitives: on-chain in a transparent, enforceable way. At its core, BlockBuster is an app-side mempool + set of proposal handlers (`PrepareProposal`/`ProcessProposal`) that allow developers to configure modular lanes of transactions in their blocks with distinct validation/ordering logic. For more - information, see the [BlockBuster README](/blockbuster/README.md). + information, see the [BlockBuster README](/block/README.md). * `x/builder`: This Cosmos SDK module gives applications the ability to process MEV bundled transactions in addition to having the ability to define how searchers and block proposers are rewarded. In addition, the module defines a `AuctionDecorator`, @@ -59,20 +59,20 @@ $ go install github.com/skip-mev/pob >* Builder module that pairs with the auction lane to process auction transactions and distribute revenue > to the auction house. > -> To build your own custom BlockBuster Lane, please see the [BlockBuster README](/blockbuster/README.md). +> To build your own custom BlockBuster Lane, please see the [BlockBuster README](/block/README.md). 1. Import the necessary dependencies into your application. This includes the - blockbuster proposal handlers +mempool, keeper, builder types, and builder module. This + block proposal handlers +mempool, keeper, builder types, and builder module. This tutorial will go into more detail into each of the dependencies. ```go import ( ... - "github.com/skip-mev/pob/blockbuster" - "github.com/skip-mev/pob/blockbuster/abci" - "github.com/skip-mev/pob/blockbuster/lanes/mev" - "github.com/skip-mev/pob/blockbuster/lanes/base" - "github.com/skip-mev/pob/blockbuster/lanes/free" + "github.com/skip-mev/pob/block" + "github.com/skip-mev/pob/block/abci" + "github.com/skip-mev/pob/block/lanes/mev" + "github.com/skip-mev/pob/block/lanes/base" + "github.com/skip-mev/pob/block/lanes/free" buildermodule "github.com/skip-mev/pob/x/builder" builderkeeper "github.com/skip-mev/pob/x/builder/keeper" ... @@ -129,18 +129,18 @@ $ go install github.com/skip-mev/pob } ``` - c. Instantiate the blockbuster mempool with the application's desired lanes. + c. Instantiate the block mempool with the application's desired lanes. ```go - // Set the blockbuster mempool into the app. + // Set the block mempool into the app. // Create the lanes. // // 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. // - // blockbuster.BaseLaneConfig is utilized for basic encoding/decoding of transactions. - tobConfig := blockbuster.BaseLaneConfig{ + // block.BaseLaneConfig is utilized for basic encoding/decoding of transactions. + tobConfig := block.BaseLaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), @@ -160,7 +160,7 @@ $ go install github.com/skip-mev/pob ) // Free lane allows transactions to be included in the next block for free. - freeConfig := blockbuster.BaseLaneConfig{ + freeConfig := block.BaseLaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), @@ -168,7 +168,7 @@ $ go install github.com/skip-mev/pob // IgnoreList is a list of lanes that if a transaction should be included in, it will be // ignored by the lane. For example, if a transaction should belong to the tob lane, it // will be ignored by the free lane. - IgnoreList: []blockbuster.Lane{ + IgnoreList: []block.Lane{ tobLane, }, } @@ -178,12 +178,12 @@ $ go install github.com/skip-mev/pob ) // Default lane accepts all other transactions. - defaultConfig := blockbuster.BaseLaneConfig{ + defaultConfig := block.BaseLaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), MaxBlockSpace: sdk.ZeroDec(), - IgnoreList: []blockbuster.Lane{ + IgnoreList: []block.Lane{ tobLane, freeLane, }, @@ -191,17 +191,17 @@ $ go install github.com/skip-mev/pob defaultLane := base.NewDefaultLane(defaultConfig) // Set the lanes into the mempool. - lanes := []blockbuster.Lane{ + lanes := []block.Lane{ tobLane, freeLane, defaultLane, } - mempool := blockbuster.NewMempool(lanes...) + mempool := block.NewMempool(lanes...) app.App.SetMempool(mempool) ``` d. Instantiate the antehandler chain for the application with awareness of the - blockbuster mempool. This will allow the application to verify the validity + block mempool. This will allow the application to verify the validity of a transaction respecting the desired logic of a given lane. In this walkthrough, we want the `FeeDecorator` to be ignored for all transactions that should belong to the free lane. Additionally, we want to add the `x/builder` module's `AuctionDecorator` to the @@ -211,8 +211,8 @@ $ go install github.com/skip-mev/pob ```go import ( ... - "github.com/skip-mev/pob/blockbuster" - "github.com/skip-mev/pob/blockbuster/utils" + "github.com/skip-mev/pob/block" + "github.com/skip-mev/pob/block/utils" builderante "github.com/skip-mev/pob/x/builder/ante" ... ) diff --git a/abci/abci.go b/abci/abci.go index e7c3777..2bb6b16 100644 --- a/abci/abci.go +++ b/abci/abci.go @@ -6,8 +6,8 @@ import ( "cosmossdk.io/log" abci "github.com/cometbft/cometbft/abci/types" 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/block" + "github.com/skip-mev/pob/block/utils" "github.com/skip-mev/pob/lanes/terminator" ) @@ -17,14 +17,14 @@ type ( ProposalHandler struct { logger log.Logger txDecoder sdk.TxDecoder - prepareLanesHandler blockbuster.PrepareLanesHandler - processLanesHandler blockbuster.ProcessLanesHandler + prepareLanesHandler block.PrepareLanesHandler + processLanesHandler block.ProcessLanesHandler } ) // 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 { +func NewProposalHandler(logger log.Logger, txDecoder sdk.TxDecoder, lanes []block.Lane) *ProposalHandler { return &ProposalHandler{ logger: logger, txDecoder: txDecoder, @@ -48,7 +48,7 @@ func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler { } }() - proposal, err := h.prepareLanesHandler(ctx, blockbuster.NewProposal(req.MaxTxBytes)) + proposal, err := h.prepareLanesHandler(ctx, block.NewProposal(req.MaxTxBytes)) if err != nil { h.logger.Error("failed to prepare proposal", "err", err) return &abci.ResponsePrepareProposal{Txs: make([][]byte, 0)}, err @@ -114,7 +114,7 @@ func (h *ProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHandler { // // In the case where any of the lanes fail to prepare the partial proposal, the lane that failed // will be skipped and the next lane in the chain will be called to prepare the proposal. -func ChainPrepareLanes(chain ...blockbuster.Lane) blockbuster.PrepareLanesHandler { +func ChainPrepareLanes(chain ...block.Lane) block.PrepareLanesHandler { if len(chain) == 0 { return nil } @@ -124,7 +124,7 @@ func ChainPrepareLanes(chain ...blockbuster.Lane) blockbuster.PrepareLanesHandle chain = append(chain, terminator.Terminator{}) } - return func(ctx sdk.Context, partialProposal blockbuster.BlockProposal) (finalProposal blockbuster.BlockProposal, err error) { + return func(ctx sdk.Context, partialProposal block.BlockProposal) (finalProposal block.BlockProposal, err error) { lane := chain[0] lane.Logger().Info("preparing lane", "lane", lane.Name()) @@ -182,7 +182,7 @@ func ChainPrepareLanes(chain ...blockbuster.Lane) blockbuster.PrepareLanesHandle // ChainProcessLanes chains together the proposal verification logic from each lane // into a single function. The first lane in the chain is the first lane to be verified and // the last lane in the chain is the last lane to be verified. -func ChainProcessLanes(chain ...blockbuster.Lane) blockbuster.ProcessLanesHandler { +func ChainProcessLanes(chain ...block.Lane) block.ProcessLanesHandler { if len(chain) == 0 { return nil } diff --git a/abci/abci_test.go b/abci/abci_test.go index 0f9d711..f7499c5 100644 --- a/abci/abci_test.go +++ b/abci/abci_test.go @@ -14,8 +14,8 @@ import ( "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/skip-mev/pob/abci" - "github.com/skip-mev/pob/blockbuster" - "github.com/skip-mev/pob/blockbuster/constructor" + "github.com/skip-mev/pob/block" + "github.com/skip-mev/pob/block/constructor" "github.com/skip-mev/pob/lanes/base" "github.com/skip-mev/pob/lanes/free" "github.com/skip-mev/pob/lanes/mev" @@ -57,7 +57,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { // Set up the default lane with no transactions defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("1"), nil) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{defaultLane}).PrepareProposalHandler() + proposalHandler := s.setUpProposalHandlers([]block.Lane{defaultLane}).PrepareProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) s.Require().NoError(err) @@ -81,7 +81,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx: true}) s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{defaultLane}).PrepareProposalHandler() + proposalHandler := s.setUpProposalHandlers([]block.Lane{defaultLane}).PrepareProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000}) s.Require().NotNil(resp) s.Require().NoError(err) @@ -119,7 +119,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx1)) s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx2)) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{defaultLane}).PrepareProposalHandler() + proposalHandler := s.setUpProposalHandlers([]block.Lane{defaultLane}).PrepareProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000}) s.Require().NotNil(resp) s.Require().NoError(err) @@ -157,7 +157,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx1)) s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx2)) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{defaultLane}).PrepareProposalHandler() + proposalHandler := s.setUpProposalHandlers([]block.Lane{defaultLane}).PrepareProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000}) s.Require().NotNil(resp) s.Require().NoError(err) @@ -171,7 +171,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), nil) defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), nil) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, defaultLane}).PrepareProposalHandler() + proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).PrepareProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) s.Require().NoError(err) @@ -201,7 +201,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), nil) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, defaultLane}).PrepareProposalHandler() + proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).PrepareProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000}) s.Require().NoError(err) @@ -239,7 +239,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) s.Require().NoError(defaultLane.Insert(sdk.Context{}, bundleTxs[0])) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, defaultLane}).PrepareProposalHandler() + proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).PrepareProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000}) s.Require().NoError(err) @@ -278,7 +278,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) s.Require().NoError(defaultLane.Insert(sdk.Context{}, bundleTxs[0])) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, defaultLane}).PrepareProposalHandler() + proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).PrepareProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000}) s.Require().NoError(err) @@ -317,7 +317,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) s.Require().NoError(defaultLane.Insert(sdk.Context{}, bundleTxs[0])) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, defaultLane}).PrepareProposalHandler() + proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).PrepareProposalHandler() proposal := s.getTxBytes(tx, bundleTxs[0]) size := int64(len(proposal[0]) - 1) @@ -353,7 +353,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { }) s.Require().NoError(freeLane.Insert(sdk.Context{}, freeTx)) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, freeLane, defaultLane}).PrepareProposalHandler() + proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, freeLane, defaultLane}).PrepareProposalHandler() proposal := s.getTxBytes(freeTx) @@ -419,7 +419,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { }) freeLane.Insert(sdk.Context{}, freeTx) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, freeLane, defaultLane}).PrepareProposalHandler() + proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, freeLane, defaultLane}).PrepareProposalHandler() proposal := s.getTxBytes(tx, bundleTxs[0], bundleTxs[1], bundleTxs[2], bundleTxs[3], freeTx, normalTx) resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 1000000000}) @@ -453,7 +453,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() { proposalHandler := abci.NewProposalHandler( log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxDecoder(), - []blockbuster.Lane{panicLane, defaultLane}, + []block.Lane{panicLane, defaultLane}, ).PrepareProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 1000000}) @@ -486,7 +486,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() { proposalHandler := abci.NewProposalHandler( log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxDecoder(), - []blockbuster.Lane{defaultLane, panicLane}, + []block.Lane{defaultLane, panicLane}, ).PrepareProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 1000000}) @@ -520,7 +520,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() { proposalHandler := abci.NewProposalHandler( log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxDecoder(), - []blockbuster.Lane{panicLane, panicLane2, defaultLane}, + []block.Lane{panicLane, panicLane2, defaultLane}, ).PrepareProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 1000000}) @@ -554,7 +554,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() { proposalHandler := abci.NewProposalHandler( log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxDecoder(), - []blockbuster.Lane{defaultLane, panicLane, panicLane2}, + []block.Lane{defaultLane, panicLane, panicLane2}, ).PrepareProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 1000000}) @@ -573,7 +573,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { freeLane := s.setUpFreeLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{}) defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{}) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, freeLane, defaultLane}).ProcessProposalHandler() + proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, freeLane, defaultLane}).ProcessProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: nil}) s.Require().NoError(err) @@ -586,7 +586,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { freeLane := s.setUpFreeLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{}) defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{}) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, freeLane, defaultLane}).ProcessProposalHandler() + proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, freeLane, defaultLane}).ProcessProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: [][]byte{{0x01, 0x02, 0x03}}}) s.Require().Error(err) @@ -606,7 +606,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { ) s.Require().NoError(err) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, panicLane}).ProcessProposalHandler() + proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, panicLane}).ProcessProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: [][]byte{txbz}}) s.Require().Error(err) s.Require().Equal(&cometabci.ResponseProcessProposal{Status: cometabci.ResponseProcessProposal_REJECT}, resp) @@ -639,7 +639,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx: true}) s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{defaultLane}).ProcessProposalHandler() + proposalHandler := s.setUpProposalHandlers([]block.Lane{defaultLane}).ProcessProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: s.getTxBytes(tx, tx2)}) s.Require().NotNil(resp) s.Require().Error(err) @@ -678,13 +678,13 @@ func (s *ProposalsTestSuite) TestProcessProposal() { // Set up the default lane defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), nil) - defaultLane.SetProcessLaneHandler(blockbuster.NoOpProcessLaneHandler()) + defaultLane.SetProcessLaneHandler(block.NoOpProcessLaneHandler()) // Set up the TOB lane mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), nil) - mevLane.SetProcessLaneHandler(blockbuster.NoOpProcessLaneHandler()) + mevLane.SetProcessLaneHandler(block.NoOpProcessLaneHandler()) - proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{mevLane, defaultLane}).ProcessProposalHandler() + proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).ProcessProposalHandler() resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: s.getTxBytes(bidTx, bundle[0], bundle[1], normalTx, normalTx2)}) s.Require().NotNil(resp) s.Require().Error(err) @@ -725,7 +725,7 @@ func (s *ProposalsTestSuite) setUpAnteHandler(expectedExecution map[sdk.Tx]bool) } func (s *ProposalsTestSuite) setUpDefaultLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *base.DefaultLane { - cfg := blockbuster.LaneConfig{ + cfg := block.LaneConfig{ Logger: log.NewTestLogger(s.T()), TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), TxDecoder: s.encodingConfig.TxConfig.TxDecoder(), @@ -737,7 +737,7 @@ func (s *ProposalsTestSuite) setUpDefaultLane(maxBlockSpace math.LegacyDec, expe } func (s *ProposalsTestSuite) setUpTOBLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *mev.MEVLane { - cfg := blockbuster.LaneConfig{ + cfg := block.LaneConfig{ Logger: log.NewTestLogger(s.T()), TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), TxDecoder: s.encodingConfig.TxConfig.TxDecoder(), @@ -749,7 +749,7 @@ func (s *ProposalsTestSuite) setUpTOBLane(maxBlockSpace math.LegacyDec, expected } func (s *ProposalsTestSuite) setUpFreeLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *free.FreeLane { - cfg := blockbuster.LaneConfig{ + cfg := block.LaneConfig{ Logger: log.NewTestLogger(s.T()), TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), TxDecoder: s.encodingConfig.TxConfig.TxDecoder(), @@ -761,7 +761,7 @@ func (s *ProposalsTestSuite) setUpFreeLane(maxBlockSpace math.LegacyDec, expecte } func (s *ProposalsTestSuite) setUpPanicLane(maxBlockSpace math.LegacyDec) *constructor.LaneConstructor[string] { - cfg := blockbuster.LaneConfig{ + cfg := block.LaneConfig{ Logger: log.NewTestLogger(s.T()), TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), TxDecoder: s.encodingConfig.TxConfig.TxDecoder(), @@ -775,14 +775,14 @@ func (s *ProposalsTestSuite) setUpPanicLane(maxBlockSpace math.LegacyDec) *const constructor.DefaultMatchHandler(), ) - lane.SetPrepareLaneHandler(blockbuster.PanicPrepareLaneHandler()) - lane.SetProcessLaneHandler(blockbuster.PanicProcessLaneHandler()) + lane.SetPrepareLaneHandler(block.PanicPrepareLaneHandler()) + lane.SetProcessLaneHandler(block.PanicProcessLaneHandler()) return lane } -func (s *ProposalsTestSuite) setUpProposalHandlers(lanes []blockbuster.Lane) *abci.ProposalHandler { - mempool := blockbuster.NewLanedMempool(log.NewTestLogger(s.T()), true, lanes...) +func (s *ProposalsTestSuite) setUpProposalHandlers(lanes []block.Lane) *abci.ProposalHandler { + mempool := block.NewLanedMempool(log.NewTestLogger(s.T()), true, lanes...) return abci.NewProposalHandler( log.NewTestLogger(s.T()), diff --git a/blockbuster/README.md b/block/README.md similarity index 97% rename from blockbuster/README.md rename to block/README.md index 8e01116..adee3e7 100644 --- a/blockbuster/README.md +++ b/block/README.md @@ -69,13 +69,13 @@ implementations of lanes: MEV lane, free lane, and a default lane. 2. Free lane allows base app to not charge certain types of transactions any fees. For example, delegations and/or re-delegations might be charged no fees. What qualifies as a free transaction is determined - [here](https://github.com/skip-mev/pob/blob/main/blockbuster/lanes/free/factory.go). + [here](https://github.com/skip-mev/pob/blob/main/block/lanes/free/factory.go). 3. Default lane accepts all other transactions and is considered to be analogous to how mempools and proposals are constructed today. * Instantiate the mempool in base app. ```go -mempool := blockbuster.NewMempool(lanes...) +mempool := block.NewMempool(lanes...) app.App.SetMempool(mempool) ``` @@ -158,7 +158,7 @@ given lane be ordered in a block / mempool. 2. Inclusion function to determine what types of transactions belong in the lane. 3. Unique block building/verification mechanism. -The general interface that each lane must implement can be found [here](https://github.com/skip-mev/pob/blob/main/blockbuster/lane.go): +The general interface that each lane must implement can be found [here](https://github.com/skip-mev/pob/blob/main/block/lane.go): ```go // Lane defines an interface used for block construction @@ -226,8 +226,8 @@ an custom `TxPriority` that orders transactions in the mempool based on their bid. ```go -func TxPriority(config Factory) blockbuster.TxPriority[string] { - return blockbuster.TxPriority[string]{ +func TxPriority(config Factory) block.TxPriority[string] { + return block.TxPriority[string]{ GetTxPriority: func(goCtx context.Context, tx sdk.Tx) string { bidInfo, err := config.GetAuctionBidInfo(tx) if err != nil { @@ -270,8 +270,8 @@ 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]{ + index: block.NewPriorityMempool( + block.PriorityNonceMempoolConfig[string]{ TxPriority: TxPriority(config), MaxTx: maxTx, }, diff --git a/blockbuster/constructor/abci.go b/block/constructor/abci.go similarity index 89% rename from blockbuster/constructor/abci.go rename to block/constructor/abci.go index de4fe72..db07807 100644 --- a/blockbuster/constructor/abci.go +++ b/block/constructor/abci.go @@ -2,8 +2,8 @@ package constructor 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/block" + "github.com/skip-mev/pob/block/utils" ) // PrepareLane will prepare a partial proposal for the lane. It will select transactions from the @@ -12,10 +12,10 @@ import ( // error. The proposal will only be modified if it passes all of the invarient checks. func (l *LaneConstructor[C]) PrepareLane( ctx sdk.Context, - proposal blockbuster.BlockProposal, + proposal block.BlockProposal, maxTxBytes int64, - next blockbuster.PrepareLanesHandler, -) (blockbuster.BlockProposal, error) { + next block.PrepareLanesHandler, +) (block.BlockProposal, error) { txs, txsToRemove, err := l.prepareLaneHandler(ctx, proposal, maxTxBytes) if err != nil { return proposal, err @@ -48,7 +48,7 @@ func (l *LaneConstructor[C]) CheckOrder(ctx sdk.Context, txs []sdk.Tx) error { // 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[C]) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next blockbuster.ProcessLanesHandler) (sdk.Context, error) { +func (l *LaneConstructor[C]) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next block.ProcessLanesHandler) (sdk.Context, error) { remainingTxs, err := l.processLaneHandler(ctx, txs) if err != nil { return ctx, err diff --git a/blockbuster/constructor/handlers.go b/block/constructor/handlers.go similarity index 89% rename from blockbuster/constructor/handlers.go rename to block/constructor/handlers.go index d5a11af..d07c61d 100644 --- a/blockbuster/constructor/handlers.go +++ b/block/constructor/handlers.go @@ -4,16 +4,16 @@ import ( "fmt" 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/block" + "github.com/skip-mev/pob/block/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[C]) DefaultPrepareLaneHandler() blockbuster.PrepareLaneHandler { - return func(ctx sdk.Context, proposal blockbuster.BlockProposal, maxTxBytes int64) ([][]byte, []sdk.Tx, error) { +func (l *LaneConstructor[C]) DefaultPrepareLaneHandler() block.PrepareLaneHandler { + return func(ctx sdk.Context, proposal block.BlockProposal, maxTxBytes int64) ([][]byte, []sdk.Tx, error) { var ( totalSize int64 txs [][]byte @@ -96,7 +96,7 @@ func (l *LaneConstructor[C]) DefaultPrepareLaneHandler() blockbuster.PrepareLane // 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[C]) DefaultProcessLaneHandler() blockbuster.ProcessLaneHandler { +func (l *LaneConstructor[C]) DefaultProcessLaneHandler() block.ProcessLaneHandler { return func(ctx sdk.Context, txs []sdk.Tx) ([]sdk.Tx, error) { var err error @@ -123,7 +123,7 @@ func (l *LaneConstructor[C]) DefaultProcessLaneHandler() blockbuster.ProcessLane // lane. // 2. Transactions that belong to other lanes cannot be interleaved with transactions that // belong to this lane. -func (l *LaneConstructor[C]) DefaultCheckOrderHandler() blockbuster.CheckOrderHandler { +func (l *LaneConstructor[C]) DefaultCheckOrderHandler() block.CheckOrderHandler { return func(ctx sdk.Context, txs []sdk.Tx) error { seenOtherLaneTx := false @@ -149,7 +149,7 @@ func (l *LaneConstructor[C]) DefaultCheckOrderHandler() blockbuster.CheckOrderHa // DefaultMatchHandler returns a default implementation of the MatchHandler. It matches all // transactions. -func DefaultMatchHandler() blockbuster.MatchHandler { +func DefaultMatchHandler() block.MatchHandler { return func(ctx sdk.Context, tx sdk.Tx) bool { return true } diff --git a/blockbuster/constructor/lane.go b/block/constructor/lane.go similarity index 91% rename from blockbuster/constructor/lane.go rename to block/constructor/lane.go index f801d57..e5d1d43 100644 --- a/blockbuster/constructor/lane.go +++ b/block/constructor/lane.go @@ -6,7 +6,7 @@ import ( "cosmossdk.io/log" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/skip-mev/pob/blockbuster" + "github.com/skip-mev/pob/block" ) // LaneConstructor is a generic implementation of a lane. It is meant to be used @@ -18,42 +18,42 @@ type LaneConstructor[C comparable] 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 blockbuster.LaneConfig + cfg block.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. - blockbuster.LaneMempool + block.LaneMempool // matchHandler is the function that determines whether or not a transaction // should be processed by this lane. - matchHandler blockbuster.MatchHandler + matchHandler block.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 blockbuster.PrepareLaneHandler + prepareLaneHandler block.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 blockbuster.CheckOrderHandler + checkOrderHandler block.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 blockbuster.ProcessLaneHandler + processLaneHandler block.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[C comparable]( - cfg blockbuster.LaneConfig, + cfg block.LaneConfig, laneName string, - laneMempool blockbuster.LaneMempool, - matchHandlerFn blockbuster.MatchHandler, + laneMempool block.LaneMempool, + matchHandlerFn block.MatchHandler, ) *LaneConstructor[C] { lane := &LaneConstructor[C]{ cfg: cfg, @@ -106,7 +106,7 @@ func (l *LaneConstructor[C]) ValidateBasic() error { // 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[C]) SetPrepareLaneHandler(prepareLaneHandler blockbuster.PrepareLaneHandler) { +func (l *LaneConstructor[C]) SetPrepareLaneHandler(prepareLaneHandler block.PrepareLaneHandler) { if prepareLaneHandler == nil { panic("prepare lane handler cannot be nil") } @@ -118,7 +118,7 @@ func (l *LaneConstructor[C]) SetPrepareLaneHandler(prepareLaneHandler blockbuste // 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[C]) SetProcessLaneHandler(processLaneHandler blockbuster.ProcessLaneHandler) { +func (l *LaneConstructor[C]) SetProcessLaneHandler(processLaneHandler block.ProcessLaneHandler) { if processLaneHandler == nil { panic("process lane handler cannot be nil") } @@ -130,7 +130,7 @@ func (l *LaneConstructor[C]) SetProcessLaneHandler(processLaneHandler blockbuste // 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[C]) SetCheckOrderHandler(checkOrderHandler blockbuster.CheckOrderHandler) { +func (l *LaneConstructor[C]) SetCheckOrderHandler(checkOrderHandler block.CheckOrderHandler) { if checkOrderHandler == nil { panic("check order handler cannot be nil") } @@ -166,7 +166,7 @@ func (l *LaneConstructor[C]) Name() string { // 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[C]) SetIgnoreList(lanes []blockbuster.Lane) { +func (l *LaneConstructor[C]) SetIgnoreList(lanes []block.Lane) { l.cfg.IgnoreList = lanes } diff --git a/blockbuster/constructor/mempool.go b/block/constructor/mempool.go similarity index 90% rename from blockbuster/constructor/mempool.go rename to block/constructor/mempool.go index 463d4a0..cf0f282 100644 --- a/blockbuster/constructor/mempool.go +++ b/block/constructor/mempool.go @@ -7,8 +7,8 @@ import ( 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" + "github.com/skip-mev/pob/block" + "github.com/skip-mev/pob/block/utils" ) type ( @@ -25,7 +25,7 @@ type ( // 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 blockbuster.TxPriority[C] + txPriority block.TxPriority[C] // txEncoder defines the sdk.Tx encoder that allows us to encode transactions // to bytes. @@ -39,8 +39,8 @@ type ( // DefaultTxPriority returns a default implementation of the TxPriority. It prioritizes // transactions by their fee. -func DefaultTxPriority() blockbuster.TxPriority[string] { - return blockbuster.TxPriority[string]{ +func DefaultTxPriority() block.TxPriority[string] { + return block.TxPriority[string]{ GetTxPriority: func(goCtx context.Context, tx sdk.Tx) string { feeTx, ok := tx.(sdk.FeeTx) if !ok { @@ -81,10 +81,10 @@ func DefaultTxPriority() blockbuster.TxPriority[string] { } // NewConstructorMempool returns a new ConstructorMempool. -func NewConstructorMempool[C comparable](txPriority blockbuster.TxPriority[C], txEncoder sdk.TxEncoder, maxTx int) *ConstructorMempool[C] { +func NewConstructorMempool[C comparable](txPriority block.TxPriority[C], txEncoder sdk.TxEncoder, maxTx int) *ConstructorMempool[C] { return &ConstructorMempool[C]{ - index: blockbuster.NewPriorityMempool( - blockbuster.PriorityNonceMempoolConfig[C]{ + index: block.NewPriorityMempool( + block.PriorityNonceMempoolConfig[C]{ TxPriority: txPriority, MaxTx: maxTx, }, diff --git a/blockbuster/lane.go b/block/lane.go similarity index 99% rename from blockbuster/lane.go rename to block/lane.go index 59ee35e..0731948 100644 --- a/blockbuster/lane.go +++ b/block/lane.go @@ -1,4 +1,4 @@ -package blockbuster +package block import ( "cosmossdk.io/log" diff --git a/blockbuster/mempool.go b/block/mempool.go similarity index 98% rename from blockbuster/mempool.go rename to block/mempool.go index 37fe45b..44bac25 100644 --- a/blockbuster/mempool.go +++ b/block/mempool.go @@ -1,4 +1,4 @@ -package blockbuster +package block import ( "context" @@ -40,7 +40,7 @@ type ( } ) -// NewLanedMempool returns a new Blockbuster mempool. The blockbuster mempool is +// NewLanedMempool returns a new Blockbuster mempool. The block mempool is // comprised of a registry of lanes. Each lane is responsible for selecting // transactions according to its own selection logic. The lanes are ordered // according to their priority. The first lane in the registry has the highest diff --git a/blockbuster/mempool_test.go b/block/mempool_test.go similarity index 95% rename from blockbuster/mempool_test.go rename to block/mempool_test.go index 2452300..6a04aba 100644 --- a/blockbuster/mempool_test.go +++ b/block/mempool_test.go @@ -1,4 +1,4 @@ -package blockbuster_test +package block_test import ( "math/rand" @@ -10,8 +10,8 @@ import ( storetypes "cosmossdk.io/store/types" "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/skip-mev/pob/blockbuster" - "github.com/skip-mev/pob/blockbuster/constructor" + "github.com/skip-mev/pob/block" + "github.com/skip-mev/pob/block/constructor" "github.com/skip-mev/pob/lanes/base" "github.com/skip-mev/pob/lanes/free" "github.com/skip-mev/pob/lanes/mev" @@ -33,8 +33,8 @@ type BlockBusterTestSuite struct { freeLane *free.FreeLane gasTokenDenom string - lanes []blockbuster.Lane - mempool blockbuster.Mempool + lanes []block.Lane + mempool block.Mempool // account set up accounts []testutils.Account @@ -58,7 +58,7 @@ func (suite *BlockBusterTestSuite) SetupTest() { // // TOB lane set up suite.gasTokenDenom = "stake" - mevConfig := blockbuster.LaneConfig{ + mevConfig := block.LaneConfig{ Logger: log.NewNopLogger(), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), @@ -71,7 +71,7 @@ func (suite *BlockBusterTestSuite) SetupTest() { ) // Free lane set up - freeConfig := blockbuster.LaneConfig{ + freeConfig := block.LaneConfig{ Logger: log.NewNopLogger(), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), @@ -85,7 +85,7 @@ func (suite *BlockBusterTestSuite) SetupTest() { ) // Base lane set up - baseConfig := blockbuster.LaneConfig{ + baseConfig := block.LaneConfig{ Logger: log.NewNopLogger(), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), @@ -97,8 +97,8 @@ func (suite *BlockBusterTestSuite) SetupTest() { ) // Mempool set up - suite.lanes = []blockbuster.Lane{suite.mevLane, suite.freeLane, suite.baseLane} - suite.mempool = blockbuster.NewLanedMempool(log.NewTestLogger(suite.T()), true, suite.lanes...) + suite.lanes = []block.Lane{suite.mevLane, suite.freeLane, suite.baseLane} + suite.mempool = block.NewLanedMempool(log.NewTestLogger(suite.T()), true, suite.lanes...) // Accounts set up suite.accounts = testutils.RandomAccounts(suite.random, 10) diff --git a/blockbuster/priority_nonce.go b/block/priority_nonce.go similarity index 99% rename from blockbuster/priority_nonce.go rename to block/priority_nonce.go index b561d50..352428f 100644 --- a/blockbuster/priority_nonce.go +++ b/block/priority_nonce.go @@ -1,4 +1,4 @@ -package blockbuster +package block import ( "context" diff --git a/blockbuster/proposals.go b/block/proposals.go similarity index 98% rename from blockbuster/proposals.go rename to block/proposals.go index 95da151..85aea87 100644 --- a/blockbuster/proposals.go +++ b/block/proposals.go @@ -1,4 +1,4 @@ -package blockbuster +package block import ( "crypto/sha256" @@ -7,7 +7,7 @@ import ( "cosmossdk.io/log" "cosmossdk.io/math" - "github.com/skip-mev/pob/blockbuster/utils" + "github.com/skip-mev/pob/block/utils" ) var _ BlockProposal = (*Proposal)(nil) diff --git a/blockbuster/types.go b/block/types.go similarity index 99% rename from blockbuster/types.go rename to block/types.go index d816863..cf94883 100644 --- a/blockbuster/types.go +++ b/block/types.go @@ -1,4 +1,4 @@ -package blockbuster +package block import ( "fmt" diff --git a/blockbuster/utils/ante.go b/block/utils/ante.go similarity index 100% rename from blockbuster/utils/ante.go rename to block/utils/ante.go diff --git a/blockbuster/utils/mocks/lane.go b/block/utils/mocks/lane.go similarity index 80% rename from blockbuster/utils/mocks/lane.go rename to block/utils/mocks/lane.go index 16faa82..3473e56 100644 --- a/blockbuster/utils/mocks/lane.go +++ b/block/utils/mocks/lane.go @@ -5,7 +5,7 @@ package mocks import ( context "context" - blockbuster "github.com/skip-mev/pob/blockbuster" + block "github.com/skip-mev/pob/block" log "cosmossdk.io/log" @@ -152,23 +152,23 @@ func (_m *Lane) Name() string { } // 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) { +func (_m *Lane) PrepareLane(ctx types.Context, proposal block.BlockProposal, maxTxBytes int64, next block.PrepareLanesHandler) (block.BlockProposal, error) { ret := _m.Called(ctx, proposal, maxTxBytes, next) - var r0 blockbuster.BlockProposal + var r0 block.BlockProposal var r1 error - if rf, ok := ret.Get(0).(func(types.Context, blockbuster.BlockProposal, int64, blockbuster.PrepareLanesHandler) (blockbuster.BlockProposal, error)); ok { + if rf, ok := ret.Get(0).(func(types.Context, block.BlockProposal, int64, block.PrepareLanesHandler) (block.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 { + if rf, ok := ret.Get(0).(func(types.Context, block.BlockProposal, int64, block.PrepareLanesHandler) block.BlockProposal); ok { r0 = rf(ctx, proposal, maxTxBytes, next) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(blockbuster.BlockProposal) + r0 = ret.Get(0).(block.BlockProposal) } } - if rf, ok := ret.Get(1).(func(types.Context, blockbuster.BlockProposal, int64, blockbuster.PrepareLanesHandler) error); ok { + if rf, ok := ret.Get(1).(func(types.Context, block.BlockProposal, int64, block.PrepareLanesHandler) error); ok { r1 = rf(ctx, proposal, maxTxBytes, next) } else { r1 = ret.Error(1) @@ -178,21 +178,21 @@ func (_m *Lane) PrepareLane(ctx types.Context, proposal blockbuster.BlockProposa } // 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) { +func (_m *Lane) ProcessLane(ctx types.Context, proposalTxs []types.Tx, next block.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 { + if rf, ok := ret.Get(0).(func(types.Context, []types.Tx, block.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 { + if rf, ok := ret.Get(0).(func(types.Context, []types.Tx, block.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 { + if rf, ok := ret.Get(1).(func(types.Context, []types.Tx, block.ProcessLanesHandler) error); ok { r1 = rf(ctx, proposalTxs, next) } else { r1 = ret.Error(1) @@ -237,7 +237,7 @@ func (_m *Lane) SetAnteHandler(antehander types.AnteHandler) { } // SetIgnoreList provides a mock function with given fields: ignoreList -func (_m *Lane) SetIgnoreList(ignoreList []blockbuster.Lane) { +func (_m *Lane) SetIgnoreList(ignoreList []block.Lane) { _m.Called(ignoreList) } diff --git a/blockbuster/utils/mocks/lane_mempool.go b/block/utils/mocks/lane_mempool.go similarity index 100% rename from blockbuster/utils/mocks/lane_mempool.go rename to block/utils/mocks/lane_mempool.go diff --git a/blockbuster/utils/utils.go b/block/utils/utils.go similarity index 100% rename from blockbuster/utils/utils.go rename to block/utils/utils.go diff --git a/blockbuster/utils/utils_test.go b/block/utils/utils_test.go similarity index 96% rename from blockbuster/utils/utils_test.go rename to block/utils/utils_test.go index e0b47e3..eb33706 100644 --- a/blockbuster/utils/utils_test.go +++ b/block/utils/utils_test.go @@ -4,7 +4,7 @@ import ( "testing" "cosmossdk.io/math" - "github.com/skip-mev/pob/blockbuster/utils" + "github.com/skip-mev/pob/block/utils" ) func TestGetMaxTxBytesForLane(t *testing.T) { diff --git a/lanes/base/abci_test.go b/lanes/base/abci_test.go index b4b24e2..c9b46ab 100644 --- a/lanes/base/abci_test.go +++ b/lanes/base/abci_test.go @@ -8,8 +8,8 @@ import ( "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/utils/mocks" + "github.com/skip-mev/pob/block" + "github.com/skip-mev/pob/block/utils/mocks" "github.com/skip-mev/pob/lanes/base" testutils "github.com/skip-mev/pob/testutils" ) @@ -41,7 +41,7 @@ func (s *BaseTestSuite) TestPrepareLane() { // Create a proposal maxTxBytes := int64(len(txBz) - 1) - proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler()) + proposal, err := lane.PrepareLane(sdk.Context{}, block.NewProposal(maxTxBytes), maxTxBytes, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) // Ensure the proposal is empty @@ -75,7 +75,7 @@ func (s *BaseTestSuite) TestPrepareLane() { // Create a proposal maxTxBytes := int64(len(txBz)) - proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler()) + proposal, err := lane.PrepareLane(sdk.Context{}, block.NewProposal(maxTxBytes), maxTxBytes, block.NoOpPrepareLanesHandler()) s.Require().Error(err) // Ensure the proposal is empty @@ -109,7 +109,7 @@ func (s *BaseTestSuite) TestPrepareLane() { // Create a proposal maxTxBytes := int64(len(txBz)) - proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler()) + proposal, err := lane.PrepareLane(sdk.Context{}, block.NewProposal(maxTxBytes), maxTxBytes, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) // Ensure the proposal is not empty and contains the transaction @@ -144,7 +144,7 @@ func (s *BaseTestSuite) TestPrepareLane() { s.Require().NoError(err) maxTxBytes := int64(len(txBz)) - proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler()) + proposal, err := lane.PrepareLane(sdk.Context{}, block.NewProposal(maxTxBytes), maxTxBytes, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) // Ensure the proposal is empty @@ -196,7 +196,7 @@ func (s *BaseTestSuite) TestPrepareLane() { s.Require().NoError(err) maxTxBytes := int64(len(txBz1)) + int64(len(txBz2)) - proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler()) + proposal, err := lane.PrepareLane(sdk.Context{}, block.NewProposal(maxTxBytes), maxTxBytes, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) // Ensure the proposal is ordered correctly @@ -245,7 +245,7 @@ func (s *BaseTestSuite) TestPrepareLane() { s.Require().NoError(err) maxTxBytes := int64(len(txBz1)) + int64(len(txBz2)) - proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler()) + proposal, err := lane.PrepareLane(sdk.Context{}, block.NewProposal(maxTxBytes), maxTxBytes, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) // Ensure the proposal is ordered correctly @@ -294,7 +294,7 @@ func (s *BaseTestSuite) TestPrepareLane() { s.Require().NoError(err) maxTxBytes := int64(len(txBz1)) + int64(len(txBz2)) - 1 - proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler()) + proposal, err := lane.PrepareLane(sdk.Context{}, block.NewProposal(maxTxBytes), maxTxBytes, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) // Ensure the proposal is ordered correctly @@ -323,7 +323,7 @@ func (s *BaseTestSuite) TestProcessLane() { tx1: true, }) - _, err = lane.ProcessLane(sdk.Context{}, proposal, blockbuster.NoOpProcessLanesHandler()) + _, err = lane.ProcessLane(sdk.Context{}, proposal, block.NoOpProcessLanesHandler()) s.Require().NoError(err) }) @@ -345,7 +345,7 @@ func (s *BaseTestSuite) TestProcessLane() { tx1: false, }) - _, err = lane.ProcessLane(sdk.Context{}, proposal, blockbuster.NoOpProcessLanesHandler()) + _, err = lane.ProcessLane(sdk.Context{}, proposal, block.NoOpProcessLanesHandler()) s.Require().Error(err) }) @@ -389,7 +389,7 @@ func (s *BaseTestSuite) TestProcessLane() { tx3: true, }) - _, err = lane.ProcessLane(sdk.Context{}, proposal, blockbuster.NoOpProcessLanesHandler()) + _, err = lane.ProcessLane(sdk.Context{}, proposal, block.NoOpProcessLanesHandler()) s.Require().Error(err) }) } @@ -487,7 +487,7 @@ func (s *BaseTestSuite) TestCheckOrder() { mocklane.On("Match", sdk.Context{}, tx2).Return(false) lane := s.initLane(math.LegacyMustNewDecFromStr("1"), nil) - lane.SetIgnoreList([]blockbuster.Lane{mocklane}) + lane.SetIgnoreList([]block.Lane{mocklane}) proposal := []sdk.Tx{ tx1, @@ -502,7 +502,7 @@ func (s *BaseTestSuite) initLane( maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool, ) *base.DefaultLane { - config := blockbuster.NewBaseLaneConfig( + config := block.NewBaseLaneConfig( log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), s.encodingConfig.TxConfig.TxDecoder(), diff --git a/lanes/base/lane.go b/lanes/base/lane.go index c842cd2..5afcce9 100644 --- a/lanes/base/lane.go +++ b/lanes/base/lane.go @@ -1,8 +1,8 @@ package base import ( - "github.com/skip-mev/pob/blockbuster" - "github.com/skip-mev/pob/blockbuster/constructor" + "github.com/skip-mev/pob/block" + "github.com/skip-mev/pob/block/constructor" ) const ( @@ -10,7 +10,7 @@ const ( LaneName = "default" ) -var _ blockbuster.Lane = (*DefaultLane)(nil) +var _ block.Lane = (*DefaultLane)(nil) // DefaultLane defines a default lane implementation. The default lane orders // transactions by the transaction fees. The default lane accepts any transaction @@ -23,7 +23,7 @@ type DefaultLane struct { } // NewDefaultLane returns a new default lane. -func NewDefaultLane(cfg blockbuster.LaneConfig) *DefaultLane { +func NewDefaultLane(cfg block.LaneConfig) *DefaultLane { lane := constructor.NewLaneConstructor[string]( cfg, LaneName, diff --git a/lanes/base/mempool_test.go b/lanes/base/mempool_test.go index 024ee37..1ca2b1e 100644 --- a/lanes/base/mempool_test.go +++ b/lanes/base/mempool_test.go @@ -3,7 +3,7 @@ package base_test import ( "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/skip-mev/pob/blockbuster/constructor" + "github.com/skip-mev/pob/block/constructor" testutils "github.com/skip-mev/pob/testutils" ) diff --git a/lanes/free/lane.go b/lanes/free/lane.go index ba4170b..2054462 100644 --- a/lanes/free/lane.go +++ b/lanes/free/lane.go @@ -3,8 +3,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/constructor" + "github.com/skip-mev/pob/block" + "github.com/skip-mev/pob/block/constructor" ) const ( @@ -12,7 +12,7 @@ const ( LaneName = "free" ) -var _ blockbuster.Lane = (*FreeLane)(nil) +var _ block.Lane = (*FreeLane)(nil) // FreeLane defines the lane that is responsible for processing free transactions. // By default, transactions that are staking related are considered free. @@ -22,9 +22,9 @@ type FreeLane struct { // NewFreeLane returns a new free lane. func NewFreeLane( - cfg blockbuster.LaneConfig, - txPriority blockbuster.TxPriority[string], - matchFn blockbuster.MatchHandler, + cfg block.LaneConfig, + txPriority block.TxPriority[string], + matchFn block.MatchHandler, ) *FreeLane { lane := constructor.NewLaneConstructor[string]( cfg, @@ -45,7 +45,7 @@ func NewFreeLane( // 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 { +func DefaultMatchHandler() block.MatchHandler { return func(ctx sdk.Context, tx sdk.Tx) bool { for _, msg := range tx.GetMsgs() { switch msg.(type) { diff --git a/lanes/mev/abci.go b/lanes/mev/abci.go index 412d97c..137da8c 100644 --- a/lanes/mev/abci.go +++ b/lanes/mev/abci.go @@ -5,8 +5,8 @@ import ( "fmt" 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/block" + "github.com/skip-mev/pob/block/utils" "github.com/skip-mev/pob/x/builder/types" ) @@ -14,8 +14,8 @@ import ( // and whose bundled transactions are valid and include them in the proposal. It // 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 *MEVLane) PrepareLaneHandler() blockbuster.PrepareLaneHandler { - return func(ctx sdk.Context, proposal blockbuster.BlockProposal, maxTxBytes int64) ([][]byte, []sdk.Tx, error) { +func (l *MEVLane) PrepareLaneHandler() block.PrepareLaneHandler { + return func(ctx sdk.Context, proposal block.BlockProposal, maxTxBytes int64) ([][]byte, []sdk.Tx, error) { // Define all of the info we need to select transactions for the partial proposal. var ( txs [][]byte @@ -147,7 +147,7 @@ func (l *MEVLane) PrepareLaneHandler() blockbuster.PrepareLaneHandler { // ProcessLaneHandler will ensure that block proposals that include transactions from // the mev lane are valid. -func (l *MEVLane) ProcessLaneHandler() blockbuster.ProcessLaneHandler { +func (l *MEVLane) ProcessLaneHandler() block.ProcessLaneHandler { return func(ctx sdk.Context, txs []sdk.Tx) ([]sdk.Tx, error) { if len(txs) == 0 { return txs, nil @@ -178,7 +178,7 @@ func (l *MEVLane) ProcessLaneHandler() blockbuster.ProcessLaneHandler { // - there are no other bid transactions in the proposal // - transactions from other lanes are not interleaved with transactions from the bid // transaction. -func (l *MEVLane) CheckOrderHandler() blockbuster.CheckOrderHandler { +func (l *MEVLane) CheckOrderHandler() block.CheckOrderHandler { return func(ctx sdk.Context, txs []sdk.Tx) error { if len(txs) == 0 { return nil diff --git a/lanes/mev/factory.go b/lanes/mev/factory.go index 7569340..d0bca0c 100644 --- a/lanes/mev/factory.go +++ b/lanes/mev/factory.go @@ -5,7 +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/block" "github.com/skip-mev/pob/x/builder/types" ) @@ -24,7 +24,7 @@ type ( GetAuctionBidInfo(tx sdk.Tx) (*types.BidInfo, error) // MatchHandler defines a function that checks if a transaction matches the auction lane. - MatchHandler() blockbuster.MatchHandler + MatchHandler() block.MatchHandler } // DefaultAuctionFactory defines a default implmentation for the auction factory interface for processing auction transactions. @@ -95,7 +95,7 @@ func (config *DefaultAuctionFactory) GetAuctionBidInfo(tx sdk.Tx) (*types.BidInf }, nil } -func (config *DefaultAuctionFactory) MatchHandler() blockbuster.MatchHandler { +func (config *DefaultAuctionFactory) MatchHandler() block.MatchHandler { return func(ctx sdk.Context, tx sdk.Tx) bool { bidInfo, err := config.GetAuctionBidInfo(tx) return bidInfo != nil && err == nil diff --git a/lanes/mev/lane.go b/lanes/mev/lane.go index 59fd600..455e865 100644 --- a/lanes/mev/lane.go +++ b/lanes/mev/lane.go @@ -4,8 +4,8 @@ import ( "context" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/skip-mev/pob/blockbuster" - "github.com/skip-mev/pob/blockbuster/constructor" + "github.com/skip-mev/pob/block" + "github.com/skip-mev/pob/block/constructor" ) const ( @@ -27,7 +27,7 @@ type ( // MEVLaneI defines the interface for the mev auction lane. This interface // is utilized by both the x/builder module and the checkTx handler. MEVLaneI interface { - blockbuster.Lane + block.Lane Factory GetTopAuctionTx(ctx context.Context) sdk.Tx } @@ -45,7 +45,7 @@ type ( // NewMEVLane returns a new TOB lane. func NewMEVLane( - cfg blockbuster.LaneConfig, + cfg block.LaneConfig, factory Factory, ) *MEVLane { lane := &MEVLane{ diff --git a/lanes/mev/mempool.go b/lanes/mev/mempool.go index 4e0caea..4f4d89c 100644 --- a/lanes/mev/mempool.go +++ b/lanes/mev/mempool.go @@ -4,13 +4,13 @@ import ( "context" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/skip-mev/pob/blockbuster" + "github.com/skip-mev/pob/block" ) // TxPriority returns a TxPriority over mev lane transactions only. It // is to be used in the mev index only. -func TxPriority(config Factory) blockbuster.TxPriority[string] { - return blockbuster.TxPriority[string]{ +func TxPriority(config Factory) block.TxPriority[string] { + return block.TxPriority[string]{ GetTxPriority: func(goCtx context.Context, tx sdk.Tx) string { bidInfo, err := config.GetAuctionBidInfo(tx) if err != nil { diff --git a/lanes/terminator/lane.go b/lanes/terminator/lane.go index 35e1453..3fce765 100644 --- a/lanes/terminator/lane.go +++ b/lanes/terminator/lane.go @@ -7,7 +7,7 @@ import ( "cosmossdk.io/math" 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/block" ) const ( @@ -36,10 +36,10 @@ const ( // snd \ \ \ / type Terminator struct{} -var _ blockbuster.Lane = (*Terminator)(nil) +var _ block.Lane = (*Terminator)(nil) // PrepareLane is a no-op -func (t Terminator) PrepareLane(_ sdk.Context, proposal blockbuster.BlockProposal, _ int64, _ blockbuster.PrepareLanesHandler) (blockbuster.BlockProposal, error) { +func (t Terminator) PrepareLane(_ sdk.Context, proposal block.BlockProposal, _ int64, _ block.PrepareLanesHandler) (block.BlockProposal, error) { return proposal, nil } @@ -49,7 +49,7 @@ func (t Terminator) CheckOrder(sdk.Context, []sdk.Tx) error { } // ProcessLane is a no-op -func (t Terminator) ProcessLane(ctx sdk.Context, _ []sdk.Tx, _ blockbuster.ProcessLanesHandler) (sdk.Context, error) { +func (t Terminator) ProcessLane(ctx sdk.Context, _ []sdk.Tx, _ block.ProcessLanesHandler) (sdk.Context, error) { return ctx, nil } @@ -72,7 +72,7 @@ func (t Terminator) Name() string { func (t Terminator) SetAnteHandler(sdk.AnteHandler) {} // SetIgnoreList is a no-op -func (t Terminator) SetIgnoreList([]blockbuster.Lane) {} +func (t Terminator) SetIgnoreList([]block.Lane) {} // Match is a no-op func (t Terminator) Match(sdk.Context, sdk.Tx) bool { diff --git a/tests/app/ante.go b/tests/app/ante.go index c10d80b..53d0e5c 100644 --- a/tests/app/ante.go +++ b/tests/app/ante.go @@ -3,20 +3,20 @@ package app import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/ante" - "github.com/skip-mev/pob/blockbuster" - "github.com/skip-mev/pob/blockbuster/utils" + "github.com/skip-mev/pob/block" + "github.com/skip-mev/pob/block/utils" builderante "github.com/skip-mev/pob/x/builder/ante" builderkeeper "github.com/skip-mev/pob/x/builder/keeper" ) type POBHandlerOptions struct { BaseOptions ante.HandlerOptions - Mempool blockbuster.Mempool + Mempool block.Mempool MEVLane builderante.MEVLane TxDecoder sdk.TxDecoder TxEncoder sdk.TxEncoder BuilderKeeper builderkeeper.Keeper - FreeLane blockbuster.Lane + FreeLane block.Lane } // NewPOBAnteHandler wraps all of the default Cosmos SDK AnteDecorators with the POB AnteHandler. diff --git a/tests/app/app.go b/tests/app/app.go index e40694b..65eafcd 100644 --- a/tests/app/app.go +++ b/tests/app/app.go @@ -62,8 +62,8 @@ import ( stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/skip-mev/pob/abci" - "github.com/skip-mev/pob/blockbuster" - "github.com/skip-mev/pob/blockbuster/constructor" + "github.com/skip-mev/pob/block" + "github.com/skip-mev/pob/block/constructor" "github.com/skip-mev/pob/lanes/base" "github.com/skip-mev/pob/lanes/free" "github.com/skip-mev/pob/lanes/mev" @@ -263,7 +263,7 @@ 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. - mevConfig := blockbuster.LaneConfig{ + mevConfig := block.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), @@ -276,7 +276,7 @@ func New( ) // Free lane allows transactions to be included in the next block for free. - freeConfig := blockbuster.LaneConfig{ + freeConfig := block.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), @@ -290,7 +290,7 @@ func New( ) // Default lane accepts all other transactions. - defaultConfig := blockbuster.LaneConfig{ + defaultConfig := block.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), @@ -300,12 +300,12 @@ func New( defaultLane := base.NewDefaultLane(defaultConfig) // Set the lanes into the mempool. - lanes := []blockbuster.Lane{ + lanes := []block.Lane{ mevLane, freeLane, defaultLane, } - mempool := blockbuster.NewLanedMempool(app.Logger(), true, lanes...) + mempool := block.NewLanedMempool(app.Logger(), true, lanes...) app.App.SetMempool(mempool) // Create a global ante handler that will be called on each transaction when diff --git a/x/builder/ante/ante_test.go b/x/builder/ante/ante_test.go index afb0fa4..f2d8563 100644 --- a/x/builder/ante/ante_test.go +++ b/x/builder/ante/ante_test.go @@ -11,7 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/golang/mock/gomock" - "github.com/skip-mev/pob/blockbuster" + "github.com/skip-mev/pob/block" "github.com/skip-mev/pob/lanes/base" "github.com/skip-mev/pob/lanes/mev" testutils "github.com/skip-mev/pob/testutils" @@ -39,10 +39,10 @@ type AnteTestSuite struct { authorityAccount sdk.AccAddress // mempool and lane set up - mempool blockbuster.Mempool + mempool block.Mempool mevLane *mev.MEVLane baseLane *base.DefaultLane - lanes []blockbuster.Lane + lanes []block.Lane // Account set up balance sdk.Coin @@ -83,7 +83,7 @@ func (suite *AnteTestSuite) SetupTest() { // Lanes configuration // // TOB lane set up - mevConfig := blockbuster.LaneConfig{ + mevConfig := block.LaneConfig{ Logger: suite.ctx.Logger(), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), @@ -96,19 +96,19 @@ func (suite *AnteTestSuite) SetupTest() { ) // Base lane set up - baseConfig := blockbuster.LaneConfig{ + baseConfig := block.LaneConfig{ Logger: suite.ctx.Logger(), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), AnteHandler: suite.anteHandler, MaxBlockSpace: math.LegacyZeroDec(), - IgnoreList: []blockbuster.Lane{suite.mevLane}, + IgnoreList: []block.Lane{suite.mevLane}, } suite.baseLane = base.NewDefaultLane(baseConfig) // Mempool set up - suite.lanes = []blockbuster.Lane{suite.mevLane, suite.baseLane} - suite.mempool = blockbuster.NewLanedMempool(log.NewTestLogger(suite.T()), true, suite.lanes...) + suite.lanes = []block.Lane{suite.mevLane, suite.baseLane} + suite.mempool = block.NewLanedMempool(log.NewTestLogger(suite.T()), true, suite.lanes...) } func (suite *AnteTestSuite) anteHandler(ctx sdk.Context, tx sdk.Tx, _ bool) (sdk.Context, error) { From 80ac448d4043d8fbd8466b979f8cc11032a64df7 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 11:24:18 -0400 Subject: [PATCH 13/41] done wit bb --- block/constructor/mempool.go | 13 ++++++------- block/{ => constructor}/priority_nonce.go | 14 +++++++++++++- lanes/free/lane.go | 2 +- lanes/mev/mempool.go | 6 +++--- 4 files changed, 23 insertions(+), 12 deletions(-) rename block/{ => constructor}/priority_nonce.go (94%) diff --git a/block/constructor/mempool.go b/block/constructor/mempool.go index cf0f282..7a9d6e4 100644 --- a/block/constructor/mempool.go +++ b/block/constructor/mempool.go @@ -7,7 +7,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool" - "github.com/skip-mev/pob/block" "github.com/skip-mev/pob/block/utils" ) @@ -25,7 +24,7 @@ type ( // 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 block.TxPriority[C] + txPriority TxPriority[C] // txEncoder defines the sdk.Tx encoder that allows us to encode transactions // to bytes. @@ -39,8 +38,8 @@ type ( // DefaultTxPriority returns a default implementation of the TxPriority. It prioritizes // transactions by their fee. -func DefaultTxPriority() block.TxPriority[string] { - return block.TxPriority[string]{ +func DefaultTxPriority() TxPriority[string] { + return TxPriority[string]{ GetTxPriority: func(goCtx context.Context, tx sdk.Tx) string { feeTx, ok := tx.(sdk.FeeTx) if !ok { @@ -81,10 +80,10 @@ func DefaultTxPriority() block.TxPriority[string] { } // NewConstructorMempool returns a new ConstructorMempool. -func NewConstructorMempool[C comparable](txPriority block.TxPriority[C], txEncoder sdk.TxEncoder, maxTx int) *ConstructorMempool[C] { +func NewConstructorMempool[C comparable](txPriority TxPriority[C], txEncoder sdk.TxEncoder, maxTx int) *ConstructorMempool[C] { return &ConstructorMempool[C]{ - index: block.NewPriorityMempool( - block.PriorityNonceMempoolConfig[C]{ + index: NewPriorityMempool( + PriorityNonceMempoolConfig[C]{ TxPriority: txPriority, MaxTx: maxTx, }, diff --git a/block/priority_nonce.go b/block/constructor/priority_nonce.go similarity index 94% rename from block/priority_nonce.go rename to block/constructor/priority_nonce.go index 352428f..c5ee7bd 100644 --- a/block/priority_nonce.go +++ b/block/constructor/priority_nonce.go @@ -1,4 +1,16 @@ -package block +package constructor + +// ------------------------------------------------------------------------------ // +// ------------------------------------------------------------------------------ // +// ------------------------------------------------------------------------------ // +// ------------------------------------------------------------------------------ // +// NOTE: THIS IS A COPY OF THE PRIORITY NONCE MEMPOOL FROM COSMOS-SDK. IT HAS BEEN +// MODIFIED FOR OUR USE CASE. THIS CODE WILL BE DEPRECATED ONCE THE COSMOS-SDK +// CUTS A FINAL v0.50.0 RELEASE. +// ------------------------------------------------------------------------------ // +// ------------------------------------------------------------------------------ // +// ------------------------------------------------------------------------------ // +// ------------------------------------------------------------------------------ // import ( "context" diff --git a/lanes/free/lane.go b/lanes/free/lane.go index 2054462..1608524 100644 --- a/lanes/free/lane.go +++ b/lanes/free/lane.go @@ -23,7 +23,7 @@ type FreeLane struct { // NewFreeLane returns a new free lane. func NewFreeLane( cfg block.LaneConfig, - txPriority block.TxPriority[string], + txPriority constructor.TxPriority[string], matchFn block.MatchHandler, ) *FreeLane { lane := constructor.NewLaneConstructor[string]( diff --git a/lanes/mev/mempool.go b/lanes/mev/mempool.go index 4f4d89c..d130770 100644 --- a/lanes/mev/mempool.go +++ b/lanes/mev/mempool.go @@ -4,13 +4,13 @@ import ( "context" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/skip-mev/pob/block" + "github.com/skip-mev/pob/block/constructor" ) // TxPriority returns a TxPriority over mev lane transactions only. It // is to be used in the mev index only. -func TxPriority(config Factory) block.TxPriority[string] { - return block.TxPriority[string]{ +func TxPriority(config Factory) constructor.TxPriority[string] { + return constructor.TxPriority[string]{ GetTxPriority: func(goCtx context.Context, tx sdk.Tx) string { bidInfo, err := config.GetAuctionBidInfo(tx) if err != nil { From af85b86241706423bb3672daf149af8b4cda9cb1 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 11:28:02 -0400 Subject: [PATCH 14/41] renaming mempool --- abci/abci_test.go | 2 +- block/constructor/mempool.go | 20 ++++++++++---------- lanes/base/lane.go | 2 +- lanes/base/mempool_test.go | 8 ++++---- lanes/free/lane.go | 2 +- lanes/mev/lane.go | 2 +- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/abci/abci_test.go b/abci/abci_test.go index f7499c5..0131a63 100644 --- a/abci/abci_test.go +++ b/abci/abci_test.go @@ -771,7 +771,7 @@ func (s *ProposalsTestSuite) setUpPanicLane(maxBlockSpace math.LegacyDec) *const lane := constructor.NewLaneConstructor[string]( cfg, "panic", - constructor.NewConstructorMempool[string](constructor.DefaultTxPriority(), cfg.TxEncoder, 0), + constructor.NewMempool[string](constructor.DefaultTxPriority(), cfg.TxEncoder, 0), constructor.DefaultMatchHandler(), ) diff --git a/block/constructor/mempool.go b/block/constructor/mempool.go index 7a9d6e4..8bcb098 100644 --- a/block/constructor/mempool.go +++ b/block/constructor/mempool.go @@ -16,7 +16,7 @@ type ( // 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 { + Mempool[C comparable] struct { // index defines an index of transactions. index sdkmempool.Mempool @@ -79,9 +79,9 @@ func DefaultTxPriority() TxPriority[string] { } } -// NewConstructorMempool returns a new ConstructorMempool. -func NewConstructorMempool[C comparable](txPriority TxPriority[C], txEncoder sdk.TxEncoder, maxTx int) *ConstructorMempool[C] { - return &ConstructorMempool[C]{ +// NewMempool returns a new ConstructorMempool. +func NewMempool[C comparable](txPriority TxPriority[C], txEncoder sdk.TxEncoder, maxTx int) *Mempool[C] { + return &Mempool[C]{ index: NewPriorityMempool( PriorityNonceMempoolConfig[C]{ TxPriority: txPriority, @@ -95,7 +95,7 @@ func NewConstructorMempool[C comparable](txPriority TxPriority[C], txEncoder sdk } // Insert inserts a transaction into the mempool. -func (cm *ConstructorMempool[C]) Insert(ctx context.Context, tx sdk.Tx) error { +func (cm *Mempool[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) } @@ -112,7 +112,7 @@ func (cm *ConstructorMempool[C]) Insert(ctx context.Context, tx sdk.Tx) error { } // Remove removes a transaction from the mempool. -func (cm *ConstructorMempool[C]) Remove(tx sdk.Tx) error { +func (cm *Mempool[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) } @@ -131,17 +131,17 @@ func (cm *ConstructorMempool[C]) Remove(tx sdk.Tx) error { // 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 { +func (cm *Mempool[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 { +func (cm *Mempool[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 { +func (cm *Mempool[C]) Contains(tx sdk.Tx) bool { _, txHashStr, err := utils.GetTxHashStr(cm.txEncoder, tx) if err != nil { return false @@ -152,7 +152,7 @@ func (cm *ConstructorMempool[C]) Contains(tx sdk.Tx) bool { } // 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 { +func (cm *Mempool[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) diff --git a/lanes/base/lane.go b/lanes/base/lane.go index 5afcce9..0819506 100644 --- a/lanes/base/lane.go +++ b/lanes/base/lane.go @@ -27,7 +27,7 @@ func NewDefaultLane(cfg block.LaneConfig) *DefaultLane { lane := constructor.NewLaneConstructor[string]( cfg, LaneName, - constructor.NewConstructorMempool[string]( + constructor.NewMempool[string]( constructor.DefaultTxPriority(), cfg.TxEncoder, cfg.MaxTxs, diff --git a/lanes/base/mempool_test.go b/lanes/base/mempool_test.go index 1ca2b1e..fef9ee1 100644 --- a/lanes/base/mempool_test.go +++ b/lanes/base/mempool_test.go @@ -84,7 +84,7 @@ func (s *BaseTestSuite) TestCompareTxPriority() { } func (s *BaseTestSuite) TestInsert() { - mempool := constructor.NewConstructorMempool[string](constructor.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) + mempool := constructor.NewMempool[string](constructor.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) s.Run("should be able to insert a transaction", func() { tx, err := testutils.CreateRandomTx( @@ -136,7 +136,7 @@ func (s *BaseTestSuite) TestInsert() { } func (s *BaseTestSuite) TestRemove() { - mempool := constructor.NewConstructorMempool[string](constructor.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) + mempool := constructor.NewMempool[string](constructor.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) s.Run("should be able to remove a transaction", func() { tx, err := testutils.CreateRandomTx( @@ -174,7 +174,7 @@ func (s *BaseTestSuite) TestRemove() { func (s *BaseTestSuite) TestSelect() { s.Run("should be able to select transactions in the correct order", func() { - mempool := constructor.NewConstructorMempool[string](constructor.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) + mempool := constructor.NewMempool[string](constructor.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) tx1, err := testutils.CreateRandomTx( s.encodingConfig.TxConfig, @@ -213,7 +213,7 @@ func (s *BaseTestSuite) TestSelect() { }) s.Run("should be able to select a single transaction", func() { - mempool := constructor.NewConstructorMempool[string](constructor.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) + mempool := constructor.NewMempool[string](constructor.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) tx1, err := testutils.CreateRandomTx( s.encodingConfig.TxConfig, diff --git a/lanes/free/lane.go b/lanes/free/lane.go index 1608524..e08f3d3 100644 --- a/lanes/free/lane.go +++ b/lanes/free/lane.go @@ -29,7 +29,7 @@ func NewFreeLane( lane := constructor.NewLaneConstructor[string]( cfg, LaneName, - constructor.NewConstructorMempool[string]( + constructor.NewMempool[string]( txPriority, cfg.TxEncoder, cfg.MaxTxs, diff --git a/lanes/mev/lane.go b/lanes/mev/lane.go index 455e865..54e8779 100644 --- a/lanes/mev/lane.go +++ b/lanes/mev/lane.go @@ -52,7 +52,7 @@ func NewMEVLane( LaneConstructor: constructor.NewLaneConstructor[string]( cfg, LaneName, - constructor.NewConstructorMempool[string]( + constructor.NewMempool[string]( TxPriority(factory), cfg.TxEncoder, cfg.MaxTxs, From dbf4b6720c71698104ff143ec5cde25b19641703 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 12:39:10 -0400 Subject: [PATCH 15/41] starting to add docs --- abci/README.md | 0 abci/abci_test.go | 8 +- block/constructor/README.md | 199 ++++++++++++++++++++++++++++++++++++ block/constructor/config.go | 79 ++++++++++++++ block/constructor/lane.go | 4 +- block/mempool_test.go | 6 +- block/types.go | 73 ------------- lanes/README.md | 0 lanes/base/README.md | 0 lanes/base/abci_test.go | 3 +- lanes/base/lane.go | 2 +- lanes/free/README.md | 0 lanes/free/lane.go | 2 +- lanes/mev/README.md | 0 lanes/mev/lane.go | 2 +- tests/app/app.go | 6 +- x/builder/ante/ante_test.go | 5 +- 17 files changed, 298 insertions(+), 91 deletions(-) create mode 100644 abci/README.md create mode 100644 block/constructor/README.md create mode 100644 block/constructor/config.go create mode 100644 lanes/README.md create mode 100644 lanes/base/README.md create mode 100644 lanes/free/README.md create mode 100644 lanes/mev/README.md diff --git a/abci/README.md b/abci/README.md new file mode 100644 index 0000000..e69de29 diff --git a/abci/abci_test.go b/abci/abci_test.go index 0131a63..23d8a73 100644 --- a/abci/abci_test.go +++ b/abci/abci_test.go @@ -725,7 +725,7 @@ func (s *ProposalsTestSuite) setUpAnteHandler(expectedExecution map[sdk.Tx]bool) } func (s *ProposalsTestSuite) setUpDefaultLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *base.DefaultLane { - cfg := block.LaneConfig{ + cfg := constructor.LaneConfig{ Logger: log.NewTestLogger(s.T()), TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), TxDecoder: s.encodingConfig.TxConfig.TxDecoder(), @@ -737,7 +737,7 @@ func (s *ProposalsTestSuite) setUpDefaultLane(maxBlockSpace math.LegacyDec, expe } func (s *ProposalsTestSuite) setUpTOBLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *mev.MEVLane { - cfg := block.LaneConfig{ + cfg := constructor.LaneConfig{ Logger: log.NewTestLogger(s.T()), TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), TxDecoder: s.encodingConfig.TxConfig.TxDecoder(), @@ -749,7 +749,7 @@ func (s *ProposalsTestSuite) setUpTOBLane(maxBlockSpace math.LegacyDec, expected } func (s *ProposalsTestSuite) setUpFreeLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *free.FreeLane { - cfg := block.LaneConfig{ + cfg := constructor.LaneConfig{ Logger: log.NewTestLogger(s.T()), TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), TxDecoder: s.encodingConfig.TxConfig.TxDecoder(), @@ -761,7 +761,7 @@ func (s *ProposalsTestSuite) setUpFreeLane(maxBlockSpace math.LegacyDec, expecte } func (s *ProposalsTestSuite) setUpPanicLane(maxBlockSpace math.LegacyDec) *constructor.LaneConstructor[string] { - cfg := block.LaneConfig{ + cfg := constructor.LaneConfig{ Logger: log.NewTestLogger(s.T()), TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), TxDecoder: s.encodingConfig.TxConfig.TxDecoder(), diff --git a/block/constructor/README.md b/block/constructor/README.md new file mode 100644 index 0000000..4527119 --- /dev/null +++ b/block/constructor/README.md @@ -0,0 +1,199 @@ +# 🎨 Lane Constructor + +> 🏗️ Build your own lane in less than 30 minutes using the Lane Constructor + +## 💡 Overview + +The Lane Constructor is a generic implementation of a lane. It comes out of the +box with default implementations for all the required interfaces. It is meant to +be used as a starting point for building your own lane. + +## 🤔 How does it work + +### Transaction Lifecycle + +The best way to understand how lanes work is to first understand the lifecycle +of a transaction. When a transaction is submitted to the chain, it will be checked +in `CheckTx` by the base application. If the transaction is valid, it will be +inserted into the applications mempool. The transaction then waits in the mempool +until a new block needs to be proposed. When a new block needs to be proposed, +the application will call `PrepareProposal` (which is a new ABCI++ addition) to +request a new block from the current proposer. The proposer will look at what the +transactions currently waiting to be included in a block in their mempool and +will iterative select transactions until the block is full. The proposer will then +send the block to other validators in the network. When a validator receives a +proposed block, the validator will first want to verify the contents of the block +before signing off on it. The validator will call `ProcessProposal` to verify the +contents of the block. If the block is valid, the validator will sign off on the +block and broadcast their vote to the network. If the block is invalid, the validator +will reject the block. Once a block is accepted by the network, it is committed +and the transactions that were included in the block are removed from the mempool. + +### Lane Lifecycle + +The Lane Constructor implements the `Lane` interface. After transactions are +check in `CheckTx`, they will be added to this lane's mempool (data structure +responsible for storing transactions). When a new block is proposed, `PrepareLane` +will be called by the `PrepareProposalHandler` defined in `abci/abci.go`. This +will trigger the lane to reap transactions from its mempool and add them to the +proposal. By default, transactions are added to proposals in the order that they +are reaped from the mempool. Transactions will only be added to a proposal +if they are valid according to the lane's verification logic. The default implementation +determines whether a transaction is valid by running the transaction through the +lane's `AnteHandler`. If any transactions are invalid, they will be removed from +lane's mempool from further consideration. + +When proposals need to be verified in `ProcessProposal`, the `ProcessProposalHandler` +defined in `abci/abci.go` will call `ProcessLane` on each lane. This will trigger +the lane to process all transactions that are included in the proposal. Lane's +should only verify transactions that belong to their lane. The default implementation +of `ProcessLane` will first check that transactions that should belong to the +current lane are ordered correctly in the proposal. If they are not, the proposal +will be rejected. If they are, the lane will run the transactions through its `ProcessLaneHandler` +which is responsible for verifying the transactions against the lane's verification +logic. If any transactions are invalid, the proposal will be rejected. + +## How to use it + +There are **three** critical +components to the Lane Constructor: + +1. The lane configuration (`LaneConfig`) which determines the basic properties +of the lane including the maximum block space that the lane can fill up. +2. The lane mempool (`LaneMempool`) which is responsible for storing +transactions as they are being verified and are waiting to be included in proposals. +3. A `MatchHandler` which is responsible for determining whether a transaction should +be accepted to this lane. +4. [**OPTIONAL**] Users can optionally define their own `PrepareLaneHandler`, which +is responsible for reaping transactions from its mempool and adding them to a proposal. +This allows users to customize the order/how transactions are added to a proposal +if any custom block building logic is required. +5. [**OPTIONAL**] Users can optionally define their own `ProcessLaneHandler`, which +is responsible for processing transactions that are included in block proposals. +In the case where a custom `PrepareLaneHandler` is defined, a custom `ProcessLaneHandler` +will likely follow. This will allow a proposal to be verified against the custom +block building logic. +6. [**OPTIONAL**] Users can optionally define their own `CheckOrderHandler`, which +is responsible for determining whether transactions that are included in a proposal +and belong to a given lane are ordered correctly in a block proposal. This is useful +for lanes that require a specific ordering of transactions in a proposal. + +### 1. Lane Config + +The lane config (`LaneConfig`) is a simple configuration +object that defines the desired amount of block space the lane should +utilize when building a proposal, an antehandler that is used to verify +transactions as they are added/verified to/in a proposal, and more. By default, +we recommend that user's pass in all of the base apps configurations (txDecoder, +logger, etc.). A sample `LaneConfig` might look like the following: + +```golang +config := block.LaneConfig{ + Logger: app.Logger(), + TxDecoder: app.TxDecoder(), + TxEncoder: app.TxEncoder(), + AnteHandler: app.AnteHandler(), + // Setting MaxTxs to 0 allows the mempool to store an unlimited number of + // transactions. This parameter may be useful for chains that have fast + // block speeds that may require a smaller number of transactions. + MaxTxs: 0, + // MaxBlockSpace is the maximum amount of block space that the lane will + // attempt to fill when building a proposal. This parameter may be useful + // lanes that should be limited (such as a free or onboarding lane) in space + // usage. Setting this to 0 will allow the lane to fill the block with as + // many transactions as possible. + MaxBlockSpace: math.LegacyZeroDec(), + // 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: []block.Lane{}, +} +``` + +The three most important parameters to set are the `AnteHandler`, `MaxTxs`, and +`MaxBlockSpace`. + +#### MaxTxs + +`MaxTxs` sets the maximum number of transactions allowed in the mempool with +the semantics: + +* if `MaxTxs` == 0, there is no cap on the number of transactions in the mempool +* if `MaxTxs` > 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 `MaxTxs` < 0, `Insert` is a no-op. + +#### MaxBlockSpace + + +### 2. LaneMempool + +This is the data structure that is responsible for storing transactions +as they are being verified and are waiting to be included in proposals. `block/constructor/mempool.go` +provides an out-of-the-box implementation that should be used as a starting +point for building out the mempool and should cover most use cases. To +utilize the mempool, you must define a `TxPriority[C]` struct that does the +following: + +- Implements a `GetTxPriority` method that returns the priority (as defined +by the type `[C]`) of a given transaction. +- Implements a `Compare` method that returns the relative priority of two +transactions. If the first transaction has a higher priority, the method +should return -1, if the second transaction has a higher priority, the method +should return 1, otherwise the method should return 0. +- Implements a `MinValue` method that returns the minimum priority value +that a transaction can have. + +The default implementation can be found in `block/constructor/mempool.go`. + +```golang +// 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: "", + } +} +``` + +### LaneHandlers diff --git a/block/constructor/config.go b/block/constructor/config.go new file mode 100644 index 0000000..a52f3c5 --- /dev/null +++ b/block/constructor/config.go @@ -0,0 +1,79 @@ +package constructor + +import ( + "fmt" + + "cosmossdk.io/log" + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/skip-mev/pob/block" +) + +// LaneConfig defines the basic functionality needed for a lane. +type 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 []block.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 NewLaneConfig( + 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 +} diff --git a/block/constructor/lane.go b/block/constructor/lane.go index e5d1d43..559e45e 100644 --- a/block/constructor/lane.go +++ b/block/constructor/lane.go @@ -18,7 +18,7 @@ type LaneConstructor[C comparable] 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 block.LaneConfig + cfg LaneConfig // laneName is the name of the lane. laneName string @@ -50,7 +50,7 @@ type LaneConstructor[C comparable] struct { // 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[C comparable]( - cfg block.LaneConfig, + cfg LaneConfig, laneName string, laneMempool block.LaneMempool, matchHandlerFn block.MatchHandler, diff --git a/block/mempool_test.go b/block/mempool_test.go index 6a04aba..6c4269c 100644 --- a/block/mempool_test.go +++ b/block/mempool_test.go @@ -58,7 +58,7 @@ func (suite *BlockBusterTestSuite) SetupTest() { // // TOB lane set up suite.gasTokenDenom = "stake" - mevConfig := block.LaneConfig{ + mevConfig := constructor.LaneConfig{ Logger: log.NewNopLogger(), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), @@ -71,7 +71,7 @@ func (suite *BlockBusterTestSuite) SetupTest() { ) // Free lane set up - freeConfig := block.LaneConfig{ + freeConfig := constructor.LaneConfig{ Logger: log.NewNopLogger(), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), @@ -85,7 +85,7 @@ func (suite *BlockBusterTestSuite) SetupTest() { ) // Base lane set up - baseConfig := block.LaneConfig{ + baseConfig := constructor.LaneConfig{ Logger: log.NewNopLogger(), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), diff --git a/block/types.go b/block/types.go index cf94883..8a1d2ed 100644 --- a/block/types.go +++ b/block/types.go @@ -1,10 +1,6 @@ package block import ( - "fmt" - - "cosmossdk.io/log" - "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -44,77 +40,8 @@ type ( // 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 { diff --git a/lanes/README.md b/lanes/README.md new file mode 100644 index 0000000..e69de29 diff --git a/lanes/base/README.md b/lanes/base/README.md new file mode 100644 index 0000000..e69de29 diff --git a/lanes/base/abci_test.go b/lanes/base/abci_test.go index c9b46ab..18f1ffe 100644 --- a/lanes/base/abci_test.go +++ b/lanes/base/abci_test.go @@ -9,6 +9,7 @@ import ( "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/skip-mev/pob/block" + "github.com/skip-mev/pob/block/constructor" "github.com/skip-mev/pob/block/utils/mocks" "github.com/skip-mev/pob/lanes/base" testutils "github.com/skip-mev/pob/testutils" @@ -502,7 +503,7 @@ func (s *BaseTestSuite) initLane( maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool, ) *base.DefaultLane { - config := block.NewBaseLaneConfig( + config := constructor.NewLaneConfig( log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), s.encodingConfig.TxConfig.TxDecoder(), diff --git a/lanes/base/lane.go b/lanes/base/lane.go index 0819506..4e59785 100644 --- a/lanes/base/lane.go +++ b/lanes/base/lane.go @@ -23,7 +23,7 @@ type DefaultLane struct { } // NewDefaultLane returns a new default lane. -func NewDefaultLane(cfg block.LaneConfig) *DefaultLane { +func NewDefaultLane(cfg constructor.LaneConfig) *DefaultLane { lane := constructor.NewLaneConstructor[string]( cfg, LaneName, diff --git a/lanes/free/README.md b/lanes/free/README.md new file mode 100644 index 0000000..e69de29 diff --git a/lanes/free/lane.go b/lanes/free/lane.go index e08f3d3..e29ed27 100644 --- a/lanes/free/lane.go +++ b/lanes/free/lane.go @@ -22,7 +22,7 @@ type FreeLane struct { // NewFreeLane returns a new free lane. func NewFreeLane( - cfg block.LaneConfig, + cfg constructor.LaneConfig, txPriority constructor.TxPriority[string], matchFn block.MatchHandler, ) *FreeLane { diff --git a/lanes/mev/README.md b/lanes/mev/README.md new file mode 100644 index 0000000..e69de29 diff --git a/lanes/mev/lane.go b/lanes/mev/lane.go index 54e8779..e2b405b 100644 --- a/lanes/mev/lane.go +++ b/lanes/mev/lane.go @@ -45,7 +45,7 @@ type ( // NewMEVLane returns a new TOB lane. func NewMEVLane( - cfg block.LaneConfig, + cfg constructor.LaneConfig, factory Factory, ) *MEVLane { lane := &MEVLane{ diff --git a/tests/app/app.go b/tests/app/app.go index 65eafcd..a0fdb5f 100644 --- a/tests/app/app.go +++ b/tests/app/app.go @@ -263,7 +263,7 @@ 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. - mevConfig := block.LaneConfig{ + mevConfig := constructor.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), @@ -276,7 +276,7 @@ func New( ) // Free lane allows transactions to be included in the next block for free. - freeConfig := block.LaneConfig{ + freeConfig := constructor.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), @@ -290,7 +290,7 @@ func New( ) // Default lane accepts all other transactions. - defaultConfig := block.LaneConfig{ + defaultConfig := constructor.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), diff --git a/x/builder/ante/ante_test.go b/x/builder/ante/ante_test.go index f2d8563..ee68912 100644 --- a/x/builder/ante/ante_test.go +++ b/x/builder/ante/ante_test.go @@ -12,6 +12,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/golang/mock/gomock" "github.com/skip-mev/pob/block" + "github.com/skip-mev/pob/block/constructor" "github.com/skip-mev/pob/lanes/base" "github.com/skip-mev/pob/lanes/mev" testutils "github.com/skip-mev/pob/testutils" @@ -83,7 +84,7 @@ func (suite *AnteTestSuite) SetupTest() { // Lanes configuration // // TOB lane set up - mevConfig := block.LaneConfig{ + mevConfig := constructor.LaneConfig{ Logger: suite.ctx.Logger(), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), @@ -96,7 +97,7 @@ func (suite *AnteTestSuite) SetupTest() { ) // Base lane set up - baseConfig := block.LaneConfig{ + baseConfig := constructor.LaneConfig{ Logger: suite.ctx.Logger(), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), From aecb504ae83a5168ce0ff875182d0c32190cb5d9 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 12:54:27 -0400 Subject: [PATCH 16/41] more docs --- block/constructor/README.md | 42 +++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/block/constructor/README.md b/block/constructor/README.md index 4527119..4653bba 100644 --- a/block/constructor/README.md +++ b/block/constructor/README.md @@ -93,23 +93,8 @@ config := block.LaneConfig{ TxDecoder: app.TxDecoder(), TxEncoder: app.TxEncoder(), AnteHandler: app.AnteHandler(), - // Setting MaxTxs to 0 allows the mempool to store an unlimited number of - // transactions. This parameter may be useful for chains that have fast - // block speeds that may require a smaller number of transactions. MaxTxs: 0, - // MaxBlockSpace is the maximum amount of block space that the lane will - // attempt to fill when building a proposal. This parameter may be useful - // lanes that should be limited (such as a free or onboarding lane) in space - // usage. Setting this to 0 will allow the lane to fill the block with as - // many transactions as possible. MaxBlockSpace: math.LegacyZeroDec(), - // 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: []block.Lane{}, } ``` @@ -117,9 +102,15 @@ config := block.LaneConfig{ The three most important parameters to set are the `AnteHandler`, `MaxTxs`, and `MaxBlockSpace`. -#### MaxTxs +### **AnteHandler** -`MaxTxs` sets the maximum number of transactions allowed in the mempool with +With the default implementation, the `AnteHandler` is responsible for verifying +transactions as they are being considered for a new proposal or are being processed +in a proposal. + +### **MaxTxs** + +This sets the maximum number of transactions allowed in the mempool with the semantics: * if `MaxTxs` == 0, there is no cap on the number of transactions in the mempool @@ -128,7 +119,22 @@ the semantics: (sequence number) when evicting transactions. * if `MaxTxs` < 0, `Insert` is a no-op. -#### MaxBlockSpace +### **MaxBlockSpace** + +MaxBlockSpace is the maximum amount of block space that the lane will attempt to +fill when building a proposal. This parameter may be useful lanes that should be +limited (such as a free or onboarding lane) in space usage. Setting this to 0 +will allow the lane to fill the block with as many transactions as possible. + +#### **[OPTIONAL] IgnoreList** + +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). ### 2. LaneMempool From 4072ad8cb8268761910045bd6209dbb69270d43a Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 15:03:50 -0400 Subject: [PATCH 17/41] init constructor docs --- block/README.md | 399 ++++-------------------------------- block/constructor/README.md | 292 ++++++++++++++++++++------ 2 files changed, 274 insertions(+), 417 deletions(-) diff --git a/block/README.md b/block/README.md index adee3e7..f80eb41 100644 --- a/block/README.md +++ b/block/README.md @@ -1,355 +1,44 @@ -# BlockBuster - -> 📕 BlockBuster is an app-side mempool + set of proposal handlers that allows -developers to configure modular lanes of transactions in their blocks with -distinct validation/ordering logic. **BlockBuster is the ultimate highway -system for transactions.** - -## High Level Overview - -**`BlockBuster`** is a framework for creating modular, application specific -mempools by **separating transactions into “lanes” with custom transaction handling.** - -You can think of BlockBuster as a **transaction highway system**, where each -lane on the highway serves a specific purpose and has its own set of rules and -traffic flow. - -Similarly, **BlockBuster** redefines block-space into **`lanes`** - where each -`lane` has its own set of rules and transaction flow management systems. - -* A lane is what we might traditionally consider to be a standard mempool -where transaction ***validation***, ***ordering*** and ***prioritization*** for -contained transactions are shared. -* Lanes implement a **standard interface** that allows each individual lane to -propose and validate a portion of a block. -* Lanes are ordered with each other, configurable by developers. All lanes -together define the desired block structure of a chain. - -## BlockBuster Use Cases - -A mempool with separate `lanes` can be used for: - -1. **MEV mitigation**: a MEV lane could be designed to create an -in-protocol mev auction (as we are doing with POB) to recapture MEV -in a transparent and governable way. -2. **Free/reduced fee txs**: transactions with certain properties (e.g. -from trusted accounts or performing encouraged actions) could leverage a -free lane to reward behavior. -3. **Dedicated oracle space** Oracles could be included before other kinds -of transactions to ensure that price updates occur first, and are not able -to be sandwiched or manipulated. -4. **Orderflow auctions**: an OFA lane could be constructed such that order -flow providers can have their submitted transactions bundled with specific -backrunners, to guarantee MEV rewards are attributed back to users. -Imagine MEV-share but in protocol. -5. **Enhanced and customizable privacy**: privacy-enhancing features could -be introduced, such as threshold encrypted lanes, to protect user data and - maintain privacy for specific use cases. -6. **Fee market improvements**: one or many fee markets - such as EIP-1559 - -could be easily adopted for different lanes (potentially custom for certain -dApps). Each smart contract/exchange could have its own fee market or auction -for transaction ordering. -7. **Congestion management**: segmentation of transactions to lanes can help -mitigate network congestion by capping usage of certain applications and -tailoring fee markets. - -## BlockBuster Design - -BlockBuster is a mempool composed of sub-mempools called **lanes**. All -lanes together define the transaction highway system and BlockBuster mempool. -When instantiating the BlockBuster mempool, developers will define all of the -desired lanes and their configurations (including lane ordering). - -Utilizing BlockBuster is a simple three step process: - -* Determine the lanes desired. Currently, POB supports three different -implementations of lanes: MEV lane, free lane, and a default lane. - 1. Top of block lane allows the top of every block to be auctioned off - and constructed using logic defined by the `x/builder` module. - 2. Free lane allows base app to not charge certain types of transactions - any fees. For example, delegations and/or re-delegations might be charged no - fees. What qualifies as a free transaction is determined - [here](https://github.com/skip-mev/pob/blob/main/block/lanes/free/factory.go). - 3. Default lane accepts all other transactions and is considered to be - analogous to how mempools and proposals are constructed today. -* Instantiate the mempool in base app. - -```go -mempool := block.NewMempool(lanes...) -app.App.SetMempool(mempool) -``` - -* Instantiate the BlockBuster proposal handlers in base app. - -```go -proposalHandlers := abci.NewProposalHandler( - app.Logger(), - app.txConfig.TxDecoder(), - mempool, // BlockBuster mempool -) -app.App.SetPrepareProposal(proposalHandlers.PrepareProposalHandler()) -app.App.SetProcessProposal(proposalHandlers.ProcessProposalHandler()) -``` - -***Note: BlockBuster should configure a `DefaultLane` that accepts transactions -that do not belong to any other lane.*** - -Transactions are inserted into the first lane that the transaction matches to. -This means that a given transaction should really only belong to one lane -(but this isn’t enforced). - -### Proposals - -The ordering of lanes when initializing BlockBuster in base app will determine -the ordering of how proposals are built. For example, say that we instantiate -three lanes: - -1. Top of block lane -2. Free lane -3. Default lane - -#### Preparing Proposals - -When the current proposer starts building a block, it will first populate the -proposal with transactions from the MEV lane, followed by free and -default lane. Each lane proposes its own set of transactions using the lane’s -`PrepareLane` (analogous to `PrepareProposal`). Each lane has a limit on the -relative percentage of total block space that the lane can consume. -For example, the free lane might be configured to only make up 10% of any -block. This is defined on each lane’s `Config` when it is instantiated. - -In the case when any lane fails to propose its portion of the block, it will -be skipped and the next lane in the set of lanes will propose its portion of -the block. Failures of partial block proposals are independent of one another. - -#### Processing Proposals - -Block proposals are validated iteratively following the exact ordering of lanes -defined on base app. Transactions included in block proposals must respect the -ordering of lanes. Any proposal that includes transactions that are out of -order relative to the ordering of lanes will be rejected. Following the -example defined above, if a proposal contains the following transactions: - -1. Default transaction (belonging to the default lane) -2. Top of block transaction (belonging to the MEV lane) -3. Free transaction (belonging to the free lane) - -It will be rejected because it does not respect the lane ordering. - -The BlockBuster `ProcessProposalHandler` processes the proposal by verifying -all transactions in the proposal according to each lane's verification logic -in a greedy fashion. If a lane's portion of the proposal is invalid, we -reject the proposal. After a lane's portion of the proposal is verified, we -pass the remaining transactions to the next lane in the chain. - -#### Coming Soon - -BlockBuster will have its own dedicated gRPC service for searchers, wallets, -and users that allows them to query what lane their transaction might belong -in, what fees they might have to pay for a given transaction, and the general -state of the BlockBuster mempool. - -### Lanes - -Each lane will define its own: - -1. Unique prioritization/ordering mechanism i.e. how will transactions from a -given lane be ordered in a block / mempool. -2. Inclusion function to determine what types of transactions belong in the lane. -3. Unique block building/verification mechanism. - -The general interface that each lane must implement can be found [here](https://github.com/skip-mev/pob/blob/main/block/lane.go): - -```go -// 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 -} -``` - -### 1. Intra-lane Transaction Ordering - -**Note: Lanes must implement the `sdk.Mempool` interface.** - -Transactions within a lane are ordered in a proposal respecting the ordering -defined on the lane’s mempool. Developers can define their own custom ordering -by implementing a custom `TxPriority` struct that allows the lane’s mempool to -determine the priority of a transaction `GetTxPriority` and relatively order -two transactions given the priority `Compare`. The MEV lane includes -an custom `TxPriority` that orders transactions in the mempool based on their -bid. - -```go -func TxPriority(config Factory) block.TxPriority[string] { - return block.TxPriority[string]{ - GetTxPriority: func(goCtx context.Context, tx sdk.Tx) string { - bidInfo, err := config.GetAuctionBidInfo(tx) - if err != nil { - panic(err) - } - - return bidInfo.Bid.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: "", - } -} - -// NewMempool returns a new auction mempool. -func NewMempool(txEncoder sdk.TxEncoder, maxTx int, config Factory) *TOBMempool { - return &TOBMempool{ - index: block.NewPriorityMempool( - block.PriorityNonceMempoolConfig[string]{ - TxPriority: TxPriority(config), - MaxTx: maxTx, - }, - ), - txEncoder: txEncoder, - txIndex: make(map[string]struct{}), - Factory: config, - } -} -``` - -### 2. [Optional] Transaction Information Retrieval - -Each lane can define a factory that configures the necessary set of interfaces -required for transaction processing, ordering, and validation. Lanes are -designed such that any given chain can adopt upstream `POB` lanes as long as -developers implement the specified interface(s) associated with transaction -information retrieval for that lane. - -***A standard cosmos chain or EVM chain can then implement their own versions -of these interfaces and automatically utilize the lane with no changes upstream!*** - -For example, the free lane defines an `Factory` that includes a single -`IsFreeTx` function that allows developers to configure what is a free -transaction. The default implementation categorizes free transactions as any -transaction that includes a delegate type message. - -```go -// 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 -} -``` - -### 3. Lane Inclusion Functionality - -Lanes must implement a `Match` interface which determines whether a transaction -should be considered for a given lane. Developer’s are encouraged to utilize the - same interfaces defined in the `Factory` to match transactions to lanes. For - example, developers might configure a MEV auction lane to accept - transactions if they contain a single `MsgAuctionBid` message in the transaction. - -### 4.1. [Optional] Transaction Validation - -Transactions will be verified the lane’s `VerifyTx` function. This logic can be -completely arbitrary. For example, the default lane verifies transactions -using base app’s `AnteHandler` while the MEV lane verifies transactions -by extracting all bundled transactions included in the bid transaction and then -verifying the transaction iteratively given the bundle. - -### 4.2. Block Building/Verification Logic - -Each lane will implement block building and verification logic - analogous to -`Prepare` and `Process` proposal - that is unique to itself. - -* `PrepareLane` will be in charge of building a partial block given the -transactions in the lane. -* `ProcessLaneBasic` ensures that transactions that should be included in the -current lane are not interleaved with other lanes i.e. transactions in -proposals are ordered respecting the ordering of lanes. -* `ProcessLane` will be in charge of verifying the lane’s partial block. - -### Inheritance - -Lanes can inherit the underlying implementation of other lanes and overwrite -any part of the implementation with their own custom functionality. We -recommend that user’s extend the functionality of the `Base` lane when first -exploring the code base. - +## 🤔 How does it work + +### Transaction Lifecycle + +The best way to understand how lanes work is to first understand the lifecycle +of a transaction. When a transaction is submitted to the chain, it will be checked +in `CheckTx` by the base application. If the transaction is valid, it will be +inserted into the applications mempool. The transaction then waits in the mempool +until a new block needs to be proposed. When a new block needs to be proposed, +the application will call `PrepareProposal` (which is a new ABCI++ addition) to +request a new block from the current proposer. The proposer will look at what the +transactions currently waiting to be included in a block in their mempool and +will iterative select transactions until the block is full. The proposer will then +send the block to other validators in the network. When a validator receives a +proposed block, the validator will first want to verify the contents of the block +before signing off on it. The validator will call `ProcessProposal` to verify the +contents of the block. If the block is valid, the validator will sign off on the +block and broadcast their vote to the network. If the block is invalid, the validator +will reject the block. Once a block is accepted by the network, it is committed +and the transactions that were included in the block are removed from the mempool. + +### Lane Lifecycle + +The Lane Constructor implements the `Lane` interface. After transactions are +check in `CheckTx`, they will be added to this lane's mempool (data structure +responsible for storing transactions). When a new block is proposed, `PrepareLane` +will be called by the `PrepareProposalHandler` defined in `abci/abci.go`. This +will trigger the lane to reap transactions from its mempool and add them to the +proposal. By default, transactions are added to proposals in the order that they +are reaped from the mempool. Transactions will only be added to a proposal +if they are valid according to the lane's verification logic. The default implementation +determines whether a transaction is valid by running the transaction through the +lane's `AnteHandler`. If any transactions are invalid, they will be removed from +lane's mempool from further consideration. + +When proposals need to be verified in `ProcessProposal`, the `ProcessProposalHandler` +defined in `abci/abci.go` will call `ProcessLane` on each lane. This will trigger +the lane to process all transactions that are included in the proposal. Lane's +should only verify transactions that belong to their lane. The default implementation +of `ProcessLane` will first check that transactions that should belong to the +current lane are ordered correctly in the proposal. If they are not, the proposal +will be rejected. If they are, the lane will run the transactions through its `ProcessLaneHandler` +which is responsible for verifying the transactions against the lane's verification +logic. If any transactions are invalid, the proposal will be rejected. \ No newline at end of file diff --git a/block/constructor/README.md b/block/constructor/README.md index 4653bba..e4238d7 100644 --- a/block/constructor/README.md +++ b/block/constructor/README.md @@ -1,6 +1,6 @@ # 🎨 Lane Constructor -> 🏗️ Build your own lane in less than 30 minutes using the Lane Constructor +> 🏗️ Build your own lane in less than 10 minutes using the Lane Constructor ## 💡 Overview @@ -8,52 +8,7 @@ The Lane Constructor is a generic implementation of a lane. It comes out of the box with default implementations for all the required interfaces. It is meant to be used as a starting point for building your own lane. -## 🤔 How does it work - -### Transaction Lifecycle - -The best way to understand how lanes work is to first understand the lifecycle -of a transaction. When a transaction is submitted to the chain, it will be checked -in `CheckTx` by the base application. If the transaction is valid, it will be -inserted into the applications mempool. The transaction then waits in the mempool -until a new block needs to be proposed. When a new block needs to be proposed, -the application will call `PrepareProposal` (which is a new ABCI++ addition) to -request a new block from the current proposer. The proposer will look at what the -transactions currently waiting to be included in a block in their mempool and -will iterative select transactions until the block is full. The proposer will then -send the block to other validators in the network. When a validator receives a -proposed block, the validator will first want to verify the contents of the block -before signing off on it. The validator will call `ProcessProposal` to verify the -contents of the block. If the block is valid, the validator will sign off on the -block and broadcast their vote to the network. If the block is invalid, the validator -will reject the block. Once a block is accepted by the network, it is committed -and the transactions that were included in the block are removed from the mempool. - -### Lane Lifecycle - -The Lane Constructor implements the `Lane` interface. After transactions are -check in `CheckTx`, they will be added to this lane's mempool (data structure -responsible for storing transactions). When a new block is proposed, `PrepareLane` -will be called by the `PrepareProposalHandler` defined in `abci/abci.go`. This -will trigger the lane to reap transactions from its mempool and add them to the -proposal. By default, transactions are added to proposals in the order that they -are reaped from the mempool. Transactions will only be added to a proposal -if they are valid according to the lane's verification logic. The default implementation -determines whether a transaction is valid by running the transaction through the -lane's `AnteHandler`. If any transactions are invalid, they will be removed from -lane's mempool from further consideration. - -When proposals need to be verified in `ProcessProposal`, the `ProcessProposalHandler` -defined in `abci/abci.go` will call `ProcessLane` on each lane. This will trigger -the lane to process all transactions that are included in the proposal. Lane's -should only verify transactions that belong to their lane. The default implementation -of `ProcessLane` will first check that transactions that should belong to the -current lane are ordered correctly in the proposal. If they are not, the proposal -will be rejected. If they are, the lane will run the transactions through its `ProcessLaneHandler` -which is responsible for verifying the transactions against the lane's verification -logic. If any transactions are invalid, the proposal will be rejected. - -## How to use it +## 🤔 How to use it There are **three** critical components to the Lane Constructor: @@ -68,15 +23,15 @@ be accepted to this lane. is responsible for reaping transactions from its mempool and adding them to a proposal. This allows users to customize the order/how transactions are added to a proposal if any custom block building logic is required. -5. [**OPTIONAL**] Users can optionally define their own `ProcessLaneHandler`, which +5. [**OPTIONAL**] Users can optionally define their own `CheckOrderHandler`, which +is responsible for determining whether transactions that are included in a proposal +and belong to a given lane are ordered correctly in a block proposal. This is useful +for lanes that require a specific ordering of transactions in a proposal. +6. [**OPTIONAL**] Users can optionally define their own `ProcessLaneHandler`, which is responsible for processing transactions that are included in block proposals. In the case where a custom `PrepareLaneHandler` is defined, a custom `ProcessLaneHandler` will likely follow. This will allow a proposal to be verified against the custom block building logic. -6. [**OPTIONAL**] Users can optionally define their own `CheckOrderHandler`, which -is responsible for determining whether transactions that are included in a proposal -and belong to a given lane are ordered correctly in a block proposal. This is useful -for lanes that require a specific ordering of transactions in a proposal. ### 1. Lane Config @@ -143,7 +98,7 @@ This is the data structure that is responsible for storing transactions as they are being verified and are waiting to be included in proposals. `block/constructor/mempool.go` provides an out-of-the-box implementation that should be used as a starting point for building out the mempool and should cover most use cases. To -utilize the mempool, you must define a `TxPriority[C]` struct that does the +utilize the mempool, you must implement a `TxPriority[C]` struct that does the following: - Implements a `GetTxPriority` method that returns the priority (as defined @@ -155,20 +110,29 @@ should return 1, otherwise the method should return 0. - Implements a `MinValue` method that returns the minimum priority value that a transaction can have. -The default implementation can be found in `block/constructor/mempool.go`. +The default implementation can be found in `block/constructor/mempool.go`. What if +we wanted to prioritize transactions by the amount they have staked on chain? Well +we could do something like the following: ```golang -// DefaultTxPriority returns a default implementation of the TxPriority. It prioritizes -// transactions by their fee. -func DefaultTxPriority() TxPriority[string] { +// CustomTxPriority returns a TxPriority that prioritizes transactions by the +// amount they have staked on chain. This means that transactions with a higher +// amount staked will be prioritized over transactions with a lower amount staked. +func (p *CustomTxPriority) CustomTxPriority() TxPriority[string] { return TxPriority[string]{ - GetTxPriority: func(goCtx context.Context, tx sdk.Tx) string { - feeTx, ok := tx.(sdk.FeeTx) - if !ok { + GetTxPriority: func(ctx context.Context, tx sdk.Tx) string { + // Get the signer of the transaction. + signer := p.getTransactionSigner(tx) + + // Get the total amount staked by the signer on chain. + // This is abstracted away in the example, but you can + // implement this using the staking keeper. + totalStake, err := p.getTotalStake(ctx, signer) + if err != nil { return "" } - return feeTx.GetFee().String() + return totalStake.String() }, Compare: func(a, b string) int { aCoins, _ := sdk.ParseCoinsNormalized(a) @@ -202,4 +166,208 @@ func DefaultTxPriority() TxPriority[string] { } ``` -### LaneHandlers +To utilize this mempool in a lane, all you have to then do is pass in the +`TxPriority[C]` to the `NewLaneMempool` function. + +```golang +// Pseudocode for creating the custom tx priority +priorityCfg := NewPriorityConfig( + stakingKeeper, + accountKeeper, + ... +) + + +// define your mempool that orders transactions by on chain stake +mempool := constructor.NewMempool[string]( + priorityCfg.CustomTxPriority(), + cfg.TxEncoder, + cfg.MaxTxs, +) + +// Initialize your lane with the mempool +lane := constructor.NewLaneConstructor( + cfg, + LaneName, + mempool, + constructor.DefaultMatchHandler(), +) +``` + +### 3. MatchHandler + +> 🔒 `MatchHandler` Invarients +> +> The handler assumes that the transactions passed into the function are already +> ordered respecting the lane's ordering rules and respecting the ordering rules +> of the mempool relative to the lanes it has. This means that the transactions +> should already be in contiguous order. + +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. +The default implementation can be found in `block/constructor/handlers.go`. + +The match handler can be as custom as desired. Following the example above, if +we wanted to make a lane that only accepts transactions if they have a large +amount staked, we could do the following: + +```golang +// CustomMatchHandler returns a custom implementation of the MatchHandler. It +// matches transactions that have a large amount staked. These transactions +// will then be charged no fees at execution time. +// +// NOTE: This is a stateful check on the transaction. The details of how to +// implement this are abstracted away in the example, but you can implement +// this using the staking keeper. +func (h *Handler) CustomMatchHandler() block.MatchHandler { + return func(ctx sdk.Context, tx sdk.Tx) bool { + if !h.IsStakingTx(tx) { + return false + } + + signer, err := getTxSigner(tx) + if err != nil { + return false + } + + stakedAmount, err := h.GetStakedAmount(signer) + if err != nil { + return false + } + + return stakeAmount.GT(h.Threshold) + } +} +``` + +If we wanted to create the lane using the custom match handler along with the +custom mempool, we could do the following: + +```golang +// Pseudocode for creating the custom match handler +handler := NewHandler( + stakingKeeper, + accountKeeper, + ... +) + +// define your mempool that orders transactions by on chain stake +mempool := constructor.NewMempool[string]( + priorityCfg.CustomTxPriority(), + cfg.TxEncoder, + cfg.MaxTxs, +) + +// Initialize your lane with the mempool +lane := constructor.NewLaneConstructor( + cfg, + LaneName, + mempool, + handler.CustomMatchHandler(), +) +``` + +### Notes on Steps 4-6 + +Although not required, if you implement any single custom handler, whether it's +the `PrepareLaneHandler`, `ProcessLaneHandler`, or `CheckOrderHandler`, you must +implement all of them. This is because the default implementation of the lane +constructor will call all of these handlers. If you do not implement all of them, +the lane may have unintended behavior. + +### 4. [OPTIONAL] PrepareLaneHandler + +> 🔒 `PrepareLaneHandler` Invarients +> +> Transactions should be reaped respecting the priority mechanism of the lane. +> By default this is the TxPriority object used to initialize the lane's mempool. + +The `PrepareLaneHandler` is an optional field you can set on the lane constructor. +This handler is responsible for the transaction selection logic when a new proposal +is requested. The default implementation should fit most use cases and can be found +in `block/constructor/handlers.go`. + +The handler should return the following for a given lane: + +1. The transactions to be included in the block proposal. +2. The transactions to be removed from the mempool. +3. An error if the lane is unable to prepare a block proposal. + +```golang +// 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) +``` + +The default implementation is simple. It will continue to select transactions +from its mempool under the following criteria: + +1. The transactions is not already included in the block proposal. +2. The transaction is valid and passes the AnteHandler check. +3. The transaction is not too large to be included in the block. + +If a more involved selection process is required, you can implement your own +`PrepareLaneHandler` and and set it after creating the lane constructor. + +```golang +customLane := constructor.NewLaneConstructor( + cfg, + LaneName, + mempool, + handler.CustomMatchHandler(), +) + +customLane.SetPrepareLaneHandler(customlane.PrepareLaneHandler()) +``` + +### 5. [OPTIONAL] CheckOrderHandler + +> 🔒 `CheckOrderHandler` Invarients +> +> The CheckOrderHandler must ensure that transactions included in block proposals +> only include transactions that are in contiguous order respecting the lane's +> ordering rules and respecting the ordering rules of the mempool relative to the +> lanes it has. This means that all transactions that belong to the same lane, must +> be right next to each other in the block proposal. Additionally, the relative priority +> of each transaction belonging to the lane must be respected. + +The `CheckOrderHandler` is an optional field you can set on the lane constructor. + + +### 6. [OPTIONAL] ProcessLaneHandler + +> 🔒 `ProcessLaneHandler` Invarients +> +> The handler assumes that the transactions passed into the function are already +> ordered respecting the lane's ordering rules and respecting the ordering rules +> of the mempool relative to the lanes it has. This means that the transactions +> should already be in contiguous order. + +The `ProcessLaneHandler` is an optional field you can set on the lane constructor. +This handler is responsible for verifying the transactions in the block proposal +that belong to the lane. The default implementation should fit most use cases and +can be found in `block/constructor/handlers.go`. + + +```golang +// 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) +``` + +Given the invarients above, the default implementation is simple. It will +continue to verify transactions in the block proposal under the following +criteria: + +1. If a transaction matches to this lane, verify it and continue. If it is not +valid, return an error. +2. If a transaction does not match to this lane, return the remaining transactions. \ No newline at end of file From 0fc161e7952884f1afb48c3c50992ff8254b77f1 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 16:31:36 -0400 Subject: [PATCH 18/41] docs for constructor done --- block/constructor/README.md | 263 +++++++++++++++++++++++------------- 1 file changed, 171 insertions(+), 92 deletions(-) diff --git a/block/constructor/README.md b/block/constructor/README.md index e4238d7..3c3cea5 100644 --- a/block/constructor/README.md +++ b/block/constructor/README.md @@ -10,30 +10,33 @@ be used as a starting point for building your own lane. ## 🤔 How to use it -There are **three** critical -components to the Lane Constructor: +> **Default Implementations** +> +> There are default implementations for all of the below which can be found in +> the `block/constructor` package. It is highly recommended that developers overview +> the default implementations before building their own lane. -1. The lane configuration (`LaneConfig`) which determines the basic properties +There are **three** critical components to building a custom lane using the lane +constructor: + +1. `LaneConfig` - The lane configuration which determines the basic properties of the lane including the maximum block space that the lane can fill up. -2. The lane mempool (`LaneMempool`) which is responsible for storing -transactions as they are being verified and are waiting to be included in proposals. -3. A `MatchHandler` which is responsible for determining whether a transaction should -be accepted to this lane. -4. [**OPTIONAL**] Users can optionally define their own `PrepareLaneHandler`, which -is responsible for reaping transactions from its mempool and adding them to a proposal. -This allows users to customize the order/how transactions are added to a proposal -if any custom block building logic is required. -5. [**OPTIONAL**] Users can optionally define their own `CheckOrderHandler`, which -is responsible for determining whether transactions that are included in a proposal -and belong to a given lane are ordered correctly in a block proposal. This is useful -for lanes that require a specific ordering of transactions in a proposal. -6. [**OPTIONAL**] Users can optionally define their own `ProcessLaneHandler`, which -is responsible for processing transactions that are included in block proposals. -In the case where a custom `PrepareLaneHandler` is defined, a custom `ProcessLaneHandler` -will likely follow. This will allow a proposal to be verified against the custom -block building logic. +2. `LaneMempool` - The lane mempool which is responsible for storing +transactions that have been verified and are waiting to be included in proposals. +3. `MatchHandler` - This is responsible for determining whether a transaction should +belong to this lane. +4. [**OPTIONAL**] `PrepareLaneHandler` - Allows developers to define their own +handler to customize the how transactions are verified and ordered before they +are included into a proposal. +5. [**OPTIONAL**] `CheckOrderHandler` - Allows developers to define their own +handler that will run any custom checks on whether transactions included in +block proposals are in the correct order (respecting the ordering rules of the +lane and the ordering rules of the other lanes). +6. [**OPTIONAL**] `ProcessLaneHandler` - Allows developers to define their own +handler for processing transactions that are included in block proposals. -### 1. Lane Config + +### 1. Lane Config 📝 The lane config (`LaneConfig`) is a simple configuration object that defines the desired amount of block space the lane should @@ -57,13 +60,41 @@ config := block.LaneConfig{ The three most important parameters to set are the `AnteHandler`, `MaxTxs`, and `MaxBlockSpace`. -### **AnteHandler** +#### **AnteHandler** With the default implementation, the `AnteHandler` is responsible for verifying transactions as they are being considered for a new proposal or are being processed -in a proposal. +in a proposed block. We recommend user's utilize the same antehandler chain that +is used in the base app. If developers want a certain `AnteDecorator` to be +ignored if it qualifies for a given lane, they can do so by using the `NewIgnoreDecorator` +defined in `block/utils/ante.go`. -### **MaxTxs** +For example, a free lane might want to ignore the `DeductFeeDecorator` so that it's +transactions are not charged any fees. Where ever the `AnteHandler` is defined, +we could add the following to ignore the `DeductFeeDecorator`: + +```golang +anteDecorators := []sdk.AnteDecorator{ + ante.NewSetUpContextDecorator(), + ..., + utils.NewIgnoreDecorator( + ante.NewDeductFeeDecorator( + options.BaseOptions.AccountKeeper, + options.BaseOptions.BankKeeper, + options.BaseOptions.FeegrantKeeper, + options.BaseOptions.TxFeeChecker, + ), + options.FreeLane, + ), + ..., +} +``` + +Anytime a transaction that qualifies for the free lane is being processed, the +`DeductFeeDecorator` will be ignored and no fees will be deducted! + + +#### **MaxTxs** This sets the maximum number of transactions allowed in the mempool with the semantics: @@ -74,25 +105,27 @@ the semantics: (sequence number) when evicting transactions. * if `MaxTxs` < 0, `Insert` is a no-op. -### **MaxBlockSpace** +#### **MaxBlockSpace** MaxBlockSpace is the maximum amount of block space that the lane will attempt to fill when building a proposal. This parameter may be useful lanes that should be limited (such as a free or onboarding lane) in space usage. Setting this to 0 will allow the lane to fill the block with as many transactions as possible. +If a block proposal request has a `MaxTxBytes` of 1000 and the lane has a +`MaxBlockSpace` of 0.5, the lane will attempt to fill the block with 500 bytes. + #### **[OPTIONAL] IgnoreList** -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 +`IgnoreList` defines the list of lanes to ignore when processing transactions. +For example, say there are two lanes: default and free. The free lane is 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). -### 2. LaneMempool +### 2. LaneMempool 🗄️ This is the data structure that is responsible for storing transactions as they are being verified and are waiting to be included in proposals. `block/constructor/mempool.go` @@ -101,18 +134,18 @@ point for building out the mempool and should cover most use cases. To utilize the mempool, you must implement a `TxPriority[C]` struct that does the following: -- Implements a `GetTxPriority` method that returns the priority (as defined +* Implements a `GetTxPriority` method that returns the priority (as defined by the type `[C]`) of a given transaction. -- Implements a `Compare` method that returns the relative priority of two +* Implements a `Compare` method that returns the relative priority of two transactions. If the first transaction has a higher priority, the method -should return -1, if the second transaction has a higher priority, the method +should return -1, if the second transaction has a higher priority the method should return 1, otherwise the method should return 0. -- Implements a `MinValue` method that returns the minimum priority value +* Implements a `MinValue` method that returns the minimum priority value that a transaction can have. -The default implementation can be found in `block/constructor/mempool.go`. What if -we wanted to prioritize transactions by the amount they have staked on chain? Well -we could do something like the following: +The default implementation can be found in `block/constructor/mempool.go`. What +if we wanted to prioritize transactions by the amount they have staked on a chain? +Well we could do something like the following: ```golang // CustomTxPriority returns a TxPriority that prioritizes transactions by the @@ -166,10 +199,19 @@ func (p *CustomTxPriority) CustomTxPriority() TxPriority[string] { } ``` -To utilize this mempool in a lane, all you have to then do is pass in the -`TxPriority[C]` to the `NewLaneMempool` function. +#### Using a Custom TxPriority + +To utilize this new priority configuration in a lane, all you have to then do +is pass in the `TxPriority[C]` to the `NewLaneMempool` function. ```golang +// Create the lane config +laneCfg := NewLaneConfig( + ... + MaxTxs: 100, + ... +) + // Pseudocode for creating the custom tx priority priorityCfg := NewPriorityConfig( stakingKeeper, @@ -178,32 +220,25 @@ priorityCfg := NewPriorityConfig( ) -// define your mempool that orders transactions by on chain stake +// define your mempool that orders transactions by on-chain stake mempool := constructor.NewMempool[string]( priorityCfg.CustomTxPriority(), - cfg.TxEncoder, - cfg.MaxTxs, + laneCfg.TxEncoder, + laneCfg.MaxTxs, ) // Initialize your lane with the mempool lane := constructor.NewLaneConstructor( - cfg, + laneCfg, LaneName, mempool, constructor.DefaultMatchHandler(), ) ``` -### 3. MatchHandler +### 3. MatchHandler 🤝 -> 🔒 `MatchHandler` Invarients -> -> The handler assumes that the transactions passed into the function are already -> ordered respecting the lane's ordering rules and respecting the ordering rules -> of the mempool relative to the lanes it has. This means that the transactions -> should already be in contiguous order. - -MatchHandler is utilized to determine if a transaction should be included in +`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. The default implementation can be found in `block/constructor/handlers.go`. @@ -235,11 +270,15 @@ func (h *Handler) CustomMatchHandler() block.MatchHandler { return false } + // The transaction can only be considered for inclusion if the amount + // staked is greater than some predetermined threshold. return stakeAmount.GT(h.Threshold) } } ``` +#### Using a Custom MatchHandler + If we wanted to create the lane using the custom match handler along with the custom mempool, we could do the following: @@ -267,30 +306,38 @@ lane := constructor.NewLaneConstructor( ) ``` -### Notes on Steps 4-6 +### Summary on Steps 1-3 -Although not required, if you implement any single custom handler, whether it's -the `PrepareLaneHandler`, `ProcessLaneHandler`, or `CheckOrderHandler`, you must -implement all of them. This is because the default implementation of the lane -constructor will call all of these handlers. If you do not implement all of them, -the lane may have unintended behavior. +The following is a summary of the steps above: -### 4. [OPTIONAL] PrepareLaneHandler +1. Create a custom `LaneConfig` struct that defines the configuration of the lane. +2. Create a custom `TxPriority[C]` struct to have a custom mempool that orders +transactions via a custom priority mechanism. +3. Create a custom `MatchHandler` that implements the `block.MatchHandler` to +have a custom lane that only accepts transactions that match a custom criteria. -> 🔒 `PrepareLaneHandler` Invarients -> -> Transactions should be reaped respecting the priority mechanism of the lane. -> By default this is the TxPriority object used to initialize the lane's mempool. +### [OPTIONAL] Steps 4-6 + +The remaining steps walk through the process of creating custom block +building/verification logic. The default implementation found in `block/constructor/handlers.go` +should fit most use cases. Please reference that file for more details on +the default implementation and whether it fits your use case. + +Implementing custom block building/verification logic is a bit more involved +than the previous steps and is a all or nothing approach. This means that if +you implement any of the handlers, you must implement all of them in most cases. +If you do not implement all of them, the lane may have unintended behavior. + +### 4. PrepareLaneHandler The `PrepareLaneHandler` is an optional field you can set on the lane constructor. This handler is responsible for the transaction selection logic when a new proposal -is requested. The default implementation should fit most use cases and can be found -in `block/constructor/handlers.go`. +is requested. The handler should return the following for a given lane: 1. The transactions to be included in the block proposal. -2. The transactions to be removed from the mempool. +2. The transactions to be removed from the lane's mempool. 3. An error if the lane is unable to prepare a block proposal. ```golang @@ -298,11 +345,8 @@ The handler should return the following for a given lane: // 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) +PrepareLaneHandler func(ctx sdk.Context,proposal BlockProposal,maxTxBytes int64) + (txsToInclude [][]byte, txsToRemove []sdk.Tx, err error) ``` The default implementation is simple. It will continue to select transactions @@ -316,43 +360,77 @@ If a more involved selection process is required, you can implement your own `PrepareLaneHandler` and and set it after creating the lane constructor. ```golang -customLane := constructor.NewLaneConstructor( +// Pseudocode for creating the custom prepare lane handler +// This assumes that the CustomLane inherits from the constructor +// lane. +customLane := constructor.NewCustomLane( cfg, LaneName, mempool, handler.CustomMatchHandler(), ) +// Set the custom PrepareLaneHandler on the lane customLane.SetPrepareLaneHandler(customlane.PrepareLaneHandler()) ``` -### 5. [OPTIONAL] CheckOrderHandler +### 5. CheckOrderHandler -> 🔒 `CheckOrderHandler` Invarients -> -> The CheckOrderHandler must ensure that transactions included in block proposals -> only include transactions that are in contiguous order respecting the lane's -> ordering rules and respecting the ordering rules of the mempool relative to the -> lanes it has. This means that all transactions that belong to the same lane, must -> be right next to each other in the block proposal. Additionally, the relative priority -> of each transaction belonging to the lane must be respected. +The `CheckOrderHandler` is an optional field you can set on the lane constructor. +This handler is responsible for verifying the ordering of the transactions in the +block proposal that belong to the lane. -The `CheckOrderHandler` is an optional field you can set on the lane constructor. +```golang +// 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 +``` + +The default implementation is simple and utilizes the same `TxPriority` struct +that the mempool uses to determine if transactions are in order. The criteria +for determining if transactions are in order is as follows: + +1. The transactions are in order according to the `TxPriority` struct. i.e. any +two transactions (that match to the lane) `tx1` and `tx2` where `tx1` has a +higher priority than `tx2` should be ordered before `tx2`. +2. The transactions are contiguous. i.e. there are no transactions from other +lanes in between the transactions that belong to this lane. i.e. if `tx1` and +`tx2` belong to the lane, there should be no transactions from other lanes in +between `tx1` and `tx2`. + +If a more involved ordering process is required, you can implement your own +`CheckOrderHandler` and and set it after creating the lane constructor. + +```golang +// Pseudocode for creating the custom check order handler +// This assumes that the CustomLane inherits from the constructor +// lane. +customLane := constructor.NewCustomLane( + cfg, + LaneName, + mempool, + handler.CustomMatchHandler(), +) + +// Set the custom CheckOrderHandler on the lane +customLane.SetCheckOrderHandler(customlane.CheckOrderHandler()) +``` -### 6. [OPTIONAL] ProcessLaneHandler - -> 🔒 `ProcessLaneHandler` Invarients -> -> The handler assumes that the transactions passed into the function are already -> ordered respecting the lane's ordering rules and respecting the ordering rules -> of the mempool relative to the lanes it has. This means that the transactions -> should already be in contiguous order. +### 6. ProcessLaneHandler The `ProcessLaneHandler` is an optional field you can set on the lane constructor. This handler is responsible for verifying the transactions in the block proposal -that belong to the lane. The default implementation should fit most use cases and -can be found in `block/constructor/handlers.go`. +that belong to the lane. This handler is executed after the `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. This means that if the first transaction +does not belong to the lane, the remaining transactions should not belong to the +lane either. ```golang @@ -370,4 +448,5 @@ criteria: 1. If a transaction matches to this lane, verify it and continue. If it is not valid, return an error. -2. If a transaction does not match to this lane, return the remaining transactions. \ No newline at end of file +2. If a transaction does not match to this lane, return the remaining transactions +to the next lane to process. From 51537eb4e3b5afe61d27f7c09e4cf308720ca3a8 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 16:39:57 -0400 Subject: [PATCH 19/41] more updates --- block/lane_abci.go | 67 ------------- block/lane_constructor.go | 198 -------------------------------------- block/lane_handlers.go | 155 ----------------------------- block/lane_interface.go | 71 -------------- block/lane_mempool.go | 159 ------------------------------ lanes/terminator/lane.go | 4 - 6 files changed, 654 deletions(-) delete mode 100644 block/lane_abci.go delete mode 100644 block/lane_constructor.go delete mode 100644 block/lane_handlers.go delete mode 100644 block/lane_interface.go delete mode 100644 block/lane_mempool.go diff --git a/block/lane_abci.go b/block/lane_abci.go deleted file mode 100644 index a70b102..0000000 --- a/block/lane_abci.go +++ /dev/null @@ -1,67 +0,0 @@ -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 -} diff --git a/block/lane_constructor.go b/block/lane_constructor.go deleted file mode 100644 index 7107814..0000000 --- a/block/lane_constructor.go +++ /dev/null @@ -1,198 +0,0 @@ -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 -} diff --git a/block/lane_handlers.go b/block/lane_handlers.go deleted file mode 100644 index 0a87a5d..0000000 --- a/block/lane_handlers.go +++ /dev/null @@ -1,155 +0,0 @@ -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 - } -} diff --git a/block/lane_interface.go b/block/lane_interface.go deleted file mode 100644 index 59ee35e..0000000 --- a/block/lane_interface.go +++ /dev/null @@ -1,71 +0,0 @@ -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 -} diff --git a/block/lane_mempool.go b/block/lane_mempool.go deleted file mode 100644 index e102ee8..0000000 --- a/block/lane_mempool.go +++ /dev/null @@ -1,159 +0,0 @@ -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) -} diff --git a/lanes/terminator/lane.go b/lanes/terminator/lane.go index c828792..3fce765 100644 --- a/lanes/terminator/lane.go +++ b/lanes/terminator/lane.go @@ -14,10 +14,6 @@ const ( LaneName = "Terminator" ) -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 // From 8b37a0a38cfcfb0dbac07ad775669eac3d99e553 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 16:42:44 -0400 Subject: [PATCH 20/41] more clean up --- abci/abci_test.go | 4 +- block/constructor/abci.go | 8 +- block/constructor/handlers.go | 6 +- block/constructor/lane.go | 36 +- block/lanes/base/abci_test.go | 547 ------------------------------- block/lanes/base/base_test.go | 32 -- block/lanes/base/mempool_test.go | 240 -------------- lanes/base/lane.go | 4 +- lanes/free/lane.go | 4 +- lanes/mev/lane.go | 4 +- 10 files changed, 34 insertions(+), 851 deletions(-) delete mode 100644 block/lanes/base/abci_test.go delete mode 100644 block/lanes/base/base_test.go delete mode 100644 block/lanes/base/mempool_test.go diff --git a/abci/abci_test.go b/abci/abci_test.go index 23d8a73..36ff22b 100644 --- a/abci/abci_test.go +++ b/abci/abci_test.go @@ -760,7 +760,7 @@ func (s *ProposalsTestSuite) setUpFreeLane(maxBlockSpace math.LegacyDec, expecte return free.NewFreeLane(cfg, constructor.DefaultTxPriority(), free.DefaultMatchHandler()) } -func (s *ProposalsTestSuite) setUpPanicLane(maxBlockSpace math.LegacyDec) *constructor.LaneConstructor[string] { +func (s *ProposalsTestSuite) setUpPanicLane(maxBlockSpace math.LegacyDec) *constructor.LaneConstructor { cfg := constructor.LaneConfig{ Logger: log.NewTestLogger(s.T()), TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), @@ -768,7 +768,7 @@ func (s *ProposalsTestSuite) setUpPanicLane(maxBlockSpace math.LegacyDec) *const MaxBlockSpace: maxBlockSpace, } - lane := constructor.NewLaneConstructor[string]( + lane := constructor.NewLaneConstructor( cfg, "panic", constructor.NewMempool[string](constructor.DefaultTxPriority(), cfg.TxEncoder, 0), diff --git a/block/constructor/abci.go b/block/constructor/abci.go index db07807..cc80fea 100644 --- a/block/constructor/abci.go +++ b/block/constructor/abci.go @@ -10,7 +10,7 @@ import ( // 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[C]) PrepareLane( +func (l *LaneConstructor) PrepareLane( ctx sdk.Context, proposal block.BlockProposal, maxTxBytes int64, @@ -40,7 +40,7 @@ func (l *LaneConstructor[C]) PrepareLane( // 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[C]) CheckOrder(ctx sdk.Context, txs []sdk.Tx) error { +func (l *LaneConstructor) CheckOrder(ctx sdk.Context, txs []sdk.Tx) error { return l.checkOrderHandler(ctx, txs) } @@ -48,7 +48,7 @@ func (l *LaneConstructor[C]) CheckOrder(ctx sdk.Context, txs []sdk.Tx) error { // 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[C]) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next block.ProcessLanesHandler) (sdk.Context, error) { +func (l *LaneConstructor) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next block.ProcessLanesHandler) (sdk.Context, error) { remainingTxs, err := l.processLaneHandler(ctx, txs) if err != nil { return ctx, err @@ -59,7 +59,7 @@ func (l *LaneConstructor[C]) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next blo // AnteVerifyTx verifies that the transaction is valid respecting the ante verification logic of // of the antehandler chain. -func (l *LaneConstructor[C]) AnteVerifyTx(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) { +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) } diff --git a/block/constructor/handlers.go b/block/constructor/handlers.go index d07c61d..a927d79 100644 --- a/block/constructor/handlers.go +++ b/block/constructor/handlers.go @@ -12,7 +12,7 @@ import ( // 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[C]) DefaultPrepareLaneHandler() block.PrepareLaneHandler { +func (l *LaneConstructor) DefaultPrepareLaneHandler() block.PrepareLaneHandler { return func(ctx sdk.Context, proposal block.BlockProposal, maxTxBytes int64) ([][]byte, []sdk.Tx, error) { var ( totalSize int64 @@ -96,7 +96,7 @@ func (l *LaneConstructor[C]) DefaultPrepareLaneHandler() block.PrepareLaneHandle // 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[C]) DefaultProcessLaneHandler() block.ProcessLaneHandler { +func (l *LaneConstructor) DefaultProcessLaneHandler() block.ProcessLaneHandler { return func(ctx sdk.Context, txs []sdk.Tx) ([]sdk.Tx, error) { var err error @@ -123,7 +123,7 @@ func (l *LaneConstructor[C]) DefaultProcessLaneHandler() block.ProcessLaneHandle // lane. // 2. Transactions that belong to other lanes cannot be interleaved with transactions that // belong to this lane. -func (l *LaneConstructor[C]) DefaultCheckOrderHandler() block.CheckOrderHandler { +func (l *LaneConstructor) DefaultCheckOrderHandler() block.CheckOrderHandler { return func(ctx sdk.Context, txs []sdk.Tx) error { seenOtherLaneTx := false diff --git a/block/constructor/lane.go b/block/constructor/lane.go index 559e45e..2cf49e6 100644 --- a/block/constructor/lane.go +++ b/block/constructor/lane.go @@ -9,12 +9,14 @@ import ( "github.com/skip-mev/pob/block" ) +var _ block.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[C comparable] struct { +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. @@ -49,13 +51,13 @@ type LaneConstructor[C comparable] struct { // 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[C comparable]( +func NewLaneConstructor( cfg LaneConfig, laneName string, laneMempool block.LaneMempool, matchHandlerFn block.MatchHandler, -) *LaneConstructor[C] { - lane := &LaneConstructor[C]{ +) *LaneConstructor { + lane := &LaneConstructor{ cfg: cfg, laneName: laneName, LaneMempool: laneMempool, @@ -71,7 +73,7 @@ func NewLaneConstructor[C comparable]( // 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[C]) ValidateBasic() error { +func (l *LaneConstructor) ValidateBasic() error { if err := l.cfg.ValidateBasic(); err != nil { return err } @@ -106,7 +108,7 @@ func (l *LaneConstructor[C]) ValidateBasic() error { // 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[C]) SetPrepareLaneHandler(prepareLaneHandler block.PrepareLaneHandler) { +func (l *LaneConstructor) SetPrepareLaneHandler(prepareLaneHandler block.PrepareLaneHandler) { if prepareLaneHandler == nil { panic("prepare lane handler cannot be nil") } @@ -118,7 +120,7 @@ func (l *LaneConstructor[C]) SetPrepareLaneHandler(prepareLaneHandler block.Prep // 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[C]) SetProcessLaneHandler(processLaneHandler block.ProcessLaneHandler) { +func (l *LaneConstructor) SetProcessLaneHandler(processLaneHandler block.ProcessLaneHandler) { if processLaneHandler == nil { panic("process lane handler cannot be nil") } @@ -130,7 +132,7 @@ func (l *LaneConstructor[C]) SetProcessLaneHandler(processLaneHandler block.Proc // 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[C]) SetCheckOrderHandler(checkOrderHandler block.CheckOrderHandler) { +func (l *LaneConstructor) SetCheckOrderHandler(checkOrderHandler block.CheckOrderHandler) { if checkOrderHandler == nil { panic("check order handler cannot be nil") } @@ -142,14 +144,14 @@ func (l *LaneConstructor[C]) SetCheckOrderHandler(checkOrderHandler block.CheckO // 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[C]) Match(ctx sdk.Context, tx sdk.Tx) bool { +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[C]) CheckIgnoreList(ctx sdk.Context, tx sdk.Tx) bool { +func (l *LaneConstructor) CheckIgnoreList(ctx sdk.Context, tx sdk.Tx) bool { for _, lane := range l.cfg.IgnoreList { if lane.Match(ctx, tx) { return true @@ -160,38 +162,38 @@ func (l *LaneConstructor[C]) CheckIgnoreList(ctx sdk.Context, tx sdk.Tx) bool { } // Name returns the name of the lane. -func (l *LaneConstructor[C]) Name() string { +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[C]) SetIgnoreList(lanes []block.Lane) { +func (l *LaneConstructor) SetIgnoreList(lanes []block.Lane) { l.cfg.IgnoreList = lanes } // SetAnteHandler sets the ante handler for the lane. -func (l *LaneConstructor[C]) SetAnteHandler(anteHandler sdk.AnteHandler) { +func (l *LaneConstructor) SetAnteHandler(anteHandler sdk.AnteHandler) { l.cfg.AnteHandler = anteHandler } // Logger returns the logger for the lane. -func (l *LaneConstructor[C]) Logger() log.Logger { +func (l *LaneConstructor) Logger() log.Logger { return l.cfg.Logger } // TxDecoder returns the tx decoder for the lane. -func (l *LaneConstructor[C]) TxDecoder() sdk.TxDecoder { +func (l *LaneConstructor) TxDecoder() sdk.TxDecoder { return l.cfg.TxDecoder } // TxEncoder returns the tx encoder for the lane. -func (l *LaneConstructor[C]) TxEncoder() sdk.TxEncoder { +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[C]) GetMaxBlockSpace() math.LegacyDec { +func (l *LaneConstructor) GetMaxBlockSpace() math.LegacyDec { return l.cfg.MaxBlockSpace } diff --git a/block/lanes/base/abci_test.go b/block/lanes/base/abci_test.go deleted file mode 100644 index d5c054d..0000000 --- a/block/lanes/base/abci_test.go +++ /dev/null @@ -1,547 +0,0 @@ -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 -} diff --git a/block/lanes/base/base_test.go b/block/lanes/base/base_test.go deleted file mode 100644 index 7852dac..0000000 --- a/block/lanes/base/base_test.go +++ /dev/null @@ -1,32 +0,0 @@ -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" -} diff --git a/block/lanes/base/mempool_test.go b/block/lanes/base/mempool_test.go deleted file mode 100644 index 4f12e73..0000000 --- a/block/lanes/base/mempool_test.go +++ /dev/null @@ -1,240 +0,0 @@ -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) - }) -} diff --git a/lanes/base/lane.go b/lanes/base/lane.go index 4e59785..99d0e10 100644 --- a/lanes/base/lane.go +++ b/lanes/base/lane.go @@ -19,12 +19,12 @@ var _ block.Lane = (*DefaultLane)(nil) // CometBFT/Tendermint consensus engine builds and verifies blocks pre SDK version // 0.47.0. type DefaultLane struct { - *constructor.LaneConstructor[string] + *constructor.LaneConstructor } // NewDefaultLane returns a new default lane. func NewDefaultLane(cfg constructor.LaneConfig) *DefaultLane { - lane := constructor.NewLaneConstructor[string]( + lane := constructor.NewLaneConstructor( cfg, LaneName, constructor.NewMempool[string]( diff --git a/lanes/free/lane.go b/lanes/free/lane.go index e29ed27..ba48d89 100644 --- a/lanes/free/lane.go +++ b/lanes/free/lane.go @@ -17,7 +17,7 @@ var _ block.Lane = (*FreeLane)(nil) // FreeLane defines the lane that is responsible for processing free transactions. // By default, transactions that are staking related are considered free. type FreeLane struct { - *constructor.LaneConstructor[string] + *constructor.LaneConstructor } // NewFreeLane returns a new free lane. @@ -26,7 +26,7 @@ func NewFreeLane( txPriority constructor.TxPriority[string], matchFn block.MatchHandler, ) *FreeLane { - lane := constructor.NewLaneConstructor[string]( + lane := constructor.NewLaneConstructor( cfg, LaneName, constructor.NewMempool[string]( diff --git a/lanes/mev/lane.go b/lanes/mev/lane.go index e2b405b..9e1e5f8 100644 --- a/lanes/mev/lane.go +++ b/lanes/mev/lane.go @@ -34,7 +34,7 @@ type ( MEVLane struct { // LaneConfig defines the base lane configuration. - *constructor.LaneConstructor[string] + *constructor.LaneConstructor // Factory defines the API/functionality which is responsible for determining // if a transaction is a bid transaction and how to extract relevant @@ -49,7 +49,7 @@ func NewMEVLane( factory Factory, ) *MEVLane { lane := &MEVLane{ - LaneConstructor: constructor.NewLaneConstructor[string]( + LaneConstructor: constructor.NewLaneConstructor( cfg, LaneName, constructor.NewMempool[string]( From f9b6b32ff12b3c9026ab5a22f8c5b688ebaa8de0 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 16:46:26 -0400 Subject: [PATCH 21/41] emojify ser --- block/constructor/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/block/constructor/README.md b/block/constructor/README.md index 3c3cea5..2f993a2 100644 --- a/block/constructor/README.md +++ b/block/constructor/README.md @@ -36,7 +36,7 @@ lane and the ordering rules of the other lanes). handler for processing transactions that are included in block proposals. -### 1. Lane Config 📝 +### 1. 📝 Lane Config The lane config (`LaneConfig`) is a simple configuration object that defines the desired amount of block space the lane should @@ -125,7 +125,7 @@ to the free lane will be processed by the default lane (which accepts all transactions by default). -### 2. LaneMempool 🗄️ +### 2. 🗄️ LaneMempool This is the data structure that is responsible for storing transactions as they are being verified and are waiting to be included in proposals. `block/constructor/mempool.go` @@ -236,7 +236,7 @@ lane := constructor.NewLaneConstructor( ) ``` -### 3. MatchHandler 🤝 +### 3. 🤝 MatchHandler `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. @@ -328,7 +328,7 @@ than the previous steps and is a all or nothing approach. This means that if you implement any of the handlers, you must implement all of them in most cases. If you do not implement all of them, the lane may have unintended behavior. -### 4. PrepareLaneHandler +### 4. 🛠️ PrepareLaneHandler The `PrepareLaneHandler` is an optional field you can set on the lane constructor. This handler is responsible for the transaction selection logic when a new proposal @@ -374,7 +374,7 @@ customLane := constructor.NewCustomLane( customLane.SetPrepareLaneHandler(customlane.PrepareLaneHandler()) ``` -### 5. CheckOrderHandler +### 5. ✅ CheckOrderHandler The `CheckOrderHandler` is an optional field you can set on the lane constructor. This handler is responsible for verifying the ordering of the transactions in the @@ -421,7 +421,7 @@ customLane.SetCheckOrderHandler(customlane.CheckOrderHandler()) ``` -### 6. ProcessLaneHandler +### 6. 🆗 ProcessLaneHandler The `ProcessLaneHandler` is an optional field you can set on the lane constructor. This handler is responsible for verifying the transactions in the block proposal From bf4e3b8af2c1e352a805af0a6d1c52ed81ba7675 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 17:46:05 -0400 Subject: [PATCH 22/41] abci done --- abci/README.md | 202 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) diff --git a/abci/README.md b/abci/README.md index e69de29..50a6c34 100644 --- a/abci/README.md +++ b/abci/README.md @@ -0,0 +1,202 @@ +# Block SDK Proposals + +> 🤓 Learn and read all about how proposals are constructed and verified using +> the Block SDK + +## 📖 Overview + +The Block SDK is a framework for building smarter blocks. The Block SDK is built +harnessing the power of ABCI++ which is a new ABCI implementation that allows +for more complex and expressive applications to be built on top of the Cosmos SDK. +The process of building and verifiying proposals can be broken down into two +distinct parts: + +1. Preparing a proposal during `PrepareProposal`. +2. Processing a proposal during `ProcessProposal`. + +The Block SDK provides a framework for building and verifying proposals by +segmenting a single block into multiple lanes. Each lane can be responsible for +proposing and verifying specific types of transaction. The Block SDK provides +a default implementation of a lane that can be used to build and verify proposals +similar to how they are built and verified in the Cosmos SDK today while also +providing a framework for building more complex lanes that can be used to build +and verify much more complex proposals. + +## 🤔 How does it work + +### 🔁 Transaction Lifecycle + +The best way to understand how lanes work is to first understand the lifecycle +of a transaction. A transaction begins its lifecycle when it is first signed and +broadcasted to a chain. After it is broadcasted to a validator, it will be checked +in `CheckTx` by the base application. If the transaction is valid, it will be +inserted into the applications mempool. + +The transaction then waits in the mempool until a new block needs to be proposed. +When a new block needs to be proposed, the application will call `PrepareProposal` +(which is a new ABCI++ addition) to request a new block from the current +proposer. The proposer will look at what transactions currently waiting to +be included in a block by looking at their mempool. The proposer will then +iteratively select transactions until the block is full. The proposer will then +send the block to other validators in the network. + +When a validator receives a proposed block, the validator will first want to +verify the contents of the block before signing off on it. The validator will +call `ProcessProposal` to verify the contents of the block. If the block is +valid, the validator will sign off on the block and broadcast their vote to the +network. If the block is invalid, the validator will reject the block. Once a +block is accepted by the network, it is committed and the transactions that +were included in the block are removed from the validator's mempool (as they no +longer need to be considered). + +### 🛣️ Lane Lifecycle + +After a transaction is verified in `CheckTx`, it will attempt to be inserted +into the `LanedMempool`. A `LanedMempool` is composed of several distinct `Lanes` +that have the ability to store their own transactions. The `LanedMempool` will +insert the transaction into all lanes that will accept it. The criteria for +whether a lane will accept a transaction is defined by the lane's +`MatchHandler`. The default implementation of a `MatchHandler` will accept all transactions. + + +When a new block is proposed, the `PrepareProposalHandler` will iteratively call +`PrepareLane` on each lane (in the order in which they are defined in the +`LanedMempool`). The `PrepareLane` method is anaolgous to `PrepareProposal`. Calling +`PrepareLane` on a lane will trigger the lane to reap transactions from its mempool +and add them to the proposal (given they are valid respecting the verification rules +of the lane). + +When proposals need to be verified in `ProcessProposal`, the `ProcessProposalHandler` +defined in `abci/abci.go` will call `ProcessLane` on each lane in the same order +as they were called in the `PrepareProposalHandler`. Each subsequent call to +`ProcessLane` will filter out transactions that belong to previous lanes. A given +lane's `ProcessLane` will only verify transactions that belong to that lane. + +> **Scenario** +> +> Let's say we have a `LanedMempool` composed of two lanes: `LaneA` and `LaneB`. +> `LaneA` is defined first in the `LanedMempool` and `LaneB` is defined second. +> `LaneA` contains transactions `Tx1` and `Tx2` and `LaneB` contains transactions +> `Tx3` and `Tx4`. + + +When a new block needs to be proposed, the `PrepareProposalHandler` will call +`PrepareLane` on `LaneA` first and `LaneB` second. When `PrepareLane` is called +on `LaneA`, `LaneA` will reap transactions from its mempool and add them to the +proposal. Same applies for `LaneB`. Say `LaneA` reaps transactions `Tx1` and `Tx2` +and `LaneB` reaps transactions `Tx3` and `Tx4`. This gives us a proposal composed +of the following: + +* `Tx1`, `Tx2`, `Tx3`, `Tx4` + +When the `ProcessProposalHandler` is called, it will call `ProcessLane` on `LaneA` +with the proposal composed of `Tx1`, `Tx2`, `Tx3`, and `Tx4`. `LaneA` will then +verify `Tx1` and `Tx2` and return the remaining transactions - `Tx3` and `Tx4`. +The `ProcessProposalHandler` will then call `ProcessLane` on `LaneB` with the +remaining transactions - `Tx3` and `Tx4`. `LaneB` will then verify `Tx3` and `Tx4` +and return no remaining transactions. + +## 🏗️ Setup + +> **Note** +> +> For a more in depth example of how to use the Block SDK, check out our +> example application in `block-sdk/tests/app/app.go`. + +### 📦 Dependencies + +The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently +compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. + +### 📥 Installation + +To install the Block SDK, run the following command: + +```bash +go get github.com/skip-mev/block-sdk/abci +``` + +### 📚 Usage + +First determine the set of lanes that you want to use in your application. The available +lanes can be found in our **Lane App Store** in `block-sdk/lanes`. In your base +application, you will need to create a `LanedMempool` composed of the lanes that +you want to use. You will also need to create a `PrepareProposalHandler` and a +`ProcessProposalHandler` that will be responsible for preparing and processing +proposals respectively. + +```golang +// 1. Create the lanes. +// +// 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. +// +// For more information on how to utilize the LaneConfig please +// visit the README in block-sdk/block/constructor. +// +// MEV lane hosts an action at the top of the block. +mevConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, +} +mevLane := mev.NewMEVLane( + mevConfig, + mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), +) + +// Free lane allows transactions to be included in the next block for free. +freeConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, +} +freeLane := free.NewFreeLane( + freeConfig, + constructor.DefaultTxPriority(), + free.DefaultMatchHandler(), +) + +// Default lane accepts all other transactions. +defaultConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, +} +defaultLane := base.NewDefaultLane(defaultConfig) + +// Set the lanes into the mempool. +lanes := []block.Lane{ + mevLane, + freeLane, + defaultLane, +} +mempool := block.NewLanedMempool(app.Logger(), true, lanes...) +app.App.SetMempool(mempool) + +... + +anteHandler := NewAnteHandler(options) + +// Set the lane ante handlers on the lanes. +for _, lane := range lanes { + lane.SetAnteHandler(anteHandler) +} +app.App.SetAnteHandler(anteHandler) + +// Set the abci handlers on base app +proposalHandler := abci.NewProposalHandler( + app.Logger(), + app.TxConfig().TxDecoder(), + lanes, +) +app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler()) +app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) +``` \ No newline at end of file From af2b5226f315295b548678693933366dad499b33 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 18:01:49 -0400 Subject: [PATCH 23/41] rename of constructor to base --- README.md | 2 +- abci/README.md | 4 +- abci/abci_test.go | 64 +++++++++---------- block/{constructor => base}/README.md | 20 +++--- block/{constructor => base}/abci.go | 10 +-- block/{constructor => base}/config.go | 2 +- block/{constructor => base}/handlers.go | 8 +-- block/{constructor => base}/lane.go | 44 ++++++------- block/{constructor => base}/mempool.go | 2 +- block/{constructor => base}/priority_nonce.go | 2 +- block/mempool_test.go | 16 ++--- lanes/free/lane.go | 14 ++-- lanes/mev/check_tx.go | 2 +- lanes/mev/lane.go | 10 +-- lanes/mev/mempool.go | 6 +- lanes/{base => standard}/README.md | 0 lanes/{base => standard}/abci_test.go | 12 ++-- lanes/{base => standard}/base_test.go | 2 +- lanes/{base => standard}/lane.go | 28 ++++---- lanes/{base => standard}/mempool_test.go | 16 ++--- tests/app/app.go | 14 ++-- x/builder/ante/ante_test.go | 16 ++--- 22 files changed, 147 insertions(+), 147 deletions(-) rename block/{constructor => base}/README.md (96%) rename block/{constructor => base}/abci.go (85%) rename block/{constructor => base}/config.go (99%) rename block/{constructor => base}/handlers.go (94%) rename block/{constructor => base}/lane.go (81%) rename block/{constructor => base}/mempool.go (99%) rename block/{constructor => base}/priority_nonce.go (99%) rename lanes/{base => standard}/README.md (100%) rename lanes/{base => standard}/abci_test.go (98%) rename lanes/{base => standard}/base_test.go (96%) rename lanes/{base => standard}/lane.go (50%) rename lanes/{base => standard}/mempool_test.go (90%) diff --git a/README.md b/README.md index 500b3a0..6ef7d94 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ $ go install github.com/skip-mev/pob freeLane, }, } - defaultLane := base.NewDefaultLane(defaultConfig) + defaultLane := base.NewStandardLane(defaultConfig) // Set the lanes into the mempool. lanes := []block.Lane{ diff --git a/abci/README.md b/abci/README.md index 50a6c34..48f006e 100644 --- a/abci/README.md +++ b/abci/README.md @@ -133,7 +133,7 @@ proposals respectively. // transactions to bid for inclusion at the top of the next block. // // For more information on how to utilize the LaneConfig please -// visit the README in block-sdk/block/constructor. +// visit the README in block-sdk/block/base. // // MEV lane hosts an action at the top of the block. mevConfig := constructor.LaneConfig{ @@ -170,7 +170,7 @@ defaultConfig := constructor.LaneConfig{ MaxBlockSpace: math.LegacyZeroDec(), MaxTxs: 0, } -defaultLane := base.NewDefaultLane(defaultConfig) +defaultLane := base.NewStandardLane(defaultConfig) // Set the lanes into the mempool. lanes := []block.Lane{ diff --git a/abci/abci_test.go b/abci/abci_test.go index 36ff22b..090a167 100644 --- a/abci/abci_test.go +++ b/abci/abci_test.go @@ -15,10 +15,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/skip-mev/pob/abci" "github.com/skip-mev/pob/block" - "github.com/skip-mev/pob/block/constructor" - "github.com/skip-mev/pob/lanes/base" + "github.com/skip-mev/pob/block/base" "github.com/skip-mev/pob/lanes/free" "github.com/skip-mev/pob/lanes/mev" + "github.com/skip-mev/pob/lanes/standard" testutils "github.com/skip-mev/pob/testutils" "github.com/stretchr/testify/suite" ) @@ -55,7 +55,7 @@ func (s *ProposalsTestSuite) SetupTest() { func (s *ProposalsTestSuite) TestPrepareProposal() { s.Run("can prepare a proposal with no transactions", func() { // Set up the default lane with no transactions - defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("1"), nil) + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("1"), nil) proposalHandler := s.setUpProposalHandlers([]block.Lane{defaultLane}).PrepareProposalHandler() @@ -78,7 +78,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(err) // Set up the default lane - defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx: true}) + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx: true}) s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) proposalHandler := s.setUpProposalHandlers([]block.Lane{defaultLane}).PrepareProposalHandler() @@ -115,7 +115,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(err) // Set up the default lane with both transactions passing - defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx1: true, tx2: true}) + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx1: true, tx2: true}) s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx1)) s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx2)) @@ -153,7 +153,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(err) // Set up the default lane with both transactions passing - defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx1: true, tx2: false}) + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx1: true, tx2: false}) s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx1)) s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx2)) @@ -169,7 +169,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Run("can build a proposal an empty proposal with multiple lanes", func() { mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), nil) - defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), nil) + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.5"), nil) proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).PrepareProposalHandler() @@ -199,7 +199,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { }) s.Require().NoError(mevLane.Insert(sdk.Context{}, tx)) - defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), nil) + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.5"), nil) proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).PrepareProposalHandler() @@ -232,7 +232,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(mevLane.Insert(sdk.Context{}, tx)) // Set up the default lane with the bid tx and the bundled tx - defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ tx: true, bundleTxs[0]: true, }) @@ -270,7 +270,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(mevLane.Insert(sdk.Context{}, tx)) // Set up the default lane with the bid tx and the bundled tx - defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{ // Even though this passes it should not include it in the proposal because it is in the ignore list tx: true, bundleTxs[0]: true, @@ -309,7 +309,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(mevLane.Insert(sdk.Context{}, tx)) // Set up the default lane with the bid tx and the bundled tx - defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ // Even though this passes it should not include it in the proposal because it is in the ignore list tx: true, bundleTxs[0]: true, @@ -343,7 +343,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { ) s.Require().NoError(err) - defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ freeTx: true, }) s.Require().NoError(defaultLane.Insert(sdk.Context{}, freeTx)) @@ -409,7 +409,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { }) mevLane.Insert(sdk.Context{}, tx) - defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ normalTx: true, }) defaultLane.Insert(sdk.Context{}, normalTx) @@ -445,7 +445,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() { ) s.Require().NoError(err) - defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ tx: true, }) s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) @@ -478,7 +478,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() { ) s.Require().NoError(err) - defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ tx: true, }) s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) @@ -512,7 +512,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() { ) s.Require().NoError(err) - defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ tx: true, }) s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) @@ -546,7 +546,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() { ) s.Require().NoError(err) - defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{ tx: true, }) s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) @@ -571,7 +571,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { s.Run("can process a valid empty proposal", func() { mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{}) freeLane := s.setUpFreeLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{}) - defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{}) + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{}) proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, freeLane, defaultLane}).ProcessProposalHandler() @@ -584,7 +584,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { s.Run("rejects a proposal with bad txs", func() { mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{}) freeLane := s.setUpFreeLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{}) - defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{}) + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{}) proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, freeLane, defaultLane}).ProcessProposalHandler() @@ -636,7 +636,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { s.Require().NoError(err) // Set up the default lane - defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx: true}) + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx: true}) s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) proposalHandler := s.setUpProposalHandlers([]block.Lane{defaultLane}).ProcessProposalHandler() @@ -677,7 +677,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { s.Require().NoError(err) // Set up the default lane - defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), nil) + defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.5"), nil) defaultLane.SetProcessLaneHandler(block.NoOpProcessLaneHandler()) // Set up the TOB lane @@ -724,8 +724,8 @@ func (s *ProposalsTestSuite) setUpAnteHandler(expectedExecution map[sdk.Tx]bool) return anteHandler } -func (s *ProposalsTestSuite) setUpDefaultLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *base.DefaultLane { - cfg := constructor.LaneConfig{ +func (s *ProposalsTestSuite) setUpStandardLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *standard.StandardLane { + cfg := base.LaneConfig{ Logger: log.NewTestLogger(s.T()), TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), TxDecoder: s.encodingConfig.TxConfig.TxDecoder(), @@ -733,11 +733,11 @@ func (s *ProposalsTestSuite) setUpDefaultLane(maxBlockSpace math.LegacyDec, expe MaxBlockSpace: maxBlockSpace, } - return base.NewDefaultLane(cfg) + return standard.NewStandardLane(cfg) } func (s *ProposalsTestSuite) setUpTOBLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *mev.MEVLane { - cfg := constructor.LaneConfig{ + cfg := base.LaneConfig{ Logger: log.NewTestLogger(s.T()), TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), TxDecoder: s.encodingConfig.TxConfig.TxDecoder(), @@ -749,7 +749,7 @@ func (s *ProposalsTestSuite) setUpTOBLane(maxBlockSpace math.LegacyDec, expected } func (s *ProposalsTestSuite) setUpFreeLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *free.FreeLane { - cfg := constructor.LaneConfig{ + cfg := base.LaneConfig{ Logger: log.NewTestLogger(s.T()), TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), TxDecoder: s.encodingConfig.TxConfig.TxDecoder(), @@ -757,22 +757,22 @@ func (s *ProposalsTestSuite) setUpFreeLane(maxBlockSpace math.LegacyDec, expecte MaxBlockSpace: maxBlockSpace, } - return free.NewFreeLane(cfg, constructor.DefaultTxPriority(), free.DefaultMatchHandler()) + return free.NewFreeLane(cfg, base.DefaultTxPriority(), free.DefaultMatchHandler()) } -func (s *ProposalsTestSuite) setUpPanicLane(maxBlockSpace math.LegacyDec) *constructor.LaneConstructor { - cfg := constructor.LaneConfig{ +func (s *ProposalsTestSuite) setUpPanicLane(maxBlockSpace math.LegacyDec) *base.BaseLane { + cfg := base.LaneConfig{ Logger: log.NewTestLogger(s.T()), TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), TxDecoder: s.encodingConfig.TxConfig.TxDecoder(), MaxBlockSpace: maxBlockSpace, } - lane := constructor.NewLaneConstructor( + lane := base.NewBaseLane( cfg, "panic", - constructor.NewMempool[string](constructor.DefaultTxPriority(), cfg.TxEncoder, 0), - constructor.DefaultMatchHandler(), + base.NewMempool[string](base.DefaultTxPriority(), cfg.TxEncoder, 0), + base.DefaultMatchHandler(), ) lane.SetPrepareLaneHandler(block.PanicPrepareLaneHandler()) diff --git a/block/constructor/README.md b/block/base/README.md similarity index 96% rename from block/constructor/README.md rename to block/base/README.md index 2f993a2..51d10ae 100644 --- a/block/constructor/README.md +++ b/block/base/README.md @@ -1,10 +1,10 @@ -# 🎨 Lane Constructor +# 🎨 Base Lane -> 🏗️ Build your own lane in less than 10 minutes using the Lane Constructor +> 🏗️ Build your own lane in less than 10 minutes using the Base Lane ## 💡 Overview -The Lane Constructor is a generic implementation of a lane. It comes out of the +The Base Lane is a generic implementation of a lane. It comes out of the box with default implementations for all the required interfaces. It is meant to be used as a starting point for building your own lane. @@ -13,7 +13,7 @@ be used as a starting point for building your own lane. > **Default Implementations** > > There are default implementations for all of the below which can be found in -> the `block/constructor` package. It is highly recommended that developers overview +> the `block/base` package. It is highly recommended that developers overview > the default implementations before building their own lane. There are **three** critical components to building a custom lane using the lane @@ -128,7 +128,7 @@ transactions by default). ### 2. 🗄️ LaneMempool This is the data structure that is responsible for storing transactions -as they are being verified and are waiting to be included in proposals. `block/constructor/mempool.go` +as they are being verified and are waiting to be included in proposals. `block/base/mempool.go` provides an out-of-the-box implementation that should be used as a starting point for building out the mempool and should cover most use cases. To utilize the mempool, you must implement a `TxPriority[C]` struct that does the @@ -143,7 +143,7 @@ should return 1, otherwise the method should return 0. * Implements a `MinValue` method that returns the minimum priority value that a transaction can have. -The default implementation can be found in `block/constructor/mempool.go`. What +The default implementation can be found in `block/base/mempool.go`. What if we wanted to prioritize transactions by the amount they have staked on a chain? Well we could do something like the following: @@ -228,7 +228,7 @@ mempool := constructor.NewMempool[string]( ) // Initialize your lane with the mempool -lane := constructor.NewLaneConstructor( +lane := constructor.NewBaseLane( laneCfg, LaneName, mempool, @@ -240,7 +240,7 @@ lane := constructor.NewLaneConstructor( `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. -The default implementation can be found in `block/constructor/handlers.go`. +The default implementation can be found in `block/base/handlers.go`. The match handler can be as custom as desired. Following the example above, if we wanted to make a lane that only accepts transactions if they have a large @@ -298,7 +298,7 @@ mempool := constructor.NewMempool[string]( ) // Initialize your lane with the mempool -lane := constructor.NewLaneConstructor( +lane := constructor.NewBaseLane( cfg, LaneName, mempool, @@ -319,7 +319,7 @@ have a custom lane that only accepts transactions that match a custom criteria. ### [OPTIONAL] Steps 4-6 The remaining steps walk through the process of creating custom block -building/verification logic. The default implementation found in `block/constructor/handlers.go` +building/verification logic. The default implementation found in `block/base/handlers.go` should fit most use cases. Please reference that file for more details on the default implementation and whether it fits your use case. diff --git a/block/constructor/abci.go b/block/base/abci.go similarity index 85% rename from block/constructor/abci.go rename to block/base/abci.go index cc80fea..4eb138a 100644 --- a/block/constructor/abci.go +++ b/block/base/abci.go @@ -1,4 +1,4 @@ -package constructor +package base import ( sdk "github.com/cosmos/cosmos-sdk/types" @@ -10,7 +10,7 @@ import ( // 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( +func (l *BaseLane) PrepareLane( ctx sdk.Context, proposal block.BlockProposal, maxTxBytes int64, @@ -40,7 +40,7 @@ func (l *LaneConstructor) PrepareLane( // 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 { +func (l *BaseLane) CheckOrder(ctx sdk.Context, txs []sdk.Tx) error { return l.checkOrderHandler(ctx, txs) } @@ -48,7 +48,7 @@ func (l *LaneConstructor) CheckOrder(ctx sdk.Context, txs []sdk.Tx) error { // 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 block.ProcessLanesHandler) (sdk.Context, error) { +func (l *BaseLane) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next block.ProcessLanesHandler) (sdk.Context, error) { remainingTxs, err := l.processLaneHandler(ctx, txs) if err != nil { return ctx, err @@ -59,7 +59,7 @@ func (l *LaneConstructor) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next block. // 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) { +func (l *BaseLane) AnteVerifyTx(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) { if l.cfg.AnteHandler != nil { return l.cfg.AnteHandler(ctx, tx, simulate) } diff --git a/block/constructor/config.go b/block/base/config.go similarity index 99% rename from block/constructor/config.go rename to block/base/config.go index a52f3c5..61bf266 100644 --- a/block/constructor/config.go +++ b/block/base/config.go @@ -1,4 +1,4 @@ -package constructor +package base import ( "fmt" diff --git a/block/constructor/handlers.go b/block/base/handlers.go similarity index 94% rename from block/constructor/handlers.go rename to block/base/handlers.go index a927d79..3782d36 100644 --- a/block/constructor/handlers.go +++ b/block/base/handlers.go @@ -1,4 +1,4 @@ -package constructor +package base import ( "fmt" @@ -12,7 +12,7 @@ import ( // 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() block.PrepareLaneHandler { +func (l *BaseLane) DefaultPrepareLaneHandler() block.PrepareLaneHandler { return func(ctx sdk.Context, proposal block.BlockProposal, maxTxBytes int64) ([][]byte, []sdk.Tx, error) { var ( totalSize int64 @@ -96,7 +96,7 @@ func (l *LaneConstructor) DefaultPrepareLaneHandler() block.PrepareLaneHandler { // 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() block.ProcessLaneHandler { +func (l *BaseLane) DefaultProcessLaneHandler() block.ProcessLaneHandler { return func(ctx sdk.Context, txs []sdk.Tx) ([]sdk.Tx, error) { var err error @@ -123,7 +123,7 @@ func (l *LaneConstructor) DefaultProcessLaneHandler() block.ProcessLaneHandler { // lane. // 2. Transactions that belong to other lanes cannot be interleaved with transactions that // belong to this lane. -func (l *LaneConstructor) DefaultCheckOrderHandler() block.CheckOrderHandler { +func (l *BaseLane) DefaultCheckOrderHandler() block.CheckOrderHandler { return func(ctx sdk.Context, txs []sdk.Tx) error { seenOtherLaneTx := false diff --git a/block/constructor/lane.go b/block/base/lane.go similarity index 81% rename from block/constructor/lane.go rename to block/base/lane.go index 2cf49e6..8fa5924 100644 --- a/block/constructor/lane.go +++ b/block/base/lane.go @@ -1,4 +1,4 @@ -package constructor +package base import ( "fmt" @@ -9,14 +9,14 @@ import ( "github.com/skip-mev/pob/block" ) -var _ block.Lane = (*LaneConstructor)(nil) +var _ block.Lane = (*BaseLane)(nil) -// LaneConstructor is a generic implementation of a lane. It is meant to be used +// BaseLane 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 { +// handlers or construct your own that you pass into the base/setters. +type BaseLane 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. @@ -48,16 +48,16 @@ type LaneConstructor struct { processLaneHandler block.ProcessLaneHandler } -// NewLaneConstructor returns a new lane constructor. When creating this lane, the type +// NewBaseLane returns a new lane base. 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( +func NewBaseLane( cfg LaneConfig, laneName string, laneMempool block.LaneMempool, matchHandlerFn block.MatchHandler, -) *LaneConstructor { - lane := &LaneConstructor{ +) *BaseLane { + lane := &BaseLane{ cfg: cfg, laneName: laneName, LaneMempool: laneMempool, @@ -73,7 +73,7 @@ func NewLaneConstructor( // 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 { +func (l *BaseLane) ValidateBasic() error { if err := l.cfg.ValidateBasic(); err != nil { return err } @@ -108,7 +108,7 @@ func (l *LaneConstructor) ValidateBasic() error { // 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 block.PrepareLaneHandler) { +func (l *BaseLane) SetPrepareLaneHandler(prepareLaneHandler block.PrepareLaneHandler) { if prepareLaneHandler == nil { panic("prepare lane handler cannot be nil") } @@ -120,7 +120,7 @@ func (l *LaneConstructor) SetPrepareLaneHandler(prepareLaneHandler block.Prepare // 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 block.ProcessLaneHandler) { +func (l *BaseLane) SetProcessLaneHandler(processLaneHandler block.ProcessLaneHandler) { if processLaneHandler == nil { panic("process lane handler cannot be nil") } @@ -132,7 +132,7 @@ func (l *LaneConstructor) SetProcessLaneHandler(processLaneHandler block.Process // 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 block.CheckOrderHandler) { +func (l *BaseLane) SetCheckOrderHandler(checkOrderHandler block.CheckOrderHandler) { if checkOrderHandler == nil { panic("check order handler cannot be nil") } @@ -144,14 +144,14 @@ func (l *LaneConstructor) SetCheckOrderHandler(checkOrderHandler block.CheckOrde // 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 { +func (l *BaseLane) 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 { +func (l *BaseLane) CheckIgnoreList(ctx sdk.Context, tx sdk.Tx) bool { for _, lane := range l.cfg.IgnoreList { if lane.Match(ctx, tx) { return true @@ -162,38 +162,38 @@ func (l *LaneConstructor) CheckIgnoreList(ctx sdk.Context, tx sdk.Tx) bool { } // Name returns the name of the lane. -func (l *LaneConstructor) Name() string { +func (l *BaseLane) 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 []block.Lane) { +func (l *BaseLane) SetIgnoreList(lanes []block.Lane) { l.cfg.IgnoreList = lanes } // SetAnteHandler sets the ante handler for the lane. -func (l *LaneConstructor) SetAnteHandler(anteHandler sdk.AnteHandler) { +func (l *BaseLane) SetAnteHandler(anteHandler sdk.AnteHandler) { l.cfg.AnteHandler = anteHandler } // Logger returns the logger for the lane. -func (l *LaneConstructor) Logger() log.Logger { +func (l *BaseLane) Logger() log.Logger { return l.cfg.Logger } // TxDecoder returns the tx decoder for the lane. -func (l *LaneConstructor) TxDecoder() sdk.TxDecoder { +func (l *BaseLane) TxDecoder() sdk.TxDecoder { return l.cfg.TxDecoder } // TxEncoder returns the tx encoder for the lane. -func (l *LaneConstructor) TxEncoder() sdk.TxEncoder { +func (l *BaseLane) 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 { +func (l *BaseLane) GetMaxBlockSpace() math.LegacyDec { return l.cfg.MaxBlockSpace } diff --git a/block/constructor/mempool.go b/block/base/mempool.go similarity index 99% rename from block/constructor/mempool.go rename to block/base/mempool.go index 8bcb098..3efe9ff 100644 --- a/block/constructor/mempool.go +++ b/block/base/mempool.go @@ -1,4 +1,4 @@ -package constructor +package base import ( "context" diff --git a/block/constructor/priority_nonce.go b/block/base/priority_nonce.go similarity index 99% rename from block/constructor/priority_nonce.go rename to block/base/priority_nonce.go index c5ee7bd..1b32019 100644 --- a/block/constructor/priority_nonce.go +++ b/block/base/priority_nonce.go @@ -1,4 +1,4 @@ -package constructor +package base // ------------------------------------------------------------------------------ // // ------------------------------------------------------------------------------ // diff --git a/block/mempool_test.go b/block/mempool_test.go index 6c4269c..048c562 100644 --- a/block/mempool_test.go +++ b/block/mempool_test.go @@ -11,10 +11,10 @@ import ( "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/skip-mev/pob/block" - "github.com/skip-mev/pob/block/constructor" - "github.com/skip-mev/pob/lanes/base" + "github.com/skip-mev/pob/block/base" "github.com/skip-mev/pob/lanes/free" "github.com/skip-mev/pob/lanes/mev" + "github.com/skip-mev/pob/lanes/standard" testutils "github.com/skip-mev/pob/testutils" buildertypes "github.com/skip-mev/pob/x/builder/types" "github.com/stretchr/testify/suite" @@ -29,7 +29,7 @@ type BlockBusterTestSuite struct { // Define all of the lanes utilized in the test suite mevLane *mev.MEVLane - baseLane *base.DefaultLane + baseLane *standard.StandardLane freeLane *free.FreeLane gasTokenDenom string @@ -58,7 +58,7 @@ func (suite *BlockBusterTestSuite) SetupTest() { // // TOB lane set up suite.gasTokenDenom = "stake" - mevConfig := constructor.LaneConfig{ + mevConfig := base.LaneConfig{ Logger: log.NewNopLogger(), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), @@ -71,7 +71,7 @@ func (suite *BlockBusterTestSuite) SetupTest() { ) // Free lane set up - freeConfig := constructor.LaneConfig{ + freeConfig := base.LaneConfig{ Logger: log.NewNopLogger(), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), @@ -80,19 +80,19 @@ func (suite *BlockBusterTestSuite) SetupTest() { } suite.freeLane = free.NewFreeLane( freeConfig, - constructor.DefaultTxPriority(), + base.DefaultTxPriority(), free.DefaultMatchHandler(), ) // Base lane set up - baseConfig := constructor.LaneConfig{ + baseConfig := base.LaneConfig{ Logger: log.NewNopLogger(), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), AnteHandler: nil, MaxBlockSpace: math.LegacyZeroDec(), } - suite.baseLane = base.NewDefaultLane( + suite.baseLane = standard.NewStandardLane( baseConfig, ) diff --git a/lanes/free/lane.go b/lanes/free/lane.go index ba48d89..358b089 100644 --- a/lanes/free/lane.go +++ b/lanes/free/lane.go @@ -4,7 +4,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/skip-mev/pob/block" - "github.com/skip-mev/pob/block/constructor" + "github.com/skip-mev/pob/block/base" ) const ( @@ -17,19 +17,19 @@ var _ block.Lane = (*FreeLane)(nil) // FreeLane defines the lane that is responsible for processing free transactions. // By default, transactions that are staking related are considered free. type FreeLane struct { - *constructor.LaneConstructor + *base.BaseLane } // NewFreeLane returns a new free lane. func NewFreeLane( - cfg constructor.LaneConfig, - txPriority constructor.TxPriority[string], + cfg base.LaneConfig, + txPriority base.TxPriority[string], matchFn block.MatchHandler, ) *FreeLane { - lane := constructor.NewLaneConstructor( + lane := base.NewBaseLane( cfg, LaneName, - constructor.NewMempool[string]( + base.NewMempool[string]( txPriority, cfg.TxEncoder, cfg.MaxTxs, @@ -38,7 +38,7 @@ func NewFreeLane( ) return &FreeLane{ - LaneConstructor: lane, + BaseLane: lane, } } diff --git a/lanes/mev/check_tx.go b/lanes/mev/check_tx.go index fc5cafe..ab18630 100644 --- a/lanes/mev/check_tx.go +++ b/lanes/mev/check_tx.go @@ -63,7 +63,7 @@ type ( } ) -// NewCheckTxHandler is a constructor for CheckTxHandler. +// NewCheckTxHandler is a base for CheckTxHandler. func NewCheckTxHandler( baseApp BaseApp, txDecoder sdk.TxDecoder, diff --git a/lanes/mev/lane.go b/lanes/mev/lane.go index 9e1e5f8..35c00d0 100644 --- a/lanes/mev/lane.go +++ b/lanes/mev/lane.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/skip-mev/pob/block" - "github.com/skip-mev/pob/block/constructor" + "github.com/skip-mev/pob/block/base" ) const ( @@ -34,7 +34,7 @@ type ( MEVLane struct { // LaneConfig defines the base lane configuration. - *constructor.LaneConstructor + *base.BaseLane // Factory defines the API/functionality which is responsible for determining // if a transaction is a bid transaction and how to extract relevant @@ -45,14 +45,14 @@ type ( // NewMEVLane returns a new TOB lane. func NewMEVLane( - cfg constructor.LaneConfig, + cfg base.LaneConfig, factory Factory, ) *MEVLane { lane := &MEVLane{ - LaneConstructor: constructor.NewLaneConstructor( + BaseLane: base.NewBaseLane( cfg, LaneName, - constructor.NewMempool[string]( + base.NewMempool[string]( TxPriority(factory), cfg.TxEncoder, cfg.MaxTxs, diff --git a/lanes/mev/mempool.go b/lanes/mev/mempool.go index d130770..68946df 100644 --- a/lanes/mev/mempool.go +++ b/lanes/mev/mempool.go @@ -4,13 +4,13 @@ import ( "context" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/skip-mev/pob/block/constructor" + "github.com/skip-mev/pob/block/base" ) // TxPriority returns a TxPriority over mev lane transactions only. It // is to be used in the mev index only. -func TxPriority(config Factory) constructor.TxPriority[string] { - return constructor.TxPriority[string]{ +func TxPriority(config Factory) base.TxPriority[string] { + return base.TxPriority[string]{ GetTxPriority: func(goCtx context.Context, tx sdk.Tx) string { bidInfo, err := config.GetAuctionBidInfo(tx) if err != nil { diff --git a/lanes/base/README.md b/lanes/standard/README.md similarity index 100% rename from lanes/base/README.md rename to lanes/standard/README.md diff --git a/lanes/base/abci_test.go b/lanes/standard/abci_test.go similarity index 98% rename from lanes/base/abci_test.go rename to lanes/standard/abci_test.go index 18f1ffe..e006e62 100644 --- a/lanes/base/abci_test.go +++ b/lanes/standard/abci_test.go @@ -1,4 +1,4 @@ -package base_test +package standard_test import ( "crypto/sha256" @@ -9,9 +9,9 @@ import ( "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/skip-mev/pob/block" - "github.com/skip-mev/pob/block/constructor" + "github.com/skip-mev/pob/block/base" "github.com/skip-mev/pob/block/utils/mocks" - "github.com/skip-mev/pob/lanes/base" + "github.com/skip-mev/pob/lanes/standard" testutils "github.com/skip-mev/pob/testutils" ) @@ -502,8 +502,8 @@ func (s *BaseTestSuite) TestCheckOrder() { func (s *BaseTestSuite) initLane( maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool, -) *base.DefaultLane { - config := constructor.NewLaneConfig( +) *standard.StandardLane { + config := base.NewLaneConfig( log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), s.encodingConfig.TxConfig.TxDecoder(), @@ -511,7 +511,7 @@ func (s *BaseTestSuite) initLane( maxBlockSpace, ) - return base.NewDefaultLane(config) + return standard.NewStandardLane(config) } func (s *BaseTestSuite) setUpAnteHandler(expectedExecution map[sdk.Tx]bool) sdk.AnteHandler { diff --git a/lanes/base/base_test.go b/lanes/standard/base_test.go similarity index 96% rename from lanes/base/base_test.go rename to lanes/standard/base_test.go index 7852dac..ac5b956 100644 --- a/lanes/base/base_test.go +++ b/lanes/standard/base_test.go @@ -1,4 +1,4 @@ -package base_test +package standard_test import ( "math/rand" diff --git a/lanes/base/lane.go b/lanes/standard/lane.go similarity index 50% rename from lanes/base/lane.go rename to lanes/standard/lane.go index 99d0e10..8230949 100644 --- a/lanes/base/lane.go +++ b/lanes/standard/lane.go @@ -1,8 +1,8 @@ -package base +package standard import ( "github.com/skip-mev/pob/block" - "github.com/skip-mev/pob/block/constructor" + "github.com/skip-mev/pob/block/base" ) const ( @@ -10,32 +10,32 @@ const ( LaneName = "default" ) -var _ block.Lane = (*DefaultLane)(nil) +var _ block.Lane = (*StandardLane)(nil) -// DefaultLane defines a default lane implementation. The default lane orders +// StandardLane defines a default lane implementation. The standard lane orders // 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 similiar fashion to how the // CometBFT/Tendermint consensus engine builds and verifies blocks pre SDK version // 0.47.0. -type DefaultLane struct { - *constructor.LaneConstructor +type StandardLane struct { + *base.BaseLane } -// NewDefaultLane returns a new default lane. -func NewDefaultLane(cfg constructor.LaneConfig) *DefaultLane { - lane := constructor.NewLaneConstructor( +// NewStandardLane returns a new default lane. +func NewStandardLane(cfg base.LaneConfig) *StandardLane { + lane := base.NewBaseLane( cfg, LaneName, - constructor.NewMempool[string]( - constructor.DefaultTxPriority(), + base.NewMempool[string]( + base.DefaultTxPriority(), cfg.TxEncoder, cfg.MaxTxs, ), - constructor.DefaultMatchHandler(), + base.DefaultMatchHandler(), ) - return &DefaultLane{ - LaneConstructor: lane, + return &StandardLane{ + BaseLane: lane, } } diff --git a/lanes/base/mempool_test.go b/lanes/standard/mempool_test.go similarity index 90% rename from lanes/base/mempool_test.go rename to lanes/standard/mempool_test.go index fef9ee1..b3052b5 100644 --- a/lanes/base/mempool_test.go +++ b/lanes/standard/mempool_test.go @@ -1,14 +1,14 @@ -package base_test +package standard_test import ( "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/skip-mev/pob/block/constructor" + "github.com/skip-mev/pob/block/base" testutils "github.com/skip-mev/pob/testutils" ) func (s *BaseTestSuite) TestGetTxPriority() { - txPriority := constructor.DefaultTxPriority() + txPriority := base.DefaultTxPriority() s.Run("should be able to get the priority off a normal transaction with fees", func() { tx, err := testutils.CreateRandomTx( @@ -56,7 +56,7 @@ func (s *BaseTestSuite) TestGetTxPriority() { } func (s *BaseTestSuite) TestCompareTxPriority() { - txPriority := constructor.DefaultTxPriority() + txPriority := base.DefaultTxPriority() s.Run("should return 0 when both priorities are nil", func() { a := sdk.NewCoin(s.gasTokenDenom, math.NewInt(0)).String() @@ -84,7 +84,7 @@ func (s *BaseTestSuite) TestCompareTxPriority() { } func (s *BaseTestSuite) TestInsert() { - mempool := constructor.NewMempool[string](constructor.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) + mempool := base.NewMempool[string](base.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) s.Run("should be able to insert a transaction", func() { tx, err := testutils.CreateRandomTx( @@ -136,7 +136,7 @@ func (s *BaseTestSuite) TestInsert() { } func (s *BaseTestSuite) TestRemove() { - mempool := constructor.NewMempool[string](constructor.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) + mempool := base.NewMempool[string](base.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) s.Run("should be able to remove a transaction", func() { tx, err := testutils.CreateRandomTx( @@ -174,7 +174,7 @@ func (s *BaseTestSuite) TestRemove() { func (s *BaseTestSuite) TestSelect() { s.Run("should be able to select transactions in the correct order", func() { - mempool := constructor.NewMempool[string](constructor.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) + mempool := base.NewMempool[string](base.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) tx1, err := testutils.CreateRandomTx( s.encodingConfig.TxConfig, @@ -213,7 +213,7 @@ func (s *BaseTestSuite) TestSelect() { }) s.Run("should be able to select a single transaction", func() { - mempool := constructor.NewMempool[string](constructor.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) + mempool := base.NewMempool[string](base.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3) tx1, err := testutils.CreateRandomTx( s.encodingConfig.TxConfig, diff --git a/tests/app/app.go b/tests/app/app.go index a0fdb5f..18294a1 100644 --- a/tests/app/app.go +++ b/tests/app/app.go @@ -63,10 +63,10 @@ import ( "github.com/skip-mev/pob/abci" "github.com/skip-mev/pob/block" - "github.com/skip-mev/pob/block/constructor" - "github.com/skip-mev/pob/lanes/base" + "github.com/skip-mev/pob/block/base" "github.com/skip-mev/pob/lanes/free" "github.com/skip-mev/pob/lanes/mev" + "github.com/skip-mev/pob/lanes/standard" buildermodule "github.com/skip-mev/pob/x/builder" builderkeeper "github.com/skip-mev/pob/x/builder/keeper" ) @@ -263,7 +263,7 @@ 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. - mevConfig := constructor.LaneConfig{ + mevConfig := base.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), @@ -276,7 +276,7 @@ func New( ) // Free lane allows transactions to be included in the next block for free. - freeConfig := constructor.LaneConfig{ + freeConfig := base.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), @@ -285,19 +285,19 @@ func New( } freeLane := free.NewFreeLane( freeConfig, - constructor.DefaultTxPriority(), + base.DefaultTxPriority(), free.DefaultMatchHandler(), ) // Default lane accepts all other transactions. - defaultConfig := constructor.LaneConfig{ + defaultConfig := base.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), MaxBlockSpace: math.LegacyZeroDec(), MaxTxs: 0, } - defaultLane := base.NewDefaultLane(defaultConfig) + defaultLane := standard.NewStandardLane(defaultConfig) // Set the lanes into the mempool. lanes := []block.Lane{ diff --git a/x/builder/ante/ante_test.go b/x/builder/ante/ante_test.go index ee68912..c62231f 100644 --- a/x/builder/ante/ante_test.go +++ b/x/builder/ante/ante_test.go @@ -12,9 +12,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/golang/mock/gomock" "github.com/skip-mev/pob/block" - "github.com/skip-mev/pob/block/constructor" - "github.com/skip-mev/pob/lanes/base" + "github.com/skip-mev/pob/block/base" "github.com/skip-mev/pob/lanes/mev" + "github.com/skip-mev/pob/lanes/standard" testutils "github.com/skip-mev/pob/testutils" "github.com/skip-mev/pob/x/builder/ante" "github.com/skip-mev/pob/x/builder/keeper" @@ -42,7 +42,7 @@ type AnteTestSuite struct { // mempool and lane set up mempool block.Mempool mevLane *mev.MEVLane - baseLane *base.DefaultLane + baseLane *standard.StandardLane lanes []block.Lane // Account set up @@ -84,7 +84,7 @@ func (suite *AnteTestSuite) SetupTest() { // Lanes configuration // // TOB lane set up - mevConfig := constructor.LaneConfig{ + mevConfig := base.LaneConfig{ Logger: suite.ctx.Logger(), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), @@ -97,7 +97,7 @@ func (suite *AnteTestSuite) SetupTest() { ) // Base lane set up - baseConfig := constructor.LaneConfig{ + baseConfig := base.LaneConfig{ Logger: suite.ctx.Logger(), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), @@ -105,7 +105,7 @@ func (suite *AnteTestSuite) SetupTest() { MaxBlockSpace: math.LegacyZeroDec(), IgnoreList: []block.Lane{suite.mevLane}, } - suite.baseLane = base.NewDefaultLane(baseConfig) + suite.baseLane = standard.NewStandardLane(baseConfig) // Mempool set up suite.lanes = []block.Lane{suite.mevLane, suite.baseLane} @@ -280,13 +280,13 @@ func (suite *AnteTestSuite) TestAnteHandler() { distribution := suite.mempool.GetTxDistribution() suite.Require().Equal(0, distribution[mev.LaneName]) - suite.Require().Equal(0, distribution[base.LaneName]) + suite.Require().Equal(0, distribution[standard.LaneName]) suite.Require().NoError(suite.mempool.Insert(suite.ctx, topAuctionTx)) distribution = suite.mempool.GetTxDistribution() suite.Require().Equal(1, distribution[mev.LaneName]) - suite.Require().Equal(0, distribution[base.LaneName]) + suite.Require().Equal(0, distribution[standard.LaneName]) } // Create the actual mev tx and insert into the mempool From faa64a541578bbe1a99f1a9a4fed3f860c80767a Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 18:04:35 -0400 Subject: [PATCH 24/41] lints --- api/pob/abci/v1/auction.pulsar.go | 2 +- block/base/lane.go | 2 +- block/proposals.go | 2 +- lanes/free/lane.go | 2 +- lanes/mev/lane.go | 8 +++----- lanes/standard/lane.go | 4 ++-- 6 files changed, 9 insertions(+), 11 deletions(-) diff --git a/api/pob/abci/v1/auction.pulsar.go b/api/pob/abci/v1/auction.pulsar.go index fb2e665..eadfe00 100644 --- a/api/pob/abci/v1/auction.pulsar.go +++ b/api/pob/abci/v1/auction.pulsar.go @@ -1411,4 +1411,4 @@ func file_pob_abci_v1_auction_proto_init() { file_pob_abci_v1_auction_proto_rawDesc = nil file_pob_abci_v1_auction_proto_goTypes = nil file_pob_abci_v1_auction_proto_depIdxs = nil -} \ No newline at end of file +} diff --git a/block/base/lane.go b/block/base/lane.go index 8fa5924..dc58cf4 100644 --- a/block/base/lane.go +++ b/block/base/lane.go @@ -16,7 +16,7 @@ var _ block.Lane = (*BaseLane)(nil) // 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 base/setters. -type BaseLane struct { +type BaseLane struct { //nolint // 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. diff --git a/block/proposals.go b/block/proposals.go index 85aea87..f2d77ea 100644 --- a/block/proposals.go +++ b/block/proposals.go @@ -30,7 +30,7 @@ type ( // and updating proposals. BlockProposals are iteratively updated as each lane prepares its // partial proposal. Each lane must call UpdateProposal with its partial proposal in PrepareLane. BlockProposals // can also include vote extensions, which are included at the top of the proposal. - BlockProposal interface { + BlockProposal interface { //nolint // UpdateProposal updates the proposal with the given transactions. There are a // few invarients that are checked: // 1. The total size of the proposal must be less than the maximum number of bytes allowed. diff --git a/lanes/free/lane.go b/lanes/free/lane.go index 358b089..10dcfaa 100644 --- a/lanes/free/lane.go +++ b/lanes/free/lane.go @@ -16,7 +16,7 @@ var _ block.Lane = (*FreeLane)(nil) // FreeLane defines the lane that is responsible for processing free transactions. // By default, transactions that are staking related are considered free. -type FreeLane struct { +type FreeLane struct { //nolint *base.BaseLane } diff --git a/lanes/mev/lane.go b/lanes/mev/lane.go index 35c00d0..2457352 100644 --- a/lanes/mev/lane.go +++ b/lanes/mev/lane.go @@ -13,9 +13,7 @@ const ( LaneName = "mev" ) -var ( - _ MEVLaneI = (*MEVLane)(nil) -) +var _ MEVLaneI = (*MEVLane)(nil) // MEVLane defines a MEV (Maximal Extracted Value) auction lane. The MEV auction lane // hosts transactions that want to bid for inclusion at the top of the next block. @@ -26,13 +24,13 @@ var ( type ( // MEVLaneI defines the interface for the mev auction lane. This interface // is utilized by both the x/builder module and the checkTx handler. - MEVLaneI interface { + MEVLaneI interface { //nolint block.Lane Factory GetTopAuctionTx(ctx context.Context) sdk.Tx } - MEVLane struct { + MEVLane struct { //nolint // LaneConfig defines the base lane configuration. *base.BaseLane diff --git a/lanes/standard/lane.go b/lanes/standard/lane.go index 8230949..d8543fc 100644 --- a/lanes/standard/lane.go +++ b/lanes/standard/lane.go @@ -15,10 +15,10 @@ var _ block.Lane = (*StandardLane)(nil) // StandardLane defines a default lane implementation. The standard lane orders // 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 similiar fashion to how the +// 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 StandardLane struct { +type StandardLane struct { //nolint *base.BaseLane } From 4cfb76e4e916edb9828ac1d0da4cfaafd5407cbd Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 18:34:36 -0400 Subject: [PATCH 25/41] readmes for all the lanes --- abci/README.md | 202 ---------------------------------- block/README.md | 228 +++++++++++++++++++++++++++++++++------ lanes/README.md | 0 lanes/free/README.md | 157 +++++++++++++++++++++++++++ lanes/mev/README.md | 160 +++++++++++++++++++++++++++ lanes/standard/README.md | 161 +++++++++++++++++++++++++++ 6 files changed, 671 insertions(+), 237 deletions(-) delete mode 100644 abci/README.md delete mode 100644 lanes/README.md diff --git a/abci/README.md b/abci/README.md deleted file mode 100644 index 48f006e..0000000 --- a/abci/README.md +++ /dev/null @@ -1,202 +0,0 @@ -# Block SDK Proposals - -> 🤓 Learn and read all about how proposals are constructed and verified using -> the Block SDK - -## 📖 Overview - -The Block SDK is a framework for building smarter blocks. The Block SDK is built -harnessing the power of ABCI++ which is a new ABCI implementation that allows -for more complex and expressive applications to be built on top of the Cosmos SDK. -The process of building and verifiying proposals can be broken down into two -distinct parts: - -1. Preparing a proposal during `PrepareProposal`. -2. Processing a proposal during `ProcessProposal`. - -The Block SDK provides a framework for building and verifying proposals by -segmenting a single block into multiple lanes. Each lane can be responsible for -proposing and verifying specific types of transaction. The Block SDK provides -a default implementation of a lane that can be used to build and verify proposals -similar to how they are built and verified in the Cosmos SDK today while also -providing a framework for building more complex lanes that can be used to build -and verify much more complex proposals. - -## 🤔 How does it work - -### 🔁 Transaction Lifecycle - -The best way to understand how lanes work is to first understand the lifecycle -of a transaction. A transaction begins its lifecycle when it is first signed and -broadcasted to a chain. After it is broadcasted to a validator, it will be checked -in `CheckTx` by the base application. If the transaction is valid, it will be -inserted into the applications mempool. - -The transaction then waits in the mempool until a new block needs to be proposed. -When a new block needs to be proposed, the application will call `PrepareProposal` -(which is a new ABCI++ addition) to request a new block from the current -proposer. The proposer will look at what transactions currently waiting to -be included in a block by looking at their mempool. The proposer will then -iteratively select transactions until the block is full. The proposer will then -send the block to other validators in the network. - -When a validator receives a proposed block, the validator will first want to -verify the contents of the block before signing off on it. The validator will -call `ProcessProposal` to verify the contents of the block. If the block is -valid, the validator will sign off on the block and broadcast their vote to the -network. If the block is invalid, the validator will reject the block. Once a -block is accepted by the network, it is committed and the transactions that -were included in the block are removed from the validator's mempool (as they no -longer need to be considered). - -### 🛣️ Lane Lifecycle - -After a transaction is verified in `CheckTx`, it will attempt to be inserted -into the `LanedMempool`. A `LanedMempool` is composed of several distinct `Lanes` -that have the ability to store their own transactions. The `LanedMempool` will -insert the transaction into all lanes that will accept it. The criteria for -whether a lane will accept a transaction is defined by the lane's -`MatchHandler`. The default implementation of a `MatchHandler` will accept all transactions. - - -When a new block is proposed, the `PrepareProposalHandler` will iteratively call -`PrepareLane` on each lane (in the order in which they are defined in the -`LanedMempool`). The `PrepareLane` method is anaolgous to `PrepareProposal`. Calling -`PrepareLane` on a lane will trigger the lane to reap transactions from its mempool -and add them to the proposal (given they are valid respecting the verification rules -of the lane). - -When proposals need to be verified in `ProcessProposal`, the `ProcessProposalHandler` -defined in `abci/abci.go` will call `ProcessLane` on each lane in the same order -as they were called in the `PrepareProposalHandler`. Each subsequent call to -`ProcessLane` will filter out transactions that belong to previous lanes. A given -lane's `ProcessLane` will only verify transactions that belong to that lane. - -> **Scenario** -> -> Let's say we have a `LanedMempool` composed of two lanes: `LaneA` and `LaneB`. -> `LaneA` is defined first in the `LanedMempool` and `LaneB` is defined second. -> `LaneA` contains transactions `Tx1` and `Tx2` and `LaneB` contains transactions -> `Tx3` and `Tx4`. - - -When a new block needs to be proposed, the `PrepareProposalHandler` will call -`PrepareLane` on `LaneA` first and `LaneB` second. When `PrepareLane` is called -on `LaneA`, `LaneA` will reap transactions from its mempool and add them to the -proposal. Same applies for `LaneB`. Say `LaneA` reaps transactions `Tx1` and `Tx2` -and `LaneB` reaps transactions `Tx3` and `Tx4`. This gives us a proposal composed -of the following: - -* `Tx1`, `Tx2`, `Tx3`, `Tx4` - -When the `ProcessProposalHandler` is called, it will call `ProcessLane` on `LaneA` -with the proposal composed of `Tx1`, `Tx2`, `Tx3`, and `Tx4`. `LaneA` will then -verify `Tx1` and `Tx2` and return the remaining transactions - `Tx3` and `Tx4`. -The `ProcessProposalHandler` will then call `ProcessLane` on `LaneB` with the -remaining transactions - `Tx3` and `Tx4`. `LaneB` will then verify `Tx3` and `Tx4` -and return no remaining transactions. - -## 🏗️ Setup - -> **Note** -> -> For a more in depth example of how to use the Block SDK, check out our -> example application in `block-sdk/tests/app/app.go`. - -### 📦 Dependencies - -The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently -compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. - -### 📥 Installation - -To install the Block SDK, run the following command: - -```bash -go get github.com/skip-mev/block-sdk/abci -``` - -### 📚 Usage - -First determine the set of lanes that you want to use in your application. The available -lanes can be found in our **Lane App Store** in `block-sdk/lanes`. In your base -application, you will need to create a `LanedMempool` composed of the lanes that -you want to use. You will also need to create a `PrepareProposalHandler` and a -`ProcessProposalHandler` that will be responsible for preparing and processing -proposals respectively. - -```golang -// 1. Create the lanes. -// -// 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. -// -// For more information on how to utilize the LaneConfig please -// visit the README in block-sdk/block/base. -// -// MEV lane hosts an action at the top of the block. -mevConfig := constructor.LaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: math.LegacyZeroDec(), - MaxTxs: 0, -} -mevLane := mev.NewMEVLane( - mevConfig, - mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), -) - -// Free lane allows transactions to be included in the next block for free. -freeConfig := constructor.LaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: math.LegacyZeroDec(), - MaxTxs: 0, -} -freeLane := free.NewFreeLane( - freeConfig, - constructor.DefaultTxPriority(), - free.DefaultMatchHandler(), -) - -// Default lane accepts all other transactions. -defaultConfig := constructor.LaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: math.LegacyZeroDec(), - MaxTxs: 0, -} -defaultLane := base.NewStandardLane(defaultConfig) - -// Set the lanes into the mempool. -lanes := []block.Lane{ - mevLane, - freeLane, - defaultLane, -} -mempool := block.NewLanedMempool(app.Logger(), true, lanes...) -app.App.SetMempool(mempool) - -... - -anteHandler := NewAnteHandler(options) - -// Set the lane ante handlers on the lanes. -for _, lane := range lanes { - lane.SetAnteHandler(anteHandler) -} -app.App.SetAnteHandler(anteHandler) - -// Set the abci handlers on base app -proposalHandler := abci.NewProposalHandler( - app.Logger(), - app.TxConfig().TxDecoder(), - lanes, -) -app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler()) -app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) -``` \ No newline at end of file diff --git a/block/README.md b/block/README.md index f80eb41..9c6082d 100644 --- a/block/README.md +++ b/block/README.md @@ -1,44 +1,202 @@ +# Block SDK 🧱 + +> 🤓 Learn and read all about how proposals are constructed and verified using +> the Block SDK + +## 📖 Overview + +The Block SDK is a framework for building smarter blocks. The Block SDK is built +harnessing the power of ABCI++ which is a new ABCI implementation that allows +for more complex and expressive applications to be built on top of the Cosmos SDK. +The process of building and verifiying proposals can be broken down into two +distinct parts: + +1. Preparing a proposal during `PrepareProposal`. +2. Processing a proposal during `ProcessProposal`. + +The Block SDK provides a framework for building and verifying proposals by +segmenting a single block into multiple lanes. Each lane can be responsible for +proposing and verifying specific types of transaction. The Block SDK provides +a default implementation of a lane that can be used to build and verify proposals +similar to how they are built and verified in the Cosmos SDK today while also +providing a framework for building more complex lanes that can be used to build +and verify much more complex proposals. + ## 🤔 How does it work -### Transaction Lifecycle +### 🔁 Transaction Lifecycle The best way to understand how lanes work is to first understand the lifecycle -of a transaction. When a transaction is submitted to the chain, it will be checked +of a transaction. A transaction begins its lifecycle when it is first signed and +broadcasted to a chain. After it is broadcasted to a validator, it will be checked in `CheckTx` by the base application. If the transaction is valid, it will be -inserted into the applications mempool. The transaction then waits in the mempool -until a new block needs to be proposed. When a new block needs to be proposed, -the application will call `PrepareProposal` (which is a new ABCI++ addition) to -request a new block from the current proposer. The proposer will look at what the -transactions currently waiting to be included in a block in their mempool and -will iterative select transactions until the block is full. The proposer will then -send the block to other validators in the network. When a validator receives a -proposed block, the validator will first want to verify the contents of the block -before signing off on it. The validator will call `ProcessProposal` to verify the -contents of the block. If the block is valid, the validator will sign off on the -block and broadcast their vote to the network. If the block is invalid, the validator -will reject the block. Once a block is accepted by the network, it is committed -and the transactions that were included in the block are removed from the mempool. +inserted into the applications mempool. -### Lane Lifecycle +The transaction then waits in the mempool until a new block needs to be proposed. +When a new block needs to be proposed, the application will call `PrepareProposal` +(which is a new ABCI++ addition) to request a new block from the current +proposer. The proposer will look at what transactions currently waiting to +be included in a block by looking at their mempool. The proposer will then +iteratively select transactions until the block is full. The proposer will then +send the block to other validators in the network. -The Lane Constructor implements the `Lane` interface. After transactions are -check in `CheckTx`, they will be added to this lane's mempool (data structure -responsible for storing transactions). When a new block is proposed, `PrepareLane` -will be called by the `PrepareProposalHandler` defined in `abci/abci.go`. This -will trigger the lane to reap transactions from its mempool and add them to the -proposal. By default, transactions are added to proposals in the order that they -are reaped from the mempool. Transactions will only be added to a proposal -if they are valid according to the lane's verification logic. The default implementation -determines whether a transaction is valid by running the transaction through the -lane's `AnteHandler`. If any transactions are invalid, they will be removed from -lane's mempool from further consideration. +When a validator receives a proposed block, the validator will first want to +verify the contents of the block before signing off on it. The validator will +call `ProcessProposal` to verify the contents of the block. If the block is +valid, the validator will sign off on the block and broadcast their vote to the +network. If the block is invalid, the validator will reject the block. Once a +block is accepted by the network, it is committed and the transactions that +were included in the block are removed from the validator's mempool (as they no +longer need to be considered). + +### 🛣️ Lane Lifecycle + +After a transaction is verified in `CheckTx`, it will attempt to be inserted +into the `LanedMempool`. A `LanedMempool` is composed of several distinct `Lanes` +that have the ability to store their own transactions. The `LanedMempool` will +insert the transaction into all lanes that will accept it. The criteria for +whether a lane will accept a transaction is defined by the lane's +`MatchHandler`. The default implementation of a `MatchHandler` will accept all transactions. + + +When a new block is proposed, the `PrepareProposalHandler` will iteratively call +`PrepareLane` on each lane (in the order in which they are defined in the +`LanedMempool`). The `PrepareLane` method is anaolgous to `PrepareProposal`. Calling +`PrepareLane` on a lane will trigger the lane to reap transactions from its mempool +and add them to the proposal (given they are valid respecting the verification rules +of the lane). When proposals need to be verified in `ProcessProposal`, the `ProcessProposalHandler` -defined in `abci/abci.go` will call `ProcessLane` on each lane. This will trigger -the lane to process all transactions that are included in the proposal. Lane's -should only verify transactions that belong to their lane. The default implementation -of `ProcessLane` will first check that transactions that should belong to the -current lane are ordered correctly in the proposal. If they are not, the proposal -will be rejected. If they are, the lane will run the transactions through its `ProcessLaneHandler` -which is responsible for verifying the transactions against the lane's verification -logic. If any transactions are invalid, the proposal will be rejected. \ No newline at end of file +defined in `abci/abci.go` will call `ProcessLane` on each lane in the same order +as they were called in the `PrepareProposalHandler`. Each subsequent call to +`ProcessLane` will filter out transactions that belong to previous lanes. A given +lane's `ProcessLane` will only verify transactions that belong to that lane. + +> **Scenario** +> +> Let's say we have a `LanedMempool` composed of two lanes: `LaneA` and `LaneB`. +> `LaneA` is defined first in the `LanedMempool` and `LaneB` is defined second. +> `LaneA` contains transactions `Tx1` and `Tx2` and `LaneB` contains transactions +> `Tx3` and `Tx4`. + + +When a new block needs to be proposed, the `PrepareProposalHandler` will call +`PrepareLane` on `LaneA` first and `LaneB` second. When `PrepareLane` is called +on `LaneA`, `LaneA` will reap transactions from its mempool and add them to the +proposal. Same applies for `LaneB`. Say `LaneA` reaps transactions `Tx1` and `Tx2` +and `LaneB` reaps transactions `Tx3` and `Tx4`. This gives us a proposal composed +of the following: + +* `Tx1`, `Tx2`, `Tx3`, `Tx4` + +When the `ProcessProposalHandler` is called, it will call `ProcessLane` on `LaneA` +with the proposal composed of `Tx1`, `Tx2`, `Tx3`, and `Tx4`. `LaneA` will then +verify `Tx1` and `Tx2` and return the remaining transactions - `Tx3` and `Tx4`. +The `ProcessProposalHandler` will then call `ProcessLane` on `LaneB` with the +remaining transactions - `Tx3` and `Tx4`. `LaneB` will then verify `Tx3` and `Tx4` +and return no remaining transactions. + +## 🏗️ Setup + +> **Note** +> +> For a more in depth example of how to use the Block SDK, check out our +> example application in `block-sdk/tests/app/app.go`. + +### 📦 Dependencies + +The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently +compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. + +### 📥 Installation + +To install the Block SDK, run the following command: + +```bash +go get github.com/skip-mev/block-sdk/abci +``` + +### 📚 Usage + +First determine the set of lanes that you want to use in your application. The available +lanes can be found in our **Lane App Store** in `block-sdk/lanes`. In your base +application, you will need to create a `LanedMempool` composed of the lanes that +you want to use. You will also need to create a `PrepareProposalHandler` and a +`ProcessProposalHandler` that will be responsible for preparing and processing +proposals respectively. + +```golang +// 1. Create the lanes. +// +// 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. +// +// For more information on how to utilize the LaneConfig please +// visit the README in block-sdk/block/base. +// +// MEV lane hosts an action at the top of the block. +mevConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, +} +mevLane := mev.NewMEVLane( + mevConfig, + mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), +) + +// Free lane allows transactions to be included in the next block for free. +freeConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, +} +freeLane := free.NewFreeLane( + freeConfig, + constructor.DefaultTxPriority(), + free.DefaultMatchHandler(), +) + +// Default lane accepts all other transactions. +defaultConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, +} +defaultLane := base.NewStandardLane(defaultConfig) + +// Set the lanes into the mempool. +lanes := []block.Lane{ + mevLane, + freeLane, + defaultLane, +} +mempool := block.NewLanedMempool(app.Logger(), true, lanes...) +app.App.SetMempool(mempool) + +... + +anteHandler := NewAnteHandler(options) + +// Set the lane ante handlers on the lanes. +for _, lane := range lanes { + lane.SetAnteHandler(anteHandler) +} +app.App.SetAnteHandler(anteHandler) + +// Set the abci handlers on base app +proposalHandler := abci.NewProposalHandler( + app.Logger(), + app.TxConfig().TxDecoder(), + lanes, +) +app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler()) +app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) +``` diff --git a/lanes/README.md b/lanes/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/lanes/free/README.md b/lanes/free/README.md index e69de29..165a579 100644 --- a/lanes/free/README.md +++ b/lanes/free/README.md @@ -0,0 +1,157 @@ +# Free Lane + +> Leverage the free lane to encourage certain activity (such as staking) on +> your chain. + +## 📖 Overview + +The Free Lane is a lane that allows transactions to be included in the next block +for free. By default, transactions that are staking related (e.g. delegation, +undelegation, redelegate, etc.) are included in the Free Lane, however, this +can be easily replaced! For more information on that, please see the +`MatchHandler` section in the README found in `block-sdk/block/base`. + +## 🏗️ Setup + +> **Note** +> +> For a more in depth example of how to use the Block SDK, check out our +> example application in `block-sdk/tests/app/app.go`. + +### 📦 Dependencies + +The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently +compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. + +### 📥 Installation + +To install the Block SDK, run the following command: + +```bash +$ go get github.com/skip-mev/block-sdk/abci +$ go get github.com/skip-mev/block-sdk/lanes/free +``` + +### 📚 Usage + +1. First determine the set of lanes that you want to use in your application. The +available lanes can be found in our **Lane App Store** in `block-sdk/lanes`. In +your base application, you will need to create a `LanedMempool` composed of the +lanes that you want to use. +2. Next, order the lanes by priority. The first lane is the highest priority lane +and the last lane is the lowest priority lane. Determine exactly where you want +the free lane to be in the priority order. +3. Set up your `FeeDeductorDecorator` to ignore the free lane where ever you +initialize your `AnteHandler`. This will ensure that the free lane is not +subject to deducting transaction fees. +4. You will also need to create a `PrepareProposalHandler` and a +`ProcessProposalHandler` that will be responsible for preparing and processing +proposals respectively. Configure the order of the lanes in the +`PrepareProposalHandler` and `ProcessProposalHandler` to match the order of the +lanes in the `LanedMempool`. + +```golang +import ( + "github.com/skip-mev/block-sdk/abci" + "github.com/skip-mev/block-sdk/lanes/free" +) + +... +``` + +```golang +func NewApp() { + ... + // 1. Create the lanes. + // + // 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. + // + // For more information on how to utilize the LaneConfig please + // visit the README in block-sdk/block/base. + // + // MEV lane hosts an action at the top of the block. + mevConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, + } + mevLane := mev.NewMEVLane( + mevConfig, + mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), + ) + + // Free lane allows transactions to be included in the next block for free. + freeConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, + } + freeLane := free.NewFreeLane( + freeConfig, + constructor.DefaultTxPriority(), + free.DefaultMatchHandler(), + ) + + // Default lane accepts all other transactions. + defaultConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, + } + defaultLane := base.NewStandardLane(defaultConfig) + + // 2. Set up the relateive priority of lanes + lanes := []block.Lane{ + mevLane, + freeLane, + defaultLane, + } + mempool := block.NewLanedMempool(app.Logger(), true, lanes...) + app.App.SetMempool(mempool) + + ... + + // 3. Set up the ante handler. + anteDecorators := []sdk.AnteDecorator{ + ante.NewSetUpContextDecorator(), + ... + utils.NewIgnoreDecorator( + ante.NewDeductFeeDecorator( + options.BaseOptions.AccountKeeper, + options.BaseOptions.BankKeeper, + options.BaseOptions.FeegrantKeeper, + options.BaseOptions.TxFeeChecker, + ), + options.FreeLane, + ), + ... + } + + anteHandler := sdk.ChainAnteDecorators(anteDecorators...) + + // Set the lane ante handlers on the lanes. + for _, lane := range lanes { + lane.SetAnteHandler(anteHandler) + } + app.App.SetAnteHandler(anteHandler) + + // 4. Set the abci handlers on base app + proposalHandler := abci.NewProposalHandler( + app.Logger(), + app.TxConfig().TxDecoder(), + lanes, + ) + app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler()) + app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) + + ... +} +``` diff --git a/lanes/mev/README.md b/lanes/mev/README.md index e69de29..4c7c701 100644 --- a/lanes/mev/README.md +++ b/lanes/mev/README.md @@ -0,0 +1,160 @@ +# MEV Lane + +> The MEV Lane hosts top of block auctions in protocol and verifiably builds +> blocks with top-of-block block space reserved for auction winners, with +> auction revenue being redistributed to chains. + +## 📖 Overview + +Blockspace is valuable, and MEV bots find arbitrage opportunities to capture +value. The Block SDK provides a fair auction for these opportunities via the +x/auction module inside the Block SDK so that protocols are rewarded while +ensuring that users are not front-run or sandwiched in the process. + +The Block SDK uses the app-side mempool, PrepareLane / ProcessLane, and CheckTx +to create an MEV marketplace inside the protocol. It introduces a new message +type, called a MsgAuctionBid, that allows the submitter to execute multiple +transactions at the top of the block atomically +(atomically = directly next to each other). + +## 🏗️ Setup + +> **Note** +> +> For a more in depth example of how to use the Block SDK, check out our +> example application in `block-sdk/tests/app/app.go`. + +### 📦 Dependencies + +The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently +compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. + +### 📥 Installation + +To install the Block SDK, run the following command: + +```bash +$ go get github.com/skip-mev/block-sdk/abci +$ go get github.com/skip-mev/block-sdk/lanes/mev +``` + +### 📚 Usage + +1. First determine the set of lanes that you want to use in your application. The +available lanes can be found in our **Lane App Store** in `block-sdk/lanes`. In +your base application, you will need to create a `LanedMempool` composed of the +lanes that you want to use. +2. Next, order the lanes by priority. The first lane is the highest priority lane +and the last lane is the lowest priority lane. **It is recommended that the first +lane is the MEV lane as the top of block is the most valuable block space.** +3. You will also need to create a `PrepareProposalHandler` and a +`ProcessProposalHandler` that will be responsible for preparing and processing +proposals respectively. Configure the order of the lanes in the +`PrepareProposalHandler` and `ProcessProposalHandler` to match the order of the +lanes in the `LanedMempool`. + +```golang +import ( + "github.com/skip-mev/block-sdk/abci" + "github.com/skip-mev/block-sdk/lanes/mev" +) + +... +``` + +```golang +func NewApp() { + ... + // 1. Create the lanes. + // + // 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. + // + // For more information on how to utilize the LaneConfig please + // visit the README in block-sdk/block/base. + // + // MEV lane hosts an action at the top of the block. + mevConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, + } + mevLane := mev.NewMEVLane( + mevConfig, + mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), + ) + + // Free lane allows transactions to be included in the next block for free. + freeConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, + } + freeLane := free.NewFreeLane( + freeConfig, + constructor.DefaultTxPriority(), + free.DefaultMatchHandler(), + ) + + // Default lane accepts all other transactions. + defaultConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, + } + defaultLane := base.NewStandardLane(defaultConfig) + + // 2. Set up the relateive priority of lanes + lanes := []block.Lane{ + mevLane, + freeLane, + defaultLane, + } + mempool := block.NewLanedMempool(app.Logger(), true, lanes...) + app.App.SetMempool(mempool) + + ... + + // 3. Set up the ante handler. + anteDecorators := []sdk.AnteDecorator{ + ante.NewSetUpContextDecorator(), + ... + utils.NewIgnoreDecorator( + ante.NewDeductFeeDecorator( + options.BaseOptions.AccountKeeper, + options.BaseOptions.BankKeeper, + options.BaseOptions.FeegrantKeeper, + options.BaseOptions.TxFeeChecker, + ), + options.FreeLane, + ), + ... + } + + anteHandler := sdk.ChainAnteDecorators(anteDecorators...) + + // Set the lane ante handlers on the lanes. + for _, lane := range lanes { + lane.SetAnteHandler(anteHandler) + } + app.App.SetAnteHandler(anteHandler) + + // 4. Set the abci handlers on base app + proposalHandler := abci.NewProposalHandler( + app.Logger(), + app.TxConfig().TxDecoder(), + lanes, + ) + app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler()) + app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) + + ... +} +``` diff --git a/lanes/standard/README.md b/lanes/standard/README.md index e69de29..f466958 100644 --- a/lanes/standard/README.md +++ b/lanes/standard/README.md @@ -0,0 +1,161 @@ +# Standard Lane + +> The Standard Lane is the most general and least restrictive lane. The Standard +> Lane accepts all transactions that are not accepted by the other lanes, is +> generally the lowest priority lane, and consumes all blockspace that is not +> consumed by the other lanes. + +## 📖 Overview + +Blockspace is valuable, and MEV bots find arbitrage opportunities to capture +value. The Block SDK provides a fair auction for these opportunities via the +x/auction module inside the Block SDK so that protocols are rewarded while +ensuring that users are not front-run or sandwiched in the process. + +The Block SDK uses the app-side mempool, PrepareLane / ProcessLane, and CheckTx +to create an MEV marketplace inside the protocol. It introduces a new message +type, called a MsgAuctionBid, that allows the submitter to execute multiple +transactions at the top of the block atomically +(atomically = directly next to each other). + +## 🏗️ Setup + +> **Note** +> +> For a more in depth example of how to use the Block SDK, check out our +> example application in `block-sdk/tests/app/app.go`. + +### 📦 Dependencies + +The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently +compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. + +### 📥 Installation + +To install the Block SDK, run the following command: + +```bash +$ go get github.com/skip-mev/block-sdk/abci +$ go get github.com/skip-mev/block-sdk/lanes/standard +``` + +### 📚 Usage + +1. First determine the set of lanes that you want to use in your application. The +available lanes can be found in our **Lane App Store** in `block-sdk/lanes`. In +your base application, you will need to create a `LanedMempool` composed of the +lanes that you want to use. +2. Next, order the lanes by priority. The first lane is the highest priority lane +and the last lane is the lowest priority lane. **It is recommended that the last +lane is the standard lane.** +3. You will also need to create a `PrepareProposalHandler` and a +`ProcessProposalHandler` that will be responsible for preparing and processing +proposals respectively. Configure the order of the lanes in the +`PrepareProposalHandler` and `ProcessProposalHandler` to match the order of the +lanes in the `LanedMempool`. + +```golang +import ( + "github.com/skip-mev/block-sdk/abci" + "github.com/skip-mev/block-sdk/lanes/standard" +) + +... +``` + +```golang +func NewApp() { + ... + // 1. Create the lanes. + // + // 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. + // + // For more information on how to utilize the LaneConfig please + // visit the README in block-sdk/block/base. + // + // MEV lane hosts an action at the top of the block. + mevConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, + } + mevLane := mev.NewMEVLane( + mevConfig, + mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), + ) + + // Free lane allows transactions to be included in the next block for free. + freeConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, + } + freeLane := free.NewFreeLane( + freeConfig, + constructor.DefaultTxPriority(), + free.DefaultMatchHandler(), + ) + + // Standard lane accepts all other transactions. + defaultConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, + } + defaultLane := base.NewStandardLane(defaultConfig) + + // 2. Set up the relateive priority of lanes + lanes := []block.Lane{ + mevLane, + freeLane, + defaultLane, + } + mempool := block.NewLanedMempool(app.Logger(), true, lanes...) + app.App.SetMempool(mempool) + + ... + + // 3. Set up the ante handler. + anteDecorators := []sdk.AnteDecorator{ + ante.NewSetUpContextDecorator(), + ... + utils.NewIgnoreDecorator( + ante.NewDeductFeeDecorator( + options.BaseOptions.AccountKeeper, + options.BaseOptions.BankKeeper, + options.BaseOptions.FeegrantKeeper, + options.BaseOptions.TxFeeChecker, + ), + options.FreeLane, + ), + ... + } + + anteHandler := sdk.ChainAnteDecorators(anteDecorators...) + + // Set the lane ante handlers on the lanes. + for _, lane := range lanes { + lane.SetAnteHandler(anteHandler) + } + app.App.SetAnteHandler(anteHandler) + + // 4. Set the abci handlers on base app + proposalHandler := abci.NewProposalHandler( + app.Logger(), + app.TxConfig().TxDecoder(), + lanes, + ) + app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler()) + app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) + + ... +} +``` From d956b79154cf625531ac3661dd5b2cf2dd9e7a4d Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 18:36:58 -0400 Subject: [PATCH 26/41] main readme --- README.md | 496 ++++++++++++++++++++---------------------------------- 1 file changed, 180 insertions(+), 316 deletions(-) diff --git a/README.md b/README.md index 6ef7d94..deab51a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -

Protocol-Owned Builder

+

Block SDK 🧱

@@ -9,337 +9,201 @@ [![License: Apache-2.0](https://img.shields.io/github/license/skip-mev/pob.svg?style=flat-square)](https://github.com/skip-mev/pob/blob/main/LICENSE) [![Lines Of Code](https://img.shields.io/tokei/lines/github/skip-mev/pob?style=flat-square)](https://github.com/skip-mev/pob) -Skip Protocol's Protocol-Owned Builder (POB) is a set of Cosmos SDK and ABCI++ -primitives that provide application developers the ability to define how their -apps construct and validate blocks on-chain in a transparent, enforceable way, -such as giving complete control to the protocol to recapture, control, and -redistribute MEV. +## 📖 Overview -Skip's POB provides developers with a set of a few core primitives: +The Block SDK is a framework for building smarter blocks. The Block SDK is built +harnessing the power of ABCI++ which is a new ABCI implementation that allows +for more complex and expressive applications to be built on top of the Cosmos SDK. +The process of building and verifiying proposals can be broken down into two +distinct parts: -* `BlockBuster`: BlockBuster is a generalized block-building and mempool SDK - that allows developers to define how their applications construct and validate blocks - on-chain in a transparent, enforceable way. At its core, BlockBuster is an app-side mempool + set - of proposal handlers (`PrepareProposal`/`ProcessProposal`) that allow developers to configure - modular lanes of transactions in their blocks with distinct validation/ordering logic. For more - information, see the [BlockBuster README](/block/README.md). -* `x/builder`: This Cosmos SDK module gives applications the ability to process - MEV bundled transactions in addition to having the ability to define how searchers - and block proposers are rewarded. In addition, the module defines a `AuctionDecorator`, - which is an AnteHandler decorator that enforces various chain configurable MEV - rules. +1. Preparing a proposal during `PrepareProposal`. +2. Processing a proposal during `ProcessProposal`. -## Releases +The Block SDK provides a framework for building and verifying proposals by +segmenting a single block into multiple lanes. Each lane can be responsible for +proposing and verifying specific types of transaction. The Block SDK provides +a default implementation of a lane that can be used to build and verify proposals +similar to how they are built and verified in the Cosmos SDK today while also +providing a framework for building more complex lanes that can be used to build +and verify much more complex proposals. -### Release Compatibility Matrix +## 🤔 How does it work -| POB Version | Cosmos SDK | -| :---------: | :--------: | -| v1.x.x | v0.47.x | -| v1.x.x | v0.48.x | -| v1.x.x | v0.49.x | -| v1.x.x | v0.50.x | +### 🔁 Transaction Lifecycle -## Install +The best way to understand how lanes work is to first understand the lifecycle +of a transaction. A transaction begins its lifecycle when it is first signed and +broadcasted to a chain. After it is broadcasted to a validator, it will be checked +in `CheckTx` by the base application. If the transaction is valid, it will be +inserted into the applications mempool. -```shell -$ go install github.com/skip-mev/pob +The transaction then waits in the mempool until a new block needs to be proposed. +When a new block needs to be proposed, the application will call `PrepareProposal` +(which is a new ABCI++ addition) to request a new block from the current +proposer. The proposer will look at what transactions currently waiting to +be included in a block by looking at their mempool. The proposer will then +iteratively select transactions until the block is full. The proposer will then +send the block to other validators in the network. + +When a validator receives a proposed block, the validator will first want to +verify the contents of the block before signing off on it. The validator will +call `ProcessProposal` to verify the contents of the block. If the block is +valid, the validator will sign off on the block and broadcast their vote to the +network. If the block is invalid, the validator will reject the block. Once a +block is accepted by the network, it is committed and the transactions that +were included in the block are removed from the validator's mempool (as they no +longer need to be considered). + +### 🛣️ Lane Lifecycle + +After a transaction is verified in `CheckTx`, it will attempt to be inserted +into the `LanedMempool`. A `LanedMempool` is composed of several distinct `Lanes` +that have the ability to store their own transactions. The `LanedMempool` will +insert the transaction into all lanes that will accept it. The criteria for +whether a lane will accept a transaction is defined by the lane's +`MatchHandler`. The default implementation of a `MatchHandler` will accept all transactions. + + +When a new block is proposed, the `PrepareProposalHandler` will iteratively call +`PrepareLane` on each lane (in the order in which they are defined in the +`LanedMempool`). The `PrepareLane` method is anaolgous to `PrepareProposal`. Calling +`PrepareLane` on a lane will trigger the lane to reap transactions from its mempool +and add them to the proposal (given they are valid respecting the verification rules +of the lane). + +When proposals need to be verified in `ProcessProposal`, the `ProcessProposalHandler` +defined in `abci/abci.go` will call `ProcessLane` on each lane in the same order +as they were called in the `PrepareProposalHandler`. Each subsequent call to +`ProcessLane` will filter out transactions that belong to previous lanes. A given +lane's `ProcessLane` will only verify transactions that belong to that lane. + +> **Scenario** +> +> Let's say we have a `LanedMempool` composed of two lanes: `LaneA` and `LaneB`. +> `LaneA` is defined first in the `LanedMempool` and `LaneB` is defined second. +> `LaneA` contains transactions `Tx1` and `Tx2` and `LaneB` contains transactions +> `Tx3` and `Tx4`. + + +When a new block needs to be proposed, the `PrepareProposalHandler` will call +`PrepareLane` on `LaneA` first and `LaneB` second. When `PrepareLane` is called +on `LaneA`, `LaneA` will reap transactions from its mempool and add them to the +proposal. Same applies for `LaneB`. Say `LaneA` reaps transactions `Tx1` and `Tx2` +and `LaneB` reaps transactions `Tx3` and `Tx4`. This gives us a proposal composed +of the following: + +* `Tx1`, `Tx2`, `Tx3`, `Tx4` + +When the `ProcessProposalHandler` is called, it will call `ProcessLane` on `LaneA` +with the proposal composed of `Tx1`, `Tx2`, `Tx3`, and `Tx4`. `LaneA` will then +verify `Tx1` and `Tx2` and return the remaining transactions - `Tx3` and `Tx4`. +The `ProcessProposalHandler` will then call `ProcessLane` on `LaneB` with the +remaining transactions - `Tx3` and `Tx4`. `LaneB` will then verify `Tx3` and `Tx4` +and return no remaining transactions. + +## 🏗️ Setup + +> **Note** +> +> For a more in depth example of how to use the Block SDK, check out our +> example application in `block-sdk/tests/app/app.go`. + +### 📦 Dependencies + +The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently +compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. + +### 📥 Installation + +To install the Block SDK, run the following command: + +```bash +go get github.com/skip-mev/block-sdk/abci ``` -## Setup +### 📚 Usage ->This set up guide will walk you through the process of setting up a POB application. In particular, we will configure an application with the following features: -> ->* Top of block lane (auction lane). This will create an auction lane where users can bid to have their -> transactions executed at the top of the block. ->* Free lane. This will create a free lane where users can submit transactions that will be executed -> for free (no fees). ->* Default lane. This will create a default lane where users can submit transactions that will be executed -> with the default app logic. ->* Builder module that pairs with the auction lane to process auction transactions and distribute revenue -> to the auction house. -> -> To build your own custom BlockBuster Lane, please see the [BlockBuster README](/block/README.md). +First determine the set of lanes that you want to use in your application. The available +lanes can be found in our **Lane App Store** in `block-sdk/lanes`. In your base +application, you will need to create a `LanedMempool` composed of the lanes that +you want to use. You will also need to create a `PrepareProposalHandler` and a +`ProcessProposalHandler` that will be responsible for preparing and processing +proposals respectively. -1. Import the necessary dependencies into your application. This includes the - block proposal handlers +mempool, keeper, builder types, and builder module. This - tutorial will go into more detail into each of the dependencies. +```golang +// 1. Create the lanes. +// +// 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. +// +// For more information on how to utilize the LaneConfig please +// visit the README in block-sdk/block/base. +// +// MEV lane hosts an action at the top of the block. +mevConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, +} +mevLane := mev.NewMEVLane( + mevConfig, + mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), +) - ```go - import ( - ... - "github.com/skip-mev/pob/block" - "github.com/skip-mev/pob/block/abci" - "github.com/skip-mev/pob/block/lanes/mev" - "github.com/skip-mev/pob/block/lanes/base" - "github.com/skip-mev/pob/block/lanes/free" - buildermodule "github.com/skip-mev/pob/x/builder" - builderkeeper "github.com/skip-mev/pob/x/builder/keeper" - ... - ) - ``` +// Free lane allows transactions to be included in the next block for free. +freeConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, +} +freeLane := free.NewFreeLane( + freeConfig, + constructor.DefaultTxPriority(), + free.DefaultMatchHandler(), +) -2. Add your module to the the `AppModuleBasic` manager. This manager is in - charge of setting up basic, non-dependent module elements such as codec - registration and genesis verification. This will register the special - `MsgAuctionBid` message. When users want to bid for MEV execution, - they will submit a transaction - which we call an auction transaction - that - includes a single `MsgAuctionBid`. We prevent any other messages from being - included in auction transaction to prevent malicious behavior - such as front - running or sandwiching. +// Default lane accepts all other transactions. +defaultConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, +} +defaultLane := base.NewStandardLane(defaultConfig) - ```go - var ( - ModuleBasics = module.NewBasicManager( - ... - buildermodule.AppModuleBasic{}, - ) - ... - ) - ``` +// Set the lanes into the mempool. +lanes := []block.Lane{ + mevLane, + freeLane, + defaultLane, +} +mempool := block.NewLanedMempool(app.Logger(), true, lanes...) +app.App.SetMempool(mempool) -3. The builder `Keeper` is POB's gateway to processing special `MsgAuctionBid` - messages that allow users to participate in the MEV auction, distribute - revenue to the auction house, and ensure the validity of auction transactions. +... - a. First add the keeper to the app's struct definition. We also want to add POB's custom - checkTx handler to the app's struct definition. This will allow us to override the - default checkTx handler to process bid transactions before they are inserted into the mempool. - NOTE: The custom handler is required as otherwise the auction can be held hostage by a malicious - users. +anteHandler := NewAnteHandler(options) - ```go - type App struct { - ... - // BuilderKeeper is the keeper that handles processing auction transactions - BuilderKeeper builderkeeper.Keeper +// Set the lane ante handlers on the lanes. +for _, lane := range lanes { + lane.SetAnteHandler(anteHandler) +} +app.App.SetAnteHandler(anteHandler) - // Custom checkTx handler - checkTxHandler abci.CheckTx - } - ``` +// Set the abci handlers on base app +proposalHandler := abci.NewProposalHandler( + app.Logger(), + app.TxConfig().TxDecoder(), + lanes, +) +app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler()) +app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) +``` - b. Add the builder module to the list of module account permissions. This will - instantiate the builder module account on genesis. - - ```go - maccPerms = map[string][]string{ - builder.ModuleName: nil, - ... - } - ``` - - c. Instantiate the block mempool with the application's desired lanes. - - ```go - // Set the block mempool into the app. - // Create the lanes. - // - // 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. - // - // block.BaseLaneConfig is utilized for basic encoding/decoding of transactions. - tobConfig := block.BaseLaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - // the desired portion of total block space to be reserved for the lane. a value of 0 - // indicates that the lane can use all available block space. - MaxBlockSpace: sdk.ZeroDec(), - } - tobLane := auction.NewMEVLane( - tobConfig, - // the maximum number of transactions that the mempool can store. a value of 0 indicates - // that the mempool can store an unlimited number of transactions. - 0, - // AuctionFactory is responsible for determining what is an auction bid transaction and - // how to extract the bid information from the transaction. There is a default implementation - // that can be used or application developers can implement their own. - auction.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), - ) - - // Free lane allows transactions to be included in the next block for free. - freeConfig := block.BaseLaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: sdk.ZeroDec(), - // IgnoreList is a list of lanes that if a transaction should be included in, it will be - // ignored by the lane. For example, if a transaction should belong to the tob lane, it - // will be ignored by the free lane. - IgnoreList: []block.Lane{ - tobLane, - }, - } - freeLane := free.NewFreeLane( - freeConfig, - free.NewDefaultFreeFactory(app.txConfig.TxDecoder()), - ) - - // Default lane accepts all other transactions. - defaultConfig := block.BaseLaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: sdk.ZeroDec(), - IgnoreList: []block.Lane{ - tobLane, - freeLane, - }, - } - defaultLane := base.NewStandardLane(defaultConfig) - - // Set the lanes into the mempool. - lanes := []block.Lane{ - tobLane, - freeLane, - defaultLane, - } - mempool := block.NewMempool(lanes...) - app.App.SetMempool(mempool) - ``` - - d. Instantiate the antehandler chain for the application with awareness of the - block mempool. This will allow the application to verify the validity - of a transaction respecting the desired logic of a given lane. In this walkthrough, - we want the `FeeDecorator` to be ignored for all transactions that should belong to the - free lane. Additionally, we want to add the `x/builder` module's `AuctionDecorator` to the - ante-handler chain. The `AuctionDecorator` is an AnteHandler decorator that enforces various - chain configurable MEV rules. - - ```go - import ( - ... - "github.com/skip-mev/pob/block" - "github.com/skip-mev/pob/block/utils" - builderante "github.com/skip-mev/pob/x/builder/ante" - ... - ) - - anteDecorators := []sdk.AnteDecorator{ - ante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first - ... - // The IgnoreDecorator allows for certain decorators to be ignored for certain transactions. In - // this case, we want to ignore the FeeDecorator for all transactions that should belong to the - // free lane. - utils.NewIgnoreDecorator( - ante.NewDeductFeeDecorator( - options.BaseOptions.AccountKeeper, - options.BaseOptions.BankKeeper, - options.BaseOptions.FeegrantKeeper, - options.BaseOptions.TxFeeChecker, - ), - options.FreeLane, - ), - ... - builderante.NewBuilderDecorator(options.BuilderKeeper, options.TxEncoder, options.MEVLane, options.Mempool), - } - - anteHandler := sdk.ChainAnteDecorators(anteDecorators...) - app.SetAnteHandler(anteHandler) - - // Set the antehandlers on the lanes. - for _, lane := range lanes { - lane.SetAnteHandler(anteHandler) - } - app.App.SetAnteHandler(anteHandler) - ``` - - e. Instantiate the builder keeper, store keys, and module manager. Note, be - sure to do this after all the required keeper dependencies have been instantiated. - - ```go - keys := storetypes.NewKVStoreKeys( - buildertypes.StoreKey, - ... - ) - - ... - app.BuilderKeeper := builderkeeper.NewKeeper( - appCodec, - keys[buildertypes.StoreKey], - app.AccountKeeper, - app.BankKeeper, - app.DistrKeeper, - app.StakingKeeper, - authtypes.NewModuleAddress(govv1.ModuleName).String(), - ) - - - app.ModuleManager = module.NewManager( - builder.NewAppModule(appCodec, app.BuilderKeeper), - ... - ) - ``` - - e. With Cosmos SDK version 0.47.0, the process of building blocks has been - updated and moved from the consensus layer, CometBFT, to the application layer. - When a new block is requested, the proposer for that height will utilize the - `PrepareProposal` handler to build a block while the `ProcessProposal` handler - will verify the contents of the block proposal by all validators. The - combination of the `BlockBuster` mempool + `PrepareProposal`/`ProcessProposal` - handlers allows the application to verifiably build valid blocks with - mev block space reserved for auctions and partial block for free transactions. - Additionally, we override the `BaseApp`'s `CheckTx` handler with our own custom - `CheckTx` handler that will be responsible for checking the validity of transactions. - We override the `CheckTx` handler so that we can verify auction transactions before they are - inserted into the mempool. With the POB `CheckTx`, we can verify the auction - transaction and all of the bundled transactions before inserting the auction - transaction into the mempool. This is important because we otherwise there may be - discrepencies between the auction transaction and the bundled transactions - are validated in `CheckTx` and `PrepareProposal` such that the auction can be - griefed. All other transactions will be executed with base app's `CheckTx`. - - ```go - - // Create the proposal handler that will be used to build and validate blocks. - proposalHandler := abci.NewProposalHandler( - app.Logger(), - app.txConfig.TxDecoder(), - mempool, - ) - app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler()) - app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) - - - // Set the custom CheckTx handler on BaseApp. - checkTxHandler := abci.NewCheckTxHandler( - app.App, - app.txConfig.TxDecoder(), - tobLane, - anteHandler, - app.ChainID(), - ) - app.SetCheckTx(checkTxHandler.CheckTx()) - ... - - // CheckTx will check the transaction with the provided checkTxHandler. We override the default - // handler so that we can verify bid transactions before they are inserted into the mempool. - // With the POB CheckTx, we can verify the bid transaction and all of the bundled transactions - // before inserting the bid transaction into the mempool. - func (app *TestApp) CheckTx(req cometabci.RequestCheckTx) cometabci.ResponseCheckTx { - return app.checkTxHandler(req) - } - - // SetCheckTx sets the checkTxHandler for the app. - func (app *TestApp) SetCheckTx(handler abci.CheckTx) { - app.checkTxHandler = handler - } - ``` - - f. Finally, update the app's `InitGenesis` order and ante-handler chain. - - ```go - genesisModuleOrder := []string{ - buildertypes.ModuleName, - ..., - } - ``` - -## Params - -Note, before building or upgrading the application, make sure to initialize the -escrow address for POB in the parameters of the module. The default parameters -initialize the escrow address to be the module account address. The escrow address -will be the address that is receiving a portion of auction house revenue alongside the proposer (or custom rewards providers). From dd861dd774bb4b74586fab028b1e628e95d80ce7 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 18:44:57 -0400 Subject: [PATCH 27/41] main readme --- README.md | 153 ++++++------------------------------------------------ 1 file changed, 16 insertions(+), 137 deletions(-) diff --git a/README.md b/README.md index deab51a..65c434c 100644 --- a/README.md +++ b/README.md @@ -11,22 +11,7 @@ ## 📖 Overview -The Block SDK is a framework for building smarter blocks. The Block SDK is built -harnessing the power of ABCI++ which is a new ABCI implementation that allows -for more complex and expressive applications to be built on top of the Cosmos SDK. -The process of building and verifiying proposals can be broken down into two -distinct parts: - -1. Preparing a proposal during `PrepareProposal`. -2. Processing a proposal during `ProcessProposal`. - -The Block SDK provides a framework for building and verifying proposals by -segmenting a single block into multiple lanes. Each lane can be responsible for -proposing and verifying specific types of transaction. The Block SDK provides -a default implementation of a lane that can be used to build and verify proposals -similar to how they are built and verified in the Cosmos SDK today while also -providing a framework for building more complex lanes that can be used to build -and verify much more complex proposals. +The Block SDK is a set of Cosmos SDK and ABCI++ primitives that allow chains to fully customize blocks to specific use cases. It turns your chain's blocks into a **transaction highway** consisting of individual lanes with their own special functionality. ## 🤔 How does it work @@ -80,130 +65,24 @@ lane's `ProcessLane` will only verify transactions that belong to that lane. > **Scenario** > -> Let's say we have a `LanedMempool` composed of two lanes: `LaneA` and `LaneB`. -> `LaneA` is defined first in the `LanedMempool` and `LaneB` is defined second. -> `LaneA` contains transactions `Tx1` and `Tx2` and `LaneB` contains transactions -> `Tx3` and `Tx4`. +> Let's say we have a `LanedMempool` composed of two lanes: LaneA and LaneB. +> LaneA is defined first in the `LanedMempool` and LaneB is defined second. +> LaneA contains transactions Tx1 and Tx2 and LaneB contains transactions +> Tx3 and Tx4. When a new block needs to be proposed, the `PrepareProposalHandler` will call -`PrepareLane` on `LaneA` first and `LaneB` second. When `PrepareLane` is called -on `LaneA`, `LaneA` will reap transactions from its mempool and add them to the -proposal. Same applies for `LaneB`. Say `LaneA` reaps transactions `Tx1` and `Tx2` -and `LaneB` reaps transactions `Tx3` and `Tx4`. This gives us a proposal composed +`PrepareLane` on LaneA first and LaneB second. When `PrepareLane` is called +on LaneA, LaneA will reap transactions from its mempool and add them to the +proposal. Same applies for LaneB. Say LaneA reaps transactions Tx1 and Tx2 +and LaneB reaps transactions Tx3 and Tx4. This gives us a proposal composed of the following: -* `Tx1`, `Tx2`, `Tx3`, `Tx4` - -When the `ProcessProposalHandler` is called, it will call `ProcessLane` on `LaneA` -with the proposal composed of `Tx1`, `Tx2`, `Tx3`, and `Tx4`. `LaneA` will then -verify `Tx1` and `Tx2` and return the remaining transactions - `Tx3` and `Tx4`. -The `ProcessProposalHandler` will then call `ProcessLane` on `LaneB` with the -remaining transactions - `Tx3` and `Tx4`. `LaneB` will then verify `Tx3` and `Tx4` -and return no remaining transactions. - -## 🏗️ Setup - -> **Note** -> -> For a more in depth example of how to use the Block SDK, check out our -> example application in `block-sdk/tests/app/app.go`. - -### 📦 Dependencies - -The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently -compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. - -### 📥 Installation - -To install the Block SDK, run the following command: - -```bash -go get github.com/skip-mev/block-sdk/abci -``` - -### 📚 Usage - -First determine the set of lanes that you want to use in your application. The available -lanes can be found in our **Lane App Store** in `block-sdk/lanes`. In your base -application, you will need to create a `LanedMempool` composed of the lanes that -you want to use. You will also need to create a `PrepareProposalHandler` and a -`ProcessProposalHandler` that will be responsible for preparing and processing -proposals respectively. - -```golang -// 1. Create the lanes. -// -// 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. -// -// For more information on how to utilize the LaneConfig please -// visit the README in block-sdk/block/base. -// -// MEV lane hosts an action at the top of the block. -mevConfig := constructor.LaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: math.LegacyZeroDec(), - MaxTxs: 0, -} -mevLane := mev.NewMEVLane( - mevConfig, - mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), -) - -// Free lane allows transactions to be included in the next block for free. -freeConfig := constructor.LaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: math.LegacyZeroDec(), - MaxTxs: 0, -} -freeLane := free.NewFreeLane( - freeConfig, - constructor.DefaultTxPriority(), - free.DefaultMatchHandler(), -) - -// Default lane accepts all other transactions. -defaultConfig := constructor.LaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: math.LegacyZeroDec(), - MaxTxs: 0, -} -defaultLane := base.NewStandardLane(defaultConfig) - -// Set the lanes into the mempool. -lanes := []block.Lane{ - mevLane, - freeLane, - defaultLane, -} -mempool := block.NewLanedMempool(app.Logger(), true, lanes...) -app.App.SetMempool(mempool) - -... - -anteHandler := NewAnteHandler(options) - -// Set the lane ante handlers on the lanes. -for _, lane := range lanes { - lane.SetAnteHandler(anteHandler) -} -app.App.SetAnteHandler(anteHandler) - -// Set the abci handlers on base app -proposalHandler := abci.NewProposalHandler( - app.Logger(), - app.TxConfig().TxDecoder(), - lanes, -) -app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler()) -app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) -``` +* Tx1, Tx2, Tx3, Tx4 +When the `ProcessProposalHandler` is called, it will call `ProcessLane` on LaneA +with the proposal composed of Tx1, Tx2, Tx3, and Tx4. LaneA will then +verify Tx1 and Tx2 and return the remaining transactions - Tx3 and Tx4. +The `ProcessProposalHandler` will then call `ProcessLane` on LaneB with the +remaining transactions - Tx3 and Tx4. LaneB will then verify Tx3 and Tx4 +and return no remaining transactions. \ No newline at end of file From 012a5bb78f0aa57d9a1558c177fa2956fab4447b Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 18:48:15 -0400 Subject: [PATCH 28/41] main readme update --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 65c434c..c769bd1 100644 --- a/README.md +++ b/README.md @@ -42,37 +42,37 @@ longer need to be considered). ### 🛣️ Lane Lifecycle -After a transaction is verified in `CheckTx`, it will attempt to be inserted -into the `LanedMempool`. A `LanedMempool` is composed of several distinct `Lanes` -that have the ability to store their own transactions. The `LanedMempool` will +After a transaction is verified in CheckTx, it will attempt to be inserted +into the `LanedMempool`. A LanedMempool is composed of several distinct `Lanes` +that have the ability to store their own transactions. The LanedMempool will insert the transaction into all lanes that will accept it. The criteria for whether a lane will accept a transaction is defined by the lane's -`MatchHandler`. The default implementation of a `MatchHandler` will accept all transactions. +`MatchHandler`. The default implementation of a MatchHandler will accept all transactions. When a new block is proposed, the `PrepareProposalHandler` will iteratively call `PrepareLane` on each lane (in the order in which they are defined in the -`LanedMempool`). The `PrepareLane` method is anaolgous to `PrepareProposal`. Calling -`PrepareLane` on a lane will trigger the lane to reap transactions from its mempool +LanedMempool). The PrepareLane method is anaolgous to PrepareProposal. Calling +PrepareLane on a lane will trigger the lane to reap transactions from its mempool and add them to the proposal (given they are valid respecting the verification rules of the lane). When proposals need to be verified in `ProcessProposal`, the `ProcessProposalHandler` defined in `abci/abci.go` will call `ProcessLane` on each lane in the same order -as they were called in the `PrepareProposalHandler`. Each subsequent call to -`ProcessLane` will filter out transactions that belong to previous lanes. A given -lane's `ProcessLane` will only verify transactions that belong to that lane. +as they were called in the PrepareProposalHandler. Each subsequent call to +ProcessLane will filter out transactions that belong to previous lanes. A given +lane's ProcessLane will only verify transactions that belong to that lane. > **Scenario** > -> Let's say we have a `LanedMempool` composed of two lanes: LaneA and LaneB. -> LaneA is defined first in the `LanedMempool` and LaneB is defined second. +> Let's say we have a LanedMempool composed of two lanes: LaneA and LaneB. +> LaneA is defined first in the LanedMempool and LaneB is defined second. > LaneA contains transactions Tx1 and Tx2 and LaneB contains transactions > Tx3 and Tx4. -When a new block needs to be proposed, the `PrepareProposalHandler` will call -`PrepareLane` on LaneA first and LaneB second. When `PrepareLane` is called +When a new block needs to be proposed, the PrepareProposalHandler will call +PrepareLane on LaneA first and LaneB second. When PrepareLane is called on LaneA, LaneA will reap transactions from its mempool and add them to the proposal. Same applies for LaneB. Say LaneA reaps transactions Tx1 and Tx2 and LaneB reaps transactions Tx3 and Tx4. This gives us a proposal composed @@ -80,9 +80,9 @@ of the following: * Tx1, Tx2, Tx3, Tx4 -When the `ProcessProposalHandler` is called, it will call `ProcessLane` on LaneA +When the ProcessProposalHandler is called, it will call ProcessLane on LaneA with the proposal composed of Tx1, Tx2, Tx3, and Tx4. LaneA will then verify Tx1 and Tx2 and return the remaining transactions - Tx3 and Tx4. -The `ProcessProposalHandler` will then call `ProcessLane` on LaneB with the +The ProcessProposalHandler will then call ProcessLane on LaneB with the remaining transactions - Tx3 and Tx4. LaneB will then verify Tx3 and Tx4 and return no remaining transactions. \ No newline at end of file From e6e8c077e912bc5cdc33fecd46864061aba01751 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 18:57:59 -0400 Subject: [PATCH 29/41] mev lane update --- README.md | 2 +- block/README.md | 202 --------------------- lanes/free/README.md | 3 +- lanes/mev/README.md | 371 ++++++++++++++++++++++++++------------- lanes/standard/README.md | 3 +- 5 files changed, 256 insertions(+), 325 deletions(-) delete mode 100644 block/README.md diff --git a/README.md b/README.md index c769bd1..ca77dc2 100644 --- a/README.md +++ b/README.md @@ -85,4 +85,4 @@ with the proposal composed of Tx1, Tx2, Tx3, and Tx4. LaneA will then verify Tx1 and Tx2 and return the remaining transactions - Tx3 and Tx4. The ProcessProposalHandler will then call ProcessLane on LaneB with the remaining transactions - Tx3 and Tx4. LaneB will then verify Tx3 and Tx4 -and return no remaining transactions. \ No newline at end of file +and return no remaining transactions. diff --git a/block/README.md b/block/README.md deleted file mode 100644 index 9c6082d..0000000 --- a/block/README.md +++ /dev/null @@ -1,202 +0,0 @@ -# Block SDK 🧱 - -> 🤓 Learn and read all about how proposals are constructed and verified using -> the Block SDK - -## 📖 Overview - -The Block SDK is a framework for building smarter blocks. The Block SDK is built -harnessing the power of ABCI++ which is a new ABCI implementation that allows -for more complex and expressive applications to be built on top of the Cosmos SDK. -The process of building and verifiying proposals can be broken down into two -distinct parts: - -1. Preparing a proposal during `PrepareProposal`. -2. Processing a proposal during `ProcessProposal`. - -The Block SDK provides a framework for building and verifying proposals by -segmenting a single block into multiple lanes. Each lane can be responsible for -proposing and verifying specific types of transaction. The Block SDK provides -a default implementation of a lane that can be used to build and verify proposals -similar to how they are built and verified in the Cosmos SDK today while also -providing a framework for building more complex lanes that can be used to build -and verify much more complex proposals. - -## 🤔 How does it work - -### 🔁 Transaction Lifecycle - -The best way to understand how lanes work is to first understand the lifecycle -of a transaction. A transaction begins its lifecycle when it is first signed and -broadcasted to a chain. After it is broadcasted to a validator, it will be checked -in `CheckTx` by the base application. If the transaction is valid, it will be -inserted into the applications mempool. - -The transaction then waits in the mempool until a new block needs to be proposed. -When a new block needs to be proposed, the application will call `PrepareProposal` -(which is a new ABCI++ addition) to request a new block from the current -proposer. The proposer will look at what transactions currently waiting to -be included in a block by looking at their mempool. The proposer will then -iteratively select transactions until the block is full. The proposer will then -send the block to other validators in the network. - -When a validator receives a proposed block, the validator will first want to -verify the contents of the block before signing off on it. The validator will -call `ProcessProposal` to verify the contents of the block. If the block is -valid, the validator will sign off on the block and broadcast their vote to the -network. If the block is invalid, the validator will reject the block. Once a -block is accepted by the network, it is committed and the transactions that -were included in the block are removed from the validator's mempool (as they no -longer need to be considered). - -### 🛣️ Lane Lifecycle - -After a transaction is verified in `CheckTx`, it will attempt to be inserted -into the `LanedMempool`. A `LanedMempool` is composed of several distinct `Lanes` -that have the ability to store their own transactions. The `LanedMempool` will -insert the transaction into all lanes that will accept it. The criteria for -whether a lane will accept a transaction is defined by the lane's -`MatchHandler`. The default implementation of a `MatchHandler` will accept all transactions. - - -When a new block is proposed, the `PrepareProposalHandler` will iteratively call -`PrepareLane` on each lane (in the order in which they are defined in the -`LanedMempool`). The `PrepareLane` method is anaolgous to `PrepareProposal`. Calling -`PrepareLane` on a lane will trigger the lane to reap transactions from its mempool -and add them to the proposal (given they are valid respecting the verification rules -of the lane). - -When proposals need to be verified in `ProcessProposal`, the `ProcessProposalHandler` -defined in `abci/abci.go` will call `ProcessLane` on each lane in the same order -as they were called in the `PrepareProposalHandler`. Each subsequent call to -`ProcessLane` will filter out transactions that belong to previous lanes. A given -lane's `ProcessLane` will only verify transactions that belong to that lane. - -> **Scenario** -> -> Let's say we have a `LanedMempool` composed of two lanes: `LaneA` and `LaneB`. -> `LaneA` is defined first in the `LanedMempool` and `LaneB` is defined second. -> `LaneA` contains transactions `Tx1` and `Tx2` and `LaneB` contains transactions -> `Tx3` and `Tx4`. - - -When a new block needs to be proposed, the `PrepareProposalHandler` will call -`PrepareLane` on `LaneA` first and `LaneB` second. When `PrepareLane` is called -on `LaneA`, `LaneA` will reap transactions from its mempool and add them to the -proposal. Same applies for `LaneB`. Say `LaneA` reaps transactions `Tx1` and `Tx2` -and `LaneB` reaps transactions `Tx3` and `Tx4`. This gives us a proposal composed -of the following: - -* `Tx1`, `Tx2`, `Tx3`, `Tx4` - -When the `ProcessProposalHandler` is called, it will call `ProcessLane` on `LaneA` -with the proposal composed of `Tx1`, `Tx2`, `Tx3`, and `Tx4`. `LaneA` will then -verify `Tx1` and `Tx2` and return the remaining transactions - `Tx3` and `Tx4`. -The `ProcessProposalHandler` will then call `ProcessLane` on `LaneB` with the -remaining transactions - `Tx3` and `Tx4`. `LaneB` will then verify `Tx3` and `Tx4` -and return no remaining transactions. - -## 🏗️ Setup - -> **Note** -> -> For a more in depth example of how to use the Block SDK, check out our -> example application in `block-sdk/tests/app/app.go`. - -### 📦 Dependencies - -The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently -compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. - -### 📥 Installation - -To install the Block SDK, run the following command: - -```bash -go get github.com/skip-mev/block-sdk/abci -``` - -### 📚 Usage - -First determine the set of lanes that you want to use in your application. The available -lanes can be found in our **Lane App Store** in `block-sdk/lanes`. In your base -application, you will need to create a `LanedMempool` composed of the lanes that -you want to use. You will also need to create a `PrepareProposalHandler` and a -`ProcessProposalHandler` that will be responsible for preparing and processing -proposals respectively. - -```golang -// 1. Create the lanes. -// -// 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. -// -// For more information on how to utilize the LaneConfig please -// visit the README in block-sdk/block/base. -// -// MEV lane hosts an action at the top of the block. -mevConfig := constructor.LaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: math.LegacyZeroDec(), - MaxTxs: 0, -} -mevLane := mev.NewMEVLane( - mevConfig, - mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), -) - -// Free lane allows transactions to be included in the next block for free. -freeConfig := constructor.LaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: math.LegacyZeroDec(), - MaxTxs: 0, -} -freeLane := free.NewFreeLane( - freeConfig, - constructor.DefaultTxPriority(), - free.DefaultMatchHandler(), -) - -// Default lane accepts all other transactions. -defaultConfig := constructor.LaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: math.LegacyZeroDec(), - MaxTxs: 0, -} -defaultLane := base.NewStandardLane(defaultConfig) - -// Set the lanes into the mempool. -lanes := []block.Lane{ - mevLane, - freeLane, - defaultLane, -} -mempool := block.NewLanedMempool(app.Logger(), true, lanes...) -app.App.SetMempool(mempool) - -... - -anteHandler := NewAnteHandler(options) - -// Set the lane ante handlers on the lanes. -for _, lane := range lanes { - lane.SetAnteHandler(anteHandler) -} -app.App.SetAnteHandler(anteHandler) - -// Set the abci handlers on base app -proposalHandler := abci.NewProposalHandler( - app.Logger(), - app.TxConfig().TxDecoder(), - lanes, -) -app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler()) -app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) -``` diff --git a/lanes/free/README.md b/lanes/free/README.md index 165a579..42816a5 100644 --- a/lanes/free/README.md +++ b/lanes/free/README.md @@ -28,8 +28,7 @@ compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. To install the Block SDK, run the following command: ```bash -$ go get github.com/skip-mev/block-sdk/abci -$ go get github.com/skip-mev/block-sdk/lanes/free +$ go install github.com/skip-mev/block-sdk ``` ### 📚 Usage diff --git a/lanes/mev/README.md b/lanes/mev/README.md index 4c7c701..b8d3b0f 100644 --- a/lanes/mev/README.md +++ b/lanes/mev/README.md @@ -17,144 +17,279 @@ type, called a MsgAuctionBid, that allows the submitter to execute multiple transactions at the top of the block atomically (atomically = directly next to each other). -## 🏗️ Setup +## Install -> **Note** -> -> For a more in depth example of how to use the Block SDK, check out our -> example application in `block-sdk/tests/app/app.go`. - -### 📦 Dependencies - -The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently -compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. - -### 📥 Installation - -To install the Block SDK, run the following command: - -```bash -$ go get github.com/skip-mev/block-sdk/abci -$ go get github.com/skip-mev/block-sdk/lanes/mev +```shell +$ go install github.com/skip-mev/block-sdk ``` -### 📚 Usage +## Setup -1. First determine the set of lanes that you want to use in your application. The -available lanes can be found in our **Lane App Store** in `block-sdk/lanes`. In -your base application, you will need to create a `LanedMempool` composed of the -lanes that you want to use. -2. Next, order the lanes by priority. The first lane is the highest priority lane -and the last lane is the lowest priority lane. **It is recommended that the first -lane is the MEV lane as the top of block is the most valuable block space.** -3. You will also need to create a `PrepareProposalHandler` and a -`ProcessProposalHandler` that will be responsible for preparing and processing -proposals respectively. Configure the order of the lanes in the -`PrepareProposalHandler` and `ProcessProposalHandler` to match the order of the -lanes in the `LanedMempool`. +> This set up guide will walk you through the process of setting up a POB +> application. In particular, we will configure an application with the +> following features: +> +>* MEV lane (auction lane). This will create an MEV lane where users can bid to +> have their transactions executed at the top of the block. +>* Free lane. This will create a free lane where users can submit transactions +> that will be executed for free (no fees). +>* Default lane. This will create a default lane where users can submit +> transactions that will be executed with the default app logic. +>* Builder module that pairs with the auction lane to process auction +> transactions and distribute revenue to the auction house. -```golang -import ( - "github.com/skip-mev/block-sdk/abci" - "github.com/skip-mev/block-sdk/lanes/mev" -) +1. Import the necessary dependencies into your application. This includes the + Block SDK proposal handlers + mempool, keeper, builder types, and builder + module. This tutorial will go into more detail into each of the dependencies. -... -``` - -```golang -func NewApp() { + ```go + import ( ... - // 1. Create the lanes. - // - // 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. - // - // For more information on how to utilize the LaneConfig please - // visit the README in block-sdk/block/base. - // - // MEV lane hosts an action at the top of the block. - mevConfig := constructor.LaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: math.LegacyZeroDec(), - MaxTxs: 0, - } - mevLane := mev.NewMEVLane( - mevConfig, - mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), - ) + "github.com/skip-mev/pob/block-sdk" + "github.com/skip-mev/pob/block-sdk/abci" + "github.com/skip-mev/pob/block-sdk/lanes/auction" + "github.com/skip-mev/pob/block-sdk/lanes/base" + "github.com/skip-mev/pob/block-sdk/lanes/free" + buildermodule "github.com/skip-mev/block-sdk/x/builder" + builderkeeper "github.com/skip-mev/block-sdk/x/builder/keeper" + ... + ) + ``` - // Free lane allows transactions to be included in the next block for free. - freeConfig := constructor.LaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: math.LegacyZeroDec(), - MaxTxs: 0, - } - freeLane := free.NewFreeLane( - freeConfig, - constructor.DefaultTxPriority(), - free.DefaultMatchHandler(), - ) +2. Add your module to the the `AppModuleBasic` manager. This manager is in + charge of setting up basic, non-dependent module elements such as codec + registration and genesis verification. This will register the special + `MsgAuctionBid` message. When users want to bid for top of block execution, + they will submit a transaction - which we call an auction transaction - that + includes a single `MsgAuctionBid`. We prevent any other messages from being + included in auction transaction to prevent malicious behavior - such as front + running or sandwiching. - // Default lane accepts all other transactions. - defaultConfig := constructor.LaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: math.LegacyZeroDec(), - MaxTxs: 0, - } - defaultLane := base.NewStandardLane(defaultConfig) + ```go + var ( + ModuleBasics = module.NewBasicManager( + ... + buildermodule.AppModuleBasic{}, + ) + ... + ) + ``` - // 2. Set up the relateive priority of lanes - lanes := []block.Lane{ - mevLane, - freeLane, - defaultLane, - } - mempool := block.NewLanedMempool(app.Logger(), true, lanes...) - app.App.SetMempool(mempool) +3. The builder `Keeper` is POB's gateway to processing special `MsgAuctionBid` + messages that allow users to participate in the top of block auction, distribute + revenue to the auction house, and ensure the validity of auction transactions. - ... + a. First add the keeper to the app's struct definition. We also want to add + MEV lane's custom checkTx handler to the app's struct definition. This will + allow us to override the default checkTx handler to process bid transactions + before they are inserted into the mempool. NOTE: The custom handler is + required as otherwise the auction can be held hostage by a malicious + users. - // 3. Set up the ante handler. - anteDecorators := []sdk.AnteDecorator{ - ante.NewSetUpContextDecorator(), + ```go + type App struct { ... - utils.NewIgnoreDecorator( - ante.NewDeductFeeDecorator( - options.BaseOptions.AccountKeeper, - options.BaseOptions.BankKeeper, - options.BaseOptions.FeegrantKeeper, - options.BaseOptions.TxFeeChecker, - ), - options.FreeLane, - ), + // BuilderKeeper is the keeper that handles processing auction transactions + BuilderKeeper builderkeeper.Keeper + + // Custom checkTx handler + checkTxHandler mev.CheckTx + } + ``` + + b. Add the builder module to the list of module account permissions. This will + instantiate the builder module account on genesis. + + ```go + maccPerms = map[string][]string{ + builder.ModuleName: nil, ... - } + } + ``` - anteHandler := sdk.ChainAnteDecorators(anteDecorators...) + c. Instantiate the blockbuster mempool with the application's desired lanes. - // Set the lane ante handlers on the lanes. - for _, lane := range lanes { - lane.SetAnteHandler(anteHandler) - } - app.App.SetAnteHandler(anteHandler) + ```go + // 1. Create the lanes. + // + // 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. + // + // For more information on how to utilize the LaneConfig please + // visit the README in block-sdk/block/base. + // + // MEV lane hosts an auction at the top of the block. + mevConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, + } + mevLane := mev.NewMEVLane( + mevConfig, + mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), + ) - // 4. Set the abci handlers on base app + // Free lane allows transactions to be included in the next block for free. + freeConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, + } + freeLane := free.NewFreeLane( + freeConfig, + constructor.DefaultTxPriority(), + free.DefaultMatchHandler(), + ) + + // Standard lane accepts all other transactions. + defaultConfig := constructor.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, + } + defaultLane := base.NewStandardLane(defaultConfig) + + // 2. Set up the relateive priority of lanes + lanes := []block.Lane{ + mevLane, + freeLane, + defaultLane, + } + mempool := block.NewLanedMempool(app.Logger(), true, lanes...) + app.App.SetMempool(mempool) + ``` + + d. Instantiate the antehandler chain for the application with awareness of the + blockbuster mempool. This will allow the application to verify the validity + of a transaction respecting the desired logic of a given lane. In this walkthrough, + we want the `FeeDecorator` to be ignored for all transactions that should + belong to the free lane. Additionally, we want to add the `x/builder` + module's `AuctionDecorator` to the ante-handler chain. The `AuctionDecorator` + is an AnteHandler decorator that enforces various chain configurable MEV rules. + + ```go + import ( + ... + "github.com/skip-mev/pob/blockbuster" + "github.com/skip-mev/pob/blockbuster/utils" + builderante "github.com/skip-mev/pob/x/builder/ante" + ... + ) + + anteDecorators := []sdk.AnteDecorator{ + ante.NewSetUpContextDecorator(), + ... + utils.NewIgnoreDecorator( + ante.NewDeductFeeDecorator( + options.BaseOptions.AccountKeeper, + options.BaseOptions.BankKeeper, + options.BaseOptions.FeegrantKeeper, + options.BaseOptions.TxFeeChecker, + ), + options.FreeLane, + ), + ... + builderante.NewBuilderDecorator( + options.BuilderKeeper, + options.TxEncoder, + options.TOBLane, + options.Mempool, + ), + } + + anteHandler := sdk.ChainAnteDecorators(anteDecorators...) + app.SetAnteHandler(anteHandler) + + // Set the antehandlers on the lanes. + for _, lane := range lanes { + lane.SetAnteHandler(anteHandler) + } + app.App.SetAnteHandler(anteHandler) + ``` + + e. Instantiate the builder keeper, store keys, and module manager. Note, be + sure to do this after all the required keeper dependencies have been instantiated. + + ```go + keys := storetypes.NewKVStoreKeys( + buildertypes.StoreKey, + ... + ) + + ... + app.BuilderKeeper := builderkeeper.NewKeeper( + appCodec, + keys[buildertypes.StoreKey], + app.AccountKeeper, + app.BankKeeper, + app.DistrKeeper, + app.StakingKeeper, + authtypes.NewModuleAddress(govv1.ModuleName).String(), + ) + + + app.ModuleManager = module.NewManager( + builder.NewAppModule(appCodec, app.BuilderKeeper), + ... + ) + ``` + + e. Configure the proposal/checkTx handlers on base app. + + ```go + + // Create the proposal handler that will be used to build and validate blocks. proposalHandler := abci.NewProposalHandler( - app.Logger(), - app.TxConfig().TxDecoder(), - lanes, + app.Logger(), + app.txConfig.TxDecoder(), + mempool, ) app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler()) app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) + + // Set the custom CheckTx handler on BaseApp. + checkTxHandler := abci.NewCheckTxHandler( + app.App, + app.txConfig.TxDecoder(), + tobLane, + anteHandler, + app.ChainID(), + ) + app.SetCheckTx(checkTxHandler.CheckTx()) ... -} -``` + + + func (app *TestApp) CheckTx(req cometabci.RequestCheckTx) + cometabci.ResponseCheckTx { + return app.checkTxHandler(req) + } + + // SetCheckTx sets the checkTxHandler for the app. + func (app *TestApp) SetCheckTx(handler abci.CheckTx) { + app.checkTxHandler = handler + } + ``` + + f. Finally, update the app's `InitGenesis` order and ante-handler chain. + + ```go + genesisModuleOrder := []string{ + buildertypes.ModuleName, + ..., + } + ``` + +## Params + +Note, before building or upgrading the application, make sure to initialize the +escrow address for POB in the parameters of the module. The default parameters +initialize the escrow address to be the module account address. diff --git a/lanes/standard/README.md b/lanes/standard/README.md index f466958..d194863 100644 --- a/lanes/standard/README.md +++ b/lanes/standard/README.md @@ -35,8 +35,7 @@ compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. To install the Block SDK, run the following command: ```bash -$ go get github.com/skip-mev/block-sdk/abci -$ go get github.com/skip-mev/block-sdk/lanes/standard +$ go install github.com/skip-mev/block-sdk ``` ### 📚 Usage From 6529c3960554850b9e124a0de24dd846ab894f3f Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 19:00:00 -0400 Subject: [PATCH 30/41] docs --- lanes/mev/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lanes/mev/README.md b/lanes/mev/README.md index b8d3b0f..11c9a66 100644 --- a/lanes/mev/README.md +++ b/lanes/mev/README.md @@ -107,7 +107,7 @@ $ go install github.com/skip-mev/block-sdk } ``` - c. Instantiate the blockbuster mempool with the application's desired lanes. + c. Instantiate the blockbuster mempool with the application's desired lanes. ```go // 1. Create the lanes. From a5503845690b11765942f391701535b1facfb60a Mon Sep 17 00:00:00 2001 From: David Terpay Date: Wed, 16 Aug 2023 10:51:55 -0400 Subject: [PATCH 31/41] more docs --- README.md | 113 +++++------ block/base/README.md | 452 ------------------------------------------- 2 files changed, 45 insertions(+), 520 deletions(-) delete mode 100644 block/base/README.md diff --git a/README.md b/README.md index ca77dc2..17d1904 100644 --- a/README.md +++ b/README.md @@ -9,80 +9,57 @@ [![License: Apache-2.0](https://img.shields.io/github/license/skip-mev/pob.svg?style=flat-square)](https://github.com/skip-mev/pob/blob/main/LICENSE) [![Lines Of Code](https://img.shields.io/tokei/lines/github/skip-mev/pob?style=flat-square)](https://github.com/skip-mev/pob) -## 📖 Overview +### 🤔 What is the Block SDK? -The Block SDK is a set of Cosmos SDK and ABCI++ primitives that allow chains to fully customize blocks to specific use cases. It turns your chain's blocks into a **transaction highway** consisting of individual lanes with their own special functionality. - -## 🤔 How does it work - -### 🔁 Transaction Lifecycle - -The best way to understand how lanes work is to first understand the lifecycle -of a transaction. A transaction begins its lifecycle when it is first signed and -broadcasted to a chain. After it is broadcasted to a validator, it will be checked -in `CheckTx` by the base application. If the transaction is valid, it will be -inserted into the applications mempool. - -The transaction then waits in the mempool until a new block needs to be proposed. -When a new block needs to be proposed, the application will call `PrepareProposal` -(which is a new ABCI++ addition) to request a new block from the current -proposer. The proposer will look at what transactions currently waiting to -be included in a block by looking at their mempool. The proposer will then -iteratively select transactions until the block is full. The proposer will then -send the block to other validators in the network. - -When a validator receives a proposed block, the validator will first want to -verify the contents of the block before signing off on it. The validator will -call `ProcessProposal` to verify the contents of the block. If the block is -valid, the validator will sign off on the block and broadcast their vote to the -network. If the block is invalid, the validator will reject the block. Once a -block is accepted by the network, it is committed and the transactions that -were included in the block are removed from the validator's mempool (as they no -longer need to be considered). - -### 🛣️ Lane Lifecycle - -After a transaction is verified in CheckTx, it will attempt to be inserted -into the `LanedMempool`. A LanedMempool is composed of several distinct `Lanes` -that have the ability to store their own transactions. The LanedMempool will -insert the transaction into all lanes that will accept it. The criteria for -whether a lane will accept a transaction is defined by the lane's -`MatchHandler`. The default implementation of a MatchHandler will accept all transactions. +> **🌐 The Block SDK is a toolkit for building customized blocks** +> The Block SDK is a set of Cosmos SDK and ABCI++ primitives that allow chains to fully customize blocks to specific use cases. It turns your chain's blocks into a **`highway`** consisting of individual **`lanes`** with their own special functionality. -When a new block is proposed, the `PrepareProposalHandler` will iteratively call -`PrepareLane` on each lane (in the order in which they are defined in the -LanedMempool). The PrepareLane method is anaolgous to PrepareProposal. Calling -PrepareLane on a lane will trigger the lane to reap transactions from its mempool -and add them to the proposal (given they are valid respecting the verification rules -of the lane). +Skip has built out a number of plug-and-play `lanes` on the SDK that your protocol can use, including in-protocol MEV recapture and Oracles! Additionally, the Block SDK can be extended to add **your own custom `lanes`** to configure your blocks to exactly fit your application needs. -When proposals need to be verified in `ProcessProposal`, the `ProcessProposalHandler` -defined in `abci/abci.go` will call `ProcessLane` on each lane in the same order -as they were called in the PrepareProposalHandler. Each subsequent call to -ProcessLane will filter out transactions that belong to previous lanes. A given -lane's ProcessLane will only verify transactions that belong to that lane. +### ❌ Problems: Blocks are not Customizable -> **Scenario** -> -> Let's say we have a LanedMempool composed of two lanes: LaneA and LaneB. -> LaneA is defined first in the LanedMempool and LaneB is defined second. -> LaneA contains transactions Tx1 and Tx2 and LaneB contains transactions -> Tx3 and Tx4. +Most Cosmos chains today utilize standard `CometBFT` block construction - which is too limited. + +- The standard `CometBFT` block building is susceptible to MEV-related issues, such as front-running and sandwich attacks, since proposers have monopolistic rights on ordering and no verification of good behavior. MEV that is created cannot be redistributed to the protocol. +- The standard `CometBFT` block building uses a one-size-fits-all approach, which can result in inefficient transaction processing for specific applications or use cases and sub-optimal fee markets. +- Transactions tailored for specific applications may need custom prioritization, ordering or validation rules that the mempool is otherwise unaware of because transactions within a block are currently in-differentiable when a blockchain might want them to be. + +### ✅ Solution: The Block SDK + +You can think of the Block SDK as a **transaction `highway` system**, where each +`lane` on the highway serves a specific purpose and has its own set of rules and +traffic flow. + +In the Block SDK, each `lane` has its own set of rules and transaction flow management systems. + +* A `lane` is what we might traditionally consider to be a standard mempool + where transaction **_validation_**, **_ordering_** and **_prioritization_** for + contained transactions are shared. +* `lanes` implement a **standard interface** that allows each individual `lane` to + propose and validate a portion of a block. +* `lanes` are ordered with each other, configurable by developers. All `lanes` + together define the desired block structure of a chain. + +### ✨ Block SDK Use Cases + +A block with separate `lanes` can be used for: + +1. **MEV mitigation**: a top of block lane could be designed to create an in-protocol top-of-block auction (as we are doing with POB) to recapture MEV in a transparent and governable way. +2. **Free/reduced fee txs**: transactions with certain properties (e.g. from trusted accounts or performing encouraged actions) could leverage a free lane to reward behavior. +3. **Dedicated oracle space** Oracles could be included before other kinds of transactions to ensure that price updates occur first, and are not able to be sandwiched or manipulated. +4. **Orderflow auctions**: an OFA lane could be constructed such that order flow providers can have their submitted transactions bundled with specific backrunners, to guarantee MEV rewards are attributed back to users. Imagine MEV-share but in protocol. +5. **Enhanced and customizable privacy**: privacy-enhancing features could be introduced, such as threshold encrypted lanes, to protect user data and maintain privacy for specific use cases. +6. **Fee market improvements**: one or many fee markets - such as EIP-1559 - could be easily adopted for different lanes (potentially custom for certain dApps). Each smart contract/exchange could have its own fee market or auction for transaction ordering. +7. **Congestion management**: segmentation of transactions to lanes can help mitigate network congestion by capping usage of certain applications and tailoring fee markets. -When a new block needs to be proposed, the PrepareProposalHandler will call -PrepareLane on LaneA first and LaneB second. When PrepareLane is called -on LaneA, LaneA will reap transactions from its mempool and add them to the -proposal. Same applies for LaneB. Say LaneA reaps transactions Tx1 and Tx2 -and LaneB reaps transactions Tx3 and Tx4. This gives us a proposal composed -of the following: +### 📚 Block SDK Documentation -* Tx1, Tx2, Tx3, Tx4 +#### Lane App Store -When the ProcessProposalHandler is called, it will call ProcessLane on LaneA -with the proposal composed of Tx1, Tx2, Tx3, and Tx4. LaneA will then -verify Tx1 and Tx2 and return the remaining transactions - Tx3 and Tx4. -The ProcessProposalHandler will then call ProcessLane on LaneB with the -remaining transactions - Tx3 and Tx4. LaneB will then verify Tx3 and Tx4 -and return no remaining transactions. +To read more about Skip's pre-built `lanes` and how to use them, check out the [Lane App Store](). + +#### Lane Development + +To read more about how to build your own custom `lanes`, check out the [Build Your Own Lane](). diff --git a/block/base/README.md b/block/base/README.md deleted file mode 100644 index 51d10ae..0000000 --- a/block/base/README.md +++ /dev/null @@ -1,452 +0,0 @@ -# 🎨 Base Lane - -> 🏗️ Build your own lane in less than 10 minutes using the Base Lane - -## 💡 Overview - -The Base Lane is a generic implementation of a lane. It comes out of the -box with default implementations for all the required interfaces. It is meant to -be used as a starting point for building your own lane. - -## 🤔 How to use it - -> **Default Implementations** -> -> There are default implementations for all of the below which can be found in -> the `block/base` package. It is highly recommended that developers overview -> the default implementations before building their own lane. - -There are **three** critical components to building a custom lane using the lane -constructor: - -1. `LaneConfig` - The lane configuration which determines the basic properties -of the lane including the maximum block space that the lane can fill up. -2. `LaneMempool` - The lane mempool which is responsible for storing -transactions that have been verified and are waiting to be included in proposals. -3. `MatchHandler` - This is responsible for determining whether a transaction should -belong to this lane. -4. [**OPTIONAL**] `PrepareLaneHandler` - Allows developers to define their own -handler to customize the how transactions are verified and ordered before they -are included into a proposal. -5. [**OPTIONAL**] `CheckOrderHandler` - Allows developers to define their own -handler that will run any custom checks on whether transactions included in -block proposals are in the correct order (respecting the ordering rules of the -lane and the ordering rules of the other lanes). -6. [**OPTIONAL**] `ProcessLaneHandler` - Allows developers to define their own -handler for processing transactions that are included in block proposals. - - -### 1. 📝 Lane Config - -The lane config (`LaneConfig`) is a simple configuration -object that defines the desired amount of block space the lane should -utilize when building a proposal, an antehandler that is used to verify -transactions as they are added/verified to/in a proposal, and more. By default, -we recommend that user's pass in all of the base apps configurations (txDecoder, -logger, etc.). A sample `LaneConfig` might look like the following: - -```golang -config := block.LaneConfig{ - Logger: app.Logger(), - TxDecoder: app.TxDecoder(), - TxEncoder: app.TxEncoder(), - AnteHandler: app.AnteHandler(), - MaxTxs: 0, - MaxBlockSpace: math.LegacyZeroDec(), - IgnoreList: []block.Lane{}, -} -``` - -The three most important parameters to set are the `AnteHandler`, `MaxTxs`, and -`MaxBlockSpace`. - -#### **AnteHandler** - -With the default implementation, the `AnteHandler` is responsible for verifying -transactions as they are being considered for a new proposal or are being processed -in a proposed block. We recommend user's utilize the same antehandler chain that -is used in the base app. If developers want a certain `AnteDecorator` to be -ignored if it qualifies for a given lane, they can do so by using the `NewIgnoreDecorator` -defined in `block/utils/ante.go`. - -For example, a free lane might want to ignore the `DeductFeeDecorator` so that it's -transactions are not charged any fees. Where ever the `AnteHandler` is defined, -we could add the following to ignore the `DeductFeeDecorator`: - -```golang -anteDecorators := []sdk.AnteDecorator{ - ante.NewSetUpContextDecorator(), - ..., - utils.NewIgnoreDecorator( - ante.NewDeductFeeDecorator( - options.BaseOptions.AccountKeeper, - options.BaseOptions.BankKeeper, - options.BaseOptions.FeegrantKeeper, - options.BaseOptions.TxFeeChecker, - ), - options.FreeLane, - ), - ..., -} -``` - -Anytime a transaction that qualifies for the free lane is being processed, the -`DeductFeeDecorator` will be ignored and no fees will be deducted! - - -#### **MaxTxs** - -This sets the maximum number of transactions allowed in the mempool with -the semantics: - -* if `MaxTxs` == 0, there is no cap on the number of transactions in the mempool -* if `MaxTxs` > 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 `MaxTxs` < 0, `Insert` is a no-op. - -#### **MaxBlockSpace** - -MaxBlockSpace is the maximum amount of block space that the lane will attempt to -fill when building a proposal. This parameter may be useful lanes that should be -limited (such as a free or onboarding lane) in space usage. Setting this to 0 -will allow the lane to fill the block with as many transactions as possible. - -If a block proposal request has a `MaxTxBytes` of 1000 and the lane has a -`MaxBlockSpace` of 0.5, the lane will attempt to fill the block with 500 bytes. - -#### **[OPTIONAL] IgnoreList** - -`IgnoreList` defines the list of lanes to ignore when processing transactions. -For example, say there are two lanes: default and free. The free lane is -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). - - -### 2. 🗄️ LaneMempool - -This is the data structure that is responsible for storing transactions -as they are being verified and are waiting to be included in proposals. `block/base/mempool.go` -provides an out-of-the-box implementation that should be used as a starting -point for building out the mempool and should cover most use cases. To -utilize the mempool, you must implement a `TxPriority[C]` struct that does the -following: - -* Implements a `GetTxPriority` method that returns the priority (as defined -by the type `[C]`) of a given transaction. -* Implements a `Compare` method that returns the relative priority of two -transactions. If the first transaction has a higher priority, the method -should return -1, if the second transaction has a higher priority the method -should return 1, otherwise the method should return 0. -* Implements a `MinValue` method that returns the minimum priority value -that a transaction can have. - -The default implementation can be found in `block/base/mempool.go`. What -if we wanted to prioritize transactions by the amount they have staked on a chain? -Well we could do something like the following: - -```golang -// CustomTxPriority returns a TxPriority that prioritizes transactions by the -// amount they have staked on chain. This means that transactions with a higher -// amount staked will be prioritized over transactions with a lower amount staked. -func (p *CustomTxPriority) CustomTxPriority() TxPriority[string] { - return TxPriority[string]{ - GetTxPriority: func(ctx context.Context, tx sdk.Tx) string { - // Get the signer of the transaction. - signer := p.getTransactionSigner(tx) - - // Get the total amount staked by the signer on chain. - // This is abstracted away in the example, but you can - // implement this using the staking keeper. - totalStake, err := p.getTotalStake(ctx, signer) - if err != nil { - return "" - } - - return totalStake.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: "", - } -} -``` - -#### Using a Custom TxPriority - -To utilize this new priority configuration in a lane, all you have to then do -is pass in the `TxPriority[C]` to the `NewLaneMempool` function. - -```golang -// Create the lane config -laneCfg := NewLaneConfig( - ... - MaxTxs: 100, - ... -) - -// Pseudocode for creating the custom tx priority -priorityCfg := NewPriorityConfig( - stakingKeeper, - accountKeeper, - ... -) - - -// define your mempool that orders transactions by on-chain stake -mempool := constructor.NewMempool[string]( - priorityCfg.CustomTxPriority(), - laneCfg.TxEncoder, - laneCfg.MaxTxs, -) - -// Initialize your lane with the mempool -lane := constructor.NewBaseLane( - laneCfg, - LaneName, - mempool, - constructor.DefaultMatchHandler(), -) -``` - -### 3. 🤝 MatchHandler - -`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. -The default implementation can be found in `block/base/handlers.go`. - -The match handler can be as custom as desired. Following the example above, if -we wanted to make a lane that only accepts transactions if they have a large -amount staked, we could do the following: - -```golang -// CustomMatchHandler returns a custom implementation of the MatchHandler. It -// matches transactions that have a large amount staked. These transactions -// will then be charged no fees at execution time. -// -// NOTE: This is a stateful check on the transaction. The details of how to -// implement this are abstracted away in the example, but you can implement -// this using the staking keeper. -func (h *Handler) CustomMatchHandler() block.MatchHandler { - return func(ctx sdk.Context, tx sdk.Tx) bool { - if !h.IsStakingTx(tx) { - return false - } - - signer, err := getTxSigner(tx) - if err != nil { - return false - } - - stakedAmount, err := h.GetStakedAmount(signer) - if err != nil { - return false - } - - // The transaction can only be considered for inclusion if the amount - // staked is greater than some predetermined threshold. - return stakeAmount.GT(h.Threshold) - } -} -``` - -#### Using a Custom MatchHandler - -If we wanted to create the lane using the custom match handler along with the -custom mempool, we could do the following: - -```golang -// Pseudocode for creating the custom match handler -handler := NewHandler( - stakingKeeper, - accountKeeper, - ... -) - -// define your mempool that orders transactions by on chain stake -mempool := constructor.NewMempool[string]( - priorityCfg.CustomTxPriority(), - cfg.TxEncoder, - cfg.MaxTxs, -) - -// Initialize your lane with the mempool -lane := constructor.NewBaseLane( - cfg, - LaneName, - mempool, - handler.CustomMatchHandler(), -) -``` - -### Summary on Steps 1-3 - -The following is a summary of the steps above: - -1. Create a custom `LaneConfig` struct that defines the configuration of the lane. -2. Create a custom `TxPriority[C]` struct to have a custom mempool that orders -transactions via a custom priority mechanism. -3. Create a custom `MatchHandler` that implements the `block.MatchHandler` to -have a custom lane that only accepts transactions that match a custom criteria. - -### [OPTIONAL] Steps 4-6 - -The remaining steps walk through the process of creating custom block -building/verification logic. The default implementation found in `block/base/handlers.go` -should fit most use cases. Please reference that file for more details on -the default implementation and whether it fits your use case. - -Implementing custom block building/verification logic is a bit more involved -than the previous steps and is a all or nothing approach. This means that if -you implement any of the handlers, you must implement all of them in most cases. -If you do not implement all of them, the lane may have unintended behavior. - -### 4. 🛠️ PrepareLaneHandler - -The `PrepareLaneHandler` is an optional field you can set on the lane constructor. -This handler is responsible for the transaction selection logic when a new proposal -is requested. - -The handler should return the following for a given lane: - -1. The transactions to be included in the block proposal. -2. The transactions to be removed from the lane's mempool. -3. An error if the lane is unable to prepare a block proposal. - -```golang -// 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) -``` - -The default implementation is simple. It will continue to select transactions -from its mempool under the following criteria: - -1. The transactions is not already included in the block proposal. -2. The transaction is valid and passes the AnteHandler check. -3. The transaction is not too large to be included in the block. - -If a more involved selection process is required, you can implement your own -`PrepareLaneHandler` and and set it after creating the lane constructor. - -```golang -// Pseudocode for creating the custom prepare lane handler -// This assumes that the CustomLane inherits from the constructor -// lane. -customLane := constructor.NewCustomLane( - cfg, - LaneName, - mempool, - handler.CustomMatchHandler(), -) - -// Set the custom PrepareLaneHandler on the lane -customLane.SetPrepareLaneHandler(customlane.PrepareLaneHandler()) -``` - -### 5. ✅ CheckOrderHandler - -The `CheckOrderHandler` is an optional field you can set on the lane constructor. -This handler is responsible for verifying the ordering of the transactions in the -block proposal that belong to the lane. - -```golang -// 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 -``` - -The default implementation is simple and utilizes the same `TxPriority` struct -that the mempool uses to determine if transactions are in order. The criteria -for determining if transactions are in order is as follows: - -1. The transactions are in order according to the `TxPriority` struct. i.e. any -two transactions (that match to the lane) `tx1` and `tx2` where `tx1` has a -higher priority than `tx2` should be ordered before `tx2`. -2. The transactions are contiguous. i.e. there are no transactions from other -lanes in between the transactions that belong to this lane. i.e. if `tx1` and -`tx2` belong to the lane, there should be no transactions from other lanes in -between `tx1` and `tx2`. - -If a more involved ordering process is required, you can implement your own -`CheckOrderHandler` and and set it after creating the lane constructor. - -```golang -// Pseudocode for creating the custom check order handler -// This assumes that the CustomLane inherits from the constructor -// lane. -customLane := constructor.NewCustomLane( - cfg, - LaneName, - mempool, - handler.CustomMatchHandler(), -) - -// Set the custom CheckOrderHandler on the lane -customLane.SetCheckOrderHandler(customlane.CheckOrderHandler()) -``` - - -### 6. 🆗 ProcessLaneHandler - -The `ProcessLaneHandler` is an optional field you can set on the lane constructor. -This handler is responsible for verifying the transactions in the block proposal -that belong to the lane. This handler is executed after the `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. This means that if the first transaction -does not belong to the lane, the remaining transactions should not belong to the -lane either. - - -```golang -// 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) -``` - -Given the invarients above, the default implementation is simple. It will -continue to verify transactions in the block proposal under the following -criteria: - -1. If a transaction matches to this lane, verify it and continue. If it is not -valid, return an error. -2. If a transaction does not match to this lane, return the remaining transactions -to the next lane to process. From 8c5353950ac799b435f21df89372fd56a637edac Mon Sep 17 00:00:00 2001 From: David Terpay Date: Wed, 16 Aug 2023 10:55:05 -0400 Subject: [PATCH 32/41] formatting --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 17d1904..5f5dd06 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,7 @@ ### 🤔 What is the Block SDK? -> **🌐 The Block SDK is a toolkit for building customized blocks** -> The Block SDK is a set of Cosmos SDK and ABCI++ primitives that allow chains to fully customize blocks to specific use cases. It turns your chain's blocks into a **`highway`** consisting of individual **`lanes`** with their own special functionality. +**🌐 The Block SDK is a toolkit for building customized blocks**. The Block SDK is a set of Cosmos SDK and ABCI++ primitives that allow chains to fully customize blocks to specific use cases. It turns your chain's blocks into a **`highway`** consisting of individual **`lanes`** with their own special functionality. Skip has built out a number of plug-and-play `lanes` on the SDK that your protocol can use, including in-protocol MEV recapture and Oracles! Additionally, the Block SDK can be extended to add **your own custom `lanes`** to configure your blocks to exactly fit your application needs. @@ -31,14 +30,14 @@ You can think of the Block SDK as a **transaction `highway` system**, where each `lane` on the highway serves a specific purpose and has its own set of rules and traffic flow. -In the Block SDK, each `lane` has its own set of rules and transaction flow management systems. +In the Block SDK, each lane has its own set of rules and transaction flow management systems. -* A `lane` is what we might traditionally consider to be a standard mempool +* A lane is what we might traditionally consider to be a standard mempool where transaction **_validation_**, **_ordering_** and **_prioritization_** for contained transactions are shared. -* `lanes` implement a **standard interface** that allows each individual `lane` to +* lanes implement a **standard interface** that allows each individual lane to propose and validate a portion of a block. -* `lanes` are ordered with each other, configurable by developers. All `lanes` +* lanes are ordered with each other, configurable by developers. All lanes together define the desired block structure of a chain. ### ✨ Block SDK Use Cases @@ -60,6 +59,10 @@ A block with separate `lanes` can be used for: To read more about Skip's pre-built `lanes` and how to use them, check out the [Lane App Store](). +#### How the Block SDK works + +To read more about how the Block SDK works, check out the [How it Works](). + #### Lane Development To read more about how to build your own custom `lanes`, check out the [Build Your Own Lane](). From fe172ffba318d54a9b7044c5a43ebb781aa2115b Mon Sep 17 00:00:00 2001 From: David Terpay Date: Wed, 16 Aug 2023 11:09:54 -0400 Subject: [PATCH 33/41] lint --- README.md | 12 ++++---- abci/abci_test.go | 6 ++-- block/mempool_test.go | 6 ++-- lanes/{standard => base}/README.md | 38 +++++++++++------------- lanes/{standard => base}/abci_test.go | 8 ++--- lanes/{standard => base}/base_test.go | 2 +- lanes/{standard => base}/lane.go | 12 ++++---- lanes/{standard => base}/mempool_test.go | 2 +- tests/app/app.go | 4 +-- x/builder/ante/ante_test.go | 10 +++---- 10 files changed, 48 insertions(+), 52 deletions(-) rename lanes/{standard => base}/README.md (78%) rename lanes/{standard => base}/abci_test.go (99%) rename lanes/{standard => base}/base_test.go (96%) rename lanes/{standard => base}/lane.go (75%) rename lanes/{standard => base}/mempool_test.go (99%) diff --git a/README.md b/README.md index 5f5dd06..ffadd3a 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,9 @@ Skip has built out a number of plug-and-play `lanes` on the SDK that your protoc Most Cosmos chains today utilize standard `CometBFT` block construction - which is too limited. -- The standard `CometBFT` block building is susceptible to MEV-related issues, such as front-running and sandwich attacks, since proposers have monopolistic rights on ordering and no verification of good behavior. MEV that is created cannot be redistributed to the protocol. -- The standard `CometBFT` block building uses a one-size-fits-all approach, which can result in inefficient transaction processing for specific applications or use cases and sub-optimal fee markets. -- Transactions tailored for specific applications may need custom prioritization, ordering or validation rules that the mempool is otherwise unaware of because transactions within a block are currently in-differentiable when a blockchain might want them to be. +* The standard `CometBFT` block building is susceptible to MEV-related issues, such as front-running and sandwich attacks, since proposers have monopolistic rights on ordering and no verification of good behavior. MEV that is created cannot be redistributed to the protocol. +* The standard `CometBFT` block building uses a one-size-fits-all approach, which can result in inefficient transaction processing for specific applications or use cases and sub-optimal fee markets. +* Transactions tailored for specific applications may need custom prioritization, ordering or validation rules that the mempool is otherwise unaware of because transactions within a block are currently in-differentiable when a blockchain might want them to be. ### ✅ Solution: The Block SDK @@ -57,12 +57,12 @@ A block with separate `lanes` can be used for: #### Lane App Store -To read more about Skip's pre-built `lanes` and how to use them, check out the [Lane App Store](). +To read more about Skip's pre-built `lanes` and how to use them, check out the [Lane App Store](https://docs.skip.money/chains/lanes/existing-lanes/default). #### How the Block SDK works -To read more about how the Block SDK works, check out the [How it Works](). +To read more about how the Block SDK works, check out the [How it Works](https://docs.skip.money/chains/how-it-works). #### Lane Development -To read more about how to build your own custom `lanes`, check out the [Build Your Own Lane](). +To read more about how to build your own custom `lanes`, check out the [Build Your Own Lane](https://docs.skip.money/chains/lanes/build-your-own-lane). diff --git a/abci/abci_test.go b/abci/abci_test.go index 090a167..dace5ed 100644 --- a/abci/abci_test.go +++ b/abci/abci_test.go @@ -16,9 +16,9 @@ import ( "github.com/skip-mev/pob/abci" "github.com/skip-mev/pob/block" "github.com/skip-mev/pob/block/base" + defaultlane "github.com/skip-mev/pob/lanes/base" "github.com/skip-mev/pob/lanes/free" "github.com/skip-mev/pob/lanes/mev" - "github.com/skip-mev/pob/lanes/standard" testutils "github.com/skip-mev/pob/testutils" "github.com/stretchr/testify/suite" ) @@ -724,7 +724,7 @@ func (s *ProposalsTestSuite) setUpAnteHandler(expectedExecution map[sdk.Tx]bool) return anteHandler } -func (s *ProposalsTestSuite) setUpStandardLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *standard.StandardLane { +func (s *ProposalsTestSuite) setUpStandardLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *defaultlane.DefaultLane { cfg := base.LaneConfig{ Logger: log.NewTestLogger(s.T()), TxEncoder: s.encodingConfig.TxConfig.TxEncoder(), @@ -733,7 +733,7 @@ func (s *ProposalsTestSuite) setUpStandardLane(maxBlockSpace math.LegacyDec, exp MaxBlockSpace: maxBlockSpace, } - return standard.NewStandardLane(cfg) + return defaultlane.NewDefaultLane(cfg) } func (s *ProposalsTestSuite) setUpTOBLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *mev.MEVLane { diff --git a/block/mempool_test.go b/block/mempool_test.go index 048c562..242b27a 100644 --- a/block/mempool_test.go +++ b/block/mempool_test.go @@ -12,9 +12,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/skip-mev/pob/block" "github.com/skip-mev/pob/block/base" + defaultlane "github.com/skip-mev/pob/lanes/base" "github.com/skip-mev/pob/lanes/free" "github.com/skip-mev/pob/lanes/mev" - "github.com/skip-mev/pob/lanes/standard" testutils "github.com/skip-mev/pob/testutils" buildertypes "github.com/skip-mev/pob/x/builder/types" "github.com/stretchr/testify/suite" @@ -29,7 +29,7 @@ type BlockBusterTestSuite struct { // Define all of the lanes utilized in the test suite mevLane *mev.MEVLane - baseLane *standard.StandardLane + baseLane *defaultlane.DefaultLane freeLane *free.FreeLane gasTokenDenom string @@ -92,7 +92,7 @@ func (suite *BlockBusterTestSuite) SetupTest() { AnteHandler: nil, MaxBlockSpace: math.LegacyZeroDec(), } - suite.baseLane = standard.NewStandardLane( + suite.baseLane = defaultlane.NewDefaultLane( baseConfig, ) diff --git a/lanes/standard/README.md b/lanes/base/README.md similarity index 78% rename from lanes/standard/README.md rename to lanes/base/README.md index d194863..9355271 100644 --- a/lanes/standard/README.md +++ b/lanes/base/README.md @@ -1,22 +1,14 @@ -# Standard Lane +# Default Lane -> The Standard Lane is the most general and least restrictive lane. The Standard +> The Default Lane is the most general and least restrictive lane. The Default > Lane accepts all transactions that are not accepted by the other lanes, is > generally the lowest priority lane, and consumes all blockspace that is not > consumed by the other lanes. ## 📖 Overview -Blockspace is valuable, and MEV bots find arbitrage opportunities to capture -value. The Block SDK provides a fair auction for these opportunities via the -x/auction module inside the Block SDK so that protocols are rewarded while -ensuring that users are not front-run or sandwiched in the process. - -The Block SDK uses the app-side mempool, PrepareLane / ProcessLane, and CheckTx -to create an MEV marketplace inside the protocol. It introduces a new message -type, called a MsgAuctionBid, that allows the submitter to execute multiple -transactions at the top of the block atomically -(atomically = directly next to each other). +The default lane should be used to accept all transactions that are not accepted +by the other lanes. ## 🏗️ Setup @@ -46,17 +38,21 @@ your base application, you will need to create a `LanedMempool` composed of the lanes that you want to use. 2. Next, order the lanes by priority. The first lane is the highest priority lane and the last lane is the lowest priority lane. **It is recommended that the last -lane is the standard lane.** +lane is the default lane.** 3. You will also need to create a `PrepareProposalHandler` and a `ProcessProposalHandler` that will be responsible for preparing and processing proposals respectively. Configure the order of the lanes in the `PrepareProposalHandler` and `ProcessProposalHandler` to match the order of the lanes in the `LanedMempool`. +NOTE: This example walks through setting up the MEV, Free, and Default Lanes. To +only utilize the default lane, ignore the MEV and Free Lane setup. + ```golang import ( "github.com/skip-mev/block-sdk/abci" - "github.com/skip-mev/block-sdk/lanes/standard" + "github.com/skip-mev/block-sdk/block/base" + defaultlane "github.com/skip-mev/block-sdk/lanes/base" ) ... @@ -75,7 +71,7 @@ func NewApp() { // visit the README in block-sdk/block/base. // // MEV lane hosts an action at the top of the block. - mevConfig := constructor.LaneConfig{ + mevConfig := base.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), @@ -88,7 +84,7 @@ func NewApp() { ) // Free lane allows transactions to be included in the next block for free. - freeConfig := constructor.LaneConfig{ + freeConfig := base.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), @@ -97,19 +93,19 @@ func NewApp() { } freeLane := free.NewFreeLane( freeConfig, - constructor.DefaultTxPriority(), + base.DefaultTxPriority(), free.DefaultMatchHandler(), ) - // Standard lane accepts all other transactions. - defaultConfig := constructor.LaneConfig{ + // Default lane accepts all other transactions. + defaultConfig := base.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), MaxBlockSpace: math.LegacyZeroDec(), MaxTxs: 0, } - defaultLane := base.NewStandardLane(defaultConfig) + defaultLane := defaultlane.NewDefaultLane(defaultConfig) // 2. Set up the relateive priority of lanes lanes := []block.Lane{ @@ -126,7 +122,7 @@ func NewApp() { anteDecorators := []sdk.AnteDecorator{ ante.NewSetUpContextDecorator(), ... - utils.NewIgnoreDecorator( + utils.NewIgnoreDecorator( // free lane specific set up ante.NewDeductFeeDecorator( options.BaseOptions.AccountKeeper, options.BaseOptions.BankKeeper, diff --git a/lanes/standard/abci_test.go b/lanes/base/abci_test.go similarity index 99% rename from lanes/standard/abci_test.go rename to lanes/base/abci_test.go index e006e62..317df3b 100644 --- a/lanes/standard/abci_test.go +++ b/lanes/base/abci_test.go @@ -1,4 +1,4 @@ -package standard_test +package base_test import ( "crypto/sha256" @@ -11,7 +11,7 @@ import ( "github.com/skip-mev/pob/block" "github.com/skip-mev/pob/block/base" "github.com/skip-mev/pob/block/utils/mocks" - "github.com/skip-mev/pob/lanes/standard" + defaultlane "github.com/skip-mev/pob/lanes/base" testutils "github.com/skip-mev/pob/testutils" ) @@ -502,7 +502,7 @@ func (s *BaseTestSuite) TestCheckOrder() { func (s *BaseTestSuite) initLane( maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool, -) *standard.StandardLane { +) *defaultlane.DefaultLane { config := base.NewLaneConfig( log.NewTestLogger(s.T()), s.encodingConfig.TxConfig.TxEncoder(), @@ -511,7 +511,7 @@ func (s *BaseTestSuite) initLane( maxBlockSpace, ) - return standard.NewStandardLane(config) + return defaultlane.NewDefaultLane(config) } func (s *BaseTestSuite) setUpAnteHandler(expectedExecution map[sdk.Tx]bool) sdk.AnteHandler { diff --git a/lanes/standard/base_test.go b/lanes/base/base_test.go similarity index 96% rename from lanes/standard/base_test.go rename to lanes/base/base_test.go index ac5b956..7852dac 100644 --- a/lanes/standard/base_test.go +++ b/lanes/base/base_test.go @@ -1,4 +1,4 @@ -package standard_test +package base_test import ( "math/rand" diff --git a/lanes/standard/lane.go b/lanes/base/lane.go similarity index 75% rename from lanes/standard/lane.go rename to lanes/base/lane.go index d8543fc..05289b4 100644 --- a/lanes/standard/lane.go +++ b/lanes/base/lane.go @@ -1,4 +1,4 @@ -package standard +package base import ( "github.com/skip-mev/pob/block" @@ -10,20 +10,20 @@ const ( LaneName = "default" ) -var _ block.Lane = (*StandardLane)(nil) +var _ block.Lane = (*DefaultLane)(nil) -// StandardLane defines a default lane implementation. The standard lane orders +// DefaultLane defines a default lane implementation. The default lane orders // 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 StandardLane struct { //nolint +type DefaultLane struct { //nolint *base.BaseLane } // NewStandardLane returns a new default lane. -func NewStandardLane(cfg base.LaneConfig) *StandardLane { +func NewDefaultLane(cfg base.LaneConfig) *DefaultLane { lane := base.NewBaseLane( cfg, LaneName, @@ -35,7 +35,7 @@ func NewStandardLane(cfg base.LaneConfig) *StandardLane { base.DefaultMatchHandler(), ) - return &StandardLane{ + return &DefaultLane{ BaseLane: lane, } } diff --git a/lanes/standard/mempool_test.go b/lanes/base/mempool_test.go similarity index 99% rename from lanes/standard/mempool_test.go rename to lanes/base/mempool_test.go index b3052b5..b8a5047 100644 --- a/lanes/standard/mempool_test.go +++ b/lanes/base/mempool_test.go @@ -1,4 +1,4 @@ -package standard_test +package base_test import ( "cosmossdk.io/math" diff --git a/tests/app/app.go b/tests/app/app.go index 18294a1..ad953e6 100644 --- a/tests/app/app.go +++ b/tests/app/app.go @@ -64,9 +64,9 @@ import ( "github.com/skip-mev/pob/abci" "github.com/skip-mev/pob/block" "github.com/skip-mev/pob/block/base" + defaultlane "github.com/skip-mev/pob/lanes/base" "github.com/skip-mev/pob/lanes/free" "github.com/skip-mev/pob/lanes/mev" - "github.com/skip-mev/pob/lanes/standard" buildermodule "github.com/skip-mev/pob/x/builder" builderkeeper "github.com/skip-mev/pob/x/builder/keeper" ) @@ -297,7 +297,7 @@ func New( MaxBlockSpace: math.LegacyZeroDec(), MaxTxs: 0, } - defaultLane := standard.NewStandardLane(defaultConfig) + defaultLane := defaultlane.NewDefaultLane(defaultConfig) // Set the lanes into the mempool. lanes := []block.Lane{ diff --git a/x/builder/ante/ante_test.go b/x/builder/ante/ante_test.go index c62231f..6c25a43 100644 --- a/x/builder/ante/ante_test.go +++ b/x/builder/ante/ante_test.go @@ -13,8 +13,8 @@ import ( "github.com/golang/mock/gomock" "github.com/skip-mev/pob/block" "github.com/skip-mev/pob/block/base" + defaultlane "github.com/skip-mev/pob/lanes/base" "github.com/skip-mev/pob/lanes/mev" - "github.com/skip-mev/pob/lanes/standard" testutils "github.com/skip-mev/pob/testutils" "github.com/skip-mev/pob/x/builder/ante" "github.com/skip-mev/pob/x/builder/keeper" @@ -42,7 +42,7 @@ type AnteTestSuite struct { // mempool and lane set up mempool block.Mempool mevLane *mev.MEVLane - baseLane *standard.StandardLane + baseLane *defaultlane.DefaultLane lanes []block.Lane // Account set up @@ -105,7 +105,7 @@ func (suite *AnteTestSuite) SetupTest() { MaxBlockSpace: math.LegacyZeroDec(), IgnoreList: []block.Lane{suite.mevLane}, } - suite.baseLane = standard.NewStandardLane(baseConfig) + suite.baseLane = defaultlane.NewDefaultLane(baseConfig) // Mempool set up suite.lanes = []block.Lane{suite.mevLane, suite.baseLane} @@ -280,13 +280,13 @@ func (suite *AnteTestSuite) TestAnteHandler() { distribution := suite.mempool.GetTxDistribution() suite.Require().Equal(0, distribution[mev.LaneName]) - suite.Require().Equal(0, distribution[standard.LaneName]) + suite.Require().Equal(0, distribution[defaultlane.LaneName]) suite.Require().NoError(suite.mempool.Insert(suite.ctx, topAuctionTx)) distribution = suite.mempool.GetTxDistribution() suite.Require().Equal(1, distribution[mev.LaneName]) - suite.Require().Equal(0, distribution[standard.LaneName]) + suite.Require().Equal(0, distribution[defaultlane.LaneName]) } // Create the actual mev tx and insert into the mempool From eb880e5d2c884322884bd4b57737559bc76ce8f8 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Wed, 16 Aug 2023 11:15:11 -0400 Subject: [PATCH 34/41] nit --- README.md | 2 +- SPEC.md | 2 ++ block/mempool.go | 2 +- lanes/free/README.md | 8 ++++---- lanes/mev/README.md | 18 +++++++++--------- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index ffadd3a..1dc3e18 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ In the Block SDK, each lane has its own set of rules and transaction flow manage A block with separate `lanes` can be used for: -1. **MEV mitigation**: a top of block lane could be designed to create an in-protocol top-of-block auction (as we are doing with POB) to recapture MEV in a transparent and governable way. +1. **MEV mitigation**: a top of block lane could be designed to create an in-protocol top-of-block auction (as we are doing with the Block SDK) to recapture MEV in a transparent and governable way. 2. **Free/reduced fee txs**: transactions with certain properties (e.g. from trusted accounts or performing encouraged actions) could leverage a free lane to reward behavior. 3. **Dedicated oracle space** Oracles could be included before other kinds of transactions to ensure that price updates occur first, and are not able to be sandwiched or manipulated. 4. **Orderflow auctions**: an OFA lane could be constructed such that order flow providers can have their submitted transactions bundled with specific backrunners, to guarantee MEV rewards are attributed back to users. Imagine MEV-share but in protocol. diff --git a/SPEC.md b/SPEC.md index 198b975..1ef7e46 100644 --- a/SPEC.md +++ b/SPEC.md @@ -1,6 +1,8 @@ # POB Specification +> **Note**: This specification is a work in progress and is subject to change. + ## Abstract The `x/builder` module is a Cosmos SDK module that allows Cosmos chains to host diff --git a/block/mempool.go b/block/mempool.go index 44bac25..61290c6 100644 --- a/block/mempool.go +++ b/block/mempool.go @@ -40,7 +40,7 @@ type ( } ) -// NewLanedMempool returns a new Blockbuster mempool. The block mempool is +// NewLanedMempool returns a new Block SDK mempool. The laned mempool is // comprised of a registry of lanes. Each lane is responsible for selecting // transactions according to its own selection logic. The lanes are ordered // according to their priority. The first lane in the registry has the highest diff --git a/lanes/free/README.md b/lanes/free/README.md index 42816a5..eef9d3a 100644 --- a/lanes/free/README.md +++ b/lanes/free/README.md @@ -71,7 +71,7 @@ func NewApp() { // visit the README in block-sdk/block/base. // // MEV lane hosts an action at the top of the block. - mevConfig := constructor.LaneConfig{ + mevConfig := base.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), @@ -84,7 +84,7 @@ func NewApp() { ) // Free lane allows transactions to be included in the next block for free. - freeConfig := constructor.LaneConfig{ + freeConfig := base.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), @@ -93,12 +93,12 @@ func NewApp() { } freeLane := free.NewFreeLane( freeConfig, - constructor.DefaultTxPriority(), + base.DefaultTxPriority(), free.DefaultMatchHandler(), ) // Default lane accepts all other transactions. - defaultConfig := constructor.LaneConfig{ + defaultConfig := base.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), diff --git a/lanes/mev/README.md b/lanes/mev/README.md index 11c9a66..d8e74c1 100644 --- a/lanes/mev/README.md +++ b/lanes/mev/README.md @@ -107,7 +107,7 @@ $ go install github.com/skip-mev/block-sdk } ``` - c. Instantiate the blockbuster mempool with the application's desired lanes. + c. Instantiate the Block SDK mempool with the application's desired lanes. ```go // 1. Create the lanes. @@ -121,7 +121,7 @@ $ go install github.com/skip-mev/block-sdk // visit the README in block-sdk/block/base. // // MEV lane hosts an auction at the top of the block. - mevConfig := constructor.LaneConfig{ + mevConfig := base.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), @@ -134,7 +134,7 @@ $ go install github.com/skip-mev/block-sdk ) // Free lane allows transactions to be included in the next block for free. - freeConfig := constructor.LaneConfig{ + freeConfig := base.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), @@ -143,12 +143,12 @@ $ go install github.com/skip-mev/block-sdk } freeLane := free.NewFreeLane( freeConfig, - constructor.DefaultTxPriority(), + base.DefaultTxPriority(), free.DefaultMatchHandler(), ) // Standard lane accepts all other transactions. - defaultConfig := constructor.LaneConfig{ + defaultConfig := base.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), TxDecoder: app.txConfig.TxDecoder(), @@ -168,7 +168,7 @@ $ go install github.com/skip-mev/block-sdk ``` d. Instantiate the antehandler chain for the application with awareness of the - blockbuster mempool. This will allow the application to verify the validity + LanedMempool. This will allow the application to verify the validity of a transaction respecting the desired logic of a given lane. In this walkthrough, we want the `FeeDecorator` to be ignored for all transactions that should belong to the free lane. Additionally, we want to add the `x/builder` @@ -178,9 +178,9 @@ $ go install github.com/skip-mev/block-sdk ```go import ( ... - "github.com/skip-mev/pob/blockbuster" - "github.com/skip-mev/pob/blockbuster/utils" - builderante "github.com/skip-mev/pob/x/builder/ante" + "github.com/skip-mev/block-sdk/block" + "github.com/skip-mev/block-sdk/block/utils" + builderante "github.com/skip-mev/block-sdk/x/builder/ante" ... ) From e7d99a294808cc3200409c7127e5af947a1cc641 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Wed, 16 Aug 2023 11:38:47 -0400 Subject: [PATCH 35/41] dep old docs on mev --- SPEC.md | 313 --------------------------------------------- lanes/base/lane.go | 2 +- 2 files changed, 1 insertion(+), 314 deletions(-) delete mode 100644 SPEC.md diff --git a/SPEC.md b/SPEC.md deleted file mode 100644 index 1ef7e46..0000000 --- a/SPEC.md +++ /dev/null @@ -1,313 +0,0 @@ - -# POB Specification - -> **Note**: This specification is a work in progress and is subject to change. - -## Abstract - -The `x/builder` module is a Cosmos SDK module that allows Cosmos chains to host -mev auctions directly in-protocol with auction revenue (MEV) being -redistributed according to the preferences of the chain. The `x/builder` module -introduces a new `MsgAuctionBid` message that allows users to submit a bid -alongside an ordered list of transactions, i.e. a **bundle**, that they want -executed at mev before any other transactions are executed for that -block. The `x/builder` module works alongside the `AuctionMempool` such that: - -* Auctions are held directly in the `AuctionMempool`, where a winner is determined - when the proposer proposes a new block in `PrepareProposal`. -* `x/builder` provides the necessary validation of auction bids and subsequent - state transitions to extract bids. - -## Concepts - -### Miner Extractable Value (MEV) - -MEV refers to the potential profit that miners, or validators in a Proof-of-Stake -system, can make by strategically ordering, selecting, or even censoring -transactions in the blocks they produce. MEV can be classified into "good MEV" -and "bad MEV" based on the effects it has on the blockchain ecosystem and its -users. It's important to note that these classifications are subjective and may -vary depending on one's perspective. - -**Good MEV** refers to the value that validators can extract while contributing -positively to the blockchain ecosystem. This typically includes activities that -enhance network efficiency, maintain fairness, and align incentives with the -intended use of the system. Examples of good MEV include: - -* **Back-running**: Validators can place their own transactions immediately - after a profitable transaction, capitalizing on the changes caused by the - preceding transaction. -* **Arbitrage**: By exploiting price differences across decentralized exchanges - or other DeFi platforms, validators help maintain more consistent price levels - across the ecosystem, ultimately contributing to its stability. -* **Liquidations**: In DeFi platforms, when users' collateral falls below a - specific threshold, validators can liquidate these positions, thereby maintaining - the overall health of the platform and protecting its users from insolvency risks. - -**Bad MEV** refers to the value that validators can extract through activities -that harm the blockchain ecosystem, lead to unfair advantages, or exploit users. -Examples of bad MEV include: - -* **Front-running**: Validators can observe pending transactions in the mempool - (the pool of unconfirmed transactions) and insert their own transactions ahead - of them. This can be particularly profitable in decentralized finance (DeFi) - applications, where a validator could front-run a large trade to take advantage - of price movements. -* **Sandwich attacks**: Validators can surround a user's transaction with their - own transactions, effectively manipulating the market price for their benefit. -* **Censorship**: Validators can selectively exclude certain transactions from - blocks to benefit their own transactions or to extract higher fees from users. - -MEV is a topic of concern in the blockchain community because it can lead to -unfair advantages for validators, reduced trust in the system, and a potential -concentration of power. Various approaches have been proposed to mitigate MEV, -such as proposer-builder separation (described below) and transparent and fair -transaction ordering mechanisms at the protocol-level (`POB`) to make MEV -extraction more incentive aligned with the users and blockchain ecosystem. - -### Proposer Builder Separation (PBS) - -Proposer-builder separation is a concept in the design of blockchain protocols, -specifically in the context of transaction ordering within a block. In traditional -blockchain systems, validators perform two main tasks: they create new blocks -(acting as proposers) and determine the ordering of transactions within those -blocks (acting as builders). - - -**Proposers**: They are responsible for creating and broadcasting new blocks, -just like in traditional blockchain systems. *However, they no longer determine -the ordering of transactions within those blocks*. - -**Builders**: They have the exclusive role of determining the order of transactions -within a block - can be full or partial block. Builders submit their proposed -transaction orderings to an auction mechanism, which selects the winning template -based on predefined criteria, e.g. highest bid. - -This dual role can lead to potential issues, such as front-running and other -manipulations that benefit the miners/builders themselves. - -* *Increased complexity*: Introducing PBS adds an extra layer of complexity to - the blockchain protocol. Designing, implementing, and maintaining an auction - mechanism for transaction ordering requires additional resources and may - introduce new vulnerabilities or points of failure in the system. -* *Centralization risks*: With PBS, there's a risk that a few dominant builders - may emerge, leading to centralization of transaction ordering. This centralization - could result in a lack of diversity in transaction ordering algorithms and an - increased potential for collusion or manipulation by the dominant builders. -* *Incentive misalignments*: The bidding process may create perverse incentives - for builders. For example, builders may be incentivized to include only high-fee - transactions to maximize their profits, potentially leading to a neglect of - lower-fee transactions. Additionally, builders may be incentivized to build - blocks that include **bad-MEV** strategies because they are more profitable. - -## Specification - -### Mempool - -As the lifeblood of blockchains, mempools serve as the intermediary space for -pending transactions, playing a vital role in transaction management, fee markets, -and network health. With ABCI++, mempools can be defined at the application layer -instead of the consensus layer (CometBFT). This means applications can define -their own mempools that have their own custom verification, block building, and -state transition logic. Adding on, these changes make it such that blocks are -built (`PrepareProposal`) and verified (`ProcessProposal`) directly in the -application layer. - -The `x/builder` module implements an application-side mempool, `AuctionMempool`, -that implements the `sdk.Mempool` interface. The mempool is composed of two -primary indexes, a global index that contains all non-auction transactions and -an index that only contains auction transactions, i.e. transactions with a single -`MsgAuctionBid` message. Both indexes order transactions based on priority respecting -the sender's sequence number. The global index prioritizes transactions based on -`ctx.Priority()` and the auction index prioritizes transactions based on the -bid. - -### Configuration - -The `AuctionMempool` mempool implementation accepts a `AuctionFactory` -interface that allows the mempool to be generic across many Cosmos SDK -applications, such that it allows the ability for the application developer to -define their business logic in terms of how to perform things such as the following: - -* Getting tx signers -* Getting bundled tx signers -* Retrieving bid information - - -```go -// AuctionFactory defines the interface for processing auction transactions. -// It is a wrapper around all of the functionality that each application chain -// must implement in order for auction processing to work. -type AuctionFactory interface { - // WrapBundleTransaction defines a function that wraps a bundle transaction - // into a sdk.Tx. Since this is a potentially expensive operation, we allow - // each application chain to define how they want to wrap the transaction - // such that it is only called when necessary (i.e. when the transaction is - // being considered in the proposal handlers). - WrapBundleTransaction(tx []byte) (sdk.Tx, error) - - // GetAuctionBidInfo defines a function that returns the bid info from an - // auction transaction. - GetAuctionBidInfo(tx sdk.Tx) (*AuctionBidInfo, error) -} -``` - -### PrepareProposal - -After the proposer of the next block has been selected, the CometBFT client will -call `PrepareProposal` to build the next block. The block will be built in two -stages. First, it will host the auction and include the winning bidder's bundle -as the first set of transactions for the block, i.e. it will select the bid -transaction itself along with automatically including all the bundled transactions -in the specified order they appear in the bid's `transactions` field. - -The auction currently supports only a single winner. Selecting the auction winner -involves a greedy search for a valid auction transaction starting from highest -paying bid, respecting user nonce, in the `AuctionMempool`. The `x/builder`'s -ante handler is responsible for verifying the auction transaction based on the -criteria described below (see **Ante Handler**). - -Then, it will build the rest of the block by reaping and validating the transactions -in the global index. The second portion of block building iterates from highest -to lowest priority transactions in the global index and adds them to the proposal -if they are valid. If the proposer comes across a transaction that was already -included in the MEV, it will be ignored. - -### ProcessProposal - -After the proposer proposes a block of transactions for the next block, the -block will be verified by other nodes in the network in `ProcessProposal`. If -there is an auction transaction in the proposal, it must be the first transaction -in the proposal and all bundled transactions must follow the auction transaction -in the exact order we would expect them to be seen. If this fails, the proposal -is rejected. If this passes, the validator will then run `CheckTx` on all of the -transactions in the block in the order in which they were provided in the proposal. - -### Ante Handler - -When users want to bid for the rights for mev execution they will submit -a normal `sdk.Tx` transaction with a single `MsgAuctionBid`. The ante handler is -responsible for verification of this transaction. The ante handler will verify that: - -1. The auction transaction specifies a timeout height where the bid is no longer - considered valid. Note, it is REQUIRED that all bid transactions include a - height timeout. -2. The auction transaction includes less than `MaxBundleSize` transactions in - its bundle. -3. The auction transaction includes only a SINGLE `MsgAuctionBid` message. We - enforce that no other messages are included to prevent front-running. -4. Enforce that the user has sufficient funds to pay the bid they entered while - covering all relevant auction fees. -5. Enforce that the transaction's min bid increment greater than the local highest - bid in the mempool. -6. Enforce that the bundle of transactions the bidder provided does not front-run - or sandwich (if enabled). - -Note, the process of selecting auction winners occurs in a greedy manner. In -`PrepareProposal`, the `AuctionMempool` will iterate from largest to smallest -bidding transaction until it finds the first valid bid transaction. - -### State - -The `x/builder` module stores the following state objects: - -```protobuf -message Params { - option (amino.name) = "cosmos-sdk/x/builder/Params"; - - // max_bundle_size is the maximum number of transactions that can be bundled - // in a single bundle. - uint32 max_bundle_size = 1; - - // escrow_account_address is the address of the account that will receive a - // portion of the bid proceeds. - string escrow_account_address = 2; - - // reserve_fee specifies the bid floor for the auction. - cosmos.base.v1beta1.Coin reserve_fee = 3 - [ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ]; - - // min_buy_in_fee specifies the fee that the bidder must pay to enter the - // auction. - cosmos.base.v1beta1.Coin min_buy_in_fee = 4 - [ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ]; - - // min_bid_increment specifies the minimum amount that the next bid must be - // greater than the previous bid. - cosmos.base.v1beta1.Coin min_bid_increment = 5 - [ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ]; - - // front_running_protection specifies whether front running and sandwich - // attack protection is enabled. - bool front_running_protection = 6; - - // proposer_fee defines the portion of the winning bid that goes to the block - // proposer that proposed the block. - string proposer_fee = 7 [ - (gogoproto.nullable) = false, - (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec" - ]; -} -``` - -## Messages - -### MsgAuctionBid - -POB defines a new Cosmos SDK `Message`, `MsgAuctionBid`, that allows users to -create an auction bid and participate in a mev auction. The `MsgAuctionBid` -message defines a bidder and a series of embedded transactions, i.e. the bundle. - -```protobuf -message MsgAuctionBid { - option (cosmos.msg.v1.signer) = "bidder"; - option (amino.name) = "pob/x/builder/MsgAuctionBid"; - - option (gogoproto.equal) = false; - - // bidder is the address of the account that is submitting a bid to the - // auction. - string bidder = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; - // bid is the amount of coins that the bidder is bidding to participate in the - // auction. - cosmos.base.v1beta1.Coin bid = 3 - [ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ]; - // transactions are the bytes of the transactions that the bidder wants to - // bundle together. - repeated bytes transactions = 4; -} -``` - -Note, the `transactions` may or may not exist in a node's application mempool. If -a transaction containing a single `MsgAuctionBid` wins the auction, the block -proposal will automatically include the `MsgAuctionBid` transaction along with -injecting all the bundled transactions such that they are executed in the same -order after the `MsgAuctionBid` transaction. - -When processing a `MsgAuctionBid`, the `x/builder` module will perform two primary -actions: - -1. Ensure the bid is valid per the module's parameters and configuration. -2. Extract fee payments from the bidder's account and escrow them to the module's - escrow account and the proposer that included the winning bid in the block - proposal. - -### MsgUpdateParams - -The `MsgUpdateParams` message allows for an authority, typically the `x/gov` -module account, to update the `x/builder`'s parameters. - -```protobuf -message MsgUpdateParams { - option (cosmos.msg.v1.signer) = "authority"; - option (amino.name) = "pob/x/builder/MsgUpdateParams"; - - option (gogoproto.equal) = false; - - // authority is the address of the account that is authorized to update the - // x/builder module parameters. - string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; - // params is the new parameters for the x/builder module. - Params params = 2 [ (gogoproto.nullable) = false ]; -} -``` diff --git a/lanes/base/lane.go b/lanes/base/lane.go index 05289b4..d34b786 100644 --- a/lanes/base/lane.go +++ b/lanes/base/lane.go @@ -18,7 +18,7 @@ var _ block.Lane = (*DefaultLane)(nil) // 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 { //nolint +type DefaultLane struct { *base.BaseLane } From c789c730b1167c6fa1c21647dd2de0e182c6b32e Mon Sep 17 00:00:00 2001 From: David Terpay Date: Wed, 16 Aug 2023 17:59:52 -0400 Subject: [PATCH 36/41] readme for default gud --- block/base/config.go | 2 +- block/base/mempool.go | 4 +- block/mempool.go | 4 +- lanes/base/README.md | 79 ++++++++------------------------------- lanes/base/lane.go | 2 +- lanes/free/README.md | 87 ++++++++++++------------------------------- 6 files changed, 45 insertions(+), 133 deletions(-) diff --git a/block/base/config.go b/block/base/config.go index 61bf266..e9a404f 100644 --- a/block/base/config.go +++ b/block/base/config.go @@ -9,7 +9,7 @@ import ( "github.com/skip-mev/pob/block" ) -// LaneConfig defines the basic functionality needed for a lane. +// LaneConfig defines the basic configurations needed for a lane. type LaneConfig struct { Logger log.Logger TxEncoder sdk.TxEncoder diff --git a/block/base/mempool.go b/block/base/mempool.go index 3efe9ff..22bcf05 100644 --- a/block/base/mempool.go +++ b/block/base/mempool.go @@ -11,7 +11,7 @@ import ( ) type ( - // ConstructorMempool defines a mempool that orders transactions based on the + // Mempool 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 @@ -79,7 +79,7 @@ func DefaultTxPriority() TxPriority[string] { } } -// NewMempool returns a new ConstructorMempool. +// NewMempool returns a new Mempool. func NewMempool[C comparable](txPriority TxPriority[C], txEncoder sdk.TxEncoder, maxTx int) *Mempool[C] { return &Mempool[C]{ index: NewPriorityMempool( diff --git a/block/mempool.go b/block/mempool.go index 61290c6..4029c09 100644 --- a/block/mempool.go +++ b/block/mempool.go @@ -14,7 +14,7 @@ import ( var _ Mempool = (*LanedMempool)(nil) type ( - // LanedMempool defines the Block SDK mempool interface. + // Mempool defines the Block SDK mempool interface. Mempool interface { sdkmempool.Mempool @@ -40,7 +40,7 @@ type ( } ) -// NewLanedMempool returns a new Block SDK mempool. The laned mempool is +// NewLanedMempool returns a new Block SDK LanedMempool. The laned mempool is // comprised of a registry of lanes. Each lane is responsible for selecting // transactions according to its own selection logic. The lanes are ordered // according to their priority. The first lane in the registry has the highest diff --git a/lanes/base/README.md b/lanes/base/README.md index 9355271..24be4b4 100644 --- a/lanes/base/README.md +++ b/lanes/base/README.md @@ -1,28 +1,11 @@ -# Default Lane +# 🏗️ Default Lane Setup -> The Default Lane is the most general and least restrictive lane. The Default -> Lane accepts all transactions that are not accepted by the other lanes, is -> generally the lowest priority lane, and consumes all blockspace that is not -> consumed by the other lanes. - -## 📖 Overview - -The default lane should be used to accept all transactions that are not accepted -by the other lanes. - -## 🏗️ Setup - -> **Note** -> -> For a more in depth example of how to use the Block SDK, check out our -> example application in `block-sdk/tests/app/app.go`. - -### 📦 Dependencies +## 📦 Dependencies The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. -### 📥 Installation +## 📥 Installation To install the Block SDK, run the following command: @@ -30,12 +13,13 @@ To install the Block SDK, run the following command: $ go install github.com/skip-mev/block-sdk ``` -### 📚 Usage +## 📚 Usage 1. First determine the set of lanes that you want to use in your application. The -available lanes can be found in our **Lane App Store** in `block-sdk/lanes`. In -your base application, you will need to create a `LanedMempool` composed of the -lanes that you want to use. +available lanes can be found in our +[Lane App Store](https://docs.skip.money/chains/lanes/existing-lanes/default). +In your base application, you will need to create a `LanedMempool` composed of the +lanes you want to use. 2. Next, order the lanes by priority. The first lane is the highest priority lane and the last lane is the lowest priority lane. **It is recommended that the last lane is the default lane.** @@ -45,9 +29,6 @@ proposals respectively. Configure the order of the lanes in the `PrepareProposalHandler` and `ProcessProposalHandler` to match the order of the lanes in the `LanedMempool`. -NOTE: This example walks through setting up the MEV, Free, and Default Lanes. To -only utilize the default lane, ignore the MEV and Free Lane setup. - ```golang import ( "github.com/skip-mev/block-sdk/abci" @@ -56,9 +37,7 @@ import ( ) ... -``` -```golang func NewApp() { ... // 1. Create the lanes. @@ -68,36 +47,9 @@ func NewApp() { // transactions to bid for inclusion at the top of the next block. // // For more information on how to utilize the LaneConfig please - // visit the README in block-sdk/block/base. + // visit the README in docs.skip.money/chains/lanes/build-your-own-lane#-lane-config. // - // MEV lane hosts an action at the top of the block. - mevConfig := base.LaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: math.LegacyZeroDec(), - MaxTxs: 0, - } - mevLane := mev.NewMEVLane( - mevConfig, - mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), - ) - - // Free lane allows transactions to be included in the next block for free. - freeConfig := base.LaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: math.LegacyZeroDec(), - MaxTxs: 0, - } - freeLane := free.NewFreeLane( - freeConfig, - base.DefaultTxPriority(), - free.DefaultMatchHandler(), - ) - - // Default lane accepts all other transactions. + // Default lane accepts all transactions. defaultConfig := base.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), @@ -107,10 +59,8 @@ func NewApp() { } defaultLane := defaultlane.NewDefaultLane(defaultConfig) - // 2. Set up the relateive priority of lanes + // 2. Set up the relative priority of lanes lanes := []block.Lane{ - mevLane, - freeLane, defaultLane, } mempool := block.NewLanedMempool(app.Logger(), true, lanes...) @@ -118,11 +68,11 @@ func NewApp() { ... - // 3. Set up the ante handler. + // 3. Set up the ante handler. anteDecorators := []sdk.AnteDecorator{ ante.NewSetUpContextDecorator(), ... - utils.NewIgnoreDecorator( // free lane specific set up + utils.NewIgnoreDecorator( ante.NewDeductFeeDecorator( options.BaseOptions.AccountKeeper, options.BaseOptions.BankKeeper, @@ -137,6 +87,9 @@ func NewApp() { anteHandler := sdk.ChainAnteDecorators(anteDecorators...) // Set the lane ante handlers on the lanes. + // + // NOTE: This step is very important. Without the antehandlers, lanes will not + // be able to verify transactions. for _, lane := range lanes { lane.SetAnteHandler(anteHandler) } diff --git a/lanes/base/lane.go b/lanes/base/lane.go index d34b786..136284f 100644 --- a/lanes/base/lane.go +++ b/lanes/base/lane.go @@ -14,7 +14,7 @@ var _ block.Lane = (*DefaultLane)(nil) // DefaultLane defines a default lane implementation. The default lane orders // 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). +// that 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. diff --git a/lanes/free/README.md b/lanes/free/README.md index eef9d3a..db89eb9 100644 --- a/lanes/free/README.md +++ b/lanes/free/README.md @@ -1,29 +1,11 @@ -# Free Lane +# 🏗️ Free Lane Setup -> Leverage the free lane to encourage certain activity (such as staking) on -> your chain. - -## 📖 Overview - -The Free Lane is a lane that allows transactions to be included in the next block -for free. By default, transactions that are staking related (e.g. delegation, -undelegation, redelegate, etc.) are included in the Free Lane, however, this -can be easily replaced! For more information on that, please see the -`MatchHandler` section in the README found in `block-sdk/block/base`. - -## 🏗️ Setup - -> **Note** -> -> For a more in depth example of how to use the Block SDK, check out our -> example application in `block-sdk/tests/app/app.go`. - -### 📦 Dependencies +## 📦 Dependencies The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. -### 📥 Installation +## 📥 Installation To install the Block SDK, run the following command: @@ -31,15 +13,16 @@ To install the Block SDK, run the following command: $ go install github.com/skip-mev/block-sdk ``` -### 📚 Usage +## 📚 Usage 1. First determine the set of lanes that you want to use in your application. The -available lanes can be found in our **Lane App Store** in `block-sdk/lanes`. In -your base application, you will need to create a `LanedMempool` composed of the -lanes that you want to use. +available lanes can be found in our +[Lane App Store](https://docs.skip.money/chains/lanes/existing-lanes/default). +In your base application, you will need to create a `LanedMempool` composed of the +lanes you want to use. *The free lane should not exist on its own. At minimum, it +is recommended that the free lane is paired with the default lane.* 2. Next, order the lanes by priority. The first lane is the highest priority lane -and the last lane is the lowest priority lane. Determine exactly where you want -the free lane to be in the priority order. +and the last lane is the lowest priority lane. 3. Set up your `FeeDeductorDecorator` to ignore the free lane where ever you initialize your `AnteHandler`. This will ensure that the free lane is not subject to deducting transaction fees. @@ -49,16 +32,18 @@ proposals respectively. Configure the order of the lanes in the `PrepareProposalHandler` and `ProcessProposalHandler` to match the order of the lanes in the `LanedMempool`. +NOTE: This example walks through setting up the Free and Default lanes. + ```golang import ( "github.com/skip-mev/block-sdk/abci" - "github.com/skip-mev/block-sdk/lanes/free" + "github.com/skip-mev/block-sdk/block/base" + defaultlane "github.com/skip-mev/block-sdk/lanes/base" + freelane "github.com/skip-mev/block-sdk/lanes/free" ) ... -``` -```golang func NewApp() { ... // 1. Create the lanes. @@ -68,36 +53,9 @@ func NewApp() { // transactions to bid for inclusion at the top of the next block. // // For more information on how to utilize the LaneConfig please - // visit the README in block-sdk/block/base. + // visit the README in docs.skip.money/chains/lanes/build-your-own-lane#-lane-config. // - // MEV lane hosts an action at the top of the block. - mevConfig := base.LaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: math.LegacyZeroDec(), - MaxTxs: 0, - } - mevLane := mev.NewMEVLane( - mevConfig, - mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), - ) - - // Free lane allows transactions to be included in the next block for free. - freeConfig := base.LaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: math.LegacyZeroDec(), - MaxTxs: 0, - } - freeLane := free.NewFreeLane( - freeConfig, - base.DefaultTxPriority(), - free.DefaultMatchHandler(), - ) - - // Default lane accepts all other transactions. + // Default lane accepts all transactions. defaultConfig := base.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), @@ -105,12 +63,10 @@ func NewApp() { MaxBlockSpace: math.LegacyZeroDec(), MaxTxs: 0, } - defaultLane := base.NewStandardLane(defaultConfig) + defaultLane := defaultlane.NewDefaultLane(defaultConfig) - // 2. Set up the relateive priority of lanes + // 2. Set up the relative priority of lanes lanes := []block.Lane{ - mevLane, - freeLane, defaultLane, } mempool := block.NewLanedMempool(app.Logger(), true, lanes...) @@ -118,7 +74,7 @@ func NewApp() { ... - // 3. Set up the ante handler. + // 3. Set up the ante handler. anteDecorators := []sdk.AnteDecorator{ ante.NewSetUpContextDecorator(), ... @@ -137,6 +93,9 @@ func NewApp() { anteHandler := sdk.ChainAnteDecorators(anteDecorators...) // Set the lane ante handlers on the lanes. + // + // NOTE: This step is very important. Without the antehandlers, lanes will not + // be able to verify transactions. for _, lane := range lanes { lane.SetAnteHandler(anteHandler) } From 935df43bfd71605b1ecb4e2c066c00e89b9b7414 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Wed, 16 Aug 2023 18:17:24 -0400 Subject: [PATCH 37/41] done wit mev lane shi --- lanes/free/README.md | 16 ++++- lanes/mev/README.md | 164 ++++++++++++++++++------------------------- 2 files changed, 82 insertions(+), 98 deletions(-) diff --git a/lanes/free/README.md b/lanes/free/README.md index db89eb9..4c51d6c 100644 --- a/lanes/free/README.md +++ b/lanes/free/README.md @@ -55,6 +55,16 @@ func NewApp() { // For more information on how to utilize the LaneConfig please // visit the README in docs.skip.money/chains/lanes/build-your-own-lane#-lane-config. // + // Set up the configuration of the free lane and instantiate it. + freeConfig := base.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, + } + freeLane := freelane.NewFreeLane(freeConfig, base.DefaultTxPriority(), freelane.DefaultMatchHandler()) + // Default lane accepts all transactions. defaultConfig := base.LaneConfig{ Logger: app.Logger(), @@ -67,6 +77,7 @@ func NewApp() { // 2. Set up the relative priority of lanes lanes := []block.Lane{ + freeLane, defaultLane, } mempool := block.NewLanedMempool(app.Logger(), true, lanes...) @@ -74,7 +85,10 @@ func NewApp() { ... - // 3. Set up the ante handler. + // 3. Set up the ante handler. + // + // This will allow any transaction that matches the to the free lane to + // be processed without paying any fees. anteDecorators := []sdk.AnteDecorator{ ante.NewSetUpContextDecorator(), ... diff --git a/lanes/mev/README.md b/lanes/mev/README.md index d8e74c1..03c8939 100644 --- a/lanes/mev/README.md +++ b/lanes/mev/README.md @@ -1,42 +1,42 @@ -# MEV Lane +# 🏗️ MEV Lane Setup -> The MEV Lane hosts top of block auctions in protocol and verifiably builds -> blocks with top-of-block block space reserved for auction winners, with -> auction revenue being redistributed to chains. +## 📦 Dependencies -## 📖 Overview +The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently +compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. -Blockspace is valuable, and MEV bots find arbitrage opportunities to capture -value. The Block SDK provides a fair auction for these opportunities via the -x/auction module inside the Block SDK so that protocols are rewarded while -ensuring that users are not front-run or sandwiched in the process. +## 📥 Installation -The Block SDK uses the app-side mempool, PrepareLane / ProcessLane, and CheckTx -to create an MEV marketplace inside the protocol. It introduces a new message -type, called a MsgAuctionBid, that allows the submitter to execute multiple -transactions at the top of the block atomically -(atomically = directly next to each other). +To install the Block SDK, run the following command: -## Install - -```shell +```bash $ go install github.com/skip-mev/block-sdk ``` -## Setup +## 📚 Usage -> This set up guide will walk you through the process of setting up a POB -> application. In particular, we will configure an application with the -> following features: -> ->* MEV lane (auction lane). This will create an MEV lane where users can bid to -> have their transactions executed at the top of the block. ->* Free lane. This will create a free lane where users can submit transactions -> that will be executed for free (no fees). ->* Default lane. This will create a default lane where users can submit -> transactions that will be executed with the default app logic. ->* Builder module that pairs with the auction lane to process auction -> transactions and distribute revenue to the auction house. +1. First determine the set of lanes that you want to use in your application. The +available lanes can be found in our +[Lane App Store](https://docs.skip.money/chains/lanes/existing-lanes/default). +In your base application, you will need to create a `LanedMempool` composed of the +lanes you want to use. *The MEV lane should not exist on its own. At minimum, it +is recommended that the MEV lane is paired with the default lane.* +2. You will need to instantiate the `x/builder` module into your application. This +module is responsible for processing auction transactions and distributing revenue +to the auction house. The `x/builder` module is also responsible for ensuring the +validity of auction transactions. *The `x/builder` module should not exist on its +own. **This is the most intensive part of the set up process.** +3. Next, order the lanes by priority. The first lane is the highest priority lane +and the last lane is the lowest priority lane. Since the MEV lane is meant to auction +off the top of the block, it should be the highest priority lane. The default lane +should follow. +4. You will also need to create a `PrepareProposalHandler` and a +`ProcessProposalHandler` that will be responsible for preparing and processing +proposals respectively. Configure the order of the lanes in the +`PrepareProposalHandler` and `ProcessProposalHandler` to match the order of the +lanes in the `LanedMempool`. + +NOTE: This example walks through setting up the MEV and Default lanes. 1. Import the necessary dependencies into your application. This includes the Block SDK proposal handlers + mempool, keeper, builder types, and builder @@ -47,11 +47,12 @@ $ go install github.com/skip-mev/block-sdk ... "github.com/skip-mev/pob/block-sdk" "github.com/skip-mev/pob/block-sdk/abci" - "github.com/skip-mev/pob/block-sdk/lanes/auction" + "github.com/skip-mev/pob/block-sdk/lanes/mev" "github.com/skip-mev/pob/block-sdk/lanes/base" - "github.com/skip-mev/pob/block-sdk/lanes/free" buildermodule "github.com/skip-mev/block-sdk/x/builder" builderkeeper "github.com/skip-mev/block-sdk/x/builder/keeper" + buildertypes "github.com/skip-mev/block-sdk/x/builder/types" + builderante "github.com/skip-mev/block-sdk/x/builder/ante" ... ) ``` @@ -75,14 +76,14 @@ $ go install github.com/skip-mev/block-sdk ) ``` -3. The builder `Keeper` is POB's gateway to processing special `MsgAuctionBid` +3. The builder `Keeper` is MEV lane's gateway to processing special `MsgAuctionBid` messages that allow users to participate in the top of block auction, distribute revenue to the auction house, and ensure the validity of auction transactions. a. First add the keeper to the app's struct definition. We also want to add MEV lane's custom checkTx handler to the app's struct definition. This will allow us to override the default checkTx handler to process bid transactions - before they are inserted into the mempool. NOTE: The custom handler is + before they are inserted into the `LanedMempool`. NOTE: The custom handler is required as otherwise the auction can be held hostage by a malicious users. @@ -107,18 +108,17 @@ $ go install github.com/skip-mev/block-sdk } ``` - c. Instantiate the Block SDK mempool with the application's desired lanes. + c. Instantiate the Block SDK's `LanedMempool` with the application's desired lanes. ```go // 1. Create the lanes. // - // 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. + // 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. // // For more information on how to utilize the LaneConfig please - // visit the README in block-sdk/block/base. + // visit the README in docs.skip.money/chains/lanes/build-your-own-lane#-lane-config. // // MEV lane hosts an auction at the top of the block. mevConfig := base.LaneConfig{ @@ -133,21 +133,7 @@ $ go install github.com/skip-mev/block-sdk mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), ) - // Free lane allows transactions to be included in the next block for free. - freeConfig := base.LaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: math.LegacyZeroDec(), - MaxTxs: 0, - } - freeLane := free.NewFreeLane( - freeConfig, - base.DefaultTxPriority(), - free.DefaultMatchHandler(), - ) - - // Standard lane accepts all other transactions. + // default lane accepts all other transactions. defaultConfig := base.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(), @@ -160,43 +146,20 @@ $ go install github.com/skip-mev/block-sdk // 2. Set up the relateive priority of lanes lanes := []block.Lane{ mevLane, - freeLane, defaultLane, } mempool := block.NewLanedMempool(app.Logger(), true, lanes...) app.App.SetMempool(mempool) ``` - d. Instantiate the antehandler chain for the application with awareness of the - LanedMempool. This will allow the application to verify the validity - of a transaction respecting the desired logic of a given lane. In this walkthrough, - we want the `FeeDecorator` to be ignored for all transactions that should - belong to the free lane. Additionally, we want to add the `x/builder` - module's `AuctionDecorator` to the ante-handler chain. The `AuctionDecorator` - is an AnteHandler decorator that enforces various chain configurable MEV rules. + d. Add the `x/builder` module's `AuctionDecorator` to the ante-handler + chain. The `AuctionDecorator` is an AnteHandler decorator that enforces + various chain configurable MEV rules. ```go - import ( - ... - "github.com/skip-mev/block-sdk/block" - "github.com/skip-mev/block-sdk/block/utils" - builderante "github.com/skip-mev/block-sdk/x/builder/ante" - ... - ) - anteDecorators := []sdk.AnteDecorator{ ante.NewSetUpContextDecorator(), ... - utils.NewIgnoreDecorator( - ante.NewDeductFeeDecorator( - options.BaseOptions.AccountKeeper, - options.BaseOptions.BankKeeper, - options.BaseOptions.FeegrantKeeper, - options.BaseOptions.TxFeeChecker, - ), - options.FreeLane, - ), - ... builderante.NewBuilderDecorator( options.BuilderKeeper, options.TxEncoder, @@ -209,6 +172,9 @@ $ go install github.com/skip-mev/block-sdk app.SetAnteHandler(anteHandler) // Set the antehandlers on the lanes. + // + // NOTE: This step is required as otherwise the lanes will not be able to + // process auction transactions. for _, lane := range lanes { lane.SetAnteHandler(anteHandler) } @@ -250,36 +216,40 @@ $ go install github.com/skip-mev/block-sdk 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( - app.App, - app.txConfig.TxDecoder(), - tobLane, - anteHandler, - app.ChainID(), - ) - app.SetCheckTx(checkTxHandler.CheckTx()) + checkTxHandler := mev.NewCheckTxHandler( + app.App, + app.txConfig.TxDecoder(), + mevLane, + anteHandler, + ) + app.SetCheckTx(checkTxHandler.CheckTx()) ... - func (app *TestApp) CheckTx(req cometabci.RequestCheckTx) - cometabci.ResponseCheckTx { - return app.checkTxHandler(req) + // CheckTx will check the transaction with the provided checkTxHandler. + // We override the default handler so that we can verify bid transactions + // before they are inserted into the mempool. With the POB CheckTx, we can + // verify the bid transaction and all of the bundled transactions + // before inserting the bid transaction into the mempool. + func (app *TestApp) CheckTx(req *cometabci.RequestCheckTx) + (*cometabci.ResponseCheckTx, error) { + return app.checkTxHandler(req) } // SetCheckTx sets the checkTxHandler for the app. - func (app *TestApp) SetCheckTx(handler abci.CheckTx) { - app.checkTxHandler = handler + func (app *TestApp) SetCheckTx(handler mev.CheckTx) { + app.checkTxHandler = handler } ``` - f. Finally, update the app's `InitGenesis` order and ante-handler chain. + f. Finally, update the app's `InitGenesis` order. ```go genesisModuleOrder := []string{ @@ -291,5 +261,5 @@ $ go install github.com/skip-mev/block-sdk ## Params Note, before building or upgrading the application, make sure to initialize the -escrow address for POB in the parameters of the module. The default parameters +escrow address in the parameters of the module. The default parameters initialize the escrow address to be the module account address. From 48ac218773fd7261de70348d4a72879d44de3f30 Mon Sep 17 00:00:00 2001 From: David Terpay Date: Wed, 16 Aug 2023 19:09:30 -0400 Subject: [PATCH 38/41] mev lane nit --- lanes/mev/README.md | 224 ++++++++++++++++++++++---------------------- 1 file changed, 111 insertions(+), 113 deletions(-) diff --git a/lanes/mev/README.md b/lanes/mev/README.md index 03c8939..4a27c2a 100644 --- a/lanes/mev/README.md +++ b/lanes/mev/README.md @@ -80,118 +80,120 @@ NOTE: This example walks through setting up the MEV and Default lanes. messages that allow users to participate in the top of block auction, distribute revenue to the auction house, and ensure the validity of auction transactions. - a. First add the keeper to the app's struct definition. We also want to add - MEV lane's custom checkTx handler to the app's struct definition. This will - allow us to override the default checkTx handler to process bid transactions - before they are inserted into the `LanedMempool`. NOTE: The custom handler is - required as otherwise the auction can be held hostage by a malicious - users. + a. First add the keeper to the app's struct definition. We also want to add + MEV lane's custom checkTx handler to the app's struct definition. This will + allow us to override the default checkTx handler to process bid transactions + before they are inserted into the `LanedMempool`. NOTE: The custom handler + is required as otherwise the auction can be held hostage by a malicious + users. - ```go - type App struct { - ... - // BuilderKeeper is the keeper that handles processing auction transactions - BuilderKeeper builderkeeper.Keeper + ```go + type App struct { + ... + // BuilderKeeper is the keeper that handles processing auction transactions + BuilderKeeper builderkeeper.Keeper - // Custom checkTx handler - checkTxHandler mev.CheckTx - } - ``` + // Custom checkTx handler + checkTxHandler mev.CheckTx + } + ``` b. Add the builder module to the list of module account permissions. This will instantiate the builder module account on genesis. - ```go - maccPerms = map[string][]string{ - builder.ModuleName: nil, - ... - } - ``` + ```go + maccPerms = map[string][]string{ + builder.ModuleName: nil, + ... + } + ``` - c. Instantiate the Block SDK's `LanedMempool` with the application's desired lanes. + c. Instantiate the Block SDK's `LanedMempool` with the application's + desired lanes. - ```go - // 1. Create the lanes. - // - // 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. - // - // For more information on how to utilize the LaneConfig please - // visit the README in docs.skip.money/chains/lanes/build-your-own-lane#-lane-config. - // - // MEV lane hosts an auction at the top of the block. - mevConfig := base.LaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: math.LegacyZeroDec(), - MaxTxs: 0, - } - mevLane := mev.NewMEVLane( - mevConfig, - mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), - ) + ```go + // 1. Create the lanes. + // + // 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. + // + // For more information on how to utilize the LaneConfig please + // visit the README in docs.skip.money/chains/lanes/build-your-own-lane#-lane-config. + // + // MEV lane hosts an auction at the top of the block. + mevConfig := base.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, + } + mevLane := mev.NewMEVLane( + mevConfig, + mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), + ) - // default lane accepts all other transactions. - defaultConfig := base.LaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: math.LegacyZeroDec(), - MaxTxs: 0, - } - defaultLane := base.NewStandardLane(defaultConfig) + // default lane accepts all other transactions. + defaultConfig := base.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, + } + defaultLane := base.NewStandardLane(defaultConfig) - // 2. Set up the relateive priority of lanes - lanes := []block.Lane{ - mevLane, - defaultLane, - } - mempool := block.NewLanedMempool(app.Logger(), true, lanes...) - app.App.SetMempool(mempool) - ``` + // 2. Set up the relateive priority of lanes + lanes := []block.Lane{ + mevLane, + defaultLane, + } + mempool := block.NewLanedMempool(app.Logger(), true, lanes...) + app.App.SetMempool(mempool) + ``` d. Add the `x/builder` module's `AuctionDecorator` to the ante-handler chain. The `AuctionDecorator` is an AnteHandler decorator that enforces various chain configurable MEV rules. - ```go - anteDecorators := []sdk.AnteDecorator{ - ante.NewSetUpContextDecorator(), - ... - builderante.NewBuilderDecorator( - options.BuilderKeeper, - options.TxEncoder, - options.TOBLane, - options.Mempool, - ), - } + ```go + anteDecorators := []sdk.AnteDecorator{ + ante.NewSetUpContextDecorator(), + ... + builderante.NewBuilderDecorator( + options.BuilderKeeper, + options.TxEncoder, + options.TOBLane, + options.Mempool, + ), + } - anteHandler := sdk.ChainAnteDecorators(anteDecorators...) - app.SetAnteHandler(anteHandler) + anteHandler := sdk.ChainAnteDecorators(anteDecorators...) + app.SetAnteHandler(anteHandler) - // Set the antehandlers on the lanes. - // - // NOTE: This step is required as otherwise the lanes will not be able to - // process auction transactions. - for _, lane := range lanes { - lane.SetAnteHandler(anteHandler) - } - app.App.SetAnteHandler(anteHandler) - ``` + // Set the antehandlers on the lanes. + // + // NOTE: This step is required as otherwise the lanes will not be able to + // process auction transactions. + for _, lane := range lanes { + lane.SetAnteHandler(anteHandler) + } + app.App.SetAnteHandler(anteHandler) + ``` e. Instantiate the builder keeper, store keys, and module manager. Note, be sure to do this after all the required keeper dependencies have been instantiated. - ```go - keys := storetypes.NewKVStoreKeys( + ```go + keys := storetypes.NewKVStoreKeys( buildertypes.StoreKey, ... - ) + ) - ... - app.BuilderKeeper := builderkeeper.NewKeeper( + ... + app.BuilderKeeper := builderkeeper.NewKeeper( appCodec, keys[buildertypes.StoreKey], app.AccountKeeper, @@ -199,43 +201,39 @@ NOTE: This example walks through setting up the MEV and Default lanes. app.DistrKeeper, app.StakingKeeper, authtypes.NewModuleAddress(govv1.ModuleName).String(), - ) + ) - - app.ModuleManager = module.NewManager( + + app.ModuleManager = module.NewManager( builder.NewAppModule(appCodec, app.BuilderKeeper), ... - ) - ``` + ) + ``` - e. Configure the proposal/checkTx handlers on base app. + f. Configure the proposal/checkTx handlers on base app. ```go - // Create the proposal handler that will be used to build and validate blocks. proposalHandler := abci.NewProposalHandler( - app.Logger(), - app.txConfig.TxDecoder(), - lanes, + app.Logger(), + app.txConfig.TxDecoder(), + lanes, ) app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler()) app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) - // Set the custom CheckTx handler on BaseApp. - checkTxHandler := mev.NewCheckTxHandler( - app.App, - app.txConfig.TxDecoder(), - mevLane, - anteHandler, - ) - app.SetCheckTx(checkTxHandler.CheckTx()) - ... - + checkTxHandler := mev.NewCheckTxHandler( + app.App, + app.txConfig.TxDecoder(), + mevLane, + anteHandler, + ) + app.SetCheckTx(checkTxHandler.CheckTx()) // CheckTx will check the transaction with the provided checkTxHandler. - // We override the default handler so that we can verify bid transactions - // before they are inserted into the mempool. With the POB CheckTx, we can + // We override the default handler so that we can verify transactions + // before they are inserted into the mempool. With the CheckTx, we can // verify the bid transaction and all of the bundled transactions // before inserting the bid transaction into the mempool. func (app *TestApp) CheckTx(req *cometabci.RequestCheckTx) @@ -249,12 +247,12 @@ NOTE: This example walks through setting up the MEV and Default lanes. } ``` - f. Finally, update the app's `InitGenesis` order. + g. Finally, update the app's `InitGenesis` order. ```go genesisModuleOrder := []string{ - buildertypes.ModuleName, - ..., + buildertypes.ModuleName, + ..., } ``` From 8bd3c76bea861f3b9fb27f70738fd8eb326c959e Mon Sep 17 00:00:00 2001 From: MaghnusM Date: Wed, 16 Aug 2023 19:14:19 -0400 Subject: [PATCH 39/41] mag add changes --- README.md | 45 +--- go.work.sum | 198 +++++++++++++++++ lanes/base/README.md | 10 +- lanes/build-your-own/README.md | 388 +++++++++++++++++++++++++++++++++ lanes/mev/README.md | 11 +- 5 files changed, 598 insertions(+), 54 deletions(-) create mode 100644 lanes/build-your-own/README.md diff --git a/README.md b/README.md index 1dc3e18..92c62bb 100644 --- a/README.md +++ b/README.md @@ -16,53 +16,14 @@ Skip has built out a number of plug-and-play `lanes` on the SDK that your protocol can use, including in-protocol MEV recapture and Oracles! Additionally, the Block SDK can be extended to add **your own custom `lanes`** to configure your blocks to exactly fit your application needs. -### ❌ Problems: Blocks are not Customizable - -Most Cosmos chains today utilize standard `CometBFT` block construction - which is too limited. - -* The standard `CometBFT` block building is susceptible to MEV-related issues, such as front-running and sandwich attacks, since proposers have monopolistic rights on ordering and no verification of good behavior. MEV that is created cannot be redistributed to the protocol. -* The standard `CometBFT` block building uses a one-size-fits-all approach, which can result in inefficient transaction processing for specific applications or use cases and sub-optimal fee markets. -* Transactions tailored for specific applications may need custom prioritization, ordering or validation rules that the mempool is otherwise unaware of because transactions within a block are currently in-differentiable when a blockchain might want them to be. - -### ✅ Solution: The Block SDK - -You can think of the Block SDK as a **transaction `highway` system**, where each -`lane` on the highway serves a specific purpose and has its own set of rules and -traffic flow. - -In the Block SDK, each lane has its own set of rules and transaction flow management systems. - -* A lane is what we might traditionally consider to be a standard mempool - where transaction **_validation_**, **_ordering_** and **_prioritization_** for - contained transactions are shared. -* lanes implement a **standard interface** that allows each individual lane to - propose and validate a portion of a block. -* lanes are ordered with each other, configurable by developers. All lanes - together define the desired block structure of a chain. - -### ✨ Block SDK Use Cases - -A block with separate `lanes` can be used for: - -1. **MEV mitigation**: a top of block lane could be designed to create an in-protocol top-of-block auction (as we are doing with the Block SDK) to recapture MEV in a transparent and governable way. -2. **Free/reduced fee txs**: transactions with certain properties (e.g. from trusted accounts or performing encouraged actions) could leverage a free lane to reward behavior. -3. **Dedicated oracle space** Oracles could be included before other kinds of transactions to ensure that price updates occur first, and are not able to be sandwiched or manipulated. -4. **Orderflow auctions**: an OFA lane could be constructed such that order flow providers can have their submitted transactions bundled with specific backrunners, to guarantee MEV rewards are attributed back to users. Imagine MEV-share but in protocol. -5. **Enhanced and customizable privacy**: privacy-enhancing features could be introduced, such as threshold encrypted lanes, to protect user data and maintain privacy for specific use cases. -6. **Fee market improvements**: one or many fee markets - such as EIP-1559 - could be easily adopted for different lanes (potentially custom for certain dApps). Each smart contract/exchange could have its own fee market or auction for transaction ordering. -7. **Congestion management**: segmentation of transactions to lanes can help mitigate network congestion by capping usage of certain applications and tailoring fee markets. - - ### 📚 Block SDK Documentation -#### Lane App Store +To read more about how the Block SDK works, check out the [How it Works](https://docs.skip.money/chains/overview). + +#### Lane "App Store" To read more about Skip's pre-built `lanes` and how to use them, check out the [Lane App Store](https://docs.skip.money/chains/lanes/existing-lanes/default). -#### How the Block SDK works - -To read more about how the Block SDK works, check out the [How it Works](https://docs.skip.money/chains/how-it-works). - #### Lane Development To read more about how to build your own custom `lanes`, check out the [Build Your Own Lane](https://docs.skip.money/chains/lanes/build-your-own-lane). diff --git a/go.work.sum b/go.work.sum index 177f0a9..1b6fa7d 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,16 +1,214 @@ +4d63.com/gocheckcompilerdirectives v1.2.1/go.mod h1:yjDJSxmDTtIHHCqX0ufRYZDL6vQtMG7tJdKVeWwsqvs= +4d63.com/gochecknoglobals v0.2.1/go.mod h1:KRE8wtJB3CXCsb1xy421JfTHIIbmT3U5ruxw2Qu8fSU= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +github.com/Abirdcfly/dupword v0.0.11/go.mod h1:wH8mVGuf3CP5fsBTkfWwwwKTjDnVVCxtU8d8rgeVYXA= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= +github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0/go.mod h1:b3g59n2Y+T5xmcxJL+UEG2f8cQploZm1mR/v6BW0mU0= +github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/OpenPeeDeeP/depguard v1.1.1/go.mod h1:JtAMzWkmFEzDPyAd+W0NHl1lvpQKTvT9jnRVsohBKpc= +github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg= +github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= +github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI= +github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= +github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= +github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= +github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= +github.com/bombsimon/wsl/v3 v3.4.0/go.mod h1:KkIB+TXkqy6MvK9BDZVbZxKNYsE1/oLRJbIFtf14qqo= +github.com/breml/bidichk v0.2.4/go.mod h1:7Zk0kRFt1LIZxtQdl9W9JwGAcLTTkOs+tN7wuEYGJ3s= +github.com/breml/errchkjson v0.3.1/go.mod h1:XroxrzKjdiutFyW3nWhw34VGg7kiMsDQox73yWCGI2U= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= +github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= +github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ= +github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8/go.mod h1:gakxgyXaaPkxvLw1XQxNGK4I37ys9iBRzNUx/B7pUCo= +github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= +github.com/cloudflare/circl v1.3.1/go.mod h1:+CauBF6R70Jqcyl8N2hC8pAXYbWkGIezuSbuGLtRhnw= github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= +github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cosmos/cosmos-db v0.0.0-20221226095112-f3c38ecb5e32 h1:zlCp9n3uwQieELltZWHRmwPmPaZ8+XoL2Sj+A2YJlr8= +github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc= +github.com/daixiang0/gci v0.10.1/go.mod h1:xtHP9N7AHdNvtRNfcx9gwTDfw7FRJx4bZUsiEfiNNAI= +github.com/denis-tingaikin/go-header v0.4.3/go.mod h1:0wOCWuN71D5qIgE2nz9KrKmuYBAC2Mra5RassOIQ2/c= +github.com/docker/cli v23.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= 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/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/esimonov/ifshort v1.0.4/go.mod h1:Pe8zjlRrJ80+q2CxHLfEOfTwxCZ4O+MuhcHcfgNWTk0= +github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY= +github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= +github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= +github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= +github.com/firefart/nonamedreturns v1.0.4/go.mod h1:TDhe/tjI1BXo48CmYbUduTV7BdIga8MAO/xbKdcVsGI= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= github.com/getsentry/sentry-go v0.17.0 h1:UustVWnOoDFHBS7IJUB2QK/nB5pap748ZEp0swnQJak= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-toolsmith/astcast v1.1.0/go.mod h1:qdcuFWeGGS2xX5bLM/c3U9lewg7+Zu4mr+xPwZIB4ZU= +github.com/go-toolsmith/astcopy v1.1.0/go.mod h1:hXM6gan18VA1T/daUEHCFcYiW8Ai1tIwIzHY6srfEAw= +github.com/go-toolsmith/astequal v1.1.0/go.mod h1:sedf7VIdCL22LD8qIvv7Nn9MuWJruQA/ysswh64lffQ= +github.com/go-toolsmith/astfmt v1.1.0/go.mod h1:OrcLlRwu0CuiIBp/8b5PYF9ktGVZUjlNMV634mhwuQ4= +github.com/go-toolsmith/astp v1.1.0/go.mod h1:0T1xFGz9hicKs8Z5MfAqSUitoUYS30pDMsRVIDHs8CA= +github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ= +github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig= +github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= +github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= +github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= +github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe/go.mod h1:gjqyPShc/m8pEMpk0a3SeagVb0kaqvhscv+i9jI5ZhQ= +github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2/go.mod h1:9wOXstvyDRshQ9LggQuzBCGysxs3b6Uo/1MvYCR2NMs= +github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= +github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= +github.com/golangci/misspell v0.4.0/go.mod h1:W6O/bwV6lGDxUCChm2ykw9NQdd5bYd1Xkjo88UcWyJc= +github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6/go.mod h1:0AKcRCkMoKvUvlf89F6O7H2LYdhr1zBh736mBItOdRs= +github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc= +github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM= +github.com/gostaticanalysis/forcetypeassert v0.1.0/go.mod h1:qZEedyP/sY1lTGV1uJ3VhWZ2mqag3IkWsDHVbplHXak= +github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= +github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= +github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= +github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.6.3/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw= +github.com/kkHAIKE/contextcheck v1.1.4/go.mod h1:1+i/gWqokIa+dm31mqGLZhZJ7Uh44DJGZVmr6QRBNJg= +github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I= +github.com/kunwardeep/paralleltest v1.0.6/go.mod h1:Y0Y0XISdZM5IKm3TREQMZ6iteqn1YuwCsJO/0kL9Zes= +github.com/kyoh86/exportloopref v0.1.11/go.mod h1:qkV4UF1zGl6EkF1ox8L5t9SwyeBAZ3qLMd6up458uqA= +github.com/ldez/gomoddirectives v0.2.3/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/leonklingele/grouper v1.1.1/go.mod h1:uk3I3uDfi9B6PeUjsCKi6ndcf63Uy7snXgR4yDYQVDY= +github.com/lufeee/execinquery v1.2.1/go.mod h1:EC7DrEKView09ocscGHC+apXMIaorh4xqSxS/dy8SbM= +github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE= +github.com/maratori/testpackage v1.1.1/go.mod h1:s4gRK/ym6AMrqpOa/kEbQTV4Q4jb7WeLZzVhVVVOQMc= +github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= +github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= 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/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= +github.com/nats-io/jwt/v2 v2.0.3/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY= +github.com/nats-io/nats-server/v2 v2.5.0/go.mod h1:Kj86UtrXAL6LwYRA6H4RqzkHhK0Vcv2ZnKD5WbQ1t3g= +github.com/nats-io/nats.go v1.12.1/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= +github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= +github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= +github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= +github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/quasilyte/go-ruleguard v0.3.19/go.mod h1:lHSn69Scl48I7Gt9cX3VrbsZYvYiBYszZOZW4A+oTEw= +github.com/quasilyte/gogrep v0.5.0/go.mod h1:Cm9lpz9NZjEoL1tgZ2OgeUKPIxL1meE7eo60Z6Sk+Ng= +github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= +github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= +github.com/ryancurrah/gomodguard v1.3.0/go.mod h1:ggBxb3luypPEzqVtq33ee7YSN35V28XeGnid8dnni50= +github.com/ryanrolds/sqlclosecheck v0.4.0/go.mod h1:TBRRjzL31JONc9i4XMinicuo+s+E8yKZ5FN8X3G6CKQ= +github.com/sanposhiho/wastedassign/v2 v2.0.7/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI= +github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ= +github.com/sashamelentyev/usestdlibvars v1.23.0/go.mod h1:YPwr/Y1LATzHI93CqoPUN/2BzGQ/6N/cl/KwgR0B/aU= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sivchari/containedctx v1.0.2/go.mod h1:PwZOeqm4/DLoJOqMSIJs3aKqXRX4YO+uXww087KZ7Bw= +github.com/sivchari/nosnakecase v1.7.0/go.mod h1:CwDzrzPea40/GB6uynrNLiorAlgFRvRbFSgJx2Gs+QY= +github.com/sivchari/tenv v1.7.1/go.mod h1:64yStXKSOxDfX47NlhVwND4dHwfZDdbp2Lyl018Icvg= +github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag= +github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa/go.mod h1:oJyF+mSPHbB5mVY2iO9KV3pTt/QbIkGaO8gQ2WrDbP4= +github.com/sonatard/noctx v0.0.2/go.mod h1:kzFz+CzWSjQ2OzIm46uJZoXuBpa2+0y3T36U18dWqIo= +github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= +github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= +github.com/stbenjam/no-sprintf-host-port v0.1.1/go.mod h1:TLhvtIvONRzdmkFiio4O8LHsN9N74I+PhRquPsxpL0I= +github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c/go.mod h1:SbErYREK7xXdsRiigaQiQkI9McGRzYMvlKYaP3Nimdk= +github.com/tdakkota/asciicheck v0.2.0/go.mod h1:Qb7Y9EgjCLJGup51gDHFzbI08/gbGhL/UVhYIPWG2rg= +github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= +github.com/tetafro/godot v1.4.11/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8= +github.com/timonwong/loggercheck v0.9.4/go.mod h1:caz4zlPcgvpEkXgVnAJGowHAMW2NwHaNlpS8xDbVhTg= +github.com/tomarrell/wrapcheck/v2 v2.8.1/go.mod h1:/n2Q3NZ4XFT50ho6Hbxg+RV1uyo2Uow/Vdm9NQcl5SE= +github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= +github.com/ultraware/whitespace v0.0.5/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= +github.com/uudashr/gocognit v1.0.6/go.mod h1:nAIUuVBnYU7pcninia3BHOvQkpQCeO76Uscky5BOwcY= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk= +github.com/yeya24/promlinter v0.2.0/go.mod h1:u54lkmBOZrpEbQQ6gox2zWKKLKu2SGe+2KOiextY+IA= +gitlab.com/bosi/decorder v0.2.3/go.mod h1:9K1RB5+VPNQYtXtTDAzd2OEftsZb1oV0IrJrzChSdGE= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +golang.org/x/exp/typeparams v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +gonum.org/v1/gonum v0.12.0/go.mod h1:73TDxJfAAHeA8Mk9mf8NlIppyhQNo5GLTcYeqgo2lvY= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.4.3/go.mod h1:36ZgoUOrqOk1GxwHhyryEkq8FQWkUO2xGuSMhUCcdvA= +mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ= +mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= +mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= +mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d/go.mod h1:IeHQjmn6TOD+e4Z3RFiZMMsLVL+A96Nvptar8Fj71is= diff --git a/lanes/base/README.md b/lanes/base/README.md index 24be4b4..48c7399 100644 --- a/lanes/base/README.md +++ b/lanes/base/README.md @@ -17,10 +17,11 @@ $ go install github.com/skip-mev/block-sdk 1. First determine the set of lanes that you want to use in your application. The available lanes can be found in our -[Lane App Store](https://docs.skip.money/chains/lanes/existing-lanes/default). -In your base application, you will need to create a `LanedMempool` composed of the -lanes you want to use. -2. Next, order the lanes by priority. The first lane is the highest priority lane +[Lane App Store](https://docs.skip.money/chains/lanes/existing-lanes/default). This guide +only sets up the `default lane` +2. In your base application, you will need to create a `LanedMempool` composed of the +`lanes` you want to use. +3. Next, order the lanes by priority. The first lane is the highest priority lane and the last lane is the lowest priority lane. **It is recommended that the last lane is the default lane.** 3. You will also need to create a `PrepareProposalHandler` and a @@ -28,6 +29,7 @@ lane is the default lane.** proposals respectively. Configure the order of the lanes in the `PrepareProposalHandler` and `ProcessProposalHandler` to match the order of the lanes in the `LanedMempool`. +4. Configure your `app.go` to include the following: ```golang import ( diff --git a/lanes/build-your-own/README.md b/lanes/build-your-own/README.md new file mode 100644 index 0000000..83b295c --- /dev/null +++ b/lanes/build-your-own/README.md @@ -0,0 +1,388 @@ +# 🏗️ Build-Your-Own Lane Setup + +## 📦 Dependencies + +The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently +compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. + +## 📥 Installation + +To install the Block SDK, run the following command: + +```bash +$ go install github.com/skip-mev/block-sdk +``` + +## 🤔 How to use it [1 hour] + +There are **five** required components to building a custom lane using the base lane: + +1. `Mempool` - The lane's mempool is responsible for storing transactions that have been verified and are waiting to be included in proposals. +2. `MatchHandler` - This is responsible for determining whether a transaction should belong to this lane. +3. [**OPTIONAL**] `PrepareLaneHandler` - Allows developers to define their own handler to customize the how transactions are verified and ordered before they are included into a proposal. +4. [**OPTIONAL**] `CheckOrderHandler` - Allows developers to define their own handler that will run any custom checks on whether transactions included in block proposals are in the correct order (respecting the ordering rules of the lane and the ordering rules of the other lanes). +5. [**OPTIONAL**] `ProcessLaneHandler` - Allows developers to define their own handler for processing transactions that are included in block proposals. +6. `Configuration` - Configure high-level options for your lane. + +### 1. 🗄️ Mempool + +This is the data structure that is responsible for storing transactions as they are being verified and are waiting to be included in proposals. `block/base/mempool.go` provides an out-of-the-box implementation that should be used as a starting point for building out the mempool and should cover most use cases. To utilize the mempool, you must implement a `TxPriority[C]` struct that does the following: + +- Implements a `GetTxPriority` method that returns the priority (as defined + by the type `[C]`) of a given transaction. +- Implements a `Compare` method that returns the relative priority of two + transactions. If the first transaction has a higher priority, the method + should return -1, if the second transaction has a higher priority the method + should return 1, otherwise the method should return 0. +- Implements a `MinValue` method that returns the minimum priority value + that a transaction can have. + +The default implementation can be found in `block/base/mempool.go`. + +:::info Scenario +What if we wanted to prioritize transactions by the amount they have staked on a chain? + +::: + +We could do the following: + +```golang +// CustomTxPriority returns a TxPriority that prioritizes transactions by the +// amount they have staked on chain. This means that transactions with a higher +// amount staked will be prioritized over transactions with a lower amount staked. +func (p *CustomTxPriority) CustomTxPriority() TxPriority[string] { + return TxPriority[string]{ + GetTxPriority: func(ctx context.Context, tx sdk.Tx) string { + // Get the signer of the transaction. + signer := p.getTransactionSigner(tx) + + // Get the total amount staked by the signer on chain. + // This is abstracted away in the example, but you can + // implement this using the staking keeper. + totalStake, err := p.getTotalStake(ctx, signer) + if err != nil { + return "" + } + + return totalStake.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: "", + } +} +``` + +#### Using a Custom TxPriority + +To utilize this new priority configuration in a lane, all you have to then do is pass in the `TxPriority[C]` to the `NewMempool` function. + +```golang +// Create the lane config +laneCfg := NewLaneConfig( + ... + MaxTxs: 100, + ... +) + +// Pseudocode for creating the custom tx priority +priorityCfg := NewPriorityConfig( + stakingKeeper, + accountKeeper, + ... +) + + +// define your mempool that orders transactions by on-chain stake +mempool := base.NewMempool[string]( + priorityCfg.CustomTxPriority(), // pass in the custom tx priority + laneCfg.TxEncoder, + laneCfg.MaxTxs, +) + +// Initialize your lane with the mempool +lane := base.NewBaseLane( + laneCfg, + LaneName, + mempool, + base.DefaultMatchHandler(), +) +``` + +### 2. 🤝 MatchHandler + +`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!** The default implementation can be found in `block/base/handlers.go`. + +The match handler can be as custom as desired. Following the example above, if we wanted to make a lane that only accepts transactions if they have a large amount staked, we could do the following: + +```golang +// CustomMatchHandler returns a custom implementation of the MatchHandler. It +// matches transactions that have a large amount staked. These transactions +// will then be charged no fees at execution time. +// +// NOTE: This is a stateful check on the transaction. The details of how to +// implement this are abstracted away in the example, but you can implement +// this using the staking keeper. +func (h *Handler) CustomMatchHandler() block.MatchHandler { + return func(ctx sdk.Context, tx sdk.Tx) bool { + if !h.IsStakingTx(tx) { + return false + } + + signer, err := getTxSigner(tx) + if err != nil { + return false + } + + stakedAmount, err := h.GetStakedAmount(signer) + if err != nil { + return false + } + + // The transaction can only be considered for inclusion if the amount + // staked is greater than some predetermined threshold. + return stakeAmount.GT(h.Threshold) + } +} +``` + +#### Using a Custom MatchHandler + +If we wanted to create the lane using the custom match handler along with the custom mempool, we could do the following: + +```golang +// Pseudocode for creating the custom match handler +handler := NewHandler( + stakingKeeper, + accountKeeper, + ... +) + +// define your mempool that orders transactions by on chain stake +mempool := base.NewMempool[string]( + priorityCfg.CustomTxPriority(), + cfg.TxEncoder, + cfg.MaxTxs, +) + +// Initialize your lane with the mempool +lane := base.NewBaseLane( + cfg, + LaneName, + mempool, + handler.CustomMatchHandler(), +) +``` + +### [OPTIONAL] Steps 3-5 + +The remaining steps walk through the process of creating custom block building/verification logic. The default implementation found in `block/base/handlers.go` should fit most use cases. Please reference that file for more details on the default implementation and whether it fits your use case. + +Implementing custom block building/verification logic is a bit more involved than the previous steps and is a all or nothing approach. This means that if you implement any of the handlers, you must implement all of them in most cases. If you do not implement all of them, the lane may have unintended behavior. + +### 3. 🛠️ PrepareLaneHandler + +The `PrepareLaneHandler` is an optional field you can set on the base lane. +This handler is responsible for the transaction selection logic when a new proposal +is requested. + +The handler should return the following for a given lane: + +1. The transactions to be included in the block proposal. +2. The transactions to be removed from the lane's mempool. +3. An error if the lane is unable to prepare a block proposal. + +```golang +// 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) +``` + +The default implementation is simple. It will continue to select transactions from its mempool under the following criteria: + +1. The transactions is not already included in the block proposal. +2. The transaction is valid and passes the AnteHandler check. +3. The transaction is not too large to be included in the block. + +If a more involved selection process is required, you can implement your own `PrepareLaneHandler` and and set it after creating the base lane. + +```golang +// Pseudocode for creating the custom prepare lane handler +// This assumes that the CustomLane inherits from the base +// lane. +customLane := NewCustomLane( + cfg, + mempool, + handler.CustomMatchHandler(), +) + +// Set the custom PrepareLaneHandler on the lane +customLane.SetPrepareLaneHandler(customlane.PrepareLaneHandler()) +``` + +### 4. ✅ CheckOrderHandler + +The `CheckOrderHandler` is an optional field you can set on the base lane. This handler is responsible for verifying the ordering of the transactions in the block proposal that belong to the lane. + +```golang +// 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 +``` + +The default implementation is simple and utilizes the same `TxPriority` struct that the mempool uses to determine if transactions are in order. The criteria for determining if transactions are in order is as follows: + +1. The transactions are in order according to the `TxPriority` struct. i.e. any two transactions (that match to the lane) `tx1` and `tx2` where `tx1` has a higher priority than `tx2` should be ordered before `tx2`. +2. The transactions are contiguous. i.e. there are no transactions from other lanes in between the transactions that belong to this lane. i.e. if `tx1` and `tx2` belong to the lane, there should be no transactions from other lanes in between `tx1` and `tx2`. + +If a more involved ordering process is required, you can implement your own `CheckOrderHandler` and and set it after creating the base lane. + +```golang +// Pseudocode for creating the custom check order handler +// This assumes that the CustomLane inherits from the base +// lane. +customLane := NewCustomLane( + cfg, + mempool, + handler.CustomMatchHandler(), +) + +// Set the custom CheckOrderHandler on the lane +customLane.SetCheckOrderHandler(customlane.CheckOrderHandler()) +``` + +### 5. 🆗 ProcessLaneHandler + +The `ProcessLaneHandler` is an optional field you can set on the base lane. This handler is responsible for verifying the transactions in the block proposal that belong to the lane. This handler is executed after the `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. This means that if the first transaction does not belong to the lane, the remaining transactions should not belong to the lane either. + +```golang +// 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) +``` + +Given the invarients above, the default implementation is simple. It will continue to verify transactions in the block proposal under the following criteria: + +1. If a transaction matches to this lane, verify it and continue. If it is not valid, return an error. +2. If a transaction does not match to this lane, return the remaining transactions to the next lane to process. + +Similar to the setup of handlers above, if a more involved verification process is required, you can implement your own `ProcessLaneHandler` and and set it after creating the base lane. + +```golang +// Pseudocode for creating the custom check order handler +// This assumes that the CustomLane inherits from the base +// lane. +customLane := NewCustomLane( + cfg, + mempool, + handler.CustomMatchHandler(), +) + +// Set the custom ProcessLaneHandler on the lane +customLane.SetProcessLaneHandler(customlane.ProcessLaneHandler()) +``` + +### 6. 📝 Lane Configuration + +Once you have created your custom lane, you can configure it in the application by doing the following: + +1. Create a custom `LaneConfig` struct that defines the configuration of the lane. +2. Instantiate the lane with the custom `LaneConfig` struct alongside any other dependencies (mempool, match handler, etc.). +3. Instantiate a new `LanedMempool` with the custom lane. +4. Set the `LanedMempool` on the `BaseApp` instance. +5. Set up the proposal handlers of the Block SDK to use your lane. +6. That's it! You're done! + +The lane config (`LaneConfig`) is a simple configuration object that defines the desired amount of block space the lane should utilize when building a proposal, an antehandler that is used to verify transactions as they are added/verified to/in a proposal, and more. By default, we recommend that user's pass in all of the base apps configurations (txDecoder, logger, etc.). A sample `LaneConfig` might look like the following: + +```golang +config := block.LaneConfig{ + Logger: app.Logger(), + TxDecoder: app.TxDecoder(), + TxEncoder: app.TxEncoder(), + AnteHandler: app.AnteHandler(), + MaxTxs: 0, + MaxBlockSpace: math.LegacyZeroDec(), + IgnoreList: []block.Lane{}, +} +``` + +The three most important parameters to set are the `AnteHandler`, `MaxTxs`, and `MaxBlockSpace`. + +#### **AnteHandler** + +With the default implementation, the `AnteHandler` is responsible for verifying transactions as they are being considered for a new proposal or are being processed in a proposed block. We recommend user's utilize the same antehandler chain that is used in the base app. If developers want a certain `AnteDecorator` to be ignored if it qualifies for a given lane, they can do so by using the `NewIgnoreDecorator` defined in `block/utils/ante.go`. + +For example, a free lane might want to ignore the `DeductFeeDecorator` so that its transactions are not charged any fees. Where ever the `AnteHandler` is defined, we could add the following to ignore the `DeductFeeDecorator`: + +```golang +anteDecorators := []sdk.AnteDecorator{ + ante.NewSetUpContextDecorator(), + ..., + utils.NewIgnoreDecorator( + ante.NewDeductFeeDecorator( + options.BaseOptions.AccountKeeper, + options.BaseOptions.BankKeeper, + options.BaseOptions.FeegrantKeeper, + options.BaseOptions.TxFeeChecker, + ), + options.FreeLane, + ), + ..., +} +``` + +Anytime a transaction that qualifies for the free lane is being processed, the `DeductFeeDecorator` will be ignored and no fees will be deducted! + +#### **MaxTxs** + +This sets the maximum number of transactions allowed in the mempool with the semantics: + +- if `MaxTxs` == 0, there is no cap on the number of transactions in the mempool +- if `MaxTxs` > 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 `MaxTxs` < 0, `Insert` is a no-op. + +#### **MaxBlockSpace** + +MaxBlockSpace is the maximum amount of block space that the lane will attempt to fill when building a proposal. This parameter may be useful lanes that should be limited (such as a free or onboarding lane) in space usage. Setting this to 0 will allow the lane to fill the block with as many transactions as possible. + +If a block proposal request has a `MaxTxBytes` of 1000 and the lane has a `MaxBlockSpace` of 0.5, the lane will attempt to fill the block with 500 bytes. + +#### **[OPTIONAL] IgnoreList** + +`IgnoreList` defines the list of lanes to ignore when processing transactions. For example, say there are two lanes: default and free. The free lane is 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). diff --git a/lanes/mev/README.md b/lanes/mev/README.md index 4a27c2a..cbbd248 100644 --- a/lanes/mev/README.md +++ b/lanes/mev/README.md @@ -15,20 +15,15 @@ $ go install github.com/skip-mev/block-sdk ## 📚 Usage -1. First determine the set of lanes that you want to use in your application. The -available lanes can be found in our -[Lane App Store](https://docs.skip.money/chains/lanes/existing-lanes/default). -In your base application, you will need to create a `LanedMempool` composed of the -lanes you want to use. *The MEV lane should not exist on its own. At minimum, it -is recommended that the MEV lane is paired with the default lane.* +1. This guide assumes you have already set up the [Block SDK (and the default lane)](https://docs.skip.money/chains/overview) 2. You will need to instantiate the `x/builder` module into your application. This module is responsible for processing auction transactions and distributing revenue to the auction house. The `x/builder` module is also responsible for ensuring the validity of auction transactions. *The `x/builder` module should not exist on its own. **This is the most intensive part of the set up process.** -3. Next, order the lanes by priority. The first lane is the highest priority lane +3. Next, add the MEV lane into the `lane` object on your `app.go`. The first lane is the highest priority lane and the last lane is the lowest priority lane. Since the MEV lane is meant to auction -off the top of the block, it should be the highest priority lane. The default lane +off the top of the block, **it should be the highest priority lane**. The default lane should follow. 4. You will also need to create a `PrepareProposalHandler` and a `ProcessProposalHandler` that will be responsible for preparing and processing From 546a245db403f23503e986363b10045467b6f09f Mon Sep 17 00:00:00 2001 From: MaghnusM Date: Wed, 16 Aug 2023 19:31:05 -0400 Subject: [PATCH 40/41] update readme --- lanes/build-your-own/README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lanes/build-your-own/README.md b/lanes/build-your-own/README.md index 83b295c..5550ec8 100644 --- a/lanes/build-your-own/README.md +++ b/lanes/build-your-own/README.md @@ -13,7 +13,7 @@ To install the Block SDK, run the following command: $ go install github.com/skip-mev/block-sdk ``` -## 🤔 How to use it [1 hour] +## 📚 Usage There are **five** required components to building a custom lane using the base lane: @@ -39,10 +39,7 @@ This is the data structure that is responsible for storing transactions as they The default implementation can be found in `block/base/mempool.go`. -:::info Scenario -What if we wanted to prioritize transactions by the amount they have staked on a chain? - -::: +### What if we wanted to prioritize transactions by the amount they have staked on a chain? We could do the following: From 16887dcce9b7c921041d8dd706f5629de4b0153a Mon Sep 17 00:00:00 2001 From: David Terpay Date: Thu, 17 Aug 2023 10:43:36 -0400 Subject: [PATCH 41/41] more nitz --- lanes/base/README.md | 12 +-- lanes/base/lane.go | 2 +- lanes/build-your-own/README.md | 163 ++++++++++++++++++++++++--------- lanes/mev/README.md | 10 +- lanes/mev/check_tx.go | 2 +- lanes/mev/lane.go | 1 - tests/app/app.go | 2 +- 7 files changed, 133 insertions(+), 59 deletions(-) diff --git a/lanes/base/README.md b/lanes/base/README.md index 48c7399..4f80073 100644 --- a/lanes/base/README.md +++ b/lanes/base/README.md @@ -17,19 +17,19 @@ $ go install github.com/skip-mev/block-sdk 1. First determine the set of lanes that you want to use in your application. The available lanes can be found in our -[Lane App Store](https://docs.skip.money/chains/lanes/existing-lanes/default). This guide -only sets up the `default lane` -2. In your base application, you will need to create a `LanedMempool` composed of the -`lanes` you want to use. +[Lane App Store](https://docs.skip.money/chains/lanes/existing-lanes/default). +This guide only sets up the `default lane` +2. In your base application, you will need to create a `LanedMempool` composed +of the `lanes` you want to use. 3. Next, order the lanes by priority. The first lane is the highest priority lane and the last lane is the lowest priority lane. **It is recommended that the last lane is the default lane.** -3. You will also need to create a `PrepareProposalHandler` and a +4. You will also need to create a `PrepareProposalHandler` and a `ProcessProposalHandler` that will be responsible for preparing and processing proposals respectively. Configure the order of the lanes in the `PrepareProposalHandler` and `ProcessProposalHandler` to match the order of the lanes in the `LanedMempool`. -4. Configure your `app.go` to include the following: +5. Configure your `app.go` to include the following: ```golang import ( diff --git a/lanes/base/lane.go b/lanes/base/lane.go index 136284f..9842592 100644 --- a/lanes/base/lane.go +++ b/lanes/base/lane.go @@ -22,7 +22,7 @@ type DefaultLane struct { *base.BaseLane } -// NewStandardLane returns a new default lane. +// NewDefaultLane returns a new default lane. func NewDefaultLane(cfg base.LaneConfig) *DefaultLane { lane := base.NewBaseLane( cfg, diff --git a/lanes/build-your-own/README.md b/lanes/build-your-own/README.md index 83b295c..f025adf 100644 --- a/lanes/build-your-own/README.md +++ b/lanes/build-your-own/README.md @@ -13,36 +13,48 @@ To install the Block SDK, run the following command: $ go install github.com/skip-mev/block-sdk ``` -## 🤔 How to use it [1 hour] +## 🤔 How to use it [30 min] There are **five** required components to building a custom lane using the base lane: -1. `Mempool` - The lane's mempool is responsible for storing transactions that have been verified and are waiting to be included in proposals. -2. `MatchHandler` - This is responsible for determining whether a transaction should belong to this lane. -3. [**OPTIONAL**] `PrepareLaneHandler` - Allows developers to define their own handler to customize the how transactions are verified and ordered before they are included into a proposal. -4. [**OPTIONAL**] `CheckOrderHandler` - Allows developers to define their own handler that will run any custom checks on whether transactions included in block proposals are in the correct order (respecting the ordering rules of the lane and the ordering rules of the other lanes). -5. [**OPTIONAL**] `ProcessLaneHandler` - Allows developers to define their own handler for processing transactions that are included in block proposals. +1. `Mempool` - The lane's mempool is responsible for storing transactions that +have been verified and are waiting to be included in proposals. +2. `MatchHandler` - This is responsible for determining whether a transaction +should belong to this lane. +3. [**OPTIONAL**] `PrepareLaneHandler` - Allows developers to define their own +handler to customize the how transactions are verified and ordered before they +are included into a proposal. +4. [**OPTIONAL**] `CheckOrderHandler` - Allows developers to define their own +handler that will run any custom checks on whether transactions included in +block proposals are in the correct order (respecting the ordering rules of the +lane and the ordering rules of the other lanes). +5. [**OPTIONAL**] `ProcessLaneHandler` - Allows developers to define their own +handler for processing transactions that are included in block proposals. 6. `Configuration` - Configure high-level options for your lane. ### 1. 🗄️ Mempool -This is the data structure that is responsible for storing transactions as they are being verified and are waiting to be included in proposals. `block/base/mempool.go` provides an out-of-the-box implementation that should be used as a starting point for building out the mempool and should cover most use cases. To utilize the mempool, you must implement a `TxPriority[C]` struct that does the following: +This is the data structure that is responsible for storing transactions as they +are being verified and are waiting to be included in proposals. +`block/base/mempool.go` provides an out-of-the-box implementation that should be +used as a starting point for building out the mempool and should cover most use +cases. To utilize the mempool, you must implement a `TxPriority[C]` struct that +does the following: -- Implements a `GetTxPriority` method that returns the priority (as defined +* Implements a `GetTxPriority` method that returns the priority (as defined by the type `[C]`) of a given transaction. -- Implements a `Compare` method that returns the relative priority of two +* Implements a `Compare` method that returns the relative priority of two transactions. If the first transaction has a higher priority, the method should return -1, if the second transaction has a higher priority the method should return 1, otherwise the method should return 0. -- Implements a `MinValue` method that returns the minimum priority value +* Implements a `MinValue` method that returns the minimum priority value that a transaction can have. The default implementation can be found in `block/base/mempool.go`. -:::info Scenario -What if we wanted to prioritize transactions by the amount they have staked on a chain? - -::: +> Scenario +What if we wanted to prioritize transactions by the amount they have staked on +a chain? We could do the following: @@ -100,7 +112,8 @@ func (p *CustomTxPriority) CustomTxPriority() TxPriority[string] { #### Using a Custom TxPriority -To utilize this new priority configuration in a lane, all you have to then do is pass in the `TxPriority[C]` to the `NewMempool` function. +To utilize this new priority configuration in a lane, all you have to then do +is pass in the `TxPriority[C]` to the `NewMempool` function. ```golang // Create the lane config @@ -136,9 +149,13 @@ lane := base.NewBaseLane( ### 2. 🤝 MatchHandler -`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!** The default implementation can be found in `block/base/handlers.go`. +`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!** The default implementation can be found in `block/base/handlers.go`. -The match handler can be as custom as desired. Following the example above, if we wanted to make a lane that only accepts transactions if they have a large amount staked, we could do the following: +The match handler can be as custom as desired. Following the example above, if +we wanted to make a lane that only accepts transactions if they have a large +amount staked, we could do the following: ```golang // CustomMatchHandler returns a custom implementation of the MatchHandler. It @@ -173,7 +190,8 @@ func (h *Handler) CustomMatchHandler() block.MatchHandler { #### Using a Custom MatchHandler -If we wanted to create the lane using the custom match handler along with the custom mempool, we could do the following: +If we wanted to create the lane using the custom match handler along with the +custom mempool, we could do the following: ```golang // Pseudocode for creating the custom match handler @@ -201,9 +219,15 @@ lane := base.NewBaseLane( ### [OPTIONAL] Steps 3-5 -The remaining steps walk through the process of creating custom block building/verification logic. The default implementation found in `block/base/handlers.go` should fit most use cases. Please reference that file for more details on the default implementation and whether it fits your use case. +The remaining steps walk through the process of creating custom block +building/verification logic. The default implementation found in +`block/base/handlers.go` should fit most use cases. Please reference that file +for more details on the default implementation and whether it fits your use case. -Implementing custom block building/verification logic is a bit more involved than the previous steps and is a all or nothing approach. This means that if you implement any of the handlers, you must implement all of them in most cases. If you do not implement all of them, the lane may have unintended behavior. +Implementing custom block building/verification logic is a bit more involved +than the previous steps and is a all or nothing approach. This means that if +you implement any of the handlers, you must implement all of them in most cases. + If you do not implement all of them, the lane may have unintended behavior. ### 3. 🛠️ PrepareLaneHandler @@ -226,13 +250,15 @@ PrepareLaneHandler func(ctx sdk.Context,proposal BlockProposal,maxTxBytes int64) (txsToInclude [][]byte, txsToRemove []sdk.Tx, err error) ``` -The default implementation is simple. It will continue to select transactions from its mempool under the following criteria: +The default implementation is simple. It will continue to select transactions +from its mempool under the following criteria: 1. The transactions is not already included in the block proposal. 2. The transaction is valid and passes the AnteHandler check. 3. The transaction is not too large to be included in the block. -If a more involved selection process is required, you can implement your own `PrepareLaneHandler` and and set it after creating the base lane. +If a more involved selection process is required, you can implement your own +`PrepareLaneHandler` and and set it after creating the base lane. ```golang // Pseudocode for creating the custom prepare lane handler @@ -250,7 +276,9 @@ customLane.SetPrepareLaneHandler(customlane.PrepareLaneHandler()) ### 4. ✅ CheckOrderHandler -The `CheckOrderHandler` is an optional field you can set on the base lane. This handler is responsible for verifying the ordering of the transactions in the block proposal that belong to the lane. +The `CheckOrderHandler` is an optional field you can set on the base lane. +This handler is responsible for verifying the ordering of the transactions in +the block proposal that belong to the lane. ```golang // CheckOrderHandler is responsible for checking the order of transactions that @@ -262,12 +290,20 @@ The `CheckOrderHandler` is an optional field you can set on the base lane. This CheckOrderHandler func(ctx sdk.Context, txs []sdk.Tx) error ``` -The default implementation is simple and utilizes the same `TxPriority` struct that the mempool uses to determine if transactions are in order. The criteria for determining if transactions are in order is as follows: +The default implementation is simple and utilizes the same `TxPriority` struct +that the mempool uses to determine if transactions are in order. The criteria +for determining if transactions are in order is as follows: -1. The transactions are in order according to the `TxPriority` struct. i.e. any two transactions (that match to the lane) `tx1` and `tx2` where `tx1` has a higher priority than `tx2` should be ordered before `tx2`. -2. The transactions are contiguous. i.e. there are no transactions from other lanes in between the transactions that belong to this lane. i.e. if `tx1` and `tx2` belong to the lane, there should be no transactions from other lanes in between `tx1` and `tx2`. +1. The transactions are in order according to the `TxPriority` struct. i.e. +any two transactions (that match to the lane) `tx1` and `tx2` where `tx1` has a +higher priority than `tx2` should be ordered before `tx2`. +2. The transactions are contiguous. i.e. there are no transactions from other +lanes in between the transactions that belong to this lane. i.e. if `tx1` and +`tx2` belong to the lane, there should be no transactions from other lanes in +between `tx1` and `tx2`. -If a more involved ordering process is required, you can implement your own `CheckOrderHandler` and and set it after creating the base lane. +If a more involved ordering process is required, you can implement your own +`CheckOrderHandler` and and set it after creating the base lane. ```golang // Pseudocode for creating the custom check order handler @@ -285,7 +321,14 @@ customLane.SetCheckOrderHandler(customlane.CheckOrderHandler()) ### 5. 🆗 ProcessLaneHandler -The `ProcessLaneHandler` is an optional field you can set on the base lane. This handler is responsible for verifying the transactions in the block proposal that belong to the lane. This handler is executed after the `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. This means that if the first transaction does not belong to the lane, the remaining transactions should not belong to the lane either. +The `ProcessLaneHandler` is an optional field you can set on the base lane. +This handler is responsible for verifying the transactions in the block proposal +that belong to the lane. This handler is executed after the `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. This means that if the first transaction +does not belong to the lane, the remaining transactions should not belong to +the lane either. ```golang // ProcessLaneHandler is responsible for processing transactions that are @@ -296,12 +339,17 @@ The `ProcessLaneHandler` is an optional field you can set on the base lane. This ProcessLaneHandler func(ctx sdk.Context, txs []sdk.Tx) ([]sdk.Tx, error) ``` -Given the invarients above, the default implementation is simple. It will continue to verify transactions in the block proposal under the following criteria: +Given the invarients above, the default implementation is simple. It will +continue to verify transactions in the block proposal under the following criteria: -1. If a transaction matches to this lane, verify it and continue. If it is not valid, return an error. -2. If a transaction does not match to this lane, return the remaining transactions to the next lane to process. +1. If a transaction matches to this lane, verify it and continue. If it is not +valid, return an error. +2. If a transaction does not match to this lane, return the remaining +transactions to the next lane to process. -Similar to the setup of handlers above, if a more involved verification process is required, you can implement your own `ProcessLaneHandler` and and set it after creating the base lane. +Similar to the setup of handlers above, if a more involved verification process +is required, you can implement your own `ProcessLaneHandler` and and set it +after creating the base lane. ```golang // Pseudocode for creating the custom check order handler @@ -319,16 +367,23 @@ customLane.SetProcessLaneHandler(customlane.ProcessLaneHandler()) ### 6. 📝 Lane Configuration -Once you have created your custom lane, you can configure it in the application by doing the following: +Once you have created your custom lane, you can configure it in the application +by doing the following: 1. Create a custom `LaneConfig` struct that defines the configuration of the lane. -2. Instantiate the lane with the custom `LaneConfig` struct alongside any other dependencies (mempool, match handler, etc.). +2. Instantiate the lane with the custom `LaneConfig` struct alongside any other +dependencies (mempool, match handler, etc.). 3. Instantiate a new `LanedMempool` with the custom lane. 4. Set the `LanedMempool` on the `BaseApp` instance. 5. Set up the proposal handlers of the Block SDK to use your lane. 6. That's it! You're done! -The lane config (`LaneConfig`) is a simple configuration object that defines the desired amount of block space the lane should utilize when building a proposal, an antehandler that is used to verify transactions as they are added/verified to/in a proposal, and more. By default, we recommend that user's pass in all of the base apps configurations (txDecoder, logger, etc.). A sample `LaneConfig` might look like the following: +The lane config (`LaneConfig`) is a simple configuration object that defines +the desired amount of block space the lane should utilize when building a +proposal, an antehandler that is used to verify transactions as they are +added/verified to/in a proposal, and more. By default, we recommend that user's +pass in all of the base apps configurations (txDecoder, logger, etc.). A sample +`LaneConfig` might look like the following: ```golang config := block.LaneConfig{ @@ -346,9 +401,16 @@ The three most important parameters to set are the `AnteHandler`, `MaxTxs`, and #### **AnteHandler** -With the default implementation, the `AnteHandler` is responsible for verifying transactions as they are being considered for a new proposal or are being processed in a proposed block. We recommend user's utilize the same antehandler chain that is used in the base app. If developers want a certain `AnteDecorator` to be ignored if it qualifies for a given lane, they can do so by using the `NewIgnoreDecorator` defined in `block/utils/ante.go`. +With the default implementation, the `AnteHandler` is responsible for verifying +transactions as they are being considered for a new proposal or are being +processed in a proposed block. We recommend user's utilize the same antehandler +chain that is used in the base app. If developers want a certain `AnteDecorator` +to be ignored if it qualifies for a given lane, they can do so by using the +`NewIgnoreDecorator` defined in `block/utils/ante.go`. -For example, a free lane might want to ignore the `DeductFeeDecorator` so that its transactions are not charged any fees. Where ever the `AnteHandler` is defined, we could add the following to ignore the `DeductFeeDecorator`: +For example, a free lane might want to ignore the `DeductFeeDecorator` so that +its transactions are not charged any fees. Where ever the `AnteHandler` is +defined, we could add the following to ignore the `DeductFeeDecorator`: ```golang anteDecorators := []sdk.AnteDecorator{ @@ -367,22 +429,35 @@ anteDecorators := []sdk.AnteDecorator{ } ``` -Anytime a transaction that qualifies for the free lane is being processed, the `DeductFeeDecorator` will be ignored and no fees will be deducted! +Anytime a transaction that qualifies for the free lane is being processed, the +`DeductFeeDecorator` will be ignored and no fees will be deducted! #### **MaxTxs** This sets the maximum number of transactions allowed in the mempool with the semantics: -- if `MaxTxs` == 0, there is no cap on the number of transactions in the mempool -- if `MaxTxs` > 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 `MaxTxs` < 0, `Insert` is a no-op. +* if `MaxTxs` == 0, there is no cap on the number of transactions in the mempool +* if `MaxTxs` > 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 `MaxTxs` < 0, `Insert` is a no-op. #### **MaxBlockSpace** -MaxBlockSpace is the maximum amount of block space that the lane will attempt to fill when building a proposal. This parameter may be useful lanes that should be limited (such as a free or onboarding lane) in space usage. Setting this to 0 will allow the lane to fill the block with as many transactions as possible. +MaxBlockSpace is the maximum amount of block space that the lane will attempt +to fill when building a proposal. This parameter may be useful lanes that +should be limited (such as a free or onboarding lane) in space usage. +Setting this to 0 will allow the lane to fill the block with as many +transactions as possible. -If a block proposal request has a `MaxTxBytes` of 1000 and the lane has a `MaxBlockSpace` of 0.5, the lane will attempt to fill the block with 500 bytes. +If a block proposal request has a `MaxTxBytes` of 1000 and the lane has a +`MaxBlockSpace` of 0.5, the lane will attempt to fill the block with 500 bytes. #### **[OPTIONAL] IgnoreList** -`IgnoreList` defines the list of lanes to ignore when processing transactions. For example, say there are two lanes: default and free. The free lane is 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` defines the list of lanes to ignore when processing transactions. +For example, say there are two lanes: default and free. The free lane is +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). diff --git a/lanes/mev/README.md b/lanes/mev/README.md index cbbd248..17258b8 100644 --- a/lanes/mev/README.md +++ b/lanes/mev/README.md @@ -21,10 +21,10 @@ module is responsible for processing auction transactions and distributing reven to the auction house. The `x/builder` module is also responsible for ensuring the validity of auction transactions. *The `x/builder` module should not exist on its own. **This is the most intensive part of the set up process.** -3. Next, add the MEV lane into the `lane` object on your `app.go`. The first lane is the highest priority lane -and the last lane is the lowest priority lane. Since the MEV lane is meant to auction -off the top of the block, **it should be the highest priority lane**. The default lane -should follow. +3. Next, add the MEV lane into the `lane` object on your `app.go`. The first +lane is the highest priority lane and the last lane is the lowest priority lane. +Since the MEV lane is meant to auction off the top of the block, **it should be +the highest priority lane**. The default lane should follow. 4. You will also need to create a `PrepareProposalHandler` and a `ProcessProposalHandler` that will be responsible for preparing and processing proposals respectively. Configure the order of the lanes in the @@ -77,7 +77,7 @@ NOTE: This example walks through setting up the MEV and Default lanes. a. First add the keeper to the app's struct definition. We also want to add MEV lane's custom checkTx handler to the app's struct definition. This will - allow us to override the default checkTx handler to process bid transactions + allow us to override the default checkTx handler to process bid transactions before they are inserted into the `LanedMempool`. NOTE: The custom handler is required as otherwise the auction can be held hostage by a malicious users. diff --git a/lanes/mev/check_tx.go b/lanes/mev/check_tx.go index ab18630..dbe890b 100644 --- a/lanes/mev/check_tx.go +++ b/lanes/mev/check_tx.go @@ -63,7 +63,7 @@ type ( } ) -// NewCheckTxHandler is a base for CheckTxHandler. +// NewCheckTxHandler constructs a new CheckTxHandler instance. func NewCheckTxHandler( baseApp BaseApp, txDecoder sdk.TxDecoder, diff --git a/lanes/mev/lane.go b/lanes/mev/lane.go index 2457352..d3da91b 100644 --- a/lanes/mev/lane.go +++ b/lanes/mev/lane.go @@ -31,7 +31,6 @@ type ( } MEVLane struct { //nolint - // LaneConfig defines the base lane configuration. *base.BaseLane // Factory defines the API/functionality which is responsible for determining diff --git a/tests/app/app.go b/tests/app/app.go index ad953e6..8185791 100644 --- a/tests/app/app.go +++ b/tests/app/app.go @@ -262,7 +262,7 @@ 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. + // MEV lane allows transactions to bid for inclusion at the top of the next block. mevConfig := base.LaneConfig{ Logger: app.Logger(), TxEncoder: app.txConfig.TxEncoder(),