@aztec/aztec-node 5.0.0-nightly.20260707 → 5.0.0-nightly.20260708
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/factory.d.ts +1 -1
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +13 -0
- package/dest/modules/node_world_state_queries.d.ts +11 -7
- package/dest/modules/node_world_state_queries.d.ts.map +1 -1
- package/dest/modules/node_world_state_queries.js +69 -54
- package/package.json +28 -28
- package/src/factory.ts +17 -0
- package/src/modules/node_world_state_queries.ts +78 -64
package/dest/factory.d.ts
CHANGED
|
@@ -30,4 +30,4 @@ export interface CreateAztecNodeOptions {
|
|
|
30
30
|
* @returns A fully synced Aztec Node for use in development/testing.
|
|
31
31
|
*/
|
|
32
32
|
export declare function createAztecNodeService(inputConfig: AztecNodeConfig, deps?: CreateAztecNodeDeps, options?: CreateAztecNodeOptions): Promise<AztecNodeService>;
|
|
33
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
33
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2ZhY3RvcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBWUEsT0FBTyxFQUFFLEtBQUssTUFBTSxFQUFnQixNQUFNLHVCQUF1QixDQUFDO0FBQ2xFLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUl2RCxPQUFPLEVBQUUsS0FBSyxhQUFhLEVBQW1CLE1BQU0sWUFBWSxDQUFDO0FBQ2pFLE9BQU8sRUFBbUIsS0FBSyxjQUFjLEVBQW9CLE1BQU0sb0JBQW9CLENBQUM7QUFFNUYsT0FBTyxFQUlMLEtBQUssa0JBQWtCLEVBQ3hCLE1BQU0seUJBQXlCLENBQUM7QUFnQmpDLE9BQU8sS0FBSyxFQUFFLFdBQVcsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQzdELE9BQU8sRUFBRSxLQUFLLGVBQWUsRUFBc0IsTUFBTSx5QkFBeUIsQ0FBQztBQVVuRixPQUFPLEtBQUssRUFBRSwwQkFBMEIsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBS25GLE9BQU8sRUFBRSxLQUFLLGVBQWUsRUFBOEIsTUFBTSx3QkFBd0IsQ0FBQztBQUMxRixPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUcxRCxvR0FBb0c7QUFDcEcsTUFBTSxXQUFXLG1CQUFtQjtJQUNsQyxTQUFTLENBQUMsRUFBRSxlQUFlLENBQUM7SUFDNUIsTUFBTSxDQUFDLEVBQUUsTUFBTSxDQUFDO0lBQ2hCLFNBQVMsQ0FBQyxFQUFFLGtCQUFrQixDQUFDO0lBQy9CLFlBQVksQ0FBQyxFQUFFLFlBQVksQ0FBQztJQUM1QixhQUFhLENBQUMsRUFBRSxhQUFhLENBQUM7SUFDOUIsY0FBYyxDQUFDLEVBQUUsT0FBTyxDQUFDLGNBQWMsQ0FBQyxDQUFDO0lBQ3pDLG9CQUFvQixDQUFDLEVBQUUsMEJBQTBCLENBQUM7Q0FDbkQ7QUFFRCw2RUFBNkU7QUFDN0UsTUFBTSxXQUFXLHNCQUFzQjtJQUNyQyxPQUFPLENBQUMsRUFBRSxXQUFXLENBQUM7SUFDdEIsa0JBQWtCLENBQUMsRUFBRSxPQUFPLENBQUM7SUFDN0IsbUJBQW1CLENBQUMsRUFBRSxPQUFPLENBQUM7Q0FDL0I7QUFFRDs7OztHQUlHO0FBQ0gsd0JBQXNCLHNCQUFzQixDQUMxQyxXQUFXLEVBQUUsZUFBZSxFQUM1QixJQUFJLEdBQUUsbUJBQXdCLEVBQzlCLE9BQU8sR0FBRSxzQkFBMkIsR0FDbkMsT0FBTyxDQUFDLGdCQUFnQixDQUFDLENBOGlCM0IifQ==
|
package/dest/factory.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAIvD,OAAO,EAAE,KAAK,aAAa,EAAmB,MAAM,YAAY,CAAC;AACjE,OAAO,EAAmB,KAAK,cAAc,EAAoB,MAAM,oBAAoB,CAAC;AAE5F,OAAO,EAIL,KAAK,kBAAkB,EACxB,MAAM,yBAAyB,CAAC;AAgBjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,yBAAyB,CAAC;AAUnF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AAKnF,OAAO,EAAE,KAAK,eAAe,EAA8B,MAAM,wBAAwB,CAAC;AAC1F,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1D,oGAAoG;AACpG,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,cAAc,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACzC,oBAAoB,CAAC,EAAE,0BAA0B,CAAC;CACnD;AAED,6EAA6E;AAC7E,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,eAAe,EAC5B,IAAI,GAAE,mBAAwB,EAC9B,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,gBAAgB,CAAC,
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAIvD,OAAO,EAAE,KAAK,aAAa,EAAmB,MAAM,YAAY,CAAC;AACjE,OAAO,EAAmB,KAAK,cAAc,EAAoB,MAAM,oBAAoB,CAAC;AAE5F,OAAO,EAIL,KAAK,kBAAkB,EACxB,MAAM,yBAAyB,CAAC;AAgBjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,yBAAyB,CAAC;AAUnF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AAKnF,OAAO,EAAE,KAAK,eAAe,EAA8B,MAAM,wBAAwB,CAAC;AAC1F,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1D,oGAAoG;AACpG,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,cAAc,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACzC,oBAAoB,CAAC,EAAE,0BAA0B,CAAC;CACnD;AAED,6EAA6E;AAC7E,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,eAAe,EAC5B,IAAI,GAAE,mBAAwB,EAC9B,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,gBAAgB,CAAC,CA8iB3B"}
|
package/dest/factory.js
CHANGED
|
@@ -166,6 +166,19 @@ import { createSentinel } from './sentinel/factory.js';
|
|
|
166
166
|
const globalVariableBuilder = new GlobalVariableBuilder(publicClient, globalVariableBuilderConfig);
|
|
167
167
|
const feeProvider = new FeeProviderImpl(dateProvider, publicClient, globalVariableBuilderConfig);
|
|
168
168
|
const collectOffenses = !config.disableValidator || config.enableOffenseCollection;
|
|
169
|
+
// A prover node may still be proving an epoch whose blocks have already finalized on L1. Its proof
|
|
170
|
+
// submission window spans (proofSubmissionEpochs + 1) epochs, so its tx pool must keep finalized txs
|
|
171
|
+
// for at least that long to re-fetch them for proving and failure upload. Operators may configure a
|
|
172
|
+
// larger margin but not a smaller one.
|
|
173
|
+
if (config.enableProverNode) {
|
|
174
|
+
const proofSubmissionEpochs = await rollupContract.getProofSubmissionEpochs();
|
|
175
|
+
const proverRetentionFloorSlots = (proofSubmissionEpochs + 1) * Number(epochDuration);
|
|
176
|
+
const configuredSlots = config.keepFinalizedTxsForSlots ?? 0;
|
|
177
|
+
if (proverRetentionFloorSlots > configuredSlots) {
|
|
178
|
+
log.warn(`Increasing keepFinalizedTxsForSlots from ${configuredSlots} to ${proverRetentionFloorSlots} ` + `(proof submission window of ${proofSubmissionEpochs + 1} epochs) to retain finalized txs for proving`);
|
|
179
|
+
config.keepFinalizedTxsForSlots = proverRetentionFloorSlots;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
169
182
|
// create the tx pool and the p2p client, which will need the l2 block source
|
|
170
183
|
const p2pClient = await createP2PClient(config, archiver, peerProofVerifier, worldStateSynchronizer, epochCache, feeProvider, packageVersion, dateProvider, telemetry, deps.p2pClientDeps, initialBlockHash);
|
|
171
184
|
started.push(p2pClient);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ARCHIVE_HEIGHT, type L1_TO_L2_MSG_TREE_HEIGHT, type NOTE_HASH_TREE_HEIGHT } from '@aztec/constants';
|
|
2
|
-
import {
|
|
2
|
+
import { type CheckpointNumber, type EpochNumber } from '@aztec/foundation/branded-types';
|
|
3
3
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
4
|
import { type Logger } from '@aztec/foundation/log';
|
|
5
5
|
import { MembershipWitness, type SiblingPath } from '@aztec/foundation/trees';
|
|
@@ -50,12 +50,16 @@ export declare class NodeWorldStateQueries {
|
|
|
50
50
|
getPublicDataWitness(referenceBlock: BlockParameter, leafSlot: Fr): Promise<PublicDataWitness | undefined>;
|
|
51
51
|
getPublicStorageAt(referenceBlock: BlockParameter, contract: AztecAddress, slot: Fr): Promise<Fr>;
|
|
52
52
|
/**
|
|
53
|
-
* Returns an instance of MerkleTreeOperations having first ensured the world state is
|
|
54
|
-
*
|
|
53
|
+
* Returns an instance of MerkleTreeOperations having first ensured the world state is synced to the requested
|
|
54
|
+
* block on the correct fork. Every query variant is resolved to a concrete (block number, block hash), which is
|
|
55
|
+
* threaded through both the sync and the snapshot read so a reorg that replaced the block at that height is
|
|
56
|
+
* detected rather than served silently. Transient failures — a prune landing between resolution and sync, or a
|
|
57
|
+
* fork flip caught at either the sync or the snapshot stage — are retried a few times, re-resolving the query
|
|
58
|
+
* against the updated chain each time; terminal failures — an unknown block hash at resolution, or a block whose
|
|
59
|
+
* history world state has pruned away — are thrown immediately.
|
|
60
|
+
* @param block - The block parameter (block number, block hash, or tag) at which to get the data.
|
|
55
61
|
* @returns An instance of a committed MerkleTreeOperations
|
|
56
62
|
*/
|
|
57
|
-
getWorldState(block: BlockParameter): Promise<import("@aztec/
|
|
58
|
-
/** Resolves any {@link BlockParameter} variant to a concrete block number. */
|
|
59
|
-
resolveBlockNumber(block: BlockParameter): Promise<BlockNumber>;
|
|
63
|
+
getWorldState(block: BlockParameter): Promise<import("@aztec/world-state").MerkleTreeReadOperations>;
|
|
60
64
|
}
|
|
61
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
65
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZV93b3JsZF9zdGF0ZV9xdWVyaWVzLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbW9kdWxlcy9ub2RlX3dvcmxkX3N0YXRlX3F1ZXJpZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGNBQWMsRUFBRSxLQUFLLHdCQUF3QixFQUFFLEtBQUsscUJBQXFCLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUM3RyxPQUFPLEVBQWUsS0FBSyxnQkFBZ0IsRUFBRSxLQUFLLFdBQVcsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRXZHLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNwRCxPQUFPLEVBQUUsS0FBSyxNQUFNLEVBQWdCLE1BQU0sdUJBQXVCLENBQUM7QUFFbEUsT0FBTyxFQUFFLGlCQUFpQixFQUFFLEtBQUssV0FBVyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDOUUsT0FBTyxLQUFLLEVBQUUsWUFBWSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFDaEUsT0FBTyxFQUNMLEtBQUssU0FBUyxFQUNkLEtBQUssY0FBYyxFQUNuQixLQUFLLFdBQVcsRUFDaEIsS0FBSyxhQUFhLEVBR25CLE1BQU0scUJBQXFCLENBQUM7QUFFN0IsT0FBTyxLQUFLLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUM5RSxPQUFPLEVBQWEsS0FBSyxtQkFBbUIsRUFBRSxLQUFLLHVCQUF1QixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDNUcsT0FBTyxFQUNMLFlBQVksRUFFWiwwQkFBMEIsRUFFMUIsaUJBQWlCLEVBQ2xCLE1BQU0scUJBQXFCLENBQUM7QUFDN0IsT0FBTyxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFXL0Msc0VBQXNFO0FBQ3RFLE1BQU0sV0FBVyx5QkFBeUI7SUFDeEMsc0JBQXNCLEVBQUUsc0JBQXNCLENBQUM7SUFDL0MsV0FBVyxF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
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node_world_state_queries.d.ts","sourceRoot":"","sources":["../../src/modules/node_world_state_queries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,wBAAwB,EAAE,KAAK,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC7G,OAAO,
|
|
1
|
+
{"version":3,"file":"node_world_state_queries.d.ts","sourceRoot":"","sources":["../../src/modules/node_world_state_queries.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,12 +2,16 @@ import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
|
2
2
|
import { chunkBy } from '@aztec/foundation/collection';
|
|
3
3
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
4
|
import { createLogger } from '@aztec/foundation/log';
|
|
5
|
+
import { sleep } from '@aztec/foundation/sleep';
|
|
5
6
|
import { MembershipWitness } from '@aztec/foundation/trees';
|
|
6
7
|
import { inspectBlockParameter } from '@aztec/stdlib/block';
|
|
7
8
|
import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
|
|
8
9
|
import { InboxLeaf } from '@aztec/stdlib/messaging';
|
|
9
10
|
import { MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
|
|
11
|
+
import { WorldStateSynchronizerError } from '@aztec/world-state';
|
|
10
12
|
import { normalizeBlockParameter } from './block_parameter.js';
|
|
13
|
+
/** Attempts at resolving a query and syncing world state to it before giving up (see {@link NodeWorldStateQueries.getWorldState}). */ const WORLD_STATE_SYNC_ATTEMPTS = 3;
|
|
14
|
+
/** Delay between world-state sync attempts. */ const WORLD_STATE_SYNC_RETRY_DELAY_MS = 100;
|
|
11
15
|
/**
|
|
12
16
|
* Serves the node's Merkle-tree and membership-witness queries against committed world-state at a
|
|
13
17
|
* requested block. Extracted from `AztecNodeService` so the block-resolution and reorg-aware sync logic
|
|
@@ -80,7 +84,7 @@ import { normalizeBlockParameter } from './block_parameter.js';
|
|
|
80
84
|
// The Noir circuit checks the archive membership proof against `anchor_block_header.last_archive.root`,
|
|
81
85
|
// which is the archive tree root BEFORE the anchor block was added (i.e. the state after block N-1).
|
|
82
86
|
// So we need the world state at block N-1, not block N, to produce a sibling path matching that root.
|
|
83
|
-
const referenceBlockNumber = await this
|
|
87
|
+
const referenceBlockNumber = await this.#resolveBlockNumber(referenceBlock);
|
|
84
88
|
if (referenceBlockNumber === BlockNumber.ZERO) {
|
|
85
89
|
// Block 0 (the initial block) has an empty archive, so no membership witness can exist.
|
|
86
90
|
return undefined;
|
|
@@ -186,72 +190,83 @@ import { normalizeBlockParameter } from './block_parameter.js';
|
|
|
186
190
|
return preimage.leaf.value;
|
|
187
191
|
}
|
|
188
192
|
/**
|
|
189
|
-
* Returns an instance of MerkleTreeOperations having first ensured the world state is
|
|
190
|
-
*
|
|
193
|
+
* Returns an instance of MerkleTreeOperations having first ensured the world state is synced to the requested
|
|
194
|
+
* block on the correct fork. Every query variant is resolved to a concrete (block number, block hash), which is
|
|
195
|
+
* threaded through both the sync and the snapshot read so a reorg that replaced the block at that height is
|
|
196
|
+
* detected rather than served silently. Transient failures — a prune landing between resolution and sync, or a
|
|
197
|
+
* fork flip caught at either the sync or the snapshot stage — are retried a few times, re-resolving the query
|
|
198
|
+
* against the updated chain each time; terminal failures — an unknown block hash at resolution, or a block whose
|
|
199
|
+
* history world state has pruned away — are thrown immediately.
|
|
200
|
+
* @param block - The block parameter (block number, block hash, or tag) at which to get the data.
|
|
191
201
|
* @returns An instance of a committed MerkleTreeOperations
|
|
192
202
|
*/ async getWorldState(block) {
|
|
193
203
|
const query = normalizeBlockParameter(block);
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
this.log.error(`Error getting world state: ${err}`);
|
|
204
|
+
for(let attempt = 1;; attempt++){
|
|
205
|
+
try {
|
|
206
|
+
return await this.#resolveWorldState(query);
|
|
207
|
+
} catch (err) {
|
|
208
|
+
if (attempt >= WORLD_STATE_SYNC_ATTEMPTS || !(err instanceof WorldStateSynchronizerError)) {
|
|
209
|
+
throw err;
|
|
210
|
+
}
|
|
211
|
+
this.log.verbose(`Retrying world state query after sync failure: ${err.message}`, {
|
|
212
|
+
attempt,
|
|
213
|
+
block: inspectBlockParameter(block)
|
|
214
|
+
});
|
|
215
|
+
await sleep(WORLD_STATE_SYNC_RETRY_DELAY_MS);
|
|
216
|
+
}
|
|
208
217
|
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Resolves `query` to a concrete (block number, block hash), syncs world state to that exact fork, and returns
|
|
221
|
+
* the committed db (for `proposed` queries) or the fork-verified snapshot at the resolved block.
|
|
222
|
+
*/ async #resolveWorldState(query) {
|
|
223
|
+
// User requests 'latest on the current fork', so the committed db is returned unverified
|
|
209
224
|
if ('tag' in query && query.tag === 'proposed') {
|
|
210
|
-
this.log.debug(`Using committed db for latest block
|
|
225
|
+
this.log.debug(`Using committed db for latest block`);
|
|
226
|
+
await this.worldStateSynchronizer.syncImmediate();
|
|
211
227
|
return this.worldStateSynchronizer.getCommitted();
|
|
212
228
|
}
|
|
213
|
-
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
this.
|
|
219
|
-
|
|
220
|
-
//
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
throw new Error(`Block hash ${requestedHash.toString()} not found in world state at block number ${blockNumber} (world state has ${blockHash?.toString() ?? 'no hash'} at that index, genesis header hash is ${this.blockSource.getGenesisBlockHash().toString()}). If the node API has been queried with anchor block hash possibly a reorg has occurred.`);
|
|
229
|
-
}
|
|
229
|
+
// Resolve the query against the block source BEFORE syncing to a concrete (number, hash), and drive the sync to
|
|
230
|
+
// that exact fork. Resolving after the sync races the block source: the resolved tip can advance past what world
|
|
231
|
+
// state synced while the sync is in flight. Passing the hash makes the sync reorg-aware — it barriers until the
|
|
232
|
+
// archive-tree commit for that block has landed and verifies it matches the requested fork, throwing otherwise.
|
|
233
|
+
const { blockNumber, blockHash } = await this.#resolveBlockNumberAndHash(query);
|
|
234
|
+
const blockSyncedTo = await this.worldStateSynchronizer.syncImmediate(blockNumber, blockHash);
|
|
235
|
+
// The fork could flip between it returning and the snapshot being read, so getVerifiedSnapshot pins the
|
|
236
|
+
// returned view to the resolved fork (re-resolved by the retry loop on mismatch).
|
|
237
|
+
this.log.debug(`Using verified snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
|
|
238
|
+
return await this.worldStateSynchronizer.getVerifiedSnapshot(blockNumber, blockHash);
|
|
239
|
+
}
|
|
240
|
+
/** Resolves any {@link BlockParameter} variant to its concrete `(blockNumber, blockHash)` via the block source. */ async #resolveBlockNumberAndHash(query) {
|
|
241
|
+
const blockData = await this.blockSource.getBlockData(query);
|
|
242
|
+
if (blockData === undefined) {
|
|
243
|
+
this.#throwOnUndefinedBlockData(query);
|
|
230
244
|
}
|
|
231
|
-
return
|
|
245
|
+
return {
|
|
246
|
+
blockNumber: blockData.header.getBlockNumber(),
|
|
247
|
+
blockHash: blockData.blockHash
|
|
248
|
+
};
|
|
232
249
|
}
|
|
233
|
-
/** Resolves any {@link BlockParameter} variant to a concrete block number. */ async resolveBlockNumber(block) {
|
|
234
|
-
const
|
|
235
|
-
const blockNumber = await this.blockSource.getBlockNumber(
|
|
250
|
+
/** Resolves any {@link BlockParameter} variant to a concrete block number. */ async #resolveBlockNumber(block) {
|
|
251
|
+
const blockQuery = normalizeBlockParameter(block);
|
|
252
|
+
const blockNumber = await this.blockSource.getBlockNumber(blockQuery);
|
|
236
253
|
if (blockNumber === undefined) {
|
|
237
|
-
|
|
238
|
-
throw new Error(`Block hash ${query.hash.toString()} not found when querying world state. If the node API has been queried with anchor block hash possibly a reorg has occurred.`);
|
|
239
|
-
}
|
|
240
|
-
if ('archive' in query) {
|
|
241
|
-
throw new Error(`Block with archive ${query.archive.toString()} not found.`);
|
|
242
|
-
}
|
|
243
|
-
throw new Error(`Block not found for ${inspectBlockParameter(block)}.`);
|
|
254
|
+
this.#throwOnUndefinedBlockData(blockQuery);
|
|
244
255
|
}
|
|
245
256
|
return blockNumber;
|
|
246
257
|
}
|
|
247
258
|
/**
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
259
|
+
* Hash and archive misses are terminal (an unknown block, likely a reorg); tag and number misses are transient —
|
|
260
|
+
* the block may have been pruned between the tag->number resolution and this data read, or may simply not have
|
|
261
|
+
* arrived yet — and surface as {@link WorldStateSynchronizerError} so the retry loop re-resolves against the
|
|
262
|
+
* current chain.
|
|
263
|
+
*/ #throwOnUndefinedBlockData(query) {
|
|
264
|
+
if ('hash' in query) {
|
|
265
|
+
throw new Error(`Block hash ${query.hash.toString()} not found when resolving query. If the node API has been queried with anchor block hash possibly a reorg has occurred.`);
|
|
266
|
+
}
|
|
267
|
+
if ('archive' in query) {
|
|
268
|
+
throw new Error(`Block with archive ${query.archive.toString()} not found when resolving query.`);
|
|
269
|
+
}
|
|
270
|
+
throw new WorldStateSynchronizerError(`Block not found for ${inspectBlockParameter(query)} when resolving query.`);
|
|
256
271
|
}
|
|
257
272
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/aztec-node",
|
|
3
|
-
"version": "5.0.0-nightly.
|
|
3
|
+
"version": "5.0.0-nightly.20260708",
|
|
4
4
|
"main": "dest/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -65,33 +65,33 @@
|
|
|
65
65
|
]
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@aztec/archiver": "5.0.0-nightly.
|
|
69
|
-
"@aztec/bb-prover": "5.0.0-nightly.
|
|
70
|
-
"@aztec/bb.js": "5.0.0-nightly.
|
|
71
|
-
"@aztec/blob-client": "5.0.0-nightly.
|
|
72
|
-
"@aztec/blob-lib": "5.0.0-nightly.
|
|
73
|
-
"@aztec/constants": "5.0.0-nightly.
|
|
74
|
-
"@aztec/epoch-cache": "5.0.0-nightly.
|
|
75
|
-
"@aztec/ethereum": "5.0.0-nightly.
|
|
76
|
-
"@aztec/foundation": "5.0.0-nightly.
|
|
77
|
-
"@aztec/kv-store": "5.0.0-nightly.
|
|
78
|
-
"@aztec/l1-artifacts": "5.0.0-nightly.
|
|
79
|
-
"@aztec/node-keystore": "5.0.0-nightly.
|
|
80
|
-
"@aztec/node-lib": "5.0.0-nightly.
|
|
81
|
-
"@aztec/noir-protocol-circuits-types": "5.0.0-nightly.
|
|
82
|
-
"@aztec/p2p": "5.0.0-nightly.
|
|
83
|
-
"@aztec/protocol-contracts": "5.0.0-nightly.
|
|
84
|
-
"@aztec/prover-client": "5.0.0-nightly.
|
|
85
|
-
"@aztec/prover-node": "5.0.0-nightly.
|
|
86
|
-
"@aztec/sequencer-client": "5.0.0-nightly.
|
|
87
|
-
"@aztec/simulator": "5.0.0-nightly.
|
|
88
|
-
"@aztec/slasher": "5.0.0-nightly.
|
|
89
|
-
"@aztec/standard-contracts": "5.0.0-nightly.
|
|
90
|
-
"@aztec/stdlib": "5.0.0-nightly.
|
|
91
|
-
"@aztec/telemetry-client": "5.0.0-nightly.
|
|
92
|
-
"@aztec/validator-client": "5.0.0-nightly.
|
|
93
|
-
"@aztec/validator-ha-signer": "5.0.0-nightly.
|
|
94
|
-
"@aztec/world-state": "5.0.0-nightly.
|
|
68
|
+
"@aztec/archiver": "5.0.0-nightly.20260708",
|
|
69
|
+
"@aztec/bb-prover": "5.0.0-nightly.20260708",
|
|
70
|
+
"@aztec/bb.js": "5.0.0-nightly.20260708",
|
|
71
|
+
"@aztec/blob-client": "5.0.0-nightly.20260708",
|
|
72
|
+
"@aztec/blob-lib": "5.0.0-nightly.20260708",
|
|
73
|
+
"@aztec/constants": "5.0.0-nightly.20260708",
|
|
74
|
+
"@aztec/epoch-cache": "5.0.0-nightly.20260708",
|
|
75
|
+
"@aztec/ethereum": "5.0.0-nightly.20260708",
|
|
76
|
+
"@aztec/foundation": "5.0.0-nightly.20260708",
|
|
77
|
+
"@aztec/kv-store": "5.0.0-nightly.20260708",
|
|
78
|
+
"@aztec/l1-artifacts": "5.0.0-nightly.20260708",
|
|
79
|
+
"@aztec/node-keystore": "5.0.0-nightly.20260708",
|
|
80
|
+
"@aztec/node-lib": "5.0.0-nightly.20260708",
|
|
81
|
+
"@aztec/noir-protocol-circuits-types": "5.0.0-nightly.20260708",
|
|
82
|
+
"@aztec/p2p": "5.0.0-nightly.20260708",
|
|
83
|
+
"@aztec/protocol-contracts": "5.0.0-nightly.20260708",
|
|
84
|
+
"@aztec/prover-client": "5.0.0-nightly.20260708",
|
|
85
|
+
"@aztec/prover-node": "5.0.0-nightly.20260708",
|
|
86
|
+
"@aztec/sequencer-client": "5.0.0-nightly.20260708",
|
|
87
|
+
"@aztec/simulator": "5.0.0-nightly.20260708",
|
|
88
|
+
"@aztec/slasher": "5.0.0-nightly.20260708",
|
|
89
|
+
"@aztec/standard-contracts": "5.0.0-nightly.20260708",
|
|
90
|
+
"@aztec/stdlib": "5.0.0-nightly.20260708",
|
|
91
|
+
"@aztec/telemetry-client": "5.0.0-nightly.20260708",
|
|
92
|
+
"@aztec/validator-client": "5.0.0-nightly.20260708",
|
|
93
|
+
"@aztec/validator-ha-signer": "5.0.0-nightly.20260708",
|
|
94
|
+
"@aztec/world-state": "5.0.0-nightly.20260708",
|
|
95
95
|
"koa": "^2.16.1",
|
|
96
96
|
"koa-router": "^13.1.1",
|
|
97
97
|
"tslib": "^2.4.0",
|
package/src/factory.ts
CHANGED
|
@@ -245,6 +245,23 @@ export async function createAztecNodeService(
|
|
|
245
245
|
|
|
246
246
|
const collectOffenses = !config.disableValidator || config.enableOffenseCollection;
|
|
247
247
|
|
|
248
|
+
// A prover node may still be proving an epoch whose blocks have already finalized on L1. Its proof
|
|
249
|
+
// submission window spans (proofSubmissionEpochs + 1) epochs, so its tx pool must keep finalized txs
|
|
250
|
+
// for at least that long to re-fetch them for proving and failure upload. Operators may configure a
|
|
251
|
+
// larger margin but not a smaller one.
|
|
252
|
+
if (config.enableProverNode) {
|
|
253
|
+
const proofSubmissionEpochs = await rollupContract.getProofSubmissionEpochs();
|
|
254
|
+
const proverRetentionFloorSlots = (proofSubmissionEpochs + 1) * Number(epochDuration);
|
|
255
|
+
const configuredSlots = config.keepFinalizedTxsForSlots ?? 0;
|
|
256
|
+
if (proverRetentionFloorSlots > configuredSlots) {
|
|
257
|
+
log.warn(
|
|
258
|
+
`Increasing keepFinalizedTxsForSlots from ${configuredSlots} to ${proverRetentionFloorSlots} ` +
|
|
259
|
+
`(proof submission window of ${proofSubmissionEpochs + 1} epochs) to retain finalized txs for proving`,
|
|
260
|
+
);
|
|
261
|
+
config.keepFinalizedTxsForSlots = proverRetentionFloorSlots;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
248
265
|
// create the tx pool and the p2p client, which will need the l2 block source
|
|
249
266
|
const p2pClient = await createP2PClient(
|
|
250
267
|
config,
|
|
@@ -3,6 +3,7 @@ import { BlockNumber, type CheckpointNumber, type EpochNumber } from '@aztec/fou
|
|
|
3
3
|
import { chunkBy } from '@aztec/foundation/collection';
|
|
4
4
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
5
5
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
6
|
+
import { sleep } from '@aztec/foundation/sleep';
|
|
6
7
|
import { MembershipWitness, type SiblingPath } from '@aztec/foundation/trees';
|
|
7
8
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
8
9
|
import {
|
|
@@ -10,6 +11,7 @@ import {
|
|
|
10
11
|
type BlockParameter,
|
|
11
12
|
type DataInBlock,
|
|
12
13
|
type L2BlockSource,
|
|
14
|
+
type NormalizedBlockParameter,
|
|
13
15
|
inspectBlockParameter,
|
|
14
16
|
} from '@aztec/stdlib/block';
|
|
15
17
|
import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
|
|
@@ -23,9 +25,16 @@ import {
|
|
|
23
25
|
PublicDataWitness,
|
|
24
26
|
} from '@aztec/stdlib/trees';
|
|
25
27
|
import type { TxHash } from '@aztec/stdlib/tx';
|
|
28
|
+
import { WorldStateSynchronizerError } from '@aztec/world-state';
|
|
26
29
|
|
|
27
30
|
import { normalizeBlockParameter } from './block_parameter.js';
|
|
28
31
|
|
|
32
|
+
/** Attempts at resolving a query and syncing world state to it before giving up (see {@link NodeWorldStateQueries.getWorldState}). */
|
|
33
|
+
const WORLD_STATE_SYNC_ATTEMPTS = 3;
|
|
34
|
+
|
|
35
|
+
/** Delay between world-state sync attempts. */
|
|
36
|
+
const WORLD_STATE_SYNC_RETRY_DELAY_MS = 100;
|
|
37
|
+
|
|
29
38
|
/** Dependencies required to build a {@link NodeWorldStateQueries}. */
|
|
30
39
|
export interface NodeWorldStateQueriesDeps {
|
|
31
40
|
worldStateSynchronizer: WorldStateSynchronizer;
|
|
@@ -128,7 +137,7 @@ export class NodeWorldStateQueries {
|
|
|
128
137
|
// The Noir circuit checks the archive membership proof against `anchor_block_header.last_archive.root`,
|
|
129
138
|
// which is the archive tree root BEFORE the anchor block was added (i.e. the state after block N-1).
|
|
130
139
|
// So we need the world state at block N-1, not block N, to produce a sibling path matching that root.
|
|
131
|
-
const referenceBlockNumber = await this
|
|
140
|
+
const referenceBlockNumber = await this.#resolveBlockNumber(referenceBlock);
|
|
132
141
|
if (referenceBlockNumber === BlockNumber.ZERO) {
|
|
133
142
|
// Block 0 (the initial block) has an empty archive, so no membership witness can exist.
|
|
134
143
|
return undefined;
|
|
@@ -270,91 +279,96 @@ export class NodeWorldStateQueries {
|
|
|
270
279
|
}
|
|
271
280
|
|
|
272
281
|
/**
|
|
273
|
-
* Returns an instance of MerkleTreeOperations having first ensured the world state is
|
|
274
|
-
*
|
|
282
|
+
* Returns an instance of MerkleTreeOperations having first ensured the world state is synced to the requested
|
|
283
|
+
* block on the correct fork. Every query variant is resolved to a concrete (block number, block hash), which is
|
|
284
|
+
* threaded through both the sync and the snapshot read so a reorg that replaced the block at that height is
|
|
285
|
+
* detected rather than served silently. Transient failures — a prune landing between resolution and sync, or a
|
|
286
|
+
* fork flip caught at either the sync or the snapshot stage — are retried a few times, re-resolving the query
|
|
287
|
+
* against the updated chain each time; terminal failures — an unknown block hash at resolution, or a block whose
|
|
288
|
+
* history world state has pruned away — are thrown immediately.
|
|
289
|
+
* @param block - The block parameter (block number, block hash, or tag) at which to get the data.
|
|
275
290
|
* @returns An instance of a committed MerkleTreeOperations
|
|
276
291
|
*/
|
|
277
292
|
public async getWorldState(block: BlockParameter) {
|
|
278
293
|
const query = normalizeBlockParameter(block);
|
|
279
294
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
} catch (err) {
|
|
294
|
-
this.log.error(`Error getting world state: ${err}`);
|
|
295
|
+
for (let attempt = 1; ; attempt++) {
|
|
296
|
+
try {
|
|
297
|
+
return await this.#resolveWorldState(query);
|
|
298
|
+
} catch (err) {
|
|
299
|
+
if (attempt >= WORLD_STATE_SYNC_ATTEMPTS || !(err instanceof WorldStateSynchronizerError)) {
|
|
300
|
+
throw err;
|
|
301
|
+
}
|
|
302
|
+
this.log.verbose(`Retrying world state query after sync failure: ${err.message}`, {
|
|
303
|
+
attempt,
|
|
304
|
+
block: inspectBlockParameter(block),
|
|
305
|
+
});
|
|
306
|
+
await sleep(WORLD_STATE_SYNC_RETRY_DELAY_MS);
|
|
307
|
+
}
|
|
295
308
|
}
|
|
309
|
+
}
|
|
296
310
|
|
|
311
|
+
/**
|
|
312
|
+
* Resolves `query` to a concrete (block number, block hash), syncs world state to that exact fork, and returns
|
|
313
|
+
* the committed db (for `proposed` queries) or the fork-verified snapshot at the resolved block.
|
|
314
|
+
*/
|
|
315
|
+
async #resolveWorldState(query: NormalizedBlockParameter) {
|
|
316
|
+
// User requests 'latest on the current fork', so the committed db is returned unverified
|
|
297
317
|
if ('tag' in query && query.tag === 'proposed') {
|
|
298
|
-
this.log.debug(`Using committed db for latest block
|
|
318
|
+
this.log.debug(`Using committed db for latest block`);
|
|
319
|
+
await this.worldStateSynchronizer.syncImmediate();
|
|
299
320
|
return this.worldStateSynchronizer.getCommitted();
|
|
300
321
|
}
|
|
301
322
|
|
|
302
|
-
|
|
323
|
+
// Resolve the query against the block source BEFORE syncing to a concrete (number, hash), and drive the sync to
|
|
324
|
+
// that exact fork. Resolving after the sync races the block source: the resolved tip can advance past what world
|
|
325
|
+
// state synced while the sync is in flight. Passing the hash makes the sync reorg-aware — it barriers until the
|
|
326
|
+
// archive-tree commit for that block has landed and verifies it matches the requested fork, throwing otherwise.
|
|
327
|
+
const { blockNumber, blockHash } = await this.#resolveBlockNumberAndHash(query);
|
|
328
|
+
const blockSyncedTo = await this.worldStateSynchronizer.syncImmediate(blockNumber, blockHash);
|
|
329
|
+
|
|
330
|
+
// The fork could flip between it returning and the snapshot being read, so getVerifiedSnapshot pins the
|
|
331
|
+
// returned view to the resolved fork (re-resolved by the retry loop on mismatch).
|
|
332
|
+
this.log.debug(`Using verified snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
|
|
333
|
+
return await this.worldStateSynchronizer.getVerifiedSnapshot(blockNumber, blockHash);
|
|
334
|
+
}
|
|
303
335
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
336
|
+
/** Resolves any {@link BlockParameter} variant to its concrete `(blockNumber, blockHash)` via the block source. */
|
|
337
|
+
async #resolveBlockNumberAndHash(
|
|
338
|
+
query: NormalizedBlockParameter,
|
|
339
|
+
): Promise<{ blockNumber: BlockNumber; blockHash: BlockHash }> {
|
|
340
|
+
const blockData = await this.blockSource.getBlockData(query);
|
|
341
|
+
if (blockData === undefined) {
|
|
342
|
+
this.#throwOnUndefinedBlockData(query);
|
|
309
343
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const snapshot = this.worldStateSynchronizer.getSnapshot(blockNumber);
|
|
313
|
-
|
|
314
|
-
// Double-check world-state synced to the same block hash as was requested.
|
|
315
|
-
// Block 0 is skipped: the snapshot returned by `getSnapshot(0)` is the *pre*-genesis archive
|
|
316
|
-
// (size 0), so leaf 0 is not yet inserted from that snapshot's view even though block 0's hash
|
|
317
|
-
// does live at archive index 0 in the committed tree. The genesis hash is already validated by
|
|
318
|
-
// the archiver when it resolves the hash query to block number 0.
|
|
319
|
-
if (requestedHash !== undefined && blockNumber !== BlockNumber.ZERO) {
|
|
320
|
-
const blockHash = await snapshot.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
321
|
-
if (!blockHash || !requestedHash.equals(blockHash)) {
|
|
322
|
-
throw new Error(
|
|
323
|
-
`Block hash ${requestedHash.toString()} not found in world state at block number ${blockNumber} (world state has ${blockHash?.toString() ?? 'no hash'} at that index, genesis header hash is ${this.blockSource.getGenesisBlockHash().toString()}). If the node API has been queried with anchor block hash possibly a reorg has occurred.`,
|
|
324
|
-
);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
return snapshot;
|
|
344
|
+
return { blockNumber: blockData.header.getBlockNumber(), blockHash: blockData.blockHash };
|
|
329
345
|
}
|
|
330
346
|
|
|
331
347
|
/** Resolves any {@link BlockParameter} variant to a concrete block number. */
|
|
332
|
-
|
|
333
|
-
const
|
|
334
|
-
const blockNumber = await this.blockSource.getBlockNumber(
|
|
348
|
+
async #resolveBlockNumber(block: BlockParameter): Promise<BlockNumber> {
|
|
349
|
+
const blockQuery = normalizeBlockParameter(block);
|
|
350
|
+
const blockNumber = await this.blockSource.getBlockNumber(blockQuery);
|
|
335
351
|
if (blockNumber === undefined) {
|
|
336
|
-
|
|
337
|
-
throw new Error(
|
|
338
|
-
`Block hash ${query.hash.toString()} not found when querying world state. If the node API has been queried with anchor block hash possibly a reorg has occurred.`,
|
|
339
|
-
);
|
|
340
|
-
}
|
|
341
|
-
if ('archive' in query) {
|
|
342
|
-
throw new Error(`Block with archive ${query.archive.toString()} not found.`);
|
|
343
|
-
}
|
|
344
|
-
throw new Error(`Block not found for ${inspectBlockParameter(block)}.`);
|
|
352
|
+
this.#throwOnUndefinedBlockData(blockQuery);
|
|
345
353
|
}
|
|
346
354
|
return blockNumber;
|
|
347
355
|
}
|
|
348
356
|
|
|
349
357
|
/**
|
|
350
|
-
*
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
*
|
|
354
|
-
* @returns A promise that fulfils once the world state is synced
|
|
358
|
+
* Hash and archive misses are terminal (an unknown block, likely a reorg); tag and number misses are transient —
|
|
359
|
+
* the block may have been pruned between the tag->number resolution and this data read, or may simply not have
|
|
360
|
+
* arrived yet — and surface as {@link WorldStateSynchronizerError} so the retry loop re-resolves against the
|
|
361
|
+
* current chain.
|
|
355
362
|
*/
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
363
|
+
#throwOnUndefinedBlockData(query: NormalizedBlockParameter): never {
|
|
364
|
+
if ('hash' in query) {
|
|
365
|
+
throw new Error(
|
|
366
|
+
`Block hash ${query.hash.toString()} not found when resolving query. If the node API has been queried with anchor block hash possibly a reorg has occurred.`,
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
if ('archive' in query) {
|
|
370
|
+
throw new Error(`Block with archive ${query.archive.toString()} not found when resolving query.`);
|
|
371
|
+
}
|
|
372
|
+
throw new WorldStateSynchronizerError(`Block not found for ${inspectBlockParameter(query)} when resolving query.`);
|
|
359
373
|
}
|
|
360
374
|
}
|