@aztec/pxe 0.70.0 → 0.72.1

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 (50) hide show
  1. package/dest/config/index.d.ts.map +1 -1
  2. package/dest/config/index.js +3 -3
  3. package/dest/database/kv_pxe_database.d.ts +7 -5
  4. package/dest/database/kv_pxe_database.d.ts.map +1 -1
  5. package/dest/database/kv_pxe_database.js +33 -11
  6. package/dest/database/note_dao.d.ts +2 -4
  7. package/dest/database/note_dao.d.ts.map +1 -1
  8. package/dest/database/note_dao.js +3 -7
  9. package/dest/database/outgoing_note_dao.d.ts +2 -4
  10. package/dest/database/outgoing_note_dao.d.ts.map +1 -1
  11. package/dest/database/outgoing_note_dao.js +3 -7
  12. package/dest/database/pxe_database.d.ts +29 -12
  13. package/dest/database/pxe_database.d.ts.map +1 -1
  14. package/dest/database/pxe_database_test_suite.d.ts.map +1 -1
  15. package/dest/database/pxe_database_test_suite.js +162 -67
  16. package/dest/note_decryption_utils/add_public_values_to_payload.js +2 -2
  17. package/dest/pxe_service/pxe_service.d.ts +6 -6
  18. package/dest/pxe_service/pxe_service.d.ts.map +1 -1
  19. package/dest/pxe_service/pxe_service.js +31 -28
  20. package/dest/pxe_service/test/pxe_test_suite.js +9 -9
  21. package/dest/simulator_oracle/index.d.ts +12 -20
  22. package/dest/simulator_oracle/index.d.ts.map +1 -1
  23. package/dest/simulator_oracle/index.js +123 -69
  24. package/package.json +15 -15
  25. package/src/config/index.ts +2 -1
  26. package/src/database/kv_pxe_database.ts +46 -12
  27. package/src/database/note_dao.ts +6 -34
  28. package/src/database/outgoing_note_dao.ts +6 -33
  29. package/src/database/pxe_database.ts +31 -12
  30. package/src/database/pxe_database_test_suite.ts +189 -75
  31. package/src/note_decryption_utils/add_public_values_to_payload.ts +1 -1
  32. package/src/pxe_service/pxe_service.ts +47 -46
  33. package/src/pxe_service/test/pxe_test_suite.ts +8 -8
  34. package/src/simulator_oracle/index.ts +237 -88
  35. package/dest/note_decryption_utils/brute_force_note_info.d.ts +0 -31
  36. package/dest/note_decryption_utils/brute_force_note_info.d.ts.map +0 -1
  37. package/dest/note_decryption_utils/brute_force_note_info.js +0 -54
  38. package/dest/note_decryption_utils/index.d.ts +0 -3
  39. package/dest/note_decryption_utils/index.d.ts.map +0 -1
  40. package/dest/note_decryption_utils/index.js +0 -2
  41. package/dest/note_decryption_utils/produce_note_daos.d.ts +0 -28
  42. package/dest/note_decryption_utils/produce_note_daos.d.ts.map +0 -1
  43. package/dest/note_decryption_utils/produce_note_daos.js +0 -33
  44. package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts +0 -8
  45. package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts.map +0 -1
  46. package/dest/note_decryption_utils/produce_note_daos_for_key.js +0 -17
  47. package/src/note_decryption_utils/brute_force_note_info.ts +0 -90
  48. package/src/note_decryption_utils/index.ts +0 -2
  49. package/src/note_decryption_utils/produce_note_daos.ts +0 -69
  50. package/src/note_decryption_utils/produce_note_daos_for_key.ts +0 -59
@@ -1,12 +1,10 @@
1
- import { type L1NotePayload, Note, TxHash, randomTxHash } from '@aztec/circuit-types';
1
+ import { Note, TxHash, randomTxHash } from '@aztec/circuit-types';
2
2
  import { AztecAddress, Fr, Point, type PublicKey } from '@aztec/circuits.js';
3
3
  import { NoteSelector } from '@aztec/foundation/abi';
4
4
  import { toBigIntBE } from '@aztec/foundation/bigint-buffer';
5
5
  import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
6
6
  import { type NoteData } from '@aztec/simulator/client';
7
7
 
