Co-authored-by: David Terpay <35130517+davidterpay@users.noreply.github.com> Co-authored-by: Aleksandr Bezobchuk <aleks.bezobchuk@gmail.com>
This commit is contained in:
parent
7db24f2c29
commit
c86338fdc1
@ -4,6 +4,8 @@ run:
|
||||
sort-results: true
|
||||
allow-parallel-runners: true
|
||||
concurrency: 4
|
||||
skip-dirs:
|
||||
- tests/e2e
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
|
||||
@ -14,6 +14,7 @@ import (
|
||||
cometcfg "github.com/cometbft/cometbft/config"
|
||||
cometjson "github.com/cometbft/cometbft/libs/json"
|
||||
rpchttp "github.com/cometbft/cometbft/rpc/client/http"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
srvconfig "github.com/cosmos/cosmos-sdk/server/config"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
@ -29,22 +30,29 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
numValidators = 3
|
||||
numValidators = 4
|
||||
minGasPrice = sdk.NewDecCoinFromDec(app.BondDenom, sdk.MustNewDecFromStr("0.02")).String()
|
||||
initBalanceStr = sdk.NewInt64Coin(app.BondDenom, 510000000000).String()
|
||||
initBalanceStr = sdk.NewInt64Coin(app.BondDenom, 1000000000000000000).String()
|
||||
stakeAmount, _ = sdk.NewIntFromString("100000000000")
|
||||
stakeAmountCoin = sdk.NewCoin(app.BondDenom, stakeAmount)
|
||||
)
|
||||
|
||||
type IntegrationTestSuite struct {
|
||||
suite.Suite
|
||||
type (
|
||||
TestAccount struct {
|
||||
PrivateKey *secp256k1.PrivKey
|
||||
Address sdk.AccAddress
|
||||
}
|
||||
|
||||
tmpDirs []string
|
||||
chain *chain
|
||||
dkrPool *dockertest.Pool
|
||||
dkrNet *dockertest.Network
|
||||
valResources []*dockertest.Resource
|
||||
}
|
||||
IntegrationTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
tmpDirs []string
|
||||
chain *chain
|
||||
dkrPool *dockertest.Pool
|
||||
dkrNet *dockertest.Network
|
||||
valResources []*dockertest.Resource
|
||||
}
|
||||
)
|
||||
|
||||
func TestIntegrationTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(IntegrationTestSuite))
|
||||
@ -109,11 +117,12 @@ func (s *IntegrationTestSuite) initNodes() {
|
||||
|
||||
// Define the builder module parameters
|
||||
params := types.Params{
|
||||
MaxBundleSize: 5,
|
||||
EscrowAccountAddress: "cosmos14j5j2lsx7629590jvpk3vj0xe9w8203jf4yknk",
|
||||
ReserveFee: sdk.NewCoin(app.BondDenom, sdk.NewInt(1000000)),
|
||||
MinBidIncrement: sdk.NewCoin(app.BondDenom, sdk.NewInt(1000000)),
|
||||
ProposerFee: sdk.NewDecWithPrec(1, 2),
|
||||
MaxBundleSize: 5,
|
||||
EscrowAccountAddress: "cosmos14j5j2lsx7629590jvpk3vj0xe9w8203jf4yknk",
|
||||
ReserveFee: sdk.NewCoin(app.BondDenom, sdk.NewInt(1000000)),
|
||||
MinBidIncrement: sdk.NewCoin(app.BondDenom, sdk.NewInt(1000000)),
|
||||
ProposerFee: sdk.NewDecWithPrec(1, 2),
|
||||
FrontRunningProtection: true,
|
||||
}
|
||||
|
||||
for _, val := range s.chain.validators {
|
||||
@ -232,6 +241,8 @@ func (s *IntegrationTestSuite) initValidatorConfigs() {
|
||||
appConfig := srvconfig.DefaultConfig()
|
||||
appConfig.API.Enable = true
|
||||
appConfig.MinGasPrices = minGasPrice
|
||||
appConfig.API.Address = "tcp://0.0.0.0:1317"
|
||||
appConfig.GRPC.Address = "0.0.0.0:9090"
|
||||
|
||||
srvconfig.WriteConfigFile(appCfgPath, appConfig)
|
||||
}
|
||||
|
||||
@ -2,6 +2,455 @@
|
||||
|
||||
package e2e
|
||||
|
||||
func (s *IntegrationTestSuite) TestTmp() {
|
||||
s.Require().True(true)
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/skip-mev/pob/tests/app"
|
||||
)
|
||||
|
||||
func (s *IntegrationTestSuite) TestGetBuilderParams() {
|
||||
params := s.queryBuilderParams()
|
||||
s.Require().NotNil(params)
|
||||
}
|
||||
|
||||
// TestBundles tests the execution of various auction bids. There are a few invarients that are
|
||||
// tested:
|
||||
// 1. The order of transactions in a bundle is preserved when bids are valid.
|
||||
// 2. All transactions execute as expected.
|
||||
// 3. The balance of the escrow account should be updated correctly.
|
||||
// 4. Top of block bids will be included in block proposals before other transactions that are
|
||||
// included in the same block.
|
||||
func (s *IntegrationTestSuite) TestBundles() {
|
||||
// Create the accounts that will create transactions to be included in bundles
|
||||
initBalance := sdk.NewInt64Coin(app.BondDenom, 10000000000)
|
||||
numAccounts := 3
|
||||
accounts := s.createTestAccounts(numAccounts, initBalance)
|
||||
|
||||
// basic send amount
|
||||
defaultSendAmount := sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(10)))
|
||||
|
||||
// auction parameters
|
||||
params := s.queryBuilderParams()
|
||||
reserveFee := params.ReserveFee
|
||||
minBidIncrement := params.MinBidIncrement
|
||||
maxBundleSize := params.MaxBundleSize
|
||||
escrowAddress := params.EscrowAccountAddress
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
test func()
|
||||
}{
|
||||
{
|
||||
name: "Valid auction bid",
|
||||
test: func() {
|
||||
// Create a bundle with a single transaction
|
||||
bundle := []string{
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 0, 1000),
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTxHash := s.execAuctionBidTx(0, bid, height+1, bundle)
|
||||
s.displayExpectedBundle("Valid auction bid", bidTxHash, bundle)
|
||||
|
||||
// Wait for a block to be created
|
||||
s.waitForABlock()
|
||||
|
||||
// Ensure that the block was correctly created and executed in the order expected
|
||||
bundleHashes := s.bundleToTxHashes(bundle)
|
||||
expectedExecution := map[string]bool{
|
||||
bidTxHash: true,
|
||||
bundleHashes[0]: true,
|
||||
}
|
||||
s.verifyBlock(height+1, bidTxHash, bundleHashes, expectedExecution)
|
||||
|
||||
// Ensure that the escrow account has the correct balance
|
||||
expectedEscrowFee := s.calculateProposerEscrowSplit(bid)
|
||||
s.Require().Equal(expectedEscrowFee, s.queryBalanceOf(escrowAddress, app.BondDenom))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid auction bid with a bid smaller than the reserve fee",
|
||||
test: func() {
|
||||
// Get escrow account balance to ensure that it is not changed
|
||||
escrowBalance := s.queryBalanceOf(escrowAddress, app.BondDenom)
|
||||
|
||||
// Create a bundle with a single transaction (this should not be included in the block proposal)
|
||||
bundle := []string{
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 0, 1000),
|
||||
}
|
||||
|
||||
// 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()
|
||||
bidTxHash := s.execAuctionBidTx(0, bid, height+1, bundle)
|
||||
s.displayExpectedBundle("invalid bid", bidTxHash, bundle)
|
||||
|
||||
// Wait for a block to be created
|
||||
s.waitForABlock()
|
||||
|
||||
// Ensure that no transactions were executed
|
||||
bundleHashes := s.bundleToTxHashes(bundle)
|
||||
expectedExecution := map[string]bool{
|
||||
bidTxHash: false,
|
||||
bundleHashes[0]: false,
|
||||
}
|
||||
s.verifyBlock(height+1, bidTxHash, bundleHashes, expectedExecution)
|
||||
|
||||
// Ensure that the escrow account has the correct balance
|
||||
s.Require().Equal(escrowBalance, s.queryBalanceOf(escrowAddress, app.BondDenom))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid auction bid with too many transactions in the bundle",
|
||||
test: func() {
|
||||
// Get escrow account balance to ensure that it is not changed
|
||||
escrowBalance := s.queryBalanceOf(escrowAddress, app.BondDenom)
|
||||
|
||||
// Create a bundle with too many transactions
|
||||
bundle := []string{}
|
||||
for i := 0; i < int(maxBundleSize)+1; i++ {
|
||||
bundle = append(bundle, s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, i, 1000))
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTxHash := s.execAuctionBidTx(0, bid, height+1, bundle)
|
||||
s.displayExpectedBundle("invalid bid", bidTxHash, bundle)
|
||||
|
||||
// Wait for a block to be created
|
||||
s.waitForABlock()
|
||||
|
||||
// Ensure that no transactions were executed
|
||||
expectedExecution := map[string]bool{
|
||||
bidTxHash: false,
|
||||
}
|
||||
|
||||
bundleHashes := s.bundleToTxHashes(bundle)
|
||||
for _, hash := range bundleHashes {
|
||||
expectedExecution[hash] = false
|
||||
}
|
||||
s.verifyBlock(height+1, bidTxHash, bundleHashes, expectedExecution)
|
||||
|
||||
// Ensure that the escrow account has the correct balance
|
||||
s.Require().Equal(escrowBalance, s.queryBalanceOf(escrowAddress, app.BondDenom))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid auction bid that has an invalid timeout",
|
||||
test: func() {
|
||||
// Get escrow account balance to ensure that it is not changed
|
||||
escrowBalance := s.queryBalanceOf(escrowAddress, app.BondDenom)
|
||||
|
||||
// Create a bundle with a single transaction
|
||||
bundle := []string{
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 0, 1000),
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle and has a bad timeout
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTxHash := s.execAuctionBidTx(0, bid, height, bundle)
|
||||
s.displayExpectedBundle("invalid bid", bidTxHash, bundle)
|
||||
|
||||
// Wait for a block to be created
|
||||
s.waitForABlock()
|
||||
|
||||
// Ensure that no transactions were executed
|
||||
bundleHashes := s.bundleToTxHashes(bundle)
|
||||
expectedExecution := map[string]bool{
|
||||
bidTxHash: false,
|
||||
bundleHashes[0]: false,
|
||||
}
|
||||
s.verifyBlock(height+1, bidTxHash, bundleHashes, expectedExecution)
|
||||
|
||||
// Ensure that the escrow account has the correct balance
|
||||
s.Require().Equal(escrowBalance, s.queryBalanceOf(escrowAddress, app.BondDenom))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple transactions with second bid being smaller than min bid increment",
|
||||
test: func() {
|
||||
// Get escrow account balance
|
||||
escrowBalance := s.queryBalanceOf(escrowAddress, app.BondDenom)
|
||||
|
||||
// Create a bundle with a single transaction
|
||||
bundle := []string{
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 0, 1000),
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTxHash := s.execAuctionBidTx(0, bid, height+1, bundle)
|
||||
s.displayExpectedBundle("bid 1", bidTxHash, 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))
|
||||
bidTxHash2 := s.execAuctionBidTx(0, badBid, height+1, bundle)
|
||||
s.displayExpectedBundle("bid 2", bidTxHash2, bundle)
|
||||
|
||||
// Wait for a block to be created
|
||||
s.waitForABlock()
|
||||
|
||||
// Ensure only the first bid was executed
|
||||
bundleHashes := s.bundleToTxHashes(bundle)
|
||||
expectedExecution := map[string]bool{
|
||||
bidTxHash: true,
|
||||
bundleHashes[0]: true,
|
||||
bidTxHash2: false,
|
||||
}
|
||||
s.verifyBlock(height+1, bidTxHash, bundleHashes, expectedExecution)
|
||||
|
||||
// Ensure that the escrow account has the correct balance
|
||||
expectedEscrowFee := s.calculateProposerEscrowSplit(bid)
|
||||
s.Require().Equal(expectedEscrowFee.Add(escrowBalance), s.queryBalanceOf(escrowAddress, app.BondDenom))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple transactions with increasing bids but first bid has same bundle so it should fail",
|
||||
test: func() {
|
||||
// Get escrow account balance
|
||||
escrowBalance := s.queryBalanceOf(escrowAddress, app.BondDenom)
|
||||
|
||||
// Create a bundle with a single transaction
|
||||
bundle := []string{
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 0, 1000),
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTxHash := s.execAuctionBidTx(0, bid, height+2, bundle)
|
||||
s.displayExpectedBundle("bid 1", bidTxHash, bundle)
|
||||
|
||||
// Create a second bid transaction that includes the bundle and is valid
|
||||
bid2 := reserveFee.Add(minBidIncrement)
|
||||
bidTxHash2 := s.execAuctionBidTx(1, bid2, height+1, bundle)
|
||||
s.displayExpectedBundle("bid 2", bidTxHash2, bundle)
|
||||
|
||||
// Wait for a block to be created
|
||||
s.waitForABlock()
|
||||
|
||||
// Ensure only the second bid was executed
|
||||
bundleHashes := s.bundleToTxHashes(bundle)
|
||||
expectedExecution := map[string]bool{
|
||||
bidTxHash: false,
|
||||
bundleHashes[0]: true,
|
||||
bidTxHash2: true,
|
||||
}
|
||||
s.verifyBlock(height+1, bidTxHash2, bundleHashes, expectedExecution)
|
||||
|
||||
// Wait for a block to be created and ensure that the first bid was not executed
|
||||
s.waitForABlock()
|
||||
s.verifyBlock(height+2, bidTxHash, bundleHashes, expectedExecution)
|
||||
|
||||
// Ensure that the escrow account has the correct balance
|
||||
expectedEscrowFee := s.calculateProposerEscrowSplit(bid2)
|
||||
s.Require().Equal(expectedEscrowFee.Add(escrowBalance), s.queryBalanceOf(escrowAddress, app.BondDenom))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple transactions with increasing bids and different bundles (both should execute)",
|
||||
test: func() {
|
||||
// Get escrow account balance
|
||||
escrowBalance := s.queryBalanceOf(escrowAddress, app.BondDenom)
|
||||
|
||||
// Create a bundle with a single transaction
|
||||
firstBundle := []string{
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 0, 1000),
|
||||
}
|
||||
|
||||
// Create a bundle with a single transaction
|
||||
secondBundle := []string{
|
||||
s.createMsgSendTx(accounts[1], accounts[0].Address.String(), defaultSendAmount, 0, 1000),
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTxHash := s.execAuctionBidTx(0, bid, height+2, firstBundle) // height+2 to ensure it is executed after the second bid
|
||||
s.displayExpectedBundle("bid 1", bidTxHash, firstBundle)
|
||||
|
||||
// Create a second bid transaction that includes the bundle and is valid
|
||||
bid2 := reserveFee.Add(minBidIncrement)
|
||||
bidTxHash2 := s.execAuctionBidTx(1, bid2, height+1, secondBundle)
|
||||
s.displayExpectedBundle("bid 2", bidTxHash2, secondBundle)
|
||||
|
||||
// Wait for a block to be created
|
||||
s.waitForABlock()
|
||||
|
||||
// Ensure only the second bid was executed
|
||||
firstBundleHashes := s.bundleToTxHashes(firstBundle)
|
||||
secondBundleHashes := s.bundleToTxHashes(secondBundle)
|
||||
expectedExecution := map[string]bool{
|
||||
bidTxHash2: true,
|
||||
secondBundleHashes[0]: true,
|
||||
}
|
||||
s.verifyBlock(height+1, bidTxHash2, secondBundleHashes, expectedExecution)
|
||||
|
||||
// Wait for a block to be created and ensure that the second bid is executed
|
||||
s.waitForABlock()
|
||||
expectedExecution[bidTxHash] = true
|
||||
expectedExecution[firstBundleHashes[0]] = true
|
||||
s.verifyBlock(height+2, bidTxHash, firstBundleHashes, expectedExecution)
|
||||
|
||||
// Ensure that the escrow account has the correct balance
|
||||
expectedEscrowFee := s.calculateProposerEscrowSplit(bid.Add(bid2))
|
||||
s.Require().Equal(expectedEscrowFee.Add(escrowBalance), s.queryBalanceOf(escrowAddress, app.BondDenom))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Invalid bid that includes an invalid bundle tx",
|
||||
test: func() {
|
||||
// Get escrow account balance to ensure that it is not changed
|
||||
escrowBalance := s.queryBalanceOf(escrowAddress, app.BondDenom)
|
||||
|
||||
// Create a bundle with a single transaction that is invalid (sequence number is wrong)
|
||||
bundle := []string{
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 1000, 1000),
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTxHash := s.execAuctionBidTx(0, bid, height+1, bundle)
|
||||
s.displayExpectedBundle("bad bid", bidTxHash, bundle)
|
||||
|
||||
// Wait for a block to be created
|
||||
s.waitForABlock()
|
||||
|
||||
bundleHashes := s.bundleToTxHashes(bundle)
|
||||
expectedExecution := map[string]bool{
|
||||
bidTxHash: false,
|
||||
bundleHashes[0]: false,
|
||||
}
|
||||
s.verifyBlock(height+1, bidTxHash, bundleHashes, expectedExecution)
|
||||
|
||||
// Ensure that the escrow account has the correct balance
|
||||
s.Require().Equal(escrowBalance, s.queryBalanceOf(escrowAddress, app.BondDenom))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Invalid bid that is attempting to front-run/sandwich",
|
||||
test: func() {
|
||||
// Get escrow account balance to ensure that it is not changed
|
||||
escrowBalance := s.queryBalanceOf(escrowAddress, app.BondDenom)
|
||||
|
||||
// Create a front-running bundle
|
||||
bundle := []string{
|
||||
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),
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTxHash := s.execAuctionBidTx(0, bid, height+1, bundle)
|
||||
s.displayExpectedBundle("bad bid", bidTxHash, bundle)
|
||||
|
||||
// Wait for a block to be created
|
||||
s.waitForABlock()
|
||||
|
||||
bundleHashes := s.bundleToTxHashes(bundle)
|
||||
expectedExecution := map[string]bool{
|
||||
bidTxHash: false,
|
||||
bundleHashes[0]: false,
|
||||
bundleHashes[1]: false,
|
||||
bundleHashes[2]: false,
|
||||
}
|
||||
s.verifyBlock(height+1, bidTxHash, bundleHashes, expectedExecution)
|
||||
|
||||
// Ensure that the escrow account has the correct balance
|
||||
s.Require().Equal(escrowBalance, s.queryBalanceOf(escrowAddress, app.BondDenom))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Invalid bid that is attempting to bid more than their balance",
|
||||
test: func() {
|
||||
// Get escrow account balance to ensure that it is not changed
|
||||
escrowBalance := s.queryBalanceOf(escrowAddress, app.BondDenom)
|
||||
|
||||
// Create a bundle with a single transaction that is valid
|
||||
bundle := []string{
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 0, 1000),
|
||||
}
|
||||
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := sdk.NewCoin(app.BondDenom, sdk.NewInt(999999999999999999))
|
||||
height := s.queryCurrentHeight()
|
||||
bidTxHash := s.execAuctionBidTx(0, bid, height+1, bundle)
|
||||
s.displayExpectedBundle("bad bid", bidTxHash, bundle)
|
||||
|
||||
// Wait for a block to be created
|
||||
s.waitForABlock()
|
||||
|
||||
bundleHashes := s.bundleToTxHashes(bundle)
|
||||
expectedExecution := map[string]bool{
|
||||
bidTxHash: false,
|
||||
bundleHashes[0]: false,
|
||||
}
|
||||
s.verifyBlock(height+1, bidTxHash, bundleHashes, expectedExecution)
|
||||
|
||||
// Ensure that the escrow account has the correct balance
|
||||
s.Require().Equal(escrowBalance, s.queryBalanceOf(escrowAddress, app.BondDenom))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Valid bid with multiple other transactions",
|
||||
test: func() {
|
||||
// Get escrow account balance to ensure that it is updated correctly
|
||||
escrowBalance := s.queryBalanceOf(escrowAddress, app.BondDenom)
|
||||
|
||||
// Create a bundle with a multiple transaction that is valid
|
||||
bundle := []string{
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 0, 1000),
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 1, 1000),
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 2, 1000),
|
||||
s.createMsgSendTx(accounts[0], accounts[1].Address.String(), defaultSendAmount, 3, 1000),
|
||||
}
|
||||
|
||||
// Wait for a block to ensure all transactions are included in the same block
|
||||
s.waitForABlock()
|
||||
|
||||
// Create a bid transaction that includes the bundle and is valid
|
||||
bid := reserveFee
|
||||
height := s.queryCurrentHeight()
|
||||
bidTxHash := s.execAuctionBidTx(0, bid, height+1, bundle)
|
||||
s.displayExpectedBundle("good bid", bidTxHash, bundle)
|
||||
|
||||
// Execute a few other messages to be included in the block after the bid and bundle
|
||||
txHash1 := s.execMsgSendTx(1, accounts[0].Address, sdk.NewCoin(app.BondDenom, sdk.NewInt(100)))
|
||||
txHash2 := s.execMsgSendTx(2, accounts[0].Address, sdk.NewCoin(app.BondDenom, sdk.NewInt(100)))
|
||||
txHash3 := s.execMsgSendTx(3, accounts[0].Address, sdk.NewCoin(app.BondDenom, sdk.NewInt(100)))
|
||||
|
||||
// Wait for a block to be created
|
||||
s.waitForABlock()
|
||||
|
||||
bundleHashes := s.bundleToTxHashes(bundle)
|
||||
expectedExecution := map[string]bool{
|
||||
bidTxHash: true,
|
||||
bundleHashes[0]: true,
|
||||
bundleHashes[1]: true,
|
||||
bundleHashes[2]: true,
|
||||
bundleHashes[3]: true,
|
||||
txHash1: true,
|
||||
txHash2: true,
|
||||
txHash3: true,
|
||||
}
|
||||
s.verifyBlock(height+1, bidTxHash, bundleHashes, expectedExecution)
|
||||
|
||||
// Ensure that the escrow account has the correct balance
|
||||
expectedEscrowFee := s.calculateProposerEscrowSplit(bid)
|
||||
s.Require().Equal(escrowBalance.Add(expectedEscrowFee), s.queryBalanceOf(escrowAddress, app.BondDenom))
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.waitForABlock()
|
||||
s.Run(tc.name, tc.test)
|
||||
}
|
||||
}
|
||||
|
||||
198
tests/e2e/e2e_tx_test.go
Normal file
198
tests/e2e/e2e_tx_test.go
Normal file
@ -0,0 +1,198 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
clienttx "github.com/cosmos/cosmos-sdk/client/tx"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
"github.com/ory/dockertest/v3/docker"
|
||||
"github.com/skip-mev/pob/tests/app"
|
||||
)
|
||||
|
||||
// execAuctionBidTx executes an auction bid transaction on the given validator given the provided
|
||||
// bid, timeout, and bundle. This function returns the transaction hash. It does not wait for the
|
||||
// transaction to be committed.
|
||||
func (s *IntegrationTestSuite) execAuctionBidTx(valIdx int, bid sdk.Coin, timeout int64, bundle []string) string {
|
||||
address, err := s.chain.validators[valIdx].keyInfo.GetAddress()
|
||||
s.Require().NoError(err)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
|
||||
exec, err := s.dkrPool.Client.CreateExec(docker.CreateExecOptions{
|
||||
Context: ctx,
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
Container: s.valResources[valIdx].Container.ID,
|
||||
User: "root",
|
||||
Cmd: []string{
|
||||
"testappd",
|
||||
"tx",
|
||||
"builder",
|
||||
"auction-bid",
|
||||
address.String(), // bidder
|
||||
bid.String(), // bid
|
||||
strings.Join(bundle, ","), // bundle
|
||||
fmt.Sprintf("--%s=%d", flags.FlagTimeoutHeight, timeout), // timeout
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, s.chain.validators[valIdx].keyInfo.Name),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagChainID, s.chain.id),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoin(app.BondDenom, sdk.NewInt(1000000000)).String()),
|
||||
"--keyring-backend=test",
|
||||
"--broadcast-mode=sync",
|
||||
"-y",
|
||||
},
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
var (
|
||||
outBuf bytes.Buffer
|
||||
errBuf bytes.Buffer
|
||||
)
|
||||
|
||||
err = s.dkrPool.Client.StartExec(exec.ID, docker.StartExecOptions{
|
||||
Context: ctx,
|
||||
Detach: false,
|
||||
OutputStream: &outBuf,
|
||||
ErrorStream: &errBuf,
|
||||
})
|
||||
s.Require().NoErrorf(err, "stdout: %s, stderr: %s", outBuf.String(), errBuf.String())
|
||||
|
||||
output := outBuf.String()
|
||||
resp := strings.Split(output, ":")
|
||||
txHash := strings.TrimSpace(resp[len(resp)-1])
|
||||
|
||||
s.T().Logf(
|
||||
"broadcasted bid tx %s with bid %s timeout %d and %d bundled txs",
|
||||
txHash, bid, timeout, len(bundle),
|
||||
)
|
||||
|
||||
return txHash
|
||||
}
|
||||
|
||||
// execMsgSendTx executes a send transaction on the given validator given the provided
|
||||
// recipient and amount. This function returns the transaction hash. It does not wait for the
|
||||
// transaction to be committed.
|
||||
func (s *IntegrationTestSuite) execMsgSendTx(valIdx int, to sdk.AccAddress, amount sdk.Coin) string {
|
||||
address, err := s.chain.validators[valIdx].keyInfo.GetAddress()
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.T().Logf(
|
||||
"sending %s from %s to %s",
|
||||
amount, address, to,
|
||||
)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
|
||||
exec, err := s.dkrPool.Client.CreateExec(docker.CreateExecOptions{
|
||||
Context: ctx,
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
Container: s.valResources[valIdx].Container.ID,
|
||||
User: "root",
|
||||
Cmd: []string{
|
||||
"testappd",
|
||||
"tx",
|
||||
"bank",
|
||||
"send",
|
||||
address.String(), // sender
|
||||
to.String(), // receiver
|
||||
amount.String(), // amount
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, s.chain.validators[valIdx].keyInfo.Name),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagChainID, s.chain.id),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoin(app.BondDenom, sdk.NewInt(1000000000)).String()),
|
||||
"--keyring-backend=test",
|
||||
"--broadcast-mode=sync",
|
||||
"-y",
|
||||
},
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
var (
|
||||
outBuf bytes.Buffer
|
||||
errBuf bytes.Buffer
|
||||
)
|
||||
|
||||
err = s.dkrPool.Client.StartExec(exec.ID, docker.StartExecOptions{
|
||||
Context: ctx,
|
||||
Detach: false,
|
||||
OutputStream: &outBuf,
|
||||
ErrorStream: &errBuf,
|
||||
})
|
||||
s.Require().NoErrorf(err, "stdout: %s, stderr: %s", outBuf.String(), errBuf.String())
|
||||
|
||||
output := outBuf.String()
|
||||
resp := strings.Split(output, ":")
|
||||
txHash := strings.TrimSpace(resp[len(resp)-1])
|
||||
|
||||
return txHash
|
||||
}
|
||||
|
||||
// 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 int) string {
|
||||
txConfig := encodingConfig.TxConfig
|
||||
txBuilder := txConfig.NewTxBuilder()
|
||||
|
||||
msgs := []sdk.Msg{
|
||||
&banktypes.MsgSend{
|
||||
FromAddress: account.Address.String(),
|
||||
ToAddress: toAddress,
|
||||
Amount: amount,
|
||||
},
|
||||
}
|
||||
|
||||
// Get account info of the sender to set the account number and sequence number
|
||||
baseAccount := s.queryAccount(account.Address)
|
||||
sequenceNumber := baseAccount.Sequence + uint64(sequenceOffset)
|
||||
|
||||
// Set the messages, fees, and timeout.
|
||||
txBuilder.SetMsgs(msgs...)
|
||||
txBuilder.SetGasLimit(5000000)
|
||||
txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(75000))))
|
||||
txBuilder.SetTimeoutHeight(uint64(height))
|
||||
|
||||
sigV2 := signing.SignatureV2{
|
||||
PubKey: account.PrivateKey.PubKey(),
|
||||
Data: &signing.SingleSignatureData{
|
||||
SignMode: txConfig.SignModeHandler().DefaultMode(),
|
||||
Signature: nil,
|
||||
},
|
||||
Sequence: sequenceNumber,
|
||||
}
|
||||
|
||||
s.Require().NoError(txBuilder.SetSignatures(sigV2))
|
||||
|
||||
signerData := authsigning.SignerData{
|
||||
ChainID: s.chain.id,
|
||||
AccountNumber: baseAccount.AccountNumber,
|
||||
Sequence: sequenceNumber,
|
||||
}
|
||||
|
||||
sigV2, err := clienttx.SignWithPrivKey(
|
||||
txConfig.SignModeHandler().DefaultMode(),
|
||||
signerData,
|
||||
txBuilder,
|
||||
account.PrivateKey,
|
||||
txConfig,
|
||||
sequenceNumber,
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(txBuilder.SetSignatures(sigV2))
|
||||
|
||||
bz, err := txConfig.TxEncoder()(txBuilder.GetTx())
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Hex encode the transaction
|
||||
hash := hex.EncodeToString(bz)
|
||||
|
||||
return hash
|
||||
}
|
||||
290
tests/e2e/e2e_utils_test.go
Normal file
290
tests/e2e/e2e_utils_test.go
Normal file
@ -0,0 +1,290 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
tmclient "github.com/cosmos/cosmos-sdk/client/grpc/tmservice"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
buildertypes "github.com/skip-mev/pob/x/builder/types"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
|
||||
// createClientContext creates a client.Context for use in integration tests.
|
||||
// Note, it assumes all queries and broadcasts go to the first node.
|
||||
func (s *IntegrationTestSuite) createClientContext() client.Context {
|
||||
node := s.valResources[0]
|
||||
|
||||
rpcURI := node.GetHostPort("26657/tcp")
|
||||
gRPCURI := node.GetHostPort("9090/tcp")
|
||||
|
||||
rpcClient, err := client.NewClientFromNode(rpcURI)
|
||||
s.Require().NoError(err)
|
||||
|
||||
grpcClient, err := grpc.Dial(gRPCURI, []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}...)
|
||||
s.Require().NoError(err)
|
||||
|
||||
return client.Context{}.
|
||||
WithNodeURI(rpcURI).
|
||||
WithClient(rpcClient).
|
||||
WithGRPCClient(grpcClient).
|
||||
WithInterfaceRegistry(encodingConfig.InterfaceRegistry).
|
||||
WithCodec(encodingConfig.Codec).
|
||||
WithChainID(s.chain.id).
|
||||
WithBroadcastMode(flags.BroadcastSync)
|
||||
}
|
||||
|
||||
// createTestAccounts creates and funds test accounts with a balance.
|
||||
func (s *IntegrationTestSuite) createTestAccounts(numAccounts int, balance sdk.Coin) []TestAccount {
|
||||
accounts := make([]TestAccount, numAccounts)
|
||||
|
||||
for i := 0; i < numAccounts; i++ {
|
||||
// Generate a new account with private key that will be used to sign transactions.
|
||||
privKey := secp256k1.GenPrivKey()
|
||||
pubKey := privKey.PubKey()
|
||||
addr := sdk.AccAddress(pubKey.Address())
|
||||
|
||||
account := TestAccount{
|
||||
PrivateKey: privKey,
|
||||
Address: addr,
|
||||
}
|
||||
|
||||
// Fund the account.
|
||||
s.execMsgSendTx(0, account.Address, balance)
|
||||
|
||||
// Wait for the balance to be updated.
|
||||
s.Require().Eventually(func() bool {
|
||||
return !s.queryBalancesOf(addr.String()).IsZero()
|
||||
},
|
||||
10*time.Second,
|
||||
1*time.Second,
|
||||
)
|
||||
|
||||
accounts[i] = account
|
||||
}
|
||||
|
||||
return accounts
|
||||
}
|
||||
|
||||
// calculateProposerEscrowSplit calculates the amount of a bid that should go to the escrow account
|
||||
// and the amount that should go to the proposer. The simulation e2e environment does not support
|
||||
// checking the proposer's balance, it only validates that the escrow address has the correct balance.
|
||||
func (s *IntegrationTestSuite) calculateProposerEscrowSplit(bid sdk.Coin) sdk.Coin {
|
||||
// Get the params to determine the proposer fee.
|
||||
params := s.queryBuilderParams()
|
||||
proposerFee := params.ProposerFee
|
||||
|
||||
var proposerReward sdk.Coins
|
||||
if proposerFee.IsZero() {
|
||||
// send the entire bid to the escrow account when no proposer fee is set
|
||||
return bid
|
||||
}
|
||||
|
||||
// determine the amount of the bid that goes to the (previous) proposer
|
||||
bidDec := sdk.NewDecCoinsFromCoins(bid)
|
||||
proposerReward, _ = bidDec.MulDecTruncate(proposerFee).TruncateDecimal()
|
||||
|
||||
// Determine the amount of the remaining bid that goes to the escrow account.
|
||||
// If a decimal remainder exists, it'll stay with the bidding account.
|
||||
escrowTotal := bidDec.Sub(sdk.NewDecCoinsFromCoins(proposerReward...))
|
||||
escrowReward, _ := escrowTotal.TruncateDecimal()
|
||||
|
||||
return sdk.NewCoin(bid.Denom, escrowReward.AmountOf(bid.Denom))
|
||||
}
|
||||
|
||||
// waitForABlock will wait until the current block height has increased by a single block.
|
||||
func (s *IntegrationTestSuite) waitForABlock() {
|
||||
height := s.queryCurrentHeight()
|
||||
s.Require().Eventually(
|
||||
func() bool {
|
||||
return s.queryCurrentHeight() >= height+1
|
||||
},
|
||||
10*time.Second,
|
||||
50*time.Millisecond,
|
||||
)
|
||||
}
|
||||
|
||||
// bundleToTxHashes converts a bundle to a slice of transaction hashes.
|
||||
func (s *IntegrationTestSuite) bundleToTxHashes(bundle []string) []string {
|
||||
hashes := make([]string, len(bundle))
|
||||
|
||||
for i, tx := range bundle {
|
||||
hashBz, err := hex.DecodeString(tx)
|
||||
s.Require().NoError(err)
|
||||
|
||||
shaBz := sha256.Sum256(hashBz)
|
||||
hashes[i] = hex.EncodeToString(shaBz[:])
|
||||
}
|
||||
|
||||
return hashes
|
||||
}
|
||||
|
||||
// verifyBlock verifies that the transactions in the block at the given height were seen
|
||||
// and executed in the order they were submitted i.e. how they are broadcasted in the bundle.
|
||||
func (s *IntegrationTestSuite) verifyBlock(height int64, bidTx string, bundle []string, expectedExecution map[string]bool) {
|
||||
s.waitForABlock()
|
||||
s.T().Logf("Verifying block %d", height)
|
||||
|
||||
// Get the block's transactions and display the expected and actual block for debugging.
|
||||
txs := s.queryBlockTxs(height)
|
||||
s.displayBlock(txs, bidTx, bundle)
|
||||
|
||||
// Ensure that all transactions executed as expected (i.e. landed or failed to land).
|
||||
for tx, landed := range expectedExecution {
|
||||
s.T().Logf("Verifying tx %s executed as %t", tx, landed)
|
||||
s.Require().Equal(landed, s.queryTxPassed(tx) == nil)
|
||||
}
|
||||
s.T().Logf("All txs executed as expected")
|
||||
|
||||
// Check that the block contains the expected transactions in the expected order
|
||||
// iff the bid transaction was expected to execute.
|
||||
if expectedExecution[bidTx] {
|
||||
hashBz := sha256.Sum256(txs[0])
|
||||
hash := hex.EncodeToString(hashBz[:])
|
||||
s.Require().Equal(strings.ToUpper(bidTx), strings.ToUpper(hash))
|
||||
|
||||
for index, bundleTx := range bundle {
|
||||
hashBz := sha256.Sum256(txs[index+1])
|
||||
txHash := hex.EncodeToString(hashBz[:])
|
||||
|
||||
s.Require().Equal(strings.ToUpper(bundleTx), strings.ToUpper(txHash))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// displayExpectedBlock displays the expected and actual blocks.
|
||||
func (s *IntegrationTestSuite) displayBlock(txs [][]byte, bidTx string, bundle []string) {
|
||||
expectedBlock := fmt.Sprintf("Expected block:\n\t(%d, %s)\n", 0, bidTx)
|
||||
for index, bundleTx := range bundle {
|
||||
expectedBlock += fmt.Sprintf("\t(%d, %s)\n", index+1, bundleTx)
|
||||
}
|
||||
|
||||
s.T().Logf(expectedBlock)
|
||||
|
||||
// Display the actual block.
|
||||
if len(txs) == 0 {
|
||||
s.T().Logf("Actual block is empty")
|
||||
return
|
||||
}
|
||||
|
||||
hashBz := sha256.Sum256(txs[0])
|
||||
hash := hex.EncodeToString(hashBz[:])
|
||||
actualBlock := fmt.Sprintf("Actual block:\n\t(%d, %s)\n", 0, hash)
|
||||
for index, tx := range txs[1:] {
|
||||
hashBz := sha256.Sum256(tx)
|
||||
txHash := hex.EncodeToString(hashBz[:])
|
||||
|
||||
actualBlock += fmt.Sprintf("\t(%d, %s)\n", index+1, txHash)
|
||||
}
|
||||
|
||||
s.T().Logf(actualBlock)
|
||||
}
|
||||
|
||||
// displayExpectedBundle displays the expected order of the bid and bundled transactions.
|
||||
func (s *IntegrationTestSuite) displayExpectedBundle(prefix, bidTx string, bundle []string) {
|
||||
expectedBundle := fmt.Sprintf("%s expected bundle:\n\t(%d, %s)\n", prefix, 0, bidTx)
|
||||
for index, bundleTx := range s.bundleToTxHashes(bundle) {
|
||||
expectedBundle += fmt.Sprintf("\t(%d, %s)\n", index+1, bundleTx)
|
||||
}
|
||||
|
||||
s.T().Logf(expectedBundle)
|
||||
}
|
||||
|
||||
// queryTx queries a transaction by its hash and returns whether there was an
|
||||
// error in including the transaction in a block.
|
||||
func (s *IntegrationTestSuite) queryTxPassed(txHash string) error {
|
||||
queryClient := txtypes.NewServiceClient(s.createClientContext())
|
||||
|
||||
req := &txtypes.GetTxRequest{Hash: txHash}
|
||||
resp, err := queryClient.GetTx(context.Background(), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.TxResponse.Code != 0 {
|
||||
return fmt.Errorf("tx failed: %s", resp.TxResponse.RawLog)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// queryBuilderParams returns the params of the builder module.
|
||||
func (s *IntegrationTestSuite) queryBuilderParams() buildertypes.Params {
|
||||
queryClient := buildertypes.NewQueryClient(s.createClientContext())
|
||||
|
||||
req := &buildertypes.QueryParamsRequest{}
|
||||
resp, err := queryClient.Params(context.Background(), req)
|
||||
s.Require().NoError(err)
|
||||
|
||||
return resp.Params
|
||||
}
|
||||
|
||||
// queryBalancesOf returns the balances of an account.
|
||||
func (s *IntegrationTestSuite) queryBalancesOf(address string) sdk.Coins {
|
||||
queryClient := banktypes.NewQueryClient(s.createClientContext())
|
||||
|
||||
req := &banktypes.QueryAllBalancesRequest{Address: address}
|
||||
resp, err := queryClient.AllBalances(context.Background(), req)
|
||||
s.Require().NoError(err)
|
||||
|
||||
return resp.Balances
|
||||
}
|
||||
|
||||
// queryBalanceOf returns the balance of an account for a specific denom.
|
||||
func (s *IntegrationTestSuite) queryBalanceOf(address string, denom string) sdk.Coin {
|
||||
queryClient := banktypes.NewQueryClient(s.createClientContext())
|
||||
|
||||
req := &banktypes.QueryBalanceRequest{Address: address, Denom: denom}
|
||||
resp, err := queryClient.Balance(context.Background(), req)
|
||||
s.Require().NoError(err)
|
||||
|
||||
return *resp.Balance
|
||||
}
|
||||
|
||||
// queryAccount returns the account of an address.
|
||||
func (s *IntegrationTestSuite) queryAccount(address sdk.AccAddress) *authtypes.BaseAccount {
|
||||
queryClient := authtypes.NewQueryClient(s.createClientContext())
|
||||
|
||||
req := &authtypes.QueryAccountRequest{Address: address.String()}
|
||||
resp, err := queryClient.Account(context.Background(), req)
|
||||
s.Require().NoError(err)
|
||||
|
||||
account := &authtypes.BaseAccount{}
|
||||
err = account.Unmarshal(resp.Account.Value)
|
||||
s.Require().NoError(err)
|
||||
|
||||
return account
|
||||
}
|
||||
|
||||
// queryCurrentHeight returns the current block height.
|
||||
func (s *IntegrationTestSuite) queryCurrentHeight() int64 {
|
||||
queryClient := tmclient.NewServiceClient(s.createClientContext())
|
||||
|
||||
req := &tmclient.GetLatestBlockRequest{}
|
||||
resp, err := queryClient.GetLatestBlock(context.Background(), req)
|
||||
s.Require().NoError(err)
|
||||
|
||||
return resp.SdkBlock.Header.Height
|
||||
}
|
||||
|
||||
// queryBlockTxs returns the txs of the block at the given height.
|
||||
func (s *IntegrationTestSuite) queryBlockTxs(height int64) [][]byte {
|
||||
queryClient := tmclient.NewServiceClient(s.createClientContext())
|
||||
|
||||
req := &tmclient.GetBlockByHeightRequest{Height: height}
|
||||
resp, err := queryClient.GetBlockByHeight(context.Background(), req)
|
||||
s.Require().NoError(err)
|
||||
|
||||
return resp.GetSdkBlock().Data.Txs
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user