default re-factor with constructor

This commit is contained in:
David Terpay 2023-08-14 18:02:08 -04:00
parent f203db880e
commit 7005d4c8a3
No known key found for this signature in database
GPG Key ID: 627EFB00DADF0CD1
11 changed files with 1305 additions and 392 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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 <sender, nonce>
// 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
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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