@aztec/simulator 0.24.0 → 0.26.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 (129) hide show
  1. package/dest/acvm/deserialize.d.ts +5 -0
  2. package/dest/acvm/deserialize.d.ts.map +1 -1
  3. package/dest/acvm/deserialize.js +8 -1
  4. package/dest/acvm/oracle/oracle.d.ts +5 -4
  5. package/dest/acvm/oracle/oracle.d.ts.map +1 -1
  6. package/dest/acvm/oracle/oracle.js +24 -11
  7. package/dest/acvm/oracle/typed_oracle.d.ts +7 -9
  8. package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
  9. package/dest/acvm/oracle/typed_oracle.js +9 -9
  10. package/dest/avm/avm_context.d.ts +4 -4
  11. package/dest/avm/avm_context.d.ts.map +1 -1
  12. package/dest/avm/avm_context.js +6 -6
  13. package/dest/avm/avm_memory_types.d.ts +11 -2
  14. package/dest/avm/avm_memory_types.d.ts.map +1 -1
  15. package/dest/avm/avm_memory_types.js +11 -1
  16. package/dest/avm/avm_simulator.d.ts +6 -4
  17. package/dest/avm/avm_simulator.d.ts.map +1 -1
  18. package/dest/avm/avm_simulator.js +17 -18
  19. package/dest/avm/fixtures/index.d.ts +17 -5
  20. package/dest/avm/fixtures/index.d.ts.map +1 -1
  21. package/dest/avm/fixtures/index.js +19 -8
  22. package/dest/avm/journal/host_storage.d.ts.map +1 -1
  23. package/dest/avm/journal/host_storage.js +1 -1
  24. package/dest/avm/journal/journal.d.ts +78 -50
  25. package/dest/avm/journal/journal.d.ts.map +1 -1
  26. package/dest/avm/journal/journal.js +125 -169
  27. package/dest/avm/journal/nullifiers.d.ts +85 -0
  28. package/dest/avm/journal/nullifiers.d.ts.map +1 -0
  29. package/dest/avm/journal/nullifiers.js +147 -0
  30. package/dest/avm/journal/public_storage.d.ts +88 -0
  31. package/dest/avm/journal/public_storage.d.ts.map +1 -0
  32. package/dest/avm/journal/public_storage.js +135 -0
  33. package/dest/avm/journal/trace.d.ts +43 -0
  34. package/dest/avm/journal/trace.d.ts.map +1 -0
  35. package/dest/avm/journal/trace.js +204 -0
  36. package/dest/avm/journal/trace_types.d.ts +26 -0
  37. package/dest/avm/journal/trace_types.d.ts.map +1 -0
  38. package/dest/avm/journal/trace_types.js +6 -0
  39. package/dest/avm/opcodes/accrued_substate.d.ts +37 -4
  40. package/dest/avm/opcodes/accrued_substate.d.ts.map +1 -1
  41. package/dest/avm/opcodes/accrued_substate.js +109 -12
  42. package/dest/avm/opcodes/comparators.d.ts.map +1 -1
  43. package/dest/avm/opcodes/comparators.js +5 -8
  44. package/dest/avm/opcodes/environment_getters.d.ts +14 -13
  45. package/dest/avm/opcodes/environment_getters.d.ts.map +1 -1
  46. package/dest/avm/opcodes/environment_getters.js +1 -1
  47. package/dest/avm/opcodes/external_calls.js +5 -5
  48. package/dest/avm/opcodes/hashing.d.ts +48 -0
  49. package/dest/avm/opcodes/hashing.d.ts.map +1 -0
  50. package/dest/avm/opcodes/hashing.js +127 -0
  51. package/dest/avm/opcodes/memory.d.ts.map +1 -1
  52. package/dest/avm/opcodes/memory.js +1 -1
  53. package/dest/avm/opcodes/storage.d.ts.map +1 -1
  54. package/dest/avm/opcodes/storage.js +3 -3
  55. package/dest/avm/serialization/bytecode_serialization.d.ts.map +1 -1
  56. package/dest/avm/serialization/bytecode_serialization.js +12 -8
  57. package/dest/avm/serialization/instruction_serialization.d.ts +10 -7
  58. package/dest/avm/serialization/instruction_serialization.d.ts.map +1 -1
  59. package/dest/avm/serialization/instruction_serialization.js +12 -9
  60. package/dest/avm/temporary_executor_migration.d.ts.map +1 -1
  61. package/dest/avm/temporary_executor_migration.js +5 -5
  62. package/dest/client/client_execution_context.d.ts +9 -5
  63. package/dest/client/client_execution_context.d.ts.map +1 -1
  64. package/dest/client/client_execution_context.js +46 -24
  65. package/dest/client/db_oracle.d.ts +7 -0
  66. package/dest/client/db_oracle.d.ts.map +1 -1
  67. package/dest/client/db_oracle.js +1 -1
  68. package/dest/client/execution_note_cache.js +1 -1
  69. package/dest/client/execution_result.d.ts +2 -2
  70. package/dest/client/execution_result.d.ts.map +1 -1
  71. package/dest/client/private_execution.d.ts.map +1 -1
  72. package/dest/client/private_execution.js +4 -4
  73. package/dest/client/simulator.d.ts +1 -1
  74. package/dest/client/simulator.d.ts.map +1 -1
  75. package/dest/client/simulator.js +3 -2
  76. package/dest/client/view_data_oracle.d.ts +9 -2
  77. package/dest/client/view_data_oracle.d.ts.map +1 -1
  78. package/dest/client/view_data_oracle.js +13 -5
  79. package/dest/public/db.d.ts +17 -4
  80. package/dest/public/db.d.ts.map +1 -1
  81. package/dest/public/execution.d.ts +9 -4
  82. package/dest/public/execution.d.ts.map +1 -1
  83. package/dest/public/execution.js +17 -4
  84. package/dest/public/executor.d.ts.map +1 -1
  85. package/dest/public/executor.js +18 -9
  86. package/dest/public/public_execution_context.d.ts +5 -4
  87. package/dest/public/public_execution_context.d.ts.map +1 -1
  88. package/dest/public/public_execution_context.js +23 -12
  89. package/dest/public/state_actions.js +2 -2
  90. package/dest/test/utils.js +4 -4
  91. package/dest/utils.js +2 -3
  92. package/package.json +6 -5
  93. package/src/acvm/deserialize.ts +8 -0
  94. package/src/acvm/oracle/oracle.ts +30 -6
  95. package/src/acvm/oracle/typed_oracle.ts +13 -5
  96. package/src/avm/avm_context.ts +5 -5
  97. package/src/avm/avm_memory_types.ts +18 -3
  98. package/src/avm/avm_simulator.ts +22 -24
  99. package/src/avm/fixtures/index.ts +34 -9
  100. package/src/avm/journal/host_storage.ts +5 -11
  101. package/src/avm/journal/journal.ts +147 -182
  102. package/src/avm/journal/nullifiers.ts +170 -0
  103. package/src/avm/journal/public_storage.ts +149 -0
  104. package/src/avm/journal/trace.ts +223 -0
  105. package/src/avm/journal/trace_types.ts +79 -0
  106. package/src/avm/opcodes/accrued_substate.ts +132 -10
  107. package/src/avm/opcodes/comparators.ts +4 -7
  108. package/src/avm/opcodes/environment_getters.ts +15 -13
  109. package/src/avm/opcodes/external_calls.ts +4 -4
  110. package/src/avm/opcodes/hashing.ts +170 -0
  111. package/src/avm/opcodes/memory.ts +1 -0
  112. package/src/avm/opcodes/storage.ts +5 -2
  113. package/src/avm/serialization/bytecode_serialization.ts +13 -6
  114. package/src/avm/serialization/instruction_serialization.ts +6 -3
  115. package/src/avm/temporary_executor_migration.ts +4 -3
  116. package/src/client/client_execution_context.ts +53 -23
  117. package/src/client/db_oracle.ts +8 -0
  118. package/src/client/execution_note_cache.ts +1 -1
  119. package/src/client/execution_result.ts +2 -2
  120. package/src/client/private_execution.ts +5 -4
  121. package/src/client/simulator.ts +2 -1
  122. package/src/client/view_data_oracle.ts +14 -4
  123. package/src/public/db.ts +19 -4
  124. package/src/public/execution.ts +30 -6
  125. package/src/public/executor.ts +29 -9
  126. package/src/public/public_execution_context.ts +36 -12
  127. package/src/public/state_actions.ts +1 -1
  128. package/src/test/utils.ts +3 -3
  129. package/src/utils.ts +1 -1
