@aztec/simulator 0.37.0 → 0.39.0

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 (181) hide show
  1. package/dest/acvm/acvm.d.ts +1 -1
  2. package/dest/acvm/acvm.d.ts.map +1 -1
  3. package/dest/acvm/acvm.js +2 -2
  4. package/dest/acvm/oracle/oracle.d.ts +6 -5
  5. package/dest/acvm/oracle/oracle.d.ts.map +1 -1
  6. package/dest/acvm/oracle/oracle.js +26 -30
  7. package/dest/acvm/oracle/typed_oracle.d.ts +5 -5
  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_execution_environment.d.ts +4 -3
  11. package/dest/avm/avm_execution_environment.d.ts.map +1 -1
  12. package/dest/avm/avm_execution_environment.js +17 -11
  13. package/dest/avm/avm_gas.d.ts.map +1 -1
  14. package/dest/avm/avm_gas.js +4 -1
  15. package/dest/avm/avm_machine_state.d.ts +5 -8
  16. package/dest/avm/avm_machine_state.d.ts.map +1 -1
  17. package/dest/avm/avm_machine_state.js +10 -22
  18. package/dest/avm/avm_memory_types.d.ts +1 -1
  19. package/dest/avm/avm_memory_types.d.ts.map +1 -1
  20. package/dest/avm/avm_message_call_result.d.ts +5 -8
  21. package/dest/avm/avm_message_call_result.d.ts.map +1 -1
  22. package/dest/avm/avm_message_call_result.js +1 -4
  23. package/dest/avm/avm_simulator.d.ts.map +1 -1
  24. package/dest/avm/avm_simulator.js +19 -13
  25. package/dest/avm/errors.d.ts +43 -2
  26. package/dest/avm/errors.d.ts.map +1 -1
  27. package/dest/avm/errors.js +86 -4
  28. package/dest/avm/journal/journal.d.ts +20 -1
  29. package/dest/avm/journal/journal.d.ts.map +1 -1
  30. package/dest/avm/journal/journal.js +70 -9
  31. package/dest/avm/journal/nullifiers.d.ts +3 -1
  32. package/dest/avm/journal/nullifiers.d.ts.map +1 -1
  33. package/dest/avm/journal/nullifiers.js +14 -6
  34. package/dest/avm/journal/public_storage.d.ts +10 -1
  35. package/dest/avm/journal/public_storage.d.ts.map +1 -1
  36. package/dest/avm/journal/public_storage.js +17 -2
  37. package/dest/avm/journal/trace.d.ts +1 -4
  38. package/dest/avm/journal/trace.d.ts.map +1 -1
  39. package/dest/avm/journal/trace.js +4 -5
  40. package/dest/avm/journal/trace_types.d.ts +1 -0
  41. package/dest/avm/journal/trace_types.d.ts.map +1 -1
  42. package/dest/avm/journal/trace_types.js +1 -1
  43. package/dest/avm/opcodes/bitwise.d.ts +4 -1
  44. package/dest/avm/opcodes/bitwise.d.ts.map +1 -1
  45. package/dest/avm/opcodes/bitwise.js +14 -2
  46. package/dest/avm/opcodes/conversion.d.ts +16 -0
  47. package/dest/avm/opcodes/conversion.d.ts.map +1 -0
  48. package/dest/avm/opcodes/conversion.js +48 -0
  49. package/dest/avm/opcodes/environment_getters.d.ts +16 -12
  50. package/dest/avm/opcodes/environment_getters.d.ts.map +1 -1
  51. package/dest/avm/opcodes/environment_getters.js +19 -48
  52. package/dest/avm/opcodes/external_calls.d.ts.map +1 -1
  53. package/dest/avm/opcodes/external_calls.js +24 -13
  54. package/dest/avm/serialization/bytecode_serialization.d.ts.map +1 -1
  55. package/dest/avm/serialization/bytecode_serialization.js +6 -2
  56. package/dest/avm/serialization/instruction_serialization.d.ts +40 -38
  57. package/dest/avm/serialization/instruction_serialization.d.ts.map +1 -1
  58. package/dest/avm/serialization/instruction_serialization.js +42 -39
  59. package/dest/client/client_execution_context.d.ts +59 -19
  60. package/dest/client/client_execution_context.d.ts.map +1 -1
  61. package/dest/client/client_execution_context.js +97 -45
  62. package/dest/client/db_oracle.d.ts +4 -11
  63. package/dest/client/db_oracle.d.ts.map +1 -1
  64. package/dest/client/execution_result.d.ts +22 -15
  65. package/dest/client/execution_result.d.ts.map +1 -1
  66. package/dest/client/execution_result.js +59 -13
  67. package/dest/client/logs_cache.d.ts +33 -0
  68. package/dest/client/logs_cache.d.ts.map +1 -0
  69. package/dest/client/logs_cache.js +59 -0
  70. package/dest/client/private_execution.d.ts +2 -2
  71. package/dest/client/private_execution.d.ts.map +1 -1
  72. package/dest/client/private_execution.js +5 -7
  73. package/dest/client/simulator.d.ts +4 -34
  74. package/dest/client/simulator.d.ts.map +1 -1
  75. package/dest/client/simulator.js +5 -43
  76. package/dest/client/unconstrained_execution.d.ts +2 -2
  77. package/dest/client/unconstrained_execution.d.ts.map +1 -1
  78. package/dest/client/unconstrained_execution.js +1 -1
  79. package/dest/client/view_data_oracle.d.ts +0 -7
  80. package/dest/client/view_data_oracle.d.ts.map +1 -1
  81. package/dest/client/view_data_oracle.js +1 -10
  82. package/dest/common/errors.d.ts +5 -0
  83. package/dest/common/errors.d.ts.map +1 -1
  84. package/dest/common/errors.js +6 -1
  85. package/dest/index.d.ts +1 -0
  86. package/dest/index.d.ts.map +1 -1
  87. package/dest/index.js +2 -1
  88. package/dest/mocks/fixtures.d.ts.map +1 -1
  89. package/dest/mocks/fixtures.js +3 -1
  90. package/dest/public/abstract_phase_manager.d.ts +10 -4
  91. package/dest/public/abstract_phase_manager.d.ts.map +1 -1
  92. package/dest/public/abstract_phase_manager.js +50 -19
  93. package/dest/public/app_logic_phase_manager.d.ts +1 -0
  94. package/dest/public/app_logic_phase_manager.d.ts.map +1 -1
  95. package/dest/public/app_logic_phase_manager.js +3 -3
  96. package/dest/public/execution.d.ts +9 -0
  97. package/dest/public/execution.d.ts.map +1 -1
  98. package/dest/public/execution.js +1 -1
  99. package/dest/public/executor.d.ts +2 -2
  100. package/dest/public/executor.d.ts.map +1 -1
  101. package/dest/public/executor.js +34 -17
  102. package/dest/public/hints_builder.d.ts +3 -3
  103. package/dest/public/hints_builder.d.ts.map +1 -1
  104. package/dest/public/hints_builder.js +3 -3
  105. package/dest/public/public_execution_context.d.ts +10 -4
  106. package/dest/public/public_execution_context.d.ts.map +1 -1
  107. package/dest/public/public_execution_context.js +19 -6
  108. package/dest/public/public_processor.d.ts.map +1 -1
  109. package/dest/public/public_processor.js +5 -3
  110. package/dest/public/setup_phase_manager.d.ts +1 -0
  111. package/dest/public/setup_phase_manager.d.ts.map +1 -1
  112. package/dest/public/setup_phase_manager.js +3 -2
  113. package/dest/public/tail_phase_manager.d.ts +1 -1
  114. package/dest/public/tail_phase_manager.d.ts.map +1 -1
  115. package/dest/public/tail_phase_manager.js +4 -26
  116. package/dest/public/teardown_phase_manager.d.ts +1 -0
  117. package/dest/public/teardown_phase_manager.d.ts.map +1 -1
  118. package/dest/public/teardown_phase_manager.js +3 -2
  119. package/dest/public/transitional_adaptors.d.ts +4 -17
  120. package/dest/public/transitional_adaptors.d.ts.map +1 -1
  121. package/dest/public/transitional_adaptors.js +27 -119
  122. package/dest/rollup/index.d.ts +2 -0
  123. package/dest/rollup/index.d.ts.map +1 -0
  124. package/dest/rollup/index.js +2 -0
  125. package/dest/rollup/rollup.d.ts +77 -0
  126. package/dest/rollup/rollup.d.ts.map +1 -0
  127. package/dest/rollup/rollup.js +78 -0
  128. package/dest/stats/index.d.ts +2 -0
  129. package/dest/stats/index.d.ts.map +1 -0
  130. package/dest/stats/index.js +2 -0
  131. package/dest/stats/stats.d.ts +4 -0
  132. package/dest/stats/stats.d.ts.map +1 -0
  133. package/dest/stats/stats.js +11 -0
  134. package/package.json +8 -8
  135. package/src/acvm/acvm.ts +2 -2
  136. package/src/acvm/oracle/oracle.ts +62 -36
  137. package/src/acvm/oracle/typed_oracle.ts +19 -11
  138. package/src/avm/avm_execution_environment.ts +34 -42
  139. package/src/avm/avm_gas.ts +3 -0
  140. package/src/avm/avm_machine_state.ts +14 -25
  141. package/src/avm/avm_memory_types.ts +1 -1
  142. package/src/avm/avm_message_call_result.ts +3 -14
  143. package/src/avm/avm_simulator.ts +24 -12
  144. package/src/avm/errors.ts +94 -4
  145. package/src/avm/journal/journal.ts +134 -9
  146. package/src/avm/journal/nullifiers.ts +19 -8
  147. package/src/avm/journal/public_storage.ts +23 -2
  148. package/src/avm/journal/trace.ts +3 -4
  149. package/src/avm/journal/trace_types.ts +1 -0
  150. package/src/avm/opcodes/bitwise.ts +18 -7
  151. package/src/avm/opcodes/conversion.ts +59 -0
  152. package/src/avm/opcodes/environment_getters.ts +21 -65
  153. package/src/avm/opcodes/external_calls.ts +32 -16
  154. package/src/avm/serialization/bytecode_serialization.ts +5 -0
  155. package/src/avm/serialization/instruction_serialization.ts +3 -0
  156. package/src/client/client_execution_context.ts +142 -46
  157. package/src/client/db_oracle.ts +4 -18
  158. package/src/client/execution_result.ts +75 -25
  159. package/src/client/logs_cache.ts +65 -0
  160. package/src/client/private_execution.ts +6 -10
  161. package/src/client/simulator.ts +8 -84
  162. package/src/client/unconstrained_execution.ts +2 -2
  163. package/src/client/view_data_oracle.ts +0 -10
  164. package/src/common/errors.ts +5 -0
  165. package/src/index.ts +1 -0
  166. package/src/mocks/fixtures.ts +2 -0
  167. package/src/public/abstract_phase_manager.ts +59 -23
  168. package/src/public/app_logic_phase_manager.ts +2 -1
  169. package/src/public/execution.ts +9 -0
  170. package/src/public/executor.ts +47 -14
  171. package/src/public/hints_builder.ts +5 -5
  172. package/src/public/public_execution_context.ts +18 -4
  173. package/src/public/public_processor.ts +8 -2
  174. package/src/public/setup_phase_manager.ts +16 -8
  175. package/src/public/tail_phase_manager.ts +8 -35
  176. package/src/public/teardown_phase_manager.ts +16 -8
  177. package/src/public/transitional_adaptors.ts +39 -177
  178. package/src/rollup/index.ts +1 -0
  179. package/src/rollup/rollup.ts +160 -0
  180. package/src/stats/index.ts +1 -0
  181. package/src/stats/stats.ts +20 -0
