@aztec/simulator 0.60.0 → 0.61.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 (124) hide show
  1. package/dest/acvm/oracle/oracle.d.ts +2 -0
  2. package/dest/acvm/oracle/oracle.d.ts.map +1 -1
  3. package/dest/acvm/oracle/oracle.js +9 -1
  4. package/dest/acvm/oracle/typed_oracle.d.ts +3 -1
  5. package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
  6. package/dest/acvm/oracle/typed_oracle.js +7 -1
  7. package/dest/avm/avm_gas.d.ts.map +1 -1
  8. package/dest/avm/avm_gas.js +6 -3
  9. package/dest/avm/avm_machine_state.d.ts +2 -0
  10. package/dest/avm/avm_machine_state.d.ts.map +1 -1
  11. package/dest/avm/avm_machine_state.js +3 -1
  12. package/dest/avm/avm_simulator.d.ts +15 -0
  13. package/dest/avm/avm_simulator.d.ts.map +1 -1
  14. package/dest/avm/avm_simulator.js +45 -4
  15. package/dest/avm/fixtures/index.d.ts +1 -1
  16. package/dest/avm/fixtures/index.d.ts.map +1 -1
  17. package/dest/avm/fixtures/index.js +7 -7
  18. package/dest/avm/journal/journal.d.ts +5 -6
  19. package/dest/avm/journal/journal.d.ts.map +1 -1
  20. package/dest/avm/journal/journal.js +42 -17
  21. package/dest/avm/opcodes/contract.d.ts +8 -1
  22. package/dest/avm/opcodes/contract.d.ts.map +1 -1
  23. package/dest/avm/opcodes/contract.js +41 -21
  24. package/dest/avm/opcodes/conversion.d.ts +1 -1
  25. package/dest/avm/opcodes/conversion.d.ts.map +1 -1
  26. package/dest/avm/opcodes/conversion.js +12 -9
  27. package/dest/avm/opcodes/environment_getters.d.ts +1 -1
  28. package/dest/avm/opcodes/environment_getters.d.ts.map +1 -1
  29. package/dest/avm/opcodes/environment_getters.js +5 -1
  30. package/dest/avm/opcodes/external_calls.d.ts +3 -6
  31. package/dest/avm/opcodes/external_calls.d.ts.map +1 -1
  32. package/dest/avm/opcodes/external_calls.js +23 -43
  33. package/dest/avm/opcodes/memory.d.ts +20 -0
  34. package/dest/avm/opcodes/memory.d.ts.map +1 -1
  35. package/dest/avm/opcodes/memory.js +59 -3
  36. package/dest/avm/serialization/bytecode_serialization.d.ts.map +1 -1
  37. package/dest/avm/serialization/bytecode_serialization.js +5 -3
  38. package/dest/avm/serialization/instruction_serialization.d.ts +36 -34
  39. package/dest/avm/serialization/instruction_serialization.d.ts.map +1 -1
  40. package/dest/avm/serialization/instruction_serialization.js +37 -35
  41. package/dest/avm/test_utils.d.ts +2 -1
  42. package/dest/avm/test_utils.d.ts.map +1 -1
  43. package/dest/avm/test_utils.js +4 -1
  44. package/dest/client/client_execution_context.d.ts +0 -8
  45. package/dest/client/client_execution_context.d.ts.map +1 -1
  46. package/dest/client/client_execution_context.js +1 -18
  47. package/dest/client/db_oracle.d.ts +17 -1
  48. package/dest/client/db_oracle.d.ts.map +1 -1
  49. package/dest/client/db_oracle.js +1 -1
  50. package/dest/client/view_data_oracle.d.ts +17 -1
  51. package/dest/client/view_data_oracle.d.ts.map +1 -1
  52. package/dest/client/view_data_oracle.js +21 -1
  53. package/dest/common/index.d.ts +0 -1
  54. package/dest/common/index.d.ts.map +1 -1
  55. package/dest/common/index.js +1 -2
  56. package/dest/public/dual_side_effect_trace.d.ts +3 -5
  57. package/dest/public/dual_side_effect_trace.d.ts.map +1 -1
  58. package/dest/public/dual_side_effect_trace.js +8 -4
  59. package/dest/public/enqueued_call_side_effect_trace.d.ts +4 -6
  60. package/dest/public/enqueued_call_side_effect_trace.d.ts.map +1 -1
  61. package/dest/public/enqueued_call_side_effect_trace.js +19 -7
  62. package/dest/public/enqueued_call_simulator.d.ts +2 -2
  63. package/dest/public/enqueued_call_simulator.d.ts.map +1 -1
  64. package/dest/public/enqueued_call_simulator.js +21 -29
  65. package/dest/public/enqueued_calls_processor.d.ts +2 -3
  66. package/dest/public/enqueued_calls_processor.d.ts.map +1 -1
  67. package/dest/public/enqueued_calls_processor.js +18 -25
  68. package/dest/public/public_kernel.d.ts +0 -1
  69. package/dest/public/public_kernel.d.ts.map +1 -1
  70. package/dest/public/public_kernel.js +5 -8
  71. package/dest/public/public_kernel_tail_simulator.d.ts +1 -5
  72. package/dest/public/public_kernel_tail_simulator.d.ts.map +1 -1
  73. package/dest/public/public_kernel_tail_simulator.js +6 -12
  74. package/dest/public/public_processor.js +4 -4
  75. package/dest/public/side_effect_trace.d.ts +4 -3
  76. package/dest/public/side_effect_trace.d.ts.map +1 -1
  77. package/dest/public/side_effect_trace.js +27 -16
  78. package/dest/public/side_effect_trace_interface.d.ts +3 -3
  79. package/dest/public/side_effect_trace_interface.d.ts.map +1 -1
  80. package/package.json +9 -9
  81. package/src/acvm/oracle/oracle.ts +13 -0
  82. package/src/acvm/oracle/typed_oracle.ts +9 -0
  83. package/src/avm/avm_gas.ts +5 -2
  84. package/src/avm/avm_machine_state.ts +2 -0
  85. package/src/avm/avm_simulator.ts +69 -6
  86. package/src/avm/fixtures/index.ts +7 -7
  87. package/src/avm/journal/journal.ts +62 -19
  88. package/src/avm/opcodes/contract.ts +45 -21
  89. package/src/avm/opcodes/conversion.ts +9 -6
  90. package/src/avm/opcodes/environment_getters.ts +7 -2
  91. package/src/avm/opcodes/external_calls.ts +21 -45
  92. package/src/avm/opcodes/memory.ts +69 -2
  93. package/src/avm/serialization/bytecode_serialization.ts +6 -2
  94. package/src/avm/serialization/instruction_serialization.ts +3 -1
  95. package/src/avm/test_utils.ts +5 -1
  96. package/src/client/client_execution_context.ts +0 -27
  97. package/src/client/db_oracle.ts +26 -0
  98. package/src/client/view_data_oracle.ts +31 -1
  99. package/src/common/index.ts +0 -1
  100. package/src/public/dual_side_effect_trace.ts +20 -6
  101. package/src/public/enqueued_call_side_effect_trace.ts +46 -8
  102. package/src/public/enqueued_call_simulator.ts +42 -26
  103. package/src/public/enqueued_calls_processor.ts +26 -38
  104. package/src/public/public_kernel.ts +9 -12
  105. package/src/public/public_kernel_tail_simulator.ts +6 -15
  106. package/src/public/public_processor.ts +3 -3
  107. package/src/public/side_effect_trace.ts +54 -15
  108. package/src/public/side_effect_trace_interface.ts +9 -4
  109. package/dest/client/test_utils.d.ts +0 -9
  110. package/dest/client/test_utils.d.ts.map +0 -1
  111. package/dest/client/test_utils.js +0 -27
  112. package/dest/common/side_effect_counter.d.ts +0 -10
  113. package/dest/common/side_effect_counter.d.ts.map +0 -1
  114. package/dest/common/side_effect_counter.js +0 -18
  115. package/dest/rollup/index.d.ts +0 -2
  116. package/dest/rollup/index.d.ts.map +0 -1
  117. package/dest/rollup/index.js +0 -2
  118. package/dest/rollup/rollup.d.ts +0 -101
  119. package/dest/rollup/rollup.d.ts.map +0 -1
  120. package/dest/rollup/rollup.js +0 -100
  121. package/src/client/test_utils.ts +0 -57
  122. package/src/common/side_effect_counter.ts +0 -17
  123. package/src/rollup/index.ts +0 -1
  124. package/src/rollup/rollup.ts +0 -228
