@aztec/archiver 0.86.0 → 0.87.0
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/archiver/archiver.d.ts +62 -5
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +362 -91
- package/dest/archiver/archiver_store.d.ts +33 -17
- package/dest/archiver/archiver_store.d.ts.map +1 -1
- package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
- package/dest/archiver/archiver_store_test_suite.js +364 -62
- package/dest/archiver/data_retrieval.d.ts +23 -14
- package/dest/archiver/data_retrieval.d.ts.map +1 -1
- package/dest/archiver/data_retrieval.js +86 -38
- package/dest/archiver/errors.d.ts +8 -0
- package/dest/archiver/errors.d.ts.map +1 -1
- package/dest/archiver/errors.js +12 -0
- package/dest/archiver/instrumentation.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.d.ts +4 -1
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.js +43 -6
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +3 -1
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_instance_store.js +17 -3
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +24 -14
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +37 -11
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/log_store.js +1 -1
- package/dest/archiver/kv_archiver_store/message_store.d.ts +21 -15
- package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/message_store.js +150 -48
- package/dest/archiver/structs/inbox_message.d.ts +14 -0
- package/dest/archiver/structs/inbox_message.d.ts.map +1 -0
- package/dest/archiver/structs/inbox_message.js +38 -0
- package/dest/rpc/index.d.ts +1 -1
- package/dest/test/mock_l2_block_source.d.ts +2 -0
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +8 -1
- package/dest/test/mock_structs.d.ts +9 -0
- package/dest/test/mock_structs.d.ts.map +1 -0
- package/dest/test/mock_structs.js +37 -0
- package/package.json +15 -15
- package/src/archiver/archiver.ts +431 -108
- package/src/archiver/archiver_store.ts +37 -18
- package/src/archiver/archiver_store_test_suite.ts +307 -52
- package/src/archiver/data_retrieval.ts +130 -53
- package/src/archiver/errors.ts +21 -0
- package/src/archiver/instrumentation.ts +4 -1
- package/src/archiver/kv_archiver_store/block_store.ts +46 -8
- package/src/archiver/kv_archiver_store/contract_instance_store.ts +20 -7
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +61 -17
- package/src/archiver/kv_archiver_store/log_store.ts +6 -2
- package/src/archiver/kv_archiver_store/message_store.ts +209 -53
- package/src/archiver/structs/inbox_message.ts +40 -0
- package/src/test/mock_l2_block_source.ts +9 -1
- package/src/test/mock_structs.ts +49 -0
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +0 -23
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts.map +0 -1
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.js +0 -49
- package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +0 -61
|
@@ -4,14 +4,15 @@ function _ts_decorate(decorators, target, key, desc) {
|
|
|
4
4
|
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
}
|
|
7
|
-
import { RollupContract, createEthereumChain } from '@aztec/ethereum';
|
|
7
|
+
import { BlockTagTooOldError, InboxContract, RollupContract, createEthereumChain } from '@aztec/ethereum';
|
|
8
|
+
import { maxBigint } from '@aztec/foundation/bigint';
|
|
9
|
+
import { Buffer16, Buffer32 } from '@aztec/foundation/buffer';
|
|
8
10
|
import { Fr } from '@aztec/foundation/fields';
|
|
9
11
|
import { createLogger } from '@aztec/foundation/log';
|
|
10
12
|
import { RunningPromise, makeLoggingErrorHandler } from '@aztec/foundation/running-promise';
|
|
11
13
|
import { sleep } from '@aztec/foundation/sleep';
|
|
12
14
|
import { count } from '@aztec/foundation/string';
|
|
13
15
|
import { elapsed } from '@aztec/foundation/timer';
|
|
14
|
-
import { InboxAbi } from '@aztec/l1-artifacts';
|
|
15
16
|
import { ContractClassRegisteredEvent, PrivateFunctionBroadcastedEvent, UtilityFunctionBroadcastedEvent } from '@aztec/protocol-contracts/class-registerer';
|
|
16
17
|
import { ContractInstanceDeployedEvent, ContractInstanceUpdatedEvent } from '@aztec/protocol-contracts/instance-deployer';
|
|
17
18
|
import { L2BlockSourceEvents } from '@aztec/stdlib/block';
|
|
@@ -20,9 +21,9 @@ import { getEpochAtSlot, getEpochNumberAtTimestamp, getSlotAtTimestamp, getSlotR
|
|
|
20
21
|
import { Attributes, trackSpan } from '@aztec/telemetry-client';
|
|
21
22
|
import { EventEmitter } from 'events';
|
|
22
23
|
import groupBy from 'lodash.groupby';
|
|
23
|
-
import { createPublicClient, fallback,
|
|
24
|
-
import { retrieveBlocksFromRollup, retrieveL1ToL2Messages } from './data_retrieval.js';
|
|
25
|
-
import { NoBlobBodiesFoundError } from './errors.js';
|
|
24
|
+
import { createPublicClient, fallback, http } from 'viem';
|
|
25
|
+
import { retrieveBlocksFromRollup, retrieveL1ToL2Message, retrieveL1ToL2Messages, retrievedBlockToPublishedL2Block } from './data_retrieval.js';
|
|
26
|
+
import { InitialBlockNumberNotSequentialError, NoBlobBodiesFoundError } from './errors.js';
|
|
26
27
|
import { ArchiverInstrumentation } from './instrumentation.js';
|
|
27
28
|
/**
|
|
28
29
|
* Pulls L2 blocks in a non-blocking manner and provides interface for their retrieval.
|
|
@@ -61,11 +62,7 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
61
62
|
this.tracer = instrumentation.tracer;
|
|
62
63
|
this.store = new ArchiverStoreHelper(dataStore);
|
|
63
64
|
this.rollup = new RollupContract(publicClient, l1Addresses.rollupAddress);
|
|
64
|
-
this.inbox =
|
|
65
|
-
address: l1Addresses.inboxAddress.toString(),
|
|
66
|
-
abi: InboxAbi,
|
|
67
|
-
client: publicClient
|
|
68
|
-
});
|
|
65
|
+
this.inbox = new InboxContract(publicClient, l1Addresses.inboxAddress);
|
|
69
66
|
}
|
|
70
67
|
/**
|
|
71
68
|
* Creates a new instance of the Archiver and blocks until it syncs from chain.
|
|
@@ -85,18 +82,25 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
85
82
|
rollup.getL1StartBlock(),
|
|
86
83
|
rollup.getL1GenesisTime()
|
|
87
84
|
]);
|
|
85
|
+
const l1StartBlockHash = await publicClient.getBlock({
|
|
86
|
+
blockNumber: l1StartBlock,
|
|
87
|
+
includeTransactions: false
|
|
88
|
+
}).then((block)=>Buffer32.fromString(block.hash));
|
|
88
89
|
const { aztecEpochDuration: epochDuration, aztecSlotDuration: slotDuration, ethereumSlotDuration, aztecProofSubmissionWindow: proofSubmissionWindow } = config;
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
batchSize: config.archiverBatchSize ?? 100
|
|
92
|
-
}, deps.blobSinkClient, await ArchiverInstrumentation.new(deps.telemetry, ()=>archiverStore.estimateSize()), {
|
|
90
|
+
const l1Constants = {
|
|
91
|
+
l1StartBlockHash,
|
|
93
92
|
l1StartBlock,
|
|
94
93
|
l1GenesisTime,
|
|
95
94
|
epochDuration,
|
|
96
95
|
slotDuration,
|
|
97
96
|
ethereumSlotDuration,
|
|
98
97
|
proofSubmissionWindow
|
|
99
|
-
}
|
|
98
|
+
};
|
|
99
|
+
const opts = {
|
|
100
|
+
pollingIntervalMs: config.archiverPollingIntervalMS ?? 10_000,
|
|
101
|
+
batchSize: config.archiverBatchSize ?? 100
|
|
102
|
+
};
|
|
103
|
+
const archiver = new Archiver(publicClient, config.l1Contracts, archiverStore, opts, deps.blobSinkClient, await ArchiverInstrumentation.new(deps.telemetry, ()=>archiverStore.estimateSize()), l1Constants);
|
|
100
104
|
await archiver.start(blockUntilSynced);
|
|
101
105
|
return archiver;
|
|
102
106
|
}
|
|
@@ -132,6 +136,8 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
132
136
|
} catch (error) {
|
|
133
137
|
if (error instanceof NoBlobBodiesFoundError) {
|
|
134
138
|
this.log.error(`Error syncing archiver: ${error.message}`);
|
|
139
|
+
} else if (error instanceof BlockTagTooOldError) {
|
|
140
|
+
this.log.warn(`Re-running archiver sync: ${error.message}`);
|
|
135
141
|
} else {
|
|
136
142
|
this.log.error('Error during archiver sync', error);
|
|
137
143
|
}
|
|
@@ -152,11 +158,21 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
152
158
|
* The archiver will stay back, until there's data on L1 that will move the pointers forward.
|
|
153
159
|
*
|
|
154
160
|
* This code does not handle reorgs.
|
|
155
|
-
*/ const { l1StartBlock } = this.l1constants;
|
|
156
|
-
const { blocksSynchedTo = l1StartBlock, messagesSynchedTo =
|
|
157
|
-
|
|
161
|
+
*/ const { l1StartBlock, l1StartBlockHash } = this.l1constants;
|
|
162
|
+
const { blocksSynchedTo = l1StartBlock, messagesSynchedTo = {
|
|
163
|
+
l1BlockNumber: l1StartBlock,
|
|
164
|
+
l1BlockHash: l1StartBlockHash
|
|
165
|
+
} } = await this.store.getSynchPoint();
|
|
166
|
+
const currentL1Block = await this.publicClient.getBlock({
|
|
167
|
+
includeTransactions: false
|
|
168
|
+
});
|
|
169
|
+
const currentL1BlockNumber = currentL1Block.number;
|
|
170
|
+
const currentL1BlockHash = Buffer32.fromString(currentL1Block.hash);
|
|
158
171
|
if (initialRun) {
|
|
159
|
-
this.log.info(`Starting archiver sync to rollup contract ${this.l1Addresses.rollupAddress.toString()} from L1 block ${
|
|
172
|
+
this.log.info(`Starting archiver sync to rollup contract ${this.l1Addresses.rollupAddress.toString()} from L1 block ${blocksSynchedTo}` + ` to current L1 block ${currentL1BlockNumber} with hash ${currentL1BlockHash.toString()}`, {
|
|
173
|
+
blocksSynchedTo,
|
|
174
|
+
messagesSynchedTo
|
|
175
|
+
});
|
|
160
176
|
}
|
|
161
177
|
// ********** Ensuring Consistency of data pulled from L1 **********
|
|
162
178
|
/**
|
|
@@ -175,7 +191,7 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
175
191
|
* data up to the currentBlockNumber captured at the top of this function. We might want to improve on this
|
|
176
192
|
* in future but for the time being it should give us the guarantees that we need
|
|
177
193
|
*/ // ********** Events that are processed per L1 block **********
|
|
178
|
-
await this.handleL1ToL2Messages(messagesSynchedTo, currentL1BlockNumber);
|
|
194
|
+
await this.handleL1ToL2Messages(messagesSynchedTo, currentL1BlockNumber, currentL1BlockHash);
|
|
179
195
|
// Get L1 timestamp for the current block
|
|
180
196
|
const currentL1Timestamp = !this.l1Timestamp || !this.l1BlockNumber || this.l1BlockNumber !== currentL1BlockNumber ? (await this.publicClient.getBlock({
|
|
181
197
|
blockNumber: currentL1BlockNumber
|
|
@@ -183,13 +199,19 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
183
199
|
// ********** Events that are processed per L2 block **********
|
|
184
200
|
if (currentL1BlockNumber > blocksSynchedTo) {
|
|
185
201
|
// First we retrieve new L2 blocks
|
|
186
|
-
const
|
|
187
|
-
//
|
|
202
|
+
const rollupStatus = await this.handleL2blocks(blocksSynchedTo, currentL1BlockNumber);
|
|
203
|
+
// Then we prune the current epoch if it'd reorg on next submission.
|
|
188
204
|
// Note that we don't do this before retrieving L2 blocks because we may need to retrieve
|
|
189
205
|
// blocks from more than 2 epochs ago, so we want to make sure we have the latest view of
|
|
190
206
|
// the chain locally before we start unwinding stuff. This can be optimized by figuring out
|
|
191
207
|
// up to which point we're pruning, and then requesting L2 blocks up to that point only.
|
|
192
|
-
await this.handleEpochPrune(provenBlockNumber, currentL1BlockNumber, currentL1Timestamp);
|
|
208
|
+
const { rollupCanPrune } = await this.handleEpochPrune(rollupStatus.provenBlockNumber, currentL1BlockNumber, currentL1Timestamp);
|
|
209
|
+
// And lastly we check if we are missing any L2 blocks behind us due to a possible L1 reorg.
|
|
210
|
+
// We only do this if rollup cant prune on the next submission. Otherwise we will end up
|
|
211
|
+
// re-syncing the blocks we have just unwound above.
|
|
212
|
+
if (!rollupCanPrune) {
|
|
213
|
+
await this.checkForNewBlocksBeforeL1SyncPoint(rollupStatus, blocksSynchedTo, currentL1BlockNumber);
|
|
214
|
+
}
|
|
193
215
|
this.instrumentation.updateL1BlockHeight(currentL1BlockNumber);
|
|
194
216
|
}
|
|
195
217
|
// After syncing has completed, update the current l1 block number and timestamp,
|
|
@@ -206,7 +228,7 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
206
228
|
});
|
|
207
229
|
}
|
|
208
230
|
}
|
|
209
|
-
/** Queries the rollup contract on whether a prune can be executed on the
|
|
231
|
+
/** Queries the rollup contract on whether a prune can be executed on the immediate next L1 block. */ async canPrune(currentL1BlockNumber, currentL1Timestamp) {
|
|
210
232
|
const time = (currentL1Timestamp ?? 0n) + BigInt(this.l1constants.ethereumSlotDuration);
|
|
211
233
|
const result = await this.rollup.canPruneAtTime(time, {
|
|
212
234
|
blockNumber: currentL1BlockNumber
|
|
@@ -221,8 +243,9 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
221
243
|
return result;
|
|
222
244
|
}
|
|
223
245
|
/** Checks if there'd be a reorg for the next block submission and start pruning now. */ async handleEpochPrune(provenBlockNumber, currentL1BlockNumber, currentL1Timestamp) {
|
|
246
|
+
const rollupCanPrune = await this.canPrune(currentL1BlockNumber, currentL1Timestamp);
|
|
224
247
|
const localPendingBlockNumber = BigInt(await this.getBlockNumber());
|
|
225
|
-
const canPrune = localPendingBlockNumber > provenBlockNumber &&
|
|
248
|
+
const canPrune = localPendingBlockNumber > provenBlockNumber && rollupCanPrune;
|
|
226
249
|
if (canPrune) {
|
|
227
250
|
const pruneFrom = provenBlockNumber + 1n;
|
|
228
251
|
const header = await this.getBlockHeader(Number(pruneFrom));
|
|
@@ -247,6 +270,9 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
247
270
|
// Seems like the next iteration should handle this.
|
|
248
271
|
// await this.store.setBlockSynchedL1BlockNumber(currentL1BlockNumber);
|
|
249
272
|
}
|
|
273
|
+
return {
|
|
274
|
+
rollupCanPrune
|
|
275
|
+
};
|
|
250
276
|
}
|
|
251
277
|
nextRange(end, limit) {
|
|
252
278
|
const batchSize = this.config.batchSize * this.l1constants.slotDuration / this.l1constants.ethereumSlotDuration;
|
|
@@ -263,41 +289,172 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
263
289
|
nextEnd
|
|
264
290
|
];
|
|
265
291
|
}
|
|
266
|
-
async handleL1ToL2Messages(
|
|
267
|
-
this.log.trace(`Handling L1 to L2 messages from ${
|
|
268
|
-
if (currentL1BlockNumber <=
|
|
292
|
+
async handleL1ToL2Messages(messagesSyncPoint, currentL1BlockNumber, currentL1BlockHash) {
|
|
293
|
+
this.log.trace(`Handling L1 to L2 messages from ${messagesSyncPoint.l1BlockNumber} to ${currentL1BlockNumber}.`);
|
|
294
|
+
if (currentL1BlockNumber <= messagesSyncPoint.l1BlockNumber) {
|
|
269
295
|
return;
|
|
270
296
|
}
|
|
271
|
-
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
297
|
+
// Load remote and local inbox states.
|
|
298
|
+
const localMessagesInserted = await this.store.getTotalL1ToL2MessageCount();
|
|
299
|
+
const localLastMessage = await this.store.getLastL1ToL2Message();
|
|
300
|
+
const remoteMessagesState = await this.inbox.getState({
|
|
301
|
+
blockNumber: currentL1BlockNumber
|
|
302
|
+
});
|
|
303
|
+
this.log.trace(`Retrieved remote inbox state at L1 block ${currentL1BlockNumber}.`, {
|
|
304
|
+
localMessagesInserted,
|
|
305
|
+
localLastMessage,
|
|
306
|
+
remoteMessagesState
|
|
307
|
+
});
|
|
308
|
+
// Compare message count and rolling hash. If they match, no need to retrieve anything.
|
|
309
|
+
if (remoteMessagesState.totalMessagesInserted === localMessagesInserted && remoteMessagesState.messagesRollingHash.equals(localLastMessage?.rollingHash ?? Buffer16.ZERO)) {
|
|
310
|
+
this.log.debug(`No L1 to L2 messages to query between L1 blocks ${messagesSyncPoint.l1BlockNumber} and ${currentL1BlockNumber}.`);
|
|
311
|
+
await this.store.setMessageSynchedL1Block({
|
|
312
|
+
l1BlockHash: currentL1BlockHash,
|
|
313
|
+
l1BlockNumber: currentL1BlockNumber
|
|
314
|
+
});
|
|
276
315
|
return;
|
|
277
316
|
}
|
|
278
|
-
//
|
|
279
|
-
|
|
280
|
-
|
|
317
|
+
// Check if our syncpoint is still valid. If not, there was an L1 reorg and we need to re-retrieve messages.
|
|
318
|
+
// Note that we need to fetch it from logs and not from inbox state at the syncpoint l1 block number, since it
|
|
319
|
+
// could be older than 128 blocks and non-archive nodes cannot resolve it.
|
|
320
|
+
if (localLastMessage) {
|
|
321
|
+
const remoteLastMessage = await this.retrieveL1ToL2Message(localLastMessage.leaf);
|
|
322
|
+
this.log.trace(`Retrieved remote message for local last`, {
|
|
323
|
+
remoteLastMessage,
|
|
324
|
+
localLastMessage
|
|
325
|
+
});
|
|
326
|
+
if (!remoteLastMessage || !remoteLastMessage.rollingHash.equals(localLastMessage.rollingHash)) {
|
|
327
|
+
this.log.warn(`Rolling back L1 to L2 messages due to hash mismatch or msg not found.`, {
|
|
328
|
+
remoteLastMessage,
|
|
329
|
+
messagesSyncPoint,
|
|
330
|
+
localLastMessage
|
|
331
|
+
});
|
|
332
|
+
messagesSyncPoint = await this.rollbackL1ToL2Messages(localLastMessage, messagesSyncPoint);
|
|
333
|
+
this.log.debug(`Rolled back L1 to L2 messages to L1 block ${messagesSyncPoint.l1BlockNumber}.`, {
|
|
334
|
+
messagesSyncPoint
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
// Retrieve and save messages in batches. Each batch is estimated to acommodate up to L2 'blockBatchSize' blocks,
|
|
339
|
+
let searchStartBlock = 0n;
|
|
340
|
+
let searchEndBlock = messagesSyncPoint.l1BlockNumber;
|
|
341
|
+
let lastMessage;
|
|
342
|
+
let messageCount = 0;
|
|
281
343
|
do {
|
|
282
344
|
[searchStartBlock, searchEndBlock] = this.nextRange(searchEndBlock, currentL1BlockNumber);
|
|
283
345
|
this.log.trace(`Retrieving L1 to L2 messages between L1 blocks ${searchStartBlock} and ${searchEndBlock}.`);
|
|
284
|
-
const
|
|
285
|
-
this.log.verbose(`Retrieved ${
|
|
286
|
-
await this.store.addL1ToL2Messages(
|
|
287
|
-
for (const msg of
|
|
346
|
+
const messages = await retrieveL1ToL2Messages(this.inbox.getContract(), searchStartBlock, searchEndBlock);
|
|
347
|
+
this.log.verbose(`Retrieved ${messages.length} new L1 to L2 messages between L1 blocks ${searchStartBlock} and ${searchEndBlock}.`);
|
|
348
|
+
await this.store.addL1ToL2Messages(messages);
|
|
349
|
+
for (const msg of messages){
|
|
288
350
|
this.log.debug(`Downloaded L1 to L2 message`, {
|
|
289
|
-
|
|
290
|
-
|
|
351
|
+
...msg,
|
|
352
|
+
leaf: msg.leaf.toString()
|
|
291
353
|
});
|
|
354
|
+
lastMessage = msg;
|
|
355
|
+
messageCount++;
|
|
292
356
|
}
|
|
293
357
|
}while (searchEndBlock < currentL1BlockNumber)
|
|
358
|
+
// Log stats for messages retrieved (if any).
|
|
359
|
+
if (messageCount > 0) {
|
|
360
|
+
this.log.info(`Retrieved ${messageCount} new L1 to L2 messages up to message with index ${lastMessage?.index} for L2 block ${lastMessage?.l2BlockNumber}`, {
|
|
361
|
+
lastMessage,
|
|
362
|
+
messageCount
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
// Warn if the resulting rolling hash does not match the remote state we had retrieved.
|
|
366
|
+
if (lastMessage && !lastMessage.rollingHash.equals(remoteMessagesState.messagesRollingHash)) {
|
|
367
|
+
this.log.warn(`Last message retrieved rolling hash does not match remote state.`, {
|
|
368
|
+
lastMessage,
|
|
369
|
+
remoteMessagesState
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
retrieveL1ToL2Message(leaf) {
|
|
374
|
+
return retrieveL1ToL2Message(this.inbox.getContract(), leaf, this.l1constants.l1StartBlock);
|
|
375
|
+
}
|
|
376
|
+
async rollbackL1ToL2Messages(localLastMessage, messagesSyncPoint) {
|
|
377
|
+
// Slowly go back through our messages until we find the last common message.
|
|
378
|
+
// We could query the logs in batch as an optimization, but the depth of the reorg should not be deep, and this
|
|
379
|
+
// is a very rare case, so it's fine to query one log at a time.
|
|
380
|
+
let commonMsg;
|
|
381
|
+
this.log.verbose(`Searching most recent common L1 to L2 message at or before index ${localLastMessage.index}`);
|
|
382
|
+
for await (const msg of this.store.iterateL1ToL2Messages({
|
|
383
|
+
reverse: true,
|
|
384
|
+
end: localLastMessage.index
|
|
385
|
+
})){
|
|
386
|
+
const remoteMsg = await this.retrieveL1ToL2Message(msg.leaf);
|
|
387
|
+
const logCtx = {
|
|
388
|
+
remoteMsg,
|
|
389
|
+
localMsg: msg
|
|
390
|
+
};
|
|
391
|
+
if (remoteMsg && remoteMsg.rollingHash.equals(msg.rollingHash)) {
|
|
392
|
+
this.log.verbose(`Found most recent common L1 to L2 message at index ${msg.index} on L1 block ${msg.l1BlockNumber}`, logCtx);
|
|
393
|
+
commonMsg = remoteMsg;
|
|
394
|
+
break;
|
|
395
|
+
} else if (remoteMsg) {
|
|
396
|
+
this.log.debug(`Local L1 to L2 message with index ${msg.index} has different rolling hash`, logCtx);
|
|
397
|
+
} else {
|
|
398
|
+
this.log.debug(`Local L1 to L2 message with index ${msg.index} not found on L1`, logCtx);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
// Delete everything after the common message we found.
|
|
402
|
+
const lastGoodIndex = commonMsg?.index;
|
|
403
|
+
this.log.warn(`Deleting all local L1 to L2 messages after index ${lastGoodIndex ?? 'undefined'}`);
|
|
404
|
+
await this.store.removeL1ToL2Messages(lastGoodIndex !== undefined ? lastGoodIndex + 1n : 0n);
|
|
405
|
+
// Update the syncpoint so the loop below reprocesses the changed messages. We go to the block before
|
|
406
|
+
// the last common one, so we force reprocessing it, in case new messages were added on that same L1 block
|
|
407
|
+
// after the last common message.
|
|
408
|
+
const syncPointL1BlockNumber = commonMsg ? commonMsg.l1BlockNumber - 1n : this.l1constants.l1StartBlock;
|
|
409
|
+
const syncPointL1BlockHash = await this.getL1BlockHash(syncPointL1BlockNumber);
|
|
410
|
+
messagesSyncPoint = {
|
|
411
|
+
l1BlockNumber: syncPointL1BlockNumber,
|
|
412
|
+
l1BlockHash: syncPointL1BlockHash
|
|
413
|
+
};
|
|
414
|
+
await this.store.setMessageSynchedL1Block(messagesSyncPoint);
|
|
415
|
+
return messagesSyncPoint;
|
|
416
|
+
}
|
|
417
|
+
async getL1BlockHash(l1BlockNumber) {
|
|
418
|
+
const block = await this.publicClient.getBlock({
|
|
419
|
+
blockNumber: l1BlockNumber,
|
|
420
|
+
includeTransactions: false
|
|
421
|
+
});
|
|
422
|
+
if (!block) {
|
|
423
|
+
throw new Error(`Missing L1 block ${l1BlockNumber}`);
|
|
424
|
+
}
|
|
425
|
+
return Buffer32.fromString(block.hash);
|
|
294
426
|
}
|
|
295
427
|
async handleL2blocks(blocksSynchedTo, currentL1BlockNumber) {
|
|
296
428
|
const localPendingBlockNumber = BigInt(await this.getBlockNumber());
|
|
297
429
|
const [provenBlockNumber, provenArchive, pendingBlockNumber, pendingArchive, archiveForLocalPendingBlockNumber] = await this.rollup.status(localPendingBlockNumber, {
|
|
298
430
|
blockNumber: currentL1BlockNumber
|
|
299
431
|
});
|
|
432
|
+
const rollupStatus = {
|
|
433
|
+
provenBlockNumber,
|
|
434
|
+
provenArchive,
|
|
435
|
+
pendingBlockNumber,
|
|
436
|
+
pendingArchive
|
|
437
|
+
};
|
|
438
|
+
this.log.trace(`Retrieved rollup status at current L1 block ${currentL1BlockNumber}.`, {
|
|
439
|
+
localPendingBlockNumber,
|
|
440
|
+
blocksSynchedTo,
|
|
441
|
+
currentL1BlockNumber,
|
|
442
|
+
archiveForLocalPendingBlockNumber,
|
|
443
|
+
...rollupStatus
|
|
444
|
+
});
|
|
300
445
|
const updateProvenBlock = async ()=>{
|
|
446
|
+
// Annoying edge case: if proven block is moved back to 0 due to a reorg at the beginning of the chain,
|
|
447
|
+
// we need to set it to zero. This is an edge case because we dont have a block zero (initial block is one),
|
|
448
|
+
// so localBlockForDestinationProvenBlockNumber would not be found below.
|
|
449
|
+
if (provenBlockNumber === 0n) {
|
|
450
|
+
const localProvenBlockNumber = await this.store.getProvenL2BlockNumber();
|
|
451
|
+
if (localProvenBlockNumber !== Number(provenBlockNumber)) {
|
|
452
|
+
await this.store.setProvenL2BlockNumber(Number(provenBlockNumber));
|
|
453
|
+
this.log.info(`Rolled back proven chain to block ${provenBlockNumber}`, {
|
|
454
|
+
provenBlockNumber
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
}
|
|
301
458
|
const localBlockForDestinationProvenBlockNumber = await this.getBlock(Number(provenBlockNumber));
|
|
302
459
|
// Sanity check. I've hit what seems to be a state where the proven block is set to a value greater than the latest
|
|
303
460
|
// synched block when requesting L2Tips from the archiver. This is the only place where the proven block is set.
|
|
@@ -305,6 +462,7 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
305
462
|
if (localBlockForDestinationProvenBlockNumber && synched < localBlockForDestinationProvenBlockNumber?.number) {
|
|
306
463
|
this.log.error(`Hit local block greater than last synched block: ${localBlockForDestinationProvenBlockNumber.number} > ${synched}`);
|
|
307
464
|
}
|
|
465
|
+
this.log.trace(`Local block for remote proven block ${provenBlockNumber} is ${localBlockForDestinationProvenBlockNumber?.archive.root.toString() ?? 'undefined'}`);
|
|
308
466
|
if (localBlockForDestinationProvenBlockNumber && provenArchive === localBlockForDestinationProvenBlockNumber.archive.root.toString()) {
|
|
309
467
|
const localProvenBlockNumber = await this.store.getProvenL2BlockNumber();
|
|
310
468
|
if (localProvenBlockNumber !== Number(provenBlockNumber)) {
|
|
@@ -312,6 +470,8 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
312
470
|
this.log.info(`Updated proven chain to block ${provenBlockNumber}`, {
|
|
313
471
|
provenBlockNumber
|
|
314
472
|
});
|
|
473
|
+
} else {
|
|
474
|
+
this.log.trace(`Proven block ${provenBlockNumber} already stored.`);
|
|
315
475
|
}
|
|
316
476
|
}
|
|
317
477
|
this.instrumentation.updateLastProvenBlock(Number(provenBlockNumber));
|
|
@@ -322,9 +482,7 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
322
482
|
if (noBlocks) {
|
|
323
483
|
await this.store.setBlockSynchedL1BlockNumber(currentL1BlockNumber);
|
|
324
484
|
this.log.debug(`No blocks to retrieve from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}, no blocks on chain`);
|
|
325
|
-
return
|
|
326
|
-
provenBlockNumber
|
|
327
|
-
};
|
|
485
|
+
return rollupStatus;
|
|
328
486
|
}
|
|
329
487
|
await updateProvenBlock();
|
|
330
488
|
// Related to the L2 reorgs of the pending chain. We are only interested in actually addressing a reorg if there
|
|
@@ -342,12 +500,10 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
342
500
|
// this block again (or any blocks before).
|
|
343
501
|
// However, in the re-org scenario, our L1 node is temporarily lying to us and we end up potentially missing blocks
|
|
344
502
|
// We must only set this block number based on actually retrieved logs.
|
|
345
|
-
// TODO(
|
|
346
|
-
//await this.store.setBlockSynchedL1BlockNumber(currentL1BlockNumber);
|
|
503
|
+
// TODO(#8621): Tackle this properly when we handle L1 Re-orgs.
|
|
504
|
+
// await this.store.setBlockSynchedL1BlockNumber(currentL1BlockNumber);
|
|
347
505
|
this.log.debug(`No blocks to retrieve from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
|
|
348
|
-
return
|
|
349
|
-
provenBlockNumber
|
|
350
|
-
};
|
|
506
|
+
return rollupStatus;
|
|
351
507
|
}
|
|
352
508
|
const localPendingBlockInChain = archiveForLocalPendingBlockNumber === localPendingBlock.archive.root.toString();
|
|
353
509
|
if (!localPendingBlockInChain) {
|
|
@@ -377,6 +533,7 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
377
533
|
// computed using the L2 block time vs the L1 block time.
|
|
378
534
|
let searchStartBlock = blocksSynchedTo;
|
|
379
535
|
let searchEndBlock = blocksSynchedTo;
|
|
536
|
+
let lastRetrievedBlock;
|
|
380
537
|
do {
|
|
381
538
|
[searchStartBlock, searchEndBlock] = this.nextRange(searchEndBlock, currentL1BlockNumber);
|
|
382
539
|
this.log.trace(`Retrieving L2 blocks from L1 block ${searchStartBlock} to ${searchEndBlock}`);
|
|
@@ -390,7 +547,8 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
390
547
|
}
|
|
391
548
|
const lastProcessedL1BlockNumber = retrievedBlocks[retrievedBlocks.length - 1].l1.blockNumber;
|
|
392
549
|
this.log.debug(`Retrieved ${retrievedBlocks.length} new L2 blocks between L1 blocks ${searchStartBlock} and ${searchEndBlock} with last processed L1 block ${lastProcessedL1BlockNumber}.`);
|
|
393
|
-
|
|
550
|
+
const publishedBlocks = retrievedBlocks.map((b)=>retrievedBlockToPublishedL2Block(b));
|
|
551
|
+
for (const block of publishedBlocks){
|
|
394
552
|
this.log.debug(`Ingesting new L2 block ${block.block.number} with ${block.block.body.txEffects.length} txs`, {
|
|
395
553
|
blockHash: block.block.hash(),
|
|
396
554
|
l1BlockNumber: block.l1.blockNumber,
|
|
@@ -398,23 +556,72 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
398
556
|
...block.block.getStats()
|
|
399
557
|
});
|
|
400
558
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
559
|
+
try {
|
|
560
|
+
const [processDuration] = await elapsed(()=>this.store.addBlocks(publishedBlocks));
|
|
561
|
+
this.instrumentation.processNewBlocks(processDuration / publishedBlocks.length, publishedBlocks.map((b)=>b.block));
|
|
562
|
+
} catch (err) {
|
|
563
|
+
if (err instanceof InitialBlockNumberNotSequentialError) {
|
|
564
|
+
const { previousBlockNumber, newBlockNumber } = err;
|
|
565
|
+
const previousBlock = previousBlockNumber ? await this.store.getPublishedBlock(previousBlockNumber) : undefined;
|
|
566
|
+
const updatedL1SyncPoint = previousBlock?.l1.blockNumber ?? this.l1constants.l1StartBlock;
|
|
567
|
+
await this.store.setBlockSynchedL1BlockNumber(updatedL1SyncPoint);
|
|
568
|
+
this.log.warn(`Attempting to insert block ${newBlockNumber} with previous block ${previousBlockNumber}. Rolling back L1 sync point to ${updatedL1SyncPoint} to try and fetch the missing blocks.`, {
|
|
569
|
+
previousBlockNumber,
|
|
570
|
+
previousBlockHash: await previousBlock?.block.hash(),
|
|
571
|
+
newBlockNumber,
|
|
572
|
+
updatedL1SyncPoint
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
throw err;
|
|
576
|
+
}
|
|
577
|
+
for (const block of publishedBlocks){
|
|
404
578
|
this.log.info(`Downloaded L2 block ${block.block.number}`, {
|
|
405
|
-
blockHash: block.block.hash(),
|
|
579
|
+
blockHash: await block.block.hash(),
|
|
406
580
|
blockNumber: block.block.number,
|
|
407
581
|
txCount: block.block.body.txEffects.length,
|
|
408
|
-
globalVariables: block.block.header.globalVariables.toInspect()
|
|
582
|
+
globalVariables: block.block.header.globalVariables.toInspect(),
|
|
583
|
+
archiveRoot: block.block.archive.root.toString(),
|
|
584
|
+
archiveNextLeafIndex: block.block.archive.nextAvailableLeafIndex
|
|
409
585
|
});
|
|
410
586
|
}
|
|
587
|
+
lastRetrievedBlock = publishedBlocks.at(-1) ?? lastRetrievedBlock;
|
|
411
588
|
}while (searchEndBlock < currentL1BlockNumber)
|
|
412
589
|
// Important that we update AFTER inserting the blocks.
|
|
413
590
|
await updateProvenBlock();
|
|
414
591
|
return {
|
|
415
|
-
|
|
592
|
+
...rollupStatus,
|
|
593
|
+
lastRetrievedBlock
|
|
416
594
|
};
|
|
417
595
|
}
|
|
596
|
+
async checkForNewBlocksBeforeL1SyncPoint(status, blocksSynchedTo, currentL1BlockNumber) {
|
|
597
|
+
const { lastRetrievedBlock, pendingBlockNumber } = status;
|
|
598
|
+
// Compare the last L2 block we have (either retrieved in this round or loaded from store) with what the
|
|
599
|
+
// rollup contract told us was the latest one (pinned at the currentL1BlockNumber).
|
|
600
|
+
const latestLocalL2BlockNumber = lastRetrievedBlock?.block.number ?? await this.store.getSynchedL2BlockNumber();
|
|
601
|
+
if (latestLocalL2BlockNumber < pendingBlockNumber) {
|
|
602
|
+
// Here we have consumed all logs until the `currentL1Block` we pinned at the beginning of the archiver loop,
|
|
603
|
+
// but still havent reached the pending block according to the call to the rollup contract.
|
|
604
|
+
// We suspect an L1 reorg that added blocks *behind* us. If that is the case, it must have happened between the
|
|
605
|
+
// last L2 block we saw and the current one, so we reset the last synched L1 block number. In the edge case we
|
|
606
|
+
// don't have one, we go back 2 L1 epochs, which is the deepest possible reorg (assuming Casper is working).
|
|
607
|
+
const latestLocalL2Block = lastRetrievedBlock ?? (latestLocalL2BlockNumber > 0 ? await this.store.getPublishedBlocks(latestLocalL2BlockNumber, 1).then(([b])=>b) : undefined);
|
|
608
|
+
const targetL1BlockNumber = latestLocalL2Block?.l1.blockNumber ?? maxBigint(currentL1BlockNumber - 64n, 0n);
|
|
609
|
+
const latestLocalL2BlockArchive = latestLocalL2Block?.block.archive.root.toString();
|
|
610
|
+
this.log.warn(`Failed to reach L2 block ${pendingBlockNumber} at ${currentL1BlockNumber} (latest is ${latestLocalL2BlockNumber}). ` + `Rolling back last synched L1 block number to ${targetL1BlockNumber}.`, {
|
|
611
|
+
latestLocalL2BlockNumber,
|
|
612
|
+
latestLocalL2BlockArchive,
|
|
613
|
+
blocksSynchedTo,
|
|
614
|
+
currentL1BlockNumber,
|
|
615
|
+
...status
|
|
616
|
+
});
|
|
617
|
+
await this.store.setBlockSynchedL1BlockNumber(targetL1BlockNumber);
|
|
618
|
+
} else {
|
|
619
|
+
this.log.trace(`No new blocks behind L1 sync point to retrieve.`, {
|
|
620
|
+
latestLocalL2BlockNumber,
|
|
621
|
+
pendingBlockNumber
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
}
|
|
418
625
|
/** Resumes the archiver after a stop. */ resume() {
|
|
419
626
|
if (!this.runningPromise) {
|
|
420
627
|
throw new Error(`Archiver was never started`);
|
|
@@ -535,7 +742,7 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
535
742
|
}
|
|
536
743
|
/** Equivalent to getBlocks but includes publish data. */ async getPublishedBlocks(from, limit, proven) {
|
|
537
744
|
const limitWithProven = proven ? Math.min(limit, Math.max(await this.store.getProvenL2BlockNumber() - from + 1, 0)) : limit;
|
|
538
|
-
return limitWithProven === 0 ? [] : await this.store.
|
|
745
|
+
return limitWithProven === 0 ? [] : await this.store.getPublishedBlocks(from, limitWithProven);
|
|
539
746
|
}
|
|
540
747
|
/**
|
|
541
748
|
* Gets an l2 block.
|
|
@@ -546,11 +753,11 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
546
753
|
if (number < 0) {
|
|
547
754
|
number = await this.store.getSynchedL2BlockNumber();
|
|
548
755
|
}
|
|
549
|
-
if (number
|
|
756
|
+
if (number === 0) {
|
|
550
757
|
return undefined;
|
|
551
758
|
}
|
|
552
|
-
const
|
|
553
|
-
return
|
|
759
|
+
const publishedBlock = await this.store.getPublishedBlock(number);
|
|
760
|
+
return publishedBlock?.block;
|
|
554
761
|
}
|
|
555
762
|
async getBlockHeader(number) {
|
|
556
763
|
if (number === 'latest') {
|
|
@@ -647,9 +854,13 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
647
854
|
this.getBlockNumber(),
|
|
648
855
|
this.getProvenBlockNumber()
|
|
649
856
|
]);
|
|
650
|
-
|
|
857
|
+
// TODO(#13569): Compute proper finalized block number based on L1 finalized block.
|
|
858
|
+
// We just force it 2 epochs worth of proven data for now.
|
|
859
|
+
const finalizedBlockNumber = Math.max(provenBlockNumber - this.l1constants.epochDuration * 2, 0);
|
|
860
|
+
const [latestBlockHeader, provenBlockHeader, finalizedBlockHeader] = await Promise.all([
|
|
651
861
|
latestBlockNumber > 0 ? this.getBlockHeader(latestBlockNumber) : undefined,
|
|
652
|
-
provenBlockNumber > 0 ? this.getBlockHeader(provenBlockNumber) : undefined
|
|
862
|
+
provenBlockNumber > 0 ? this.getBlockHeader(provenBlockNumber) : undefined,
|
|
863
|
+
finalizedBlockNumber > 0 ? this.getBlockHeader(finalizedBlockNumber) : undefined
|
|
653
864
|
]);
|
|
654
865
|
if (latestBlockNumber > 0 && !latestBlockHeader) {
|
|
655
866
|
throw new Error(`Failed to retrieve latest block header for block ${latestBlockNumber}`);
|
|
@@ -657,9 +868,12 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
657
868
|
if (provenBlockNumber > 0 && !provenBlockHeader) {
|
|
658
869
|
throw new Error(`Failed to retrieve proven block header for block ${provenBlockNumber} (latest block is ${latestBlockNumber})`);
|
|
659
870
|
}
|
|
871
|
+
if (finalizedBlockNumber > 0 && !finalizedBlockHeader) {
|
|
872
|
+
throw new Error(`Failed to retrieve finalized block header for block ${finalizedBlockNumber} (latest block is ${latestBlockNumber})`);
|
|
873
|
+
}
|
|
660
874
|
const latestBlockHeaderHash = await latestBlockHeader?.hash();
|
|
661
875
|
const provenBlockHeaderHash = await provenBlockHeader?.hash();
|
|
662
|
-
const finalizedBlockHeaderHash = await
|
|
876
|
+
const finalizedBlockHeaderHash = await finalizedBlockHeader?.hash();
|
|
663
877
|
return {
|
|
664
878
|
latest: {
|
|
665
879
|
number: latestBlockNumber,
|
|
@@ -670,11 +884,46 @@ import { ArchiverInstrumentation } from './instrumentation.js';
|
|
|
670
884
|
hash: provenBlockHeaderHash?.toString()
|
|
671
885
|
},
|
|
672
886
|
finalized: {
|
|
673
|
-
number:
|
|
887
|
+
number: finalizedBlockNumber,
|
|
674
888
|
hash: finalizedBlockHeaderHash?.toString()
|
|
675
889
|
}
|
|
676
890
|
};
|
|
677
891
|
}
|
|
892
|
+
async rollbackTo(targetL2BlockNumber) {
|
|
893
|
+
const currentBlocks = await this.getL2Tips();
|
|
894
|
+
const currentL2Block = currentBlocks.latest.number;
|
|
895
|
+
const currentProvenBlock = currentBlocks.proven.number;
|
|
896
|
+
// const currentFinalizedBlock = currentBlocks.finalized.number;
|
|
897
|
+
if (targetL2BlockNumber >= currentL2Block) {
|
|
898
|
+
throw new Error(`Target L2 block ${targetL2BlockNumber} must be less than current L2 block ${currentL2Block}`);
|
|
899
|
+
}
|
|
900
|
+
const blocksToUnwind = currentL2Block - targetL2BlockNumber;
|
|
901
|
+
const targetL2Block = await this.store.getPublishedBlock(targetL2BlockNumber);
|
|
902
|
+
if (!targetL2Block) {
|
|
903
|
+
throw new Error(`Target L2 block ${targetL2BlockNumber} not found`);
|
|
904
|
+
}
|
|
905
|
+
const targetL1BlockNumber = targetL2Block.l1.blockNumber;
|
|
906
|
+
const targetL1BlockHash = await this.getL1BlockHash(targetL1BlockNumber);
|
|
907
|
+
this.log.info(`Unwinding ${blocksToUnwind} blocks from L2 block ${currentL2Block}`);
|
|
908
|
+
await this.store.unwindBlocks(currentL2Block, blocksToUnwind);
|
|
909
|
+
this.log.info(`Unwinding L1 to L2 messages to ${targetL2BlockNumber}`);
|
|
910
|
+
await this.store.rollbackL1ToL2MessagesToL2Block(targetL2BlockNumber);
|
|
911
|
+
this.log.info(`Setting L1 syncpoints to ${targetL1BlockNumber}`);
|
|
912
|
+
await this.store.setBlockSynchedL1BlockNumber(targetL1BlockNumber);
|
|
913
|
+
await this.store.setMessageSynchedL1Block({
|
|
914
|
+
l1BlockNumber: targetL1BlockNumber,
|
|
915
|
+
l1BlockHash: targetL1BlockHash
|
|
916
|
+
});
|
|
917
|
+
if (targetL2BlockNumber < currentProvenBlock) {
|
|
918
|
+
this.log.info(`Clearing proven L2 block number`);
|
|
919
|
+
await this.store.setProvenL2BlockNumber(0);
|
|
920
|
+
}
|
|
921
|
+
// TODO(palla/reorg): Set the finalized block when we add support for it.
|
|
922
|
+
// if (targetL2BlockNumber < currentFinalizedBlock) {
|
|
923
|
+
// this.log.info(`Clearing finalized L2 block number`);
|
|
924
|
+
// await this.store.setFinalizedL2BlockNumber(0);
|
|
925
|
+
// }
|
|
926
|
+
}
|
|
678
927
|
}
|
|
679
928
|
_ts_decorate([
|
|
680
929
|
trackSpan('Archiver.sync', (initialRun)=>({
|
|
@@ -691,7 +940,7 @@ var Operation = /*#__PURE__*/ function(Operation) {
|
|
|
691
940
|
*
|
|
692
941
|
* I would have preferred to not have this type. But it is useful for handling the logic that any
|
|
693
942
|
* store would need to include otherwise while exposing fewer functions and logic directly to the archiver.
|
|
694
|
-
*/ class ArchiverStoreHelper {
|
|
943
|
+
*/ export class ArchiverStoreHelper {
|
|
695
944
|
store;
|
|
696
945
|
#log;
|
|
697
946
|
constructor(store){
|
|
@@ -796,33 +1045,40 @@ var Operation = /*#__PURE__*/ function(Operation) {
|
|
|
796
1045
|
}
|
|
797
1046
|
return true;
|
|
798
1047
|
}
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
1048
|
+
addBlocks(blocks) {
|
|
1049
|
+
// Add the blocks to the store. Store will throw if the blocks are not in order, there are gaps,
|
|
1050
|
+
// or if the previous block is not in the store.
|
|
1051
|
+
return this.store.transactionAsync(async ()=>{
|
|
1052
|
+
await this.store.addBlocks(blocks);
|
|
1053
|
+
const opResults = await Promise.all([
|
|
1054
|
+
this.store.addLogs(blocks.map((block)=>block.block)),
|
|
1055
|
+
// Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
|
|
1056
|
+
...blocks.map(async (block)=>{
|
|
1057
|
+
const contractClassLogs = block.block.body.txEffects.flatMap((txEffect)=>txEffect.contractClassLogs);
|
|
1058
|
+
// ContractInstanceDeployed event logs are broadcast in privateLogs.
|
|
1059
|
+
const privateLogs = block.block.body.txEffects.flatMap((txEffect)=>txEffect.privateLogs);
|
|
1060
|
+
const publicLogs = block.block.body.txEffects.flatMap((txEffect)=>txEffect.publicLogs);
|
|
1061
|
+
return (await Promise.all([
|
|
1062
|
+
this.#updateRegisteredContractClasses(contractClassLogs, block.block.number, 0),
|
|
1063
|
+
this.#updateDeployedContractInstances(privateLogs, block.block.number, 0),
|
|
1064
|
+
this.#updateUpdatedContractInstances(publicLogs, block.block.number, 0),
|
|
1065
|
+
this.#storeBroadcastedIndividualFunctions(contractClassLogs, block.block.number)
|
|
1066
|
+
])).every(Boolean);
|
|
1067
|
+
})
|
|
1068
|
+
]);
|
|
1069
|
+
return opResults.every(Boolean);
|
|
1070
|
+
});
|
|
818
1071
|
}
|
|
819
1072
|
async unwindBlocks(from, blocksToUnwind) {
|
|
820
1073
|
const last = await this.getSynchedL2BlockNumber();
|
|
821
1074
|
if (from != last) {
|
|
822
|
-
throw new Error(`
|
|
1075
|
+
throw new Error(`Cannot unwind blocks from block ${from} when the last block is ${last}`);
|
|
1076
|
+
}
|
|
1077
|
+
if (blocksToUnwind <= 0) {
|
|
1078
|
+
throw new Error(`Cannot unwind ${blocksToUnwind} blocks`);
|
|
823
1079
|
}
|
|
824
1080
|
// from - blocksToUnwind = the new head, so + 1 for what we need to remove
|
|
825
|
-
const blocks = await this.
|
|
1081
|
+
const blocks = await this.getPublishedBlocks(from - blocksToUnwind + 1, blocksToUnwind);
|
|
826
1082
|
const opResults = await Promise.all([
|
|
827
1083
|
// Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
|
|
828
1084
|
...blocks.map(async (block)=>{
|
|
@@ -841,8 +1097,11 @@ var Operation = /*#__PURE__*/ function(Operation) {
|
|
|
841
1097
|
]);
|
|
842
1098
|
return opResults.every(Boolean);
|
|
843
1099
|
}
|
|
844
|
-
|
|
845
|
-
return this.store.
|
|
1100
|
+
getPublishedBlocks(from, limit) {
|
|
1101
|
+
return this.store.getPublishedBlocks(from, limit);
|
|
1102
|
+
}
|
|
1103
|
+
getPublishedBlock(number) {
|
|
1104
|
+
return this.store.getPublishedBlock(number);
|
|
846
1105
|
}
|
|
847
1106
|
getBlockHeaders(from, limit) {
|
|
848
1107
|
return this.store.getBlockHeaders(from, limit);
|
|
@@ -886,8 +1145,8 @@ var Operation = /*#__PURE__*/ function(Operation) {
|
|
|
886
1145
|
setBlockSynchedL1BlockNumber(l1BlockNumber) {
|
|
887
1146
|
return this.store.setBlockSynchedL1BlockNumber(l1BlockNumber);
|
|
888
1147
|
}
|
|
889
|
-
|
|
890
|
-
return this.store.
|
|
1148
|
+
setMessageSynchedL1Block(l1Block) {
|
|
1149
|
+
return this.store.setMessageSynchedL1Block(l1Block);
|
|
891
1150
|
}
|
|
892
1151
|
getSynchPoint() {
|
|
893
1152
|
return this.store.getSynchPoint();
|
|
@@ -916,4 +1175,16 @@ var Operation = /*#__PURE__*/ function(Operation) {
|
|
|
916
1175
|
estimateSize() {
|
|
917
1176
|
return this.store.estimateSize();
|
|
918
1177
|
}
|
|
1178
|
+
rollbackL1ToL2MessagesToL2Block(targetBlockNumber) {
|
|
1179
|
+
return this.store.rollbackL1ToL2MessagesToL2Block(targetBlockNumber);
|
|
1180
|
+
}
|
|
1181
|
+
iterateL1ToL2Messages(range = {}) {
|
|
1182
|
+
return this.store.iterateL1ToL2Messages(range);
|
|
1183
|
+
}
|
|
1184
|
+
removeL1ToL2Messages(startIndex) {
|
|
1185
|
+
return this.store.removeL1ToL2Messages(startIndex);
|
|
1186
|
+
}
|
|
1187
|
+
getLastL1ToL2Message() {
|
|
1188
|
+
return this.store.getLastL1ToL2Message();
|
|
1189
|
+
}
|
|
919
1190
|
}
|