@cardano-sdk/e2e 0.16.6-patch.0 → 0.16.6
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 +4 -6
- package/CHANGELOG.md +11 -9
- package/README.md +2 -18
- package/dist/cjs/environment.d.ts +1 -1
- package/dist/cjs/environment.d.ts.map +1 -1
- package/dist/cjs/environment.js +1 -15
- package/dist/cjs/environment.js.map +1 -1
- package/dist/cjs/factories.d.ts +0 -2
- package/dist/cjs/factories.d.ts.map +1 -1
- package/dist/cjs/factories.js +6 -12
- package/dist/cjs/factories.js.map +1 -1
- package/dist/cjs/index.d.ts +0 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +0 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/scripts/is-local-network-ready.js +5 -28
- package/dist/cjs/scripts/is-local-network-ready.js.map +1 -1
- package/dist/cjs/tools/multi-delegation-data-gen/index.js +13 -3
- package/dist/cjs/tools/multi-delegation-data-gen/index.js.map +1 -1
- package/dist/cjs/tools/multi-delegation-data-gen/utils/config.d.ts +1 -0
- package/dist/cjs/tools/multi-delegation-data-gen/utils/config.d.ts.map +1 -1
- package/dist/cjs/tools/multi-delegation-data-gen/utils/config.js +12 -0
- package/dist/cjs/tools/multi-delegation-data-gen/utils/config.js.map +1 -1
- package/dist/cjs/tools/multi-delegation-data-gen/utils/utils.d.ts +2 -5
- 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 +12 -49
- package/dist/cjs/tools/multi-delegation-data-gen/utils/utils.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/util/util.d.ts +0 -1
- package/dist/cjs/util/util.d.ts.map +1 -1
- package/dist/cjs/util/util.js +2 -43
- package/dist/cjs/util/util.js.map +1 -1
- package/dist/esm/environment.d.ts +1 -1
- package/dist/esm/environment.d.ts.map +1 -1
- package/dist/esm/environment.js +1 -15
- package/dist/esm/environment.js.map +1 -1
- package/dist/esm/factories.d.ts +0 -2
- package/dist/esm/factories.d.ts.map +1 -1
- package/dist/esm/factories.js +6 -12
- package/dist/esm/factories.js.map +1 -1
- package/dist/esm/index.d.ts +0 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +0 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/scripts/is-local-network-ready.js +5 -5
- package/dist/esm/scripts/is-local-network-ready.js.map +1 -1
- package/dist/esm/tools/multi-delegation-data-gen/index.js +14 -4
- package/dist/esm/tools/multi-delegation-data-gen/index.js.map +1 -1
- package/dist/esm/tools/multi-delegation-data-gen/utils/config.d.ts +1 -0
- package/dist/esm/tools/multi-delegation-data-gen/utils/config.d.ts.map +1 -1
- package/dist/esm/tools/multi-delegation-data-gen/utils/config.js +12 -0
- package/dist/esm/tools/multi-delegation-data-gen/utils/config.js.map +1 -1
- package/dist/esm/tools/multi-delegation-data-gen/utils/utils.d.ts +2 -5
- 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 +11 -45
- package/dist/esm/tools/multi-delegation-data-gen/utils/utils.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/util/util.d.ts +0 -1
- package/dist/esm/util/util.d.ts.map +1 -1
- package/dist/esm/util/util.js +3 -20
- package/dist/esm/util/util.js.map +1 -1
- package/docker-compose.yml +3 -21
- package/local-network/scripts/mint-handles.sh +108 -6
- package/local-network/scripts/mint-tokens.sh +21 -2
- package/local-network/scripts/mnemonic_keys.sh +14 -0
- package/local-network/scripts/setup-wallets.sh +18 -0
- package/local-network/scripts/start.sh +1 -2
- package/package.json +22 -20
- package/src/environment.ts +1 -19
- package/src/factories.ts +5 -13
- package/src/index.ts +0 -1
- package/src/scripts/is-local-network-ready.ts +6 -8
- package/src/tools/multi-delegation-data-gen/example.json +1 -0
- package/src/tools/multi-delegation-data-gen/index.ts +18 -8
- package/src/tools/multi-delegation-data-gen/utils/config.ts +13 -0
- package/src/tools/multi-delegation-data-gen/utils/utils.ts +13 -110
- package/src/util/util.ts +2 -29
- package/test/k6/endpoints/asset/get-asset.test.js +15 -14
- package/test/k6/endpoints/asset/get-assets.test.js +269 -269
- package/test/k6/endpoints/chain-history/blocks/by-hashes.test.js +12 -11
- package/test/k6/endpoints/chain-history/txs/by-addresses.test.js +18 -15
- package/test/k6/endpoints/chain-history/txs/by-hashes.test.js +12 -11
- package/test/k6/endpoints/network-info/era-summaries.test.js +11 -10
- package/test/k6/endpoints/network-info/genesis-parameters.test.js +11 -10
- package/test/k6/endpoints/network-info/ledger-tip.test.js +11 -10
- package/test/k6/endpoints/network-info/lovelace-supply.test.js +11 -10
- package/test/k6/endpoints/network-info/protocol-parameters.test.js +11 -10
- package/test/k6/endpoints/network-info/stake.test.js +11 -10
- package/test/k6/endpoints/rewards/account-balance.test.js +12 -11
- package/test/k6/endpoints/stake-pool/stats.test.js +11 -10
- package/test/k6/endpoints/utxo/utxo-by-addresses.test.js +16 -11
- package/test/k6/scenarios/tx-submission.test.js +47 -46
- package/test/k6/scenarios/wallet-creation.test.js +127 -120
- package/test/k6/scenarios/wallet-restoration.test.js +264 -126
- package/test/long-running/cache-invalidation.test.ts +64 -13
- package/test/providers/HandleProvider.test.ts +39 -0
- package/test/wallet/PersonalWallet/handle.test.ts +38 -16
- package/test/web-extension/webpack.config.base.js +0 -4
- package/dist/cjs/FaucetProvider/index.d.ts +0 -3
- package/dist/cjs/FaucetProvider/index.d.ts.map +0 -1
- package/dist/cjs/FaucetProvider/index.js +0 -19
- package/dist/cjs/FaucetProvider/index.js.map +0 -1
- package/dist/cjs/FaucetProvider/providers/cardanoWalletFaucetProvider.d.ts +0 -15
- package/dist/cjs/FaucetProvider/providers/cardanoWalletFaucetProvider.d.ts.map +0 -1
- package/dist/cjs/FaucetProvider/providers/cardanoWalletFaucetProvider.js +0 -148
- package/dist/cjs/FaucetProvider/providers/cardanoWalletFaucetProvider.js.map +0 -1
- package/dist/cjs/FaucetProvider/types.d.ts +0 -22
- package/dist/cjs/FaucetProvider/types.d.ts.map +0 -1
- package/dist/cjs/FaucetProvider/types.js +0 -14
- package/dist/cjs/FaucetProvider/types.js.map +0 -1
- package/dist/esm/FaucetProvider/index.d.ts +0 -3
- package/dist/esm/FaucetProvider/index.d.ts.map +0 -1
- package/dist/esm/FaucetProvider/index.js +0 -3
- package/dist/esm/FaucetProvider/index.js.map +0 -1
- package/dist/esm/FaucetProvider/providers/cardanoWalletFaucetProvider.d.ts +0 -15
- package/dist/esm/FaucetProvider/providers/cardanoWalletFaucetProvider.d.ts.map +0 -1
- package/dist/esm/FaucetProvider/providers/cardanoWalletFaucetProvider.js +0 -141
- package/dist/esm/FaucetProvider/providers/cardanoWalletFaucetProvider.js.map +0 -1
- package/dist/esm/FaucetProvider/types.d.ts +0 -22
- package/dist/esm/FaucetProvider/types.d.ts.map +0 -1
- package/dist/esm/FaucetProvider/types.js +0 -10
- package/dist/esm/FaucetProvider/types.js.map +0 -1
- package/local-network/faucet/README.md +0 -12
- package/local-network/faucet/faucet-addresses.json +0 -45
- package/local-network/faucet/faucet-mnemonic.json +0 -6
- package/local-network/faucet/faucet-send-funds.json +0 -23
- package/local-network/scripts/is-faucet-ready.sh +0 -29
- package/src/FaucetProvider/index.ts +0 -2
- package/src/FaucetProvider/providers/cardanoWalletFaucetProvider.ts +0 -233
- package/src/FaucetProvider/types.ts +0 -94
|
@@ -1,192 +1,330 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
/* eslint-disable func-style */
|
|
3
3
|
import { Counter, Trend } from 'k6/metrics';
|
|
4
|
+
import { check, sleep } from 'k6';
|
|
5
|
+
import http from 'k6/http';
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
|
-
* Script overall description:
|
|
7
|
-
* One wallet == one VU
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* #Script overall description:
|
|
9
|
+
* - One wallet == one VU
|
|
10
|
+
* - One wallet == 1 address for `RunMode.Onboard` and `RunMode.Restore`
|
|
11
|
+
* - One wallet == n addresses for `RunMode.RestoreHD` mode
|
|
12
|
+
* - Configure RUN_MODE to have the test run with empty wallets (onboarding), wallets with history (restoring) or HD wallets with history
|
|
13
|
+
* and discovery phase.
|
|
14
|
+
* - SDK `PersonalWallet` does HD discovery for every wallet, to determine the active addresses, so it will be done even for single address wallets.
|
|
15
|
+
* To simulate this use `RunMode.RestoreHD` with `hdWalletParams.activeAddrCount: 1`.
|
|
16
|
+
* - Configure MAX_VU: number of wallets to be synced (e.g. 100 wallets)
|
|
17
|
+
* - Setup RAMP_UP_DURATION: ramp-up time to synced these wallets (e.g. 30s)
|
|
11
18
|
* - During this time, wallets start evenly distributed (e.g. ~3 wallets every second)
|
|
12
|
-
* Each wallet performs as many iterations as possible
|
|
13
|
-
* - 1st iteration: restoration steps
|
|
19
|
+
* - Each wallet performs as many iterations as possible
|
|
20
|
+
* - 1st iteration: restoration steps.
|
|
14
21
|
* - Subsequent iterations will only query the tip
|
|
15
|
-
* ITERATION_SLEEP: Every iteration has a sleep of to simulate Lace tip polling interval
|
|
16
|
-
* During the STEADY_STATE_DURATION:
|
|
22
|
+
* - ITERATION_SLEEP: Every iteration has a sleep of to simulate Lace tip polling interval
|
|
23
|
+
* - During the STEADY_STATE_DURATION:
|
|
17
24
|
* - Synced wallets do tip queries.
|
|
18
25
|
* - Wallets not synced yet will wait for restoration to complete, then will also start tip queries
|
|
19
|
-
* No ramp-down is needed. We can simply stop the test as there is no point in sending fewer and fewer tip queries during ramp-down.
|
|
20
|
-
* wallet_sync: is a custom trend metric measuring the trend of wallet sync calls.
|
|
21
|
-
* wallet_sync_count: is a custom count metric measuring the number of wallets that were successfully synced.
|
|
26
|
+
* - No ramp-down is needed. We can simply stop the test as there is no point in sending fewer and fewer tip queries during ramp-down.
|
|
27
|
+
* - wallet_sync: is a custom trend metric measuring the trend of wallet sync calls.
|
|
28
|
+
* - wallet_sync_count: is a custom count metric measuring the number of wallets that were successfully synced.
|
|
22
29
|
*/
|
|
23
30
|
|
|
24
31
|
const RunMode = {
|
|
25
|
-
|
|
26
|
-
|
|
32
|
+
Onboard: 'Onboard',
|
|
33
|
+
Restore: 'Restore',
|
|
34
|
+
RestoreHD: 'RestoreHD'
|
|
27
35
|
};
|
|
28
36
|
/** Determines run mode: Restore or Onboard */
|
|
29
37
|
const RUN_MODE = RunMode.Restore;
|
|
38
|
+
|
|
39
|
+
// eslint-disable-next-line no-undef
|
|
30
40
|
const PROVIDER_SERVER_URL = __ENV.PROVIDER_SERVER_URL;
|
|
31
41
|
|
|
32
42
|
/** URL of the JSON file containing the wallets */
|
|
33
43
|
const WALLET_ADDRESSES_URL =
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
44
|
+
RUN_MODE === RunMode.Onboard
|
|
45
|
+
? 'https://raw.githubusercontent.com/input-output-hk/cardano-js-sdk/master/packages/e2e/test/dump/addresses/no-history-mainnet.json'
|
|
46
|
+
: 'https://raw.githubusercontent.com/input-output-hk/cardano-js-sdk/master/packages/e2e/test/dump/addresses/mainnet.json';
|
|
37
47
|
|
|
38
48
|
/** URL of the JSON file containing the stake pool addresses */
|
|
39
49
|
const POOL_ADDRESSES_URL =
|
|
40
|
-
|
|
50
|
+
'https://raw.githubusercontent.com/input-output-hk/cardano-js-sdk/master/packages/e2e/test/dump/pool_addresses/mainnet.json';
|
|
41
51
|
|
|
42
52
|
/**
|
|
43
53
|
* Define the maximum number of virtual users to simulate
|
|
44
|
-
* The mainnet.json file contains
|
|
45
|
-
* For this reason, it's a good practice to configure MAX_VUs in multiples of 100 in order to maintain the desired distribution
|
|
54
|
+
* The mainnet.json file contains wallet addresses in chunks of 100, each chunk having the required distribution
|
|
55
|
+
* For this reason, it's a good practice to configure MAX_VUs in multiples of 100 in order to maintain the desired distribution.
|
|
56
|
+
* In `RunMode.RestoreHD`, each VU will have multiple addresses.
|
|
46
57
|
*/
|
|
47
|
-
const MAX_VU =
|
|
58
|
+
const MAX_VU = 10;
|
|
48
59
|
|
|
49
60
|
/** Time span during which all virtual users are started in a linear fashion */
|
|
50
|
-
const RAMP_UP_DURATION = '
|
|
61
|
+
const RAMP_UP_DURATION = '10s';
|
|
51
62
|
/** Time span during which synced wallets do tip queries */
|
|
52
|
-
const STEADY_STATE_DURATION = '
|
|
63
|
+
const STEADY_STATE_DURATION = '20s';
|
|
53
64
|
|
|
54
65
|
/** Time to sleep between iterations. Simulates polling tip to keep wallet in sync */
|
|
55
66
|
const ITERATION_SLEEP = 5;
|
|
56
67
|
|
|
68
|
+
/** HD wallet discovery. Used when RunMode is `RestoreHD` */
|
|
69
|
+
const hdWalletParams = {
|
|
70
|
+
/** HD wallet size. The number of addresses with transaction history per wallet. They are queried at discover time. */
|
|
71
|
+
activeAddrCount: 10,
|
|
72
|
+
/** Use only addresses with a transaction history up to this value */
|
|
73
|
+
maxTxHistory: 100,
|
|
74
|
+
/** number of payment addresses to search for. It will search both internal and external address, thus multiplied by 2 */
|
|
75
|
+
paymentAddrSearchGap: 20 * 2,
|
|
76
|
+
/** number of stake keys to search for. It will search both internal and external address, thus multiplied by 2 */
|
|
77
|
+
stakeAddrSearchGap: 5 * 2
|
|
78
|
+
};
|
|
79
|
+
|
|
57
80
|
/** Custom trend statistic to measure trend to sync wallets */
|
|
58
81
|
const walletSyncTrend = new Trend('wallet_sync', true);
|
|
59
82
|
/** Custom count statistic to measure how many wallets were successfully syncd */
|
|
60
83
|
const walletSyncCount = new Counter('wallet_sync_count');
|
|
61
84
|
|
|
62
|
-
/**
|
|
85
|
+
/** Repetitive endpoints */
|
|
86
|
+
const TIP_URL = 'network-info/ledger-tip';
|
|
87
|
+
|
|
88
|
+
/** equivalent to lodash.chunk */
|
|
89
|
+
const chunkArray = (array, chunkSize) => {
|
|
90
|
+
const arrayCopy = [...array];
|
|
91
|
+
const chunked = [];
|
|
92
|
+
while (arrayCopy.length > 0) {
|
|
93
|
+
chunked.push(arrayCopy.splice(0, chunkSize));
|
|
94
|
+
}
|
|
95
|
+
return chunked;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Grab the wallets json file to be used by the scenario.
|
|
100
|
+
* Group the addresses per wallet (single address or HD wallets).
|
|
101
|
+
*/
|
|
63
102
|
export function setup() {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
103
|
+
console.log(`Running in ${RUN_MODE} mode`);
|
|
104
|
+
console.log(`Ramp-up: ${RAMP_UP_DURATION}; Sustain: ${STEADY_STATE_DURATION}; Iteration sleep: ${ITERATION_SLEEP}s`);
|
|
105
|
+
|
|
106
|
+
if (RUN_MODE === RunMode.RestoreHD) {
|
|
107
|
+
console.log('HD wallet params are:', hdWalletParams);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const res = http.batch([WALLET_ADDRESSES_URL, POOL_ADDRESSES_URL]);
|
|
111
|
+
check(res, { 'get wallets and pools files': (r) => r.every(({ status }) => status >= 200 && status < 300) });
|
|
112
|
+
|
|
113
|
+
const [{ body: resBodyWallets }, { body: resBodyPools }] = res;
|
|
114
|
+
const walletsOrig = JSON.parse(resBodyWallets);
|
|
115
|
+
const walletsOrigCount = walletsOrig ? walletsOrig.length : 0;
|
|
116
|
+
check(walletsOrigCount, {
|
|
117
|
+
'At least one wallet is required to run the test': (count) => count > 0
|
|
118
|
+
});
|
|
119
|
+
console.log(`Wallet addresses configuration file contains ${walletsOrigCount} addresses`);
|
|
120
|
+
|
|
121
|
+
// One wallet, one address
|
|
122
|
+
let wallets = chunkArray(walletsOrig, 1);
|
|
123
|
+
if (RUN_MODE === RunMode.RestoreHD) {
|
|
124
|
+
// One wallet, multiple addresses
|
|
125
|
+
// Remove "big transaction history wallets"
|
|
126
|
+
const filteredWallets = walletsOrig.filter(({ tx_count }) => tx_count < hdWalletParams.maxTxHistory);
|
|
127
|
+
// Create chunks of `activeAddrCount` addresses per HD wallet
|
|
128
|
+
wallets = chunkArray(filteredWallets, hdWalletParams.activeAddrCount);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const requestedAddrCount = RUN_MODE === RunMode.RestoreHD ? MAX_VU * hdWalletParams.activeAddrCount : MAX_VU;
|
|
132
|
+
const availableAddrCount = wallets.length;
|
|
80
133
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
134
|
+
if (availableAddrCount < requestedAddrCount) {
|
|
135
|
+
console.warn(
|
|
136
|
+
`Requested wallet count * addresses per wallet: ${requestedAddrCount}, is greater than the available addresses: ${availableAddrCount}. Some addresses will be reused`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const poolAddresses = JSON.parse(resBodyPools);
|
|
141
|
+
check(poolAddresses, {
|
|
142
|
+
'At least one stake pool address is required to run the test': (p) => p && p.length > 0
|
|
143
|
+
});
|
|
144
|
+
return { poolAddresses, wallets: wallets.slice(0, MAX_VU) };
|
|
86
145
|
}
|
|
87
146
|
|
|
88
|
-
/** Keeps track of wallets that were successfully
|
|
147
|
+
/** Keeps track of wallets that were successfully synced to avoid restoring twice */
|
|
89
148
|
const syncedWallets = new Set();
|
|
90
149
|
|
|
91
|
-
export
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
stages: [
|
|
109
|
-
{ duration: RAMP_UP_DURATION, target: MAX_VU },
|
|
110
|
-
{ duration: STEADY_STATE_DURATION, target: MAX_VU }
|
|
111
|
-
],
|
|
112
|
-
gracefulRampDown: '0s',
|
|
113
|
-
gracefulStop: '0s'
|
|
114
|
-
}
|
|
150
|
+
export const options = {
|
|
151
|
+
ext: {
|
|
152
|
+
loadimpact: {
|
|
153
|
+
apm: [],
|
|
154
|
+
distribution: { 'amazon:us:portland': { loadZone: 'amazon:us:portland', percent: 100 } }
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
scenarios: {
|
|
158
|
+
SyncDifferentSizeWallets: {
|
|
159
|
+
executor: 'ramping-vus',
|
|
160
|
+
gracefulRampDown: '0s',
|
|
161
|
+
gracefulStop: '0s',
|
|
162
|
+
stages: [
|
|
163
|
+
{ duration: RAMP_UP_DURATION, target: MAX_VU },
|
|
164
|
+
{ duration: STEADY_STATE_DURATION, target: MAX_VU }
|
|
165
|
+
],
|
|
166
|
+
startVUs: 1
|
|
115
167
|
}
|
|
168
|
+
},
|
|
169
|
+
thresholds: {
|
|
170
|
+
// All wallets should have syncd
|
|
171
|
+
// Use https://k6.io/docs/using-k6/thresholds/ to set more thresholds. E.g.:
|
|
172
|
+
// wallet_sync: ['p(95)<5000'], // 95% of wallets should sync in under 5 seconds
|
|
173
|
+
wallet_sync: [{ delayAbortEval: '5s', threshold: 'p(95) < 30000' }],
|
|
174
|
+
|
|
175
|
+
wallet_sync_count: [`count >= ${MAX_VU}`] // We get a nice graph if we enable thresholds. See this stat on a graph
|
|
176
|
+
}
|
|
116
177
|
};
|
|
117
178
|
|
|
118
179
|
/** Util functions for sending the http post requests to cardano-sdk services */
|
|
119
180
|
const cardanoHttpPost = (url, body = {}) => {
|
|
120
|
-
|
|
121
|
-
|
|
181
|
+
const opts = { headers: { 'content-type': 'application/json' } };
|
|
182
|
+
return http.post(`${PROVIDER_SERVER_URL}/${url}`, JSON.stringify(body), opts);
|
|
122
183
|
};
|
|
123
|
-
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
*
|
|
187
|
+
* @param addresses Bech32 cardano addresses: `Cardano.Address[]`
|
|
188
|
+
* @param takeOne true: query only the first page; false: query until no more pages
|
|
189
|
+
* @param pageSize Use as request page size. Also, bundle this many addresses on each request.
|
|
190
|
+
*/
|
|
191
|
+
const txsByAddress = (addresses, takeOne = false, pageSize = 25) => {
|
|
192
|
+
const addressChunks = chunkArray(addresses, pageSize);
|
|
193
|
+
for (const chunk of addressChunks) {
|
|
124
194
|
let startAt = 0;
|
|
125
|
-
const pageSize = 25;
|
|
126
195
|
let txCount = 0;
|
|
196
|
+
|
|
127
197
|
do {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
} while (txCount === pageSize);
|
|
198
|
+
const resp = cardanoHttpPost('chain-history/txs/by-addresses', {
|
|
199
|
+
addresses: chunk,
|
|
200
|
+
blockRange: { lowerBound: { __type: 'undefined' } },
|
|
201
|
+
pagination: { limit: pageSize, startAt }
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
if (resp.status !== 200) {
|
|
205
|
+
// No point in trying to get the other pages.
|
|
206
|
+
// Should we log this? it will show up as if the restoration was quicker since this wallet did not fetch all the pages
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const { pageResults } = JSON.parse(resp.body);
|
|
211
|
+
startAt += pageSize;
|
|
212
|
+
txCount = pageResults.length;
|
|
213
|
+
} while (txCount === pageSize && !takeOne);
|
|
214
|
+
}
|
|
144
215
|
};
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
|
|
216
|
+
|
|
217
|
+
const utxosByAddresses = (addresses) => {
|
|
218
|
+
const addressChunks = chunkArray(addresses, 25);
|
|
219
|
+
for (const chunk of addressChunks) {
|
|
220
|
+
cardanoHttpPost('utxo/utxo-by-addresses', { addresses: chunk });
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const rewardsAccBalance = (rewardAccount) => cardanoHttpPost('rewards/account-balance', { rewardAccount });
|
|
148
225
|
const stakePoolSearch = (poolAddress) =>
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
226
|
+
cardanoHttpPost('stake-pool/search', {
|
|
227
|
+
filters: { identifier: { values: [{ id: poolAddress }] } },
|
|
228
|
+
pagination: { limit: 1, startAt: 0 }
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Changes the last 3 chars. Checksum will be broken but I assume it is not verified
|
|
233
|
+
* Avoiding to import keyagent here to generate actual addresses as it would be difficult to do it
|
|
234
|
+
* in K6 cloud.
|
|
235
|
+
*/
|
|
236
|
+
const getDummyAddr = (addr, idx, suffix = 'mh') => {
|
|
237
|
+
const last3Chars = addr.slice(-3);
|
|
238
|
+
const updateChars = last3Chars !== `${suffix}${idx}` ? `${suffix}${idx}` : `${idx}${suffix}`;
|
|
239
|
+
return addr.slice(0, -3) + updateChars;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Simulate http requests normally done in discovery mode.
|
|
244
|
+
* `wallet` MUST have at least 2 elements
|
|
245
|
+
*/
|
|
246
|
+
const walletDiscovery = (wallet) => {
|
|
247
|
+
check(wallet, {
|
|
248
|
+
'At least one address is required to run HD wallet discovery mode': (walletArray) =>
|
|
249
|
+
walletArray && walletArray.length > 0
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
console.debug(`Start walletDiscovery for ${wallet.length} addresses`);
|
|
253
|
+
|
|
254
|
+
// Discover stake keys derived at index > 0 on the first payment key
|
|
255
|
+
// We don't expect to find anything here so we'll use dummy addresses
|
|
256
|
+
console.debug('Discover stake keys on payment #0');
|
|
257
|
+
for (let i = 0; i < hdWalletParams.stakeAddrSearchGap; i++) {
|
|
258
|
+
const addr = getDummyAddr(wallet[0].address, i);
|
|
259
|
+
txsByAddress([addr], true, 1);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Discover active payment addresses
|
|
263
|
+
console.debug('Discover payment addresses #1+');
|
|
264
|
+
for (const { address } of wallet) {
|
|
265
|
+
// Even if txByAddresses accepts multiple addresses, discovery does it one by one
|
|
266
|
+
txsByAddress([address], true, 1);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Discover calls in payment address gap
|
|
270
|
+
console.debug('Discover in search gap');
|
|
271
|
+
for (let i = 0; i < hdWalletParams.paymentAddrSearchGap; i++) {
|
|
272
|
+
const addr = getDummyAddr(wallet[0].address, i, 'hm');
|
|
273
|
+
txsByAddress([addr], true, 1);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
153
276
|
|
|
154
277
|
/** Simulation of requests performed by a wallet while restoring */
|
|
155
278
|
const syncWallet = ({ wallet, poolAddress }) => {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
279
|
+
const startTime = Date.now();
|
|
280
|
+
const addresses = wallet.map(({ address }) => address);
|
|
281
|
+
|
|
282
|
+
if (RUN_MODE === RunMode.RestoreHD) {
|
|
283
|
+
walletDiscovery(wallet);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
cardanoHttpPost('network-info/era-summaries');
|
|
287
|
+
cardanoHttpPost(TIP_URL);
|
|
288
|
+
txsByAddress(addresses);
|
|
289
|
+
utxosByAddresses(addresses);
|
|
290
|
+
cardanoHttpPost('network-info/era-summaries');
|
|
291
|
+
cardanoHttpPost('network-info/genesis-parameters');
|
|
292
|
+
cardanoHttpPost('network-info/protocol-parameters');
|
|
293
|
+
// Test restoring HD wallets with a single stake key
|
|
294
|
+
rewardsAccBalance(wallet[0].stake_address);
|
|
295
|
+
cardanoHttpPost(TIP_URL);
|
|
296
|
+
cardanoHttpPost('network-info/lovelace-supply');
|
|
297
|
+
cardanoHttpPost('network-info/stake');
|
|
298
|
+
if (RUN_MODE === RunMode.Restore) {
|
|
299
|
+
stakePoolSearch(poolAddress);
|
|
300
|
+
}
|
|
301
|
+
cardanoHttpPost('stake-pool/stats');
|
|
173
302
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
303
|
+
// Consider the wallet synced by tracking its first address
|
|
304
|
+
syncedWallets.add(addresses[0]);
|
|
305
|
+
walletSyncTrend.add(Date.now() - startTime);
|
|
306
|
+
walletSyncCount.add(1);
|
|
177
307
|
};
|
|
178
308
|
|
|
179
309
|
/**
|
|
180
310
|
* Simulate keeping wallet in sync
|
|
181
311
|
* For now, just polling the tip
|
|
182
312
|
*/
|
|
183
|
-
const emulateIdleClient = () => cardanoHttpPost(
|
|
313
|
+
const emulateIdleClient = () => cardanoHttpPost(TIP_URL);
|
|
184
314
|
|
|
315
|
+
/**
|
|
316
|
+
* K6 default VU action function
|
|
317
|
+
*
|
|
318
|
+
* wallets: {address: Cardano.Address, stake_address: Cardano.RewardAccount, tx_count: number}[][]
|
|
319
|
+
* poolAddresses: Cardano.PoolId[]
|
|
320
|
+
*/
|
|
185
321
|
export default function ({ wallets, poolAddresses }) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
322
|
+
// Get the wallet for the current virtual user
|
|
323
|
+
// eslint-disable-next-line no-undef
|
|
324
|
+
const vu = __VU;
|
|
325
|
+
const wallet = wallets[vu % wallets.length]; // each wallet is a collection of addresses
|
|
326
|
+
const poolAddress = poolAddresses[vu % poolAddresses.length];
|
|
189
327
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
328
|
+
syncedWallets.has(wallet[0].address) ? emulateIdleClient() : syncWallet({ poolAddress, wallet });
|
|
329
|
+
sleep(ITERATION_SLEEP);
|
|
330
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable max-statements */
|
|
2
1
|
import { AddressType, KeyRole } from '@cardano-sdk/key-management';
|
|
3
2
|
import { Cardano } from '@cardano-sdk/core';
|
|
4
3
|
import {
|
|
@@ -13,24 +12,72 @@ import {
|
|
|
13
12
|
walletReady,
|
|
14
13
|
walletVariables
|
|
15
14
|
} from '../../src';
|
|
15
|
+
import { containerExec } from 'dockerode-utils';
|
|
16
|
+
import { getRandomPort } from 'get-port-please';
|
|
16
17
|
import { logger } from '@cardano-sdk/util-dev';
|
|
18
|
+
import Docker from 'dockerode';
|
|
19
|
+
import path from 'path';
|
|
17
20
|
|
|
18
|
-
const env = getEnv(walletVariables);
|
|
19
21
|
const vrf = Cardano.VrfVkHex('2ee5a4c423224bb9c42107fc18a60556d6a83cec1d9dd37a71f56af7198fc759');
|
|
20
22
|
|
|
21
|
-
const wallet1Params: KeyAgentFactoryProps = {
|
|
22
|
-
accountIndex: 0,
|
|
23
|
-
chainId: env.KEY_MANAGEMENT_PARAMS.chainId,
|
|
24
|
-
mnemonic:
|
|
25
|
-
// eslint-disable-next-line max-len
|
|
26
|
-
'phrase raw learn suspect inmate powder combine apology regular hero gain chronic fruit ritual short screen goddess odor keen creek brand today kit machine',
|
|
27
|
-
passphrase: 'some_passphrase'
|
|
28
|
-
};
|
|
29
|
-
|
|
30
23
|
describe('cache invalidation', () => {
|
|
24
|
+
let testProviderServer: Docker.Container;
|
|
31
25
|
let wallet1: TestWallet;
|
|
32
26
|
|
|
33
27
|
beforeAll(async () => {
|
|
28
|
+
const port = await getRandomPort();
|
|
29
|
+
|
|
30
|
+
// Get environment from original provider server container
|
|
31
|
+
const docker = new Docker();
|
|
32
|
+
const originalProviderServer = docker.getContainer('local-network-e2e-provider-server-1');
|
|
33
|
+
const cmdOutput = await containerExec(originalProviderServer, [
|
|
34
|
+
'node',
|
|
35
|
+
'-e',
|
|
36
|
+
'console.log(`sdk_token${JSON.stringify(process.env)}sdk_token`)'
|
|
37
|
+
]);
|
|
38
|
+
const matchResult = cmdOutput[0].match(/sdk_token(.*)sdk_token/);
|
|
39
|
+
|
|
40
|
+
if (!matchResult) throw new Error('Error getting original container environment');
|
|
41
|
+
|
|
42
|
+
const [, encodedEnv] = matchResult;
|
|
43
|
+
|
|
44
|
+
const Env = Object.entries({
|
|
45
|
+
...JSON.parse(encodedEnv),
|
|
46
|
+
DISABLE_DB_CACHE: 'false',
|
|
47
|
+
LOGGER_MIN_SEVERITY: 'debug'
|
|
48
|
+
}).map(([key, value]) => `${key}=${value}`);
|
|
49
|
+
|
|
50
|
+
const network = docker.getNetwork('local-network-e2e_default');
|
|
51
|
+
|
|
52
|
+
// Test container
|
|
53
|
+
testProviderServer = await docker.createContainer({
|
|
54
|
+
Env,
|
|
55
|
+
HostConfig: {
|
|
56
|
+
Binds: [`${path.join(__dirname, '..', '..', '..', '..', 'compose', 'placeholder-secrets')}:/run/secrets`],
|
|
57
|
+
PortBindings: { '3000/tcp': [{ HostPort: port.toString() }] }
|
|
58
|
+
},
|
|
59
|
+
Image: 'local-network-e2e-provider-server',
|
|
60
|
+
name: 'local-network-e2e-provider-server-test'
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
await network.connect({ Container: testProviderServer.id });
|
|
64
|
+
await testProviderServer.start();
|
|
65
|
+
|
|
66
|
+
const override = Object.fromEntries(
|
|
67
|
+
Object.entries(process.env)
|
|
68
|
+
.filter(([key]) => walletVariables.includes(key as typeof walletVariables[number]))
|
|
69
|
+
.map(([key, value]) => [key, value?.replace('localhost:4000/', `localhost:${port}/`)])
|
|
70
|
+
);
|
|
71
|
+
const env = getEnv(walletVariables, { override });
|
|
72
|
+
const wallet1Params: KeyAgentFactoryProps = {
|
|
73
|
+
accountIndex: 0,
|
|
74
|
+
chainId: env.KEY_MANAGEMENT_PARAMS.chainId,
|
|
75
|
+
mnemonic:
|
|
76
|
+
// eslint-disable-next-line max-len
|
|
77
|
+
'phrase raw learn suspect inmate powder combine apology regular hero gain chronic fruit ritual short screen goddess odor keen creek brand today kit machine',
|
|
78
|
+
passphrase: 'some_passphrase'
|
|
79
|
+
};
|
|
80
|
+
|
|
34
81
|
jest.setTimeout(180_000);
|
|
35
82
|
|
|
36
83
|
wallet1 = await getWallet({
|
|
@@ -45,9 +92,13 @@ describe('cache invalidation', () => {
|
|
|
45
92
|
await waitForWalletStateSettle(wallet1.wallet);
|
|
46
93
|
});
|
|
47
94
|
|
|
48
|
-
afterAll(() =>
|
|
95
|
+
afterAll(async () => {
|
|
96
|
+
wallet1.wallet.shutdown();
|
|
97
|
+
await testProviderServer.stop();
|
|
98
|
+
await testProviderServer.remove();
|
|
99
|
+
});
|
|
49
100
|
|
|
50
|
-
test
|
|
101
|
+
test('cache is invalidated on epoch rollover', async () => {
|
|
51
102
|
const wallet = wallet1.wallet;
|
|
52
103
|
|
|
53
104
|
await walletReady(wallet);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createLogger } from '@cardano-sdk/util-dev';
|
|
2
|
+
import { getEnv, walletVariables } from '../../src';
|
|
3
|
+
import { handleHttpProvider } from '@cardano-sdk/cardano-services-client';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
|
|
7
|
+
const logger = createLogger();
|
|
8
|
+
const env = getEnv(walletVariables);
|
|
9
|
+
|
|
10
|
+
describe('HandleProvider', () => {
|
|
11
|
+
it('resolves handle', async () => {
|
|
12
|
+
const policyPath = path.join(__dirname, '../../local-network/sdk-ipc/handle_policy_ids');
|
|
13
|
+
const policyId = fs.readFileSync(policyPath, 'utf8').toString().trim();
|
|
14
|
+
const handleName = 'HelloHandle'; // handle minted in mint-handles.sh
|
|
15
|
+
const handleName2 = 'TestHandle';
|
|
16
|
+
const config = { baseUrl: env.HANDLE_PROVIDER_PARAMS.baseUrl, logger };
|
|
17
|
+
const handleProvider = handleHttpProvider(config);
|
|
18
|
+
const handle = await handleProvider.resolveHandles({ handles: [handleName, handleName2] });
|
|
19
|
+
expect(handle.length).toEqual(2);
|
|
20
|
+
expect(handle[0]?.handle).toEqual('HelloHandle');
|
|
21
|
+
expect(handle[0]?.hasDatum).toEqual(false);
|
|
22
|
+
expect(handle[0]?.policyId).toEqual(policyId);
|
|
23
|
+
expect(handle[0]?.resolvedAt).toBeDefined();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('resolves non existent handle with null', async () => {
|
|
27
|
+
const config = { baseUrl: env.HANDLE_PROVIDER_PARAMS.baseUrl, logger };
|
|
28
|
+
const handleProvider = handleHttpProvider(config);
|
|
29
|
+
const handle = await handleProvider.resolveHandles({ handles: ['nonexistent'] });
|
|
30
|
+
expect(handle).toEqual([null]);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('allows request an empty list of handles', async () => {
|
|
34
|
+
const config = { baseUrl: env.HANDLE_PROVIDER_PARAMS.baseUrl, logger };
|
|
35
|
+
const handleProvider = handleHttpProvider(config);
|
|
36
|
+
const handle = await handleProvider.resolveHandles({ handles: [] });
|
|
37
|
+
expect(handle.length).toEqual(0);
|
|
38
|
+
});
|
|
39
|
+
});
|