@aztec/end-to-end 0.0.1-commit.2ed92850 → 0.0.1-commit.2f68f620

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 (234) hide show
  1. package/README.md +27 -0
  2. package/dest/bench/client_flows/benchmark.d.ts +15 -1
  3. package/dest/bench/client_flows/benchmark.d.ts.map +1 -1
  4. package/dest/bench/client_flows/benchmark.js +17 -0
  5. package/dest/bench/client_flows/client_flows_benchmark.d.ts +3 -3
  6. package/dest/bench/client_flows/client_flows_benchmark.d.ts.map +1 -1
  7. package/dest/bench/client_flows/client_flows_benchmark.js +36 -39
  8. package/dest/bench/client_flows/config.d.ts +2 -2
  9. package/dest/bench/client_flows/config.d.ts.map +1 -1
  10. package/dest/bench/client_flows/config.js +18 -0
  11. package/dest/bench/utils.d.ts +1 -1
  12. package/dest/bench/utils.d.ts.map +1 -1
  13. package/dest/bench/utils.js +8 -3
  14. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts +8 -5
  15. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts.map +1 -1
  16. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.js +36 -17
  17. package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.d.ts +16 -5
  18. package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.d.ts.map +1 -1
  19. package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.js +42 -9
  20. package/dest/e2e_deploy_contract/deploy_test.d.ts +4 -4
  21. package/dest/e2e_deploy_contract/deploy_test.d.ts.map +1 -1
  22. package/dest/e2e_deploy_contract/deploy_test.js +2 -1
  23. package/dest/e2e_epochs/epochs_test.d.ts +39 -8
  24. package/dest/e2e_epochs/epochs_test.d.ts.map +1 -1
  25. package/dest/e2e_epochs/epochs_test.js +170 -52
  26. package/dest/e2e_fees/fees_test.d.ts +6 -3
  27. package/dest/e2e_fees/fees_test.d.ts.map +1 -1
  28. package/dest/e2e_fees/fees_test.js +50 -17
  29. package/dest/e2e_l1_publisher/write_json.d.ts +3 -2
  30. package/dest/e2e_l1_publisher/write_json.d.ts.map +1 -1
  31. package/dest/e2e_l1_publisher/write_json.js +1 -7
  32. package/dest/e2e_nested_contract/nested_contract_test.d.ts +3 -3
  33. package/dest/e2e_nested_contract/nested_contract_test.d.ts.map +1 -1
  34. package/dest/e2e_nested_contract/nested_contract_test.js +6 -7
  35. package/dest/e2e_p2p/inactivity_slash_test.d.ts +1 -1
  36. package/dest/e2e_p2p/inactivity_slash_test.d.ts.map +1 -1
  37. package/dest/e2e_p2p/inactivity_slash_test.js +4 -3
  38. package/dest/e2e_p2p/p2p_network.d.ts +14 -12
  39. package/dest/e2e_p2p/p2p_network.d.ts.map +1 -1
  40. package/dest/e2e_p2p/p2p_network.js +70 -34
  41. package/dest/e2e_p2p/reqresp/utils.d.ts +22 -0
  42. package/dest/e2e_p2p/reqresp/utils.d.ts.map +1 -0
  43. package/dest/e2e_p2p/reqresp/utils.js +206 -0
  44. package/dest/e2e_p2p/shared.d.ts +37 -8
  45. package/dest/e2e_p2p/shared.d.ts.map +1 -1
  46. package/dest/e2e_p2p/shared.js +93 -53
  47. package/dest/e2e_storage_proof/fixtures/storage_proof_fetcher.d.ts +2 -0
  48. package/dest/e2e_storage_proof/fixtures/storage_proof_fetcher.d.ts.map +1 -0
  49. package/dest/e2e_storage_proof/fixtures/storage_proof_fetcher.js +184 -0
  50. package/dest/e2e_storage_proof/fixtures/storage_proof_fixture.d.ts +18 -0
  51. package/dest/e2e_storage_proof/fixtures/storage_proof_fixture.d.ts.map +1 -0
  52. package/dest/e2e_storage_proof/fixtures/storage_proof_fixture.js +120 -0
  53. package/dest/e2e_token_contract/token_contract_test.d.ts +6 -4
  54. package/dest/e2e_token_contract/token_contract_test.d.ts.map +1 -1
  55. package/dest/e2e_token_contract/token_contract_test.js +23 -11
  56. package/dest/fixtures/authwit_proxy.d.ts +15 -0
  57. package/dest/fixtures/authwit_proxy.d.ts.map +1 -0
  58. package/dest/fixtures/authwit_proxy.js +34 -0
  59. package/dest/fixtures/e2e_prover_test.d.ts +9 -8
  60. package/dest/fixtures/e2e_prover_test.d.ts.map +1 -1
  61. package/dest/fixtures/e2e_prover_test.js +40 -51
  62. package/dest/fixtures/elu_monitor.d.ts +21 -0
  63. package/dest/fixtures/elu_monitor.d.ts.map +1 -0
  64. package/dest/fixtures/elu_monitor.js +102 -0
  65. package/dest/fixtures/fixtures.d.ts +74 -1
  66. package/dest/fixtures/fixtures.d.ts.map +1 -1
  67. package/dest/fixtures/fixtures.js +71 -0
  68. package/dest/fixtures/get_bb_config.d.ts +1 -1
  69. package/dest/fixtures/get_bb_config.d.ts.map +1 -1
  70. package/dest/fixtures/get_bb_config.js +5 -5
  71. package/dest/fixtures/ha_setup.d.ts +71 -0
  72. package/dest/fixtures/ha_setup.d.ts.map +1 -0
  73. package/dest/fixtures/ha_setup.js +116 -0
  74. package/dest/fixtures/index.d.ts +2 -1
  75. package/dest/fixtures/index.d.ts.map +1 -1
  76. package/dest/fixtures/index.js +1 -0
  77. package/dest/fixtures/schnorr_hardcoded_account_contract.d.ts +25 -0
  78. package/dest/fixtures/schnorr_hardcoded_account_contract.d.ts.map +1 -0
  79. package/dest/fixtures/schnorr_hardcoded_account_contract.js +37 -0
  80. package/dest/fixtures/setup.d.ts +88 -34
  81. package/dest/fixtures/setup.d.ts.map +1 -1
  82. package/dest/fixtures/setup.js +225 -180
  83. package/dest/fixtures/setup_p2p_test.d.ts +22 -11
  84. package/dest/fixtures/setup_p2p_test.d.ts.map +1 -1
  85. package/dest/fixtures/setup_p2p_test.js +43 -32
  86. package/dest/fixtures/token_utils.d.ts +2 -2
  87. package/dest/fixtures/token_utils.d.ts.map +1 -1
  88. package/dest/fixtures/token_utils.js +5 -7
  89. package/dest/fixtures/utils.d.ts +2 -2
  90. package/dest/fixtures/utils.d.ts.map +1 -1
  91. package/dest/fixtures/utils.js +1 -1
  92. package/dest/forward-compatibility/wallet_rpc_client.d.ts +7 -0
  93. package/dest/forward-compatibility/wallet_rpc_client.d.ts.map +1 -0
  94. package/dest/forward-compatibility/wallet_rpc_client.js +15 -0
  95. package/dest/forward-compatibility/wallet_service.d.ts +3 -0
  96. package/dest/forward-compatibility/wallet_service.d.ts.map +1 -0
  97. package/dest/forward-compatibility/wallet_service.js +109 -0
  98. package/dest/install_legacy_contracts.d.cts +10 -0
  99. package/dest/install_legacy_contracts.d.cts.map +1 -0
  100. package/dest/legacy-jest-resolver.d.cts +3 -0
  101. package/dest/legacy-jest-resolver.d.cts.map +1 -0
  102. package/dest/shared/cross_chain_test_harness.d.ts +4 -2
  103. package/dest/shared/cross_chain_test_harness.d.ts.map +1 -1
  104. package/dest/shared/cross_chain_test_harness.js +22 -18
  105. package/dest/shared/gas_portal_test_harness.d.ts +8 -5
  106. package/dest/shared/gas_portal_test_harness.d.ts.map +1 -1
  107. package/dest/shared/gas_portal_test_harness.js +19 -10
  108. package/dest/shared/index.d.ts +2 -1
  109. package/dest/shared/index.d.ts.map +1 -1
  110. package/dest/shared/index.js +1 -0
  111. package/dest/shared/jest_setup.js +41 -1
  112. package/dest/shared/mock_state_view.d.ts +86 -0
  113. package/dest/shared/mock_state_view.d.ts.map +1 -0
  114. package/dest/shared/mock_state_view.js +186 -0
  115. package/dest/shared/submit-transactions.d.ts +2 -2
  116. package/dest/shared/submit-transactions.d.ts.map +1 -1
  117. package/dest/shared/submit-transactions.js +1 -1
  118. package/dest/shared/uniswap_l1_l2.d.ts +1 -1
  119. package/dest/shared/uniswap_l1_l2.d.ts.map +1 -1
  120. package/dest/shared/uniswap_l1_l2.js +57 -40
  121. package/dest/shared/wait_for_l1_to_l2_message.d.ts +13 -0
  122. package/dest/shared/wait_for_l1_to_l2_message.d.ts.map +1 -0
  123. package/dest/shared/wait_for_l1_to_l2_message.js +10 -0
  124. package/dest/simulators/lending_simulator.d.ts +10 -3
  125. package/dest/simulators/lending_simulator.d.ts.map +1 -1
  126. package/dest/simulators/lending_simulator.js +26 -14
  127. package/dest/simulators/token_simulator.d.ts +1 -1
  128. package/dest/simulators/token_simulator.d.ts.map +1 -1
  129. package/dest/simulators/token_simulator.js +3 -24
  130. package/dest/spartan/setup_test_wallets.d.ts +12 -3
  131. package/dest/spartan/setup_test_wallets.d.ts.map +1 -1
  132. package/dest/spartan/setup_test_wallets.js +108 -41
  133. package/dest/spartan/tx_metrics.d.ts +52 -4
  134. package/dest/spartan/tx_metrics.d.ts.map +1 -1
  135. package/dest/spartan/tx_metrics.js +224 -21
  136. package/dest/spartan/utils/bot.d.ts +3 -2
  137. package/dest/spartan/utils/bot.d.ts.map +1 -1
  138. package/dest/spartan/utils/bot.js +2 -1
  139. package/dest/spartan/utils/config.d.ts +11 -28
  140. package/dest/spartan/utils/config.d.ts.map +1 -1
  141. package/dest/spartan/utils/config.js +4 -1
  142. package/dest/spartan/utils/index.d.ts +6 -4
  143. package/dest/spartan/utils/index.d.ts.map +1 -1
  144. package/dest/spartan/utils/index.js +6 -2
  145. package/dest/spartan/utils/k8s.d.ts +31 -1
  146. package/dest/spartan/utils/k8s.d.ts.map +1 -1
  147. package/dest/spartan/utils/k8s.js +124 -0
  148. package/dest/spartan/utils/nodes.d.ts +14 -5
  149. package/dest/spartan/utils/nodes.d.ts.map +1 -1
  150. package/dest/spartan/utils/nodes.js +204 -33
  151. package/dest/spartan/utils/pod_logs.d.ts +25 -0
  152. package/dest/spartan/utils/pod_logs.d.ts.map +1 -0
  153. package/dest/spartan/utils/pod_logs.js +74 -0
  154. package/dest/spartan/utils/scripts.d.ts +18 -4
  155. package/dest/spartan/utils/scripts.d.ts.map +1 -1
  156. package/dest/spartan/utils/scripts.js +19 -4
  157. package/dest/test-wallet/test_wallet.d.ts +85 -0
  158. package/dest/test-wallet/test_wallet.d.ts.map +1 -0
  159. package/dest/test-wallet/test_wallet.js +273 -0
  160. package/dest/test-wallet/utils.d.ts +41 -0
  161. package/dest/test-wallet/utils.d.ts.map +1 -0
  162. package/dest/test-wallet/utils.js +66 -0
  163. package/dest/test-wallet/wallet_worker_script.d.ts +2 -0
  164. package/dest/test-wallet/wallet_worker_script.d.ts.map +1 -0
  165. package/dest/test-wallet/wallet_worker_script.js +53 -0
  166. package/dest/test-wallet/worker_wallet.d.ts +53 -0
  167. package/dest/test-wallet/worker_wallet.d.ts.map +1 -0
  168. package/dest/test-wallet/worker_wallet.js +155 -0
  169. package/dest/test-wallet/worker_wallet_schema.d.ts +160 -0
  170. package/dest/test-wallet/worker_wallet_schema.d.ts.map +1 -0
  171. package/dest/test-wallet/worker_wallet_schema.js +22 -0
  172. package/package.json +52 -45
  173. package/src/bench/client_flows/benchmark.ts +19 -0
  174. package/src/bench/client_flows/client_flows_benchmark.ts +64 -49
  175. package/src/bench/client_flows/config.ts +9 -1
  176. package/src/bench/utils.ts +10 -4
  177. package/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts +52 -25
  178. package/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts +56 -19
  179. package/src/e2e_deploy_contract/deploy_test.ts +6 -5
  180. package/src/e2e_epochs/epochs_test.ts +196 -77
  181. package/src/e2e_fees/bridging_race.notest.ts +1 -1
  182. package/src/e2e_fees/fees_test.ts +57 -32
  183. package/src/e2e_l1_publisher/write_json.ts +1 -6
  184. package/src/e2e_nested_contract/nested_contract_test.ts +10 -6
  185. package/src/e2e_p2p/inactivity_slash_test.ts +8 -7
  186. package/src/e2e_p2p/p2p_network.ts +93 -49
  187. package/src/e2e_p2p/reqresp/utils.ts +274 -0
  188. package/src/e2e_p2p/shared.ts +117 -65
  189. package/src/e2e_storage_proof/fixtures/storage_proof.json +915 -0
  190. package/src/e2e_storage_proof/fixtures/storage_proof_fetcher.ts +190 -0
  191. package/src/e2e_storage_proof/fixtures/storage_proof_fixture.ts +173 -0
  192. package/src/e2e_token_contract/token_contract_test.ts +38 -11
  193. package/src/fixtures/authwit_proxy.ts +54 -0
  194. package/src/fixtures/dumps/epoch_proof_result.json +1 -1
  195. package/src/fixtures/e2e_prover_test.ts +50 -57
  196. package/src/fixtures/elu_monitor.ts +126 -0
  197. package/src/fixtures/fixtures.ts +93 -0
  198. package/src/fixtures/get_bb_config.ts +7 -6
  199. package/src/fixtures/ha_setup.ts +188 -0
  200. package/src/fixtures/index.ts +1 -0
  201. package/src/fixtures/schnorr_hardcoded_account_contract.ts +49 -0
  202. package/src/fixtures/setup.ts +282 -243
  203. package/src/fixtures/setup_p2p_test.ts +52 -52
  204. package/src/fixtures/token_utils.ts +3 -3
  205. package/src/fixtures/utils.ts +2 -0
  206. package/src/forward-compatibility/wallet_rpc_client.ts +14 -0
  207. package/src/forward-compatibility/wallet_service.ts +104 -0
  208. package/src/guides/up_quick_start.sh +3 -5
  209. package/src/install_legacy_contracts.cjs +75 -0
  210. package/src/legacy-jest-resolver.cjs +112 -0
  211. package/src/shared/cross_chain_test_harness.ts +27 -13
  212. package/src/shared/gas_portal_test_harness.ts +21 -11
  213. package/src/shared/index.ts +1 -0
  214. package/src/shared/jest_setup.ts +51 -1
  215. package/src/shared/mock_state_view.ts +188 -0
  216. package/src/shared/submit-transactions.ts +3 -2
  217. package/src/shared/uniswap_l1_l2.ts +103 -54
  218. package/src/shared/wait_for_l1_to_l2_message.ts +23 -0
  219. package/src/simulators/lending_simulator.ts +32 -14
  220. package/src/simulators/token_simulator.ts +6 -30
  221. package/src/spartan/setup_test_wallets.ts +146 -35
  222. package/src/spartan/tx_metrics.ts +208 -24
  223. package/src/spartan/utils/bot.ts +4 -1
  224. package/src/spartan/utils/config.ts +3 -0
  225. package/src/spartan/utils/index.ts +10 -1
  226. package/src/spartan/utils/k8s.ts +160 -0
  227. package/src/spartan/utils/nodes.ts +251 -31
  228. package/src/spartan/utils/pod_logs.ts +99 -0
  229. package/src/spartan/utils/scripts.ts +43 -7
  230. package/src/test-wallet/test_wallet.ts +376 -0
  231. package/src/test-wallet/utils.ts +108 -0
  232. package/src/test-wallet/wallet_worker_script.ts +63 -0
  233. package/src/test-wallet/worker_wallet.ts +218 -0
  234. package/src/test-wallet/worker_wallet_schema.ts +13 -0
