@aztec/simulator 0.87.1 → 0.87.2-nightly.20250524

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 (103) hide show
  1. package/dest/private/acvm/oracle/oracle.d.ts +1 -1
  2. package/dest/private/acvm/oracle/oracle.d.ts.map +1 -1
  3. package/dest/private/acvm/oracle/oracle.js +2 -2
  4. package/dest/private/acvm/oracle/typed_oracle.d.ts +1 -1
  5. package/dest/private/acvm/oracle/typed_oracle.d.ts.map +1 -1
  6. package/dest/private/acvm/oracle/typed_oracle.js +2 -2
  7. package/dest/private/private_execution_oracle.d.ts +1 -1
  8. package/dest/private/private_execution_oracle.d.ts.map +1 -1
  9. package/dest/private/private_execution_oracle.js +1 -1
  10. package/dest/private/providers/circuit_recording/circuit_recorder.d.ts +1 -1
  11. package/dest/private/providers/circuit_recording/circuit_recorder.js +1 -1
  12. package/dest/private/utility_execution_oracle.d.ts +1 -1
  13. package/dest/private/utility_execution_oracle.d.ts.map +1 -1
  14. package/dest/private/utility_execution_oracle.js +1 -1
  15. package/dest/public/avm/avm_gas.d.ts +4 -5
  16. package/dest/public/avm/avm_gas.d.ts.map +1 -1
  17. package/dest/public/avm/avm_gas.js +28 -18
  18. package/dest/public/avm/fixtures/utils.js +1 -1
  19. package/dest/public/avm/opcodes/accrued_substate.d.ts.map +1 -1
  20. package/dest/public/avm/opcodes/accrued_substate.js +8 -7
  21. package/dest/public/avm/opcodes/addressing_mode.d.ts +2 -0
  22. package/dest/public/avm/opcodes/addressing_mode.d.ts.map +1 -1
  23. package/dest/public/avm/opcodes/addressing_mode.js +6 -0
  24. package/dest/public/avm/opcodes/arithmetic.d.ts.map +1 -1
  25. package/dest/public/avm/opcodes/arithmetic.js +1 -1
  26. package/dest/public/avm/opcodes/bitwise.d.ts +5 -1
  27. package/dest/public/avm/opcodes/bitwise.d.ts.map +1 -1
  28. package/dest/public/avm/opcodes/bitwise.js +17 -2
  29. package/dest/public/avm/opcodes/comparators.d.ts.map +1 -1
  30. package/dest/public/avm/opcodes/comparators.js +1 -1
  31. package/dest/public/avm/opcodes/contract.d.ts.map +1 -1
  32. package/dest/public/avm/opcodes/contract.js +1 -1
  33. package/dest/public/avm/opcodes/control_flow.d.ts.map +1 -1
  34. package/dest/public/avm/opcodes/control_flow.js +4 -4
  35. package/dest/public/avm/opcodes/conversion.d.ts +1 -0
  36. package/dest/public/avm/opcodes/conversion.d.ts.map +1 -1
  37. package/dest/public/avm/opcodes/conversion.js +263 -2
  38. package/dest/public/avm/opcodes/ec_add.d.ts.map +1 -1
  39. package/dest/public/avm/opcodes/ec_add.js +1 -1
  40. package/dest/public/avm/opcodes/environment_getters.d.ts.map +1 -1
  41. package/dest/public/avm/opcodes/environment_getters.js +1 -1
  42. package/dest/public/avm/opcodes/external_calls.d.ts.map +1 -1
  43. package/dest/public/avm/opcodes/external_calls.js +6 -8
  44. package/dest/public/avm/opcodes/hashing.d.ts.map +1 -1
  45. package/dest/public/avm/opcodes/hashing.js +3 -3
  46. package/dest/public/avm/opcodes/instruction.d.ts +9 -3
  47. package/dest/public/avm/opcodes/instruction.d.ts.map +1 -1
  48. package/dest/public/avm/opcodes/instruction.js +12 -7
  49. package/dest/public/avm/opcodes/memory.d.ts.map +1 -1
  50. package/dest/public/avm/opcodes/memory.js +8 -6
  51. package/dest/public/avm/opcodes/misc.js +1 -3
  52. package/dest/public/avm/opcodes/storage.d.ts.map +1 -1
  53. package/dest/public/avm/opcodes/storage.js +5 -3
  54. package/dest/public/fixtures/index.d.ts +1 -0
  55. package/dest/public/fixtures/index.d.ts.map +1 -1
  56. package/dest/public/fixtures/index.js +1 -0
  57. package/dest/public/fixtures/minimal_public_tx.d.ts +9 -0
  58. package/dest/public/fixtures/minimal_public_tx.d.ts.map +1 -0
  59. package/dest/public/fixtures/minimal_public_tx.js +43 -0
  60. package/dest/public/public_tx_simulator/apps_tests/amm_test.d.ts.map +1 -1
  61. package/dest/public/public_tx_simulator/apps_tests/amm_test.js +32 -18
  62. package/dest/public/public_tx_simulator/public_tx_context.d.ts +1 -1
  63. package/dest/public/public_tx_simulator/public_tx_context.d.ts.map +1 -1
  64. package/dest/public/public_tx_simulator/public_tx_context.js +5 -4
  65. package/dest/public/side_effect_trace.d.ts +4 -1
  66. package/dest/public/side_effect_trace.d.ts.map +1 -1
  67. package/dest/public/side_effect_trace.js +13 -2
  68. package/dest/public/side_effect_trace_interface.d.ts +1 -0
  69. package/dest/public/side_effect_trace_interface.d.ts.map +1 -1
  70. package/dest/public/state_manager/state_manager.d.ts +1 -0
  71. package/dest/public/state_manager/state_manager.d.ts.map +1 -1
  72. package/dest/public/state_manager/state_manager.js +3 -0
  73. package/package.json +15 -15
  74. package/src/private/acvm/oracle/oracle.ts +2 -2
  75. package/src/private/acvm/oracle/typed_oracle.ts +2 -2
  76. package/src/private/private_execution_oracle.ts +1 -1
  77. package/src/private/providers/circuit_recording/circuit_recorder.ts +1 -1
  78. package/src/private/utility_execution_oracle.ts +1 -1
  79. package/src/public/avm/avm_gas.ts +20 -14
  80. package/src/public/avm/fixtures/utils.ts +1 -1
  81. package/src/public/avm/opcodes/accrued_substate.ts +23 -7
  82. package/src/public/avm/opcodes/addressing_mode.ts +8 -0
  83. package/src/public/avm/opcodes/arithmetic.ts +3 -1
  84. package/src/public/avm/opcodes/bitwise.ts +26 -2
  85. package/src/public/avm/opcodes/comparators.ts +3 -1
  86. package/src/public/avm/opcodes/contract.ts +3 -1
  87. package/src/public/avm/opcodes/control_flow.ts +6 -4
  88. package/src/public/avm/opcodes/conversion.ts +21 -2
  89. package/src/public/avm/opcodes/ec_add.ts +3 -1
  90. package/src/public/avm/opcodes/environment_getters.ts +3 -1
  91. package/src/public/avm/opcodes/external_calls.ts +16 -8
  92. package/src/public/avm/opcodes/hashing.ts +11 -3
  93. package/src/public/avm/opcodes/instruction.ts +14 -7
  94. package/src/public/avm/opcodes/memory.ts +23 -6
  95. package/src/public/avm/opcodes/misc.ts +4 -4
  96. package/src/public/avm/opcodes/storage.ts +13 -3
  97. package/src/public/fixtures/index.ts +1 -0
  98. package/src/public/fixtures/minimal_public_tx.ts +57 -0
  99. package/src/public/public_tx_simulator/apps_tests/amm_test.ts +51 -20
  100. package/src/public/public_tx_simulator/public_tx_context.ts +26 -5
  101. package/src/public/side_effect_trace.ts +13 -0
  102. package/src/public/side_effect_trace_interface.ts +1 -0
  103. package/src/public/state_manager/state_manager.ts +4 -0
