@aztec/archiver 0.0.1-commit.5914bae → 0.0.1-commit.59a0419c6
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 +7 -5
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +18 -5
- package/dest/config.d.ts +5 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +15 -3
- package/dest/errors.d.ts +44 -2
- package/dest/errors.d.ts.map +1 -1
- package/dest/errors.js +58 -2
- package/dest/factory.d.ts +2 -2
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +3 -4
- package/dest/index.d.ts +3 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +2 -1
- 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 +24 -12
- package/dest/l1/data_retrieval.d.ts.map +1 -1
- package/dest/l1/data_retrieval.js +36 -37
- package/dest/l1/validate_historical_logs.d.ts +23 -0
- package/dest/l1/validate_historical_logs.d.ts.map +1 -0
- package/dest/l1/validate_historical_logs.js +108 -0
- 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 +15 -10
- package/dest/modules/data_store_updater.d.ts.map +1 -1
- package/dest/modules/data_store_updater.js +27 -59
- package/dest/modules/instrumentation.d.ts +18 -2
- package/dest/modules/instrumentation.d.ts.map +1 -1
- package/dest/modules/instrumentation.js +32 -6
- package/dest/modules/l1_synchronizer.d.ts +6 -2
- package/dest/modules/l1_synchronizer.d.ts.map +1 -1
- package/dest/modules/l1_synchronizer.js +248 -149
- 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 +46 -5
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +225 -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 +1 -65
- package/dest/store/kv_archiver_store.d.ts +35 -14
- package/dest/store/kv_archiver_store.d.ts.map +1 -1
- package/dest/store/kv_archiver_store.js +40 -14
- 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 +47 -10
- 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 +7 -3
- package/dest/test/fake_l1_state.d.ts.map +1 -1
- package/dest/test/fake_l1_state.js +50 -10
- 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 +4 -2
- package/package.json +13 -13
- package/src/archiver.ts +33 -8
- package/src/config.ts +22 -2
- package/src/errors.ts +94 -2
- package/src/factory.ts +2 -3
- package/src/index.ts +2 -1
- package/src/l1/calldata_retriever.ts +2 -1
- package/src/l1/data_retrieval.ts +52 -53
- package/src/l1/validate_historical_logs.ts +140 -0
- package/src/modules/data_source_base.ts +23 -4
- package/src/modules/data_store_updater.ts +43 -85
- package/src/modules/instrumentation.ts +47 -7
- package/src/modules/l1_synchronizer.ts +321 -185
- package/src/modules/validation.ts +2 -2
- package/src/store/block_store.ts +295 -73
- package/src/store/contract_class_store.ts +1 -103
- package/src/store/kv_archiver_store.ts +67 -24
- package/src/store/l2_tips_cache.ts +58 -13
- package/src/store/log_store.ts +62 -20
- package/src/store/message_store.ts +26 -9
- package/src/structs/inbox_message.ts +1 -1
- package/src/test/fake_l1_state.ts +72 -15
- 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 +3 -1
|
@@ -10,18 +10,24 @@ import {
|
|
|
10
10
|
type BlockData,
|
|
11
11
|
BlockHash,
|
|
12
12
|
CheckpointedL2Block,
|
|
13
|
+
type CommitteeAttestation,
|
|
13
14
|
L2Block,
|
|
14
15
|
type ValidateCheckpointResult,
|
|
15
16
|
} from '@aztec/stdlib/block';
|
|
16
|
-
import type {
|
|
17
|
+
import type {
|
|
18
|
+
CheckpointData,
|
|
19
|
+
CommonCheckpointData,
|
|
20
|
+
L1PublishedData,
|
|
21
|
+
ProposedCheckpointData,
|
|
22
|
+
ProposedCheckpointInput,
|
|
23
|
+
PublishedCheckpoint,
|
|
24
|
+
} from '@aztec/stdlib/checkpoint';
|
|
17
25
|
import type {
|
|
18
26
|
ContractClassPublic,
|
|
19
27
|
ContractClassPublicWithCommitment,
|
|
20
28
|
ContractDataSource,
|
|
21
29
|
ContractInstanceUpdateWithAddress,
|
|
22
30
|
ContractInstanceWithAddress,
|
|
23
|
-
ExecutablePrivateFunctionWithMembershipProof,
|
|
24
|
-
UtilityFunctionWithMembershipProof,
|
|
25
31
|
} from '@aztec/stdlib/contract';
|
|
26
32
|
import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
|
|
27
33
|
import type { GetContractClassLogsResponse, GetPublicLogsResponse } from '@aztec/stdlib/interfaces/client';
|
|
@@ -38,7 +44,7 @@ import { ContractInstanceStore } from './contract_instance_store.js';
|
|
|
38
44
|
import { LogStore } from './log_store.js';
|
|
39
45
|
import { MessageStore } from './message_store.js';
|
|
40
46
|
|
|
41
|
-
export const ARCHIVER_DB_VERSION =
|
|
47
|
+
export const ARCHIVER_DB_VERSION = 6;
|
|
42
48
|
export const MAX_FUNCTION_SIGNATURES = 1000;
|
|
43
49
|
export const MAX_FUNCTION_NAME_LEN = 256;
|
|
44
50
|
|
|
@@ -190,15 +196,6 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
190
196
|
return this.#contractClassStore.getBytecodeCommitment(contractClassId);
|
|
191
197
|
}
|
|
192
198
|
|
|
193
|
-
/** Adds private functions to a contract class. */
|
|
194
|
-
addFunctions(
|
|
195
|
-
contractClassId: Fr,
|
|
196
|
-
privateFunctions: ExecutablePrivateFunctionWithMembershipProof[],
|
|
197
|
-
utilityFunctions: UtilityFunctionWithMembershipProof[],
|
|
198
|
-
): Promise<boolean> {
|
|
199
|
-
return this.#contractClassStore.addFunctions(contractClassId, privateFunctions, utilityFunctions);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
199
|
/**
|
|
203
200
|
* Add new contract instances from an L2 block to the store's list.
|
|
204
201
|
* @param data - List of contract instances to be added.
|
|
@@ -265,7 +262,7 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
265
262
|
* @returns The number of the latest block
|
|
266
263
|
*/
|
|
267
264
|
getLatestBlockNumber(): Promise<BlockNumber> {
|
|
268
|
-
return this.#blockStore.
|
|
265
|
+
return this.#blockStore.getLatestL2BlockNumber();
|
|
269
266
|
}
|
|
270
267
|
|
|
271
268
|
/**
|
|
@@ -470,10 +467,11 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
470
467
|
* array implies no logs match that tag.
|
|
471
468
|
* @param tags - The tags to search for.
|
|
472
469
|
* @param page - The page number (0-indexed) for pagination. Returns at most 10 logs per tag per page.
|
|
470
|
+
* @param upToBlockNumber - If set, only return logs from blocks up to and including this block number.
|
|
473
471
|
*/
|
|
474
|
-
getPrivateLogsByTags(tags: SiloedTag[], page?: number): Promise<TxScopedL2Log[][]> {
|
|
472
|
+
getPrivateLogsByTags(tags: SiloedTag[], page?: number, upToBlockNumber?: BlockNumber): Promise<TxScopedL2Log[][]> {
|
|
475
473
|
try {
|
|
476
|
-
return this.#logStore.getPrivateLogsByTags(tags, page);
|
|
474
|
+
return this.#logStore.getPrivateLogsByTags(tags, page, upToBlockNumber);
|
|
477
475
|
} catch (err) {
|
|
478
476
|
return Promise.reject(err);
|
|
479
477
|
}
|
|
@@ -485,14 +483,16 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
485
483
|
* @param contractAddress - The contract address to search logs for.
|
|
486
484
|
* @param tags - The tags to search for.
|
|
487
485
|
* @param page - The page number (0-indexed) for pagination. Returns at most 10 logs per tag per page.
|
|
486
|
+
* @param upToBlockNumber - If set, only return logs from blocks up to and including this block number.
|
|
488
487
|
*/
|
|
489
488
|
getPublicLogsByTagsFromContract(
|
|
490
489
|
contractAddress: AztecAddress,
|
|
491
490
|
tags: Tag[],
|
|
492
491
|
page?: number,
|
|
492
|
+
upToBlockNumber?: BlockNumber,
|
|
493
493
|
): Promise<TxScopedL2Log[][]> {
|
|
494
494
|
try {
|
|
495
|
-
return this.#logStore.getPublicLogsByTagsFromContract(contractAddress, tags, page);
|
|
495
|
+
return this.#logStore.getPublicLogsByTagsFromContract(contractAddress, tags, page, upToBlockNumber);
|
|
496
496
|
} catch (err) {
|
|
497
497
|
return Promise.reject(err);
|
|
498
498
|
}
|
|
@@ -560,13 +560,6 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
560
560
|
await this.#blockStore.setSynchedL1BlockNumber(l1BlockNumber);
|
|
561
561
|
}
|
|
562
562
|
|
|
563
|
-
/**
|
|
564
|
-
* Stores the l1 block that messages have been synched until
|
|
565
|
-
*/
|
|
566
|
-
async setMessageSynchedL1Block(l1Block: L1BlockId) {
|
|
567
|
-
await this.#messageStore.setSynchedL1Block(l1Block);
|
|
568
|
-
}
|
|
569
|
-
|
|
570
563
|
/**
|
|
571
564
|
* Returns the number of the most recent proven block
|
|
572
565
|
* @returns The number of the most recent proven block
|
|
@@ -599,6 +592,11 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
599
592
|
return this.#messageStore.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber);
|
|
600
593
|
}
|
|
601
594
|
|
|
595
|
+
/** Atomically updates the message sync state: the L1 sync point and the inbox tree-in-progress marker. */
|
|
596
|
+
public setMessageSyncState(l1Block: L1BlockId, treeInProgress: bigint | undefined): Promise<void> {
|
|
597
|
+
return this.#messageStore.setMessageSyncState(l1Block, treeInProgress);
|
|
598
|
+
}
|
|
599
|
+
|
|
602
600
|
/** Returns an async iterator to all L1 to L2 messages on the range. */
|
|
603
601
|
public iterateL1ToL2Messages(range: CustomRange<bigint> = {}): AsyncIterableIterator<InboxMessage> {
|
|
604
602
|
return this.#messageStore.iterateL1ToL2Messages(range);
|
|
@@ -619,6 +617,51 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
619
617
|
return this.#blockStore.setPendingChainValidationStatus(status);
|
|
620
618
|
}
|
|
621
619
|
|
|
620
|
+
/**
|
|
621
|
+
* Gets the L2 block number of the proposed checkpoint.
|
|
622
|
+
* @returns The block number of the proposed checkpoint, or the checkpointed block number if none.
|
|
623
|
+
*/
|
|
624
|
+
public getProposedCheckpointL2BlockNumber(): Promise<BlockNumber> {
|
|
625
|
+
return this.#blockStore.getProposedCheckpointL2BlockNumber();
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/** Returns the checkpoint data at the proposed tip */
|
|
629
|
+
public getProposedCheckpoint(): Promise<CommonCheckpointData | undefined> {
|
|
630
|
+
return this.#blockStore.getProposedCheckpoint();
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/** Returns the proposed checkpoint data, or undefined if no proposed checkpoint exists. No fallback to confirmed. */
|
|
634
|
+
public getProposedCheckpointOnly(): Promise<ProposedCheckpointData | undefined> {
|
|
635
|
+
return this.#blockStore.getProposedCheckpointOnly();
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Set proposed checkpoint
|
|
640
|
+
* @param proposedCheckpoint
|
|
641
|
+
* @returns
|
|
642
|
+
*/
|
|
643
|
+
public setProposedCheckpoint(proposedCheckpoint: ProposedCheckpointInput): Promise<void> {
|
|
644
|
+
return this.#blockStore.setProposedCheckpoint(proposedCheckpoint);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/** Deletes the proposed checkpoint from storage. */
|
|
648
|
+
public deleteProposedCheckpoint(): Promise<void> {
|
|
649
|
+
return this.#blockStore.deleteProposedCheckpoint();
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Promotes the proposed checkpoint to a confirmed checkpoint entry.
|
|
654
|
+
* Should only be called after the checkpoint has been validated.
|
|
655
|
+
* @param expectedArchiveRoot - The archive root to match against the proposed checkpoint, to guard against races.
|
|
656
|
+
*/
|
|
657
|
+
public promoteProposedToCheckpointed(
|
|
658
|
+
l1: L1PublishedData,
|
|
659
|
+
attestations: CommitteeAttestation[],
|
|
660
|
+
expectedArchiveRoot: Fr,
|
|
661
|
+
): Promise<void> {
|
|
662
|
+
return this.#blockStore.promoteProposedToCheckpointed(l1, attestations, expectedArchiveRoot);
|
|
663
|
+
}
|
|
664
|
+
|
|
622
665
|
/**
|
|
623
666
|
* Gets the number of the latest L2 block processed.
|
|
624
667
|
* @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
3
|
import { compactArray, filterAsync } from '@aztec/foundation/collection';
|
|
4
|
-
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
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,13 +301,11 @@ 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> {
|
|
@@ -353,17 +373,30 @@ export class LogStore {
|
|
|
353
373
|
* array implies no logs match that tag.
|
|
354
374
|
* @param tags - The tags to search for.
|
|
355
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.
|
|
356
377
|
* @returns An array of log arrays, one per tag. Returns at most MAX_LOGS_PER_TAG logs per tag per page. If
|
|
357
378
|
* MAX_LOGS_PER_TAG logs are returned for a tag, the caller should fetch the next page to check for more logs.
|
|
358
379
|
*/
|
|
359
|
-
async getPrivateLogsByTags(
|
|
380
|
+
async getPrivateLogsByTags(
|
|
381
|
+
tags: SiloedTag[],
|
|
382
|
+
page: number = 0,
|
|
383
|
+
upToBlockNumber?: BlockNumber,
|
|
384
|
+
): Promise<TxScopedL2Log[][]> {
|
|
360
385
|
const logs = await Promise.all(tags.map(tag => this.#privateLogsByTag.getAsync(tag.toString())));
|
|
386
|
+
|
|
361
387
|
const start = page * MAX_LOGS_PER_TAG;
|
|
362
388
|
const end = start + MAX_LOGS_PER_TAG;
|
|
363
389
|
|
|
364
|
-
return logs.map(
|
|
365
|
-
|
|
366
|
-
|
|
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
|
+
});
|
|
367
400
|
}
|
|
368
401
|
|
|
369
402
|
/**
|
|
@@ -372,6 +405,7 @@ export class LogStore {
|
|
|
372
405
|
* @param contractAddress - The contract address to search logs for.
|
|
373
406
|
* @param tags - The tags to search for.
|
|
374
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.
|
|
375
409
|
* @returns An array of log arrays, one per tag. Returns at most MAX_LOGS_PER_TAG logs per tag per page. If
|
|
376
410
|
* MAX_LOGS_PER_TAG logs are returned for a tag, the caller should fetch the next page to check for more logs.
|
|
377
411
|
*/
|
|
@@ -379,6 +413,7 @@ export class LogStore {
|
|
|
379
413
|
contractAddress: AztecAddress,
|
|
380
414
|
tags: Tag[],
|
|
381
415
|
page: number = 0,
|
|
416
|
+
upToBlockNumber?: BlockNumber,
|
|
382
417
|
): Promise<TxScopedL2Log[][]> {
|
|
383
418
|
const logs = await Promise.all(
|
|
384
419
|
tags.map(tag => {
|
|
@@ -389,9 +424,16 @@ export class LogStore {
|
|
|
389
424
|
const start = page * MAX_LOGS_PER_TAG;
|
|
390
425
|
const end = start + MAX_LOGS_PER_TAG;
|
|
391
426
|
|
|
392
|
-
return logs.map(
|
|
393
|
-
|
|
394
|
-
|
|
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
|
+
});
|
|
395
437
|
}
|
|
396
438
|
|
|
397
439
|
/**
|
|
@@ -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);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { BlobClientInterface } from '@aztec/blob-client/client';
|
|
2
2
|
import { type Blob, getBlobsPerL1Block, getPrefixedEthBlobCommitments } from '@aztec/blob-lib';
|
|
3
|
+
import { INITIAL_CHECKPOINT_NUMBER } from '@aztec/constants';
|
|
3
4
|
import type { CheckpointProposedLog, InboxContract, MessageSentLog, RollupContract } from '@aztec/ethereum/contracts';
|
|
4
5
|
import { MULTI_CALL_3_ADDRESS } from '@aztec/ethereum/contracts';
|
|
5
6
|
import type { ViemPublicClient } from '@aztec/ethereum/types';
|
|
@@ -150,8 +151,10 @@ export class FakeL1State {
|
|
|
150
151
|
// Computed from checkpoints based on L1 block visibility
|
|
151
152
|
private pendingCheckpointNumber: CheckpointNumber = CheckpointNumber(0);
|
|
152
153
|
|
|
153
|
-
// The L1 block number reported as "finalized" (defaults to the start block)
|
|
154
|
-
|
|
154
|
+
// The L1 block number reported as "finalized" (defaults to the start block).
|
|
155
|
+
// `undefined` simulates the startup window on a fresh devnet where
|
|
156
|
+
// `getBlock({ blockTag: 'finalized' })` fails with "finalized block not found".
|
|
157
|
+
private finalizedL1BlockNumber: bigint | undefined;
|
|
155
158
|
|
|
156
159
|
constructor(private readonly config: FakeL1StateConfig) {
|
|
157
160
|
this.l1BlockNumber = config.l1StartBlock;
|
|
@@ -287,8 +290,11 @@ export class FakeL1State {
|
|
|
287
290
|
this.updatePendingCheckpointNumber();
|
|
288
291
|
}
|
|
289
292
|
|
|
290
|
-
/**
|
|
291
|
-
|
|
293
|
+
/**
|
|
294
|
+
* Sets the L1 block number that will be reported as "finalized". Pass `undefined` to
|
|
295
|
+
* simulate a chain that does not yet have a finalized block (devnet startup).
|
|
296
|
+
*/
|
|
297
|
+
setFinalizedL1BlockNumber(blockNumber: bigint | undefined): void {
|
|
292
298
|
this.finalizedL1BlockNumber = blockNumber;
|
|
293
299
|
}
|
|
294
300
|
|
|
@@ -465,21 +471,44 @@ export class FakeL1State {
|
|
|
465
471
|
createMockInboxContract(_publicClient: MockProxy<ViemPublicClient>): MockProxy<InboxContract> {
|
|
466
472
|
const mockInbox = mock<InboxContract>();
|
|
467
473
|
|
|
468
|
-
mockInbox.getState.mockImplementation(() =>
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
474
|
+
mockInbox.getState.mockImplementation((opts: { blockTag?: string; blockNumber?: bigint } = {}) => {
|
|
475
|
+
// Filter messages visible at the given block number (or all if not specified)
|
|
476
|
+
const blockNumber = opts.blockNumber ?? this.l1BlockNumber;
|
|
477
|
+
const visibleMessages = this.messages.filter(m => m.l1BlockNumber <= blockNumber);
|
|
478
|
+
|
|
479
|
+
// treeInProgress must be > any sealed checkpoint. On L1, a checkpoint can only be proposed
|
|
480
|
+
// after its messages are sealed, so treeInProgress > checkpointNumber for all published checkpoints.
|
|
481
|
+
const maxFromMessages =
|
|
482
|
+
visibleMessages.length > 0 ? Math.max(...visibleMessages.map(m => Number(m.checkpointNumber))) + 1 : 0;
|
|
483
|
+
const maxFromCheckpoints =
|
|
484
|
+
this.checkpoints.length > 0
|
|
485
|
+
? Math.max(
|
|
486
|
+
...this.checkpoints
|
|
487
|
+
.filter(cp => !cp.pruned && cp.l1BlockNumber <= blockNumber)
|
|
488
|
+
.map(cp => Number(cp.checkpointNumber)),
|
|
489
|
+
0,
|
|
490
|
+
) + 1
|
|
491
|
+
: 0;
|
|
492
|
+
const treeInProgress = Math.max(maxFromMessages, maxFromCheckpoints, INITIAL_CHECKPOINT_NUMBER);
|
|
493
|
+
|
|
494
|
+
// Compute rolling hash only for visible messages
|
|
495
|
+
const rollingHash =
|
|
496
|
+
visibleMessages.length > 0 ? visibleMessages[visibleMessages.length - 1].rollingHash : Buffer16.ZERO;
|
|
497
|
+
|
|
498
|
+
return Promise.resolve({
|
|
499
|
+
messagesRollingHash: rollingHash,
|
|
500
|
+
totalMessagesInserted: BigInt(visibleMessages.length),
|
|
501
|
+
treeInProgress: BigInt(treeInProgress),
|
|
502
|
+
});
|
|
503
|
+
});
|
|
475
504
|
|
|
476
505
|
// Mock the wrapper methods for fetching message events
|
|
477
506
|
mockInbox.getMessageSentEvents.mockImplementation((fromBlock: bigint, toBlock: bigint) =>
|
|
478
507
|
Promise.resolve(this.getMessageSentLogs(fromBlock, toBlock)),
|
|
479
508
|
);
|
|
480
509
|
|
|
481
|
-
mockInbox.getMessageSentEventByHash.mockImplementation((
|
|
482
|
-
Promise.resolve(this.
|
|
510
|
+
mockInbox.getMessageSentEventByHash.mockImplementation((msgHash: string, aroundL1BlockNumber: bigint) =>
|
|
511
|
+
Promise.resolve(this.getMessageSentLogByHash(msgHash, aroundL1BlockNumber) as MessageSentLog),
|
|
483
512
|
);
|
|
484
513
|
|
|
485
514
|
return mockInbox;
|
|
@@ -495,6 +524,11 @@ export class FakeL1State {
|
|
|
495
524
|
publicClient.getBlock.mockImplementation((async (args: { blockNumber?: bigint; blockTag?: string } = {}) => {
|
|
496
525
|
let blockNum: bigint;
|
|
497
526
|
if (args.blockTag === 'finalized') {
|
|
527
|
+
if (this.finalizedL1BlockNumber === undefined) {
|
|
528
|
+
throw Object.assign(new Error('finalized block not found'), {
|
|
529
|
+
details: 'finalized block not found',
|
|
530
|
+
});
|
|
531
|
+
}
|
|
498
532
|
blockNum = this.finalizedL1BlockNumber;
|
|
499
533
|
} else {
|
|
500
534
|
blockNum = args.blockNumber ?? (await publicClient.getBlockNumber());
|
|
@@ -520,10 +554,10 @@ export class FakeL1State {
|
|
|
520
554
|
createMockBlobClient(): MockProxy<BlobClientInterface> {
|
|
521
555
|
const blobClient = mock<BlobClientInterface>();
|
|
522
556
|
|
|
523
|
-
// The blockId is the
|
|
557
|
+
// The blockId is the L1 block hash, which we derive from the L1 block number
|
|
524
558
|
blobClient.getBlobSidecar.mockImplementation((blockId: `0x${string}`) =>
|
|
525
559
|
Promise.resolve(
|
|
526
|
-
this.checkpoints.find(cpData => cpData.
|
|
560
|
+
this.checkpoints.find(cpData => Buffer32.fromBigInt(cpData.l1BlockNumber).toString() === blockId)?.blobs ?? [],
|
|
527
561
|
),
|
|
528
562
|
);
|
|
529
563
|
|
|
@@ -599,6 +633,29 @@ export class FakeL1State {
|
|
|
599
633
|
}));
|
|
600
634
|
}
|
|
601
635
|
|
|
636
|
+
private getMessageSentLogByHash(msgHash: string, aroundL1BlockNumber: bigint): MessageSentLog | undefined {
|
|
637
|
+
const msg = this.messages.find(
|
|
638
|
+
msg =>
|
|
639
|
+
msg.leaf.toString() === msgHash &&
|
|
640
|
+
msg.l1BlockNumber >= aroundL1BlockNumber - 5n &&
|
|
641
|
+
msg.l1BlockNumber <= aroundL1BlockNumber + 5n,
|
|
642
|
+
);
|
|
643
|
+
if (!msg) {
|
|
644
|
+
return undefined;
|
|
645
|
+
}
|
|
646
|
+
return {
|
|
647
|
+
l1BlockNumber: msg.l1BlockNumber,
|
|
648
|
+
l1BlockHash: Buffer32.fromBigInt(msg.l1BlockNumber),
|
|
649
|
+
l1TransactionHash: `0x${msg.l1BlockNumber.toString(16)}` as `0x${string}`,
|
|
650
|
+
args: {
|
|
651
|
+
checkpointNumber: msg.checkpointNumber,
|
|
652
|
+
index: msg.index,
|
|
653
|
+
leaf: msg.leaf,
|
|
654
|
+
rollingHash: msg.rollingHash,
|
|
655
|
+
},
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
|
|
602
659
|
private async makeRollupTx(
|
|
603
660
|
checkpoint: Checkpoint,
|
|
604
661
|
signers: Secp256k1Signer[],
|