@aztec/simulator 0.26.6 → 0.27.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dest/acvm/acvm.js +2 -2
  2. package/dest/acvm/serialize.d.ts.map +1 -1
  3. package/dest/acvm/serialize.js +8 -3
  4. package/dest/avm/avm_context.d.ts +3 -3
  5. package/dest/avm/avm_context.d.ts.map +1 -1
  6. package/dest/avm/avm_context.js +6 -5
  7. package/dest/avm/avm_execution_environment.d.ts +3 -3
  8. package/dest/avm/avm_execution_environment.d.ts.map +1 -1
  9. package/dest/avm/avm_execution_environment.js +16 -17
  10. package/dest/avm/avm_memory_types.d.ts +9 -0
  11. package/dest/avm/avm_memory_types.d.ts.map +1 -1
  12. package/dest/avm/avm_memory_types.js +34 -4
  13. package/dest/avm/avm_simulator.d.ts.map +1 -1
  14. package/dest/avm/avm_simulator.js +3 -3
  15. package/dest/avm/opcodes/arithmetic.d.ts.map +1 -1
  16. package/dest/avm/opcodes/arithmetic.js +2 -1
  17. package/dest/avm/opcodes/comparators.d.ts.map +1 -1
  18. package/dest/avm/opcodes/comparators.js +5 -7
  19. package/dest/avm/opcodes/control_flow.d.ts +0 -20
  20. package/dest/avm/opcodes/control_flow.d.ts.map +1 -1
  21. package/dest/avm/opcodes/control_flow.js +2 -46
  22. package/dest/avm/opcodes/external_calls.d.ts +24 -2
  23. package/dest/avm/opcodes/external_calls.d.ts.map +1 -1
  24. package/dest/avm/opcodes/external_calls.js +74 -17
  25. package/dest/avm/serialization/instruction_serialization.d.ts +1 -2
  26. package/dest/avm/serialization/instruction_serialization.d.ts.map +1 -1
  27. package/dest/avm/serialization/instruction_serialization.js +9 -3
  28. package/dest/avm/temporary_executor_migration.d.ts.map +1 -1
  29. package/dest/avm/temporary_executor_migration.js +3 -1
  30. package/dest/client/client_execution_context.d.ts.map +1 -1
  31. package/dest/client/client_execution_context.js +4 -6
  32. package/dest/client/simulator.d.ts.map +1 -1
  33. package/dest/client/simulator.js +3 -3
  34. package/dest/public/execution.d.ts +2 -0
  35. package/dest/public/execution.d.ts.map +1 -1
  36. package/dest/public/execution.js +1 -1
  37. package/dest/public/executor.d.ts +21 -0
  38. package/dest/public/executor.d.ts.map +1 -1
  39. package/dest/public/executor.js +91 -2
  40. package/dest/public/public_execution_context.d.ts.map +1 -1
  41. package/dest/public/public_execution_context.js +1 -2
  42. package/package.json +5 -5
  43. package/src/acvm/acvm.ts +1 -1
  44. package/src/acvm/serialize.ts +9 -3
  45. package/src/avm/avm_context.ts +21 -5
  46. package/src/avm/avm_execution_environment.ts +27 -12
  47. package/src/avm/avm_memory_types.ts +36 -3
  48. package/src/avm/avm_simulator.ts +10 -3
  49. package/src/avm/opcodes/arithmetic.ts +2 -0
  50. package/src/avm/opcodes/comparators.ts +4 -6
  51. package/src/avm/opcodes/control_flow.ts +1 -47
  52. package/src/avm/opcodes/external_calls.ts +88 -14
  53. package/src/avm/serialization/instruction_serialization.ts +8 -2
  54. package/src/avm/temporary_executor_migration.ts +2 -0
  55. package/src/client/client_execution_context.ts +1 -13
  56. package/src/client/simulator.ts +1 -2
  57. package/src/public/execution.ts +2 -0
  58. package/src/public/executor.ts +103 -0
  59. package/src/public/public_execution_context.ts +0 -1