package/src/avm/errors.ts CHANGED
@@ -1,12 +1,16 @@
1
- import { type AztecAddress } from '@aztec/circuits.js';
1
+ import { type FailingFunction, type NoirCallStack } from '@aztec/circuit-types';
2
+ import { type AztecAddress, type Fr } from '@aztec/circuits.js';
3
+
4
+ import { ExecutionError } from '../common/errors.js';
5
+ import { type AvmContext } from './avm_context.js';
2
6
 
3
7
  /**
4
8
  * Avm-specific errors should derive from this
5
9
  */
6
10
  export abstract class AvmExecutionError extends Error {
7
- constructor(message: string, ...rest: any[]) {
8
- super(message, ...rest);
9
- this.name = 'AvmInterpreterError';
11
+ constructor(message: string) {
12
+ super(message);
13
+ this.name = 'AvmExecutionError';
10
14
  }
11
15
  }
12
16
 
@@ -63,3 +67,89 @@ export class OutOfGasError extends AvmExecutionError {
63
67
  this.name = 'OutOfGasError';
64
68
  }
65
69
  }
70
+
71
+ /**
72
+ * Error thrown to propagate a nested call's revert.
73
+ * @param message - the error's message
74
+ * @param nestedError - the revert reason of the nested call
75
+ */
76
+ export class RethrownError extends AvmExecutionError {
77
+ constructor(message: string, public nestedError: AvmRevertReason) {
78
+ super(message);
79
+ this.name = 'RethrownError';
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Meaningfully named alias for ExecutionError when used in the context of the AVM.
85
+ * Maintains a recursive structure reflecting the AVM's external callstack/errorstack, where
86
+ * options.cause is the error that caused this error (if this is not the root-cause itself).
87
+ */
88
+ export class AvmRevertReason extends ExecutionError {
89
+ constructor(message: string, failingFunction: FailingFunction, noirCallStack: NoirCallStack, options?: ErrorOptions) {
90
+ super(message, failingFunction, noirCallStack, options);
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Helper to create a "revert reason" error optionally with a nested error cause.
96
+ *
97
+ * @param message - the error message
98
+ * @param context - the context of the AVM execution used to extract the failingFunction and noirCallStack
99
+ * @param nestedError - the error that caused this one (if this is not the root-cause itself)
100
+ */
101
+ function createRevertReason(message: string, context: AvmContext, nestedError?: AvmRevertReason): AvmRevertReason {
102
+ return new AvmRevertReason(
103
+ message,
104
+ /*failingFunction=*/ {
105
+ contractAddress: context.environment.address,
106
+ functionSelector: context.environment.temporaryFunctionSelector,
107
+ },
108
+ /*noirCallStack=*/ [...context.machineState.internalCallStack, context.machineState.pc].map(pc => `0.${pc}`),
109
+ /*options=*/ { cause: nestedError },
110
+ );
111
+ }
112
+
113
+ /**
114
+ * Create a "revert reason" error for an exceptional halt,
115
+ * creating the recursive structure if the halt was a RethrownError.
116
+ *
117
+ * @param haltingError - the lower-level error causing the exceptional halt
118
+ * @param context - the context of the AVM execution used to extract the failingFunction and noirCallStack
119
+ */
120
+ export function revertReasonFromExceptionalHalt(haltingError: AvmExecutionError, context: AvmContext): AvmRevertReason {
121
+ // A RethrownError has a nested/child AvmRevertReason
122
+ const nestedError = haltingError instanceof RethrownError ? haltingError.nestedError : undefined;
123
+ return createRevertReason(haltingError.message, context, nestedError);
124
+ }
125
+
126
+ /**
127
+ * Create a "revert reason" error for an explicit revert (a root cause).
128
+ *
129
+ * @param revertData - output data of the explicit REVERT instruction
130
+ * @param context - the context of the AVM execution used to extract the failingFunction and noirCallStack
131
+ */
132
+ export function revertReasonFromExplicitRevert(revertData: Fr[], context: AvmContext): AvmRevertReason {
133
+ const revertMessage = decodeRevertDataAsMessage(revertData);
134
+ return createRevertReason(revertMessage, context);
135
+ }
136
+
137
+ /**
138
+ * Interpret revert data as a message string.
139
+ *
140
+ * @param revertData - output data of an explicit REVERT instruction
141
+ */
142
+ export function decodeRevertDataAsMessage(revertData: Fr[]): string {
143
+ if (revertData.length === 0) {
144
+ return 'Assertion failed.';
145
+ } else {
146
+ try {
147
+ // We remove the first element which is the 'error selector'.
148
+ const revertOutput = revertData.slice(1);
149
+ // Try to interpret the output as a text string.
150
+ return 'Assertion failed: ' + String.fromCharCode(...revertOutput.map(fr => fr.toNumber()));
151
+ } catch (e) {
152
+ return 'Assertion failed: <cannot interpret as string>';
153
+ }
154
+ }
155
+ }
@@ -1,9 +1,21 @@
1
+ // TODO(5818): Rename file and all uses of "journal"
1
2
  import { UnencryptedL2Log } from '@aztec/circuit-types';
2
- import { AztecAddress, EthAddress, L2ToL1Message } from '@aztec/circuits.js';
3
+ import {
4
+ AztecAddress,
5
+ ContractStorageRead,
6
+ ContractStorageUpdateRequest,
7
+ EthAddress,
8
+ L2ToL1Message,
9
+ NoteHash,
10
+ Nullifier,
11
+ ReadRequest,
12
+ SideEffect,
13
+ } from '@aztec/circuits.js';
3
14
  import { EventSelector } from '@aztec/foundation/abi';
4
15
  import { Fr } from '@aztec/foundation/fields';
5
16
  import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
6
17
 
18
+ import { type PublicExecutionResult } from '../../index.js';
7
19
  import { type HostStorage } from './host_storage.js';
8
20
  import { Nullifiers } from './nullifiers.js';
9
21
  import { PublicStorage } from './public_storage.js';
@@ -19,6 +31,7 @@ import {
19
31
  type TracedUnencryptedL2Log,
20
32
  } from './trace_types.js';
21
33
 
34
+ // TODO:(5818): do we need this type anymore?
22
35
  /**
23
36
  * Data held within the journal
24
37
  */
@@ -37,6 +50,25 @@ export type JournalData = {
37
50
  newLogsHashes: TracedUnencryptedL2Log[];
38
51
  /** contract address -\> key -\> value */
39
52
  currentStorageValue: Map<bigint, Map<bigint, Fr>>;
53
+
54
+ sideEffectCounter: number;
55
+ };
56
+
57
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
58
+ type PartialPublicExecutionResult = {
59
+ nullifierReadRequests: ReadRequest[];
60
+ nullifierNonExistentReadRequests: ReadRequest[];
61
+ newNoteHashes: NoteHash[];
62
+ newL2ToL1Messages: L2ToL1Message[];
63
+ startSideEffectCounter: number;
64
+ newNullifiers: Nullifier[];
65
+ contractStorageReads: ContractStorageRead[];
66
+ contractStorageUpdateRequests: ContractStorageUpdateRequest[];
67
+ unencryptedLogsHashes: SideEffect[];
68
+ unencryptedLogs: UnencryptedL2Log[];
69
+ unencryptedLogPreimagesLength: Fr;
70
+ allUnencryptedLogs: UnencryptedL2Log[];
71
+ nestedExecutions: PublicExecutionResult[];
40
72
  };
41
73
 
42
74
  /**
@@ -53,7 +85,7 @@ export class AvmPersistableStateManager {
53
85
  /** Reference to node storage */
54
86
  public readonly hostStorage: HostStorage;
55
87
 
56
- // TODO: make members private once this is not used in transitional_adaptors.ts.
88
+ // TODO(5818): make members private once this is not used in transitional_adaptors.ts.
57
89
  /** World State */
58
90
  /** Public storage, including cached writes */
59
91
  public publicStorage: PublicStorage;
@@ -67,11 +99,31 @@ export class AvmPersistableStateManager {
67
99
  public newL1Messages: L2ToL1Message[] = [];
68
100
  public newLogs: UnencryptedL2Log[] = [];
69
101
 
102
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
103
+ public transitionalExecutionResult: PartialPublicExecutionResult;
104
+
70
105
  constructor(hostStorage: HostStorage, parent?: AvmPersistableStateManager) {
71
106
  this.hostStorage = hostStorage;
72
107
  this.publicStorage = new PublicStorage(hostStorage.publicStateDb, parent?.publicStorage);
73
108
  this.nullifiers = new Nullifiers(hostStorage.commitmentsDb, parent?.nullifiers);
74
109
  this.trace = new WorldStateAccessTrace(parent?.trace);
110
+
111
+ this.transitionalExecutionResult = {
112
+ nullifierReadRequests: [],
113
+ nullifierNonExistentReadRequests: [],
114
+ newNoteHashes: [],
115
+ newL2ToL1Messages: [],
116
+ startSideEffectCounter: this.trace.accessCounter,
117
+ newNullifiers: [],
118
+ contractStorageReads: [],
119
+ contractStorageUpdateRequests: [],
120
+ unencryptedLogsHashes: [],
121
+ unencryptedLogs: [],
122
+ // The length starts at 4 because it will always include the size.
123
+ unencryptedLogPreimagesLength: new Fr(4),
124
+ allUnencryptedLogs: [],
125
+ nestedExecutions: [],
126
+ };
75
127
  }
76
128
 
77
129
  /**
@@ -92,6 +144,21 @@ export class AvmPersistableStateManager {
92
144
  this.log.debug(`storage(${storageAddress})@${slot} <- ${value}`);
93
145
  // Cache storage writes for later reference/reads
94
146
  this.publicStorage.write(storageAddress, slot, value);
147
+
148
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
149
+ // The current info to the kernel clears any previous read or write request.
150
+ this.transitionalExecutionResult.contractStorageReads =
151
+ this.transitionalExecutionResult.contractStorageReads.filter(
152
+ read => !read.storageSlot.equals(slot) || !read.contractAddress!.equals(storageAddress),
153
+ );
154
+ this.transitionalExecutionResult.contractStorageUpdateRequests =
155
+ this.transitionalExecutionResult.contractStorageUpdateRequests.filter(
156
+ update => !update.storageSlot.equals(slot) || !update.contractAddress!.equals(storageAddress),
157
+ );
158
+ this.transitionalExecutionResult.contractStorageUpdateRequests.push(
159
+ new ContractStorageUpdateRequest(slot, value, this.trace.accessCounter, storageAddress),
160
+ );
161
+
95
162
  // Trace all storage writes (even reverted ones)
96
163
  this.trace.tracePublicStorageWrite(storageAddress, slot, value);
97
164
  }
@@ -104,10 +171,24 @@ export class AvmPersistableStateManager {
104
171
  * @returns the latest value written to slot, or 0 if never written to before
105
172
  */
106
173
  public async readStorage(storageAddress: Fr, slot: Fr): Promise<Fr> {
107
- const [exists, value] = await this.publicStorage.read(storageAddress, slot);
108
- this.log.debug(`storage(${storageAddress})@${slot} ?? value: ${value}, exists: ${exists}.`);
174
+ const { value, exists, cached } = await this.publicStorage.read(storageAddress, slot);
175
+ this.log.debug(`storage(${storageAddress})@${slot} ?? value: ${value}, exists: ${exists}, cached: ${cached}.`);
176
+
177
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
178
+ // The current info to the kernel kernel does not consider cached reads.
179
+ if (!cached) {
180
+ // The current info to the kernel removes any previous reads to the same slot.
181
+ this.transitionalExecutionResult.contractStorageReads =
182
+ this.transitionalExecutionResult.contractStorageReads.filter(
183
+ read => !read.storageSlot.equals(slot) || !read.contractAddress!.equals(storageAddress),
184
+ );
185
+ this.transitionalExecutionResult.contractStorageReads.push(
186
+ new ContractStorageRead(slot, value, this.trace.accessCounter, storageAddress),
187
+ );
188
+ }
189
+
109
190
  // We want to keep track of all performed reads (even reverted ones)
110
- this.trace.tracePublicStorageRead(storageAddress, slot, value, exists);
191
+ this.trace.tracePublicStorageRead(storageAddress, slot, value, exists, cached);
111
192
  return Promise.resolve(value);
112
193
  }
113
194
 
@@ -133,6 +214,9 @@ export class AvmPersistableStateManager {
133
214
  * @param noteHash - the unsiloed note hash to write
134
215
  */
135
216
  public writeNoteHash(storageAddress: Fr, noteHash: Fr) {
217
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
218
+ this.transitionalExecutionResult.newNoteHashes.push(new NoteHash(noteHash, this.trace.accessCounter));
219
+
136
220
  this.log.debug(`noteHashes(${storageAddress}) += @${noteHash}.`);
137
221
  this.trace.traceNewNoteHash(storageAddress, noteHash);
138
222
  }
@@ -148,6 +232,16 @@ export class AvmPersistableStateManager {
148
232
  this.log.debug(
149
233
  `nullifiers(${storageAddress})@${nullifier} ?? leafIndex: ${leafIndex}, pending: ${isPending}, exists: ${exists}.`,
150
234
  );
235
+
236
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
237
+ if (exists) {
238
+ this.transitionalExecutionResult.nullifierReadRequests.push(new ReadRequest(nullifier, this.trace.accessCounter));
239
+ } else {
240
+ this.transitionalExecutionResult.nullifierNonExistentReadRequests.push(
241
+ new ReadRequest(nullifier, this.trace.accessCounter),
242
+ );
243
+ }
244
+
151
245
  this.trace.traceNullifierCheck(storageAddress, nullifier, exists, isPending, leafIndex);
152
246
  return Promise.resolve(exists);
153
247
  }
@@ -158,6 +252,9 @@ export class AvmPersistableStateManager {
158
252
  * @param nullifier - the unsiloed nullifier to write
159
253
  */
160
254
  public async writeNullifier(storageAddress: Fr, nullifier: Fr) {
255
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
256
+ this.transitionalExecutionResult.newNullifiers.push(new Nullifier(nullifier, this.trace.accessCounter, Fr.ZERO));
257
+
161
258
  this.log.debug(`nullifiers(${storageAddress}) += ${nullifier}.`);
162
259
  // Cache pending nullifiers for later access
163
260
  await this.nullifiers.append(storageAddress, nullifier);
@@ -189,18 +286,39 @@ export class AvmPersistableStateManager {
189
286
  public writeL1Message(recipient: EthAddress | Fr, content: Fr) {
190
287
  this.log.debug(`L1Messages(${recipient}) += ${content}.`);
191
288
  const recipientAddress = recipient instanceof EthAddress ? recipient : EthAddress.fromField(recipient);
192
- this.newL1Messages.push(new L2ToL1Message(recipientAddress, content));
289
+ const message = new L2ToL1Message(recipientAddress, content, 0);
290
+ this.newL1Messages.push(message);
291
+
292
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
293
+ this.transitionalExecutionResult.newL2ToL1Messages.push(message);
193
294
  }
194
295
 
195
296
  public writeLog(contractAddress: Fr, event: Fr, log: Fr[]) {
196
297
  this.log.debug(`UnencryptedL2Log(${contractAddress}) += event ${event} with ${log.length} fields.`);
197
- const L2log = new UnencryptedL2Log(
298
+ const ulog = new UnencryptedL2Log(
198
299
  AztecAddress.fromField(contractAddress),
199
300
  EventSelector.fromField(event),
200
301
  Buffer.concat(log.map(f => f.toBuffer())),
201
302
  );
202
- this.newLogs.push(L2log);
203
- this.trace.traceNewLog(Fr.fromBuffer(L2log.hash()));
303
+ const logHash = Fr.fromBuffer(ulog.hash());
304
+
305
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
306
+ this.transitionalExecutionResult.unencryptedLogs.push(ulog);
307
+ this.transitionalExecutionResult.allUnencryptedLogs.push(ulog);
308
+ // this duplicates exactly what happens in the trace just for the purpose of transitional integration with the kernel
309
+ this.transitionalExecutionResult.unencryptedLogsHashes.push(
310
+ new SideEffect(logHash, new Fr(this.trace.accessCounter)),
311
+ );
312
+ // Duplicates computation performed in public_context.nr::emit_unencrypted_log
313
+ // 44 = addr (32) + selector (4) + raw log len (4) + processed log len (4).
314
+ this.transitionalExecutionResult.unencryptedLogPreimagesLength = new Fr(
315
+ this.transitionalExecutionResult.unencryptedLogPreimagesLength.toNumber() + 44 + log.length * Fr.SIZE_IN_BYTES,
316
+ );
317
+ // TODO(6206): likely need to track this here and not just in the transitional logic.
318
+
319
+ // TODO(6205): why are logs pushed here but logs hashes are traced?
320
+ this.newLogs.push(ulog);
321
+ this.trace.traceNewLog(logHash);
204
322
  }
205
323
 
206
324
  /**
@@ -216,6 +334,11 @@ export class AvmPersistableStateManager {
216
334
  // Accrued Substate
217
335
  this.newL1Messages = this.newL1Messages.concat(nestedJournal.newL1Messages);
218
336
  this.newLogs = this.newLogs.concat(nestedJournal.newLogs);
337
+
338
+ // TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
339
+ this.transitionalExecutionResult.allUnencryptedLogs.concat(
340
+ nestedJournal.transitionalExecutionResult.allUnencryptedLogs,
341
+ );
219
342
  }
220
343
 
221
344
  /**
@@ -226,6 +349,7 @@ export class AvmPersistableStateManager {
226
349
  this.trace.acceptAndMerge(nestedJournal.trace);
227
350
  }
228
351
 
352
+ // TODO:(5818): do we need this type anymore?
229
353
  /**
230
354
  * Access the current state of the journal
231
355
  *
@@ -244,6 +368,7 @@ export class AvmPersistableStateManager {
244
368
  currentStorageValue: this.publicStorage.getCache().cachePerContract,
245
369
  storageReads: this.trace.publicStorageReads,
246
370
  storageWrites: this.trace.publicStorageWrites,
371
+ sideEffectCounter: this.trace.accessCounter,
247
372
  };
248
373
  }
249
374
  }
@@ -1,3 +1,4 @@
1
+ import { AztecAddress } from '@aztec/circuits.js';
1
2
  import { siloNullifier } from '@aztec/circuits.js/hash';
2
3
  import { Fr } from '@aztec/foundation/fields';
3
4
 
@@ -10,7 +11,7 @@ import type { CommitmentsDB } from '../../index.js';
10
11
  */
11
12
  export class Nullifiers {
12
13
  /** Cached nullifiers. */
13
- private cache: NullifierCache;
14
+ public cache: NullifierCache;
14
15
  /** Parent's nullifier cache. Checked on cache-miss. */
15
16
  private readonly parentCache: NullifierCache | undefined;
16
17
  /** Reference to node storage. Checked on parent cache-miss. */
@@ -95,6 +96,7 @@ export class NullifierCache {
95
96
  * each entry being a nullifier.
96
97
  */
97
98
  private cachePerContract: Map<bigint, Set<bigint>> = new Map();
99
+ private siloedNullifiers: Set<bigint> = new Set();
98
100
 
99
101
  /**
100
102
  * Check whether a nullifier exists in the cache.
@@ -104,8 +106,10 @@ export class NullifierCache {
104
106
  * @returns whether the nullifier is found in the cache
105
107
  */
106
108
  public exists(storageAddress: Fr, nullifier: Fr): boolean {
107
- const exists = this.cachePerContract.get(storageAddress.toBigInt())?.has(nullifier.toBigInt());
108
- return exists ? true : false;
109
+ const exists =
110
+ this.cachePerContract.get(storageAddress.toBigInt())?.has(nullifier.toBigInt()) ||
111
+ this.siloedNullifiers.has(siloNullifier(AztecAddress.fromField(storageAddress), nullifier).toBigInt());
112
+ return !!exists;
109
113
  }
110
114
 
111
115
  /**
@@ -115,20 +119,25 @@ export class NullifierCache {
115
119
  * @param nullifier - the nullifier to stage
116
120
  */
117
121
  public append(storageAddress: Fr, nullifier: Fr) {
122
+ if (this.exists(storageAddress, nullifier)) {
123
+ throw new NullifierCollisionError(
124
+ `Nullifier ${nullifier} at contract ${storageAddress} already exists in cache.`,
125
+ );
126
+ }
127
+
118
128
  let nullifiersForContract = this.cachePerContract.get(storageAddress.toBigInt());
119
129
  // If this contract's nullifier set has no cached nullifiers, create a new Set to store them
120
130
  if (!nullifiersForContract) {
121
131
  nullifiersForContract = new Set();
122
132
  this.cachePerContract.set(storageAddress.toBigInt(), nullifiersForContract);
123
133
  }
124
- if (nullifiersForContract.has(nullifier.toBigInt())) {
125
- throw new NullifierCollisionError(
126
- `Nullifier ${nullifier} at contract ${storageAddress} already exists in cache.`,
127
- );
128
- }
129
134
  nullifiersForContract.add(nullifier.toBigInt());
130
135
  }
131
136
 
137
+ public appendSiloed(siloedNullifier: Fr) {
138
+ this.siloedNullifiers.add(siloedNullifier.toBigInt());
139
+ }
140
+
132
141
  /**
133
142
  * Merge another cache's nullifiers into this instance's.
134
143
  *
@@ -139,6 +148,8 @@ export class NullifierCache {
139
148
  * @param incomingNullifiers - the incoming cached nullifiers to merge into this instance's
140
149
  */
141
150
  public acceptAndMerge(incomingNullifiers: NullifierCache) {
151
+ // Merge siloed nullifiers.
152
+ this.siloedNullifiers = new Set([...this.siloedNullifiers, ...incomingNullifiers.siloedNullifiers]);
142
153
  // Iterate over all contracts with staged writes in the child.
143
154
  for (const [incomingAddress, incomingCacheAtContract] of incomingNullifiers.cachePerContract) {
144
155
  const thisCacheAtContract = this.cachePerContract.get(incomingAddress);
@@ -1,7 +1,14 @@
1
+ import { AztecAddress } from '@aztec/circuits.js';
1
2
  import { Fr } from '@aztec/foundation/fields';
2
3
 
3
4
  import type { PublicStateDB } from '../../index.js';
4
5
 
6
+ type PublicStorageReadResult = {
7
+ value: Fr;
8
+ exists: boolean;
9
+ cached: boolean;
10
+ };
11
+
5
12
  /**
6
13
  * A class to manage public storage reads and writes during a contract call's AVM simulation.
7
14
  * Maintains a storage write cache, and ensures that reads fall back to the correct source.
@@ -39,7 +46,8 @@ export class PublicStorage {
39
46
  * @param slot - the slot in the contract's storage being read from
40
47
  * @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
48
  */
42
- public async read(storageAddress: Fr, slot: Fr): Promise<[/*exists=*/ boolean, /*value=*/ Fr]> {
49
+ public async read(storageAddress: Fr, slot: Fr): Promise<PublicStorageReadResult> {
50
+ let cached = false;
43
51
  // First try check this storage cache
44
52
  let value = this.cache.read(storageAddress, slot);
45
53
  // Then try parent's storage cache (if it exists / written to earlier in this TX)
@@ -49,11 +57,13 @@ export class PublicStorage {
49
57
  // Finally try the host's Aztec state (a trip to the database)
50
58
  if (!value) {
51
59
  value = await this.hostPublicStorage.storageRead(storageAddress, slot);
60
+ } else {
61
+ cached = true;
52
62
  }
53
63
  // if value is undefined, that means this slot has never been written to!
54
64
  const exists = value !== undefined;
55
65
  const valueOrZero = exists ? value : Fr.ZERO;
56
- return Promise.resolve([exists, valueOrZero]);
66
+ return Promise.resolve({ value: valueOrZero, exists, cached });
57
67
  }
58
68
 
59
69
  /**
@@ -75,6 +85,17 @@ export class PublicStorage {
75
85
  public acceptAndMerge(incomingPublicStorage: PublicStorage) {
76
86
  this.cache.acceptAndMerge(incomingPublicStorage.cache);
77
87
  }
88
+
89
+ /**
90
+ * Commits ALL staged writes to the host's state.
91
+ */
92
+ public async commitToDB() {
93
+ for (const [storageAddress, cacheAtContract] of this.cache.cachePerContract) {
94
+ for (const [slot, value] of cacheAtContract) {
95
+ await this.hostPublicStorage.storageWrite(AztecAddress.fromBigInt(storageAddress), new Fr(slot), value);
96
+ }
97
+ }
98
+ }
78
99
  }
79
100
 
80
101
  /**
@@ -29,13 +29,14 @@ export class WorldStateAccessTrace {
29
29
 
30
30
  constructor(parentTrace?: WorldStateAccessTrace) {
31
31
  this.accessCounter = parentTrace ? parentTrace.accessCounter : 0;
32
+ // TODO(4805): consider tracking the parent's trace vector lengths so we can enforce limits
32
33
  }
33
34
 
34
35
  public getAccessCounter() {
35
36
  return this.accessCounter;
36
37
  }
37
38
 
38
- public tracePublicStorageRead(storageAddress: Fr, slot: Fr, value: Fr, exists: boolean) {
39
+ public tracePublicStorageRead(storageAddress: Fr, slot: Fr, value: Fr, exists: boolean, cached: boolean) {
39
40
  // TODO(4805): check if some threshold is reached for max storage reads
40
41
  // (need access to parent length, or trace needs to be initialized with parent's contents)
41
42
  const traced: TracedPublicStorageRead = {
@@ -44,6 +45,7 @@ export class WorldStateAccessTrace {
44
45
  slot,
45
46
  value,
46
47
  exists,
48
+ cached,
47
49
  counter: new Fr(this.accessCounter),
48
50
  // endLifetime: Fr.ZERO,
49
51
  };
@@ -151,9 +153,6 @@ export class WorldStateAccessTrace {
151
153
  /**
152
154
  * Merges another trace into this one
153
155
  *
154
- * - Public state journals (r/w logs), with the accessing being appended in chronological order
155
- * - Utxo objects are concatenated
156
- *
157
156
  * @param incomingTrace - the incoming trace to merge into this instance
158
157
  */
159
158
  public acceptAndMerge(incomingTrace: WorldStateAccessTrace) {
@@ -11,6 +11,7 @@ export type TracedPublicStorageRead = {
11
11
  // callPointer: Fr;
12
12
  storageAddress: Fr;
13
13
  exists: boolean;
14
+ cached: boolean;
14
15
  slot: Fr;
15
16
  value: Fr;
16
17
  counter: Fr;
@@ -1,5 +1,5 @@
1
1
  import type { AvmContext } from '../avm_context.js';
2
- import { type IntegralValue } from '../avm_memory_types.js';
2
+ import { type IntegralValue, type TaggedMemoryInterface, TypeTag } from '../avm_memory_types.js';
3
3
  import { Opcode } from '../serialization/instruction_serialization.js';
4
4
  import { ThreeOperandInstruction, TwoOperandInstruction } from './instruction_impl.js';
5
5
 
@@ -9,7 +9,7 @@ abstract class ThreeOperandBitwiseInstruction extends ThreeOperandInstruction {
9
9
  const memory = context.machineState.memory.track(this.type);
10
10
  context.machineState.consumeGas(this.gasCost(memoryOperations));
11
11
 
12
- memory.checkTags(this.inTag, this.aOffset, this.bOffset);
12
+ this.checkTags(memory, this.inTag, this.aOffset, this.bOffset);
13
13
 
14
14
  const a = memory.getAs<IntegralValue>(this.aOffset);
15
15
  const b = memory.getAs<IntegralValue>(this.bOffset);
@@ -22,13 +22,16 @@ abstract class ThreeOperandBitwiseInstruction extends ThreeOperandInstruction {
22
22
  }
23
23
 
24
24
  protected abstract compute(a: IntegralValue, b: IntegralValue): IntegralValue;
25
+ protected checkTags(memory: TaggedMemoryInterface, inTag: number, aOffset: number, bOffset: number) {
26
+ memory.checkTags(inTag, aOffset, bOffset);
27
+ }
25
28
  }
26
29
 
27
30
  export class And extends ThreeOperandBitwiseInstruction {
28
31
  static readonly type: string = 'AND';
29
32
  static readonly opcode = Opcode.AND;
30
33
 
31
- protected compute(a: IntegralValue, b: IntegralValue): IntegralValue {
34
+ protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue {
32
35
  return a.and(b);
33
36
  }
34
37
  }
@@ -37,7 +40,7 @@ export class Or extends ThreeOperandBitwiseInstruction {
37
40
  static readonly type: string = 'OR';
38
41
  static readonly opcode = Opcode.OR;
39
42
 
40
- protected compute(a: IntegralValue, b: IntegralValue): IntegralValue {
43
+ protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue {
41
44
  return a.or(b);
42
45
  }
43
46
  }
@@ -46,7 +49,7 @@ export class Xor extends ThreeOperandBitwiseInstruction {
46
49
  static readonly type: string = 'XOR';
47
50
  static readonly opcode = Opcode.XOR;
48
51
 
49
- protected compute(a: IntegralValue, b: IntegralValue): IntegralValue {
52
+ protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue {
50
53
  return a.xor(b);
51
54
  }
52
55
  }
@@ -55,18 +58,26 @@ export class Shl extends ThreeOperandBitwiseInstruction {
55
58
  static readonly type: string = 'SHL';
56
59
  static readonly opcode = Opcode.SHL;
57
60
 
58
- protected compute(a: IntegralValue, b: IntegralValue): IntegralValue {
61
+ protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue {
59
62
  return a.shl(b);
60
63
  }
64
+ protected override checkTags(memory: TaggedMemoryInterface, inTag: number, aOffset: number, bOffset: number) {
65
+ memory.checkTag(inTag, aOffset);
66
+ memory.checkTag(TypeTag.UINT8, bOffset);
67
+ }
61
68
  }
62
69
 
63
70
  export class Shr extends ThreeOperandBitwiseInstruction {
64
71
  static readonly type: string = 'SHR';
65
72
  static readonly opcode = Opcode.SHR;
66
73
 
67
- protected compute(a: IntegralValue, b: IntegralValue): IntegralValue {
74
+ protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue {
68
75
  return a.shr(b);
69
76
  }
77
+ protected override checkTags(memory: TaggedMemoryInterface, inTag: number, aOffset: number, bOffset: number) {
78
+ memory.checkTag(inTag, aOffset);
79
+ memory.checkTag(TypeTag.UINT8, bOffset);
80
+ }
70
81
  }
71
82
 
72
83
  export class Not extends TwoOperandInstruction {
@@ -0,0 +1,59 @@
1
+ import { strict as assert } from 'assert';
2
+
3
+ import { type AvmContext } from '../avm_context.js';
4
+ import { TypeTag, Uint8 } from '../avm_memory_types.js';
5
+ import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
6
+ import { Addressing } from './addressing_mode.js';
7
+ import { Instruction } from './instruction.js';
8
+
9
+ export class ToRadixLE extends Instruction {
10
+ static type: string = 'TORADIXLE';
11
+ static readonly opcode: Opcode = Opcode.TORADIXLE;
12
+
13
+ // Informs (de)serialization. See Instruction.deserialize.
14
+ static readonly wireFormat: OperandType[] = [
15
+ OperandType.UINT8, // Opcode
16
+ OperandType.UINT8, // Indirect
17
+ OperandType.UINT32, // src memory address
18
+ OperandType.UINT32, // dst memory address
19
+ OperandType.UINT32, // radix (immediate)
20
+ OperandType.UINT32, // number of limbs (Immediate)
21
+ ];
22
+
23
+ constructor(
24
+ private indirect: number,
25
+ private srcOffset: number,
26
+ private dstOffset: number,
27
+ private radix: number,
28
+ private numLimbs: number,
29
+ ) {
30
+ assert(radix <= 256, 'Radix cannot be greater than 256');
31
+ super();
32
+ }
33
+
34
+ public async execute(context: AvmContext): Promise<void> {
35
+ const memory = context.machineState.memory.track(this.type);
36
+ const [srcOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve([this.srcOffset, this.dstOffset], memory);
37
+ const memoryOperations = { reads: 1, writes: this.numLimbs, indirect: this.indirect };
38
+ context.machineState.consumeGas(this.gasCost(memoryOperations));
39
+
40
+ // The radix gadget only takes in a Field
41
+ memory.checkTag(TypeTag.FIELD, srcOffset);
42
+
43
+ let value: bigint = memory.get(srcOffset).toBigInt();
44
+ const radixBN: bigint = BigInt(this.radix);
45
+ const limbArray = [];
46
+
47
+ for (let i = 0; i < this.numLimbs; i++) {
48
+ const limb = value % radixBN;
49
+ limbArray.push(limb);
50
+ value /= radixBN;
51
+ }
52
+
53
+ const res = [...limbArray].map(byte => new Uint8(byte));
54
+ memory.setSlice(dstOffset, res);
55
+
56
+ memory.assert(memoryOperations);
57
+ context.machineState.incrementPc();
58
+ }
59
+ }