@aztec/archiver 0.0.1-commit.10bd49492 → 0.0.1-commit.11bf3dd6e
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/README.md +12 -6
- package/dest/archiver.d.ts +6 -5
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +32 -15
- package/dest/config.d.ts +3 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +3 -2
- package/dest/errors.d.ts +28 -2
- package/dest/errors.d.ts.map +1 -1
- package/dest/errors.js +36 -2
- package/dest/factory.d.ts +2 -2
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +16 -13
- package/dest/l1/calldata_retriever.d.ts +1 -1
- package/dest/l1/calldata_retriever.d.ts.map +1 -1
- package/dest/l1/calldata_retriever.js +2 -1
- package/dest/l1/data_retrieval.d.ts +3 -3
- package/dest/l1/data_retrieval.d.ts.map +1 -1
- package/dest/l1/data_retrieval.js +14 -15
- package/dest/modules/data_source_base.d.ts +6 -4
- package/dest/modules/data_source_base.d.ts.map +1 -1
- package/dest/modules/data_source_base.js +10 -4
- package/dest/modules/data_store_updater.d.ts +5 -7
- package/dest/modules/data_store_updater.d.ts.map +1 -1
- package/dest/modules/data_store_updater.js +56 -65
- package/dest/modules/instrumentation.d.ts +4 -2
- package/dest/modules/instrumentation.d.ts.map +1 -1
- package/dest/modules/instrumentation.js +17 -6
- package/dest/modules/l1_synchronizer.d.ts +3 -2
- package/dest/modules/l1_synchronizer.d.ts.map +1 -1
- package/dest/modules/l1_synchronizer.js +139 -128
- package/dest/modules/validation.d.ts +1 -1
- package/dest/modules/validation.d.ts.map +1 -1
- package/dest/modules/validation.js +2 -2
- package/dest/store/block_store.d.ts +39 -4
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +232 -63
- package/dest/store/contract_class_store.d.ts +2 -3
- package/dest/store/contract_class_store.d.ts.map +1 -1
- package/dest/store/contract_class_store.js +7 -67
- 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 +30 -16
- package/dest/store/kv_archiver_store.d.ts.map +1 -1
- package/dest/store/kv_archiver_store.js +36 -18
- package/dest/store/l2_tips_cache.d.ts +2 -1
- package/dest/store/l2_tips_cache.d.ts.map +1 -1
- package/dest/store/l2_tips_cache.js +27 -7
- package/dest/store/log_store.d.ts +6 -3
- package/dest/store/log_store.d.ts.map +1 -1
- package/dest/store/log_store.js +95 -20
- package/dest/store/message_store.d.ts +5 -1
- package/dest/store/message_store.d.ts.map +1 -1
- package/dest/store/message_store.js +20 -8
- package/dest/test/fake_l1_state.d.ts +9 -1
- package/dest/test/fake_l1_state.d.ts.map +1 -1
- package/dest/test/fake_l1_state.js +49 -6
- package/dest/test/mock_l1_to_l2_message_source.d.ts +1 -1
- package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
- package/dest/test/mock_l1_to_l2_message_source.js +2 -1
- package/dest/test/mock_l2_block_source.d.ts +7 -2
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +28 -3
- package/dest/test/noop_l1_archiver.d.ts +1 -1
- package/dest/test/noop_l1_archiver.d.ts.map +1 -1
- package/dest/test/noop_l1_archiver.js +0 -1
- package/package.json +13 -13
- package/src/archiver.ts +43 -20
- package/src/config.ts +9 -2
- package/src/errors.ts +60 -2
- package/src/factory.ts +17 -10
- package/src/l1/calldata_retriever.ts +2 -1
- package/src/l1/data_retrieval.ts +8 -12
- package/src/modules/data_source_base.ts +23 -4
- package/src/modules/data_store_updater.ts +68 -95
- package/src/modules/instrumentation.ts +19 -7
- package/src/modules/l1_synchronizer.ts +159 -160
- package/src/modules/validation.ts +2 -2
- package/src/store/block_store.ts +301 -74
- package/src/store/contract_class_store.ts +8 -106
- package/src/store/contract_instance_store.ts +8 -5
- package/src/store/kv_archiver_store.ts +56 -32
- package/src/store/l2_tips_cache.ts +58 -13
- package/src/store/log_store.ts +128 -32
- package/src/store/message_store.ts +26 -9
- package/src/structs/inbox_message.ts +1 -1
- package/src/test/fake_l1_state.ts +68 -9
- package/src/test/mock_l1_to_l2_message_source.ts +1 -0
- package/src/test/mock_l2_block_source.ts +37 -2
- package/src/test/noop_l1_archiver.ts +0 -1
|
@@ -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
|
|
|
@@ -13,14 +13,19 @@ import {
|
|
|
13
13
|
L2Block,
|
|
14
14
|
type ValidateCheckpointResult,
|
|
15
15
|
} from '@aztec/stdlib/block';
|
|
16
|
-
import type {
|
|
16
|
+
import type {
|
|
17
|
+
CheckpointData,
|
|
18
|
+
CommonCheckpointData,
|
|
19
|
+
ProposedCheckpointData,
|
|
20
|
+
ProposedCheckpointInput,
|
|
21
|
+
PublishedCheckpoint,
|
|
22
|
+
} from '@aztec/stdlib/checkpoint';
|
|
17
23
|
import type {
|
|
18
24
|
ContractClassPublic,
|
|
25
|
+
ContractClassPublicWithCommitment,
|
|
19
26
|
ContractDataSource,
|
|
20
27
|
ContractInstanceUpdateWithAddress,
|
|
21
28
|
ContractInstanceWithAddress,
|
|
22
|
-
ExecutablePrivateFunctionWithMembershipProof,
|
|
23
|
-
UtilityFunctionWithMembershipProof,
|
|
24
29
|
} from '@aztec/stdlib/contract';
|
|
25
30
|
import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
|
|
26
31
|
import type { GetContractClassLogsResponse, GetPublicLogsResponse } from '@aztec/stdlib/interfaces/client';
|
|
@@ -37,7 +42,7 @@ import { ContractInstanceStore } from './contract_instance_store.js';
|
|
|
37
42
|
import { LogStore } from './log_store.js';
|
|
38
43
|
import { MessageStore } from './message_store.js';
|
|
39
44
|
|
|
40
|
-
export const ARCHIVER_DB_VERSION =
|
|
45
|
+
export const ARCHIVER_DB_VERSION = 6;
|
|
41
46
|
export const MAX_FUNCTION_SIGNATURES = 1000;
|
|
42
47
|
export const MAX_FUNCTION_NAME_LEN = 256;
|
|
43
48
|
|
|
@@ -167,19 +172,14 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
167
172
|
|
|
168
173
|
/**
|
|
169
174
|
* 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.
|
|
175
|
+
* @param data - List of contract classes (with bytecode commitments) to be added.
|
|
172
176
|
* @param blockNumber - Number of the L2 block the contracts were registered in.
|
|
173
177
|
* @returns True if the operation is successful.
|
|
174
178
|
*/
|
|
175
|
-
async addContractClasses(
|
|
176
|
-
data: ContractClassPublic[],
|
|
177
|
-
bytecodeCommitments: Fr[],
|
|
178
|
-
blockNumber: BlockNumber,
|
|
179
|
-
): Promise<boolean> {
|
|
179
|
+
async addContractClasses(data: ContractClassPublicWithCommitment[], blockNumber: BlockNumber): Promise<boolean> {
|
|
180
180
|
return (
|
|
181
181
|
await Promise.all(
|
|
182
|
-
data.map(
|
|
182
|
+
data.map(c => this.#contractClassStore.addContractClass(c, c.publicBytecodeCommitment, blockNumber)),
|
|
183
183
|
)
|
|
184
184
|
).every(Boolean);
|
|
185
185
|
}
|
|
@@ -194,15 +194,6 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
194
194
|
return this.#contractClassStore.getBytecodeCommitment(contractClassId);
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
/** Adds private functions to a contract class. */
|
|
198
|
-
addFunctions(
|
|
199
|
-
contractClassId: Fr,
|
|
200
|
-
privateFunctions: ExecutablePrivateFunctionWithMembershipProof[],
|
|
201
|
-
utilityFunctions: UtilityFunctionWithMembershipProof[],
|
|
202
|
-
): Promise<boolean> {
|
|
203
|
-
return this.#contractClassStore.addFunctions(contractClassId, privateFunctions, utilityFunctions);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
197
|
/**
|
|
207
198
|
* Add new contract instances from an L2 block to the store's list.
|
|
208
199
|
* @param data - List of contract instances to be added.
|
|
@@ -269,7 +260,7 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
269
260
|
* @returns The number of the latest block
|
|
270
261
|
*/
|
|
271
262
|
getLatestBlockNumber(): Promise<BlockNumber> {
|
|
272
|
-
return this.#blockStore.
|
|
263
|
+
return this.#blockStore.getLatestL2BlockNumber();
|
|
273
264
|
}
|
|
274
265
|
|
|
275
266
|
/**
|
|
@@ -474,10 +465,11 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
474
465
|
* array implies no logs match that tag.
|
|
475
466
|
* @param tags - The tags to search for.
|
|
476
467
|
* @param page - The page number (0-indexed) for pagination. Returns at most 10 logs per tag per page.
|
|
468
|
+
* @param upToBlockNumber - If set, only return logs from blocks up to and including this block number.
|
|
477
469
|
*/
|
|
478
|
-
getPrivateLogsByTags(tags: SiloedTag[], page?: number): Promise<TxScopedL2Log[][]> {
|
|
470
|
+
getPrivateLogsByTags(tags: SiloedTag[], page?: number, upToBlockNumber?: BlockNumber): Promise<TxScopedL2Log[][]> {
|
|
479
471
|
try {
|
|
480
|
-
return this.#logStore.getPrivateLogsByTags(tags, page);
|
|
472
|
+
return this.#logStore.getPrivateLogsByTags(tags, page, upToBlockNumber);
|
|
481
473
|
} catch (err) {
|
|
482
474
|
return Promise.reject(err);
|
|
483
475
|
}
|
|
@@ -489,14 +481,16 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
489
481
|
* @param contractAddress - The contract address to search logs for.
|
|
490
482
|
* @param tags - The tags to search for.
|
|
491
483
|
* @param page - The page number (0-indexed) for pagination. Returns at most 10 logs per tag per page.
|
|
484
|
+
* @param upToBlockNumber - If set, only return logs from blocks up to and including this block number.
|
|
492
485
|
*/
|
|
493
486
|
getPublicLogsByTagsFromContract(
|
|
494
487
|
contractAddress: AztecAddress,
|
|
495
488
|
tags: Tag[],
|
|
496
489
|
page?: number,
|
|
490
|
+
upToBlockNumber?: BlockNumber,
|
|
497
491
|
): Promise<TxScopedL2Log[][]> {
|
|
498
492
|
try {
|
|
499
|
-
return this.#logStore.getPublicLogsByTagsFromContract(contractAddress, tags, page);
|
|
493
|
+
return this.#logStore.getPublicLogsByTagsFromContract(contractAddress, tags, page, upToBlockNumber);
|
|
500
494
|
} catch (err) {
|
|
501
495
|
return Promise.reject(err);
|
|
502
496
|
}
|
|
@@ -564,13 +558,6 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
564
558
|
await this.#blockStore.setSynchedL1BlockNumber(l1BlockNumber);
|
|
565
559
|
}
|
|
566
560
|
|
|
567
|
-
/**
|
|
568
|
-
* Stores the l1 block that messages have been synched until
|
|
569
|
-
*/
|
|
570
|
-
async setMessageSynchedL1Block(l1Block: L1BlockId) {
|
|
571
|
-
await this.#messageStore.setSynchedL1Block(l1Block);
|
|
572
|
-
}
|
|
573
|
-
|
|
574
561
|
/**
|
|
575
562
|
* Returns the number of the most recent proven block
|
|
576
563
|
* @returns The number of the most recent proven block
|
|
@@ -603,6 +590,11 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
603
590
|
return this.#messageStore.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber);
|
|
604
591
|
}
|
|
605
592
|
|
|
593
|
+
/** Atomically updates the message sync state: the L1 sync point and the inbox tree-in-progress marker. */
|
|
594
|
+
public setMessageSyncState(l1Block: L1BlockId, treeInProgress: bigint | undefined): Promise<void> {
|
|
595
|
+
return this.#messageStore.setMessageSyncState(l1Block, treeInProgress);
|
|
596
|
+
}
|
|
597
|
+
|
|
606
598
|
/** Returns an async iterator to all L1 to L2 messages on the range. */
|
|
607
599
|
public iterateL1ToL2Messages(range: CustomRange<bigint> = {}): AsyncIterableIterator<InboxMessage> {
|
|
608
600
|
return this.#messageStore.iterateL1ToL2Messages(range);
|
|
@@ -623,6 +615,38 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
623
615
|
return this.#blockStore.setPendingChainValidationStatus(status);
|
|
624
616
|
}
|
|
625
617
|
|
|
618
|
+
/**
|
|
619
|
+
* Gets the L2 block number of the proposed checkpoint.
|
|
620
|
+
* @returns The block number of the proposed checkpoint, or the checkpointed block number if none.
|
|
621
|
+
*/
|
|
622
|
+
public getProposedCheckpointL2BlockNumber(): Promise<BlockNumber> {
|
|
623
|
+
return this.#blockStore.getProposedCheckpointL2BlockNumber();
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/** Returns the checkpoint data at the proposed tip */
|
|
627
|
+
public getProposedCheckpoint(): Promise<CommonCheckpointData | undefined> {
|
|
628
|
+
return this.#blockStore.getProposedCheckpoint();
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/** Returns the proposed checkpoint data, or undefined if no proposed checkpoint exists. No fallback to confirmed. */
|
|
632
|
+
public getProposedCheckpointOnly(): Promise<ProposedCheckpointData | undefined> {
|
|
633
|
+
return this.#blockStore.getProposedCheckpointOnly();
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Set proposed checkpoint
|
|
638
|
+
* @param proposedCheckpoint
|
|
639
|
+
* @returns
|
|
640
|
+
*/
|
|
641
|
+
public setProposedCheckpoint(proposedCheckpoint: ProposedCheckpointInput): Promise<void> {
|
|
642
|
+
return this.#blockStore.setProposedCheckpoint(proposedCheckpoint);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/** Deletes the proposed checkpoint from storage. */
|
|
646
|
+
public deleteProposedCheckpoint(): Promise<void> {
|
|
647
|
+
return this.#blockStore.deleteProposedCheckpoint();
|
|
648
|
+
}
|
|
649
|
+
|
|
626
650
|
/**
|
|
627
651
|
* Gets the number of the latest L2 block processed.
|
|
628
652
|
* @returns The number of the latest L2 block processed.
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
2
2
|
import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
type BlockData,
|
|
5
|
+
type CheckpointId,
|
|
6
|
+
GENESIS_BLOCK_HEADER_HASH,
|
|
7
|
+
GENESIS_CHECKPOINT_HEADER_HASH,
|
|
8
|
+
type L2Tips,
|
|
9
|
+
} from '@aztec/stdlib/block';
|
|
4
10
|
|
|
5
11
|
import type { BlockStore } from './block_store.js';
|
|
6
12
|
|
|
@@ -26,9 +32,16 @@ export class L2TipsCache {
|
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
private async loadFromStore(): Promise<L2Tips> {
|
|
29
|
-
const [
|
|
30
|
-
|
|
35
|
+
const [
|
|
36
|
+
latestBlockNumber,
|
|
37
|
+
provenBlockNumber,
|
|
38
|
+
proposedCheckpointBlockNumber,
|
|
39
|
+
checkpointedBlockNumber,
|
|
40
|
+
finalizedBlockNumber,
|
|
41
|
+
] = await Promise.all([
|
|
42
|
+
this.blockStore.getLatestL2BlockNumber(),
|
|
31
43
|
this.blockStore.getProvenBlockNumber(),
|
|
44
|
+
this.blockStore.getProposedCheckpointL2BlockNumber(),
|
|
32
45
|
this.blockStore.getCheckpointedL2BlockNumber(),
|
|
33
46
|
this.blockStore.getFinalizedL2BlockNumber(),
|
|
34
47
|
]);
|
|
@@ -42,19 +55,34 @@ export class L2TipsCache {
|
|
|
42
55
|
const getBlockData = (blockNumber: BlockNumber) =>
|
|
43
56
|
blockNumber > beforeInitialBlockNumber ? this.blockStore.getBlockData(blockNumber) : genesisBlockHeader;
|
|
44
57
|
|
|
45
|
-
const [latestBlockData, provenBlockData, checkpointedBlockData, finalizedBlockData] =
|
|
46
|
-
|
|
47
|
-
|
|
58
|
+
const [latestBlockData, provenBlockData, proposedCheckpointBlockData, checkpointedBlockData, finalizedBlockData] =
|
|
59
|
+
await Promise.all(
|
|
60
|
+
[
|
|
61
|
+
latestBlockNumber,
|
|
62
|
+
provenBlockNumber,
|
|
63
|
+
proposedCheckpointBlockNumber,
|
|
64
|
+
checkpointedBlockNumber,
|
|
65
|
+
finalizedBlockNumber,
|
|
66
|
+
].map(getBlockData),
|
|
67
|
+
);
|
|
48
68
|
|
|
49
|
-
if (
|
|
69
|
+
if (
|
|
70
|
+
!latestBlockData ||
|
|
71
|
+
!provenBlockData ||
|
|
72
|
+
!finalizedBlockData ||
|
|
73
|
+
!checkpointedBlockData ||
|
|
74
|
+
!proposedCheckpointBlockData
|
|
75
|
+
) {
|
|
50
76
|
throw new Error('Failed to load block data for L2 tips');
|
|
51
77
|
}
|
|
52
78
|
|
|
53
|
-
const [provenCheckpointId, finalizedCheckpointId, checkpointedCheckpointId] =
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
79
|
+
const [provenCheckpointId, finalizedCheckpointId, proposedCheckpointId, checkpointedCheckpointId] =
|
|
80
|
+
await Promise.all([
|
|
81
|
+
this.getCheckpointIdForBlock(provenBlockData),
|
|
82
|
+
this.getCheckpointIdForBlock(finalizedBlockData),
|
|
83
|
+
this.getCheckpointIdForProposedCheckpoint(checkpointedBlockData),
|
|
84
|
+
this.getCheckpointIdForBlock(checkpointedBlockData),
|
|
85
|
+
]);
|
|
58
86
|
|
|
59
87
|
return {
|
|
60
88
|
proposed: { number: latestBlockNumber, hash: latestBlockData.blockHash.toString() },
|
|
@@ -62,6 +90,10 @@ export class L2TipsCache {
|
|
|
62
90
|
block: { number: provenBlockNumber, hash: provenBlockData.blockHash.toString() },
|
|
63
91
|
checkpoint: provenCheckpointId,
|
|
64
92
|
},
|
|
93
|
+
proposedCheckpoint: {
|
|
94
|
+
block: { number: proposedCheckpointBlockNumber, hash: proposedCheckpointBlockData.blockHash.toString() },
|
|
95
|
+
checkpoint: proposedCheckpointId,
|
|
96
|
+
},
|
|
65
97
|
finalized: {
|
|
66
98
|
block: { number: finalizedBlockNumber, hash: finalizedBlockData.blockHash.toString() },
|
|
67
99
|
checkpoint: finalizedCheckpointId,
|
|
@@ -73,6 +105,19 @@ export class L2TipsCache {
|
|
|
73
105
|
};
|
|
74
106
|
}
|
|
75
107
|
|
|
108
|
+
private async getCheckpointIdForProposedCheckpoint(
|
|
109
|
+
checkpointedBlockData: Pick<BlockData, 'checkpointNumber'>,
|
|
110
|
+
): Promise<CheckpointId> {
|
|
111
|
+
const checkpointData = await this.blockStore.getProposedCheckpointOnly();
|
|
112
|
+
if (!checkpointData) {
|
|
113
|
+
return this.getCheckpointIdForBlock(checkpointedBlockData);
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
number: checkpointData.checkpointNumber,
|
|
117
|
+
hash: checkpointData.header.hash().toString(),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
76
121
|
private async getCheckpointIdForBlock(blockData: Pick<BlockData, 'checkpointNumber'>): Promise<CheckpointId> {
|
|
77
122
|
const checkpointData = await this.blockStore.getCheckpointData(blockData.checkpointNumber);
|
|
78
123
|
if (!checkpointData) {
|
package/src/store/log_store.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
2
2
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
3
|
-
import { filterAsync } from '@aztec/foundation/collection';
|
|
4
|
-
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
|
+
import { compactArray, filterAsync } from '@aztec/foundation/collection';
|
|
5
4
|
import { createLogger } from '@aztec/foundation/log';
|
|
6
5
|
import { BufferReader, numToUInt32BE } from '@aztec/foundation/serialize';
|
|
7
6
|
import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
|
|
@@ -22,6 +21,7 @@ import {
|
|
|
22
21
|
} from '@aztec/stdlib/logs';
|
|
23
22
|
import { TxHash } from '@aztec/stdlib/tx';
|
|
24
23
|
|
|
24
|
+
import { OutOfOrderLogInsertionError } from '../errors.js';
|
|
25
25
|
import type { BlockStore } from './block_store.js';
|
|
26
26
|
|
|
27
27
|
/**
|
|
@@ -165,10 +165,21 @@ export class LogStore {
|
|
|
165
165
|
|
|
166
166
|
for (const taggedLogBuffer of currentPrivateTaggedLogs) {
|
|
167
167
|
if (taggedLogBuffer.logBuffers && taggedLogBuffer.logBuffers.length > 0) {
|
|
168
|
-
privateTaggedLogs.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
168
|
+
const newLogs = privateTaggedLogs.get(taggedLogBuffer.tag)!;
|
|
169
|
+
if (newLogs.length === 0) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const lastExisting = TxScopedL2Log.fromBuffer(taggedLogBuffer.logBuffers.at(-1)!);
|
|
173
|
+
const firstNew = TxScopedL2Log.fromBuffer(newLogs[0]);
|
|
174
|
+
if (lastExisting.blockNumber > firstNew.blockNumber) {
|
|
175
|
+
throw new OutOfOrderLogInsertionError(
|
|
176
|
+
'private',
|
|
177
|
+
taggedLogBuffer.tag,
|
|
178
|
+
lastExisting.blockNumber,
|
|
179
|
+
firstNew.blockNumber,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
privateTaggedLogs.set(taggedLogBuffer.tag, taggedLogBuffer.logBuffers.concat(newLogs));
|
|
172
183
|
}
|
|
173
184
|
}
|
|
174
185
|
|
|
@@ -200,10 +211,21 @@ export class LogStore {
|
|
|
200
211
|
|
|
201
212
|
for (const taggedLogBuffer of currentPublicTaggedLogs) {
|
|
202
213
|
if (taggedLogBuffer.logBuffers && taggedLogBuffer.logBuffers.length > 0) {
|
|
203
|
-
publicTaggedLogs.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
214
|
+
const newLogs = publicTaggedLogs.get(taggedLogBuffer.tag)!;
|
|
215
|
+
if (newLogs.length === 0) {
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
const lastExisting = TxScopedL2Log.fromBuffer(taggedLogBuffer.logBuffers.at(-1)!);
|
|
219
|
+
const firstNew = TxScopedL2Log.fromBuffer(newLogs[0]);
|
|
220
|
+
if (lastExisting.blockNumber > firstNew.blockNumber) {
|
|
221
|
+
throw new OutOfOrderLogInsertionError(
|
|
222
|
+
'public',
|
|
223
|
+
taggedLogBuffer.tag,
|
|
224
|
+
lastExisting.blockNumber,
|
|
225
|
+
firstNew.blockNumber,
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
publicTaggedLogs.set(taggedLogBuffer.tag, taggedLogBuffer.logBuffers.concat(newLogs));
|
|
207
229
|
}
|
|
208
230
|
}
|
|
209
231
|
|
|
@@ -279,29 +301,58 @@ export class LogStore {
|
|
|
279
301
|
}
|
|
280
302
|
|
|
281
303
|
#unpackBlockHash(reader: BufferReader): BlockHash {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
if (!blockHash) {
|
|
304
|
+
if (reader.remainingBytes() === 0) {
|
|
285
305
|
throw new Error('Failed to read block hash from log entry buffer');
|
|
286
306
|
}
|
|
287
307
|
|
|
288
|
-
return
|
|
308
|
+
return BlockHash.fromBuffer(reader);
|
|
289
309
|
}
|
|
290
310
|
|
|
291
311
|
deleteLogs(blocks: L2Block[]): Promise<boolean> {
|
|
292
312
|
return this.db.transactionAsync(async () => {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
313
|
+
const blockNumbers = new Set(blocks.map(block => block.number));
|
|
314
|
+
const firstBlockToDelete = Math.min(...blockNumbers);
|
|
315
|
+
|
|
316
|
+
// Collect all unique private tags across all blocks being deleted
|
|
317
|
+
const allPrivateTags = new Set(
|
|
318
|
+
compactArray(await Promise.all(blocks.map(block => this.#privateLogKeysByBlock.getAsync(block.number)))).flat(),
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
// Trim private logs: for each tag, delete all instances including and after the first block being deleted.
|
|
322
|
+
// This hinges on the invariant that logs for a given tag are always inserted in order of block number, which is enforced in #addPrivateLogs.
|
|
323
|
+
for (const tag of allPrivateTags) {
|
|
324
|
+
const existing = await this.#privateLogsByTag.getAsync(tag);
|
|
325
|
+
if (existing === undefined || existing.length === 0) {
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
const lastIndexToKeep = existing.findLastIndex(
|
|
329
|
+
buf => TxScopedL2Log.getBlockNumberFromBuffer(buf) < firstBlockToDelete,
|
|
330
|
+
);
|
|
331
|
+
const remaining = existing.slice(0, lastIndexToKeep + 1);
|
|
332
|
+
await (remaining.length > 0 ? this.#privateLogsByTag.set(tag, remaining) : this.#privateLogsByTag.delete(tag));
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Collect all unique public keys across all blocks being deleted
|
|
336
|
+
const allPublicKeys = new Set(
|
|
337
|
+
compactArray(await Promise.all(blocks.map(block => this.#publicLogKeysByBlock.getAsync(block.number)))).flat(),
|
|
303
338
|
);
|
|
304
339
|
|
|
340
|
+
// And do the same as we did with private logs
|
|
341
|
+
for (const key of allPublicKeys) {
|
|
342
|
+
const existing = await this.#publicLogsByContractAndTag.getAsync(key);
|
|
343
|
+
if (existing === undefined || existing.length === 0) {
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
const lastIndexToKeep = existing.findLastIndex(
|
|
347
|
+
buf => TxScopedL2Log.getBlockNumberFromBuffer(buf) < firstBlockToDelete,
|
|
348
|
+
);
|
|
349
|
+
const remaining = existing.slice(0, lastIndexToKeep + 1);
|
|
350
|
+
await (remaining.length > 0
|
|
351
|
+
? this.#publicLogsByContractAndTag.set(key, remaining)
|
|
352
|
+
: this.#publicLogsByContractAndTag.delete(key));
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// After trimming the tagged logs, we can delete the block-level keys that track which tags are in which blocks.
|
|
305
356
|
await Promise.all(
|
|
306
357
|
blocks.map(block =>
|
|
307
358
|
Promise.all([
|
|
@@ -322,17 +373,30 @@ export class LogStore {
|
|
|
322
373
|
* array implies no logs match that tag.
|
|
323
374
|
* @param tags - The tags to search for.
|
|
324
375
|
* @param page - The page number (0-indexed) for pagination.
|
|
376
|
+
* @param upToBlockNumber - If set, only return logs from blocks up to and including this block number.
|
|
325
377
|
* @returns An array of log arrays, one per tag. Returns at most MAX_LOGS_PER_TAG logs per tag per page. If
|
|
326
378
|
* MAX_LOGS_PER_TAG logs are returned for a tag, the caller should fetch the next page to check for more logs.
|
|
327
379
|
*/
|
|
328
|
-
async getPrivateLogsByTags(
|
|
380
|
+
async getPrivateLogsByTags(
|
|
381
|
+
tags: SiloedTag[],
|
|
382
|
+
page: number = 0,
|
|
383
|
+
upToBlockNumber?: BlockNumber,
|
|
384
|
+
): Promise<TxScopedL2Log[][]> {
|
|
329
385
|
const logs = await Promise.all(tags.map(tag => this.#privateLogsByTag.getAsync(tag.toString())));
|
|
386
|
+
|
|
330
387
|
const start = page * MAX_LOGS_PER_TAG;
|
|
331
388
|
const end = start + MAX_LOGS_PER_TAG;
|
|
332
389
|
|
|
333
|
-
return logs.map(
|
|
334
|
-
|
|
335
|
-
|
|
390
|
+
return logs.map(logBuffers => {
|
|
391
|
+
const deserialized = logBuffers?.slice(start, end).map(buf => TxScopedL2Log.fromBuffer(buf)) ?? [];
|
|
392
|
+
if (upToBlockNumber !== undefined) {
|
|
393
|
+
const cutoff = deserialized.findIndex(log => log.blockNumber > upToBlockNumber);
|
|
394
|
+
if (cutoff !== -1) {
|
|
395
|
+
return deserialized.slice(0, cutoff);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return deserialized;
|
|
399
|
+
});
|
|
336
400
|
}
|
|
337
401
|
|
|
338
402
|
/**
|
|
@@ -341,6 +405,7 @@ export class LogStore {
|
|
|
341
405
|
* @param contractAddress - The contract address to search logs for.
|
|
342
406
|
* @param tags - The tags to search for.
|
|
343
407
|
* @param page - The page number (0-indexed) for pagination.
|
|
408
|
+
* @param upToBlockNumber - If set, only return logs from blocks up to and including this block number.
|
|
344
409
|
* @returns An array of log arrays, one per tag. Returns at most MAX_LOGS_PER_TAG logs per tag per page. If
|
|
345
410
|
* MAX_LOGS_PER_TAG logs are returned for a tag, the caller should fetch the next page to check for more logs.
|
|
346
411
|
*/
|
|
@@ -348,6 +413,7 @@ export class LogStore {
|
|
|
348
413
|
contractAddress: AztecAddress,
|
|
349
414
|
tags: Tag[],
|
|
350
415
|
page: number = 0,
|
|
416
|
+
upToBlockNumber?: BlockNumber,
|
|
351
417
|
): Promise<TxScopedL2Log[][]> {
|
|
352
418
|
const logs = await Promise.all(
|
|
353
419
|
tags.map(tag => {
|
|
@@ -358,9 +424,16 @@ export class LogStore {
|
|
|
358
424
|
const start = page * MAX_LOGS_PER_TAG;
|
|
359
425
|
const end = start + MAX_LOGS_PER_TAG;
|
|
360
426
|
|
|
361
|
-
return logs.map(
|
|
362
|
-
|
|
363
|
-
|
|
427
|
+
return logs.map(logBuffers => {
|
|
428
|
+
const deserialized = logBuffers?.slice(start, end).map(buf => TxScopedL2Log.fromBuffer(buf)) ?? [];
|
|
429
|
+
if (upToBlockNumber !== undefined) {
|
|
430
|
+
const cutoff = deserialized.findIndex(log => log.blockNumber > upToBlockNumber);
|
|
431
|
+
if (cutoff !== -1) {
|
|
432
|
+
return deserialized.slice(0, cutoff);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return deserialized;
|
|
436
|
+
});
|
|
364
437
|
}
|
|
365
438
|
|
|
366
439
|
/**
|
|
@@ -588,11 +661,24 @@ export class LogStore {
|
|
|
588
661
|
txLogs: PublicLog[],
|
|
589
662
|
filter: LogFilter = {},
|
|
590
663
|
): boolean {
|
|
664
|
+
if (filter.fromBlock && blockNumber < filter.fromBlock) {
|
|
665
|
+
return false;
|
|
666
|
+
}
|
|
667
|
+
if (filter.toBlock && blockNumber >= filter.toBlock) {
|
|
668
|
+
return false;
|
|
669
|
+
}
|
|
670
|
+
if (filter.txHash && !txHash.equals(filter.txHash)) {
|
|
671
|
+
return false;
|
|
672
|
+
}
|
|
673
|
+
|
|
591
674
|
let maxLogsHit = false;
|
|
592
675
|
let logIndex = typeof filter.afterLog?.logIndex === 'number' ? filter.afterLog.logIndex + 1 : 0;
|
|
593
676
|
for (; logIndex < txLogs.length; logIndex++) {
|
|
594
677
|
const log = txLogs[logIndex];
|
|
595
|
-
if (
|
|
678
|
+
if (
|
|
679
|
+
(!filter.contractAddress || log.contractAddress.equals(filter.contractAddress)) &&
|
|
680
|
+
(!filter.tag || log.fields[0]?.equals(filter.tag))
|
|
681
|
+
) {
|
|
596
682
|
results.push(
|
|
597
683
|
new ExtendedPublicLog(new LogId(BlockNumber(blockNumber), blockHash, txHash, txIndex, logIndex), log),
|
|
598
684
|
);
|
|
@@ -616,6 +702,16 @@ export class LogStore {
|
|
|
616
702
|
txLogs: ContractClassLog[],
|
|
617
703
|
filter: LogFilter = {},
|
|
618
704
|
): boolean {
|
|
705
|
+
if (filter.fromBlock && blockNumber < filter.fromBlock) {
|
|
706
|
+
return false;
|
|
707
|
+
}
|
|
708
|
+
if (filter.toBlock && blockNumber >= filter.toBlock) {
|
|
709
|
+
return false;
|
|
710
|
+
}
|
|
711
|
+
if (filter.txHash && !txHash.equals(filter.txHash)) {
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
714
|
+
|
|
619
715
|
let maxLogsHit = false;
|
|
620
716
|
let logIndex = typeof filter.afterLog?.logIndex === 'number' ? filter.afterLog.logIndex + 1 : 0;
|
|
621
717
|
for (; logIndex < txLogs.length; logIndex++) {
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from '@aztec/kv-store';
|
|
15
15
|
import { InboxLeaf } from '@aztec/stdlib/messaging';
|
|
16
16
|
|
|
17
|
+
import { L1ToL2MessagesNotReadyError } from '../errors.js';
|
|
17
18
|
import {
|
|
18
19
|
type InboxMessage,
|
|
19
20
|
deserializeInboxMessage,
|
|
@@ -40,6 +41,8 @@ export class MessageStore {
|
|
|
40
41
|
#lastSynchedL1Block: AztecAsyncSingleton<Buffer>;
|
|
41
42
|
/** Stores total messages stored */
|
|
42
43
|
#totalMessageCount: AztecAsyncSingleton<bigint>;
|
|
44
|
+
/** Stores the checkpoint number whose message tree is currently being filled on L1. */
|
|
45
|
+
#inboxTreeInProgress: AztecAsyncSingleton<bigint>;
|
|
43
46
|
|
|
44
47
|
#log = createLogger('archiver:message_store');
|
|
45
48
|
|
|
@@ -48,6 +51,7 @@ export class MessageStore {
|
|
|
48
51
|
this.#l1ToL2MessageIndices = db.openMap('archiver_l1_to_l2_message_indices');
|
|
49
52
|
this.#lastSynchedL1Block = db.openSingleton('archiver_last_l1_block_id');
|
|
50
53
|
this.#totalMessageCount = db.openSingleton('archiver_l1_to_l2_message_count');
|
|
54
|
+
this.#inboxTreeInProgress = db.openSingleton('archiver_inbox_tree_in_progress');
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
public async getTotalL1ToL2MessageCount(): Promise<bigint> {
|
|
@@ -157,15 +161,6 @@ export class MessageStore {
|
|
|
157
161
|
lastMessage = message;
|
|
158
162
|
}
|
|
159
163
|
|
|
160
|
-
// Update the L1 sync point to that of the last message added.
|
|
161
|
-
const currentSyncPoint = await this.getSynchedL1Block();
|
|
162
|
-
if (!currentSyncPoint || currentSyncPoint.l1BlockNumber < lastMessage!.l1BlockNumber) {
|
|
163
|
-
await this.setSynchedL1Block({
|
|
164
|
-
l1BlockNumber: lastMessage!.l1BlockNumber,
|
|
165
|
-
l1BlockHash: lastMessage!.l1BlockHash,
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
164
|
// Update total message count with the number of inserted messages.
|
|
170
165
|
await this.increaseTotalMessageCount(messageCount);
|
|
171
166
|
});
|
|
@@ -185,7 +180,29 @@ export class MessageStore {
|
|
|
185
180
|
return msg ? deserializeInboxMessage(msg) : undefined;
|
|
186
181
|
}
|
|
187
182
|
|
|
183
|
+
/** Returns the inbox tree-in-progress checkpoint number from L1, or undefined if not yet set. */
|
|
184
|
+
public getInboxTreeInProgress(): Promise<bigint | undefined> {
|
|
185
|
+
return this.#inboxTreeInProgress.getAsync();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Atomically updates the message sync state: the L1 sync point and the inbox tree-in-progress marker. */
|
|
189
|
+
public setMessageSyncState(l1Block: L1BlockId, treeInProgress: bigint | undefined): Promise<void> {
|
|
190
|
+
return this.db.transactionAsync(async () => {
|
|
191
|
+
await this.setSynchedL1Block(l1Block);
|
|
192
|
+
if (treeInProgress !== undefined) {
|
|
193
|
+
await this.#inboxTreeInProgress.set(treeInProgress);
|
|
194
|
+
} else {
|
|
195
|
+
await this.#inboxTreeInProgress.delete();
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
188
200
|
public async getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise<Fr[]> {
|
|
201
|
+
const treeInProgress = await this.#inboxTreeInProgress.getAsync();
|
|
202
|
+
if (treeInProgress !== undefined && BigInt(checkpointNumber) >= treeInProgress) {
|
|
203
|
+
throw new L1ToL2MessagesNotReadyError(checkpointNumber, treeInProgress);
|
|
204
|
+
}
|
|
205
|
+
|
|
189
206
|
const messages: Fr[] = [];
|
|
190
207
|
|
|
191
208
|
const [startIndex, endIndex] = InboxLeaf.indexRangeForCheckpoint(checkpointNumber);
|