@@ -36,12 +36,19 @@ export class AvmExecutionEnvironment {
36
36
 
37
37
  public readonly calldata: Fr[],
38
38
 
39
+ // Function selector is temporary since eventually public contract bytecode will be one blob
40
+ // containing all functions, and function selector will become an application-level mechanism
41
+ // (e.g. first few bytes of calldata + compiler-generated jump table)
39
42
  public readonly temporaryFunctionSelector: FunctionSelector,
40
43
  ) {}
41
44
 
42
- public deriveEnvironmentForNestedCall(address: AztecAddress, calldata: Fr[]): AvmExecutionEnvironment {
45
+ public deriveEnvironmentForNestedCall(
46
+ address: AztecAddress,
47
+ calldata: Fr[],
48
+ temporaryFunctionSelector: FunctionSelector = FunctionSelector.empty(),
49
+ ): AvmExecutionEnvironment {
43
50
  return new AvmExecutionEnvironment(
44
- /*address=*/ address,
51
+ address,
45
52
  /*storageAddress=*/ address,
46
53
  this.origin,
47
54
  this.sender,
@@ -53,14 +60,18 @@ export class AvmExecutionEnvironment {
53
60
  this.globals,
54
61
  this.isStaticCall,
55
62
  this.isDelegateCall,
56
- /*calldata=*/ calldata,
57
- this.temporaryFunctionSelector,
63
+ calldata,
64
+ temporaryFunctionSelector,
58
65
  );
59
66
  }
60
67
 
61
- public deriveEnvironmentForNestedStaticCall(address: AztecAddress, calldata: Fr[]): AvmExecutionEnvironment {
68
+ public deriveEnvironmentForNestedStaticCall(
69
+ address: AztecAddress,
70
+ calldata: Fr[],
71
+ temporaryFunctionSelector: FunctionSelector = FunctionSelector.empty(),
72
+ ): AvmExecutionEnvironment {
62
73
  return new AvmExecutionEnvironment(
63
- /*address=*/ address,
74
+ address,
64
75
  /*storageAddress=*/ address,
65
76
  this.origin,
66
77
  this.sender,
@@ -72,14 +83,18 @@ export class AvmExecutionEnvironment {
72
83
  this.globals,
73
84
  /*isStaticCall=*/ true,
74
85
  this.isDelegateCall,
75
- /*calldata=*/ calldata,
76
- this.temporaryFunctionSelector,
86
+ calldata,
87
+ temporaryFunctionSelector,
77
88
  );
78
89
  }
79
90
 
80
- public newDelegateCall(address: AztecAddress, calldata: Fr[]): AvmExecutionEnvironment {
91
+ public newDelegateCall(
92
+ address: AztecAddress,
93
+ calldata: Fr[],
94
+ temporaryFunctionSelector: FunctionSelector = FunctionSelector.empty(),
95
+ ): AvmExecutionEnvironment {
81
96
  return new AvmExecutionEnvironment(
82
- /*address=*/ address,
97
+ address,
83
98
  this.storageAddress,
84
99
  this.origin,
85
100
  this.sender,
@@ -91,8 +106,8 @@ export class AvmExecutionEnvironment {
91
106
  this.globals,
92
107
  this.isStaticCall,
93
108
  /*isDelegateCall=*/ true,
94
- /*calldata=*/ calldata,
95
- this.temporaryFunctionSelector,
109
+ calldata,
110
+ temporaryFunctionSelector,
96
111
  );
97
112
  }
98
113
  }
@@ -1,5 +1,6 @@
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
4
 
4
5
  import { strict as assert } from 'assert';
5
6
 
@@ -28,6 +29,10 @@ export abstract class MemoryValue {
28
29
  public toFr(): Fr {
29
30
  return new Fr(this.toBigInt());
30
31
  }
32
+
33
+ public toString(): string {
34
+ return `${this.constructor.name}(0x${this.toBigInt().toString(16)})`;
35
+ }
31
36
  }
32
37
 
33
38
  /** IntegralValue gathers the common operations for all integral memory types. */
@@ -189,6 +194,8 @@ export enum TypeTag {
189
194
 
190
195
  // TODO: Consider automatic conversion when getting undefined values.
191
196
  export class TaggedMemory {
197
+ static readonly log: DebugLogger = createDebugLogger('aztec:avm_simulator:memory');
198
+
192
199
  // FIXME: memory should be 2^32, but TS doesn't allow for arrays that big.
193
200
  static readonly MAX_MEMORY_SIZE = Number((1n << 32n) - 2n);
194
201
  private _mem: MemoryValue[];
@@ -200,25 +207,29 @@ export class TaggedMemory {
200
207
 
201
208
  public get(offset: number): MemoryValue {
202
209
  assert(offset < TaggedMemory.MAX_MEMORY_SIZE);
203
- return this.getAs<MemoryValue>(offset);
210
+ const value = this.getAs<MemoryValue>(offset);
211
+ return value;
204
212
  }
205
213
 
206
214
  public getAs<T>(offset: number): T {
207
215
  assert(offset < TaggedMemory.MAX_MEMORY_SIZE);
208
216
  const word = this._mem[offset];
217
+ TaggedMemory.log(`get(${offset}) = ${word}`);
209
218
  return word as T;
210
219
  }
211
220
 
212
221
  public getSlice(offset: number, size: number): MemoryValue[] {
213
222
  assert(offset < TaggedMemory.MAX_MEMORY_SIZE);
214
223
  assert(offset + size < TaggedMemory.MAX_MEMORY_SIZE);
215
- return this._mem.slice(offset, offset + size);
224
+ const value = this._mem.slice(offset, offset + size);
225
+ TaggedMemory.log(`getSlice(${offset}, ${size}) = ${value}`);
226
+ return value;
216
227
  }
217
228
 
218
229
  public getSliceAs<T>(offset: number, size: number): T[] {
219
230
  assert(offset < TaggedMemory.MAX_MEMORY_SIZE);
220
231
  assert(offset + size < TaggedMemory.MAX_MEMORY_SIZE);
221
- return this._mem.slice(offset, offset + size) as T[];
232
+ return this.getSlice(offset, size) as T[];
222
233
  }
223
234
 
224
235
  public getSliceTags(offset: number, size: number): TypeTag[] {
@@ -230,6 +241,7 @@ export class TaggedMemory {
230
241
  public set(offset: number, v: MemoryValue) {
231
242
  assert(offset < TaggedMemory.MAX_MEMORY_SIZE);
232
243
  this._mem[offset] = v;
244
+ TaggedMemory.log(`set(${offset}, ${v})`);
233
245
  }
234
246
 
235
247
  public setSlice(offset: number, vs: MemoryValue[]) {
@@ -240,6 +252,7 @@ export class TaggedMemory {
240
252
  this._mem.length = offset + vs.length;
241
253
  }
242
254
  this._mem.splice(offset, vs.length, ...vs);
255
+ TaggedMemory.log(`setSlice(${offset}, ${vs})`);
243
256
  }
244
257
 
245
258
  public getTag(offset: number): TypeTag {
@@ -327,4 +340,24 @@ export class TaggedMemory {
327
340
  throw new Error(`${TypeTag[tag]} is not a valid integral type.`);
328
341
  }
329
342
  }
343
+
344
+ // Does not truncate. Type constructor will check that it fits.
345
+ public static buildFromTagOrDie(v: bigint | number, tag: TypeTag): MemoryValue {
346
+ switch (tag) {
347
+ case TypeTag.UINT8:
348
+ return new Uint8(v);
349
+ case TypeTag.UINT16:
350
+ return new Uint16(v);
351
+ case TypeTag.UINT32:
352
+ return new Uint32(v);
353
+ case TypeTag.UINT64:
354
+ return new Uint64(v);
355
+ case TypeTag.UINT128:
356
+ return new Uint128(v);
357
+ case TypeTag.FIELD:
358
+ return new Field(v);
359
+ default:
360
+ throw new Error(`${TypeTag[tag]} is not a valid integral type.`);
361
+ }
362
+ }
330
363
  }
@@ -9,9 +9,13 @@ import type { Instruction } from './opcodes/index.js';
9
9
  import { decodeFromBytecode } from './serialization/bytecode_serialization.js';
10
10
 
11
11
  export class AvmSimulator {
12
- private log: DebugLogger = createDebugLogger('aztec:avm_simulator');
12
+ private log: DebugLogger;
13
13
 
14
- constructor(private context: AvmContext) {}
14
+ constructor(private context: AvmContext) {
15
+ this.log = createDebugLogger(
16
+ `aztec:avm_simulator:core(f:${context.environment.temporaryFunctionSelector.toString()})`,
17
+ );
18
+ }
15
19
 
16
20
  /**
17
21
  * Fetch the bytecode and execute it in the current context.
@@ -52,7 +56,10 @@ export class AvmSimulator {
52
56
  // continuing until the machine state signifies a halt
53
57
  while (!this.context.machineState.halted) {
54
58
  const instruction = instructions[this.context.machineState.pc];
55
- assert(!!instruction); // This should never happen
59
+ assert(
60
+ !!instruction,
61
+ 'AVM attempted to execute non-existent instruction. This should never happen (invalid bytecode or AVM simulator bug)!',
62
+ );
56
63
 
57
64
  this.log.debug(`@${this.context.machineState.pc} ${instruction.toString()}`);
58
65
  // Execute the instruction.
@@ -11,6 +11,8 @@ export class Add extends ThreeOperandInstruction {
11
11
  }
12
12
 
13
13
  async execute(context: AvmContext): Promise<void> {
14
+ context.machineState.memory.checkTags(this.inTag, this.aOffset, this.bOffset);
15
+
14
16
  const a = context.machineState.memory.get(this.aOffset);
15
17
  const b = context.machineState.memory.get(this.bOffset);
16
18
 
@@ -1,4 +1,5 @@
1
1
  import type { AvmContext } from '../avm_context.js';
2
+ import { TaggedMemory } from '../avm_memory_types.js';
2
3
  import { Opcode } from '../serialization/instruction_serialization.js';
3
4
  import { ThreeOperandInstruction } from './instruction_impl.js';
4
5
 
@@ -16,8 +17,7 @@ export class Eq extends ThreeOperandInstruction {
16
17
  const a = context.machineState.memory.get(this.aOffset);
17
18
  const b = context.machineState.memory.get(this.bOffset);
18
19
 
19
- // Result will be of the same type as 'a'.
20
- const dest = a.build(a.equals(b) ? 1n : 0n);
20
+ const dest = TaggedMemory.buildFromTagOrDie(a.equals(b) ? 1n : 0n, this.inTag);
21
21
  context.machineState.memory.set(this.dstOffset, dest);
22
22
 
23
23
  context.machineState.incrementPc();
@@ -38,8 +38,7 @@ export class Lt extends ThreeOperandInstruction {
38
38
  const a = context.machineState.memory.get(this.aOffset);
39
39
  const b = context.machineState.memory.get(this.bOffset);
40
40
 
41
- // Result will be of the same type as 'a'.
42
- const dest = a.build(a.lt(b) ? 1n : 0n);
41
+ const dest = TaggedMemory.buildFromTagOrDie(a.lt(b) ? 1n : 0n, this.inTag);
43
42
  context.machineState.memory.set(this.dstOffset, dest);
44
43
 
45
44
  context.machineState.incrementPc();
@@ -60,8 +59,7 @@ export class Lte extends ThreeOperandInstruction {
60
59
  const a = context.machineState.memory.get(this.aOffset);
61
60
  const b = context.machineState.memory.get(this.bOffset);
62
61
 
63
- // Result will be of the same type as 'a'.
64
- const dest = a.build(a.equals(b) || a.lt(b) ? 1n : 0n);
62
+ const dest = TaggedMemory.buildFromTagOrDie(a.lt(b) || a.equals(b) ? 1n : 0n, this.inTag);
65
63
  context.machineState.memory.set(this.dstOffset, dest);
66
64
 
67
65
  context.machineState.incrementPc();
@@ -4,52 +4,6 @@ import { InstructionExecutionError } from '../errors.js';
4
4
  import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
5
5
  import { Instruction } from './instruction.js';
6
6
 
7
- export class Return extends Instruction {
8
- static type: string = 'RETURN';
9
- static readonly opcode: Opcode = Opcode.RETURN;
10
- // Informs (de)serialization. See Instruction.deserialize.
11
- static readonly wireFormat: OperandType[] = [
12
- OperandType.UINT8,
13
- OperandType.UINT8,
14
- OperandType.UINT32,
15
- OperandType.UINT32,
16
- ];
17
-
18
- constructor(private indirect: number, private returnOffset: number, private copySize: number) {
19
- super();
20
- }
21
-
22
- async execute(context: AvmContext): Promise<void> {
23
- const output = context.machineState.memory.getSlice(this.returnOffset, this.copySize).map(word => word.toFr());
24
-
25
- context.machineState.return(output);
26
- }
27
- }
28
-
29
- export class Revert extends Instruction {
30
- static type: string = 'RETURN';
31
- static readonly opcode: Opcode = Opcode.REVERT;
32
- // Informs (de)serialization. See Instruction.deserialize.
33
- static readonly wireFormat: OperandType[] = [
34
- OperandType.UINT8,
35
- OperandType.UINT8,
36
- OperandType.UINT32,
37
- OperandType.UINT32,
38
- ];
39
-
40
- constructor(private indirect: number, private returnOffset: number, private retSize: number) {
41
- super();
42
- }
43
-
44
- async execute(context: AvmContext): Promise<void> {
45
- const output = context.machineState.memory
46
- .getSlice(this.returnOffset, this.returnOffset + this.retSize)
47
- .map(word => word.toFr());
48
-
49
- context.machineState.revert(output);
50
- }
51
- }
52
-
53
7
  export class Jump extends Instruction {
54
8
  static type: string = 'JUMP';
55
9
  static readonly opcode: Opcode = Opcode.JUMP;
@@ -122,7 +76,7 @@ export class InternalReturn extends Instruction {
122
76
  async execute(context: AvmContext): Promise<void> {
123
77
  const jumpOffset = context.machineState.internalCallStack.pop();
124
78
  if (jumpOffset === undefined) {
125
- throw new InstructionExecutionError('Internal call empty!');
79
+ throw new InstructionExecutionError('Internal call stack empty!');
126
80
  }
127
81
  context.machineState.pc = jumpOffset;
128
82
  }
@@ -1,9 +1,10 @@
1
- import { Fr } from '@aztec/foundation/fields';
1
+ import { FunctionSelector } from '@aztec/circuits.js';
2
2
 
3
3
  import type { AvmContext } from '../avm_context.js';
4
- import { Field } from '../avm_memory_types.js';
4
+ import { Field, Uint8 } from '../avm_memory_types.js';
5
5
  import { AvmSimulator } from '../avm_simulator.js';
6
6
  import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
7
+ import { Addressing } from './addressing_mode.js';
7
8
  import { Instruction } from './instruction.js';
8
9
 
9
10
  export class Call extends Instruction {
@@ -20,6 +21,8 @@ export class Call extends Instruction {
20
21
  OperandType.UINT32,
21
22
  OperandType.UINT32,
22
23
  OperandType.UINT32,
24
+ /* temporary function selector */
25
+ OperandType.UINT32,
23
26
  ];
24
27
 
25
28
  constructor(
@@ -31,16 +34,30 @@ export class Call extends Instruction {
31
34
  private retOffset: number,
32
35
  private retSize: number,
33
36
  private successOffset: number,
37
+ // Function selector is temporary since eventually public contract bytecode will be one blob
38
+ // containing all functions, and function selector will become an application-level mechanism
39
+ // (e.g. first few bytes of calldata + compiler-generated jump table)
40
+ private temporaryFunctionSelectorOffset: number,
34
41
  ) {
35
42
  super();
36
43
  }
37
44
 
38
45
  // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3992): there is no concept of remaining / available gas at this moment
39
46
  async execute(context: AvmContext): Promise<void> {
40
- const callAddress = context.machineState.memory.getAs<Field>(this.addrOffset);
41
- const calldata = context.machineState.memory.getSlice(this.argsOffset, this.argsSize).map(f => f.toFr());
47
+ const [_gasOffset, addrOffset, argsOffset, retOffset, successOffset] = Addressing.fromWire(this.indirect).resolve(
48
+ [this._gasOffset, this.addrOffset, this.argsOffset, this.retOffset, this.successOffset],
49
+ context.machineState.memory,
50
+ );
51
+
52
+ const callAddress = context.machineState.memory.getAs<Field>(addrOffset);
53
+ const calldata = context.machineState.memory.getSlice(argsOffset, this.argsSize).map(f => f.toFr());
54
+ const functionSelector = context.machineState.memory.getAs<Field>(this.temporaryFunctionSelectorOffset).toFr();
42
55
 
43
- const nestedContext = context.createNestedContractCallContext(callAddress.toFr(), calldata);
56
+ const nestedContext = context.createNestedContractCallContext(
57
+ callAddress.toFr(),
58
+ calldata,
59
+ FunctionSelector.fromField(functionSelector),
60
+ );
44
61
 
45
62
  const nestedCallResults = await new AvmSimulator(nestedContext).execute();
46
63
  const success = !nestedCallResults.reverted;
@@ -50,8 +67,8 @@ export class Call extends Instruction {
50
67
  const convertedReturnData = returnData.map(f => new Field(f));
51
68
 
52
69
  // Write our return data into memory
53
- context.machineState.memory.set(this.successOffset, new Field(success ? 1 : 0));
54
- context.machineState.memory.setSlice(this.retOffset, convertedReturnData);
70
+ context.machineState.memory.set(successOffset, new Uint8(success ? 1 : 0));
71
+ context.machineState.memory.setSlice(retOffset, convertedReturnData);
55
72
 
56
73
  if (success) {
57
74
  context.persistableState.acceptNestedCallState(nestedContext.persistableState);
@@ -77,6 +94,8 @@ export class StaticCall extends Instruction {
77
94
  OperandType.UINT32,
78
95
  OperandType.UINT32,
79
96
  OperandType.UINT32,
97
+ /* temporary function selector */
98
+ OperandType.UINT32,
80
99
  ];
81
100
 
82
101
  constructor(
@@ -88,17 +107,26 @@ export class StaticCall extends Instruction {
88
107
  private retOffset: number,
89
108
  private retSize: number,
90
109
  private successOffset: number,
110
+ private temporaryFunctionSelectorOffset: number,
91
111
  ) {
92
112
  super();
93
113
  }
94
114
 
95
115
  async execute(context: AvmContext): Promise<void> {
96
- const callAddress = context.machineState.memory.get(this.addrOffset);
97
- const calldata = context.machineState.memory
98
- .getSlice(this.argsOffset, this.argsSize)
99
- .map(f => new Fr(f.toBigInt()));
116
+ const [_gasOffset, addrOffset, argsOffset, retOffset, successOffset] = Addressing.fromWire(this.indirect).resolve(
117
+ [this._gasOffset, this.addrOffset, this.argsOffset, this.retOffset, this.successOffset],
118
+ context.machineState.memory,
119
+ );
100
120
 
101
- const nestedContext = context.createNestedContractStaticCallContext(callAddress.toFr(), calldata);
121
+ const callAddress = context.machineState.memory.get(addrOffset);
122
+ const calldata = context.machineState.memory.getSlice(argsOffset, this.argsSize).map(f => f.toFr());
123
+ const functionSelector = context.machineState.memory.getAs<Field>(this.temporaryFunctionSelectorOffset).toFr();
124
+
125
+ const nestedContext = context.createNestedContractStaticCallContext(
126
+ callAddress.toFr(),
127
+ calldata,
128
+ FunctionSelector.fromField(functionSelector),
129
+ );
102
130
 
103
131
  const nestedCallResults = await new AvmSimulator(nestedContext).execute();
104
132
  const success = !nestedCallResults.reverted;
@@ -108,8 +136,8 @@ export class StaticCall extends Instruction {
108
136
  const convertedReturnData = returnData.map(f => new Field(f));
109
137
 
110
138
  // Write our return data into memory
111
- context.machineState.memory.set(this.successOffset, new Field(success ? 1 : 0));
112
- context.machineState.memory.setSlice(this.retOffset, convertedReturnData);
139
+ context.machineState.memory.set(successOffset, new Uint8(success ? 1 : 0));
140
+ context.machineState.memory.setSlice(retOffset, convertedReturnData);
113
141
 
114
142
  if (success) {
115
143
  context.persistableState.acceptNestedCallState(nestedContext.persistableState);
@@ -120,3 +148,49 @@ export class StaticCall extends Instruction {
120
148
  context.machineState.incrementPc();
121
149
  }
122
150
  }
151
+
152
+ export class Return extends Instruction {
153
+ static type: string = 'RETURN';
154
+ static readonly opcode: Opcode = Opcode.RETURN;
155
+ // Informs (de)serialization. See Instruction.deserialize.
156
+ static readonly wireFormat: OperandType[] = [
157
+ OperandType.UINT8,
158
+ OperandType.UINT8,
159
+ OperandType.UINT32,
160
+ OperandType.UINT32,
161
+ ];
162
+
163
+ constructor(private indirect: number, private returnOffset: number, private copySize: number) {
164
+ super();
165
+ }
166
+
167
+ async execute(context: AvmContext): Promise<void> {
168
+ const output = context.machineState.memory.getSlice(this.returnOffset, this.copySize).map(word => word.toFr());
169
+
170
+ context.machineState.return(output);
171
+ }
172
+ }
173
+
174
+ export class Revert extends Instruction {
175
+ static type: string = 'RETURN';
176
+ static readonly opcode: Opcode = Opcode.REVERT;
177
+ // Informs (de)serialization. See Instruction.deserialize.
178
+ static readonly wireFormat: OperandType[] = [
179
+ OperandType.UINT8,
180
+ OperandType.UINT8,
181
+ OperandType.UINT32,
182
+ OperandType.UINT32,
183
+ ];
184
+
185
+ constructor(private indirect: number, private returnOffset: number, private retSize: number) {
186
+ super();
187
+ }
188
+
189
+ async execute(context: AvmContext): Promise<void> {
190
+ const output = context.machineState.memory
191
+ .getSlice(this.returnOffset, this.returnOffset + this.retSize)
192
+ .map(word => word.toFr());
193
+
194
+ context.machineState.revert(output);
195
+ }
196
+ }
@@ -7,6 +7,7 @@ import { BufferCursor } from './buffer_cursor.js';
7
7
  * Source: https://yp-aztec.netlify.app/docs/public-vm/instruction-set
8
8
  */
9
9
  export enum Opcode {
10
+ // Compute
10
11
  ADD,
11
12
  SUB,
12
13
  MUL,
@@ -21,6 +22,7 @@ export enum Opcode {
21
22
  SHL,
22
23
  SHR,
23
24
  CAST,
25
+ // Execution environment
24
26
  ADDRESS,
25
27
  STORAGEADDRESS,
26
28
  ORIGIN,
@@ -39,16 +41,20 @@ export enum Opcode {
39
41
  BLOCKL2GASLIMIT,
40
42
  BLOCKDAGASLIMIT,
41
43
  CALLDATACOPY,
44
+ // Gas
42
45
  L1GASLEFT,
43
46
  L2GASLEFT,
44
47
  DAGASLEFT,
48
+ // Control flow
45
49
  JUMP,
46
50
  JUMPI,
47
51
  INTERNALCALL,
48
52
  INTERNALRETURN,
53
+ // Memory
49
54
  SET,
50
55
  MOV,
51
56
  CMOV,
57
+ // World state
52
58
  SLOAD,
53
59
  SSTORE,
54
60
  NOTEHASHEXISTS,
@@ -59,17 +65,17 @@ export enum Opcode {
59
65
  HEADERMEMBER,
60
66
  EMITUNENCRYPTEDLOG,
61
67
  SENDL2TOL1MSG,
68
+ // External calls
62
69
  CALL,
63
70
  STATICCALL,
64
71
  DELEGATECALL,
65
72
  RETURN,
66
73
  REVERT,
74
+ // Gadgets
67
75
  KECCAK,
68
76
  POSEIDON,
69
- // Add new opcodes before this
70
77
  SHA256, // temp - may be removed, but alot of contracts rely on it
71
78
  PEDERSEN, // temp - may be removed, but alot of contracts rely on it
72
- TOTAL_OPCODES_NUMBER,
73
79
  }
74
80
 
75
81
  // Possible types for an instruction's operand in its wire format. (Keep in sync with CPP code.
@@ -94,6 +94,7 @@ export function temporaryConvertAvmResults(
94
94
  // Disabled.
95
95
  const nestedExecutions: PublicExecutionResult[] = [];
96
96
  const nullifierReadRequests: ReadRequest[] = [];
97
+ const nullifierNonExistentReadRequests: ReadRequest[] = [];
97
98
  const newNullifiers: SideEffectLinkedToNoteHash[] = [];
98
99
  const unencryptedLogs = FunctionL2Logs.empty();
99
100
  const newL2ToL1Messages = newWorldState.newL1Messages.map(() => L2ToL1Message.empty());
@@ -101,6 +102,7 @@ export function temporaryConvertAvmResults(
101
102
  return {
102
103
  execution,
103
104
  nullifierReadRequests,
105
+ nullifierNonExistentReadRequests,
104
106
  newNoteHashes,
105
107
  newL2ToL1Messages,
106
108
  newNullifiers,
@@ -10,7 +10,6 @@ import {
10
10
  } from '@aztec/circuit-types';
11
11
  import {
12
12
  CallContext,
13
- ContractDeploymentData,
14
13
  FunctionData,
15
14
  FunctionSelector,
16
15
  Header,
@@ -89,8 +88,6 @@ export class ClientExecutionContext extends ViewDataOracle {
89
88
  * @returns The initial witness.
90
89
  */
91
90
  public getInitialWitness(abi: FunctionAbi) {
92
- const contractDeploymentData = this.txContext.contractDeploymentData;
93
-
94
91
  const argumentsSize = countArgumentsSize(abi);
95
92
 
96
93
  const args = this.packedArgsCache.unpack(this.argsHash);
@@ -102,7 +99,6 @@ export class ClientExecutionContext extends ViewDataOracle {
102
99
  const fields = [
103
100
  ...this.callContext.toFields(),
104
101
  ...this.historicalHeader.toFields(),
105
- ...contractDeploymentData.toFields(),
106
102
 
107
103
  this.txContext.chainId,
108
104
  this.txContext.version,
@@ -348,14 +344,7 @@ export class ClientExecutionContext extends ViewDataOracle {
348
344
  const targetArtifact = await this.db.getFunctionArtifact(targetContractAddress, functionSelector);
349
345
  const targetFunctionData = FunctionData.fromAbi(targetArtifact);
350
346
 
351
- const derivedTxContext = new TxContext(
352
- false,
353
- false,
354
- false,
355
- ContractDeploymentData.empty(),
356
- this.txContext.chainId,
357
- this.txContext.version,
358
- );
347
+ const derivedTxContext = new TxContext(false, false, this.txContext.chainId, this.txContext.version);
359
348
 
360
349
  const derivedCallContext = await this.deriveCallContext(
361
350
  targetContractAddress,
@@ -470,7 +459,6 @@ export class ClientExecutionContext extends ViewDataOracle {
470
459
  FunctionSelector.fromNameAndParameters(targetArtifact.name, targetArtifact.parameters),
471
460
  isDelegateCall,
472
461
  isStaticCall,
473
- false,
474
462
  startSideEffectCounter,
475
463
  );
476
464
  }
@@ -92,10 +92,9 @@ export class AcirSimulator {
92
92
  FunctionSelector.fromNameAndParameters(entryPointArtifact.name, entryPointArtifact.parameters),
93
93
  false,
94
94
  false,
95
- request.functionData.isConstructor,
96
95
  // TODO: when contract deployment is done in-app, we should only reserve one counter for the tx hash
97
96
  // 2 counters are reserved for tx hash and contract deployment nullifier
98
- request.txContext.isContractDeploymentTx ? 2 : 1,
97
+ 1,
99
98
  );
100
99
  const context = new ClientExecutionContext(
101
100
  contractAddress,
@@ -30,6 +30,8 @@ export interface PublicExecutionResult {
30
30
  newNullifiers: SideEffectLinkedToNoteHash[];
31
31
  /** The nullifier read requests emitted in this call. */
32
32
  nullifierReadRequests: ReadRequest[];
33
+ /** The nullifier non existent read requests emitted in this call. */
34
+ nullifierNonExistentReadRequests: ReadRequest[];
33
35
  /** The contract storage reads performed by the function. */
34
36
  contractStorageReads: ContractStorageRead[];
35
37
  /** The contract storage update requests performed by the function. */