feat: Greedy Algorithm for Lane Verification (#236)

* greedy approach to lane verification

* docs

* base lane testing

* mev lane testing nits

* abci top level testing done

* network spamming in E2E

* removing logs from testing

* nit
This commit is contained in:
David Terpay
2023-11-27 16:43:09 -05:00
committed by GitHub
parent f607439637
commit f7dfbda2b1
21 changed files with 1213 additions and 935 deletions
@@ -64,7 +64,7 @@ var (
}
consensusParams = ictestutil.Toml{
"timeout_commit": "3500ms",
"timeout_commit": "5000ms",
}
// interchain specification
+166
View File
@@ -2,6 +2,8 @@ package integration
import (
"context"
"math/rand"
"time"
"cosmossdk.io/math"
rpctypes "github.com/cometbft/cometbft/rpc/core/types"
@@ -39,6 +41,8 @@ type IntegrationTestSuite struct {
user1, user2, user3 ibc.Wallet
// denom
denom string
// fuzzusers
fuzzusers []ibc.Wallet
// overrides for key-ring configuration of the broadcaster
broadcasterOverrides *KeyringOverride
@@ -86,6 +90,10 @@ func (s *IntegrationTestSuite) SetupSuite() {
s.user2 = interchaintest.GetAndFundTestUsers(s.T(), ctx, s.T().Name(), initBalance, s.chain)[0]
s.user3 = interchaintest.GetAndFundTestUsers(s.T(), ctx, s.T().Name(), initBalance, s.chain)[0]
for i := 0; i < 10; i++ {
s.fuzzusers = append(s.fuzzusers, interchaintest.GetAndFundTestUsers(s.T(), ctx, s.T().Name(), initBalance, s.chain)[0])
}
// create the broadcaster
s.T().Log("creating broadcaster")
s.setupBroadcaster()
@@ -1258,3 +1266,161 @@ func (s *IntegrationTestSuite) TestLanes() {
require.Equal(s.T(), user2BalanceBefore, user2BalanceAfter+delegation.Amount.Int64())
})
}
func (s *IntegrationTestSuite) TestNetwork() {
amountToTest := time.NewTicker(time.Second * 45)
defer amountToTest.Stop()
numTxs := 10
sendAmount := sdk.NewCoins(sdk.NewCoin(s.denom, math.NewInt(100)))
delegation := sdk.NewCoin(s.denom, math.NewInt(100))
validators := QueryValidators(s.T(), s.chain.(*cosmos.CosmosChain))
s.Run("can produce blocks with only default transactions", func() {
for {
select {
case <-amountToTest.C:
return
default:
height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background())
s.NoError(err)
WaitForHeight(s.T(), s.chain.(*cosmos.CosmosChain), height+1)
s.T().Logf("height: %d", height+1)
for i := 0; i < numTxs; i++ {
for _, user := range s.fuzzusers {
fee := rand.Int63n(100000)
sequenceOffset := uint64(i)
normalTx := s.CreateDummyNormalTx(user, s.user1, sendAmount, sequenceOffset, fee)
s.BroadcastTxs(context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{normalTx})
}
}
}
}
})
s.Run("can produce blocks with only free transactions", func() {
for {
select {
case <-amountToTest.C:
return
default:
height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background())
s.NoError(err)
WaitForHeight(s.T(), s.chain.(*cosmos.CosmosChain), height+1)
s.T().Logf("height: %d", height+1)
for i := 0; i < numTxs; i++ {
for _, user := range s.fuzzusers {
sequenceOffset := uint64(i)
freeTx := s.CreateDummyFreeTx(user, validators[0], delegation, sequenceOffset)
s.BroadcastTxs(context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{freeTx})
}
}
}
}
})
s.Run("can produce blocks with only MEV transactions", func() {
for {
select {
case <-amountToTest.C:
return
default:
height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background())
s.NoError(err)
WaitForHeight(s.T(), s.chain.(*cosmos.CosmosChain), height+1)
s.T().Logf("height: %d", height+1)
for i := 0; i < numTxs; i++ {
for _, user := range s.fuzzusers {
bid := rand.Int63n(1000000)
bidAmount := sdk.NewCoin(s.denom, math.NewInt(bid))
mevTx := s.CreateDummyAuctionBidTx(
height+2,
user,
bidAmount,
)
s.BroadcastTxs(context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{mevTx})
}
}
}
}
})
amountToTest.Reset(5 * time.Minute)
s.Run("can produce blocks with all types of transactions", func() {
for {
select {
case <-amountToTest.C:
return
default:
height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background())
s.NoError(err)
WaitForHeight(s.T(), s.chain.(*cosmos.CosmosChain), height+1)
s.T().Logf("height: %d", height+1)
txs := []Tx{}
for i := 0; i < numTxs; i++ {
for _, user := range s.fuzzusers[0:3] {
bid := rand.Int63n(1000000)
bidAmount := sdk.NewCoin(s.denom, math.NewInt(bid))
bidTx := s.CreateDummyAuctionBidTx(
height+2,
user,
bidAmount,
)
txs = append(txs, bidTx)
}
}
for i := 0; i < numTxs; i++ {
for _, user := range s.fuzzusers[3:6] {
sequenceOffset := uint64(i)
freeTx := s.CreateDummyFreeTx(user, validators[0], delegation, sequenceOffset)
txs = append(txs, freeTx)
}
}
for i := 0; i < numTxs; i++ {
for _, user := range s.fuzzusers[6:10] {
fee := rand.Int63n(100000)
sequenceOffset := uint64(i)
normalTx := s.CreateDummyNormalTx(user, s.user1, sendAmount, sequenceOffset, fee)
txs = append(txs, normalTx)
}
}
s.BroadcastTxs(context.Background(), s.chain.(*cosmos.CosmosChain), txs)
}
}
})
// Wait for 1 minute for the network to stabilize
amountToTest.Reset(1 * time.Minute)
s.Run("can produce empty blocks", func() {
for {
select {
case <-amountToTest.C:
return
default:
height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background())
s.NoError(err)
WaitForHeight(s.T(), s.chain.(*cosmos.CosmosChain), height+1)
s.T().Logf("height: %d", height+1)
}
}
})
}
+74 -21
View File
@@ -24,6 +24,7 @@ import (
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
interchaintest "github.com/strangelove-ventures/interchaintest/v8"
"github.com/strangelove-ventures/interchaintest/v8/chain/cosmos"
@@ -124,6 +125,71 @@ func (s *IntegrationTestSuite) CreateTx(ctx context.Context, chain *cosmos.Cosmo
return bz
}
func (s *IntegrationTestSuite) CreateDummyAuctionBidTx(
height uint64,
searcher ibc.Wallet,
bid sdk.Coin,
) Tx {
msgAuctionBid := auctiontypes.NewMsgAuctionBid(
searcher.Address(),
bid,
nil,
)
return Tx{
User: searcher,
Msgs: []sdk.Msg{msgAuctionBid},
GasPrice: 1000,
Height: height + 1,
SkipInclusionCheck: true,
IgnoreChecks: true,
}
}
func (s *IntegrationTestSuite) CreateDummyNormalTx(
from, to ibc.Wallet,
coins sdk.Coins,
sequenceOffset uint64,
gasPrice int64,
) Tx {
msgSend := banktypes.NewMsgSend(
sdk.AccAddress(from.Address()),
sdk.AccAddress(to.Address()),
coins,
)
return Tx{
User: from,
Msgs: []sdk.Msg{msgSend},
GasPrice: gasPrice,
SequenceIncrement: sequenceOffset,
SkipInclusionCheck: true,
IgnoreChecks: true,
}
}
func (s *IntegrationTestSuite) CreateDummyFreeTx(
user ibc.Wallet,
validator sdk.ValAddress,
delegation sdk.Coin,
sequenceOffset uint64,
) Tx {
delegateMsg := stakingtypes.NewMsgDelegate(
sdk.AccAddress(user.Address()).String(),
sdk.ValAddress(validator).String(),
delegation,
)
return Tx{
User: user,
Msgs: []sdk.Msg{delegateMsg},
GasPrice: 1000,
SequenceIncrement: sequenceOffset,
SkipInclusionCheck: true,
IgnoreChecks: true,
}
}
// SimulateTx simulates the provided messages, and checks whether the provided failure condition is met
func (s *IntegrationTestSuite) SimulateTx(ctx context.Context, chain *cosmos.CosmosChain, user cosmos.User, height uint64, expectFail bool, msgs ...sdk.Msg) {
// create tx factory + Client Context
@@ -154,6 +220,7 @@ type Tx struct {
Height uint64
SkipInclusionCheck bool
ExpectFail bool
IgnoreChecks bool
}
// CreateAuctionBidMsg creates a new AuctionBid tx signed by the given user, the order of txs in the MsgAuctionBid will be determined by the contents + order of the MessageForUsers
@@ -203,13 +270,13 @@ func (s *IntegrationTestSuite) BroadcastTxsWithCallback(
s.Require().True(len(chain.Nodes()) > 0)
client := chain.Nodes()[0].Client
statusResp, err := client.Status(context.Background())
s.Require().NoError(err)
s.T().Logf("broadcasting transactions at latest height of %d", statusResp.SyncInfo.LatestBlockHeight)
for i, tx := range rawTxs {
// broadcast tx
if txs[i].IgnoreChecks {
client.BroadcastTxAsync(ctx, tx)
continue
}
resp, err := client.BroadcastTxSync(ctx, tx)
// check execution was successful
@@ -228,7 +295,7 @@ func (s *IntegrationTestSuite) BroadcastTxsWithCallback(
eg := errgroup.Group{}
for i, tx := range rawTxs {
// if we don't expect this tx to be included.. skip it
if txs[i].SkipInclusionCheck || txs[i].ExpectFail {
if txs[i].SkipInclusionCheck || txs[i].ExpectFail || txs[i].IgnoreChecks {
continue
}
@@ -356,25 +423,11 @@ func WaitForHeight(t *testing.T, chain *cosmos.CosmosChain, height uint64) {
require.NoError(t, err)
}
// VerifyBlock takes a Block and verifies that it contains the given bid at the 0-th index, and the bundled txs immediately after
func VerifyBlock(t *testing.T, block *rpctypes.ResultBlock, offset int, bidTxHash string, txs [][]byte) {
// verify the block
if bidTxHash != "" {
require.Equal(t, bidTxHash, TxHash(block.Block.Data.Txs[offset+1]))
offset += 1
}
// verify the txs in sequence
for i, tx := range txs {
require.Equal(t, TxHash(tx), TxHash(block.Block.Data.Txs[i+offset+1]))
}
}
// VerifyBlockWithExpectedBlock takes in a list of raw tx bytes and compares each tx hash to the tx hashes in the block.
// The expected block is the block that should be returned by the chain at the given height.
func VerifyBlockWithExpectedBlock(t *testing.T, chain *cosmos.CosmosChain, height uint64, txs [][]byte) {
block := Block(t, chain, int64(height))
blockTxs := block.Block.Data.Txs[1:]
blockTxs := block.Block.Data.Txs
t.Logf("verifying block %d", height)
require.Equal(t, len(txs), len(blockTxs))