@aztec/archiver 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/archiver.d.ts +1 -1
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +28 -14
- package/dest/errors.d.ts +7 -9
- package/dest/errors.d.ts.map +1 -1
- package/dest/errors.js +9 -14
- package/dest/factory.d.ts +2 -2
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +16 -11
- package/dest/modules/data_store_updater.d.ts +5 -5
- package/dest/modules/data_store_updater.d.ts.map +1 -1
- package/dest/modules/data_store_updater.js +40 -19
- package/dest/store/block_store.d.ts +5 -5
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +30 -48
- package/dest/store/contract_class_store.d.ts +1 -1
- package/dest/store/contract_class_store.d.ts.map +1 -1
- package/dest/store/contract_class_store.js +6 -2
- package/dest/store/contract_instance_store.d.ts +1 -1
- package/dest/store/contract_instance_store.d.ts.map +1 -1
- package/dest/store/contract_instance_store.js +6 -2
- package/dest/store/kv_archiver_store.d.ts +8 -10
- package/dest/store/kv_archiver_store.d.ts.map +1 -1
- package/dest/store/kv_archiver_store.js +8 -9
- package/package.json +13 -13
- package/src/archiver.ts +31 -12
- package/src/errors.ts +10 -24
- package/src/factory.ts +17 -8
- package/src/modules/data_store_updater.ts +42 -22
- package/src/store/block_store.ts +31 -56
- package/src/store/contract_class_store.ts +7 -3
- package/src/store/contract_instance_store.ts +8 -5
- package/src/store/kv_archiver_store.ts +9 -13
|
@@ -14,10 +14,11 @@ import {
|
|
|
14
14
|
import type { L2Block, ValidateCheckpointResult } from '@aztec/stdlib/block';
|
|
15
15
|
import { type PublishedCheckpoint, validateCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
16
16
|
import {
|
|
17
|
+
type ContractClassPublicWithCommitment,
|
|
17
18
|
type ExecutablePrivateFunctionWithMembershipProof,
|
|
18
19
|
type UtilityFunctionWithMembershipProof,
|
|
19
20
|
computeContractAddressFromInstance,
|
|
20
|
-
|
|
21
|
+
computeContractClassId,
|
|
21
22
|
isValidPrivateFunctionMembershipProof,
|
|
22
23
|
isValidUtilityFunctionMembershipProof,
|
|
23
24
|
} from '@aztec/stdlib/contract';
|
|
@@ -54,29 +55,29 @@ export class ArchiverDataStoreUpdater {
|
|
|
54
55
|
) {}
|
|
55
56
|
|
|
56
57
|
/**
|
|
57
|
-
* Adds proposed
|
|
58
|
-
*
|
|
58
|
+
* Adds a proposed block to the store with contract class/instance extraction from logs.
|
|
59
|
+
* This is an uncheckpointed block that has been proposed by the sequencer but not yet included in a checkpoint on L1.
|
|
59
60
|
* Extracts ContractClassPublished, ContractInstancePublished, ContractInstanceUpdated events,
|
|
60
61
|
* and individually broadcasted functions from the block logs.
|
|
61
62
|
*
|
|
62
|
-
* @param
|
|
63
|
+
* @param block - The proposed L2 block to add.
|
|
63
64
|
* @param pendingChainValidationStatus - Optional validation status to set.
|
|
64
65
|
* @returns True if the operation is successful.
|
|
65
66
|
*/
|
|
66
|
-
public async
|
|
67
|
-
|
|
67
|
+
public async addProposedBlock(
|
|
68
|
+
block: L2Block,
|
|
68
69
|
pendingChainValidationStatus?: ValidateCheckpointResult,
|
|
69
70
|
): Promise<boolean> {
|
|
70
71
|
const result = await this.store.transactionAsync(async () => {
|
|
71
|
-
await this.store.
|
|
72
|
+
await this.store.addProposedBlock(block);
|
|
72
73
|
|
|
73
74
|
const opResults = await Promise.all([
|
|
74
75
|
// Update the pending chain validation status if provided
|
|
75
76
|
pendingChainValidationStatus && this.store.setPendingChainValidationStatus(pendingChainValidationStatus),
|
|
76
|
-
// Add any logs emitted during the retrieved
|
|
77
|
-
this.store.addLogs(
|
|
78
|
-
// Unroll all logs emitted during the retrieved
|
|
79
|
-
|
|
77
|
+
// Add any logs emitted during the retrieved block
|
|
78
|
+
this.store.addLogs([block]),
|
|
79
|
+
// Unroll all logs emitted during the retrieved block and extract any contract classes and instances from it
|
|
80
|
+
this.addContractDataToDb(block),
|
|
80
81
|
]);
|
|
81
82
|
|
|
82
83
|
await this.l2TipsCache?.refresh();
|
|
@@ -110,7 +111,7 @@ export class ArchiverDataStoreUpdater {
|
|
|
110
111
|
|
|
111
112
|
await this.store.addCheckpoints(checkpoints);
|
|
112
113
|
|
|
113
|
-
// Filter out blocks that were already inserted via
|
|
114
|
+
// Filter out blocks that were already inserted via addProposedBlock() to avoid duplicating logs/contract data
|
|
114
115
|
const newBlocks = checkpoints
|
|
115
116
|
.flatMap((ch: PublishedCheckpoint) => ch.checkpoint.blocks)
|
|
116
117
|
.filter(b => lastAlreadyInsertedBlockNumber === undefined || b.number > lastAlreadyInsertedBlockNumber);
|
|
@@ -334,18 +335,37 @@ export class ArchiverDataStoreUpdater {
|
|
|
334
335
|
.filter(log => ContractClassPublishedEvent.isContractClassPublishedEvent(log))
|
|
335
336
|
.map(log => ContractClassPublishedEvent.fromLog(log));
|
|
336
337
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
contractClasses.
|
|
340
|
-
|
|
341
|
-
// TODO: Will probably want to create some worker threads to compute these bytecode commitments as they are expensive
|
|
342
|
-
const commitments = await Promise.all(
|
|
343
|
-
contractClasses.map(c => computePublicBytecodeCommitment(c.packedBytecode)),
|
|
344
|
-
);
|
|
345
|
-
return await this.store.addContractClasses(contractClasses, commitments, blockNum);
|
|
346
|
-
} else if (operation == Operation.Delete) {
|
|
338
|
+
if (operation == Operation.Delete) {
|
|
339
|
+
const contractClasses = contractClassPublishedEvents.map(e => e.toContractClassPublic());
|
|
340
|
+
if (contractClasses.length > 0) {
|
|
341
|
+
contractClasses.forEach(c => this.log.verbose(`${Operation[operation]} contract class ${c.id.toString()}`));
|
|
347
342
|
return await this.store.deleteContractClasses(contractClasses, blockNum);
|
|
348
343
|
}
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Compute bytecode commitments and validate class IDs in a single pass.
|
|
348
|
+
const contractClasses: ContractClassPublicWithCommitment[] = [];
|
|
349
|
+
for (const event of contractClassPublishedEvents) {
|
|
350
|
+
const contractClass = await event.toContractClassPublicWithBytecodeCommitment();
|
|
351
|
+
const computedClassId = await computeContractClassId({
|
|
352
|
+
artifactHash: contractClass.artifactHash,
|
|
353
|
+
privateFunctionsRoot: contractClass.privateFunctionsRoot,
|
|
354
|
+
publicBytecodeCommitment: contractClass.publicBytecodeCommitment,
|
|
355
|
+
});
|
|
356
|
+
if (!computedClassId.equals(contractClass.id)) {
|
|
357
|
+
this.log.warn(
|
|
358
|
+
`Skipping contract class with mismatched id at block ${blockNum}. Claimed ${contractClass.id}, computed ${computedClassId}`,
|
|
359
|
+
{ blockNum, contractClassId: event.contractClassId.toString() },
|
|
360
|
+
);
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
contractClasses.push(contractClass);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (contractClasses.length > 0) {
|
|
367
|
+
contractClasses.forEach(c => this.log.verbose(`${Operation[operation]} contract class ${c.id.toString()}`));
|
|
368
|
+
return await this.store.addContractClasses(contractClasses, blockNum);
|
|
349
369
|
}
|
|
350
370
|
return true;
|
|
351
371
|
}
|
package/src/store/block_store.ts
CHANGED
|
@@ -35,15 +35,14 @@ import {
|
|
|
35
35
|
} from '@aztec/stdlib/tx';
|
|
36
36
|
|
|
37
37
|
import {
|
|
38
|
+
BlockAlreadyCheckpointedError,
|
|
38
39
|
BlockArchiveNotConsistentError,
|
|
39
40
|
BlockIndexNotSequentialError,
|
|
40
41
|
BlockNotFoundError,
|
|
41
42
|
BlockNumberNotSequentialError,
|
|
42
43
|
CannotOverwriteCheckpointedBlockError,
|
|
43
44
|
CheckpointNotFoundError,
|
|
44
|
-
CheckpointNumberNotConsistentError,
|
|
45
45
|
CheckpointNumberNotSequentialError,
|
|
46
|
-
InitialBlockNumberNotSequentialError,
|
|
47
46
|
InitialCheckpointNumberNotSequentialError,
|
|
48
47
|
} from '../errors.js';
|
|
49
48
|
|
|
@@ -147,23 +146,18 @@ export class BlockStore {
|
|
|
147
146
|
}
|
|
148
147
|
|
|
149
148
|
/**
|
|
150
|
-
* Append new proposed
|
|
151
|
-
*
|
|
149
|
+
* Append a new proposed block to the store.
|
|
150
|
+
* This is an uncheckpointed block that has been proposed by the sequencer but not yet included in a checkpoint on L1.
|
|
152
151
|
* For checkpointed blocks (already published to L1), use addCheckpoints() instead.
|
|
153
|
-
* @param
|
|
152
|
+
* @param block - The proposed L2 block to be added to the store.
|
|
154
153
|
* @returns True if the operation is successful.
|
|
155
154
|
*/
|
|
156
|
-
async
|
|
157
|
-
if (blocks.length === 0) {
|
|
158
|
-
return true;
|
|
159
|
-
}
|
|
160
|
-
|
|
155
|
+
async addProposedBlock(block: L2Block, opts: { force?: boolean } = {}): Promise<boolean> {
|
|
161
156
|
return await this.db.transactionAsync(async () => {
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
const firstBlockLastArchive = blocks[0].header.lastArchive.root;
|
|
157
|
+
const blockNumber = block.number;
|
|
158
|
+
const blockCheckpointNumber = block.checkpointNumber;
|
|
159
|
+
const blockIndex = block.indexWithinCheckpoint;
|
|
160
|
+
const blockLastArchive = block.header.lastArchive.root;
|
|
167
161
|
|
|
168
162
|
// Extract the latest block and checkpoint numbers
|
|
169
163
|
const previousBlockNumber = await this.getLatestBlockNumber();
|
|
@@ -171,71 +165,52 @@ export class BlockStore {
|
|
|
171
165
|
|
|
172
166
|
// Verify we're not overwriting checkpointed blocks
|
|
173
167
|
const lastCheckpointedBlockNumber = await this.getCheckpointedL2BlockNumber();
|
|
174
|
-
if (!opts.force &&
|
|
175
|
-
|
|
168
|
+
if (!opts.force && blockNumber <= lastCheckpointedBlockNumber) {
|
|
169
|
+
// Check if the proposed block matches the already-checkpointed one
|
|
170
|
+
const existingBlock = await this.getBlock(BlockNumber(blockNumber));
|
|
171
|
+
if (existingBlock && existingBlock.archive.root.equals(block.archive.root)) {
|
|
172
|
+
throw new BlockAlreadyCheckpointedError(blockNumber);
|
|
173
|
+
}
|
|
174
|
+
throw new CannotOverwriteCheckpointedBlockError(blockNumber, lastCheckpointedBlockNumber);
|
|
176
175
|
}
|
|
177
176
|
|
|
178
|
-
// Check that the
|
|
179
|
-
if (!opts.force && previousBlockNumber !==
|
|
180
|
-
throw new
|
|
177
|
+
// Check that the block number is the expected one
|
|
178
|
+
if (!opts.force && previousBlockNumber !== blockNumber - 1) {
|
|
179
|
+
throw new BlockNumberNotSequentialError(blockNumber, previousBlockNumber);
|
|
181
180
|
}
|
|
182
181
|
|
|
183
182
|
// The same check as above but for checkpoints
|
|
184
|
-
if (!opts.force && previousCheckpointNumber !==
|
|
185
|
-
throw new
|
|
183
|
+
if (!opts.force && previousCheckpointNumber !== blockCheckpointNumber - 1) {
|
|
184
|
+
throw new CheckpointNumberNotSequentialError(blockCheckpointNumber, previousCheckpointNumber);
|
|
186
185
|
}
|
|
187
186
|
|
|
188
187
|
// Extract the previous block if there is one and see if it is for the same checkpoint or not
|
|
189
188
|
const previousBlockResult = await this.getBlock(previousBlockNumber);
|
|
190
189
|
|
|
191
|
-
let
|
|
190
|
+
let expectedBlockIndex = 0;
|
|
192
191
|
let previousBlockIndex: number | undefined = undefined;
|
|
193
192
|
if (previousBlockResult !== undefined) {
|
|
194
|
-
if (previousBlockResult.checkpointNumber ===
|
|
193
|
+
if (previousBlockResult.checkpointNumber === blockCheckpointNumber) {
|
|
195
194
|
// The previous block is for the same checkpoint, therefore our index should follow it
|
|
196
195
|
previousBlockIndex = previousBlockResult.indexWithinCheckpoint;
|
|
197
|
-
|
|
196
|
+
expectedBlockIndex = previousBlockIndex + 1;
|
|
198
197
|
}
|
|
199
|
-
if (!previousBlockResult.archive.root.equals(
|
|
198
|
+
if (!previousBlockResult.archive.root.equals(blockLastArchive)) {
|
|
200
199
|
throw new BlockArchiveNotConsistentError(
|
|
201
|
-
|
|
200
|
+
blockNumber,
|
|
202
201
|
previousBlockResult.number,
|
|
203
|
-
|
|
202
|
+
blockLastArchive,
|
|
204
203
|
previousBlockResult.archive.root,
|
|
205
204
|
);
|
|
206
205
|
}
|
|
207
206
|
}
|
|
208
207
|
|
|
209
|
-
// Now check that the
|
|
210
|
-
if (!opts.force &&
|
|
211
|
-
throw new BlockIndexNotSequentialError(
|
|
208
|
+
// Now check that the block has the expected index value
|
|
209
|
+
if (!opts.force && expectedBlockIndex !== blockIndex) {
|
|
210
|
+
throw new BlockIndexNotSequentialError(blockIndex, previousBlockIndex);
|
|
212
211
|
}
|
|
213
212
|
|
|
214
|
-
|
|
215
|
-
let previousBlock: L2Block | undefined = undefined;
|
|
216
|
-
for (const block of blocks) {
|
|
217
|
-
if (!opts.force && previousBlock) {
|
|
218
|
-
if (previousBlock.number + 1 !== block.number) {
|
|
219
|
-
throw new BlockNumberNotSequentialError(block.number, previousBlock.number);
|
|
220
|
-
}
|
|
221
|
-
if (previousBlock.indexWithinCheckpoint + 1 !== block.indexWithinCheckpoint) {
|
|
222
|
-
throw new BlockIndexNotSequentialError(block.indexWithinCheckpoint, previousBlock.indexWithinCheckpoint);
|
|
223
|
-
}
|
|
224
|
-
if (!previousBlock.archive.root.equals(block.header.lastArchive.root)) {
|
|
225
|
-
throw new BlockArchiveNotConsistentError(
|
|
226
|
-
block.number,
|
|
227
|
-
previousBlock.number,
|
|
228
|
-
block.header.lastArchive.root,
|
|
229
|
-
previousBlock.archive.root,
|
|
230
|
-
);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
if (!opts.force && firstBlockCheckpointNumber !== block.checkpointNumber) {
|
|
234
|
-
throw new CheckpointNumberNotConsistentError(block.checkpointNumber, firstBlockCheckpointNumber);
|
|
235
|
-
}
|
|
236
|
-
previousBlock = block;
|
|
237
|
-
await this.addBlockToDatabase(block, block.checkpointNumber, block.indexWithinCheckpoint);
|
|
238
|
-
}
|
|
213
|
+
await this.addBlockToDatabase(block, block.checkpointNumber, block.indexWithinCheckpoint);
|
|
239
214
|
|
|
240
215
|
return true;
|
|
241
216
|
});
|
|
@@ -29,11 +29,15 @@ export class ContractClassStore {
|
|
|
29
29
|
blockNumber: number,
|
|
30
30
|
): Promise<void> {
|
|
31
31
|
await this.db.transactionAsync(async () => {
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
const key = contractClass.id.toString();
|
|
33
|
+
if (await this.#contractClasses.hasAsync(key)) {
|
|
34
|
+
throw new Error(`Contract class ${key} already exists, cannot add again at block ${blockNumber}`);
|
|
35
|
+
}
|
|
36
|
+
await this.#contractClasses.set(
|
|
37
|
+
key,
|
|
34
38
|
serializeContractClassPublic({ ...contractClass, l2BlockNumber: blockNumber }),
|
|
35
39
|
);
|
|
36
|
-
await this.#bytecodeCommitments.
|
|
40
|
+
await this.#bytecodeCommitments.set(key, bytecodeCommitment.toBuffer());
|
|
37
41
|
});
|
|
38
42
|
}
|
|
39
43
|
|
|
@@ -27,11 +27,14 @@ export class ContractInstanceStore {
|
|
|
27
27
|
|
|
28
28
|
addContractInstance(contractInstance: ContractInstanceWithAddress, blockNumber: number): Promise<void> {
|
|
29
29
|
return this.db.transactionAsync(async () => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
new
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
const key = contractInstance.address.toString();
|
|
31
|
+
if (await this.#contractInstances.hasAsync(key)) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`Contract instance at ${key} already exists (deployed at block ${await this.#contractInstancePublishedAt.getAsync(key)}), cannot add again at block ${blockNumber}`,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
await this.#contractInstances.set(key, new SerializableContractInstance(contractInstance).toBuffer());
|
|
37
|
+
await this.#contractInstancePublishedAt.set(key, blockNumber);
|
|
35
38
|
});
|
|
36
39
|
}
|
|
37
40
|
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import type { CheckpointData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
17
17
|
import type {
|
|
18
18
|
ContractClassPublic,
|
|
19
|
+
ContractClassPublicWithCommitment,
|
|
19
20
|
ContractDataSource,
|
|
20
21
|
ContractInstanceUpdateWithAddress,
|
|
21
22
|
ContractInstanceWithAddress,
|
|
@@ -167,19 +168,14 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
167
168
|
|
|
168
169
|
/**
|
|
169
170
|
* Add new contract classes from an L2 block to the store's list.
|
|
170
|
-
* @param data - List of contract classes to be added.
|
|
171
|
-
* @param bytecodeCommitments - Bytecode commitments for the contract classes.
|
|
171
|
+
* @param data - List of contract classes (with bytecode commitments) to be added.
|
|
172
172
|
* @param blockNumber - Number of the L2 block the contracts were registered in.
|
|
173
173
|
* @returns True if the operation is successful.
|
|
174
174
|
*/
|
|
175
|
-
async addContractClasses(
|
|
176
|
-
data: ContractClassPublic[],
|
|
177
|
-
bytecodeCommitments: Fr[],
|
|
178
|
-
blockNumber: BlockNumber,
|
|
179
|
-
): Promise<boolean> {
|
|
175
|
+
async addContractClasses(data: ContractClassPublicWithCommitment[], blockNumber: BlockNumber): Promise<boolean> {
|
|
180
176
|
return (
|
|
181
177
|
await Promise.all(
|
|
182
|
-
data.map(
|
|
178
|
+
data.map(c => this.#contractClassStore.addContractClass(c, c.publicBytecodeCommitment, blockNumber)),
|
|
183
179
|
)
|
|
184
180
|
).every(Boolean);
|
|
185
181
|
}
|
|
@@ -245,14 +241,14 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
245
241
|
}
|
|
246
242
|
|
|
247
243
|
/**
|
|
248
|
-
* Append new proposed
|
|
249
|
-
*
|
|
244
|
+
* Append a new proposed block to the store.
|
|
245
|
+
* This is an uncheckpointed block that has been proposed by the sequencer but not yet included in a checkpoint on L1.
|
|
250
246
|
* For checkpointed blocks (already published to L1), use addCheckpoints() instead.
|
|
251
|
-
* @param
|
|
247
|
+
* @param block - The proposed L2 block to be added to the store.
|
|
252
248
|
* @returns True if the operation is successful.
|
|
253
249
|
*/
|
|
254
|
-
|
|
255
|
-
return this.#blockStore.
|
|
250
|
+
addProposedBlock(block: L2Block, opts: { force?: boolean } = {}): Promise<boolean> {
|
|
251
|
+
return this.#blockStore.addProposedBlock(block, opts);
|
|
256
252
|
}
|
|
257
253
|
|
|
258
254
|
/**
|