@aztec/aztec-node 6.0.0-nightly.20260604 → 6.0.0-nightly.20260721

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dest/aztec-node/config.d.ts +10 -1
  2. package/dest/aztec-node/config.d.ts.map +1 -1
  3. package/dest/aztec-node/config.js +10 -0
  4. package/dest/aztec-node/node_public_calls_simulator.d.ts +97 -0
  5. package/dest/aztec-node/node_public_calls_simulator.d.ts.map +1 -0
  6. package/dest/aztec-node/node_public_calls_simulator.js +351 -0
  7. package/dest/aztec-node/register_node_rpc_handlers.d.ts +10 -0
  8. package/dest/aztec-node/register_node_rpc_handlers.d.ts.map +1 -0
  9. package/dest/aztec-node/register_node_rpc_handlers.js +31 -0
  10. package/dest/aztec-node/server.d.ts +92 -64
  11. package/dest/aztec-node/server.d.ts.map +1 -1
  12. package/dest/aztec-node/server.js +229 -1131
  13. package/dest/bin/index.js +15 -10
  14. package/dest/factory.d.ts +33 -0
  15. package/dest/factory.d.ts.map +1 -0
  16. package/dest/factory.js +523 -0
  17. package/dest/index.d.ts +3 -1
  18. package/dest/index.d.ts.map +1 -1
  19. package/dest/index.js +2 -0
  20. package/dest/modules/block_parameter.d.ts +25 -0
  21. package/dest/modules/block_parameter.d.ts.map +1 -0
  22. package/dest/modules/block_parameter.js +100 -0
  23. package/dest/modules/node_block_provider.d.ts +19 -0
  24. package/dest/modules/node_block_provider.d.ts.map +1 -0
  25. package/dest/modules/node_block_provider.js +112 -0
  26. package/dest/modules/node_tx_receipt.d.ts +24 -0
  27. package/dest/modules/node_tx_receipt.d.ts.map +1 -0
  28. package/dest/modules/node_tx_receipt.js +70 -0
  29. package/dest/modules/node_world_state_queries.d.ts +65 -0
  30. package/dest/modules/node_world_state_queries.d.ts.map +1 -0
  31. package/dest/modules/node_world_state_queries.js +272 -0
  32. package/dest/sentinel/factory.d.ts +3 -3
  33. package/dest/sentinel/factory.d.ts.map +1 -1
  34. package/dest/sentinel/factory.js +8 -1
  35. package/dest/sentinel/sentinel.d.ts +21 -21
  36. package/dest/sentinel/sentinel.d.ts.map +1 -1
  37. package/dest/sentinel/sentinel.js +27 -47
  38. package/package.json +28 -27
  39. package/src/aztec-node/config.ts +19 -0
  40. package/src/aztec-node/node_public_calls_simulator.ts +394 -0
  41. package/src/aztec-node/register_node_rpc_handlers.ts +29 -0
  42. package/src/aztec-node/server.ts +319 -1315
  43. package/src/bin/index.ts +19 -12
  44. package/src/factory.ts +688 -0
  45. package/src/index.ts +2 -0
  46. package/src/modules/block_parameter.ts +93 -0
  47. package/src/modules/node_block_provider.ts +149 -0
  48. package/src/modules/node_tx_receipt.ts +115 -0
  49. package/src/modules/node_world_state_queries.ts +374 -0
  50. package/src/sentinel/README.md +3 -3
  51. package/src/sentinel/factory.ts +15 -3
  52. package/src/sentinel/sentinel.ts +34 -72
