starting to add docs

This commit is contained in:
David Terpay 2023-08-15 12:39:10 -04:00
parent af85b86241
commit dbf4b6720c
No known key found for this signature in database
GPG Key ID: 627EFB00DADF0CD1
17 changed files with 298 additions and 91 deletions

0
abci/README.md Normal file
View File

View File

@ -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(),

199
block/constructor/README.md Normal file
View File

@ -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

View File

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

View File

@ -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,

View File

@ -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(),

View File

@ -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 {

0
lanes/README.md Normal file
View File

0
lanes/base/README.md Normal file
View File

View File

@ -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(),

View File

@ -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,

0
lanes/free/README.md Normal file
View File

View File

@ -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 {

0
lanes/mev/README.md Normal file
View File

View File

@ -45,7 +45,7 @@ type (
// NewMEVLane returns a new TOB lane.
func NewMEVLane(
cfg block.LaneConfig,
cfg constructor.LaneConfig,
factory Factory,
) *MEVLane {
lane := &MEVLane{

View File

@ -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(),

View File

@ -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(),