@@ -500,7 +500,7 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle {
500
500
  await this.executionDataProvider.incrementAppTaggingSecretIndexAsSender(this.contractAddress, sender, recipient);
501
501
  }
502
502
 
503
- public override async syncNotes(pendingTaggedLogArrayBaseSlot: Fr) {
503
+ public override async fetchTaggedLogs(pendingTaggedLogArrayBaseSlot: Fr) {
504
504
  await this.executionDataProvider.syncTaggedLogs(this.contractAddress, pendingTaggedLogArrayBaseSlot, this.scopes);
505
505
 
506
506
  await this.executionDataProvider.removeNullifiedNotes(this.contractAddress);
@@ -51,7 +51,7 @@ import { Oracle } from '../../acvm/oracle/oracle.js';
51
51
  * ]
52
52
  * },
53
53
  * {
54
- * "name": "syncNotes",
54
+ * "name": "fetchTaggedLogs",
55
55
  * "inputs": []
56
56
  * }
57
57
  * ]
@@ -274,7 +274,7 @@ export class UtilityExecutionOracle extends TypedOracle {
274
274
  return await this.executionDataProvider.getIndexedTaggingSecretAsSender(this.contractAddress, sender, recipient);
275
275
  }
276
276
 
277
- public override async syncNotes(pendingTaggedLogArrayBaseSlot: Fr) {
277
+ public override async fetchTaggedLogs(pendingTaggedLogArrayBaseSlot: Fr) {
278
278
  await this.executionDataProvider.syncTaggedLogs(this.contractAddress, pendingTaggedLogArrayBaseSlot, this.scopes);
279
279
 
280
280
  await this.executionDataProvider.removeNullifiedNotes(this.contractAddress);
@@ -104,7 +104,7 @@ const BASE_GAS_COSTS: Record<Opcode, Gas> = {
104
104
  [Opcode.MOV_8]: makeCost(c.AVM_MOV_BASE_L2_GAS, 0),
105
105
  [Opcode.MOV_16]: makeCost(c.AVM_MOV_BASE_L2_GAS, 0),
106
106
  [Opcode.SLOAD]: makeCost(c.AVM_SLOAD_BASE_L2_GAS, 0),
107
- [Opcode.SSTORE]: makeCost(c.AVM_SSTORE_BASE_L2_GAS, c.AVM_SSTORE_BASE_DA_GAS),
107
+ [Opcode.SSTORE]: makeCost(c.AVM_SSTORE_BASE_L2_GAS, 0), // DA gas is dynamic
108
108
  [Opcode.NOTEHASHEXISTS]: makeCost(c.AVM_NOTEHASHEXISTS_BASE_L2_GAS, 0),
109
109
  [Opcode.EMITNOTEHASH]: makeCost(c.AVM_EMITNOTEHASH_BASE_L2_GAS, c.AVM_EMITNOTEHASH_BASE_DA_GAS),
110
110
  [Opcode.NULLIFIEREXISTS]: makeCost(c.AVM_NULLIFIEREXISTS_BASE_L2_GAS, 0),
@@ -129,13 +129,16 @@ const BASE_GAS_COSTS: Record<Opcode, Gas> = {
129
129
  const DYNAMIC_GAS_COSTS = new Map<Opcode, Gas>([
130
130
  [Opcode.CALLDATACOPY, makeCost(c.AVM_CALLDATACOPY_DYN_L2_GAS, 0)],
131
131
  [Opcode.RETURNDATACOPY, makeCost(c.AVM_RETURNDATACOPY_DYN_L2_GAS, 0)],
132
+ // TODO: Call and static call based on bytecode length
132
133
  [Opcode.EMITUNENCRYPTEDLOG, makeCost(c.AVM_EMITUNENCRYPTEDLOG_DYN_L2_GAS, c.AVM_EMITUNENCRYPTEDLOG_DYN_DA_GAS)],
133
- [Opcode.CALL, makeCost(c.AVM_CALL_DYN_L2_GAS, 0)],
134
- [Opcode.STATICCALL, makeCost(c.AVM_STATICCALL_DYN_L2_GAS, 0)],
135
- [Opcode.RETURN, makeCost(c.AVM_RETURN_DYN_L2_GAS, 0)],
136
- [Opcode.REVERT_8, makeCost(c.AVM_REVERT_DYN_L2_GAS, 0)],
137
- [Opcode.REVERT_16, makeCost(c.AVM_REVERT_DYN_L2_GAS, 0)],
138
134
  [Opcode.TORADIXBE, makeCost(c.AVM_TORADIXBE_DYN_L2_GAS, 0)],
135
+ [Opcode.AND_8, makeCost(c.AVM_BITWISE_DYN_L2_GAS, 0)],
136
+ [Opcode.AND_16, makeCost(c.AVM_BITWISE_DYN_L2_GAS, 0)],
137
+ [Opcode.OR_8, makeCost(c.AVM_BITWISE_DYN_L2_GAS, 0)],
138
+ [Opcode.OR_16, makeCost(c.AVM_BITWISE_DYN_L2_GAS, 0)],
139
+ [Opcode.XOR_8, makeCost(c.AVM_BITWISE_DYN_L2_GAS, 0)],
140
+ [Opcode.XOR_16, makeCost(c.AVM_BITWISE_DYN_L2_GAS, 0)],
141
+ [Opcode.SSTORE, makeCost(0, c.AVM_SSTORE_DYN_DA_GAS)],
139
142
  ]);
140
143
 
141
144
  /** Returns the fixed base gas cost for a given opcode. */
@@ -143,17 +146,20 @@ export function getBaseGasCost(opcode: Opcode): Gas {
143
146
  return BASE_GAS_COSTS[opcode];
144
147
  }
145
148
 
146
- export function getDynamicGasCost(opcode: Opcode): Gas {
147
- return DYNAMIC_GAS_COSTS.has(opcode) ? DYNAMIC_GAS_COSTS.get(opcode)! : makeCost(0, 0);
149
+ export function computeAddressingCost(indirectOperandsCount: number, relativeOperandsCount: number): Gas {
150
+ return makeCost(
151
+ indirectOperandsCount * c.AVM_ADDRESSING_INDIRECT_L2_GAS + relativeOperandsCount * c.AVM_ADDRESSING_RELATIVE_L2_GAS,
152
+ 0,
153
+ );
148
154
  }
149
155
 
150
- /** Returns gas cost for an operation on a given type tag based on the base cost per byte. */
151
- export function getGasCostForTypeTag(tag: TypeTag, baseCost: Gas) {
152
- return mulGas(baseCost, getGasCostMultiplierFromTypeTag(tag));
156
+ export function getDynamicGasCost(opcode: Opcode): Gas {
157
+ return DYNAMIC_GAS_COSTS.has(opcode) ? DYNAMIC_GAS_COSTS.get(opcode)! : makeCost(0, 0);
153
158
  }
154
159
 
155
- /** Returns a multiplier based on the size of the type represented by the tag. Throws on uninitialized or invalid. */
156
- function getGasCostMultiplierFromTypeTag(tag: TypeTag) {
160
+ /** Returns a multiplier based on the byte size of the type represented by the integer tag.
161
+ * Used to account for necessary rows in the bitwise trace. Throws on invalid. */
162
+ export function getBitwiseDynamicGasMultiplier(tag: TypeTag) {
157
163
  switch (tag) {
158
164
  case TypeTag.UINT1: // same as u8
159
165
  return 1;
@@ -168,7 +174,7 @@ function getGasCostMultiplierFromTypeTag(tag: TypeTag) {
168
174
  case TypeTag.UINT128:
169
175
  return 16;
170
176
  case TypeTag.FIELD:
171
- return 32;
177
+ return 0; // Field is not allowed for bitwise operations. However we don't fail in gas, since we'll fail in bitwise.
172
178
  case TypeTag.INVALID:
173
179
  throw new InstructionExecutionError(`Invalid tag type for gas cost multiplier: ${TypeTag[tag]}`);
174
180
  }
@@ -122,7 +122,7 @@ export async function createContractClassAndInstance(
122
122
  const contractClass = await makeContractClassPublic(seed, bytecode);
123
123
 
124
124
  const constructorAbi = getContractFunctionAbi('constructor', contractArtifact);
125
- const { publicKeys } = await deriveKeys(Fr.random());
125
+ const { publicKeys } = await deriveKeys(new Fr(seed));
126
126
  const initializationHash = await computeInitializationHash(constructorAbi, constructorArgs);
127
127
  const contractInstance =
128
128
  originalContractClassId === undefined
@@ -31,7 +31,9 @@ export class NoteHashExists extends Instruction {
31
31
  const memory = context.machineState.memory;
32
32
  const addressing = Addressing.fromWire(this.indirect);
33
33
 
34
- context.machineState.consumeGas(this.gasCost());
34
+ context.machineState.consumeGas(
35
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
36
+ );
35
37
  const operands = [this.noteHashOffset, this.leafIndexOffset, this.existsOffset];
36
38
  const [noteHashOffset, leafIndexOffset, existsOffset] = addressing.resolve(operands, memory);
37
39
  memory.checkTags(TypeTag.FIELD, noteHashOffset, leafIndexOffset);
@@ -62,7 +64,9 @@ export class EmitNoteHash extends Instruction {
62
64
  const memory = context.machineState.memory;
63
65
  const addressing = Addressing.fromWire(this.indirect);
64
66
 
65
- context.machineState.consumeGas(this.gasCost());
67
+ context.machineState.consumeGas(
68
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
69
+ );
66
70
 
67
71
  const operands = [this.noteHashOffset];
68
72
  const [noteHashOffset] = addressing.resolve(operands, memory);
@@ -102,7 +106,9 @@ export class NullifierExists extends Instruction {
102
106
  const memory = context.machineState.memory;
103
107
  const addressing = Addressing.fromWire(this.indirect);
104
108
 
105
- context.machineState.consumeGas(this.gasCost());
109
+ context.machineState.consumeGas(
110
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
111
+ );
106
112
 
107
113
  const operands = [this.nullifierOffset, this.addressOffset, this.existsOffset];
108
114
  const [nullifierOffset, addressOffset, existsOffset] = addressing.resolve(operands, memory);
@@ -137,7 +143,9 @@ export class EmitNullifier extends Instruction {
137
143
  const memory = context.machineState.memory;
138
144
  const addressing = Addressing.fromWire(this.indirect);
139
145
 
140
- context.machineState.consumeGas(this.gasCost());
146
+ context.machineState.consumeGas(
147
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
148
+ );
141
149
 
142
150
  const operands = [this.nullifierOffset];
143
151
  const [nullifierOffset] = addressing.resolve(operands, memory);
@@ -184,7 +192,9 @@ export class L1ToL2MessageExists extends Instruction {
184
192
  const memory = context.machineState.memory;
185
193
  const addressing = Addressing.fromWire(this.indirect);
186
194
 
187
- context.machineState.consumeGas(this.gasCost());
195
+ context.machineState.consumeGas(
196
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
197
+ );
188
198
 
189
199
  const operands = [this.msgHashOffset, this.msgLeafIndexOffset, this.existsOffset];
190
200
  const [msgHashOffset, msgLeafIndexOffset, existsOffset] = addressing.resolve(operands, memory);
@@ -220,6 +230,10 @@ export class EmitUnencryptedLog extends Instruction {
220
230
  const memory = context.machineState.memory;
221
231
  const addressing = Addressing.fromWire(this.indirect);
222
232
 
233
+ context.machineState.consumeGas(
234
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
235
+ );
236
+
223
237
  const operands = [this.logOffset, this.logSizeOffset];
224
238
  const [logOffset, logSizeOffset] = addressing.resolve(operands, memory);
225
239
  memory.checkTag(TypeTag.UINT32, logSizeOffset);
@@ -228,7 +242,7 @@ export class EmitUnencryptedLog extends Instruction {
228
242
 
229
243
  const contractAddress = context.environment.address;
230
244
 
231
- context.machineState.consumeGas(this.gasCost(logSize));
245
+ context.machineState.consumeGas(this.dynamicGasCost(logSize));
232
246
  const log = memory.getSlice(logOffset, logSize).map(f => f.toFr());
233
247
  context.persistableState.writePublicLog(contractAddress, log);
234
248
  }
@@ -256,7 +270,9 @@ export class SendL2ToL1Message extends Instruction {
256
270
  const memory = context.machineState.memory;
257
271
  const addressing = Addressing.fromWire(this.indirect);
258
272
 
259
- context.machineState.consumeGas(this.gasCost());
273
+ context.machineState.consumeGas(
274
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
275
+ );
260
276
 
261
277
  const operands = [this.recipientOffset, this.contentOffset];
262
278
  const [recipientOffset, contentOffset] = addressing.resolve(operands, memory);
@@ -54,6 +54,14 @@ export class Addressing {
54
54
  return wire;
55
55
  }
56
56
 
57
+ public indirectOperandsCount(): number {
58
+ return this.modePerOperand.filter(mode => mode & AddressingMode.INDIRECT).length;
59
+ }
60
+
61
+ public relativeOperandsCount(): number {
62
+ return this.modePerOperand.filter(mode => mode & AddressingMode.RELATIVE).length;
63
+ }
64
+
57
65
  /**
58
66
  * Resolves the offsets using the addressing mode.
59
67
  * @param offsets The offsets to resolve.
@@ -16,7 +16,9 @@ export abstract class ThreeOperandArithmeticInstruction extends ThreeOperandInst
16
16
  const memory = context.machineState.memory;
17
17
  const addressing = Addressing.fromWire(this.indirect);
18
18
 
19
- context.machineState.consumeGas(this.gasCost());
19
+ context.machineState.consumeGas(
20
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
21
+ );
20
22
 
21
23
  const operands = [this.aOffset, this.bOffset, this.dstOffset];
22
24
  const [aOffset, bOffset, dstOffset] = addressing.resolve(operands, memory);
@@ -1,4 +1,5 @@
1
1
  import type { AvmContext } from '../avm_context.js';
2
+ import { getBitwiseDynamicGasMultiplier } from '../avm_gas.js';
2
3
  import { type IntegralValue, TaggedMemory, type TaggedMemoryInterface, TypeTag } from '../avm_memory_types.js';
3
4
  import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
4
5
  import { Addressing } from './addressing_mode.js';
@@ -10,12 +11,17 @@ abstract class ThreeOperandBitwiseInstruction extends ThreeOperandInstruction {
10
11
  const memory = context.machineState.memory;
11
12
  const addressing = Addressing.fromWire(this.indirect);
12
13
 
13
- context.machineState.consumeGas(this.gasCost());
14
+ context.machineState.consumeGas(
15
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
16
+ );
14
17
 
15
18
  const operands = [this.aOffset, this.bOffset, this.dstOffset];
16
19
  const [aOffset, bOffset, dstOffset] = addressing.resolve(operands, memory);
17
20
  this.checkTags(memory, aOffset, bOffset);
18
21
 
22
+ const multiplier = this.getDynamicMultiplier(memory.getTag(aOffset));
23
+ context.machineState.consumeGas(this.dynamicGasCost(multiplier));
24
+
19
25
  const a = memory.getAs<IntegralValue>(aOffset);
20
26
  const b = memory.getAs<IntegralValue>(bOffset);
21
27
 
@@ -28,6 +34,10 @@ abstract class ThreeOperandBitwiseInstruction extends ThreeOperandInstruction {
28
34
  TaggedMemory.checkIsIntegralTag(memory.getTag(aOffset));
29
35
  memory.checkTagsAreSame(aOffset, bOffset);
30
36
  }
37
+
38
+ protected getDynamicMultiplier(_lhsTag: TypeTag): number {
39
+ return 0;
40
+ }
31
41
  }
32
42
 
33
43
  export class And extends ThreeOperandBitwiseInstruction {
@@ -37,6 +47,10 @@ export class And extends ThreeOperandBitwiseInstruction {
37
47
  protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue {
38
48
  return a.and(b);
39
49
  }
50
+
51
+ protected override getDynamicMultiplier(lhsTag: TypeTag): number {
52
+ return getBitwiseDynamicGasMultiplier(lhsTag);
53
+ }
40
54
  }
41
55
 
42
56
  export class Or extends ThreeOperandBitwiseInstruction {
@@ -46,6 +60,10 @@ export class Or extends ThreeOperandBitwiseInstruction {
46
60
  protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue {
47
61
  return a.or(b);
48
62
  }
63
+
64
+ protected override getDynamicMultiplier(lhsTag: TypeTag): number {
65
+ return getBitwiseDynamicGasMultiplier(lhsTag);
66
+ }
49
67
  }
50
68
 
51
69
  export class Xor extends ThreeOperandBitwiseInstruction {
@@ -55,6 +73,10 @@ export class Xor extends ThreeOperandBitwiseInstruction {
55
73
  protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue {
56
74
  return a.xor(b);
57
75
  }
76
+
77
+ protected override getDynamicMultiplier(lhsTag: TypeTag): number {
78
+ return getBitwiseDynamicGasMultiplier(lhsTag);
79
+ }
58
80
  }
59
81
 
60
82
  export class Shl extends ThreeOperandBitwiseInstruction {
@@ -102,7 +124,9 @@ export class Not extends Instruction {
102
124
  const memory = context.machineState.memory;
103
125
  const addressing = Addressing.fromWire(this.indirect);
104
126
 
105
- context.machineState.consumeGas(this.gasCost());
127
+ context.machineState.consumeGas(
128
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
129
+ );
106
130
 
107
131
  const operands = [this.srcOffset, this.dstOffset];
108
132
  const [srcOffset, dstOffset] = addressing.resolve(operands, memory);
@@ -9,7 +9,9 @@ abstract class ComparatorInstruction extends ThreeOperandInstruction {
9
9
  const memory = context.machineState.memory;
10
10
  const addressing = Addressing.fromWire(this.indirect);
11
11
 
12
- context.machineState.consumeGas(this.gasCost());
12
+ context.machineState.consumeGas(
13
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
14
+ );
13
15
 
14
16
  const operands = [this.aOffset, this.bOffset, this.dstOffset];
15
17
  const [aOffset, bOffset, dstOffset] = addressing.resolve(operands, memory);
@@ -36,7 +36,9 @@ export class GetContractInstance extends Instruction {
36
36
  const memory = context.machineState.memory;
37
37
  const addressing = Addressing.fromWire(this.indirect);
38
38
 
39
- context.machineState.consumeGas(this.gasCost());
39
+ context.machineState.consumeGas(
40
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
41
+ );
40
42
 
41
43
  if (!(this.memberEnum in ContractInstanceMember)) {
42
44
  throw new InstructionExecutionError(`Invalid GETCONSTRACTINSTANCE member enum ${this.memberEnum}`);
@@ -16,7 +16,7 @@ export class Jump extends Instruction {
16
16
  }
17
17
 
18
18
  public async execute(context: AvmContext): Promise<void> {
19
- context.machineState.consumeGas(this.gasCost());
19
+ context.machineState.consumeGas(this.baseGasCost(0, 0));
20
20
 
21
21
  context.machineState.pc = this.jumpOffset;
22
22
  }
@@ -50,7 +50,9 @@ export class JumpI extends Instruction {
50
50
  const memory = context.machineState.memory;
51
51
  const addressing = Addressing.fromWire(this.indirect);
52
52
 
53
- context.machineState.consumeGas(this.gasCost());
53
+ context.machineState.consumeGas(
54
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
55
+ );
54
56
 
55
57
  const operands = [this.condOffset];
56
58
  const [condOffset] = addressing.resolve(operands, memory);
@@ -79,7 +81,7 @@ export class InternalCall extends Instruction {
79
81
  }
80
82
 
81
83
  public async execute(context: AvmContext): Promise<void> {
82
- context.machineState.consumeGas(this.gasCost());
84
+ context.machineState.consumeGas(this.baseGasCost(0, 0));
83
85
 
84
86
  context.machineState.internalCallStack.push({
85
87
  callPc: context.machineState.pc,
@@ -104,7 +106,7 @@ export class InternalReturn extends Instruction {
104
106
  }
105
107
 
106
108
  public async execute(context: AvmContext): Promise<void> {
107
- context.machineState.consumeGas(this.gasCost());
109
+ context.machineState.consumeGas(this.baseGasCost(0, 0));
108
110
 
109
111
  const stackEntry = context.machineState.internalCallStack.pop();
110
112
  if (stackEntry === undefined) {
@@ -35,6 +35,10 @@ export class ToRadixBE extends Instruction {
35
35
  const memory = context.machineState.memory;
36
36
  const addressing = Addressing.fromWire(this.indirect);
37
37
 
38
+ context.machineState.consumeGas(
39
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
40
+ );
41
+
38
42
  const operands = [this.srcOffset, this.radixOffset, this.numLimbsOffset, this.outputBitsOffset, this.dstOffset];
39
43
  const [srcOffset, radixOffset, numLimbsOffset, outputBitsOffset, dstOffset] = addressing.resolve(operands, memory);
40
44
 
@@ -45,11 +49,13 @@ export class ToRadixBE extends Instruction {
45
49
  memory.checkTag(TypeTag.UINT1, outputBitsOffset);
46
50
 
47
51
  const numLimbs = memory.get(numLimbsOffset).toNumber();
48
- context.machineState.consumeGas(this.gasCost(numLimbs));
52
+ const radix: bigint = memory.get(radixOffset).toBigInt();
53
+ context.machineState.consumeGas(
54
+ this.dynamicGasCost(Math.max(numLimbs, radix > 256n ? 32 : MODULUS_LIMBS_PER_RADIX[Number(radix)])),
55
+ );
49
56
  const outputBits = memory.get(outputBitsOffset).toNumber();
50
57
 
51
58
  let value: bigint = memory.get(srcOffset).toBigInt();
52
- const radix: bigint = memory.get(radixOffset).toBigInt();
53
59
 
54
60
  if (radix < 2 || radix > 256) {
55
61
  throw new InvalidToRadixInputsError(`ToRadixBE instruction's radix should be in range [2,256] (was ${radix}).`);
@@ -79,3 +85,16 @@ export class ToRadixBE extends Instruction {
79
85
  memory.setSlice(dstOffset, res);
80
86
  }
81
87
  }
88
+
89
+ // First two are for radix = 0 and 1, which are invalid, so we have 0 limbs for those cases.
90
+ export const MODULUS_LIMBS_PER_RADIX: number[] = [
91
+ 0, 0, 254, 161, 127, 110, 99, 91, 85, 81, 77, 74, 71, 69, 67, 65, 64, 63, 61, 60, 59, 58, 57, 57, 56, 55, 54, 54, 53,
92
+ 53, 52, 52, 51, 51, 50, 50, 50, 49, 49, 48, 48, 48, 48, 47, 47, 47, 46, 46, 46, 46, 45, 45, 45, 45, 45, 44, 44, 44,
93
+ 44, 44, 43, 43, 43, 43, 43, 43, 42, 42, 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 40, 40, 40,
94
+ 40, 40, 40, 40, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
95
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
96
+ 36, 36, 36, 36, 36, 36, 36, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
97
+ 35, 35, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
98
+ 34, 34, 34, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
99
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
100
+ ];
@@ -41,7 +41,9 @@ export class EcAdd extends Instruction {
41
41
  const memory = context.machineState.memory;
42
42
  const addressing = Addressing.fromWire(this.indirect);
43
43
 
44
- context.machineState.consumeGas(this.gasCost());
44
+ context.machineState.consumeGas(
45
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
46
+ );
45
47
 
46
48
  const operands = [
47
49
  this.p1XOffset,
@@ -73,7 +73,9 @@ export class GetEnvVar extends Instruction {
73
73
  const memory = context.machineState.memory;
74
74
  const addressing = Addressing.fromWire(this.indirect);
75
75
 
76
- context.machineState.consumeGas(this.gasCost());
76
+ context.machineState.consumeGas(
77
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
78
+ );
77
79
 
78
80
  if (!(this.varEnum in EnvironmentVariable)) {
79
81
  throw new InstructionExecutionError(`Invalid GETENVVAR var enum ${this.varEnum}`);
@@ -32,11 +32,15 @@ abstract class ExternalCall extends Instruction {
32
32
  const memory = context.machineState.memory;
33
33
  const addressing = Addressing.fromWire(this.indirect);
34
34
 
35
+ context.machineState.consumeGas(
36
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
37
+ );
38
+
35
39
  const operands = [this.l2GasOffset, this.daGasOffset, this.addrOffset, this.argsSizeOffset, this.argsOffset];
36
40
  const [l2GasOffset, daGasOffset, addrOffset, argsSizeOffset, argsOffset] = addressing.resolve(operands, memory);
37
- // TODO: Should be U32
38
- memory.checkTags(TypeTag.FIELD, l2GasOffset);
39
- memory.checkTags(TypeTag.FIELD, daGasOffset);
41
+
42
+ memory.checkTags(TypeTag.UINT32, l2GasOffset);
43
+ memory.checkTags(TypeTag.UINT32, daGasOffset);
40
44
  memory.checkTag(TypeTag.FIELD, addrOffset);
41
45
  memory.checkTag(TypeTag.UINT32, argsSizeOffset);
42
46
 
@@ -48,9 +52,7 @@ abstract class ExternalCall extends Instruction {
48
52
  // If we are already in a static call, we propagate the environment.
49
53
  const callType = context.environment.isStaticCall ? 'STATICCALL' : this.type;
50
54
 
51
- // First we consume the gas for this operation.
52
- context.machineState.consumeGas(this.gasCost(calldataSize));
53
- // Then we consume the gas allocated for the nested call. The excess will be refunded later.
55
+ // We consume the gas allocated for the nested call. The excess will be refunded later.
54
56
  // Gas allocation is capped by the amount of gas left in the current context.
55
57
  // We have to do some dancing here because the gas allocation is a field,
56
58
  // but in the machine state we track gas as a number.
@@ -179,12 +181,15 @@ export class Return extends Instruction {
179
181
  const memory = context.machineState.memory;
180
182
  const addressing = Addressing.fromWire(this.indirect);
181
183
 
184
+ context.machineState.consumeGas(
185
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
186
+ );
187
+
182
188
  const operands = [this.returnSizeOffset, this.returnOffset];
183
189
  const [returnSizeOffset, returnOffset] = addressing.resolve(operands, memory);
184
190
 
185
191
  memory.checkTag(TypeTag.UINT32, returnSizeOffset);
186
192
  const returnSize = memory.get(returnSizeOffset).toNumber();
187
- context.machineState.consumeGas(this.gasCost(returnSize));
188
193
 
189
194
  const output = memory.getSlice(returnOffset, returnSize).map(word => word.toFr());
190
195
 
@@ -225,12 +230,15 @@ export class Revert extends Instruction {
225
230
  const memory = context.machineState.memory;
226
231
  const addressing = Addressing.fromWire(this.indirect);
227
232
 
233
+ context.machineState.consumeGas(
234
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
235
+ );
236
+
228
237
  const operands = [this.retSizeOffset, this.returnOffset];
229
238
  const [retSizeOffset, returnOffset] = addressing.resolve(operands, memory);
230
239
 
231
240
  memory.checkTag(TypeTag.UINT32, retSizeOffset);
232
241
  const retSize = memory.get(retSizeOffset).toNumber();
233
- context.machineState.consumeGas(this.gasCost(retSize));
234
242
  const output = memory.getSlice(returnOffset, retSize).map(word => word.toFr());
235
243
 
236
244
  context.machineState.revert(output);
@@ -31,7 +31,9 @@ export class Poseidon2 extends Instruction {
31
31
  const memory = context.machineState.memory;
32
32
  const addressing = Addressing.fromWire(this.indirect);
33
33
 
34
- context.machineState.consumeGas(this.gasCost());
34
+ context.machineState.consumeGas(
35
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
36
+ );
35
37
 
36
38
  const operands = [this.inputStateOffset, this.outputStateOffset];
37
39
 
@@ -74,9 +76,12 @@ export class KeccakF1600 extends Instruction {
74
76
  const memory = context.machineState.memory;
75
77
  const addressing = Addressing.fromWire(this.indirect);
76
78
 
79
+ context.machineState.consumeGas(
80
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
81
+ );
82
+
77
83
  const operands = [this.dstOffset, this.inputOffset];
78
84
  const [dstOffset, inputOffset] = addressing.resolve(operands, memory);
79
- context.machineState.consumeGas(this.gasCost());
80
85
 
81
86
  const stateData = memory.getSlice(inputOffset, inputSize).map(word => word.toBigInt());
82
87
  memory.checkTagsRange(TypeTag.UINT64, inputOffset, inputSize);
@@ -117,11 +122,14 @@ export class Sha256Compression extends Instruction {
117
122
  const memory = context.machineState.memory;
118
123
  const addressing = Addressing.fromWire(this.indirect);
119
124
 
125
+ context.machineState.consumeGas(
126
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
127
+ );
128
+
120
129
  const operands = [this.outputOffset, this.stateOffset, this.inputsOffset];
121
130
  const [outputOffset, stateOffset, inputsOffset] = addressing.resolve(operands, memory);
122
131
 
123
132
  // Note: size of output is same as size of state
124
- context.machineState.consumeGas(this.gasCost());
125
133
  const inputs = Uint32Array.from(memory.getSlice(inputsOffset, INPUTS_SIZE).map(word => word.toNumber()));
126
134
  const state = Uint32Array.from(memory.getSlice(stateOffset, STATE_SIZE).map(word => word.toNumber()));
127
135
 
@@ -3,7 +3,7 @@ import type { Bufferable } from '@aztec/foundation/serialize';
3
3
  import { strict as assert } from 'assert';
4
4
 
5
5
  import type { AvmContext } from '../avm_context.js';
6
- import { type Gas, getBaseGasCost, getDynamicGasCost, mulGas, sumGas } from '../avm_gas.js';
6
+ import { type Gas, computeAddressingCost, getBaseGasCost, getDynamicGasCost, mulGas, sumGas } from '../avm_gas.js';
7
7
  import type { BufferCursor } from '../serialization/buffer_cursor.js';
8
8
  import { Opcode, type OperandType, deserialize, serializeAs } from '../serialization/instruction_serialization.js';
9
9
 
@@ -92,13 +92,20 @@ export abstract class Instruction {
92
92
  }
93
93
 
94
94
  /**
95
- * Computes gas cost for the instruction based on its base cost and memory operations.
96
- * @returns Gas cost.
95
+ * Returns the base gas cost for the instruction.
96
+ * @returns The base gas cost.
97
97
  */
98
- protected gasCost(dynMultiplier: number = 0): Gas {
99
- const baseGasCost = getBaseGasCost(this.opcode);
100
- const dynGasCost = mulGas(getDynamicGasCost(this.opcode), dynMultiplier);
101
- return sumGas(baseGasCost, dynGasCost);
98
+ protected baseGasCost(indirectOperandsCount: number, relativeOperandsCount: number): Gas {
99
+ return sumGas(getBaseGasCost(this.opcode), computeAddressingCost(indirectOperandsCount, relativeOperandsCount));
100
+ }
101
+
102
+ /**
103
+ * Computes the dynamic gas cost for the instruction
104
+ * @param dynMultiplier - The multiplier for the dynamic gas cost.
105
+ * @returns The dynamic gas cost.
106
+ */
107
+ protected dynamicGasCost(dynMultiplier: number = 0): Gas {
108
+ return mulGas(getDynamicGasCost(this.opcode), dynMultiplier);
102
109
  }
103
110
 
104
111
  /**
@@ -68,7 +68,9 @@ export class Set extends Instruction {
68
68
  const memory = context.machineState.memory;
69
69
  const addressing = Addressing.fromWire(this.indirect);
70
70
 
71
- context.machineState.consumeGas(this.gasCost());
71
+ context.machineState.consumeGas(
72
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
73
+ );
72
74
 
73
75
  const operands = [this.dstOffset];
74
76
  const [dstOffset] = addressing.resolve(operands, memory);
@@ -108,7 +110,9 @@ export class Cast extends Instruction {
108
110
  const memory = context.machineState.memory;
109
111
  const addressing = Addressing.fromWire(this.indirect);
110
112
 
111
- context.machineState.consumeGas(this.gasCost());
113
+ context.machineState.consumeGas(
114
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
115
+ );
112
116
 
113
117
  const operands = [this.srcOffset, this.dstOffset];
114
118
  const [srcOffset, dstOffset] = addressing.resolve(operands, memory);
@@ -151,7 +155,9 @@ export class Mov extends Instruction {
151
155
  const memory = context.machineState.memory;
152
156
  const addressing = Addressing.fromWire(this.indirect);
153
157
 
154
- context.machineState.consumeGas(this.gasCost());
158
+ context.machineState.consumeGas(
159
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
160
+ );
155
161
 
156
162
  const operands = [this.srcOffset, this.dstOffset];
157
163
  const [srcOffset, dstOffset] = addressing.resolve(operands, memory);
@@ -185,13 +191,17 @@ export class CalldataCopy extends Instruction {
185
191
  const memory = context.machineState.memory;
186
192
  const addressing = Addressing.fromWire(this.indirect);
187
193
 
194
+ context.machineState.consumeGas(
195
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
196
+ );
197
+
188
198
  const operands = [this.copySizeOffset, this.cdStartOffset, this.dstOffset];
189
199
  const [copySizeOffset, cdStartOffset, dstOffset] = addressing.resolve(operands, memory);
190
200
 
191
201
  memory.checkTags(TypeTag.UINT32, cdStartOffset, copySizeOffset);
192
202
  const cdStart = memory.get(cdStartOffset).toNumber();
193
203
  const copySize = memory.get(copySizeOffset).toNumber();
194
- context.machineState.consumeGas(this.gasCost(copySize));
204
+ context.machineState.consumeGas(this.dynamicGasCost(copySize));
195
205
 
196
206
  // Values which are out-of-range of the calldata array will be set with Field(0);
197
207
  const slice = context.environment.calldata.slice(cdStart, cdStart + copySize).map(f => new Field(f));
@@ -219,9 +229,12 @@ export class ReturndataSize extends Instruction {
219
229
  const memory = context.machineState.memory;
220
230
  const addressing = Addressing.fromWire(this.indirect);
221
231
 
232
+ context.machineState.consumeGas(
233
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
234
+ );
235
+
222
236
  const operands = [this.dstOffset];
223
237
  const [dstOffset] = addressing.resolve(operands, memory);
224
- context.machineState.consumeGas(this.gasCost());
225
238
 
226
239
  memory.set(dstOffset, new Uint32(context.machineState.nestedReturndata.length));
227
240
  }
@@ -252,13 +265,17 @@ export class ReturndataCopy extends Instruction {
252
265
  const memory = context.machineState.memory;
253
266
  const addressing = Addressing.fromWire(this.indirect);
254
267
 
268
+ context.machineState.consumeGas(
269
+ this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()),
270
+ );
271
+
255
272
  const operands = [this.copySizeOffset, this.rdStartOffset, this.dstOffset];
256
273
  const [copySizeOffset, rdStartOffset, dstOffset] = addressing.resolve(operands, memory);
257
274
 
258
275
  memory.checkTags(TypeTag.UINT32, rdStartOffset, copySizeOffset);
259
276
  const rdStart = memory.get(rdStartOffset).toNumber();
260
277
  const copySize = memory.get(copySizeOffset).toNumber();
261
- context.machineState.consumeGas(this.gasCost(copySize));
278
+ context.machineState.consumeGas(this.dynamicGasCost(copySize));
262
279
 
263
280
  // Values which are out-of-range of the returndata array will be set with Field(0);
264
281
  const slice = context.machineState.nestedReturndata.slice(rdStart, rdStart + copySize).map(f => new Field(f));