feat(bb): Multi-lane testing + docs (#174)
This commit is contained in:
parent
cee39a9eb7
commit
04f3862ecb
355
blockbuster/README.md
Normal file
355
blockbuster/README.md
Normal file
@ -0,0 +1,355 @@
|
||||
# BlockBuster
|
||||
|
||||
> 📕 BlockBuster is an app-side mempool + set of proposal handlers that allows
|
||||
developers to configure modular lanes of transactions in their blocks with
|
||||
distinct validation/ordering logic. **BlockBuster is the ultimate highway
|
||||
system for transactions.**
|
||||
|
||||
## High Level Overview
|
||||
|
||||
**`BlockBuster`** is a framework for creating modular, application specific
|
||||
mempools by **separating transactions into “lanes” with custom transaction handling.**
|
||||
|
||||
You can think of BlockBuster as a **transaction highway system**, where each
|
||||
lane on the highway serves a specific purpose and has its own set of rules and
|
||||
traffic flow.
|
||||
|
||||
Similarly, **BlockBuster** redefines block-space into **`lanes`** - where each
|
||||
`lane` has its own set of rules and transaction flow management systems.
|
||||
|
||||
* A lane is what we might traditionally consider to be a standard mempool
|
||||
where transaction ***validation***, ***ordering*** and ***prioritization*** for
|
||||
contained transactions are shared.
|
||||
* Lanes implement a **standard interface** that allows each individual lane to
|
||||
propose and validate a portion of a block.
|
||||
* Lanes are ordered with each other, configurable by developers. All lanes
|
||||
together define the desired block structure of a chain.
|
||||
|
||||
## BlockBuster Use Cases
|
||||
|
||||
A mempool with separate `lanes` can be used for:
|
||||
|
||||
1. **MEV mitigation**: a top of block lane could be designed to create an
|
||||
in-protocol top-of-block auction (as we are doing with POB) to recapture MEV
|
||||
in a transparent and governable way.
|
||||
2. **Free/reduced fee txs**: transactions with certain properties (e.g.
|
||||
from trusted accounts or performing encouraged actions) could leverage a
|
||||
free lane to reward behavior.
|
||||
3. **Dedicated oracle space** Oracles could be included before other kinds
|
||||
of transactions to ensure that price updates occur first, and are not able
|
||||
to be sandwiched or manipulated.
|
||||
4. **Orderflow auctions**: an OFA lane could be constructed such that order
|
||||
flow providers can have their submitted transactions bundled with specific
|
||||
backrunners, to guarantee MEV rewards are attributed back to users.
|
||||
Imagine MEV-share but in protocol.
|
||||
5. **Enhanced and customizable privacy**: privacy-enhancing features could
|
||||
be introduced, such as threshold encrypted lanes, to protect user data and
|
||||
maintain privacy for specific use cases.
|
||||
6. **Fee market improvements**: one or many fee markets - such as EIP-1559 -
|
||||
could be easily adopted for different lanes (potentially custom for certain
|
||||
dApps). Each smart contract/exchange could have its own fee market or auction
|
||||
for transaction ordering.
|
||||
7. **Congestion management**: segmentation of transactions to lanes can help
|
||||
mitigate network congestion by capping usage of certain applications and
|
||||
tailoring fee markets.
|
||||
|
||||
## BlockBuster Design
|
||||
|
||||
BlockBuster is a mempool composed of sub-mempools called **lanes**. All
|
||||
lanes together define the transaction highway system and BlockBuster mempool.
|
||||
When instantiating the BlockBuster mempool, developers will define all of the
|
||||
desired lanes and their configurations (including lane ordering).
|
||||
|
||||
Utilizing BlockBuster is a simple three step process:
|
||||
|
||||
* Determine the lanes desired. Currently, POB supports three different
|
||||
implementations of lanes: top of block lane, free lane, and a default lane.
|
||||
1. Top of block lane allows the top of every block to be auctioned off
|
||||
and constructed using logic defined by the `x/builder` module.
|
||||
2. Free lane allows base app to not charge certain types of transactions
|
||||
any fees. For example, delegations and/or re-delegations might be charged no
|
||||
fees. What qualifies as a free transaction is determined
|
||||
[here](https://github.com/skip-mev/pob/blob/main/blockbuster/lanes/free/factory.go).
|
||||
3. Default lane accepts all other transactions and is considered to be
|
||||
analogous to how mempools and proposals are constructed today.
|
||||
* Instantiate the mempool in base app.
|
||||
|
||||
```go
|
||||
mempool := blockbuster.NewMempool(lanes...)
|
||||
app.App.SetMempool(mempool)
|
||||
```
|
||||
|
||||
* Instantiate the BlockBuster proposal handlers in base app.
|
||||
|
||||
```go
|
||||
proposalHandlers := abci.NewProposalHandler(
|
||||
app.Logger(),
|
||||
app.txConfig.TxDecoder(),
|
||||
mempool, // BlockBuster mempool
|
||||
)
|
||||
app.App.SetPrepareProposal(proposalHandlers.PrepareProposalHandler())
|
||||
app.App.SetProcessProposal(proposalHandlers.ProcessProposalHandler())
|
||||
```
|
||||
|
||||
***Note: BlockBuster should configure a `DefaultLane` that accepts transactions
|
||||
that do not belong to any other lane.***
|
||||
|
||||
Transactions are inserted into the first lane that the transaction matches to.
|
||||
This means that a given transaction should really only belong to one lane
|
||||
(but this isn’t enforced).
|
||||
|
||||
### Proposals
|
||||
|
||||
The ordering of lanes when initializing BlockBuster in base app will determine
|
||||
the ordering of how proposals are built. For example, say that we instantiate
|
||||
three lanes:
|
||||
|
||||
1. Top of block lane
|
||||
2. Free lane
|
||||
3. Default lane
|
||||
|
||||
#### Preparing Proposals
|
||||
|
||||
When the current proposer starts building a block, it will first populate the
|
||||
proposal with transactions from the top of block lane, followed by free and
|
||||
default lane. Each lane proposes its own set of transactions using the lane’s
|
||||
`PrepareLane` (analogous to `PrepareProposal`). Each lane has a limit on the
|
||||
relative percentage of total block space that the lane can consume.
|
||||
For example, the free lane might be configured to only make up 10% of any
|
||||
block. This is defined on each lane’s `Config` when it is instantiated.
|
||||
|
||||
In the case when any lane fails to propose its portion of the block, it will
|
||||
be skipped and the next lane in the set of lanes will propose its portion of
|
||||
the block. Failures of partial block proposals are independent of one another.
|
||||
|
||||
#### Processing Proposals
|
||||
|
||||
Block proposals are validated iteratively following the exact ordering of lanes
|
||||
defined on base app. Transactions included in block proposals must respect the
|
||||
ordering of lanes. Any proposal that includes transactions that are out of
|
||||
order relative to the ordering of lanes will be rejected. Following the
|
||||
example defined above, if a proposal contains the following transactions:
|
||||
|
||||
1. Default transaction (belonging to the default lane)
|
||||
2. Top of block transaction (belonging to the top of block lane)
|
||||
3. Free transaction (belonging to the free lane)
|
||||
|
||||
It will be rejected because it does not respect the lane ordering.
|
||||
|
||||
The BlockBuster `ProcessProposalHandler` processes the proposal by verifying
|
||||
all transactions in the proposal according to each lane's verification logic
|
||||
in a greedy fashion. If a lane's portion of the proposal is invalid, we
|
||||
reject the proposal. After a lane's portion of the proposal is verified, we
|
||||
pass the remaining transactions to the next lane in the chain.
|
||||
|
||||
#### Coming Soon
|
||||
|
||||
BlockBuster will have its own dedicated gRPC service for searchers, wallets,
|
||||
and users that allows them to query what lane their transaction might belong
|
||||
in, what fees they might have to pay for a given transaction, and the general
|
||||
state of the BlockBuster mempool.
|
||||
|
||||
### Lanes
|
||||
|
||||
Each lane will define its own:
|
||||
|
||||
1. Unique prioritization/ordering mechanism i.e. how will transactions from a
|
||||
given lane be ordered in a block / mempool.
|
||||
2. Inclusion function to determine what types of transactions belong in the lane.
|
||||
3. Unique block building/verification mechanism.
|
||||
|
||||
The general interface that each lane must implement can be found [here](https://github.com/skip-mev/pob/blob/main/blockbuster/lane.go):
|
||||
|
||||
```go
|
||||
// Lane defines an interface used for block construction
|
||||
Lane interface {
|
||||
sdkmempool.Mempool
|
||||
|
||||
// Name returns the name of the lane.
|
||||
Name() string
|
||||
|
||||
// Match determines if a transaction belongs to this lane.
|
||||
Match(tx sdk.Tx) bool
|
||||
|
||||
// VerifyTx verifies the transaction belonging to this lane.
|
||||
VerifyTx(ctx sdk.Context, tx sdk.Tx) error
|
||||
|
||||
// Contains returns true if the mempool contains the given transaction.
|
||||
Contains(tx sdk.Tx) (bool, error)
|
||||
|
||||
// PrepareLane builds a portion of the block. It inputs the maxTxBytes that
|
||||
// can be included in the proposal for the given lane, the partial
|
||||
// proposal, and a function to call the next lane in the chain. The
|
||||
// next lane in the chain will be called with the updated proposal and context.
|
||||
PrepareLane(
|
||||
ctx sdk.Context,
|
||||
proposal BlockProposal,
|
||||
maxTxBytes int64,
|
||||
next PrepareLanesHandler,
|
||||
) (BlockProposal, error)
|
||||
|
||||
// ProcessLaneBasic validates that transactions belonging to this lane are
|
||||
// not misplaced in the block proposal.
|
||||
ProcessLaneBasic(txs []sdk.Tx) error
|
||||
|
||||
// ProcessLane verifies this lane's portion of a proposed block. It inputs
|
||||
// the transactions that may belong to this lane and a function to call
|
||||
// the next lane in the chain. The next lane in the chain will be
|
||||
// called with the updated context and filtered down transactions.
|
||||
ProcessLane(
|
||||
ctx sdk.Context,
|
||||
proposalTxs []sdk.Tx,
|
||||
next ProcessLanesHandler,
|
||||
) (sdk.Context, error)
|
||||
|
||||
// SetAnteHandler sets the lane's antehandler.
|
||||
SetAnteHandler(antehander sdk.AnteHandler)
|
||||
|
||||
// Logger returns the lane's logger.
|
||||
Logger() log.Logger
|
||||
|
||||
// GetMaxBlockSpace returns the max block space for the lane as a relative percentage.
|
||||
GetMaxBlockSpace() sdk.Dec
|
||||
}
|
||||
```
|
||||
|
||||
### 1. Intra-lane Transaction Ordering
|
||||
|
||||
**Note: Lanes must implement the `sdk.Mempool` interface.**
|
||||
|
||||
Transactions within a lane are ordered in a proposal respecting the ordering
|
||||
defined on the lane’s mempool. Developers can define their own custom ordering
|
||||
by implementing a custom `TxPriority` struct that allows the lane’s mempool to
|
||||
determine the priority of a transaction `GetTxPriority` and relatively order
|
||||
two transactions given the priority `Compare`. The top of block lane includes
|
||||
an custom `TxPriority` that orders transactions in the mempool based on their
|
||||
bid.
|
||||
|
||||
```go
|
||||
func TxPriority(config Factory) blockbuster.TxPriority[string] {
|
||||
return blockbuster.TxPriority[string]{
|
||||
GetTxPriority: func(goCtx context.Context, tx sdk.Tx) string {
|
||||
bidInfo, err := config.GetAuctionBidInfo(tx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return bidInfo.Bid.String()
|
||||
},
|
||||
Compare: func(a, b string) int {
|
||||
aCoins, _ := sdk.ParseCoinsNormalized(a)
|
||||
bCoins, _ := sdk.ParseCoinsNormalized(b)
|
||||
|
||||
switch {
|
||||
case aCoins == nil && bCoins == nil:
|
||||
return 0
|
||||
|
||||
case aCoins == nil:
|
||||
return -1
|
||||
|
||||
case bCoins == nil:
|
||||
return 1
|
||||
|
||||
default:
|
||||
switch {
|
||||
case aCoins.IsAllGT(bCoins):
|
||||
return 1
|
||||
|
||||
case aCoins.IsAllLT(bCoins):
|
||||
return -1
|
||||
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
},
|
||||
MinValue: "",
|
||||
}
|
||||
}
|
||||
|
||||
// NewMempool returns a new auction mempool.
|
||||
func NewMempool(txEncoder sdk.TxEncoder, maxTx int, config Factory) *TOBMempool {
|
||||
return &TOBMempool{
|
||||
index: blockbuster.NewPriorityMempool(
|
||||
blockbuster.PriorityNonceMempoolConfig[string]{
|
||||
TxPriority: TxPriority(config),
|
||||
MaxTx: maxTx,
|
||||
},
|
||||
),
|
||||
txEncoder: txEncoder,
|
||||
txIndex: make(map[string]struct{}),
|
||||
Factory: config,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. [Optional] Transaction Information Retrieval
|
||||
|
||||
Each lane can define a factory that configures the necessary set of interfaces
|
||||
required for transaction processing, ordering, and validation. Lanes are
|
||||
designed such that any given chain can adopt upstream `POB` lanes as long as
|
||||
developers implement the specified interface(s) associated with transaction
|
||||
information retrieval for that lane.
|
||||
|
||||
***A standard cosmos chain or EVM chain can then implement their own versions
|
||||
of these interfaces and automatically utilize the lane with no changes upstream!***
|
||||
|
||||
For example, the free lane defines an `Factory` that includes a single
|
||||
`IsFreeTx` function that allows developers to configure what is a free
|
||||
transaction. The default implementation categorizes free transactions as any
|
||||
transaction that includes a delegate type message.
|
||||
|
||||
```go
|
||||
// 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
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Lane Inclusion Functionality
|
||||
|
||||
Lanes must implement a `Match` interface which determines whether a transaction
|
||||
should be considered for a given lane. Developer’s are encouraged to utilize the
|
||||
same interfaces defined in the `Factory` to match transactions to lanes. For
|
||||
example, developers might configure a top of block auction lane to accept
|
||||
transactions if they contain a single `MsgAuctionBid` message in the transaction.
|
||||
|
||||
### 4.1. [Optional] Transaction Validation
|
||||
|
||||
Transactions will be verified the lane’s `VerifyTx` function. This logic can be
|
||||
completely arbitrary. For example, the default lane verifies transactions
|
||||
using base app’s `AnteHandler` while the top of block lane verifies transactions
|
||||
by extracting all bundled transactions included in the bid transaction and then
|
||||
verifying the transaction iteratively given the bundle.
|
||||
|
||||
### 4.2. Block Building/Verification Logic
|
||||
|
||||
Each lane will implement block building and verification logic - analogous to
|
||||
`Prepare` and `Process` proposal - that is unique to itself.
|
||||
|
||||
* `PrepareLane` will be in charge of building a partial block given the
|
||||
transactions in the lane.
|
||||
* `ProcessLaneBasic` ensures that transactions that should be included in the
|
||||
current lane are not interleaved with other lanes i.e. transactions in
|
||||
proposals are ordered respecting the ordering of lanes.
|
||||
* `ProcessLane` will be in charge of verifying the lane’s partial block.
|
||||
|
||||
### Inheritance
|
||||
|
||||
Lanes can inherit the underlying implementation of other lanes and overwrite
|
||||
any part of the implementation with their own custom functionality. We
|
||||
recommend that user’s extend the functionality of the `Base` lane when first
|
||||
exploring the code base.
|
||||
|
||||
@ -32,7 +32,7 @@ selectBidTxLoop:
|
||||
cacheCtx, write := ctx.CacheContext()
|
||||
tmpBidTx := bidTxIterator.Tx()
|
||||
|
||||
bidTxBz, _, err := utils.GetTxHashStr(l.Cfg.TxEncoder, tmpBidTx)
|
||||
bidTxBz, hash, err := utils.GetTxHashStr(l.Cfg.TxEncoder, tmpBidTx)
|
||||
if err != nil {
|
||||
txsToRemove[tmpBidTx] = struct{}{}
|
||||
continue selectBidTxLoop
|
||||
@ -47,6 +47,11 @@ selectBidTxLoop:
|
||||
if bidTxSize <= maxTxBytes {
|
||||
// Verify the bid transaction and all of its bundled transactions.
|
||||
if err := l.VerifyTx(cacheCtx, tmpBidTx); err != nil {
|
||||
l.Logger().Info(
|
||||
"failed to verify auction bid tx",
|
||||
"tx_hash", hash,
|
||||
"err", err,
|
||||
)
|
||||
txsToRemove[tmpBidTx] = struct{}{}
|
||||
continue selectBidTxLoop
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ func (l *DefaultLane) PrepareLane(
|
||||
for iterator := l.Mempool.Select(ctx, nil); iterator != nil; iterator = iterator.Next() {
|
||||
tx := iterator.Tx()
|
||||
|
||||
txBytes, _, err := utils.GetTxHashStr(l.Cfg.TxEncoder, tx)
|
||||
txBytes, hash, err := utils.GetTxHashStr(l.Cfg.TxEncoder, tx)
|
||||
if err != nil {
|
||||
txsToRemove[tx] = struct{}{}
|
||||
continue
|
||||
@ -48,6 +48,11 @@ func (l *DefaultLane) PrepareLane(
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@ -37,6 +37,10 @@ func (s *IntegrationTestSuite) TestValidBids() {
|
||||
maxBundleSize := params.MaxBundleSize
|
||||
escrowAddress := params.EscrowAccountAddress
|
||||
|
||||
// standard tx params
|
||||
gasLimit := uint64(5000000)
|
||||
fees := sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(150000)))
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
test func()
|
||||
@ -49,13 +53,13 @@ func (s *IntegrationTestSuite) TestValidBids() {
|
||||
|
||||
// Create a bundle with a single transaction
|
||||
bundle := [][]byte{
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 1, 1000),
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 1, 1000, gasLimit, fees),
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[0], bid, bundle, 0, height+1)
|
||||
bidTx := s.createAuctionBidTx(accounts[0], bid, bundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
s.displayExpectedBundle("Valid auction bid", bidTx, bundle)
|
||||
|
||||
@ -84,7 +88,7 @@ func (s *IntegrationTestSuite) TestValidBids() {
|
||||
// Create a bundle with a multiple transaction that is valid
|
||||
bundle := make([][]byte, maxBundleSize)
|
||||
for i := 0; i < int(maxBundleSize); i++ {
|
||||
bundle[i] = s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, uint64(i), 1000)
|
||||
bundle[i] = s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, uint64(i), 1000, gasLimit, fees)
|
||||
}
|
||||
|
||||
// Wait for a block to ensure all transactions are included in the same block
|
||||
@ -93,15 +97,15 @@ func (s *IntegrationTestSuite) TestValidBids() {
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[1], bid, bundle, 0, height+1)
|
||||
bidTx := s.createAuctionBidTx(accounts[1], bid, bundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
s.displayExpectedBundle("gud auction bid", bidTx, bundle)
|
||||
|
||||
// Execute a few other messages to be included in the block after the bid and bundle
|
||||
normalTxs := make([][]byte, 3)
|
||||
normalTxs[0] = s.createMsgSendTx(accounts[2], accounts[1].Address.String(), defaultSendAmount, 0, 1000)
|
||||
normalTxs[1] = s.createMsgSendTx(accounts[2], accounts[1].Address.String(), defaultSendAmount, 1, 1000)
|
||||
normalTxs[2] = s.createMsgSendTx(accounts[2], accounts[1].Address.String(), defaultSendAmount, 2, 1000)
|
||||
normalTxs[0] = s.createMsgSendTx(accounts[2], accounts[1].Address.String(), defaultSendAmount, 0, 1000, gasLimit, fees)
|
||||
normalTxs[1] = s.createMsgSendTx(accounts[2], accounts[1].Address.String(), defaultSendAmount, 1, 1000, gasLimit, fees)
|
||||
normalTxs[2] = s.createMsgSendTx(accounts[2], accounts[1].Address.String(), defaultSendAmount, 2, 1000, gasLimit, fees)
|
||||
|
||||
for _, tx := range normalTxs {
|
||||
s.broadcastTx(tx, 0)
|
||||
@ -140,7 +144,7 @@ func (s *IntegrationTestSuite) TestValidBids() {
|
||||
// Create a bundle with a multiple transaction that is valid
|
||||
bundle := make([][]byte, maxBundleSize)
|
||||
for i := 0; i < int(maxBundleSize); i++ {
|
||||
bundle[i] = s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, uint64(i), 1000)
|
||||
bundle[i] = s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, uint64(i), 1000, gasLimit, fees)
|
||||
}
|
||||
|
||||
// Wait for a block to ensure all transactions are included in the same block
|
||||
@ -149,20 +153,20 @@ func (s *IntegrationTestSuite) TestValidBids() {
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[1], bid, bundle, 0, height+1)
|
||||
bidTx := s.createAuctionBidTx(accounts[1], bid, bundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
s.displayExpectedBundle("gud auction bid 1", bidTx, bundle)
|
||||
|
||||
// Create another bid transaction that includes the bundle and is valid from the same account
|
||||
// to verify that user can bid with the same account multiple times in the same block
|
||||
bid2 := bid.Add(minBidIncrement)
|
||||
bidTx2 := s.createAuctionBidTx(accounts[1], bid2, bundle, 0, height+1)
|
||||
bidTx2 := s.createAuctionBidTx(accounts[1], bid2, bundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx2, 0)
|
||||
s.displayExpectedBundle("gud auction bid 2", bidTx2, bundle)
|
||||
|
||||
// Create a third bid
|
||||
bid3 := bid2.Add(minBidIncrement)
|
||||
bidTx3 := s.createAuctionBidTx(accounts[1], bid3, bundle, 0, height+1)
|
||||
bidTx3 := s.createAuctionBidTx(accounts[1], bid3, bundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx3, 0)
|
||||
s.displayExpectedBundle("gud auction bid 3", bidTx3, bundle)
|
||||
|
||||
@ -199,7 +203,7 @@ func (s *IntegrationTestSuite) TestValidBids() {
|
||||
// Create a bundle with a multiple transaction that is valid
|
||||
bundle := make([][]byte, maxBundleSize)
|
||||
for i := 0; i < int(maxBundleSize); i++ {
|
||||
bundle[i] = s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, uint64(i), 1000)
|
||||
bundle[i] = s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, uint64(i), 1000, gasLimit, fees)
|
||||
}
|
||||
|
||||
// Wait for a block to ensure all transactions are included in the same block
|
||||
@ -208,7 +212,7 @@ func (s *IntegrationTestSuite) TestValidBids() {
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[2], bid, bundle, 0, height+1)
|
||||
bidTx := s.createAuctionBidTx(accounts[2], bid, bundle, 0, height+1, gasLimit, fees)
|
||||
s.displayExpectedBundle("gud auction bid", bidTx, bundle)
|
||||
|
||||
// Broadcast all of the transactions in the bundle to the mempool
|
||||
@ -222,7 +226,7 @@ func (s *IntegrationTestSuite) TestValidBids() {
|
||||
// Broadcast some other transactions to the mempool
|
||||
normalTxs := make([][]byte, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
normalTxs[i] = s.createMsgSendTx(accounts[1], accounts[3].Address.String(), defaultSendAmount, uint64(i), 1000)
|
||||
normalTxs[i] = s.createMsgSendTx(accounts[1], accounts[3].Address.String(), defaultSendAmount, uint64(i), 1000, gasLimit, fees)
|
||||
s.broadcastTx(normalTxs[i], 0)
|
||||
}
|
||||
|
||||
@ -259,7 +263,7 @@ func (s *IntegrationTestSuite) TestValidBids() {
|
||||
// Create a bundle with a multiple transaction that is valid
|
||||
bundle := make([][]byte, maxBundleSize)
|
||||
for i := 0; i < int(maxBundleSize); i++ {
|
||||
bundle[i] = s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, uint64(i), 1000)
|
||||
bundle[i] = s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, uint64(i), 1000, gasLimit, fees)
|
||||
}
|
||||
|
||||
// Wait for a block to ensure all transactions are included in the same block
|
||||
@ -268,14 +272,14 @@ func (s *IntegrationTestSuite) TestValidBids() {
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[1], bid, bundle, 0, height+1)
|
||||
bidTx := s.createAuctionBidTx(accounts[1], bid, bundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
|
||||
// Execute a few other messages to be included in the block after the bid and bundle
|
||||
normalTxs := make([][]byte, 3)
|
||||
normalTxs[0] = s.createMsgSendTx(accounts[1], accounts[1].Address.String(), defaultSendAmount, 0, 1000)
|
||||
normalTxs[1] = s.createMsgSendTx(accounts[1], accounts[1].Address.String(), defaultSendAmount, 1, 1000)
|
||||
normalTxs[2] = s.createMsgSendTx(accounts[1], accounts[1].Address.String(), defaultSendAmount, 2, 1000)
|
||||
normalTxs[0] = s.createMsgSendTx(accounts[1], accounts[1].Address.String(), defaultSendAmount, 0, 1000, gasLimit, fees)
|
||||
normalTxs[1] = s.createMsgSendTx(accounts[1], accounts[1].Address.String(), defaultSendAmount, 1, 1000, gasLimit, fees)
|
||||
normalTxs[2] = s.createMsgSendTx(accounts[1], accounts[1].Address.String(), defaultSendAmount, 2, 1000, gasLimit, fees)
|
||||
|
||||
for _, tx := range normalTxs {
|
||||
s.broadcastTx(tx, 0)
|
||||
@ -343,6 +347,10 @@ func (s *IntegrationTestSuite) TestMultipleBids() {
|
||||
maxBundleSize := params.MaxBundleSize
|
||||
escrowAddress := params.EscrowAccountAddress
|
||||
|
||||
// standard tx params
|
||||
gasLimit := uint64(5000000)
|
||||
fees := sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(150000)))
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
test func()
|
||||
@ -356,22 +364,22 @@ func (s *IntegrationTestSuite) TestMultipleBids() {
|
||||
// Create a bundle with a multiple transaction that is valid
|
||||
bundle := make([][]byte, maxBundleSize)
|
||||
for i := 0; i < int(maxBundleSize); i++ {
|
||||
bundle[i] = s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, uint64(i), 1000)
|
||||
bundle[i] = s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, uint64(i), 1000, gasLimit, fees)
|
||||
}
|
||||
|
||||
bundle2 := make([][]byte, maxBundleSize)
|
||||
for i := 0; i < int(maxBundleSize); i++ {
|
||||
bundle2[i] = s.createMsgSendTx(accounts[1], accounts[0].Address.String(), defaultSendAmount, uint64(i), 1000)
|
||||
bundle2[i] = s.createMsgSendTx(accounts[1], accounts[0].Address.String(), defaultSendAmount, uint64(i), 1000, gasLimit, fees)
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[2], bid, bundle, 0, height+5)
|
||||
bidTx := s.createAuctionBidTx(accounts[2], bid, bundle, 0, height+5, gasLimit, fees)
|
||||
|
||||
// Createa a second bid transaction that includes the bundle and is valid
|
||||
bid2 := reserveFee.Add(sdk.NewCoin(app.BondDenom, sdk.NewInt(10)))
|
||||
bidTx2 := s.createAuctionBidTx(accounts[3], bid2, bundle2, 0, height+5)
|
||||
bidTx2 := s.createAuctionBidTx(accounts[3], bid2, bundle2, 0, height+5, gasLimit, fees)
|
||||
|
||||
// Wait for a block to ensure all transactions are included in the same block
|
||||
s.waitForABlock()
|
||||
@ -421,12 +429,12 @@ func (s *IntegrationTestSuite) TestMultipleBids() {
|
||||
// Create a bundle with a multiple transaction that is valid
|
||||
bundle := make([][]byte, maxBundleSize)
|
||||
for i := 0; i < int(maxBundleSize); i++ {
|
||||
bundle[i] = s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, uint64(i), 1000)
|
||||
bundle[i] = s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, uint64(i), 1000, gasLimit, fees)
|
||||
}
|
||||
|
||||
bundle2 := make([][]byte, maxBundleSize)
|
||||
for i := 0; i < int(maxBundleSize); i++ {
|
||||
bundle2[i] = s.createMsgSendTx(accounts[1], accounts[0].Address.String(), defaultSendAmount, uint64(i), 1000)
|
||||
bundle2[i] = s.createMsgSendTx(accounts[1], accounts[0].Address.String(), defaultSendAmount, uint64(i), 1000, gasLimit, fees)
|
||||
}
|
||||
|
||||
// Wait for a block to ensure all transactions are included in the same block
|
||||
@ -435,13 +443,13 @@ func (s *IntegrationTestSuite) TestMultipleBids() {
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[2], bid, bundle, 0, height+2)
|
||||
bidTx := s.createAuctionBidTx(accounts[2], bid, bundle, 0, height+2, gasLimit, fees)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
s.displayExpectedBundle("gud auction bid 1", bidTx, bundle)
|
||||
|
||||
// Create another bid transaction that includes the bundle and is valid from a different account
|
||||
bid2 := bid.Add(minBidIncrement)
|
||||
bidTx2 := s.createAuctionBidTx(accounts[3], bid2, bundle2, 0, height+1)
|
||||
bidTx2 := s.createAuctionBidTx(accounts[3], bid2, bundle2, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx2, 0)
|
||||
s.displayExpectedBundle("gud auction bid 2", bidTx2, bundle2)
|
||||
|
||||
@ -488,19 +496,19 @@ func (s *IntegrationTestSuite) TestMultipleBids() {
|
||||
|
||||
// Create a bundle with a single transaction
|
||||
bundle := [][]byte{
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 1, 1000),
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 1, 1000, gasLimit, fees),
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[0], bid, bundle, 0, height+1)
|
||||
bidTx := s.createAuctionBidTx(accounts[0], bid, bundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
s.displayExpectedBundle("bid 1", bidTx, bundle)
|
||||
|
||||
// Create a second bid transaction that includes the bundle and is valid (but smaller than the min bid increment)
|
||||
badBid := reserveFee.Add(sdk.NewInt64Coin(app.BondDenom, 10))
|
||||
bidTx2 := s.createAuctionBidTx(accounts[0], badBid, bundle, 0, height+1)
|
||||
bidTx2 := s.createAuctionBidTx(accounts[0], badBid, bundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx2, 0)
|
||||
s.displayExpectedBundle("bid 2", bidTx2, bundle)
|
||||
|
||||
@ -534,19 +542,19 @@ func (s *IntegrationTestSuite) TestMultipleBids() {
|
||||
|
||||
// Create a bundle with a single transaction
|
||||
bundle := [][]byte{
|
||||
s.createMsgSendTx(accounts[2], accounts[1].Address.String(), defaultSendAmount, 0, 1000),
|
||||
s.createMsgSendTx(accounts[2], accounts[1].Address.String(), defaultSendAmount, 0, 1000, gasLimit, fees),
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[0], bid, bundle, 0, height+1)
|
||||
bidTx := s.createAuctionBidTx(accounts[0], bid, bundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
s.displayExpectedBundle("bid 1", bidTx, bundle)
|
||||
|
||||
// Create a second bid transaction that includes the bundle and is valid (but smaller than the min bid increment)
|
||||
badBid := reserveFee.Add(sdk.NewInt64Coin(app.BondDenom, 10))
|
||||
bidTx2 := s.createAuctionBidTx(accounts[1], badBid, bundle, 0, height+1)
|
||||
bidTx2 := s.createAuctionBidTx(accounts[1], badBid, bundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx2, 0)
|
||||
s.displayExpectedBundle("bid 2", bidTx2, bundle)
|
||||
|
||||
@ -580,19 +588,19 @@ func (s *IntegrationTestSuite) TestMultipleBids() {
|
||||
|
||||
// Create a bundle with a single transaction
|
||||
bundle := [][]byte{
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 1, 1000),
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 1, 1000, gasLimit, fees),
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[0], bid, bundle, 0, height+2)
|
||||
bidTx := s.createAuctionBidTx(accounts[0], bid, bundle, 0, height+2, gasLimit, fees)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
s.displayExpectedBundle("bid 1", bidTx, bundle)
|
||||
|
||||
// Create a second bid transaction that includes the bundle and is valid
|
||||
bid2 := reserveFee.Add(minBidIncrement)
|
||||
bidTx2 := s.createAuctionBidTx(accounts[0], bid2, bundle, 0, height+1)
|
||||
bidTx2 := s.createAuctionBidTx(accounts[0], bid2, bundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx2, 0)
|
||||
s.displayExpectedBundle("bid 2", bidTx2, bundle)
|
||||
|
||||
@ -626,19 +634,19 @@ func (s *IntegrationTestSuite) TestMultipleBids() {
|
||||
|
||||
// Create a bundle with a single transaction
|
||||
bundle := [][]byte{
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 0, 1000),
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 0, 1000, gasLimit, fees),
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[2], bid, bundle, 0, height+2)
|
||||
bidTx := s.createAuctionBidTx(accounts[2], bid, bundle, 0, height+2, gasLimit, fees)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
s.displayExpectedBundle("bid 1", bidTx, bundle)
|
||||
|
||||
// Create a second bid transaction that includes the bundle and is valid
|
||||
bid2 := reserveFee.Add(minBidIncrement)
|
||||
bidTx2 := s.createAuctionBidTx(accounts[1], bid2, bundle, 0, height+1)
|
||||
bidTx2 := s.createAuctionBidTx(accounts[1], bid2, bundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx2, 0)
|
||||
s.displayExpectedBundle("bid 2", bidTx2, bundle)
|
||||
|
||||
@ -672,24 +680,24 @@ func (s *IntegrationTestSuite) TestMultipleBids() {
|
||||
|
||||
// Create a bundle with a single transaction
|
||||
firstBundle := [][]byte{
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 0, 1000),
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 0, 1000, gasLimit, fees),
|
||||
}
|
||||
|
||||
// Create a bundle with a single transaction
|
||||
secondBundle := [][]byte{
|
||||
s.createMsgSendTx(accounts[1], accounts[0].Address.String(), defaultSendAmount, 0, 1000),
|
||||
s.createMsgSendTx(accounts[1], accounts[0].Address.String(), defaultSendAmount, 0, 1000, gasLimit, fees),
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[2], bid, firstBundle, 0, height+1)
|
||||
bidTx := s.createAuctionBidTx(accounts[2], bid, firstBundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
s.displayExpectedBundle("bid 1", bidTx, firstBundle)
|
||||
|
||||
// Create a second bid transaction that includes the bundle and is valid
|
||||
bid2 := reserveFee.Add(minBidIncrement)
|
||||
bidTx2 := s.createAuctionBidTx(accounts[3], bid2, secondBundle, 0, height+1)
|
||||
bidTx2 := s.createAuctionBidTx(accounts[3], bid2, secondBundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx2, 0)
|
||||
s.displayExpectedBundle("bid 2", bidTx2, secondBundle)
|
||||
|
||||
@ -742,6 +750,10 @@ func (s *IntegrationTestSuite) TestInvalidBids() {
|
||||
maxBundleSize := params.MaxBundleSize
|
||||
escrowAddress := params.EscrowAccountAddress
|
||||
|
||||
// standard tx params
|
||||
gasLimit := uint64(5000000)
|
||||
fees := sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(150000)))
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
test func()
|
||||
@ -751,7 +763,7 @@ func (s *IntegrationTestSuite) TestInvalidBids() {
|
||||
test: func() {
|
||||
// Create a bundle with a multiple transaction that is valid
|
||||
bundle := [][]byte{
|
||||
s.createAuctionBidTx(accounts[0], reserveFee, nil, 0, 1000),
|
||||
s.createAuctionBidTx(accounts[0], reserveFee, nil, 0, 1000, gasLimit, fees),
|
||||
}
|
||||
|
||||
// Wait for a block to ensure all transactions are included in the same block
|
||||
@ -760,7 +772,7 @@ func (s *IntegrationTestSuite) TestInvalidBids() {
|
||||
// Create a bid transaction that includes the bundle
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[1], bid, bundle, 0, height+1)
|
||||
bidTx := s.createAuctionBidTx(accounts[1], bid, bundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
s.displayExpectedBundle("bad auction bid", bidTx, bundle)
|
||||
|
||||
@ -779,13 +791,13 @@ func (s *IntegrationTestSuite) TestInvalidBids() {
|
||||
test: func() {
|
||||
// Create a bundle with a single transaction that is valid
|
||||
bundle := [][]byte{
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 0, 1000),
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 0, 1000, gasLimit, fees),
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle that is attempting to bid more than their balance
|
||||
bid := sdk.NewCoin(app.BondDenom, sdk.NewInt(999999999999999999))
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[1], bid, bundle, 0, height+1)
|
||||
bidTx := s.createAuctionBidTx(accounts[1], bid, bundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
s.displayExpectedBundle("bad auction bid", bidTx, bundle)
|
||||
|
||||
@ -807,15 +819,15 @@ func (s *IntegrationTestSuite) TestInvalidBids() {
|
||||
test: func() {
|
||||
// Create a front-running bundle
|
||||
bundle := [][]byte{
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 0, 1000),
|
||||
s.createMsgSendTx(accounts[1], accounts[0].Address.String(), defaultSendAmount, 0, 1000),
|
||||
s.createMsgSendTx(accounts[2], accounts[1].Address.String(), defaultSendAmount, 0, 1000),
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 0, 1000, gasLimit, fees),
|
||||
s.createMsgSendTx(accounts[1], accounts[0].Address.String(), defaultSendAmount, 0, 1000, gasLimit, fees),
|
||||
s.createMsgSendTx(accounts[2], accounts[1].Address.String(), defaultSendAmount, 0, 1000, gasLimit, fees),
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[1], bid, bundle, 0, height+1)
|
||||
bidTx := s.createAuctionBidTx(accounts[1], bid, bundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
s.displayExpectedBundle("front-running auction bid", bidTx, bundle)
|
||||
|
||||
@ -839,13 +851,13 @@ func (s *IntegrationTestSuite) TestInvalidBids() {
|
||||
test: func() {
|
||||
// Create a bundle with a single transaction that is invalid (sequence number is wrong)
|
||||
bundle := [][]byte{
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 1000, 1000),
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 1000, 1000, gasLimit, fees),
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[1], bid, bundle, 0, height+1)
|
||||
bidTx := s.createAuctionBidTx(accounts[1], bid, bundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
s.displayExpectedBundle("invalid auction bid", bidTx, bundle)
|
||||
|
||||
@ -867,13 +879,13 @@ func (s *IntegrationTestSuite) TestInvalidBids() {
|
||||
test: func() {
|
||||
// Create a bundle with a single transaction (this should not be included in the block proposal)
|
||||
bundle := [][]byte{
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 1, 1000),
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 1, 1000, gasLimit, fees),
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes a bid that is smaller than the reserve fee
|
||||
bid := reserveFee.Sub(sdk.NewInt64Coin(app.BondDenom, 1))
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[0], bid, bundle, 0, height+1)
|
||||
bidTx := s.createAuctionBidTx(accounts[0], bid, bundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
s.displayExpectedBundle("invalid auction bid", bidTx, bundle)
|
||||
|
||||
@ -896,13 +908,13 @@ func (s *IntegrationTestSuite) TestInvalidBids() {
|
||||
// Create a bundle with too many transactions
|
||||
bundle := [][]byte{}
|
||||
for i := 0; i < int(maxBundleSize)+1; i++ {
|
||||
bundle = append(bundle, s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, uint64(i+1), 1000))
|
||||
bundle = append(bundle, s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, uint64(i+1), 1000, gasLimit, fees))
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[0], bid, bundle, 0, height+1)
|
||||
bidTx := s.createAuctionBidTx(accounts[0], bid, bundle, 0, height+1, gasLimit, fees)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
s.displayExpectedBundle("invalid auction bid", bidTx, bundle)
|
||||
|
||||
@ -924,13 +936,13 @@ func (s *IntegrationTestSuite) TestInvalidBids() {
|
||||
test: func() {
|
||||
// Create a bundle with a single transaction
|
||||
bundle := [][]byte{
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 0, 1000),
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 0, 1000, gasLimit, fees),
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle and has a bad timeout
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[0], bid, bundle, 0, height)
|
||||
bidTx := s.createAuctionBidTx(accounts[0], bid, bundle, 0, height, gasLimit, fees)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
s.displayExpectedBundle("invalid auction bid", bidTx, bundle)
|
||||
|
||||
@ -953,13 +965,13 @@ func (s *IntegrationTestSuite) TestInvalidBids() {
|
||||
// Create a bundle with multiple transactions
|
||||
bundle := make([][]byte, 3)
|
||||
for i := 0; i < 3; i++ {
|
||||
bundle[i] = s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, uint64(i), 1000)
|
||||
bundle[i] = s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, uint64(i), 1000, gasLimit, fees)
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle and is invalid
|
||||
bid := reserveFee.Sub(sdk.NewInt64Coin(app.BondDenom, 1))
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[1], bid, bundle, 0, height+1)
|
||||
bidTx := s.createAuctionBidTx(accounts[1], bid, bundle, 0, height+1, gasLimit, fees)
|
||||
s.displayExpectedBundle("invalid auction bid", bidTx, bundle)
|
||||
|
||||
// Wait for a block to ensure all transactions are included in the same block
|
||||
@ -1017,6 +1029,10 @@ func (s *IntegrationTestSuite) TestFreeLane() {
|
||||
defaultStakeAmount := sdk.NewCoin(app.BondDenom, sdk.NewInt(10))
|
||||
defaultSendAmountCoins := sdk.NewCoins(defaultSendAmount)
|
||||
|
||||
// standard tx params
|
||||
gasLimit := uint64(5000000)
|
||||
fees := sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(150000)))
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
test func()
|
||||
@ -1029,7 +1045,7 @@ func (s *IntegrationTestSuite) TestFreeLane() {
|
||||
// basic stake amount
|
||||
validators := s.queryValidators()
|
||||
validator := validators[0]
|
||||
tx := s.createMsgDelegateTx(accounts[0], validator.OperatorAddress, defaultStakeAmount, 0, 1000)
|
||||
tx := s.createMsgDelegateTx(accounts[0], validator.OperatorAddress, defaultStakeAmount, 0, 1000, gasLimit, fees)
|
||||
|
||||
// Broadcast the transaction
|
||||
s.waitForABlock()
|
||||
@ -1052,10 +1068,10 @@ func (s *IntegrationTestSuite) TestFreeLane() {
|
||||
// basic free transaction
|
||||
validators := s.queryValidators()
|
||||
validator := validators[0]
|
||||
freeTx := s.createMsgDelegateTx(accounts[0], validator.OperatorAddress, defaultStakeAmount, 0, 1000)
|
||||
freeTx := s.createMsgDelegateTx(accounts[0], validator.OperatorAddress, defaultStakeAmount, 0, 1000, gasLimit, fees)
|
||||
|
||||
// other normal transaction
|
||||
normalTx := s.createMsgSendTx(accounts[1], accounts[2].Address.String(), defaultSendAmountCoins, 0, 1000)
|
||||
normalTx := s.createMsgSendTx(accounts[1], accounts[2].Address.String(), defaultSendAmountCoins, 0, 1000, gasLimit, fees)
|
||||
|
||||
// Broadcast the transactions
|
||||
s.waitForABlock()
|
||||
@ -1093,10 +1109,10 @@ func (s *IntegrationTestSuite) TestFreeLane() {
|
||||
// basic free transaction
|
||||
validators := s.queryValidators()
|
||||
validator := validators[0]
|
||||
freeTx := s.createMsgDelegateTx(accounts[0], validator.OperatorAddress, defaultStakeAmount, 0, 1000)
|
||||
freeTx := s.createMsgDelegateTx(accounts[0], validator.OperatorAddress, defaultStakeAmount, 0, 1000, gasLimit, fees)
|
||||
|
||||
// other normal transaction
|
||||
freeTx2 := s.createMsgDelegateTx(accounts[1], validator.OperatorAddress, defaultStakeAmount, 0, 1000)
|
||||
freeTx2 := s.createMsgDelegateTx(accounts[1], validator.OperatorAddress, defaultStakeAmount, 0, 1000, gasLimit, fees)
|
||||
|
||||
// Broadcast the transactions
|
||||
s.waitForABlock()
|
||||
@ -1138,28 +1154,84 @@ func (s *IntegrationTestSuite) TestLanes() {
|
||||
params := s.queryBuilderParams()
|
||||
reserveFee := params.ReserveFee
|
||||
|
||||
// standard tx params
|
||||
gasLimit := uint64(5000000)
|
||||
fees := sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(150000)))
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
test func()
|
||||
}{
|
||||
{
|
||||
"block with tob, free, and normal tx (free tx delegates entire balances)",
|
||||
func() {
|
||||
// basic free transaction
|
||||
validators := s.queryValidators()
|
||||
validator := validators[0]
|
||||
freeTx := s.createMsgDelegateTx(accounts[0], validator.OperatorAddress, initBalance, 0, 1000, gasLimit, sdk.NewCoins())
|
||||
|
||||
// other normal transaction
|
||||
normalTx := s.createMsgSendTx(accounts[1], accounts[2].Address.String(), defaultSendAmountCoins, 0, 1000, gasLimit, fees)
|
||||
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bundle := [][]byte{
|
||||
s.createMsgSendTx(accounts[3], accounts[1].Address.String(), defaultSendAmountCoins, 0, 1000, gasLimit, fees),
|
||||
}
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[2], bid, bundle, 0, height+5, gasLimit, fees)
|
||||
s.displayExpectedBundle("Valid auction bid", bidTx, bundle)
|
||||
|
||||
// Broadcast the transactions
|
||||
s.waitForABlock()
|
||||
s.broadcastTx(freeTx, 0)
|
||||
s.broadcastTx(normalTx, 0)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
|
||||
// Wait for a block to be created
|
||||
s.waitForABlock()
|
||||
|
||||
// Ensure that the transaction was executed
|
||||
height = s.queryCurrentHeight()
|
||||
hashes := s.normalTxsToTxHashes([][]byte{
|
||||
bidTx,
|
||||
bundle[0],
|
||||
freeTx,
|
||||
normalTx,
|
||||
})
|
||||
|
||||
expectedExecution := map[string]bool{
|
||||
hashes[0]: true,
|
||||
hashes[1]: true,
|
||||
hashes[2]: true,
|
||||
hashes[3]: true,
|
||||
}
|
||||
|
||||
// Ensure that the block was built correctly
|
||||
s.verifyBlock(height, hashes, expectedExecution)
|
||||
|
||||
// Reset the balances
|
||||
accounts = s.createTestAccounts(numAccounts, initBalance)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "block with tob, free, and normal tx",
|
||||
test: func() {
|
||||
// basic free transaction
|
||||
validators := s.queryValidators()
|
||||
validator := validators[0]
|
||||
freeTx := s.createMsgDelegateTx(accounts[0], validator.OperatorAddress, defaultStakeAmount, 0, 1000)
|
||||
freeTx := s.createMsgDelegateTx(accounts[0], validator.OperatorAddress, defaultStakeAmount, 0, 1000, gasLimit, fees)
|
||||
|
||||
// other normal transaction
|
||||
normalTx := s.createMsgSendTx(accounts[1], accounts[2].Address.String(), defaultSendAmountCoins, 0, 1000)
|
||||
normalTx := s.createMsgSendTx(accounts[1], accounts[2].Address.String(), defaultSendAmountCoins, 0, 1000, gasLimit, fees)
|
||||
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bundle := [][]byte{
|
||||
s.createMsgSendTx(accounts[3], accounts[1].Address.String(), defaultSendAmountCoins, 0, 1000),
|
||||
s.createMsgSendTx(accounts[3], accounts[1].Address.String(), defaultSendAmountCoins, 0, 1000, gasLimit, fees),
|
||||
}
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[2], bid, bundle, 0, height+5)
|
||||
bidTx := s.createAuctionBidTx(accounts[2], bid, bundle, 0, height+5, gasLimit, fees)
|
||||
s.displayExpectedBundle("Valid auction bid", bidTx, bundle)
|
||||
|
||||
// Broadcast the transactions
|
||||
@ -1186,6 +1258,152 @@ func (s *IntegrationTestSuite) TestLanes() {
|
||||
hashes[3]: true,
|
||||
}
|
||||
|
||||
// Ensure that the block was built correctly
|
||||
s.verifyBlock(height, hashes, expectedExecution)
|
||||
},
|
||||
},
|
||||
{
|
||||
"failing top of block transaction, free, and normal tx",
|
||||
func() {
|
||||
// basic free transaction
|
||||
validators := s.queryValidators()
|
||||
validator := validators[0]
|
||||
freeTx := s.createMsgDelegateTx(accounts[0], validator.OperatorAddress, defaultStakeAmount, 0, 1000, gasLimit, fees)
|
||||
|
||||
// other normal transaction
|
||||
normalTx := s.createMsgSendTx(accounts[1], accounts[2].Address.String(), defaultSendAmountCoins, 0, 1000, gasLimit, fees)
|
||||
|
||||
// Create a bid transaction that includes the bundle and is invalid (out of sequence number)
|
||||
bundle := [][]byte{
|
||||
s.createMsgSendTx(accounts[3], accounts[1].Address.String(), defaultSendAmountCoins, 3, 1000, gasLimit, fees),
|
||||
}
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[2], bid, bundle, 0, height+5, gasLimit, fees)
|
||||
s.displayExpectedBundle("Valid auction bid", bidTx, bundle)
|
||||
|
||||
// Broadcast the transactions
|
||||
s.waitForABlock()
|
||||
s.broadcastTx(freeTx, 0)
|
||||
s.broadcastTx(normalTx, 0)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
|
||||
// Wait for a block to be created
|
||||
s.waitForABlock()
|
||||
height = s.queryCurrentHeight()
|
||||
|
||||
// Ensure that the transaction was executed
|
||||
hashes := s.normalTxsToTxHashes([][]byte{
|
||||
bidTx,
|
||||
bundle[0],
|
||||
freeTx,
|
||||
normalTx,
|
||||
})
|
||||
expectedExecution := map[string]bool{
|
||||
hashes[0]: false,
|
||||
hashes[1]: false,
|
||||
hashes[2]: true,
|
||||
hashes[3]: true,
|
||||
}
|
||||
|
||||
// Ensure that the block was built correctly
|
||||
s.verifyBlock(height, hashes[2:], expectedExecution)
|
||||
},
|
||||
},
|
||||
{
|
||||
"top of block transaction that includes transactions from the free lane (no fees paid)",
|
||||
func() {
|
||||
// basic free transaction
|
||||
validators := s.queryValidators()
|
||||
validator := validators[0]
|
||||
freeTx := s.createMsgDelegateTx(accounts[0], validator.OperatorAddress, defaultStakeAmount, 0, 1000, gasLimit, sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(0)))) // Free transaction with no fees
|
||||
|
||||
// Create a bid transaction that includes the bundle and is invalid (out of sequence number)
|
||||
bundle := [][]byte{
|
||||
freeTx,
|
||||
s.createMsgSendTx(accounts[1], accounts[2].Address.String(), defaultSendAmountCoins, 1, 1000, gasLimit, fees),
|
||||
}
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[1], bid, bundle, 0, height+5, gasLimit, fees)
|
||||
s.displayExpectedBundle("Valid auction bid", bidTx, bundle)
|
||||
|
||||
// Broadcast the transactions
|
||||
s.waitForABlock()
|
||||
s.broadcastTx(freeTx, 0)
|
||||
s.broadcastTx(bidTx, 0)
|
||||
|
||||
// Wait for a block to be created
|
||||
s.waitForABlock()
|
||||
height = s.queryCurrentHeight()
|
||||
|
||||
// Ensure that the transaction was executed
|
||||
hashes := s.normalTxsToTxHashes([][]byte{
|
||||
bidTx,
|
||||
freeTx,
|
||||
bundle[1],
|
||||
})
|
||||
expectedExecution := map[string]bool{
|
||||
hashes[0]: true,
|
||||
hashes[1]: true,
|
||||
hashes[2]: true,
|
||||
}
|
||||
|
||||
// Ensure that the block was built correctly
|
||||
s.verifyBlock(height, hashes, expectedExecution)
|
||||
},
|
||||
},
|
||||
{
|
||||
"top of block transaction that includes transaction from free lane + other free lane txs + normal txs",
|
||||
func() {
|
||||
// basic free transaction
|
||||
validators := s.queryValidators()
|
||||
validator := validators[0]
|
||||
freeTx := s.createMsgDelegateTx(accounts[0], validator.OperatorAddress, defaultStakeAmount, 0, 1000, gasLimit, sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(0)))) // Free transaction with no fees
|
||||
|
||||
// Another free transaction that should be included in the block
|
||||
freeTx2 := s.createMsgDelegateTx(accounts[0], validator.OperatorAddress, defaultStakeAmount, 1, 1000, gasLimit, sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(0)))) // Free transaction with no fees
|
||||
|
||||
// Create a bid transaction that includes the bundle and is invalid (out of sequence number)
|
||||
bundle := [][]byte{
|
||||
freeTx,
|
||||
s.createMsgSendTx(accounts[1], accounts[2].Address.String(), defaultSendAmountCoins, 1, 1000, gasLimit, fees),
|
||||
}
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTx := s.createAuctionBidTx(accounts[1], bid, bundle, 0, height+5, gasLimit, fees)
|
||||
s.displayExpectedBundle("Valid auction bid", bidTx, bundle)
|
||||
|
||||
normalTx := s.createMsgSendTx(accounts[3], accounts[2].Address.String(), defaultSendAmountCoins, 0, 1000, gasLimit, fees)
|
||||
|
||||
// Broadcast the transactions (including the ones in the bundle)
|
||||
s.waitForABlock()
|
||||
s.broadcastTx(bidTx, 0)
|
||||
s.broadcastTx(freeTx, 0)
|
||||
s.broadcastTx(bundle[1], 0)
|
||||
s.broadcastTx(freeTx2, 0)
|
||||
s.broadcastTx(normalTx, 0)
|
||||
|
||||
// Wait for a block to be created
|
||||
s.waitForABlock()
|
||||
height = s.queryCurrentHeight()
|
||||
|
||||
// Ensure that the transaction was executed
|
||||
hashes := s.normalTxsToTxHashes([][]byte{
|
||||
bidTx,
|
||||
freeTx,
|
||||
bundle[1],
|
||||
freeTx2,
|
||||
normalTx,
|
||||
})
|
||||
expectedExecution := map[string]bool{
|
||||
hashes[0]: true,
|
||||
hashes[1]: true,
|
||||
hashes[2]: true,
|
||||
hashes[3]: true,
|
||||
hashes[4]: true,
|
||||
}
|
||||
|
||||
// Ensure that the block was built correctly
|
||||
s.verifyBlock(height, hashes, expectedExecution)
|
||||
},
|
||||
|
||||
@ -78,7 +78,7 @@ func (s *IntegrationTestSuite) execMsgSendTx(valIdx int, to sdk.AccAddress, amou
|
||||
}
|
||||
|
||||
// createAuctionBidTx creates a transaction that bids on an auction given the provided bidder, bid, and transactions.
|
||||
func (s *IntegrationTestSuite) createAuctionBidTx(account TestAccount, bid sdk.Coin, transactions [][]byte, sequenceOffset, height uint64) []byte {
|
||||
func (s *IntegrationTestSuite) createAuctionBidTx(account TestAccount, bid sdk.Coin, transactions [][]byte, sequenceOffset, height, gasLimit uint64, fees sdk.Coins) []byte {
|
||||
msgs := []sdk.Msg{
|
||||
&buildertypes.MsgAuctionBid{
|
||||
Bidder: account.Address.String(),
|
||||
@ -87,12 +87,12 @@ func (s *IntegrationTestSuite) createAuctionBidTx(account TestAccount, bid sdk.C
|
||||
},
|
||||
}
|
||||
|
||||
return s.createTx(account, msgs, sequenceOffset, height)
|
||||
return s.createTx(account, msgs, sequenceOffset, height, gasLimit, fees)
|
||||
}
|
||||
|
||||
// createMsgSendTx creates a send transaction given the provided signer, recipient, amount, sequence number offset, and block height timeout.
|
||||
// This function is primarily used to create bundles of transactions.
|
||||
func (s *IntegrationTestSuite) createMsgSendTx(account TestAccount, toAddress string, amount sdk.Coins, sequenceOffset, height uint64) []byte {
|
||||
func (s *IntegrationTestSuite) createMsgSendTx(account TestAccount, toAddress string, amount sdk.Coins, sequenceOffset, height, gasLimit uint64, fees sdk.Coins) []byte {
|
||||
msgs := []sdk.Msg{
|
||||
&banktypes.MsgSend{
|
||||
FromAddress: account.Address.String(),
|
||||
@ -101,12 +101,12 @@ func (s *IntegrationTestSuite) createMsgSendTx(account TestAccount, toAddress st
|
||||
},
|
||||
}
|
||||
|
||||
return s.createTx(account, msgs, sequenceOffset, height)
|
||||
return s.createTx(account, msgs, sequenceOffset, height, gasLimit, fees)
|
||||
}
|
||||
|
||||
// createMsgDelegateTx creates a delegate transaction given the provided signer, validator, amount, sequence number offset
|
||||
// and block height timeout.
|
||||
func (s *IntegrationTestSuite) createMsgDelegateTx(account TestAccount, validator string, amount sdk.Coin, sequenceOffset, height uint64) []byte {
|
||||
func (s *IntegrationTestSuite) createMsgDelegateTx(account TestAccount, validator string, amount sdk.Coin, sequenceOffset, height, gasLimit uint64, fees sdk.Coins) []byte {
|
||||
msgs := []sdk.Msg{
|
||||
&stakingtypes.MsgDelegate{
|
||||
DelegatorAddress: account.Address.String(),
|
||||
@ -115,11 +115,11 @@ func (s *IntegrationTestSuite) createMsgDelegateTx(account TestAccount, validato
|
||||
},
|
||||
}
|
||||
|
||||
return s.createTx(account, msgs, sequenceOffset, height)
|
||||
return s.createTx(account, msgs, sequenceOffset, height, gasLimit, fees)
|
||||
}
|
||||
|
||||
// createTx creates a transaction given the provided messages, sequence number offset, and block height timeout.
|
||||
func (s *IntegrationTestSuite) createTx(account TestAccount, msgs []sdk.Msg, sequenceOffset, height uint64) []byte {
|
||||
func (s *IntegrationTestSuite) createTx(account TestAccount, msgs []sdk.Msg, sequenceOffset, height, gasLimit uint64, fees sdk.Coins) []byte {
|
||||
txConfig := encodingConfig.TxConfig
|
||||
txBuilder := txConfig.NewTxBuilder()
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user