@@ -0,0 +1,149 @@
1
+ import { Fr } from '@aztec/foundation/fields';
2
+
3
+ import type { PublicStateDB } from '../../index.js';
4
+
5
+ /**
6
+ * A class to manage public storage reads and writes during a contract call's AVM simulation.
7
+ * Maintains a storage write cache, and ensures that reads fall back to the correct source.
8
+ * When a contract call completes, its storage cache can be merged into its parent's.
9
+ */
10
+ export class PublicStorage {
11
+ /** Cached storage writes. */
12
+ private cache: PublicStorageCache;
13
+ /** Parent's storage cache. Checked on cache-miss. */
14
+ private readonly parentCache: PublicStorageCache | undefined;
15
+ /** Reference to node storage. Checked on parent cache-miss. */
16
+ private readonly hostPublicStorage: PublicStateDB;
17
+
18
+ constructor(hostPublicStorage: PublicStateDB, parent?: PublicStorage) {
19
+ this.hostPublicStorage = hostPublicStorage;
20
+ this.parentCache = parent?.cache;
21
+ this.cache = new PublicStorageCache();
22
+ }
23
+
24
+ /**
25
+ * Get the pending storage.
26
+ */
27
+ public getCache() {
28
+ return this.cache;
29
+ }
30
+
31
+ /**
32
+ * Read a value from storage.
33
+ * 1. Check cache.
34
+ * 2. Check parent's cache.
35
+ * 3. Fall back to the host state.
36
+ * 4. Not found! Value has never been written to before. Flag it as non-existent and return value zero.
37
+ *
38
+ * @param storageAddress - the address of the contract whose storage is being read from
39
+ * @param slot - the slot in the contract's storage being read from
40
+ * @returns exists: whether the slot has EVER been written to before, value: the latest value written to slot, or 0 if never written to before
41
+ */
42
+ public async read(storageAddress: Fr, slot: Fr): Promise<[/*exists=*/ boolean, /*value=*/ Fr]> {
43
+ // First try check this storage cache
44
+ let value = this.cache.read(storageAddress, slot);
45
+ // Then try parent's storage cache (if it exists / written to earlier in this TX)
46
+ if (!value && this.parentCache) {
47
+ value = this.parentCache?.read(storageAddress, slot);
48
+ }
49
+ // Finally try the host's Aztec state (a trip to the database)
50
+ if (!value) {
51
+ value = await this.hostPublicStorage.storageRead(storageAddress, slot);
52
+ }
53
+ // if value is undefined, that means this slot has never been written to!
54
+ const exists = value !== undefined;
55
+ const valueOrZero = exists ? value : Fr.ZERO;
56
+ return Promise.resolve([exists, valueOrZero]);
57
+ }
58
+
59
+ /**
60
+ * Stage a storage write.
61
+ *
62
+ * @param storageAddress - the address of the contract whose storage is being written to
63
+ * @param slot - the slot in the contract's storage being written to
64
+ * @param value - the value being written to the slot
65
+ */
66
+ public write(storageAddress: Fr, key: Fr, value: Fr) {
67
+ this.cache.write(storageAddress, key, value);
68
+ }
69
+
70
+ /**
71
+ * Merges another PublicStorage's cache (pending writes) into this one.
72
+ *
73
+ * @param incomingPublicStorage - the incoming public storage to merge into this instance's
74
+ */
75
+ public acceptAndMerge(incomingPublicStorage: PublicStorage) {
76
+ this.cache.acceptAndMerge(incomingPublicStorage.cache);
77
+ }
78
+ }
79
+
80
+ /**
81
+ * A class to cache writes to public storage during a contract call's AVM simulation.
82
+ * "Writes" update a map, "reads" check that map or return undefined.
83
+ * An instance of this class can merge another instance's staged writes into its own.
84
+ */
85
+ class PublicStorageCache {
86
+ /**
87
+ * Map for staging storage writes.
88
+ * One inner-map per contract storage address,
89
+ * mapping storage slot to latest staged write value.
90
+ */
91
+ public cachePerContract: Map<bigint, Map<bigint, Fr>> = new Map();
92
+ // FIXME: storage ^ should be private, but its value is used in tests for "currentStorageValue"
93
+
94
+ /**
95
+ * Read a staged value from storage, if it has been previously written to.
96
+ *
97
+ * @param storageAddress - the address of the contract whose storage is being read from
98
+ * @param slot - the slot in the contract's storage being read from
99
+ * @returns the latest value written to slot, or undefined if no value has been written
100
+ */
101
+ public read(storageAddress: Fr, slot: Fr): Fr | undefined {
102
+ return this.cachePerContract.get(storageAddress.toBigInt())?.get(slot.toBigInt());
103
+ }
104
+
105
+ /**
106
+ * Stage a storage write.
107
+ *
108
+ * @param storageAddress - the address of the contract whose storage is being written to
109
+ * @param slot - the slot in the contract's storage being written to
110
+ * @param value - the value being written to the slot
111
+ */
112
+ public write(storageAddress: Fr, slot: Fr, value: Fr) {
113
+ let cacheAtContract = this.cachePerContract.get(storageAddress.toBigInt());
114
+ if (!cacheAtContract) {
115
+ // If this contract's storage has no staged modifications, create a new inner map to store them
116
+ cacheAtContract = new Map();
117
+ this.cachePerContract.set(storageAddress.toBigInt(), cacheAtContract);
118
+ }
119
+ cacheAtContract.set(slot.toBigInt(), value);
120
+ }
121
+
122
+ /**
123
+ * Merges another cache's staged writes into this instance's cache.
124
+ *
125
+ * Staged modifications in "incoming" take precedence over those
126
+ * present in "this" as they are assumed to occur after this' writes.
127
+ *
128
+ * In practice, "this" is a parent call's storage cache, and "incoming" is a nested call's.
129
+ *
130
+ * @param incomingStorageCache - the incoming storage write cache to merge into this instance's
131
+ */
132
+ public acceptAndMerge(incomingStorageCache: PublicStorageCache) {
133
+ // Iterate over all incoming contracts with staged writes.
134
+ for (const [incomingAddress, incomingCacheAtContract] of incomingStorageCache.cachePerContract) {
135
+ const thisCacheAtContract = this.cachePerContract.get(incomingAddress);
136
+ if (!thisCacheAtContract) {
137
+ // The contract has no storage writes staged here
138
+ // so just accept the incoming cache as-is for this contract.
139
+ this.cachePerContract.set(incomingAddress, incomingCacheAtContract);
140
+ } else {
141
+ // "Incoming" and "this" both have staged writes for this contract.
142
+ // Merge in incoming staged writes, giving them precedence over this'.
143
+ for (const [slot, value] of incomingCacheAtContract) {
144
+ thisCacheAtContract.set(slot, value);
145
+ }
146
+ }
147
+ }
148
+ }
149
+ }
@@ -0,0 +1,223 @@
1
+ import { Fr } from '@aztec/foundation/fields';
2
+
3
+ import { TracedL1toL2MessageCheck, TracedNoteHashCheck, TracedNullifierCheck } from './trace_types.js';
4
+
5
+ export class WorldStateAccessTrace {
6
+ public accessCounter: number;
7
+ //public contractCalls: Array<TracedContractCall> = [];
8
+
9
+ //public publicStorageReads: Array<TracedPublicStorageRead> = [];
10
+ public publicStorageReads: Map<bigint, Map<bigint, Fr[]>> = new Map();
11
+ //public publicStorageWrites: Array<TracedPublicStorageWrite> = [];
12
+ public publicStorageWrites: Map<bigint, Map<bigint, Fr[]>> = new Map();
13
+
14
+ public noteHashChecks: TracedNoteHashCheck[] = [];
15
+ //public newNoteHashes: TracedNoteHash[] = [];
16
+ public newNoteHashes: Fr[] = [];
17
+ public nullifierChecks: TracedNullifierCheck[] = [];
18
+ //public newNullifiers: TracedNullifier[] = [];
19
+ public newNullifiers: Fr[] = [];
20
+ public l1ToL2MessageChecks: TracedL1toL2MessageCheck[] = [];
21
+ //public archiveChecks: TracedArchiveLeafCheck[] = [];
22
+
23
+ constructor(parentTrace?: WorldStateAccessTrace) {
24
+ this.accessCounter = parentTrace ? parentTrace.accessCounter : 0;
25
+ }
26
+
27
+ public getAccessCounter() {
28
+ return this.accessCounter;
29
+ }
30
+
31
+ public tracePublicStorageRead(storageAddress: Fr, slot: Fr, value: Fr /*, _exists: boolean*/) {
32
+ // TODO(4805): check if some threshold is reached for max storage reads
33
+ // (need access to parent length, or trace needs to be initialized with parent's contents)
34
+ //const traced: TracedPublicStorageRead = {
35
+ // callPointer: Fr.ZERO,
36
+ // storageAddress,
37
+ // slot,
38
+ // value,
39
+ // exists,
40
+ // counter: new Fr(this.accessCounter),
41
+ // endLifetime: Fr.ZERO,
42
+ //};
43
+ //this.publicStorageReads.push(traced);
44
+ this.journalRead(storageAddress, slot, value);
45
+ this.incrementAccessCounter();
46
+ }
47
+
48
+ public tracePublicStorageWrite(storageAddress: Fr, slot: Fr, value: Fr) {
49
+ // TODO(4805): check if some threshold is reached for max storage writes
50
+ // (need access to parent length, or trace needs to be initialized with parent's contents)
51
+ //const traced: TracedPublicStorageWrite = {
52
+ // callPointer: Fr.ZERO,
53
+ // storageAddress,
54
+ // slot,
55
+ // value,
56
+ // counter: new Fr(this.accessCounter),
57
+ // endLifetime: Fr.ZERO,
58
+ //};
59
+ //this.publicStorageWrites.push(traced);
60
+ this.journalWrite(storageAddress, slot, value);
61
+ this.incrementAccessCounter();
62
+ }
63
+
64
+ public traceNoteHashCheck(storageAddress: Fr, noteHash: Fr, exists: boolean, leafIndex: Fr) {
65
+ const traced: TracedNoteHashCheck = {
66
+ callPointer: Fr.ZERO, // FIXME
67
+ storageAddress,
68
+ noteHash,
69
+ exists,
70
+ counter: new Fr(this.accessCounter),
71
+ endLifetime: Fr.ZERO,
72
+ leafIndex,
73
+ };
74
+ this.noteHashChecks.push(traced);
75
+ this.incrementAccessCounter();
76
+ }
77
+
78
+ public traceNewNoteHash(_storageAddress: Fr, noteHash: Fr) {
79
+ // TODO(4805): check if some threshold is reached for max new note hash
80
+ //const traced: TracedNoteHash = {
81
+ // callPointer: Fr.ZERO,
82
+ // storageAddress,
83
+ // noteHash,
84
+ // counter: new Fr(this.accessCounter),
85
+ // endLifetime: Fr.ZERO,
86
+ //};
87
+ //this.newNoteHashes.push(traced);
88
+ this.newNoteHashes.push(noteHash);
89
+ this.incrementAccessCounter();
90
+ }
91
+
92
+ public traceNullifierCheck(storageAddress: Fr, nullifier: Fr, exists: boolean, isPending: boolean, leafIndex: Fr) {
93
+ // TODO(4805): check if some threshold is reached for max new nullifier
94
+ const traced: TracedNullifierCheck = {
95
+ callPointer: Fr.ZERO, // FIXME
96
+ storageAddress,
97
+ nullifier,
98
+ exists,
99
+ counter: new Fr(this.accessCounter),
100
+ endLifetime: Fr.ZERO,
101
+ isPending,
102
+ leafIndex,
103
+ };
104
+ this.nullifierChecks.push(traced);
105
+ this.incrementAccessCounter();
106
+ }
107
+
108
+ public traceNewNullifier(_storageAddress: Fr, nullifier: Fr) {
109
+ // TODO(4805): check if some threshold is reached for max new nullifier
110
+ //const traced: TracedNullifier = {
111
+ // callPointer: Fr.ZERO,
112
+ // storageAddress,
113
+ // nullifier,
114
+ // counter: new Fr(this.accessCounter),
115
+ // endLifetime: Fr.ZERO,
116
+ //};
117
+ //this.newNullifiers.push(traced);
118
+ this.newNullifiers.push(nullifier);
119
+ this.incrementAccessCounter();
120
+ }
121
+
122
+ public traceL1ToL2MessageCheck(msgHash: Fr, msgLeafIndex: Fr, exists: boolean) {
123
+ // TODO(4805): check if some threshold is reached for max message reads
124
+ const traced: TracedL1toL2MessageCheck = {
125
+ //callPointer: Fr.ZERO, // FIXME
126
+ leafIndex: msgLeafIndex,
127
+ msgHash: msgHash,
128
+ exists: exists,
129
+ //endLifetime: Fr.ZERO, // FIXME
130
+ };
131
+ this.l1ToL2MessageChecks.push(traced);
132
+ this.incrementAccessCounter();
133
+ }
134
+
135
+ private incrementAccessCounter() {
136
+ this.accessCounter++;
137
+ }
138
+
139
+ /**
140
+ * Merges another trace into this one
141
+ *
142
+ * - Public state journals (r/w logs), with the accessing being appended in chronological order
143
+ * - Utxo objects are concatenated
144
+ *
145
+ * @param incomingTrace - the incoming trace to merge into this instance
146
+ */
147
+ public acceptAndMerge(incomingTrace: WorldStateAccessTrace) {
148
+ // Merge storage read and write journals
149
+ mergeContractJournalMaps(this.publicStorageReads, incomingTrace.publicStorageReads);
150
+ mergeContractJournalMaps(this.publicStorageWrites, incomingTrace.publicStorageWrites);
151
+ // Merge new note hashes and nullifiers
152
+ this.noteHashChecks = this.noteHashChecks.concat(incomingTrace.noteHashChecks);
153
+ this.newNoteHashes = this.newNoteHashes.concat(incomingTrace.newNoteHashes);
154
+ this.nullifierChecks = this.nullifierChecks.concat(incomingTrace.nullifierChecks);
155
+ this.newNullifiers = this.newNullifiers.concat(incomingTrace.newNullifiers);
156
+ this.l1ToL2MessageChecks = this.l1ToL2MessageChecks.concat(incomingTrace.l1ToL2MessageChecks);
157
+ // it is assumed that the incoming trace was initialized with this as parent, so accept counter
158
+ this.accessCounter = incomingTrace.accessCounter;
159
+ }
160
+
161
+ /**
162
+ * We want to keep track of all performed reads in the journal
163
+ * This information is hinted to the avm circuit
164
+
165
+ * @param contractAddress -
166
+ * @param key -
167
+ * @param value -
168
+ */
169
+ journalUpdate(map: Map<bigint, Map<bigint, Fr[]>>, contractAddress: Fr, key: Fr, value: Fr): void {
170
+ let contractMap = map.get(contractAddress.toBigInt());
171
+ if (!contractMap) {
172
+ contractMap = new Map<bigint, Array<Fr>>();
173
+ map.set(contractAddress.toBigInt(), contractMap);
174
+ }
175
+
176
+ let accessArray = contractMap.get(key.toBigInt());
177
+ if (!accessArray) {
178
+ accessArray = new Array<Fr>();
179
+ contractMap.set(key.toBigInt(), accessArray);
180
+ }
181
+ accessArray.push(value);
182
+ }
183
+
184
+ // Create an instance of journalUpdate that appends to the read array
185
+ private journalRead = this.journalUpdate.bind(this, this.publicStorageReads);
186
+ // Create an instance of journalUpdate that appends to the writes array
187
+ private journalWrite = this.journalUpdate.bind(this, this.publicStorageWrites);
188
+ }
189
+
190
+ /**
191
+ * Merges two contract journalling maps together
192
+ * For read maps, we just append the childMap arrays into the host map arrays, as the order is important
193
+ *
194
+ * @param hostMap - The map to be merged into
195
+ * @param childMap - The map to be merged from
196
+ */
197
+ function mergeContractJournalMaps(hostMap: Map<bigint, Map<bigint, Fr[]>>, childMap: Map<bigint, Map<bigint, Fr[]>>) {
198
+ for (const [key, value] of childMap) {
199
+ const map1Value = hostMap.get(key);
200
+ if (!map1Value) {
201
+ hostMap.set(key, value);
202
+ } else {
203
+ mergeStorageJournalMaps(map1Value, value);
204
+ }
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Merge two storage journalling maps together (for a particular contract).
210
+ *
211
+ * @param hostMap - The map to be merge into
212
+ * @param childMap - The map to be merged from
213
+ */
214
+ function mergeStorageJournalMaps(hostMap: Map<bigint, Fr[]>, childMap: Map<bigint, Fr[]>) {
215
+ for (const [key, value] of childMap) {
216
+ const readArr = hostMap.get(key);
217
+ if (!readArr) {
218
+ hostMap.set(key, value);
219
+ } else {
220
+ hostMap.set(key, readArr?.concat(...value));
221
+ }
222
+ }
223
+ }
@@ -0,0 +1,79 @@
1
+ import { Fr } from '@aztec/foundation/fields';
2
+
3
+ //export type TracedContractCall = {
4
+ // callPointer: Fr;
5
+ // address: Fr;
6
+ // storageAddress: Fr;
7
+ // endLifetime: Fr;
8
+ //};
9
+ //
10
+ //export type TracedPublicStorageRead = {
11
+ // callPointer: Fr;
12
+ // storageAddress: Fr;
13
+ // exists: boolean;
14
+ // slot: Fr;
15
+ // value: Fr;
16
+ // counter: Fr;
17
+ // endLifetime: Fr;
18
+ //};
19
+ //
20
+ //export type TracedPublicStorageWrite = {
21
+ // callPointer: Fr;
22
+ // storageAddress: Fr;
23
+ // slot: Fr;
24
+ // value: Fr;
25
+ // counter: Fr;
26
+ // endLifetime: Fr;
27
+ //};
28
+ //
29
+ export type TracedNoteHashCheck = {
30
+ callPointer: Fr;
31
+ storageAddress: Fr;
32
+ leafIndex: Fr;
33
+ noteHash: Fr;
34
+ exists: boolean;
35
+ counter: Fr;
36
+ endLifetime: Fr;
37
+ };
38
+ //
39
+ //export type TracedNoteHash = {
40
+ // callPointer: Fr;
41
+ // storageAddress: Fr;
42
+ // noteHash: Fr;
43
+ // counter: Fr;
44
+ // endLifetime: Fr;
45
+ //};
46
+
47
+ export type TracedNullifierCheck = {
48
+ callPointer: Fr;
49
+ storageAddress: Fr;
50
+ nullifier: Fr;
51
+ exists: boolean;
52
+ counter: Fr;
53
+ endLifetime: Fr;
54
+ // the fields below are relevant only to the public kernel
55
+ // and are therefore omitted from VM inputs
56
+ isPending: boolean;
57
+ leafIndex: Fr;
58
+ };
59
+
60
+ //export type TracedNullifier = {
61
+ // callPointer: Fr;
62
+ // storageAddress: Fr;
63
+ // nullifier: Fr;
64
+ // counter: Fr;
65
+ // endLifetime: Fr;
66
+ //};
67
+
68
+ export type TracedL1toL2MessageCheck = {
69
+ //callPointer: Fr;
70
+ leafIndex: Fr;
71
+ msgHash: Fr;
72
+ exists: boolean;
73
+ //endLifetime: Fr;
74
+ };
75
+
76
+ //export type TracedArchiveLeafCheck = {
77
+ // leafIndex: Fr;
78
+ // leaf: Fr;
79
+ //};
@@ -1,8 +1,49 @@
1
1
  import type { AvmContext } from '../avm_context.js';
2
+ import { Uint8 } from '../avm_memory_types.js';
3
+ import { InstructionExecutionError } from '../errors.js';
4
+ import { NullifierCollisionError } from '../journal/nullifiers.js';
2
5
  import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
6
+ import { Addressing } from './addressing_mode.js';
3
7
  import { Instruction } from './instruction.js';
4
8
  import { StaticCallStorageAlterError } from './storage.js';
5
9
 
10
+ export class NoteHashExists extends Instruction {
11
+ static type: string = 'NOTEHASHEXISTS';
12
+ static readonly opcode: Opcode = Opcode.NOTEHASHEXISTS;
13
+ // Informs (de)serialization. See Instruction.deserialize.
14
+ static readonly wireFormat = [
15
+ OperandType.UINT8,
16
+ OperandType.UINT8,
17
+ OperandType.UINT32,
18
+ OperandType.UINT32,
19
+ OperandType.UINT32,
20
+ ];
21
+
22
+ constructor(
23
+ private indirect: number,
24
+ private noteHashOffset: number,
25
+ private leafIndexOffset: number,
26
+ private existsOffset: number,
27
+ ) {
28
+ super();
29
+ }
30
+
31
+ async execute(context: AvmContext): Promise<void> {
32
+ // Note that this instruction accepts any type in memory, and converts to Field.
33
+ const noteHash = context.machineState.memory.get(this.noteHashOffset).toFr();
34
+ const leafIndex = context.machineState.memory.get(this.leafIndexOffset).toFr();
35
+
36
+ const exists = await context.persistableState.checkNoteHashExists(
37
+ context.environment.storageAddress,
38
+ noteHash,
39
+ leafIndex,
40
+ );
41
+ context.machineState.memory.set(this.existsOffset, exists ? new Uint8(1) : new Uint8(0));
42
+
43
+ context.machineState.incrementPc();
44
+ }
45
+ }
46
+
6
47
  export class EmitNoteHash extends Instruction {
7
48
  static type: string = 'EMITNOTEHASH';
8
49
  static readonly opcode: Opcode = Opcode.EMITNOTEHASH;
@@ -19,7 +60,27 @@ export class EmitNoteHash extends Instruction {
19
60
  }
20
61
 
21
62
  const noteHash = context.machineState.memory.get(this.noteHashOffset).toFr();
22
- context.worldState.writeNoteHash(noteHash);
63
+ context.persistableState.writeNoteHash(noteHash);
64
+
65
+ context.machineState.incrementPc();
66
+ }
67
+ }
68
+
69
+ export class NullifierExists extends Instruction {
70
+ static type: string = 'NULLIFIEREXISTS';
71
+ static readonly opcode: Opcode = Opcode.NULLIFIEREXISTS;
72
+ // Informs (de)serialization. See Instruction.deserialize.
73
+ static readonly wireFormat = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT32, OperandType.UINT32];
74
+
75
+ constructor(private indirect: number, private nullifierOffset: number, private existsOffset: number) {
76
+ super();
77
+ }
78
+
79
+ async execute(context: AvmContext): Promise<void> {
80
+ const nullifier = context.machineState.memory.get(this.nullifierOffset).toFr();
81
+ const exists = await context.persistableState.checkNullifierExists(context.environment.storageAddress, nullifier);
82
+
83
+ context.machineState.memory.set(this.existsOffset, exists ? new Uint8(1) : new Uint8(0));
23
84
 
24
85
  context.machineState.incrementPc();
25
86
  }
@@ -41,7 +102,49 @@ export class EmitNullifier extends Instruction {
41
102
  }
42
103
 
43
104
  const nullifier = context.machineState.memory.get(this.nullifierOffset).toFr();
44
- context.worldState.writeNullifier(nullifier);
105
+ try {
106
+ await context.persistableState.writeNullifier(context.environment.storageAddress, nullifier);
107
+ } catch (e) {
108
+ if (e instanceof NullifierCollisionError) {
109
+ // Error is known/expected, raise as InstructionExecutionError that the will lead the simulator to revert this call
110
+ throw new InstructionExecutionError(
111
+ `Attempted to emit duplicate nullifier ${nullifier} (storage address: ${context.environment.storageAddress}).`,
112
+ );
113
+ } else {
114
+ throw e;
115
+ }
116
+ }
117
+
118
+ context.machineState.incrementPc();
119
+ }
120
+ }
121
+
122
+ export class L1ToL2MessageExists extends Instruction {
123
+ static type: string = 'L1TOL2MSGEXISTS';
124
+ static readonly opcode: Opcode = Opcode.L1TOL2MSGEXISTS;
125
+ // Informs (de)serialization. See Instruction.deserialize.
126
+ static readonly wireFormat = [
127
+ OperandType.UINT8,
128
+ OperandType.UINT8,
129
+ OperandType.UINT32,
130
+ OperandType.UINT32,
131
+ OperandType.UINT32,
132
+ ];
133
+
134
+ constructor(
135
+ private indirect: number,
136
+ private msgHashOffset: number,
137
+ private msgLeafIndexOffset: number,
138
+ private existsOffset: number,
139
+ ) {
140
+ super();
141
+ }
142
+
143
+ async execute(context: AvmContext): Promise<void> {
144
+ const msgHash = context.machineState.memory.get(this.msgHashOffset).toFr();
145
+ const msgLeafIndex = context.machineState.memory.get(this.msgLeafIndexOffset).toFr();
146
+ const exists = await context.persistableState.checkL1ToL2MessageExists(msgHash, msgLeafIndex);
147
+ context.machineState.memory.set(this.existsOffset, exists ? new Uint8(1) : new Uint8(0));
45
148
 
46
149
  context.machineState.incrementPc();
47
150
  }
@@ -51,9 +154,20 @@ export class EmitUnencryptedLog extends Instruction {
51
154
  static type: string = 'EMITUNENCRYPTEDLOG';
52
155
  static readonly opcode: Opcode = Opcode.EMITUNENCRYPTEDLOG;
53
156
  // Informs (de)serialization. See Instruction.deserialize.
54
- static readonly wireFormat = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT32, OperandType.UINT32];
55
-
56
- constructor(private indirect: number, private logOffset: number, private logSize: number) {
157
+ static readonly wireFormat = [
158
+ OperandType.UINT8,
159
+ OperandType.UINT8,
160
+ OperandType.UINT32,
161
+ OperandType.UINT32,
162
+ OperandType.UINT32,
163
+ ];
164
+
165
+ constructor(
166
+ private indirect: number,
167
+ private eventSelectorOffset: number,
168
+ private logOffset: number,
169
+ private logSize: number,
170
+ ) {
57
171
  super();
58
172
  }
59
173
 
@@ -62,8 +176,15 @@ export class EmitUnencryptedLog extends Instruction {
62
176
  throw new StaticCallStorageAlterError();
63
177
  }
64
178
 
65
- const log = context.machineState.memory.getSlice(this.logOffset, this.logSize).map(f => f.toFr());
66
- context.worldState.writeLog(log);
179
+ const [eventSelectorOffset, logOffset] = Addressing.fromWire(this.indirect).resolve(
180
+ [this.eventSelectorOffset, this.logOffset],
181
+ context.machineState.memory,
182
+ );
183
+
184
+ const contractAddress = context.environment.address;
185
+ const event = context.machineState.memory.get(eventSelectorOffset).toFr();
186
+ const log = context.machineState.memory.getSlice(logOffset, this.logSize).map(f => f.toFr());
187
+ context.persistableState.writeLog(contractAddress, event, log);
67
188
 
68
189
  context.machineState.incrementPc();
69
190
  }
@@ -75,7 +196,7 @@ export class SendL2ToL1Message extends Instruction {
75
196
  // Informs (de)serialization. See Instruction.deserialize.
76
197
  static readonly wireFormat = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT32, OperandType.UINT32];
77
198
 
78
- constructor(private indirect: number, private msgOffset: number, private msgSize: number) {
199
+ constructor(private indirect: number, private recipientOffset: number, private contentOffset: number) {
79
200
  super();
80
201
  }
81
202
 
@@ -84,8 +205,9 @@ export class SendL2ToL1Message extends Instruction {
84
205
  throw new StaticCallStorageAlterError();
85
206
  }
86
207
 
87
- const msg = context.machineState.memory.getSlice(this.msgOffset, this.msgSize).map(f => f.toFr());
88
- context.worldState.writeL1Message(msg);
208
+ const recipient = context.machineState.memory.get(this.recipientOffset).toFr();
209
+ const content = context.machineState.memory.get(this.contentOffset).toFr();
210
+ context.persistableState.writeL1Message(recipient, content);
89
211
 
90
212
  context.machineState.incrementPc();
91
213
  }
@@ -1,5 +1,4 @@
1
1
  import type { AvmContext } from '../avm_context.js';
2
- import { IntegralValue, TaggedMemory } from '../avm_memory_types.js';
3
2
  import { Opcode } from '../serialization/instruction_serialization.js';
4
3
  import { ThreeOperandInstruction } from './instruction_impl.js';
5
4
 
@@ -35,10 +34,9 @@ export class Lt extends ThreeOperandInstruction {
35
34
 
36
35
  async execute(context: AvmContext): Promise<void> {
37
36
  context.machineState.memory.checkTags(this.inTag, this.aOffset, this.bOffset);
38
- TaggedMemory.checkIsIntegralTag(this.inTag);
39
37
 
40
- const a = context.machineState.memory.getAs<IntegralValue>(this.aOffset);
41
- const b = context.machineState.memory.getAs<IntegralValue>(this.bOffset);
38
+ const a = context.machineState.memory.get(this.aOffset);
39
+ const b = context.machineState.memory.get(this.bOffset);
42
40
 
43
41
  // Result will be of the same type as 'a'.
44
42
  const dest = a.build(a.lt(b) ? 1n : 0n);
@@ -58,10 +56,9 @@ export class Lte extends ThreeOperandInstruction {
58
56
 
59
57
  async execute(context: AvmContext): Promise<void> {
60
58
  context.machineState.memory.checkTags(this.inTag, this.aOffset, this.bOffset);
61
- TaggedMemory.checkIsIntegralTag(this.inTag);
62
59
 
63
- const a = context.machineState.memory.getAs<IntegralValue>(this.aOffset);
64
- const b = context.machineState.memory.getAs<IntegralValue>(this.bOffset);
60
+ const a = context.machineState.memory.get(this.aOffset);
61
+ const b = context.machineState.memory.get(this.bOffset);
65
62
 
66
63
  // Result will be of the same type as 'a'.
67
64
  const dest = a.build(a.equals(b) || a.lt(b) ? 1n : 0n);