mempool

package
v3.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 5, 2025 License: GPL-3.0 Imports: 17 Imported by: 0

README

Mempool Package

The mempool package implements a transaction mempool for the Cosmos SDK blockchain. It provides a sophisticated transaction management system that organizes transactions into different lanes based on their types and priorities.

Overview

The mempool is designed to handle transaction processing and block proposal preparation in a Cosmos SDK-based blockchain. It implements the sdkmempool.Mempool interface and uses a lane-based architecture to manage different types of transactions.

Architecture

Core Components
  1. Mempool: The main structure that manages multiple transaction lanes and implements the core mempool interface.
  2. Lane: A logical grouping of transactions with specific matching criteria and space allocation.
  3. Proposal: Represents a block proposal under construction, managing transaction inclusion and space limits.
  4. BlockSpace: Manages block space constraints including transaction bytes and gas limits.
Key Features
  • Lane-based Organization: Transactions are organized into different lanes based on their matching functions
  • Space Management: Efficient management of block space and gas limits
  • Transaction Prioritization: Support for different transaction priorities among and within lanes
  • Thread Safety: Built-in mutex protection for concurrent access
  • Error Recovery: Panic recovery mechanisms for robust operation

Usage

Creating a Lane

A lane is created with specific matching criteria and space allocation. Here's an example of creating a lane for bank send transactions:

bankSendLane := NewLane(
    logger,
    txEncoder,
    "bankSend",                    // Lane name
    isBankSendTx,                  // Matching function
    math.LegacyMustNewDecFromStr("0.2"),  // Max transaction space ratio
    math.LegacyMustNewDecFromStr("0.3"),  // Max lane space ratio
    sdkmempool.DefaultPriorityMempool(),  // Underlying mempool implementation
    nil,                           // Lane limit check handler
)

// Example matching function for bank send transactions
func isBankSendTx(_ sdk.Context, tx sdk.Tx) bool {
    msgs := tx.GetMsgs()
    if len(msgs) == 0 {
        return false
    }
    for _, msg := range msgs {
        if _, ok := msg.(*banktypes.MsgSend); !ok {
            return false
        }
    }
    return true
}

Key parameters for lane creation:

  • logger: Logger instance for lane operations
  • txEncoder: Function to encode transactions
  • name: Unique identifier for the lane
  • matchFn: Function to determine if a transaction belongs in this lane
  • maxTransactionBlockRatio: Maximum space ratio for individual transactions relative to total block space more details on Space management
  • maxLaneBlockRatio: Maximum space ratio for the entire lane relative to total block space more details on Space management
  • mempool: Underlying Cosmos SDK mempool implementation that handles transaction storage and ordering within the lane. This determines how transactions are stored and selected within the lane
  • callbackAfterFillProposal: Optional callback function that is called when the lane fills its space limit. This can be used to implement inter-lane dependencies, such as blocking other lanes when a lane fills its limit.
Inter-Lane Dependencies

Lanes can be configured to interact with each other through the callbackAfterFillProposal callback. This is useful for implementing priority systems or dependencies between different types of transactions. For example, you might want to block certain lanes when a high-priority lane fills its limit:

// Create a dependent lane that will be blocked when the dependency lane fills its limit
dependentLane := NewLane(
    logger,
    txEncoder,
    signerExtractor,
    "dependent",
    isOtherTx,
    math.LegacyMustNewDecFromStr("0.5"),
    math.LegacyMustNewDecFromStr("0.5"),
    sdkmempool.DefaultPriorityMempool(),
    nil,
)

// Create a dependency lane that controls the dependent lane
dependencyLane := NewLane(
    logger,
    txEncoder,
    signerExtractor,
    "dependency",
    isBankSendTx,
    math.LegacyMustNewDecFromStr("0.5"),
    math.LegacyMustNewDecFromStr("0.5"),
    sdkmempool.DefaultPriorityMempool(),
    func(isLaneLimitExceeded bool) {
        dependentLane.SetBlocked(isLaneLimitExceeded)
    },
)

In this example, when the dependency lane fills its space limit, it will block the dependent lane from processing transactions. This mechanism allows for sophisticated transaction prioritization and coordination between different types of transactions.

Creating a Mempool
mempool := NewMempool(
    logger,
    []*Lane{
        BankSendLane,
        DelegationLane,
        // Lane order is critical - first lane in the slice matches and processes the transaction first
    },
)

// set the mempool in Chain application
app.SetMempool(mempool)

Key parameters for mempool creation:

  • logger: Logger instance for mempool operations
  • lanes: Array of lane configurations, where each lane is responsible for a specific type of transaction
    • The order of lanes determines the priority of transaction types
    • The sum of all lane space ratios can exceed 100% as it represents the maximum potential allocation for each lane, not a strict partition of the block space
Space Management
Space Allocation

