@aztec/simulator 0.30.1 → 0.32.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 (115) hide show
  1. package/dest/acvm/oracle/oracle.d.ts +1 -1
  2. package/dest/acvm/oracle/oracle.d.ts.map +1 -1
  3. package/dest/acvm/oracle/oracle.js +3 -3
  4. package/dest/acvm/oracle/typed_oracle.d.ts +1 -1
  5. package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
  6. package/dest/acvm/oracle/typed_oracle.js +2 -2
  7. package/dest/avm/avm_execution_environment.d.ts +7 -0
  8. package/dest/avm/avm_execution_environment.d.ts.map +1 -1
  9. package/dest/avm/avm_execution_environment.js +16 -1
  10. package/dest/avm/avm_gas_cost.d.ts +322 -0
  11. package/dest/avm/avm_gas_cost.d.ts.map +1 -0
  12. package/dest/avm/avm_gas_cost.js +118 -0
  13. package/dest/avm/avm_machine_state.d.ts +12 -1
  14. package/dest/avm/avm_machine_state.d.ts.map +1 -1
  15. package/dest/avm/avm_machine_state.js +31 -2
  16. package/dest/avm/avm_memory_types.d.ts.map +1 -1
  17. package/dest/avm/avm_memory_types.js +2 -1
  18. package/dest/avm/avm_simulator.d.ts.map +1 -1
  19. package/dest/avm/avm_simulator.js +2 -2
  20. package/dest/avm/errors.d.ts +4 -0
  21. package/dest/avm/errors.d.ts.map +1 -1
  22. package/dest/avm/errors.js +8 -1
  23. package/dest/avm/fixtures/index.d.ts +7 -1
  24. package/dest/avm/fixtures/index.d.ts.map +1 -1
  25. package/dest/avm/fixtures/index.js +20 -6
  26. package/dest/avm/journal/journal.d.ts.map +1 -1
  27. package/dest/avm/journal/journal.js +7 -2
  28. package/dest/avm/opcodes/addressing_mode.d.ts +1 -1
  29. package/dest/avm/opcodes/addressing_mode.d.ts.map +1 -1
  30. package/dest/avm/opcodes/addressing_mode.js +1 -1
  31. package/dest/avm/opcodes/arithmetic.d.ts +15 -12
  32. package/dest/avm/opcodes/arithmetic.d.ts.map +1 -1
  33. package/dest/avm/opcodes/arithmetic.js +25 -36
  34. package/dest/avm/opcodes/hashing.d.ts +12 -12
  35. package/dest/avm/opcodes/hashing.d.ts.map +1 -1
  36. package/dest/avm/opcodes/hashing.js +24 -23
  37. package/dest/avm/opcodes/instruction.d.ts +24 -2
  38. package/dest/avm/opcodes/instruction.d.ts.map +1 -1
  39. package/dest/avm/opcodes/instruction.js +45 -2
  40. package/dest/avm/opcodes/memory.d.ts +3 -0
  41. package/dest/avm/opcodes/memory.d.ts.map +1 -1
  42. package/dest/avm/opcodes/memory.js +8 -1
  43. package/dest/avm/temporary_executor_migration.js +3 -3
  44. package/dest/client/client_execution_context.d.ts +3 -3
  45. package/dest/client/client_execution_context.d.ts.map +1 -1
  46. package/dest/client/client_execution_context.js +5 -5
  47. package/dest/client/execution_result.d.ts +5 -5
  48. package/dest/client/execution_result.d.ts.map +1 -1
  49. package/dest/client/private_execution.d.ts.map +1 -1
  50. package/dest/client/private_execution.js +4 -5
  51. package/dest/client/unconstrained_execution.js +2 -2
  52. package/dest/client/view_data_oracle.d.ts +8 -5
  53. package/dest/client/view_data_oracle.d.ts.map +1 -1
  54. package/dest/client/view_data_oracle.js +9 -6
  55. package/dest/index.d.ts +1 -0
  56. package/dest/index.d.ts.map +1 -1
  57. package/dest/index.js +2 -1
  58. package/dest/public/db.d.ts +7 -5
  59. package/dest/public/db.d.ts.map +1 -1
  60. package/dest/public/execution.d.ts +3 -3
  61. package/dest/public/execution.d.ts.map +1 -1
  62. package/dest/public/execution.js +1 -1
  63. package/dest/public/executor.d.ts.map +1 -1
  64. package/dest/public/executor.js +6 -4
  65. package/dest/public/public_execution_context.d.ts +9 -6
  66. package/dest/public/public_execution_context.d.ts.map +1 -1
  67. package/dest/public/public_execution_context.js +11 -8
  68. package/dest/simulator/acvm_native.d.ts +20 -0
  69. package/dest/simulator/acvm_native.d.ts.map +1 -0
  70. package/dest/simulator/acvm_native.js +96 -0
  71. package/dest/simulator/acvm_wasm.d.ts +7 -0
  72. package/dest/simulator/acvm_wasm.d.ts.map +1 -0
  73. package/dest/simulator/acvm_wasm.js +23 -0
  74. package/dest/simulator/index.d.ts +4 -0
  75. package/dest/simulator/index.d.ts.map +1 -0
  76. package/dest/simulator/index.js +4 -0
  77. package/dest/simulator/simulation_provider.d.ts +9 -0
  78. package/dest/simulator/simulation_provider.d.ts.map +1 -0
  79. package/dest/simulator/simulation_provider.js +2 -0
  80. package/dest/test/utils.d.ts.map +1 -1
  81. package/dest/test/utils.js +4 -5
  82. package/dest/utils.js +2 -2
  83. package/package.json +6 -5
  84. package/src/acvm/oracle/oracle.ts +10 -2
  85. package/src/acvm/oracle/typed_oracle.ts +5 -1
  86. package/src/avm/avm_execution_environment.ts +17 -1
  87. package/src/avm/avm_gas_cost.ts +132 -0
  88. package/src/avm/avm_machine_state.ts +34 -1
  89. package/src/avm/avm_memory_types.ts +1 -0
  90. package/src/avm/avm_simulator.ts +1 -2
  91. package/src/avm/errors.ts +8 -0
  92. package/src/avm/fixtures/index.ts +21 -5
  93. package/src/avm/journal/journal.ts +10 -1
  94. package/src/avm/opcodes/addressing_mode.ts +1 -1
  95. package/src/avm/opcodes/arithmetic.ts +34 -47
  96. package/src/avm/opcodes/hashing.ts +27 -20
  97. package/src/avm/opcodes/instruction.ts +49 -2
  98. package/src/avm/opcodes/memory.ts +9 -0
  99. package/src/avm/temporary_executor_migration.ts +2 -2
  100. package/src/client/client_execution_context.ts +7 -5
  101. package/src/client/execution_result.ts +5 -5
  102. package/src/client/private_execution.ts +3 -4
  103. package/src/client/unconstrained_execution.ts +1 -1
  104. package/src/client/view_data_oracle.ts +8 -5
  105. package/src/index.ts +1 -0
  106. package/src/public/db.ts +11 -5
  107. package/src/public/execution.ts +3 -3
  108. package/src/public/executor.ts +5 -3
  109. package/src/public/public_execution_context.ts +10 -7
  110. package/src/simulator/acvm_native.ts +112 -0
  111. package/src/simulator/acvm_wasm.ts +31 -0
  112. package/src/simulator/index.ts +3 -0
  113. package/src/simulator/simulation_provider.ts +10 -0
  114. package/src/test/utils.ts +2 -4
  115. package/src/utils.ts +1 -1
