fix: Removing IgnoreList from Lane Interface (#245)

* greedy approach to lane verification

* docs

* base lane testing

* mev lane testing nits

* abci top level testing done

* network spamming in E2E

* string rep of escrow address

* nit

* nit

* nit v1.0.1

* removing logs from testing

* query test

* logging with tx info

* nits

* nit

* nit

* testing nit

* adding readmes which i will fill out l8r

* removing ignore list from convo, ur done

* removing logs in testing

* nits

* eh ig we dont need it rn

* removing ignore list from config

* nit test

---------

Co-authored-by: Alex Johnson <alex@skip.money>
This commit is contained in:
David Terpay 2023-11-29 12:10:09 -05:00 committed by GitHub
parent be4465ae95
commit b91cfb617c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 358 additions and 374 deletions

View File

@ -532,8 +532,6 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() {
)
s.Require().NoError(err)
defaultLane.SetIgnoreList(nil)
proposalHandler := abci.NewProposalHandler(
log.NewNopLogger(),
s.encodingConfig.TxConfig.TxDecoder(),
@ -598,8 +596,6 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() {
)
s.Require().NoError(err)
defaultLane.SetIgnoreList(nil)
proposalHandler := abci.NewProposalHandler(
log.NewNopLogger(),
s.encodingConfig.TxConfig.TxDecoder(),
@ -671,10 +667,6 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() {
)
s.Require().NoError(err)
panicLane.SetIgnoreList(nil)
panicLane2.SetIgnoreList(nil)
defaultLane.SetIgnoreList(nil)
proposalHandler := abci.NewProposalHandler(
log.NewNopLogger(),
s.encodingConfig.TxConfig.TxDecoder(),
@ -746,10 +738,6 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() {
)
s.Require().NoError(err)
panicLane.SetIgnoreList(nil)
panicLane2.SetIgnoreList(nil)
defaultLane.SetIgnoreList(nil)
proposalHandler := abci.NewProposalHandler(
log.NewNopLogger(),
s.encodingConfig.TxConfig.TxDecoder(),

View File

@ -85,11 +85,10 @@ func (s *ProposalsTestSuite) setUpStandardLane(maxBlockSpace math.LegacyDec, exp
TxDecoder: s.encodingConfig.TxConfig.TxDecoder(),
AnteHandler: s.setUpAnteHandler(expectedExecution),
MaxBlockSpace: maxBlockSpace,
IgnoreList: make([]block.Lane, 0),
SignerExtractor: signeradaptors.NewDefaultAdapter(),
}
return defaultlane.NewDefaultLane(cfg)
return defaultlane.NewDefaultLane(cfg, base.DefaultMatchHandler())
}
func (s *ProposalsTestSuite) setUpTOBLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *mev.MEVLane {
@ -102,7 +101,8 @@ func (s *ProposalsTestSuite) setUpTOBLane(maxBlockSpace math.LegacyDec, expected
SignerExtractor: signeradaptors.NewDefaultAdapter(),
}
return mev.NewMEVLane(cfg, mev.NewDefaultAuctionFactory(cfg.TxDecoder, signeradaptors.NewDefaultAdapter()))
factory := mev.NewDefaultAuctionFactory(cfg.TxDecoder, signeradaptors.NewDefaultAdapter())
return mev.NewMEVLane(cfg, factory, factory.MatchHandler())
}
func (s *ProposalsTestSuite) setUpFreeLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *free.FreeLane {

View File

@ -8,7 +8,6 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
signer_extraction "github.com/skip-mev/block-sdk/adapters/signer_extraction_adapter"
"github.com/skip-mev/block-sdk/block"
)
// LaneConfig defines the basic configurations needed for a lane.
@ -28,14 +27,6 @@ type LaneConfig struct {
// 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

View File

@ -170,3 +170,19 @@ func DefaultMatchHandler() MatchHandler {
return true
}
}
// 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)
}
}

View File

@ -121,24 +121,7 @@ func (l *BaseLane) SetProcessLaneHandler(processLaneHandler ProcessLaneHandler)
// if the transaction is on the ignore list. If the transaction is on the ignore
// list, it returns false.
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 *BaseLane) CheckIgnoreList(ctx sdk.Context, tx sdk.Tx) bool {
if l.cfg.IgnoreList == nil {
return false
}
for _, lane := range l.cfg.IgnoreList {
if lane.Match(ctx, tx) {
return true
}
}
return false
return l.matchHandler(ctx, tx)
}
// Name returns the name of the lane.
@ -146,18 +129,6 @@ 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 *BaseLane) SetIgnoreList(lanes []block.Lane) {
l.cfg.IgnoreList = lanes
}
// GetIgnoreList returns the ignore list for the lane. The ignore list is a list
// of lanes that the lane should ignore when processing transactions.
func (l *BaseLane) GetIgnoreList() []block.Lane {
return l.cfg.IgnoreList
}
// SetAnteHandler sets the ante handler for the lane.
func (l *BaseLane) SetAnteHandler(anteHandler sdk.AnteHandler) {
l.cfg.AnteHandler = anteHandler

View File

@ -70,12 +70,6 @@ type Lane interface {
// SetAnteHandler sets the lane's antehandler.
SetAnteHandler(antehander sdk.AnteHandler)
// SetIgnoreList sets the lanes that should be ignored by this lane.
SetIgnoreList(ignoreList []Lane)
// GetIgnoreList returns the lanes that should be ignored by this lane.
GetIgnoreList() []Lane
// Match determines if a transaction belongs to this lane.
Match(ctx sdk.Context, tx sdk.Tx) bool

View File

@ -12,12 +12,6 @@ import (
blocksdkmoduletypes "github.com/skip-mev/block-sdk/x/blocksdk/types"
)
const (
// DefaultLaneName is the default lane name. We enforce that a lane with the name
// "default" is provided when constructing the mempool.
DefaultLaneName = "default"
)
var _ Mempool = (*LanedMempool)(nil)
// LaneFetcher defines the interface to get a lane stored in the x/blocksdk module.
@ -62,61 +56,11 @@ type (
// to its own selection logic. The lanes are ordered according to their priority. The
// first lane in the registry has the highest priority. Proposals are verified according
// to the order of the lanes in the registry. Each transaction SHOULD only belong in one lane.
// To enforce that transactions only belong to one lane, each lane has an ignore list.
//
// For example, say we have three lanes, MEV, default, and free. The ignore list of each
// lane will look like the following:
// - MEV: free
// - default: MEV, free
// - free: MEV.
//
// Note that a name with the value "default" MUST be provided.
func NewLanedMempool(
logger log.Logger,
lanes []Lane,
laneFetcher LaneFetcher,
) (*LanedMempool, error) {
laneCache := make(map[Lane]struct{})
seenDefault := false
// Ensure that each of the lanes are mutually exclusive. The default lane should
// ignore all other lanes, while all other lanes should ignore every lane except
// the default lane.
for index, lane := range lanes {
if lane.Name() == DefaultLaneName {
lowerIgnoreList := make([]Lane, index)
copy(lowerIgnoreList, lanes[:index])
upperIgnoreList := make([]Lane, len(lanes)-index-1)
copy(upperIgnoreList, lanes[index+1:])
lane.SetIgnoreList(append(lowerIgnoreList, upperIgnoreList...))
seenDefault = true
} else {
laneCache[lane] = struct{}{}
}
}
if !seenDefault {
return nil, fmt.Errorf("default lane not found. a lane with the name %s must be provided", DefaultLaneName)
}
for _, lane := range lanes {
if lane.Name() == DefaultLaneName {
continue
}
delete(laneCache, lane)
ignoreList := make([]Lane, 0)
for otherLane := range laneCache {
ignoreList = append(ignoreList, otherLane)
}
lane.SetIgnoreList(ignoreList)
laneCache[lane] = struct{}{}
}
mempool := &LanedMempool{
logger: logger,
registry: lanes,

View File

@ -78,9 +78,11 @@ func (suite *BlockBusterTestSuite) SetupTest() {
AnteHandler: nil,
MaxBlockSpace: math.LegacyZeroDec(),
}
factory := mev.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder(), signer_extraction.NewDefaultAdapter())
suite.mevLane = mev.NewMEVLane(
mevConfig,
mev.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder(), signer_extraction.NewDefaultAdapter()),
factory,
factory.MatchHandler(),
)
suite.mevSDKLane = blocksdkmoduletypes.Lane{
@ -121,6 +123,7 @@ func (suite *BlockBusterTestSuite) SetupTest() {
}
suite.baseLane = defaultlane.NewDefaultLane(
baseConfig,
base.DefaultMatchHandler(),
)
suite.baseSDKLane = blocksdkmoduletypes.Lane{
@ -169,10 +172,12 @@ func (suite *BlockBusterTestSuite) TestNewMempool() {
MaxBlockSpace: math.LegacyZeroDec(),
}
defaultLane := defaultlane.NewDefaultLane(baseConfig)
defaultLane := defaultlane.NewDefaultLane(baseConfig, base.DefaultMatchHandler())
factory := mev.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder(), signer_extraction.NewDefaultAdapter())
mevLane := mev.NewMEVLane(
baseConfig,
mev.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder(), signer_extraction.NewDefaultAdapter()),
factory,
factory.MatchHandler(),
)
freeLane := free.NewFreeLane(
baseConfig,
@ -189,9 +194,6 @@ func (suite *BlockBusterTestSuite) TestNewMempool() {
fetcher,
)
suite.Require().NoError(err)
ignoreList := defaultLane.GetIgnoreList()
suite.Require().Equal(0, len(ignoreList))
})
suite.Run("works mev and default lane", func() {
@ -203,12 +205,6 @@ func (suite *BlockBusterTestSuite) TestNewMempool() {
fetcher,
)
suite.Require().NoError(err)
ignoreList := defaultLane.GetIgnoreList()
suite.Require().Equal(1, len(ignoreList))
ignoreList = mevLane.GetIgnoreList()
suite.Require().Equal(0, len(ignoreList))
})
suite.Run("works mev and default lane in reverse order", func() {
@ -220,12 +216,6 @@ func (suite *BlockBusterTestSuite) TestNewMempool() {
fetcher,
)
suite.Require().NoError(err)
ignoreList := defaultLane.GetIgnoreList()
suite.Require().Equal(1, len(ignoreList))
ignoreList = mevLane.GetIgnoreList()
suite.Require().Equal(0, len(ignoreList))
})
suite.Run("works with mev, free, and default lane", func() {
@ -237,17 +227,6 @@ func (suite *BlockBusterTestSuite) TestNewMempool() {
fetcher,
)
suite.Require().NoError(err)
ignoreList := defaultLane.GetIgnoreList()
suite.Require().Equal(2, len(ignoreList))
ignoreList = mevLane.GetIgnoreList()
suite.Require().Equal(1, len(ignoreList))
suite.Require().Equal(freeLane, ignoreList[0])
ignoreList = freeLane.GetIgnoreList()
suite.Require().Equal(1, len(ignoreList))
suite.Require().Equal(mevLane, ignoreList[0])
})
suite.Run("works with mev, default, free lane", func() {
@ -259,17 +238,6 @@ func (suite *BlockBusterTestSuite) TestNewMempool() {
fetcher,
)
suite.Require().NoError(err)
ignoreList := defaultLane.GetIgnoreList()
suite.Require().Equal(2, len(ignoreList))
ignoreList = mevLane.GetIgnoreList()
suite.Require().Equal(1, len(ignoreList))
suite.Require().Equal(freeLane, ignoreList[0])
ignoreList = freeLane.GetIgnoreList()
suite.Require().Equal(1, len(ignoreList))
suite.Require().Equal(mevLane, ignoreList[0])
})
suite.Run("works with free, mev, and default lane", func() {
@ -281,17 +249,6 @@ func (suite *BlockBusterTestSuite) TestNewMempool() {
fetcher,
)
suite.Require().NoError(err)
ignoreList := defaultLane.GetIgnoreList()
suite.Require().Equal(2, len(ignoreList))
ignoreList = mevLane.GetIgnoreList()
suite.Require().Equal(1, len(ignoreList))
suite.Require().Equal(freeLane, ignoreList[0])
ignoreList = freeLane.GetIgnoreList()
suite.Require().Equal(1, len(ignoreList))
suite.Require().Equal(mevLane, ignoreList[0])
})
suite.Run("works with default, free, mev lanes", func() {
@ -303,17 +260,6 @@ func (suite *BlockBusterTestSuite) TestNewMempool() {
fetcher,
)
suite.Require().NoError(err)
ignoreList := defaultLane.GetIgnoreList()
suite.Require().Equal(2, len(ignoreList))
ignoreList = mevLane.GetIgnoreList()
suite.Require().Equal(1, len(ignoreList))
suite.Require().Equal(freeLane, ignoreList[0])
ignoreList = freeLane.GetIgnoreList()
suite.Require().Equal(1, len(ignoreList))
suite.Require().Equal(mevLane, ignoreList[0])
})
suite.Run("default lane not included", func() {
@ -324,7 +270,7 @@ func (suite *BlockBusterTestSuite) TestNewMempool() {
lanes,
fetcher,
)
suite.Require().Error(err)
suite.Require().NoError(err)
})
suite.Run("duplicate lanes", func() {

View File

@ -77,22 +77,6 @@ func (_m *Lane) CountTx() int {
return r0
}
// GetIgnoreList provides a mock function with given fields:
func (_m *Lane) GetIgnoreList() []block.Lane {
ret := _m.Called()
var r0 []block.Lane
if rf, ok := ret.Get(0).(func() []block.Lane); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]block.Lane)
}
}
return r0
}
// GetMaxBlockSpace provides a mock function with given fields:
func (_m *Lane) GetMaxBlockSpace() math.LegacyDec {
ret := _m.Called()
@ -272,11 +256,6 @@ func (_m *Lane) SetAnteHandler(antehander types.AnteHandler) {
_m.Called(antehander)
}
// SetIgnoreList provides a mock function with given fields: ignoreList
func (_m *Lane) SetIgnoreList(ignoreList []block.Lane) {
_m.Called(ignoreList)
}
// SetMaxBlockSpace provides a mock function with given fields: _a0
func (_m *Lane) SetMaxBlockSpace(_a0 math.LegacyDec) {
_m.Called(_a0)

View File

@ -591,7 +591,7 @@ func getTxsWithInfo(txs []sdk.Tx) ([]utils.TxWithInfo, error) {
signerextraction.NewDefaultAdapter(),
math.LegacyNewDec(1),
)
lane := defaultlane.NewDefaultLane(cfg)
lane := defaultlane.NewDefaultLane(cfg, base.DefaultMatchHandler())
txsWithInfo := make([]utils.TxWithInfo, len(txs))
for i, tx := range txs {

View File

@ -10,7 +10,6 @@ import (
"cosmossdk.io/log"
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/mock"
signer_extraction "github.com/skip-mev/block-sdk/adapters/signer_extraction_adapter"
"github.com/skip-mev/block-sdk/block"
@ -539,23 +538,19 @@ func (s *BaseTestSuite) TestPrepareLane() {
)
s.Require().NoError(err)
mh := func(ctx sdk.Context, tx sdk.Tx) bool {
return true
}
// 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.LegacyOneDec(), expectedExecution)
lane := s.initLaneWithMatchHandlers(math.LegacyOneDec(), expectedExecution, []base.MatchHandler{mh})
// Insert the transaction into the lane
s.Require().NoError(lane.Insert(s.ctx, tx))
mockLane := mocks.NewLane(s.T())
mockLane.On(
"Match",
mock.Anything,
tx,
).Return(true, nil)
lane.SetIgnoreList([]block.Lane{mockLane})
txBz, err := s.encodingConfig.TxConfig.TxEncoder()(tx)
s.Require().NoError(err)
@ -1060,27 +1055,18 @@ func (s *BaseTestSuite) TestProcessLane() {
s.Require().NoError(err)
// First lane matches this lane the other does not.
otherLane := mocks.NewLane(s.T())
otherLane.On(
"Match",
mock.Anything,
tx1,
).Return(true, nil)
mh := func(ctx sdk.Context, tx sdk.Tx) bool {
return tx == tx1
}
otherLane.On(
"Match",
mock.Anything,
tx2,
).Return(false, nil)
lane := s.initLane(
lane := s.initLaneWithMatchHandlers(
math.LegacyOneDec(),
map[sdk.Tx]bool{
tx1: true,
tx2: true,
},
[]base.MatchHandler{mh},
)
lane.SetIgnoreList([]block.Lane{otherLane})
proposal := []sdk.Tx{
tx1,
@ -1332,39 +1318,22 @@ func (s *BaseTestSuite) TestProcessLane() {
tx4,
}
otherLane := mocks.NewLane(s.T())
otherLane.On(
"Match",
mock.Anything,
tx1,
).Return(false, nil)
mh := func(ctx sdk.Context, tx sdk.Tx) bool {
if tx == tx1 || tx == tx2 {
return false
}
otherLane.On(
"Match",
mock.Anything,
tx2,
).Return(false, nil)
return true
}
otherLane.On(
"Match",
mock.Anything,
tx3,
).Return(true, nil)
otherLane.On(
"Match",
mock.Anything,
tx4,
).Return(true, nil)
lane := s.initLane(
lane := s.initLaneWithMatchHandlers(
math.LegacyOneDec(),
map[sdk.Tx]bool{
tx1: true,
tx2: true,
},
[]base.MatchHandler{mh},
)
lane.SetIgnoreList([]block.Lane{otherLane})
txsFromLane, remainingTxs, err := lane.DefaultProcessLaneHandler()(s.ctx, proposal)
s.Require().NoError(err)
@ -1436,36 +1405,15 @@ func (s *BaseTestSuite) TestProcessLane() {
tx4,
}
otherLane := mocks.NewLane(s.T())
otherLane.On(
"Match",
mock.Anything,
tx1,
).Return(true, nil)
mh := func(ctx sdk.Context, tx sdk.Tx) bool {
return true
}
otherLane.On(
"Match",
mock.Anything,
tx2,
).Return(true, nil)
otherLane.On(
"Match",
mock.Anything,
tx3,
).Return(true, nil)
otherLane.On(
"Match",
mock.Anything,
tx4,
).Return(true, nil)
lane := s.initLane(
lane := s.initLaneWithMatchHandlers(
math.LegacyOneDec(),
map[sdk.Tx]bool{},
[]base.MatchHandler{mh},
)
lane.SetIgnoreList([]block.Lane{otherLane})
txsFromLane, remainingTxs, err := lane.DefaultProcessLaneHandler()(s.ctx, proposal)
s.Require().NoError(err)
@ -1533,32 +1481,15 @@ func (s *BaseTestSuite) TestProcessLane() {
tx4,
}
otherLane := mocks.NewLane(s.T())
otherLane.On(
"Match",
mock.Anything,
tx1,
).Return(false, nil)
mh := func(ctx sdk.Context, tx sdk.Tx) bool {
if tx == tx1 || tx == tx3 {
return false
}
otherLane.On(
"Match",
mock.Anything,
tx2,
).Return(true, nil)
return true
}
otherLane.On(
"Match",
mock.Anything,
tx3,
).Return(false, nil).Maybe()
otherLane.On(
"Match",
mock.Anything,
tx4,
).Return(true, nil).Maybe()
lane := s.initLane(
lane := s.initLaneWithMatchHandlers(
math.LegacyOneDec(),
map[sdk.Tx]bool{
tx1: true,
@ -1566,8 +1497,8 @@ func (s *BaseTestSuite) TestProcessLane() {
tx3: true,
tx4: true,
},
[]base.MatchHandler{mh},
)
lane.SetIgnoreList([]block.Lane{otherLane})
txsFromLane, remainingTxs, err := lane.DefaultProcessLaneHandler()(s.ctx, proposal)
s.Require().Error(err)
@ -1739,7 +1670,26 @@ func (s *BaseTestSuite) initLane(
maxBlockSpace,
)
return defaultlane.NewDefaultLane(config)
return defaultlane.NewDefaultLane(config, base.DefaultMatchHandler())
}
func (s *BaseTestSuite) initLaneWithMatchHandlers(
maxBlockSpace math.LegacyDec,
expectedExecution map[sdk.Tx]bool,
matchHandlers []base.MatchHandler,
) *defaultlane.DefaultLane {
config := base.NewLaneConfig(
log.NewNopLogger(),
s.encodingConfig.TxConfig.TxEncoder(),
s.encodingConfig.TxConfig.TxDecoder(),
s.setUpAnteHandler(expectedExecution),
signer_extraction.NewDefaultAdapter(),
maxBlockSpace,
)
mh := base.NewMatchHandler(base.DefaultMatchHandler(), matchHandlers...)
return defaultlane.NewDefaultLane(config, mh)
}
func (s *BaseTestSuite) setUpAnteHandler(expectedExecution map[sdk.Tx]bool) sdk.AnteHandler {

View File

@ -13,8 +13,7 @@ const (
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 should not be ignored (as defined by the IgnoreList in the LaneConfig).
// transactions by the transaction fees. The default lane accepts any transaction.
// 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.
@ -23,7 +22,7 @@ type DefaultLane struct {
}
// NewDefaultLane returns a new default lane.
func NewDefaultLane(cfg base.LaneConfig) *DefaultLane {
func NewDefaultLane(cfg base.LaneConfig, matchHandler base.MatchHandler) *DefaultLane {
lane := base.NewBaseLane(
cfg,
LaneName,
@ -33,7 +32,7 @@ func NewDefaultLane(cfg base.LaneConfig) *DefaultLane {
cfg.SignerExtractor,
cfg.MaxTxs,
),
base.DefaultMatchHandler(),
matchHandler,
)
if err := lane.ValidateBasic(); err != nil {

View File

@ -45,6 +45,7 @@ type (
func NewMEVLane(
cfg base.LaneConfig,
factory Factory,
matchHandler base.MatchHandler,
) *MEVLane {
lane := &MEVLane{
BaseLane: base.NewBaseLane(
@ -56,7 +57,7 @@ func NewMEVLane(
cfg.SignerExtractor,
cfg.MaxTxs,
),
factory.MatchHandler(),
matchHandler,
),
Factory: factory,
}

View File

@ -64,7 +64,7 @@ func (s *MEVTestSuite) initLane(
)
factory := mev.NewDefaultAuctionFactory(s.encCfg.TxConfig.TxDecoder(), signer_extraction.NewDefaultAdapter())
return mev.NewMEVLane(config, factory)
return mev.NewMEVLane(config, factory, factory.MatchHandler())
}
func (s *MEVTestSuite) setUpAnteHandler(expectedExecution map[sdk.Tx]bool) sdk.AnteHandler {

159
tests/app/README.md Normal file
View File

@ -0,0 +1,159 @@
# Block SDK Enabled Test App (10 min)
## Overview
This readme describes how to build a test app that uses the Block SDK. This assumes that you have already installed the Block SDK and have a working development environment. To install the Block SDK, please run the following:
```bash
go get github.com/skip-mev/block-sdk
```
## Building the Test App
There are fix critical steps to building a test app that uses the Block SDK:
1. Set up the signer extractor.
2. Create the lane configurations for each individual lane i.e. `LaneConfig`.
3. Configure the match handlers for each lane i.e. `MatchHandler`.
4. Creating the Block SDK mempool i.e. `LanedMempool`.
5. Setting the antehandlers - used for transaction validation - for each lane.
6. Setting the proposal handlers - used for block creation and verification - for the application to utilize the Block SDK's Prepare and Process Proposal handlers.
### 1. Signer Extractor
The signer extractor is responsible for extracting signers and relevant information about who is signing the transaction. We recommend using the default implementation provided by the Block SDK.
```go
signerAdapter := signerextraction.NewDefaultAdapter()
```
### 2. Lane Configurations
This controls how many transactions can be stored by each lane, how much block space is allocated to each lane, how to extract transacation information such as signers, fees, and more. Each lane should have a separate `LaneConfig` object.
For example, in [`lanes.go`](./lanes.go) we see the following:
```go
mevConfig := base.LaneConfig{
Logger: app.Logger(),
TxEncoder: app.txConfig.TxEncoder(),
TxDecoder: app.txConfig.TxDecoder(),
MaxBlockSpace: math.LegacyMustNewDecFromStr("0.2"),
SignerExtractor: signerAdapter,
MaxTxs: 1000,
}
```
Following the example above:
* `Logger`: This is the logger that will be utilized by the lane when outputting information as blocks are being processed and constructed.
* `TxEncoder`: This is the encoder that will be used to encode transactions.
* `TxDecoder`: This is the decoder that will be used to decode transactions.
* `MaxBlockSpace`: This is the maximum amount of block space that can be allocated to this lane. In this case, we allocate 20% of the block space to this lane.
* `SignerExtractor`: This is the signer extractor that will be used to extract signers from transactions. In this case, we utilize the default signer extractor provided by the Block SDK. **This is the recommended approach.**
* `MaxTxs`: This is the maximum number of transactions that can be stored in this lane. In this case, we allow up to 1000 transactions to be stored in this lane at any given time.
### Match Handlers
Match handlers are responsible for matching transactions to lanes. Each lane should have a unique match handler. By default, we recommend that the default lane be the last lane in your application. This is because the default lane matches all transactions that do not match to any of the other lanes. If you want to have a lane after the default lane, please see the section below.
#### (OPTIONAL) Having Lanes after the Default Lane
If you want to have lanes after the default lane, you will need to utilize the `base.NewMatchHandler` function. This function allows you to construct a match handler that can ignore other lane's match handlers.
For example, if we wanted the free and MEV lanes to be processed after the default lane - default, MEV, free - we can do the following:
```go
// Create the final match handler for the default lane.
defaultMatchHandler := base.NewMatchHandler(
base.DefaultMatchHandler(),
factory.MatchHandler(),
freelane.DefaultMatchHandler(),
)
```
Following the example, we can see the following:
* `base.DefaultMatchHandler()`: This is the default match handler provided by the Block SDK. This matches all transactions to the lane.
* `factory.MatchHandler()`: This is the MEV lane's match handler. This is passed as a parameter to the `base.NewMatchHandler` function - which means that all transactions that match to the MEV lane will be ignored by the default match handler.
* `freelane.DefaultMatchHandler()`: This is the default match handler for the free lane. This is passed as a parameter to the `base.NewMatchHandler` function - which means that all transactions that match to the free lane will be ignored by the default match handler.
**This will allow the default match handler to only match transactions that do not match to the MEV lane or the free lane.**
### Block SDK Mempool
After constructing the lanes, we can create the Block SDK mempool - `LanedMempool`. This object is responsible for managing the lanes and processing transactions.
```go
// STEP 1: Create the Block SDK lanes.
mevLane, freeLane, defaultLane := CreateLanes(app)
// STEP 2: Construct a mempool based off the lanes.
mempool, err := block.NewLanedMempool(
app.Logger(),
[]block.Lane{mevLane, freeLane, defaultLane},
&app.blocksdkKeeper,
)
if err != nil {
panic(err)
}
// STEP 3: Set the mempool on the app.
app.App.SetMempool(mempool)
```
Note that we pass the lanes to the `block.NewLanedMempool` function. **The order of the lanes is important.** Proposals will be constructed based on the order of lanes passed to the `block.NewLanedMempool` function. In the example above, the MEV lane will be processed first, followed by the free lane, and finally the default lane.
### AnteHandlers
`AnteHandlers` are responsible for validating transactions. We recommend that developers utilize the same antehandler chain that is used by the application. In the example test app, we construct the `AnteHandler` with `NewBSDKAnteHandler`. In the case where the certain ante decorators should ignore certain lanes, we can wrap a `Decorator` with the `block.NewIgnoreDecorator` function as seen in `ante.go`.
After constructing the `AnteHandler`, we can set it on the application and on the lanes.
```go
// STEP 4: Create a global ante handler that will be called on each transaction when
// proposals are being built and verified. Note that this step must be done before
// setting the ante handler on the lanes.
handlerOptions := ante.HandlerOptions{
AccountKeeper: app.AccountKeeper,
BankKeeper: app.BankKeeper,
FeegrantKeeper: app.FeeGrantKeeper,
SigGasConsumer: ante.DefaultSigVerificationGasConsumer,
SignModeHandler: app.txConfig.SignModeHandler(),
}
options := BSDKHandlerOptions{
BaseOptions: handlerOptions,
auctionkeeper: app.auctionkeeper,
TxDecoder: app.txConfig.TxDecoder(),
TxEncoder: app.txConfig.TxEncoder(),
FreeLane: freeLane,
MEVLane: mevLane,
}
anteHandler := NewBSDKAnteHandler(options)
app.App.SetAnteHandler(anteHandler)
// Set the AnteHandlers on the lanes.
mevLane.SetAnteHandler(anteHandler)
freeLane.SetAnteHandler(anteHandler)
defaultLane.SetAnteHandler(anteHandler)
```
### Proposal Handlers
The proposal handlers - `PrepareProposal` and `ProcessProposal` - are responsible for building and verifying block proposals. To add it to your application, follow the example below:
```go
// Step 5: Create the proposal handler and set it on the app.
proposalHandler := abci.NewProposalHandler(
app.Logger(),
app.TxConfig().TxDecoder(),
app.TxConfig().TxEncoder(),
mempool,
)
app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler())
app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler())
```
## Conclusion
Adding the Block SDK to your application is a simple 6 step process. If you have any questions, please feel free to reach out to the [Skip team](https://skip.money/contact). We are happy to help!

View File

@ -6,7 +6,6 @@ import (
"path/filepath"
"cosmossdk.io/log"
"cosmossdk.io/math"
dbm "github.com/cosmos/cosmos-db"
"cosmossdk.io/depinject"
@ -43,11 +42,7 @@ import (
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
"github.com/skip-mev/block-sdk/abci"
signer_extraction "github.com/skip-mev/block-sdk/adapters/signer_extraction_adapter"
"github.com/skip-mev/block-sdk/block"
"github.com/skip-mev/block-sdk/block/base"
defaultlane "github.com/skip-mev/block-sdk/lanes/base"
"github.com/skip-mev/block-sdk/lanes/free"
"github.com/skip-mev/block-sdk/lanes/mev"
auctionkeeper "github.com/skip-mev/block-sdk/x/auction/keeper"
blocksdkkeeper "github.com/skip-mev/block-sdk/x/blocksdk/keeper"
@ -213,71 +208,27 @@ func New(
// ---------------------------------------------------------------------------- //
// ------------------------- Begin Custom Code -------------------------------- //
// ---------------------------------------------------------------------------- //
// STEP 1-3: Create the Block SDK lanes.
mevLane, freeLane, defaultLane := CreateLanes(app)
// Set POB's 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.
// 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(),
TxDecoder: app.txConfig.TxDecoder(),
MaxBlockSpace: math.LegacyMustNewDecFromStr("0.2"),
SignerExtractor: signer_extraction.NewDefaultAdapter(),
MaxTxs: 1000,
}
mevLane := mev.NewMEVLane(
mevConfig,
mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder(), signer_extraction.NewDefaultAdapter()),
)
// 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.LegacyMustNewDecFromStr("0.2"),
SignerExtractor: signer_extraction.NewDefaultAdapter(),
MaxTxs: 1000,
}
freeLane := free.NewFreeLane(
freeConfig,
base.DefaultTxPriority(),
free.DefaultMatchHandler(),
)
// Default lane accepts all other transactions.
defaultConfig := base.LaneConfig{
Logger: app.Logger(),
TxEncoder: app.txConfig.TxEncoder(),
TxDecoder: app.txConfig.TxDecoder(),
MaxBlockSpace: math.LegacyMustNewDecFromStr("0.6"),
SignerExtractor: signer_extraction.NewDefaultAdapter(),
MaxTxs: 1000,
}
defaultLane := defaultlane.NewDefaultLane(defaultConfig)
// Set the lanes into the mempool.
lanes := []block.Lane{
mevLane,
freeLane,
defaultLane,
}
// STEP 4: Construct a mempool based off the lanes. Note that the order of the lanes
// matters. Blocks are constructed from the top lane to the bottom lane. The top lane
// is the first lane in the array and the bottom lane is the last lane in the array.
mempool, err := block.NewLanedMempool(
app.Logger(),
lanes,
[]block.Lane{mevLane, freeLane, defaultLane},
&app.blocksdkKeeper,
)
if err != nil {
panic(err)
}
// The application's mempool is now powered by the Block SDK!
app.App.SetMempool(mempool)
// Create a global ante handler that will be called on each transaction when
// proposals are being built and verified.
// STEP 5: Create a global ante handler that will be called on each transaction when
// proposals are being built and verified. Note that this step must be done before
// setting the ante handler on the lanes.
handlerOptions := ante.HandlerOptions{
AccountKeeper: app.AccountKeeper,
BankKeeper: app.BankKeeper,
@ -294,14 +245,15 @@ func New(
MEVLane: mevLane,
}
anteHandler := NewBSDKAnteHandler(options)
// Set the lane config on the lanes.
for _, lane := range lanes {
lane.SetAnteHandler(anteHandler)
}
app.App.SetAnteHandler(anteHandler)
// Set the abci handlers on base app
// Set the ante handler on the lanes.
mevLane.SetAnteHandler(anteHandler)
freeLane.SetAnteHandler(anteHandler)
defaultLane.SetAnteHandler(anteHandler)
// Step 6: Create the proposal handler and set it on the app. Now the application
// will build and verify proposals using the Block SDK!
proposalHandler := abci.NewProposalHandler(
app.Logger(),
app.TxConfig().TxDecoder(),
@ -311,7 +263,8 @@ func New(
app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler())
app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler())
// Set the custom CheckTx handler on BaseApp.
// Step 7: Set the custom CheckTx handler on BaseApp. This is only required if you
// use the MEV lane.
checkTxHandler := mev.NewCheckTxHandler(
app.App,
app.txConfig.TxDecoder(),

93
tests/app/lanes.go Normal file
View File

@ -0,0 +1,93 @@
package app
import (
"cosmossdk.io/math"
signerextraction "github.com/skip-mev/block-sdk/adapters/signer_extraction_adapter"
"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"
mevlane "github.com/skip-mev/block-sdk/lanes/mev"
)
// CreateLanes walks through the process of creating the lanes for the block sdk. In this function
// we create three separate lanes - MEV, Free, and Default - and then return them.
//
// NOTE: Application Developers should closely replicate this function in their own application.
func CreateLanes(app *TestApp) (*mevlane.MEVLane, *freelane.FreeLane, *defaultlane.DefaultLane) {
// 1. Create the signer extractor. This is used to extract the expected signers from
// a transaction. Each lane can have a different signer extractor if needed.
signerAdapter := signerextraction.NewDefaultAdapter()
// 2. Create the configurations for each lane. These configurations determine how many
// transactions the lane can store, the maximum block space the lane can consume, and
// the signer extractor used to extract the expected signers from a transaction.
//
// IMPORTANT NOTE: If the block sdk module is utilized to store lanes, than the maximum
// block space will be replaced with what is in state / in the genesis file.
// Create a mev configuration that accepts 1000 transactions and consumes 20% of the
// block space.
mevConfig := base.LaneConfig{
Logger: app.Logger(),
TxEncoder: app.txConfig.TxEncoder(),
TxDecoder: app.txConfig.TxDecoder(),
MaxBlockSpace: math.LegacyMustNewDecFromStr("0.2"),
SignerExtractor: signerAdapter,
MaxTxs: 1000,
}
// Create a free configuration that accepts 1000 transactions and consumes 20% of the
// block space.
freeConfig := base.LaneConfig{
Logger: app.Logger(),
TxEncoder: app.txConfig.TxEncoder(),
TxDecoder: app.txConfig.TxDecoder(),
MaxBlockSpace: math.LegacyMustNewDecFromStr("0.2"),
SignerExtractor: signerAdapter,
MaxTxs: 1000,
}
// Create a default configuration that accepts 1000 transactions and consumes 60% of the
// block space.
defaultConfig := base.LaneConfig{
Logger: app.Logger(),
TxEncoder: app.txConfig.TxEncoder(),
TxDecoder: app.txConfig.TxDecoder(),
MaxBlockSpace: math.LegacyMustNewDecFromStr("0.6"),
SignerExtractor: signerAdapter,
MaxTxs: 1000,
}
// 3. Create the match handlers for each lane. These match handlers determine whether or not
// a transaction belongs in the lane.
// Create the final match handler for the mev lane.
factory := mevlane.NewDefaultAuctionFactory(app.txConfig.TxDecoder(), signerAdapter)
mevMatchHandler := factory.MatchHandler()
// Create the final match handler for the free lane.
freeMatchHandler := freelane.DefaultMatchHandler()
// Create the final match handler for the default lane.
defaultMatchHandler := base.DefaultMatchHandler()
// 4. Create the lanes.
mevLane := mevlane.NewMEVLane(
mevConfig,
factory,
mevMatchHandler,
)
freeLane := freelane.NewFreeLane(
freeConfig,
base.DefaultTxPriority(),
freeMatchHandler,
)
defaultLane := defaultlane.NewDefaultLane(
defaultConfig,
defaultMatchHandler,
)
return mevLane, freeLane, defaultLane
}