@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.
Files changed (78) hide show
  1. package/.env.example +1 -7
  2. package/CHANGELOG.md +24 -0
  3. package/README.md +0 -29
  4. package/dist/cjs/environment.d.ts.map +1 -1
  5. package/dist/cjs/environment.js.map +1 -1
  6. package/dist/cjs/factories.d.ts.map +1 -1
  7. package/dist/cjs/factories.js +7 -1
  8. package/dist/cjs/factories.js.map +1 -1
  9. package/dist/cjs/measurement-util.d.ts.map +1 -1
  10. package/dist/cjs/measurement-util.js.map +1 -1
  11. package/dist/cjs/scripts/is-local-network-ready.js.map +1 -1
  12. package/dist/cjs/scripts/mnemonic.js.map +1 -1
  13. package/dist/cjs/tools/multi-delegation-data-gen/utils/files.d.ts.map +1 -1
  14. package/dist/cjs/tools/multi-delegation-data-gen/utils/files.js.map +1 -1
  15. package/dist/cjs/tools/multi-delegation-data-gen/utils/terminal-progress-monitor.d.ts.map +1 -1
  16. package/dist/cjs/tools/multi-delegation-data-gen/utils/terminal-progress-monitor.js.map +1 -1
  17. package/dist/cjs/tools/multi-delegation-data-gen/utils/utils.d.ts.map +1 -1
  18. package/dist/cjs/tools/multi-delegation-data-gen/utils/utils.js.map +1 -1
  19. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  20. package/dist/esm/environment.d.ts.map +1 -1
  21. package/dist/esm/environment.js.map +1 -1
  22. package/dist/esm/factories.d.ts.map +1 -1
  23. package/dist/esm/factories.js +7 -1
  24. package/dist/esm/factories.js.map +1 -1
  25. package/dist/esm/measurement-util.d.ts.map +1 -1
  26. package/dist/esm/measurement-util.js.map +1 -1
  27. package/dist/esm/scripts/is-local-network-ready.js.map +1 -1
  28. package/dist/esm/scripts/mnemonic.js.map +1 -1
  29. package/dist/esm/tools/multi-delegation-data-gen/utils/files.d.ts.map +1 -1
  30. package/dist/esm/tools/multi-delegation-data-gen/utils/files.js.map +1 -1
  31. package/dist/esm/tools/multi-delegation-data-gen/utils/terminal-progress-monitor.d.ts.map +1 -1
  32. package/dist/esm/tools/multi-delegation-data-gen/utils/terminal-progress-monitor.js.map +1 -1
  33. package/dist/esm/tools/multi-delegation-data-gen/utils/utils.d.ts.map +1 -1
  34. package/dist/esm/tools/multi-delegation-data-gen/utils/utils.js.map +1 -1
  35. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  36. package/docker-compose.yml +4 -0
  37. package/jest.config.js +0 -1
  38. package/local-network/scripts/common.sh +19 -0
  39. package/local-network/scripts/make-babbage.sh +2 -1
  40. package/local-network/scripts/mint-handles.sh +2 -18
  41. package/local-network/scripts/mint-tokens.sh +2 -18
  42. package/local-network/scripts/mnemonic_keys.sh +0 -0
  43. package/local-network/scripts/setup-wallets.sh +2 -17
  44. package/local-network/templates/babbage/submit-api-config.json +115 -0
  45. package/package.json +27 -29
  46. package/src/environment.ts +2 -6
  47. package/src/factories.ts +11 -5
  48. package/src/measurement-util.ts +1 -4
  49. package/src/scripts/is-local-network-ready.ts +1 -3
  50. package/src/scripts/mnemonic.ts +1 -3
  51. package/src/tools/multi-delegation-data-gen/utils/files.ts +1 -3
  52. package/src/tools/multi-delegation-data-gen/utils/terminal-progress-monitor.ts +2 -6
  53. package/src/tools/multi-delegation-data-gen/utils/utils.ts +1 -3
  54. package/test/artillery/StakePoolSearch.ts +5 -16
  55. package/test/artillery/artillery.ts +20 -61
  56. package/test/artillery/wallet-restoration/WalletRestoration.ts +1 -3
  57. package/test/artillery/wallet-restoration/queries.ts +1 -3
  58. package/test/artillery/wallet-restoration/types.ts +1 -3
  59. package/test/load-test-custom/stake-pool-search/stake-pool-search.test.ts +1 -3
  60. package/test/load-test-custom/wallet-init/wallet-init.test.ts +10 -1
  61. package/test/load-test-custom/wallet-restoration/wallet-restoration.test.ts +1 -4
  62. package/test/local-network/register-pool.test.ts +24 -14
  63. package/test/long-running/cache-invalidation.test.ts +7 -4
  64. package/test/long-running/multisig-wallet/MultiSigTx.ts +117 -0
  65. package/test/long-running/multisig-wallet/MultiSigWallet.ts +491 -0
  66. package/test/long-running/multisig-wallet/multisig-delegation-rewards.test.ts +318 -0
  67. package/test/projection/offline-fork.test.ts +47 -20
  68. package/test/projection/single-tenant-utxo.test.ts +40 -27
  69. package/test/providers/StakePoolProvider.test.ts +22 -17
  70. package/test/tsconfig.json +3 -0
  71. package/test/wallet/PersonalWallet/delegation.test.ts +6 -3
  72. package/test/wallet/PersonalWallet/handle.test.ts +2 -1
  73. package/test/wallet/PersonalWallet/mint.test.ts +2 -1
  74. package/test/wallet/PersonalWallet/multiAddress.test.ts +2 -1
  75. package/test/wallet/PersonalWallet/multisignature.test.ts +4 -2
  76. package/test/wallet/PersonalWallet/nft.test.ts +2 -1
  77. package/test/web-extension/extension/ui.ts +2 -8
  78. 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 apy', () => {
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?.apy !== undefined && pool.metrics?.apy > 0) {
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
- it('ascending sort by apy', async () => {
436
+
437
+ it(`ascending sort by ${field}`, async () => {
436
438
  if (poolsId.length < 2)
437
- return console.log("test 'ascending sort by apy' can't run because no suitable pools were found");
438
- const { pageResults, totalResultCount } = await query(filters, { field: 'apy', order: 'asc' });
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?.apy)).toStrictEqual(
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!.apy! < b.metrics!.apy! ? -1 : 1))
445
- .map((p) => p.metrics?.apy)
447
+ .sort((a, b) => (a.metrics![field]! < b.metrics![field]! ? -1 : 1))
448
+ .map((p) => p.metrics![field])
446
449
  );
447
450
  });
