@aztec/archiver 0.0.0-test.0 → 0.0.1-commit.24de95ac

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 (102) hide show
  1. package/README.md +27 -6
  2. package/dest/archiver/archiver.d.ts +126 -46
  3. package/dest/archiver/archiver.d.ts.map +1 -1
  4. package/dest/archiver/archiver.js +683 -261
  5. package/dest/archiver/archiver_store.d.ts +84 -49
  6. package/dest/archiver/archiver_store.d.ts.map +1 -1
  7. package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
  8. package/dest/archiver/archiver_store_test_suite.js +707 -213
  9. package/dest/archiver/config.d.ts +4 -20
  10. package/dest/archiver/config.d.ts.map +1 -1
  11. package/dest/archiver/config.js +16 -12
  12. package/dest/archiver/data_retrieval.d.ts +25 -20
  13. package/dest/archiver/data_retrieval.d.ts.map +1 -1
  14. package/dest/archiver/data_retrieval.js +147 -68
  15. package/dest/archiver/errors.d.ts +8 -0
  16. package/dest/archiver/errors.d.ts.map +1 -1
  17. package/dest/archiver/errors.js +12 -0
  18. package/dest/archiver/index.d.ts +2 -3
  19. package/dest/archiver/index.d.ts.map +1 -1
  20. package/dest/archiver/index.js +1 -2
  21. package/dest/archiver/instrumentation.d.ts +9 -3
  22. package/dest/archiver/instrumentation.d.ts.map +1 -1
  23. package/dest/archiver/instrumentation.js +58 -17
  24. package/dest/archiver/kv_archiver_store/block_store.d.ts +47 -10
  25. package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
  26. package/dest/archiver/kv_archiver_store/block_store.js +216 -63
  27. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +2 -2
  28. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
  29. package/dest/archiver/kv_archiver_store/contract_class_store.js +12 -18
  30. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +10 -7
  31. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
  32. package/dest/archiver/kv_archiver_store/contract_instance_store.js +30 -16
  33. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +49 -34
  34. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
  35. package/dest/archiver/kv_archiver_store/kv_archiver_store.js +88 -46
  36. package/dest/archiver/kv_archiver_store/log_store.d.ts +1 -1
  37. package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
  38. package/dest/archiver/kv_archiver_store/log_store.js +18 -46
  39. package/dest/archiver/kv_archiver_store/message_store.d.ts +22 -16
  40. package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
  41. package/dest/archiver/kv_archiver_store/message_store.js +150 -48
  42. package/dest/archiver/structs/inbox_message.d.ts +15 -0
  43. package/dest/archiver/structs/inbox_message.d.ts.map +1 -0
  44. package/dest/archiver/structs/inbox_message.js +38 -0
  45. package/dest/archiver/structs/published.d.ts +1 -10
  46. package/dest/archiver/structs/published.d.ts.map +1 -1
  47. package/dest/archiver/structs/published.js +1 -1
  48. package/dest/archiver/validation.d.ts +11 -0
  49. package/dest/archiver/validation.d.ts.map +1 -0
  50. package/dest/archiver/validation.js +90 -0
  51. package/dest/factory.d.ts +7 -12
  52. package/dest/factory.d.ts.map +1 -1
  53. package/dest/factory.js +18 -49
  54. package/dest/rpc/index.d.ts +1 -2
  55. package/dest/rpc/index.d.ts.map +1 -1
  56. package/dest/rpc/index.js +1 -4
  57. package/dest/test/mock_archiver.d.ts +1 -1
  58. package/dest/test/mock_l1_to_l2_message_source.d.ts +4 -2
  59. package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
  60. package/dest/test/mock_l1_to_l2_message_source.js +14 -1
  61. package/dest/test/mock_l2_block_source.d.ts +32 -5
  62. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  63. package/dest/test/mock_l2_block_source.js +118 -7
  64. package/dest/test/mock_structs.d.ts +9 -0
  65. package/dest/test/mock_structs.d.ts.map +1 -0
  66. package/dest/test/mock_structs.js +37 -0
  67. package/package.json +25 -27
  68. package/src/archiver/archiver.ts +858 -317
  69. package/src/archiver/archiver_store.ts +97 -55
  70. package/src/archiver/archiver_store_test_suite.ts +663 -210
  71. package/src/archiver/config.ts +23 -41
  72. package/src/archiver/data_retrieval.ts +215 -92
  73. package/src/archiver/errors.ts +21 -0
  74. package/src/archiver/index.ts +2 -3
  75. package/src/archiver/instrumentation.ts +75 -20
  76. package/src/archiver/kv_archiver_store/block_store.ts +270 -72
  77. package/src/archiver/kv_archiver_store/contract_class_store.ts +13 -23
  78. package/src/archiver/kv_archiver_store/contract_instance_store.ts +35 -27
  79. package/src/archiver/kv_archiver_store/kv_archiver_store.ts +127 -63
  80. package/src/archiver/kv_archiver_store/log_store.ts +24 -62
  81. package/src/archiver/kv_archiver_store/message_store.ts +209 -53
  82. package/src/archiver/structs/inbox_message.ts +41 -0
  83. package/src/archiver/structs/published.ts +1 -11
  84. package/src/archiver/validation.ts +99 -0
  85. package/src/factory.ts +24 -66
  86. package/src/rpc/index.ts +1 -5
  87. package/src/test/mock_archiver.ts +1 -1
  88. package/src/test/mock_l1_to_l2_message_source.ts +14 -3
  89. package/src/test/mock_l2_block_source.ts +152 -8
  90. package/src/test/mock_structs.ts +49 -0
  91. package/dest/archiver/kv_archiver_store/nullifier_store.d.ts +0 -12
  92. package/dest/archiver/kv_archiver_store/nullifier_store.d.ts.map +0 -1
  93. package/dest/archiver/kv_archiver_store/nullifier_store.js +0 -73
  94. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +0 -23
  95. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts.map +0 -1
  96. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.js +0 -49
  97. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +0 -175
  98. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +0 -1
  99. package/dest/archiver/memory_archiver_store/memory_archiver_store.js +0 -636
  100. package/src/archiver/kv_archiver_store/nullifier_store.ts +0 -97
  101. package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +0 -61
  102. package/src/archiver/memory_archiver_store/memory_archiver_store.ts +0 -801
@@ -1,15 +1,22 @@
1
- import { INITIAL_L2_BLOCK_NUM, L1_TO_L2_MSG_SUBTREE_HEIGHT, MAX_NULLIFIERS_PER_TX, PRIVATE_LOG_SIZE_IN_FIELDS, PUBLIC_LOG_DATA_SIZE_IN_FIELDS } from '@aztec/constants';
1
+ import { INITIAL_L2_BLOCK_NUM, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, PRIVATE_LOG_SIZE_IN_FIELDS } from '@aztec/constants';
2
+ import { makeTuple } from '@aztec/foundation/array';
3
+ import { Buffer16, Buffer32 } from '@aztec/foundation/buffer';
2
4
  import { times, timesParallel } from '@aztec/foundation/collection';
3
5
  import { randomInt } from '@aztec/foundation/crypto';
4
6
  import { Fr } from '@aztec/foundation/fields';
7
+ import { toArray } from '@aztec/foundation/iterable';
8
+ import { sleep } from '@aztec/foundation/sleep';
5
9
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
6
- import { L2Block, wrapInBlock } from '@aztec/stdlib/block';
10
+ import { CommitteeAttestation, EthAddress, L2Block, L2BlockHash, PublishedL2Block, randomBlockInfo, wrapInBlock } from '@aztec/stdlib/block';
7
11
  import { SerializableContractInstance, computePublicBytecodeCommitment } from '@aztec/stdlib/contract';
8
12
  import { LogId, PrivateLog, PublicLog } from '@aztec/stdlib/logs';
9
13
  import { InboxLeaf } from '@aztec/stdlib/messaging';
10
- import { makeContractClassPublic, makeExecutablePrivateFunctionWithMembershipProof, makeUnconstrainedFunctionWithMembershipProof } from '@aztec/stdlib/testing';
14
+ import { makeContractClassPublic, makeExecutablePrivateFunctionWithMembershipProof, makeUtilityFunctionWithMembershipProof } from '@aztec/stdlib/testing';
11
15
  import '@aztec/stdlib/testing/jest';
12
16
  import { TxEffect, TxHash } from '@aztec/stdlib/tx';
