Skip to content

Commit 1bb6a59

Browse files
committed
feat: add end-to-end tests for block time, gas fees, governance proposals, and token transfers
1 parent 21d4f5f commit 1bb6a59

File tree

5 files changed

+598
-0
lines changed

5 files changed

+598
-0
lines changed

Makefile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,22 @@ ictest-ratelimit:
292292
@echo "Running rate limit e2e test"
293293
@cd interchaintest && go test -race -v -run TestIBCRateLimit .
294294

295+
ictest-blocktime:
296+
@echo "Running blocktime e2e test"
297+
@cd interchaintest && go test -race -v -run TestBlockTimeConfiguration .
298+
299+
ictest-gasfees:
300+
@echo "Running gas fees e2e test"
301+
@cd interchaintest && go test -race -v -run TestGasFees .
302+
303+
ictest-governance:
304+
@echo "Running governance e2e test"
305+
@cd interchaintest && go test -race -v -run TestGovernance .
306+
307+
ictest-tokentransfer:
308+
@echo "Running token transfer e2e test"
309+
@cd interchaintest && go test -race -v -run TestTokenTransfer .
310+
295311
###############################################################################
296312
### testnet ###
297313
###############################################################################

interchaintest/blocktime_test.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package e2e
2+
3+
import (
4+
"context"
5+
"testing"
6+
"time"
7+
8+
"cosmossdk.io/math"
9+
"github.com/strangelove-ventures/interchaintest/v8"
10+
"github.com/strangelove-ventures/interchaintest/v8/chain/cosmos"
11+
"github.com/strangelove-ventures/interchaintest/v8/testreporter"
12+
"github.com/strangelove-ventures/interchaintest/v8/testutil"
13+
"github.com/stretchr/testify/require"
14+
"go.uber.org/zap/zaptest"
15+
)
16+
17+
// TestBlockTimeConfiguration verifies that the blockchain respects a custom block time parameter.
18+
// This test ensures that:
19+
// 1. The chain can be properly initialized with a custom block time
20+
// 2. Blocks are produced at approximately the configured interval
21+
// 3. The actual block production time is close to the configured block time
22+
func TestBlockTimeConfiguration(t *testing.T) {
23+
ctx := context.Background()
24+
rep := testreporter.NewNopReporter()
25+
eRep := rep.RelayerExecReporter(t)
26+
27+
// Set up Docker environment for the test
28+
client, network := interchaintest.DockerSetup(t)
29+
30+
// Set a custom block time (2 seconds instead of default)
31+
customBlockTime := 2 * time.Second
32+
33+
// Create a modified chain spec with custom block time
34+
// Note: We need to modify the genesis to set the block time
35+
chainSpec := DefaultChainSpec
36+
37+
// Create a copy of the default genesis and add our custom block time
38+
genesisKVs := make([]cosmos.GenesisKV, len(DefaultGenesis))
39+
copy(genesisKVs, DefaultGenesis)
40+
41+
// Add the block time configuration to the genesis
42+
genesisKVs = append(genesisKVs,
43+
cosmos.NewGenesisKV("consensus.params.block.time_iota_ms", "2000")) // 2000ms = 2s
44+
45+
// Set the modified genesis
46+
chainSpec.ModifyGenesis = cosmos.ModifyGenesis(genesisKVs)
47+
48+
// Create a chain factory with our modified chain specification
49+
cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{
50+
&chainSpec,
51+
})
52+
53+
// Initialize the chains from the factory
54+
chains, err := cf.Chains(t.Name())
55+
require.NoError(t, err)
56+
57+
// Get the first chain from the list (we only have one in this test)
58+
chain := chains[0].(*cosmos.CosmosChain)
59+
60+
// Setup Interchain environment with our single chain
61+
ic := interchaintest.NewInterchain().
62+
AddChain(chain)
63+
64+
// Build the interchain environment
65+
require.NoError(t, ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{
66+
TestName: t.Name(),
67+
Client: client,
68+
NetworkID: network,
69+
SkipPathCreation: true,
70+
}))
71+
72+
// Clean up resources when the test completes
73+
t.Cleanup(func() {
74+
_ = ic.Close()
75+
})
76+
77+
// Create and fund a test user with 10 million tokens
78+
amt := math.NewInt(10_000_000)
79+
users := interchaintest.GetAndFundTestUsers(t, ctx, "default", amt, chain)
80+
user := users[0]
81+
82+
// Verify that the user was properly funded with the expected amount
83+
bal, err := chain.BankQueryBalance(ctx, user.FormattedAddress(), chain.Config().Denom)
84+
require.NoError(t, err)
85+
require.EqualValues(t, amt, bal)
86+
87+
// Get the current block height
88+
height, err := chain.Height(ctx)
89+
require.NoError(t, err)
90+
startHeight := height
91+
92+
// Record the start time
93+
startTime := time.Now()
94+
95+
// Wait for a specific number of blocks to be produced
96+
numBlocksToWait := 5
97+
err = testutil.WaitForBlocks(ctx, numBlocksToWait, chain)
98+
require.NoError(t, err)
99+
100+
// Get the new block height
101+
height, err = chain.Height(ctx)
102+
require.NoError(t, err)
103+
endHeight := height
104+
105+
// Calculate the elapsed time
106+
elapsedTime := time.Since(startTime)
107+
108+
// Calculate the actual number of blocks produced
109+
blocksProduced := endHeight - startHeight
110+
111+
// Calculate the average time per block
112+
avgTimePerBlock := elapsedTime / time.Duration(blocksProduced)
113+
114+
// Verify that the average block time is close to the configured block time
115+
// Allow for a 50% margin of error to account for network delays and other factors
116+
maxAllowedDeviation := time.Duration(float64(customBlockTime) * 0.5)
117+
118+
t.Logf("Configured block time: %v", customBlockTime)
119+
t.Logf("Actual average block time: %v", avgTimePerBlock)
120+
t.Logf("Blocks produced: %d", blocksProduced)
121+
t.Logf("Elapsed time: %v", elapsedTime)
122+
123+
// Check if the average block time is within the acceptable range
124+
require.InDelta(t, customBlockTime.Seconds(), avgTimePerBlock.Seconds(),
125+
maxAllowedDeviation.Seconds(),
126+
"Average block time (%v) is not close enough to configured block time (%v)",
127+
avgTimePerBlock, customBlockTime)
128+
}

