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