8
- import { type NoteInfo } from '../note_decryption_utils/index.js';
9
-
10
8
  /**
11
9
  * A Note Data Access Object, representing a note that was comitted to the note hash tree, holding all of the
12
10
  * information required to use it during execution and manage its state.
@@ -61,32 +59,6 @@ export class NoteDao implements NoteData {
61
59
  public noteTypeId: NoteSelector,
62
60
  ) {}
63
61
 
64
- static fromPayloadAndNoteInfo(
65
- note: Note,
66
- payload: L1NotePayload,
67
- noteInfo: NoteInfo,
68
- l2BlockNumber: number,
69
- l2BlockHash: string,
70
- dataStartIndexForTx: number,
71
- addressPoint: PublicKey,
72
- ) {
73
- const noteHashIndexInTheWholeTree = BigInt(dataStartIndexForTx + noteInfo.noteHashIndex);
74
- return new NoteDao(
75
- note,
76
- payload.contractAddress,
77
- payload.storageSlot,
78
- noteInfo.nonce,
79
- noteInfo.noteHash,
80
- noteInfo.siloedNullifier,
81
- noteInfo.txHash,
82
- l2BlockNumber,
83
- l2BlockHash,
84
- noteHashIndexInTheWholeTree,
85
- addressPoint,
86
- payload.noteTypeId,
87
- );
88
- }
89
-
90
62
  toBuffer(): Buffer {
91
63
  return serializeToBuffer([
92
64
  this.note,
@@ -155,9 +127,9 @@ export class NoteDao implements NoteData {
155
127
  return noteSize + AztecAddress.SIZE_IN_BYTES + Fr.SIZE_IN_BYTES * 4 + TxHash.SIZE + Point.SIZE_IN_BYTES + indexSize;
156
128
  }
157
129
 
158
- static random({
130
+ static async random({
159
131
  note = Note.random(),
160
- contractAddress = AztecAddress.random(),
132
+ contractAddress = undefined,
161
133
  storageSlot = Fr.random(),
162
134
  nonce = Fr.random(),
163
135
  noteHash = Fr.random(),
@@ -166,12 +138,12 @@ export class NoteDao implements NoteData {
166
138
  l2BlockNumber = Math.floor(Math.random() * 1000),
167
139
  l2BlockHash = Fr.random().toString(),
168
140
  index = Fr.random().toBigInt(),
169
- addressPoint = Point.random(),
141
+ addressPoint = undefined,
170
142
  noteTypeId = NoteSelector.random(),
171
143
  }: Partial<NoteDao> = {}) {
172
144
  return new NoteDao(
173
145
  note,
174
- contractAddress,
146
+ contractAddress ?? (await AztecAddress.random()),
175
147
  storageSlot,
176
148
  nonce,
177
149
  noteHash,
@@ -180,7 +152,7 @@ export class NoteDao implements NoteData {
180
152
  l2BlockNumber,
181
153
  l2BlockHash,
182
154
  index,
183
- addressPoint,
155
+ addressPoint ?? (await Point.random()),
184
156
  noteTypeId,
185
157
  );
186
158
  }
@@ -1,11 +1,9 @@
1
- import { type L1NotePayload, Note, TxHash, randomTxHash } from '@aztec/circuit-types';
1
+ import { Note, TxHash, randomTxHash } from '@aztec/circuit-types';
2
2
  import { AztecAddress, Fr, Point, type PublicKey } from '@aztec/circuits.js';
3
3
  import { NoteSelector } from '@aztec/foundation/abi';
4
4
  import { toBigIntBE } from '@aztec/foundation/bigint-buffer';
5
5
  import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
6
6
 
7
- import { type NoteInfo } from '../note_decryption_utils/index.js';
8
-
9
7
  /**
10
8
  * A note with contextual data which was decrypted as outgoing.
11
9
  */
@@ -38,31 +36,6 @@ export class OutgoingNoteDao {
38
36
  public ovpkM: PublicKey,
39
37
  ) {}
40
38
 