@@ -0,0 +1,132 @@
1
+ import { TypeTag } from './avm_memory_types.js';
2
+ import { Opcode } from './serialization/instruction_serialization.js';
3
+
4
+ /** Gas cost in L1, L2, and DA for a given instruction. */
5
+ export type GasCost = {
6
+ l1Gas: number;
7
+ l2Gas: number;
8
+ daGas: number;
9
+ };
10
+
11
+ /** 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 };
14
+ }
15
+
16
+ /** Gas cost of zero across all gas dimensions. */
17
+ export const EmptyGasCost = {
18
+ l1Gas: 0,
19
+ l2Gas: 0,
20
+ daGas: 0,
21
+ };
22
+
23
+ /** Dimensions of gas usage: L1, L2, and DA. */
24
+ export const GasDimensions = ['l1Gas', 'l2Gas', 'daGas'] as const;
25
+
26
+ /** Null object to represent a gas cost that's dynamic instead of fixed for a given instruction. */
27
+ export const DynamicGasCost = Symbol('DynamicGasCost');
28
+
29
+ /** Temporary default gas cost. We should eventually remove all usage of this variable in favor of actual gas for each opcode. */
30
+ const TemporaryDefaultGasCost = { l1Gas: 0, l2Gas: 10, daGas: 0 };
31
+
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,
38
+ [Opcode.FDIV]: TemporaryDefaultGasCost,
39
+ [Opcode.EQ]: TemporaryDefaultGasCost,
40
+ [Opcode.LT]: TemporaryDefaultGasCost,
41
+ [Opcode.LTE]: TemporaryDefaultGasCost,
42
+ [Opcode.AND]: TemporaryDefaultGasCost,
43
+ [Opcode.OR]: TemporaryDefaultGasCost,
44
+ [Opcode.XOR]: TemporaryDefaultGasCost,
45
+ [Opcode.NOT]: TemporaryDefaultGasCost,
46
+ [Opcode.SHL]: TemporaryDefaultGasCost,
47
+ [Opcode.SHR]: TemporaryDefaultGasCost,
48
+ [Opcode.CAST]: TemporaryDefaultGasCost,
49
+ // Execution environment
50
+ [Opcode.ADDRESS]: TemporaryDefaultGasCost,
51
+ [Opcode.STORAGEADDRESS]: TemporaryDefaultGasCost,
52
+ [Opcode.ORIGIN]: TemporaryDefaultGasCost,
53
+ [Opcode.SENDER]: TemporaryDefaultGasCost,
54
+ [Opcode.PORTAL]: TemporaryDefaultGasCost,
55
+ [Opcode.FEEPERL1GAS]: TemporaryDefaultGasCost,
56
+ [Opcode.FEEPERL2GAS]: TemporaryDefaultGasCost,
57
+ [Opcode.FEEPERDAGAS]: TemporaryDefaultGasCost,
58
+ [Opcode.CONTRACTCALLDEPTH]: TemporaryDefaultGasCost,
59
+ [Opcode.CHAINID]: TemporaryDefaultGasCost,
60
+ [Opcode.VERSION]: TemporaryDefaultGasCost,
61
+ [Opcode.BLOCKNUMBER]: TemporaryDefaultGasCost,
62
+ [Opcode.TIMESTAMP]: TemporaryDefaultGasCost,
63
+ [Opcode.COINBASE]: TemporaryDefaultGasCost,
64
+ [Opcode.BLOCKL1GASLIMIT]: TemporaryDefaultGasCost,
65
+ [Opcode.BLOCKL2GASLIMIT]: TemporaryDefaultGasCost,
66
+ [Opcode.BLOCKDAGASLIMIT]: TemporaryDefaultGasCost,
67
+ [Opcode.CALLDATACOPY]: DynamicGasCost,
68
+ // Gas
69
+ [Opcode.L1GASLEFT]: TemporaryDefaultGasCost,
70
+ [Opcode.L2GASLEFT]: TemporaryDefaultGasCost,
71
+ [Opcode.DAGASLEFT]: TemporaryDefaultGasCost,
72
+ // Control flow
73
+ [Opcode.JUMP]: TemporaryDefaultGasCost,
74
+ [Opcode.JUMPI]: TemporaryDefaultGasCost,
75
+ [Opcode.INTERNALCALL]: TemporaryDefaultGasCost,
76
+ [Opcode.INTERNALRETURN]: TemporaryDefaultGasCost,
77
+ // Memory
78
+ [Opcode.SET]: DynamicGasCost,
79
+ [Opcode.MOV]: TemporaryDefaultGasCost,
80
+ [Opcode.CMOV]: TemporaryDefaultGasCost,
81
+ // World state
82
+ [Opcode.SLOAD]: TemporaryDefaultGasCost,
83
+ [Opcode.SSTORE]: TemporaryDefaultGasCost,
84
+ [Opcode.NOTEHASHEXISTS]: TemporaryDefaultGasCost,
85
+ [Opcode.EMITNOTEHASH]: TemporaryDefaultGasCost,
86
+ [Opcode.NULLIFIEREXISTS]: TemporaryDefaultGasCost,
87
+ [Opcode.EMITNULLIFIER]: TemporaryDefaultGasCost,
88
+ [Opcode.L1TOL2MSGEXISTS]: TemporaryDefaultGasCost,
89
+ [Opcode.HEADERMEMBER]: TemporaryDefaultGasCost,
90
+ [Opcode.EMITUNENCRYPTEDLOG]: TemporaryDefaultGasCost,
91
+ [Opcode.SENDL2TOL1MSG]: TemporaryDefaultGasCost,
92
+ // External calls
93
+ [Opcode.CALL]: TemporaryDefaultGasCost,
94
+ [Opcode.STATICCALL]: TemporaryDefaultGasCost,
95
+ [Opcode.DELEGATECALL]: TemporaryDefaultGasCost,
96
+ [Opcode.RETURN]: TemporaryDefaultGasCost,
97
+ [Opcode.REVERT]: TemporaryDefaultGasCost,
98
+ // Gadgets
99
+ [Opcode.KECCAK]: TemporaryDefaultGasCost,
100
+ [Opcode.POSEIDON]: TemporaryDefaultGasCost,
101
+ [Opcode.SHA256]: TemporaryDefaultGasCost, // temp - may be removed, but alot of contracts rely on i: TemporaryDefaultGasCost,
102
+ [Opcode.PEDERSEN]: TemporaryDefaultGasCost, // temp - may be removed, but alot of contracts rely on i: TemporaryDefaultGasCost,t
103
+ } as const;
104
+
105
+ /** Constants used in base cost calculations. */
106
+ 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,
111
+ };
112
+
113
+ /** 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) {
115
+ switch (tag) {
116
+ case TypeTag.UINT8:
117
+ return 1;
118
+ case TypeTag.UINT16:
119
+ return 2;
120
+ case TypeTag.UINT32:
121
+ return 4;
122
+ case TypeTag.UINT64:
123
+ return 8;
124
+ case TypeTag.UINT128:
125
+ return 16;
126
+ case TypeTag.FIELD:
127
+ return 32;
128
+ case TypeTag.INVALID:
129
+ case TypeTag.UNINITIALIZED:
130
+ throw new Error(`Invalid tag type for gas cost multiplier: ${TypeTag[tag]}`);
131
+ }
132
+ }
@@ -1,7 +1,9 @@
1
1
  import { Fr } from '@aztec/circuits.js';
2
2
 
3
+ import { GasCost, GasDimensions } from './avm_gas_cost.js';
3
4
  import { TaggedMemory } from './avm_memory_types.js';
4
5
  import { AvmContractCallResults } from './avm_message_call_result.js';
6
+ import { OutOfGasError } from './errors.js';
5
7
 
6
8
  /**
7
9
  * A few fields of machine state are initialized from AVM session inputs or call instruction arguments
@@ -35,7 +37,7 @@ export class AvmMachineState {
35
37
  /**
36
38
  * Signals that execution should end.
37
39
  * AvmContext execution continues executing instructions until the machine state signals "halted"
38
- * */
40
+ */
39
41
  public halted: boolean = false;