448
- it('descending sort by apy', async () => {
451
+
452
+ it(`descending sort by ${field}`, async () => {
449
453
  if (poolsId.length < 2)
450
- return console.log("test 'descending sort by apy' can't run because no suitable pools were found");
451
- const { pageResults, totalResultCount } = await query(filters, { field: 'apy', order: 'desc' });
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?.apy)).toStrictEqual(
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!.apy! > b.metrics!.apy! ? -1 : 1))
458
- .map((p) => p.metrics?.apy)
462
+ .sort((a, b) => (a.metrics![field]! > b.metrics![field]! ? -1 : 1))
463
+ .map((p) => p.metrics![field])
459
464
  );
460
465
  });
461
466
  });
@@ -26,6 +26,9 @@
26
26
  {
27
27
  "path": "../../util-dev/src"
28
28
  },
29
+ {
30
+ "path": "../../util-rxjs/src"
31
+ },
29
32
  {
30
33
  "path": "../../wallet/src"
31
34
  },
@@ -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 wallet.keyAgent.getBip32Ed25519()
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 wallet.keyAgent.getBip32Ed25519()
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 wallet.keyAgent.getBip32Ed25519()
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
- await wallet.keyAgent.getBip32Ed25519()
42
+ bip32Ed25519
41
43
  );
42
44
  const bobKeyAgent = await createStandaloneKeyAgent(
43
45
  env.KEY_MANAGEMENT_PARAMS.mnemonic.split(' '),
44
46
  genesis,
45
- await wallet.keyAgent.getBip32Ed25519()
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 wallet.keyAgent.getBip32Ed25519()
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
- });