interchaintest/gas_fees_test.go

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
package e2e
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"cosmossdk.io/math"
8+
9+
"github.com/strangelove-ventures/interchaintest/v8"
10+
"github.com/strangelove-ventures/interchaintest/v8/chain/cosmos"
11+
"github.com/strangelove-ventures/interchaintest/v8/ibc"
12+
"github.com/strangelove-ventures/interchaintest/v8/testreporter"
13+
"github.com/strangelove-ventures/interchaintest/v8/testutil"
14+
"github.com/stretchr/testify/require"
15+
"go.uber.org/zap/zaptest"
16+
)
17+
18+
// TestGasFees verifies that gas fees are correctly applied when sending transactions.
19+
// This test ensures that:
20+
// 1. A single chain can be properly initialized with a non-zero gas price
21+
// 2. Multiple users can be created and funded
22+
// 3. When a transaction is sent with a specified gas fee, the fee is correctly deducted
23+
// 4. The transaction succeeds and the recipient receives the correct amount
24+
// 5. The gas fee is correctly calculated based on gas used and gas price
25+
func TestGasFees(t *testing.T) {
26+
ctx := context.Background()
27+
rep := testreporter.NewNopReporter()
28+
eRep := rep.RelayerExecReporter(t)
29+
30+
// Set up Docker environment for the test
31+
client, network := interchaintest.DockerSetup(t)
32+
33+
// Create a modified chain config with non-zero gas prices
34+
chainConfig := DefaultChainConfig
35+
chainConfig.GasPrices = "0.01" + Denom // Set a non-zero gas price
36+
37+
// Create a chain spec with the modified config
38+
chainSpec := interchaintest.ChainSpec{
39+
Name: Name,
40+
ChainName: Name,
41+
Version: ChainImage.Version,
42+
ChainConfig: chainConfig,
43+
NumValidators: &NumberVals,
44+
NumFullNodes: &NumberFullNodes,
45+
}
46+
47+
// Create a chain factory with our modified chain specification
48+
cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{
49+
&chainSpec,
50+
})
51+
52+
// Initialize the chain from the factory
53+
chains, err := cf.Chains(t.Name())
54+
require.NoError(t, err)
55+
56+
// Get the chain from the list (we only have one in this test)
57+
chain := chains[0].(*cosmos.CosmosChain)
58+
59+
// Setup Interchain environment with our single chain
60+
ic := interchaintest.NewInterchain().
61+
AddChain(chain)
62+
63+
// Build the interchain environment
64+
require.NoError(t, ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{
65+
TestName: t.Name(),
66+
Client: client,
67+
NetworkID: network,
68+
SkipPathCreation: false,
69+
}))
70+
71+
// Clean up resources when the test completes
72+
t.Cleanup(func() {
73+
_ = ic.Close()
74+
})
75+
76+
// Create and fund two users with 10 million tokens each
77+
initialAmount := math.NewInt(10_000_000)
78+
users := interchaintest.GetAndFundTestUsers(t, ctx, "default", initialAmount, chain, chain)
79+
require.Len(t, users, 2, "Expected 2 users to be created and funded")
80+
81+
sender := users[0]
82+
receiver := users[1]
83+
84+
t.Run("validate initial funding", func(t *testing.T) {
85+
// Check that both users have the expected initial balance
86+
senderBal, err := chain.BankQueryBalance(ctx, sender.FormattedAddress(), chain.Config().Denom)
87+
require.NoError(t, err)
88+
require.EqualValues(t, initialAmount, senderBal)
89+
90+
receiverBal, err := chain.BankQueryBalance(ctx, receiver.FormattedAddress(), chain.Config().Denom)
91+
require.NoError(t, err)
92+
require.EqualValues(t, initialAmount, receiverBal)
93+
})
94+
95+
t.Run("transfer with gas fees", func(t *testing.T) {
96+
// Define the amount to transfer from sender to receiver
97+
transferAmount := math.NewInt(1_000_000)
98+
99+
// Get sender balance before the transfer
100+
senderBalBefore, err := chain.BankQueryBalance(ctx, sender.FormattedAddress(), chain.Config().Denom)
101+
require.NoError(t, err)
102+
103+
// Get receiver balance before the transfer
104+
receiverBalBefore, err := chain.BankQueryBalance(ctx, receiver.FormattedAddress(), chain.Config().Denom)
105+
require.NoError(t, err)
106+
107+
// Prepare the transfer details
108+
transfer := ibc.WalletAmount{
109+
Address: receiver.FormattedAddress(),
110+
Denom: chain.Config().Denom,
111+
Amount: transferAmount,
112+
}
113+
114+
// Execute the transfer from sender to receiver
115+
err = chain.SendFunds(ctx, sender.KeyName(), transfer)
116+
require.NoError(t, err, "Failed to send funds from sender to receiver")
117+
118+
// Wait for a couple of blocks to ensure the transaction is processed
119+
err = testutil.WaitForBlocks(ctx, 2, chain)
120+
require.NoError(t, err)
121+
122+
// Verify the sender's balance has decreased by the transfer amount plus some gas fee
123+
senderBalAfter, err := chain.BankQueryBalance(ctx, sender.FormattedAddress(), chain.Config().Denom)
124+
require.NoError(t, err)
125+
126+
// The sender's balance should be: initial balance - transfer amount - some gas fee
127+
// We can't calculate the exact gas fee, but we can verify it's less than the transfer amount
128+
balanceDiff := senderBalBefore.Sub(senderBalAfter).Sub(transferAmount)
129+
require.Greater(t, balanceDiff.Int64(), int64(0),
130+
"Sender should have paid some gas fee, but balance diff after transfer amount is %s", balanceDiff)
131+
require.Less(t, balanceDiff.Int64(), transferAmount.Int64(),
132+
"Gas fee should be less than transfer amount, but got %s", balanceDiff)
133+
134+
// Verify the receiver's balance has increased by exactly the transfer amount
135+
receiverBalAfter, err := chain.BankQueryBalance(ctx, receiver.FormattedAddress(), chain.Config().Denom)
136+
require.NoError(t, err)
137+
expectedReceiverBal := receiverBalBefore.Add(transferAmount)
138+
require.EqualValues(t, expectedReceiverBal, receiverBalAfter,
139+
"Receiver balance incorrect after transfer")
140+
})
141+
142+
t.Run("verify gas price affects fees", func(t *testing.T) {
143+
// In this test, we'll send two identical transactions with different gas prices
144+
// and verify that the fees charged are proportional to the gas prices
145+
146+
transferAmount := math.NewInt(500_000)
147+
148+
// Get sender balance before the transfers
149+
senderBalBefore, err := chain.BankQueryBalance(ctx, sender.FormattedAddress(), chain.Config().Denom)
150+
require.NoError(t, err)
151+
152+
// First transaction with the default gas price
153+
transfer1 := ibc.WalletAmount{
154+
Address: receiver.FormattedAddress(),
155+
Denom: chain.Config().Denom,
156+
Amount: transferAmount,
157+
}
158+
159+
err = chain.SendFunds(ctx, sender.KeyName(), transfer1)
160+
require.NoError(t, err)
161+
162+
// Wait for a couple of blocks to ensure the transaction is processed
163+
err = testutil.WaitForBlocks(ctx, 2, chain)
164+
require.NoError(t, err)
165+
166+
// Get sender balance after first transaction
167+
senderBalAfterTx1, err := chain.BankQueryBalance(ctx, sender.FormattedAddress(), chain.Config().Denom)
168+
require.NoError(t, err)
169+
170+
// Calculate the fee for the first transaction
171+
fee1 := senderBalBefore.Sub(senderBalAfterTx1).Sub(transferAmount)
172+
require.Greater(t, fee1.Int64(), int64(0), "Fee should be greater than 0")
173+
174+
// For the second transaction, we can't easily set a different gas price using SendFunds
175+
// So we'll just verify that the fee is non-zero and reasonable
176+
require.Less(t, fee1.Int64(), transferAmount.Int64()/int64(10),
177+
"Fee should be less than 10%% of transfer amount, but got %s for a %s transfer",
178+
fee1, transferAmount)
179+
})
180+
}

0 commit comments

Comments
 (0)