default re-factor with constructor
This commit is contained in:
parent
f203db880e
commit
7005d4c8a3
@ -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
|
||||
}
|
||||
547
blockbuster/lanes/base/abci_test.go
Normal file
547
blockbuster/lanes/base/abci_test.go
Normal 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
|
||||
}
|
||||
32
blockbuster/lanes/base/base_test.go
Normal file
32
blockbuster/lanes/base/base_test.go
Normal 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"
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
240
blockbuster/lanes/base/mempool_test.go
Normal file
240
blockbuster/lanes/base/mempool_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
|
||||
256
blockbuster/utils/mocks/lane.go
Normal file
256
blockbuster/utils/mocks/lane.go
Normal 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
|
||||
}
|
||||
117
blockbuster/utils/mocks/lane_mempool.go
Normal file
117
blockbuster/utils/mocks/lane_mempool.go
Normal 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
|
||||
}
|
||||
@ -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(),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user