Skip to content

Commit

Permalink
feat: Lane Options (backport #272) (#273)
Browse files Browse the repository at this point in the history
* feat: Lane Options (#272)

* init

* lint

* update mock

* cr

* readme nit

(cherry picked from commit db58154)

# Conflicts:
#	block/base/lane.go
#	block/mempool_test.go
#	block/mocks/lane.go

* merge nit

---------

Co-authored-by: David Terpay <[email protected]>
Co-authored-by: David Terpay <[email protected]>
  • Loading branch information
3 people authored Dec 6, 2023
1 parent ea5748f commit 863649c
Show file tree
Hide file tree
Showing 20 changed files with 377 additions and 279 deletions.
9 changes: 4 additions & 5 deletions abci/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package abci_test
import (
"context"
"math/rand"
"os"
"testing"

"cosmossdk.io/math"
Expand Down Expand Up @@ -531,7 +530,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() {
s.Require().NoError(err)

proposalHandler := abci.NewProposalHandler(
log.NewTMLogger(os.Stdout),
log.NewNopLogger(),
s.encodingConfig.TxConfig.TxDecoder(),
s.encodingConfig.TxConfig.TxEncoder(),
mempool,
Expand Down Expand Up @@ -577,7 +576,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() {
s.Require().NoError(err)

proposalHandler := abci.NewProposalHandler(
log.NewTMLogger(os.Stdout),
log.NewNopLogger(),
s.encodingConfig.TxConfig.TxDecoder(),
s.encodingConfig.TxConfig.TxEncoder(),
mempool,
Expand Down Expand Up @@ -625,7 +624,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() {
s.Require().NoError(err)

proposalHandler := abci.NewProposalHandler(
log.NewTMLogger(os.Stdout),
log.NewNopLogger(),
s.encodingConfig.TxConfig.TxDecoder(),
s.encodingConfig.TxConfig.TxEncoder(),
mempool,
Expand Down Expand Up @@ -673,7 +672,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() {
s.Require().NoError(err)

proposalHandler := abci.NewProposalHandler(
log.NewTMLogger(os.Stdout),
log.NewNopLogger(),
s.encodingConfig.TxConfig.TxDecoder(),
s.encodingConfig.TxConfig.TxEncoder(),
mempool,
Expand Down
34 changes: 20 additions & 14 deletions abci/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,22 @@ func (s *ProposalsTestSuite) setUpCustomMatchHandlerLane(maxBlockSpace math.Lega
SignerExtractor: signeradaptors.NewDefaultAdapter(),
}

lane := base.NewBaseLane(
options := []base.LaneOption{
base.WithMatchHandler(mh),
base.WithMempoolConfigs[string](cfg, base.DefaultTxPriority()),
}

lane, err := base.NewBaseLane(
cfg,
name,
base.NewMempool[string](base.DefaultTxPriority(), cfg.TxEncoder, cfg.SignerExtractor, 0),
mh,
options...,
)

lane.SetPrepareLaneHandler(lane.DefaultPrepareLaneHandler())
lane.SetProcessLaneHandler(lane.DefaultProcessLaneHandler())
s.Require().NoError(err)

return lane
}

func (s *ProposalsTestSuite) setUpStandardLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *defaultlane.DefaultLane {
func (s *ProposalsTestSuite) setUpStandardLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *base.BaseLane {
cfg := base.LaneConfig{
Logger: log.NewNopLogger(),
TxEncoder: s.encodingConfig.TxConfig.TxEncoder(),
Expand All @@ -103,7 +105,7 @@ func (s *ProposalsTestSuite) setUpTOBLane(maxBlockSpace math.LegacyDec, expected
return mev.NewMEVLane(cfg, factory, factory.MatchHandler())
}

func (s *ProposalsTestSuite) setUpFreeLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *free.FreeLane {
func (s *ProposalsTestSuite) setUpFreeLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *base.BaseLane {
cfg := base.LaneConfig{
Logger: log.NewNopLogger(),
TxEncoder: s.encodingConfig.TxConfig.TxEncoder(),
Expand All @@ -125,15 +127,19 @@ func (s *ProposalsTestSuite) setUpPanicLane(name string, maxBlockSpace math.Lega
SignerExtractor: signeradaptors.NewDefaultAdapter(),
}

lane := base.NewBaseLane(
options := []base.LaneOption{
base.WithMatchHandler(base.DefaultMatchHandler()),
base.WithMempoolConfigs[string](cfg, base.DefaultTxPriority()),
base.WithPrepareLaneHandler(base.PanicPrepareLaneHandler()),
base.WithProcessLaneHandler(base.PanicProcessLaneHandler()),
}

lane, err := base.NewBaseLane(
cfg,
name,
base.NewMempool[string](base.DefaultTxPriority(), cfg.TxEncoder, cfg.SignerExtractor, 0),
base.DefaultMatchHandler(),
options...,
)

lane.SetPrepareLaneHandler(base.PanicPrepareLaneHandler())
lane.SetProcessLaneHandler(base.PanicProcessLaneHandler())
s.Require().NoError(err)

return lane
}
Expand Down
3 changes: 0 additions & 3 deletions block/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,6 @@ type Lane interface {
// Name returns the name of the lane.
Name() string

// SetAnteHandler sets the lane's antehandler.
SetAnteHandler(antehander sdk.AnteHandler)

// Match determines if a transaction belongs to this lane.
Match(ctx sdk.Context, tx sdk.Tx) bool

Expand Down
6 changes: 3 additions & 3 deletions block/base/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func DefaultMatchHandler() base.MatchHandler {
}
```

The default `MatchHandler` is implemented in the [base lane](./handlers.go) and matches all transactions.
The default `MatchHandler` is implemented in the [base lane](./match.go) and matches all transactions.

## PrepareLaneHandler

Expand All @@ -60,7 +60,7 @@ PrepareLaneHandler func(

To create a custom lane with a custom `PrepareLaneHandler`, you must implement this function and set it on the lane after it has been created. Please visit the [MEV lane's](../../lanes/mev/abci.go) `PrepareLaneHandler` for an example of how to implement this function.

The default `PrepareLaneHandler` is implemented in the [base lane](./handlers.go). It reaps transactions from the mempool, validates them, ensures that the lane's block space limit is not exceeded, and returns the transactions to be included in the block and the ones that need to be removed.
The default `PrepareLaneHandler` is implemented in the [base lane](./proposals.go). It reaps transactions from the mempool, validates them, ensures that the lane's block space limit is not exceeded, and returns the transactions to be included in the block and the ones that need to be removed.

## ProcessLaneHandler

Expand All @@ -74,7 +74,7 @@ ProcessLaneHandler func(ctx sdk.Context, partialProposal []sdk.Tx) (
)
```

Note that block proposals built using the Block SDK contain contiguous sections of transactions in the block that belong to a given lane, to read more about how proposals are constructed relative to other lanes, please visit the [abci section](../../abci/README.md). As such, a given lane will recieve some transactions in (partialProposal) that belong to it and some that do not. The transactions that belong to it must be contiguous from the start, and the transactions that do not belong to it must be contiguous from the end. The lane must return the transactions that belong to it and the transactions that do not belong to it. The transactions that do not belong to it will be passed to the next lane in the proposal. The default `ProcessLaneHandler` is implemented in the [base lane](./handlers.go). It verifies the transactions that belong to the lane and returns them alongside the transactions that do not belong to the lane.
Note that block proposals built using the Block SDK contain contiguous sections of transactions in the block that belong to a given lane, to read more about how proposals are constructed relative to other lanes, please visit the [abci section](../../abci/README.md). As such, a given lane will recieve some transactions in (partialProposal) that belong to it and some that do not. The transactions that belong to it must be contiguous from the start, and the transactions that do not belong to it must be contiguous from the end. The lane must return the transactions that belong to it and the transactions that do not belong to it. The transactions that do not belong to it will be passed to the next lane in the proposal. The default `ProcessLaneHandler` is implemented in the [base lane](./proposals.go). It verifies the transactions that belong to the lane and returns them alongside the transactions that do not belong to the lane.

Please visit the [MEV lane's](../../lanes/mev/abci.go) `ProcessLaneHandler` for an example of how to implement a custom handler.

Expand Down
75 changes: 37 additions & 38 deletions block/base/lane.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,35 @@ type BaseLane struct { //nolint
func NewBaseLane(
cfg LaneConfig,
laneName string,
laneMempool block.LaneMempool,
matchHandlerFn MatchHandler,
) *BaseLane {
options ...LaneOption,
) (*BaseLane, error) {
lane := &BaseLane{
cfg: cfg,
laneName: laneName,
LaneMempool: laneMempool,
matchHandler: matchHandlerFn,
cfg: cfg,
laneName: laneName,
}

return lane
lane.LaneMempool = NewMempool(
DefaultTxPriority(),
lane.cfg.TxEncoder,
lane.cfg.SignerExtractor,
lane.cfg.MaxTxs,
)

lane.matchHandler = DefaultMatchHandler()

handler := NewDefaultProposalHandler(lane)
lane.prepareLaneHandler = handler.PrepareLaneHandler()
lane.processLaneHandler = handler.ProcessLaneHandler()

for _, option := range options {
option(lane)
}

if err := lane.ValidateBasic(); err != nil {
return nil, err
}

return lane, nil
}

// ValidateBasic ensures that the lane was constructed properly. In the case that
Expand All @@ -83,39 +101,16 @@ func (l *BaseLane) ValidateBasic() error {
}

if l.prepareLaneHandler == nil {
l.prepareLaneHandler = l.DefaultPrepareLaneHandler()
return fmt.Errorf("prepare lane handler cannot be nil")
}

if l.processLaneHandler == nil {
l.processLaneHandler = l.DefaultProcessLaneHandler()
return fmt.Errorf("process lane handler cannot be nil")
}

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 *BaseLane) 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 *BaseLane) SetProcessLaneHandler(processLaneHandler ProcessLaneHandler) {
if processLaneHandler == nil {
panic("process lane handler cannot be nil")
}

l.processLaneHandler = processLaneHandler
}

// 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
Expand All @@ -129,11 +124,6 @@ func (l *BaseLane) Name() string {
return l.laneName
}

// SetAnteHandler sets the ante handler for the lane.
func (l *BaseLane) SetAnteHandler(anteHandler sdk.AnteHandler) {
l.cfg.AnteHandler = anteHandler
}

// Logger returns the logger for the lane.
func (l *BaseLane) Logger() log.Logger {
return l.cfg.Logger
Expand All @@ -154,3 +144,12 @@ func (l *BaseLane) TxEncoder() sdk.TxEncoder {
func (l *BaseLane) GetMaxBlockSpace() math.LegacyDec {
return l.cfg.MaxBlockSpace
}

// WithOptions returns a new lane with the given options.
func (l *BaseLane) WithOptions(options ...LaneOption) *BaseLane {
for _, option := range options {
option(l)
}

return l
}
42 changes: 42 additions & 0 deletions block/base/match.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package base

import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
)

// 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
}
}

// VerifyNoMatches returns an error if any of the transactions match the lane.
func (l *BaseLane) VerifyNoMatches(ctx sdk.Context, txs []sdk.Tx) error {
for _, tx := range txs {
if l.Match(ctx, tx) {
return fmt.Errorf("transaction belongs to lane when it should not")
}
}

return nil
}

// NewMatchHandler returns a match handler that matches transactions
// that match the lane and do not match with any of the provided match handlers.
// In the context of building an application, you would want to use this to
// ignore the match handlers of other lanes in the application.
func NewMatchHandler(mh MatchHandler, ignoreMHs ...MatchHandler) MatchHandler {
return func(ctx sdk.Context, tx sdk.Tx) bool {
for _, ignoreMH := range ignoreMHs {
if ignoreMH(ctx, tx) {
return false
}
}

return mh(ctx, tx)
}
}
80 changes: 80 additions & 0 deletions block/base/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package base

import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/skip-mev/block-sdk/block"
)

// LaneOption defines a function that can be used to set options on a lane.
type LaneOption func(*BaseLane)

// WithAnteHandler sets the ante handler for the lane.
func WithAnteHandler(anteHandler sdk.AnteHandler) LaneOption {
return func(l *BaseLane) { l.cfg.AnteHandler = anteHandler }
}

// WithPrepareLaneHandler 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 WithPrepareLaneHandler(prepareLaneHandler PrepareLaneHandler) LaneOption {
return func(l *BaseLane) {
if prepareLaneHandler == nil {
panic("prepare lane handler cannot be nil")
}

l.prepareLaneHandler = prepareLaneHandler
}
}

// WithProcessLaneHandler 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 WithProcessLaneHandler(processLaneHandler ProcessLaneHandler) LaneOption {
return func(l *BaseLane) {
if processLaneHandler == nil {
panic("process lane handler cannot be nil")
}

l.processLaneHandler = processLaneHandler
}
}

// WithMatchHandler sets the match handler for the lane. This handler is called
// when a new transaction is being submitted to the lane and the lane needs to
// determine if the transaction should be processed by the lane.
func WithMatchHandler(matchHandler MatchHandler) LaneOption {
return func(l *BaseLane) {
if matchHandler == nil {
panic("match handler cannot be nil")
}

l.matchHandler = matchHandler
}
}

// WithMempool sets the mempool for the lane. This mempool is used to store
// transactions that are waiting to be processed.
func WithMempool(mempool block.LaneMempool) LaneOption {
return func(l *BaseLane) {
if mempool == nil {
panic("mempool cannot be nil")
}

l.LaneMempool = mempool
}
}

// WithMempoolConfigs sets the mempool for the lane with the given lane config
// and TxPriority struct. This mempool is used to store transactions that are waiting
// to be processed.
func WithMempoolConfigs[C comparable](cfg LaneConfig, txPriority TxPriority[C]) LaneOption {
return func(l *BaseLane) {
l.LaneMempool = NewMempool(
txPriority,
cfg.TxEncoder,
cfg.SignerExtractor,
cfg.MaxTxs,
)
}
}
Loading

0 comments on commit 863649c

Please sign in to comment.