@aztec/ethereum 0.0.1-commit.3fd054f6 → 0.0.1-commit.431c48d
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/dest/client.d.ts +19 -3
- package/dest/client.d.ts.map +1 -1
- package/dest/client.js +52 -3
- package/dest/config.d.ts +15 -6
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +29 -9
- package/dest/contracts/chain_state_override.d.ts +78 -0
- package/dest/contracts/chain_state_override.d.ts.map +1 -0
- package/dest/contracts/chain_state_override.js +137 -0
- package/dest/contracts/empire_base.d.ts +1 -3
- package/dest/contracts/empire_base.d.ts.map +1 -1
- package/dest/contracts/fee_asset_price_oracle.d.ts +6 -2
- package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -1
- package/dest/contracts/fee_asset_price_oracle.js +11 -6
- package/dest/contracts/governance.d.ts +135 -35
- package/dest/contracts/governance.d.ts.map +1 -1
- package/dest/contracts/governance.js +226 -15
- package/dest/contracts/governance_proposer.d.ts +33 -4
- package/dest/contracts/governance_proposer.d.ts.map +1 -1
- package/dest/contracts/governance_proposer.js +52 -13
- package/dest/contracts/gse.d.ts +4 -2
- package/dest/contracts/gse.d.ts.map +1 -1
- package/dest/contracts/gse.js +2 -2
- package/dest/contracts/inbox.d.ts +3 -3
- package/dest/contracts/inbox.d.ts.map +1 -1
- package/dest/contracts/inbox.js +12 -7
- package/dest/contracts/index.d.ts +3 -3
- package/dest/contracts/index.d.ts.map +1 -1
- package/dest/contracts/index.js +2 -2
- package/dest/contracts/multicall.d.ts +79 -13
- package/dest/contracts/multicall.d.ts.map +1 -1
- package/dest/contracts/multicall.js +117 -95
- package/dest/contracts/outbox.d.ts +27 -4
- package/dest/contracts/outbox.d.ts.map +1 -1
- package/dest/contracts/outbox.js +34 -7
- package/dest/contracts/rollup.d.ts +5418 -28
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +389 -95
- package/dest/contracts/slashing_proposer.d.ts +171 -0
- package/dest/contracts/slashing_proposer.d.ts.map +1 -0
- package/dest/contracts/slashing_proposer.js +753 -0
- package/dest/contracts/watch_event.d.ts +36 -0
- package/dest/contracts/watch_event.d.ts.map +1 -0
- package/dest/contracts/watch_event.js +66 -0
- package/dest/deploy_aztec_l1_contracts.d.ts +4 -7
- package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
- package/dest/deploy_aztec_l1_contracts.js +15 -8
- package/dest/foundry_binary.d.ts +13 -0
- package/dest/foundry_binary.d.ts.map +1 -0
- package/dest/foundry_binary.js +52 -0
- package/dest/generated/l1-contracts-defaults.d.ts +2 -2
- package/dest/generated/l1-contracts-defaults.js +2 -2
- package/dest/l1_artifacts.d.ts +14941 -16273
- package/dest/l1_artifacts.d.ts.map +1 -1
- package/dest/l1_artifacts.js +9 -24
- package/dest/l1_contract_addresses.d.ts +61 -65
- package/dest/l1_contract_addresses.d.ts.map +1 -1
- package/dest/l1_contract_addresses.js +101 -82
- package/dest/l1_reader.d.ts +3 -5
- package/dest/l1_reader.d.ts.map +1 -1
- package/dest/l1_reader.js +3 -6
- package/dest/l1_tx_utils/config.js +3 -3
- package/dest/l1_tx_utils/l1_tx_utils.d.ts +9 -1
- package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/l1_tx_utils.js +115 -54
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +1 -1
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/readonly_l1_tx_utils.js +49 -19
- package/dest/l1_tx_utils/tx_delayer.d.ts +5 -3
- package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -1
- package/dest/l1_tx_utils/tx_delayer.js +7 -5
- package/dest/publisher_manager.d.ts +18 -6
- package/dest/publisher_manager.d.ts.map +1 -1
- package/dest/publisher_manager.js +44 -7
- package/dest/queries.d.ts +13 -2
- package/dest/queries.d.ts.map +1 -1
- package/dest/queries.js +62 -3
- package/dest/test/blob_kzg_warmup.d.ts +19 -0
- package/dest/test/blob_kzg_warmup.d.ts.map +1 -0
- package/dest/test/blob_kzg_warmup.js +64 -0
- package/dest/test/chain_monitor.d.ts +34 -2
- package/dest/test/chain_monitor.d.ts.map +1 -1
- package/dest/test/chain_monitor.js +76 -6
- package/dest/test/eth_cheat_codes.d.ts +26 -3
- package/dest/test/eth_cheat_codes.d.ts.map +1 -1
- package/dest/test/eth_cheat_codes.js +61 -40
- package/dest/test/index.d.ts +2 -1
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/index.js +1 -0
- package/dest/test/recording_rpc_server.d.ts +18 -0
- package/dest/test/recording_rpc_server.d.ts.map +1 -0
- package/dest/test/recording_rpc_server.js +68 -0
- package/dest/test/rollup_cheat_codes.d.ts +60 -3
- package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
- package/dest/test/rollup_cheat_codes.js +86 -7
- package/dest/test/start_anvil.d.ts +1 -1
- package/dest/test/start_anvil.d.ts.map +1 -1
- package/dest/test/start_anvil.js +41 -5
- package/dest/utils.d.ts +9 -2
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +45 -14
- package/package.json +6 -6
- package/src/client.ts +63 -1
- package/src/config.ts +44 -13
- package/src/contracts/chain_state_override.ts +200 -0
- package/src/contracts/empire_base.ts +0 -2
- package/src/contracts/fee_asset_price_oracle.ts +10 -5
- package/src/contracts/governance.ts +310 -10
- package/src/contracts/governance_proposer.ts +56 -13
- package/src/contracts/gse.ts +7 -2
- package/src/contracts/inbox.ts +13 -5
- package/src/contracts/index.ts +2 -2
- package/src/contracts/multicall.ts +179 -105
- package/src/contracts/outbox.ts +42 -8
- package/src/contracts/rollup.ts +429 -86
- package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +119 -45
- package/src/contracts/watch_event.ts +110 -0
- package/src/deploy_aztec_l1_contracts.ts +19 -10
- package/src/foundry_binary.ts +57 -0
- package/src/generated/l1-contracts-defaults.ts +2 -2
- package/src/l1_artifacts.ts +12 -35
- package/src/l1_contract_addresses.ts +120 -94
- package/src/l1_reader.ts +4 -9
- package/src/l1_tx_utils/config.ts +3 -3
- package/src/l1_tx_utils/l1_tx_utils.ts +107 -50
- package/src/l1_tx_utils/readonly_l1_tx_utils.ts +49 -13
- package/src/l1_tx_utils/tx_delayer.ts +16 -7
- package/src/publisher_manager.ts +54 -18
- package/src/queries.ts +70 -4
- package/src/test/blob_kzg_warmup.ts +65 -0
- package/src/test/chain_monitor.ts +93 -5
- package/src/test/eth_cheat_codes.ts +57 -43
- package/src/test/index.ts +1 -0
- package/src/test/recording_rpc_server.ts +69 -0
- package/src/test/rollup_cheat_codes.ts +115 -6
- package/src/test/start_anvil.ts +38 -6
- package/src/utils.ts +41 -17
- package/dest/contracts/empire_slashing_proposer.d.ts +0 -69
- package/dest/contracts/empire_slashing_proposer.d.ts.map +0 -1
- package/dest/contracts/empire_slashing_proposer.js +0 -216
- package/dest/contracts/tally_slashing_proposer.d.ts +0 -140
- package/dest/contracts/tally_slashing_proposer.d.ts.map +0 -1
- package/dest/contracts/tally_slashing_proposer.js +0 -320
- package/src/contracts/empire_slashing_proposer.ts +0 -265
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readonly_l1_tx_utils.d.ts","sourceRoot":"","sources":["../../src/l1_tx_utils/readonly_l1_tx_utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAElE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"readonly_l1_tx_utils.d.ts","sourceRoot":"","sources":["../../src/l1_tx_utils/readonly_l1_tx_utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,15 +1,33 @@
|
|
|
1
1
|
import { getKeys, merge, pick, times } from '@aztec/foundation/collection';
|
|
2
2
|
import { createLogger } from '@aztec/foundation/log';
|
|
3
3
|
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
4
|
+
import { getErrorCause } from '@aztec/foundation/types';
|
|
4
5
|
import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
|
|
5
6
|
import pickBy from 'lodash.pickby';
|
|
6
|
-
import { MethodNotFoundRpcError, MethodNotSupportedRpcError, decodeErrorResult, formatGwei, getContractError, hexToBytes } from 'viem';
|
|
7
|
+
import { MethodNotFoundRpcError, MethodNotSupportedRpcError, RpcRequestError, decodeErrorResult, formatGwei, getContractError, hexToBytes } from 'viem';
|
|
8
|
+
import { getL1RpcErrorCode } from '../client.js';
|
|
7
9
|
import { defaultL1TxUtilsConfig, l1TxUtilsConfigMappings } from './config.js';
|
|
8
10
|
import { BLOCK_TIME_MS, MAX_L1_TX_LIMIT, MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE, MIN_REPLACEMENT_BUMP_PERCENTAGE, WEI_CONST } from './constants.js';
|
|
9
11
|
import { P75AllTxsPriorityFeeStrategy } from './fee-strategies/index.js';
|
|
10
12
|
import { getCalldataGasUsage, tryGetCustomErrorNameContractFunction } from './utils.js';
|
|
11
13
|
// Change this to the current strategy we want to use
|
|
12
14
|
const CurrentStrategy = P75AllTxsPriorityFeeStrategy;
|
|
15
|
+
/** Error code returned by eth_simulateV1 for `insufficient funds for gas * price + value`. */ const INSUFFICIENT_FUNDS_RPC_ERROR_CODE = -38014;
|
|
16
|
+
/**
|
|
17
|
+
* Returns true when an RPC transport error reports the node's upfront funds check
|
|
18
|
+
* (sender balance >= gasLimit * maxFeePerGas) rejecting the request. Matches on the message as well as on the
|
|
19
|
+
* error code, since RPC gateways differ in how they wrap node errors. Apply this only to transport errors: an
|
|
20
|
+
* execution revert string can mention insufficient funds without being this kind of rejection.
|
|
21
|
+
*/ function isInsufficientFundsRpcError(err) {
|
|
22
|
+
if (getL1RpcErrorCode(err) === INSUFFICIENT_FUNDS_RPC_ERROR_CODE) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
const messages = [
|
|
26
|
+
err,
|
|
27
|
+
getErrorCause(err, RpcRequestError)
|
|
28
|
+
].map((e)=>e instanceof Error ? e.message : '').join(' ');
|
|
29
|
+
return /insufficient funds/i.test(messages);
|
|
30
|
+
}
|
|
13
31
|
export class ReadOnlyL1TxUtils {
|
|
14
32
|
client;
|
|
15
33
|
logger;
|
|
@@ -268,8 +286,7 @@ export class ReadOnlyL1TxUtils {
|
|
|
268
286
|
return await this._simulate(call, blockOverrides, stateOverrides, gasConfig, abi);
|
|
269
287
|
}
|
|
270
288
|
async _simulate(call, blockOverrides = {}, stateOverrides = [], gasConfig, abi) {
|
|
271
|
-
|
|
272
|
-
const result = await this.client.simulateBlocks({
|
|
289
|
+
const simulateBlocks = ()=>this.client.simulateBlocks({
|
|
273
290
|
validation: false,
|
|
274
291
|
blocks: [
|
|
275
292
|
{
|
|
@@ -281,23 +298,13 @@ export class ReadOnlyL1TxUtils {
|
|
|
281
298
|
}
|
|
282
299
|
]
|
|
283
300
|
});
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
});
|
|
290
|
-
throw new Error(`L1 transaction simulation failed with error ${decodedError.errorName}(${decodedError.args?.join(',')})`);
|
|
291
|
-
}
|
|
292
|
-
this.logger?.debug(`L1 transaction simulation succeeded`, {
|
|
293
|
-
...result[0].calls[0]
|
|
294
|
-
});
|
|
295
|
-
return {
|
|
296
|
-
gasUsed: result[0].gasUsed,
|
|
297
|
-
result: result[0].calls[0].data
|
|
298
|
-
};
|
|
301
|
+
// Only the request itself is wrapped, so that transport-level rejections are never confused with an
|
|
302
|
+
// execution revert (whose decoded revert string could well mention insufficient funds too).
|
|
303
|
+
let result;
|
|
304
|
+
try {
|
|
305
|
+
result = await simulateBlocks();
|
|
299
306
|
} catch (err) {
|
|
300
|
-
if (err
|
|
307
|
+
if (getErrorCause(err, MethodNotFoundRpcError) || getErrorCause(err, MethodNotSupportedRpcError)) {
|
|
301
308
|
if (gasConfig.fallbackGasEstimate) {
|
|
302
309
|
this.logger?.warn(`Node does not support eth_simulateV1 API. Using fallback gas estimate: ${gasConfig.fallbackGasEstimate}`);
|
|
303
310
|
return {
|
|
@@ -307,8 +314,31 @@ export class ReadOnlyL1TxUtils {
|
|
|
307
314
|
}
|
|
308
315
|
this.logger?.error('Node does not support eth_simulateV1 API');
|
|
309
316
|
}
|
|
317
|
+
if (isInsufficientFundsRpcError(err)) {
|
|
318
|
+
const rpcError = getErrorCause(err, RpcRequestError);
|
|
319
|
+
const providerMessage = rpcError?.details ?? (err instanceof Error ? err.message : String(err));
|
|
320
|
+
const gas = call?.gas !== undefined ? `, gas ${call.gas}` : '';
|
|
321
|
+
throw new Error(`L1 node rejected the eth_simulateV1 request with an insufficient funds error before executing it ` + `(sender ${call?.from ?? 'unknown'}${gas}): ${providerMessage}`, {
|
|
322
|
+
cause: err
|
|
323
|
+
});
|
|
324
|
+
}
|
|
310
325
|
throw err;
|
|
311
326
|
}
|
|
327
|
+
if (result[0].calls[0].status === 'failure') {
|
|
328
|
+
this.logger?.error('L1 transaction simulation failed', result[0].calls[0].error);
|
|
329
|
+
const decodedError = decodeErrorResult({
|
|
330
|
+
abi,
|
|
331
|
+
data: result[0].calls[0].data
|
|
332
|
+
});
|
|
333
|
+
throw new Error(`L1 transaction simulation failed with error ${decodedError.errorName}(${decodedError.args?.join(',')})`);
|
|
334
|
+
}
|
|
335
|
+
this.logger?.debug(`L1 transaction simulation succeeded`, {
|
|
336
|
+
...result[0].calls[0]
|
|
337
|
+
});
|
|
338
|
+
return {
|
|
339
|
+
gasUsed: result[0].gasUsed,
|
|
340
|
+
result: result[0].calls[0].data
|
|
341
|
+
};
|
|
312
342
|
}
|
|
313
343
|
bumpGasLimit(gasLimit, _gasConfig) {
|
|
314
344
|
const gasConfig = {
|
|
@@ -12,8 +12,10 @@ export declare class Delayer {
|
|
|
12
12
|
ethereumSlotDuration: bigint;
|
|
13
13
|
nextWait: {
|
|
14
14
|
l1Timestamp: bigint;
|
|
15
|
+
timeoutSeconds?: number;
|
|
15
16
|
} | {
|
|
16
17
|
l1BlockNumber: bigint;
|
|
18
|
+
timeoutSeconds?: number;
|
|
17
19
|
} | {
|
|
18
20
|
indefinitely: true;
|
|
19
21
|
} | undefined;
|
|
@@ -29,9 +31,9 @@ export declare class Delayer {
|
|
|
29
31
|
/** Returns the raw hex for all cancelled txs. */
|
|
30
32
|
getCancelledTxs(): Hex[];
|
|
31
33
|
/** Delays the next tx to be sent so it lands on the given L1 block number. */
|
|
32
|
-
pauseNextTxUntilBlock(l1BlockNumber: number | bigint): void;
|
|
34
|
+
pauseNextTxUntilBlock(l1BlockNumber: number | bigint, timeoutSeconds?: number): void;
|
|
33
35
|
/** Delays the next tx to be sent so it lands on the given timestamp. */
|
|
34
|
-
pauseNextTxUntilTimestamp(l1Timestamp: number | bigint): void;
|
|
36
|
+
pauseNextTxUntilTimestamp(l1Timestamp: number | bigint, timeoutSeconds?: number): void;
|
|
35
37
|
/** Delays the next tx to be sent indefinitely. */
|
|
36
38
|
cancelNextTx(): void;
|
|
37
39
|
/**
|
|
@@ -53,4 +55,4 @@ export declare function createDelayer(dateProvider: DateProvider, opts: {
|
|
|
53
55
|
* The delayer intercepts sendRawTransaction calls and delays them based on the delayer's state.
|
|
54
56
|
*/
|
|
55
57
|
export declare function wrapClientWithDelayer<T extends ViemClient>(client: T, delayer: Delayer): T;
|
|
56
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
58
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHhfZGVsYXllci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2wxX3R4X3V0aWxzL3R4X2RlbGF5ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFFLEtBQUssTUFBTSxFQUFFLEtBQUssY0FBYyxFQUFnQixNQUFNLHVCQUF1QixDQUFDO0FBRXZGLE9BQU8sS0FBSyxFQUFFLFlBQVksRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRzVELE9BQU8sRUFDTCxLQUFLLE1BQU0sRUFDWCxLQUFLLEdBQUcsRUFVVCxNQUFNLE1BQU0sQ0FBQztBQUVkLE9BQU8sS0FBSyxFQUE0QixVQUFVLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFJeEUsd0JBQWdCLGNBQWMsQ0FBQyxDQUFDLFNBQVMsTUFBTSxFQUM3QyxNQUFNLEVBQUUsQ0FBQyxFQUNULFdBQVcsRUFBRSxNQUFNLEdBQUcsTUFBTSxFQUM1QixNQUFNLENBQUMsRUFBRSxNQUFNLEVBQ2YsT0FBTyxDQUFDLEVBQUUsTUFBTSxvQkFpQmpCO0FBRUQsd0JBQWdCLG9CQUFvQixDQUFDLENBQUMsU0FBUyxNQUFNLEVBQ25ELE1BQU0sRUFBRSxDQUFDLEVBQ1QsU0FBUyxFQUFFLE1BQU0sR0FBRyxNQUFNLEVBQzFCLE1BQU0sQ0FBQyxFQUFFLE1BQU0sRUFDZixPQUFPLENBQUMsRUFBRSxNQUFNLG9CQXdCakI7QUFFRCxzR0FBc0c7QUFDdEcscUJBQWEsT0FBTztJQWNULFlBQVksRUFBRSxZQUFZO0lBYm5DLE9BQU8sQ0FBQyxNQUFNLENBQVM7SUFFaEIsd0JBQXdCLEVBQUUsTUFBTSxHQUFHLFNBQVMsQ0FBYTtJQUN6RCxvQkFBb0IsRUFBRSxNQUFNLENBQUM7SUFDN0IsUUFBUSxFQUNYO1FBQUUsV0FBVyxFQUFFLE1BQU0sQ0FBQztRQUFDLGNBQWMsQ0FBQyxFQUFFLE1BQU0sQ0FBQTtLQUFFLEdBQ2hEO1FBQUUsYUFBYSxFQUFFLE1BQU0sQ0FBQztRQUFDLGNBQWMsQ0FBQyxFQUFFLE1BQU0sQ0FBQTtLQUFFLEdBQ2xEO1FBQUUsWUFBWSxFQUFFLElBQUksQ0FBQTtLQUFFLEdBQ3RCLFNBQVMsQ0FBYTtJQUNuQixZQUFZLEVBQUUsR0FBRyxFQUFFLENBQU07SUFDekIsWUFBWSxFQUFFLEdBQUcsRUFBRSxDQUFNO0lBRWhDLFlBQ1MsWUFBWSxFQUFFLFlBQVksRUFDakMsSUFBSSxFQUFFO1FBQUUsb0JBQW9CLEVBQUUsTUFBTSxHQUFHLE1BQU0sQ0FBQTtLQUFFLEVBQy9DLFFBQVEsRUFBRSxjQUFjLEVBSXpCO0lBRUQsd0RBQXdEO0lBQ3hELFNBQVMsSUFBSSxNQUFNLENBRWxCO0lBRUQsc0RBQXNEO0lBQ3RELGVBQWUsb0JBRWQ7SUFFRCxpREFBaUQ7SUFDakQsZUFBZSxJQUFJLEdBQUcsRUFBRSxDQUV2QjtJQUVELDhFQUE4RTtJQUM5RSxxQkFBcUIsQ0FBQyxhQUFhLEVBQUUsTUFBTSxHQUFHLE1BQU0sRUFBRSxjQUFjLENBQUMsRUFBRSxNQUFNLFFBRTVFO0lBRUQsd0VBQXdFO0lBQ3hFLHlCQUF5QixDQUFDLFdBQVcsRUFBRSxNQUFNLEdBQUcsTUFBTSxFQUFFLGNBQWMsQ0FBQyxFQUFFLE1BQU0sUUFFOUU7SUFFRCxrREFBa0Q7SUFDbEQsWUFBWSxTQUVYO0lBRUQ7Ozs7T0FJRztJQUNILDJCQUEyQixDQUFDLE9BQU8sRUFBRSxNQUFNLEdBQUcsU0FBUyxRQUV0RDtDQUNGO0FBRUQ7OztHQUdHO0FBQ0gsd0JBQWdCLGFBQWEsQ0FDM0IsWUFBWSxFQUFFLFlBQVksRUFDMUIsSUFBSSxFQUFFO0lBQUUsb0JBQW9CLEVBQUUsTUFBTSxHQUFHLE1BQU0sQ0FBQTtDQUFFLEVBQy9DLFFBQVEsRUFBRSxjQUFjLEdBQ3ZCLE9BQU8sQ0FFVDtBQWFEOzs7R0FHRztBQUNILHdCQUFnQixxQkFBcUIsQ0FBQyxDQUFDLFNBQVMsVUFBVSxFQUFFLE1BQU0sRUFBRSxDQUFDLEVBQUUsT0FBTyxFQUFFLE9BQU8sR0FBRyxDQUFDLENBdUgxRiJ9
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tx_delayer.d.ts","sourceRoot":"","sources":["../../src/l1_tx_utils/tx_delayer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,cAAc,EAAgB,MAAM,uBAAuB,CAAC;AAEvF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAG5D,OAAO,EACL,KAAK,MAAM,EACX,KAAK,GAAG,EAUT,MAAM,MAAM,CAAC;AAEd,OAAO,KAAK,EAA4B,UAAU,EAAE,MAAM,aAAa,CAAC;AAIxE,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAC7C,MAAM,EAAE,CAAC,EACT,WAAW,EAAE,MAAM,GAAG,MAAM,EAC5B,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,oBAiBjB;AAED,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,MAAM,EACnD,MAAM,EAAE,CAAC,EACT,SAAS,EAAE,MAAM,GAAG,MAAM,EAC1B,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,oBAwBjB;AAED,sGAAsG;AACtG,qBAAa,OAAO;
|
|
1
|
+
{"version":3,"file":"tx_delayer.d.ts","sourceRoot":"","sources":["../../src/l1_tx_utils/tx_delayer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,cAAc,EAAgB,MAAM,uBAAuB,CAAC;AAEvF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAG5D,OAAO,EACL,KAAK,MAAM,EACX,KAAK,GAAG,EAUT,MAAM,MAAM,CAAC;AAEd,OAAO,KAAK,EAA4B,UAAU,EAAE,MAAM,aAAa,CAAC;AAIxE,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAC7C,MAAM,EAAE,CAAC,EACT,WAAW,EAAE,MAAM,GAAG,MAAM,EAC5B,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,oBAiBjB;AAED,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,MAAM,EACnD,MAAM,EAAE,CAAC,EACT,SAAS,EAAE,MAAM,GAAG,MAAM,EAC1B,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,oBAwBjB;AAED,sGAAsG;AACtG,qBAAa,OAAO;IAcT,YAAY,EAAE,YAAY;IAbnC,OAAO,CAAC,MAAM,CAAS;IAEhB,wBAAwB,EAAE,MAAM,GAAG,SAAS,CAAa;IACzD,oBAAoB,EAAE,MAAM,CAAC;IAC7B,QAAQ,EACX;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAChD;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAClD;QAAE,YAAY,EAAE,IAAI,CAAA;KAAE,GACtB,SAAS,CAAa;IACnB,YAAY,EAAE,GAAG,EAAE,CAAM;IACzB,YAAY,EAAE,GAAG,EAAE,CAAM;IAEhC,YACS,YAAY,EAAE,YAAY,EACjC,IAAI,EAAE;QAAE,oBAAoB,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,EAC/C,QAAQ,EAAE,cAAc,EAIzB;IAED,wDAAwD;IACxD,SAAS,IAAI,MAAM,CAElB;IAED,sDAAsD;IACtD,eAAe,oBAEd;IAED,iDAAiD;IACjD,eAAe,IAAI,GAAG,EAAE,CAEvB;IAED,8EAA8E;IAC9E,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,QAE5E;IAED,wEAAwE;IACxE,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,QAE9E;IAED,kDAAkD;IAClD,YAAY,SAEX;IAED;;;;OAIG;IACH,2BAA2B,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,QAEtD;CACF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,YAAY,EAAE,YAAY,EAC1B,IAAI,EAAE;IAAE,oBAAoB,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,EAC/C,QAAQ,EAAE,cAAc,GACvB,OAAO,CAET;AAaD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,UAAU,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,CAAC,CAuH1F"}
|
|
@@ -60,14 +60,16 @@ export function waitUntilL1Timestamp(client, timestamp, logger, timeout) {
|
|
|
60
60
|
/** Returns the raw hex for all cancelled txs. */ getCancelledTxs() {
|
|
61
61
|
return this.cancelledTxs;
|
|
62
62
|
}
|
|
63
|
-
/** Delays the next tx to be sent so it lands on the given L1 block number. */ pauseNextTxUntilBlock(l1BlockNumber) {
|
|
63
|
+
/** Delays the next tx to be sent so it lands on the given L1 block number. */ pauseNextTxUntilBlock(l1BlockNumber, timeoutSeconds) {
|
|
64
64
|
this.nextWait = {
|
|
65
|
-
l1BlockNumber: BigInt(l1BlockNumber)
|
|
65
|
+
l1BlockNumber: BigInt(l1BlockNumber),
|
|
66
|
+
timeoutSeconds
|
|
66
67
|
};
|
|
67
68
|
}
|
|
68
|
-
/** Delays the next tx to be sent so it lands on the given timestamp. */ pauseNextTxUntilTimestamp(l1Timestamp) {
|
|
69
|
+
/** Delays the next tx to be sent so it lands on the given timestamp. */ pauseNextTxUntilTimestamp(l1Timestamp, timeoutSeconds) {
|
|
69
70
|
this.nextWait = {
|
|
70
|
-
l1Timestamp: BigInt(l1Timestamp)
|
|
71
|
+
l1Timestamp: BigInt(l1Timestamp),
|
|
72
|
+
timeoutSeconds
|
|
71
73
|
};
|
|
72
74
|
}
|
|
73
75
|
/** Delays the next tx to be sent indefinitely. */ cancelNextTx() {
|
|
@@ -131,7 +133,7 @@ export function waitUntilL1Timestamp(client, timestamp, logger, timeout) {
|
|
|
131
133
|
return Promise.resolve(txHash);
|
|
132
134
|
}
|
|
133
135
|
// Or wait until the desired block number or timestamp
|
|
134
|
-
wait = 'l1BlockNumber' in waitUntil ? waitUntilBlock(publicClient, waitUntil.l1BlockNumber - 1n, logger) : 'l1Timestamp' in waitUntil ? waitUntilL1Timestamp(publicClient, waitUntil.l1Timestamp - delayer.ethereumSlotDuration, logger) : undefined;
|
|
136
|
+
wait = 'l1BlockNumber' in waitUntil ? waitUntilBlock(publicClient, waitUntil.l1BlockNumber - 1n, logger, waitUntil.timeoutSeconds) : 'l1Timestamp' in waitUntil ? waitUntilL1Timestamp(publicClient, waitUntil.l1Timestamp - delayer.ethereumSlotDuration, logger, waitUntil.timeoutSeconds) : undefined;
|
|
135
137
|
logger.info(`Delaying tx ${txHash} until ${inspect(waitUntil)}`, {
|
|
136
138
|
sender,
|
|
137
139
|
argsLen: args.length,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type LoggerBindings } from '@aztec/foundation/log';
|
|
2
|
+
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
2
3
|
import { L1TxUtils } from './l1_tx_utils/index.js';
|
|
3
4
|
export type PublisherFilter<UtilsType extends L1TxUtils> = (utils: UtilsType) => boolean;
|
|
4
5
|
/** Config accepted by PublisherManager. */
|
|
@@ -8,23 +9,34 @@ type PublisherManagerConfig = {
|
|
|
8
9
|
publisherFundingAmount?: bigint;
|
|
9
10
|
};
|
|
10
11
|
export declare class PublisherManager<UtilsType extends L1TxUtils = L1TxUtils> {
|
|
11
|
-
|
|
12
|
+
protected publishers: UtilsType[];
|
|
12
13
|
private log;
|
|
13
14
|
private config;
|
|
14
15
|
private static readonly FUNDING_CHECK_INTERVAL_MS;
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
protected funder?: UtilsType;
|
|
17
|
+
protected readonly fundingPromise?: RunningPromise;
|
|
18
|
+
private started;
|
|
17
19
|
constructor(publishers: UtilsType[], config: PublisherManagerConfig, opts?: {
|
|
18
20
|
bindings?: LoggerBindings;
|
|
19
21
|
funder?: UtilsType;
|
|
20
22
|
});
|
|
21
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Clears any interrupted flag left by a previous {@link stop} so publishing works again after a restart,
|
|
25
|
+
* loads the state of all publishers and the funder, and starts periodic funding checks. Idempotent: a
|
|
26
|
+
* start while already started is a no-op, so it never re-runs `loadStateAndResumeMonitoring` (which
|
|
27
|
+
* would spawn a duplicate background monitor per pending nonce). Lifecycle calls are expected to be
|
|
28
|
+
* serialized by the caller.
|
|
29
|
+
*/
|
|
22
30
|
start(): Promise<void>;
|
|
23
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Stops the funding loop, interrupts all publishers so no further L1 txs are sent, and waits (bounded)
|
|
33
|
+
* for their in-flight tx monitor loops to wind down. Idempotent, and the manager may be restarted
|
|
34
|
+
* afterwards via {@link start}, which clears the interrupted flag.
|
|
35
|
+
*/
|
|
24
36
|
stop(): Promise<void>;
|
|
25
37
|
getAvailablePublisher(filter?: PublisherFilter<UtilsType>): Promise<UtilsType>;
|
|
26
38
|
private triggerFundingIfNeeded;
|
|
27
39
|
private fundPublishers;
|
|
28
40
|
}
|
|
29
41
|
export {};
|
|
30
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
42
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGlzaGVyX21hbmFnZXIuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9wdWJsaXNoZXJfbWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQWUsS0FBSyxjQUFjLEVBQWdCLE1BQU0sdUJBQXVCLENBQUM7QUFDdkYsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBR25FLE9BQU8sRUFBRSxTQUFTLEVBQWdCLE1BQU0sd0JBQXdCLENBQUM7QUF3QmpFLE1BQU0sTUFBTSxlQUFlLENBQUMsU0FBUyxTQUFTLFNBQVMsSUFBSSxDQUFDLEtBQUssRUFBRSxTQUFTLEtBQUssT0FBTyxDQUFDO0FBRXpGLDJDQUEyQztBQUMzQyxLQUFLLHNCQUFzQixHQUFHO0lBQzVCLDJCQUEyQixDQUFDLEVBQUUsT0FBTyxDQUFDO0lBQ3RDLHlCQUF5QixDQUFDLEVBQUUsTUFBTSxDQUFDO0lBQ25DLHNCQUFzQixDQUFDLEVBQUUsTUFBTSxDQUFDO0NBQ2pDLENBQUM7QUFFRixxQkFBYSxnQkFBZ0IsQ0FBQyxTQUFTLFNBQVMsU0FBUyxHQUFHLFNBQVM7SUFTakUsU0FBUyxDQUFDLFVBQVUsRUFBRSxTQUFTLEVBQUU7SUFSbkMsT0FBTyxDQUFDLEdBQUcsQ0FBUztJQUNwQixPQUFPLENBQUMsTUFBTSxDQUF5QjtJQUN2QyxPQUFPLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyx5QkFBeUIsQ0FBaUI7SUFDbEUsU0FBUyxDQUFDLE1BQU0sQ0FBQyxFQUFFLFNBQVMsQ0FBQztJQUM3QixTQUFTLENBQUMsUUFBUSxDQUFDLGNBQWMsQ0FBQyxFQUFFLGNBQWMsQ0FBQztJQUNuRCxPQUFPLENBQUMsT0FBTyxDQUFTO0lBRXhCLFlBQ1ksVUFBVSxFQUFFLFNBQVMsRUFBRSxFQUNqQyxNQUFNLEVBQUUsc0JBQXNCLEVBQzlCLElBQUksQ0FBQyxFQUFFO1FBQUUsUUFBUSxDQUFDLEVBQUUsY0FBYyxDQUFDO1FBQUMsTUFBTSxDQUFDLEVBQUUsU0FBUyxDQUFBO0tBQUUsRUE2QnpEO0lBRUQ7Ozs7OztPQU1HO0lBQ1UsS0FBSyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FtQmxDO0lBRUQ7Ozs7T0FJRztJQUNVLElBQUksSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBV2pDO0lBUVkscUJBQXFCLENBQUMsTUFBTSxHQUFFLGVBQWUsQ0FBQyxTQUFTLENBQWMsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBd0R0RztZQUdhLHNCQUFzQjtZQXFDdEIsY0FBYztDQVU3QiJ9
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publisher_manager.d.ts","sourceRoot":"","sources":["../src/publisher_manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,KAAK,cAAc,EAAgB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"publisher_manager.d.ts","sourceRoot":"","sources":["../src/publisher_manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,KAAK,cAAc,EAAgB,MAAM,uBAAuB,CAAC;AACvF,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAGnE,OAAO,EAAE,SAAS,EAAgB,MAAM,wBAAwB,CAAC;AAwBjE,MAAM,MAAM,eAAe,CAAC,SAAS,SAAS,SAAS,IAAI,CAAC,KAAK,EAAE,SAAS,KAAK,OAAO,CAAC;AAEzF,2CAA2C;AAC3C,KAAK,sBAAsB,GAAG;IAC5B,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF,qBAAa,gBAAgB,CAAC,SAAS,SAAS,SAAS,GAAG,SAAS;IASjE,SAAS,CAAC,UAAU,EAAE,SAAS,EAAE;IARnC,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,yBAAyB,CAAiB;IAClE,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAC7B,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;IACnD,OAAO,CAAC,OAAO,CAAS;IAExB,YACY,UAAU,EAAE,SAAS,EAAE,EACjC,MAAM,EAAE,sBAAsB,EAC9B,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,cAAc,CAAC;QAAC,MAAM,CAAC,EAAE,SAAS,CAAA;KAAE,EA6BzD;IAED;;;;;;OAMG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAmBlC;IAED;;;;OAIG;IACU,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAWjC;IAQY,qBAAqB,CAAC,MAAM,GAAE,eAAe,CAAC,SAAS,CAAc,GAAG,OAAO,CAAC,SAAS,CAAC,CAwDtG;YAGa,sBAAsB;YAqCtB,cAAc;CAU7B"}
|
|
@@ -30,8 +30,10 @@ export class PublisherManager {
|
|
|
30
30
|
static FUNDING_CHECK_INTERVAL_MS = 2 * 60 * 1000;
|
|
31
31
|
funder;
|
|
32
32
|
fundingPromise;
|
|
33
|
+
started;
|
|
33
34
|
constructor(publishers, config, opts){
|
|
34
35
|
this.publishers = publishers;
|
|
36
|
+
this.started = false;
|
|
35
37
|
this.funder = opts?.funder;
|
|
36
38
|
this.log = createLogger('publisher:manager', opts?.bindings);
|
|
37
39
|
this.log.info(`PublisherManager initialized with ${publishers.length} publishers.`);
|
|
@@ -49,21 +51,48 @@ export class PublisherManager {
|
|
|
49
51
|
this.funder = undefined;
|
|
50
52
|
}
|
|
51
53
|
}
|
|
54
|
+
if (this.funder && hasThreshold && hasAmount) {
|
|
55
|
+
this.fundingPromise = new RunningPromise(()=>this.triggerFundingIfNeeded(), this.log, PublisherManager.FUNDING_CHECK_INTERVAL_MS);
|
|
56
|
+
}
|
|
52
57
|
}
|
|
53
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Clears any interrupted flag left by a previous {@link stop} so publishing works again after a restart,
|
|
60
|
+
* loads the state of all publishers and the funder, and starts periodic funding checks. Idempotent: a
|
|
61
|
+
* start while already started is a no-op, so it never re-runs `loadStateAndResumeMonitoring` (which
|
|
62
|
+
* would spawn a duplicate background monitor per pending nonce). Lifecycle calls are expected to be
|
|
63
|
+
* serialized by the caller.
|
|
64
|
+
*/ async start() {
|
|
65
|
+
if (this.started) {
|
|
66
|
+
this.log.debug('PublisherManager already started, ignoring start');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// Clear the interrupted flag set by a previous stop() so a restarted manager can publish again.
|
|
70
|
+
// On a first start this is a no-op (the flag is already clear).
|
|
71
|
+
this.publishers.forEach((pub)=>pub.restart());
|
|
72
|
+
this.funder?.restart();
|
|
54
73
|
await Promise.all([
|
|
55
74
|
...this.publishers.map((pub)=>pub.loadStateAndResumeMonitoring()),
|
|
56
75
|
this.funder?.loadStateAndResumeMonitoring()
|
|
57
76
|
]);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
77
|
+
this.fundingPromise?.start();
|
|
78
|
+
// Marked started only once fully up, so a start that failed to load state can be retried.
|
|
79
|
+
this.started = true;
|
|
62
80
|
}
|
|
63
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* Stops the funding loop, interrupts all publishers so no further L1 txs are sent, and waits (bounded)
|
|
83
|
+
* for their in-flight tx monitor loops to wind down. Idempotent, and the manager may be restarted
|
|
84
|
+
* afterwards via {@link start}, which clears the interrupted flag.
|
|
85
|
+
*/ async stop() {
|
|
86
|
+
this.started = false;
|
|
64
87
|
await this.fundingPromise?.stop();
|
|
65
88
|
this.publishers.forEach((pub)=>pub.interrupt());
|
|
66
89
|
this.funder?.interrupt();
|
|
90
|
+
// Wait for in-flight tx monitor loops to observe the interrupt, so no L1 requests are still
|
|
91
|
+
// being issued after shutdown (e.g. against an anvil instance a test is about to tear down).
|
|
92
|
+
await Promise.all([
|
|
93
|
+
...this.publishers.map((pub)=>pub.waitMonitoringStopped()),
|
|
94
|
+
this.funder?.waitMonitoringStopped()
|
|
95
|
+
]);
|
|
67
96
|
}
|
|
68
97
|
// Finds and prioritises available publishers based on
|
|
69
98
|
// 1. Validity as per the provided filter function
|
|
@@ -97,8 +126,16 @@ export class PublisherManager {
|
|
|
97
126
|
publisher: pub
|
|
98
127
|
};
|
|
99
128
|
}));
|
|
129
|
+
// Discount unfunded publishers: a publisher with no ETH cannot send a tx, so it must never win
|
|
130
|
+
// selection regardless of how favourable its state or last-used time is. Fall back to the full
|
|
131
|
+
// set only if every candidate is unfunded, so behaviour is no worse than before.
|
|
132
|
+
let fundedPublishers = publishersWithBalance.filter((p)=>p.balance > 0n);
|
|
133
|
+
if (fundedPublishers.length === 0) {
|
|
134
|
+
this.log.warn(`All candidate publishers have zero balance; selecting from unfunded publishers.`);
|
|
135
|
+
fundedPublishers = publishersWithBalance;
|
|
136
|
+
}
|
|
100
137
|
// Sort based on state, then balance, then time since last use
|
|
101
|
-
const sortedPublishers =
|
|
138
|
+
const sortedPublishers = fundedPublishers.sort((a, b)=>{
|
|
102
139
|
const stateComparison = sortOrder.indexOf(a.publisher.state) - sortOrder.indexOf(b.publisher.state);
|
|
103
140
|
if (stateComparison !== 0) {
|
|
104
141
|
return stateComparison;
|
package/dest/queries.d.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
2
|
+
import { type Block } from 'viem';
|
|
2
3
|
import { type L1ContractsConfig } from './config.js';
|
|
3
|
-
import type { ViemPublicClient } from './types.js';
|
|
4
|
+
import type { ViemClient, ViemPublicClient } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Returns the L1 finalized block, or `undefined` if the chain does not yet have one
|
|
7
|
+
* (common on freshly started devnets). Rethrows any other RPC error.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getFinalizedL1Block(client: ViemClient): Promise<Block<bigint, false, 'finalized'> | undefined>;
|
|
10
|
+
/**
|
|
11
|
+
* Returns true if the error originates from an RPC call that failed because
|
|
12
|
+
* the "finalized" (or "safe") block tag is not yet available on the chain.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isFinalizedBlockTagNotFoundError(err: unknown): boolean;
|
|
4
15
|
/** Reads the L1ContractsConfig from L1 contracts. */
|
|
5
16
|
export declare function getL1ContractsConfig(publicClient: ViemPublicClient, addresses: {
|
|
6
17
|
governanceAddress: EthAddress;
|
|
@@ -11,4 +22,4 @@ export declare function getL1ContractsConfig(publicClient: ViemPublicClient, add
|
|
|
11
22
|
rollupVersion: number;
|
|
12
23
|
genesisArchiveTreeRoot: `0x${string}`;
|
|
13
24
|
}>;
|
|
14
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
25
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicXVlcmllcy5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3F1ZXJpZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBRTNELE9BQU8sRUFBYSxLQUFLLEtBQUssRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUU3QyxPQUFPLEVBQTRCLEtBQUssaUJBQWlCLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFLL0UsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLGdCQUFnQixFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRS9EOzs7R0FHRztBQUNILHdCQUFzQixtQkFBbUIsQ0FBQyxNQUFNLEVBQUUsVUFBVSxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUMsTUFBTSxFQUFFLEtBQUssRUFBRSxXQUFXLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FTcEg7QUFtQkQ7OztHQUdHO0FBQ0gsd0JBQWdCLGdDQUFnQyxDQUFDLEdBQUcsRUFBRSxPQUFPLEdBQUcsT0FBTyxDQWtCdEU7QUFVRCxxREFBcUQ7QUFDckQsd0JBQXNCLG9CQUFvQixDQUN4QyxZQUFZLEVBQUUsZ0JBQWdCLEVBQzlCLFNBQVMsRUFBRTtJQUFFLGlCQUFpQixFQUFFLFVBQVUsQ0FBQztJQUFDLGFBQWEsQ0FBQyxFQUFFLFVBQVUsQ0FBQTtDQUFFLEdBQ3ZFLE9BQU8sQ0FDUixJQUFJLENBQUMsaUJBQWlCLEVBQUUsc0JBQXNCLENBQUMsR0FBRztJQUNoRCxZQUFZLEVBQUUsTUFBTSxDQUFDO0lBQ3JCLGFBQWEsRUFBRSxNQUFNLENBQUM7SUFDdEIsYUFBYSxFQUFFLE1BQU0sQ0FBQztJQUN0QixzQkFBc0IsRUFBRSxLQUFLLE1BQU0sRUFBRSxDQUFDO0NBQ3ZDLENBQ0YsQ0F1R0EifQ==
|
package/dest/queries.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAE3D,OAAO,EAA4B,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAK/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAE3D,OAAO,EAAa,KAAK,KAAK,EAAE,MAAM,MAAM,CAAC;AAE7C,OAAO,EAA4B,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAK/E,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE/D;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,GAAG,SAAS,CAAC,CASpH;AAmBD;;;GAGG;AACH,wBAAgB,gCAAgC,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAkBtE;AAUD,qDAAqD;AACrD,wBAAsB,oBAAoB,CACxC,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE;IAAE,iBAAiB,EAAE,UAAU,CAAC;IAAC,aAAa,CAAC,EAAE,UAAU,CAAA;CAAE,GACvE,OAAO,CACR,IAAI,CAAC,iBAAiB,EAAE,sBAAsB,CAAC,GAAG;IAChD,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB,EAAE,KAAK,MAAM,EAAE,CAAC;CACvC,CACF,CAuGA"}
|
package/dest/queries.js
CHANGED
|
@@ -1,9 +1,68 @@
|
|
|
1
1
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
2
|
+
import { BaseError } from 'viem';
|
|
2
3
|
import { DefaultL1ContractsConfig } from './config.js';
|
|
3
4
|
import { ReadOnlyGovernanceContract } from './contracts/governance.js';
|
|
4
5
|
import { GovernanceProposerContract } from './contracts/governance_proposer.js';
|
|
5
6
|
import { InboxContract } from './contracts/inbox.js';
|
|
6
7
|
import { RollupContract } from './contracts/rollup.js';
|
|
8
|
+
/**
|
|
9
|
+
* Returns the L1 finalized block, or `undefined` if the chain does not yet have one
|
|
10
|
+
* (common on freshly started devnets). Rethrows any other RPC error.
|
|
11
|
+
*/ export async function getFinalizedL1Block(client) {
|
|
12
|
+
try {
|
|
13
|
+
return await client.getBlock({
|
|
14
|
+
blockTag: 'finalized',
|
|
15
|
+
includeTransactions: false
|
|
16
|
+
});
|
|
17
|
+
} catch (err) {
|
|
18
|
+
if (isFinalizedBlockTagNotFoundError(err)) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
throw err;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Error messages returned by popular Ethereum execution clients when
|
|
25
|
+
// eth_getBlockByNumber / eth_call is called with blockTag "finalized" or
|
|
26
|
+
// "safe" and no such block has been produced yet:
|
|
27
|
+
//
|
|
28
|
+
// geth "finalized block not found"
|
|
29
|
+
// "safe block not found"
|
|
30
|
+
// reth "block not found: finalized"
|
|
31
|
+
// "block not found: safe"
|
|
32
|
+
// nethermind "Unknown block error" (same for both tags)
|
|
33
|
+
// besu "Unknown block"
|
|
34
|
+
// erigon 'block "finalized" not available (head block: N)'
|
|
35
|
+
// 'block "safe" not available (head block: N)'
|
|
36
|
+
//
|
|
37
|
+
// A combined regex covers all five:
|
|
38
|
+
const FINALIZED_BLOCK_TAG_NOT_FOUND_MESSAGE_RE = /(finalized|safe) block not found|block not found: (finalized|safe)|unknown block|block "(finalized|safe)" not available/i;
|
|
39
|
+
/**
|
|
40
|
+
* Returns true if the error originates from an RPC call that failed because
|
|
41
|
+
* the "finalized" (or "safe") block tag is not yet available on the chain.
|
|
42
|
+
*/ export function isFinalizedBlockTagNotFoundError(err) {
|
|
43
|
+
if (!err) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
if (err instanceof BaseError) {
|
|
47
|
+
const hit = err.walk((e)=>matchesFinalizedBlockTagNotFound(e));
|
|
48
|
+
if (hit) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
for(let cur = err, i = 0; cur && i < 10; cur = cur.cause, i++){
|
|
53
|
+
if (matchesFinalizedBlockTagNotFound(cur)) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
function matchesFinalizedBlockTagNotFound(e) {
|
|
60
|
+
if (!e) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const text = `${e.details ?? ''} ${e.message ?? ''} ${e.shortMessage ?? ''}`;
|
|
64
|
+
return FINALIZED_BLOCK_TAG_NOT_FOUND_MESSAGE_RE.test(text);
|
|
65
|
+
}
|
|
7
66
|
/** Reads the L1ContractsConfig from L1 contracts. */ export async function getL1ContractsConfig(publicClient, addresses) {
|
|
8
67
|
const governance = new ReadOnlyGovernanceContract(addresses.governanceAddress.toString(), publicClient);
|
|
9
68
|
const governanceProposerAddress = await governance.getGovernanceProposerAddress();
|
|
@@ -33,8 +92,8 @@ import { RollupContract } from './contracts/rollup.js';
|
|
|
33
92
|
slasherProposer?.getRoundSize() ?? 0n,
|
|
34
93
|
slasherProposer?.getLifetimeInRounds() ?? 0n,
|
|
35
94
|
slasherProposer?.getExecutionDelayInRounds() ?? 0n,
|
|
36
|
-
slasherProposer?.
|
|
37
|
-
slasherProposer?.
|
|
95
|
+
slasherProposer?.getSlashOffsetInRounds() ?? 0n,
|
|
96
|
+
slasherProposer?.getSlashingAmounts() ?? [
|
|
38
97
|
0n,
|
|
39
98
|
0n,
|
|
40
99
|
0n
|
|
@@ -74,7 +133,7 @@ import { RollupContract } from './contracts/rollup.js';
|
|
|
74
133
|
rollupVersion: Number(rollupVersion),
|
|
75
134
|
genesisArchiveTreeRoot: genesisArchiveTreeRoot.toString(),
|
|
76
135
|
exitDelaySeconds: Number(exitDelay),
|
|
77
|
-
|
|
136
|
+
slasherEnabled: !!slasherProposer,
|
|
78
137
|
slashingOffsetInRounds: Number(slashingOffsetInRounds),
|
|
79
138
|
slashAmountSmall: slashingAmounts[0],
|
|
80
139
|
slashAmountMedium: slashingAmounts[1],
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Logger } from '@aztec/foundation/log';
|
|
2
|
+
import type { ExtendedViemWalletClient } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Warms both KZG trusted setups in parallel for tests against a local anvil:
|
|
5
|
+
* - anvil's own setup, loaded lazily when it first validates a blob sidecar in `eth_sendRawTransaction`
|
|
6
|
+
* - our `@crate-crypto/node-eth-kzg` singleton (`getKzg()`, a ~2.2s synchronous precomp build)
|
|
7
|
+
*
|
|
8
|
+
* Without this, the first checkpoint publish pays both serially (~4.4s). The recipe does all RPC round
|
|
9
|
+
* trips up front, fires a single bare raw send carrying a fake (garbage) blob sidecar that anvil rejects
|
|
10
|
+
* after loading its trusted setup, yields once to flush the socket write, then synchronously builds our
|
|
11
|
+
* own precomp tables while anvil warms in parallel. The fake tx is rejected at admission, so it never
|
|
12
|
+
* enters the pool, mines no block, and burns no nonce — sending it from the passed client is harmless.
|
|
13
|
+
*
|
|
14
|
+
* Best-effort: any failure is debug-logged and swallowed, leaving lazy init as the fallback. Never throws.
|
|
15
|
+
*
|
|
16
|
+
* @param l1Client - The L1 client whose account sends the (rejected) warm-up blob tx.
|
|
17
|
+
*/
|
|
18
|
+
export declare function warmBlobKzg(l1Client: ExtendedViemWalletClient, logger?: Logger): Promise<void>;
|
|
19
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxvYl9remdfd2FybXVwLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdGVzdC9ibG9iX2t6Z193YXJtdXAudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFFcEQsT0FBTyxLQUFLLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFFNUQ7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFDSCx3QkFBc0IsV0FBVyxDQUFDLFFBQVEsRUFBRSx3QkFBd0IsRUFBRSxNQUFNLENBQUMsRUFBRSxNQUFNLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQTRDcEcifQ==
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blob_kzg_warmup.d.ts","sourceRoot":"","sources":["../../src/test/blob_kzg_warmup.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAEpD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAE5D;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,WAAW,CAAC,QAAQ,EAAE,wBAAwB,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4CpG"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { getBytesPerBlob, getBytesPerCommitment, getKzg } from '@aztec/blob-lib';
|
|
2
|
+
/**
|
|
3
|
+
* Warms both KZG trusted setups in parallel for tests against a local anvil:
|
|
4
|
+
* - anvil's own setup, loaded lazily when it first validates a blob sidecar in `eth_sendRawTransaction`
|
|
5
|
+
* - our `@crate-crypto/node-eth-kzg` singleton (`getKzg()`, a ~2.2s synchronous precomp build)
|
|
6
|
+
*
|
|
7
|
+
* Without this, the first checkpoint publish pays both serially (~4.4s). The recipe does all RPC round
|
|
8
|
+
* trips up front, fires a single bare raw send carrying a fake (garbage) blob sidecar that anvil rejects
|
|
9
|
+
* after loading its trusted setup, yields once to flush the socket write, then synchronously builds our
|
|
10
|
+
* own precomp tables while anvil warms in parallel. The fake tx is rejected at admission, so it never
|
|
11
|
+
* enters the pool, mines no block, and burns no nonce — sending it from the passed client is harmless.
|
|
12
|
+
*
|
|
13
|
+
* Best-effort: any failure is debug-logged and swallowed, leaving lazy init as the fallback. Never throws.
|
|
14
|
+
*
|
|
15
|
+
* @param l1Client - The L1 client whose account sends the (rejected) warm-up blob tx.
|
|
16
|
+
*/ export async function warmBlobKzg(l1Client, logger) {
|
|
17
|
+
try {
|
|
18
|
+
// A pure-JS fake kzg: returns deterministic garbage of the correct lengths (commitment and proof are
|
|
19
|
+
// both 48 bytes). It makes viem build a syntactically valid sidecar without initializing any native
|
|
20
|
+
// module, so anvil loads its trusted setup to validate the (invalid) proof and then rejects the tx.
|
|
21
|
+
const fakeKzg = {
|
|
22
|
+
blobToKzgCommitment: ()=>{
|
|
23
|
+
const commitment = new Uint8Array(getBytesPerCommitment());
|
|
24
|
+
commitment.fill(0xab);
|
|
25
|
+
commitment[0] = 0xc0;
|
|
26
|
+
return commitment;
|
|
27
|
+
},
|
|
28
|
+
computeBlobKzgProof: ()=>{
|
|
29
|
+
const proof = new Uint8Array(getBytesPerCommitment());
|
|
30
|
+
proof.fill(0xcd);
|
|
31
|
+
proof[0] = 0xc0;
|
|
32
|
+
return proof;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
// All RPC round trips (nonce, fees, gas) happen here, so only a single socket write remains to flush.
|
|
36
|
+
const prepared = await l1Client.prepareTransactionRequest({
|
|
37
|
+
blobs: [
|
|
38
|
+
new Uint8Array(getBytesPerBlob())
|
|
39
|
+
],
|
|
40
|
+
kzg: fakeKzg,
|
|
41
|
+
to: '0x0000000000000000000000000000000000000000',
|
|
42
|
+
value: 0n,
|
|
43
|
+
maxFeePerBlobGas: 1_000_000_000_000n
|
|
44
|
+
});
|
|
45
|
+
const serialized = await l1Client.signTransaction(prepared);
|
|
46
|
+
// Fire the raw send without awaiting and swallow the expected rejection.
|
|
47
|
+
const sent = l1Client.request({
|
|
48
|
+
method: 'eth_sendRawTransaction',
|
|
49
|
+
params: [
|
|
50
|
+
serialized
|
|
51
|
+
]
|
|
52
|
+
}).catch(()=>{});
|
|
53
|
+
// Flush the single socket write so anvil receives the tx and starts loading its trusted setup in its
|
|
54
|
+
// own process before we block the event loop below.
|
|
55
|
+
await new Promise((resolve)=>setImmediate(resolve));
|
|
56
|
+
// Synchronously build our precomp tables (~2.2s, event-loop-blocking); anvil warms in parallel.
|
|
57
|
+
getKzg();
|
|
58
|
+
await sent;
|
|
59
|
+
} catch (err) {
|
|
60
|
+
logger?.debug('Failed to warm blob KZG; falling back to lazy init', {
|
|
61
|
+
err
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|