From aff1a1868a7dca67d02f5c9191bae70cd0af62bd Mon Sep 17 00:00:00 2001 From: Murad Karammaev Date: Fri, 9 Dec 2022 23:28:10 +0200 Subject: [PATCH 1/4] chore: use websocket to wait for new block Previous behaviour used to be HTTP polling busy loop. --- README.md | 2 + src/helpers/cosmos.ts | 21 ++++++--- src/helpers/env.ts | 14 ++++-- src/helpers/ica.ts | 2 +- src/helpers/icq.ts | 4 +- src/helpers/wait.ts | 46 ++++++++++++++----- src/testcases/common_localcosmosnet.ts | 12 ++++- src/testcases/governance.test.ts | 15 +++--- src/testcases/interchain_kv_query.test.ts | 20 ++++---- src/testcases/interchain_tx_query.test.ts | 13 +++--- .../interchain_tx_query_resubmit.test.ts | 7 +-- src/testcases/interchaintx.test.ts | 18 ++++---- src/testcases/simple.test.ts | 20 ++++---- src/testcases/subdao.test.ts | 29 +++++++----- src/testcases/tokenomics.test.ts | 4 +- src/testcases/treasury.test.ts | 2 + 16 files changed, 148 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index e4e122c1..7672537d 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,9 @@ CONTRACTS_PATH - path to contracts that will be used in tests NEUTRON_ADDRESS_PREFIX - address prefix for neutron controller network COSMOS_ADDRESS_PREFIX - address prefix for gaia (cosmoshub) host network NODE1_URL - url to the first node +NODE1_WS_URL - url to websocket of the first node NODE2_URL - url to the second node +NODE2_WS_URL - url to websocket of the second node BLOCKS_COUNT_BEFORE_START - how many blocks we wait before start first test NO_DOCKER - do not start cosmopark for tests NO_REBUILD - skip containers rebuilding diff --git a/src/helpers/cosmos.ts b/src/helpers/cosmos.ts index 317c0143..00d732d2 100644 --- a/src/helpers/cosmos.ts +++ b/src/helpers/cosmos.ts @@ -8,7 +8,7 @@ import axios from 'axios'; import { CodeId, Wallet } from '../types'; import Long from 'long'; import path from 'path'; -import { waitBlocks, getWithAttempts } from './wait'; +import { BlockWaiter, getWithAttempts } from './wait'; import { CosmosTxV1beta1GetTxResponse, InlineResponse20075TxResponse, @@ -17,14 +17,14 @@ import { cosmos, google } from '@cosmos-client/core/cjs/proto'; import { CosmosSDK } from '@cosmos-client/core/cjs/sdk'; import { ibc } from '@cosmos-client/ibc/cjs/proto'; import crypto from 'crypto'; -import ICoin = cosmos.base.v1beta1.ICoin; -import IHeight = ibc.core.client.v1.IHeight; import { paramChangeProposal, ParamChangeProposalInfo, sendProposal, SendProposalInfo, } from './proposal'; +import ICoin = cosmos.base.v1beta1.ICoin; +import IHeight = ibc.core.client.v1.IHeight; export const NEUTRON_DENOM = process.env.NEUTRON_DENOM || 'stake'; export const COSMOS_DENOM = process.env.COSMOS_DENOM || 'uatom'; @@ -191,12 +191,19 @@ cosmosclient.codec.register( export class CosmosWrapper { sdk: cosmosclient.CosmosSDK; + blockWaiter: BlockWaiter; wallet: Wallet; denom: string; - constructor(sdk: cosmosclient.CosmosSDK, wallet: Wallet, denom: string) { + constructor( + sdk: cosmosclient.CosmosSDK, + blockWaiter: BlockWaiter, + wallet: Wallet, + denom: string, + ) { this.denom = denom; this.sdk = sdk; + this.blockWaiter = blockWaiter; this.wallet = wallet; } @@ -250,7 +257,7 @@ export class CosmosWrapper { const txhash = res.data?.tx_response.txhash; let error = null; while (numAttempts > 0) { - await waitBlocks(this.sdk, 1); + await this.blockWaiter.next(); numAttempts--; const data = await rest.tx .getTx(this.sdk as CosmosSDK, txhash) @@ -365,7 +372,7 @@ export class CosmosWrapper { } numAttempts--; - await waitBlocks(this.sdk, 1); + await this.blockWaiter.next(); } throw new Error('failed to query contract'); @@ -480,7 +487,7 @@ export class CosmosWrapper { expect(pauseInfo.paused.until_height).toBeGreaterThan(0); // wait and check contract's pause info after unpausing - await waitBlocks(this.sdk, short_pause_duration); + await this.blockWaiter.waitBlocks(short_pause_duration); pauseInfo = await this.queryPausedInfo(testingContract); expect(pauseInfo).toEqual({ unpaused: {} }); expect(pauseInfo.paused).toEqual(undefined); diff --git a/src/helpers/env.ts b/src/helpers/env.ts index 5430fd6d..420ca464 100644 --- a/src/helpers/env.ts +++ b/src/helpers/env.ts @@ -9,7 +9,7 @@ const BLOCKS_COUNT_BEFORE_START = process.env.BLOCKS_COUNT_BEFORE_START let alreadySetUp = false; -export const setup = async (host: string) => { +export const setup = async (host1: string, host2: string) => { if (alreadySetUp) { console.log('already set up'); return; @@ -32,8 +32,12 @@ export const setup = async (host: string) => { showVersions(); await showContractsHashes(); - await waitForHTTP(host); - await waitForChannel(host); + await waitForHTTP(host1); + await waitForChannel(host1); + await waitForHTTP(host2); + await waitForChannel(host2); + await wait(20); // FIXME: this hardcoded sleep is here to wait until hermes is fully initialized. + // proper fix would be to monitor hermes status events. alreadySetUp = true; }; @@ -53,7 +57,7 @@ export const waitForHTTP = async ( } // eslint-disable-next-line no-empty } catch (e) {} - await wait(10); + await wait(1); } throw new Error('No port opened'); }; @@ -80,7 +84,7 @@ export const waitForChannel = async ( } // eslint-disable-next-line no-empty } catch (e) {} - await wait(10); + await wait(1); } throw new Error('No channel opened'); diff --git a/src/helpers/ica.ts b/src/helpers/ica.ts index 9a67c082..6b1cfbc9 100644 --- a/src/helpers/ica.ts +++ b/src/helpers/ica.ts @@ -9,7 +9,7 @@ export const getIca = ( numAttempts = 20, ) => getWithAttempts( - cm.sdk, + cm, () => cm.queryContract<{ interchain_account_address: string; diff --git a/src/helpers/icq.ts b/src/helpers/icq.ts index d27f17df..852b8678 100644 --- a/src/helpers/icq.ts +++ b/src/helpers/icq.ts @@ -48,7 +48,7 @@ export const waitForICQResultWithRemoteHeight = ( numAttempts = 20, ) => getWithAttempts( - cm.sdk, + cm, () => getRegisteredQuery(cm, contractAddress, queryId), async (query) => query.registered_query.last_submitted_result_remote_height >= @@ -80,7 +80,7 @@ export const waitForTransfersAmount = ( numAttempts = 50, ) => getWithAttempts( - cm.sdk, + cm, async () => (await queryTransfersNumber(cm, contractAddress)).transfers_number, async (amount) => amount == expectedTransfersAmount, diff --git a/src/helpers/wait.ts b/src/helpers/wait.ts index df94e438..2272974f 100644 --- a/src/helpers/wait.ts +++ b/src/helpers/wait.ts @@ -1,4 +1,6 @@ -import { rest } from '@cosmos-client/core'; +import { rest, websocket } from '@cosmos-client/core'; + +(global as any).WebSocket = require('ws'); export const wait = async (seconds: number) => new Promise((r) => { @@ -16,23 +18,45 @@ export const getRemoteHeight = async (sdk: any) => { return +block.data.block.header.height; }; -export const waitBlocks = async (sdk: any, n: number) => { - const targetHeight = (await getRemoteHeight(sdk)) + n; - for (;;) { - await wait(1); - const currentHeight = await getRemoteHeight(sdk); - if (currentHeight >= targetHeight) { - break; +export class BlockWaiter { + url; + + constructor(url: string) { + this.url = url; + } + + next() { + return new Promise((r) => { + const ws = websocket.connect(this.url); + ws.next({ + id: '1', + jsonrpc: '2.0', + method: 'subscribe', + params: ["tm.event='NewBlock'"], + }); + ws.subscribe((x) => { + if (Object.entries((x as any).result).length !== 0) { + ws.unsubscribe(); + r(x); + } + }); + }); + } + + async waitBlocks(n: number) { + while (n > 0) { + await this.next(); + n--; } } -}; +} /** * getWithAttempts waits until readyFunc(getFunc()) returns true * and only then returns result of getFunc() */ export const getWithAttempts = async ( - sdk: any, + cm: any, getFunc: () => Promise, readyFunc: (t: T) => Promise, numAttempts = 20, @@ -48,7 +72,7 @@ export const getWithAttempts = async ( } catch (e) { error = e; } - await waitBlocks(sdk, 1); + await cm.blockWaiter.next(); } throw error != null ? error : new Error('getWithAttempts: no attempts left'); }; diff --git a/src/testcases/common_localcosmosnet.ts b/src/testcases/common_localcosmosnet.ts index 489972db..8d5777a1 100644 --- a/src/testcases/common_localcosmosnet.ts +++ b/src/testcases/common_localcosmosnet.ts @@ -3,6 +3,7 @@ import { cosmosclient } from '@cosmos-client/core'; import { Wallet } from '../types'; import { mnemonicToWallet } from '../helpers/cosmos'; import { setup } from '../helpers/env'; +import { BlockWaiter } from '../helpers/wait'; const config = require('../config.json'); @@ -56,6 +57,8 @@ const walletSet = async ( export class TestStateLocalCosmosTestNet { sdk1: cosmosclient.CosmosSDK; sdk2: cosmosclient.CosmosSDK; + blockWaiter1: BlockWaiter; + blockWaiter2: BlockWaiter; wallets: Record>; icq_web_host: string; init = async () => { @@ -70,7 +73,14 @@ export class TestStateLocalCosmosTestNet { this.icq_web_host = 'http://localhost:9999'; - await setup(host1); + this.blockWaiter1 = new BlockWaiter( + process.env.NODE1_WS_URL || 'ws://localhost:26657', + ); + this.blockWaiter2 = new BlockWaiter( + process.env.NODE2_WS_URL || 'ws://localhost:16657', + ); + + await setup(host1, host2); this.wallets = {}; this.wallets.neutron = await walletSet(this.sdk1, neutron_prefix); diff --git a/src/testcases/governance.test.ts b/src/testcases/governance.test.ts index 676deed2..bf8879d5 100644 --- a/src/testcases/governance.test.ts +++ b/src/testcases/governance.test.ts @@ -21,16 +21,19 @@ describe('Neutron / Governance', () => { await testState.init(); cm = new CosmosWrapper( testState.sdk1, + testState.blockWaiter1, testState.wallets.neutron.demo1, NEUTRON_DENOM, ); cm2 = new CosmosWrapper( testState.sdk1, + testState.blockWaiter1, testState.wallets.neutron.demo2, NEUTRON_DENOM, ); cm3 = new CosmosWrapper( testState.sdk1, + testState.blockWaiter1, testState.wallets.neutron.rly1, NEUTRON_DENOM, ); @@ -44,7 +47,7 @@ describe('Neutron / Governance', () => { cm.wallet.address.toString(), ); await getWithAttempts( - cm.sdk, + cm, async () => await cm.queryVotingPower( CORE_CONTRACT_ADDRESS, @@ -61,7 +64,7 @@ describe('Neutron / Governance', () => { cm2.wallet.address.toString(), ); await getWithAttempts( - cm2.sdk, + cm2, async () => await cm2.queryVotingPower( CORE_CONTRACT_ADDRESS, @@ -78,7 +81,7 @@ describe('Neutron / Governance', () => { cm3.wallet.address.toString(), ); await getWithAttempts( - cm3.sdk, + cm3, async () => await cm3.queryVotingPower( CORE_CONTRACT_ADDRESS, @@ -90,7 +93,7 @@ describe('Neutron / Governance', () => { }); test('check voting power', async () => { await getWithAttempts( - cm.sdk, + cm, async () => await cm.queryTotalVotingPower(CORE_CONTRACT_ADDRESS), async (response) => response.power == '3000', 20, @@ -102,7 +105,7 @@ describe('Neutron / Governance', () => { test('send funds from wallet 1', async () => { await cm.msgSend(CORE_CONTRACT_ADDRESS, '1000'); await getWithAttempts( - cm.sdk, + cm, async () => await cm.queryBalances(CORE_CONTRACT_ADDRESS), async (response) => response.balances[0].amount == '1000', 20, @@ -291,7 +294,7 @@ describe('Neutron / Governance', () => { } expect(rawLog.includes("proposal is not in 'passed' state")); await getWithAttempts( - cm.sdk, + cm, async () => await cm.queryProposal(PROPOSE_CONTRACT_ADDRESS, proposalId), async (response) => response.proposal.status === 'rejected', diff --git a/src/testcases/interchain_kv_query.test.ts b/src/testcases/interchain_kv_query.test.ts index 4c0e4b96..d980b756 100644 --- a/src/testcases/interchain_kv_query.test.ts +++ b/src/testcases/interchain_kv_query.test.ts @@ -9,7 +9,7 @@ import { PROPOSE_CONTRACT_ADDRESS, } from '../helpers/cosmos'; import { TestStateLocalCosmosTestNet } from './common_localcosmosnet'; -import { getRemoteHeight, getWithAttempts, waitBlocks } from '../helpers/wait'; +import { getRemoteHeight, getWithAttempts } from '../helpers/wait'; import { AccAddress, ValAddress } from '@cosmos-client/core/cjs/types'; import { CosmosSDK } from '@cosmos-client/core/cjs/sdk'; import { @@ -181,14 +181,14 @@ const acceptInterchainqueriesParamsChangeProposal = async ( const proposalId = parseInt(attribute); expect(proposalId).toBeGreaterThanOrEqual(0); - await waitBlocks(cm.sdk, 1); + await cm.blockWaiter.next(); await cm.voteYes( PROPOSE_CONTRACT_ADDRESS, proposalId, wallet.address.toString(), ); - await waitBlocks(cm.sdk, 1); + await cm.blockWaiter.next(); await cm.executeProposal( PROPOSE_CONTRACT_ADDRESS, proposalId, @@ -196,7 +196,7 @@ const acceptInterchainqueriesParamsChangeProposal = async ( ); await getWithAttempts( - cm.sdk, + cm, async () => await cm.queryProposal(PROPOSE_CONTRACT_ADDRESS, proposalId), async (response) => response.proposal.status === 'executed', 20, @@ -286,11 +286,13 @@ describe('Neutron / Interchain KV Query', () => { cm = { 1: new CosmosWrapper( testState.sdk1, + testState.blockWaiter1, testState.wallets.neutron.demo1, NEUTRON_DENOM, ), 2: new CosmosWrapper( testState.sdk2, + testState.blockWaiter2, testState.wallets.cosmos.demo2, COSMOS_DENOM, ), @@ -610,7 +612,7 @@ describe('Neutron / Interchain KV Query', () => { for (const j of res) { expect(j).not.toEqual(0); } - await waitBlocks(cm[1].sdk, 1); + await cm[1].blockWaiter.next(); } const end = await Promise.all( [2, 3, 4].map((i) => getKvCallbackStatus(cm[1], contractAddress, i)), @@ -671,7 +673,7 @@ describe('Neutron / Interchain KV Query', () => { testState.wallets.cosmos.demo2.address, ); - await waitBlocks(cm[1].sdk, 1); + await cm[1].blockWaiter.next(); const queryResult = await getRegisteredQuery( cm[1], @@ -731,7 +733,7 @@ describe('Neutron / Interchain KV Query', () => { testState.wallets.cosmos.demo2.address, ); - await waitBlocks(cm[1].sdk, 1); + await cm[1].blockWaiter.next(); const queryResult = await getRegisteredQuery( cm[1], @@ -782,7 +784,7 @@ describe('Neutron / Interchain KV Query', () => { ); await getWithAttempts( - cm[1].sdk, + cm[1], () => getRegisteredQuery(cm[1], contractAddress, queryId), async (response) => response.registered_query.last_submitted_result_local_height > 0 && @@ -798,7 +800,7 @@ describe('Neutron / Interchain KV Query', () => { await removeQueryViaTx(cm[1], queryId); await getWithAttempts( - cm[1].sdk, + cm[1], async () => await cm[1].queryBalances( testState.wallets.neutron.demo1.address.toString(), diff --git a/src/testcases/interchain_tx_query.test.ts b/src/testcases/interchain_tx_query.test.ts index 992a3227..91a52b9f 100644 --- a/src/testcases/interchain_tx_query.test.ts +++ b/src/testcases/interchain_tx_query.test.ts @@ -6,7 +6,6 @@ import { } from '../helpers/cosmos'; import { proto } from '@cosmos-client/core'; import { TestStateLocalCosmosTestNet } from './common_localcosmosnet'; -import { waitBlocks } from '../helpers/wait'; import Long from 'long'; import { getRegisteredQuery, @@ -28,11 +27,13 @@ describe('Neutron / Interchain TX Query', () => { await testState.init(); cm = new CosmosWrapper( testState.sdk1, + testState.blockWaiter1, testState.wallets.neutron.demo1, NEUTRON_DENOM, ); cm2 = new CosmosWrapper( testState.sdk2, + testState.blockWaiter2, testState.wallets.cosmos.demo2, COSMOS_DENOM, ); @@ -134,7 +135,7 @@ describe('Neutron / Interchain TX Query', () => { expect(balances.balances).toEqual([ { amount: addr2ExpectedBalance.toString(), denom: cm2.denom }, ]); - await waitBlocks(cm.sdk, query1UpdatePeriod * 2); // we are waiting for quite a big time just to be sure + await cm.blockWaiter.waitBlocks(query1UpdatePeriod * 2); // we are waiting for quite a big time just to be sure // the different address is not registered by the contract, so its receivings aren't tracked let deposits = await queryRecipientTxs( @@ -163,7 +164,7 @@ describe('Neutron / Interchain TX Query', () => { expect(balances.balances).toEqual([ { amount: addr1ExpectedBalance.toString(), denom: cm2.denom }, // balance hasn't changed thus tx failed ]); - await waitBlocks(cm.sdk, query1UpdatePeriod * 2 + 1); // we are waiting for quite a big time just to be sure + await cm.blockWaiter.waitBlocks(query1UpdatePeriod * 2 + 1); // we are waiting for quite a big time just to be sure // the watched address receivings are not changed const deposits = await queryRecipientTxs( @@ -466,7 +467,7 @@ describe('Neutron / Interchain TX Query', () => { const watchedAddr5: string = addr5; const query5UpdatePeriod = 12; - // by this checks we ensure the transactions will be processed in the desired order + // by these checks we ensure the transactions will be processed in the desired order test('validate update periods', async () => { expect(query5UpdatePeriod).toBeGreaterThanOrEqual(9); expect(query5UpdatePeriod).toBeGreaterThanOrEqual(query4UpdatePeriod * 3); @@ -489,7 +490,7 @@ describe('Neutron / Interchain TX Query', () => { query5UpdatePeriod, watchedAddr5, ); - await waitBlocks(cm.sdk, 2); // wait for queries handling on init + await cm.blockWaiter.waitBlocks(2); // wait for queries handling on init }); test('make older sending', async () => { @@ -608,7 +609,7 @@ describe('Neutron / Interchain TX Query', () => { }); test('check that transfer has not been recorded', async () => { - await waitBlocks(cm.sdk, query4UpdatePeriod * 2 + 1); // we are waiting for quite a big time just to be sure + await cm.blockWaiter.waitBlocks(query4UpdatePeriod * 2 + 1); // we are waiting for quite a big time just to be sure const deposits = await queryRecipientTxs( cm, contractAddress, diff --git a/src/testcases/interchain_tx_query_resubmit.test.ts b/src/testcases/interchain_tx_query_resubmit.test.ts index ca968369..a02ebab9 100644 --- a/src/testcases/interchain_tx_query_resubmit.test.ts +++ b/src/testcases/interchain_tx_query_resubmit.test.ts @@ -1,6 +1,5 @@ import { COSMOS_DENOM, CosmosWrapper, NEUTRON_DENOM } from '../helpers/cosmos'; import { TestStateLocalCosmosTestNet } from './common_localcosmosnet'; -import { waitBlocks } from '../helpers/wait'; import { getRegisteredQuery, getUnsuccessfulTxs, @@ -22,11 +21,13 @@ describe('Neutron / Interchain TX Query Resubmit', () => { await testState.init(); cm = new CosmosWrapper( testState.sdk1, + testState.blockWaiter1, testState.wallets.neutron.demo1, NEUTRON_DENOM, ); cm2 = new CosmosWrapper( testState.sdk2, + testState.blockWaiter2, testState.wallets.cosmos.demo2, COSMOS_DENOM, ); @@ -96,7 +97,7 @@ describe('Neutron / Interchain TX Query Resubmit', () => { expect(res.code).toEqual(0); } - await waitBlocks(cm.sdk, 5); + await cm.blockWaiter.waitBlocks(5); const txs = await getUnsuccessfulTxs(testState.icq_web_host); expect(txs.length).toEqual(5); @@ -116,7 +117,7 @@ describe('Neutron / Interchain TX Query Resubmit', () => { const resp = await postResubmitTxs(testState.icq_web_host, resubmit_txs); expect(resp.status).toEqual(200); - await waitBlocks(cm.sdk, 20); + await cm.blockWaiter.waitBlocks(20); await waitForTransfersAmount( cm, diff --git a/src/testcases/interchaintx.test.ts b/src/testcases/interchaintx.test.ts index 7ce7560d..5744af22 100644 --- a/src/testcases/interchaintx.test.ts +++ b/src/testcases/interchaintx.test.ts @@ -11,7 +11,7 @@ import { } from '../helpers/cosmos'; import { AcknowledgementResult } from '../helpers/contract_types'; import { TestStateLocalCosmosTestNet } from './common_localcosmosnet'; -import { getWithAttempts, waitBlocks } from '../helpers/wait'; +import { getWithAttempts } from '../helpers/wait'; import { CosmosSDK } from '@cosmos-client/core/cjs/sdk'; import { getIca } from '../helpers/ica'; @@ -31,11 +31,13 @@ describe('Neutron / Interchain TXs', () => { await testState.init(); cm1 = new CosmosWrapper( testState.sdk1, + testState.blockWaiter1, testState.wallets.neutron.demo1, NEUTRON_DENOM, ); cm2 = new CosmosWrapper( testState.sdk2, + testState.blockWaiter2, testState.wallets.cosmos.demo2, COSMOS_DENOM, ); @@ -83,7 +85,7 @@ describe('Neutron / Interchain TXs', () => { }); test('multiple IBC accounts created', async () => { const channels = await getWithAttempts( - cm1.sdk, + cm1, () => cm1.listIBCChannels(), // Wait until there are 3 channels: // - one exists already, it is open for IBC transfers; @@ -177,7 +179,7 @@ describe('Neutron / Interchain TXs', () => { }); test('check validator state', async () => { const res1 = await getWithAttempts( - cm2.sdk, + cm2, () => rest.staking.delegatorDelegations( cm2.sdk as CosmosSDK, @@ -427,7 +429,7 @@ describe('Neutron / Interchain TXs', () => { ); expect(res.code).toEqual(0); await getWithAttempts( - cm1.sdk, + cm1, async () => cm1.listIBCChannels(), // Wait until there are 4 channels: // - one exists already, it is open for IBC transfers; @@ -436,7 +438,7 @@ describe('Neutron / Interchain TXs', () => { async (channels) => channels.channels.length == 4, ); await getWithAttempts( - cm1.sdk, + cm1, () => cm1.listIBCChannels(), async (channels) => channels.channels.find((c) => c.channel_id == 'channel-3').state == @@ -510,7 +512,7 @@ describe('Neutron / Interchain TXs', () => { }), ); - await waitBlocks(cm1.sdk, 10); + await cm1.blockWaiter.waitBlocks(10); // Testing ACK timeout failure await cm1.executeContract( @@ -527,7 +529,7 @@ describe('Neutron / Interchain TXs', () => { ); const failuresAfterCall = await getWithAttempts( - cm1.sdk, + cm1, async () => cm1.queryAckFailures(contractAddress), // Wait until there 2 failure in the list async (data) => data.failures.length == 2, @@ -579,7 +581,7 @@ const waitForAck = ( numAttempts = 20, ) => getWithAttempts( - cm.sdk, + cm, () => cm.queryContract(contractAddress, { acknowledgement_result: { diff --git a/src/testcases/simple.test.ts b/src/testcases/simple.test.ts index 19720f60..7ec6bc2f 100644 --- a/src/testcases/simple.test.ts +++ b/src/testcases/simple.test.ts @@ -8,7 +8,7 @@ import { NeutronContract, PageRequest, } from '../helpers/cosmos'; -import { getRemoteHeight, getWithAttempts, waitBlocks } from '../helpers/wait'; +import { getRemoteHeight, getWithAttempts } from '../helpers/wait'; import { TestStateLocalCosmosTestNet } from './common_localcosmosnet'; describe('Neutron / Simple', () => { @@ -22,11 +22,13 @@ describe('Neutron / Simple', () => { await testState.init(); cm = new CosmosWrapper( testState.sdk1, + testState.blockWaiter1, testState.wallets.neutron.demo1, NEUTRON_DENOM, ); cm2 = new CosmosWrapper( testState.sdk2, + testState.blockWaiter2, testState.wallets.cosmos.demo2, COSMOS_DENOM, ); @@ -59,7 +61,7 @@ describe('Neutron / Simple', () => { describe('Correct way', () => { let relayerBalance = 0; beforeAll(async () => { - await waitBlocks(cm.sdk, 10); + await cm.blockWaiter.waitBlocks(10); const balances = await cm.queryBalances(IBC_RELAYER_NEUTRON_ADDRESS); relayerBalance = parseInt( balances.balances.find((bal) => bal.denom == NEUTRON_DENOM)?.amount || @@ -88,7 +90,7 @@ describe('Neutron / Simple', () => { expect(res.code).toEqual(0); }); test('check IBC token balance', async () => { - await waitBlocks(cm.sdk, 10); + await cm.blockWaiter.waitBlocks(10); const balances = await cm2.queryBalances( testState.wallets.cosmos.demo2.address.toString(), ); @@ -111,7 +113,7 @@ describe('Neutron / Simple', () => { expect(res.code).toEqual(0); }); test('check uatom token balance transfered via IBC on Neutron', async () => { - await waitBlocks(cm.sdk, 10); + await cm.blockWaiter.waitBlocks(10); const balances = await cm.queryBalances( testState.wallets.neutron.demo1.address.toString(), ); @@ -160,7 +162,7 @@ describe('Neutron / Simple', () => { }); test('check wallet balance', async () => { - await waitBlocks(cm.sdk, 10); + await cm.blockWaiter.waitBlocks(10); const balances = await cm2.queryBalances( testState.wallets.cosmos.demo2.address.toString(), ); @@ -183,7 +185,7 @@ describe('Neutron / Simple', () => { expect(balance - 2333 * 2 - relayerBalance).toBeLessThan(5); // it may differ by about 1-2 because of the gas fee }); test('contract should be refunded', async () => { - await waitBlocks(cm.sdk, 10); + await cm.blockWaiter.waitBlocks(10); const balances = await cm.queryBalances(contractAddress); const balance = parseInt( balances.balances.find((bal) => bal.denom == NEUTRON_DENOM)?.amount || @@ -241,7 +243,7 @@ describe('Neutron / Simple', () => { ); expect(res.code).toEqual(0); - await waitBlocks(cm.sdk, 10); + await cm.blockWaiter.waitBlocks(10); const balances = await cm.queryBalances(contractAddress); expect( balances.balances.find((bal): boolean => bal.denom == uatomIBCDenom) @@ -355,7 +357,7 @@ describe('Neutron / Simple', () => { attempts -= 1; try { - await waitBlocks(cm.sdk, 3); + await cm.blockWaiter.waitBlocks(3); const currentHeight = await getRemoteHeight(cm.sdk); await cm.executeContract( @@ -377,7 +379,7 @@ describe('Neutron / Simple', () => { expect(attempts).toBeGreaterThan(0); const failuresAfterCall = await getWithAttempts( - cm.sdk, + cm, async () => cm.queryAckFailures(contractAddress), // Wait until there 4 failure in the list async (data) => data.failures.length == 4, diff --git a/src/testcases/subdao.test.ts b/src/testcases/subdao.test.ts index db5e14ae..33915df0 100644 --- a/src/testcases/subdao.test.ts +++ b/src/testcases/subdao.test.ts @@ -1,11 +1,11 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { CosmosWrapper, - NEUTRON_DENOM, - VAULT_CONTRACT_ADDRESS, - getEventAttributesFromTx, createBankMassage, + getEventAttributesFromTx, + NEUTRON_DENOM, NeutronContract, + VAULT_CONTRACT_ADDRESS, } from '../helpers/cosmos'; import { InlineResponse20075TxResponse } from '@cosmos-client/core/cjs/openapi/api'; import { @@ -14,12 +14,7 @@ import { TimeLockSingleChoiceProposal, SubDaoConfig, } from '../helpers/dao'; -import { - wait, - waitBlocks, - getWithAttempts, - getRemoteHeight, -} from '../helpers/wait'; +import { getRemoteHeight, getWithAttempts, wait } from '../helpers/wait'; import { TestStateLocalCosmosTestNet } from './common_localcosmosnet'; import { AccAddress, ValAddress } from '@cosmos-client/core/cjs/types'; import { Wallet } from '../types'; @@ -45,8 +40,18 @@ describe('Neutron / Subdao', () => { main_dao_addr = main_dao_wallet.address; security_dao_addr = security_dao_wallet.address; demo2_addr = demo2_wallet.address; - cm = new CosmosWrapper(testState.sdk1, main_dao_wallet, NEUTRON_DENOM); - cm3 = new CosmosWrapper(testState.sdk1, demo2_wallet, NEUTRON_DENOM); + cm = new CosmosWrapper( + testState.sdk1, + testState.blockWaiter1, + main_dao_wallet, + NEUTRON_DENOM, + ); + cm3 = new CosmosWrapper( + testState.sdk1, + testState.blockWaiter1, + demo2_wallet, + NEUTRON_DENOM, + ); subDAO = await setupSubDaoTimelockSet( cm, main_dao_addr.toString(), @@ -393,7 +398,7 @@ describe('Neutron / Subdao', () => { expect(pauseInfo.paused.until_height).toBeGreaterThan(pauseHeight); // wait and check contract's pause info after unpausing - await waitBlocks(cm.sdk, short_pause_duration); + await cm.blockWaiter.waitBlocks(short_pause_duration); pauseInfo = await cm.queryPausedInfo(subDAO.core.address); expect(pauseInfo).toEqual({ unpaused: {} }); expect(pauseInfo.paused).toEqual(undefined); diff --git a/src/testcases/tokenomics.test.ts b/src/testcases/tokenomics.test.ts index 1ecb6655..f5576550 100644 --- a/src/testcases/tokenomics.test.ts +++ b/src/testcases/tokenomics.test.ts @@ -20,11 +20,13 @@ describe('Neutron / Tokenomics', () => { await testState.init(); cmNeutron = new CosmosWrapper( testState.sdk1, + testState.blockWaiter1, testState.wallets.neutron.demo1, NEUTRON_DENOM, ); cmGaia = new CosmosWrapper( testState.sdk2, + testState.blockWaiter2, testState.wallets.cosmos.demo2, COSMOS_DENOM, ); @@ -147,7 +149,7 @@ describe('Neutron / Tokenomics', () => { { revision_number: 2, revision_height: 100000000 }, ); await getWithAttempts( - cmNeutron.sdk, + cmNeutron, async () => cmNeutron.queryBalances( testState.wallets.neutron.demo1.address.toString(), diff --git a/src/testcases/treasury.test.ts b/src/testcases/treasury.test.ts index 8fc98a75..dbf1f58b 100644 --- a/src/testcases/treasury.test.ts +++ b/src/testcases/treasury.test.ts @@ -32,11 +32,13 @@ describe('Neutron / Treasury', () => { await testState.init(); cm = new CosmosWrapper( testState.sdk1, + testState.blockWaiter1, testState.wallets.neutron.demo1, NEUTRON_DENOM, ); cm2 = new CosmosWrapper( testState.sdk1, + testState.blockWaiter1, testState.wallets.neutron.demo2, NEUTRON_DENOM, ); From d127d099ab3ce96ee42f8593e225e9454c41c7ef Mon Sep 17 00:00:00 2001 From: Murad Karammaev Date: Mon, 16 Jan 2023 12:44:38 +0200 Subject: [PATCH 2/4] review-fix: don't resubscribe between blocks --- src/helpers/cosmos.ts | 4 ++-- src/helpers/wait.ts | 18 +++++++----------- src/testcases/interchain_kv_query.test.ts | 14 +++++++------- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/helpers/cosmos.ts b/src/helpers/cosmos.ts index 00d732d2..193f044d 100644 --- a/src/helpers/cosmos.ts +++ b/src/helpers/cosmos.ts @@ -257,7 +257,7 @@ export class CosmosWrapper { const txhash = res.data?.tx_response.txhash; let error = null; while (numAttempts > 0) { - await this.blockWaiter.next(); + await this.blockWaiter.waitBlocks(1); numAttempts--; const data = await rest.tx .getTx(this.sdk as CosmosSDK, txhash) @@ -372,7 +372,7 @@ export class CosmosWrapper { } numAttempts--; - await this.blockWaiter.next(); + await this.blockWaiter.waitBlocks(1); } throw new Error('failed to query contract'); diff --git a/src/helpers/wait.ts b/src/helpers/wait.ts index 2272974f..d7deee62 100644 --- a/src/helpers/wait.ts +++ b/src/helpers/wait.ts @@ -25,7 +25,7 @@ export class BlockWaiter { this.url = url; } - next() { + waitBlocks(n: number) { return new Promise((r) => { const ws = websocket.connect(this.url); ws.next({ @@ -36,19 +36,15 @@ export class BlockWaiter { }); ws.subscribe((x) => { if (Object.entries((x as any).result).length !== 0) { - ws.unsubscribe(); - r(x); + n--; + if (n == 0) { + ws.unsubscribe(); + r(x); + } } }); }); } - - async waitBlocks(n: number) { - while (n > 0) { - await this.next(); - n--; - } - } } /** @@ -72,7 +68,7 @@ export const getWithAttempts = async ( } catch (e) { error = e; } - await cm.blockWaiter.next(); + await cm.blockWaiter.waitBlocks(1); } throw error != null ? error : new Error('getWithAttempts: no attempts left'); }; diff --git a/src/testcases/interchain_kv_query.test.ts b/src/testcases/interchain_kv_query.test.ts index d980b756..e453411c 100644 --- a/src/testcases/interchain_kv_query.test.ts +++ b/src/testcases/interchain_kv_query.test.ts @@ -3,10 +3,10 @@ import { COSMOS_DENOM, CosmosWrapper, NEUTRON_DENOM, - VAULT_CONTRACT_ADDRESS, - PRE_PROPOSE_CONTRACT_ADDRESS, NeutronContract, + PRE_PROPOSE_CONTRACT_ADDRESS, PROPOSE_CONTRACT_ADDRESS, + VAULT_CONTRACT_ADDRESS, } from '../helpers/cosmos'; import { TestStateLocalCosmosTestNet } from './common_localcosmosnet'; import { getRemoteHeight, getWithAttempts } from '../helpers/wait'; @@ -181,14 +181,14 @@ const acceptInterchainqueriesParamsChangeProposal = async ( const proposalId = parseInt(attribute); expect(proposalId).toBeGreaterThanOrEqual(0); - await cm.blockWaiter.next(); + await cm.blockWaiter.waitBlocks(1); await cm.voteYes( PROPOSE_CONTRACT_ADDRESS, proposalId, wallet.address.toString(), ); - await cm.blockWaiter.next(); + await cm.blockWaiter.waitBlocks(1); await cm.executeProposal( PROPOSE_CONTRACT_ADDRESS, proposalId, @@ -612,7 +612,7 @@ describe('Neutron / Interchain KV Query', () => { for (const j of res) { expect(j).not.toEqual(0); } - await cm[1].blockWaiter.next(); + await cm[1].blockWaiter.waitBlocks(1); } const end = await Promise.all( [2, 3, 4].map((i) => getKvCallbackStatus(cm[1], contractAddress, i)), @@ -673,7 +673,7 @@ describe('Neutron / Interchain KV Query', () => { testState.wallets.cosmos.demo2.address, ); - await cm[1].blockWaiter.next(); + await cm[1].blockWaiter.waitBlocks(1); const queryResult = await getRegisteredQuery( cm[1], @@ -733,7 +733,7 @@ describe('Neutron / Interchain KV Query', () => { testState.wallets.cosmos.demo2.address, ); - await cm[1].blockWaiter.next(); + await cm[1].blockWaiter.waitBlocks(1); const queryResult = await getRegisteredQuery( cm[1], From 6210a025b48ca1428e941f97bc4b67e2811b2c4f Mon Sep 17 00:00:00 2001 From: Murad Karammaev Date: Mon, 16 Jan 2023 14:47:04 +0200 Subject: [PATCH 3/4] fix: broken subdao test --- src/testcases/subdao.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/testcases/subdao.test.ts b/src/testcases/subdao.test.ts index 33915df0..07699fb9 100644 --- a/src/testcases/subdao.test.ts +++ b/src/testcases/subdao.test.ts @@ -60,7 +60,7 @@ describe('Neutron / Subdao', () => { await cm.bondFunds(VAULT_CONTRACT_ADDRESS, '10000'); await getWithAttempts( - cm.sdk, + cm, async () => await cm.queryVotingPower( subDAO.core.address, From 7e139ceee2e656d20dc6d4a5aa7f84144e50b878 Mon Sep 17 00:00:00 2001 From: Murad Karammaev Date: Tue, 17 Jan 2023 15:03:55 +0200 Subject: [PATCH 4/4] review-fix: add timeout --- src/helpers/wait.ts | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/helpers/wait.ts b/src/helpers/wait.ts index d7deee62..6991b24a 100644 --- a/src/helpers/wait.ts +++ b/src/helpers/wait.ts @@ -25,21 +25,29 @@ export class BlockWaiter { this.url = url; } - waitBlocks(n: number) { - return new Promise((r) => { - const ws = websocket.connect(this.url); + waitBlocks(n: number, timeout = 120000): Promise { + return new Promise((resolve, reject) => { + let ws = null; + const x = setTimeout(() => { + if (ws != null) { + ws.unsubscribe(); + } + reject(new Error('waitBlocks: timeout')); + }, timeout); + ws = websocket.connect(this.url); ws.next({ id: '1', jsonrpc: '2.0', method: 'subscribe', params: ["tm.event='NewBlock'"], }); - ws.subscribe((x) => { - if (Object.entries((x as any).result).length !== 0) { + ws.subscribe((res) => { + if (Object.entries((res as any).result).length !== 0) { n--; if (n == 0) { ws.unsubscribe(); - r(x); + clearTimeout(x); + resolve(); } } });