diff --git a/chain/cosmos/chain_node.go b/chain/cosmos/chain_node.go index 9c6bb9d63..ea949d75f 100644 --- a/chain/cosmos/chain_node.go +++ b/chain/cosmos/chain_node.go @@ -674,7 +674,7 @@ func (tn *ChainNode) SendIBCTransfer( ) (string, error) { command := []string{ "ibc-transfer", "transfer", "transfer", channelID, - amount.Address, fmt.Sprintf("%d%s", amount.Amount, amount.Denom), + amount.Address, fmt.Sprintf("%s%s", amount.Amount.String(), amount.Denom), } if options.Timeout != nil { if options.Timeout.NanoSeconds > 0 { @@ -692,7 +692,7 @@ func (tn *ChainNode) SendIBCTransfer( func (tn *ChainNode) SendFunds(ctx context.Context, keyName string, amount ibc.WalletAmount) error { _, err := tn.ExecTx(ctx, keyName, "bank", "send", keyName, - amount.Address, fmt.Sprintf("%d%s", amount.Amount, amount.Denom), + amount.Address, fmt.Sprintf("%s%s", amount.Amount.String(), amount.Denom), ) return err } @@ -1097,7 +1097,7 @@ func (tn *ChainNode) SendICABankTransfer(ctx context.Context, connectionID, from "amount": []map[string]any{ { "denom": amount.Denom, - "amount": amount.Amount, + "amount": amount.Amount.String(), }, }, }) diff --git a/chain/cosmos/cosmos_chain.go b/chain/cosmos/cosmos_chain.go index e686e9d99..b610ed08b 100644 --- a/chain/cosmos/cosmos_chain.go +++ b/chain/cosmos/cosmos_chain.go @@ -10,6 +10,7 @@ import ( "strings" "sync" + "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" @@ -411,12 +412,12 @@ func (c *CosmosChain) ExportState(ctx context.Context, height int64) (string, er // GetBalance fetches the current balance for a specific account address and denom. // Implements Chain interface -func (c *CosmosChain) GetBalance(ctx context.Context, address string, denom string) (int64, error) { +func (c *CosmosChain) GetBalance(ctx context.Context, address string, denom string) (math.Int, error) { params := &bankTypes.QueryBalanceRequest{Address: address, Denom: denom} grpcAddress := c.getFullNode().hostGRPCPort conn, err := grpc.Dial(grpcAddress, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { - return 0, err + return math.Int{}, err } defer conn.Close() @@ -424,10 +425,10 @@ func (c *CosmosChain) GetBalance(ctx context.Context, address string, denom stri res, err := queryClient.Balance(ctx, params) if err != nil { - return 0, err + return math.Int{}, err } - return res.Balance.Amount.Int64(), nil + return res.Balance.Amount, nil } // AllBalances fetches an account address's balance for all denoms it holds @@ -719,7 +720,7 @@ func (c *CosmosChain) Start(testName string, ctx context.Context, additionalGene } for _, wallet := range additionalGenesisWallets { - if err := validator0.AddGenesisAccount(ctx, wallet.Address, []types.Coin{{Denom: wallet.Denom, Amount: types.NewInt(wallet.Amount)}}); err != nil { + if err := validator0.AddGenesisAccount(ctx, wallet.Address, []types.Coin{{Denom: wallet.Denom, Amount: wallet.Amount}}); err != nil { return err } } diff --git a/chain/cosmos/poll.go b/chain/cosmos/poll.go index 0aecf53cf..6ae004333 100644 --- a/chain/cosmos/poll.go +++ b/chain/cosmos/poll.go @@ -71,8 +71,8 @@ func PollForBalance(ctx context.Context, chain *CosmosChain, deltaBlocks uint64, if err != nil { return nil, err } - if bal != balance.Amount { - return nil, fmt.Errorf("balance (%d) does not match expected: (%d)", bal, balance.Amount) + if !balance.Amount.Equal(bal) { + return nil, fmt.Errorf("balance (%s) does not match expected: (%s)", bal.String(), balance.Amount.String()) } return nil, nil } diff --git a/chain/penumbra/penumbra_app_node.go b/chain/penumbra/penumbra_app_node.go index b8830eedd..12687b31c 100644 --- a/chain/penumbra/penumbra_app_node.go +++ b/chain/penumbra/penumbra_app_node.go @@ -138,7 +138,7 @@ func (p *PenumbraAppNode) GenerateGenesisFile( } allocationsCsv := []byte(`"amount","denom","address"\n`) for _, allocation := range allocations { - allocationsCsv = append(allocationsCsv, []byte(fmt.Sprintf(`"%d","%s","%s"\n`, allocation.Amount, allocation.Denom, allocation.Address))...) + allocationsCsv = append(allocationsCsv, []byte(fmt.Sprintf(`"%s","%s","%s"\n`, allocation.Amount.String(), allocation.Denom, allocation.Address))...) } if err := fw.WriteFile(ctx, p.VolumeName, "allocations.csv", allocationsCsv); err != nil { return fmt.Errorf("error writing allocations to file: %w", err) diff --git a/chain/penumbra/penumbra_chain.go b/chain/penumbra/penumbra_chain.go index 1b06c84b9..cdfe6a0b5 100644 --- a/chain/penumbra/penumbra_chain.go +++ b/chain/penumbra/penumbra_chain.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" + "cosmossdk.io/math" "github.com/BurntSushi/toml" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -67,9 +68,9 @@ type PenumbraValidatorFundingStream struct { } type PenumbraGenesisAppStateAllocation struct { - Amount int64 `json:"amount"` - Denom string `json:"denom"` - Address string `json:"address"` + Amount math.Int `json:"amount"` + Denom string `json:"denom"` + Address string `json:"address"` } func NewPenumbraChain(log *zap.Logger, testName string, chainConfig ibc.ChainConfig, numValidators int, numFullNodes int) *PenumbraChain { @@ -235,7 +236,7 @@ func (c *PenumbraChain) Height(ctx context.Context) (uint64, error) { } // Implements Chain interface -func (c *PenumbraChain) GetBalance(ctx context.Context, address string, denom string) (int64, error) { +func (c *PenumbraChain) GetBalance(ctx context.Context, address string, denom string) (math.Int, error) { panic("implement me") } @@ -433,13 +434,13 @@ func (c *PenumbraChain) Start(testName string, ctx context.Context, additionalGe // self delegation allocations[2*i] = PenumbraGenesisAppStateAllocation{ - Amount: 100_000_000_000, + Amount: math.NewInt(100_000_000_000), Denom: fmt.Sprintf("udelegation_%s", validatorTemplateDefinition.IdentityKey), Address: validatorTemplateDefinition.FundingStreams[0].Address, } // liquid allocations[2*i+1] = PenumbraGenesisAppStateAllocation{ - Amount: 1_000_000_000_000, + Amount: math.NewInt(1_000_000_000_000), Denom: chainCfg.Denom, Address: validatorTemplateDefinition.FundingStreams[0].Address, } diff --git a/chain/polkadot/parachain_node.go b/chain/polkadot/parachain_node.go index d0b478997..04fb5d6c5 100644 --- a/chain/polkadot/parachain_node.go +++ b/chain/polkadot/parachain_node.go @@ -6,9 +6,9 @@ import ( "encoding/json" "fmt" "path/filepath" - "strconv" "strings" + "cosmossdk.io/math" "github.com/avast/retry-go/v4" gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" sdktypes "github.com/cosmos/cosmos-sdk/types" @@ -152,7 +152,7 @@ func (pn *ParachainNode) GenerateParachainGenesisFile(ctx context.Context, addit for _, wallet := range additionalGenesisWallets { balances = append(balances, - []interface{}{wallet.Address, wallet.Amount * parachainScaling}, + []interface{}{wallet.Address, wallet.Amount.MulRaw(parachainScaling)}, ) } if err := dyno.Set(chainSpec, balances, "genesis", "runtime", "balances", "balances"); err != nil { @@ -305,7 +305,7 @@ func (pn *ParachainNode) Exec(ctx context.Context, cmd []string, env []string) d return job.Run(ctx, cmd, opts) } -func (pn *ParachainNode) GetBalance(ctx context.Context, address string, denom string) (int64, error) { +func (pn *ParachainNode) GetBalance(ctx context.Context, address string, denom string) (math.Int, error) { return GetBalance(pn.api, address) } @@ -331,7 +331,7 @@ func (pn *ParachainNode) SendFunds(ctx context.Context, keyName string, amount i "ParachainNode SendFunds", zap.String("From", kp.Address), zap.String("To", amount.Address), - zap.String("Amount", strconv.FormatInt(amount.Amount, 10)), + zap.String("Amount", amount.Amount.String()), ) hash, err := SendFundsTx(pn.api, kp, amount) if err != nil { @@ -359,7 +359,7 @@ func (pn *ParachainNode) SendIbcFunds( "ParachainNode SendIbcFunds", zap.String("From", kp.Address), zap.String("To", amount.Address), - zap.String("Amount", strconv.FormatInt(amount.Amount, 10)), + zap.String("Amount", amount.Amount.String()), ) hash, err := SendIbcFundsTx(pn.api, kp, channelID, amount, options) if err != nil { @@ -385,7 +385,7 @@ func (pn *ParachainNode) MintFunds( "ParachainNode MintFunds", zap.String("From", kp.Address), zap.String("To", amount.Address), - zap.String("Amount", strconv.FormatInt(amount.Amount, 10)), + zap.String("Amount", amount.Amount.String()), ) hash, err := MintFundsTx(pn.api, kp, amount) if err != nil { diff --git a/chain/polkadot/polkadot_chain.go b/chain/polkadot/polkadot_chain.go index 39fdc8f77..24029ed39 100644 --- a/chain/polkadot/polkadot_chain.go +++ b/chain/polkadot/polkadot_chain.go @@ -9,6 +9,7 @@ import ( "io" "strings" + "cosmossdk.io/math" "github.com/99designs/keyring" "github.com/StirlingMarketingGroup/go-namecase" "github.com/centrifuge/go-substrate-rpc-client/v4/signature" @@ -340,7 +341,7 @@ func (c *PolkadotChain) modifyRelayChainGenesis(ctx context.Context, chainSpec i } for _, wallet := range additionalGenesisWallets { balances = append(balances, - []interface{}{wallet.Address, wallet.Amount * polkadotScaling}, + []interface{}{wallet.Address, wallet.Amount.MulRaw(polkadotScaling)}, ) } @@ -778,7 +779,7 @@ func (c *PolkadotChain) SendIBCTransfer( // GetBalance fetches the current balance for a specific account address and denom. // Implements Chain interface. -func (c *PolkadotChain) GetBalance(ctx context.Context, address string, denom string) (int64, error) { +func (c *PolkadotChain) GetBalance(ctx context.Context, address string, denom string) (math.Int, error) { // If denom == polkadot denom, it is a relay chain query, else parachain query if denom == c.cfg.Denom { return c.RelayChainNodes[0].GetBalance(ctx, address, denom) diff --git a/chain/polkadot/query.go b/chain/polkadot/query.go index 56eea38ce..97278c0fe 100644 --- a/chain/polkadot/query.go +++ b/chain/polkadot/query.go @@ -1,33 +1,39 @@ package polkadot import ( +<<<<<<< HEAD gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" gstypes "github.com/centrifuge/go-substrate-rpc-client/v4/types" +======= + "cosmossdk.io/math" + gsrpc "github.com/misko9/go-substrate-rpc-client/v4" + gstypes "github.com/misko9/go-substrate-rpc-client/v4/types" +>>>>>>> 8e02aef (refactor: use cosmos sdk Int type for balances/token amounts (#679)) ) // GetBalance fetches the current balance for a specific account address using the SubstrateAPI -func GetBalance(api *gsrpc.SubstrateAPI, address string) (int64, error) { +func GetBalance(api *gsrpc.SubstrateAPI, address string) (math.Int, error) { meta, err := api.RPC.State.GetMetadataLatest() if err != nil { - return -1, err + return math.Int{}, err } pubKey, err := DecodeAddressSS58(address) if err != nil { - return -2, err + return math.Int{}, err } key, err := gstypes.CreateStorageKey(meta, "System", "Account", pubKey, nil) if err != nil { - return -3, err + return math.Int{}, err } var accountInfo AccountInfo ok, err := api.RPC.State.GetStorageLatest(key, &accountInfo) if err != nil { - return -4, err + return math.Int{}, err } if !ok { - return -5, nil + return math.Int{}, nil } - return accountInfo.Data.Free.Int64(), nil + return math.NewIntFromBigInt(accountInfo.Data.Free.Int), nil } diff --git a/chain/polkadot/relay_chain_node.go b/chain/polkadot/relay_chain_node.go index 6a65eeba3..b3530c451 100644 --- a/chain/polkadot/relay_chain_node.go +++ b/chain/polkadot/relay_chain_node.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "cosmossdk.io/math" "github.com/avast/retry-go/v4" gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" "github.com/docker/docker/client" @@ -294,6 +295,6 @@ func (p *RelayChainNode) SendFunds(ctx context.Context, keyName string, amount i // GetBalance fetches the current balance for a specific account address and denom. // Implements Chain interface. -func (p *RelayChainNode) GetBalance(ctx context.Context, address string, denom string) (int64, error) { +func (p *RelayChainNode) GetBalance(ctx context.Context, address string, denom string) (math.Int, error) { return GetBalance(p.api, address) } diff --git a/chain/polkadot/tx.go b/chain/polkadot/tx.go index 3b76bc2b6..b1c7ce1de 100644 --- a/chain/polkadot/tx.go +++ b/chain/polkadot/tx.go @@ -29,7 +29,7 @@ func SendFundsTx(api *gsrpc.SubstrateAPI, senderKeypair signature.KeyringPair, a return hash, err } - call, err := gstypes.NewCall(meta, "Balances.transfer", receiver, gstypes.NewUCompactFromUInt(uint64(amount.Amount))) + call, err := gstypes.NewCall(meta, "Balances.transfer", receiver, gstypes.NewUCompact(amount.Amount.BigInt())) if err != nil { return hash, err } @@ -85,7 +85,7 @@ func SendIbcFundsTx( timestamp := gstypes.NewOptionU64(gstypes.NewU64(0)) height := gstypes.NewOptionU64(gstypes.NewU64(3000)) // Must set timestamp or height assetId := gstypes.NewU128(*big.NewInt(assetNum)) - amount2 := gstypes.NewU128(*big.NewInt(amount.Amount)) + amount2 := gstypes.NewU128(*amount.Amount.BigInt()) memo := gstypes.NewU8(0) call, err := gstypes.NewCall(meta, "Ibc.transfer", raw, size, to, channel, timeout, timestamp, height, assetId, amount2, memo) @@ -124,7 +124,7 @@ func MintFundsTx( } assetId := gstypes.NewU128(*big.NewInt(assetNum)) - amount2 := gstypes.NewUCompactFromUInt(uint64(amount.Amount)) + amount2 := gstypes.NewUCompact(amount.Amount.BigInt()) call, err := gstypes.NewCall(meta, "Assets.mint", assetId, receiver, amount2) if err != nil { diff --git a/conformance/flush.go b/conformance/flush.go index ca282c73b..19cbbccfd 100644 --- a/conformance/flush.go +++ b/conformance/flush.go @@ -5,12 +5,21 @@ import ( "fmt" "testing" + "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/types" +<<<<<<< HEAD interchaintest "github.com/strangelove-ventures/interchaintest/v6" "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/relayer" "github.com/strangelove-ventures/interchaintest/v6/testreporter" "github.com/strangelove-ventures/interchaintest/v6/testutil" +======= + "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/relayer" + "github.com/strangelove-ventures/interchaintest/v7/testreporter" + "github.com/strangelove-ventures/interchaintest/v7/testutil" +>>>>>>> 8e02aef (refactor: use cosmos sdk Int type for balances/token amounts (#679)) "github.com/stretchr/testify/require" ) @@ -77,7 +86,7 @@ func TestRelayerFlushing(t *testing.T, ctx context.Context, cf interchaintest.Ch tx, err := c0.SendIBCTransfer(ctx, c0ChannelID, interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ Address: c1FaucetAddr, Denom: c0.Config().Denom, - Amount: txAmount, + Amount: math.NewInt(txAmount), }, ibc.TransferOptions{}) req.NoError(err) req.NoError(tx.Validate()) diff --git a/conformance/test.go b/conformance/test.go index a77dab57c..06f152016 100644 --- a/conformance/test.go +++ b/conformance/test.go @@ -35,6 +35,7 @@ import ( "testing" "time" +<<<<<<< HEAD transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" "github.com/docker/docker/client" interchaintest "github.com/strangelove-ventures/interchaintest/v6" @@ -44,6 +45,18 @@ import ( "github.com/strangelove-ventures/interchaintest/v6/relayer" "github.com/strangelove-ventures/interchaintest/v6/testreporter" "github.com/strangelove-ventures/interchaintest/v6/testutil" +======= + "cosmossdk.io/math" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + "github.com/docker/docker/client" + "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/internal/dockerutil" + "github.com/strangelove-ventures/interchaintest/v7/relayer" + "github.com/strangelove-ventures/interchaintest/v7/testreporter" + "github.com/strangelove-ventures/interchaintest/v7/testutil" +>>>>>>> 8e02aef (refactor: use cosmos sdk Int type for balances/token amounts (#679)) "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" ) @@ -145,12 +158,12 @@ func sendIBCTransfersFromBothChainsWithTimeout( testCoinSrcToDst := ibc.WalletAmount{ Address: srcUser.(*cosmos.CosmosWallet).FormattedAddressWithPrefix(dstChainCfg.Bech32Prefix), Denom: srcChainCfg.Denom, - Amount: testCoinAmount, + Amount: math.NewInt(testCoinAmount), } testCoinDstToSrc := ibc.WalletAmount{ Address: dstUser.(*cosmos.CosmosWallet).FormattedAddressWithPrefix(srcChainCfg.Bech32Prefix), Denom: dstChainCfg.Denom, - Amount: testCoinAmount, + Amount: math.NewInt(testCoinAmount), } var eg errgroup.Group @@ -405,8 +418,8 @@ func testPacketRelaySuccess( for i, srcTx := range testCase.TxCache.Src { t.Logf("Asserting %s to %s transfer", srcChainCfg.ChainID, dstChainCfg.ChainID) // Assuming these values since the ibc transfers were sent in PreRelayerStart, so balances may have already changed by now - srcInitialBalance := userFaucetFund - dstInitialBalance := int64(0) + srcInitialBalance := math.NewInt(userFaucetFund) + dstInitialBalance := math.ZeroInt() srcAck, err := testutil.PollForAck(ctx, srcChain, srcTx.Height, srcTx.Height+pollHeightMax, srcTx.Packet) req.NoError(err, "failed to get acknowledgement on source chain") @@ -425,8 +438,8 @@ func testPacketRelaySuccess( totalFees := srcChain.GetGasFeesInNativeDenom(srcTx.GasSpent) expectedDifference := testCoinAmount + totalFees - req.Equal(srcInitialBalance-expectedDifference, srcFinalBalance) - req.Equal(dstInitialBalance+testCoinAmount, dstFinalBalance) + req.True(srcFinalBalance.Equal(srcInitialBalance.SubRaw(expectedDifference))) + req.True(dstFinalBalance.Equal(dstInitialBalance.AddRaw(testCoinAmount))) } // [END] assert on source to destination transfer @@ -437,8 +450,8 @@ func testPacketRelaySuccess( dstUser := testCase.Users[1] dstDenom := dstChainCfg.Denom // Assuming these values since the ibc transfers were sent in PreRelayerStart, so balances may have already changed by now - srcInitialBalance := int64(0) - dstInitialBalance := userFaucetFund + srcInitialBalance := math.ZeroInt() + dstInitialBalance := math.NewInt(userFaucetFund) dstAck, err := testutil.PollForAck(ctx, dstChain, dstTx.Height, dstTx.Height+pollHeightMax, dstTx.Packet) req.NoError(err, "failed to get acknowledgement on destination chain") @@ -457,8 +470,8 @@ func testPacketRelaySuccess( totalFees := dstChain.GetGasFeesInNativeDenom(dstTx.GasSpent) expectedDifference := testCoinAmount + totalFees - req.Equal(srcInitialBalance+testCoinAmount, srcFinalBalance) - req.Equal(dstInitialBalance-expectedDifference, dstFinalBalance) + req.True(srcFinalBalance.Equal(srcInitialBalance.AddRaw(testCoinAmount))) + req.True(dstFinalBalance.Equal(dstInitialBalance.SubRaw(expectedDifference))) } //[END] assert on destination to source transfer } @@ -486,8 +499,8 @@ func testPacketRelayFail( // [BEGIN] assert on source to destination transfer for i, srcTx := range testCase.TxCache.Src { // Assuming these values since the ibc transfers were sent in PreRelayerStart, so balances may have already changed by now - srcInitialBalance := userFaucetFund - dstInitialBalance := int64(0) + srcInitialBalance := math.NewInt(userFaucetFund) + dstInitialBalance := math.ZeroInt() timeout, err := testutil.PollForTimeout(ctx, srcChain, srcTx.Height, srcTx.Height+pollHeightMax, srcTx.Packet) req.NoError(err, "failed to get timeout packet on source chain") @@ -509,16 +522,16 @@ func testPacketRelayFail( totalFees := srcChain.GetGasFeesInNativeDenom(srcTx.GasSpent) - req.Equal(srcInitialBalance-totalFees, srcFinalBalance) - req.Equal(dstInitialBalance, dstFinalBalance) + req.True(srcFinalBalance.Equal(srcInitialBalance.SubRaw(totalFees))) + req.True(dstFinalBalance.Equal(dstInitialBalance)) } // [END] assert on source to destination transfer // [BEGIN] assert on destination to source transfer for i, dstTx := range testCase.TxCache.Dst { // Assuming these values since the ibc transfers were sent in PreRelayerStart, so balances may have already changed by now - srcInitialBalance := int64(0) - dstInitialBalance := userFaucetFund + srcInitialBalance := math.ZeroInt() + dstInitialBalance := math.NewInt(userFaucetFund) timeout, err := testutil.PollForTimeout(ctx, dstChain, dstTx.Height, dstTx.Height+pollHeightMax, dstTx.Packet) req.NoError(err, "failed to get timeout packet on destination chain") @@ -536,8 +549,8 @@ func testPacketRelayFail( totalFees := dstChain.GetGasFeesInNativeDenom(dstTx.GasSpent) - req.Equal(srcInitialBalance, srcFinalBalance) - req.Equal(dstInitialBalance-totalFees, dstFinalBalance) + req.True(srcFinalBalance.Equal(srcInitialBalance)) + req.True(dstFinalBalance.Equal(dstInitialBalance.SubRaw(totalFees))) } // [END] assert on destination to source transfer } diff --git a/examples/cosmos/light_client_test.go b/examples/cosmos/light_client_test.go index 84c3f090d..34182b204 100644 --- a/examples/cosmos/light_client_test.go +++ b/examples/cosmos/light_client_test.go @@ -4,11 +4,20 @@ import ( "context" "testing" +<<<<<<< HEAD clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" interchaintest "github.com/strangelove-ventures/interchaintest/v6" "github.com/strangelove-ventures/interchaintest/v6/chain/cosmos" "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/testreporter" +======= + "cosmossdk.io/math" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/testreporter" +>>>>>>> 8e02aef (refactor: use cosmos sdk Int type for balances/token amounts (#679)) "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" ) @@ -83,7 +92,7 @@ func TestUpdateLightClients(t *testing.T) { transfer := ibc.WalletAmount{ Address: dstAddress, Denom: gaia.Config().Denom, - Amount: amountToSend, + Amount: math.NewInt(amountToSend), } tx, err := gaia.SendIBCTransfer(ctx, chanID, gaiaUser.KeyName(), transfer, ibc.TransferOptions{}) require.NoError(t, err) diff --git a/examples/hyperspace/hyperspace_test.go b/examples/hyperspace/hyperspace_test.go new file mode 100644 index 000000000..7e9a4b7e4 --- /dev/null +++ b/examples/hyperspace/hyperspace_test.go @@ -0,0 +1,402 @@ +package hyperspace_test + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "testing" + "time" + + "cosmossdk.io/math" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + "github.com/icza/dyno" + "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/chain/polkadot" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/relayer" + "github.com/strangelove-ventures/interchaintest/v7/testreporter" + "github.com/strangelove-ventures/interchaintest/v7/testutil" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest" +) + +// TestHyperspace setup +// Must build local docker images of hyperspace, parachain, and polkadot +// ###### hyperspace ###### +// * Repo: ComposableFi/centauri +// * Branch: vmarkushin/wasm +// * Commit: 00ee58381df66b035be75721e6e16c2bbf82f076 +// * Build local Hyperspace docker from centauri repo: +// amd64: "docker build -f scripts/hyperspace.Dockerfile -t hyperspace:local ." +// arm64: "docker build -f scripts/hyperspace.aarch64.Dockerfile -t hyperspace:latest --platform=linux/arm64/v8 . +// ###### parachain ###### +// * Repo: ComposableFi/centauri +// * Branch: vmarkushin/wasm +// * Commit: 00ee58381df66b035be75721e6e16c2bbf82f076 +// * Build local parachain docker from centauri repo: +// ./scripts/build-parachain-node-docker.sh (you can change the script to compile for ARM arch if needed) +// ###### polkadot ###### +// * Repo: paritytech/polkadot +// * Branch: release-v0.9.36 +// * Commit: dc25abc712e42b9b51d87ad1168e453a42b5f0bc +// * Build local polkadot docker from polkadot repo +// amd64: docker build -f scripts/ci/dockerfiles/polkadot/polkadot_builder.Dockerfile . -t polkadot-node:local +// arm64: docker build --platform linux/arm64 -f scripts/ci/dockerfiles/polkadot/polkadot_builder.aarch64.Dockerfile . -t polkadot-node:local + +const ( + heightDelta = uint64(20) + votingPeriod = "30s" + maxDepositPeriod = "10s" + aliceAddress = "5yNZjX24n2eg7W6EVamaTXNQbWCwchhThEaSWB7V3GRjtHeL" +) + +// TestHyperspace features +// * sets up a Polkadot parachain +// * sets up a Cosmos chain +// * sets up the Hyperspace relayer +// * Funds a user wallet on both chains +// * Pushes a wasm client contract to the Cosmos chain +// * create client, connection, and channel in relayer +// * start relayer +// * send transfer over ibc +func TestHyperspace(t *testing.T) { + if testing.Short() { + t.Skip() + } + + t.Parallel() + + client, network := interchaintest.DockerSetup(t) + + rep := testreporter.NewNopReporter() + eRep := rep.RelayerExecReporter(t) + + ctx := context.Background() + + nv := 5 // Number of validators + nf := 3 // Number of full nodes + + consensusOverrides := make(testutil.Toml) + blockTime := 5 // seconds, parachain is 12 second blocks, don't make relayer work harder than needed + blockT := (time.Duration(blockTime) * time.Second).String() + consensusOverrides["timeout_commit"] = blockT + consensusOverrides["timeout_propose"] = blockT + + configTomlOverrides := make(testutil.Toml) + configTomlOverrides["consensus"] = consensusOverrides + + configFileOverrides := make(map[string]any) + configFileOverrides["config/config.toml"] = configTomlOverrides + + // Get both chains + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ + { + ChainName: "composable", // Set ChainName so that a suffix with a "dash" is not appended (required for hyperspace) + ChainConfig: ibc.ChainConfig{ + Type: "polkadot", + Name: "composable", + ChainID: "rococo-local", + Images: []ibc.DockerImage{ + { + Repository: "polkadot-node", + Version: "local", + UidGid: "1000:1000", + }, + { + Repository: "parachain-node", + Version: "latest", + //UidGid: "1025:1025", + }, + }, + Bin: "polkadot", + Bech32Prefix: "composable", + Denom: "uDOT", + GasPrices: "", + GasAdjustment: 0, + TrustingPeriod: "", + CoinType: "354", + }, + NumValidators: &nv, + NumFullNodes: &nf, + }, + { + ChainName: "simd", // Set chain name so that a suffix with a "dash" is not appended (required for hyperspace) + ChainConfig: ibc.ChainConfig{ + Type: "cosmos", + Name: "simd", + ChainID: "simd", + Images: []ibc.DockerImage{ + { + Repository: "ghcr.io/strangelove-ventures/heighliner/ibc-go-simd", + Version: "feat-wasm-clients", + UidGid: "1025:1025", + }, + }, + Bin: "simd", + Bech32Prefix: "cosmos", + Denom: "stake", + GasPrices: "0.00stake", + GasAdjustment: 1.3, + TrustingPeriod: "504h", + CoinType: "118", + //EncodingConfig: WasmClientEncoding(), + NoHostMount: true, + ConfigFileOverrides: configFileOverrides, + ModifyGenesis: modifyGenesisShortProposals(votingPeriod, maxDepositPeriod), + }, + }, + }) + + chains, err := cf.Chains(t.Name()) + require.NoError(t, err) + + polkadotChain := chains[0].(*polkadot.PolkadotChain) + cosmosChain := chains[1].(*cosmos.CosmosChain) + + // Get a relayer instance + r := interchaintest.NewBuiltinRelayerFactory( + ibc.Hyperspace, + zaptest.NewLogger(t), + // These two fields are used to pass in a custom Docker image built locally + relayer.ImagePull(false), + relayer.CustomDockerImage("hyperspace", "local", "1000:1000"), + ).Build(t, client, network) + + // Build the network; spin up the chains and configure the relayer + const pathName = "composable-simd" + const relayerName = "hyperspace" + + ic := interchaintest.NewInterchain(). + AddChain(polkadotChain). + AddChain(cosmosChain). + AddRelayer(r, relayerName). + AddLink(interchaintest.InterchainLink{ + Chain1: polkadotChain, + Chain2: cosmosChain, + Relayer: r, + Path: pathName, + }) + + require.NoError(t, ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ + TestName: t.Name(), + Client: client, + NetworkID: network, + BlockDatabaseFile: interchaintest.DefaultBlockDatabaseFilepath(), + SkipPathCreation: true, // Skip path creation, so we can have granular control over the process + })) + fmt.Println("Interchain built") + + t.Cleanup(func() { + _ = ic.Close() + }) + + // Create a proposal, vote, and wait for it to pass. Return code hash for relayer. + codeHash := pushWasmContractViaGov(t, ctx, cosmosChain) + + // Set client contract hash in cosmos chain config + err = r.SetClientContractHash(ctx, eRep, cosmosChain.Config(), codeHash) + require.NoError(t, err) + + // Ensure parachain has started (starts 1 session/epoch after relay chain) + err = testutil.WaitForBlocks(ctx, 1, polkadotChain) + require.NoError(t, err, "polkadot chain failed to make blocks") + + // Fund users on both cosmos and parachain, mints Asset 1 for Alice + fundAmount := int64(12_333_000_000_000) + polkadotUser, cosmosUser := fundUsers(t, ctx, fundAmount, polkadotChain, cosmosChain) + + err = r.GeneratePath(ctx, eRep, cosmosChain.Config().ChainID, polkadotChain.Config().ChainID, pathName) + require.NoError(t, err) + + // Create new clients + err = r.CreateClients(ctx, eRep, pathName, ibc.DefaultClientOpts()) + require.NoError(t, err) + err = testutil.WaitForBlocks(ctx, 1, cosmosChain, polkadotChain) // these 1 block waits seem to be needed to reduce flakiness + require.NoError(t, err) + + // Create a new connection + err = r.CreateConnections(ctx, eRep, pathName) + require.NoError(t, err) + err = testutil.WaitForBlocks(ctx, 1, cosmosChain, polkadotChain) + require.NoError(t, err) + + // Create a new channel & get channels from each chain + err = r.CreateChannel(ctx, eRep, pathName, ibc.DefaultChannelOpts()) + require.NoError(t, err) + err = testutil.WaitForBlocks(ctx, 1, cosmosChain, polkadotChain) + require.NoError(t, err) + + // Start relayer + r.StartRelayer(ctx, eRep, pathName) + require.NoError(t, err) + t.Cleanup(func() { + err = r.StopRelayer(ctx, eRep) + if err != nil { + panic(err) + } + }) + + // Send 1.77 stake from cosmosUser to parachainUser + amountToSend := int64(1_770_000) + transfer := ibc.WalletAmount{ + Address: polkadotUser.FormattedAddress(), + Denom: cosmosChain.Config().Denom, + Amount: math.NewInt(amountToSend), + } + tx, err := cosmosChain.SendIBCTransfer(ctx, "channel-0", cosmosUser.KeyName(), transfer, ibc.TransferOptions{}) + require.NoError(t, err) + require.NoError(t, tx.Validate()) // test source wallet has decreased funds + err = testutil.WaitForBlocks(ctx, 5, cosmosChain, polkadotChain) + require.NoError(t, err) + + // Verify tokens arrived on parachain user + parachainUserStake, err := polkadotChain.GetIbcBalance(ctx, string(polkadotUser.Address()), 2) + require.NoError(t, err) + require.Equal(t, amountToSend, parachainUserStake.Amount.Int64(), "parachain user's stake amount not expected after first tx") + + // Send 1.16 stake from parachainUser to cosmosUser + amountToReflect := int64(1_160_000) + reflectTransfer := ibc.WalletAmount{ + Address: cosmosUser.FormattedAddress(), + Denom: "2", // stake + Amount: math.NewInt(amountToReflect), + } + _, err = polkadotChain.SendIBCTransfer(ctx, "channel-0", polkadotUser.KeyName(), reflectTransfer, ibc.TransferOptions{}) + require.NoError(t, err) + + // Send 1.88 "UNIT" from Alice to cosmosUser + amountUnits := math.NewInt(1_880_000_000_000) + unitTransfer := ibc.WalletAmount{ + Address: cosmosUser.FormattedAddress(), + Denom: "1", // UNIT + Amount: amountUnits, + } + _, err = polkadotChain.SendIBCTransfer(ctx, "channel-0", "alice", unitTransfer, ibc.TransferOptions{}) + require.NoError(t, err) + + // Wait for MsgRecvPacket on cosmos chain + finalStakeBal := math.NewInt(fundAmount - amountToSend + amountToReflect) + err = cosmos.PollForBalance(ctx, cosmosChain, 20, ibc.WalletAmount{ + Address: cosmosUser.FormattedAddress(), + Denom: cosmosChain.Config().Denom, + Amount: finalStakeBal, + }) + require.NoError(t, err) + + // Verify cosmos user's final "stake" balance + cosmosUserStakeBal, err := cosmosChain.GetBalance(ctx, cosmosUser.FormattedAddress(), cosmosChain.Config().Denom) + require.NoError(t, err) + require.True(t, cosmosUserStakeBal.Equal(finalStakeBal)) + + // Verify cosmos user's final "unit" balance + unitDenomTrace := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", "channel-0", "UNIT")) + cosmosUserUnitBal, err := cosmosChain.GetBalance(ctx, cosmosUser.FormattedAddress(), unitDenomTrace.IBCDenom()) + require.NoError(t, err) + require.True(t, cosmosUserUnitBal.Equal(amountUnits)) + + // Verify parachain user's final "unit" balance (will be less than expected due gas costs for stake tx) + parachainUserUnits, err := polkadotChain.GetIbcBalance(ctx, string(polkadotUser.Address()), 1) + require.NoError(t, err) + require.True(t, parachainUserUnits.Amount.LTE(math.NewInt(fundAmount)), "parachain user's final unit amount not expected") + + // Verify parachain user's final "stake" balance + parachainUserStake, err = polkadotChain.GetIbcBalance(ctx, string(polkadotUser.Address()), 2) + require.NoError(t, err) + require.True(t, parachainUserStake.Equal(math.NewInt(amountToSend-amountToReflect)), "parachain user's final stake amount not expected") +} + +type GetCodeQueryMsgResponse struct { + Code []byte `json:"code"` +} + +func pushWasmContractViaGov(t *testing.T, ctx context.Context, cosmosChain *cosmos.CosmosChain) string { + // Set up cosmos user for pushing new wasm code msg via governance + fundAmountForGov := int64(10_000_000_000) + contractUsers := interchaintest.GetAndFundTestUsers(t, ctx, "default", int64(fundAmountForGov), cosmosChain) + contractUser := contractUsers[0] + + contractUserBalInitial, err := cosmosChain.GetBalance(ctx, contractUser.FormattedAddress(), cosmosChain.Config().Denom) + require.NoError(t, err) + require.True(t, contractUserBalInitial.Equal(math.NewInt(fundAmountForGov))) + + proposal := cosmos.TxProposalv1{ + Metadata: "none", + Deposit: "500000000" + cosmosChain.Config().Denom, // greater than min deposit + Title: "Grandpa Contract", + Summary: "new grandpa contract", + } + + proposalTx, codeHash, err := cosmosChain.PushNewWasmClientProposal(ctx, contractUser.KeyName(), "../polkadot/ics10_grandpa_cw.wasm", proposal) + require.NoError(t, err, "error submitting new wasm contract proposal tx") + + height, err := cosmosChain.Height(ctx) + require.NoError(t, err, "error fetching height before submit upgrade proposal") + + err = cosmosChain.VoteOnProposalAllValidators(ctx, proposalTx.ProposalID, cosmos.ProposalVoteYes) + require.NoError(t, err, "failed to submit votes") + + _, err = cosmos.PollForProposalStatus(ctx, cosmosChain, height, height+heightDelta, proposalTx.ProposalID, cosmos.ProposalStatusPassed) + require.NoError(t, err, "proposal status did not change to passed in expected number of blocks") + + err = testutil.WaitForBlocks(ctx, 1, cosmosChain) + require.NoError(t, err) + + var getCodeQueryMsgRsp GetCodeQueryMsgResponse + err = cosmosChain.QueryClientContractCode(ctx, codeHash, &getCodeQueryMsgRsp) + codeHashByte32 := sha256.Sum256(getCodeQueryMsgRsp.Code) + codeHash2 := hex.EncodeToString(codeHashByte32[:]) + require.NoError(t, err) + require.NotEmpty(t, getCodeQueryMsgRsp.Code) + require.Equal(t, codeHash, codeHash2) + + return codeHash +} + +func fundUsers(t *testing.T, ctx context.Context, fundAmount int64, polkadotChain ibc.Chain, cosmosChain ibc.Chain) (ibc.Wallet, ibc.Wallet) { + users := interchaintest.GetAndFundTestUsers(t, ctx, "user", fundAmount, polkadotChain, cosmosChain) + polkadotUser, cosmosUser := users[0], users[1] + err := testutil.WaitForBlocks(ctx, 2, polkadotChain, cosmosChain) // Only waiting 1 block is flaky for parachain + require.NoError(t, err, "cosmos or polkadot chain failed to make blocks") + + // Check balances are correct + amount := math.NewInt(fundAmount) + polkadotUserAmount, err := polkadotChain.GetBalance(ctx, polkadotUser.FormattedAddress(), polkadotChain.Config().Denom) + require.NoError(t, err) + require.True(t, polkadotUserAmount.Equal(amount), "Initial polkadot user amount not expected") + + parachainUserAmount, err := polkadotChain.GetBalance(ctx, polkadotUser.FormattedAddress(), "") + require.NoError(t, err) + require.True(t, parachainUserAmount.Equal(amount), "Initial parachain user amount not expected") + + cosmosUserAmount, err := cosmosChain.GetBalance(ctx, cosmosUser.FormattedAddress(), cosmosChain.Config().Denom) + require.NoError(t, err) + require.True(t, cosmosUserAmount.Equal(amount), "Initial cosmos user amount not expected") + + return polkadotUser, cosmosUser +} + +func modifyGenesisShortProposals(votingPeriod string, maxDepositPeriod string) func(ibc.ChainConfig, []byte) ([]byte, error) { + return func(chainConfig ibc.ChainConfig, genbz []byte) ([]byte, error) { + g := make(map[string]interface{}) + if err := json.Unmarshal(genbz, &g); err != nil { + return nil, fmt.Errorf("failed to unmarshal genesis file: %w", err) + } + if err := dyno.Set(g, votingPeriod, "app_state", "gov", "params", "voting_period"); err != nil { + return nil, fmt.Errorf("failed to set voting period in genesis json: %w", err) + } + if err := dyno.Set(g, maxDepositPeriod, "app_state", "gov", "params", "max_deposit_period"); err != nil { + return nil, fmt.Errorf("failed to set max deposit period in genesis json: %w", err) + } + if err := dyno.Set(g, chainConfig.Denom, "app_state", "gov", "params", "min_deposit", 0, "denom"); err != nil { + return nil, fmt.Errorf("failed to set min deposit in genesis json: %w", err) + } + out, err := json.Marshal(g) + if err != nil { + return nil, fmt.Errorf("failed to marshal genesis bytes to json: %w", err) + } + return out, nil + } +} diff --git a/examples/ibc/interchain_accounts_test.go b/examples/ibc/interchain_accounts_test.go index f64c89b06..6f8bb0efb 100644 --- a/examples/ibc/interchain_accounts_test.go +++ b/examples/ibc/interchain_accounts_test.go @@ -3,12 +3,13 @@ package ibc import ( "context" "encoding/json" - "strconv" "strings" "testing" "time" + "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/crypto/keyring" +<<<<<<< HEAD chantypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types" interchaintest "github.com/strangelove-ventures/interchaintest/v6" "github.com/strangelove-ventures/interchaintest/v6/chain/cosmos" @@ -16,6 +17,15 @@ import ( "github.com/strangelove-ventures/interchaintest/v6/relayer" "github.com/strangelove-ventures/interchaintest/v6/testreporter" "github.com/strangelove-ventures/interchaintest/v6/testutil" +======= + chantypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/relayer" + "github.com/strangelove-ventures/interchaintest/v7/testreporter" + "github.com/strangelove-ventures/interchaintest/v7/testutil" +>>>>>>> 8e02aef (refactor: use cosmos sdk Int type for balances/token amounts (#679)) "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" ) @@ -183,7 +193,7 @@ func TestInterchainAccounts(t *testing.T) { require.NoError(t, err) // Send funds to ICA from user account on chain2 - const transferAmount = 10000 + transferAmount := math.NewInt(1000) transfer := ibc.WalletAmount{ Address: icaAddr, Denom: chain2.Config().Denom, @@ -194,11 +204,11 @@ func TestInterchainAccounts(t *testing.T) { chain2Bal, err := chain2.GetBalance(ctx, chain2Addr, chain2.Config().Denom) require.NoError(t, err) - require.Equal(t, chain2OrigBal-transferAmount, chain2Bal) + require.True(t, chain2Bal.Equal(chain2OrigBal.Sub(transferAmount))) icaBal, err := chain2.GetBalance(ctx, icaAddr, chain2.Config().Denom) require.NoError(t, err) - require.Equal(t, icaOrigBal+transferAmount, icaBal) + require.True(t, icaBal.Equal(icaOrigBal.Add(transferAmount))) // Build bank transfer msg rawMsg, err := json.Marshal(map[string]any{ @@ -208,7 +218,7 @@ func TestInterchainAccounts(t *testing.T) { "amount": []map[string]any{ { "denom": chain2.Config().Denom, - "amount": strconv.Itoa(transferAmount), + "amount": transferAmount.String(), }, }, }) @@ -245,12 +255,12 @@ func TestInterchainAccounts(t *testing.T) { // Assert that the funds have been received by the user account on chain2 chain2Bal, err = chain2.GetBalance(ctx, chain2Addr, chain2.Config().Denom) require.NoError(t, err) - require.Equal(t, chain2OrigBal, chain2Bal) + require.True(t, chain2Bal.Equal(chain2OrigBal)) // Assert that the funds have been removed from the ICA on chain2 icaBal, err = chain2.GetBalance(ctx, icaAddr, chain2.Config().Denom) require.NoError(t, err) - require.Equal(t, icaOrigBal, icaBal) + require.True(t, icaBal.Equal(icaOrigBal)) // Stop the relayer and wait for the process to terminate err = r.StopRelayer(ctx, eRep) @@ -282,11 +292,11 @@ func TestInterchainAccounts(t *testing.T) { // Assert that the packet timed out and that the acc balances are correct chain2Bal, err = chain2.GetBalance(ctx, chain2Addr, chain2.Config().Denom) require.NoError(t, err) - require.Equal(t, chain2OrigBal, chain2Bal) + require.True(t, chain2Bal.Equal(chain2OrigBal)) icaBal, err = chain2.GetBalance(ctx, icaAddr, chain2.Config().Denom) require.NoError(t, err) - require.Equal(t, icaOrigBal, icaBal) + require.True(t, icaBal.Equal(icaOrigBal)) // Assert that the channel ends are both closed chain1Chans, err := r.GetChannels(ctx, eRep, chain1.Config().ChainID) diff --git a/examples/ibc/learn_ibc_test.go b/examples/ibc/learn_ibc_test.go index 047bf504f..ea4636b60 100644 --- a/examples/ibc/learn_ibc_test.go +++ b/examples/ibc/learn_ibc_test.go @@ -6,10 +6,18 @@ import ( "testing" "time" +<<<<<<< HEAD transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" interchaintest "github.com/strangelove-ventures/interchaintest/v6" "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/testreporter" +======= + "cosmossdk.io/math" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/testreporter" +>>>>>>> 8e02aef (refactor: use cosmos sdk Int type for balances/token amounts (#679)) "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" ) @@ -74,14 +82,14 @@ func TestLearn(t *testing.T) { ) // Create and Fund User Wallets - fundAmount := int64(10_000_000) - users := interchaintest.GetAndFundTestUsers(t, ctx, "default", fundAmount, gaia, osmosis) + fundAmount := math.NewInt(10_000_000) + users := interchaintest.GetAndFundTestUsers(t, ctx, "default", fundAmount.Int64(), gaia, osmosis) gaiaUser := users[0] osmosisUser := users[1] gaiaUserBalInitial, err := gaia.GetBalance(ctx, gaiaUser.FormattedAddress(), gaia.Config().Denom) require.NoError(t, err) - require.Equal(t, fundAmount, gaiaUserBalInitial) + require.True(t, gaiaUserBalInitial.Equal(fundAmount)) // Get Channel ID gaiaChannelInfo, err := r.GetChannels(ctx, eRep, gaia.Config().ChainID) @@ -93,7 +101,7 @@ func TestLearn(t *testing.T) { osmoChannelID := osmoChannelInfo[0].ChannelID // Send Transaction - amountToSend := int64(1_000_000) + amountToSend := math.NewInt(1_000_000) dstAddress := osmosisUser.FormattedAddress() transfer := ibc.WalletAmount{ Address: dstAddress, @@ -108,10 +116,10 @@ func TestLearn(t *testing.T) { require.NoError(t, r.Flush(ctx, eRep, ibcPath, gaiaChannelID)) // test source wallet has decreased funds - expectedBal := gaiaUserBalInitial - amountToSend + expectedBal := gaiaUserBalInitial.Sub(amountToSend) gaiaUserBalNew, err := gaia.GetBalance(ctx, gaiaUser.FormattedAddress(), gaia.Config().Denom) require.NoError(t, err) - require.Equal(t, expectedBal, gaiaUserBalNew) + require.True(t, gaiaUserBalNew.Equal(expectedBal)) // Trace IBC Denom srcDenomTrace := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", osmoChannelID, gaia.Config().Denom)) @@ -120,5 +128,5 @@ func TestLearn(t *testing.T) { // Test destination wallet has increased funds osmosUserBalNew, err := osmosis.GetBalance(ctx, osmosisUser.FormattedAddress(), dstIbcDenom) require.NoError(t, err) - require.Equal(t, amountToSend, osmosUserBalNew) + require.True(t, osmosUserBalNew.Equal(amountToSend)) } diff --git a/examples/ibc/packet_forward_test.go b/examples/ibc/packet_forward_test.go index 8aad1f741..07fb71b5b 100644 --- a/examples/ibc/packet_forward_test.go +++ b/examples/ibc/packet_forward_test.go @@ -6,12 +6,22 @@ import ( "testing" "time" +<<<<<<< HEAD transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" interchaintest "github.com/strangelove-ventures/interchaintest/v6" "github.com/strangelove-ventures/interchaintest/v6/chain/cosmos" "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/testreporter" "github.com/strangelove-ventures/interchaintest/v6/testutil" +======= + "cosmossdk.io/math" + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/testreporter" + "github.com/strangelove-ventures/interchaintest/v7/testutil" +>>>>>>> 8e02aef (refactor: use cosmos sdk Int type for balances/token amounts (#679)) "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" ) @@ -102,8 +112,8 @@ func TestPacketForwardMiddleware(t *testing.T) { _ = ic.Close() }) - const userFunds = int64(10_000_000_000) - users := interchaintest.GetAndFundTestUsers(t, ctx, t.Name(), userFunds, chainA, chainB, chainC, chainD) + initBal := math.NewInt(10_000_000_000) + users := interchaintest.GetAndFundTestUsers(t, ctx, t.Name(), initBal.Int64(), chainA, chainB, chainC, chainD) abChan, err := ibc.GetTransferChannel(ctx, r, eRep, chainID_A, chainID_B) require.NoError(t, err) @@ -136,8 +146,6 @@ func TestPacketForwardMiddleware(t *testing.T) { // Get original account balances userA, userB, userC, userD := users[0], users[1], users[2], users[3] - const transferAmount int64 = 100000 - // Compose the prefixed denoms and ibc denom for asserting balances firstHopDenom := transfertypes.GetPrefixedDenom(baChan.PortID, baChan.ChannelID, chainA.Config().Denom) secondHopDenom := transfertypes.GetPrefixedDenom(cbChan.PortID, cbChan.ChannelID, firstHopDenom) @@ -155,9 +163,11 @@ func TestPacketForwardMiddleware(t *testing.T) { secondHopEscrowAccount := transfertypes.GetEscrowAddress(bcChan.PortID, bcChan.ChannelID).String() thirdHopEscrowAccount := transfertypes.GetEscrowAddress(cdChan.PortID, abChan.ChannelID).String() + zeroBal := math.ZeroInt() + transferAmount := math.NewInt(100_000) + t.Run("multi-hop a->b->c->d", func(t *testing.T) { // Send packet from Chain A->Chain B->Chain C->Chain D - transfer := ibc.WalletAmount{ Address: userB.FormattedAddress(), Denom: chainA.Config().Denom, @@ -209,10 +219,10 @@ func TestPacketForwardMiddleware(t *testing.T) { chainDBalance, err := chainD.GetBalance(ctx, userD.FormattedAddress(), thirdHopIBCDenom) require.NoError(t, err) - require.Equal(t, userFunds-transferAmount, chainABalance) - require.Equal(t, int64(0), chainBBalance) - require.Equal(t, int64(0), chainCBalance) - require.Equal(t, transferAmount, chainDBalance) + require.True(t, chainABalance.Equal(initBal.Sub(transferAmount))) + require.True(t, chainBBalance.Equal(zeroBal)) + require.True(t, chainCBalance.Equal(zeroBal)) + require.True(t, chainDBalance.Equal(transferAmount)) firstHopEscrowBalance, err := chainA.GetBalance(ctx, firstHopEscrowAccount, chainA.Config().Denom) require.NoError(t, err) @@ -223,9 +233,9 @@ func TestPacketForwardMiddleware(t *testing.T) { thirdHopEscrowBalance, err := chainC.GetBalance(ctx, thirdHopEscrowAccount, secondHopIBCDenom) require.NoError(t, err) - require.Equal(t, transferAmount, firstHopEscrowBalance) - require.Equal(t, transferAmount, secondHopEscrowBalance) - require.Equal(t, transferAmount, thirdHopEscrowBalance) + require.True(t, firstHopEscrowBalance.Equal(transferAmount)) + require.True(t, secondHopEscrowBalance.Equal(transferAmount)) + require.True(t, thirdHopEscrowBalance.Equal(transferAmount)) }) t.Run("multi-hop denom unwind d->c->b->a", func(t *testing.T) { @@ -284,10 +294,10 @@ func TestPacketForwardMiddleware(t *testing.T) { chainABalance, err := chainA.GetBalance(ctx, userA.FormattedAddress(), chainA.Config().Denom) require.NoError(t, err) - require.Equal(t, int64(0), chainDBalance) - require.Equal(t, int64(0), chainCBalance) - require.Equal(t, int64(0), chainBBalance) - require.Equal(t, userFunds, chainABalance) + require.True(t, chainDBalance.Equal(zeroBal)) + require.True(t, chainCBalance.Equal(zeroBal)) + require.True(t, chainBBalance.Equal(zeroBal)) + require.True(t, chainABalance.Equal(initBal)) // assert balances for IBC escrow accounts firstHopEscrowBalance, err := chainA.GetBalance(ctx, firstHopEscrowAccount, chainA.Config().Denom) @@ -299,9 +309,9 @@ func TestPacketForwardMiddleware(t *testing.T) { thirdHopEscrowBalance, err := chainC.GetBalance(ctx, thirdHopEscrowAccount, secondHopIBCDenom) require.NoError(t, err) - require.Equal(t, int64(0), firstHopEscrowBalance) - require.Equal(t, int64(0), secondHopEscrowBalance) - require.Equal(t, int64(0), thirdHopEscrowBalance) + require.True(t, firstHopEscrowBalance.Equal(zeroBal)) + require.True(t, secondHopEscrowBalance.Equal(zeroBal)) + require.True(t, thirdHopEscrowBalance.Equal(zeroBal)) }) t.Run("forward ack error refund", func(t *testing.T) { @@ -344,9 +354,9 @@ func TestPacketForwardMiddleware(t *testing.T) { chainCBalance, err := chainC.GetBalance(ctx, userC.FormattedAddress(), secondHopIBCDenom) require.NoError(t, err) - require.Equal(t, userFunds, chainABalance) - require.Equal(t, int64(0), chainBBalance) - require.Equal(t, int64(0), chainCBalance) + require.True(t, chainABalance.Equal(initBal)) + require.True(t, chainBBalance.Equal(zeroBal)) + require.True(t, chainCBalance.Equal(zeroBal)) // assert balances for IBC escrow accounts firstHopEscrowBalance, err := chainA.GetBalance(ctx, firstHopEscrowAccount, chainA.Config().Denom) @@ -355,8 +365,8 @@ func TestPacketForwardMiddleware(t *testing.T) { secondHopEscrowBalance, err := chainB.GetBalance(ctx, secondHopEscrowAccount, firstHopIBCDenom) require.NoError(t, err) - require.Equal(t, int64(0), firstHopEscrowBalance) - require.Equal(t, int64(0), secondHopEscrowBalance) + require.True(t, firstHopEscrowBalance.Equal(zeroBal)) + require.True(t, secondHopEscrowBalance.Equal(zeroBal)) }) t.Run("forward timeout refund", func(t *testing.T) { @@ -401,9 +411,9 @@ func TestPacketForwardMiddleware(t *testing.T) { chainCBalance, err := chainC.GetBalance(ctx, userC.FormattedAddress(), secondHopIBCDenom) require.NoError(t, err) - require.Equal(t, userFunds, chainABalance) - require.Equal(t, int64(0), chainBBalance) - require.Equal(t, int64(0), chainCBalance) + require.True(t, chainABalance.Equal(initBal)) + require.True(t, chainBBalance.Equal(zeroBal)) + require.True(t, chainCBalance.Equal(zeroBal)) firstHopEscrowBalance, err := chainA.GetBalance(ctx, firstHopEscrowAccount, chainA.Config().Denom) require.NoError(t, err) @@ -411,8 +421,8 @@ func TestPacketForwardMiddleware(t *testing.T) { secondHopEscrowBalance, err := chainB.GetBalance(ctx, secondHopEscrowAccount, firstHopIBCDenom) require.NoError(t, err) - require.Equal(t, int64(0), firstHopEscrowBalance) - require.Equal(t, int64(0), secondHopEscrowBalance) + require.True(t, firstHopEscrowBalance.Equal(zeroBal)) + require.True(t, secondHopEscrowBalance.Equal(zeroBal)) }) t.Run("multi-hop ack error refund", func(t *testing.T) { @@ -473,10 +483,10 @@ func TestPacketForwardMiddleware(t *testing.T) { chainABalance, err := chainA.GetBalance(ctx, userA.FormattedAddress(), chainA.Config().Denom) require.NoError(t, err) - require.Equal(t, userFunds, chainABalance) - require.Equal(t, int64(0), chainBBalance) - require.Equal(t, int64(0), chainCBalance) - require.Equal(t, int64(0), chainDBalance) + require.True(t, chainABalance.Equal(initBal)) + require.True(t, chainBBalance.Equal(zeroBal)) + require.True(t, chainCBalance.Equal(zeroBal)) + require.True(t, chainDBalance.Equal(zeroBal)) // assert balances for IBC escrow accounts firstHopEscrowBalance, err := chainA.GetBalance(ctx, firstHopEscrowAccount, chainA.Config().Denom) @@ -488,9 +498,9 @@ func TestPacketForwardMiddleware(t *testing.T) { thirdHopEscrowBalance, err := chainC.GetBalance(ctx, thirdHopEscrowAccount, secondHopIBCDenom) require.NoError(t, err) - require.Equal(t, int64(0), firstHopEscrowBalance) - require.Equal(t, int64(0), secondHopEscrowBalance) - require.Equal(t, int64(0), thirdHopEscrowBalance) + require.True(t, firstHopEscrowBalance.Equal(zeroBal)) + require.True(t, secondHopEscrowBalance.Equal(zeroBal)) + require.True(t, thirdHopEscrowBalance.Equal(zeroBal)) }) t.Run("multi-hop through native chain ack error refund", func(t *testing.T) { @@ -533,8 +543,8 @@ func TestPacketForwardMiddleware(t *testing.T) { baEscrowBalance, err := chainB.GetBalance(ctx, transfertypes.GetEscrowAddress(baChan.PortID, baChan.ChannelID).String(), chainB.Config().Denom) require.NoError(t, err) - require.Equal(t, transferAmount, chainABalance) - require.Equal(t, transferAmount, baEscrowBalance) + require.True(t, chainABalance.Equal(transferAmount)) + require.True(t, baEscrowBalance.Equal(transferAmount)) // Send a malformed packet with invalid receiver address from Chain A->Chain B->Chain C->Chain D // This should succeed in the first hop and second hop, then fail to make the third hop. @@ -593,10 +603,10 @@ func TestPacketForwardMiddleware(t *testing.T) { chainABalance, err = chainA.GetBalance(ctx, userA.FormattedAddress(), baIBCDenom) require.NoError(t, err) - require.Equal(t, transferAmount, chainABalance) - require.Equal(t, userFunds-transferAmount, chainBBalance) - require.Equal(t, int64(0), chainCBalance) - require.Equal(t, int64(0), chainDBalance) + require.True(t, chainABalance.Equal(transferAmount)) + require.True(t, chainBBalance.Equal(initBal.Sub(transferAmount))) + require.True(t, chainCBalance.Equal(zeroBal)) + require.True(t, chainDBalance.Equal(zeroBal)) // assert balances for IBC escrow accounts cdEscrowBalance, err := chainC.GetBalance(ctx, transfertypes.GetEscrowAddress(cdChan.PortID, cdChan.ChannelID).String(), bcIBCDenom) @@ -608,14 +618,13 @@ func TestPacketForwardMiddleware(t *testing.T) { baEscrowBalance, err = chainB.GetBalance(ctx, transfertypes.GetEscrowAddress(baChan.PortID, baChan.ChannelID).String(), chainB.Config().Denom) require.NoError(t, err) - require.Equal(t, transferAmount, baEscrowBalance) - require.Equal(t, int64(0), bcEscrowBalance) - require.Equal(t, int64(0), cdEscrowBalance) + require.True(t, baEscrowBalance.Equal(transferAmount)) + require.True(t, bcEscrowBalance.Equal(zeroBal)) + require.True(t, cdEscrowBalance.Equal(zeroBal)) }) t.Run("forward a->b->a", func(t *testing.T) { // Send packet from Chain A->Chain B->Chain A - userABalance, err := chainA.GetBalance(ctx, userA.FormattedAddress(), chainA.Config().Denom) require.NoError(t, err, "failed to get user a balance") @@ -655,7 +664,7 @@ func TestPacketForwardMiddleware(t *testing.T) { chainBBalance, err := chainB.GetBalance(ctx, userB.FormattedAddress(), firstHopIBCDenom) require.NoError(t, err) - require.Equal(t, userABalance, chainABalance) - require.Equal(t, userBBalance, chainBBalance) + require.True(t, chainABalance.Equal(userABalance)) + require.True(t, chainBBalance.Equal(userBBalance)) }) } diff --git a/examples/ibc/wasm/wasm_ibc_test.go b/examples/ibc/wasm/wasm_ibc_test.go new file mode 100644 index 000000000..5bd2c034c --- /dev/null +++ b/examples/ibc/wasm/wasm_ibc_test.go @@ -0,0 +1,221 @@ +package wasm_test + +import ( + "context" + "fmt" + "testing" + "time" + + "cosmossdk.io/math" + "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos/wasm" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/testreporter" + "github.com/strangelove-ventures/interchaintest/v7/testutil" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest" +) + +func TestWasmIbc(t *testing.T) { + if testing.Short() { + t.Skip("skipping in short mode") + } + + ctx := context.Background() + + // Chain Factory + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ + {Name: "juno", ChainName: "juno1", Version: "latest", ChainConfig: ibc.ChainConfig{ + GasPrices: "0.00ujuno", + EncodingConfig: wasm.WasmEncoding(), + }}, + {Name: "juno", ChainName: "juno2", Version: "latest", ChainConfig: ibc.ChainConfig{ + GasPrices: "0.00ujuno", + EncodingConfig: wasm.WasmEncoding(), + }}, + }) + + chains, err := cf.Chains(t.Name()) + require.NoError(t, err) + juno1, juno2 := chains[0], chains[1] + + // Relayer Factory + client, network := interchaintest.DockerSetup(t) + r := interchaintest.NewBuiltinRelayerFactory(ibc.CosmosRly, zaptest.NewLogger(t)).Build( + t, client, network) + + // Prep Interchain + const ibcPath = "wasmpath" + ic := interchaintest.NewInterchain(). + AddChain(juno1). + AddChain(juno2). + AddRelayer(r, "relayer"). + AddLink(interchaintest.InterchainLink{ + Chain1: juno1, + Chain2: juno2, + Relayer: r, + Path: ibcPath, + }) + + // Log location + f, err := interchaintest.CreateLogFile(fmt.Sprintf("wasm_ibc_test_%d.json", time.Now().Unix())) + require.NoError(t, err) + // Reporter/logs + rep := testreporter.NewReporter(f) + eRep := rep.RelayerExecReporter(t) + + // Build interchain + require.NoError(t, ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ + TestName: t.Name(), + Client: client, + NetworkID: network, + BlockDatabaseFile: interchaintest.DefaultBlockDatabaseFilepath(), + SkipPathCreation: false, + })) + t.Cleanup(func() { + _ = ic.Close() + }) + + // Create and Fund User Wallets + initBal := math.NewInt(100_000_000) + users := interchaintest.GetAndFundTestUsers(t, ctx, "default", initBal.Int64(), juno1, juno2) + juno1User := users[0] + juno2User := users[1] + + err = testutil.WaitForBlocks(ctx, 2, juno1, juno2) + require.NoError(t, err) + + juno1UserBalInitial, err := juno1.GetBalance(ctx, juno1User.FormattedAddress(), juno1.Config().Denom) + require.NoError(t, err) + require.True(t, juno1UserBalInitial.Equal(initBal)) + + juno2UserBalInitial, err := juno2.GetBalance(ctx, juno2User.FormattedAddress(), juno2.Config().Denom) + require.NoError(t, err) + require.True(t, juno2UserBalInitial.Equal(initBal)) + + // Start the relayer + err = r.StartRelayer(ctx, eRep, ibcPath) + require.NoError(t, err) + + t.Cleanup( + func() { + err := r.StopRelayer(ctx, eRep) + if err != nil { + t.Logf("an error occured while stopping the relayer: %s", err) + } + }, + ) + + juno1Chain := juno1.(*cosmos.CosmosChain) + juno2Chain := juno2.(*cosmos.CosmosChain) + + // Store ibc_reflect_send.wasm contract + ibcReflectSendCodeId, err := juno1Chain.StoreContract( + ctx, juno1User.KeyName(), "sample_contracts/ibc_reflect_send.wasm") + require.NoError(t, err) + + // Instantiate ibc_reflect_send.wasm contract + ibcReflectSendContractAddr, err := juno1Chain.InstantiateContract( + ctx, juno1User.KeyName(), ibcReflectSendCodeId, "{}", true) + require.NoError(t, err) + + // Store reflect.wasm contract + reflectCodeId, err := juno2Chain.StoreContract( + ctx, juno2User.KeyName(), "sample_contracts/reflect.wasm") + require.NoError(t, err) + + // Instantiate reflect.wasm contract + _, err = juno2Chain.InstantiateContract( + ctx, juno2User.KeyName(), reflectCodeId, "{}", true) + require.NoError(t, err) + + // Store ibc_reflect.wasm contract + ibcReflectCodeId, err := juno2Chain.StoreContract( + ctx, juno2User.KeyName(), "sample_contracts/ibc_reflect.wasm") + require.NoError(t, err) + + // Instantiate ibc_reflect_send.wasm contract + initMsg := "{\"reflect_code_id\":" + reflectCodeId + "}" + ibcReflectContractAddr, err := juno2Chain.InstantiateContract( + ctx, juno2User.KeyName(), ibcReflectCodeId, initMsg, true) + require.NoError(t, err) + + err = testutil.WaitForBlocks(ctx, 2, juno1, juno2) + require.NoError(t, err) + + // Set up channel + ibcReflectSendPortId := "wasm." + ibcReflectSendContractAddr + ibcReflectPortId := "wasm." + ibcReflectContractAddr + err = r.CreateChannel(ctx, eRep, ibcPath, ibc.CreateChannelOptions{ + SourcePortName: ibcReflectSendPortId, + DestPortName: ibcReflectPortId, + Order: ibc.Ordered, + Version: "ibc-reflect-v1", + }) + require.NoError(t, err) + + // Wait for the channel to get set up and whoami message to exchange + err = testutil.WaitForBlocks(ctx, 10, juno1, juno2) + require.NoError(t, err) + + // Get contract channel + juno1ChannelInfo, err := r.GetChannels(ctx, eRep, juno1.Config().ChainID) + require.NoError(t, err) + juno1ChannelID := juno1ChannelInfo[len(juno1ChannelInfo)-1].ChannelID + + // Query ibc_reflect_send contract on Juno1 for remote address (populated via ibc) + queryMsg := ReflectSendQueryMsg{Account: &AccountQuery{ChannelID: juno1ChannelID}} + var ibcReflectSendResponse IbcReflectSendResponseData + err = juno1Chain.QueryContract(ctx, ibcReflectSendContractAddr, queryMsg, &ibcReflectSendResponse) + require.NoError(t, err) + require.NotEmpty(t, ibcReflectSendResponse.Data.RemoteAddr) + + // Query ibc_reflect contract on Juno2 for local account address + var ibcReflectResponse IbcReflectResponseData + err = juno2Chain.QueryContract(ctx, ibcReflectContractAddr, queryMsg, &ibcReflectResponse) + require.NoError(t, err) + require.NotEmpty(t, ibcReflectResponse.Data.Account) + + // Verify that these addresses match, a match is a successful test run + // - ibc_reflect_send contract (Juno1) remote address (retrieved via ibc) + // - ibc_reflect contract (Juno2) account address populated locally + require.Equal(t, ibcReflectSendResponse.Data.RemoteAddr, ibcReflectResponse.Data.Account) +} + +type ReflectSendQueryMsg struct { + Admin *struct{} `json:"admin,omitempty"` + ListAccounts *struct{} `json:"list_accounts,omitempty"` + Account *AccountQuery `json:"account,omitempty"` +} + +type AccountQuery struct { + ChannelID string `json:"channel_id"` +} + +type Coin struct { + Denom string `json:"denom"` // type, eg. "ATOM" + Amount string `json:"amount"` // string encoing of decimal value, eg. "12.3456" +} + +type Coins []Coin + +type IbcReflectSendAccountResponse struct { + LastUpdateTime uint64 `json:"last_update_time,string"` + RemoteAddr string `json:"remote_addr"` + RemoteBalance Coins `json:"remote_balance"` +} + +// ibc_reflect_send response data +type IbcReflectSendResponseData struct { + Data IbcReflectSendAccountResponse `json:"data"` +} + +type IbcReflectAccountResponse struct { + Account string `json:"account"` +} + +// ibc_reflect response data +type IbcReflectResponseData struct { + Data IbcReflectAccountResponse `json:"data"` +} diff --git a/examples/ibc/wasm/wasm_icq_test.go b/examples/ibc/wasm/wasm_icq_test.go new file mode 100644 index 000000000..ebc90eabb --- /dev/null +++ b/examples/ibc/wasm/wasm_icq_test.go @@ -0,0 +1,406 @@ +package wasm + +import ( + "context" + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + "strconv" + "testing" + "time" + + "cosmossdk.io/math" + "github.com/strangelove-ventures/interchaintest/v7" + cosmosChain "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos/wasm" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/internal/dockerutil" + "github.com/strangelove-ventures/interchaintest/v7/relayer" + "github.com/strangelove-ventures/interchaintest/v7/testreporter" + "github.com/strangelove-ventures/interchaintest/v7/testutil" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "go.uber.org/zap/zaptest" +) + +// TestInterchainQueriesWASM is a test case that performs a round trip query from an ICQ wasm contract <> ICQ module. +// On the sender chain, CosmWasm capability is required to instantiate/execute the smart contract. On the receiver chain, +// the ICQ module is required to be present in order to receive interchain queries. +func TestInterchainQueriesWASM(t *testing.T) { + //TODO (1): force relayer to use specific versions of the chains configured in the file. + //os.Setenv("IBCTEST_CONFIGURED_CHAINS", "./icq_wasm_configured_chains.yaml") + + //TODO (2): use Juno as sender "ghcr.io/strangelove-ventures/heighliner/juno:v10.1.0" + //and Strangelove's icqd (or another chain with ICQ module present) as receiver. + + logger := zaptest.NewLogger(t) + + if testing.Short() { + t.Skip() + } + + client, network := interchaintest.DockerSetup(t) + f, err := interchaintest.CreateLogFile(fmt.Sprintf("wasm_ibc_test_%d.json", time.Now().Unix())) + require.NoError(t, err) + rep := testreporter.NewReporter(f) + eRep := rep.RelayerExecReporter(t) + ctx := context.Background() + contractFilePath := "sample_contracts/icq.wasm" //Contract that will be initialized on chain + + wasmImage := ibc.DockerImage{ + Repository: "ghcr.io/strangelove-ventures/heighliner/wasmd", + Version: "v0.0.1", + UidGid: dockerutil.GetHeighlinerUserString(), + } + + genesisAllowICQ := map[string]interface{}{ + "interchainquery": map[string]interface{}{ + "host_port": "icqhost", + "params": map[string]interface{}{ + "host_enabled": true, + "allow_queries": []interface{}{"/cosmos.bank.v1beta1.Query/AllBalances"}, + }, + }, + } + + minVal := 1 + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ + { + ChainName: "sender", + NumValidators: &minVal, + ChainConfig: ibc.ChainConfig{ + Type: "cosmos", + Name: "sender", + ChainID: "sender", + Images: []ibc.DockerImage{wasmImage}, + Bin: "wasmd", + Bech32Prefix: "wasm", + Denom: "uatom", + GasPrices: "0.00uatom", + TrustingPeriod: "300h", + GasAdjustment: 1.1, + EncodingConfig: wasm.WasmEncoding(), + ModifyGenesis: modifyGenesisAtPath(genesisAllowICQ, "app_state"), + }}, + { + ChainName: "receiver", + NumValidators: &minVal, + ChainConfig: ibc.ChainConfig{ + Type: "cosmos", + Name: "receiver", + ChainID: "receiver", + Images: []ibc.DockerImage{wasmImage}, + Bin: "wasmd", + Bech32Prefix: "wasm", + Denom: "uatom", + GasPrices: "0.00uatom", + TrustingPeriod: "300h", + GasAdjustment: 1.1, + EncodingConfig: wasm.WasmEncoding(), + ModifyGenesis: modifyGenesisAtPath(genesisAllowICQ, "app_state"), + }}, + }) + + chains, err := cf.Chains(t.Name()) + require.NoError(t, err) + chain1, chain2 := chains[0], chains[1] + + // Get a relayer instance + r := interchaintest.NewBuiltinRelayerFactory( + ibc.CosmosRly, + logger, + relayer.RelayerOptionExtraStartFlags{Flags: []string{"-p", "events", "-b", "100"}}, + ).Build(t, client, network) + + // Build the network; spin up the chains and configure the relayer + const pathName = "test1-test2" + const relayerName = "relayer" + + ic := interchaintest.NewInterchain(). + AddChain(chain1). + AddChain(chain2). + AddRelayer(r, relayerName). + AddLink(interchaintest.InterchainLink{ + Chain1: chain1, + Chain2: chain2, + Relayer: r, + Path: pathName, + }) + + logger.Info("ic.Build()") + require.NoError(t, ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ + TestName: t.Name(), + Client: client, + NetworkID: network, + // BlockDatabaseFile: interchaintest.DefaultBlockDatabaseFilepath(), + SkipPathCreation: false, + })) + + // Wait a few blocks for user accounts to be created on chain + logger.Info("wait for user accounts") + err = testutil.WaitForBlocks(ctx, 5, chain1, chain2) + require.NoError(t, err) + + // Fund user accounts so we can query balances + chain1UserAmt := math.NewInt(10_000_000_000) + chain2UserAmt := math.NewInt(99_999_999_999) + chain1User := interchaintest.GetAndFundTestUsers(t, ctx, t.Name(), chain1UserAmt.Int64(), chain1)[0] + chain2User := interchaintest.GetAndFundTestUsers(t, ctx, t.Name(), chain2UserAmt.Int64(), chain2)[0] + + err = testutil.WaitForBlocks(ctx, 5, chain1, chain2) + require.NoError(t, err) + + chain1UserAddress := chain1User.FormattedAddress() + require.NotEmpty(t, chain1UserAddress) + + chain2UserAddress := chain2User.FormattedAddress() + logger.Info("Address", zap.String("chain 2 user", chain2UserAddress)) + require.NotEmpty(t, chain2UserAddress) + + chain1UserBalInitial, err := chain1.GetBalance(ctx, chain1UserAddress, chain1.Config().Denom) + require.NoError(t, err) + require.True(t, chain1UserBalInitial.Equal(chain1UserAmt)) + + chain2UserBalInitial, err := chain2.GetBalance(ctx, chain2UserAddress, chain2.Config().Denom) + require.NoError(t, err) + require.True(t, chain2UserBalInitial.Equal(chain2UserAmt)) + + logger.Info("instantiating contract") + initMessage := "{\"default_timeout\": 1000}" + chain1CChain := chain1.(*cosmosChain.CosmosChain) + + wasmIcqCodeId, err := chain1CChain.StoreContract(ctx, chain1User.KeyName(), contractFilePath) + require.NoError(t, err) + err = testutil.WaitForBlocks(ctx, 5, chain1, chain2) + require.NoError(t, err) + + //Instantiate the smart contract on the test chain, facilitating testing of ICQ WASM functionality + contractAddr, err := chain1CChain.InstantiateContract(ctx, chain1User.KeyName(), wasmIcqCodeId, initMessage, true) + require.NoError(t, err) + logger.Info("icq contract deployed", zap.String("contractAddr", contractAddr)) + + err = testutil.WaitForBlocks(ctx, 5, chain1, chain2) + require.NoError(t, err) + + icqWasmPortId := "wasm." + contractAddr + destPort := "icqhost" + // Create channel between icq wasm contract <> icq module. + err = r.CreateChannel(ctx, eRep, pathName, ibc.CreateChannelOptions{ + SourcePortName: icqWasmPortId, + DestPortName: destPort, + Order: ibc.Unordered, + Version: "icq-1", + }) + require.NoError(t, err) + err = testutil.WaitForBlocks(ctx, 5, chain1, chain2) + require.NoError(t, err) + + // Query for the recently created channel-id. + chain2Channels, err := r.GetChannels(ctx, eRep, chain2.Config().ChainID) + require.NoError(t, err) + + for _, c1 := range chain2Channels { + logger.Info("Channel", zap.String("Info", fmt.Sprintf("Channel ID: %s, Port ID: %s, Version: %s, Counterparty Channel ID: %s, Counterparty Port ID: %s", c1.ChannelID, c1.PortID, c1.Version, c1.Counterparty.ChannelID, c1.Counterparty.PortID))) + } + + require.NoError(t, err) + channel := FirstWithPort(chain2Channels, destPort) + require.NotNil(t, channel) + require.NotEmpty(t, channel.Counterparty.ChannelID) + + // Start the relayer and set the cleanup function. + err = r.StartRelayer(ctx, eRep, pathName) + require.NoError(t, err) + + t.Cleanup( + func() { + err := r.StopRelayer(ctx, eRep) + if err != nil { + t.Logf("an error occured while stopping the relayer: %s", err) + } + }, + ) + + // Wait a few blocks for the relayer to start. + err = testutil.WaitForBlocks(ctx, 5, chain1, chain2) + require.NoError(t, err) + logger.Info("channel", zap.String("info", fmt.Sprintf("Channel Port: %s, Channel ID: %s, Counterparty Channel ID: %s", channel.PortID, channel.ChannelID, channel.Counterparty.ChannelID))) + + //Query for the balances of an account on the counterparty chain using interchain queries. + //Get the base64 encoded chain2 user address in the format required by the AllBalances query + chain2UserAddrQuery := fmt.Sprintf(`{"address":"%s"}`, chain2UserAddress) + chain2UserAddrQueryB64 := base64.StdEncoding.EncodeToString([]byte(chain2UserAddrQuery)) + + // Get current block height for chain 2 + cmd := []string{chain2.Config().Bin, "status", + "--node", chain2.GetRPCAddress(), + "--home", chain2.HomeDir(), + } + stdout, _, err := chain2.Exec(ctx, cmd, nil) + require.NoError(t, err) + blockHeightC2 := &statusResults{} + err = json.Unmarshal(stdout, blockHeightC2) + require.NoError(t, err) + + //and chain 1 + // Get current block height + cmd = []string{chain1.Config().Bin, "status", + "--node", chain1.GetRPCAddress(), + "--home", chain1.HomeDir(), + } + stdout, _, err = chain1.Exec(ctx, cmd, nil) + require.NoError(t, err) + blockHeightC1 := &statusResults{} + err = json.Unmarshal(stdout, blockHeightC1) + require.NoError(t, err) + + logger.Info("Chain height", zap.String("Chain 1", blockHeightC1.SyncInfo.Height), zap.String("Chain 2", blockHeightC2.SyncInfo.Height)) + + query := executeQuery{ + Query: msgQuery{ + Timeout: 1000, + Channel: channel.ChannelID, + Requests: []RequestQuery{ //can't use abci.RequestQuery since Height/Prove JSON fields are omitted which causes contract errors + { + Height: 0, + Prove: false, + Path: "/cosmos.bank.v1beta1.Query/AllBalances", + Data: []byte(chain2UserAddrQueryB64), + }, + }, + }, + } + + b, err := json.Marshal(query) + require.NoError(t, err) + msg := string(b) + logger.Info("Executing msg ->", zap.String("msg", msg)) + + //Query the contract on chain 1. The contract makes an interchain query to chain 2 to get the chain 2 user's balance. + hash, err := chain1CChain.ExecuteContract(ctx, chain1User.KeyName(), contractAddr, msg) + + require.NoError(t, err) + + // Check the results from the interchain query above. + cmd = []string{chain1.Config().Bin, "query", "tx", hash, + "--node", chain1.GetRPCAddress(), + "--home", chain1.HomeDir(), + "--chain-id", chain1.Config().ChainID, + "--output", "json", + } + _, _, err = chain1.Exec(ctx, cmd, nil) + require.NoError(t, err) + + // Wait a few blocks for query to be sent to counterparty. + err = testutil.WaitForBlocks(ctx, 5, chain1, chain2) + require.NoError(t, err) + + // Check the results from the interchain query above. + cmd = []string{chain1.Config().Bin, "query", "wasm", "contract-state", "all", contractAddr, + "--node", chain1.GetRPCAddress(), + "--home", chain1.HomeDir(), + "--chain-id", chain1.Config().ChainID, + "--output", "json", + } + + stdout, _, err = chain1.Exec(ctx, cmd, nil) + require.NoError(t, err) + results := &contractStateResp{} + err = json.Unmarshal(stdout, results) + require.NoError(t, err) + hasIcqQuery := false + + for _, kv := range results.Models { + keyBytes, _ := hex.DecodeString(kv.Key) + valueBytes, err := base64.StdEncoding.DecodeString(kv.Value) + require.NoError(t, err) + if string(keyBytes) == "query_result_counter" { + res, err := strconv.Atoi(string(valueBytes)) + require.NoError(t, err) + if res > 0 { + hasIcqQuery = true + logger.Info("ICQ query result counter", zap.Int("counter", res)) + } + } + } + require.Equal(t, hasIcqQuery, true) +} + +func FirstWithPort(channels []ibc.ChannelOutput, port string) *ibc.ChannelOutput { + for _, channel := range channels { + if channel.PortID == port { + return &channel + } + } + return nil +} + +type RequestQuery struct { + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Height int64 `protobuf:"varint,3,opt,name=height,proto3" json:"height"` //do NOT 'omitempty' for JSON field or contract queries will error + Prove bool `protobuf:"varint,4,opt,name=prove,proto3" json:"prove"` //do NOT 'omitempty' for JSON field or contract queries will error +} + +type msgQuery struct { + Channel string `json:"channel"` + Requests []RequestQuery `json:"requests"` + Timeout uint64 `json:"timeout"` +} + +type executeQuery struct { + Query msgQuery `json:"query"` +} + +type kvPair struct { + Key string // hex encoded string + Value string // b64 encoded json +} + +type contractStateResp struct { + Models []kvPair +} + +type statusResults struct { + SyncInfo struct { + Height string `json:"latest_block_height"` + } `json:"SyncInfo"` +} + +func modifyGenesisAtPath(insertedBlock map[string]interface{}, key string) func(ibc.ChainConfig, []byte) ([]byte, error) { + return func(chainConfig ibc.ChainConfig, genbz []byte) ([]byte, error) { + g := make(map[string]interface{}) + if err := json.Unmarshal(genbz, &g); err != nil { + return nil, fmt.Errorf("failed to unmarshal genesis file: %w", err) + } + + //Get the section of the genesis file under the given key (e.g. "app_state") + genesisBlockI, ok := g[key] + if !ok { + return nil, fmt.Errorf("genesis json does not have top level key: %s", key) + } + + blockBytes, mErr := json.Marshal(genesisBlockI) + if mErr != nil { + return nil, fmt.Errorf("genesis json marshal error for block with key: %s", key) + } + + genesisBlock := make(map[string]interface{}) + mErr = json.Unmarshal(blockBytes, &genesisBlock) + if mErr != nil { + return nil, fmt.Errorf("genesis json unmarshal error for block with key: %s", key) + } + + for k, v := range insertedBlock { + genesisBlock[k] = v + } + + g[key] = genesisBlock + out, err := json.Marshal(g) + if err != nil { + return nil, fmt.Errorf("failed to marshal genesis bytes to json: %w", err) + } + return out, nil + } +} diff --git a/examples/polkadot/polkadot_chain_test.go b/examples/polkadot/polkadot_chain_test.go index a20c579dc..b3fc14656 100644 --- a/examples/polkadot/polkadot_chain_test.go +++ b/examples/polkadot/polkadot_chain_test.go @@ -5,11 +5,20 @@ import ( "fmt" "testing" +<<<<<<< HEAD interchaintest "github.com/strangelove-ventures/interchaintest/v6" "github.com/strangelove-ventures/interchaintest/v6/chain/polkadot" "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/testreporter" "github.com/strangelove-ventures/interchaintest/v6/testutil" +======= + "cosmossdk.io/math" + "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/chain/polkadot" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/testreporter" + "github.com/strangelove-ventures/interchaintest/v7/testutil" +>>>>>>> 8e02aef (refactor: use cosmos sdk Int type for balances/token amounts (#679)) "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" ) @@ -83,22 +92,24 @@ func TestPolkadotComposableChainStart(t *testing.T) { err = testutil.WaitForBlocks(ctx, 2, chain) require.NoError(t, err, "polkadot chain failed to make blocks") - PARACHAIN_DEFAULT_AMOUNT := 1_152_921_504_606_847_000 - RELAYCHAIN_DEFAULT_AMOUNT := 1_100_000_000_000_000_000 - FAUCET_AMOUNT := 100_000_000_000_000_000 // set in interchain.go/global + PARACHAIN_DEFAULT_AMOUNT := math.NewInt(1_152_921_504_606_847_000) + RELAYCHAIN_DEFAULT_AMOUNT := math.NewInt(1_100_000_000_000_000_000) + FAUCET_AMOUNT := math.NewInt(100_000_000_000_000_000) // set in interchain.go/global //RELAYER_AMOUNT := 1_000_000_000_000 // set in interchain.go/global // Check the faucet amounts polkadotFaucetAddress, err := polkadotChain.GetAddress(ctx, "faucet") require.NoError(t, err) + polkadotFaucetAmount, err := polkadotChain.GetBalance(ctx, string(polkadotFaucetAddress), polkadotChain.Config().Denom) require.NoError(t, err) fmt.Println("Polkadot faucet amount: ", polkadotFaucetAmount) - require.Equal(t, int64(FAUCET_AMOUNT), polkadotFaucetAmount, "Polkadot faucet amount not expected") + require.True(t, polkadotFaucetAmount.Equal(FAUCET_AMOUNT), "Polkadot faucet amount not expected") + parachainFaucetAmount, err := polkadotChain.GetBalance(ctx, string(polkadotFaucetAddress), "") require.NoError(t, err) fmt.Println("Parachain faucet amount: ", parachainFaucetAmount) - require.Equal(t, int64(FAUCET_AMOUNT), parachainFaucetAmount, "Parachain faucet amount not expected") + require.True(t, parachainFaucetAmount.Equal(FAUCET_AMOUNT), "Parachain faucet amount not expected") // Check alice polkadotAliceAddress, err := polkadotChain.GetAddress(ctx, "alice") @@ -106,11 +117,12 @@ func TestPolkadotComposableChainStart(t *testing.T) { polkadotAliceAmount, err := polkadotChain.GetBalance(ctx, string(polkadotAliceAddress), polkadotChain.Config().Denom) require.NoError(t, err) fmt.Println("Polkadot alice amount: ", polkadotAliceAmount) - require.Equal(t, int64(RELAYCHAIN_DEFAULT_AMOUNT), polkadotAliceAmount, "Relaychain alice amount not expected") + require.True(t, polkadotAliceAmount.Equal(RELAYCHAIN_DEFAULT_AMOUNT), "Relaychain alice amount not expected") + parachainAliceAmount, err := polkadotChain.GetBalance(ctx, string(polkadotAliceAddress), "") require.NoError(t, err) fmt.Println("Parachain alice amount: ", parachainAliceAmount) - require.Equal(t, int64(PARACHAIN_DEFAULT_AMOUNT), parachainAliceAmount, "Parachain alice amount not expected") + require.True(t, parachainAliceAmount.Equal(PARACHAIN_DEFAULT_AMOUNT), "Parachain alice amount not expected") // Check alice stash polkadotAliceStashAddress, err := polkadotChain.GetAddress(ctx, "alicestash") @@ -118,11 +130,12 @@ func TestPolkadotComposableChainStart(t *testing.T) { polkadotAliceStashAmount, err := polkadotChain.GetBalance(ctx, string(polkadotAliceStashAddress), polkadotChain.Config().Denom) require.NoError(t, err) fmt.Println("Polkadot alice stash amount: ", polkadotAliceStashAmount) - require.Equal(t, int64(RELAYCHAIN_DEFAULT_AMOUNT), polkadotAliceStashAmount, "Relaychain alice stash amount not expected") + require.True(t, polkadotAliceStashAmount.Equal(RELAYCHAIN_DEFAULT_AMOUNT), "Relaychain alice stash amount not expected") + parachainAliceStashAmount, err := polkadotChain.GetBalance(ctx, string(polkadotAliceStashAddress), "") require.NoError(t, err) fmt.Println("Parachain alice stash amount: ", parachainAliceStashAmount) - require.Equal(t, int64(PARACHAIN_DEFAULT_AMOUNT), parachainAliceStashAmount, "Parachain alice stash amount not expected") + require.True(t, parachainAliceStashAmount.Equal(PARACHAIN_DEFAULT_AMOUNT), "Parachain alice stash amount not expected") // Check bob polkadotBobAddress, err := polkadotChain.GetAddress(ctx, "bob") @@ -130,11 +143,12 @@ func TestPolkadotComposableChainStart(t *testing.T) { polkadotBobAmount, err := polkadotChain.GetBalance(ctx, string(polkadotBobAddress), polkadotChain.Config().Denom) require.NoError(t, err) fmt.Println("Polkadot bob amount: ", polkadotBobAmount) - require.Equal(t, int64(RELAYCHAIN_DEFAULT_AMOUNT), polkadotBobAmount, "Relaychain bob amount not expected") + require.True(t, polkadotBobAmount.Equal(RELAYCHAIN_DEFAULT_AMOUNT), "Relaychain bob amount not expected") + parachainBobAmount, err := polkadotChain.GetBalance(ctx, string(polkadotBobAddress), "") require.NoError(t, err) fmt.Println("Parachain bob amount: ", parachainBobAmount) - require.Equal(t, int64(PARACHAIN_DEFAULT_AMOUNT), parachainBobAmount, "Parachain bob amount not expected") + require.True(t, parachainBobAmount.Equal(PARACHAIN_DEFAULT_AMOUNT), "Parachain bob amount not expected") // Check bob stash polkadotBobStashAddress, err := polkadotChain.GetAddress(ctx, "bobstash") @@ -142,30 +156,33 @@ func TestPolkadotComposableChainStart(t *testing.T) { polkadotBobStashAmount, err := polkadotChain.GetBalance(ctx, string(polkadotBobStashAddress), polkadotChain.Config().Denom) require.NoError(t, err) fmt.Println("Polkadot bob stash amount: ", polkadotBobStashAmount) - require.Equal(t, int64(RELAYCHAIN_DEFAULT_AMOUNT), polkadotBobStashAmount, "Relaychain bob stash amount not expected") + require.True(t, polkadotBobStashAmount.Equal(RELAYCHAIN_DEFAULT_AMOUNT), "Relaychain bob stash amount not expected") + parachainBobStashAmount, err := polkadotChain.GetBalance(ctx, string(polkadotBobStashAddress), "") require.NoError(t, err) fmt.Println("Parachain bob stash amount: ", parachainBobStashAmount) - require.Equal(t, int64(PARACHAIN_DEFAULT_AMOUNT), parachainBobStashAmount, "Parachain bob stash amount not expected") + require.True(t, parachainBobStashAmount.Equal(PARACHAIN_DEFAULT_AMOUNT), "Parachain bob stash amount not expected") // Fund user1 on both relay and parachain, must wait a block to fund user2 due to same faucet address - fundAmount := int64(12_333_000_000_000) - users1 := interchaintest.GetAndFundTestUsers(t, ctx, "user1", fundAmount, polkadotChain) + fundAmount := math.NewInt(12_333_000_000_000) + users1 := interchaintest.GetAndFundTestUsers(t, ctx, "user1", fundAmount.Int64(), polkadotChain) user1 := users1[0] err = testutil.WaitForBlocks(ctx, 2, chain) require.NoError(t, err, "polkadot chain failed to make blocks") // Fund user2 on both relay and parachain, check that user1 was funded properly - users2 := interchaintest.GetAndFundTestUsers(t, ctx, "user2", fundAmount, polkadotChain) + users2 := interchaintest.GetAndFundTestUsers(t, ctx, "user2", fundAmount.Int64(), polkadotChain) user2 := users2[0] polkadotUser1Amount, err := polkadotChain.GetBalance(ctx, user1.FormattedAddress(), polkadotChain.Config().Denom) require.NoError(t, err) fmt.Println("Polkadot user1 amount: ", polkadotUser1Amount) - require.Equal(t, fundAmount, polkadotUser1Amount, "Initial polkadot user1 amount not expected") + require.True(t, polkadotUser1Amount.Equal(fundAmount), "Initial polkadot user1 amount not expected") + parachainUser1Amount, err := polkadotChain.GetBalance(ctx, user1.FormattedAddress(), "") require.NoError(t, err) fmt.Println("Parachain user1 amount: ", parachainUser1Amount) - require.Equal(t, fundAmount, parachainUser1Amount, "Initial parachain user1 amount not expected") + require.True(t, parachainUser1Amount.Equal(fundAmount), "Initial parachain user1 amount not expected") + err = testutil.WaitForBlocks(ctx, 2, chain) require.NoError(t, err, "polkadot chain failed to make blocks") @@ -173,14 +190,15 @@ func TestPolkadotComposableChainStart(t *testing.T) { polkadotUser2Amount, err := polkadotChain.GetBalance(ctx, user2.FormattedAddress(), polkadotChain.Config().Denom) require.NoError(t, err) fmt.Println("Polkadot user2 amount: ", polkadotUser2Amount) - require.Equal(t, fundAmount, polkadotUser2Amount, "Initial polkadot user2 amount not expected") + require.True(t, polkadotUser2Amount.Equal(fundAmount), "Initial polkadot user2 amount not expected") + parachainUser2Amount, err := polkadotChain.GetBalance(ctx, user2.FormattedAddress(), "") require.NoError(t, err) fmt.Println("Parachain user2 amount: ", parachainUser2Amount) - require.Equal(t, fundAmount, parachainUser2Amount, "Initial parachain user2 amount not expected") + require.True(t, parachainUser2Amount.Equal(fundAmount), "Initial parachain user2 amount not expected") // Transfer 1T units from user1 to user2 on both chains - txAmount := int64(1_000_000_000_000) + txAmount := math.NewInt(1_000_000_000_000) polkadotTxUser1ToUser2 := ibc.WalletAmount{ Address: user2.FormattedAddress(), Amount: txAmount, @@ -203,18 +221,20 @@ func TestPolkadotComposableChainStart(t *testing.T) { polkadotUser1Amount, err = polkadotChain.GetBalance(ctx, user1.FormattedAddress(), polkadotChain.Config().Denom) require.NoError(t, err) fmt.Println("Polkadot user1 amount: ", polkadotUser1Amount) - require.LessOrEqual(t, polkadotUser1Amount, fundAmount-txAmount, "Final polkadot user1 amount not expected") + require.True(t, polkadotUser1Amount.LTE(fundAmount.Sub(txAmount)), "Final polkadot user1 amount not expected") + polkadotUser2Amount, err = polkadotChain.GetBalance(ctx, user2.FormattedAddress(), polkadotChain.Config().Denom) require.NoError(t, err) fmt.Println("Polkadot user2 amount: ", polkadotUser2Amount) - require.Equal(t, fundAmount+txAmount, polkadotUser2Amount, "Final polkadot user2 amount not expected") + require.True(t, fundAmount.Add(txAmount).Equal(polkadotUser2Amount), "Final polkadot user2 amount not expected") + parachainUser1Amount, err = polkadotChain.GetBalance(ctx, user1.FormattedAddress(), "") require.NoError(t, err) fmt.Println("Parachain user1 amount: ", parachainUser1Amount) - require.LessOrEqual(t, parachainUser1Amount, fundAmount-txAmount, "Final parachain user1 amount not expected") + require.True(t, parachainUser1Amount.LTE(fundAmount.Sub(txAmount)), "Final parachain user1 amount not expected") + parachainUser2Amount, err = polkadotChain.GetBalance(ctx, user2.FormattedAddress(), "") require.NoError(t, err) fmt.Println("Parachain user2 amount: ", parachainUser2Amount) - require.Equal(t, fundAmount+txAmount, parachainUser2Amount, "Final parachain user2 amount not expected") - + require.True(t, fundAmount.Add(txAmount).Equal(parachainUser2Amount), "Final parachain user2 amount not expected") } diff --git a/examples/polkadot/push_wasm_client_code_test.go b/examples/polkadot/push_wasm_client_code_test.go new file mode 100644 index 000000000..2f55ae5b7 --- /dev/null +++ b/examples/polkadot/push_wasm_client_code_test.go @@ -0,0 +1,183 @@ +package polkadot_test + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "testing" + + "cosmossdk.io/math" + "github.com/icza/dyno" + "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/testreporter" + "github.com/strangelove-ventures/interchaintest/v7/testutil" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest" +) + +const ( + heightDelta = uint64(20) + votingPeriod = "30s" + maxDepositPeriod = "10s" +) + +// Spin up a simd chain, push a contract, and get that contract code from chain +func TestPushWasmClientCode(t *testing.T) { + if testing.Short() { + t.Skip() + } + + t.Parallel() + + client, network := interchaintest.DockerSetup(t) + + rep := testreporter.NewNopReporter() + eRep := rep.RelayerExecReporter(t) + + ctx := context.Background() + + // Override config files to support an ~2.5MB contract + configFileOverrides := make(map[string]any) + + appTomlOverrides := make(testutil.Toml) + configTomlOverrides := make(testutil.Toml) + + apiOverrides := make(testutil.Toml) + apiOverrides["rpc-max-body-bytes"] = 2_000_000 + appTomlOverrides["api"] = apiOverrides + + rpcOverrides := make(testutil.Toml) + rpcOverrides["max_body_bytes"] = 2_000_000 + rpcOverrides["max_header_bytes"] = 2_100_000 + configTomlOverrides["rpc"] = rpcOverrides + + //mempoolOverrides := make(testutil.Toml) + //mempoolOverrides["max_tx_bytes"] = 6000000 + //configTomlOverrides["mempool"] = mempoolOverrides + + configFileOverrides["config/app.toml"] = appTomlOverrides + configFileOverrides["config/config.toml"] = configTomlOverrides + + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ + {ChainConfig: ibc.ChainConfig{ + Type: "cosmos", + Name: "ibc-go-simd", + ChainID: "simd", + Images: []ibc.DockerImage{ + { + Repository: "ghcr.io/strangelove-ventures/heighliner/ibc-go-simd", + Version: "feat-wasm-clients", + UidGid: "1025:1025", + }, + }, + Bin: "simd", + Bech32Prefix: "cosmos", + Denom: "stake", + GasPrices: "0.00stake", + GasAdjustment: 1.3, + TrustingPeriod: "504h", + //EncodingConfig: WasmClientEncoding(), + NoHostMount: true, + ConfigFileOverrides: configFileOverrides, + ModifyGenesis: modifyGenesisShortProposals(votingPeriod, maxDepositPeriod), + }, + }, + }) + + chains, err := cf.Chains(t.Name()) + require.NoError(t, err) + + simd := chains[0] + + ic := interchaintest.NewInterchain(). + AddChain(simd) + + require.NoError(t, ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ + TestName: t.Name(), + Client: client, + NetworkID: network, + BlockDatabaseFile: interchaintest.DefaultBlockDatabaseFilepath(), + SkipPathCreation: true, // Skip path creation, so we can have granular control over the process + })) + + t.Cleanup(func() { + _ = ic.Close() + }) + + // Create and Fund User Wallets + fundAmount := math.NewInt(10_000_000_000) + users := interchaintest.GetAndFundTestUsers(t, ctx, "default", fundAmount.Int64(), simd) + simd1User := users[0] + + simd1UserBalInitial, err := simd.GetBalance(ctx, simd1User.FormattedAddress(), simd.Config().Denom) + require.NoError(t, err) + require.True(t, simd1UserBalInitial.Equal(fundAmount)) + + simdChain := simd.(*cosmos.CosmosChain) + + // Verify a normal user cannot push a wasm light client contract + _, err = simdChain.StoreClientContract(ctx, simd1User.KeyName(), "ics10_grandpa_cw.wasm") + require.ErrorContains(t, err, "invalid authority") + + proposal := cosmos.TxProposalv1{ + Metadata: "none", + Deposit: "500000000" + simdChain.Config().Denom, // greater than min deposit + Title: "Grandpa Contract", + Summary: "new grandpa contract", + } + + proposalTx, codeHash, err := simdChain.PushNewWasmClientProposal(ctx, simd1User.KeyName(), "ics10_grandpa_cw.wasm", proposal) + require.NoError(t, err, "error submitting new wasm contract proposal tx") + + height, err := simdChain.Height(ctx) + require.NoError(t, err, "error fetching height before submit upgrade proposal") + + err = simdChain.VoteOnProposalAllValidators(ctx, proposalTx.ProposalID, cosmos.ProposalVoteYes) + require.NoError(t, err, "failed to submit votes") + + _, err = cosmos.PollForProposalStatus(ctx, simdChain, height, height+heightDelta, proposalTx.ProposalID, cosmos.ProposalStatusPassed) + require.NoError(t, err, "proposal status did not change to passed in expected number of blocks") + + err = testutil.WaitForBlocks(ctx, 2, simd) + require.NoError(t, err) + + var getCodeQueryMsgRsp GetCodeQueryMsgResponse + err = simdChain.QueryClientContractCode(ctx, codeHash, &getCodeQueryMsgRsp) + codeHashByte32 := sha256.Sum256(getCodeQueryMsgRsp.Code) + codeHash2 := hex.EncodeToString(codeHashByte32[:]) + t.Logf("Contract codeHash from code: %s", codeHash2) + require.NoError(t, err) + require.NotEmpty(t, getCodeQueryMsgRsp.Code) + require.Equal(t, codeHash, codeHash2) +} + +type GetCodeQueryMsgResponse struct { + Code []byte `json:"code"` +} + +func modifyGenesisShortProposals(votingPeriod string, maxDepositPeriod string) func(ibc.ChainConfig, []byte) ([]byte, error) { + return func(chainConfig ibc.ChainConfig, genbz []byte) ([]byte, error) { + g := make(map[string]interface{}) + if err := json.Unmarshal(genbz, &g); err != nil { + return nil, fmt.Errorf("failed to unmarshal genesis file: %w", err) + } + if err := dyno.Set(g, votingPeriod, "app_state", "gov", "params", "voting_period"); err != nil { + return nil, fmt.Errorf("failed to set voting period in genesis json: %w", err) + } + if err := dyno.Set(g, maxDepositPeriod, "app_state", "gov", "params", "max_deposit_period"); err != nil { + return nil, fmt.Errorf("failed to set voting period in genesis json: %w", err) + } + if err := dyno.Set(g, chainConfig.Denom, "app_state", "gov", "params", "min_deposit", 0, "denom"); err != nil { + return nil, fmt.Errorf("failed to set voting period in genesis json: %w", err) + } + out, err := json.Marshal(g) + if err != nil { + return nil, fmt.Errorf("failed to marshal genesis bytes to json: %w", err) + } + return out, nil + } +} diff --git a/go.mod b/go.mod index e97431040..c93ae2f4f 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/strangelove-ventures/interchaintest/v6 go 1.19 require ( + cosmossdk.io/math v1.0.1 github.com/99designs/keyring v1.2.2 github.com/BurntSushi/toml v1.3.2 github.com/ChainSafe/go-schnorrkel/1 v0.0.0-00010101000000-000000000000 @@ -41,10 +42,21 @@ require ( cloud.google.com/go v0.110.0 // indirect cloud.google.com/go/compute v1.18.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect +<<<<<<< HEAD cloud.google.com/go/iam v0.12.0 // indirect cloud.google.com/go/storage v1.28.1 // indirect cosmossdk.io/errors v1.0.0-beta.7 // indirect cosmossdk.io/math v1.0.0-rc.0 // indirect +======= + cloud.google.com/go/iam v1.1.0 // indirect + cloud.google.com/go/storage v1.30.1 // indirect + cosmossdk.io/api v0.3.1 // indirect + cosmossdk.io/core v0.5.1 // indirect + cosmossdk.io/depinject v1.0.0-alpha.3 // indirect + cosmossdk.io/errors v1.0.0 // indirect + cosmossdk.io/log v1.1.1-0.20230704160919-88f2c830b0ca // indirect + cosmossdk.io/tools/rosetta v0.2.1 // indirect +>>>>>>> 8e02aef (refactor: use cosmos sdk Int type for balances/token amounts (#679)) filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect diff --git a/ibc/chain.go b/ibc/chain.go index a66cf3523..d9120f5ba 100644 --- a/ibc/chain.go +++ b/ibc/chain.go @@ -3,6 +3,7 @@ package ibc import ( "context" + "cosmossdk.io/math" "github.com/docker/docker/client" ) @@ -63,7 +64,7 @@ type Chain interface { Height(ctx context.Context) (uint64, error) // GetBalance fetches the current balance for a specific account address and denom. - GetBalance(ctx context.Context, address string, denom string) (int64, error) + GetBalance(ctx context.Context, address string, denom string) (math.Int, error) // GetGasFeesInNativeDenom gets the fees in native denom for an amount of spent gas. GetGasFeesInNativeDenom(gasPaid int64) int64 diff --git a/ibc/types.go b/ibc/types.go index 87ff303c1..26abf7701 100644 --- a/ibc/types.go +++ b/ibc/types.go @@ -4,8 +4,14 @@ import ( "reflect" "strconv" +<<<<<<< HEAD simappparams "github.com/cosmos/cosmos-sdk/simapp/params" ibcexported "github.com/cosmos/ibc-go/v6/modules/core/03-connection/types" +======= + "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/types/module/testutil" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" +>>>>>>> 8e02aef (refactor: use cosmos sdk Int type for balances/token amounts (#679)) ) // ChainConfig defines the chain parameters requires to run an interchaintest testnet for a chain. @@ -182,7 +188,7 @@ func (i DockerImage) Ref() string { type WalletAmount struct { Address string Denom string - Amount int64 + Amount math.Int } type IBCTimeout struct { diff --git a/interchain.go b/interchain.go index c253fb3b7..9e0afb4cc 100644 --- a/interchain.go +++ b/interchain.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "cosmossdk.io/math" "github.com/docker/docker/client" "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/testreporter" @@ -351,7 +352,7 @@ func (ic *Interchain) genesisWalletAmounts(ctx context.Context) (map[ibc.Chain][ { Address: faucetAddresses[c], Denom: c.Config().Denom, - Amount: 100_000_000_000_000, // Faucet wallet gets 100T units of denom. + Amount: math.NewInt(100_000_000_000_000), // Faucet wallet gets 100T units of denom. }, } @@ -366,7 +367,7 @@ func (ic *Interchain) genesisWalletAmounts(ctx context.Context) (map[ibc.Chain][ walletAmounts[c] = append(walletAmounts[c], ibc.WalletAmount{ Address: wallet.FormattedAddress(), Denom: c.Config().Denom, - Amount: 1_000_000_000_000, // Every wallet gets 1t units of denom. + Amount: math.NewInt(1_000_000_000_000), // Every wallet gets 1t units of denom. }) } diff --git a/interchain_test.go b/interchain_test.go index 2de48b7b6..4bd5aaf94 100644 --- a/interchain_test.go +++ b/interchain_test.go @@ -5,18 +5,28 @@ import ( "fmt" "testing" + "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" +<<<<<<< HEAD interchaintest "github.com/strangelove-ventures/interchaintest/v6" "github.com/strangelove-ventures/interchaintest/v6/chain/cosmos" "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/relayer/rly" "github.com/strangelove-ventures/interchaintest/v6/testreporter" "github.com/strangelove-ventures/interchaintest/v6/testutil" +======= + "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/relayer/rly" + "github.com/strangelove-ventures/interchaintest/v7/testreporter" + "github.com/strangelove-ventures/interchaintest/v7/testutil" +>>>>>>> 8e02aef (refactor: use cosmos sdk Int type for balances/token amounts (#679)) "github.com/stretchr/testify/require" "go.uber.org/zap" "go.uber.org/zap/zaptest" @@ -200,6 +210,8 @@ func TestInterchain_CreateUser(t *testing.T) { NetworkID: network, })) + initBal := math.NewInt(10_000) + t.Run("with mnemonic", func(t *testing.T) { keyName := "mnemonic-user-name" registry := codectypes.NewInterfaceRegistry() @@ -218,7 +230,7 @@ func TestInterchain_CreateUser(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, mnemonic) - user, err := interchaintest.GetAndFundTestUserWithMnemonic(ctx, keyName, mnemonic, 10000, gaia0) + user, err := interchaintest.GetAndFundTestUserWithMnemonic(ctx, keyName, mnemonic, initBal.Int64(), gaia0) require.NoError(t, err) require.NoError(t, testutil.WaitForBlocks(ctx, 2, gaia0)) require.NotEmpty(t, user.Address()) @@ -226,13 +238,12 @@ func TestInterchain_CreateUser(t *testing.T) { actualBalance, err := gaia0.GetBalance(ctx, user.FormattedAddress(), gaia0.Config().Denom) require.NoError(t, err) - require.Equal(t, int64(10000), actualBalance) - + require.True(t, actualBalance.Equal(initBal)) }) t.Run("without mnemonic", func(t *testing.T) { keyName := "regular-user-name" - users := interchaintest.GetAndFundTestUsers(t, ctx, keyName, 10000, gaia0) + users := interchaintest.GetAndFundTestUsers(t, ctx, keyName, initBal.Int64(), gaia0) require.NoError(t, testutil.WaitForBlocks(ctx, 2, gaia0)) require.Len(t, users, 1) require.NotEmpty(t, users[0].Address()) @@ -240,7 +251,7 @@ func TestInterchain_CreateUser(t *testing.T) { actualBalance, err := gaia0.GetBalance(ctx, users[0].FormattedAddress(), gaia0.Config().Denom) require.NoError(t, err) - require.Equal(t, int64(10000), actualBalance) + require.True(t, actualBalance.Equal(initBal)) }) } @@ -300,7 +311,7 @@ func broadcastTxCosmosChainTest(t *testing.T, relayerImpl ibc.RelayerImplementat testUser := interchaintest.GetAndFundTestUsers(t, ctx, "gaia-user-1", 10_000_000, gaia0)[0] - sendAmount := int64(10000) + sendAmount := math.NewInt(10_000) t.Run("relayer starts", func(t *testing.T) { require.NoError(t, r.StartRelayer(ctx, eRep, pathName)) @@ -308,7 +319,12 @@ func broadcastTxCosmosChainTest(t *testing.T, relayerImpl ibc.RelayerImplementat t.Run("broadcast success", func(t *testing.T) { b := cosmos.NewBroadcaster(t, gaia0.(*cosmos.CosmosChain)) +<<<<<<< HEAD transferAmount := sdk.Coin{Denom: gaia0.Config().Denom, Amount: sdk.NewInt(sendAmount)} +======= + transferAmount := sdk.Coin{Denom: gaia0.Config().Denom, Amount: sendAmount} + memo := "" +>>>>>>> 8e02aef (refactor: use cosmos sdk Int type for balances/token amounts (#679)) msg := transfertypes.NewMsgTransfer( "transfer", @@ -333,7 +349,7 @@ func broadcastTxCosmosChainTest(t *testing.T, relayerImpl ibc.RelayerImplementat dstFinalBalance, err := gaia1.GetBalance(ctx, testUser.(*cosmos.CosmosWallet).FormattedAddressWithPrefix(gaia1.Config().Bech32Prefix), dstIbcDenom) require.NoError(t, err, "failed to get balance from dest chain") - require.Equal(t, sendAmount, dstFinalBalance) + require.True(t, dstFinalBalance.Equal(sendAmount)) }) } diff --git a/internal/blockdb/messages_view_test.go b/internal/blockdb/messages_view_test.go index 27ee871ba..2432b3f30 100644 --- a/internal/blockdb/messages_view_test.go +++ b/internal/blockdb/messages_view_test.go @@ -9,12 +9,21 @@ import ( "path/filepath" "testing" + "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/types" +<<<<<<< HEAD interchaintest "github.com/strangelove-ventures/interchaintest/v6" "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/relayer" "github.com/strangelove-ventures/interchaintest/v6/testreporter" "github.com/strangelove-ventures/interchaintest/v6/testutil" +======= + "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/relayer" + "github.com/strangelove-ventures/interchaintest/v7/testreporter" + "github.com/strangelove-ventures/interchaintest/v7/testutil" +>>>>>>> 8e02aef (refactor: use cosmos sdk Int type for balances/token amounts (#679)) "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" ) @@ -258,7 +267,7 @@ WHERE type = "/ibc.core.channel.v1.MsgChannelOpenConfirm" AND chain_id = ? transfer := ibc.WalletAmount{ Address: gaia1FaucetAddr, Denom: gaia0.Config().Denom, - Amount: txAmount, + Amount: math.NewInt(txAmount), } tx, err := gaia0.SendIBCTransfer(ctx, gaia0ChannelID, interchaintest.FaucetAccountKeyName, transfer, ibc.TransferOptions{}) require.NoError(t, err) diff --git a/test_user.go b/test_user.go index 0ab8c85df..f31697b0c 100644 --- a/test_user.go +++ b/test_user.go @@ -5,9 +5,16 @@ import ( "fmt" "testing" +<<<<<<< HEAD "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/internal/dockerutil" "github.com/strangelove-ventures/interchaintest/v6/testutil" +======= + "cosmossdk.io/math" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/internal/dockerutil" + "github.com/strangelove-ventures/interchaintest/v7/testutil" +>>>>>>> 8e02aef (refactor: use cosmos sdk Int type for balances/token amounts (#679)) "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" ) @@ -30,7 +37,7 @@ func GetAndFundTestUserWithMnemonic( err = chain.SendFunds(ctx, FaucetAccountKeyName, ibc.WalletAmount{ Address: user.FormattedAddress(), - Amount: amount, + Amount: math.NewInt(amount), Denom: chainCfg.Denom, }) if err != nil {