@@ -0,0 +1,272 @@
1
+ import { BlockNumber } from '@aztec/foundation/branded-types';
2
+ import { chunkBy } from '@aztec/foundation/collection';
3
+ import { Fr } from '@aztec/foundation/curves/bn254';
4
+ import { createLogger } from '@aztec/foundation/log';
5
+ import { sleep } from '@aztec/foundation/sleep';
6
+ import { MembershipWitness } from '@aztec/foundation/trees';
7
+ import { inspectBlockParameter } from '@aztec/stdlib/block';
8
+ import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
9
+ import { InboxLeaf } from '@aztec/stdlib/messaging';
10
+ import { MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
11
+ import { WorldStateSynchronizerError } from '@aztec/world-state';
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;
15
+ /**
16
+ * Serves the node's Merkle-tree and membership-witness queries against committed world-state at a
17
+ * requested block. Extracted from `AztecNodeService` so the block-resolution and reorg-aware sync logic
18
+ * can be unit-tested without standing up the whole node, and to keep `server.ts` smaller.
19
+ */ export class NodeWorldStateQueries {
20
+ worldStateSynchronizer;
21
+ blockSource;
22
+ l1ToL2MessageSource;
23
+ log;
24
+ constructor(deps){
25
+ this.worldStateSynchronizer = deps.worldStateSynchronizer;
26
+ this.blockSource = deps.blockSource;
27
+ this.l1ToL2MessageSource = deps.l1ToL2MessageSource;
28
+ this.log = deps.log ?? createLogger('node:world-state-queries');
29
+ }
30
+ async findLeavesIndexes(referenceBlock, treeId, leafValues) {
31
+ const committedDb = await this.getWorldState(referenceBlock);
32
+ const maybeIndices = await committedDb.findLeafIndices(treeId, leafValues.map((x)=>x.toBuffer()));
33
+ // Filter out undefined values to query block numbers only for found leaves
34
+ const definedIndices = maybeIndices.filter((x)=>x !== undefined);
35
+ // Now we find the block numbers for the defined indices
36
+ const blockNumbers = await committedDb.getBlockNumbersForLeafIndices(treeId, definedIndices);
37
+ // Build a map from leaf index to block number
38
+ const indexToBlockNumber = new Map();
39
+ for(let i = 0; i < definedIndices.length; i++){
40
+ const blockNumber = blockNumbers[i];
41
+ if (blockNumber === undefined) {
42
+ throw new Error(`Block number is undefined for leaf index ${definedIndices[i]} in tree ${MerkleTreeId[treeId]}`);
43
+ }
44
+ indexToBlockNumber.set(definedIndices[i], blockNumber);
45
+ }
46
+ // Get unique block numbers in order to optimize num calls to getLeafValue function.
47
+ const uniqueBlockNumbers = [
48
+ ...new Set(indexToBlockNumber.values())
49
+ ];
50
+ // Now we obtain the block hashes from the archive tree (block number = leaf index in archive tree).
51
+ const blockHashes = await Promise.all(uniqueBlockNumbers.map((blockNumber)=>{
52
+ return committedDb.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
53
+ }));
54
+ // Build a map from block number to block hash
55
+ const blockNumberToHash = new Map();
56
+ for(let i = 0; i < uniqueBlockNumbers.length; i++){
57
+ const blockHash = blockHashes[i];
58
+ if (blockHash === undefined) {
59
+ throw new Error(`Block hash is undefined for block number ${uniqueBlockNumbers[i]}`);
60
+ }
61
+ blockNumberToHash.set(uniqueBlockNumbers[i], blockHash);
62
+ }
63
+ // Create DataInBlock objects by combining indices, blockNumbers and blockHashes and return them.
64
+ return maybeIndices.map((index)=>{
65
+ if (index === undefined) {
66
+ return undefined;
67
+ }
68
+ const blockNumber = indexToBlockNumber.get(index);
69
+ if (blockNumber === undefined) {
70
+ throw new Error(`Block number not found for leaf index ${index} in tree ${MerkleTreeId[treeId]}`);
71
+ }
72
+ const l2BlockHash = blockNumberToHash.get(blockNumber);
73
+ if (l2BlockHash === undefined) {
74
+ throw new Error(`Block hash not found for block number ${blockNumber}`);
75
+ }
76
+ return {
77
+ l2BlockNumber: blockNumber,
78
+ l2BlockHash,
79
+ data: index
80
+ };
81
+ });
82
+ }
83
+ async getBlockHashMembershipWitness(referenceBlock, blockHash) {
84
+ // The Noir circuit checks the archive membership proof against `anchor_block_header.last_archive.root`,
85
+ // which is the archive tree root BEFORE the anchor block was added (i.e. the state after block N-1).
86
+ // So we need the world state at block N-1, not block N, to produce a sibling path matching that root.
87
+ const referenceBlockNumber = await this.#resolveBlockNumber(referenceBlock);
88
+ if (referenceBlockNumber === BlockNumber.ZERO) {
89
+ // Block 0 (the initial block) has an empty archive, so no membership witness can exist.
90
+ return undefined;
91
+ }
92
+ const committedDb = await this.getWorldState(BlockNumber(referenceBlockNumber - 1));
93
+ const [pathAndIndex] = await committedDb.findSiblingPaths(MerkleTreeId.ARCHIVE, [
94
+ blockHash
95
+ ]);
96
+ return pathAndIndex === undefined ? undefined : MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
97
+ }
98
+ async getNoteHashMembershipWitness(referenceBlock, noteHash) {
99
+ const committedDb = await this.getWorldState(referenceBlock);
100
+ const [pathAndIndex] = await committedDb.findSiblingPaths(MerkleTreeId.NOTE_HASH_TREE, [
101
+ noteHash
102
+ ]);
103
+ return pathAndIndex === undefined ? undefined : MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
104
+ }
105
+ async getL1ToL2MessageMembershipWitness(referenceBlock, l1ToL2Message) {
106
+ const db = await this.getWorldState(referenceBlock);
107
+ const [witness] = await db.findSiblingPaths(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, [
108
+ l1ToL2Message
109
+ ]);
110
+ if (!witness) {
111
+ return undefined;
112
+ }
113
+ // REFACTOR: Return a MembershipWitness object
114
+ return [
115
+ witness.index,
116
+ witness.path
117
+ ];
118
+ }
119
+ async getL1ToL2MessageCheckpoint(l1ToL2Message) {
120
+ const messageIndex = await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message);
121
+ return messageIndex !== undefined ? InboxLeaf.checkpointNumberFromIndex(messageIndex) : undefined;
122
+ }
123
+ /**
124
+ * Returns all the L2 to L1 messages in an epoch (empty array if the epoch is not found). The public
125
+ * `AztecNodeService.getL2ToL1Messages` that delegates here is deprecated in favor of
126
+ * {@link getL2ToL1MembershipWitness}.
127
+ * @param epoch - The epoch at which to get the data.
128
+ */ async getL2ToL1Messages(epoch) {
129
+ const blocks = await this.blockSource.getBlocks({
130
+ epoch,
131
+ onlyCheckpointed: true
132
+ });
133
+ const blocksInCheckpoints = chunkBy(blocks, (block)=>block.header.globalVariables.slotNumber);
134
+ return blocksInCheckpoints.map((slotBlocks)=>slotBlocks.map((block)=>block.body.txEffects.map((txEffect)=>txEffect.l2ToL1Msgs)));
135
+ }
136
+ /**
137
+ * Returns the L2-to-L1 membership witness for a message in `txHash`. Passthrough to the
138
+ * archiver's locally-cached resolver — see {@link Archiver.getL2ToL1MembershipWitness}.
139
+ */ getL2ToL1MembershipWitness(txHash, message, messageIndexInTx) {
140
+ return this.blockSource.getL2ToL1MembershipWitness(txHash, message, messageIndexInTx);
141
+ }
142
+ async getNullifierMembershipWitness(referenceBlock, nullifier) {
143
+ const db = await this.getWorldState(referenceBlock);
144
+ const [witness] = await db.findSiblingPaths(MerkleTreeId.NULLIFIER_TREE, [
145
+ nullifier.toBuffer()
146
+ ]);
147
+ if (!witness) {
148
+ return undefined;
149
+ }
150
+ const { index, path } = witness;
151
+ const leafPreimage = await db.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index);
152
+ if (!leafPreimage) {
153
+ return undefined;
154
+ }
155
+ return new NullifierMembershipWitness(index, leafPreimage, path);
156
+ }
157
+ async getLowNullifierMembershipWitness(referenceBlock, nullifier) {
158
+ const committedDb = await this.getWorldState(referenceBlock);
159
+ const findResult = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt());
160
+ if (!findResult) {
161
+ return undefined;
162
+ }
163
+ const { index, alreadyPresent } = findResult;
164
+ if (alreadyPresent) {
165
+ throw new Error(`Cannot prove nullifier non-inclusion: nullifier ${nullifier.toBigInt()} already exists in the tree`);
166
+ }
167
+ const preimageData = await committedDb.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index);
168
+ const siblingPath = await committedDb.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, BigInt(index));
169
+ return new NullifierMembershipWitness(BigInt(index), preimageData, siblingPath);
170
+ }
171
+ async getPublicDataWitness(referenceBlock, leafSlot) {
172
+ const committedDb = await this.getWorldState(referenceBlock);
173
+ const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
174
+ if (!lowLeafResult) {
175
+ return undefined;
176
+ } else {
177
+ const preimage = await committedDb.getLeafPreimage(MerkleTreeId.PUBLIC_DATA_TREE, lowLeafResult.index);
178
+ const path = await committedDb.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, lowLeafResult.index);
179
+ return new PublicDataWitness(lowLeafResult.index, preimage, path);
180
+ }
181
+ }
182
+ async getPublicStorageAt(referenceBlock, contract, slot) {
183
+ const committedDb = await this.getWorldState(referenceBlock);
184
+ const leafSlot = await computePublicDataTreeLeafSlot(contract, slot);
185
+ const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
186
+ if (!lowLeafResult || !lowLeafResult.alreadyPresent) {
187
+ return Fr.ZERO;
188
+ }
189
+ const preimage = await committedDb.getLeafPreimage(MerkleTreeId.PUBLIC_DATA_TREE, lowLeafResult.index);
190
+ return preimage.leaf.value;
191
+ }
192
+ /**
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.
201
+ * @returns An instance of a committed MerkleTreeOperations
202
+ */ async getWorldState(block) {
203
+ const query = normalizeBlockParameter(block);
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
+ }
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
224
+ if ('tag' in query && query.tag === 'proposed') {
225
+ this.log.debug(`Using committed db for latest block`);
226
+ await this.worldStateSynchronizer.syncImmediate();
227
+ return this.worldStateSynchronizer.getCommitted();
228
+ }
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);
244
+ }
245
+ return {
246
+ blockNumber: blockData.header.getBlockNumber(),
247
+ blockHash: blockData.blockHash
248
+ };
249
+ }
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);
253
+ if (blockNumber === undefined) {
254
+ this.#throwOnUndefinedBlockData(blockQuery);
255
+ }
256
+ return blockNumber;
257
+ }
258
+ /**
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.`);
271
+ }
272
+ }
@@ -3,9 +3,9 @@ import type { P2PClient } from '@aztec/p2p';
3
3
  import type { L2BlockSource } from '@aztec/stdlib/block';
4
4
  import type { CheckpointReexecutionTracker } from '@aztec/stdlib/checkpoint';
5
5
  import type { ChainConfig } from '@aztec/stdlib/config';
6
- import type { SlasherConfig } from '@aztec/stdlib/interfaces/server';
6
+ import type { SlasherConfig, ValidatorClientConfig } from '@aztec/stdlib/interfaces/server';
7
7
  import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
8
8
  import type { SentinelConfig } from './config.js';
9
9
  import { Sentinel } from './sentinel.js';
10
- export declare function createSentinel(epochCache: EpochCache, archiver: L2BlockSource, p2p: P2PClient, reexecutionTracker: CheckpointReexecutionTracker, config: SentinelConfig & DataStoreConfig & SlasherConfig & Pick<ChainConfig, 'l1ChainId' | 'rollupAddress'>, logger?: import("@aztec/foundation/log").Logger): Promise<Sentinel | undefined>;
11
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlbnRpbmVsL2ZhY3RvcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFHckQsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQzVDLE9BQU8sS0FBSyxFQUFFLGFBQWEsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3pELE9BQU8sS0FBSyxFQUFFLDRCQUE0QixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDN0UsT0FBTyxLQUFLLEVBQUUsV0FBVyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDeEQsT0FBTyxLQUFLLEVBQUUsYUFBYSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDckUsT0FBTyxLQUFLLEVBQUUsZUFBZSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFFOUQsT0FBTyxLQUFLLEVBQUUsY0FBYyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ2xELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFHekMsd0JBQXNCLGNBQWMsQ0FDbEMsVUFBVSxFQUFFLFVBQVUsRUFDdEIsUUFBUSxFQUFFLGFBQWEsRUFDdkIsR0FBRyxFQUFFLFNBQVMsRUFDZCxrQkFBa0IsRUFBRSw0QkFBNEIsRUFDaEQsTUFBTSxFQUFFLGNBQWMsR0FBRyxlQUFlLEdBQUcsYUFBYSxHQUFHLElBQUksQ0FBQyxXQUFXLEVBQUUsV0FBVyxHQUFHLGVBQWUsQ0FBQyxFQUMzRyxNQUFNLHlDQUFnQyxHQUNyQyxPQUFPLENBQUMsUUFBUSxHQUFHLFNBQVMsQ0FBQyxDQVkvQiJ9
10
+ export declare function createSentinel(epochCache: EpochCache, archiver: L2BlockSource, p2p: P2PClient, reexecutionTracker: CheckpointReexecutionTracker, config: SentinelConfig & DataStoreConfig & SlasherConfig & Pick<ChainConfig, 'l1ChainId' | 'rollupAddress'> & Pick<ValidatorClientConfig, 'disableValidator'>, logger?: import("@aztec/foundation/log").Logger): Promise<Sentinel | undefined>;
11
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlbnRpbmVsL2ZhY3RvcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFHckQsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQzVDLE9BQU8sS0FBSyxFQUFFLGFBQWEsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3pELE9BQU8sS0FBSyxFQUFFLDRCQUE0QixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDN0UsT0FBTyxLQUFLLEVBQUUsV0FBVyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDeEQsT0FBTyxLQUFLLEVBQUUsYUFBYSxFQUFFLHFCQUFxQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDNUYsT0FBTyxLQUFLLEVBQUUsZUFBZSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFFOUQsT0FBTyxLQUFLLEVBQUUsY0FBYyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ2xELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFHekMsd0JBQXNCLGNBQWMsQ0FDbEMsVUFBVSxFQUFFLFVBQVUsRUFDdEIsUUFBUSxFQUFFLGFBQWEsRUFDdkIsR0FBRyxFQUFFLFNBQVMsRUFDZCxrQkFBa0IsRUFBRSw0QkFBNEIsRUFDaEQsTUFBTSxFQUFFLGNBQWMsR0FDcEIsZUFBZSxHQUNmLGFBQWEsR0FDYixJQUFJLENBQUMsV0FBVyxFQUFFLFdBQVcsR0FBRyxlQUFlLENBQUMsR0FDaEQsSUFBSSxDQUFDLHFCQUFxQixFQUFFLGtCQUFrQixDQUFDLEVBQ2pELE1BQU0seUNBQWdDLEdBQ3JDLE9BQU8sQ0FBQyxRQUFRLEdBQUcsU0FBUyxDQUFDLENBb0IvQiJ9
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/sentinel/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,wBAAsB,cAAc,CAClC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,aAAa,EACvB,GAAG,EAAE,SAAS,EACd,kBAAkB,EAAE,4BAA4B,EAChD,MAAM,EAAE,cAAc,GAAG,eAAe,GAAG,aAAa,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,eAAe,CAAC,EAC3G,MAAM,yCAAgC,GACrC,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAY/B"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/sentinel/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAC5F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,wBAAsB,cAAc,CAClC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,aAAa,EACvB,GAAG,EAAE,SAAS,EACd,kBAAkB,EAAE,4BAA4B,EAChD,MAAM,EAAE,cAAc,GACpB,eAAe,GACf,aAAa,GACb,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,eAAe,CAAC,GAChD,IAAI,CAAC,qBAAqB,EAAE,kBAAkB,CAAC,EACjD,MAAM,yCAAgC,GACrC,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAoB/B"}
@@ -3,9 +3,16 @@ import { createStore } from '@aztec/kv-store/lmdb-v2';
3
3
  import { Sentinel } from './sentinel.js';
4
4
  import { SentinelStore } from './store.js';
5
5
  export async function createSentinel(epochCache, archiver, p2p, reexecutionTracker, config, logger = createLogger('node:sentinel')) {
6
- if (!config.sentinelEnabled) {
6
+ const runsValidator = !config.disableValidator;
7
+ if (!runsValidator && !config.sentinelEnabled) {
8
+ logger.verbose('Sentinel is disabled');
7
9
  return undefined;
8
10
  }
11
+ if (runsValidator) {
12
+ logger.info('Enabling sentinel since this node runs a validator');
13
+ } else {
14
+ logger.info('Enabling sentinel from SENTINEL_ENABLED configuration');
15
+ }
9
16
  const kvStore = await createStore('sentinel', SentinelStore.SCHEMA_VERSION, config, logger.getBindings());
10
17
  const storeHistoryLength = config.sentinelHistoryLengthInEpochs * epochCache.getL1Constants().epochDuration;
11
18
  const storeHistoricEpochPerformanceLength = config.sentinelHistoricEpochPerformanceLengthInEpochs;
@@ -2,11 +2,10 @@ import type { EpochCache } from '@aztec/epoch-cache';
2
2
  import { CheckpointNumber, CheckpointProposalHash, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
3
3
  import { EthAddress } from '@aztec/foundation/eth-address';
4
4
  import { RunningPromise } from '@aztec/foundation/running-promise';
5
- import { type L2TipsStore } from '@aztec/kv-store/stores';
6
5
  import type { P2PClient } from '@aztec/p2p';
7
6
  import { type Watcher, type WatcherEmitter } from '@aztec/slasher';
8
7
  import type { SlasherConfig } from '@aztec/slasher/config';
9
- import { type L2BlockSource, L2BlockStream, type L2BlockStreamEvent, type L2BlockStreamEventHandler } from '@aztec/stdlib/block';
8
+ import { type L2BlockSource } from '@aztec/stdlib/block';
10
9
  import type { CheckpointReexecutionTracker } from '@aztec/stdlib/checkpoint';
11
10
  import type { ChainConfig } from '@aztec/stdlib/config';
12
11
  import type { SingleValidatorStats, ValidatorStats, ValidatorStatusHistory, ValidatorStatusInSlot, ValidatorStatusType, ValidatorsEpochPerformance, ValidatorsStats } from '@aztec/stdlib/validators';
@@ -46,7 +45,7 @@ declare const Sentinel_base: new () => WatcherEmitter;
46
45
  * first:
47
46
  *
48
47
  * - `checkpoint-mined` — a checkpoint covering this slot has landed on L1
49
- * (`slotNumberToCheckpoint` populated from `chain-checkpointed`).
48
+ * (fetched on demand via `archiver.getCheckpoint({ slot })`).
50
49
  * - `checkpoint-valid` — the local node re-executed a checkpoint proposal for this slot
51
50
  * successfully (consulted via `CheckpointReexecutionTracker`).
52
51
  * - `checkpoint-invalid` — the local node re-executed a checkpoint proposal for this slot
@@ -84,7 +83,7 @@ declare const Sentinel_base: new () => WatcherEmitter;
84
83
  * (no history entries for that slot) and per-epoch evaluation writes an empty performance map
85
84
  * (no slashing).
86
85
  */
