@cardano-sdk/e2e 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +1 -7
- package/CHANGELOG.md +24 -0
- package/README.md +0 -29
- package/dist/cjs/environment.d.ts.map +1 -1
- package/dist/cjs/environment.js.map +1 -1
- package/dist/cjs/factories.d.ts.map +1 -1
- package/dist/cjs/factories.js +7 -1
- package/dist/cjs/factories.js.map +1 -1
- package/dist/cjs/measurement-util.d.ts.map +1 -1
- package/dist/cjs/measurement-util.js.map +1 -1
- package/dist/cjs/scripts/is-local-network-ready.js.map +1 -1
- package/dist/cjs/scripts/mnemonic.js.map +1 -1
- package/dist/cjs/tools/multi-delegation-data-gen/utils/files.d.ts.map +1 -1
- package/dist/cjs/tools/multi-delegation-data-gen/utils/files.js.map +1 -1
- package/dist/cjs/tools/multi-delegation-data-gen/utils/terminal-progress-monitor.d.ts.map +1 -1
- package/dist/cjs/tools/multi-delegation-data-gen/utils/terminal-progress-monitor.js.map +1 -1
- package/dist/cjs/tools/multi-delegation-data-gen/utils/utils.d.ts.map +1 -1
- package/dist/cjs/tools/multi-delegation-data-gen/utils/utils.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/environment.d.ts.map +1 -1
- package/dist/esm/environment.js.map +1 -1
- package/dist/esm/factories.d.ts.map +1 -1
- package/dist/esm/factories.js +7 -1
- package/dist/esm/factories.js.map +1 -1
- package/dist/esm/measurement-util.d.ts.map +1 -1
- package/dist/esm/measurement-util.js.map +1 -1
- package/dist/esm/scripts/is-local-network-ready.js.map +1 -1
- package/dist/esm/scripts/mnemonic.js.map +1 -1
- package/dist/esm/tools/multi-delegation-data-gen/utils/files.d.ts.map +1 -1
- package/dist/esm/tools/multi-delegation-data-gen/utils/files.js.map +1 -1
- package/dist/esm/tools/multi-delegation-data-gen/utils/terminal-progress-monitor.d.ts.map +1 -1
- package/dist/esm/tools/multi-delegation-data-gen/utils/terminal-progress-monitor.js.map +1 -1
- package/dist/esm/tools/multi-delegation-data-gen/utils/utils.d.ts.map +1 -1
- package/dist/esm/tools/multi-delegation-data-gen/utils/utils.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/docker-compose.yml +4 -0
- package/jest.config.js +0 -1
- package/local-network/scripts/common.sh +19 -0
- package/local-network/scripts/make-babbage.sh +2 -1
- package/local-network/scripts/mint-handles.sh +2 -18
- package/local-network/scripts/mint-tokens.sh +2 -18
- package/local-network/scripts/mnemonic_keys.sh +0 -0
- package/local-network/scripts/setup-wallets.sh +2 -17
- package/local-network/templates/babbage/submit-api-config.json +115 -0
- package/package.json +27 -29
- package/src/environment.ts +2 -6
- package/src/factories.ts +11 -5
- package/src/measurement-util.ts +1 -4
- package/src/scripts/is-local-network-ready.ts +1 -3
- package/src/scripts/mnemonic.ts +1 -3
- package/src/tools/multi-delegation-data-gen/utils/files.ts +1 -3
- package/src/tools/multi-delegation-data-gen/utils/terminal-progress-monitor.ts +2 -6
- package/src/tools/multi-delegation-data-gen/utils/utils.ts +1 -3
- package/test/artillery/StakePoolSearch.ts +5 -16
- package/test/artillery/artillery.ts +20 -61
- package/test/artillery/wallet-restoration/WalletRestoration.ts +1 -3
- package/test/artillery/wallet-restoration/queries.ts +1 -3
- package/test/artillery/wallet-restoration/types.ts +1 -3
- package/test/load-test-custom/stake-pool-search/stake-pool-search.test.ts +1 -3
- package/test/load-test-custom/wallet-init/wallet-init.test.ts +10 -1
- package/test/load-test-custom/wallet-restoration/wallet-restoration.test.ts +1 -4
- package/test/local-network/register-pool.test.ts +24 -14
- package/test/long-running/cache-invalidation.test.ts +7 -4
- package/test/long-running/multisig-wallet/MultiSigTx.ts +117 -0
- package/test/long-running/multisig-wallet/MultiSigWallet.ts +491 -0
- package/test/long-running/multisig-wallet/multisig-delegation-rewards.test.ts +318 -0
- package/test/projection/offline-fork.test.ts +47 -20
- package/test/projection/single-tenant-utxo.test.ts +40 -27
- package/test/providers/StakePoolProvider.test.ts +22 -17
- package/test/tsconfig.json +3 -0
- package/test/wallet/PersonalWallet/delegation.test.ts +6 -3
- package/test/wallet/PersonalWallet/handle.test.ts +2 -1
- package/test/wallet/PersonalWallet/mint.test.ts +2 -1
- package/test/wallet/PersonalWallet/multiAddress.test.ts +2 -1
- package/test/wallet/PersonalWallet/multisignature.test.ts +4 -2
- package/test/wallet/PersonalWallet/nft.test.ts +2 -1
- package/test/web-extension/extension/ui.ts +2 -8
- package/test/load-testing/tx-submit-load.test.ts +0 -341
|
@@ -388,6 +388,7 @@ describe('StakePoolProvider', () => {
|
|
|
388
388
|
|
|
389
389
|
filters = { identifier: { values: poolsId.map((id) => ({ id })) } };
|
|
390
390
|
});
|
|
391
|
+
|
|
391
392
|
it('ascending sort by live saturation', async () => {
|
|
392
393
|
if (poolsId.length < 2)
|
|
393
394
|
return console.log(
|
|
@@ -403,6 +404,7 @@ describe('StakePoolProvider', () => {
|
|
|
403
404
|
.map((p) => p.metrics?.saturation)
|
|
404
405
|
);
|
|
405
406
|
});
|
|
407
|
+
|
|
406
408
|
it('descending sort by live saturation', async () => {
|
|
407
409
|
if (poolsId.length < 2)
|
|
408
410
|
return console.log(
|
|
@@ -420,42 +422,45 @@ describe('StakePoolProvider', () => {
|
|
|
420
422
|
});
|
|
421
423
|
});
|
|
422
424
|
|
|
423
|
-
describe('sort by
|
|
425
|
+
describe.each(['lastRos', 'ros'] as const)('sort by %s', (field) => {
|
|
424
426
|
let filters: QueryStakePoolsArgs['filters'] = {};
|
|
425
427
|
const poolsId: Cardano.PoolId[] = [];
|
|
426
428
|
|
|
427
429
|
beforeAll(() => {
|
|
428
|
-
for (const pool of pools)
|
|
429
|
-
if (poolsId.length < 20 && pool.metrics?.
|
|
430
|
+
for (const pool of pools)
|
|
431
|
+
if (poolsId.length < 20 && pool.metrics?.[field] !== undefined && pool.metrics?.[field] > 0)
|
|
430
432
|
poolsId.push(pool.id);
|
|
431
|
-
|
|
432
|
-
}
|
|
433
|
+
|
|
433
434
|
filters = { identifier: { values: poolsId.map((id) => ({ id })) } };
|
|
434
435
|
});
|
|
435
|
-
|
|
436
|
+
|
|
437
|
+
it(`ascending sort by ${field}`, async () => {
|
|
436
438
|
if (poolsId.length < 2)
|
|
437
|
-
return console.log(
|
|
438
|
-
|
|
439
|
+
return console.log(`test 'ascending sort by ${field}' can't run because no suitable pools were found`);
|
|
440
|
+
|
|
441
|
+
const { pageResults, totalResultCount } = await query(filters, { field, order: 'asc' });
|
|
439
442
|
|
|
440
443
|
expect(totalResultCount).toBe(poolsId.length);
|
|
441
|
-
expect(pageResults.map((p) => p.metrics?.
|
|
444
|
+
expect(pageResults.map((p) => p.metrics?.[field])).toStrictEqual(
|
|
442
445
|
pools
|
|
443
446
|
.filter(({ id }) => poolsId.includes(id))
|
|
444
|
-
.sort((a, b) => (a.metrics
|
|
445
|
-
.map((p) => p.metrics
|
|
447
|
+
.sort((a, b) => (a.metrics![field]! < b.metrics![field]! ? -1 : 1))
|
|
448
|
+
.map((p) => p.metrics![field])
|
|
446
449
|
);
|
|
447
450
|
});
|
|
448
|
-
|
|
451
|
+
|
|
452
|
+
it(`descending sort by ${field}`, async () => {
|
|
449
453
|
if (poolsId.length < 2)
|
|
450
|
-
return console.log(
|
|
451
|
-
|
|
454
|
+
return console.log(`test 'descending sort by ${field}' can't run because no suitable pools were found`);
|
|
455
|
+
|
|
456
|
+
const { pageResults, totalResultCount } = await query(filters, { field, order: 'desc' });
|
|
452
457
|
|
|
453
458
|
expect(totalResultCount).toBe(poolsId.length);
|
|
454
|
-
expect(pageResults.map((p) => p.metrics?.
|
|
459
|
+
expect(pageResults.map((p) => p.metrics?.[field])).toStrictEqual(
|
|
455
460
|
pools
|
|
456
461
|
.filter(({ id }) => poolsId.includes(id))
|
|
457
|
-
.sort((a, b) => (a.metrics
|
|
458
|
-
.map((p) => p.metrics
|
|
462
|
+
.sort((a, b) => (a.metrics![field]! > b.metrics![field]! ? -1 : 1))
|
|
463
|
+
.map((p) => p.metrics![field])
|
|
459
464
|
);
|
|
460
465
|
});
|
|
461
466
|
});
|
package/test/tsconfig.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/* eslint-disable max-statements */
|
|
2
|
+
import * as Crypto from '@cardano-sdk/crypto';
|
|
2
3
|
import { BigIntMath } from '@cardano-sdk/util';
|
|
3
4
|
import { Cardano } from '@cardano-sdk/core';
|
|
4
5
|
import { ObservableWallet, PersonalWallet } from '@cardano-sdk/wallet';
|
|
5
6
|
import {
|
|
6
7
|
TX_TIMEOUT_DEFAULT,
|
|
7
8
|
TestWallet,
|
|
9
|
+
bip32Ed25519Factory,
|
|
8
10
|
firstValueFromTimed,
|
|
9
11
|
getEnv,
|
|
10
12
|
getWallet,
|
|
@@ -27,6 +29,7 @@ const getWalletStateSnapshot = async (wallet: ObservableWallet) => {
|
|
|
27
29
|
const utxoTotal = await firstValueFrom(wallet.utxo.total$);
|
|
28
30
|
const utxoAvailable = await firstValueFrom(wallet.utxo.available$);
|
|
29
31
|
const rewardsBalance = await firstValueFrom(wallet.balance.rewardAccounts.rewards$);
|
|
32
|
+
|
|
30
33
|
return {
|
|
31
34
|
balance: { available: balanceAvailable, deposit, total: balanceTotal },
|
|
32
35
|
epoch: epoch.epochNo,
|
|
@@ -54,6 +57,7 @@ const waitForTx = async (wallet: ObservableWallet, hash: Cardano.TransactionId)
|
|
|
54
57
|
describe('PersonalWallet/delegation', () => {
|
|
55
58
|
let wallet1: TestWallet;
|
|
56
59
|
let wallet2: TestWallet;
|
|
60
|
+
let bip32Ed25519: Crypto.Bip32Ed25519;
|
|
57
61
|
|
|
58
62
|
beforeAll(async () => {
|
|
59
63
|
jest.setTimeout(180_000);
|
|
@@ -61,6 +65,7 @@ describe('PersonalWallet/delegation', () => {
|
|
|
61
65
|
wallet2 = await getWallet({ env, idx: 1, logger, name: 'Test Wallet 2', polling: { interval: 500 } });
|
|
62
66
|
|
|
63
67
|
await Promise.all([waitForWalletStateSettle(wallet1.wallet), waitForWalletStateSettle(wallet2.wallet)]);
|
|
68
|
+
bip32Ed25519 = await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger);
|
|
64
69
|
});
|
|
65
70
|
|
|
66
71
|
afterAll(() => {
|
|
@@ -164,9 +169,7 @@ describe('PersonalWallet/delegation', () => {
|
|
|
164
169
|
expect(tx1ConfirmedState.rewardAccount.delegatee?.currentEpoch?.id).toEqual(poolId);
|
|
165
170
|
}
|
|
166
171
|
|
|
167
|
-
const stakeKeyHash = await (
|
|
168
|
-
await sourceWallet.keyAgent.getBip32Ed25519()
|
|
169
|
-
).getPubKeyHash(tx1ConfirmedState.publicStakeKey.publicStakeKey);
|
|
172
|
+
const stakeKeyHash = await bip32Ed25519.getPubKeyHash(tx1ConfirmedState.publicStakeKey.publicStakeKey);
|
|
170
173
|
expect(stakeKeyHash).toEqual(Cardano.RewardAccount.toHash(tx1ConfirmedState.rewardAccount.address));
|
|
171
174
|
expect(tx1ConfirmedState.publicStakeKey.keyStatus).toBe(Cardano.StakeKeyStatus.Registered);
|
|
172
175
|
|
|
@@ -3,6 +3,7 @@ import { Cardano, metadatum } from '@cardano-sdk/core';
|
|
|
3
3
|
import { KeyAgent, TransactionSigner } from '@cardano-sdk/key-management';
|
|
4
4
|
import { PersonalWallet } from '@cardano-sdk/wallet';
|
|
5
5
|
import {
|
|
6
|
+
bip32Ed25519Factory,
|
|
6
7
|
burnTokens,
|
|
7
8
|
coinsRequiredByHandleMint,
|
|
8
9
|
createHandleMetadata,
|
|
@@ -49,7 +50,7 @@ describe('Ada handle', () => {
|
|
|
49
50
|
keyAgent = await createStandaloneKeyAgent(
|
|
50
51
|
env.KEY_MANAGEMENT_PARAMS.mnemonic.split(' '),
|
|
51
52
|
await firstValueFrom(wallet.genesisParameters$),
|
|
52
|
-
await
|
|
53
|
+
await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger)
|
|
53
54
|
);
|
|
54
55
|
({ policyScript, policySigner, policyId } = await createHandlePolicy(keyAgent));
|
|
55
56
|
const handleProviderPolicyId = await getHandlePolicyId(
|
|
@@ -3,6 +3,7 @@ import { FinalizeTxProps, PersonalWallet } from '@cardano-sdk/wallet';
|
|
|
3
3
|
import { InitializeTxProps } from '@cardano-sdk/tx-construction';
|
|
4
4
|
import { KeyRole, util } from '@cardano-sdk/key-management';
|
|
5
5
|
import {
|
|
6
|
+
bip32Ed25519Factory,
|
|
6
7
|
burnTokens,
|
|
7
8
|
createStandaloneKeyAgent,
|
|
8
9
|
getEnv,
|
|
@@ -36,7 +37,7 @@ describe('PersonalWallet/mint', () => {
|
|
|
36
37
|
const aliceKeyAgent = await createStandaloneKeyAgent(
|
|
37
38
|
env.KEY_MANAGEMENT_PARAMS.mnemonic.split(' '),
|
|
38
39
|
genesis,
|
|
39
|
-
await
|
|
40
|
+
await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger)
|
|
40
41
|
);
|
|
41
42
|
|
|
42
43
|
const derivationPath = {
|
|
@@ -3,6 +3,7 @@ import { AddressType, GroupedAddress, util } from '@cardano-sdk/key-management';
|
|
|
3
3
|
import { Cardano } from '@cardano-sdk/core';
|
|
4
4
|
import {
|
|
5
5
|
KeyAgentFactoryProps,
|
|
6
|
+
bip32Ed25519Factory,
|
|
6
7
|
createStandaloneKeyAgent,
|
|
7
8
|
firstValueFromTimed,
|
|
8
9
|
getWallet,
|
|
@@ -42,7 +43,7 @@ describe('PersonalWallet/multiAddress', () => {
|
|
|
42
43
|
const multiAddressKeyAgent = await createStandaloneKeyAgent(
|
|
43
44
|
mnemonics,
|
|
44
45
|
genesis,
|
|
45
|
-
await
|
|
46
|
+
await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger)
|
|
46
47
|
);
|
|
47
48
|
|
|
48
49
|
let txBuilder = wallet.createTxBuilder();
|
|
@@ -4,6 +4,7 @@ import { FinalizeTxProps, PersonalWallet } from '@cardano-sdk/wallet';
|
|
|
4
4
|
import { InitializeTxProps } from '@cardano-sdk/tx-construction';
|
|
5
5
|
import { KeyRole, util } from '@cardano-sdk/key-management';
|
|
6
6
|
import {
|
|
7
|
+
bip32Ed25519Factory,
|
|
7
8
|
burnTokens,
|
|
8
9
|
createStandaloneKeyAgent,
|
|
9
10
|
getEnv,
|
|
@@ -34,15 +35,16 @@ describe('PersonalWallet/multisignature', () => {
|
|
|
34
35
|
|
|
35
36
|
const genesis = await firstValueFrom(wallet.genesisParameters$);
|
|
36
37
|
|
|
38
|
+
const bip32Ed25519 = await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger);
|
|
37
39
|
const aliceKeyAgent = await createStandaloneKeyAgent(
|
|
38
40
|
env.KEY_MANAGEMENT_PARAMS.mnemonic.split(' '),
|
|
39
41
|
genesis,
|
|
40
|
-
|
|
42
|
+
bip32Ed25519
|
|
41
43
|
);
|
|
42
44
|
const bobKeyAgent = await createStandaloneKeyAgent(
|
|
43
45
|
env.KEY_MANAGEMENT_PARAMS.mnemonic.split(' '),
|
|
44
46
|
genesis,
|
|
45
|
-
|
|
47
|
+
bip32Ed25519
|
|
46
48
|
);
|
|
47
49
|
|
|
48
50
|
const aliceDerivationPath = {
|
|
@@ -4,6 +4,7 @@ import { Assets, FinalizeTxProps, PersonalWallet } from '@cardano-sdk/wallet';
|
|
|
4
4
|
import { InitializeTxProps } from '@cardano-sdk/tx-construction';
|
|
5
5
|
import { KeyRole, TransactionSigner, util } from '@cardano-sdk/key-management';
|
|
6
6
|
import {
|
|
7
|
+
bip32Ed25519Factory,
|
|
7
8
|
burnTokens,
|
|
8
9
|
createStandaloneKeyAgent,
|
|
9
10
|
firstValueFromTimed,
|
|
@@ -60,7 +61,7 @@ describe('PersonalWallet.assets/nft', () => {
|
|
|
60
61
|
const keyAgent = await createStandaloneKeyAgent(
|
|
61
62
|
env.KEY_MANAGEMENT_PARAMS.mnemonic.split(' '),
|
|
62
63
|
genesis,
|
|
63
|
-
await
|
|
64
|
+
await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger)
|
|
64
65
|
);
|
|
65
66
|
|
|
66
67
|
const derivationPath = {
|
|
@@ -165,10 +165,7 @@ const deactivateWallet = async (): Promise<void> => {
|
|
|
165
165
|
clearWalletValues();
|
|
166
166
|
};
|
|
167
167
|
|
|
168
|
-
/**
|
|
169
|
-
* Wallet does not have any active delegations.
|
|
170
|
-
* Show a `<p class="noDelegation">No delegation found</p>`
|
|
171
|
-
*/
|
|
168
|
+
/** Wallet does not have any active delegations. Show a `<p class="noDelegation">No delegation found</p>` */
|
|
172
169
|
const createEmptyDelegationEl = () => {
|
|
173
170
|
const emptyDistribution = document.createElement('p');
|
|
174
171
|
emptyDistribution.classList.add('noDelegation');
|
|
@@ -176,10 +173,7 @@ const createEmptyDelegationEl = () => {
|
|
|
176
173
|
return emptyDistribution;
|
|
177
174
|
};
|
|
178
175
|
|
|
179
|
-
/**
|
|
180
|
-
* Create a list item for a delegation
|
|
181
|
-
* `<li> <span class="poolId">thePoolId</span> <span class="percent">50</span> </li>`
|
|
182
|
-
*/
|
|
176
|
+
/** Create a list item for a delegation `<li> <span class="poolId">thePoolId</span> <span class="percent">50</span> </li>` */
|
|
183
177
|
const createDelegationLi = (poolId: string, percent: string) => {
|
|
184
178
|
const delegationLi = document.createElement('li');
|
|
185
179
|
const poolIdSpan = document.createElement('span');
|
|
@@ -1,341 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { Cardano } from '@cardano-sdk/core';
|
|
3
|
-
import { ChildProcess, fork } from 'child_process';
|
|
4
|
-
import { InitializeTxResult } from '@cardano-sdk/tx-construction';
|
|
5
|
-
import { ObservableWallet } from '@cardano-sdk/wallet';
|
|
6
|
-
import { RabbitMQContainer } from '../../../cardano-services/test/TxSubmit/rabbitmq/docker';
|
|
7
|
-
import { ServiceNames } from '@cardano-sdk/cardano-services';
|
|
8
|
-
import { createLogger } from '@cardano-sdk/util-dev';
|
|
9
|
-
import { filter, firstValueFrom } from 'rxjs';
|
|
10
|
-
import { getEnv, submitAndConfirm, walletVariables } from '../../src';
|
|
11
|
-
import { getWallet } from '../../src/factories';
|
|
12
|
-
import JSONBig from 'json-bigint';
|
|
13
|
-
import path from 'path';
|
|
14
|
-
|
|
15
|
-
// Verify environment.
|
|
16
|
-
const env = getEnv([
|
|
17
|
-
...walletVariables,
|
|
18
|
-
'OGMIOS_URL',
|
|
19
|
-
'START_LOCAL_HTTP_SERVER',
|
|
20
|
-
'TRANSACTIONS_NUMBER',
|
|
21
|
-
'TX_SUBMIT_HTTP_URL',
|
|
22
|
-
'WORKER_PARALLEL_TRANSACTION'
|
|
23
|
-
]);
|
|
24
|
-
|
|
25
|
-
interface TestOptions {
|
|
26
|
-
directlyToOgmios?: boolean;
|
|
27
|
-
parallel?: boolean;
|
|
28
|
-
withRunningWorker?: boolean;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
interface TestReport extends TestOptions {
|
|
32
|
-
timeBeforeSubmitTxs: number;
|
|
33
|
-
timeAfterWorkerStarted: number;
|
|
34
|
-
timeAfterTxsInMempool: number;
|
|
35
|
-
timeAfterTxsInBlockchain: number;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface TestWallet {
|
|
39
|
-
address: Cardano.PaymentAddress;
|
|
40
|
-
coins: bigint;
|
|
41
|
-
wallet: ObservableWallet;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const logger = createLogger({ env: process.env.TL_LEVEL ? process.env : { ...process.env, TL_LEVEL: 'info' } });
|
|
45
|
-
|
|
46
|
-
let commonArgs: string[];
|
|
47
|
-
|
|
48
|
-
const runCli = (args: string[], startedString: string) =>
|
|
49
|
-
new Promise<ChildProcess>((resolve, reject) => {
|
|
50
|
-
const proc = fork(path.join(__dirname, '..', '..', '..', 'cardano-services', 'dist', 'cjs', 'cli.js'), args, {
|
|
51
|
-
stdio: 'pipe'
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
const logChunk = (method: typeof logger.info, chunk: string) => {
|
|
55
|
-
for (const line of chunk.split('\n')) {
|
|
56
|
-
if (line) {
|
|
57
|
-
let msg = line;
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
({ msg } = JSON.parse(line));
|
|
61
|
-
// eslint-disable-next-line no-empty
|
|
62
|
-
} catch {}
|
|
63
|
-
|
|
64
|
-
if (!msg.includes('\u001B[')) method(`${args[0]}: ${msg}`);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
proc.stderr!.once('data', (data) => reject(new Error(data.toString())));
|
|
70
|
-
proc.stderr!.on('data', (data) => logChunk(logger.error.bind(logger), data.toString()));
|
|
71
|
-
proc.stdout!.on('data', (data) => {
|
|
72
|
-
const chunk = data.toString();
|
|
73
|
-
|
|
74
|
-
logChunk(logger.info.bind(logger), chunk);
|
|
75
|
-
if (chunk.includes(startedString)) resolve(proc);
|
|
76
|
-
});
|
|
77
|
-
proc.once('error', reject);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
const stopProc = (proc: ChildProcess) =>
|
|
81
|
-
new Promise<void>((resolve) => (proc?.kill() ? proc.on('close', resolve) : resolve()));
|
|
82
|
-
|
|
83
|
-
let rabbitmqUrl: URL;
|
|
84
|
-
let serverProc: ChildProcess;
|
|
85
|
-
let workerProc: ChildProcess;
|
|
86
|
-
|
|
87
|
-
const startServer = async (options: TestOptions = {}) => {
|
|
88
|
-
if (env.START_LOCAL_HTTP_SERVER)
|
|
89
|
-
serverProc = await runCli(
|
|
90
|
-
[
|
|
91
|
-
'start-provider-server',
|
|
92
|
-
'--api-url',
|
|
93
|
-
env.TX_SUBMIT_HTTP_URL,
|
|
94
|
-
...(options.directlyToOgmios ? [] : ['--use-queue', 'true']),
|
|
95
|
-
...commonArgs,
|
|
96
|
-
ServiceNames.TxSubmit
|
|
97
|
-
],
|
|
98
|
-
'[HttpServer] Started'
|
|
99
|
-
);
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const startWorker = async (options: TestOptions = {}) => {
|
|
103
|
-
workerProc = await runCli(
|
|
104
|
-
[
|
|
105
|
-
'start-worker',
|
|
106
|
-
...commonArgs,
|
|
107
|
-
...(options.parallel ? ['--parallel', 'true', '--parallel-txs', env.WORKER_PARALLEL_TRANSACTION.toString()] : [])
|
|
108
|
-
],
|
|
109
|
-
'"msg":"TxSubmitWorker: starting'
|
|
110
|
-
);
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const stopServer = () => stopProc(serverProc);
|
|
114
|
-
const stopWorker = () => stopProc(workerProc);
|
|
115
|
-
|
|
116
|
-
const grace = (time: number) => `${time.toString().padStart(5, ' ')}ms`;
|
|
117
|
-
|
|
118
|
-
const waitForTxInBlockchain = async (wallet: ObservableWallet, txId: Cardano.TransactionId) => {
|
|
119
|
-
logger.info(`Waiting for tx ${txId} in blockchain...`);
|
|
120
|
-
await firstValueFrom(
|
|
121
|
-
wallet.transactions.history$.pipe(filter((txs) => txs.filter((tx) => tx.id === txId).length === 1))
|
|
122
|
-
);
|
|
123
|
-
logger.info(`Tx ${txId} in blockchain`);
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
describe('load', () => {
|
|
127
|
-
const container = new RabbitMQContainer('rabbitmq-load-test');
|
|
128
|
-
const testReports: TestReport[] = [];
|
|
129
|
-
const testWallets: TestWallet[] = [];
|
|
130
|
-
|
|
131
|
-
const prepareWallet = async (idx: number) => {
|
|
132
|
-
const walletEnv = getEnv(walletVariables, {
|
|
133
|
-
override: { TX_SUBMIT_PROVIDER_PARAMS: JSON.stringify({ baseUrl: env.TX_SUBMIT_HTTP_URL }) }
|
|
134
|
-
});
|
|
135
|
-
const { wallet } = await getWallet({ env: walletEnv, idx, logger, name: `Test Wallet ${idx}` });
|
|
136
|
-
const { address } = (await firstValueFrom(wallet.addresses$))[0];
|
|
137
|
-
logger.info(`Got wallet idx: ${idx} - address: ${address}`);
|
|
138
|
-
|
|
139
|
-
logger.debug(`Waiting to settle wallet ${idx} status`);
|
|
140
|
-
await firstValueFrom(wallet.syncStatus.isSettled$.pipe(filter((isSettled) => isSettled)));
|
|
141
|
-
logger.debug(`Wallet ${idx} status settled`);
|
|
142
|
-
|
|
143
|
-
testWallets.push({ address, coins: 0n, wallet });
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
const prepareWallets = async () => {
|
|
147
|
-
const promises: Promise<void>[] = [];
|
|
148
|
-
|
|
149
|
-
logger.info('Preparing wallets...');
|
|
150
|
-
|
|
151
|
-
for (let i = 0; i < env.TRANSACTIONS_NUMBER; ++i) promises.push(prepareWallet(i));
|
|
152
|
-
|
|
153
|
-
await Promise.all(promises);
|
|
154
|
-
|
|
155
|
-
logger.info('Wallets prepared');
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
const refreshWallets = async () => {
|
|
159
|
-
for (const testWallet of testWallets)
|
|
160
|
-
testWallet.coins = (await firstValueFrom(testWallet.wallet.balance.utxo.available$)).coins;
|
|
161
|
-
|
|
162
|
-
// Sort wallets from the one with the highest coins to the one with the lower
|
|
163
|
-
testWallets.sort((a, b) => Number(b.coins - a.coins));
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
const fragmentWhenRequired = async (options: TestOptions) => {
|
|
167
|
-
await refreshWallets();
|
|
168
|
-
|
|
169
|
-
const toRefill: Cardano.PaymentAddress[] = [];
|
|
170
|
-
const { wallet } = testWallets[0];
|
|
171
|
-
|
|
172
|
-
for (let i = 0; i < env.TRANSACTIONS_NUMBER; ++i)
|
|
173
|
-
if (testWallets[i].coins < 2_000_000n) toRefill.push(testWallets[i].address);
|
|
174
|
-
|
|
175
|
-
if (toRefill.length === 0) {
|
|
176
|
-
logger.info('Fragmentation tx not required');
|
|
177
|
-
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const coins = testWallets[0].coins / BigInt(toRefill.length + 1);
|
|
182
|
-
|
|
183
|
-
if (coins < 2_000_000n) throw new Error('Not enough coins to perform the test');
|
|
184
|
-
|
|
185
|
-
const fragment = async () => {
|
|
186
|
-
const tx = await wallet.initializeTx({
|
|
187
|
-
outputs: new Set(toRefill.map((address) => ({ address, value: { coins } })))
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
logger.info(`Fragmentation tx: ${tx.hash}`);
|
|
191
|
-
await submitAndConfirm(wallet, await wallet.finalizeTx({ tx }));
|
|
192
|
-
logger.info('Fragmentation completed');
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
if (options.directlyToOgmios) await fragment();
|
|
196
|
-
else {
|
|
197
|
-
await Promise.all([startWorker(), fragment()]);
|
|
198
|
-
await stopWorker();
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
beforeAll(async () => {
|
|
203
|
-
jest.setTimeout(180_000);
|
|
204
|
-
|
|
205
|
-
({ rabbitmqUrl } = await container.start());
|
|
206
|
-
|
|
207
|
-
commonArgs = [
|
|
208
|
-
'--logger-min-severity',
|
|
209
|
-
'debug',
|
|
210
|
-
'--ogmios-url',
|
|
211
|
-
env.OGMIOS_URL,
|
|
212
|
-
'--rabbitmq-url',
|
|
213
|
-
rabbitmqUrl.toString()
|
|
214
|
-
];
|
|
215
|
-
|
|
216
|
-
await startServer({ directlyToOgmios: true });
|
|
217
|
-
await prepareWallets();
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
afterAll(async () => {
|
|
221
|
-
logger.info(' Test result');
|
|
222
|
-
|
|
223
|
-
for (const report of testReports) {
|
|
224
|
-
const {
|
|
225
|
-
directlyToOgmios,
|
|
226
|
-
parallel,
|
|
227
|
-
withRunningWorker,
|
|
228
|
-
timeAfterTxsInBlockchain,
|
|
229
|
-
timeAfterTxsInMempool,
|
|
230
|
-
timeAfterWorkerStarted,
|
|
231
|
-
timeBeforeSubmitTxs
|
|
232
|
-
} = report;
|
|
233
|
-
|
|
234
|
-
const timeReport =
|
|
235
|
-
directlyToOgmios || withRunningWorker
|
|
236
|
-
? `submission -> mempool: ${grace(timeAfterTxsInMempool - timeBeforeSubmitTxs)}`
|
|
237
|
-
: `start worker -> mempool: ${grace(timeAfterTxsInMempool - timeAfterWorkerStarted)}`;
|
|
238
|
-
|
|
239
|
-
const workerDescription = `with${withRunningWorker ? ' ' : 'out'} running ${
|
|
240
|
-
parallel ? 'parallel' : 'serial '
|
|
241
|
-
} worker`;
|
|
242
|
-
|
|
243
|
-
logger.info(
|
|
244
|
-
` ${
|
|
245
|
-
directlyToOgmios ? 'directly to ogmios ' : workerDescription
|
|
246
|
-
} - ${timeReport} - mempool -> blockchain: ${grace(
|
|
247
|
-
timeAfterTxsInBlockchain - timeAfterTxsInMempool
|
|
248
|
-
)} - total: ${grace(timeAfterTxsInBlockchain - timeBeforeSubmitTxs)}`
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
for (const { wallet } of testWallets) wallet.shutdown();
|
|
253
|
-
|
|
254
|
-
// Dependencies teardown parallelization
|
|
255
|
-
await Promise.all([container.stop(), stopServer()]);
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
afterEach(stopWorker);
|
|
259
|
-
|
|
260
|
-
const performTest = async (options: TestOptions) => {
|
|
261
|
-
const { directlyToOgmios, parallel, withRunningWorker } = options;
|
|
262
|
-
const submitPromises: Promise<void>[] = [];
|
|
263
|
-
const txIds: Cardano.TransactionId[] = [];
|
|
264
|
-
let timeAfterWorkerStarted = 0;
|
|
265
|
-
let timeBeforeSubmitTxs = 0;
|
|
266
|
-
|
|
267
|
-
logger.info(`Starting test with options: ${JSON.stringify({ directlyToOgmios, parallel, withRunningWorker })}`);
|
|
268
|
-
|
|
269
|
-
await fragmentWhenRequired(options);
|
|
270
|
-
|
|
271
|
-
const startWorkerForTest = async () => {
|
|
272
|
-
if (!directlyToOgmios) await startWorker(options);
|
|
273
|
-
timeAfterWorkerStarted = Date.now();
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
const finalizeAndSubmit = async (wallet: ObservableWallet, tx: InitializeTxResult) => {
|
|
277
|
-
try {
|
|
278
|
-
await wallet.submitTx(await wallet.finalizeTx({ tx }));
|
|
279
|
-
logger.info(`Submitted tx: ${tx.hash}`);
|
|
280
|
-
} catch (error) {
|
|
281
|
-
logger.error(JSONBig.stringify(tx), error);
|
|
282
|
-
throw error;
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
const submitTransactions = async () => {
|
|
287
|
-
timeBeforeSubmitTxs = Date.now();
|
|
288
|
-
|
|
289
|
-
for (let i = 0; i < env.TRANSACTIONS_NUMBER; ++i) {
|
|
290
|
-
const { address, wallet } = testWallets[i];
|
|
291
|
-
const coins = 1_000_000n + BigInt(i);
|
|
292
|
-
const tx = await wallet.initializeTx({ outputs: new Set([{ address, value: { coins } }]) });
|
|
293
|
-
logger.info(`Initializing tx idx ${i}: ${tx.hash}`);
|
|
294
|
-
|
|
295
|
-
submitPromises.push(finalizeAndSubmit(wallet, tx));
|
|
296
|
-
txIds.push(tx.hash);
|
|
297
|
-
}
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
if (withRunningWorker) {
|
|
301
|
-
await startWorkerForTest();
|
|
302
|
-
await submitTransactions();
|
|
303
|
-
} else {
|
|
304
|
-
await submitTransactions();
|
|
305
|
-
await startWorkerForTest();
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
await expect(Promise.all(submitPromises)).resolves.not.toThrow();
|
|
309
|
-
const timeAfterTxsInMempool = Date.now();
|
|
310
|
-
|
|
311
|
-
await Promise.all(txIds.map((txId, i) => waitForTxInBlockchain(testWallets[i].wallet, txId)));
|
|
312
|
-
|
|
313
|
-
testReports.push({
|
|
314
|
-
...options,
|
|
315
|
-
timeAfterTxsInBlockchain: Date.now(),
|
|
316
|
-
timeAfterTxsInMempool,
|
|
317
|
-
timeAfterWorkerStarted,
|
|
318
|
-
timeBeforeSubmitTxs
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
logger.info(`Completed test with options: ${JSON.stringify({ directlyToOgmios, parallel, withRunningWorker })}`);
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
describe('directly to ogmios', () => {
|
|
325
|
-
afterAll(stopServer);
|
|
326
|
-
|
|
327
|
-
it('without queue', async () => {
|
|
328
|
-
if (env.TRANSACTIONS_NUMBER < 30) await performTest({ directlyToOgmios: true });
|
|
329
|
-
else logger.info('Skipping directly to ogmios test due to transaction number > 30');
|
|
330
|
-
});
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
describe('using queue', () => {
|
|
334
|
-
beforeAll(() => startServer({}));
|
|
335
|
-
|
|
336
|
-
it('without running serial worker', async () => await performTest({}));
|
|
337
|
-
it('with running serial worker', async () => await performTest({ withRunningWorker: true }));
|
|
338
|
-
it('without running parallel worker', async () => await performTest({ parallel: true }));
|
|
339
|
-
it('with running parallel worker', async () => await performTest({ parallel: true, withRunningWorker: true }));
|
|
340
|
-
});
|
|
341
|
-
});
|