@aztec/archiver 0.0.1-commit.d431d1c → 0.0.1-commit.db765a8
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 +9 -0
- package/dest/archiver.d.ts +10 -6
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +50 -111
- package/dest/errors.d.ts +6 -1
- package/dest/errors.d.ts.map +1 -1
- package/dest/errors.js +8 -0
- package/dest/factory.d.ts +5 -2
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +16 -13
- package/dest/index.d.ts +2 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -0
- package/dest/l1/bin/retrieve-calldata.js +35 -32
- package/dest/l1/calldata_retriever.d.ts +73 -50
- package/dest/l1/calldata_retriever.d.ts.map +1 -1
- package/dest/l1/calldata_retriever.js +190 -259
- package/dest/l1/data_retrieval.d.ts +9 -9
- package/dest/l1/data_retrieval.d.ts.map +1 -1
- package/dest/l1/data_retrieval.js +24 -22
- package/dest/l1/spire_proposer.d.ts +5 -5
- package/dest/l1/spire_proposer.d.ts.map +1 -1
- package/dest/l1/spire_proposer.js +9 -17
- package/dest/l1/validate_trace.d.ts +6 -3
- package/dest/l1/validate_trace.d.ts.map +1 -1
- package/dest/l1/validate_trace.js +13 -9
- package/dest/modules/data_source_base.d.ts +23 -19
- package/dest/modules/data_source_base.d.ts.map +1 -1
- package/dest/modules/data_source_base.js +44 -119
- package/dest/modules/data_store_updater.d.ts +31 -20
- package/dest/modules/data_store_updater.d.ts.map +1 -1
- package/dest/modules/data_store_updater.js +79 -60
- package/dest/modules/instrumentation.d.ts +17 -4
- package/dest/modules/instrumentation.d.ts.map +1 -1
- package/dest/modules/instrumentation.js +36 -12
- package/dest/modules/l1_synchronizer.d.ts +4 -8
- package/dest/modules/l1_synchronizer.d.ts.map +1 -1
- package/dest/modules/l1_synchronizer.js +23 -19
- package/dest/store/block_store.d.ts +50 -32
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +147 -54
- 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 +11 -7
- package/dest/store/kv_archiver_store.d.ts +43 -25
- package/dest/store/kv_archiver_store.d.ts.map +1 -1
- package/dest/store/kv_archiver_store.js +38 -17
- package/dest/store/l2_tips_cache.d.ts +19 -0
- package/dest/store/l2_tips_cache.d.ts.map +1 -0
- package/dest/store/l2_tips_cache.js +89 -0
- package/dest/store/log_store.d.ts +4 -4
- package/dest/store/log_store.d.ts.map +1 -1
- package/dest/store/log_store.js +57 -37
- package/dest/test/fake_l1_state.d.ts +9 -4
- package/dest/test/fake_l1_state.d.ts.map +1 -1
- package/dest/test/fake_l1_state.js +56 -18
- package/dest/test/index.js +3 -1
- package/dest/test/mock_archiver.d.ts +1 -1
- package/dest/test/mock_archiver.d.ts.map +1 -1
- package/dest/test/mock_archiver.js +3 -2
- package/dest/test/mock_l2_block_source.d.ts +36 -21
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +151 -109
- package/dest/test/mock_structs.d.ts +3 -2
- package/dest/test/mock_structs.d.ts.map +1 -1
- package/dest/test/mock_structs.js +11 -9
- package/dest/test/noop_l1_archiver.d.ts +23 -0
- package/dest/test/noop_l1_archiver.d.ts.map +1 -0
- package/dest/test/noop_l1_archiver.js +68 -0
- package/package.json +14 -13
- package/src/archiver.ts +71 -136
- package/src/errors.ts +12 -0
- package/src/factory.ts +30 -14
- package/src/index.ts +1 -0
- package/src/l1/README.md +25 -68
- package/src/l1/bin/retrieve-calldata.ts +45 -33
- package/src/l1/calldata_retriever.ts +249 -379
- package/src/l1/data_retrieval.ts +27 -29
- package/src/l1/spire_proposer.ts +7 -15
- package/src/l1/validate_trace.ts +24 -6
- package/src/modules/data_source_base.ts +73 -163
- package/src/modules/data_store_updater.ts +92 -63
- package/src/modules/instrumentation.ts +46 -14
- package/src/modules/l1_synchronizer.ts +26 -24
- package/src/store/block_store.ts +188 -92
- package/src/store/contract_class_store.ts +11 -7
- package/src/store/kv_archiver_store.ts +69 -29
- package/src/store/l2_tips_cache.ts +89 -0
- package/src/store/log_store.ts +105 -43
- package/src/test/fake_l1_state.ts +77 -19
- package/src/test/index.ts +3 -0
- package/src/test/mock_archiver.ts +3 -2
- package/src/test/mock_l2_block_source.ts +196 -126
- package/src/test/mock_structs.ts +26 -10
- package/src/test/noop_l1_archiver.ts +109 -0
|
@@ -8,24 +8,28 @@ import { createLogger } from '@aztec/foundation/log';
|
|
|
8
8
|
import type { FunctionSelector } from '@aztec/stdlib/abi';
|
|
9
9
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
10
10
|
import {
|
|
11
|
+
type BlockData,
|
|
12
|
+
BlockHash,
|
|
11
13
|
CheckpointedL2Block,
|
|
12
|
-
|
|
13
|
-
L2BlockNew,
|
|
14
|
+
L2Block,
|
|
14
15
|
type L2BlockSource,
|
|
15
16
|
type L2Tips,
|
|
16
17
|
type ValidateCheckpointResult,
|
|
17
18
|
} from '@aztec/stdlib/block';
|
|
18
|
-
import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
19
|
+
import { Checkpoint, type CheckpointData, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
19
20
|
import type { ContractClassPublic, ContractDataSource, ContractInstanceWithAddress } from '@aztec/stdlib/contract';
|
|
20
21
|
import { EmptyL1RollupConstants, type L1RollupConstants, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
|
|
21
|
-
import {
|
|
22
|
+
import { computeCheckpointOutHash } from '@aztec/stdlib/messaging';
|
|
23
|
+
import { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
24
|
+
import { type BlockHeader, TxExecutionResult, TxHash, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
|
|
22
25
|
import type { UInt64 } from '@aztec/stdlib/types';
|
|
23
26
|
|
|
24
27
|
/**
|
|
25
28
|
* A mocked implementation of L2BlockSource to be used in tests.
|
|
26
29
|
*/
|
|
27
30
|
export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
|
|
28
|
-
protected l2Blocks:
|
|
31
|
+
protected l2Blocks: L2Block[] = [];
|
|
32
|
+
protected checkpointList: Checkpoint[] = [];
|
|
29
33
|
|
|
30
34
|
private provenBlockNumber: number = 0;
|
|
31
35
|
private finalizedBlockNumber: number = 0;
|
|
@@ -33,23 +37,49 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
|
|
|
33
37
|
|
|
34
38
|
private log = createLogger('archiver:mock_l2_block_source');
|
|
35
39
|
|
|
40
|
+
/** Creates blocks grouped into single-block checkpoints. */
|
|
36
41
|
public async createBlocks(numBlocks: number) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
await this.createCheckpoints(numBlocks, 1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Creates checkpoints, each containing `blocksPerCheckpoint` blocks. */
|
|
46
|
+
public async createCheckpoints(numCheckpoints: number, blocksPerCheckpoint: number = 1) {
|
|
47
|
+
for (let c = 0; c < numCheckpoints; c++) {
|
|
48
|
+
const checkpointNum = CheckpointNumber(this.checkpointList.length + 1);
|
|
49
|
+
const startBlockNum = this.l2Blocks.length + 1;
|
|
50
|
+
const slotNumber = SlotNumber(Number(checkpointNum));
|
|
51
|
+
const checkpoint = await Checkpoint.random(checkpointNum, {
|
|
52
|
+
numBlocks: blocksPerCheckpoint,
|
|
53
|
+
startBlockNumber: startBlockNum,
|
|
54
|
+
slotNumber,
|
|
55
|
+
checkpointNumber: checkpointNum,
|
|
56
|
+
});
|
|
57
|
+
this.checkpointList.push(checkpoint);
|
|
58
|
+
this.l2Blocks.push(...checkpoint.blocks);
|
|
41
59
|
}
|
|
42
60
|
|
|
43
|
-
this.log.verbose(
|
|
61
|
+
this.log.verbose(
|
|
62
|
+
`Created ${numCheckpoints} checkpoints with ${blocksPerCheckpoint} blocks each in the mock L2 block source`,
|
|
63
|
+
);
|
|
44
64
|
}
|
|
45
65
|
|
|
46
|
-
public
|
|
66
|
+
public addProposedBlocks(blocks: L2Block[]) {
|
|
47
67
|
this.l2Blocks.push(...blocks);
|
|
48
|
-
this.log.verbose(`Added ${blocks.length} blocks to the mock L2 block source`);
|
|
68
|
+
this.log.verbose(`Added ${blocks.length} proposed blocks to the mock L2 block source`);
|
|
49
69
|
}
|
|
50
70
|
|
|
51
71
|
public removeBlocks(numBlocks: number) {
|
|
52
72
|
this.l2Blocks = this.l2Blocks.slice(0, -numBlocks);
|
|
73
|
+
const maxBlockNum = this.l2Blocks.length;
|
|
74
|
+
// Remove any checkpoint whose last block is beyond the remaining blocks.
|
|
75
|
+
this.checkpointList = this.checkpointList.filter(c => {
|
|
76
|
+
const lastBlockNum = c.blocks[0].number + c.blocks.length - 1;
|
|
77
|
+
return lastBlockNum <= maxBlockNum;
|
|
78
|
+
});
|
|
79
|
+
// Keep tip numbers consistent with remaining blocks.
|
|
80
|
+
this.checkpointedBlockNumber = Math.min(this.checkpointedBlockNumber, maxBlockNum);
|
|
81
|
+
this.provenBlockNumber = Math.min(this.provenBlockNumber, maxBlockNum);
|
|
82
|
+
this.finalizedBlockNumber = Math.min(this.finalizedBlockNumber, maxBlockNum);
|
|
53
83
|
this.log.verbose(`Removed ${numBlocks} blocks from the mock L2 block source`);
|
|
54
84
|
}
|
|
55
85
|
|
|
@@ -65,7 +95,33 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
|
|
|
65
95
|
}
|
|
66
96
|
|
|
67
97
|
public setCheckpointedBlockNumber(checkpointedBlockNumber: number) {
|
|
98
|
+
const prevCheckpointed = this.checkpointedBlockNumber;
|
|
68
99
|
this.checkpointedBlockNumber = checkpointedBlockNumber;
|
|
100
|
+
// Auto-create single-block checkpoints for newly checkpointed blocks that don't have one yet.
|
|
101
|
+
// This handles blocks added via addProposedBlocks that are now being marked as checkpointed.
|
|
102
|
+
const newCheckpoints: Checkpoint[] = [];
|
|
103
|
+
for (let blockNum = prevCheckpointed + 1; blockNum <= checkpointedBlockNumber; blockNum++) {
|
|
104
|
+
const block = this.l2Blocks[blockNum - 1];
|
|
105
|
+
if (!block) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (this.checkpointList.some(c => c.blocks.some(b => b.number === block.number))) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const checkpointNum = CheckpointNumber(this.checkpointList.length + newCheckpoints.length + 1);
|
|
112
|
+
const checkpoint = new Checkpoint(
|
|
113
|
+
block.archive,
|
|
114
|
+
CheckpointHeader.random({ slotNumber: block.header.globalVariables.slotNumber }),
|
|
115
|
+
[block],
|
|
116
|
+
checkpointNum,
|
|
117
|
+
);
|
|
118
|
+
newCheckpoints.push(checkpoint);
|
|
119
|
+
}
|
|
120
|
+
// Insert new checkpoints in order by number.
|
|
121
|
+
if (newCheckpoints.length > 0) {
|
|
122
|
+
this.checkpointList.push(...newCheckpoints);
|
|
123
|
+
this.checkpointList.sort((a, b) => a.number - b.number);
|
|
124
|
+
}
|
|
69
125
|
}
|
|
70
126
|
|
|
71
127
|
/**
|
|
@@ -96,6 +152,14 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
|
|
|
96
152
|
return Promise.resolve(BlockNumber(this.provenBlockNumber));
|
|
97
153
|
}
|
|
98
154
|
|
|
155
|
+
public getCheckpointedL2BlockNumber() {
|
|
156
|
+
return Promise.resolve(BlockNumber(this.checkpointedBlockNumber));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
public getFinalizedL2BlockNumber() {
|
|
160
|
+
return Promise.resolve(BlockNumber(this.finalizedBlockNumber));
|
|
161
|
+
}
|
|
162
|
+
|
|
99
163
|
public getCheckpointedBlock(number: BlockNumber): Promise<CheckpointedL2Block | undefined> {
|
|
100
164
|
if (number > this.checkpointedBlockNumber) {
|
|
101
165
|
return Promise.resolve(undefined);
|
|
@@ -104,20 +168,10 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
|
|
|
104
168
|
if (!block) {
|
|
105
169
|
return Promise.resolve(undefined);
|
|
106
170
|
}
|
|
107
|
-
|
|
108
|
-
CheckpointNumber(number),
|
|
109
|
-
block,
|
|
110
|
-
new L1PublishedData(BigInt(number), BigInt(number), `0x${number.toString(16).padStart(64, '0')}`),
|
|
111
|
-
[],
|
|
112
|
-
);
|
|
113
|
-
return Promise.resolve(checkpointedBlock);
|
|
171
|
+
return Promise.resolve(this.toCheckpointedBlock(block));
|
|
114
172
|
}
|
|
115
173
|
|
|
116
|
-
public async getCheckpointedBlocks(
|
|
117
|
-
from: BlockNumber,
|
|
118
|
-
limit: number,
|
|
119
|
-
_proven?: boolean,
|
|
120
|
-
): Promise<CheckpointedL2Block[]> {
|
|
174
|
+
public async getCheckpointedBlocks(from: BlockNumber, limit: number): Promise<CheckpointedL2Block[]> {
|
|
121
175
|
const result: CheckpointedL2Block[] = [];
|
|
122
176
|
for (let i = 0; i < limit; i++) {
|
|
123
177
|
const blockNum = from + i;
|
|
@@ -137,7 +191,7 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
|
|
|
137
191
|
* @param number - The block number to return (inclusive).
|
|
138
192
|
* @returns The requested L2 block.
|
|
139
193
|
*/
|
|
140
|
-
public getBlock(number: number): Promise<
|
|
194
|
+
public getBlock(number: number): Promise<L2Block | undefined> {
|
|
141
195
|
const block = this.l2Blocks[number - 1];
|
|
142
196
|
return Promise.resolve(block);
|
|
143
197
|
}
|
|
@@ -147,7 +201,7 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
|
|
|
147
201
|
* @param number - The block number to return.
|
|
148
202
|
* @returns The requested L2 block.
|
|
149
203
|
*/
|
|
150
|
-
public
|
|
204
|
+
public getL2Block(number: BlockNumber): Promise<L2Block | undefined> {
|
|
151
205
|
const block = this.l2Blocks[number - 1];
|
|
152
206
|
return Promise.resolve(block);
|
|
153
207
|
}
|
|
@@ -158,95 +212,41 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
|
|
|
158
212
|
* @param limit - The maximum number of blocks to return.
|
|
159
213
|
* @returns The requested mocked L2 blocks.
|
|
160
214
|
*/
|
|
161
|
-
public getBlocks(from: number, limit: number
|
|
162
|
-
return Promise.resolve(
|
|
163
|
-
this.l2Blocks
|
|
164
|
-
.slice(from - 1, from - 1 + limit)
|
|
165
|
-
.filter(b => !proven || this.provenBlockNumber === undefined || b.number <= this.provenBlockNumber),
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
public getPublishedCheckpoints(from: CheckpointNumber, limit: number) {
|
|
170
|
-
// TODO(mbps): Implement this properly. This only works when we have one block per checkpoint.
|
|
171
|
-
const blocks = this.l2Blocks.slice(from - 1, from - 1 + limit);
|
|
172
|
-
return Promise.all(
|
|
173
|
-
blocks.map(async block => {
|
|
174
|
-
// Create a checkpoint from the block - manually construct since L2BlockNew doesn't have toCheckpoint()
|
|
175
|
-
const checkpoint = await Checkpoint.random(block.checkpointNumber, { numBlocks: 1 });
|
|
176
|
-
checkpoint.blocks = [block];
|
|
177
|
-
return new PublishedCheckpoint(
|
|
178
|
-
checkpoint,
|
|
179
|
-
new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
|
|
180
|
-
[],
|
|
181
|
-
);
|
|
182
|
-
}),
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
public async getCheckpointByArchive(archive: Fr): Promise<Checkpoint | undefined> {
|
|
187
|
-
// TODO(mbps): Implement this properly. This only works when we have one block per checkpoint.
|
|
188
|
-
const block = this.l2Blocks.find(b => b.archive.root.equals(archive));
|
|
189
|
-
if (!block) {
|
|
190
|
-
return undefined;
|
|
191
|
-
}
|
|
192
|
-
// Create a checkpoint from the block - manually construct since L2BlockNew doesn't have toCheckpoint()
|
|
193
|
-
const checkpoint = await Checkpoint.random(block.checkpointNumber, { numBlocks: 1 });
|
|
194
|
-
checkpoint.blocks = [block];
|
|
195
|
-
return checkpoint;
|
|
215
|
+
public getBlocks(from: number, limit: number): Promise<L2Block[]> {
|
|
216
|
+
return Promise.resolve(this.l2Blocks.slice(from - 1, from - 1 + limit));
|
|
196
217
|
}
|
|
197
218
|
|
|
198
|
-
public
|
|
199
|
-
const
|
|
200
|
-
.slice(from - 1, from - 1 + limit)
|
|
201
|
-
.filter(b => !proven || this.provenBlockNumber === undefined || b.number <= this.provenBlockNumber);
|
|
219
|
+
public getCheckpoints(from: CheckpointNumber, limit: number) {
|
|
220
|
+
const checkpoints = this.checkpointList.slice(from - 1, from - 1 + limit);
|
|
202
221
|
return Promise.resolve(
|
|
203
|
-
|
|
204
|
-
CheckpointedL2Block.fromFields({
|
|
205
|
-
checkpointNumber: CheckpointNumber(block.number),
|
|
206
|
-
block,
|
|
207
|
-
l1: new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
|
|
208
|
-
attestations: [],
|
|
209
|
-
}),
|
|
210
|
-
),
|
|
222
|
+
checkpoints.map(checkpoint => new PublishedCheckpoint(checkpoint, this.mockL1DataForCheckpoint(checkpoint), [])),
|
|
211
223
|
);
|
|
212
224
|
}
|
|
213
225
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
return
|
|
226
|
+
public getCheckpointByArchive(archive: Fr): Promise<Checkpoint | undefined> {
|
|
227
|
+
const checkpoint = this.checkpointList.find(c => c.archive.root.equals(archive));
|
|
228
|
+
return Promise.resolve(checkpoint);
|
|
217
229
|
}
|
|
218
230
|
|
|
219
|
-
public async
|
|
231
|
+
public async getCheckpointedBlockByHash(blockHash: BlockHash): Promise<CheckpointedL2Block | undefined> {
|
|
220
232
|
for (const block of this.l2Blocks) {
|
|
221
233
|
const hash = await block.hash();
|
|
222
234
|
if (hash.equals(blockHash)) {
|
|
223
|
-
return
|
|
224
|
-
checkpointNumber: CheckpointNumber(block.number),
|
|
225
|
-
block,
|
|
226
|
-
l1: new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
|
|
227
|
-
attestations: [],
|
|
228
|
-
});
|
|
235
|
+
return this.toCheckpointedBlock(block);
|
|
229
236
|
}
|
|
230
237
|
}
|
|
231
238
|
return undefined;
|
|
232
239
|
}
|
|
233
240
|
|
|
234
|
-
public
|
|
241
|
+
public getCheckpointedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
|
|
235
242
|
const block = this.l2Blocks.find(b => b.archive.root.equals(archive));
|
|
236
243
|
if (!block) {
|
|
237
244
|
return Promise.resolve(undefined);
|
|
238
245
|
}
|
|
239
|
-
return Promise.resolve(
|
|
240
|
-
CheckpointedL2Block.fromFields({
|
|
241
|
-
checkpointNumber: CheckpointNumber(block.number),
|
|
242
|
-
block,
|
|
243
|
-
l1: new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
|
|
244
|
-
attestations: [],
|
|
245
|
-
}),
|
|
246
|
-
);
|
|
246
|
+
return Promise.resolve(this.toCheckpointedBlock(block));
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
-
public async
|
|
249
|
+
public async getL2BlockByHash(blockHash: BlockHash): Promise<L2Block | undefined> {
|
|
250
250
|
for (const block of this.l2Blocks) {
|
|
251
251
|
const hash = await block.hash();
|
|
252
252
|
if (hash.equals(blockHash)) {
|
|
@@ -256,12 +256,12 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
|
|
|
256
256
|
return undefined;
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
-
public
|
|
259
|
+
public getL2BlockByArchive(archive: Fr): Promise<L2Block | undefined> {
|
|
260
260
|
const block = this.l2Blocks.find(b => b.archive.root.equals(archive));
|
|
261
261
|
return Promise.resolve(block);
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
-
public async getBlockHeaderByHash(blockHash:
|
|
264
|
+
public async getBlockHeaderByHash(blockHash: BlockHash): Promise<BlockHeader | undefined> {
|
|
265
265
|
for (const block of this.l2Blocks) {
|
|
266
266
|
const hash = await block.hash();
|
|
267
267
|
if (hash.equals(blockHash)) {
|
|
@@ -276,46 +276,77 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
|
|
|
276
276
|
return Promise.resolve(block?.header);
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
+
public async getBlockData(number: BlockNumber): Promise<BlockData | undefined> {
|
|
280
|
+
const block = this.l2Blocks[number - 1];
|
|
281
|
+
if (!block) {
|
|
282
|
+
return undefined;
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
header: block.header,
|
|
286
|
+
archive: block.archive,
|
|
287
|
+
blockHash: await block.hash(),
|
|
288
|
+
checkpointNumber: block.checkpointNumber,
|
|
289
|
+
indexWithinCheckpoint: block.indexWithinCheckpoint,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
public async getBlockDataByArchive(archive: Fr): Promise<BlockData | undefined> {
|
|
294
|
+
const block = this.l2Blocks.find(b => b.archive.root.equals(archive));
|
|
295
|
+
if (!block) {
|
|
296
|
+
return undefined;
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
header: block.header,
|
|
300
|
+
archive: block.archive,
|
|
301
|
+
blockHash: await block.hash(),
|
|
302
|
+
checkpointNumber: block.checkpointNumber,
|
|
303
|
+
indexWithinCheckpoint: block.indexWithinCheckpoint,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
279
307
|
getBlockHeader(number: number | 'latest'): Promise<BlockHeader | undefined> {
|
|
280
308
|
return Promise.resolve(this.l2Blocks.at(typeof number === 'number' ? number - 1 : -1)?.header);
|
|
281
309
|
}
|
|
282
310
|
|
|
283
311
|
getCheckpointsForEpoch(epochNumber: EpochNumber): Promise<Checkpoint[]> {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
312
|
+
return Promise.resolve(this.getCheckpointsInEpoch(epochNumber));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
getCheckpointsDataForEpoch(epochNumber: EpochNumber): Promise<CheckpointData[]> {
|
|
316
|
+
const checkpoints = this.getCheckpointsInEpoch(epochNumber);
|
|
317
|
+
return Promise.resolve(
|
|
318
|
+
checkpoints.map(
|
|
319
|
+
(checkpoint): CheckpointData => ({
|
|
320
|
+
checkpointNumber: checkpoint.number,
|
|
321
|
+
header: checkpoint.header,
|
|
322
|
+
archive: checkpoint.archive,
|
|
323
|
+
checkpointOutHash: computeCheckpointOutHash(
|
|
324
|
+
checkpoint.blocks.map(b => b.body.txEffects.map(tx => tx.l2ToL1Msgs)),
|
|
325
|
+
),
|
|
326
|
+
startBlock: checkpoint.blocks[0].number,
|
|
327
|
+
blockCount: checkpoint.blocks.length,
|
|
328
|
+
attestations: [],
|
|
329
|
+
l1: this.mockL1DataForCheckpoint(checkpoint),
|
|
330
|
+
}),
|
|
331
|
+
),
|
|
298
332
|
);
|
|
299
333
|
}
|
|
300
334
|
|
|
301
|
-
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
return slot >= start && slot <= end;
|
|
307
|
-
});
|
|
308
|
-
return Promise.resolve(blocks);
|
|
335
|
+
getCheckpointedBlocksForEpoch(epochNumber: EpochNumber): Promise<CheckpointedL2Block[]> {
|
|
336
|
+
const checkpoints = this.getCheckpointsInEpoch(epochNumber);
|
|
337
|
+
return Promise.resolve(
|
|
338
|
+
checkpoints.flatMap(checkpoint => checkpoint.blocks.map(block => this.toCheckpointedBlock(block))),
|
|
339
|
+
);
|
|
309
340
|
}
|
|
310
341
|
|
|
311
|
-
getBlocksForSlot(slotNumber: SlotNumber): Promise<
|
|
342
|
+
getBlocksForSlot(slotNumber: SlotNumber): Promise<L2Block[]> {
|
|
312
343
|
const blocks = this.l2Blocks.filter(b => b.header.globalVariables.slotNumber === slotNumber);
|
|
313
344
|
return Promise.resolve(blocks);
|
|
314
345
|
}
|
|
315
346
|
|
|
316
|
-
async
|
|
317
|
-
const
|
|
318
|
-
return
|
|
347
|
+
async getCheckpointedBlockHeadersForEpoch(epochNumber: EpochNumber): Promise<BlockHeader[]> {
|
|
348
|
+
const checkpointedBlocks = await this.getCheckpointedBlocksForEpoch(epochNumber);
|
|
349
|
+
return checkpointedBlocks.map(b => b.block.header);
|
|
319
350
|
}
|
|
320
351
|
|
|
321
352
|
/**
|
|
@@ -334,7 +365,7 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
|
|
|
334
365
|
return {
|
|
335
366
|
data: txEffect,
|
|
336
367
|
l2BlockNumber: block.number,
|
|
337
|
-
l2BlockHash:
|
|
368
|
+
l2BlockHash: await block.hash(),
|
|
338
369
|
txIndexInBlock: block.body.txEffects.indexOf(txEffect),
|
|
339
370
|
};
|
|
340
371
|
}
|
|
@@ -348,12 +379,14 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
|
|
|
348
379
|
for (const block of this.l2Blocks) {
|
|
349
380
|
for (const txEffect of block.body.txEffects) {
|
|
350
381
|
if (txEffect.txHash.equals(txHash)) {
|
|
382
|
+
// In mock, assume all txs are checkpointed with successful execution
|
|
351
383
|
return new TxReceipt(
|
|
352
384
|
txHash,
|
|
353
|
-
TxStatus.
|
|
354
|
-
|
|
385
|
+
TxStatus.CHECKPOINTED,
|
|
386
|
+
TxExecutionResult.SUCCESS,
|
|
387
|
+
undefined,
|
|
355
388
|
txEffect.transactionFee.toBigInt(),
|
|
356
|
-
|
|
389
|
+
await block.hash(),
|
|
357
390
|
block.number,
|
|
358
391
|
);
|
|
359
392
|
}
|
|
@@ -394,7 +427,10 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
|
|
|
394
427
|
|
|
395
428
|
const makeTipId = (blockId: typeof latestBlockId) => ({
|
|
396
429
|
block: blockId,
|
|
397
|
-
checkpoint: {
|
|
430
|
+
checkpoint: {
|
|
431
|
+
number: this.findCheckpointNumberForBlock(blockId.number) ?? CheckpointNumber(0),
|
|
432
|
+
hash: blockId.hash,
|
|
433
|
+
},
|
|
398
434
|
});
|
|
399
435
|
|
|
400
436
|
return {
|
|
@@ -482,4 +518,38 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
|
|
|
482
518
|
getPendingChainValidationStatus(): Promise<ValidateCheckpointResult> {
|
|
483
519
|
return Promise.resolve({ valid: true });
|
|
484
520
|
}
|
|
521
|
+
|
|
522
|
+
/** Returns checkpoints whose slot falls within the given epoch. */
|
|
523
|
+
private getCheckpointsInEpoch(epochNumber: EpochNumber): Checkpoint[] {
|
|
524
|
+
const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
|
|
525
|
+
const [start, end] = getSlotRangeForEpoch(epochNumber, { epochDuration });
|
|
526
|
+
return this.checkpointList.filter(c => c.header.slotNumber >= start && c.header.slotNumber <= end);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/** Creates a mock L1PublishedData for a checkpoint. */
|
|
530
|
+
private mockL1DataForCheckpoint(checkpoint: Checkpoint): L1PublishedData {
|
|
531
|
+
return new L1PublishedData(BigInt(checkpoint.number), BigInt(checkpoint.number), Buffer32.random().toString());
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/** Creates a CheckpointedL2Block from a block using stored checkpoint info. */
|
|
535
|
+
private toCheckpointedBlock(block: L2Block): CheckpointedL2Block {
|
|
536
|
+
const checkpoint = this.checkpointList.find(c => c.blocks.some(b => b.number === block.number));
|
|
537
|
+
const checkpointNumber = checkpoint?.number ?? block.checkpointNumber;
|
|
538
|
+
return new CheckpointedL2Block(
|
|
539
|
+
checkpointNumber,
|
|
540
|
+
block,
|
|
541
|
+
new L1PublishedData(
|
|
542
|
+
BigInt(block.number),
|
|
543
|
+
BigInt(block.number),
|
|
544
|
+
`0x${block.number.toString(16).padStart(64, '0')}`,
|
|
545
|
+
),
|
|
546
|
+
[],
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/** Finds the checkpoint number for a block, or undefined if the block is not in any checkpoint. */
|
|
551
|
+
private findCheckpointNumberForBlock(blockNumber: BlockNumber): CheckpointNumber | undefined {
|
|
552
|
+
const checkpoint = this.checkpointList.find(c => c.blocks.some(b => b.number === blockNumber));
|
|
553
|
+
return checkpoint?.number;
|
|
554
|
+
}
|
|
485
555
|
}
|
package/src/test/mock_structs.ts
CHANGED
|
@@ -12,7 +12,7 @@ import type { Secp256k1Signer } from '@aztec/foundation/crypto/secp256k1-signer'
|
|
|
12
12
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
13
13
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
14
14
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
15
|
-
import { CommitteeAttestation,
|
|
15
|
+
import { CommitteeAttestation, L2Block } from '@aztec/stdlib/block';
|
|
16
16
|
import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
17
17
|
import { PrivateLog, PublicLog, SiloedTag, Tag } from '@aztec/stdlib/logs';
|
|
18
18
|
import { InboxLeaf } from '@aztec/stdlib/messaging';
|
|
@@ -46,24 +46,40 @@ export function makeInboxMessage(
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
export function makeInboxMessages(
|
|
49
|
-
|
|
49
|
+
totalCount: number,
|
|
50
50
|
opts: {
|
|
51
51
|
initialHash?: Buffer16;
|
|
52
52
|
initialCheckpointNumber?: CheckpointNumber;
|
|
53
|
+
messagesPerCheckpoint?: number;
|
|
53
54
|
overrideFn?: (msg: InboxMessage, index: number) => InboxMessage;
|
|
54
55
|
} = {},
|
|
55
56
|
): InboxMessage[] {
|
|
56
|
-
const {
|
|
57
|
+
const {
|
|
58
|
+
initialHash = Buffer16.ZERO,
|
|
59
|
+
overrideFn = msg => msg,
|
|
60
|
+
initialCheckpointNumber = CheckpointNumber(1),
|
|
61
|
+
messagesPerCheckpoint = 1,
|
|
62
|
+
} = opts;
|
|
63
|
+
|
|
57
64
|
const messages: InboxMessage[] = [];
|
|
58
65
|
let rollingHash = initialHash;
|
|
59
|
-
for (let i = 0; i <
|
|
66
|
+
for (let i = 0; i < totalCount; i++) {
|
|
67
|
+
const msgIndex = i % messagesPerCheckpoint;
|
|
68
|
+
const checkpointNumber = CheckpointNumber.fromBigInt(
|
|
69
|
+
BigInt(initialCheckpointNumber) + BigInt(i) / BigInt(messagesPerCheckpoint),
|
|
70
|
+
);
|
|
60
71
|
const leaf = Fr.random();
|
|
61
|
-
const
|
|
62
|
-
|
|
72
|
+
const message = overrideFn(
|
|
73
|
+
makeInboxMessage(rollingHash, {
|
|
74
|
+
leaf,
|
|
75
|
+
checkpointNumber,
|
|
76
|
+
index: InboxLeaf.smallestIndexForCheckpoint(checkpointNumber) + BigInt(msgIndex),
|
|
77
|
+
}),
|
|
78
|
+
i,
|
|
79
|
+
);
|
|
63
80
|
rollingHash = message.rollingHash;
|
|
64
81
|
messages.push(message);
|
|
65
82
|
}
|
|
66
|
-
|
|
67
83
|
return messages;
|
|
68
84
|
}
|
|
69
85
|
|
|
@@ -268,8 +284,8 @@ export async function makeCheckpointWithLogs(
|
|
|
268
284
|
): Promise<PublishedCheckpoint> {
|
|
269
285
|
const { previousArchive, numTxsPerBlock = 4, privateLogs, publicLogs } = options;
|
|
270
286
|
|
|
271
|
-
const block = await
|
|
272
|
-
checkpointNumber: CheckpointNumber(blockNumber),
|
|
287
|
+
const block = await L2Block.random(BlockNumber(blockNumber), {
|
|
288
|
+
checkpointNumber: CheckpointNumber.fromBlockNumber(BlockNumber(blockNumber)),
|
|
273
289
|
indexWithinCheckpoint: IndexWithinCheckpoint(0),
|
|
274
290
|
state: makeStateForBlock(blockNumber, numTxsPerBlock),
|
|
275
291
|
...(previousArchive ? { lastArchive: previousArchive } : {}),
|
|
@@ -289,7 +305,7 @@ export async function makeCheckpointWithLogs(
|
|
|
289
305
|
AppendOnlyTreeSnapshot.random(),
|
|
290
306
|
CheckpointHeader.random(),
|
|
291
307
|
[block],
|
|
292
|
-
CheckpointNumber(blockNumber),
|
|
308
|
+
CheckpointNumber.fromBlockNumber(BlockNumber(blockNumber)),
|
|
293
309
|
);
|
|
294
310
|
return makePublishedCheckpoint(checkpoint, blockNumber);
|
|
295
311
|
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { BlobClientInterface } from '@aztec/blob-client/client';
|
|
2
|
+
import type { RollupContract } from '@aztec/ethereum/contracts';
|
|
3
|
+
import type { ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
|
|
4
|
+
import { Buffer32 } from '@aztec/foundation/buffer';
|
|
5
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
6
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
7
|
+
import type { FunctionsOf } from '@aztec/foundation/types';
|
|
8
|
+
import type { ArchiverEmitter } from '@aztec/stdlib/block';
|
|
9
|
+
import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
|
|
10
|
+
import { type TelemetryClient, type Tracer, getTelemetryClient } from '@aztec/telemetry-client';
|
|
11
|
+
|
|
12
|
+
import { mock } from 'jest-mock-extended';
|
|
13
|
+
import { EventEmitter } from 'node:events';
|
|
14
|
+
|
|
15
|
+
import { Archiver } from '../archiver.js';
|
|
16
|
+
import { ArchiverInstrumentation } from '../modules/instrumentation.js';
|
|
17
|
+
import type { ArchiverL1Synchronizer } from '../modules/l1_synchronizer.js';
|
|
18
|
+
import type { KVArchiverDataStore } from '../store/kv_archiver_store.js';
|
|
19
|
+
|
|
20
|
+
/** Noop L1 synchronizer for testing without L1 connectivity. */
|
|
21
|
+
class NoopL1Synchronizer implements FunctionsOf<ArchiverL1Synchronizer> {
|
|
22
|
+
public readonly tracer: Tracer;
|
|
23
|
+
|
|
24
|
+
constructor(tracer: Tracer) {
|
|
25
|
+
this.tracer = tracer;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
setConfig(_config: unknown) {}
|
|
29
|
+
getL1BlockNumber(): bigint | undefined {
|
|
30
|
+
return 0n;
|
|
31
|
+
}
|
|
32
|
+
getL1Timestamp(): bigint | undefined {
|
|
33
|
+
return 0n;
|
|
34
|
+
}
|
|
35
|
+
testEthereumNodeSynced(): Promise<void> {
|
|
36
|
+
return Promise.resolve();
|
|
37
|
+
}
|
|
38
|
+
syncFromL1(_initialSyncComplete: boolean): Promise<void> {
|
|
39
|
+
return Promise.resolve();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Archiver with mocked L1 connectivity for testing.
|
|
45
|
+
* Uses mock L1 clients and a noop synchronizer, enabling tests that
|
|
46
|
+
* don't require real Ethereum connectivity.
|
|
47
|
+
*/
|
|
48
|
+
export class NoopL1Archiver extends Archiver {
|
|
49
|
+
constructor(
|
|
50
|
+
dataStore: KVArchiverDataStore,
|
|
51
|
+
l1Constants: L1RollupConstants & { genesisArchiveRoot: Fr },
|
|
52
|
+
instrumentation: ArchiverInstrumentation,
|
|
53
|
+
) {
|
|
54
|
+
// Create mocks for L1 clients
|
|
55
|
+
const publicClient = mock<ViemPublicClient>();
|
|
56
|
+
const debugClient = mock<ViemPublicDebugClient>();
|
|
57
|
+
const rollup = mock<RollupContract>();
|
|
58
|
+
const blobClient = mock<BlobClientInterface>();
|
|
59
|
+
|
|
60
|
+
// Mock methods called during start()
|
|
61
|
+
blobClient.testSources.mockResolvedValue();
|
|
62
|
+
publicClient.getBlockNumber.mockResolvedValue(1n);
|
|
63
|
+
|
|
64
|
+
const events = new EventEmitter() as ArchiverEmitter;
|
|
65
|
+
const synchronizer = new NoopL1Synchronizer(instrumentation.tracer);
|
|
66
|
+
|
|
67
|
+
super(
|
|
68
|
+
publicClient,
|
|
69
|
+
debugClient,
|
|
70
|
+
rollup,
|
|
71
|
+
{
|
|
72
|
+
registryAddress: EthAddress.ZERO,
|
|
73
|
+
governanceProposerAddress: EthAddress.ZERO,
|
|
74
|
+
slashFactoryAddress: EthAddress.ZERO,
|
|
75
|
+
slashingProposerAddress: EthAddress.ZERO,
|
|
76
|
+
},
|
|
77
|
+
dataStore,
|
|
78
|
+
{
|
|
79
|
+
pollingIntervalMs: 1000,
|
|
80
|
+
batchSize: 100,
|
|
81
|
+
skipValidateCheckpointAttestations: true,
|
|
82
|
+
maxAllowedEthClientDriftSeconds: 300,
|
|
83
|
+
ethereumAllowNoDebugHosts: true, // Skip trace validation
|
|
84
|
+
},
|
|
85
|
+
blobClient,
|
|
86
|
+
instrumentation,
|
|
87
|
+
{ ...l1Constants, l1StartBlockHash: Buffer32.random() },
|
|
88
|
+
synchronizer as ArchiverL1Synchronizer,
|
|
89
|
+
events,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Override start to skip L1 validation checks. */
|
|
94
|
+
public override start(_blockUntilSynced?: boolean): Promise<void> {
|
|
95
|
+
// Just start the running promise without L1 checks
|
|
96
|
+
this.runningPromise.start();
|
|
97
|
+
return Promise.resolve();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Creates an archiver with mocked L1 connectivity for testing. */
|
|
102
|
+
export async function createNoopL1Archiver(
|
|
103
|
+
dataStore: KVArchiverDataStore,
|
|
104
|
+
l1Constants: L1RollupConstants & { genesisArchiveRoot: Fr },
|
|
105
|
+
telemetry: TelemetryClient = getTelemetryClient(),
|
|
106
|
+
): Promise<NoopL1Archiver> {
|
|
107
|
+
const instrumentation = await ArchiverInstrumentation.new(telemetry, () => dataStore.estimateSize());
|
|
108
|
+
return new NoopL1Archiver(dataStore, l1Constants, instrumentation);
|
|
109
|
+
}
|