@aztec/archiver 0.0.1-commit.04852196a → 0.0.1-commit.04d373f

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.
Files changed (117) hide show
  1. package/README.md +19 -11
  2. package/dest/archiver.d.ts +36 -19
  3. package/dest/archiver.d.ts.map +1 -1
  4. package/dest/archiver.js +250 -79
  5. package/dest/config.d.ts +6 -3
  6. package/dest/config.d.ts.map +1 -1
  7. package/dest/config.js +23 -15
  8. package/dest/errors.d.ts +55 -9
  9. package/dest/errors.d.ts.map +1 -1
  10. package/dest/errors.js +81 -14
  11. package/dest/factory.d.ts +12 -8
  12. package/dest/factory.d.ts.map +1 -1
  13. package/dest/factory.js +42 -32
  14. package/dest/index.d.ts +11 -3
  15. package/dest/index.d.ts.map +1 -1
  16. package/dest/index.js +10 -2
  17. package/dest/l1/calldata_retriever.d.ts +2 -1
  18. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  19. package/dest/l1/calldata_retriever.js +15 -5
  20. package/dest/l1/data_retrieval.d.ts +19 -10
  21. package/dest/l1/data_retrieval.d.ts.map +1 -1
  22. package/dest/l1/data_retrieval.js +25 -32
  23. package/dest/l1/trace_tx.d.ts +12 -66
  24. package/dest/l1/trace_tx.d.ts.map +1 -1
  25. package/dest/l1/validate_historical_logs.d.ts +23 -0
  26. package/dest/l1/validate_historical_logs.d.ts.map +1 -0
  27. package/dest/l1/validate_historical_logs.js +108 -0
  28. package/dest/modules/contract_data_source_adapter.d.ts +25 -0
  29. package/dest/modules/contract_data_source_adapter.d.ts.map +1 -0
  30. package/dest/modules/contract_data_source_adapter.js +40 -0
  31. package/dest/modules/data_source_base.d.ts +70 -46
  32. package/dest/modules/data_source_base.d.ts.map +1 -1
  33. package/dest/modules/data_source_base.js +270 -135
  34. package/dest/modules/data_store_updater.d.ts +39 -17
  35. package/dest/modules/data_store_updater.d.ts.map +1 -1
  36. package/dest/modules/data_store_updater.js +183 -122
  37. package/dest/modules/instrumentation.d.ts +7 -2
  38. package/dest/modules/instrumentation.d.ts.map +1 -1
  39. package/dest/modules/instrumentation.js +25 -7
  40. package/dest/modules/l1_synchronizer.d.ts +12 -7
  41. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  42. package/dest/modules/l1_synchronizer.js +430 -205
  43. package/dest/modules/validation.d.ts +4 -3
  44. package/dest/modules/validation.d.ts.map +1 -1
  45. package/dest/modules/validation.js +6 -6
  46. package/dest/store/block_store.d.ts +174 -70
  47. package/dest/store/block_store.d.ts.map +1 -1
  48. package/dest/store/block_store.js +696 -250
  49. package/dest/store/contract_class_store.d.ts +17 -4
  50. package/dest/store/contract_class_store.d.ts.map +1 -1
  51. package/dest/store/contract_class_store.js +24 -68
  52. package/dest/store/contract_instance_store.d.ts +28 -1
  53. package/dest/store/contract_instance_store.d.ts.map +1 -1
  54. package/dest/store/contract_instance_store.js +37 -2
  55. package/dest/store/data_stores.d.ts +68 -0
  56. package/dest/store/data_stores.d.ts.map +1 -0
  57. package/dest/store/data_stores.js +54 -0
  58. package/dest/store/function_names_cache.d.ts +17 -0
  59. package/dest/store/function_names_cache.d.ts.map +1 -0
  60. package/dest/store/function_names_cache.js +30 -0
  61. package/dest/store/l2_tips_cache.d.ts +13 -7
  62. package/dest/store/l2_tips_cache.d.ts.map +1 -1
  63. package/dest/store/l2_tips_cache.js +13 -76
  64. package/dest/store/log_store.d.ts +42 -37
  65. package/dest/store/log_store.d.ts.map +1 -1
  66. package/dest/store/log_store.js +262 -408
  67. package/dest/store/log_store_codec.d.ts +70 -0
  68. package/dest/store/log_store_codec.d.ts.map +1 -0
  69. package/dest/store/log_store_codec.js +101 -0
  70. package/dest/store/message_store.d.ts +11 -1
  71. package/dest/store/message_store.d.ts.map +1 -1
  72. package/dest/store/message_store.js +50 -8
  73. package/dest/test/fake_l1_state.d.ts +20 -1
  74. package/dest/test/fake_l1_state.d.ts.map +1 -1
  75. package/dest/test/fake_l1_state.js +114 -18
  76. package/dest/test/mock_l1_to_l2_message_source.d.ts +1 -1
  77. package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
  78. package/dest/test/mock_l1_to_l2_message_source.js +2 -1
  79. package/dest/test/mock_l2_block_source.d.ts +51 -46
  80. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  81. package/dest/test/mock_l2_block_source.js +243 -170
  82. package/dest/test/noop_l1_archiver.d.ts +12 -6
  83. package/dest/test/noop_l1_archiver.d.ts.map +1 -1
  84. package/dest/test/noop_l1_archiver.js +26 -9
  85. package/package.json +14 -14
  86. package/src/archiver.ts +301 -86
  87. package/src/config.ts +32 -12
  88. package/src/errors.ts +122 -21
  89. package/src/factory.ts +50 -28
  90. package/src/index.ts +18 -2
  91. package/src/l1/calldata_retriever.ts +16 -5
  92. package/src/l1/data_retrieval.ts +36 -45
  93. package/src/l1/validate_historical_logs.ts +140 -0
  94. package/src/modules/contract_data_source_adapter.ts +55 -0
  95. package/src/modules/data_source_base.ts +336 -171
  96. package/src/modules/data_store_updater.ts +219 -154
  97. package/src/modules/instrumentation.ts +28 -8
  98. package/src/modules/l1_synchronizer.ts +566 -248
  99. package/src/modules/validation.ts +10 -9
  100. package/src/store/block_store.ts +865 -290
  101. package/src/store/contract_class_store.ts +31 -103
  102. package/src/store/contract_instance_store.ts +51 -5
  103. package/src/store/data_stores.ts +104 -0
  104. package/src/store/function_names_cache.ts +37 -0
  105. package/src/store/l2_tips_cache.ts +16 -70
  106. package/src/store/log_store.ts +301 -559
  107. package/src/store/log_store_codec.ts +132 -0
  108. package/src/store/message_store.ts +59 -9
  109. package/src/structs/inbox_message.ts +1 -1
  110. package/src/test/fake_l1_state.ts +142 -29
  111. package/src/test/mock_l1_to_l2_message_source.ts +1 -0
  112. package/src/test/mock_l2_block_source.ts +303 -205
  113. package/src/test/noop_l1_archiver.ts +39 -9
  114. package/dest/store/kv_archiver_store.d.ts +0 -354
  115. package/dest/store/kv_archiver_store.d.ts.map +0 -1
  116. package/dest/store/kv_archiver_store.js +0 -464
  117. package/src/store/kv_archiver_store.ts +0 -671