40
42
  /** Signals that execution has reverted normally (this does not cover exceptional halts) */
41
43
  private reverted: boolean = false;
@@ -52,6 +54,28 @@ export class AvmMachineState {
52
54
  return new AvmMachineState(state.l1GasLeft, state.l2GasLeft, state.daGasLeft);
53
55
  }
54
56
 
57
+ /**
58
+ * Consumes the given gas.
59
+ * Should any of the gas dimensions get depleted, it sets all gas left to zero and triggers
60
+ * an exceptional halt by throwing an OutOfGasError.
61
+ */
62
+ public consumeGas(gasCost: Partial<GasCost>) {
63
+ // Assert there is enough gas on every dimension.
64
+ const outOfGasDimensions = GasDimensions.filter(
65
+ dimension => this[`${dimension}Left`] - (gasCost[dimension] ?? 0) < 0,
66
+ );
67
+ // If not, trigger an exceptional halt.
68
+ // See https://yp-aztec.netlify.app/docs/public-vm/execution#gas-checks-and-tracking
69
+ if (outOfGasDimensions.length > 0) {
70
+ this.exceptionalHalt();
71
+ throw new OutOfGasError(outOfGasDimensions);
72
+ }
73
+ // Otherwise, charge the corresponding gas
74
+ for (const dimension of GasDimensions) {
75
+ this[`${dimension}Left`] -= gasCost[dimension] ?? 0;
76
+ }
77
+ }
78
+
55
79
  /**
56
80
  * Most instructions just increment PC before they complete
57
81
  */
