Skip to content

Commit

Permalink
Merge pull request #50 from neutron-org/chore/NTRN-288-wait-for-new-b…
Browse files Browse the repository at this point in the history
…lock-websocket
  • Loading branch information
zavgorodnii authored Jan 18, 2023
2 parents 5f41e33 + 7e139ce commit c8e2d0a
Show file tree
Hide file tree
Showing 16 changed files with 156 additions and 85 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 14 additions & 7 deletions src/helpers/cosmos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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';
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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.waitBlocks(1);
numAttempts--;
const data = await rest.tx
.getTx(this.sdk as CosmosSDK, txhash)
Expand Down Expand Up @@ -365,7 +372,7 @@ export class CosmosWrapper {
}

numAttempts--;
await waitBlocks(this.sdk, 1);
await this.blockWaiter.waitBlocks(1);
}

throw new Error('failed to query contract');
Expand Down Expand Up @@ -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);
Expand Down
14 changes: 9 additions & 5 deletions src/helpers/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
};

Expand All @@ -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');
};
Expand All @@ -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');
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/ica.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const getIca = (
numAttempts = 20,
) =>
getWithAttempts(
cm.sdk,
cm,
() =>
cm.queryContract<{
interchain_account_address: string;
Expand Down
4 changes: 2 additions & 2 deletions src/helpers/icq.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 >=
Expand Down Expand Up @@ -80,7 +80,7 @@ export const waitForTransfersAmount = (
numAttempts = 50,
) =>
getWithAttempts(
cm.sdk,
cm,
async () =>
(await queryTransfersNumber(cm, contractAddress)).transfers_number,
async (amount) => amount == expectedTransfersAmount,
Expand Down
52 changes: 40 additions & 12 deletions src/helpers/wait.ts
Original file line number Diff line number Diff line change
@@ -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) => {
Expand All @@ -16,23 +18,49 @@ 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;
}
};

waitBlocks(n: number, timeout = 120000): Promise<void> {
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((res) => {
if (Object.entries((res as any).result).length !== 0) {
n--;
if (n == 0) {
ws.unsubscribe();
clearTimeout(x);
resolve();
}
}
});
});
}
}

/**
* getWithAttempts waits until readyFunc(getFunc()) returns true
* and only then returns result of getFunc()
*/
export const getWithAttempts = async <T>(
sdk: any,
cm: any,
getFunc: () => Promise<T>,
readyFunc: (t: T) => Promise<boolean>,
numAttempts = 20,
Expand All @@ -48,7 +76,7 @@ export const getWithAttempts = async <T>(
} catch (e) {
error = e;
}
await waitBlocks(sdk, 1);
await cm.blockWaiter.waitBlocks(1);
}
throw error != null ? error : new Error('getWithAttempts: no attempts left');
};
12 changes: 11 additions & 1 deletion src/testcases/common_localcosmosnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand Down Expand Up @@ -56,6 +57,8 @@ const walletSet = async (
export class TestStateLocalCosmosTestNet {
sdk1: cosmosclient.CosmosSDK;
sdk2: cosmosclient.CosmosSDK;
blockWaiter1: BlockWaiter;
blockWaiter2: BlockWaiter;
wallets: Record<string, Record<string, Wallet>>;
icq_web_host: string;
init = async () => {
Expand All @@ -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);
Expand Down
15 changes: 9 additions & 6 deletions src/testcases/governance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
Expand All @@ -44,7 +47,7 @@ describe('Neutron / Governance', () => {
cm.wallet.address.toString(),
);
await getWithAttempts(
cm.sdk,
cm,
async () =>
await cm.queryVotingPower(
CORE_CONTRACT_ADDRESS,
Expand All @@ -61,7 +64,7 @@ describe('Neutron / Governance', () => {
cm2.wallet.address.toString(),
);
await getWithAttempts(
cm2.sdk,
cm2,
async () =>
await cm2.queryVotingPower(
CORE_CONTRACT_ADDRESS,
Expand All @@ -78,7 +81,7 @@ describe('Neutron / Governance', () => {
cm3.wallet.address.toString(),
);
await getWithAttempts(
cm3.sdk,
cm3,
async () =>
await cm3.queryVotingPower(
CORE_CONTRACT_ADDRESS,
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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',
Expand Down
Loading

0 comments on commit c8e2d0a

Please sign in to comment.