@@ -1,16 +1,17 @@
1
1
  import { GENESIS_ARCHIVE_ROOT } from '@aztec/constants';
2
2
  import { DefaultL1ContractsConfig } from '@aztec/ethereum/config';
3
- import { BlockNumber, CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
3
+ import { BlockNumber, CheckpointNumber, IndexWithinCheckpoint, SlotNumber } from '@aztec/foundation/branded-types';
4
4
  import { Buffer32 } from '@aztec/foundation/buffer';
5
5
  import { Fr } from '@aztec/foundation/curves/bn254';
6
6
  import { EthAddress } from '@aztec/foundation/eth-address';
7
7
  import { createLogger } from '@aztec/foundation/log';
8
- import { CheckpointedL2Block } from '@aztec/stdlib/block';
8
+ import { Body, GENESIS_BLOCK_HEADER_HASH, GENESIS_CHECKPOINT_HEADER_HASH, L2Block } from '@aztec/stdlib/block';
9
9
  import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
10
- import { EmptyL1RollupConstants, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
10
+ import { EmptyL1RollupConstants, getEpochAtSlot, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
11
11
  import { computeCheckpointOutHash } from '@aztec/stdlib/messaging';
12
12
  import { CheckpointHeader } from '@aztec/stdlib/rollup';
13
- import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
13
+ import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
14
+ import { BlockHeader, TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
14
15
  /**
15
16
  * A mocked implementation of L2BlockSource to be used in tests.
16
17
  */ export class MockL2BlockSource {
@@ -19,7 +20,48 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
19
20
  provenBlockNumber = 0;
20
21
  finalizedBlockNumber = 0;
21
22
  checkpointedBlockNumber = 0;
23
+ proposedCheckpointBlockNumber = 0;
24
+ initialHeader = BlockHeader.empty();
25
+ initialHeaderHash = GENESIS_BLOCK_HEADER_HASH;
26
+ genesisArchiveRoot;
27
+ genesisBlock;
22
28
  log = createLogger('archiver:mock_l2_block_source');
29
+ /** Returns the initial header used to synthesize block 0. */ getInitialHeader() {
30
+ return this.initialHeader;
31
+ }
32
+ /**
33
+ * Sets the initial header used to synthesize block 0. Tests that wire up a real
34
+ * world-state should call this with `worldState.getInitialHeader()` so the L2BlockStream
35
+ * agrees on the genesis hash on both sides. Precomputes and caches the header hash so
36
+ * `getGenesisBlockHash()` can return synchronously.
37
+ */ async setInitialHeader(header) {
38
+ this.initialHeader = header;
39
+ this.initialHeaderHash = await header.hash();
40
+ this.genesisBlock = undefined;
41
+ }
42
+ /**
43
+ * Returns the precomputed hash of the genesis block header. Defaults to the static
44
+ * {@link GENESIS_BLOCK_HEADER_HASH} unless {@link setInitialHeader} has been called with a
45
+ * custom header.
46
+ */ getGenesisBlockHash() {
47
+ return this.initialHeaderHash;
48
+ }
49
+ /**
50
+ * Sets the post-genesis archive root used to synthesize block 0. Mirrors the real archiver,
51
+ * whose synthetic block 0 carries `new AppendOnlyTreeSnapshot(genesisArchiveRoot, 1)` rather
52
+ * than `AppendOnlyTreeSnapshot.empty()`. Tests wiring up a real world-state should set this so
53
+ * archive-based block lookups against the mock match production semantics.
54
+ */ setGenesisArchiveRoot(root) {
55
+ this.genesisArchiveRoot = root;
56
+ this.genesisBlock = undefined;
57
+ }
58
+ getGenesisBlock() {
59
+ if (this.genesisBlock) {
60
+ return this.genesisBlock;
61
+ }
62
+ const archive = this.genesisArchiveRoot ? new AppendOnlyTreeSnapshot(this.genesisArchiveRoot, 1) : AppendOnlyTreeSnapshot.empty();
63
+ return this.genesisBlock = new L2Block(archive, this.initialHeader, Body.empty(), CheckpointNumber.ZERO, IndexWithinCheckpoint(0));
64
+ }
23
65
  /** Creates blocks grouped into single-block checkpoints. */ async createBlocks(numBlocks) {
24
66
  await this.createCheckpoints(numBlocks, 1);
25
67
  }
@@ -56,6 +98,7 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
56
98
  });
57
99
  // Keep tip numbers consistent with remaining blocks.
58
100
  this.checkpointedBlockNumber = Math.min(this.checkpointedBlockNumber, maxBlockNum);
101
+ this.proposedCheckpointBlockNumber = Math.min(this.proposedCheckpointBlockNumber, maxBlockNum);
59
102
  this.provenBlockNumber = Math.min(this.provenBlockNumber, maxBlockNum);
60
103
  this.finalizedBlockNumber = Math.min(this.finalizedBlockNumber, maxBlockNum);
61
104
  this.log.verbose(`Removed ${numBlocks} blocks from the mock L2 block source`);
@@ -69,9 +112,16 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
69
112
  }
70
113
  this.finalizedBlockNumber = finalizedBlockNumber;
71
114
  }
115
+ setProposedCheckpointBlockNumber(blockNumber) {
116
+ this.proposedCheckpointBlockNumber = blockNumber;
117
+ }
72
118
  setCheckpointedBlockNumber(checkpointedBlockNumber) {
73
119
  const prevCheckpointed = this.checkpointedBlockNumber;
74
120
  this.checkpointedBlockNumber = checkpointedBlockNumber;
121
+ // Proposed checkpoint is always at least as advanced as checkpointed
122
+ if (this.proposedCheckpointBlockNumber < checkpointedBlockNumber) {
123
+ this.proposedCheckpointBlockNumber = checkpointedBlockNumber;
124
+ }
75
125
  // Auto-create single-block checkpoints for newly checkpointed blocks that don't have one yet.
76
126
  // This handles blocks added via addProposedBlocks that are now being marked as checkpointed.
77
127
  const newCheckpoints = [];
@@ -109,176 +159,93 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
109
159
  */ getRegistryAddress() {
110
160
  return Promise.resolve(EthAddress.random());
111
161
  }
112
- /**
113
- * Gets the number of the latest L2 block processed by the block source implementation.
114
- * @returns In this mock instance, returns the number of L2 blocks that we've mocked.
115
- */ getBlockNumber() {
116
- return Promise.resolve(BlockNumber(this.l2Blocks.length));
117
- }
118
- getProvenBlockNumber() {
119
- return Promise.resolve(BlockNumber(this.provenBlockNumber));
120
- }
121
- getCheckpointedL2BlockNumber() {
122
- return Promise.resolve(BlockNumber(this.checkpointedBlockNumber));
123
- }
124
- getFinalizedL2BlockNumber() {
125
- return Promise.resolve(BlockNumber(this.finalizedBlockNumber));
126
- }
127
- getCheckpointedBlock(number) {
128
- if (number > this.checkpointedBlockNumber) {
129
- return Promise.resolve(undefined);
162
+ async getBlockNumber(query) {
163
+ if (!query) {
164
+ return BlockNumber(this.l2Blocks.length);
130
165
  }
131
- const block = this.l2Blocks[number - 1];
132
- if (!block) {
133
- return Promise.resolve(undefined);
166
+ if ('number' in query) {
167
+ return query.number;
134
168
  }
135
- return Promise.resolve(this.toCheckpointedBlock(block));
136
- }
137
- async getCheckpointedBlocks(from, limit) {
138
- const result = [];
139
- for(let i = 0; i < limit; i++){
140
- const blockNum = from + i;
141
- if (blockNum > this.checkpointedBlockNumber) {
142
- break;
143
- }
144
- const block = await this.getCheckpointedBlock(BlockNumber(blockNum));
145
- if (block) {
146
- result.push(block);
147
- }
169
+ if ('tag' in query) {
170
+ return BlockNumber(this.resolveBlockTag(query.tag));
148
171
  }
149
- return result;
172
+ const block = await this.getBlock(query);
173
+ return block ? block.header.globalVariables.blockNumber : undefined;
150
174
  }
151
- /**
152
- * Gets an l2 block.
153
- * @param number - The block number to return (inclusive).
154
- * @returns The requested L2 block.
155
- */ getBlock(number) {
156
- const block = this.l2Blocks[number - 1];
157
- return Promise.resolve(block);
175
+ getProposedCheckpointL2BlockNumber() {
176
+ return Promise.resolve(BlockNumber(this.proposedCheckpointBlockNumber));
158
177
  }
159
- /**
160
- * Gets an L2 block (new format).
161
- * @param number - The block number to return.
162
- * @returns The requested L2 block.
163
- */ getL2Block(number) {
164
- const block = this.l2Blocks[number - 1];
165
- return Promise.resolve(block);
178
+ getCheckpoint(query) {
179
+ const checkpoint = this.resolveCheckpointQuery(query);
180
+ if (!checkpoint) {
181
+ return Promise.resolve(undefined);
182
+ }
183
+ return Promise.resolve(new PublishedCheckpoint(checkpoint, this.mockL1DataForCheckpoint(checkpoint), []));
166
184
  }
167
- /**
168
- * Gets up to `limit` amount of L2 blocks starting from `from`.
169
- * @param from - Number of the first block to return (inclusive).
170
- * @param limit - The maximum number of blocks to return.
171
- * @returns The requested mocked L2 blocks.
172
- */ getBlocks(from, limit) {
173
- return Promise.resolve(this.l2Blocks.slice(from - 1, from - 1 + limit));
174
- }
175
- getCheckpoints(from, limit) {
176
- const checkpoints = this.checkpointList.slice(from - 1, from - 1 + limit);
185
+ getCheckpoints(query) {
186
+ const checkpoints = this.resolveCheckpointsQuery(query);
177
187
  return Promise.resolve(checkpoints.map((checkpoint)=>new PublishedCheckpoint(checkpoint, this.mockL1DataForCheckpoint(checkpoint), [])));
178
188
  }
179
189
  getCheckpointByArchive(archive) {
180
190
  const checkpoint = this.checkpointList.find((c)=>c.archive.root.equals(archive));
181
191
  return Promise.resolve(checkpoint);
182
192
  }
183
- async getCheckpointedBlockByHash(blockHash) {
184
- for (const block of this.l2Blocks){
185
- const hash = await block.hash();
186
- if (hash.equals(blockHash)) {
187
- return this.toCheckpointedBlock(block);
188
- }
189
- }
190
- return undefined;
191
- }
192
- getCheckpointedBlockByArchive(archive) {
193
- const block = this.l2Blocks.find((b)=>b.archive.root.equals(archive));
194
- if (!block) {
193
+ getCheckpointData(query) {
194
+ const checkpoint = this.resolveCheckpointQuery(query);
195
+ if (!checkpoint) {
195
196
  return Promise.resolve(undefined);
196
197
  }
197
- return Promise.resolve(this.toCheckpointedBlock(block));
198
- }
199
- async getL2BlockByHash(blockHash) {
200
- for (const block of this.l2Blocks){
201
- const hash = await block.hash();
202
- if (hash.equals(blockHash)) {
203
- return block;
204
- }
205
- }
206
- return undefined;
198
+ return Promise.resolve(this.checkpointToData(checkpoint));
207
199
  }
208
- getL2BlockByArchive(archive) {
209
- const block = this.l2Blocks.find((b)=>b.archive.root.equals(archive));
210
- return Promise.resolve(block);
200
+ getCheckpointsData(query) {
201
+ const checkpoints = this.resolveCheckpointsQuery(query);
202
+ return Promise.resolve(checkpoints.map((c)=>this.checkpointToData(c)));
211
203
  }
212
- async getBlockHeaderByHash(blockHash) {
213
- for (const block of this.l2Blocks){
214
- const hash = await block.hash();
215
- if (hash.equals(blockHash)) {
216
- return block.header;
217
- }
218
- }
219
- return undefined;
220
- }
221
- getBlockHeaderByArchive(archive) {
222
- const block = this.l2Blocks.find((b)=>b.archive.root.equals(archive));
223
- return Promise.resolve(block?.header);
224
- }
225
- async getBlockData(number) {
226
- const block = this.l2Blocks[number - 1];
227
- if (!block) {
228
- return undefined;
229
- }
204
+ checkpointToData(checkpoint) {
230
205
  return {
231
- header: block.header,
232
- archive: block.archive,
233
- blockHash: await block.hash(),
234
- checkpointNumber: block.checkpointNumber,
235
- indexWithinCheckpoint: block.indexWithinCheckpoint
206
+ checkpointNumber: checkpoint.number,
207
+ header: checkpoint.header,
208
+ archive: checkpoint.archive,
209
+ checkpointOutHash: computeCheckpointOutHash(checkpoint.blocks.map((b)=>b.body.txEffects.map((tx)=>tx.l2ToL1Msgs))),
210
+ startBlock: checkpoint.blocks[0].number,
211
+ blockCount: checkpoint.blocks.length,
212
+ feeAssetPriceModifier: checkpoint.feeAssetPriceModifier,
213
+ attestations: [],
214
+ l1: this.mockL1DataForCheckpoint(checkpoint)
236
215
  };
237
216
  }
238
- async getBlockDataByArchive(archive) {
239
- const block = this.l2Blocks.find((b)=>b.archive.root.equals(archive));
240
- if (!block) {
241
- return undefined;
217
+ resolveCheckpointQuery(query) {
218
+ if ('number' in query) {
219
+ return this.checkpointList[query.number - 1];
220
+ }
221
+ if ('slot' in query) {
222
+ return this.checkpointList.find((c)=>c.header.slotNumber === query.slot);
223
+ }
224
+ switch(query.tag){
225
+ case 'checkpointed':
226
+ return this.checkpointList[this.checkpointList.length - 1];
227
+ case 'proven':
228
+ {
229
+ const provenCheckpoint = this.checkpointList.filter((c)=>c.blocks.some((b)=>b.number <= this.provenBlockNumber));
230
+ return provenCheckpoint.at(-1);
231
+ }
232
+ case 'finalized':
233
+ {
234
+ const finalizedCheckpoint = this.checkpointList.filter((c)=>c.blocks.some((b)=>b.number <= this.finalizedBlockNumber));
235
+ return finalizedCheckpoint.at(-1);
236
+ }
242
237
  }
243
- return {
244
- header: block.header,
245
- archive: block.archive,
246
- blockHash: await block.hash(),
247
- checkpointNumber: block.checkpointNumber,
248
- indexWithinCheckpoint: block.indexWithinCheckpoint
249
- };
250
- }
251
- getBlockHeader(number) {
252
- return Promise.resolve(this.l2Blocks.at(typeof number === 'number' ? number - 1 : -1)?.header);
253
- }
254
- getCheckpointsForEpoch(epochNumber) {
255
- return Promise.resolve(this.getCheckpointsInEpoch(epochNumber));
256
- }
257
- getCheckpointsDataForEpoch(epochNumber) {
258
- const checkpoints = this.getCheckpointsInEpoch(epochNumber);
259
- return Promise.resolve(checkpoints.map((checkpoint)=>({
260
- checkpointNumber: checkpoint.number,
261
- header: checkpoint.header,
262
- archive: checkpoint.archive,
263
- checkpointOutHash: computeCheckpointOutHash(checkpoint.blocks.map((b)=>b.body.txEffects.map((tx)=>tx.l2ToL1Msgs))),
264
- startBlock: checkpoint.blocks[0].number,
265
- blockCount: checkpoint.blocks.length,
266
- attestations: [],
267
- l1: this.mockL1DataForCheckpoint(checkpoint)
268
- })));
269
238
  }
270
- getCheckpointedBlocksForEpoch(epochNumber) {
271
- const checkpoints = this.getCheckpointsInEpoch(epochNumber);
272
- return Promise.resolve(checkpoints.flatMap((checkpoint)=>checkpoint.blocks.map((block)=>this.toCheckpointedBlock(block))));
239
+ resolveCheckpointsQuery(query) {
240
+ if ('from' in query) {
241
+ return this.checkpointList.slice(query.from - 1, query.from - 1 + query.limit);
242
+ }
243
+ return this.getCheckpointsInEpoch(query.epoch);
273
244
  }
274
245
  getBlocksForSlot(slotNumber) {
275
246
  const blocks = this.l2Blocks.filter((b)=>b.header.globalVariables.slotNumber === slotNumber);
276
247
  return Promise.resolve(blocks);
277
248
  }
278
- async getCheckpointedBlockHeadersForEpoch(epochNumber) {
279
- const checkpointedBlocks = await this.getCheckpointedBlocksForEpoch(epochNumber);
280
- return checkpointedBlocks.map((b)=>b.block.header);
281
- }
282
249
  /**
283
250
  * Gets a tx effect.
284
251
  * @param txHash - The hash of the tx corresponding to the tx effect.
@@ -296,7 +263,7 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
296
263
  data: txEffect,
297
264
  l2BlockNumber: block.number,
298
265
  l2BlockHash: await block.hash(),
299
- txIndexInBlock: block.body.txEffects.indexOf(txEffect)
266
+ txIndexInBlock: block.body.txEffects.findIndex((t)=>t.txHash.equals(txHash))
300
267
  };
301
268
  }
302
269
  /**
@@ -308,57 +275,80 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
308
275
  for (const txEffect of block.body.txEffects){
309
276
  if (txEffect.txHash.equals(txHash)) {
310
277
  // In mock, assume all txs are checkpointed with successful execution
311
- return new TxReceipt(txHash, TxStatus.CHECKPOINTED, TxExecutionResult.SUCCESS, undefined, txEffect.transactionFee.toBigInt(), await block.hash(), block.number);
278
+ return new TxReceipt(txHash, TxStatus.CHECKPOINTED, TxExecutionResult.SUCCESS, undefined, txEffect.transactionFee.toBigInt(), await block.hash(), block.number, getEpochAtSlot(block.slot, EmptyL1RollupConstants));
312
279
  }
313
280
  }
314
281
  }
315
282
  return undefined;
316
283
  }
317
284
  async getL2Tips() {
318
- const [latest, proven, finalized, checkpointed] = [
285
+ const [latest, proven, finalized, checkpointed, proposedCheckpoint] = [
319
286
  await this.getBlockNumber(),
320
- await this.getProvenBlockNumber(),
287
+ this.provenBlockNumber,
321
288
  this.finalizedBlockNumber,
322
- this.checkpointedBlockNumber
289
+ this.checkpointedBlockNumber,
290
+ await this.getProposedCheckpointL2BlockNumber()
323
291
  ];
324
292
  const latestBlock = this.l2Blocks[latest - 1];
325
293
  const provenBlock = this.l2Blocks[proven - 1];
326
294
  const finalizedBlock = this.l2Blocks[finalized - 1];
327
295
  const checkpointedBlock = this.l2Blocks[checkpointed - 1];
296
+ const proposedCheckpointBlock = this.l2Blocks[proposedCheckpoint - 1];
297
+ // For genesis tips (block number 0) report the dynamic initial header hash so consumers
298
+ // running L2BlockStream against this mock agree at block 0 with their local tip store.
299
+ const genesisHash = (await this.initialHeader.hash()).toString();
300
+ const tipHash = async (block, number)=>{
301
+ if (block) {
302
+ return (await block.hash()).toString();
303
+ }
304
+ return number === 0 ? genesisHash : '';
305
+ };
328
306
  const latestBlockId = {
329
307
  number: BlockNumber(latest),
330
- hash: (await latestBlock?.hash())?.toString()
308
+ hash: await tipHash(latestBlock, latest)
331
309
  };
332
310
  const provenBlockId = {
333
311
  number: BlockNumber(proven),
334
- hash: (await provenBlock?.hash())?.toString()
312
+ hash: await tipHash(provenBlock, proven)
335
313
  };
336
314
  const finalizedBlockId = {
337
315
  number: BlockNumber(finalized),
338
- hash: (await finalizedBlock?.hash())?.toString()
316
+ hash: await tipHash(finalizedBlock, finalized)
339
317
  };
340
318
  const checkpointedBlockId = {
341
319
  number: BlockNumber(checkpointed),
342
- hash: (await checkpointedBlock?.hash())?.toString()
320
+ hash: await tipHash(checkpointedBlock, checkpointed)
321
+ };
322
+ const proposedCheckpointBlockId = {
323
+ number: BlockNumber(proposedCheckpoint),
324
+ hash: await tipHash(proposedCheckpointBlock, proposedCheckpoint)
343
325
  };
344
- const makeTipId = (blockId)=>({
326
+ const makeTipId = (blockId)=>{
327
+ const checkpointNumber = this.findCheckpointNumberForBlock(blockId.number) ?? CheckpointNumber(0);
328
+ // Match production semantics: checkpoint 0 is fully synthetic (no real checkpoint header
329
+ // exists at 0), so its hash stays at the protocol constant `GENESIS_CHECKPOINT_HEADER_HASH`
330
+ // even though the block-0 hash is dynamic. See L2TipsCache for the production path.
331
+ const hash = checkpointNumber === 0 ? GENESIS_CHECKPOINT_HEADER_HASH.toString() : blockId.hash;
332
+ return {
345
333
  block: blockId,
346
334
  checkpoint: {
347
- number: this.findCheckpointNumberForBlock(blockId.number) ?? CheckpointNumber(0),
348
- hash: blockId.hash
335
+ number: checkpointNumber,
336
+ hash
349
337
  }
350
- });
338
+ };
339
+ };
351
340
  return {
352
341
  proposed: latestBlockId,
353
342
  checkpointed: makeTipId(checkpointedBlockId),
354
343
  proven: makeTipId(provenBlockId),
355
- finalized: makeTipId(finalizedBlockId)
344
+ finalized: makeTipId(finalizedBlockId),
345
+ proposedCheckpoint: makeTipId(proposedCheckpointBlockId)
356
346
  };
357
347
  }
358
- getL2EpochNumber() {
348
+ getSyncedL2EpochNumber() {
359
349
  throw new Error('Method not implemented.');
360
350
  }
361
- getL2SlotNumber() {
351
+ getSyncedL2SlotNumber() {
362
352
  throw new Error('Method not implemented.');
363
353
  }
364
354
  isEpochComplete(_epochNumber) {
@@ -367,9 +357,12 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
367
357
  getL1Constants() {
368
358
  return Promise.resolve(EmptyL1RollupConstants);
369
359
  }
360
+ isPruneDueAtSlot(_slot) {
361
+ return Promise.resolve(false);
362
+ }
370
363
  getGenesisValues() {
371
364
  return Promise.resolve({
372
- genesisArchiveRoot: new Fr(GENESIS_ARCHIVE_ROOT)
365
+ genesisArchiveRoot: this.genesisArchiveRoot ?? new Fr(GENESIS_ARCHIVE_ROOT)
373
366
  });
374
367
  }
375
368
  getL1Timestamp() {
@@ -410,6 +403,88 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
410
403
  syncImmediate() {
411
404
  return Promise.resolve();
412
405
  }
406
+ async getBlock(query) {
407
+ if ('number' in query) {
408
+ if (query.number === 0) {
409
+ return this.getGenesisBlock();
410
+ }
411
+ return this.l2Blocks[query.number - 1];
412
+ }
413
+ if ('hash' in query) {
414
+ const genesis = this.getGenesisBlock();
415
+ if ((await genesis.hash()).equals(query.hash)) {
416
+ return genesis;
417
+ }
418
+ for (const b of this.l2Blocks){
419
+ const hash = await b.hash();
420
+ if (hash.equals(query.hash)) {
421
+ return b;
422
+ }
423
+ }
424
+ return undefined;
425
+ }
426
+ if ('archive' in query) {
427
+ const genesis = this.getGenesisBlock();
428
+ if (genesis.archive.root.equals(query.archive)) {
429
+ return genesis;
430
+ }
431
+ return this.l2Blocks.find((b)=>b.archive.root.equals(query.archive));
432
+ }
433
+ const number = this.resolveBlockTag(query.tag);
434
+ if (number === 0) {
435
+ return this.getGenesisBlock();
436
+ }
437
+ return this.l2Blocks[number - 1];
438
+ }
439
+ resolveBlockTag(tag) {
440
+ switch(tag){
441
+ case 'latest':
442
+ case 'proposed':
443
+ return this.l2Blocks.length;
444
+ case 'checkpointed':
445
+ return this.checkpointedBlockNumber;
446
+ case 'proven':
447
+ return this.provenBlockNumber;
448
+ case 'finalized':
449
+ return this.finalizedBlockNumber;
450
+ }
451
+ }
452
+ getBlocks(query) {
453
+ let blocks;
454
+ if ('from' in query) {
455
+ blocks = this.l2Blocks.slice(query.from - 1, query.from - 1 + query.limit);
456
+ } else {
457
+ const epochCheckpoints = this.getCheckpointsInEpoch(query.epoch);
458
+ blocks = epochCheckpoints.flatMap((c)=>c.blocks);
459
+ }
460
+ if (query.onlyCheckpointed) {
461
+ blocks = blocks.filter((b)=>b.header.globalVariables.blockNumber <= this.checkpointedBlockNumber);
462
+ }
463
+ return Promise.resolve(blocks);
464
+ }
465
+ async getBlockData(query) {
466
+ const block = await this.getBlock(query);
467
+ if (!block) {
468
+ return undefined;
469
+ }
470
+ return {
471
+ header: block.header,
472
+ archive: block.archive,
473
+ blockHash: await block.hash(),
474
+ checkpointNumber: block.checkpointNumber,
475
+ indexWithinCheckpoint: block.indexWithinCheckpoint
476
+ };
477
+ }
478
+ async getBlocksData(query) {
479
+ const blocks = await this.getBlocks(query);
480
+ return Promise.all(blocks.map(async (block)=>({
481
+ header: block.header,
482
+ archive: block.archive,
483
+ blockHash: await block.hash(),
484
+ checkpointNumber: block.checkpointNumber,
485
+ indexWithinCheckpoint: block.indexWithinCheckpoint
486
+ })));
487
+ }
413
488
  isPendingChainInvalid() {
414
489
  return Promise.resolve(false);
415
490
  }
@@ -418,6 +493,9 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
418
493
  valid: true
419
494
  });
420
495
  }
496
+ getProposedCheckpointData(_query) {
497
+ return Promise.resolve(undefined);
498
+ }
421
499
  /** Returns checkpoints whose slot falls within the given epoch. */ getCheckpointsInEpoch(epochNumber) {
422
500
  const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
423
501
  const [start, end] = getSlotRangeForEpoch(epochNumber, {
@@ -428,11 +506,6 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
428
506
  /** Creates a mock L1PublishedData for a checkpoint. */ mockL1DataForCheckpoint(checkpoint) {
429
507
  return new L1PublishedData(BigInt(checkpoint.number), BigInt(checkpoint.number), Buffer32.random().toString());
430
508
  }
431
- /** Creates a CheckpointedL2Block from a block using stored checkpoint info. */ toCheckpointedBlock(block) {
432
- const checkpoint = this.checkpointList.find((c)=>c.blocks.some((b)=>b.number === block.number));
433
- const checkpointNumber = checkpoint?.number ?? block.checkpointNumber;
434
- return new CheckpointedL2Block(checkpointNumber, block, new L1PublishedData(BigInt(block.number), BigInt(block.number), `0x${block.number.toString(16).padStart(64, '0')}`), []);
435
- }
436
509
  /** Finds the checkpoint number for a block, or undefined if the block is not in any checkpoint. */ findCheckpointNumberForBlock(blockNumber) {
437
510
  const checkpoint = this.checkpointList.find((c)=>c.blocks.some((b)=>b.number === blockNumber));
438
511
  return checkpoint?.number;
@@ -1,23 +1,29 @@
1
+ import { SlotNumber } from '@aztec/foundation/branded-types';
1
2
  import { Fr } from '@aztec/foundation/curves/bn254';
3
+ import type { BlockHash } from '@aztec/stdlib/block';
2
4
  import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
5
+ import type { BlockHeader } from '@aztec/stdlib/tx';
3
6
  import { type TelemetryClient } from '@aztec/telemetry-client';
4
7
  import { Archiver } from '../archiver.js';
5
8
  import { ArchiverInstrumentation } from '../modules/instrumentation.js';
6
- import type { KVArchiverDataStore } from '../store/kv_archiver_store.js';
9
+ import type { ArchiverDataStores } from '../store/data_stores.js';
10
+ import { L2TipsCache } from '../store/l2_tips_cache.js';
7
11
  /**
8
12
  * Archiver with mocked L1 connectivity for testing.
9
13
  * Uses mock L1 clients and a noop synchronizer, enabling tests that
10
14
  * don't require real Ethereum connectivity.
11
15
  */
12
16
  export declare class NoopL1Archiver extends Archiver {
13
- constructor(dataStore: KVArchiverDataStore, l1Constants: L1RollupConstants & {
17
+ constructor(dataStores: ArchiverDataStores, l1Constants: L1RollupConstants & {
14
18
  genesisArchiveRoot: Fr;
15
- }, instrumentation: ArchiverInstrumentation);
19
+ }, instrumentation: ArchiverInstrumentation, initialHeader: BlockHeader, initialBlockHash: BlockHash, l2TipsCache: L2TipsCache);
16
20
  /** Override start to skip L1 validation checks. */
17
21
  start(_blockUntilSynced?: boolean): Promise<void>;
22
+ /** Always reports as fully synced since there is no real L1 to sync from. */
23
+ getSyncedL2SlotNumber(): Promise<SlotNumber | undefined>;
18
24
  }
19
25
  /** Creates an archiver with mocked L1 connectivity for testing. */
20
- export declare function createNoopL1Archiver(dataStore: KVArchiverDataStore, l1Constants: L1RollupConstants & {
26
+ export declare function createNoopL1Archiver(dataStores: ArchiverDataStores, l1Constants: L1RollupConstants & {
21
27
  genesisArchiveRoot: Fr;
22
- }, telemetry?: TelemetryClient): Promise<NoopL1Archiver>;
23
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9vcF9sMV9hcmNoaXZlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Rlc3Qvbm9vcF9sMV9hcmNoaXZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFJQSxPQUFPLEVBQUUsRUFBRSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFJcEQsT0FBTyxLQUFLLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUNyRSxPQUFPLEVBQUUsS0FBSyxlQUFlLEVBQW1DLE1BQU0seUJBQXlCLENBQUM7QUFLaEcsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzFDLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBRXhFLE9BQU8sS0FBSyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sK0JBQStCLENBQUM7QUF5QnpFOzs7O0dBSUc7QUFDSCxxQkFBYSxjQUFlLFNBQVEsUUFBUTtJQUMxQyxZQUNFLFNBQVMsRUFBRSxtQkFBbUIsRUFDOUIsV0FBVyxFQUFFLGlCQUFpQixHQUFHO1FBQUUsa0JBQWtCLEVBQUUsRUFBRSxDQUFBO0tBQUUsRUFDM0QsZUFBZSxFQUFFLHVCQUF1QixFQXVDekM7SUFFRCxtREFBbUQ7SUFDbkMsS0FBSyxDQUFDLGlCQUFpQixDQUFDLEVBQUUsT0FBTyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FJaEU7Q0FDRjtBQUVELG1FQUFtRTtBQUNuRSx3QkFBc0Isb0JBQW9CLENBQ3hDLFNBQVMsRUFBRSxtQkFBbUIsRUFDOUIsV0FBVyxFQUFFLGlCQUFpQixHQUFHO0lBQUUsa0JBQWtCLEVBQUUsRUFBRSxDQUFBO0NBQUUsRUFDM0QsU0FBUyxHQUFFLGVBQXNDLEdBQ2hELE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0FHekIifQ==
28
+ }, telemetry: TelemetryClient | undefined, initialHeader: BlockHeader): Promise<NoopL1Archiver>;
29
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9vcF9sMV9hcmNoaXZlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Rlc3Qvbm9vcF9sMV9hcmNoaXZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFJQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFFN0QsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBSXBELE9BQU8sS0FBSyxFQUFtQixTQUFTLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUN0RSxPQUFPLEtBQUssRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQ3JFLE9BQU8sS0FBSyxFQUFFLFdBQVcsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ3BELE9BQU8sRUFBRSxLQUFLLGVBQWUsRUFBbUMsTUFBTSx5QkFBeUIsQ0FBQztBQUtoRyxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDMUMsT0FBTyxFQUFFLHVCQUF1QixFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFFeEUsT0FBTyxLQUFLLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNsRSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUF5QnhEOzs7O0dBSUc7QUFDSCxxQkFBYSxjQUFlLFNBQVEsUUFBUTtJQUMxQyxZQUNFLFVBQVUsRUFBRSxrQkFBa0IsRUFDOUIsV0FBVyxFQUFFLGlCQUFpQixHQUFHO1FBQUUsa0JBQWtCLEVBQUUsRUFBRSxDQUFBO0tBQUUsRUFDM0QsZUFBZSxFQUFFLHVCQUF1QixFQUN4QyxhQUFhLEVBQUUsV0FBVyxFQUMxQixnQkFBZ0IsRUFBRSxTQUFTLEVBQzNCLFdBQVcsRUFBRSxXQUFXLEVBaUR6QjtJQUVELG1EQUFtRDtJQUNuQyxLQUFLLENBQUMsaUJBQWlCLENBQUMsRUFBRSxPQUFPLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUloRTtJQUVELDZFQUE2RTtJQUM3RCxxQkFBcUIsSUFBSSxPQUFPLENBQUMsVUFBVSxHQUFHLFNBQVMsQ0FBQyxDQUV2RTtDQUNGO0FBRUQsbUVBQW1FO0FBQ25FLHdCQUFzQixvQkFBb0IsQ0FDeEMsVUFBVSxFQUFFLGtCQUFrQixFQUM5QixXQUFXLEVBQUUsaUJBQWlCLEdBQUc7SUFBRSxrQkFBa0IsRUFBRSxFQUFFLENBQUE7Q0FBRSxFQUMzRCxTQUFTLDZCQUF3QyxFQUNqRCxhQUFhLEVBQUUsV0FBVyxHQUN6QixPQUFPLENBQUMsY0FBYyxDQUFDLENBU3pCIn0=
@@ -1 +1 @@
1
- {"version":3,"file":"noop_l1_archiver.d.ts","sourceRoot":"","sources":["../../src/test/noop_l1_archiver.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AAIpD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,KAAK,eAAe,EAAmC,MAAM,yBAAyB,CAAC;AAKhG,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAExE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAyBzE;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,QAAQ;IAC1C,YACE,SAAS,EAAE,mBAAmB,EAC9B,WAAW,EAAE,iBAAiB,GAAG;QAAE,kBAAkB,EAAE,EAAE,CAAA;KAAE,EAC3D,eAAe,EAAE,uBAAuB,EAuCzC;IAED,mDAAmD;IACnC,KAAK,CAAC,iBAAiB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhE;CACF;AAED,mEAAmE;AACnE,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,mBAAmB,EAC9B,WAAW,EAAE,iBAAiB,GAAG;IAAE,kBAAkB,EAAE,EAAE,CAAA;CAAE,EAC3D,SAAS,GAAE,eAAsC,GAChD,OAAO,CAAC,cAAc,CAAC,CAGzB"}
1
+ {"version":3,"file":"noop_l1_archiver.d.ts","sourceRoot":"","sources":["../../src/test/noop_l1_archiver.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAE7D,OAAO,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AAIpD,OAAO,KAAK,EAAmB,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,KAAK,eAAe,EAAmC,MAAM,yBAAyB,CAAC;AAKhG,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAExE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAyBxD;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,QAAQ;IAC1C,YACE,UAAU,EAAE,kBAAkB,EAC9B,WAAW,EAAE,iBAAiB,GAAG;QAAE,kBAAkB,EAAE,EAAE,CAAA;KAAE,EAC3D,eAAe,EAAE,uBAAuB,EACxC,aAAa,EAAE,WAAW,EAC1B,gBAAgB,EAAE,SAAS,EAC3B,WAAW,EAAE,WAAW,EAiDzB;IAED,mDAAmD;IACnC,KAAK,CAAC,iBAAiB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhE;IAED,6EAA6E;IAC7D,qBAAqB,IAAI,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAEvE;CACF;AAED,mEAAmE;AACnE,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,kBAAkB,EAC9B,WAAW,EAAE,iBAAiB,GAAG;IAAE,kBAAkB,EAAE,EAAE,CAAA;CAAE,EAC3D,SAAS,6BAAwC,EACjD,aAAa,EAAE,WAAW,GACzB,OAAO,CAAC,cAAc,CAAC,CASzB"}