@aztec/world-state 4.1.2 → 4.2.0-aztecnr-rc.2
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/synchronizer/server_world_state_synchronizer.d.ts +4 -4
- package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
- package/dest/synchronizer/server_world_state_synchronizer.js +28 -5
- package/package.json +10 -10
- package/src/synchronizer/server_world_state_synchronizer.ts +36 -8
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import { type Logger } from '@aztec/foundation/log';
|
|
3
|
-
import { type L2BlockSource, L2BlockStream, type L2BlockStreamEvent, type L2BlockStreamEventHandler, type L2BlockStreamLocalDataProvider, type L2Tips } from '@aztec/stdlib/block';
|
|
3
|
+
import { type BlockHash, type L2BlockSource, L2BlockStream, type L2BlockStreamEvent, type L2BlockStreamEventHandler, type L2BlockStreamLocalDataProvider, type L2Tips } from '@aztec/stdlib/block';
|
|
4
4
|
import { type WorldStateSynchronizer, type WorldStateSynchronizerStatus } from '@aztec/stdlib/interfaces/server';
|
|
5
5
|
import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
6
6
|
import type { SnapshotDataKeys } from '@aztec/stdlib/snapshots';
|
|
@@ -45,10 +45,10 @@ export declare class ServerWorldStateSynchronizer implements WorldStateSynchroni
|
|
|
45
45
|
/**
|
|
46
46
|
* Forces an immediate sync.
|
|
47
47
|
* @param targetBlockNumber - The target block number that we must sync to. Will download unproven blocks if needed to reach it.
|
|
48
|
-
* @param
|
|
48
|
+
* @param blockHash - If provided, verifies the block at targetBlockNumber matches this hash. On mismatch, triggers a resync (reorg detection).
|
|
49
49
|
* @returns A promise that resolves with the block number the world state was synced to
|
|
50
50
|
*/
|
|
51
|
-
syncImmediate(targetBlockNumber?: BlockNumber,
|
|
51
|
+
syncImmediate(targetBlockNumber?: BlockNumber, blockHash?: BlockHash): Promise<BlockNumber>;
|
|
52
52
|
/** Returns the L2 block hash for a given number. Used by the L2BlockStream for detecting reorgs. */
|
|
53
53
|
getL2BlockHash(number: BlockNumber): Promise<string | undefined>;
|
|
54
54
|
/** Returns the latest L2 block number for each tip of the chain (latest, proven, finalized). */
|
|
@@ -66,4 +66,4 @@ export declare class ServerWorldStateSynchronizer implements WorldStateSynchroni
|
|
|
66
66
|
*/
|
|
67
67
|
private setCurrentState;
|
|
68
68
|
}
|
|
69
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
69
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmVyX3dvcmxkX3N0YXRlX3N5bmNocm9uaXplci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N5bmNocm9uaXplci9zZXJ2ZXJfd29ybGRfc3RhdGVfc3luY2hyb25pemVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sRUFBRSxXQUFXLEVBQW9CLE1BQU0saUNBQWlDLENBQUM7QUFFaEYsT0FBTyxFQUFFLEtBQUssTUFBTSxFQUFnQixNQUFNLHVCQUF1QixDQUFDO0FBR2xFLE9BQU8sRUFDTCxLQUFLLFNBQVMsRUFJZCxLQUFLLGFBQWEsRUFDbEIsYUFBYSxFQUNiLEtBQUssa0JBQWtCLEVBQ3ZCLEtBQUsseUJBQXlCLEVBQzlCLEtBQUssOEJBQThCLEVBQ25DLEtBQUssTUFBTSxFQUNaLE1BQU0scUJBQXFCLENBQUM7QUFDN0IsT0FBTyxFQUdMLEtBQUssc0JBQXNCLEVBQzNCLEtBQUssNEJBQTRCLEVBQ2xDLE1BQU0saUNBQWlDLENBQUM7QUFDekMsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNuRSxPQUFPLEtBQUssRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRWhFLE9BQU8sRUFBZ0IsS0FBSyx3QkFBd0IsRUFBRSxLQUFLLHlCQUF5QixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFHbEgsT0FBTyxFQUFFLHlCQUF5QixFQUFFLE1BQU0sdUNBQXVDLENBQUM7QUFFbEYsT0FBTyxLQUFLLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxxQ0FBcUMsQ0FBQztBQUNuRixPQUFPLEtBQUssRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUdwRCxZQUFZLEVBQUUsZ0JBQWdCLEVBQUUsQ0FBQztBQUVqQzs7OztHQUlHO0FBQ0gscUJBQWEsNEJBQ1gsWUFBVyxzQkFBc0IsRUFBRSw4QkFBOEIsRUFBRSx5QkFBeUI7SUFnQjFGLE9BQU8sQ0FBQyxRQUFRLENBQUMsWUFBWTtJQUM3QixPQUFPLENBQUMsUUFBUSxDQUFDLGFBQWE7SUFDOUIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxNQUFNO0lBQ3ZCLE9BQU8sQ0FBQyxlQUFlO0lBQ3ZCLE9BQU8sQ0FBQyxRQUFRLENBQUMsR0FBRztJQWxCdEIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxtQkFBbUIsQ0FBMkI7SUFFL0QsT0FBTyxDQUFDLHdCQUF3QixDQUFvQjtJQUNwRCxPQUFPLENBQUMsYUFBYSxDQUFxQjtJQUMxQyxPQUFPLENBQUMsWUFBWSxDQUF1RDtJQUUzRSxPQUFPLENBQUMsV0FBVyxDQUFnQztJQUNuRCxTQUFTLENBQUMsV0FBVyxFQUFFLGFBQWEsR0FBRyxTQUFTLENBQUM7SUFJakQsT0FBTyxDQUFDLGlCQUFpQixDQUEwQjtJQUVuRCxZQUNtQixZQUFZLEVBQUUsdUJBQXVCLEVBQ3JDLGFBQWEsRUFBRSxhQUFhLEdBQUcsbUJBQW1CLEVBQ2xELE1BQU0sRUFBRSxnQkFBZ0IsRUFDakMsZUFBZSw0QkFBc0QsRUFDNUQsR0FBRyxHQUFFLE1BQW9DLEVBUzNEO0lBRU0sWUFBWSxJQUFJLHdCQUF3QixDQUU5QztJQUVNLFdBQVcsQ0FBQyxXQUFXLEVBQUUsV0FBVyxHQUFHLHdCQUF3QixDQUVyRTtJQUVNLElBQUksQ0FBQyxXQUFXLENBQUMsRUFBRSxXQUFXLEVBQUUsSUFBSSxDQUFDLEVBQUU7UUFBRSxZQUFZLENBQUMsRUFBRSxNQUFNLENBQUE7S0FBRSxHQUFHLE9BQU8sQ0FBQyx5QkFBeUIsQ0FBQyxDQUUzRztJQUVNLFFBQVEsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLE9BQU8sQ0FBQyxFQUFFLE9BQU8sR0FBRyxPQUFPLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsRUFBRSxVQUFVLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUVsSDtJQUVNLEtBQUssSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBRTVCO0lBRVksS0FBSyxtRkE0QmpCO0lBRUQsU0FBUyxDQUFDLGlCQUFpQixJQUFJLGFBQWEsQ0FPM0M7SUFFWSxJQUFJLGtCQU9oQjtJQUVZLE1BQU0sSUFBSSxPQUFPLENBQUMsNEJBQTRCLENBQUMsQ0FhM0Q7SUFFWSxvQkFBb0IseUJBRWhDO0lBRVksUUFBUSxrQkFJcEI7SUFFTSxVQUFVLFNBT2hCO0lBRUQ7Ozs7O09BS0c7SUFDVSxhQUFhLENBQUMsaUJBQWlCLENBQUMsRUFBRSxXQUFXLEVBQUUsU0FBUyxDQUFDLEVBQUUsU0FBUyxHQUFHLE9BQU8sQ0FBQyxXQUFXLENBQUMsQ0EyRXZHO0lBRUQsb0dBQW9HO0lBQ3ZGLGNBQWMsQ0FBQyxNQUFNLEVBQUUsV0FBVyxHQUFHLE9BQU8sQ0FBQyxNQUFNLEdBQUcsU0FBUyxDQUFDLENBSzVFO0lBRUQsZ0dBQWdHO0lBQ25GLFNBQVMsSUFBSSxPQUFPLENBQUMsTUFBTSxDQUFDLENBbUN4QztJQUVELG9EQUFvRDtJQUN2QyxzQkFBc0IsQ0FBQyxLQUFLLEVBQUUsa0JBQWtCLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQWU1RTtZQU9hLGNBQWM7WUF5Q2QsYUFBYTtZQWtCYixvQkFBb0I7SUErRGxDLE9BQU8sQ0FBQyxpQkFBaUI7WUFNWCxpQkFBaUI7SUFPL0I7OztPQUdHO0lBQ0gsT0FBTyxDQUFDLGVBQWU7Q0FJeEIifQ==
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server_world_state_synchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/server_world_state_synchronizer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAoB,MAAM,iCAAiC,CAAC;AAEhF,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAGlE,OAAO,
|
|
1
|
+
{"version":3,"file":"server_world_state_synchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/server_world_state_synchronizer.ts"],"names":[],"mappings":"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"}
|
|
@@ -130,9 +130,9 @@ import { WorldStateSynchronizerError } from './errors.js';
|
|
|
130
130
|
/**
|
|
131
131
|
* Forces an immediate sync.
|
|
132
132
|
* @param targetBlockNumber - The target block number that we must sync to. Will download unproven blocks if needed to reach it.
|
|
133
|
-
* @param
|
|
133
|
+
* @param blockHash - If provided, verifies the block at targetBlockNumber matches this hash. On mismatch, triggers a resync (reorg detection).
|
|
134
134
|
* @returns A promise that resolves with the block number the world state was synced to
|
|
135
|
-
*/ async syncImmediate(targetBlockNumber,
|
|
135
|
+
*/ async syncImmediate(targetBlockNumber, blockHash) {
|
|
136
136
|
if (this.currentState !== WorldStateRunningState.RUNNING) {
|
|
137
137
|
throw new Error(`World State is not running. Unable to perform sync.`);
|
|
138
138
|
}
|
|
@@ -142,7 +142,16 @@ import { WorldStateSynchronizerError } from './errors.js';
|
|
|
142
142
|
// If we have been given a block number to sync to and we have reached that number then return
|
|
143
143
|
const currentBlockNumber = await this.getLatestBlockNumber();
|
|
144
144
|
if (targetBlockNumber !== undefined && targetBlockNumber <= currentBlockNumber) {
|
|
145
|
-
|
|
145
|
+
if (blockHash === undefined) {
|
|
146
|
+
return currentBlockNumber;
|
|
147
|
+
}
|
|
148
|
+
// If a block hash was provided, verify we're on the expected fork
|
|
149
|
+
const currentHash = await this.getL2BlockHash(targetBlockNumber);
|
|
150
|
+
if (currentHash === blockHash.toString()) {
|
|
151
|
+
return currentBlockNumber;
|
|
152
|
+
}
|
|
153
|
+
// Hash mismatch: a reorg may have occurred, fall through to trigger sync
|
|
154
|
+
this.log.debug(`World state block hash mismatch at ${targetBlockNumber} (expected ${blockHash}, got ${currentHash}). Triggering resync.`);
|
|
146
155
|
}
|
|
147
156
|
this.log.debug(`World State at ${currentBlockNumber} told to sync to ${targetBlockNumber ?? 'latest'}`);
|
|
148
157
|
// If the archiver is behind the target block, force an archiver sync
|
|
@@ -157,7 +166,7 @@ import { WorldStateSynchronizerError } from './errors.js';
|
|
|
157
166
|
await this.blockStream.sync();
|
|
158
167
|
// If we have been given a block number to sync to and we have not reached that number then fail
|
|
159
168
|
const updatedBlockNumber = await this.getLatestBlockNumber();
|
|
160
|
-
if (
|
|
169
|
+
if (targetBlockNumber !== undefined && targetBlockNumber > updatedBlockNumber) {
|
|
161
170
|
throw new WorldStateSynchronizerError(`Unable to sync to block number ${targetBlockNumber} (last synced is ${updatedBlockNumber})`, {
|
|
162
171
|
cause: {
|
|
163
172
|
reason: 'block_not_available',
|
|
@@ -167,6 +176,20 @@ import { WorldStateSynchronizerError } from './errors.js';
|
|
|
167
176
|
}
|
|
168
177
|
});
|
|
169
178
|
}
|
|
179
|
+
// If a block hash was provided, verify we're on the expected fork after syncing, throw otherwise
|
|
180
|
+
if (blockHash !== undefined && targetBlockNumber !== undefined) {
|
|
181
|
+
const updatedHash = await this.getL2BlockHash(targetBlockNumber);
|
|
182
|
+
if (updatedHash !== blockHash.toString()) {
|
|
183
|
+
throw new WorldStateSynchronizerError(`Block hash mismatch at block ${targetBlockNumber} (expected ${blockHash} but got ${updatedHash})`, {
|
|
184
|
+
cause: {
|
|
185
|
+
reason: 'block_hash_mismatch',
|
|
186
|
+
targetBlockNumber,
|
|
187
|
+
expectedHash: blockHash.toString(),
|
|
188
|
+
actualHash: updatedHash
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
170
193
|
return updatedBlockNumber;
|
|
171
194
|
}
|
|
172
195
|
/** Returns the L2 block hash for a given number. Used by the L2BlockStream for detecting reorgs. */ async getL2BlockHash(number) {
|
|
@@ -295,7 +318,7 @@ import { WorldStateSynchronizerError } from './errors.js';
|
|
|
295
318
|
return result;
|
|
296
319
|
}
|
|
297
320
|
async handleChainFinalized(blockNumber) {
|
|
298
|
-
this.log.
|
|
321
|
+
this.log.verbose(`Updating world state finalized chain to block ${blockNumber}`);
|
|
299
322
|
// If the finalized block number is older than the oldest available block in world state,
|
|
300
323
|
// skip entirely. The finalized block number can jump backwards (e.g. when the finalization
|
|
301
324
|
// heuristic changes) and try to read block data that has already been pruned. When this
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/world-state",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0-aztecnr-rc.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js",
|
|
@@ -64,19 +64,19 @@
|
|
|
64
64
|
]
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@aztec/constants": "4.
|
|
68
|
-
"@aztec/foundation": "4.
|
|
69
|
-
"@aztec/kv-store": "4.
|
|
70
|
-
"@aztec/merkle-tree": "4.
|
|
71
|
-
"@aztec/native": "4.
|
|
72
|
-
"@aztec/protocol-contracts": "4.
|
|
73
|
-
"@aztec/stdlib": "4.
|
|
74
|
-
"@aztec/telemetry-client": "4.
|
|
67
|
+
"@aztec/constants": "4.2.0-aztecnr-rc.2",
|
|
68
|
+
"@aztec/foundation": "4.2.0-aztecnr-rc.2",
|
|
69
|
+
"@aztec/kv-store": "4.2.0-aztecnr-rc.2",
|
|
70
|
+
"@aztec/merkle-tree": "4.2.0-aztecnr-rc.2",
|
|
71
|
+
"@aztec/native": "4.2.0-aztecnr-rc.2",
|
|
72
|
+
"@aztec/protocol-contracts": "4.2.0-aztecnr-rc.2",
|
|
73
|
+
"@aztec/stdlib": "4.2.0-aztecnr-rc.2",
|
|
74
|
+
"@aztec/telemetry-client": "4.2.0-aztecnr-rc.2",
|
|
75
75
|
"tslib": "^2.4.0",
|
|
76
76
|
"zod": "^3.23.8"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
|
-
"@aztec/archiver": "4.
|
|
79
|
+
"@aztec/archiver": "4.2.0-aztecnr-rc.2",
|
|
80
80
|
"@jest/globals": "^30.0.0",
|
|
81
81
|
"@types/jest": "^30.0.0",
|
|
82
82
|
"@types/node": "^22.15.17",
|
|
@@ -5,6 +5,7 @@ import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
|
5
5
|
import { promiseWithResolvers } from '@aztec/foundation/promise';
|
|
6
6
|
import { elapsed } from '@aztec/foundation/timer';
|
|
7
7
|
import {
|
|
8
|
+
type BlockHash,
|
|
8
9
|
GENESIS_CHECKPOINT_HEADER_HASH,
|
|
9
10
|
type L2Block,
|
|
10
11
|
type L2BlockId,
|
|
@@ -177,13 +178,10 @@ export class ServerWorldStateSynchronizer
|
|
|
177
178
|
/**
|
|
178
179
|
* Forces an immediate sync.
|
|
179
180
|
* @param targetBlockNumber - The target block number that we must sync to. Will download unproven blocks if needed to reach it.
|
|
180
|
-
* @param
|
|
181
|
+
* @param blockHash - If provided, verifies the block at targetBlockNumber matches this hash. On mismatch, triggers a resync (reorg detection).
|
|
181
182
|
* @returns A promise that resolves with the block number the world state was synced to
|
|
182
183
|
*/
|
|
183
|
-
public async syncImmediate(
|
|
184
|
-
targetBlockNumber?: BlockNumber,
|
|
185
|
-
skipThrowIfTargetNotReached?: boolean,
|
|
186
|
-
): Promise<BlockNumber> {
|
|
184
|
+
public async syncImmediate(targetBlockNumber?: BlockNumber, blockHash?: BlockHash): Promise<BlockNumber> {
|
|
187
185
|
if (this.currentState !== WorldStateRunningState.RUNNING) {
|
|
188
186
|
throw new Error(`World State is not running. Unable to perform sync.`);
|
|
189
187
|
}
|
|
@@ -195,7 +193,19 @@ export class ServerWorldStateSynchronizer
|
|
|
195
193
|
// If we have been given a block number to sync to and we have reached that number then return
|
|
196
194
|
const currentBlockNumber = await this.getLatestBlockNumber();
|
|
197
195
|
if (targetBlockNumber !== undefined && targetBlockNumber <= currentBlockNumber) {
|
|
198
|
-
|
|
196
|
+
if (blockHash === undefined) {
|
|
197
|
+
return currentBlockNumber;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// If a block hash was provided, verify we're on the expected fork
|
|
201
|
+
const currentHash = await this.getL2BlockHash(targetBlockNumber);
|
|
202
|
+
if (currentHash === blockHash.toString()) {
|
|
203
|
+
return currentBlockNumber;
|
|
204
|
+
}
|
|
205
|
+
// Hash mismatch: a reorg may have occurred, fall through to trigger sync
|
|
206
|
+
this.log.debug(
|
|
207
|
+
`World state block hash mismatch at ${targetBlockNumber} (expected ${blockHash}, got ${currentHash}). Triggering resync.`,
|
|
208
|
+
);
|
|
199
209
|
}
|
|
200
210
|
this.log.debug(`World State at ${currentBlockNumber} told to sync to ${targetBlockNumber ?? 'latest'}`);
|
|
201
211
|
|
|
@@ -213,7 +223,7 @@ export class ServerWorldStateSynchronizer
|
|
|
213
223
|
|
|
214
224
|
// If we have been given a block number to sync to and we have not reached that number then fail
|
|
215
225
|
const updatedBlockNumber = await this.getLatestBlockNumber();
|
|
216
|
-
if (
|
|
226
|
+
if (targetBlockNumber !== undefined && targetBlockNumber > updatedBlockNumber) {
|
|
217
227
|
throw new WorldStateSynchronizerError(
|
|
218
228
|
`Unable to sync to block number ${targetBlockNumber} (last synced is ${updatedBlockNumber})`,
|
|
219
229
|
{
|
|
@@ -227,6 +237,24 @@ export class ServerWorldStateSynchronizer
|
|
|
227
237
|
);
|
|
228
238
|
}
|
|
229
239
|
|
|
240
|
+
// If a block hash was provided, verify we're on the expected fork after syncing, throw otherwise
|
|
241
|
+
if (blockHash !== undefined && targetBlockNumber !== undefined) {
|
|
242
|
+
const updatedHash = await this.getL2BlockHash(targetBlockNumber);
|
|
243
|
+
if (updatedHash !== blockHash.toString()) {
|
|
244
|
+
throw new WorldStateSynchronizerError(
|
|
245
|
+
`Block hash mismatch at block ${targetBlockNumber} (expected ${blockHash} but got ${updatedHash})`,
|
|
246
|
+
{
|
|
247
|
+
cause: {
|
|
248
|
+
reason: 'block_hash_mismatch',
|
|
249
|
+
targetBlockNumber,
|
|
250
|
+
expectedHash: blockHash.toString(),
|
|
251
|
+
actualHash: updatedHash,
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
230
258
|
return updatedBlockNumber;
|
|
231
259
|
}
|
|
232
260
|
|
|
@@ -359,7 +387,7 @@ export class ServerWorldStateSynchronizer
|
|
|
359
387
|
}
|
|
360
388
|
|
|
361
389
|
private async handleChainFinalized(blockNumber: BlockNumber) {
|
|
362
|
-
this.log.
|
|
390
|
+
this.log.verbose(`Updating world state finalized chain to block ${blockNumber}`);
|
|
363
391
|
// If the finalized block number is older than the oldest available block in world state,
|
|
364
392
|
// skip entirely. The finalized block number can jump backwards (e.g. when the finalization
|
|
365
393
|
// heuristic changes) and try to read block data that has already been pruned. When this
|