@@ -1,23 +1,29 @@
1
1
  import { generateSchnorrAccounts } from '@aztec/accounts/testing';
2
+ import { NO_FROM } from '@aztec/aztec.js/account';
2
3
  import { AztecAddress } from '@aztec/aztec.js/addresses';
3
4
  import { NO_WAIT } from '@aztec/aztec.js/contracts';
4
5
  import { L1FeeJuicePortalManager } from '@aztec/aztec.js/ethereum';
5
6
  import { FeeJuicePaymentMethodWithClaim } from '@aztec/aztec.js/fee';
6
7
  import { type FeePaymentMethod, SponsoredFeePaymentMethod } from '@aztec/aztec.js/fee';
7
8
  import { Fr } from '@aztec/aztec.js/fields';
9
+ import { waitForL1ToL2MessageReady } from '@aztec/aztec.js/messaging';
8
10
  import { type AztecNode, createAztecNodeClient, waitForTx } from '@aztec/aztec.js/node';
9
11
  import type { Wallet } from '@aztec/aztec.js/wallet';
10
12
  import { createEthereumChain } from '@aztec/ethereum/chain';
11
13
  import { createExtendedL1Client } from '@aztec/ethereum/client';
12
14
  import type { Logger } from '@aztec/foundation/log';
13
- import { retryUntil } from '@aztec/foundation/retry';
15
+ import { makeBackoff, retry, retryUntil } from '@aztec/foundation/retry';
14
16
  import { TokenContract } from '@aztec/noir-contracts.js/Token';