Both maxTransactionBlockRatio and maxLaneBlockRatio are expressed as ratios of the total block space and are used specifically during proposal preparation. For example, a maxTransactionBlockRatio of 0.2 means a single transaction can use up to 20% of the total block space in a proposal, while a maxLaneBlockRatio of 0.3 means the entire lane can use up to 30% of the total block space in a proposal. These ratios are used to ensure fair distribution of block space among different transaction types during proposal construction.

Space Cap Behavior
  • Transaction Space Cap (Hard Cap): The maxTransactionBlockRatio ratio enforces a strict limit on individual transaction sizes of each lane. For example, with a maxTransactionBlockRatio of 0.2, a transaction requiring more than 20% of the total block space will not be accepted.
  • Lane Space Cap (Soft Cap): The maxLaneBlockRatio ratio serves as a guideline for space allocation during the first round of proposal construction. If a lane's maxLaneBlockRatio is 0.3, it can still include one last transaction that would cause it to exceed this limit in the proposal, provided each individual transaction respects the maxTransactionBlockRatio limit. For instance, a lane with a 0.3 maxLaneBlockRatio could include two transactions each using 20% of the block space (totaling 40%) in the proposal, as long as both transactions individually respect the maxTransactionBlockRatio limit.
Proposal Preparation

The lane space cap (maxLaneBlockRatio) is only enforced during the first round of proposal preparation. In subsequent rounds, when filling the remaining block space, the lane cap is not considered, allowing lanes to potentially use more space than their initial allocation if space is available. This two-phase approach ensures both fair initial distribution and efficient use of remaining block space.

Block Proposal Preparation

The mempool provides functionality to prepare block proposals by:

  1. Filling proposals with transactions from each lane with maxLaneBlockRatio
  2. Filling remaining proposal space with transactions from each lane in the same order without maxLaneBlockRatio

Best Practices

  1. Configure appropriate lane ratios based on your application's needs
  2. Implement proper transaction matching functions for each lane
  3. Always place the default lane as the last lane to ensure every transaction type can be matched

Note: Lane order is critical as it determines transaction processing priority. This is why the default lane should always be last, to ensure it only catches transactions that don't match any other lane.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type BlockSpace

type BlockSpace struct {
	// contains filtered or unexported fields
}

BlockSpace defines the block space.

func NewBlockSpace

func NewBlockSpace(txBytes uint64, gas uint64) BlockSpace

NewBlockSpace returns a new block space.

func (BlockSpace) Add

func (bs BlockSpace) Add(other BlockSpace) BlockSpace

Add returns the sum of this BlockSpace and another BlockSpace.

func (BlockSpace) Gas

func (bs BlockSpace) Gas() uint64

func (BlockSpace) IsExceededBy

func (bs BlockSpace) IsExceededBy(other BlockSpace) bool

IsExceededBy returns true if 'other' usage has exceeded this BlockSpace's limits.

func (BlockSpace) IsReachedBy

func (bs BlockSpace) IsReachedBy(other BlockSpace) bool

IsReachedBy returns true if 'other' usage has reached this BlockSpace's limits.

func (BlockSpace) Scale

func (bs BlockSpace) Scale(dec sdkmath.LegacyDec) (BlockSpace, error)

Scale returns a new BlockSpace with txBytes and gas multiplied by a decimal.

func (BlockSpace) String

func (bs BlockSpace) String() string

String returns a string representation of the BlockSpace.

func (BlockSpace) Sub

func (bs BlockSpace) Sub(other BlockSpace) BlockSpace

Sub returns the difference between this BlockSpace and another BlockSpace. Ensures txBytes and gas never go below zero.

func (BlockSpace) TxBytes

func (bs BlockSpace) TxBytes() uint64

--- Getters ---

type Lane

type Lane struct {
	// contains filtered or unexported fields
}

Lane defines a logical grouping of transactions within the mempool.

func NewLane

func NewLane(
	logger log.Logger,
	txEncoder sdk.TxEncoder,
	name string,
	txMatchFn TxMatchFn,
	maxTransactionBlockRatio math.LegacyDec,
	maxLaneBlockRatio math.LegacyDec,
	mempool sdkmempool.Mempool,
	callbackAfterFillProposal func(isLaneLimitExceeded bool),
) *Lane

NewLane is a constructor for a lane.

func (*Lane) Contains

func (l *Lane) Contains(tx sdk.Tx) bool

Contains returns true if the lane's mempool contains the transaction.

func (*Lane) CountTx

func (l *Lane) CountTx() int

CountTx returns the total number of transactions in the lane's mempool.

func (*Lane) FillProposal

func (l *Lane) FillProposal(
	ctx sdk.Context,
	proposal *Proposal,
) (blockUsed BlockSpace, iterator sdkmempool.Iterator)

FillProposal fills the proposal with transactions from the lane mempool with its own limit. It returns the total size and gas of the transactions added to the proposal. It also returns an iterator to the next transaction in the mempool.