17
+ import { makeInboxMessage, makeInboxMessages } from '../test/mock_structs.js';
18
+ import { BlockNumberNotSequentialError, InitialBlockNumberNotSequentialError } from './errors.js';
19
+ import { MessageStoreError } from './kv_archiver_store/message_store.js';
13
20
  /**
14
21
  * @param testName - The name of the test suite.
15
22
  * @param getStore - Returns an instance of a store that's already been initialized.
@@ -44,17 +51,29 @@ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
44
51
  ()=>blocks.slice(4, 6)
45
52
  ]
46
53
  ];
47
- const makeL1Published = (block, l1BlockNumber)=>({
48
- data: block,
54
+ const makeBlockHash = (blockNumber)=>`0x${blockNumber.toString(16).padStart(64, '0')}`;
55
+ const makePublished = (block, l1BlockNumber)=>PublishedL2Block.fromFields({
56
+ block: block,
49
57
  l1: {
50
58
  blockNumber: BigInt(l1BlockNumber),
51
- blockHash: `0x${l1BlockNumber}`,
59
+ blockHash: makeBlockHash(l1BlockNumber),
52
60
  timestamp: BigInt(l1BlockNumber * 1000)
53
- }
61
+ },
62
+ attestations: times(3, CommitteeAttestation.random)
54
63
  });
64
+ const expectBlocksEqual = (actual, expected)=>{
65
+ expect(actual.length).toEqual(expected.length);
66
+ for(let i = 0; i < expected.length; i++){
67
+ const expectedBlock = expected[i];
68
+ const actualBlock = actual[i];
69
+ expect(actualBlock.l1).toEqual(expectedBlock.l1);
70
+ expect(actualBlock.block.equals(expectedBlock.block)).toBe(true);
71
+ expect(actualBlock.attestations.every((a, i)=>a.equals(expectedBlock.attestations[i]))).toBe(true);
72
+ }
73
+ };
55
74
  beforeEach(async ()=>{
56
75
  store = await getStore();
57
- blocks = await timesParallel(10, async (i)=>makeL1Published(await L2Block.random(i + 1), i + 10));
76
+ blocks = await timesParallel(10, async (i)=>makePublished(await L2Block.random(i + 1), i + 10));
58
77
  });
59
78
  describe('addBlocks', ()=>{
60
79
  it('returns success when adding blocks', async ()=>{
@@ -64,25 +83,40 @@ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
64
83
  await store.addBlocks(blocks);
65
84
  await expect(store.addBlocks(blocks)).resolves.toBe(true);
66
85
  });
86
+ it('throws an error if the previous block does not exist in the store', async ()=>{
87
+ const block = makePublished(await L2Block.random(2), 2);
88
+ await expect(store.addBlocks([
89
+ block
90
+ ])).rejects.toThrow(InitialBlockNumberNotSequentialError);
91
+ await expect(store.getPublishedBlocks(1, 10)).resolves.toEqual([]);
92
+ });
93
+ it('throws an error if there is a gap in the blocks being added', async ()=>{
94
+ const blocks = [
95
+ makePublished(await L2Block.random(1), 1),
96
+ makePublished(await L2Block.random(3), 3)
97
+ ];
98
+ await expect(store.addBlocks(blocks)).rejects.toThrow(BlockNumberNotSequentialError);
99
+ await expect(store.getPublishedBlocks(1, 10)).resolves.toEqual([]);
100
+ });
67
101
  });
68
102
  describe('unwindBlocks', ()=>{
69
103
  it('unwinding blocks will remove blocks from the chain', async ()=>{
70
104
  await store.addBlocks(blocks);
71
105
  const blockNumber = await store.getSynchedL2BlockNumber();
72
- expect(await store.getBlocks(blockNumber, 1)).toEqual([
106
+ expectBlocksEqual(await store.getPublishedBlocks(blockNumber, 1), [
73
107
  blocks[blocks.length - 1]
74
108
  ]);
75
109
  await store.unwindBlocks(blockNumber, 1);
76
110
  expect(await store.getSynchedL2BlockNumber()).toBe(blockNumber - 1);
77
- expect(await store.getBlocks(blockNumber, 1)).toEqual([]);
111
+ expect(await store.getPublishedBlocks(blockNumber, 1)).toEqual([]);
78
112
  });
79
113
  it('can unwind multiple empty blocks', async ()=>{
80
- const emptyBlocks = await timesParallel(10, async (i)=>makeL1Published(await L2Block.random(i + 1, 0), i + 10));
114
+ const emptyBlocks = await timesParallel(10, async (i)=>makePublished(await L2Block.random(i + 1, 0), i + 10));
81
115
  await store.addBlocks(emptyBlocks);
82
116
  expect(await store.getSynchedL2BlockNumber()).toBe(10);
83
117
  await store.unwindBlocks(10, 3);
84
118
  expect(await store.getSynchedL2BlockNumber()).toBe(7);
85
- expect((await store.getBlocks(1, 10)).map((b)=>b.data.number)).toEqual([
119
+ expect((await store.getPublishedBlocks(1, 10)).map((b)=>b.block.number)).toEqual([
86
120
  1,
87
121
  2,
88
122
  3,
@@ -96,22 +130,129 @@ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
96
130
  await store.addBlocks(blocks);
97
131
  await expect(store.unwindBlocks(5, 1)).rejects.toThrow(/can only unwind blocks from the tip/i);
98
132
  });
133
+ it('unwound blocks and headers cannot be retrieved by hash or archive', async ()=>{
134
+ await store.addBlocks(blocks);
135
+ const lastBlock = blocks[blocks.length - 1];
136
+ const blockHash = await lastBlock.block.hash();
137
+ const archive = lastBlock.block.archive.root;
138
+ // Verify block and header exist before unwinding
139
+ expect(await store.getPublishedBlockByHash(blockHash)).toBeDefined();
140
+ expect(await store.getPublishedBlockByArchive(archive)).toBeDefined();
141
+ expect(await store.getBlockHeaderByHash(blockHash)).toBeDefined();
142
+ expect(await store.getBlockHeaderByArchive(archive)).toBeDefined();
143
+ // Unwind the block
144
+ await store.unwindBlocks(lastBlock.block.number, 1);
145
+ // Verify neither block nor header can be retrieved after unwinding
146
+ expect(await store.getPublishedBlockByHash(blockHash)).toBeUndefined();
147
+ expect(await store.getPublishedBlockByArchive(archive)).toBeUndefined();
148
+ expect(await store.getBlockHeaderByHash(blockHash)).toBeUndefined();
149
+ expect(await store.getBlockHeaderByArchive(archive)).toBeUndefined();
150
+ });
99
151
  });
100
152
  describe('getBlocks', ()=>{
101
153
  beforeEach(async ()=>{
102
154
  await store.addBlocks(blocks);
103
155
  });
104
156
  it.each(blockTests)('retrieves previously stored blocks', async (start, limit, getExpectedBlocks)=>{
105
- await expect(store.getBlocks(start, limit)).resolves.toEqual(getExpectedBlocks());
157
+ expectBlocksEqual(await store.getPublishedBlocks(start, limit), getExpectedBlocks());
106
158
  });
107
159
  it('returns an empty array if no blocks are found', async ()=>{
108
- await expect(store.getBlocks(12, 1)).resolves.toEqual([]);
160
+ await expect(store.getPublishedBlocks(12, 1)).resolves.toEqual([]);
109
161
  });
110
162
  it('throws an error if limit is invalid', async ()=>{
111
- await expect(store.getBlocks(1, 0)).rejects.toThrow('Invalid limit: 0');
163
+ await expect(store.getPublishedBlocks(1, 0)).rejects.toThrow('Invalid limit: 0');
112
164
  });
113
165
  it('throws an error if `from` it is out of range', async ()=>{
114
- await expect(store.getBlocks(INITIAL_L2_BLOCK_NUM - 100, 1)).rejects.toThrow('Invalid start: -99');
166
+ await expect(store.getPublishedBlocks(INITIAL_L2_BLOCK_NUM - 100, 1)).rejects.toThrow('Invalid start: -99');
167
+ });
168
+ it('throws an error if unexpected initial block number is found', async ()=>{
169
+ await store.addBlocks([
170
+ makePublished(await L2Block.random(21), 31)
171
+ ], {
172
+ force: true
173
+ });
174
+ await expect(store.getPublishedBlocks(20, 1)).rejects.toThrow(`mismatch`);
175
+ });
176
+ it('throws an error if a gap is found', async ()=>{
177
+ await store.addBlocks([
178
+ makePublished(await L2Block.random(20), 30),
179
+ makePublished(await L2Block.random(22), 32)
180
+ ], {
181
+ force: true
182
+ });
183
+ await expect(store.getPublishedBlocks(20, 2)).rejects.toThrow(`mismatch`);
184
+ });
185
+ });
186
+ describe('getPublishedBlockByHash', ()=>{
187
+ beforeEach(async ()=>{
188
+ await store.addBlocks(blocks);
189
+ });
190
+ it('retrieves a block by its hash', async ()=>{
191
+ const expectedBlock = blocks[5];
192
+ const blockHash = await expectedBlock.block.hash();
193
+ const retrievedBlock = await store.getPublishedBlockByHash(blockHash);
194
+ expect(retrievedBlock).toBeDefined();
195
+ expectBlocksEqual([
196
+ retrievedBlock
197
+ ], [
198
+ expectedBlock
199
+ ]);
200
+ });
201
+ it('returns undefined for non-existent block hash', async ()=>{
202
+ const nonExistentHash = Fr.random();
203
+ await expect(store.getPublishedBlockByHash(nonExistentHash)).resolves.toBeUndefined();
204
+ });
205
+ });
206
+ describe('getPublishedBlockByArchive', ()=>{
207
+ beforeEach(async ()=>{
208
+ await store.addBlocks(blocks);
209
+ });
210
+ it('retrieves a block by its archive root', async ()=>{
211
+ const expectedBlock = blocks[3];
212
+ const archive = expectedBlock.block.archive.root;
213
+ const retrievedBlock = await store.getPublishedBlockByArchive(archive);
214
+ expect(retrievedBlock).toBeDefined();
215
+ expectBlocksEqual([
216
+ retrievedBlock
217
+ ], [
218
+ expectedBlock
219
+ ]);
220
+ });
221
+ it('returns undefined for non-existent archive root', async ()=>{
222
+ const nonExistentArchive = Fr.random();
223
+ await expect(store.getPublishedBlockByArchive(nonExistentArchive)).resolves.toBeUndefined();
224
+ });
225
+ });
226
+ describe('getBlockHeaderByHash', ()=>{
227
+ beforeEach(async ()=>{
228
+ await store.addBlocks(blocks);
229
+ });
230
+ it('retrieves a block header by its hash', async ()=>{
231
+ const expectedBlock = blocks[7];
232
+ const blockHash = await expectedBlock.block.hash();
233
+ const retrievedHeader = await store.getBlockHeaderByHash(blockHash);
234
+ expect(retrievedHeader).toBeDefined();
235
+ expect(retrievedHeader.equals(expectedBlock.block.getBlockHeader())).toBe(true);
236
+ });
237
+ it('returns undefined for non-existent block hash', async ()=>{
238
+ const nonExistentHash = Fr.random();
239
+ await expect(store.getBlockHeaderByHash(nonExistentHash)).resolves.toBeUndefined();
240
+ });
241
+ });
242
+ describe('getBlockHeaderByArchive', ()=>{
243
+ beforeEach(async ()=>{
244
+ await store.addBlocks(blocks);
245
+ });
246
+ it('retrieves a block header by its archive root', async ()=>{
247
+ const expectedBlock = blocks[2];
248
+ const archive = expectedBlock.block.archive.root;
249
+ const retrievedHeader = await store.getBlockHeaderByArchive(archive);
250
+ expect(retrievedHeader).toBeDefined();
251
+ expect(retrievedHeader.equals(expectedBlock.block.getBlockHeader())).toBe(true);
252
+ });
253
+ it('returns undefined for non-existent archive root', async ()=>{
254
+ const nonExistentArchive = Fr.random();
255
+ await expect(store.getBlockHeaderByArchive(nonExistentArchive)).resolves.toBeUndefined();
115
256
  });
116
257
  });
117
258
  describe('getSyncedL2BlockNumber', ()=>{
@@ -120,7 +261,7 @@ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
120
261
  });
121
262
  it("returns the most recently added block's number", async ()=>{
122
263
  await store.addBlocks(blocks);
123
- await expect(store.getSynchedL2BlockNumber()).resolves.toEqual(blocks.at(-1).data.number);
264
+ await expect(store.getSynchedL2BlockNumber()).resolves.toEqual(blocks.at(-1).block.number);
124
265
  });
125
266
  });
126
267
  describe('getSynchPoint', ()=>{
@@ -138,21 +279,52 @@ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
138
279
  });
139
280
  });
140
281
  it('returns the L1 block number that most recently added messages from inbox', async ()=>{
141
- await store.addL1ToL2Messages({
142
- lastProcessedL1BlockNumber: 1n,
143
- retrievedData: [
144
- new InboxLeaf(1n, Fr.ZERO)
145
- ]
282
+ const l1BlockHash = Buffer32.random();
283
+ const l1BlockNumber = 10n;
284
+ await store.setMessageSynchedL1Block({
285
+ l1BlockNumber: 5n,
286
+ l1BlockHash: Buffer32.random()
146
287
  });
288
+ await store.addL1ToL2Messages([
289
+ makeInboxMessage(Buffer16.ZERO, {
290
+ l1BlockNumber,
291
+ l1BlockHash
292
+ })
293
+ ]);
147
294
  await expect(store.getSynchPoint()).resolves.toEqual({
148
295
  blocksSynchedTo: undefined,
149
- messagesSynchedTo: 1n
296
+ messagesSynchedTo: {
297
+ l1BlockHash,
298
+ l1BlockNumber
299
+ }
300
+ });
301
+ });
302
+ it('returns the latest syncpoint if latest message is behind', async ()=>{
303
+ const l1BlockHash = Buffer32.random();
304
+ const l1BlockNumber = 10n;
305
+ await store.setMessageSynchedL1Block({
306
+ l1BlockNumber,
307
+ l1BlockHash
308
+ });
309
+ const msg = makeInboxMessage(Buffer16.ZERO, {
310
+ l1BlockNumber: 5n,
311
+ l1BlockHash: Buffer32.random()
312
+ });
313
+ await store.addL1ToL2Messages([
314
+ msg
315
+ ]);
316
+ await expect(store.getSynchPoint()).resolves.toEqual({
317
+ blocksSynchedTo: undefined,
318
+ messagesSynchedTo: {
319
+ l1BlockHash,
320
+ l1BlockNumber
321
+ }
150
322
  });
151
323
  });
152
324
  });
153
325
  describe('addLogs', ()=>{
154
326
  it('adds private & public logs', async ()=>{
155
- const block = blocks[0].data;
327
+ const block = blocks[0].block;
156
328
  await expect(store.addLogs([
157
329
  block
158
330
  ])).resolves.toEqual(true);
@@ -160,7 +332,7 @@ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
160
332
  });
161
333
  describe('deleteLogs', ()=>{
162
334
  it('deletes private & public logs', async ()=>{
163
- const block = blocks[0].data;
335
+ const block = blocks[0].block;
164
336
  await store.addBlocks([
165
337
  blocks[0]
166
338
  ]);
@@ -183,7 +355,7 @@ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
183
355
  });
184
356
  describe('getPrivateLogs', ()=>{
185
357
  it('gets added private logs', async ()=>{
186
- const block = blocks[0].data;
358
+ const block = blocks[0].block;
187
359
  await store.addBlocks([
188
360
  blocks[0]
189
361
  ]);
@@ -196,29 +368,55 @@ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
196
368
  });
197
369
  describe('getTxEffect', ()=>{
198
370
  beforeEach(async ()=>{
199
- await store.addLogs(blocks.map((b)=>b.data));
371
+ await store.addLogs(blocks.map((b)=>b.block));
200
372
  await store.addBlocks(blocks);
201
373
  });
202
374
  it.each([
203
- ()=>wrapInBlock(blocks[0].data.body.txEffects[0], blocks[0].data),
204
- ()=>wrapInBlock(blocks[9].data.body.txEffects[3], blocks[9].data),
205
- ()=>wrapInBlock(blocks[3].data.body.txEffects[1], blocks[3].data),
206
- ()=>wrapInBlock(blocks[5].data.body.txEffects[2], blocks[5].data),
207
- ()=>wrapInBlock(blocks[1].data.body.txEffects[0], blocks[1].data)
375
+ ()=>({
376
+ data: blocks[0].block.body.txEffects[0],
377
+ block: blocks[0].block,
378
+ txIndexInBlock: 0
379
+ }),
380
+ ()=>({
381
+ data: blocks[9].block.body.txEffects[3],
382
+ block: blocks[9].block,
383
+ txIndexInBlock: 3
384
+ }),
385
+ ()=>({
386
+ data: blocks[3].block.body.txEffects[1],
387
+ block: blocks[3].block,
388
+ txIndexInBlock: 1
389
+ }),
390
+ ()=>({
391
+ data: blocks[5].block.body.txEffects[2],
392
+ block: blocks[5].block,
393
+ txIndexInBlock: 2
394
+ }),
395
+ ()=>({
396
+ data: blocks[1].block.body.txEffects[0],
397
+ block: blocks[1].block,
398
+ txIndexInBlock: 0
399
+ })
208
400
  ])('retrieves a previously stored transaction', async (getExpectedTx)=>{
209
- const expectedTx = await getExpectedTx();
210
- const actualTx = await store.getTxEffect(expectedTx.data.txHash);
401
+ const { data, block, txIndexInBlock } = getExpectedTx();
402
+ const expectedTx = {
403
+ data,
404
+ l2BlockNumber: block.number,
405
+ l2BlockHash: L2BlockHash.fromField(await block.hash()),
406
+ txIndexInBlock
407
+ };
408
+ const actualTx = await store.getTxEffect(data.txHash);
211
409
  expect(actualTx).toEqual(expectedTx);
212
410
  });
213
411
  it('returns undefined if tx is not found', async ()=>{
214
412
  await expect(store.getTxEffect(TxHash.random())).resolves.toBeUndefined();
215
413
  });
216
414
  it.each([
217
- ()=>wrapInBlock(blocks[0].data.body.txEffects[0], blocks[0].data),
218
- ()=>wrapInBlock(blocks[9].data.body.txEffects[3], blocks[9].data),
219
- ()=>wrapInBlock(blocks[3].data.body.txEffects[1], blocks[3].data),
220
- ()=>wrapInBlock(blocks[5].data.body.txEffects[2], blocks[5].data),
221
- ()=>wrapInBlock(blocks[1].data.body.txEffects[0], blocks[1].data)
415
+ ()=>wrapInBlock(blocks[0].block.body.txEffects[0], blocks[0].block),
416
+ ()=>wrapInBlock(blocks[9].block.body.txEffects[3], blocks[9].block),
417
+ ()=>wrapInBlock(blocks[3].block.body.txEffects[1], blocks[3].block),
418
+ ()=>wrapInBlock(blocks[5].block.body.txEffects[2], blocks[5].block),
419
+ ()=>wrapInBlock(blocks[1].block.body.txEffects[0], blocks[1].block)
222
420
  ])('tries to retrieves a previously stored transaction after deleted', async (getExpectedTx)=>{
223
421
  await store.unwindBlocks(blocks.length, blocks.length);
224
422
  const expectedTx = await getExpectedTx();
@@ -228,42 +426,245 @@ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
228
426
  it('returns undefined if tx is not found', async ()=>{
229
427
  await expect(store.getTxEffect(TxHash.random())).resolves.toBeUndefined();
230
428
  });
429
+ it('does not fail if the block is unwound while requesting a tx', async ()=>{
430
+ const expectedTx = await wrapInBlock(blocks[1].block.body.txEffects[0], blocks[1].block);
431
+ let done = false;
432
+ void (async ()=>{
433
+ while(!done){
434
+ void store.getTxEffect(expectedTx.data.txHash);
435
+ await sleep(1);
436
+ }
437
+ })();
438
+ await store.unwindBlocks(blocks.length, blocks.length);
439
+ done = true;
440
+ expect(await store.getTxEffect(expectedTx.data.txHash)).toEqual(undefined);
441
+ });
231
442
  });
232
443
  describe('L1 to L2 Messages', ()=>{
233
- const l2BlockNumber = 13n;
234
- const l1ToL2MessageSubtreeSize = 2 ** L1_TO_L2_MSG_SUBTREE_HEIGHT;
235
- const generateBlockMessages = (blockNumber, numMessages)=>Array.from({
236
- length: numMessages
237
- }, (_, i)=>new InboxLeaf(InboxLeaf.smallestIndexFromL2Block(blockNumber) + BigInt(i), Fr.random()));
238
- it('returns messages in correct order', async ()=>{
239
- const msgs = generateBlockMessages(l2BlockNumber, l1ToL2MessageSubtreeSize);
240
- const shuffledMessages = msgs.slice().sort(()=>randomInt(1) - 0.5);
241
- await store.addL1ToL2Messages({
242
- lastProcessedL1BlockNumber: 100n,
243
- retrievedData: shuffledMessages
244
- });
245
- const retrievedMessages = await store.getL1ToL2Messages(l2BlockNumber);
246
- const expectedLeavesOrder = msgs.map((msg)=>msg.leaf);
247
- expect(expectedLeavesOrder).toEqual(retrievedMessages);
248
- });
249
- it('throws if it is impossible to sequence messages correctly', async ()=>{
250
- const msgs = generateBlockMessages(l2BlockNumber, l1ToL2MessageSubtreeSize - 1);
251
- // We replace a message with index 4 with a message with index at the end of the tree
252
- // --> with that there will be a gap and it will be impossible to sequence the
253
- // end of tree = start of next tree/block - 1
254
- msgs[4] = new InboxLeaf(InboxLeaf.smallestIndexFromL2Block(l2BlockNumber + 1n) - 1n, Fr.random());
255
- await store.addL1ToL2Messages({
256
- lastProcessedL1BlockNumber: 100n,
257
- retrievedData: msgs
258
- });
259
- await expect(async ()=>{
260
- await store.getL1ToL2Messages(l2BlockNumber);
261
- }).rejects.toThrow(`L1 to L2 message gap found in block ${l2BlockNumber}`);
444
+ const initialL2BlockNumber = 13;
445
+ const checkMessages = async (msgs)=>{
446
+ expect(await store.getLastL1ToL2Message()).toEqual(msgs.at(-1));
447
+ expect(await toArray(store.iterateL1ToL2Messages())).toEqual(msgs);
448
+ expect(await store.getTotalL1ToL2MessageCount()).toEqual(BigInt(msgs.length));
449
+ };
450
+ const makeInboxMessagesWithFullBlocks = (blockCount, opts = {})=>makeInboxMessages(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * blockCount, {
451
+ overrideFn: (msg, i)=>{
452
+ const l2BlockNumber = (opts.initialL2BlockNumber ?? initialL2BlockNumber) + Math.floor(i / NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
453
+ const index = InboxLeaf.smallestIndexFromL2Block(l2BlockNumber) + BigInt(i % NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
454
+ return {
455
+ ...msg,
456
+ l2BlockNumber,
457
+ index
458
+ };
459
+ }
460
+ });
461
+ it('stores first message ever', async ()=>{
462
+ const msg = makeInboxMessage(Buffer16.ZERO, {
463
+ index: 0n,
464
+ l2BlockNumber: 1
465
+ });
466
+ await store.addL1ToL2Messages([
467
+ msg
468
+ ]);
469
+ await checkMessages([
470
+ msg
471
+ ]);
472
+ expect(await store.getL1ToL2Messages(1)).toEqual([
473
+ msg.leaf
474
+ ]);
475
+ });
476
+ it('stores single message', async ()=>{
477
+ const msg = makeInboxMessage(Buffer16.ZERO, {
478
+ l2BlockNumber: 2
479
+ });
480
+ await store.addL1ToL2Messages([
481
+ msg
482
+ ]);
483
+ await checkMessages([
484
+ msg
485
+ ]);
486
+ expect(await store.getL1ToL2Messages(2)).toEqual([
487
+ msg.leaf
488
+ ]);
489
+ });
490
+ it('stores and returns messages across different blocks', async ()=>{
491
+ const msgs = makeInboxMessages(5, {
492
+ initialL2BlockNumber
493
+ });
494
+ await store.addL1ToL2Messages(msgs);
495
+ await checkMessages(msgs);
496
+ expect(await store.getL1ToL2Messages(initialL2BlockNumber + 2)).toEqual([
497
+ msgs[2]
498
+ ].map((m)=>m.leaf));
499
+ });
500
+ it('stores the same messages again', async ()=>{
501
+ const msgs = makeInboxMessages(5, {
502
+ initialL2BlockNumber
503
+ });
504
+ await store.addL1ToL2Messages(msgs);
505
+ await store.addL1ToL2Messages(msgs.slice(2));
506
+ await checkMessages(msgs);
507
+ });
508
+ it('stores and returns messages across different blocks with gaps', async ()=>{
509
+ const msgs1 = makeInboxMessages(3, {
510
+ initialL2BlockNumber: 1
511
+ });
512
+ const msgs2 = makeInboxMessages(3, {
513
+ initialL2BlockNumber: 20,
514
+ initialHash: msgs1.at(-1).rollingHash
515
+ });
516
+ await store.addL1ToL2Messages(msgs1);
517
+ await store.addL1ToL2Messages(msgs2);
518
+ await checkMessages([
519
+ ...msgs1,
520
+ ...msgs2
521
+ ]);
522
+ expect(await store.getL1ToL2Messages(1)).toEqual([
523
+ msgs1[0].leaf
524
+ ]);
525
+ expect(await store.getL1ToL2Messages(4)).toEqual([]);
526
+ expect(await store.getL1ToL2Messages(20)).toEqual([
527
+ msgs2[0].leaf
528
+ ]);
529
+ expect(await store.getL1ToL2Messages(24)).toEqual([]);
530
+ });
531
+ it('stores and returns messages with block numbers larger than a byte', async ()=>{
532
+ const msgs = makeInboxMessages(5, {
533
+ initialL2BlockNumber: 1000
534
+ });
535
+ await store.addL1ToL2Messages(msgs);
536
+ await checkMessages(msgs);
537
+ expect(await store.getL1ToL2Messages(1002)).toEqual([
538
+ msgs[2]
539
+ ].map((m)=>m.leaf));
540
+ });
541
+ it('stores and returns multiple messages per block', async ()=>{
542
+ const msgs = makeInboxMessagesWithFullBlocks(4);
543
+ await store.addL1ToL2Messages(msgs);
544
+ await checkMessages(msgs);
545
+ const blockMessages = await store.getL1ToL2Messages(initialL2BlockNumber + 1);
546
+ expect(blockMessages).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
547
+ expect(blockMessages).toEqual(msgs.slice(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2).map((m)=>m.leaf));
548
+ });
549
+ it('stores messages in multiple operations', async ()=>{
550
+ const msgs = makeInboxMessages(20, {
551
+ initialL2BlockNumber
552
+ });
553
+ await store.addL1ToL2Messages(msgs.slice(0, 10));
554
+ await store.addL1ToL2Messages(msgs.slice(10, 20));
555
+ expect(await store.getL1ToL2Messages(initialL2BlockNumber + 2)).toEqual([
556
+ msgs[2]
557
+ ].map((m)=>m.leaf));
558
+ expect(await store.getL1ToL2Messages(initialL2BlockNumber + 12)).toEqual([
559
+ msgs[12]
560
+ ].map((m)=>m.leaf));
561
+ await checkMessages(msgs);
562
+ });
563
+ it('iterates over messages from start index', async ()=>{
564
+ const msgs = makeInboxMessages(10, {
565
+ initialL2BlockNumber
566
+ });
567
+ await store.addL1ToL2Messages(msgs);
568
+ const iterated = await toArray(store.iterateL1ToL2Messages({
569
+ start: msgs[3].index
570
+ }));
571
+ expect(iterated).toEqual(msgs.slice(3));
572
+ });
573
+ it('iterates over messages in reverse', async ()=>{
574
+ const msgs = makeInboxMessages(10, {
575
+ initialL2BlockNumber
576
+ });
577
+ await store.addL1ToL2Messages(msgs);
578
+ const iterated = await toArray(store.iterateL1ToL2Messages({
579
+ reverse: true,
580
+ end: msgs[3].index
581
+ }));
582
+ expect(iterated).toEqual(msgs.slice(0, 4).reverse());
583
+ });
584
+ it('throws if messages are added out of order', async ()=>{
585
+ const msgs = makeInboxMessages(5, {
586
+ overrideFn: (msg, i)=>({
587
+ ...msg,
588
+ index: BigInt(10 - i)
589
+ })
590
+ });
591
+ await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
592
+ });
593
+ it('throws if block number for the first message is out of order', async ()=>{
594
+ const msgs = makeInboxMessages(4, {
595
+ initialL2BlockNumber
596
+ });
597
+ msgs[2].l2BlockNumber = initialL2BlockNumber - 1;
598
+ await store.addL1ToL2Messages(msgs.slice(0, 2));
599
+ await expect(store.addL1ToL2Messages(msgs.slice(2, 4))).rejects.toThrow(MessageStoreError);
600
+ });
601
+ it('throws if rolling hash is not correct', async ()=>{
602
+ const msgs = makeInboxMessages(5);
603
+ msgs[1].rollingHash = Buffer16.random();
604
+ await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
605
+ });
606
+ it('throws if rolling hash for first message is not correct', async ()=>{
607
+ const msgs = makeInboxMessages(4);
608
+ msgs[2].rollingHash = Buffer16.random();
609
+ await store.addL1ToL2Messages(msgs.slice(0, 2));
610
+ await expect(store.addL1ToL2Messages(msgs.slice(2, 4))).rejects.toThrow(MessageStoreError);
611
+ });
612
+ it('throws if index is not in the correct range', async ()=>{
613
+ const msgs = makeInboxMessages(5, {
614
+ initialL2BlockNumber
615
+ });
616
+ msgs.at(-1).index += 100n;
617
+ await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
618
+ });
619
+ it('throws if first index in block has gaps', async ()=>{
620
+ const msgs = makeInboxMessages(4, {
621
+ initialL2BlockNumber
622
+ });
623
+ msgs[2].index++;
624
+ await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
625
+ });
626
+ it('throws if index does not follow previous one', async ()=>{
627
+ const msgs = makeInboxMessages(2, {
628
+ initialL2BlockNumber,
629
+ overrideFn: (msg, i)=>({
630
+ ...msg,
631
+ l2BlockNumber: 2,
632
+ index: BigInt(i + NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2)
633
+ })
634
+ });
635
+ msgs[1].index++;
636
+ await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
637
+ });
638
+ it('removes messages up to the given block number', async ()=>{
639
+ const msgs = makeInboxMessagesWithFullBlocks(4, {
640
+ initialL2BlockNumber: 1
641
+ });
642
+ await store.addL1ToL2Messages(msgs);
643
+ await checkMessages(msgs);
644
+ expect(await store.getL1ToL2Messages(1)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
645
+ expect(await store.getL1ToL2Messages(2)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
646
+ expect(await store.getL1ToL2Messages(3)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
647
+ expect(await store.getL1ToL2Messages(4)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
648
+ await store.rollbackL1ToL2MessagesToL2Block(2);
649
+ expect(await store.getL1ToL2Messages(1)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
650
+ expect(await store.getL1ToL2Messages(2)).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
651
+ expect(await store.getL1ToL2Messages(3)).toHaveLength(0);
652
+ expect(await store.getL1ToL2Messages(4)).toHaveLength(0);
653
+ await checkMessages(msgs.slice(0, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2));
654
+ });
655
+ it('removes messages starting with the given index', async ()=>{
656
+ const msgs = makeInboxMessagesWithFullBlocks(4, {
657
+ initialL2BlockNumber: 1
658
+ });
659
+ await store.addL1ToL2Messages(msgs);
660
+ await store.removeL1ToL2Messages(msgs[13].index);
661
+ await checkMessages(msgs.slice(0, 13));
262
662
  });
263
663
  });
264
664
  describe('contractInstances', ()=>{
265
665
  let contractInstance;
266
666
  const blockNum = 10;
667
+ const timestamp = 3600n;
267
668
  beforeEach(async ()=>{
268
669
  const classId = Fr.random();
269
670
  const randomInstance = await SerializableContractInstance.random({
@@ -279,16 +680,103 @@ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
279
680
  ], blockNum);
280
681
  });
281
682
  it('returns previously stored contract instances', async ()=>{
282
- await expect(store.getContractInstance(contractInstance.address)).resolves.toMatchObject(contractInstance);
683
+ await expect(store.getContractInstance(contractInstance.address, timestamp)).resolves.toMatchObject(contractInstance);
283
684
  });
284
685
  it('returns undefined if contract instance is not found', async ()=>{
285
- await expect(store.getContractInstance(await AztecAddress.random())).resolves.toBeUndefined();
686
+ await expect(store.getContractInstance(await AztecAddress.random(), timestamp)).resolves.toBeUndefined();
286
687
  });
287
688
  it('returns undefined if previously stored contract instances was deleted', async ()=>{
288
689
  await store.deleteContractInstances([
289
690
  contractInstance
290
691
  ], blockNum);
291
- await expect(store.getContractInstance(contractInstance.address)).resolves.toBeUndefined();
692
+ await expect(store.getContractInstance(contractInstance.address, timestamp)).resolves.toBeUndefined();
693
+ });
694
+ });
695
+ describe('contractInstanceUpdates', ()=>{
696
+ let contractInstance;
697
+ let classId;
698
+ let nextClassId;
699
+ const timestampOfChange = 3600n;
700
+ beforeEach(async ()=>{
701
+ classId = Fr.random();
702
+ nextClassId = Fr.random();
703
+ const randomInstance = await SerializableContractInstance.random({
704
+ currentContractClassId: classId,
705
+ originalContractClassId: classId
706
+ });
707
+ contractInstance = {
708
+ ...randomInstance,
709
+ address: await AztecAddress.random()
710
+ };
711
+ await store.addContractInstances([
712
+ contractInstance
713
+ ], 1);
714
+ await store.addContractInstanceUpdates([
715
+ {
716
+ prevContractClassId: classId,
717
+ newContractClassId: nextClassId,
718
+ timestampOfChange,
719
+ address: contractInstance.address
720
+ }
721
+ ], timestampOfChange - 1n);
722
+ });
723
+ it('gets the correct current class id for a contract not updated yet', async ()=>{
724
+ const fetchedInstance = await store.getContractInstance(contractInstance.address, timestampOfChange - 1n);
725
+ expect(fetchedInstance?.originalContractClassId).toEqual(classId);
726
+ expect(fetchedInstance?.currentContractClassId).toEqual(classId);
727
+ });
728
+ it('gets the correct current class id for a contract that has just been updated', async ()=>{
729
+ const fetchedInstance = await store.getContractInstance(contractInstance.address, timestampOfChange);
730
+ expect(fetchedInstance?.originalContractClassId).toEqual(classId);
731
+ expect(fetchedInstance?.currentContractClassId).toEqual(nextClassId);
732
+ });
733
+ it('gets the correct current class id for a contract that was updated in the past', async ()=>{
734
+ const fetchedInstance = await store.getContractInstance(contractInstance.address, timestampOfChange + 1n);
735
+ expect(fetchedInstance?.originalContractClassId).toEqual(classId);
736
+ expect(fetchedInstance?.currentContractClassId).toEqual(nextClassId);
737
+ });
738
+ it('ignores updates for the wrong contract', async ()=>{
739
+ const otherClassId = Fr.random();
740
+ const randomInstance = await SerializableContractInstance.random({
741
+ currentContractClassId: otherClassId,
742
+ originalContractClassId: otherClassId
743
+ });
744
+ const otherContractInstance = {
745
+ ...randomInstance,
746
+ address: await AztecAddress.random()
747
+ };
748
+ await store.addContractInstances([
749
+ otherContractInstance
750
+ ], 1);
751
+ const fetchedInstance = await store.getContractInstance(otherContractInstance.address, timestampOfChange + 1n);
752
+ expect(fetchedInstance?.originalContractClassId).toEqual(otherClassId);
753
+ expect(fetchedInstance?.currentContractClassId).toEqual(otherClassId);
754
+ });
755
+ it('bounds its search to the right contract if more than than one update exists', async ()=>{
756
+ const otherClassId = Fr.random();
757
+ const otherNextClassId = Fr.random();
758
+ const randomInstance = await SerializableContractInstance.random({
759
+ currentContractClassId: otherClassId,
760
+ originalContractClassId: otherNextClassId
761
+ });
762
+ const otherContractInstance = {
763
+ ...randomInstance,
764
+ address: await AztecAddress.random()
765
+ };
766
+ await store.addContractInstances([
767
+ otherContractInstance
768
+ ], 1);
769
+ await store.addContractInstanceUpdates([
770
+ {
771
+ prevContractClassId: otherClassId,
772
+ newContractClassId: otherNextClassId,
773
+ timestampOfChange,
774
+ address: otherContractInstance.address
775
+ }
776
+ ], timestampOfChange - 1n);
777
+ const fetchedInstance = await store.getContractInstance(contractInstance.address, timestampOfChange + 1n);
778
+ expect(fetchedInstance?.originalContractClassId).toEqual(classId);
779
+ expect(fetchedInstance?.currentContractClassId).toEqual(nextClassId);
292
780
  });
293
781
  });
294
782
  describe('contractClasses', ()=>{
@@ -338,18 +826,18 @@ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
338
826
  const stored = await store.getContractClass(contractClass.id);
339
827
  expect(stored?.privateFunctions).toEqual(fns);
340
828
  });
341
- it('adds new unconstrained functions', async ()=>{
342
- const fns = times(3, makeUnconstrainedFunctionWithMembershipProof);
829
+ it('adds new utility functions', async ()=>{
830
+ const fns = times(3, makeUtilityFunctionWithMembershipProof);
343
831
  await store.addFunctions(contractClass.id, [], fns);
344
832
  const stored = await store.getContractClass(contractClass.id);
345
- expect(stored?.unconstrainedFunctions).toEqual(fns);
833
+ expect(stored?.utilityFunctions).toEqual(fns);
346
834
  });
347
- it('does not duplicate unconstrained functions', async ()=>{
348
- const fns = times(3, makeUnconstrainedFunctionWithMembershipProof);
835
+ it('does not duplicate utility functions', async ()=>{
836
+ const fns = times(3, makeUtilityFunctionWithMembershipProof);
349
837
  await store.addFunctions(contractClass.id, [], fns.slice(0, 1));
350
838
  await store.addFunctions(contractClass.id, [], fns);
351
839
  const stored = await store.getContractClass(contractClass.id);
352
- expect(stored?.unconstrainedFunctions).toEqual(fns);
840
+ expect(stored?.utilityFunctions).toEqual(fns);
353
841
  });
354
842
  });
355
843
  describe('getLogsByTags', ()=>{
@@ -358,27 +846,17 @@ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
358
846
  const numPrivateLogsPerTx = 3;
359
847
  const numPublicLogsPerTx = 2;
360
848
  let blocks;
361
- const makeTag = (blockNumber, txIndex, logIndex, isPublic = false)=>new Fr((blockNumber * 100 + txIndex * 10 + logIndex) * (isPublic ? 123 : 1));
362
- // See parseLogFromPublic
363
- // Search the codebase for "disgusting encoding" to see other hardcoded instances of this encoding, that you might need to change if you ever find yourself here.
364
- const makeLengthsField = (publicValuesLen, privateValuesLen)=>{
365
- const buf = Buffer.alloc(32);
366
- buf.writeUint16BE(publicValuesLen, 27);
367
- buf.writeUint16BE(privateValuesLen, 30);
368
- return Fr.fromBuffer(buf);
369
- };
370
- const makePrivateLog = (tag)=>PrivateLog.fromFields([
371
- tag,
372
- ...times(PRIVATE_LOG_SIZE_IN_FIELDS - 1, (i)=>new Fr(tag.toNumber() + i))
373
- ]);
374
- // The tag lives in field 1, not 0, of a public log
375
- // See extractTaggedLogsFromPublic and noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr -> emit_log
376
- const makePublicLog = (tag)=>PublicLog.fromFields([
377
- AztecAddress.fromNumber(1).toField(),
378
- makeLengthsField(2, PUBLIC_LOG_DATA_SIZE_IN_FIELDS - 3),
379
- tag,
380
- ...times(PUBLIC_LOG_DATA_SIZE_IN_FIELDS - 1, (i)=>new Fr(tag.toNumber() + i))
381
- ]);
849
+ const makeTag = (blockNumber, txIndex, logIndex, isPublic = false)=>blockNumber === 1 && txIndex === 0 && logIndex === 0 ? Fr.ZERO // Shared tag
850
+ : new Fr((blockNumber * 100 + txIndex * 10 + logIndex) * (isPublic ? 123 : 1));
851
+ const makePrivateLog = (tag)=>PrivateLog.from({
852
+ fields: makeTuple(PRIVATE_LOG_SIZE_IN_FIELDS, (i)=>!i ? tag : new Fr(tag.toNumber() + i)),
853
+ emittedLength: PRIVATE_LOG_SIZE_IN_FIELDS
854
+ });
855
+ const makePublicLog = (tag)=>PublicLog.from({
856
+ contractAddress: AztecAddress.fromNumber(1),
857
+ // Arbitrary length
858
+ fields: new Array(10).fill(null).map((_, i)=>!i ? tag : new Fr(tag.toNumber() + i))
859
+ });
382
860
  const mockPrivateLogs = (blockNumber, txIndex)=>{
383
861
  return times(numPrivateLogsPerTx, (logIndex)=>{
384
862
  const tag = makeTag(blockNumber, txIndex, logIndex);
@@ -393,66 +871,67 @@ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
393
871
  };
394
872
  const mockBlockWithLogs = async (blockNumber)=>{
395
873
  const block = await L2Block.random(blockNumber);
396
- block.header.globalVariables.blockNumber = new Fr(blockNumber);
874
+ block.header.globalVariables.blockNumber = blockNumber;
397
875
  block.body.txEffects = await timesParallel(numTxsPerBlock, async (txIndex)=>{
398
876
  const txEffect = await TxEffect.random();
399
877
  txEffect.privateLogs = mockPrivateLogs(blockNumber, txIndex);
400
878
  txEffect.publicLogs = mockPublicLogs(blockNumber, txIndex);
401
879
  return txEffect;
402
880
  });
403
- return {
404
- data: block,
881
+ return PublishedL2Block.fromFields({
882
+ block: block,
883
+ attestations: times(3, CommitteeAttestation.random),
405
884
  l1: {
406
885
  blockNumber: BigInt(blockNumber),
407
- blockHash: `0x${blockNumber}`,
886
+ blockHash: makeBlockHash(blockNumber),
408
887
  timestamp: BigInt(blockNumber)
409
888
  }
410
- };
889
+ });
411
890
  };
412
891
  beforeEach(async ()=>{
413
- blocks = await timesParallel(numBlocks, (index)=>mockBlockWithLogs(index));
892
+ blocks = await timesParallel(numBlocks, (index)=>mockBlockWithLogs(index + 1));
414
893
  await store.addBlocks(blocks);
415
- await store.addLogs(blocks.map((b)=>b.data));
894
+ await store.addLogs(blocks.map((b)=>b.block));
416
895
  });
417
896
  it('is possible to batch request private logs via tags', async ()=>{
418
897
  const tags = [
419
- makeTag(1, 1, 2),
420
- makeTag(0, 2, 0)
898
+ makeTag(2, 1, 2),
899
+ makeTag(1, 2, 0)
421
900
  ];
422
901
  const logsByTags = await store.getLogsByTags(tags);
423
902
  expect(logsByTags).toEqual([
424
903
  [
425
904
  expect.objectContaining({
426
- blockNumber: 1,
427
- logData: makePrivateLog(tags[0]).toBuffer(),
905
+ blockNumber: 2,
906
+ log: makePrivateLog(tags[0]),
428
907
  isFromPublic: false
429
908
  })
430
909
  ],
431
910
  [
432
911
  expect.objectContaining({
433
- blockNumber: 0,
434
- logData: makePrivateLog(tags[1]).toBuffer(),
912
+ blockNumber: 1,
913
+ log: makePrivateLog(tags[1]),
435
914
  isFromPublic: false
436
915
  })
437
916
  ]
438
917
  ]);
439
918
  });
440
919
  it('is possible to batch request all logs (private and public) via tags', async ()=>{
441
- // Tag(0, 0, 0) is shared with the first private log and the first public log.
920
+ // Tag(1, 0, 0) is shared with the first private log and the first public log.
442
921
  const tags = [
443
- makeTag(0, 0, 0)
922
+ makeTag(1, 0, 0)
444
923
  ];
445
924
  const logsByTags = await store.getLogsByTags(tags);
446
925
  expect(logsByTags).toEqual([
447
926
  [
448
927
  expect.objectContaining({
449
- blockNumber: 0,
450
- logData: makePrivateLog(tags[0]).toBuffer(),
928
+ blockNumber: 1,
929
+ log: makePrivateLog(tags[0]),
451
930
  isFromPublic: false
452
931
  }),
453
932
  expect.objectContaining({
454
- blockNumber: 0,
455
- logData: makePublicLog(tags[0]).toBuffer(),
933
+ blockNumber: 1,
934
+ log: makePublicLog(tags[0]),
456
935
  isFromPublic: true
457
936
  })
458
937
  ]
@@ -465,26 +944,26 @@ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
465
944
  // Create a block containing logs that have the same tag as the blocks before.
466
945
  const newBlockNumber = numBlocks;
467
946
  const newBlock = await mockBlockWithLogs(newBlockNumber);
468
- const newLog = newBlock.data.body.txEffects[1].privateLogs[1];
947
+ const newLog = newBlock.block.body.txEffects[1].privateLogs[1];
469
948
  newLog.fields[0] = tags[0];
470
- newBlock.data.body.txEffects[1].privateLogs[1] = newLog;
949
+ newBlock.block.body.txEffects[1].privateLogs[1] = newLog;
471
950
  await store.addBlocks([
472
951
  newBlock
473
952
  ]);
474
953
  await store.addLogs([
475
- newBlock.data
954
+ newBlock.block
476
955
  ]);
477
956
  const logsByTags = await store.getLogsByTags(tags);
478
957
  expect(logsByTags).toEqual([
479
958
  [
480
959
  expect.objectContaining({
481
960
  blockNumber: 1,
482
- logData: makePrivateLog(tags[0]).toBuffer(),
961
+ log: makePrivateLog(tags[0]),
483
962
  isFromPublic: false
484
963
  }),
485
964
  expect.objectContaining({
486
965
  blockNumber: newBlockNumber,
487
- logData: newLog.toBuffer(),
966
+ log: newLog,
488
967
  isFromPublic: false
489
968
  })
490
969
  ]
@@ -501,46 +980,12 @@ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
501
980
  [
502
981
  expect.objectContaining({
503
982
  blockNumber: 1,
504
- logData: makePrivateLog(tags[1]).toBuffer(),
983
+ log: makePrivateLog(tags[1]),
505
984
  isFromPublic: false
506
985
  })
507
986
  ]
508
987
  ]);
509
988
  });
510
- it('is not possible to add public logs by tag if they are invalid', async ()=>{
511
- const tag = makeTag(99, 88, 77);
512
- const invalidLogs = [
513
- PublicLog.fromFields([
514
- AztecAddress.fromNumber(1).toField(),
515
- makeLengthsField(2, 3),
516
- tag,
517
- ...times(PUBLIC_LOG_DATA_SIZE_IN_FIELDS - 1, (i)=>new Fr(tag.toNumber() + i))
518
- ]),
519
- PublicLog.fromFields([
520
- AztecAddress.fromNumber(1).toField(),
521
- makeLengthsField(2, PUBLIC_LOG_DATA_SIZE_IN_FIELDS),
522
- tag,
523
- ...times(PUBLIC_LOG_DATA_SIZE_IN_FIELDS - 1, (i)=>new Fr(tag.toNumber() + i))
524
- ])
525
- ];
526
- // Create a block containing these invalid logs
527
- const newBlockNumber = numBlocks;
528
- const newBlock = await mockBlockWithLogs(newBlockNumber);
529
- newBlock.data.body.txEffects[0].publicLogs = invalidLogs;
530
- await store.addBlocks([
531
- newBlock
532
- ]);
533
- await store.addLogs([
534
- newBlock.data
535
- ]);
536
- const logsByTags = await store.getLogsByTags([
537
- tag
538
- ]);
539
- // Neither of the logs should have been added:
540
- expect(logsByTags).toEqual([
541
- []
542
- ]);
543
- });
544
989
  });
545
990
  describe('getPublicLogs', ()=>{
546
991
  const txsPerBlock = 4;
@@ -549,25 +994,26 @@ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
549
994
  const numBlocks = 10;
550
995
  let blocks;
551
996
  beforeEach(async ()=>{
552
- blocks = await timesParallel(numBlocks, async (index)=>({
553
- data: await L2Block.random(index + 1, txsPerBlock, numPublicFunctionCalls, numPublicLogs),
997
+ blocks = await timesParallel(numBlocks, async (index)=>PublishedL2Block.fromFields({
998
+ block: await L2Block.random(index + 1, txsPerBlock, numPublicFunctionCalls, numPublicLogs),
554
999
  l1: {
555
1000
  blockNumber: BigInt(index),
556
- blockHash: `0x${index}`,
1001
+ blockHash: makeBlockHash(index),
557
1002
  timestamp: BigInt(index)
558
- }
1003
+ },
1004
+ attestations: times(3, CommitteeAttestation.random)
559
1005
  }));
560
1006
  await store.addBlocks(blocks);
561
- await store.addLogs(blocks.map((b)=>b.data));
1007
+ await store.addLogs(blocks.map((b)=>b.block));
562
1008
  });
563
1009
  it('no logs returned if deleted ("txHash" filter param is respected variant)', async ()=>{
564
1010
  // get random tx
565
1011
  const targetBlockIndex = randomInt(numBlocks);
566
1012
  const targetTxIndex = randomInt(txsPerBlock);
567
- const targetTxHash = blocks[targetBlockIndex].data.body.txEffects[targetTxIndex].txHash;
1013
+ const targetTxHash = blocks[targetBlockIndex].block.body.txEffects[targetTxIndex].txHash;
568
1014
  await Promise.all([
569
1015
  store.unwindBlocks(blocks.length, blocks.length),
570
- store.deleteLogs(blocks.map((b)=>b.data))
1016
+ store.deleteLogs(blocks.map((b)=>b.block))
571
1017
  ]);
572
1018
  const response = await store.getPublicLogs({
573
1019
  txHash: targetTxHash
@@ -580,7 +1026,7 @@ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
580
1026
  // get random tx
581
1027
  const targetBlockIndex = randomInt(numBlocks);
582
1028
  const targetTxIndex = randomInt(txsPerBlock);
583
- const targetTxHash = blocks[targetBlockIndex].data.body.txEffects[targetTxIndex].txHash;
1029
+ const targetTxHash = blocks[targetBlockIndex].block.body.txEffects[targetTxIndex].txHash;
584
1030
  const response = await store.getPublicLogs({
585
1031
  txHash: targetTxHash
586
1032
  });
@@ -617,7 +1063,7 @@ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
617
1063
  const targetBlockIndex = randomInt(numBlocks);
618
1064
  const targetTxIndex = randomInt(txsPerBlock);
619
1065
  const targetLogIndex = randomInt(numPublicLogs * numPublicFunctionCalls);
620
- const targetContractAddress = blocks[targetBlockIndex].data.body.txEffects[targetTxIndex].publicLogs[targetLogIndex].contractAddress;
1066
+ const targetContractAddress = blocks[targetBlockIndex].block.body.txEffects[targetTxIndex].publicLogs[targetLogIndex].contractAddress;
621
1067
  const response = await store.getPublicLogs({
622
1068
  contractAddress: targetContractAddress
623
1069
  });
@@ -740,54 +1186,102 @@ import { TxEffect, TxHash } from '@aztec/stdlib/tx';
740
1186
  }
741
1187
  });
742
1188
  });
743
- describe('findNullifiersIndexesWithBlock', ()=>{
744
- let blocks;
745
- const numBlocks = 10;
746
- const nullifiersPerBlock = new Map();
747
- beforeEach(async ()=>{
748
- blocks = await timesParallel(numBlocks, (index)=>L2Block.random(index + 1, 1));
749
- blocks.forEach((block, blockIndex)=>{
750
- nullifiersPerBlock.set(blockIndex, block.body.txEffects.flatMap((txEffect)=>txEffect.nullifiers));
751
- });
1189
+ describe('pendingChainValidationStatus', ()=>{
1190
+ it('should return undefined when no status is set', async ()=>{
1191
+ const status = await store.getPendingChainValidationStatus();
1192
+ expect(status).toBeUndefined();
752
1193
  });
753
- it('returns wrapped nullifiers with blocks if they exist', async ()=>{
754
- await store.addNullifiers(blocks);
755
- const nullifiersToRetrieve = [
756
- ...nullifiersPerBlock.get(0),
757
- ...nullifiersPerBlock.get(5),
758
- Fr.random()
759
- ];
760
- const blockScopedNullifiers = await store.findNullifiersIndexesWithBlock(10, nullifiersToRetrieve);
761
- expect(blockScopedNullifiers).toHaveLength(nullifiersToRetrieve.length);
762
- const [undefinedNullifier] = blockScopedNullifiers.slice(-1);
763
- const realNullifiers = blockScopedNullifiers.slice(0, -1);
764
- realNullifiers.forEach((blockScopedNullifier, index)=>{
765
- expect(blockScopedNullifier).not.toBeUndefined();
766
- const { data, l2BlockNumber } = blockScopedNullifier;
767
- expect(data).toEqual(expect.any(BigInt));
768
- expect(l2BlockNumber).toEqual(index < MAX_NULLIFIERS_PER_TX ? 1 : 6);
769
- });
770
- expect(undefinedNullifier).toBeUndefined();
771
- });
772
- it('returns wrapped nullifiers filtering by blockNumber', async ()=>{
773
- await store.addNullifiers(blocks);
774
- const nullifiersToRetrieve = [
775
- ...nullifiersPerBlock.get(0),
776
- ...nullifiersPerBlock.get(5)
777
- ];
778
- const blockScopedNullifiers = await store.findNullifiersIndexesWithBlock(5, nullifiersToRetrieve);
779
- expect(blockScopedNullifiers).toHaveLength(nullifiersToRetrieve.length);
780
- const undefinedNullifiers = blockScopedNullifiers.slice(-MAX_NULLIFIERS_PER_TX);
781
- const realNullifiers = blockScopedNullifiers.slice(0, -MAX_NULLIFIERS_PER_TX);
782
- realNullifiers.forEach((blockScopedNullifier)=>{
783
- expect(blockScopedNullifier).not.toBeUndefined();
784
- const { data, l2BlockNumber } = blockScopedNullifier;
785
- expect(data).toEqual(expect.any(BigInt));
786
- expect(l2BlockNumber).toEqual(1);
787
- });
788
- undefinedNullifiers.forEach((undefinedNullifier)=>{
789
- expect(undefinedNullifier).toBeUndefined();
790
- });
1194
+ it('should store and retrieve a valid validation status', async ()=>{
1195
+ const validStatus = {
1196
+ valid: true
1197
+ };
1198
+ await store.setPendingChainValidationStatus(validStatus);
1199
+ const retrievedStatus = await store.getPendingChainValidationStatus();
1200
+ expect(retrievedStatus).toEqual(validStatus);
1201
+ });
1202
+ it('should store and retrieve an invalid validation status with insufficient attestations', async ()=>{
1203
+ const invalidStatus = {
1204
+ valid: false,
1205
+ block: randomBlockInfo(1),
1206
+ committee: [
1207
+ EthAddress.random(),
1208
+ EthAddress.random()
1209
+ ],
1210
+ epoch: 123n,
1211
+ seed: 456n,
1212
+ attestors: [
1213
+ EthAddress.random()
1214
+ ],
1215
+ attestations: [
1216
+ CommitteeAttestation.random()
1217
+ ],
1218
+ reason: 'insufficient-attestations'
1219
+ };
1220
+ await store.setPendingChainValidationStatus(invalidStatus);
1221
+ const retrievedStatus = await store.getPendingChainValidationStatus();
1222
+ expect(retrievedStatus).toEqual(invalidStatus);
1223
+ });
1224
+ it('should store and retrieve an invalid validation status with invalid attestation', async ()=>{
1225
+ const invalidStatus = {
1226
+ valid: false,
1227
+ block: randomBlockInfo(2),
1228
+ committee: [
1229
+ EthAddress.random()
1230
+ ],
1231
+ attestors: [
1232
+ EthAddress.random()
1233
+ ],
1234
+ epoch: 789n,
1235
+ seed: 101n,
1236
+ attestations: [
1237
+ CommitteeAttestation.random()
1238
+ ],
1239
+ reason: 'invalid-attestation',
1240
+ invalidIndex: 5
1241
+ };
1242
+ await store.setPendingChainValidationStatus(invalidStatus);
1243
+ const retrievedStatus = await store.getPendingChainValidationStatus();
1244
+ expect(retrievedStatus).toEqual(invalidStatus);
1245
+ });
1246
+ it('should overwrite existing status when setting a new one', async ()=>{
1247
+ const firstStatus = {
1248
+ valid: true
1249
+ };
1250
+ const secondStatus = {
1251
+ valid: false,
1252
+ block: randomBlockInfo(3),
1253
+ committee: [
1254
+ EthAddress.random()
1255
+ ],
1256
+ epoch: 999n,
1257
+ seed: 888n,
1258
+ attestors: [
1259
+ EthAddress.random()
1260
+ ],
1261
+ attestations: [
1262
+ CommitteeAttestation.random()
1263
+ ],
1264
+ reason: 'insufficient-attestations'
1265
+ };
1266
+ await store.setPendingChainValidationStatus(firstStatus);
1267
+ await store.setPendingChainValidationStatus(secondStatus);
1268
+ const retrievedStatus = await store.getPendingChainValidationStatus();
1269
+ expect(retrievedStatus).toEqual(secondStatus);
1270
+ });
1271
+ it('should handle empty committee and attestations arrays', async ()=>{
1272
+ const statusWithEmptyArrays = {
1273
+ valid: false,
1274
+ block: randomBlockInfo(4),
1275
+ committee: [],
1276
+ epoch: 0n,
1277
+ seed: 0n,
1278
+ attestors: [],
1279
+ attestations: [],
1280
+ reason: 'insufficient-attestations'
1281
+ };
1282
+ await store.setPendingChainValidationStatus(statusWithEmptyArrays);
1283
+ const retrievedStatus = await store.getPendingChainValidationStatus();
1284
+ expect(retrievedStatus).toEqual(statusWithEmptyArrays);
791
1285
  });
792
1286
  });
793
1287
  });