87
- export declare class Sentinel extends Sentinel_base implements L2BlockStreamEventHandler, Watcher {
86
+ export declare class Sentinel extends Sentinel_base implements Watcher {
88
87
  protected epochCache: EpochCache;
89
88
  protected archiver: L2BlockSource;
90
89
  protected p2p: P2PClient;
@@ -93,29 +92,19 @@ export declare class Sentinel extends Sentinel_base implements L2BlockStreamEven
93
92
  protected config: SentinelRuntimeConfig;
94
93
  protected logger: import("@aztec/foundation/log").Logger;
95
94
  protected runningPromise: RunningPromise;
96
- protected blockStream: L2BlockStream;
97
- protected l2TipsStore: L2TipsStore;
98
95
  protected initialSlot: SlotNumber | undefined;
99
96
  protected lastProcessedSlot: SlotNumber | undefined;
100
97
  /** Largest epoch number for which the end-of-epoch aggregator has run. */
101
98
  protected lastEvaluatedEpoch: EpochNumber | undefined;
102
- protected slotNumberToCheckpoint: Map<SlotNumber, {
103
- checkpointNumber: CheckpointNumber;
104
- archive: string;
105
- /** Hex keccak256 of the consensus payload bytes; used to fetch matching p2p attestations. */
106
- proposalPayloadHash: CheckpointProposalHash;
107
- attestors: EthAddress[];
108
- }>;
109
99
  constructor(epochCache: EpochCache, archiver: L2BlockSource, p2p: P2PClient, store: SentinelStore, reexecutionTracker: CheckpointReexecutionTracker, config: SentinelRuntimeConfig, logger?: import("@aztec/foundation/log").Logger);
110
100
  private getSignatureContext;
111
101
  updateConfig(config: Partial<SlasherConfig>): void;
112
102
  start(): Promise<void>;
113
103
  /**
114
- * Loads initial slot and initializes blockstream. We will not process anything at or before
115
- * the initial slot. Floors at the archiver's synced L2 slot so the sentinel keeps making
116
- * forward progress when L1 is advancing but L2 has no activity (the synced slot is driven by
117
- * L1 sync, not by L2 blocks). Falls back to the wallclock if the archiver isn't ready yet
118
- * (cold start).
104
+ * Loads the initial slot. We will not process anything at or before the initial slot. Floors at the
105
+ * archiver's synced L2 slot so the sentinel keeps making forward progress when L1 is advancing but L2 has no
106
+ * activity (the synced slot is driven by L1 sync, not by L2 blocks). Falls back to the wallclock if the
107
+ * archiver isn't ready yet (cold start).
119
108
  */
120
109
  protected init(): Promise<void>;
121
110
  /**
@@ -127,8 +116,19 @@ export declare class Sentinel extends Sentinel_base implements L2BlockStreamEven
127
116
  */
128
117
  protected getCurrentSlot(): Promise<SlotNumber>;
129
118
  stop(): Promise<void>;
130
- handleBlockStreamEvent(event: L2BlockStreamEvent): Promise<void>;
131
- protected handleCheckpoint(event: L2BlockStreamEvent): void;
119
+ /**
120
+ * Fetches the L1-confirmed checkpoint covering a slot (if any) and derives the slot-level data the
121
+ * activity classifier needs: the checkpoint number, archive root, consensus payload hash (used to fetch
122
+ * matching p2p attestations regardless of feeAssetPriceModifier variants), and the recovered attestor set.
123
+ * Reads on demand so the result is always against the canonical chain — a reorged-out checkpoint simply
124
+ * stops being returned, with no stale mapping to clean up.
125
+ */
126
+ protected getCheckpointForSlot(slot: SlotNumber): Promise<{
127
+ checkpointNumber: CheckpointNumber;
128
+ archive: string;
129
+ proposalPayloadHash: CheckpointProposalHash;
130
+ attestors: EthAddress[];
131
+ } | undefined>;
132
132
  /**
133
133
  * Called once per epoch, after the configured end-of-epoch buffer has elapsed beyond the
134
134
  * epoch's last slot. Computes per-epoch performance from the slot-level history collected
@@ -214,4 +214,4 @@ export declare class Sentinel extends Sentinel_base implements L2BlockStreamEven
214
214
  } | undefined;
215
215
  }
216
216
  export {};
217
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VudGluZWwuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZW50aW5lbC9zZW50aW5lbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxVQUFVLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNyRCxPQUFPLEVBRUwsZ0JBQWdCLEVBQ2hCLHNCQUFzQixFQUN0QixXQUFXLEVBQ1gsVUFBVSxFQUNYLE1BQU0saUNBQWlDLENBQUM7QUFFekMsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBRTNELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUNuRSxPQUFPLEVBQXFCLEtBQUssV0FBVyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDN0UsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQzVDLE9BQU8sRUFJTCxLQUFLLE9BQU8sRUFDWixLQUFLLGNBQWMsRUFFcEIsTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QixPQUFPLEtBQUssRUFBRSxhQUFhLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMzRCxPQUFPLEVBQ0wsS0FBSyxhQUFhLEVBQ2xCLGFBQWEsRUFDYixLQUFLLGtCQUFrQixFQUN2QixLQUFLLHlCQUF5QixFQUUvQixNQUFNLHFCQUFxQixDQUFDO0FBQzdCLE9BQU8sS0FBSyxFQUFFLDRCQUE0QixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDN0UsT0FBTyxLQUFLLEVBQUUsV0FBVyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFHeEQsT0FBTyxLQUFLLEVBQ1Ysb0JBQW9CLEVBQ3BCLGNBQWMsRUFDZCxzQkFBc0IsRUFDdEIscUJBQXFCLEVBQ3JCLG1CQUFtQixFQUNuQiwwQkFBMEIsRUFDMUIsZUFBZSxFQUNoQixNQUFNLDBCQUEwQixDQUFDO0FBSWxDLE9BQU8sS0FBSyxFQUFFLGNBQWMsRUFBRSxNQUFNLGFBQWEsQ0FB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
217
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VudGluZWwuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZW50aW5lbC9zZW50aW5lbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxVQUFVLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNyRCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsc0JBQXNCLEVBQUUsV0FBVyxFQUFFLFVBQVUsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRXBILE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUUzRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFDbkUsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQzVDLE9BQU8sRUFJTCxLQUFLLE9BQU8sRUFDWixLQUFLLGNBQWMsRUFFcEIsTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QixPQUFPLEtBQUssRUFBRSxhQUFhLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsS0FBSyxhQUFhLEVBQTZDLE1BQU0scUJBQXFCLENBQUM7QUFDcEcsT0FBTyxLQUFLLEVBQUUsNEJBQTRCLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUM3RSxPQUFPLEtBQUssRUFBRSxXQUFXLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUd4RCxPQUFPLEtBQUssRUFDVixvQkFBb0IsRUFDcEIsY0FBYyxFQUNkLHNCQUFzQixFQUN0QixxQkFBcUIsRUFDckIsbUJBQW1CLEVBQ25CLDBCQUEwQixFQUMxQixlQUFlLEVBQ2hCLE1BQU0sMEJBQTBCLENBQUM7QUFJbEMsT0FBTyxLQUFLLEVBQUUsY0FBYyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ2xELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFFM0MsTUFBTSxNQUFNLHFCQUFxQixHQUFHLElBQUksQ0FDdEMsYUFBYSxFQUNiLGlDQUFp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
@@ -1 +1 @@
1
- {"version":3,"file":"sentinel.d.ts","sourceRoot":"","sources":["../../src/sentinel/sentinel.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sentinel.d.ts","sourceRoot":"","sources":["../../src/sentinel/sentinel.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,10 @@
1
- import { BlockNumber, CheckpointProposalHash, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
1
+ import { CheckpointProposalHash, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
2
2
  import { countWhile, filterAsync, fromEntries, getEntries, mapValues } from '@aztec/foundation/collection';
3
3
  import { EthAddress } from '@aztec/foundation/eth-address';
4
4
  import { createLogger } from '@aztec/foundation/log';
5
5
  import { RunningPromise } from '@aztec/foundation/running-promise';
6
- import { L2TipsMemoryStore } from '@aztec/kv-store/stores';
7
6
  import { OffenseType, WANT_TO_SLASH_EVENT, getOffenseTypeName } from '@aztec/slasher';
8
- import { L2BlockStream, getAttestationInfoFromPublishedCheckpoint } from '@aztec/stdlib/block';
7
+ import { getAttestationInfoFromPublishedCheckpoint } from '@aztec/stdlib/block';
9
8
  import { getEpochAtSlot, getSlotRangeForEpoch, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
10
9
  import { ConsensusPayload } from '@aztec/stdlib/p2p';
11
10
  import EventEmitter from 'node:events';
@@ -50,7 +49,7 @@ import EventEmitter from 'node:events';
50
49
  * first:
51
50
  *
52
51
  * - `checkpoint-mined` — a checkpoint covering this slot has landed on L1
53
- * (`slotNumberToCheckpoint` populated from `chain-checkpointed`).
52
+ * (fetched on demand via `archiver.getCheckpoint({ slot })`).
54
53
  * - `checkpoint-valid` — the local node re-executed a checkpoint proposal for this slot
55
54
  * successfully (consulted via `CheckpointReexecutionTracker`).
56
55
  * - `checkpoint-invalid` — the local node re-executed a checkpoint proposal for this slot
@@ -96,15 +95,11 @@ import EventEmitter from 'node:events';
96
95
  config;
97
96
  logger;
98
97
  runningPromise;
99
- blockStream;
100
- l2TipsStore;
101
98
  initialSlot;
102
99
  lastProcessedSlot;
103
100
  /** Largest epoch number for which the end-of-epoch aggregator has run. */ lastEvaluatedEpoch;
104
- slotNumberToCheckpoint;
105
101
  constructor(epochCache, archiver, p2p, store, reexecutionTracker, config, logger = createLogger('node:sentinel')){
106
- super(), this.epochCache = epochCache, this.archiver = archiver, this.p2p = p2p, this.store = store, this.reexecutionTracker = reexecutionTracker, this.config = config, this.logger = logger, this.slotNumberToCheckpoint = new Map();
107
- this.l2TipsStore = new L2TipsMemoryStore(archiver.getGenesisBlockHash());
102
+ super(), this.epochCache = epochCache, this.archiver = archiver, this.p2p = p2p, this.store = store, this.reexecutionTracker = reexecutionTracker, this.config = config, this.logger = logger;
108
103
  const interval = epochCache.getL1Constants().ethereumSlotDuration * 1000 / 4;
109
104
  this.runningPromise = new RunningPromise(this.work.bind(this), logger, interval);
110
105
  }
@@ -125,18 +120,13 @@ import EventEmitter from 'node:events';
125
120
  this.runningPromise.start();
126
121
  }
127
122
  /**
128
- * Loads initial slot and initializes blockstream. We will not process anything at or before
129
- * the initial slot. Floors at the archiver's synced L2 slot so the sentinel keeps making
130
- * forward progress when L1 is advancing but L2 has no activity (the synced slot is driven by
131
- * L1 sync, not by L2 blocks). Falls back to the wallclock if the archiver isn't ready yet
132
- * (cold start).
123
+ * Loads the initial slot. We will not process anything at or before the initial slot. Floors at the
124
+ * archiver's synced L2 slot so the sentinel keeps making forward progress when L1 is advancing but L2 has no
125
+ * activity (the synced slot is driven by L1 sync, not by L2 blocks). Falls back to the wallclock if the
126
+ * archiver isn't ready yet (cold start).
133
127
  */ async init() {
134
128
  this.initialSlot = await this.getCurrentSlot();
135
- const startingBlock = BlockNumber(await this.archiver.getBlockNumber());
136
- this.logger.info(`Starting validator sentinel with initial slot ${this.initialSlot} and block ${startingBlock}`);
137
- this.blockStream = new L2BlockStream(this.archiver, this.l2TipsStore, this, this.logger, {
138
- startingBlock
139
- });
129
+ this.logger.info(`Starting validator sentinel with initial slot ${this.initialSlot}`);
140
130
  }
141
131
  /**
142
132
  * Returns the L2 slot the sentinel should treat as "current": the archiver's last fully
@@ -150,35 +140,27 @@ import EventEmitter from 'node:events';
150
140
  stop() {
151
141
  return this.runningPromise.stop();
152
142
  }
153
- async handleBlockStreamEvent(event) {
154
- await this.l2TipsStore.handleBlockStreamEvent(event);
155
- if (event.type === 'chain-checkpointed') {
156
- this.handleCheckpoint(event);
157
- }
158
- }
159
- handleCheckpoint(event) {
160
- if (event.type !== 'chain-checkpointed') {
161
- return;
143
+ /**
144
+ * Fetches the L1-confirmed checkpoint covering a slot (if any) and derives the slot-level data the
145
+ * activity classifier needs: the checkpoint number, archive root, consensus payload hash (used to fetch
146
+ * matching p2p attestations regardless of feeAssetPriceModifier variants), and the recovered attestor set.
147
+ * Reads on demand so the result is always against the canonical chain — a reorged-out checkpoint simply
148
+ * stops being returned, with no stale mapping to clean up.
149
+ */ async getCheckpointForSlot(slot) {
150
+ const checkpoint = await this.archiver.getCheckpoint({
151
+ slot
152
+ });
153
+ if (!checkpoint) {
154
+ return undefined;
162
155
  }
163
- const checkpoint = event.checkpoint;
164
- // Store mapping from slot to archive, checkpoint number, attestors, and the consensus payload
165
- // hash (used to query matching p2p attestations regardless of feeAssetPriceModifier variants).
166
156
  const signatureContext = this.getSignatureContext();
167
157
  const proposalPayloadHash = CheckpointProposalHash.fromBuffer(ConsensusPayload.fromCheckpoint(checkpoint.checkpoint, signatureContext).getPayloadHash());
168
- this.slotNumberToCheckpoint.set(checkpoint.checkpoint.header.slotNumber, {
158
+ return {
169
159
  checkpointNumber: checkpoint.checkpoint.number,
170
160
  archive: checkpoint.checkpoint.archive.root.toString(),
171
161
  proposalPayloadHash,
172
162
  attestors: getAttestationInfoFromPublishedCheckpoint(checkpoint, signatureContext).filter((a)=>a.status === 'recovered-from-signature').map((a)=>a.address)
173
- });
174
- // Prune the archive map to only keep at most N entries
175
- const historyLength = this.store.getHistoryLength();
176
- if (this.slotNumberToCheckpoint.size > historyLength) {
177
- const toDelete = Array.from(this.slotNumberToCheckpoint.keys()).sort((a, b)=>Number(a - b)).slice(0, this.slotNumberToCheckpoint.size - historyLength);
178
- for (const key of toDelete){
179
- this.slotNumberToCheckpoint.delete(key);
180
- }
181
- }
163
+ };
182
164
  }
183
165
  /**
184
166
  * Called once per epoch, after the configured end-of-epoch buffer has elapsed beyond the
@@ -284,9 +266,6 @@ import EventEmitter from 'node:events';
284
266
  */ async work() {
285
267
  const currentSlot = await this.getCurrentSlot();
286
268
  try {
287
- // Manually sync the block stream to ensure we have the latest data.
288
- // Note we never `start` the blockstream, so it loops at the same pace as we do.
289
- await this.blockStream.sync();
290
269
  // Per-slot activity recording (lag = 2 slots for P2P attestation settlement).
291
270
  const targetSlot = await this.isReadyToProcess(currentSlot);
292
271
  if (targetSlot !== false) {
@@ -363,7 +342,7 @@ import EventEmitter from 'node:events';
363
342
  });
364
343
  return false;
365
344
  }
366
- const archiverLastBlockHash = await this.l2TipsStore.getL2Tips().then((tip)=>tip.proposed.hash);
345
+ const archiverLastBlockHash = await this.archiver.getL2Tips().then((tip)=>tip.proposed.hash);
367
346
  const p2pLastBlockHash = await this.p2p.getL2Tips().then((tips)=>tips.proposed.hash);
368
347
  const isP2pSynced = archiverLastBlockHash === p2pLastBlockHash;
369
348
  if (!isP2pSynced) {
@@ -421,8 +400,9 @@ import EventEmitter from 'node:events';
421
400
  committee
422
401
  });
423
402
  // Gather attestors from both p2p (live attestations) and the archiver (signers on the
424
- // checkpoint if one has landed on L1). Used regardless of which case applies.
425
- const checkpoint = this.slotNumberToCheckpoint.get(slot);
403
+ // checkpoint if one has landed on L1). Fetched on demand so it always reflects the canonical chain.
404
+ // Used regardless of which case applies.
405
+ const checkpoint = await this.getCheckpointForSlot(slot);
426
406
  const p2pAttested = await this.p2p.getCheckpointAttestationsForSlot(slot, checkpoint?.proposalPayloadHash);
427
407
  const p2pAttestors = p2pAttested.map((a)=>a.getSender()).filter((s)=>s !== undefined);
428
408
  const attestors = new Set([