func (*Lane) FillProposalByIterator

func (l *Lane) FillProposalByIterator(
	proposal *Proposal,
	iterator sdkmempool.Iterator,
	limit BlockSpace,
) (blockUsed BlockSpace)

FillProposalByIterator fills the proposal with transactions from the lane mempool with the given iterator and limit. It returns the total size and gas of the transactions added to the proposal.

func (*Lane) Insert

func (l *Lane) Insert(ctx context.Context, tx sdk.Tx) error

Insert inserts a transaction into the lane's mempool.

func (*Lane) Match

func (l *Lane) Match(ctx sdk.Context, tx sdk.Tx) bool

Match returns true if the transaction belongs to the lane.

func (*Lane) Remove

func (l *Lane) Remove(tx sdk.Tx) error

Remove removes a transaction from the lane's mempool.

func (*Lane) SetBlocked

func (l *Lane) SetBlocked(blocked bool)

SetBlocked sets the blocked flag to the given value.

type Mempool

type Mempool struct {
	// contains filtered or unexported fields
}

Mempool implements the sdkmempool.Mempool interface and uses Lanes internally.

func NewMempool

func NewMempool(
	logger log.Logger,
	lanes []*Lane,
) *Mempool

NewMempool returns a new mempool with the given lanes.

func (*Mempool) Contains

func (m *Mempool) Contains(tx sdk.Tx) (contains bool)

Contains returns true if the transaction is contained in any of the lanes.

func (*Mempool) CountTx

func (m *Mempool) CountTx() int

CountTx returns the total number of transactions across all lanes. Returns math.MaxInt if the total count would overflow.

func (*Mempool) GetLane

func (m *Mempool) GetLane(name string) *Lane

GetLane returns the lane with the given name.

func (*Mempool) Insert

func (m *Mempool) Insert(ctx context.Context, tx sdk.Tx) (err error)

Insert will insert a transaction into the mempool. It inserts the transaction into the first lane that it matches.

func (*Mempool) PrepareProposal

func (m *Mempool) PrepareProposal(ctx sdk.Context, proposal Proposal) (Proposal, error)

PrepareProposal divides the block gas limit among lanes (based on lane percentage), then calls each lane's FillProposal method. If leftover gas is important to you, you can implement a second pass or distribute leftover to subsequent lanes, etc.

func (*Mempool) Remove

func (m *Mempool) Remove(tx sdk.Tx) (err error)

Remove removes a transaction from the mempool. This assumes that the transaction is contained in only one of the lanes.

func (*Mempool) Select

func (m *Mempool) Select(ctx context.Context, txs [][]byte) sdkmempool.Iterator

Select returns a Mempool iterator (currently nil).

type Proposal

type Proposal struct {
	// contains filtered or unexported fields
}

Proposal represents a block proposal under construction.

func NewProposal

func NewProposal(logger log.Logger, maxBlockSize uint64, maxGasLimit uint64) Proposal

NewProposal returns a new empty proposal constrained by max block size and max gas limit.

func (*Proposal) Add

func (p *Proposal) Add(txInfo TxWithInfo) error

Add attempts to add a transaction to the proposal, respecting size/gas limits.

func (*Proposal) Contains

func (p *Proposal) Contains(txHash string) bool

Contains returns true if the proposal already has a transaction with the given txHash.

func (*Proposal) GetRemainingBlockSpace

func (p *Proposal) GetRemainingBlockSpace() BlockSpace

GetRemainingBlockSpace returns the remaining block space available for the proposal.

type ProposalHandler

type ProposalHandler struct {
	// contains filtered or unexported fields
}

ProposalHandler wraps ABCI++ PrepareProposal/ProcessProposal for the Mempool.

func NewProposalHandler

func NewProposalHandler(
	logger log.Logger,
	txDecoder sdk.TxDecoder,
	mempool *Mempool,
) *ProposalHandler

NewProposalHandler returns a new ABCI++ proposal handler for the Mempool.

func (*ProposalHandler) PrepareProposalHandler

func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler

PrepareProposalHandler builds the next block proposal from the Mempool.

func (*ProposalHandler) ProcessProposalHandler

func (h *ProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHandler

ProcessProposalHandler returns a no-op process proposal handler.

type TxMatchFn

type TxMatchFn func(sdk.Context, sdk.Tx) bool

func NewLaneTxMatchFn

func NewLaneTxMatchFn(msgs []sdk.Msg, onlyFree bool) TxMatchFn

type TxWithInfo

type TxWithInfo struct {
	// Hash is the hex-encoded hash of the transaction.
	Hash string
	// BlockSpace is the block space used by the transaction.
	BlockSpace BlockSpace
	// TxBytes is the raw transaction bytes.
	TxBytes []byte
}

TxWithInfo holds metadata required for a transaction to be included in a proposal.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL