@aztec/archiver 0.0.0-test.1 → 0.0.1-commit.5476d83

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 (110) hide show
  1. package/README.md +27 -6
  2. package/dest/archiver/archiver.d.ts +147 -57
  3. package/dest/archiver/archiver.d.ts.map +1 -1
  4. package/dest/archiver/archiver.js +841 -333
  5. package/dest/archiver/archiver_store.d.ts +85 -50
  6. package/dest/archiver/archiver_store.d.ts.map +1 -1
  7. package/dest/archiver/archiver_store_test_suite.d.ts +1 -1
  8. package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
  9. package/dest/archiver/archiver_store_test_suite.js +708 -213
  10. package/dest/archiver/config.d.ts +5 -21
  11. package/dest/archiver/config.d.ts.map +1 -1
  12. package/dest/archiver/config.js +21 -12
  13. package/dest/archiver/data_retrieval.d.ts +32 -27
  14. package/dest/archiver/data_retrieval.d.ts.map +1 -1
  15. package/dest/archiver/data_retrieval.js +197 -94
  16. package/dest/archiver/errors.d.ts +9 -1
  17. package/dest/archiver/errors.d.ts.map +1 -1
  18. package/dest/archiver/errors.js +12 -0
  19. package/dest/archiver/index.d.ts +3 -4
  20. package/dest/archiver/index.d.ts.map +1 -1
  21. package/dest/archiver/index.js +1 -2
  22. package/dest/archiver/instrumentation.d.ts +12 -6
  23. package/dest/archiver/instrumentation.d.ts.map +1 -1
  24. package/dest/archiver/instrumentation.js +58 -17
  25. package/dest/archiver/kv_archiver_store/block_store.d.ts +48 -11
  26. package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
  27. package/dest/archiver/kv_archiver_store/block_store.js +216 -63
  28. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +3 -3
  29. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
  30. package/dest/archiver/kv_archiver_store/contract_class_store.js +12 -18
  31. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +11 -8
  32. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
  33. package/dest/archiver/kv_archiver_store/contract_instance_store.js +30 -16
  34. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +50 -35
  35. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
  36. package/dest/archiver/kv_archiver_store/kv_archiver_store.js +88 -46
  37. package/dest/archiver/kv_archiver_store/log_store.d.ts +2 -2
  38. package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
  39. package/dest/archiver/kv_archiver_store/log_store.js +18 -46
  40. package/dest/archiver/kv_archiver_store/message_store.d.ts +23 -17
  41. package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
  42. package/dest/archiver/kv_archiver_store/message_store.js +150 -48
  43. package/dest/archiver/structs/data_retrieval.d.ts +1 -1
  44. package/dest/archiver/structs/inbox_message.d.ts +15 -0
  45. package/dest/archiver/structs/inbox_message.d.ts.map +1 -0
  46. package/dest/archiver/structs/inbox_message.js +38 -0
  47. package/dest/archiver/structs/published.d.ts +3 -11
  48. package/dest/archiver/structs/published.d.ts.map +1 -1
  49. package/dest/archiver/structs/published.js +1 -1
  50. package/dest/archiver/validation.d.ts +17 -0
  51. package/dest/archiver/validation.d.ts.map +1 -0
  52. package/dest/archiver/validation.js +98 -0
  53. package/dest/factory.d.ts +8 -13
  54. package/dest/factory.d.ts.map +1 -1
  55. package/dest/factory.js +18 -49
  56. package/dest/index.d.ts +2 -2
  57. package/dest/index.d.ts.map +1 -1
  58. package/dest/index.js +1 -1
  59. package/dest/rpc/index.d.ts +2 -3
  60. package/dest/rpc/index.d.ts.map +1 -1
  61. package/dest/rpc/index.js +1 -4
  62. package/dest/test/index.d.ts +1 -1
  63. package/dest/test/mock_archiver.d.ts +2 -2
  64. package/dest/test/mock_archiver.d.ts.map +1 -1
  65. package/dest/test/mock_l1_to_l2_message_source.d.ts +5 -3
  66. package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
  67. package/dest/test/mock_l1_to_l2_message_source.js +14 -1
  68. package/dest/test/mock_l2_block_source.d.ts +38 -10
  69. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  70. package/dest/test/mock_l2_block_source.js +119 -8
  71. package/dest/test/mock_structs.d.ts +9 -0
  72. package/dest/test/mock_structs.d.ts.map +1 -0
  73. package/dest/test/mock_structs.js +37 -0
  74. package/package.json +28 -30
  75. package/src/archiver/archiver.ts +1087 -410
  76. package/src/archiver/archiver_store.ts +97 -55
  77. package/src/archiver/archiver_store_test_suite.ts +664 -210
  78. package/src/archiver/config.ts +28 -41
  79. package/src/archiver/data_retrieval.ts +279 -125
  80. package/src/archiver/errors.ts +21 -0
  81. package/src/archiver/index.ts +2 -3
  82. package/src/archiver/instrumentation.ts +77 -22
  83. package/src/archiver/kv_archiver_store/block_store.ts +270 -72
  84. package/src/archiver/kv_archiver_store/contract_class_store.ts +13 -23
  85. package/src/archiver/kv_archiver_store/contract_instance_store.ts +35 -27
  86. package/src/archiver/kv_archiver_store/kv_archiver_store.ts +127 -63
  87. package/src/archiver/kv_archiver_store/log_store.ts +24 -62
  88. package/src/archiver/kv_archiver_store/message_store.ts +209 -53
  89. package/src/archiver/structs/inbox_message.ts +41 -0
  90. package/src/archiver/structs/published.ts +2 -11
  91. package/src/archiver/validation.ts +124 -0
  92. package/src/factory.ts +24 -66
  93. package/src/index.ts +1 -1
  94. package/src/rpc/index.ts +1 -5
  95. package/src/test/mock_archiver.ts +1 -1
  96. package/src/test/mock_l1_to_l2_message_source.ts +14 -3
  97. package/src/test/mock_l2_block_source.ts +158 -13
  98. package/src/test/mock_structs.ts +49 -0
  99. package/dest/archiver/kv_archiver_store/nullifier_store.d.ts +0 -12
  100. package/dest/archiver/kv_archiver_store/nullifier_store.d.ts.map +0 -1
  101. package/dest/archiver/kv_archiver_store/nullifier_store.js +0 -73
  102. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +0 -23
  103. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts.map +0 -1
  104. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.js +0 -49
  105. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +0 -175
  106. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +0 -1
  107. package/dest/archiver/memory_archiver_store/memory_archiver_store.js +0 -636
  108. package/src/archiver/kv_archiver_store/nullifier_store.ts +0 -97
  109. package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +0 -61
  110. package/src/archiver/memory_archiver_store/memory_archiver_store.ts +0 -801
@@ -1,22 +1,44 @@
1
1
  import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
2
+ import { Fr } from '@aztec/foundation/fields';
2
3
  import { toArray } from '@aztec/foundation/iterable';
3
4
  import { createLogger } from '@aztec/foundation/log';
5
+ import { BufferReader } from '@aztec/foundation/serialize';
6
+ import { bufferToHex } from '@aztec/foundation/string';
4
7
  import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncSingleton, Range } from '@aztec/kv-store';
5
8
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
6
- import { Body, type InBlock, L2Block, L2BlockHash } from '@aztec/stdlib/block';
9
+ import {
10
+ Body,
11
+ CommitteeAttestation,
12
+ L2Block,
13
+ L2BlockHash,
14
+ PublishedL2Block,
15
+ type ValidateBlockResult,
16
+ } from '@aztec/stdlib/block';
17
+ import { L2BlockHeader, deserializeValidateBlockResult, serializeValidateBlockResult } from '@aztec/stdlib/block';
7
18
  import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
8
- import { BlockHeader, TxEffect, TxHash, TxReceipt } from '@aztec/stdlib/tx';
19
+ import {
20
+ BlockHeader,
21
+ type IndexedTxEffect,
22
+ TxEffect,
23
+ TxHash,
24
+ TxReceipt,
25
+ deserializeIndexedTxEffect,
26
+ serializeIndexedTxEffect,
27
+ } from '@aztec/stdlib/tx';
9
28
 
10
- import type { L1Published, L1PublishedData } from '../structs/published.js';
29
+ import { BlockNumberNotSequentialError, InitialBlockNumberNotSequentialError } from '../errors.js';
30
+ import type { L1PublishedData } from '../structs/published.js';
11
31
 
12
- export { type TxEffect, type TxHash, TxReceipt } from '@aztec/stdlib/tx';
32
+ export { TxReceipt, type TxEffect, type TxHash } from '@aztec/stdlib/tx';
13
33
 
14
34
  type BlockIndexValue = [blockNumber: number, index: number];
15
35
 
16
36
  type BlockStorage = {
17
37
  header: Buffer;
38
+ blockHash: Buffer;
18
39
  archive: Buffer;
19
40
  l1: L1PublishedData;
41
+ attestations: Buffer[];
20
42
  };
21
43
 
22
44
  /**
@@ -26,8 +48,11 @@ export class BlockStore {
26
48
  /** Map block number to block data */
27
49
  #blocks: AztecAsyncMap<number, BlockStorage>;
28
50
 
29
- /** Map block hash to block body */
30
- #blockBodies: AztecAsyncMap<string, Buffer>;
51
+ /** Map block hash to list of tx hashes */
52
+ #blockTxs: AztecAsyncMap<string, Buffer>;
53
+
54
+ /** Tx hash to serialized IndexedTxEffect */
55
+ #txEffects: AztecAsyncMap<string, Buffer>;
31
56
 
32
57
  /** Stores L1 block number in which the last processed L2 block was included */
33
58
  #lastSynchedL1Block: AztecAsyncSingleton<bigint>;
@@ -35,25 +60,30 @@ export class BlockStore {
35
60
  /** Stores l2 block number of the last proven block */
36
61
  #lastProvenL2Block: AztecAsyncSingleton<number>;
37
62
 
38
- /** Stores l2 epoch number of the last proven epoch */
39
- #lastProvenL2Epoch: AztecAsyncSingleton<number>;
40
-
41
- /** Index mapping transaction hash (as a string) to its location in a block */
42
- #txIndex: AztecAsyncMap<string, BlockIndexValue>;
63
+ /** Stores the pending chain validation status */
64
+ #pendingChainValidationStatus: AztecAsyncSingleton<Buffer>;
43
65
 
44
66
  /** Index mapping a contract's address (as a string) to its location in a block */
45
67
  #contractIndex: AztecAsyncMap<string, BlockIndexValue>;
46
68
 
69
+ /** Index mapping block hash to block number */
70
+ #blockHashIndex: AztecAsyncMap<string, number>;
71
+
72
+ /** Index mapping block archive to block number */
73
+ #blockArchiveIndex: AztecAsyncMap<string, number>;
74
+
47
75
  #log = createLogger('archiver:block_store');
48
76
 
49
77
  constructor(private db: AztecAsyncKVStore) {
50
78
  this.#blocks = db.openMap('archiver_blocks');
51
- this.#blockBodies = db.openMap('archiver_block_bodies');
52
- this.#txIndex = db.openMap('archiver_tx_index');
79
+ this.#blockTxs = db.openMap('archiver_block_txs');
80
+ this.#txEffects = db.openMap('archiver_tx_effects');
53
81
  this.#contractIndex = db.openMap('archiver_contract_index');
82
+ this.#blockHashIndex = db.openMap('archiver_block_hash_index');
83
+ this.#blockArchiveIndex = db.openMap('archiver_block_archive_index');
54
84
  this.#lastSynchedL1Block = db.openSingleton('archiver_last_synched_l1_block');
55
85
  this.#lastProvenL2Block = db.openSingleton('archiver_last_proven_l2_block');
56
- this.#lastProvenL2Epoch = db.openSingleton('archiver_last_proven_l2_epoch');
86
+ this.#pendingChainValidationStatus = db.openSingleton('archiver_pending_chain_validation_status');
57
87
  }
58
88
 
59
89
  /**
@@ -61,25 +91,59 @@ export class BlockStore {
61
91
  * @param blocks - The L2 blocks to be added to the store.
62
92
  * @returns True if the operation is successful.
63
93
  */
64
- async addBlocks(blocks: L1Published<L2Block>[]): Promise<boolean> {
94
+ async addBlocks(blocks: PublishedL2Block[], opts: { force?: boolean } = {}): Promise<boolean> {
65
95
  if (blocks.length === 0) {
66
96
  return true;
67
97
  }
68
98
 
69
99
  return await this.db.transactionAsync(async () => {
100
+ // Check that the block immediately before the first block to be added is present in the store.
101
+ const firstBlockNumber = blocks[0].block.number;
102
+ const [previousBlockNumber] = await toArray(
103
+ this.#blocks.keysAsync({ reverse: true, limit: 1, end: firstBlockNumber - 1 }),
104
+ );
105
+ const hasPreviousBlock =
106
+ firstBlockNumber === INITIAL_L2_BLOCK_NUM ||
107
+ (previousBlockNumber !== undefined && previousBlockNumber === firstBlockNumber - 1);
108
+ if (!opts.force && !hasPreviousBlock) {
109
+ throw new InitialBlockNumberNotSequentialError(firstBlockNumber, previousBlockNumber);
110
+ }
111
+
112
+ // Iterate over blocks array and insert them, checking that the block numbers are sequential.
113
+ let previousBlock: PublishedL2Block | undefined = undefined;
70
114
  for (const block of blocks) {
71
- await this.#blocks.set(block.data.number, {
72
- header: block.data.header.toBuffer(),
73
- archive: block.data.archive.toBuffer(),
115
+ if (!opts.force && previousBlock && previousBlock.block.number + 1 !== block.block.number) {
116
+ throw new BlockNumberNotSequentialError(block.block.number, previousBlock.block.number);
117
+ }
118
+ previousBlock = block;
119
+ const blockHash = L2BlockHash.fromField(await block.block.hash());
120
+
121
+ await this.#blocks.set(block.block.number, {
122
+ header: block.block.header.toBuffer(),
123
+ blockHash: blockHash.toBuffer(),
124
+ archive: block.block.archive.toBuffer(),
74
125
  l1: block.l1,
126
+ attestations: block.attestations.map(attestation => attestation.toBuffer()),
75
127
  });
76
128
 
77
- for (let i = 0; i < block.data.body.txEffects.length; i++) {
78
- const txEffect = block.data.body.txEffects[i];
79
- await this.#txIndex.set(txEffect.txHash.toString(), [block.data.number, i]);
129
+ for (let i = 0; i < block.block.body.txEffects.length; i++) {
130
+ const txEffect: IndexedTxEffect = {
131
+ data: block.block.body.txEffects[i],
132
+ l2BlockNumber: block.block.number,
133
+ l2BlockHash: blockHash,
134
+ txIndexInBlock: i,
135
+ };
136
+ await this.#txEffects.set(txEffect.data.txHash.toString(), serializeIndexedTxEffect(txEffect));
80
137
  }
81
138
 
82
- await this.#blockBodies.set((await block.data.hash()).toString(), block.data.body.toBuffer());
139
+ await this.#blockTxs.set(
140
+ blockHash.toString(),
141
+ Buffer.concat(block.block.body.txEffects.map(tx => tx.txHash.toBuffer())),
142
+ );
143
+
144
+ // Update indices for block hash and archive
145
+ await this.#blockHashIndex.set(blockHash.toString(), block.block.number);
146
+ await this.#blockArchiveIndex.set(block.block.archive.root.toString(), block.block.number);
83
147
  }
84
148
 
85
149
  await this.#lastSynchedL1Block.set(blocks[blocks.length - 1].l1.blockNumber);
@@ -101,6 +165,11 @@ export class BlockStore {
101
165
  throw new Error(`Can only unwind blocks from the tip (requested ${from} but current tip is ${last})`);
102
166
  }
103
167
 
168
+ const proven = await this.getProvenL2BlockNumber();
169
+ if (from - blocksToUnwind < proven) {
170
+ await this.setProvenL2BlockNumber(from - blocksToUnwind);
171
+ }
172
+
104
173
  for (let i = 0; i < blocksToUnwind; i++) {
105
174
  const blockNumber = from - i;
106
175
  const block = await this.getBlock(blockNumber);
@@ -109,10 +178,15 @@ export class BlockStore {
109
178
  this.#log.warn(`Cannot remove block ${blockNumber} from the store since we don't have it`);
110
179
  continue;
111
180
  }
112
- await this.#blocks.delete(block.data.number);
113
- await Promise.all(block.data.body.txEffects.map(tx => this.#txIndex.delete(tx.txHash.toString())));
114
- const blockHash = (await block.data.hash()).toString();
115
- await this.#blockBodies.delete(blockHash);
181
+ await this.#blocks.delete(block.block.number);
182
+ await Promise.all(block.block.body.txEffects.map(tx => this.#txEffects.delete(tx.txHash.toString())));
183
+ const blockHash = (await block.block.hash()).toString();
184
+ await this.#blockTxs.delete(blockHash);
185
+
186
+ // Clean up indices
187
+ await this.#blockHashIndex.delete(blockHash);
188
+ await this.#blockArchiveIndex.delete(block.block.archive.root.toString());
189
+
116
190
  this.#log.debug(`Unwound block ${blockNumber} ${blockHash}`);
117
191
  }
118
192
 
@@ -126,10 +200,12 @@ export class BlockStore {
126
200
  * @param limit - The number of blocks to return.
127
201
  * @returns The requested L2 blocks
128
202
  */
129
- async *getBlocks(start: number, limit: number): AsyncIterableIterator<L1Published<L2Block>> {
130
- for await (const blockStorage of this.#blocks.valuesAsync(this.#computeBlockRange(start, limit))) {
131
- const block = await this.getBlockFromBlockStorage(blockStorage);
132
- yield block;
203
+ async *getBlocks(start: number, limit: number): AsyncIterableIterator<PublishedL2Block> {
204
+ for await (const [blockNumber, blockStorage] of this.getBlockStorages(start, limit)) {
205
+ const block = await this.getBlockFromBlockStorage(blockNumber, blockStorage);
206
+ if (block) {
207
+ yield block;
208
+ }
133
209
  }
134
210
  }
135
211
 
@@ -138,13 +214,72 @@ export class BlockStore {
138
214
  * @param blockNumber - The number of the block to return.
139
215
  * @returns The requested L2 block.
140
216
  */
141
- async getBlock(blockNumber: number): Promise<L1Published<L2Block> | undefined> {
217
+ async getBlock(blockNumber: number): Promise<PublishedL2Block | undefined> {
142
218
  const blockStorage = await this.#blocks.getAsync(blockNumber);
143
219
  if (!blockStorage || !blockStorage.header) {
144
220
  return Promise.resolve(undefined);
145
221
  }
222
+ return this.getBlockFromBlockStorage(blockNumber, blockStorage);
223
+ }
224
+
225
+ /**
226
+ * Gets an L2 block by its hash.
227
+ * @param blockHash - The hash of the block to return.
228
+ * @returns The requested L2 block.
229
+ */
230
+ async getBlockByHash(blockHash: L2BlockHash): Promise<PublishedL2Block | undefined> {
231
+ const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
232
+ if (blockNumber === undefined) {
233
+ return undefined;
234
+ }
235
+ return this.getBlock(blockNumber);
236
+ }
237
+
238
+ /**
239
+ * Gets an L2 block by its archive root.
240
+ * @param archive - The archive root of the block to return.
241
+ * @returns The requested L2 block.
242
+ */
243
+ async getBlockByArchive(archive: Fr): Promise<PublishedL2Block | undefined> {
244
+ const blockNumber = await this.#blockArchiveIndex.getAsync(archive.toString());
245
+ if (blockNumber === undefined) {
246
+ return undefined;
247
+ }
248
+ return this.getBlock(blockNumber);
249
+ }
250
+
251
+ /**
252
+ * Gets a block header by its hash.
253
+ * @param blockHash - The hash of the block to return.
254
+ * @returns The requested block header.
255
+ */
256
+ async getBlockHeaderByHash(blockHash: L2BlockHash): Promise<BlockHeader | undefined> {
257
+ const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
258
+ if (blockNumber === undefined) {
259
+ return undefined;
260
+ }
261
+ const blockStorage = await this.#blocks.getAsync(blockNumber);
262
+ if (!blockStorage || !blockStorage.header) {
263
+ return undefined;
264
+ }
265
+ return L2BlockHeader.fromBuffer(blockStorage.header).toBlockHeader();
266
+ }
146
267
 
147
- return this.getBlockFromBlockStorage(blockStorage);
268
+ /**
269
+ * Gets a block header by its archive root.
270
+ * @param archive - The archive root of the block to return.
271
+ * @returns The requested block header.
272
+ */
273
+ async getBlockHeaderByArchive(archive: Fr): Promise<BlockHeader | undefined> {
274
+ const blockNumber = await this.#blockArchiveIndex.getAsync(archive.toString());
275
+ if (blockNumber === undefined) {
276
+ return undefined;
277
+ }
278
+ const blockStorage = await this.#blocks.getAsync(blockNumber);
279
+ if (!blockStorage || !blockStorage.header) {
280
+ return undefined;
281
+ }
282
+ return L2BlockHeader.fromBuffer(blockStorage.header).toBlockHeader();
148
283
  }
149
284
 
150
285
  /**
@@ -154,48 +289,80 @@ export class BlockStore {
154
289
  * @returns The requested L2 block headers
155
290
  */
156
291
  async *getBlockHeaders(start: number, limit: number): AsyncIterableIterator<BlockHeader> {
157
- for await (const blockStorage of this.#blocks.valuesAsync(this.#computeBlockRange(start, limit))) {
158
- yield BlockHeader.fromBuffer(blockStorage.header);
292
+ for await (const [blockNumber, blockStorage] of this.getBlockStorages(start, limit)) {
293
+ const header = L2BlockHeader.fromBuffer(blockStorage.header).toBlockHeader();
294
+ if (header.getBlockNumber() !== blockNumber) {
295
+ throw new Error(
296
+ `Block number mismatch when retrieving block header from archive (expected ${blockNumber} but got ${header.getBlockNumber()})`,
297
+ );
298
+ }
299
+ yield header;
159
300
  }
160
301
  }
161
302
 
162
- private async getBlockFromBlockStorage(blockStorage: BlockStorage) {
163
- const header = BlockHeader.fromBuffer(blockStorage.header);
303
+ private async *getBlockStorages(start: number, limit: number) {
304
+ let expectedBlockNumber = start;
305
+ for await (const [blockNumber, blockStorage] of this.#blocks.entriesAsync(this.#computeBlockRange(start, limit))) {
306
+ if (blockNumber !== expectedBlockNumber) {
307
+ throw new Error(
308
+ `Block number mismatch when iterating blocks from archive (expected ${expectedBlockNumber} but got ${blockNumber})`,
309
+ );
310
+ }
311
+ expectedBlockNumber++;
312
+ yield [blockNumber, blockStorage] as const;
313
+ }
314
+ }
315
+
316
+ private async getBlockFromBlockStorage(
317
+ blockNumber: number,
318
+ blockStorage: BlockStorage,
319
+ ): Promise<PublishedL2Block | undefined> {
320
+ const header = L2BlockHeader.fromBuffer(blockStorage.header);
164
321
  const archive = AppendOnlyTreeSnapshot.fromBuffer(blockStorage.archive);
165
- const blockHash = (await header.hash()).toString();
166
- const blockBodyBuffer = await this.#blockBodies.getAsync(blockHash);
167
- if (blockBodyBuffer === undefined) {
322
+ const blockHash = blockStorage.blockHash;
323
+ const blockHashString = bufferToHex(blockHash);
324
+ const blockTxsBuffer = await this.#blockTxs.getAsync(blockHashString);
325
+ if (blockTxsBuffer === undefined) {
326
+ this.#log.warn(`Could not find body for block ${header.globalVariables.blockNumber} ${blockHash}`);
327
+ return undefined;
328
+ }
329
+
330
+ const txEffects: TxEffect[] = [];
331
+ const reader = BufferReader.asReader(blockTxsBuffer);
332
+ while (!reader.isEmpty()) {
333
+ const txHash = reader.readObject(TxHash);
334
+ const txEffect = await this.#txEffects.getAsync(txHash.toString());
335
+ if (txEffect === undefined) {
336
+ this.#log.warn(`Could not find tx effect for tx ${txHash} in block ${blockNumber}`);
337
+ return undefined;
338
+ }
339
+ txEffects.push(deserializeIndexedTxEffect(txEffect).data);
340
+ }
341
+ const body = new Body(txEffects);
342
+ const block = new L2Block(archive, header, body, Fr.fromBuffer(blockHash));
343
+
344
+ if (block.number !== blockNumber) {
168
345
  throw new Error(
169
- `Could not retrieve body for block ${header.globalVariables.blockNumber.toNumber()} ${blockHash}`,
346
+ `Block number mismatch when retrieving block from archive (expected ${blockNumber} but got ${
347
+ block.number
348
+ } with hash ${blockHashString})`,
170
349
  );
171
350
  }
172
- const body = Body.fromBuffer(blockBodyBuffer);
173
-
174
- const l2Block = new L2Block(archive, header, body);
175
- return { data: l2Block, l1: blockStorage.l1 };
351
+ const attestations = blockStorage.attestations.map(CommitteeAttestation.fromBuffer);
352
+ return PublishedL2Block.fromFields({ block, l1: blockStorage.l1, attestations });
176
353
  }
177
354
 
178
355
  /**
179
356
  * Gets a tx effect.
180
- * @param txHash - The txHash of the tx corresponding to the tx effect.
181
- * @returns The requested tx effect (or undefined if not found).
357
+ * @param txHash - The hash of the tx corresponding to the tx effect.
358
+ * @returns The requested tx effect with block info (or undefined if not found).
182
359
  */
183
- async getTxEffect(txHash: TxHash): Promise<InBlock<TxEffect> | undefined> {
184
- const [blockNumber, txIndex] = (await this.getTxLocation(txHash)) ?? [];
185
- if (typeof blockNumber !== 'number' || typeof txIndex !== 'number') {
360
+ async getTxEffect(txHash: TxHash): Promise<IndexedTxEffect | undefined> {
361
+ const buffer = await this.#txEffects.getAsync(txHash.toString());
362
+ if (!buffer) {
186
363
  return undefined;
187
364
  }
188
-
189
- const block = await this.getBlock(blockNumber);
190
- if (!block) {
191
- return undefined;
192
- }
193
-
194
- return {
195
- data: block.data.body.txEffects[txIndex],
196
- l2BlockNumber: block.data.number,
197
- l2BlockHash: (await block.data.hash()).toString(),
198
- };
365
+ return deserializeIndexedTxEffect(buffer);
199
366
  }
200
367
 
201
368
  /**
@@ -204,21 +371,18 @@ export class BlockStore {
204
371
  * @returns The requested tx receipt (or undefined if not found).
205
372
  */
206
373
  async getSettledTxReceipt(txHash: TxHash): Promise<TxReceipt | undefined> {
207
- const [blockNumber, txIndex] = (await this.getTxLocation(txHash)) ?? [];
208
- if (typeof blockNumber !== 'number' || typeof txIndex !== 'number') {
374
+ const txEffect = await this.getTxEffect(txHash);
375
+ if (!txEffect) {
209
376
  return undefined;
210
377
  }
211
378
 
212
- const block = (await this.getBlock(blockNumber))!;
213
- const tx = block.data.body.txEffects[txIndex];
214
-
215
379
  return new TxReceipt(
216
380
  txHash,
217
- TxReceipt.statusFromRevertCode(tx.revertCode),
381
+ TxReceipt.statusFromRevertCode(txEffect.data.revertCode),
218
382
  '',
219
- tx.transactionFee.toBigInt(),
220
- L2BlockHash.fromField(await block.data.hash()),
221
- block.data.number,
383
+ txEffect.data.transactionFee.toBigInt(),
384
+ txEffect.l2BlockHash,
385
+ txEffect.l2BlockNumber,
222
386
  );
223
387
  }
224
388
 
@@ -227,8 +391,13 @@ export class BlockStore {
227
391
  * @param txHash - The txHash of the tx.
228
392
  * @returns The block number and index of the tx.
229
393
  */
230
- getTxLocation(txHash: TxHash): Promise<[blockNumber: number, txIndex: number] | undefined> {
231
- return this.#txIndex.getAsync(txHash.toString());
394
+ public async getTxLocation(txHash: TxHash): Promise<[blockNumber: number, txIndex: number] | undefined> {
395
+ const txEffect = await this.#txEffects.getAsync(txHash.toString());
396
+ if (!txEffect) {
397
+ return undefined;
398
+ }
399
+ const { l2BlockNumber, txIndexInBlock } = deserializeIndexedTxEffect(txEffect);
400
+ return [l2BlockNumber, txIndexInBlock];
232
401
  }
233
402
 
234
403
  /**
@@ -262,7 +431,11 @@ export class BlockStore {
262
431
  }
263
432
 
264
433
  async getProvenL2BlockNumber(): Promise<number> {
265
- return (await this.#lastProvenL2Block.getAsync()) ?? 0;
434
+ const [latestBlockNumber, provenBlockNumber] = await Promise.all([
435
+ this.getSynchedL2BlockNumber(),
436
+ this.#lastProvenL2Block.getAsync(),
437
+ ]);
438
+ return (provenBlockNumber ?? 0) > latestBlockNumber ? latestBlockNumber : (provenBlockNumber ?? 0);
266
439
  }
267
440
 
268
441
  setProvenL2BlockNumber(blockNumber: number) {
@@ -280,4 +453,29 @@ export class BlockStore {
280
453
 
281
454
  return { start, limit };
282
455
  }
456
+
457
+ /**
458
+ * Gets the pending chain validation status.
459
+ * @returns The validation status or undefined if not set.
460
+ */
461
+ async getPendingChainValidationStatus(): Promise<ValidateBlockResult | undefined> {
462
+ const buffer = await this.#pendingChainValidationStatus.getAsync();
463
+ if (!buffer) {
464
+ return undefined;
465
+ }
466
+ return deserializeValidateBlockResult(buffer);
467
+ }
468
+
469
+ /**
470
+ * Sets the pending chain validation status.
471
+ * @param status - The validation status to store.
472
+ */
473
+ async setPendingChainValidationStatus(status: ValidateBlockResult | undefined): Promise<void> {
474
+ if (status) {
475
+ const buffer = serializeValidateBlockResult(status);
476
+ await this.#pendingChainValidationStatus.set(buffer);
477
+ } else {
478
+ await this.#pendingChainValidationStatus.delete();
479
+ }
480
+ }
283
481
  }
@@ -7,7 +7,7 @@ import type {
7
7
  ContractClassPublic,
8
8
  ContractClassPublicWithBlockNumber,
9
9
  ExecutablePrivateFunctionWithMembershipProof,
10
- UnconstrainedFunctionWithMembershipProof,
10
+ UtilityFunctionWithMembershipProof,
11
11
  } from '@aztec/stdlib/contract';
12
12
  import { Vector } from '@aztec/stdlib/types';
13
13
 
@@ -60,7 +60,7 @@ export class ContractClassStore {
60
60
  async addFunctions(
61
61
  contractClassId: Fr,
62
62
  newPrivateFunctions: ExecutablePrivateFunctionWithMembershipProof[],
63
- newUnconstrainedFunctions: UnconstrainedFunctionWithMembershipProof[],
63
+ newUtilityFunctions: UtilityFunctionWithMembershipProof[],
64
64
  ): Promise<boolean> {
65
65
  await this.db.transactionAsync(async () => {
66
66
  const existingClassBuffer = await this.#contractClasses.getAsync(contractClassId.toString());
@@ -69,7 +69,7 @@ export class ContractClassStore {
69
69
  }
70
70
 
71
71
  const existingClass = deserializeContractClassPublic(existingClassBuffer);
72
- const { privateFunctions: existingPrivateFns, unconstrainedFunctions: existingUnconstrainedFns } = existingClass;
72
+ const { privateFunctions: existingPrivateFns, utilityFunctions: existingUtilityFns } = existingClass;
73
73
 
74
74
  const updatedClass: Omit<ContractClassPublicWithBlockNumber, 'id'> = {
75
75
  ...existingClass,
@@ -77,11 +77,9 @@ export class ContractClassStore {
77
77
  ...existingPrivateFns,
78
78
  ...newPrivateFunctions.filter(newFn => !existingPrivateFns.some(f => f.selector.equals(newFn.selector))),
79
79
  ],
80
- unconstrainedFunctions: [
81
- ...existingUnconstrainedFns,
82
- ...newUnconstrainedFunctions.filter(
83
- newFn => !existingUnconstrainedFns.some(f => f.selector.equals(newFn.selector)),
84
- ),
80
+ utilityFunctions: [
81
+ ...existingUtilityFns,
82
+ ...newUtilityFunctions.filter(newFn => !existingUtilityFns.some(f => f.selector.equals(newFn.selector))),
85
83
  ],
86
84
  };
87
85
  await this.#contractClasses.set(contractClassId.toString(), serializeContractClassPublic(updatedClass));
@@ -96,12 +94,10 @@ function serializeContractClassPublic(contractClass: Omit<ContractClassPublicWit
96
94
  contractClass.l2BlockNumber,
97
95
  numToUInt8(contractClass.version),
98
96
  contractClass.artifactHash,
99
- contractClass.publicFunctions.length,
100
- contractClass.publicFunctions?.map(f => serializeToBuffer(f.selector, f.bytecode.length, f.bytecode)) ?? [],
101
97
  contractClass.privateFunctions.length,
102
98
  contractClass.privateFunctions.map(serializePrivateFunction),
103
- contractClass.unconstrainedFunctions.length,
104
- contractClass.unconstrainedFunctions.map(serializeUnconstrainedFunction),
99
+ contractClass.utilityFunctions.length,
100
+ contractClass.utilityFunctions.map(serializeUtilityFunction),
105
101
  contractClass.packedBytecode.length,
106
102
  contractClass.packedBytecode,
107
103
  contractClass.privateFunctionsRoot,
@@ -116,7 +112,7 @@ function serializePrivateFunction(fn: ExecutablePrivateFunctionWithMembershipPro
116
112
  fn.bytecode,
117
113
  fn.functionMetadataHash,
118
114
  fn.artifactMetadataHash,
119
- fn.unconstrainedFunctionsArtifactTreeRoot,
115
+ fn.utilityFunctionsTreeRoot,
120
116
  new Vector(fn.privateFunctionTreeSiblingPath),
121
117
  fn.privateFunctionTreeLeafIndex,
122
118
  new Vector(fn.artifactTreeSiblingPath),
@@ -124,7 +120,7 @@ function serializePrivateFunction(fn: ExecutablePrivateFunctionWithMembershipPro
124
120
  );
125
121
  }
126
122
 
127
- function serializeUnconstrainedFunction(fn: UnconstrainedFunctionWithMembershipProof): Buffer {
123
+ function serializeUtilityFunction(fn: UtilityFunctionWithMembershipProof): Buffer {
128
124
  return serializeToBuffer(
129
125
  fn.selector,
130
126
  fn.bytecode.length,
@@ -143,14 +139,8 @@ function deserializeContractClassPublic(buffer: Buffer): Omit<ContractClassPubli
143
139
  l2BlockNumber: reader.readNumber(),
144
140
  version: reader.readUInt8() as 1,
145
141
  artifactHash: reader.readObject(Fr),
146
- publicFunctions: reader.readVector({
147
- fromBuffer: reader => ({
148
- selector: reader.readObject(FunctionSelector),
149
- bytecode: reader.readBuffer(),
150
- }),
151
- }),
152
142
  privateFunctions: reader.readVector({ fromBuffer: deserializePrivateFunction }),
153
- unconstrainedFunctions: reader.readVector({ fromBuffer: deserializeUnconstrainedFunction }),
143
+ utilityFunctions: reader.readVector({ fromBuffer: deserializeUtilityFunction }),
154
144
  packedBytecode: reader.readBuffer(),
155
145
  privateFunctionsRoot: reader.readObject(Fr),
156
146
  };
@@ -164,7 +154,7 @@ function deserializePrivateFunction(buffer: Buffer | BufferReader): ExecutablePr
164
154
  bytecode: reader.readBuffer(),
165
155
  functionMetadataHash: reader.readObject(Fr),
166
156
  artifactMetadataHash: reader.readObject(Fr),
167
- unconstrainedFunctionsArtifactTreeRoot: reader.readObject(Fr),
157
+ utilityFunctionsTreeRoot: reader.readObject(Fr),
168
158
  privateFunctionTreeSiblingPath: reader.readVector(Fr),
169
159
  privateFunctionTreeLeafIndex: reader.readNumber(),
170
160
  artifactTreeSiblingPath: reader.readVector(Fr),
@@ -172,7 +162,7 @@ function deserializePrivateFunction(buffer: Buffer | BufferReader): ExecutablePr
172
162
  };
173
163
  }
174
164
 
175
- function deserializeUnconstrainedFunction(buffer: Buffer | BufferReader): UnconstrainedFunctionWithMembershipProof {
165
+ function deserializeUtilityFunction(buffer: Buffer | BufferReader): UtilityFunctionWithMembershipProof {
176
166
  const reader = BufferReader.asReader(buffer);
177
167
  return {
178
168
  selector: reader.readObject(FunctionSelector),