@@ -80,6 +104,15 @@ export class AvmMachineState {
80
104
  this.output = output;
81
105
  }
82
106
 
107
+ /**
108
+ * Flag an exceptional halt. Clears gas left and sets the reverted flag. No output data.
109
+ */
110
+ protected exceptionalHalt() {
111
+ GasDimensions.forEach(dimension => (this[`${dimension}Left`] = 0));
112
+ this.reverted = true;
113
+ this.halted = true;
114
+ }
115
+
83
116
  /**
84
117
  * Get a summary of execution results for a halted machine state
85
118
  * @returns summary of execution results
@@ -229,6 +229,7 @@ export class TaggedMemory {
229
229
  assert(offset + size < TaggedMemory.MAX_MEMORY_SIZE);
230
230
  const value = this._mem.slice(offset, offset + size);
231
231
  TaggedMemory.log(`getSlice(${offset}, ${size}) = ${value}`);
232
+ assert(value.length === size, `Expected slice of size ${size}, got ${value.length}.`);
232
233
  return value;
233
234
  }
234
235
 
@@ -50,7 +50,6 @@ export class AvmSimulator {
50
50
  */
51
51
  public async executeInstructions(instructions: Instruction[]): Promise<AvmContractCallResults> {
52
52
  assert(instructions.length > 0);
53
-
54
53
  try {
55
54
  // Execute instruction pointed to by the current program counter
56
55
  // continuing until the machine state signifies a halt
@@ -65,7 +64,7 @@ export class AvmSimulator {
65
64
  // Execute the instruction.
66
65
  // Normal returns and reverts will return normally here.
67
66
  // "Exceptional halts" will throw.
68
- await instruction.execute(this.context);
67
+ await instruction.run(this.context);
69
68
 
70
69
  if (this.context.machineState.pc >= instructions.length) {
71
70
  this.log('Passed end of program!');
package/src/avm/errors.ts CHANGED
@@ -55,3 +55,11 @@ export class TagCheckError extends AvmExecutionError {
55
55
  this.name = 'TagCheckError';
56
56
  }
57
57
  }
58
+
59
+ /** Error thrown when out of gas. */
60
+ export class OutOfGasError extends AvmExecutionError {
61
+ constructor(dimensions: string[]) {
62
+ super(`Not enough ${dimensions.map(d => d.toUpperCase()).join(', ')} gas left`);
63
+ this.name = 'OutOfGasError';
64
+ }
65
+ }
@@ -10,7 +10,7 @@ import merge from 'lodash.merge';
10
10
 
11
11
  import { CommitmentsDB, MessageLoadOracleInputs, PublicContractsDB, PublicStateDB } from '../../index.js';
12
12
  import { AvmContext } from '../avm_context.js';
13
- import { AvmExecutionEnvironment } from '../avm_execution_environment.js';
13
+ import { AvmContextInputs, AvmExecutionEnvironment } from '../avm_execution_environment.js';
14
14
  import { AvmMachineState } from '../avm_machine_state.js';
15
15
  import { HostStorage } from '../journal/host_storage.js';
16
16
  import { AvmPersistableStateManager } from '../journal/journal.js';
@@ -85,13 +85,13 @@ export function initGlobalVariables(overrides?: Partial<GlobalVariables>): Globa
85
85
  }
86
86
 
87
87
  /**
88
- * Create an empty instance of the Machine State where all values are zero, unless overridden in the overrides object
88
+ * Create an empty instance of the Machine State where all values are set to a large enough amount, unless overridden in the overrides object
89
89
  */
90
90
  export function initMachineState(overrides?: Partial<AvmMachineState>): AvmMachineState {
91
91
  return AvmMachineState.fromState({
92
- l1GasLeft: overrides?.l1GasLeft ?? 0,
93
- l2GasLeft: overrides?.l2GasLeft ?? 0,
94
- daGasLeft: overrides?.daGasLeft ?? 0,
92
+ l1GasLeft: overrides?.l1GasLeft ?? 100e6,
93
+ l2GasLeft: overrides?.l2GasLeft ?? 100e6,
94
+ daGasLeft: overrides?.daGasLeft ?? 100e6,
95
95
  });
96
96
  }
97
97
 
@@ -113,3 +113,19 @@ export function initL1ToL2MessageOracleInput(
113
113
  new SiblingPath(L1_TO_L2_MSG_TREE_HEIGHT, Array(L1_TO_L2_MSG_TREE_HEIGHT)),
114
114
  );
115
115
  }
116
+
117
+ /**
118
+ * Adjust the user index to account for the AvmContextInputs size.
119
+ * This is a hack for testing, and should go away once AvmContextInputs themselves go away.
120
+ */
121
+ export function adjustCalldataIndex(userIndex: number): number {
122
+ return userIndex + AvmContextInputs.SIZE;
123
+ }
124
+
125
+ export function anyAvmContextInputs() {
126
+ const tv = [];
127
+ for (let i = 0; i < AvmContextInputs.SIZE; i++) {
128
+ tv.push(expect.any(Fr));
129
+ }
130
+ return tv;
131
+ }
@@ -156,7 +156,16 @@ export class AvmPersistableStateManager {
156
156
  public async checkL1ToL2MessageExists(msgHash: Fr, msgLeafIndex: Fr): Promise<boolean> {
157
157
  let exists = false;
158
158
  try {
159
- const gotMessage = await this.hostStorage.commitmentsDb.getL1ToL2MembershipWitness(msgHash);
159
+ // The following 2 values are used to compute a message nullifier. Given that here we do not care about getting
160
+ // non-nullified messages we can just pass in random values and the nullifier check will effectively be ignored
161
+ // (no nullifier will be found).
162
+ const ignoredContractAddress = AztecAddress.random();
163
+ const ignoredSecret = Fr.random();
164
+ const gotMessage = await this.hostStorage.commitmentsDb.getL1ToL2MembershipWitness(
165
+ ignoredContractAddress,
166
+ msgHash,
167
+ ignoredSecret,
168
+ );
160
169
  exists = gotMessage !== undefined && gotMessage.index == msgLeafIndex.toBigInt();
161
170
  } catch {
162
171
  // error getting message - doesn't exist!
@@ -12,7 +12,7 @@ export enum AddressingMode {
12
12
  export class Addressing {
13
13
  public constructor(
14
14
  /** The addressing mode for each operand. The length of this array is the number of operands of the instruction. */
15
- private readonly modePerOperand: AddressingMode[],
15
+ public readonly modePerOperand: AddressingMode[],
16
16
  ) {
17
17
  assert(modePerOperand.length <= 8, 'At most 8 operands are supported');
18
18
  }
@@ -1,84 +1,71 @@
1
1
  import type { AvmContext } from '../avm_context.js';
2
- import { Field, TypeTag } from '../avm_memory_types.js';
2
+ import { GasCost, GasCostConstants, getGasCostMultiplierFromTypeTag, makeGasCost } from '../avm_gas_cost.js';
3
+ import { Field, MemoryValue, TypeTag } from '../avm_memory_types.js';
3
4
  import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
5
+ import { Addressing, AddressingMode } from './addressing_mode.js';
4
6
  import { Instruction } from './instruction.js';
5
7
  import { ThreeOperandInstruction } from './instruction_impl.js';
6
8
 
7
- export class Add extends ThreeOperandInstruction {
8
- static readonly type: string = 'ADD';
9
- static readonly opcode = Opcode.ADD;
10
-
11
- constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) {
12
- super(indirect, inTag, aOffset, bOffset, dstOffset);
13
- }
14
-
9
+ export abstract class ThreeOperandArithmeticInstruction extends ThreeOperandInstruction {
15
10
  async execute(context: AvmContext): Promise<void> {
16
11
  context.machineState.memory.checkTags(this.inTag, this.aOffset, this.bOffset);
17
12
 
18
13
  const a = context.machineState.memory.get(this.aOffset);
19
14
  const b = context.machineState.memory.get(this.bOffset);
20
15
 
21
- const dest = a.add(b);
16
+ const dest = this.compute(a, b);
22
17
  context.machineState.memory.set(this.dstOffset, dest);
23
18
 
24
19
  context.machineState.incrementPc();
25
20
  }
26
- }
27
21
 
28
- export class Sub extends ThreeOperandInstruction {
29
- static readonly type: string = 'SUB';
30
- static readonly opcode = Opcode.SUB;
22
+ protected gasCost(): GasCost {
23
+ const indirectCount = Addressing.fromWire(this.indirect).modePerOperand.filter(
24
+ mode => mode === AddressingMode.INDIRECT,
25
+ ).length;
31
26
 
32
- constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) {
33
- super(indirect, inTag, aOffset, bOffset, dstOffset);
27
+ const l2Gas =
28
+ indirectCount * GasCostConstants.ARITHMETIC_COST_PER_INDIRECT_ACCESS +
29
+ getGasCostMultiplierFromTypeTag(this.inTag) * GasCostConstants.ARITHMETIC_COST_PER_BYTE;
30
+ return makeGasCost({ l2Gas });
34
31
  }
35
32
 
36
- async execute(context: AvmContext): Promise<void> {
37
- const a = context.machineState.memory.get(this.aOffset);
38
- const b = context.machineState.memory.get(this.bOffset);
33
+ protected abstract compute(a: MemoryValue, b: MemoryValue): MemoryValue;
34
+ }
39
35
 
40
- const dest = a.sub(b);
41
- context.machineState.memory.set(this.dstOffset, dest);
36
+ export class Add extends ThreeOperandArithmeticInstruction {
37
+ static readonly type: string = 'ADD';
38
+ static readonly opcode = Opcode.ADD;
42
39
 
43
- context.machineState.incrementPc();
40
+ protected compute(a: MemoryValue, b: MemoryValue): MemoryValue {
41
+ return a.add(b);
44
42
  }
45
43
  }
46
44
 
47
- export class Mul extends ThreeOperandInstruction {
48
- static type: string = 'MUL';
49
- static readonly opcode = Opcode.MUL;
45
+ export class Sub extends ThreeOperandArithmeticInstruction {
46
+ static readonly type: string = 'SUB';
47
+ static readonly opcode = Opcode.SUB;
50
48
 
51
- constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) {
52
- super(indirect, inTag, aOffset, bOffset, dstOffset);
49
+ protected compute(a: MemoryValue, b: MemoryValue): MemoryValue {
50
+ return a.sub(b);
53
51
  }
52
+ }
54
53
 
55
- async execute(context: AvmContext): Promise<void> {
56
- const a = context.machineState.memory.get(this.aOffset);
57
- const b = context.machineState.memory.get(this.bOffset);
58
-
59
- const dest = a.mul(b);
60
- context.machineState.memory.set(this.dstOffset, dest);
54
+ export class Mul extends ThreeOperandArithmeticInstruction {
55
+ static type: string = 'MUL';
56
+ static readonly opcode = Opcode.MUL;
61
57
 
62
- context.machineState.incrementPc();
58
+ protected compute(a: MemoryValue, b: MemoryValue): MemoryValue {
59
+ return a.mul(b);
63
60
  }
64
61
  }
65
62
 
66
- export class Div extends ThreeOperandInstruction {
63
+ export class Div extends ThreeOperandArithmeticInstruction {
67
64
  static type: string = 'DIV';
68
65
  static readonly opcode = Opcode.DIV;
69
66
 
70
- constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) {
71
- super(indirect, inTag, aOffset, bOffset, dstOffset);
72
- }
73
-
74
- async execute(context: AvmContext): Promise<void> {
75
- const a = context.machineState.memory.get(this.aOffset);
76
- const b = context.machineState.memory.get(this.bOffset);
77
-
78
- const dest = a.div(b);
79
- context.machineState.memory.set(this.dstOffset, dest);
80
-
81
- context.machineState.incrementPc();
67
+ protected compute(a: MemoryValue, b: MemoryValue): MemoryValue {
68
+ return a.div(b);
82
69
  }
83
70
  }
84
71
 
@@ -23,21 +23,24 @@ export class Poseidon2 extends Instruction {
23
23
  constructor(
24
24
  private indirect: number,
25
25
  private dstOffset: number,
26
- private hashOffset: number,
27
- private hashSize: number,
26
+ private messageOffset: number,
27
+ private messageSize: number,
28
28
  ) {
29
29
  super();
30
30
  }
31
31
 
32
32
  async execute(context: AvmContext): Promise<void> {
33
33
  // We hash a set of field elements
34
- const [hashOffset] = Addressing.fromWire(this.indirect).resolve([this.hashOffset], context.machineState.memory);
34
+ const [dstOffset, messageOffset] = Addressing.fromWire(this.indirect).resolve(
35
+ [this.dstOffset, this.messageOffset],
36
+ context.machineState.memory,
37
+ );
35
38
 
36
39
  // Memory pointer will be indirect
37
- const hashData = context.machineState.memory.getSlice(hashOffset, this.hashSize).map(word => word.toBuffer());
40
+ const hashData = context.machineState.memory.getSlice(messageOffset, this.messageSize).map(word => word.toBuffer());
38
41
 
39
42
  const hash = poseidonHash(hashData);
40
- context.machineState.memory.set(this.dstOffset, new Field(hash));
43
+ context.machineState.memory.set(dstOffset, new Field(hash));
41
44
 
42
45
  context.machineState.incrementPc();
43
46
  }
@@ -59,8 +62,8 @@ export class Keccak extends Instruction {
59
62
  constructor(
60
63
  private indirect: number,
61
64
  private dstOffset: number,
62
- private hashOffset: number,
63
- private hashSize: number,
65
+ private messageOffset: number,
66
+ private messageSize: number,
64
67
  ) {
65
68
  super();
66
69
  }
@@ -68,12 +71,12 @@ export class Keccak extends Instruction {
68
71
  // Note hash output is 32 bytes, so takes up two fields
69
72
  async execute(context: AvmContext): Promise<void> {
70
73
  // We hash a set of field elements
71
- const [hashOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve(
72
- [this.hashOffset, this.dstOffset],
74
+ const [dstOffset, messageOffset] = Addressing.fromWire(this.indirect).resolve(
75
+ [this.dstOffset, this.messageOffset],
73
76
  context.machineState.memory,
74
77
  );
75
78
 
76
- const hashData = context.machineState.memory.getSlice(hashOffset, this.hashSize).map(word => word.toBuffer());
79
+ const hashData = context.machineState.memory.getSlice(messageOffset, this.messageSize).map(word => word.toBuffer());
77
80
 
78
81
  const hash = keccak(Buffer.concat(hashData));
79
82
 
@@ -104,21 +107,21 @@ export class Sha256 extends Instruction {
104
107
  constructor(
105
108
  private indirect: number,
106
109
  private dstOffset: number,
107
- private hashOffset: number,
108
- private hashSize: number,
110
+ private messageOffset: number,
111
+ private messageSize: number,
109
112
  ) {
110
113
  super();
111
114
  }
112
115
 
113
116
  // Note hash output is 32 bytes, so takes up two fields
114
117
  async execute(context: AvmContext): Promise<void> {
115
- const [hashOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve(
116
- [this.hashOffset, this.dstOffset],
118
+ const [dstOffset, messageOffset] = Addressing.fromWire(this.indirect).resolve(
119
+ [this.dstOffset, this.messageOffset],
117
120
  context.machineState.memory,
118
121
  );
119
122
 
120
123
  // We hash a set of field elements
121
- const hashData = context.machineState.memory.getSlice(hashOffset, this.hashSize).map(word => word.toBuffer());
124
+ const hashData = context.machineState.memory.getSlice(messageOffset, this.messageSize).map(word => word.toBuffer());
122
125
 
123
126
  const hash = sha256(Buffer.concat(hashData));
124
127
 
@@ -149,21 +152,25 @@ export class Pedersen extends Instruction {
149
152
  constructor(
150
153
  private indirect: number,
151
154
  private dstOffset: number,
152
- private hashOffset: number,
153
- private hashSize: number,
155
+ private messageOffset: number,
156
+ private messageSizeOffset: number,
154
157
  ) {
155
158
  super();
156
159
  }
157
160
 
158
161
  async execute(context: AvmContext): Promise<void> {
159
- const [hashOffset] = Addressing.fromWire(this.indirect).resolve([this.hashOffset], context.machineState.memory);
162
+ const [dstOffset, messageOffset, messageSizeOffset] = Addressing.fromWire(this.indirect).resolve(
163
+ [this.dstOffset, this.messageOffset, this.messageSizeOffset],
164
+ context.machineState.memory,
165
+ );
160
166
 
161
167
  // We hash a set of field elements
162
- const hashData = context.machineState.memory.getSlice(hashOffset, this.hashSize).map(word => word.toBuffer());
168
+ const messageSize = Number(context.machineState.memory.get(messageSizeOffset).toBigInt());
169
+ const hashData = context.machineState.memory.getSlice(messageOffset, messageSize);
163
170
 
164
171
  // No domain sep for now
165
172
  const hash = pedersenHash(hashData);
166
- context.machineState.memory.set(this.dstOffset, new Field(hash));
173
+ context.machineState.memory.set(dstOffset, new Field(hash));
167
174
 
168
175
  context.machineState.incrementPc();
169
176
  }
@@ -1,8 +1,9 @@
1
1
  import { strict as assert } from 'assert';
2
2
 
3
3
  import type { AvmContext } from '../avm_context.js';
4
+ import { DynamicGasCost, GasCost, GasCosts } from '../avm_gas_cost.js';
4
5
  import { BufferCursor } from '../serialization/buffer_cursor.js';
5
- import { OperandType, deserialize, serialize } from '../serialization/instruction_serialization.js';
6
+ import { Opcode, OperandType, deserialize, serialize } from '../serialization/instruction_serialization.js';
6
7
 
7
8
  type InstructionConstructor = {
8
9
  new (...args: any[]): Instruction;
@@ -14,6 +15,28 @@ type InstructionConstructor = {
14
15
  * It's most important aspects are execute and (de)serialize.
15
16
  */
16
17
  export abstract class Instruction {
18
+ /**
19
+ * Consumes gas and executes the instruction.
20
+ * This is the main entry point for the instruction.
21
+ * @param context - The AvmContext in which the instruction executes.
22
+ */
23
+ public run(context: AvmContext): Promise<void> {
24
+ context.machineState.consumeGas(this.gasCost());
25
+ return this.execute(context);
26
+ }
27
+
28
+ /**
29
+ * Loads default gas cost for the instruction from the GasCosts table.
30
+ * Instruction sub-classes can override this if their gas cost is not fixed.
31
+ */
32
+ protected gasCost(): GasCost {
33
+ const gasCost = GasCosts[this.opcode];
34
+ if (gasCost === DynamicGasCost) {
35
+ throw new Error(`Instruction ${this.type} must define its own gas cost`);
36
+ }
37
+ return gasCost;
38
+ }
39
+
17
40
  /**
18
41
  * Execute the instruction.
19
42
  * Instruction sub-classes must implement this.
@@ -21,7 +44,7 @@ export abstract class Instruction {
21
44
  * each instruction until the machine state signals "halted".
22
45
  * @param context - The AvmContext in which the instruction executes.
23
46
  */
24
- public abstract execute(context: AvmContext): Promise<void>;
47
+ protected abstract execute(context: AvmContext): Promise<void>;
25
48
 
26
49
  /**
27
50
  * Generate a string representation of the instruction including
@@ -61,4 +84,28 @@ export abstract class Instruction {
61
84
  const args = res.slice(1); // Remove opcode.
62
85
  return new this(...args);
63
86
  }
87
+
88
+ /**
89
+ * Returns the stringified type of the instruction.
90
+ * Instruction sub-classes should have a static `type` property.
91
+ */
92
+ public get type(): string {
93
+ const type = 'type' in this.constructor && (this.constructor.type as string);
94
+ if (!type) {
95
+ throw new Error(`Instruction class ${this.constructor.name} does not have a static 'type' property defined.`);
96
+ }
97
+ return type;
98
+ }
99
+
100
+ /**
101
+ * Returns the opcode of the instruction.
102
+ * Instruction sub-classes should have a static `opcode` property.
103
+ */
104
+ public get opcode(): Opcode {
105
+ const opcode = 'opcode' in this.constructor ? (this.constructor.opcode as Opcode) : undefined;
106
+ if (opcode === undefined || Opcode[opcode] === undefined) {
107
+ throw new Error(`Instruction class ${this.constructor.name} does not have a static 'opcode' property defined.`);
108
+ }
109
+ return opcode;
110
+ }
64
111
  }
@@ -1,4 +1,5 @@
1
1
  import type { AvmContext } from '../avm_context.js';
2
+ import { GasCost, GasCostConstants, getGasCostMultiplierFromTypeTag, makeGasCost } from '../avm_gas_cost.js';
2
3
  import { Field, TaggedMemory, TypeTag } from '../avm_memory_types.js';
3
4
  import { InstructionExecutionError } from '../errors.js';
4
5
  import { BufferCursor } from '../serialization/buffer_cursor.js';
@@ -79,6 +80,10 @@ export class Set extends Instruction {
79
80
 
80
81
  context.machineState.incrementPc();
81
82
  }
83
+
84
+ protected gasCost(): GasCost {
85
+ return makeGasCost({ l2Gas: GasCostConstants.SET_COST_PER_BYTE * getGasCostMultiplierFromTypeTag(this.inTag) });
86
+ }
82
87
  }
83
88
 
84
89
  export class CMov extends Instruction {
@@ -193,4 +198,8 @@ export class CalldataCopy extends Instruction {
193
198
 
194
199
  context.machineState.incrementPc();
195
200
  }
201
+
202
+ protected gasCost(): GasCost {
203
+ return makeGasCost({ l2Gas: GasCostConstants.CALLDATACOPY_COST_PER_BYTE * this.copySize });
204
+ }
196
205
  }
@@ -1,5 +1,5 @@
1
1
  // All code in this file needs to die once the public executor is phased out.
2
- import { FunctionL2Logs } from '@aztec/circuit-types';
2
+ import { UnencryptedFunctionL2Logs } from '@aztec/circuit-types';
3
3
  import {
4
4
  ContractStorageRead,
5
5
  ContractStorageUpdateRequest,
@@ -96,7 +96,7 @@ export function temporaryConvertAvmResults(
96
96
  const nullifierReadRequests: ReadRequest[] = [];
97
97
  const nullifierNonExistentReadRequests: ReadRequest[] = [];
98
98
  const newNullifiers: SideEffectLinkedToNoteHash[] = [];
99
- const unencryptedLogs = FunctionL2Logs.empty();
99
+ const unencryptedLogs = UnencryptedFunctionL2Logs.empty();
100
100
  const newL2ToL1Messages = newWorldState.newL1Messages.map(() => L2ToL1Message.empty());
101
101
  // TODO keep track of side effect counters
102
102
  const startSideEffectCounter = Fr.ZERO;