@aztec/simulator 0.32.0 → 0.33.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 (227) hide show
  1. package/README.md +5 -3
  2. package/dest/acvm/acvm.d.ts +5 -5
  3. package/dest/acvm/acvm.d.ts.map +1 -1
  4. package/dest/acvm/acvm_types.d.ts +1 -1
  5. package/dest/acvm/acvm_types.d.ts.map +1 -1
  6. package/dest/acvm/deserialize.d.ts +1 -1
  7. package/dest/acvm/deserialize.d.ts.map +1 -1
  8. package/dest/acvm/oracle/index.d.ts +1 -2
  9. package/dest/acvm/oracle/index.d.ts.map +1 -1
  10. package/dest/acvm/oracle/index.js +1 -2
  11. package/dest/acvm/oracle/oracle.d.ts +2 -2
  12. package/dest/acvm/oracle/oracle.d.ts.map +1 -1
  13. package/dest/acvm/oracle/oracle.js +2 -3
  14. package/dest/acvm/oracle/typed_oracle.d.ts +6 -6
  15. package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
  16. package/dest/acvm/serialize.d.ts +4 -4
  17. package/dest/acvm/serialize.d.ts.map +1 -1
  18. package/dest/acvm/serialize.js +1 -1
  19. package/dest/avm/avm_context.d.ts +8 -18
  20. package/dest/avm/avm_context.d.ts.map +1 -1
  21. package/dest/avm/avm_context.js +10 -22
  22. package/dest/avm/avm_execution_environment.d.ts +4 -4
  23. package/dest/avm/avm_execution_environment.d.ts.map +1 -1
  24. package/dest/avm/avm_execution_environment.js +1 -1
  25. package/dest/avm/avm_gas.d.ts +71 -0
  26. package/dest/avm/avm_gas.d.ts.map +1 -0
  27. package/dest/avm/avm_gas.js +161 -0
  28. package/dest/avm/avm_machine_state.d.ts +5 -3
  29. package/dest/avm/avm_machine_state.d.ts.map +1 -1
  30. package/dest/avm/avm_machine_state.js +8 -2
  31. package/dest/avm/avm_memory_types.d.ts +54 -2
  32. package/dest/avm/avm_memory_types.d.ts.map +1 -1
  33. package/dest/avm/avm_memory_types.js +95 -2
  34. package/dest/avm/avm_message_call_result.d.ts +1 -1
  35. package/dest/avm/avm_message_call_result.d.ts.map +1 -1
  36. package/dest/avm/avm_simulator.d.ts.map +1 -1
  37. package/dest/avm/avm_simulator.js +10 -8
  38. package/dest/avm/errors.d.ts +1 -1
  39. package/dest/avm/errors.d.ts.map +1 -1
  40. package/dest/avm/fixtures/index.d.ts +1 -1
  41. package/dest/avm/fixtures/index.d.ts.map +1 -1
  42. package/dest/avm/fixtures/index.js +2 -2
  43. package/dest/avm/journal/host_storage.d.ts +1 -1
  44. package/dest/avm/journal/host_storage.d.ts.map +1 -1
  45. package/dest/avm/journal/journal.d.ts +2 -2
  46. package/dest/avm/journal/journal.d.ts.map +1 -1
  47. package/dest/avm/journal/trace.d.ts +1 -1
  48. package/dest/avm/journal/trace.d.ts.map +1 -1
  49. package/dest/avm/journal/trace_types.d.ts +1 -1
  50. package/dest/avm/journal/trace_types.d.ts.map +1 -1
  51. package/dest/avm/opcodes/accrued_substate.d.ts.map +1 -1
  52. package/dest/avm/opcodes/accrued_substate.js +44 -16
  53. package/dest/avm/opcodes/addressing_mode.d.ts +5 -3
  54. package/dest/avm/opcodes/addressing_mode.d.ts.map +1 -1
  55. package/dest/avm/opcodes/addressing_mode.js +5 -1
  56. package/dest/avm/opcodes/arithmetic.d.ts +7 -3
  57. package/dest/avm/opcodes/arithmetic.d.ts.map +1 -1
  58. package/dest/avm/opcodes/arithmetic.js +27 -16
  59. package/dest/avm/opcodes/bitwise.d.ts +21 -20
  60. package/dest/avm/opcodes/bitwise.d.ts.map +1 -1
  61. package/dest/avm/opcodes/bitwise.js +43 -65
  62. package/dest/avm/opcodes/comparators.d.ts +12 -9
  63. package/dest/avm/opcodes/comparators.d.ts.map +1 -1
  64. package/dest/avm/opcodes/comparators.js +22 -32
  65. package/dest/avm/opcodes/context_getters.d.ts +20 -0
  66. package/dest/avm/opcodes/context_getters.d.ts.map +1 -0
  67. package/dest/avm/opcodes/context_getters.js +26 -0
  68. package/dest/avm/opcodes/contract.d.ts +14 -0
  69. package/dest/avm/opcodes/contract.d.ts.map +1 -0
  70. package/dest/avm/opcodes/contract.js +49 -0
  71. package/dest/avm/opcodes/control_flow.d.ts.map +1 -1
  72. package/dest/avm/opcodes/control_flow.js +12 -2
  73. package/dest/avm/opcodes/environment_getters.d.ts +31 -34
  74. package/dest/avm/opcodes/environment_getters.d.ts.map +1 -1
  75. package/dest/avm/opcodes/environment_getters.js +34 -43
  76. package/dest/avm/opcodes/external_calls.d.ts +13 -19
  77. package/dest/avm/opcodes/external_calls.d.ts.map +1 -1
  78. package/dest/avm/opcodes/external_calls.js +50 -68
  79. package/dest/avm/opcodes/hashing.d.ts +3 -2
  80. package/dest/avm/opcodes/hashing.d.ts.map +1 -1
  81. package/dest/avm/opcodes/hashing.js +37 -18
  82. package/dest/avm/opcodes/index.d.ts +1 -0
  83. package/dest/avm/opcodes/index.d.ts.map +1 -1
  84. package/dest/avm/opcodes/index.js +2 -1
  85. package/dest/avm/opcodes/instruction.d.ts +12 -17
  86. package/dest/avm/opcodes/instruction.d.ts.map +1 -1
  87. package/dest/avm/opcodes/instruction.js +12 -22
  88. package/dest/avm/opcodes/instruction_impl.d.ts +14 -0
  89. package/dest/avm/opcodes/instruction_impl.d.ts.map +1 -1
  90. package/dest/avm/opcodes/instruction_impl.js +37 -16
  91. package/dest/avm/opcodes/memory.d.ts +4 -3
  92. package/dest/avm/opcodes/memory.d.ts.map +1 -1
  93. package/dest/avm/opcodes/memory.js +38 -19
  94. package/dest/avm/opcodes/storage.d.ts +5 -0
  95. package/dest/avm/opcodes/storage.d.ts.map +1 -1
  96. package/dest/avm/opcodes/storage.js +21 -7
  97. package/dest/avm/serialization/bytecode_serialization.d.ts.map +1 -1
  98. package/dest/avm/serialization/bytecode_serialization.js +7 -5
  99. package/dest/avm/serialization/instruction_serialization.d.ts +12 -11
  100. package/dest/avm/serialization/instruction_serialization.d.ts.map +1 -1
  101. package/dest/avm/serialization/instruction_serialization.js +13 -12
  102. package/dest/avm/temporary_executor_migration.d.ts +6 -4
  103. package/dest/avm/temporary_executor_migration.d.ts.map +1 -1
  104. package/dest/avm/temporary_executor_migration.js +14 -3
  105. package/dest/client/client_execution_context.d.ts +11 -11
  106. package/dest/client/client_execution_context.d.ts.map +1 -1
  107. package/dest/client/client_execution_context.js +1 -1
  108. package/dest/client/db_oracle.d.ts +9 -9
  109. package/dest/client/db_oracle.d.ts.map +1 -1
  110. package/dest/client/db_oracle.js +1 -1
  111. package/dest/client/execution_note_cache.d.ts +2 -2
  112. package/dest/client/execution_note_cache.d.ts.map +1 -1
  113. package/dest/client/execution_result.d.ts +5 -5
  114. package/dest/client/execution_result.d.ts.map +1 -1
  115. package/dest/client/execution_result.js +1 -1
  116. package/dest/client/pick_notes.d.ts +1 -1
  117. package/dest/client/pick_notes.d.ts.map +1 -1
  118. package/dest/client/pick_notes.js +1 -1
  119. package/dest/client/private_execution.d.ts +5 -5
  120. package/dest/client/private_execution.d.ts.map +1 -1
  121. package/dest/client/private_execution.js +6 -2
  122. package/dest/client/simulator.d.ts +6 -6
  123. package/dest/client/simulator.d.ts.map +1 -1
  124. package/dest/client/simulator.js +1 -1
  125. package/dest/client/unconstrained_execution.d.ts +5 -5
  126. package/dest/client/unconstrained_execution.d.ts.map +1 -1
  127. package/dest/client/unconstrained_execution.js +1 -1
  128. package/dest/client/view_data_oracle.d.ts +6 -6
  129. package/dest/client/view_data_oracle.d.ts.map +1 -1
  130. package/dest/client/view_data_oracle.js +1 -1
  131. package/dest/common/errors.d.ts +1 -1
  132. package/dest/common/errors.d.ts.map +1 -1
  133. package/dest/common/errors.js +1 -1
  134. package/dest/public/db.d.ts +6 -6
  135. package/dest/public/db.d.ts.map +1 -1
  136. package/dest/public/execution.d.ts +2 -2
  137. package/dest/public/execution.d.ts.map +1 -1
  138. package/dest/public/executor.d.ts +13 -5
  139. package/dest/public/executor.d.ts.map +1 -1
  140. package/dest/public/executor.js +59 -20
  141. package/dest/public/index.d.ts +1 -1
  142. package/dest/public/index.d.ts.map +1 -1
  143. package/dest/public/public_execution_context.d.ts +7 -7
  144. package/dest/public/public_execution_context.d.ts.map +1 -1
  145. package/dest/public/public_execution_context.js +3 -3
  146. package/dest/public/state_actions.d.ts +2 -2
  147. package/dest/public/state_actions.d.ts.map +1 -1
  148. package/dest/simulator/acvm_native.d.ts +3 -3
  149. package/dest/simulator/acvm_native.d.ts.map +1 -1
  150. package/dest/simulator/acvm_wasm.d.ts +3 -3
  151. package/dest/simulator/acvm_wasm.d.ts.map +1 -1
  152. package/dest/simulator/acvm_wasm.js +2 -2
  153. package/dest/simulator/simulation_provider.d.ts +2 -2
  154. package/dest/simulator/simulation_provider.d.ts.map +1 -1
  155. package/dest/test/utils.d.ts +1 -1
  156. package/dest/test/utils.d.ts.map +1 -1
  157. package/dest/test/utils.js +1 -1
  158. package/dest/utils.d.ts +1 -1
  159. package/dest/utils.d.ts.map +1 -1
  160. package/package.json +15 -9
  161. package/src/acvm/acvm.ts +8 -8
  162. package/src/acvm/acvm_types.ts +1 -1
  163. package/src/acvm/deserialize.ts +1 -1
  164. package/src/acvm/oracle/index.ts +1 -2
  165. package/src/acvm/oracle/oracle.ts +3 -4
  166. package/src/acvm/oracle/typed_oracle.ts +18 -18
  167. package/src/acvm/serialize.ts +4 -4
  168. package/src/avm/avm_context.ts +15 -37
  169. package/src/avm/avm_execution_environment.ts +4 -4
  170. package/src/avm/{avm_gas_cost.ts → avm_gas.ts} +75 -21
  171. package/src/avm/avm_machine_state.ts +10 -3
  172. package/src/avm/avm_memory_types.ts +131 -3
  173. package/src/avm/avm_message_call_result.ts +1 -1
  174. package/src/avm/avm_simulator.ts +10 -8
  175. package/src/avm/errors.ts +1 -1
  176. package/src/avm/fixtures/index.ts +6 -1
  177. package/src/avm/journal/host_storage.ts +1 -1
  178. package/src/avm/journal/journal.ts +2 -2
  179. package/src/avm/journal/trace.ts +1 -1
  180. package/src/avm/journal/trace_types.ts +1 -1
  181. package/src/avm/opcodes/accrued_substate.ts +57 -22
  182. package/src/avm/opcodes/addressing_mode.ts +8 -3
  183. package/src/avm/opcodes/arithmetic.ts +32 -22
  184. package/src/avm/opcodes/bitwise.ts +50 -84
  185. package/src/avm/opcodes/comparators.ts +28 -43
  186. package/src/avm/opcodes/context_getters.ts +32 -0
  187. package/src/avm/opcodes/contract.ts +58 -0
  188. package/src/avm/opcodes/control_flow.ts +24 -6
  189. package/src/avm/opcodes/environment_getters.ts +36 -45
  190. package/src/avm/opcodes/external_calls.ts +65 -84
  191. package/src/avm/opcodes/hashing.ts +46 -23
  192. package/src/avm/opcodes/index.ts +1 -0
  193. package/src/avm/opcodes/instruction.ts +16 -28
  194. package/src/avm/opcodes/instruction_impl.ts +45 -15
  195. package/src/avm/opcodes/memory.ts +48 -28
  196. package/src/avm/opcodes/storage.ts +26 -12
  197. package/src/avm/serialization/bytecode_serialization.ts +6 -3
  198. package/src/avm/serialization/instruction_serialization.ts +1 -0
  199. package/src/avm/temporary_executor_migration.ts +20 -6
  200. package/src/client/client_execution_context.ts +15 -15
  201. package/src/client/db_oracle.ts +16 -10
  202. package/src/client/execution_note_cache.ts +2 -2
  203. package/src/client/execution_result.ts +9 -5
  204. package/src/client/pick_notes.ts +1 -1
  205. package/src/client/private_execution.ts +12 -6
  206. package/src/client/simulator.ts +8 -8
  207. package/src/client/unconstrained_execution.ts +5 -5
  208. package/src/client/view_data_oracle.ts +11 -11
  209. package/src/common/errors.ts +1 -1
  210. package/src/public/db.ts +6 -6
  211. package/src/public/execution.ts +10 -10
  212. package/src/public/executor.ts +78 -25
  213. package/src/public/index.ts +2 -2
  214. package/src/public/public_execution_context.ts +17 -7
  215. package/src/public/state_actions.ts +2 -2
  216. package/src/simulator/acvm_native.ts +3 -3
  217. package/src/simulator/acvm_wasm.ts +8 -4
  218. package/src/simulator/simulation_provider.ts +2 -2
  219. package/src/test/utils.ts +1 -1
  220. package/src/utils.ts +1 -1
  221. package/dest/acvm/oracle/debug.d.ts +0 -19
  222. package/dest/acvm/oracle/debug.d.ts.map +0 -1
  223. package/dest/acvm/oracle/debug.js +0 -95
  224. package/dest/avm/avm_gas_cost.d.ts +0 -322
  225. package/dest/avm/avm_gas_cost.d.ts.map +0 -1
  226. package/dest/avm/avm_gas_cost.js +0 -118
  227. package/src/acvm/oracle/debug.ts +0 -109