41
- static fromPayloadAndNoteInfo(
42
- note: Note,
43
- payload: L1NotePayload,
44
- noteInfo: NoteInfo,
45
- l2BlockNumber: number,
46
- l2BlockHash: string,
47
- dataStartIndexForTx: number,
48
- ovpkM: PublicKey,
49
- ) {
50
- const noteHashIndexInTheWholeTree = BigInt(dataStartIndexForTx + noteInfo.noteHashIndex);
51
- return new OutgoingNoteDao(
52
- note,
53
- payload.contractAddress,
54
- payload.storageSlot,
55
- payload.noteTypeId,
56
- noteInfo.txHash,
57
- l2BlockNumber,
58
- l2BlockHash,
59
- noteInfo.nonce,
60
- noteInfo.noteHash,
61
- noteHashIndexInTheWholeTree,
62
- ovpkM,
63
- );
64
- }
65
-
66
39
  toBuffer(): Buffer {
67
40
  return serializeToBuffer([
68
41
  this.note,
@@ -126,9 +99,9 @@ export class OutgoingNoteDao {
126
99
  return noteSize + AztecAddress.SIZE_IN_BYTES + Fr.SIZE_IN_BYTES * 2 + TxHash.SIZE + Point.SIZE_IN_BYTES;
127
100
  }
128
101
 
129
- static random({
102
+ static async random({
130
103
  note = Note.random(),
131
- contractAddress = AztecAddress.random(),
104
+ contractAddress = undefined,
132
105
  txHash = randomTxHash(),
133
106
  storageSlot = Fr.random(),
134
107
  noteTypeId = NoteSelector.random(),
@@ -137,11 +110,11 @@ export class OutgoingNoteDao {
137
110
  l2BlockHash = Fr.random().toString(),
138
111
  noteHash = Fr.random(),
139
112
  index = Fr.random().toBigInt(),
140
- ovpkM = Point.random(),
113
+ ovpkM = undefined,
141
114
  }: Partial<OutgoingNoteDao> = {}) {
142
115
  return new OutgoingNoteDao(
143
116
  note,
144
- contractAddress,
117
+ contractAddress ?? (await AztecAddress.random()),
145
118
  storageSlot,
146
119
  noteTypeId,
147
120
  txHash,
@@ -150,7 +123,7 @@ export class OutgoingNoteDao {
150
123
  nonce,
151
124
  noteHash,
152
125
  index,
153
- ovpkM,
126
+ ovpkM ?? (await Point.random()),
154
127
  );
155
128
  }
156
129
  }
@@ -215,20 +215,39 @@ export interface PxeDatabase extends ContractArtifactDatabase, ContractInstanceD
215
215
  resetNoteSyncData(): Promise<void>;
216
216
 
217
217
  /**
218
- * Used by contracts during execution to store arbitrary data in the local PXE database. The data is siloed/scoped
219
- * to a specific `contract`.
220
- * @param contract - An address of a contract that is requesting to store the data.
221
- * @param key - A field element representing the key to store the data under.
222
- * @param values - An array of field elements representing the data to store.
218
+ * Stores arbitrary information in a per-contract non-volatile database, which can later be retrieved with `dbLoad`.
219
+ * If data was already stored at this slot, it is overwrriten.
220
+ * @param contractAddress - The contract address to scope the data under.
221
+ * @param slot - The slot in the database in which to store the value. Slots need not be contiguous.
222
+ * @param values - The data to store.
223
223
  */
224
- store(contract: AztecAddress, key: Fr, values: Fr[]): Promise<void>;
224
+ dbStore(contractAddress: AztecAddress, slot: Fr, values: Fr[]): Promise<void>;
225
225
 
226
226
  /**
227
- * Used by contracts during execution to load arbitrary data from the local PXE database. The data is siloed/scoped
228
- * to a specific `contract`.
229
- * @param contract - An address of a contract that is requesting to load the data.
230
- * @param key - A field element representing the key under which to load the data..
231
- * @returns An array of field elements representing the stored data or `null` if no data is stored under the key.
227
+ * Returns data previously stored via `dbStore` in the per-contract non-volatile database.
228
+ * @param contractAddress - The contract address under which the data is scoped.
229
+ * @param slot - The slot in the database to read.
230
+ * @returns The stored data or `null` if no data is stored under the slot.
232
231
  */
233
- load(contract: AztecAddress, key: Fr): Promise<Fr[] | null>;
232
+ dbLoad(contractAddress: AztecAddress, slot: Fr): Promise<Fr[] | null>;
233
+
234
+ /**
235
+ * Deletes data in the per-contract non-volatile database. Does nothing if no data was present.
236
+ * @param contractAddress - The contract address under which the data is scoped.
237
+ * @param slot - The slot in the database to delete.
238
+ */
239
+ dbDelete(contractAddress: AztecAddress, slot: Fr): Promise<void>;
240
+
241
+ /**
242
+ * Copies a number of contiguous entries in the per-contract non-volatile database. This allows for efficient data
243
+ * structures by avoiding repeated calls to `dbLoad` and `dbStore`.
244
+ * Supports overlapping source and destination regions (which will result in the overlapped source values being
245
+ * overwritten). All copied slots must exist in the database (i.e. have been stored and not deleted)
246
+ *
247
+ * @param contractAddress - The contract address under which the data is scoped.
248
+ * @param srcSlot - The first slot to copy from.
249
+ * @param dstSlot - The first slot to copy to.
250
+ * @param numEntries - The number of entries to copy.
251
+ */
252
+ dbCopy(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise<void>;
234
253
  }
@@ -8,11 +8,14 @@ import {
8
8
  } from '@aztec/circuits.js';
9
9
  import { makeHeader } from '@aztec/circuits.js/testing';
10
10
  import { FunctionType } from '@aztec/foundation/abi';
11
+ import { timesParallel } from '@aztec/foundation/collection';
11
12
  import { randomInt } from '@aztec/foundation/crypto';
12
13
  import { Fr, Point } from '@aztec/foundation/fields';
13
14
  import { BenchmarkingContractArtifact } from '@aztec/noir-contracts.js/Benchmarking';
14
15
  import { TestContractArtifact } from '@aztec/noir-contracts.js/Test';
15
16
 
17
+ import times from 'lodash.times';
18
+
16
19
  import { NoteDao } from './note_dao.js';
17
20
  import { type PxeDatabase } from './pxe_database.js';
18
21
 
@@ -80,53 +83,62 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
80
83
  let storageSlots: Fr[];
81
84
  let notes: NoteDao[];
82
85
 
83
- const filteringTests: [() => NotesFilter, () => NoteDao[]][] = [
84
- [() => ({}), () => notes],
86
+ const filteringTests: [() => Promise<NotesFilter>, () => Promise<NoteDao[]>][] = [
87
+ [() => Promise.resolve({}), () => Promise.resolve(notes)],
85
88
 
86
89
  [
87
- () => ({ contractAddress: contractAddresses[0] }),
88
- () => notes.filter(note => note.contractAddress.equals(contractAddresses[0])),
90
+ () => Promise.resolve({ contractAddress: contractAddresses[0] }),
91
+ () => Promise.resolve(notes.filter(note => note.contractAddress.equals(contractAddresses[0]))),
89
92
  ],
90
- [() => ({ contractAddress: AztecAddress.random() }), () => []],
93
+ [async () => ({ contractAddress: await AztecAddress.random() }), () => Promise.resolve([])],
91
94
 
92
95
  [
93
- () => ({ storageSlot: storageSlots[0] }),
94
- () => notes.filter(note => note.storageSlot.equals(storageSlots[0])),
96
+ () => Promise.resolve({ storageSlot: storageSlots[0] }),
97
+ () => Promise.resolve(notes.filter(note => note.storageSlot.equals(storageSlots[0]))),
95
98
  ],
96
- [() => ({ storageSlot: Fr.random() }), () => []],
99
+ [() => Promise.resolve({ storageSlot: Fr.random() }), () => Promise.resolve([])],
97
100
 
98
- [() => ({ txHash: notes[0].txHash }), () => [notes[0]]],
99
- [() => ({ txHash: randomTxHash() }), () => []],
101
+ [() => Promise.resolve({ txHash: notes[0].txHash }), () => Promise.resolve([notes[0]])],
102
+ [() => Promise.resolve({ txHash: randomTxHash() }), () => Promise.resolve([])],
100
103
 
101
104
  [
102
- () => ({ owner: owners[0].address }),
103
- () => notes.filter(note => note.addressPoint.equals(owners[0].address.toAddressPoint())),
105
+ () => Promise.resolve({ owner: owners[0].address }),
106
+ async () => {
107
+ const ownerAddressPoint = await owners[0].address.toAddressPoint();
108
+ return notes.filter(note => note.addressPoint.equals(ownerAddressPoint));
109
+ },
104
110
  ],
105
111
 
106
112
  [
107
- () => ({ contractAddress: contractAddresses[0], storageSlot: storageSlots[0] }),
113
+ () => Promise.resolve({ contractAddress: contractAddresses[0], storageSlot: storageSlots[0] }),
108
114
  () =>
109
- notes.filter(
110
- note => note.contractAddress.equals(contractAddresses[0]) && note.storageSlot.equals(storageSlots[0]),
115
+ Promise.resolve(
116
+ notes.filter(
117
+ note => note.contractAddress.equals(contractAddresses[0]) && note.storageSlot.equals(storageSlots[0]),
118
+ ),
111
119
  ),
112
120
  ],
113
- [() => ({ contractAddress: contractAddresses[0], storageSlot: storageSlots[1] }), () => []],
121
+ [
122
+ () => Promise.resolve({ contractAddress: contractAddresses[0], storageSlot: storageSlots[1] }),
123
+ () => Promise.resolve([]),
124
+ ],
114
125
  ];
115
126
 
116
127
  beforeEach(async () => {
117
- owners = Array.from({ length: 2 }).map(() => CompleteAddress.random());
118
- contractAddresses = Array.from({ length: 2 }).map(() => AztecAddress.random());
119
- storageSlots = Array.from({ length: 2 }).map(() => Fr.random());
128
+ owners = await timesParallel(2, () => CompleteAddress.random());
129
+ contractAddresses = await timesParallel(2, () => AztecAddress.random());
130
+ storageSlots = times(2, () => Fr.random());
120
131
 
121
- notes = Array.from({ length: 10 }).map((_, i) =>
122
- NoteDao.random({
132
+ notes = await timesParallel(10, async i => {
133
+ const addressPoint = await owners[i % owners.length].address.toAddressPoint();
134
+ return NoteDao.random({
123
135
  contractAddress: contractAddresses[i % contractAddresses.length],
124
136
  storageSlot: storageSlots[i % storageSlots.length],
125
- addressPoint: owners[i % owners.length].address.toAddressPoint(),
137
+ addressPoint,
126
138
  index: BigInt(i),
127
139
  l2BlockNumber: i,
128
- }),
129
- );
140
+ });
141
+ });
130
142
 
131
143
  for (const owner of owners) {
132
144
  await database.addCompleteAddress(owner);
@@ -135,9 +147,9 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
135
147
 
136
148
  it.each(filteringTests)('stores notes in bulk and retrieves notes', async (getFilter, getExpected) => {
137
149
  await database.addNotes(notes);
138
- const returnedNotes = await database.getNotes(getFilter());
139
-
140
- expect(returnedNotes.sort()).toEqual(getExpected().sort());
150
+ const returnedNotes = await database.getNotes(await getFilter());
151
+ const expected = await getExpected();
152
+ expect(returnedNotes.sort()).toEqual(expected.sort());
141
153
  });
142
154
 
143
155
  it.each(filteringTests)('stores notes one by one and retrieves notes', async (getFilter, getExpected) => {
@@ -145,9 +157,10 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
145
157
  await database.addNote(note);
146
158
  }
147
159
 
148
- const returnedNotes = await database.getNotes(getFilter());
160
+ const returnedNotes = await database.getNotes(await getFilter());
149
161
 
150
- expect(returnedNotes.sort()).toEqual(getExpected().sort());
162
+ const expected = await getExpected();
163
+ expect(returnedNotes.sort()).toEqual(expected.sort());
151
164
  });
152
165
 
153
166
  it.each(filteringTests)('retrieves nullified notes', async (getFilter, getExpected) => {
@@ -155,26 +168,25 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
155
168
 
156
169
  // Nullify all notes and use the same filter as other test cases
157
170
  for (const owner of owners) {
158
- const notesToNullify = notes.filter(note => note.addressPoint.equals(owner.address.toAddressPoint()));
171
+ const ownerAddressPoint = await owner.address.toAddressPoint();
172
+ const notesToNullify = notes.filter(note => note.addressPoint.equals(ownerAddressPoint));
159
173
  const nullifiers = notesToNullify.map(note => ({
160
174
  data: note.siloedNullifier,
161
175
  l2BlockNumber: note.l2BlockNumber,
162
176
  l2BlockHash: note.l2BlockHash,
163
177
  }));
164
- await expect(database.removeNullifiedNotes(nullifiers, owner.address.toAddressPoint())).resolves.toEqual(
165
- notesToNullify,
166
- );
178
+ await expect(database.removeNullifiedNotes(nullifiers, ownerAddressPoint)).resolves.toEqual(notesToNullify);
167
179
  }
168
-
169
- await expect(database.getNotes({ ...getFilter(), status: NoteStatus.ACTIVE_OR_NULLIFIED })).resolves.toEqual(
170
- getExpected(),
171
- );
180
+ const filter = await getFilter();
181
+ const returnedNotes = await database.getNotes({ ...filter, status: NoteStatus.ACTIVE_OR_NULLIFIED });
182
+ const expected = await getExpected();
183
+ expect(returnedNotes.sort()).toEqual(expected.sort());
172
184
  });
173
185
 
174
186
  it('skips nullified notes by default or when requesting active', async () => {
175
187
  await database.addNotes(notes);
176
-
177
- const notesToNullify = notes.filter(note => note.addressPoint.equals(owners[0].address.toAddressPoint()));
188
+ const ownerAddressPoint = await owners[0].address.toAddressPoint();
189
+ const notesToNullify = notes.filter(note => note.addressPoint.equals(ownerAddressPoint));
178
190
  const nullifiers = notesToNullify.map(note => ({
179
191
  data: note.siloedNullifier,
180
192
  l2BlockNumber: note.l2BlockNumber,
@@ -194,8 +206,9 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
194
206
  it('handles note unnullification', async () => {
195
207
  await database.setHeader(makeHeader(randomInt(1000), 100, 0 /** slot number */));
196
208
  await database.addNotes(notes);
209
+ const ownerAddressPoint = await owners[0].address.toAddressPoint();
197
210
 
198
- const notesToNullify = notes.filter(note => note.addressPoint.equals(owners[0].address.toAddressPoint()));
211
+ const notesToNullify = notes.filter(note => note.addressPoint.equals(ownerAddressPoint));
199
212
  const nullifiers = notesToNullify.map(note => ({
200
213
  data: note.siloedNullifier,
201
214
  l2BlockNumber: 99,
@@ -213,8 +226,9 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
213
226
 
214
227
  it('returns active and nullified notes when requesting either', async () => {
215
228
  await database.addNotes(notes);
229
+ const ownerAddressPoint = await owners[0].address.toAddressPoint();
216
230
 
217
- const notesToNullify = notes.filter(note => note.addressPoint.equals(owners[0].address.toAddressPoint()));
231
+ const notesToNullify = notes.filter(note => note.addressPoint.equals(ownerAddressPoint));
218
232
  const nullifiers = notesToNullify.map(note => ({
219
233
  data: note.siloedNullifier,
220
234
  l2BlockNumber: note.l2BlockNumber,
@@ -275,7 +289,7 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
275
289
  scopes: [owners[1].address],
276
290
  }),
277
291
  ).resolves.toEqual([notes[0]]);
278
-
292
+ const ownerAddressPoint = await owners[0].address.toAddressPoint();
279
293
  await expect(
280
294
  database.removeNullifiedNotes(
281
295
  [
@@ -285,7 +299,7 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
285
299
  l2BlockNumber: notes[0].l2BlockNumber,
286
300
  },
287
301
  ],
288
- owners[0].address.toAddressPoint(),
302
+ ownerAddressPoint,
289
303
  ),
290
304
  ).resolves.toEqual([notes[0]]);
291
305
 
@@ -325,22 +339,22 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
325
339
 
326
340
  describe('addresses', () => {
327
341
  it('stores and retrieves addresses', async () => {
328
- const address = CompleteAddress.random();
342
+ const address = await CompleteAddress.random();
329
343
  await expect(database.addCompleteAddress(address)).resolves.toBe(true);
330
344
  await expect(database.getCompleteAddress(address.address)).resolves.toEqual(address);
331
345
  });
332
346
 
333
347
  it('silently ignores an address it already knows about', async () => {
334
- const address = CompleteAddress.random();
348
+ const address = await CompleteAddress.random();
335
349
  await expect(database.addCompleteAddress(address)).resolves.toBe(true);
336
350
  await expect(database.addCompleteAddress(address)).resolves.toBe(false);
337
351
  });
338
352
 
339
353
  it.skip('refuses to overwrite an address with a different public key', async () => {
340
- const address = CompleteAddress.random();
341
- const otherAddress = new CompleteAddress(
354
+ const address = await CompleteAddress.random();
355
+ const otherAddress = await CompleteAddress.create(
342
356
  address.address,
343
- new PublicKeys(Point.random(), Point.random(), Point.random(), Point.random()),
357
+ new PublicKeys(await Point.random(), await Point.random(), await Point.random(), await Point.random()),
344
358
  address.partialAddress,
345
359
  );
346
360
 
@@ -349,7 +363,7 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
349
363
  });
350
364
 
351
365
  it('returns all addresses', async () => {
352
- const addresses = Array.from({ length: 10 }).map(() => CompleteAddress.random());
366
+ const addresses = await timesParallel(10, () => CompleteAddress.random());
353
367
  for (const address of addresses) {
354
368
  await database.addCompleteAddress(address);
355
369
  }
@@ -359,7 +373,7 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
359
373
  });
360
374
 
361
375
  it('returns a single address', async () => {
362
- const addresses = Array.from({ length: 10 }).map(() => CompleteAddress.random());
376
+ const addresses = await timesParallel(10, () => CompleteAddress.random());
363
377
  for (const address of addresses) {
364
378
  await database.addCompleteAddress(address);
365
379
  }
@@ -373,7 +387,8 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
373
387
  });
374
388
 
375
389
  it("returns undefined if it doesn't have an address", async () => {
376
- expect(await database.getCompleteAddress(CompleteAddress.random().address)).toBeUndefined();
390
+ const completeAddress = await CompleteAddress.random();
391
+ expect(await database.getCompleteAddress(completeAddress.address)).toBeUndefined();
377
392
  });
378
393
  });
379
394
 
@@ -399,72 +414,171 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
399
414
  });
400
415
 
401
416
  it('stores a contract instance', async () => {
402
- const address = AztecAddress.random();
403
- const instance = SerializableContractInstance.random().withAddress(address);
417
+ const address = await AztecAddress.random();
418
+ const instance = (await SerializableContractInstance.random()).withAddress(address);
404
419
  await database.addContractInstance(instance);
405
420
  await expect(database.getContractInstance(address)).resolves.toEqual(instance);
406
421
  });
407
422
  });
408
423
 
409
- describe('contract store', () => {
424
+ describe('contract non-volatile database', () => {
410
425
  let contract: AztecAddress;
411
426
 
412
- beforeEach(() => {
427
+ beforeEach(async () => {
413
428
  // Setup mock contract address
414
- contract = AztecAddress.random();
429
+ contract = await AztecAddress.random();
415
430
  });
416
431
 
417
432
  it('stores and loads a single value', async () => {
418
- const key = new Fr(1);
433
+ const slot = new Fr(1);
419
434
  const values = [new Fr(42)];
420
435
 
421
- await database.store(contract, key, values);
422
- const result = await database.load(contract, key);
436
+ await database.dbStore(contract, slot, values);
437
+ const result = await database.dbLoad(contract, slot);
423
438
  expect(result).toEqual(values);
424
439
  });
425
440
 
426
441
  it('stores and loads multiple values', async () => {
427
- const key = new Fr(1);
442
+ const slot = new Fr(1);
428
443
  const values = [new Fr(42), new Fr(43), new Fr(44)];
429
444
 
430
- await database.store(contract, key, values);
431
- const result = await database.load(contract, key);
445
+ await database.dbStore(contract, slot, values);
446
+ const result = await database.dbLoad(contract, slot);
432
447
  expect(result).toEqual(values);
433
448
  });
434
449
 
435
450
  it('overwrites existing values', async () => {
436
- const key = new Fr(1);
451
+ const slot = new Fr(1);
437
452
  const initialValues = [new Fr(42)];
438
453
  const newValues = [new Fr(100)];
439
454
 
440
- await database.store(contract, key, initialValues);
441
- await database.store(contract, key, newValues);
455
+ await database.dbStore(contract, slot, initialValues);
456
+ await database.dbStore(contract, slot, newValues);
442
457
 
443
- const result = await database.load(contract, key);
458
+ const result = await database.dbLoad(contract, slot);
444
459
  expect(result).toEqual(newValues);
445
460
  });
446
461
 
447
462
  it('stores values for different contracts independently', async () => {
448
- const anotherContract = AztecAddress.random();
449
- const key = new Fr(1);
463
+ const anotherContract = await AztecAddress.random();
464
+ const slot = new Fr(1);
450
465
  const values1 = [new Fr(42)];
451
466
  const values2 = [new Fr(100)];
452
467
 
453
- await database.store(contract, key, values1);
454
- await database.store(anotherContract, key, values2);
468
+ await database.dbStore(contract, slot, values1);
469
+ await database.dbStore(anotherContract, slot, values2);
455
470
 
456
- const result1 = await database.load(contract, key);
457
- const result2 = await database.load(anotherContract, key);
471
+ const result1 = await database.dbLoad(contract, slot);
472
+ const result2 = await database.dbLoad(anotherContract, slot);
458
473
 
459
474
  expect(result1).toEqual(values1);
460
475
  expect(result2).toEqual(values2);
461
476
  });
462
477
 
463
- it('returns null for non-existent keys', async () => {
464
- const key = Fr.random();
465
- const result = await database.load(contract, key);
478
+ it('returns null for non-existent slots', async () => {
479
+ const slot = Fr.random();
480
+ const result = await database.dbLoad(contract, slot);
466
481
  expect(result).toBeNull();
467
482
  });
483
+
484
+ it('deletes a slot', async () => {
485
+ const slot = new Fr(1);
486
+ const values = [new Fr(42)];
487
+
488
+ await database.dbStore(contract, slot, values);
489
+ await database.dbDelete(contract, slot);
490
+
491
+ expect(await database.dbLoad(contract, slot)).toBeNull();
492
+ });
493
+
494
+ it('deletes an empty slot', async () => {
495
+ const slot = new Fr(1);
496
+ await database.dbDelete(contract, slot);
497
+
498
+ expect(await database.dbLoad(contract, slot)).toBeNull();
499
+ });
500
+
501
+ it('copies a single value', async () => {
502
+ const slot = new Fr(1);
503
+ const values = [new Fr(42)];
504
+
505
+ await database.dbStore(contract, slot, values);
506
+
507
+ const dstSlot = new Fr(5);
508
+ await database.dbCopy(contract, slot, dstSlot, 1);
509
+
510
+ expect(await database.dbLoad(contract, dstSlot)).toEqual(values);
511
+ });
512
+
513
+ it('copies multiple non-overlapping values', async () => {
514
+ const src = new Fr(1);
515
+ const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]];
516
+
517
+ await database.dbStore(contract, src, valuesArray[0]);
518
+ await database.dbStore(contract, src.add(new Fr(1)), valuesArray[1]);
519
+ await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]);
520
+
521
+ const dst = new Fr(5);
522
+ await database.dbCopy(contract, src, dst, 3);
523
+
524
+ expect(await database.dbLoad(contract, dst)).toEqual(valuesArray[0]);
525
+ expect(await database.dbLoad(contract, dst.add(new Fr(1)))).toEqual(valuesArray[1]);
526
+ expect(await database.dbLoad(contract, dst.add(new Fr(2)))).toEqual(valuesArray[2]);
527
+ });
528
+
529
+ it('copies overlapping values with src ahead', async () => {
530
+ const src = new Fr(1);
531
+ const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]];
532
+
533
+ await database.dbStore(contract, src, valuesArray[0]);
534
+ await database.dbStore(contract, src.add(new Fr(1)), valuesArray[1]);
535
+ await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]);
536
+
537
+ const dst = new Fr(2);
538
+ await database.dbCopy(contract, src, dst, 3);
539
+
540
+ expect(await database.dbLoad(contract, dst)).toEqual(valuesArray[0]);
541
+ expect(await database.dbLoad(contract, dst.add(new Fr(1)))).toEqual(valuesArray[1]);
542
+ expect(await database.dbLoad(contract, dst.add(new Fr(2)))).toEqual(valuesArray[2]);
543
+
544
+ // Slots 2 and 3 (src[1] and src[2]) should have been overwritten since they are also dst[0] and dst[1]
545
+ expect(await database.dbLoad(contract, src)).toEqual(valuesArray[0]); // src[0] (unchanged)
546
+ expect(await database.dbLoad(contract, src.add(new Fr(1)))).toEqual(valuesArray[0]); // dst[0]
547
+ expect(await database.dbLoad(contract, src.add(new Fr(2)))).toEqual(valuesArray[1]); // dst[1]
548
+ });
549
+
550
+ it('copies overlapping values with dst ahead', async () => {
551
+ const src = new Fr(5);
552
+ const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]];
553
+
554
+ await database.dbStore(contract, src, valuesArray[0]);
555
+ await database.dbStore(contract, src.add(new Fr(1)), valuesArray[1]);
556
+ await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]);
557
+
558
+ const dst = new Fr(4);
559
+ await database.dbCopy(contract, src, dst, 3);
560
+
561
+ expect(await database.dbLoad(contract, dst)).toEqual(valuesArray[0]);
562
+ expect(await database.dbLoad(contract, dst.add(new Fr(1)))).toEqual(valuesArray[1]);
563
+ expect(await database.dbLoad(contract, dst.add(new Fr(2)))).toEqual(valuesArray[2]);
564
+
565
+ // Slots 5 and 6 (src[0] and src[1]) should have been overwritten since they are also dst[1] and dst[2]
566
+ expect(await database.dbLoad(contract, src)).toEqual(valuesArray[1]); // dst[1]
567
+ expect(await database.dbLoad(contract, src.add(new Fr(1)))).toEqual(valuesArray[2]); // dst[2]
568
+ expect(await database.dbLoad(contract, src.add(new Fr(2)))).toEqual(valuesArray[2]); // src[2] (unchanged)
569
+ });
570
+
571
+ it('copying fails if any value is empty', async () => {
572
+ const src = new Fr(1);
573
+ const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]];
574
+
575
+ await database.dbStore(contract, src, valuesArray[0]);
576
+ // We skip src[1]
577
+ await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]);
578
+
579
+ const dst = new Fr(5);
580
+ await expect(database.dbCopy(contract, src, dst, 3)).rejects.toThrow('Attempted to copy empty slot');
581
+ });
468
582
  });
469
583
  });
470
584
  }