15
17
  import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
16
- import { TestWallet, proveInteraction, registerInitialLocalNetworkAccountsInWallet } from '@aztec/test-wallet/server';
18
+ import { TxStatus } from '@aztec/stdlib/tx';
19
+ import { registerInitialLocalNetworkAccountsInWallet } from '@aztec/wallets/testing';
17
20
 
18
21
  import { getACVMConfig } from '../fixtures/get_acvm_config.js';
19
22
  import { getBBConfig } from '../fixtures/get_bb_config.js';
20
23
  import { getSponsoredFPCAddress, registerSponsoredFPC } from '../fixtures/utils.js';
24
+ import { TestWallet } from '../test-wallet/test_wallet.js';
25
+ import { proveInteraction } from '../test-wallet/utils.js';
26
+ import { WorkerWallet } from '../test-wallet/worker_wallet.js';
21
27
 
22
28
  export interface TestAccounts {
23
29
  aztecNode: AztecNode;
@@ -85,11 +91,19 @@ export async function deploySponsoredTestAccountsWithTokens(
85
91
 
86
92
  const paymentMethod = new SponsoredFeePaymentMethod(await getSponsoredFPCAddress());
87
93
  const recipientDeployMethod = await recipientAccount.getDeployMethod();
88
- await recipientDeployMethod.send({ from: AztecAddress.ZERO, fee: { paymentMethod }, wait: { timeout: 2400 } });
94
+ await recipientDeployMethod.send({
95
+ from: NO_FROM,
96
+ fee: { paymentMethod },
97
+ wait: { timeout: 2400 },
98
+ });
89
99
  await Promise.all(
90
100
  fundedAccounts.map(async a => {
91
101
  const deployMethod = await a.getDeployMethod();
92
- await deployMethod.send({ from: AztecAddress.ZERO, fee: { paymentMethod }, wait: { timeout: 2400 } }); // increase timeout on purpose in order to account for two empty epochs
102
+ await deployMethod.send({
103
+ from: NO_FROM,
104
+ fee: { paymentMethod },
105
+ wait: { timeout: 2400 },
106
+ }); // increase timeout on purpose in order to account for two empty epochs
93
107
  logger.info(`Account deployed at ${a.address}`);
94
108
  }),
95
109
  );
@@ -118,49 +132,91 @@ export async function deploySponsoredTestAccountsWithTokens(
118
132
  }
119
133
 
120
134
  async function deployAccountWithDiagnostics(
121
- account: { getDeployMethod: () => Promise<{ send: (opts: any) => any }>; address: any },
135
+ account: { getDeployMethod: () => Promise<{ simulate: (opts: any) => any; send: (opts: any) => any }>; address: any },
122
136
  paymentMethod: SponsoredFeePaymentMethod,
123
137
  aztecNode: AztecNode,
124
138
  logger: Logger,
125
139
  accountLabel: string,
140
+ estimateGas?: boolean,
126
141
  ): Promise<void> {
127
142
  const deployMethod = await account.getDeployMethod();
128
- let txHash;
129
- try {
130
- txHash = await deployMethod.send({ from: AztecAddress.ZERO, fee: { paymentMethod }, wait: NO_WAIT });
131
- await waitForTx(aztecNode, txHash, { timeout: 2400 });
132
- logger.info(`${accountLabel} deployed at ${account.address}`);
133
- } catch (error) {
134
- const blockNumber = await aztecNode.getBlockNumber();
135
- let receipt;
136
- try {
137
- receipt = await aztecNode.getTxReceipt(txHash);
138
- } catch {
139
- receipt = 'unavailable';
140
- }
141
- logger.error(`${accountLabel} deployment failed`, {
142
- txHash: txHash.toString(),
143
- receipt: JSON.stringify(receipt),
144
- currentBlockNumber: blockNumber,
145
- error: String(error),
146
- });
147
- throw error;
143
+ let gasSettings: any;
144
+ if (estimateGas) {
145
+ const sim = await deployMethod.simulate({ from: NO_FROM, fee: { paymentMethod } });
146
+ gasSettings = sim.estimatedGas;
147
+ logger.info(`${accountLabel} estimated gas: DA=${gasSettings.gasLimits.daGas} L2=${gasSettings.gasLimits.l2Gas}`);
148
148
  }
149
+
150
+ // Track the tx hash across retries so we don't re-send when the previous tx is still pending.
151
+ let sentTxHash: { txHash: any } | undefined;
152
+
153
+ await retry(
154
+ async () => {
155
+ // Check if already deployed (handles case where previous attempt succeeded but waitForTx timed out)
156
+ const existing = await aztecNode.getContract(account.address);
157
+ if (existing) {
158
+ logger.info(`${accountLabel} already deployed at ${account.address}, skipping`);
159
+ return;
160
+ }
161
+
162
+ // If we already sent a tx, check if it was dropped before deciding to re-send.
163
+ if (sentTxHash) {
164
+ const prevReceipt = await aztecNode.getTxReceipt(sentTxHash.txHash);
165
+ if (prevReceipt.isDropped()) {
166
+ logger.info(`${accountLabel} previous tx ${sentTxHash.txHash} was dropped, re-sending`);
167
+ sentTxHash = undefined;
168
+ } else {
169
+ logger.info(`${accountLabel} previous tx ${sentTxHash.txHash} still pending, waiting again...`);
170
+ }
171
+ }
172
+
173
+ if (!sentTxHash) {
174
+ const deployResult = await deployMethod.send({
175
+ from: NO_FROM,
176
+ fee: { paymentMethod, gasSettings },
177
+ wait: NO_WAIT,
178
+ });
179
+ sentTxHash = { txHash: deployResult.txHash };
180
+ logger.info(`${accountLabel} tx sent`, { txHash: sentTxHash.txHash.toString() });
181
+ }
182
+
183
+ const receipt = await waitForTx(aztecNode, sentTxHash.txHash, { timeout: 600 });
184
+ if (receipt.isDropped()) {
185
+ sentTxHash = undefined;
186
+ throw new Error(`${accountLabel} tx was dropped, retrying...`);
187
+ }
188
+ logger.info(`${accountLabel} deployed at ${account.address}`);
189
+ },
190
+ `deploy ${accountLabel}`,
191
+ makeBackoff([1, 2, 4, 8, 16]),
192
+ logger,
193
+ );
149
194
  }
150
195
 
151
196
  async function deployAccountsInBatches(
152
- accounts: { getDeployMethod: () => Promise<{ send: (opts: any) => any }>; address: any }[],
197
+ accounts: {
198
+ getDeployMethod: () => Promise<{ simulate: (opts: any) => any; send: (opts: any) => any }>;
199
+ address: any;
200
+ }[],
153
201
  paymentMethod: SponsoredFeePaymentMethod,
154
202
  aztecNode: AztecNode,
155
203
  logger: Logger,
156
204
  labelPrefix: string,
157
205
  batchSize = 2,
206
+ estimateGas?: boolean,
158
207
  ): Promise<void> {
159
208
  for (let i = 0; i < accounts.length; i += batchSize) {
160
209
  const batch = accounts.slice(i, i + batchSize);
161
210
  await Promise.all(
162
211
  batch.map((account, idx) =>
163
- deployAccountWithDiagnostics(account, paymentMethod, aztecNode, logger, `${labelPrefix}${i + idx + 1}`),
212
+ deployAccountWithDiagnostics(
213
+ account,
214
+ paymentMethod,
215
+ aztecNode,
216
+ logger,
217
+ `${labelPrefix}${i + idx + 1}`,
218
+ estimateGas,
219
+ ),
164
220
  ),
165
221
  );
166
222
  }
@@ -171,6 +227,7 @@ export async function deploySponsoredTestAccounts(
171
227
  aztecNode: AztecNode,
172
228
  logger: Logger,
173
229
  numberOfFundedWallets = 1,
230
+ opts?: { estimateGas?: boolean },
174
231
  ): Promise<TestAccountsWithoutTokens> {
175
232
  const [recipient, ...funded] = await generateSchnorrAccounts(numberOfFundedWallets + 1);
176
233
  const recipientAccount = await wallet.createSchnorrAccount(recipient.secret, recipient.salt);
@@ -180,8 +237,23 @@ export async function deploySponsoredTestAccounts(
180
237
 
181
238
  const paymentMethod = new SponsoredFeePaymentMethod(await getSponsoredFPCAddress());
182
239
 
183
- await deployAccountWithDiagnostics(recipientAccount, paymentMethod, aztecNode, logger, 'Recipient account');
184
- await deployAccountsInBatches(fundedAccounts, paymentMethod, aztecNode, logger, 'Funded account ', 2);
240
+ await deployAccountWithDiagnostics(
241
+ recipientAccount,
242
+ paymentMethod,
243
+ aztecNode,
244
+ logger,
245
+ 'Recipient account',
246
+ opts?.estimateGas,
247
+ );
248
+ await deployAccountsInBatches(
249
+ fundedAccounts,
250
+ paymentMethod,
251
+ aztecNode,
252
+ logger,
253
+ 'Funded account ',
254
+ 2,
255
+ opts?.estimateGas,
256
+ );
185
257
 
186
258
  return {
187
259
  aztecNode,
@@ -219,7 +291,7 @@ export async function deployTestAccountsWithTokens(
219
291
  fundedAccounts.map(async (a, i) => {
220
292
  const paymentMethod = new FeeJuicePaymentMethodWithClaim(a.address, claims[i]);
221
293
  const deployMethod = await a.getDeployMethod();
222
- await deployMethod.send({ from: AztecAddress.ZERO, fee: { paymentMethod } });
294
+ await deployMethod.send({ from: NO_FROM, fee: { paymentMethod } });
223
295
  logger.info(`Account deployed at ${a.address}`);
224
296
  }),
225
297
  );
@@ -262,9 +334,7 @@ async function bridgeL1FeeJuice(
262
334
  const portal = await L1FeeJuicePortalManager.new(aztecNode, l1Client, log);
263
335
  const claim = await portal.bridgeTokensPublic(recipient, amount, true /* mint */);
264
336
 
265
- const isSynced = async () =>
266
- (await aztecNode.getL1ToL2MessageBlock(Fr.fromHexString(claim.messageHash))) !== undefined;
267
- await retryUntil(isSynced, `message ${claim.messageHash} sync`, 24, 0.5);
337
+ await waitForL1ToL2MessageReady(aztecNode, Fr.fromHexString(claim.messageHash), { timeoutSeconds: 24 });
268
338
 
269
339
  log.info(`Created a claim for ${amount} L1 fee juice to ${recipient}.`, claim);
270
340
  return claim;
@@ -306,7 +376,7 @@ async function deployTokenAndMint(
306
376
  fee: {
307
377
  paymentMethod,
308
378
  },
309
- wait: { timeout: 600, returnReceipt: true },
379
+ wait: { timeout: 600 },
310
380
  });
311
381
 
312
382
  const tokenAddress = tokenContract.address;
@@ -357,7 +427,9 @@ export async function performTransfers({
357
427
 
358
428
  const provenTxs = await Promise.all(txs);
359
429
 
360
- await Promise.all(provenTxs.map(t => t.send({ wait: { timeout: 600 } })));
430
+ // Wait only for the txs to be proposed, not checkpointed. This is enough to keep the chain
431
+ // loaded for the reorg scenario, and avoids each round blocking on the (slower) checkpoint lag.
432
+ await Promise.all(provenTxs.map(t => t.send({ wait: { timeout: 600, waitForStatus: TxStatus.PROPOSED } })));
361
433
 
362
434
  logger.info(`Completed round ${i + 1} / ${rounds}`);
363
435
  }
@@ -395,3 +467,42 @@ export async function createWalletAndAztecNodeClient(
395
467
  },
396
468
  };
397
469
  }
470
+
471
+ export type WorkerWalletWrapper = {
472
+ wallet: WorkerWallet;
473
+ aztecNode: AztecNode;
474
+ cleanup: () => Promise<void>;
475
+ };
476
+
477
+ export async function createWorkerWalletClient(
478
+ nodeUrl: string,
479
+ proverEnabled: boolean,
480
+ logger: Logger,
481
+ ): Promise<WorkerWalletWrapper> {
482
+ const aztecNode = createAztecNodeClient(nodeUrl);
483
+ const [bbConfig, acvmConfig] = await Promise.all([getBBConfig(logger), getACVMConfig(logger)]);
484
+
485
+ // Strip cleanup functions — they can't be structured-cloned for worker transfer
486
+ const { cleanup: bbCleanup, ...bbPaths } = bbConfig ?? {};
487
+ const { cleanup: acvmCleanup, ...acvmPaths } = acvmConfig ?? {};
488
+
489
+ const pxeConfig = {
490
+ dataDirectory: undefined,
491
+ dataStoreMapSizeKb: 1024 * 1024,
492
+ ...bbPaths,
493
+ ...acvmPaths,
494
+ proverEnabled,
495
+ };
496
+
497
+ const wallet = await WorkerWallet.create(nodeUrl, pxeConfig);
498
+
499
+ return {
500
+ wallet,
501
+ aztecNode,
502
+ async cleanup() {
503
+ await wallet.stop();
504
+ await bbCleanup?.();
505
+ await acvmCleanup?.();
506
+ },
507
+ };
508
+ }
@@ -1,16 +1,145 @@
1
1
  import type { AztecNode } from '@aztec/aztec.js/node';
2
2
  import type { Logger } from '@aztec/foundation/log';
3
- import type { L2Block } from '@aztec/stdlib/block';
3
+ import type { BlockResponse } from '@aztec/stdlib/interfaces/client';
4
4
  import type { TopicType } from '@aztec/stdlib/p2p';
5
5
  import { Tx, type TxReceipt } from '@aztec/stdlib/tx';
6
6
 
7
7
  import { createHistogram } from 'perf_hooks';
8
8
 
9
+ /** Metrics class for proving-related benchmarks. */
10
+ export class ProvingMetrics {
11
+ private successfulTxs: number | undefined;
12
+ private proofDuration: number | undefined;
13
+ private activeAgents: number | undefined;
14
+ private avgQueueTime: number | undefined;
15
+ private jobRetries: number | undefined;
16
+ private jobDuration: number | undefined;
17
+ private timedOutJobs: number | undefined;
18
+ private resolvedJobs: number | undefined;
19
+ private rejectedJobs: number | undefined;
20
+ private epochProvingDuration: number | undefined;
21
+ private provenTransactions: number | undefined;
22
+ private provenBlocks: number | undefined;
23
+
24
+ constructor(private prefix: string) {}
25
+
26
+ recordSuccessfulTxs(count: number): void {
27
+ this.successfulTxs = count;
28
+ }
29
+
30
+ recordProofDuration(seconds: number): void {
31
+ this.proofDuration = seconds;
32
+ }
33
+
34
+ recordActiveAgents(count: number): void {
35
+ this.activeAgents = count;
36
+ }
37
+
38
+ recordAvgQueueTime(ms: number): void {
39
+ this.avgQueueTime = ms;
40
+ }
41
+
42
+ recordJobRetries(count: number): void {
43
+ this.jobRetries = count;
44
+ }
45
+
46
+ recordJobDuration(ms: number): void {
47
+ this.jobDuration = ms;
48
+ }
49
+
50
+ recordTimedOutJobs(count: number): void {
51
+ this.timedOutJobs = count;
52
+ }
53
+
54
+ recordResolvedJobs(count: number): void {
55
+ this.resolvedJobs = count;
56
+ }
57
+
58
+ recordRejectedJobs(count: number): void {
59
+ this.rejectedJobs = count;
60
+ }
61
+
62
+ recordEpochProvingDuration(seconds: number): void {
63
+ this.epochProvingDuration = seconds;
64
+ }
65
+
66
+ recordProvenTransactions(count: number): void {
67
+ this.provenTransactions = count;
68
+ }
69
+
70
+ recordProvenBlocks(count: number): void {
71
+ this.provenBlocks = count;
72
+ }
73
+
74
+ toGithubActionBenchmarkJSON(): Array<{ name: string; unit: string; value: number }> {
75
+ const data: Array<{ name: string; unit: string; value: number }> = [];
76
+
77
+ if (this.successfulTxs !== undefined) {
78
+ data.push({ name: `${this.prefix}/successful_txs`, unit: 'count', value: this.successfulTxs });
79
+ }
80
+
81
+ if (this.proofDuration !== undefined) {
82
+ data.push({ name: `${this.prefix}/proof_duration`, unit: 's', value: this.proofDuration });
83
+ }
84
+
85
+ if (this.activeAgents !== undefined) {
86
+ data.push({ name: `${this.prefix}/active_agents`, unit: 'count', value: this.activeAgents });
87
+ }
88
+
89
+ if (this.avgQueueTime !== undefined) {
90
+ data.push({ name: `${this.prefix}/avg_queue_time`, unit: 'ms', value: this.avgQueueTime });
91
+ }
92
+
93
+ if (this.jobRetries !== undefined) {
94
+ data.push({ name: `${this.prefix}/job_retries`, unit: 'count', value: this.jobRetries });
95
+ }
96
+
97
+ if (this.jobDuration !== undefined) {
98
+ data.push({ name: `${this.prefix}/job_duration`, unit: 'ms', value: this.jobDuration });
99
+ }
100
+
101
+ if (this.timedOutJobs !== undefined) {
102
+ data.push({ name: `${this.prefix}/timed_out_jobs`, unit: 'count', value: this.timedOutJobs });
103
+ }
104
+
105
+ if (this.resolvedJobs !== undefined) {
106
+ data.push({ name: `${this.prefix}/resolved_jobs`, unit: 'count', value: this.resolvedJobs });
107
+ }
108
+
109
+ if (this.rejectedJobs !== undefined) {
110
+ data.push({ name: `${this.prefix}/rejected_jobs`, unit: 'count', value: this.rejectedJobs });
111
+ }
112
+
113
+ if (this.epochProvingDuration !== undefined) {
114
+ data.push({ name: `${this.prefix}/epoch_proving_duration`, unit: 's', value: this.epochProvingDuration });
115
+ }
116
+
117
+ if (this.provenTransactions !== undefined) {
118
+ data.push({ name: `${this.prefix}/proven_transactions`, unit: 'count', value: this.provenTransactions });
119
+ }
120
+
121
+ if (this.provenBlocks !== undefined) {
122
+ data.push({ name: `${this.prefix}/proven_blocks`, unit: 'count', value: this.provenBlocks });
123
+ }
124
+
125
+ const scenario = process.env.BENCH_SCENARIO?.trim();
126
+ if (!scenario) {
127
+ return data;
128
+ }
129
+
130
+ const scenarioPrefix = `scenario/${scenario}/`;
131
+ return data.map(entry => ({ ...entry, name: `${scenarioPrefix}${entry.name}` }));
132
+ }
133
+ }
134
+
9
135
  export type TxInclusionData = {
10
136
  txHash: string;
11
- sentAt: number;
12
- minedAt: number;
13
- attestedAt: number;
137
+ /** Wall-clock at client when the tx was submitted, in ms (Date.now()). */
138
+ sentAtMs: number;
139
+ /** Wall-clock at client when the block containing the tx first became visible, in ms (Date.now()). -1 if never observed. */
140
+ minedAtMs: number;
141
+ /** Reserved for future attestation-observed-at signal; -1 today. */
142
+ attestedAtMs: number;
14
143
  blocknumber: number;
15
144
  priorityFee: number;
16
145
  totalFee: number;
@@ -21,7 +150,7 @@ export type TxInclusionData = {
21
150
  export class TxInclusionMetrics {
22
151
  private data = new Map<string, TxInclusionData>();
23
152
  private groups = new Set<string>();
24
- private blocks = new Map<number, Promise<L2Block | undefined>>();
153
+ private blocks = new Map<number, Promise<BlockResponse<{ includeTransactions: true }> | undefined>>();
25
154
 
26
155
  private p2pGossipLatencyByTopic: Partial<Record<TopicType, { p50: number; p95: number }>> = {};
27
156
 
@@ -48,9 +177,9 @@ export class TxInclusionMetrics {
48
177
 
49
178
  this.data.set(txHash, {
50
179
  txHash,
51
- sentAt: Math.trunc(Date.now() / 1000),
52
- minedAt: -1,
53
- attestedAt: -1,
180
+ sentAtMs: Date.now(),
181
+ minedAtMs: -1,
182
+ attestedAtMs: -1,
54
183
  blocknumber: -1,
55
184
  priorityFee: Number(priorityFees.feePerDaGas + priorityFees.feePerL2Gas),
56
185
  totalFee: -1,
@@ -60,6 +189,28 @@ export class TxInclusionMetrics {
60
189
  this.groups.add(group);
61
190
  }
62
191
 
192
+ /**
193
+ * Stamp mined-at metadata for any tracked tx contained in this block, using
194
+ * `observedAtMs` (caller-supplied wall-clock at the moment they first saw the
195
+ * block). Idempotent: existing minedAtMs is preserved so the first observer
196
+ * wins (typically the block-watcher; recordMinedTx is a fallback).
197
+ */
198
+ observeBlockForMinedTxs(
199
+ blockNumber: number,
200
+ txHashes: ReadonlyArray<{ toString(): string }>,
201
+ observedAtMs: number,
202
+ ): void {
203
+ txHashes.forEach((txHash, position) => {
204
+ const data = this.data.get(txHash.toString());
205
+ if (!data || data.minedAtMs !== -1) {
206
+ return;
207
+ }
208
+ data.blocknumber = blockNumber;
209
+ data.minedAtMs = observedAtMs;
210
+ data.positionInBlock = position;
211
+ });
212
+ }
213
+
63
214
  async recordMinedTx(txReceipt: TxReceipt): Promise<void> {
64
215
  const { txHash, blockNumber } = txReceipt;
65
216
  if (!txReceipt.isMined() || !txReceipt.hasExecutionSucceeded() || !blockNumber) {
@@ -71,26 +222,43 @@ export class TxInclusionMetrics {
71
222
  return;
72
223
  }
73
224
 
74
- if (!this.blocks.has(blockNumber)) {
75
- this.blocks.set(blockNumber, this.aztecNode.getBlock(blockNumber));
76
- }
77
-
78
- const block = await this.blocks.get(blockNumber)!;
79
- if (!block) {
80
- this.logger?.warn('Failed to load block for mined tx receipt', { txHash: txHash.toString(), blockNumber });
81
- return;
82
- }
83
225
  const data = this.data.get(txHash.toString());
84
226
  if (!data) {
85
227
  const message = `Missing sent tx record for mined tx ${txHash.toString()}`;
86
228
  this.logger?.warn(message, { txHash: txHash.toString(), blockNumber });
87
229
  throw new Error(message);
88
230
  }
89
- data.blocknumber = blockNumber;
90
- data.minedAt = Number(block.header.globalVariables.timestamp);
91
- data.attestedAt = -1;
92
231
  data.totalFee = Number(txReceipt.transactionFee ?? 0n);
93
- data.positionInBlock = block.body.txEffects.findIndex(txEffect => txEffect.txHash.equals(txHash));
232
+
233
+ // Fallback path for txs the block-watcher missed (e.g. observed only after
234
+ // the watcher stopped). Stamp with the block's L2 slot timestamp; this is
235
+ // earlier than the true client-observed time by attestation+propagation
236
+ // lag, but it's the only deterministic timestamp available post-hoc.
237
+ if (data.minedAtMs === -1) {
238
+ if (!this.blocks.has(blockNumber)) {
239
+ this.blocks.set(blockNumber, this.aztecNode.getBlock(blockNumber, { includeTransactions: true }));
240
+ }
241
+ const block = await this.blocks.get(blockNumber)!;
242
+ if (!block) {
243
+ this.logger?.warn('Failed to load block for mined tx receipt', { txHash: txHash.toString(), blockNumber });
244
+ return;
245
+ }
246
+ data.blocknumber = blockNumber;
247
+ data.minedAtMs = Number(block.header.globalVariables.timestamp) * 1000;
248
+ data.positionInBlock = block.body.txEffects.findIndex(txEffect => txEffect.txHash.equals(txHash));
249
+ }
250
+ }
251
+
252
+ /** Per-tx inclusion records for a group. Used to serialise out for downstream tooling. */
253
+ getInclusionRecords(group?: string): TxInclusionData[] {
254
+ const out: TxInclusionData[] = [];
255
+ for (const tx of this.data.values()) {
256
+ if (group !== undefined && tx.group !== group) {
257
+ continue;
258
+ }
259
+ out.push({ ...tx });
260
+ }
261
+ return out;
94
262
  }
95
263
 
96
264
  public inclusionTimeInSeconds(group: string): {
@@ -103,12 +271,28 @@ export class TxInclusionMetrics {
103
271
  p99: number;
104
272
  } {
105
273
  const histogram = createHistogram({});
274
+ let nonPositive = 0;
106
275
  for (const tx of this.data.values()) {
107
- if (!tx.blocknumber || tx.group !== group || tx.minedAt === -1) {
276
+ if (!tx.blocknumber || tx.group !== group || tx.minedAtMs === -1) {
108
277
  continue;
109
278
  }
110
279
 
111
- histogram.record(tx.minedAt - tx.sentAt);
280
+ // Both timestamps are client wall-clock (ms). A negative delta should be
281
+ // impossible since the watcher stamps minedAtMs strictly after sentAtMs,
282
+ // but the fallback path (recordMinedTx via L2 slot timestamp) can stamp
283
+ // earlier than sentAtMs. perf_hooks.createHistogram rejects <=0; skip
284
+ // those instead of crashing.
285
+ const deltaMs = tx.minedAtMs - tx.sentAtMs;
286
+ if (deltaMs <= 0) {
287
+ nonPositive++;
288
+ continue;
289
+ }
290
+ // Histogram is recorded in seconds (rounded) to match the existing
291
+ // toGithubActionBenchmarkJSON output unit; per-tx records carry the raw ms.
292
+ histogram.record(Math.max(1, Math.round(deltaMs / 1000)));
293
+ }
294
+ if (nonPositive > 0) {
295
+ this.logger?.debug(`Dropped ${nonPositive} tx inclusion samples with non-positive delta`, { group });
112
296
  }
113
297
 
114
298
  if (histogram.count === 0) {
@@ -170,7 +354,7 @@ export class TxInclusionMetrics {
170
354
  value: stats.mean,
171
355
  },
172
356
  {
173
- name: `${group}/median_inclusion`,
357
+ name: `${group}/p50_inclusion`,
174
358
  unit: 's',
175
359
  value: stats.median,
176
360
  },
@@ -34,7 +34,8 @@ export async function installTransferBot({
34
34
  logger: log,
35
35
  replicas = 1,
36
36
  txIntervalSeconds = 10,
37
- followChain = 'PENDING',
37
+ followChain = 'CHECKPOINTED',
38
+ pxeSyncChainTip = 'proposed',
38
39
  mnemonic = process.env.LABS_INFRA_MNEMONIC ?? 'test test test test test test test test test test test junk',
39
40
  mnemonicStartIndex,
40
41
  botPrivateKey = process.env.BOT_TRANSFERS_L2_PRIVATE_KEY ?? '0xcafe01',
@@ -49,6 +50,7 @@ export async function installTransferBot({
49
50
  replicas?: number;
50
51
  txIntervalSeconds?: number;
51
52
  followChain?: string;
53
+ pxeSyncChainTip?: string;
52
54
  mnemonic?: string;
53
55
  mnemonicStartIndex?: number | string;
54
56
  botPrivateKey?: string;
@@ -67,6 +69,7 @@ export async function installTransferBot({
67
69
  'bot.replicaCount': replicas,
68
70
  'bot.txIntervalSeconds': txIntervalSeconds,
69
71
  'bot.followChain': followChain,
72
+ 'bot.pxeSyncChainTip': pxeSyncChainTip,
70
73
  'bot.botPrivateKey': botPrivateKey,
71
74
  'bot.nodeUrl': resolvedNodeUrl,
72
75
  'bot.mnemonic': mnemonic,
@@ -8,6 +8,7 @@ const logger = createLogger('e2e:k8s-utils');
8
8
  const testConfigSchema = z.object({
9
9
  NAMESPACE: z.string().default('scenario'),
10
10
  REAL_VERIFIER: schemas.Boolean.optional().default(true),
11
+ DEBUG_FORCE_TX_PROOF_VERIFICATION: schemas.Boolean.optional().default(true),
11
12
  CREATE_ETH_DEVNET: schemas.Boolean.optional().default(false),
12
13
  L1_RPC_URLS_JSON: z.string().optional(),
13
14
  L1_ACCOUNT_MNEMONIC: z.string().optional(),
@@ -15,6 +16,8 @@ const testConfigSchema = z.object({
15
16
  AZTEC_EPOCH_DURATION: z.coerce.number().optional().default(32),
16
17
  AZTEC_PROOF_SUBMISSION_WINDOW: z.coerce.number().optional().default(5),
17
18
  AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET: z.coerce.number().optional().default(2),
19
+ FUNDING_PRIVATE_KEY: z.string().optional(),
20
+ AZTEC_ADMIN_API_KEY: z.string().optional(),
18
21
  });
19
22
 
20
23
  export type TestConfig = z.infer<typeof testConfigSchema>;
@@ -5,7 +5,7 @@
5
5
  export { type TestConfig, setupEnvironment } from './config.js';
6
6
 
7
7
  // Scripts
8
- export { getGitProjectRoot, getAztecBin, runAztecBin, runProjectScript } from './scripts.js';
8
+ export { type ScriptResult, getGitProjectRoot, getAztecBin, runAztecBin, runProjectScript } from './scripts.js';
9
9
 
10
10
  // K8s operations
11
11
  export {
@@ -24,6 +24,8 @@ export {
24
24
  getServiceEndpoint,
25
25
  getRPCEndpoint,
26
26
  getEthereumEndpoint,
27
+ createResilientPrometheusConnection,
28
+ scaleProverAgents,
27
29
  } from './k8s.js';
28
30
 
29
31
  // Chaos Mesh
@@ -39,12 +41,16 @@ export {
39
41
  applyNetworkShaping,
40
42
  } from './chaos.js';
41
43
 
44
+ // Helm
45
+ export { hasDeployedHelmRelease } from './helm.js';
46
+
42
47
  // Bot management
43
48
  export { restartBot, installTransferBot, uninstallTransferBot } from './bot.js';
44
49
 
45
50
  // Node operations (sequencers, validators, pods)
46
51
  export {
47
52
  awaitCheckpointNumber,
53
+ waitForProvenToAdvance,
48
54
  getSequencers,
49
55
  updateSequencersConfig,
50
56
  getSequencersConfig,
@@ -60,3 +66,6 @@ export { getPublicViemClient, getL1DeploymentAddresses, getNodeClient } from './
60
66
 
61
67
  // Health checks
62
68
  export { ChainHealth, type ChainHealthSnapshot } from './health.js';
69
+
70
+ // Pod log extraction
71
+ export { type BlockBuiltLogEntry, fetchBlockBuiltLogs } from './pod_logs.js';