@@ -1,20 +1,49 @@
1
1
  import { TypeTag } from './avm_memory_types.js';
2
+ import { InstructionExecutionError } from './errors.js';
3
+ import { Addressing, AddressingMode } from './opcodes/addressing_mode.js';
2
4
  import { Opcode } from './serialization/instruction_serialization.js';
3
5
 
4
- /** Gas cost in L1, L2, and DA for a given instruction. */
5
- export type GasCost = {
6
+ /** Gas counters in L1, L2, and DA. */
7
+ export type Gas = {
6
8
  l1Gas: number;
7
9
  l2Gas: number;
8
10
  daGas: number;
9
11
  };
10
12
 
13
+ /** Maps a Gas struct to gasLeft properties. */
14
+ export function gasToGasLeft(gas: Gas) {
15
+ return { l1GasLeft: gas.l1Gas, l2GasLeft: gas.l2Gas, daGasLeft: gas.daGas };
16
+ }
17
+
18
+ /** Maps gasLeft properties to a gas struct. */
19
+ export function gasLeftToGas(gasLeft: { l1GasLeft: number; l2GasLeft: number; daGasLeft: number }) {
20
+ return { l1Gas: gasLeft.l1GasLeft, l2Gas: gasLeft.l2GasLeft, daGas: gasLeft.daGasLeft };
21
+ }
22
+
11
23
  /** Creates a new instance with all values set to zero except the ones set. */
12
- export function makeGasCost(gasCost: Partial<GasCost>) {
13
- return { ...EmptyGasCost, ...gasCost };
24
+ export function makeGas(gasCost: Partial<Gas>) {
25
+ return { ...EmptyGas, ...gasCost };
26
+ }
27
+
28
+ /** Sums together multiple instances of Gas. */
29
+ export function sumGas(...gases: Partial<Gas>[]) {
30
+ return gases.reduce(
31
+ (acc: Gas, gas) => ({
32
+ l1Gas: acc.l1Gas + (gas.l1Gas ?? 0),
33
+ l2Gas: acc.l2Gas + (gas.l2Gas ?? 0),
34
+ daGas: acc.daGas + (gas.daGas ?? 0),
35
+ }),
36
+ EmptyGas,
37
+ );
38
+ }
39
+
40
+ /** Multiplies a gas instance by a scalar. */
41
+ export function mulGas(gas: Partial<Gas>, scalar: number) {
42
+ return { l1Gas: (gas.l1Gas ?? 0) * scalar, l2Gas: (gas.l2Gas ?? 0) * scalar, daGas: (gas.daGas ?? 0) * scalar };
14
43
  }
15
44
 
16
- /** Gas cost of zero across all gas dimensions. */
17
- export const EmptyGasCost = {
45
+ /** Zero gas across all gas dimensions. */
46
+ export const EmptyGas: Gas = {
18
47
  l1Gas: 0,
19
48
  l2Gas: 0,
20
49
  daGas: 0,
@@ -29,12 +58,12 @@ export const DynamicGasCost = Symbol('DynamicGasCost');
29
58
  /** Temporary default gas cost. We should eventually remove all usage of this variable in favor of actual gas for each opcode. */
30
59
  const TemporaryDefaultGasCost = { l1Gas: 0, l2Gas: 10, daGas: 0 };
31
60
 
32
- /** Gas costs for each instruction. */
33
- export const GasCosts = {
34
- [Opcode.ADD]: DynamicGasCost,
35
- [Opcode.SUB]: DynamicGasCost,
36
- [Opcode.MUL]: DynamicGasCost,
37
- [Opcode.DIV]: DynamicGasCost,
61
+ /** Base gas costs for each instruction. Additional gas cost may be added on top due to memory or storage accesses, etc. */
62
+ export const GasCosts: Record<Opcode, Gas | typeof DynamicGasCost> = {
63
+ [Opcode.ADD]: TemporaryDefaultGasCost,
64
+ [Opcode.SUB]: TemporaryDefaultGasCost,
65
+ [Opcode.MUL]: TemporaryDefaultGasCost,
66
+ [Opcode.DIV]: TemporaryDefaultGasCost,
38
67
  [Opcode.FDIV]: TemporaryDefaultGasCost,
39
68
  [Opcode.EQ]: TemporaryDefaultGasCost,
40
69
  [Opcode.LT]: TemporaryDefaultGasCost,
@@ -64,7 +93,7 @@ export const GasCosts = {
64
93
  [Opcode.BLOCKL1GASLIMIT]: TemporaryDefaultGasCost,
65
94
  [Opcode.BLOCKL2GASLIMIT]: TemporaryDefaultGasCost,
66
95
  [Opcode.BLOCKDAGASLIMIT]: TemporaryDefaultGasCost,
67
- [Opcode.CALLDATACOPY]: DynamicGasCost,
96
+ [Opcode.CALLDATACOPY]: TemporaryDefaultGasCost,
68
97
  // Gas
69
98
  [Opcode.L1GASLEFT]: TemporaryDefaultGasCost,
70
99
  [Opcode.L2GASLEFT]: TemporaryDefaultGasCost,
@@ -75,7 +104,7 @@ export const GasCosts = {
75
104
  [Opcode.INTERNALCALL]: TemporaryDefaultGasCost,
76
105
  [Opcode.INTERNALRETURN]: TemporaryDefaultGasCost,
77
106
  // Memory
78
- [Opcode.SET]: DynamicGasCost,
107
+ [Opcode.SET]: TemporaryDefaultGasCost,
79
108
  [Opcode.MOV]: TemporaryDefaultGasCost,
80
109
  [Opcode.CMOV]: TemporaryDefaultGasCost,
81
110
  // World state
@@ -89,6 +118,7 @@ export const GasCosts = {
89
118
  [Opcode.HEADERMEMBER]: TemporaryDefaultGasCost,
90
119
  [Opcode.EMITUNENCRYPTEDLOG]: TemporaryDefaultGasCost,
91
120
  [Opcode.SENDL2TOL1MSG]: TemporaryDefaultGasCost,
121
+ [Opcode.GETCONTRACTINSTANCE]: TemporaryDefaultGasCost,
92
122
  // External calls
93
123
  [Opcode.CALL]: TemporaryDefaultGasCost,
94
124
  [Opcode.STATICCALL]: TemporaryDefaultGasCost,
@@ -100,18 +130,42 @@ export const GasCosts = {
100
130
  [Opcode.POSEIDON]: TemporaryDefaultGasCost,
101
131
  [Opcode.SHA256]: TemporaryDefaultGasCost, // temp - may be removed, but alot of contracts rely on i: TemporaryDefaultGasCost,
102
132
  [Opcode.PEDERSEN]: TemporaryDefaultGasCost, // temp - may be removed, but alot of contracts rely on i: TemporaryDefaultGasCost,t
103
- } as const;
133
+ };
134
+
135
+ /** Returns the fixed base gas cost for a given opcode, or throws if set to dynamic. */
136
+ export function getBaseGasCost(opcode: Opcode): Gas {
137
+ const cost = GasCosts[opcode];
138
+ if (cost === DynamicGasCost) {
139
+ throw new Error(`Opcode ${Opcode[opcode]} has dynamic gas cost`);
140
+ }
141
+ return cost;
142
+ }
143
+
144
+ /** Returns the gas cost associated with the memory operations performed. */
145
+ export function getMemoryGasCost(args: { reads?: number; writes?: number; indirect?: number }) {
146
+ const { reads, writes, indirect } = args;
147
+ const indirectCount = Addressing.fromWire(indirect ?? 0).count(AddressingMode.INDIRECT);
148
+ const l2MemoryGasCost =
149
+ (reads ?? 0) * GasCostConstants.MEMORY_READ +
150
+ (writes ?? 0) * GasCostConstants.MEMORY_WRITE +
151
+ indirectCount * GasCostConstants.MEMORY_INDIRECT_READ_PENALTY;
152
+ return makeGas({ l2Gas: l2MemoryGasCost });
153
+ }
104
154
 
105
155
  /** Constants used in base cost calculations. */
106
156
  export const GasCostConstants = {
107
- SET_COST_PER_BYTE: 100,
108
- CALLDATACOPY_COST_PER_BYTE: 10,
109
- ARITHMETIC_COST_PER_BYTE: 10,
110
- ARITHMETIC_COST_PER_INDIRECT_ACCESS: 5,
157
+ MEMORY_READ: 10,
158
+ MEMORY_INDIRECT_READ_PENALTY: 10,
159
+ MEMORY_WRITE: 100,
111
160
  };
112
161
 
162
+ /** Returns gas cost for an operation on a given type tag based on the base cost per byte. */
163
+ export function getGasCostForTypeTag(tag: TypeTag, baseCost: Gas) {
164
+ return mulGas(baseCost, getGasCostMultiplierFromTypeTag(tag));
165
+ }
166
+
113
167
  /** Returns a multiplier based on the size of the type represented by the tag. Throws on uninitialized or invalid. */
114
- export function getGasCostMultiplierFromTypeTag(tag: TypeTag) {
168
+ function getGasCostMultiplierFromTypeTag(tag: TypeTag) {
115
169
  switch (tag) {
116
170
  case TypeTag.UINT8:
117
171
  return 1;
@@ -127,6 +181,6 @@ export function getGasCostMultiplierFromTypeTag(tag: TypeTag) {
127
181
  return 32;
128
182
  case TypeTag.INVALID:
129
183
  case TypeTag.UNINITIALIZED:
130
- throw new Error(`Invalid tag type for gas cost multiplier: ${TypeTag[tag]}`);
184
+ throw new InstructionExecutionError(`Invalid tag type for gas cost multiplier: ${TypeTag[tag]}`);
131
185
  }
132
186
  }
@@ -1,6 +1,6 @@
1
- import { Fr } from '@aztec/circuits.js';
1
+ import { type Fr } from '@aztec/circuits.js';
2
2
 
3
- import { GasCost, GasDimensions } from './avm_gas_cost.js';
3
+ import { type Gas, GasDimensions } from './avm_gas.js';
4
4
  import { TaggedMemory } from './avm_memory_types.js';
5
5
  import { AvmContractCallResults } from './avm_message_call_result.js';
6
6
  import { OutOfGasError } from './errors.js';
@@ -59,7 +59,7 @@ export class AvmMachineState {
59
59
  * Should any of the gas dimensions get depleted, it sets all gas left to zero and triggers
60
60
  * an exceptional halt by throwing an OutOfGasError.
61
61
  */
62
- public consumeGas(gasCost: Partial<GasCost>) {
62
+ public consumeGas(gasCost: Partial<Gas>) {
63
63
  // Assert there is enough gas on every dimension.
64
64
  const outOfGasDimensions = GasDimensions.filter(
65
65
  dimension => this[`${dimension}Left`] - (gasCost[dimension] ?? 0) < 0,
@@ -76,6 +76,13 @@ export class AvmMachineState {
76
76
  }
77
77
  }
78
78
 
79
+ /** Increases the gas left by the amounts specified. */
80
+ public refundGas(gasRefund: Partial<Gas>) {
81
+ for (const dimension of GasDimensions) {
82
+ this[`${dimension}Left`] += gasRefund[dimension] ?? 0;
83
+ }
84
+ }
85
+
79
86
  /**
80
87
  * Most instructions just increment PC before they complete
81
88
  */
@@ -1,10 +1,12 @@
1
1
  import { toBufferBE } from '@aztec/foundation/bigint-buffer';
2
2
  import { Fr } from '@aztec/foundation/fields';
3
- import { DebugLogger, createDebugLogger } from '@aztec/foundation/log';
3
+ import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
4
+ import { type FunctionsOf } from '@aztec/foundation/types';
4
5
 
5
6
  import { strict as assert } from 'assert';
6
7
 
7
- import { TagCheckError } from './errors.js';
8
+ import { InstructionExecutionError, TagCheckError } from './errors.js';
9
+ import { Addressing, AddressingMode } from './opcodes/addressing_mode.js';
8
10
 
9
11
  /** MemoryValue gathers the common operations for all memory types. */
10
12
  export abstract class MemoryValue {
@@ -30,6 +32,11 @@ export abstract class MemoryValue {
30
32
  return new Fr(this.toBigInt());
31
33
  }
32
34
 
35
+ // To number. Throws if exceeds max safe int.
36
+ public toNumber(): number {
37
+ return this.toFr().toNumber();
38
+ }
39
+
33
40
  public toString(): string {
34
41
  return `${this.constructor.name}(0x${this.toBigInt().toString(16)})`;
35
42
  }
@@ -198,10 +205,16 @@ export enum TypeTag {
198
205
  INVALID,
199
206
  }
200
207
 
208
+ // Lazy interface definition for tagged memory
209
+ export type TaggedMemoryInterface = FunctionsOf<TaggedMemory>;
210
+
201
211
  // TODO: Consider automatic conversion when getting undefined values.
202
- export class TaggedMemory {
212
+ export class TaggedMemory implements TaggedMemoryInterface {
203
213
  static readonly log: DebugLogger = createDebugLogger('aztec:avm_simulator:memory');
204
214
 
215
+ // Whether to track and validate memory accesses for each instruction.
216
+ static readonly TRACK_MEMORY_ACCESSES = process.env.NODE_ENV === 'test';
217
+
205
218
  // FIXME: memory should be 2^32, but TS doesn't allow for arrays that big.
206
219
  static readonly MAX_MEMORY_SIZE = Number((1n << 32n) - 2n);
207
220
  private _mem: MemoryValue[];
@@ -211,6 +224,11 @@ export class TaggedMemory {
211
224
  this._mem = [];
212
225
  }
213
226
 
227
+ /** Returns a MeteredTaggedMemory instance to track the number of reads and writes if TRACK_MEMORY_ACCESSES is set. */
228
+ public track(type: string = 'instruction') {
229
+ return TaggedMemory.TRACK_MEMORY_ACCESSES ? new MeteredTaggedMemory(this, type) : this;
230
+ }
231
+
214
232
  public get(offset: number): MemoryValue {
215
233
  assert(offset < TaggedMemory.MAX_MEMORY_SIZE);
216
234
  const value = this.getAs<MemoryValue>(offset);
@@ -221,6 +239,9 @@ export class TaggedMemory {
221
239
  assert(offset < TaggedMemory.MAX_MEMORY_SIZE);
222
240
  const word = this._mem[offset];
223
241
  TaggedMemory.log(`get(${offset}) = ${word}`);
242
+ if (word === undefined) {
243
+ TaggedMemory.log.warn(`Memory at offset ${offset} is undefined! This might be OK if it's stack dumping.`);
244
+ }
224
245
  return word as T;
225
246
  }
226
247
 
@@ -229,6 +250,7 @@ export class TaggedMemory {
229
250
  assert(offset + size < TaggedMemory.MAX_MEMORY_SIZE);
230
251
  const value = this._mem.slice(offset, offset + size);
231
252
  TaggedMemory.log(`getSlice(${offset}, ${size}) = ${value}`);
253
+ assert(!value.some(e => e === undefined), 'Memory slice contains undefined values.');
232
254
  assert(value.length === size, `Expected slice of size ${size}, got ${value.length}.`);
233
255
  return value;
234
256
  }
@@ -367,4 +389,110 @@ export class TaggedMemory {
367
389
  throw new Error(`${TypeTag[tag]} is not a valid integral type.`);
368
390
  }
369
391
  }
392
+
393
+ /** No-op. Implemented here for compatibility with the MeteredTaggedMemory. */
394
+ public assert(_operations: Partial<MemoryOperations & { indirect: number }>) {}
395
+ }
396
+
397
+ /** Tagged memory wrapper with metering for each memory read and write operation. */
398
+ export class MeteredTaggedMemory implements TaggedMemoryInterface {
399
+ private reads: number = 0;
400
+ private writes: number = 0;
401
+
402
+ constructor(private wrapped: TaggedMemory, private type: string = 'instruction') {}
403
+
404
+ /** Returns the number of reads and writes tracked so far and resets them to zero. */
405
+ public reset(): MemoryOperations {
406
+ const stats = { reads: this.reads, writes: this.writes };
407
+ this.reads = 0;
408
+ this.writes = 0;
409
+ return stats;
410
+ }
411
+
412
+ /**
413
+ * Asserts that the exact number of memory operations have been performed.
414
+ * Indirect represents the flags for indirect accesses: each bit set to one counts as an extra read.
415
+ */
416
+ public assert(operations: Partial<MemoryOperations & { indirect: number }>) {
417
+ const { reads: expectedReads, writes: expectedWrites, indirect } = { reads: 0, writes: 0, ...operations };
418
+
419
+ const totalExpectedReads = expectedReads + Addressing.fromWire(indirect ?? 0).count(AddressingMode.INDIRECT);
420
+ const { reads: actualReads, writes: actualWrites } = this.reset();
421
+ if (actualReads !== totalExpectedReads) {
422
+ throw new InstructionExecutionError(
423
+ `Incorrect number of memory reads for ${this.type}: expected ${totalExpectedReads} but executed ${actualReads}`,
424
+ );
425
+ }
426
+ if (actualWrites !== expectedWrites) {
427
+ throw new InstructionExecutionError(
428
+ `Incorrect number of memory writes for ${this.type}: expected ${expectedWrites} but executed ${actualWrites}`,
429
+ );
430
+ }
431
+ }
432
+
433
+ public track(type: string = 'instruction'): MeteredTaggedMemory {
434
+ return new MeteredTaggedMemory(this.wrapped, type);
435
+ }
436
+
437
+ public get(offset: number): MemoryValue {
438
+ this.reads++;
439
+ return this.wrapped.get(offset);
440
+ }
441
+
442
+ public getSliceAs<T>(offset: number, size: number): T[] {
443
+ this.reads += size;
444
+ return this.wrapped.getSliceAs<T>(offset, size);
445
+ }
446
+
447
+ public getAs<T>(offset: number): T {
448
+ this.reads++;
449
+ return this.wrapped.getAs(offset);
450
+ }
451
+
452
+ public getSlice(offset: number, size: number): MemoryValue[] {
453
+ this.reads += size;
454
+ return this.wrapped.getSlice(offset, size);
455
+ }
456
+
457
+ public set(offset: number, v: MemoryValue): void {
458
+ this.writes++;
459
+ this.wrapped.set(offset, v);
460
+ }
461
+
462
+ public setSlice(offset: number, vs: MemoryValue[]): void {
463
+ this.writes += vs.length;
464
+ this.wrapped.setSlice(offset, vs);
465
+ }
466
+
467
+ public getSliceTags(offset: number, size: number): TypeTag[] {
468
+ return this.wrapped.getSliceTags(offset, size);
469
+ }
470
+
471
+ public getTag(offset: number): TypeTag {
472
+ return this.wrapped.getTag(offset);
473
+ }
474
+
475
+ public checkTag(tag: TypeTag, offset: number): void {
476
+ this.wrapped.checkTag(tag, offset);
477
+ }
478
+
479
+ public checkIsValidMemoryOffsetTag(offset: number): void {
480
+ this.wrapped.checkIsValidMemoryOffsetTag(offset);
481
+ }
482
+
483
+ public checkTags(tag: TypeTag, ...offsets: number[]): void {
484
+ this.wrapped.checkTags(tag, ...offsets);
485
+ }
486
+
487
+ public checkTagsRange(tag: TypeTag, startOffset: number, size: number): void {
488
+ this.wrapped.checkTagsRange(tag, startOffset, size);
489
+ }
370
490
  }
491
+
492
+ /** Tracks number of memory reads and writes. */
493
+ export type MemoryOperations = {
494
+ /** How many total reads are performed. Slice reads are count as one per element. */
495
+ reads: number;
496
+ /** How many total writes are performed. Slice writes are count as one per element. */
497
+ writes: number;
498
+ };
@@ -1,4 +1,4 @@
1
- import { Fr } from '@aztec/foundation/fields';
1
+ import { type Fr } from '@aztec/foundation/fields';
2
2
 
3
3
  /**
4
4
  * Results of an contract call's execution in the AVM.
@@ -1,4 +1,4 @@
1
- import { DebugLogger, createDebugLogger } from '@aztec/foundation/log';
1
+ import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
2
2
 
3
3
  import { strict as assert } from 'assert';
4
4
 
@@ -50,30 +50,32 @@ export class AvmSimulator {
50
50
  */
51
51
  public async executeInstructions(instructions: Instruction[]): Promise<AvmContractCallResults> {
52
52
  assert(instructions.length > 0);
53
+ const { machineState } = this.context;
53
54
  try {
54
55
  // Execute instruction pointed to by the current program counter
55
56
  // continuing until the machine state signifies a halt
56
- while (!this.context.machineState.halted) {
57
- const instruction = instructions[this.context.machineState.pc];
57
+ while (!machineState.halted) {
58
+ const instruction = instructions[machineState.pc];
58
59
  assert(
59
60
  !!instruction,
60
61
  'AVM attempted to execute non-existent instruction. This should never happen (invalid bytecode or AVM simulator bug)!',
61
62
  );
62
63
 
63
- this.log.debug(`@${this.context.machineState.pc} ${instruction.toString()}`);
64
+ const gasLeft = `l1=${machineState.l1GasLeft} l2=${machineState.l2GasLeft} da=${machineState.daGasLeft}`;
65
+ this.log.debug(`@${machineState.pc} (${gasLeft}) ${instruction.toString()}`);
64
66
  // Execute the instruction.
65
67
  // Normal returns and reverts will return normally here.
66
68
  // "Exceptional halts" will throw.
67
- await instruction.run(this.context);
69
+ await instruction.execute(this.context);
68
70
 
69
- if (this.context.machineState.pc >= instructions.length) {
71
+ if (machineState.pc >= instructions.length) {
70
72
  this.log('Passed end of program!');
71
- throw new InvalidProgramCounterError(this.context.machineState.pc, /*max=*/ instructions.length);
73
+ throw new InvalidProgramCounterError(machineState.pc, /*max=*/ instructions.length);
72
74
  }
73
75
  }
74
76
 
75
77
  // Return results for processing by calling context
76
- const results = this.context.machineState.getResults();
78
+ const results = machineState.getResults();
77
79
  this.log(`Context execution results: ${results.toString()}`);
78
80
  return results;
79
81
  } catch (e) {
package/src/avm/errors.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AztecAddress } from '@aztec/circuits.js';
1
+ import { type AztecAddress } from '@aztec/circuits.js';
2
2
 
3
3
  /**
4
4
  * Avm-specific errors should derive from this
@@ -8,7 +8,12 @@ import { Fr } from '@aztec/foundation/fields';
8
8
  import { mock } from 'jest-mock-extended';
9
9
  import merge from 'lodash.merge';
10
10
 
11
- import { CommitmentsDB, MessageLoadOracleInputs, PublicContractsDB, PublicStateDB } from '../../index.js';
11
+ import {
12
+ type CommitmentsDB,
13
+ MessageLoadOracleInputs,
14
+ type PublicContractsDB,
15
+ type PublicStateDB,
16
+ } from '../../index.js';
12
17
  import { AvmContext } from '../avm_context.js';
13
18
  import { AvmContextInputs, AvmExecutionEnvironment } from '../avm_execution_environment.js';
14
19
  import { AvmMachineState } from '../avm_machine_state.js';
@@ -1,4 +1,4 @@
1
- import { CommitmentsDB, PublicContractsDB, PublicStateDB } from '../../public/db.js';
1
+ import { type CommitmentsDB, type PublicContractsDB, type PublicStateDB } from '../../public/db.js';
2
2
 
3
3
  /**
4
4
  * Host storage
@@ -3,11 +3,11 @@ import { AztecAddress, EthAddress, L2ToL1Message } from '@aztec/circuits.js';
3
3
  import { EventSelector } from '@aztec/foundation/abi';
4
4
  import { Fr } from '@aztec/foundation/fields';
5
5
 
6
- import { HostStorage } from './host_storage.js';
6
+ import { type HostStorage } from './host_storage.js';
7
7
  import { Nullifiers } from './nullifiers.js';
8
8
  import { PublicStorage } from './public_storage.js';
9
9
  import { WorldStateAccessTrace } from './trace.js';
10
- import { TracedL1toL2MessageCheck, TracedNoteHashCheck, TracedNullifierCheck } from './trace_types.js';
10
+ import { type TracedL1toL2MessageCheck, type TracedNoteHashCheck, type TracedNullifierCheck } from './trace_types.js';
11
11
 
12
12
  /**
13
13
  * Data held within the journal
@@ -1,6 +1,6 @@
1
1
  import { Fr } from '@aztec/foundation/fields';
2
2
 
3
- import { TracedL1toL2MessageCheck, TracedNoteHashCheck, TracedNullifierCheck } from './trace_types.js';
3
+ import { type TracedL1toL2MessageCheck, type TracedNoteHashCheck, type TracedNullifierCheck } from './trace_types.js';
4
4
 
5
5
  export class WorldStateAccessTrace {
6
6
  public accessCounter: number;
@@ -1,4 +1,4 @@
1
- import { Fr } from '@aztec/foundation/fields';
1
+ import { type Fr } from '@aztec/foundation/fields';
2
2
 
3
3
  //export type TracedContractCall = {
4
4
  // callPointer: Fr;
@@ -28,18 +28,23 @@ export class NoteHashExists extends Instruction {
28
28
  super();
29
29
  }
30
30
 
31
- async execute(context: AvmContext): Promise<void> {
31
+ public async execute(context: AvmContext): Promise<void> {
32
+ const memoryOperations = { reads: 2, writes: 1, indirect: this.indirect };
33
+ const memory = context.machineState.memory.track(this.type);
34
+ context.machineState.consumeGas(this.gasCost(memoryOperations));
35
+
32
36
  // 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();
37
+ const noteHash = memory.get(this.noteHashOffset).toFr();
38
+ const leafIndex = memory.get(this.leafIndexOffset).toFr();
35
39
 
36
40
  const exists = await context.persistableState.checkNoteHashExists(
37
41
  context.environment.storageAddress,
38
42
  noteHash,
39
43
  leafIndex,
40
44
  );
41
- context.machineState.memory.set(this.existsOffset, exists ? new Uint8(1) : new Uint8(0));
45
+ memory.set(this.existsOffset, exists ? new Uint8(1) : new Uint8(0));
42
46
 
47
+ memory.assert(memoryOperations);
43
48
  context.machineState.incrementPc();
44
49
  }
45
50
  }
@@ -54,14 +59,19 @@ export class EmitNoteHash extends Instruction {
54
59
  super();
55
60
  }
56
61
 
57
- async execute(context: AvmContext): Promise<void> {
62
+ public async execute(context: AvmContext): Promise<void> {
63
+ const memoryOperations = { reads: 1, indirect: this.indirect };
64
+ const memory = context.machineState.memory.track(this.type);
65
+ context.machineState.consumeGas(this.gasCost(memoryOperations));
66
+
58
67
  if (context.environment.isStaticCall) {
59
68
  throw new StaticCallStorageAlterError();
60
69
  }
61
70
 
62
- const noteHash = context.machineState.memory.get(this.noteHashOffset).toFr();
71
+ const noteHash = memory.get(this.noteHashOffset).toFr();
63
72
  context.persistableState.writeNoteHash(noteHash);
64
73
 
74
+ memory.assert(memoryOperations);
65
75
  context.machineState.incrementPc();
66
76
  }
67
77
  }
@@ -76,12 +86,17 @@ export class NullifierExists extends Instruction {
76
86
  super();
77
87
  }
78
88
 
79
- async execute(context: AvmContext): Promise<void> {
80
- const nullifier = context.machineState.memory.get(this.nullifierOffset).toFr();
89
+ public async execute(context: AvmContext): Promise<void> {
90
+ const memoryOperations = { reads: 1, writes: 1, indirect: this.indirect };
91
+ const memory = context.machineState.memory.track(this.type);
92
+ context.machineState.consumeGas(this.gasCost(memoryOperations));
93
+
94
+ const nullifier = memory.get(this.nullifierOffset).toFr();
81
95
  const exists = await context.persistableState.checkNullifierExists(context.environment.storageAddress, nullifier);
82
96
 
83
- context.machineState.memory.set(this.existsOffset, exists ? new Uint8(1) : new Uint8(0));
97
+ memory.set(this.existsOffset, exists ? new Uint8(1) : new Uint8(0));
84
98
 
99
+ memory.assert(memoryOperations);
85
100
  context.machineState.incrementPc();
86
101
  }
87
102
  }
@@ -96,12 +111,16 @@ export class EmitNullifier extends Instruction {
96
111
  super();
97
112
  }
98
113
 
99
- async execute(context: AvmContext): Promise<void> {
114
+ public async execute(context: AvmContext): Promise<void> {
100
115
  if (context.environment.isStaticCall) {
101
116
  throw new StaticCallStorageAlterError();
102
117
  }
103
118
 
104
- const nullifier = context.machineState.memory.get(this.nullifierOffset).toFr();
119
+ const memoryOperations = { reads: 1, indirect: this.indirect };
120
+ const memory = context.machineState.memory.track(this.type);
121
+ context.machineState.consumeGas(this.gasCost(memoryOperations));
122
+
123
+ const nullifier = memory.get(this.nullifierOffset).toFr();
105
124
  try {
106
125
  await context.persistableState.writeNullifier(context.environment.storageAddress, nullifier);
107
126
  } catch (e) {
@@ -115,6 +134,7 @@ export class EmitNullifier extends Instruction {
115
134
  }
116
135
  }
117
136
 
137
+ memory.assert(memoryOperations);
118
138
  context.machineState.incrementPc();
119
139
  }
120
140
  }
@@ -140,12 +160,17 @@ export class L1ToL2MessageExists extends Instruction {
140
160
  super();
141
161
  }
142
162
 
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();
163
+ public async execute(context: AvmContext): Promise<void> {
164
+ const memoryOperations = { reads: 2, writes: 1, indirect: this.indirect };
165
+ const memory = context.machineState.memory.track(this.type);
166
+ context.machineState.consumeGas(this.gasCost(memoryOperations));
167
+
168
+ const msgHash = memory.get(this.msgHashOffset).toFr();
169
+ const msgLeafIndex = memory.get(this.msgLeafIndexOffset).toFr();
146
170
  const exists = await context.persistableState.checkL1ToL2MessageExists(msgHash, msgLeafIndex);
147
- context.machineState.memory.set(this.existsOffset, exists ? new Uint8(1) : new Uint8(0));
171
+ memory.set(this.existsOffset, exists ? new Uint8(1) : new Uint8(0));
148
172
 
173
+ memory.assert(memoryOperations);
149
174
  context.machineState.incrementPc();
150
175
  }
151
176
  }
@@ -171,21 +196,26 @@ export class EmitUnencryptedLog extends Instruction {
171
196
  super();
172
197
  }
173
198
 
174
- async execute(context: AvmContext): Promise<void> {
199
+ public async execute(context: AvmContext): Promise<void> {
175
200
  if (context.environment.isStaticCall) {
176
201
  throw new StaticCallStorageAlterError();
177
202
  }
178
203
 
204
+ const memoryOperations = { reads: 1 + this.logSize, indirect: this.indirect };
205
+ const memory = context.machineState.memory.track(this.type);
206
+ context.machineState.consumeGas(this.gasCost(memoryOperations));
207
+
179
208
  const [eventSelectorOffset, logOffset] = Addressing.fromWire(this.indirect).resolve(
180
209
  [this.eventSelectorOffset, this.logOffset],
181
- context.machineState.memory,
210
+ memory,
182
211
  );
183
212
 
184
213
  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());
214
+ const event = memory.get(eventSelectorOffset).toFr();
215
+ const log = memory.getSlice(logOffset, this.logSize).map(f => f.toFr());
187
216
  context.persistableState.writeLog(contractAddress, event, log);
188
217
 
218
+ memory.assert(memoryOperations);
189
219
  context.machineState.incrementPc();
190
220
  }
191
221
  }
@@ -200,15 +230,20 @@ export class SendL2ToL1Message extends Instruction {
200
230
  super();
201
231
  }
202
232
 
203
- async execute(context: AvmContext): Promise<void> {
233
+ public async execute(context: AvmContext): Promise<void> {
204
234
  if (context.environment.isStaticCall) {
205
235
  throw new StaticCallStorageAlterError();
206
236
  }
207
237
 
208
- const recipient = context.machineState.memory.get(this.recipientOffset).toFr();
209
- const content = context.machineState.memory.get(this.contentOffset).toFr();
238
+ const memoryOperations = { reads: 2, indirect: this.indirect };
239
+ const memory = context.machineState.memory.track(this.type);
240
+ context.machineState.consumeGas(this.gasCost(memoryOperations));
241
+
242
+ const recipient = memory.get(this.recipientOffset).toFr();
243
+ const content = memory.get(this.contentOffset).toFr();
210
244
  context.persistableState.writeL1Message(recipient, content);
211
245
 
246
+ memory.assert(memoryOperations);
212
247
  context.machineState.incrementPc();
213
248
  }
214
249
  }