@@ -6,6 +6,7 @@ import { strict as assert } from 'assert';
6
6
  import { SideEffectLimitReachedError } from '../public/side_effect_errors.js';
7
7
  import type { AvmContext } from './avm_context.js';
8
8
  import { AvmContractCallResult } from './avm_contract_call_result.js';
9
+ import { type Gas } from './avm_gas.js';
9
10
  import { isAvmBytecode } from './bytecode_utils.js';
10
11
  import {
11
12
  AvmExecutionError,
@@ -17,9 +18,21 @@ import {
17
18
  import type { Instruction } from './opcodes/index.js';
18
19
  import { decodeFromBytecode } from './serialization/bytecode_serialization.js';
19
20
 
21
+ type OpcodeTally = {
22
+ count: number;
23
+ gas: Gas;
24
+ };
25
+ type PcTally = {
26
+ opcode: string;
27
+ count: number;
28
+ gas: Gas;
29
+ };
30
+
20
31
  export class AvmSimulator {
21
32
  private log: DebugLogger;
22
33
  private bytecode: Buffer | undefined;
34
+ public opcodeTallies: Map<string, OpcodeTally> = new Map();
35
+ public pcTallies: Map<number, PcTally> = new Map();
23
36
 
24
37
  constructor(private context: AvmContext) {
25
38
  assert(
@@ -33,10 +46,7 @@ export class AvmSimulator {
33
46
  * Fetch the bytecode and execute it in the current context.
34
47
  */
35
48
  public async execute(): Promise<AvmContractCallResult> {
36
- const bytecode = await this.context.persistableState.getBytecode(
37
- this.context.environment.address,
38
- this.context.environment.functionSelector,
39
- );
49
+ const bytecode = await this.context.persistableState.getBytecode(this.context.environment.address);
40
50
 
41
51
  // This assumes that we will not be able to send messages to accounts without code
42
52
  // Pending classes and instances impl details
@@ -75,6 +85,7 @@ export class AvmSimulator {
75
85
  try {
76
86
  // Execute instruction pointed to by the current program counter
77
87
  // continuing until the machine state signifies a halt
88
+ let instrCounter = 0;
78
89
  while (!machineState.getHalted()) {
79
90
  const instruction = instructions[machineState.pc];
80
91
  assert(
@@ -82,13 +93,26 @@ export class AvmSimulator {
82
93
  'AVM attempted to execute non-existent instruction. This should never happen (invalid bytecode or AVM simulator bug)!',
83
94
  );
84
95
 
85
- const gasLeft = `l2=${machineState.l2GasLeft} da=${machineState.daGasLeft}`;
86
- this.log.debug(`@${machineState.pc} ${instruction.toString()} (${gasLeft})`);
96
+ const instrStartGas = machineState.gasLeft; // Save gas before executing instruction (for profiling)
97
+ const instrPc = machineState.pc; // Save PC before executing instruction (for profiling)
98
+
99
+ this.log.debug(
100
+ `[PC:${machineState.pc}] [IC:${instrCounter++}] ${instruction.toString()} (gasLeft l2=${
101
+ machineState.l2GasLeft
102
+ } da=${machineState.daGasLeft})`,
103
+ );
87
104
  // Execute the instruction.
88
105
  // Normal returns and reverts will return normally here.
89
106
  // "Exceptional halts" will throw.
90
107
  await instruction.execute(this.context);
91
108
 
109
+ // gas used by this instruction - used for profiling/tallying
110
+ const gasUsed: Gas = {
111
+ l2Gas: instrStartGas.l2Gas - machineState.l2GasLeft,
112
+ daGas: instrStartGas.daGas - machineState.daGasLeft,
113
+ };
114
+ this.tallyInstruction(instrPc, instruction.constructor.name, gasUsed);
115
+
92
116
  if (machineState.pc >= instructions.length) {
93
117
  this.log.warn('Passed end of program');
94
118
  throw new InvalidProgramCounterError(machineState.pc, /*max=*/ instructions.length);
@@ -100,6 +124,8 @@ export class AvmSimulator {
100
124
  const revertReason = reverted ? revertReasonFromExplicitRevert(output, this.context) : undefined;
101
125
  const results = new AvmContractCallResult(reverted, output, revertReason);
102
126
  this.log.debug(`Context execution results: ${results.toString()}`);
127
+
128
+ this.printOpcodeTallies();
103
129
  // Return results for processing by calling context
104
130
  return results;
105
131
  } catch (err: any) {
@@ -113,8 +139,45 @@ export class AvmSimulator {
113
139
  // Note: "exceptional halts" cannot return data, hence []
114
140
  const results = new AvmContractCallResult(/*reverted=*/ true, /*output=*/ [], revertReason);
115
141
  this.log.debug(`Context execution results: ${results.toString()}`);
142
+
143
+ this.printOpcodeTallies();
116
144
  // Return results for processing by calling context
117
145
  return results;
118
146
  }
119
147
  }
148
+
149
+ private tallyInstruction(pc: number, opcode: string, gasUsed: Gas) {
150
+ const opcodeTally = this.opcodeTallies.get(opcode) || ({ count: 0, gas: { l2Gas: 0, daGas: 0 } } as OpcodeTally);
151
+ opcodeTally.count++;
152
+ opcodeTally.gas.l2Gas += gasUsed.l2Gas;
153
+ opcodeTally.gas.daGas += gasUsed.daGas;
154
+ this.opcodeTallies.set(opcode, opcodeTally);
155
+
156
+ const pcTally = this.pcTallies.get(pc) || ({ opcode: opcode, count: 0, gas: { l2Gas: 0, daGas: 0 } } as PcTally);
157
+ pcTally.count++;
158
+ pcTally.gas.l2Gas += gasUsed.l2Gas;
159
+ pcTally.gas.daGas += gasUsed.daGas;
160
+ this.pcTallies.set(pc, pcTally);
161
+ }
162
+
163
+ private printOpcodeTallies() {
164
+ this.log.debug(`Printing tallies per opcode sorted by gas...`);
165
+ // sort descending by L2 gas consumed
166
+ const sortedOpcodes = Array.from(this.opcodeTallies.entries()).sort((a, b) => b[1].gas.l2Gas - a[1].gas.l2Gas);
167
+ for (const [opcode, tally] of sortedOpcodes) {
168
+ // NOTE: don't care to clutter the logs with DA gas for now
169
+ this.log.debug(`${opcode} executed ${tally.count} times consuming a total of ${tally.gas.l2Gas} L2 gas`);
170
+ }
171
+
172
+ this.log.debug(`Printing tallies per PC sorted by #times each PC was executed...`);
173
+ const sortedPcs = Array.from(this.pcTallies.entries())
174
+ .sort((a, b) => b[1].count - a[1].count)
175
+ .filter((_, i) => i < 20);
176
+ for (const [pc, tally] of sortedPcs) {
177
+ // NOTE: don't care to clutter the logs with DA gas for now
178
+ this.log.debug(
179
+ `PC:${pc} containing opcode ${tally.opcode} executed ${tally.count} times consuming a total of ${tally.gas.l2Gas} L2 gas`,
180
+ );
181
+ }
182
+ }
120
183
  }
@@ -117,6 +117,13 @@ export function randomMemoryFields(length: number): Field[] {
117
117
  return [...Array(length)].map(_ => new Field(Fr.random()));
118
118
  }
119
119
 
120
+ export function getAvmTestContractFunctionSelector(functionName: string): FunctionSelector {
121
+ const artifact = AvmTestContractArtifact.functions.find(f => f.name === functionName)!;
122
+ assert(!!artifact, `Function ${functionName} not found in AvmTestContractArtifact`);
123
+ const params = artifact.parameters;
124
+ return FunctionSelector.fromNameAndParameters(artifact.name, params);
125
+ }
126
+
120
127
  export function getAvmTestContractBytecode(functionName: string): Buffer {
121
128
  const artifact = AvmTestContractArtifact.functions.find(f => f.name === functionName)!;
122
129
  assert(
@@ -147,10 +154,3 @@ export function resolveAvmTestContractAssertionMessage(
147
154
 
148
155
  return resolveAssertionMessage(revertReason.noirCallStack, debugMetadata);
149
156
  }
150
-
151
- export function getAvmTestContractFunctionSelector(functionName: string): FunctionSelector {
152
- const artifact = AvmTestContractArtifact.functions.find(f => f.name === functionName)!;
153
- assert(!!artifact, `Function ${functionName} not found in AvmTestContractArtifact`);
154
- const params = artifact.parameters;
155
- return FunctionSelector.fromNameAndParameters(artifact.name, params);
156
- }
@@ -1,10 +1,16 @@
1
- import { AztecAddress, type FunctionSelector, type Gas, SerializableContractInstance } from '@aztec/circuits.js';
1
+ import {
2
+ AztecAddress,
3
+ type Gas,
4
+ SerializableContractInstance,
5
+ computePublicBytecodeCommitment,
6
+ } from '@aztec/circuits.js';
2
7
  import { Fr } from '@aztec/foundation/fields';
3
8
  import { createDebugLogger } from '@aztec/foundation/log';
4
9
 
10
+ import assert from 'assert';
11
+
5
12
  import { getPublicFunctionDebugName } from '../../common/debug_fn_name.js';
6
13
  import { type WorldStateDB } from '../../public/public_db_sources.js';
7
- import { type TracedContractInstance } from '../../public/side_effect_trace.js';
8
14
  import { type PublicSideEffectTraceInterface } from '../../public/side_effect_trace_interface.js';
9
15
  import { type AvmContractCallResult } from '../avm_contract_call_result.js';
10
16
  import { type AvmExecutionEnvironment } from '../avm_execution_environment.js';
@@ -210,22 +216,26 @@ export class AvmPersistableStateManager {
210
216
  /**
211
217
  * Get a contract instance.
212
218
  * @param contractAddress - address of the contract instance to retrieve.
213
- * @returns the contract instance with an "exists" flag
219
+ * @returns the contract instance or undefined if it does not exist.
214
220
  */
215
- public async getContractInstance(contractAddress: Fr): Promise<TracedContractInstance> {
216
- let exists = true;
217
- const aztecAddress = AztecAddress.fromField(contractAddress);
218
- let instance = await this.worldStateDB.getContractInstance(aztecAddress);
219
- if (instance === undefined) {
220
- instance = SerializableContractInstance.default().withAddress(aztecAddress);
221
- exists = false;
221
+ public async getContractInstance(contractAddress: Fr): Promise<SerializableContractInstance | undefined> {
222
+ this.log.debug(`Getting contract instance for address ${contractAddress}`);
223
+ const instanceWithAddress = await this.worldStateDB.getContractInstance(AztecAddress.fromField(contractAddress));
224
+ const exists = instanceWithAddress !== undefined;
225
+
226
+ if (exists) {
227
+ const instance = new SerializableContractInstance(instanceWithAddress);
228
+ this.log.debug(
229
+ `Got contract instance (address=${contractAddress}): exists=${exists}, instance=${JSON.stringify(instance)}`,
230
+ );
231
+ this.trace.traceGetContractInstance(contractAddress, exists, instance);
232
+
233
+ return Promise.resolve(instance);
234
+ } else {
235
+ this.log.debug(`Contract instance NOT FOUND (address=${contractAddress})`);
236
+ this.trace.traceGetContractInstance(contractAddress, exists);
237
+ return Promise.resolve(undefined);
222
238
  }
223
- this.log.debug(
224
- `Get Contract instance (address=${contractAddress}): exists=${exists}, instance=${JSON.stringify(instance)}`,
225
- );
226
- const tracedInstance = { ...instance, exists };
227
- this.trace.traceGetContractInstance(tracedInstance);
228
- return Promise.resolve(tracedInstance);
229
239
  }
230
240
 
231
241
  /**
@@ -237,10 +247,43 @@ export class AvmPersistableStateManager {
237
247
  }
238
248
 
239
249
  /**
240
- * Get a contract's bytecode from the contracts DB
250
+ * Get a contract's bytecode from the contracts DB, also trace the contract class and instance
241
251
  */
242
- public async getBytecode(contractAddress: AztecAddress, selector: FunctionSelector): Promise<Buffer | undefined> {
243
- return await this.worldStateDB.getBytecode(contractAddress, selector);
252
+ public async getBytecode(contractAddress: AztecAddress): Promise<Buffer | undefined> {
253
+ this.log.debug(`Getting bytecode for contract address ${contractAddress}`);
254
+ const instanceWithAddress = await this.worldStateDB.getContractInstance(contractAddress);
255
+ const exists = instanceWithAddress !== undefined;
256
+
257
+ if (exists) {
258
+ const instance = new SerializableContractInstance(instanceWithAddress);
259
+
260
+ const contractClass = await this.worldStateDB.getContractClass(instance.contractClassId);
261
+ assert(
262
+ contractClass,
263
+ `Contract class not found in DB, but a contract instance was found with this class ID (${instance.contractClassId}). This should not happen!`,
264
+ );
265
+
266
+ const contractClassPreimage = {
267
+ artifactHash: contractClass.artifactHash,
268
+ privateFunctionsRoot: contractClass.privateFunctionsRoot,
269
+ publicBytecodeCommitment: computePublicBytecodeCommitment(contractClass.packedBytecode),
270
+ };
271
+
272
+ this.trace.traceGetBytecode(
273
+ contractAddress,
274
+ exists,
275
+ contractClass.packedBytecode,
276
+ instance,
277
+ contractClassPreimage,
278
+ );
279
+ return contractClass.packedBytecode;
280
+ } else {
281
+ // If the contract instance is not found, we assume it has not been deployed.
282
+ // It doesnt matter what the values of the contract instance are in this case, as long as we tag it with exists=false.
283
+ // This will hint to the avm circuit to just perform the non-membership check on the address and disregard the bytecode hash
284
+ this.trace.traceGetBytecode(contractAddress, exists); // bytecode, instance, class undefined
285
+ return undefined;
286
+ }
244
287
  }
245
288
 
246
289
  /**
@@ -1,23 +1,36 @@
1
- import { Fr } from '@aztec/circuits.js';
2
-
3
1
  import type { AvmContext } from '../avm_context.js';
4
- import { Field, TypeTag } from '../avm_memory_types.js';
2
+ import { Field, TypeTag, Uint1 } from '../avm_memory_types.js';
3
+ import { InstructionExecutionError } from '../errors.js';
5
4
  import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
6
5
  import { Addressing } from './addressing_mode.js';
7
6
  import { Instruction } from './instruction.js';
8
7
 
8
+ export enum ContractInstanceMember {
9
+ DEPLOYER,
10
+ CLASS_ID,
11
+ INIT_HASH,
12
+ }
13
+
9
14
  export class GetContractInstance extends Instruction {
10
15
  static readonly type: string = 'GETCONTRACTINSTANCE';
11
16
  static readonly opcode: Opcode = Opcode.GETCONTRACTINSTANCE;
12
17
  // Informs (de)serialization. See Instruction.deserialize.
13
18
  static readonly wireFormat: OperandType[] = [
14
- OperandType.UINT8,
15
- OperandType.UINT8,
16
- OperandType.UINT32,
17
- OperandType.UINT32,
19
+ OperandType.UINT8, // opcode
20
+ OperandType.UINT8, // indirect bits
21
+ OperandType.UINT8, // member enum (immediate)
22
+ OperandType.UINT16, // addressOffset
23
+ OperandType.UINT16, // dstOffset
24
+ OperandType.UINT16, // existsOfsset
18
25
  ];
19
26
 
20
- constructor(private indirect: number, private addressOffset: number, private dstOffset: number) {
27
+ constructor(
28
+ private indirect: number,
29
+ private memberEnum: number,
30
+ private addressOffset: number,
31
+ private dstOffset: number,
32
+ private existsOffset: number,
33
+ ) {
21
34
  super();
22
35
  }
23
36
 
@@ -25,27 +38,38 @@ export class GetContractInstance extends Instruction {
25
38
  const memory = context.machineState.memory.track(this.type);
26
39
  context.machineState.consumeGas(this.gasCost());
27
40
 
28
- const operands = [this.addressOffset, this.dstOffset];
41
+ if (!(this.memberEnum in ContractInstanceMember)) {
42
+ throw new InstructionExecutionError(`Invalid GETCONSTRACTINSTANCE member enum ${this.memberEnum}`);
43
+ }
44
+
45
+ const operands = [this.addressOffset, this.dstOffset, this.existsOffset];
29
46
  const addressing = Addressing.fromWire(this.indirect, operands.length);
30
- const [addressOffset, dstOffset] = addressing.resolve(operands, memory);
47
+ const [addressOffset, dstOffset, existsOffset] = addressing.resolve(operands, memory);
31
48
  memory.checkTag(TypeTag.FIELD, addressOffset);
32
49
 
33
50
  const address = memory.get(addressOffset).toFr();
34
51
  const instance = await context.persistableState.getContractInstance(address);
52
+ const exists = instance !== undefined;
35
53
 
36
- const data = [
37
- new Fr(instance.exists),
38
- instance.salt,
39
- instance.deployer.toField(),
40
- instance.contractClassId,
41
- instance.initializationHash,
42
- // This this okay ?
43
- ...instance.publicKeys.toFields(),
44
- ].map(f => new Field(f));
54
+ let memberValue = new Field(0);
55
+ if (exists) {
56
+ switch (this.memberEnum as ContractInstanceMember) {
57
+ case ContractInstanceMember.DEPLOYER:
58
+ memberValue = new Field(instance.deployer.toField());
59
+ break;
60
+ case ContractInstanceMember.CLASS_ID:
61
+ memberValue = new Field(instance.contractClassId.toField());
62
+ break;
63
+ case ContractInstanceMember.INIT_HASH:
64
+ memberValue = new Field(instance.initializationHash);
65
+ break;
66
+ }
67
+ }
45
68
 
46
- memory.setSlice(dstOffset, data);
69
+ memory.set(existsOffset, new Uint1(exists ? 1 : 0));
70
+ memory.set(dstOffset, memberValue);
47
71
 
48
- memory.assert({ reads: 1, writes: 17, addressing });
72
+ memory.assert({ reads: 1, writes: 2, addressing });
49
73
  context.machineState.incrementPc();
50
74
  }
51
75
  }
@@ -5,9 +5,9 @@ import { Opcode, OperandType } from '../serialization/instruction_serialization.
5
5
  import { Addressing } from './addressing_mode.js';
6
6
  import { Instruction } from './instruction.js';
7
7
 
8
- export class ToRadixLE extends Instruction {
8
+ export class ToRadixBE extends Instruction {
9
9
  static type: string = 'TORADIXLE';
10
- static readonly opcode: Opcode = Opcode.TORADIXLE;
10
+ static readonly opcode: Opcode = Opcode.TORADIXBE;
11
11
 
12
12
  // Informs (de)serialization. See Instruction.deserialize.
13
13
  static readonly wireFormat: OperandType[] = [
@@ -44,15 +44,18 @@ export class ToRadixLE extends Instruction {
44
44
 
45
45
  let value: bigint = memory.get(srcOffset).toBigInt();
46
46
  const radix: bigint = memory.get(radixOffset).toBigInt();
47
+ if (this.numLimbs < 1) {
48
+ throw new InstructionExecutionError(`ToRadixBE instruction's numLimbs should be > 0 (was ${this.numLimbs})`);
49
+ }
47
50
  if (radix > 256) {
48
- throw new InstructionExecutionError(`ToRadixLE instruction's radix should be <= 256 (was ${radix})`);
51
+ throw new InstructionExecutionError(`ToRadixBE instruction's radix should be <= 256 (was ${radix})`);
49
52
  }
50
53
  const radixBN: bigint = BigInt(radix);
51
- const limbArray = [];
54
+ const limbArray = new Array(this.numLimbs);
52
55
 
53
- for (let i = 0; i < this.numLimbs; i++) {
56
+ for (let i = this.numLimbs - 1; i >= 0; i--) {
54
57
  const limb = value % radixBN;
55
- limbArray.push(limb);
58
+ limbArray[i] = limb;
56
59
  value /= radixBN;
57
60
  }
58
61
 
@@ -1,5 +1,6 @@
1
1
  import type { AvmContext } from '../avm_context.js';
2
2
  import { Field, Uint32, Uint64 } from '../avm_memory_types.js';
3
+ import { InstructionExecutionError } from '../errors.js';
3
4
  import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
4
5
  import { Addressing } from './addressing_mode.js';
5
6
  import { Instruction } from './instruction.js';
@@ -63,7 +64,7 @@ export class GetEnvVar extends Instruction {
63
64
  OperandType.UINT16, // dstOffset
64
65
  ];
65
66
 
66
- constructor(private indirect: number, private varEnum: EnvironmentVariable, private dstOffset: number) {
67
+ constructor(private indirect: number, private varEnum: number, private dstOffset: number) {
67
68
  super();
68
69
  }
69
70
 
@@ -71,11 +72,15 @@ export class GetEnvVar extends Instruction {
71
72
  const memory = context.machineState.memory.track(this.type);
72
73
  context.machineState.consumeGas(this.gasCost());
73
74
 
75
+ if (!(this.varEnum in EnvironmentVariable)) {
76
+ throw new InstructionExecutionError(`Invalid GETENVVAR var enum ${this.varEnum}`);
77
+ }
78
+
74
79
  const operands = [this.dstOffset];
75
80
  const addressing = Addressing.fromWire(this.indirect, operands.length);
76
81
  const [dstOffset] = addressing.resolve(operands, memory);
77
82
 
78
- memory.set(dstOffset, getValue(this.varEnum, context));
83
+ memory.set(dstOffset, getValue(this.varEnum as EnvironmentVariable, context));
79
84
 
80
85
  memory.assert({ writes: 1, addressing });
81
86
  context.machineState.incrementPc();
@@ -1,10 +1,9 @@
1
- import { FunctionSelector, Gas } from '@aztec/circuits.js';
2
- import { padArrayEnd } from '@aztec/foundation/collection';
1
+ import { Fr, FunctionSelector, Gas, PUBLIC_DISPATCH_SELECTOR } from '@aztec/circuits.js';
3
2
 
4
3
  import type { AvmContext } from '../avm_context.js';
5
4
  import { type AvmContractCallResult } from '../avm_contract_call_result.js';
6
5
  import { gasLeftToGas } from '../avm_gas.js';
7
- import { Field, TypeTag, Uint8 } from '../avm_memory_types.js';
6
+ import { type Field, TypeTag, Uint1 } from '../avm_memory_types.js';
8
7
  import { AvmSimulator } from '../avm_simulator.js';
9
8
  import { RethrownError } from '../errors.js';
10
9
  import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
@@ -21,58 +20,39 @@ abstract class ExternalCall extends Instruction {
21
20
  OperandType.UINT16,
22
21
  OperandType.UINT16,
23
22
  OperandType.UINT16,
24
- OperandType.UINT16,
25
- OperandType.UINT16,
26
- OperandType.UINT16,
27
23
  ];
28
24
 
29
25
  constructor(
30
26
  private indirect: number,
31
- private gasOffset: number /* Unused due to no formal gas implementation at this moment */,
27
+ private gasOffset: number,
32
28
  private addrOffset: number,
33
29
  private argsOffset: number,
34
30
  private argsSizeOffset: number,
35
- private retOffset: number,
36
- private retSize: number,
37
31
  private successOffset: number,
38
- // NOTE: Function selector is likely temporary since eventually public contract bytecode will be one
39
- // blob containing all functions, and function selector will become an application-level mechanism
40
- // (e.g. first few bytes of calldata + compiler-generated jump table)
41
- private functionSelectorOffset: number,
42
32
  ) {
43
33
  super();
44
34
  }
45
35
 
46
36
  public async execute(context: AvmContext) {
47
37
  const memory = context.machineState.memory.track(this.type);
48
- const operands = [
49
- this.gasOffset,
50
- this.addrOffset,
51
- this.argsOffset,
52
- this.argsSizeOffset,
53
- this.retOffset,
54
- this.successOffset,
55
- this.functionSelectorOffset,
56
- ];
38
+ const operands = [this.gasOffset, this.addrOffset, this.argsOffset, this.argsSizeOffset, this.successOffset];
57
39
  const addressing = Addressing.fromWire(this.indirect, operands.length);
58
- const [gasOffset, addrOffset, argsOffset, argsSizeOffset, retOffset, successOffset, functionSelectorOffset] =
59
- addressing.resolve(operands, memory);
40
+ const [gasOffset, addrOffset, argsOffset, argsSizeOffset, successOffset] = addressing.resolve(operands, memory);
60
41
  memory.checkTags(TypeTag.FIELD, gasOffset, gasOffset + 1);
61
42
  memory.checkTag(TypeTag.FIELD, addrOffset);
62
43
  memory.checkTag(TypeTag.UINT32, argsSizeOffset);
63
- memory.checkTag(TypeTag.FIELD, functionSelectorOffset);
64
44
 
65
45
  const calldataSize = memory.get(argsSizeOffset).toNumber();
66
46
  memory.checkTagsRange(TypeTag.FIELD, argsOffset, calldataSize);
67
47
 
68
48
  const callAddress = memory.getAs<Field>(addrOffset);
69
49
  const calldata = memory.getSlice(argsOffset, calldataSize).map(f => f.toFr());
70
- const functionSelector = memory.getAs<Field>(functionSelectorOffset).toFr();
50
+ const functionSelector = new Fr(PUBLIC_DISPATCH_SELECTOR);
71
51
  // If we are already in a static call, we propagate the environment.
72
52
  const callType = context.environment.isStaticCall ? 'STATICCALL' : this.type;
73
53
 
74
54
  // First we consume the gas for this operation.
75
- context.machineState.consumeGas(this.gasCost(calldataSize + this.retSize));
55
+ context.machineState.consumeGas(this.gasCost(calldataSize));
76
56
  // Then we consume the gas allocated for the nested call. The excess will be refunded later.
77
57
  // Gas allocation is capped by the amount of gas left in the current context.
78
58
  // We have to do some dancing here because the gas allocation is a field,
@@ -106,18 +86,12 @@ abstract class ExternalCall extends Instruction {
106
86
  throw new RethrownError(nestedCallResults.revertReason.message, nestedCallResults.revertReason);
107
87
  }
108
88
 
109
- // We only take as much data as was specified in the return size and pad with zeroes if the return data is smaller
110
- // than the specified size in order to prevent that memory to be left with garbage
111
- const returnData = nestedCallResults.output.slice(0, this.retSize);
112
- const convertedReturnData = padArrayEnd(
113
- returnData.map(f => new Field(f)),
114
- new Field(0),
115
- this.retSize,
116
- );
89
+ // Save return/revert data for later.
90
+ const fullReturnData = nestedCallResults.output;
91
+ context.machineState.nestedReturndata = fullReturnData;
117
92
 
118
- // Write our return data into memory
119
- memory.set(successOffset, new Uint8(success ? 1 : 0));
120
- memory.setSlice(retOffset, convertedReturnData);
93
+ // Write our success flag into memory.
94
+ memory.set(successOffset, new Uint1(success ? 1 : 0));
121
95
 
122
96
  // Refund unused gas
123
97
  context.machineState.refundGas(gasLeftToGas(nestedContext.machineState));
@@ -132,7 +106,7 @@ abstract class ExternalCall extends Instruction {
132
106
  /*avmCallResults=*/ nestedCallResults,
133
107
  );
134
108
 
135
- memory.assert({ reads: calldataSize + 5, writes: 1 + this.retSize, addressing });
109
+ memory.assert({ reads: calldataSize + 4, writes: 1, addressing });
136
110
  context.machineState.incrementPc();
137
111
  }
138
112
 
@@ -204,22 +178,24 @@ export class Revert extends Instruction {
204
178
  OperandType.UINT16,
205
179
  ];
206
180
 
207
- constructor(private indirect: number, private returnOffset: number, private retSize: number) {
181
+ constructor(private indirect: number, private returnOffset: number, private retSizeOffset: number) {
208
182
  super();
209
183
  }
210
184
 
211
185
  public async execute(context: AvmContext): Promise<void> {
212
186
  const memory = context.machineState.memory.track(this.type);
213
- context.machineState.consumeGas(this.gasCost(this.retSize));
214
187
 
215
- const operands = [this.returnOffset];
188
+ const operands = [this.returnOffset, this.retSizeOffset];
216
189
  const addressing = Addressing.fromWire(this.indirect, operands.length);
217
- const [returnOffset] = addressing.resolve(operands, memory);
190
+ const [returnOffset, retSizeOffset] = addressing.resolve(operands, memory);
218
191
 
219
- const output = memory.getSlice(returnOffset, this.retSize).map(word => word.toFr());
192
+ memory.checkTag(TypeTag.UINT32, retSizeOffset);
193
+ const retSize = memory.get(retSizeOffset).toNumber();
194
+ context.machineState.consumeGas(this.gasCost(retSize));
195
+ const output = memory.getSlice(returnOffset, retSize).map(word => word.toFr());
220
196
 
221
197
  context.machineState.revert(output);
222
- memory.assert({ reads: this.retSize, addressing });
198
+ memory.assert({ reads: retSize + 1, addressing });
223
199
  }
224
200
  }
225
201
 
@@ -1,5 +1,5 @@
1
1
  import type { AvmContext } from '../avm_context.js';
2
- import { Field, TaggedMemory } from '../avm_memory_types.js';
2
+ import { Field, TaggedMemory, TypeTag, Uint32 } from '../avm_memory_types.js';
3
3
  import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
4
4
  import { Addressing } from './addressing_mode.js';
5
5
  import { Instruction } from './instruction.js';
@@ -179,11 +179,11 @@ export class CalldataCopy extends Instruction {
179
179
 
180
180
  public async execute(context: AvmContext): Promise<void> {
181
181
  const memory = context.machineState.memory.track(this.type);
182
- // We don't need to check tags here because: (1) the calldata is NOT in memory, and (2) we are the ones writing to destination.
183
182
  const operands = [this.cdStartOffset, this.copySizeOffset, this.dstOffset];
184
183
  const addressing = Addressing.fromWire(this.indirect, operands.length);
185
184
  const [cdStartOffset, copySizeOffset, dstOffset] = addressing.resolve(operands, memory);
186
185
 
186
+ memory.checkTags(TypeTag.UINT32, cdStartOffset, copySizeOffset);
187
187
  const cdStart = memory.get(cdStartOffset).toNumber();
188
188
  const copySize = memory.get(copySizeOffset).toNumber();
189
189
  context.machineState.consumeGas(this.gasCost(copySize));
@@ -196,3 +196,70 @@ export class CalldataCopy extends Instruction {
196
196
  context.machineState.incrementPc();
197
197
  }
198
198
  }
199
+
200
+ export class ReturndataSize extends Instruction {
201
+ static readonly type: string = 'RETURNDATASIZE';
202
+ static readonly opcode: Opcode = Opcode.RETURNDATASIZE;
203
+ // Informs (de)serialization. See Instruction.deserialize.
204
+ static readonly wireFormat: OperandType[] = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT16];
205
+
206
+ constructor(private indirect: number, private dstOffset: number) {
207
+ super();
208
+ }
209
+
210
+ public async execute(context: AvmContext): Promise<void> {
211
+ const memory = context.machineState.memory.track(this.type);
212
+ const operands = [this.dstOffset];
213
+ const addressing = Addressing.fromWire(this.indirect, operands.length);
214
+ const [dstOffset] = addressing.resolve(operands, memory);
215
+ context.machineState.consumeGas(this.gasCost());
216
+
217
+ memory.set(dstOffset, new Uint32(context.machineState.nestedReturndata.length));
218
+
219
+ memory.assert({ writes: 1, addressing });
220
+ context.machineState.incrementPc();
221
+ }
222
+ }
223
+
224
+ export class ReturndataCopy extends Instruction {
225
+ static readonly type: string = 'RETURNDATACOPY';
226
+ static readonly opcode: Opcode = Opcode.RETURNDATACOPY;
227
+ // Informs (de)serialization. See Instruction.deserialize.
228
+ static readonly wireFormat: OperandType[] = [
229
+ OperandType.UINT8,
230
+ OperandType.UINT8,
231
+ OperandType.UINT16,
232
+ OperandType.UINT16,
233
+ OperandType.UINT16,
234
+ ];
235
+
236
+ constructor(
237
+ private indirect: number,
238
+ private rdStartOffset: number,
239
+ private copySizeOffset: number,
240
+ private dstOffset: number,
241
+ ) {
242
+ super();
243
+ }
244
+
245
+ public async execute(context: AvmContext): Promise<void> {
246
+ const memory = context.machineState.memory.track(this.type);
247
+ const operands = [this.rdStartOffset, this.copySizeOffset, this.dstOffset];
248
+ const addressing = Addressing.fromWire(this.indirect, operands.length);
249
+ const [rdStartOffset, copySizeOffset, dstOffset] = addressing.resolve(operands, memory);
250
+
251
+ memory.checkTags(TypeTag.UINT32, rdStartOffset, copySizeOffset);
252
+ const rdStart = memory.get(rdStartOffset).toNumber();
253
+ const copySize = memory.get(copySizeOffset).toNumber();
254
+ context.machineState.consumeGas(this.gasCost(copySize));
255
+
256
+ const transformedData = context.machineState.nestedReturndata
257
+ .slice(rdStart, rdStart + copySize)
258
+ .map(f => new Field(f));
259
+
260
+ memory.setSlice(dstOffset, transformedData);
261
+
262
+ memory.assert({ reads: 2, writes: copySize, addressing });
263
+ context.machineState.incrementPc();
264
+ }
265
+ }