@aztec/simulator 0.30.1 → 0.31.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 (88) 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 +18 -0
  11. package/dest/avm/avm_gas_cost.d.ts.map +1 -0
  12. package/dest/avm/avm_gas_cost.js +84 -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/hashing.d.ts +12 -12
  29. package/dest/avm/opcodes/hashing.d.ts.map +1 -1
  30. package/dest/avm/opcodes/hashing.js +24 -23
  31. package/dest/avm/opcodes/instruction.d.ts +24 -2
  32. package/dest/avm/opcodes/instruction.d.ts.map +1 -1
  33. package/dest/avm/opcodes/instruction.js +41 -2
  34. package/dest/client/private_execution.d.ts.map +1 -1
  35. package/dest/client/private_execution.js +4 -5
  36. package/dest/client/unconstrained_execution.js +2 -2
  37. package/dest/client/view_data_oracle.d.ts +8 -5
  38. package/dest/client/view_data_oracle.d.ts.map +1 -1
  39. package/dest/client/view_data_oracle.js +9 -6
  40. package/dest/index.d.ts +1 -0
  41. package/dest/index.d.ts.map +1 -1
  42. package/dest/index.js +2 -1
  43. package/dest/public/db.d.ts +7 -5
  44. package/dest/public/db.d.ts.map +1 -1
  45. package/dest/public/executor.d.ts.map +1 -1
  46. package/dest/public/executor.js +4 -2
  47. package/dest/public/public_execution_context.d.ts +7 -4
  48. package/dest/public/public_execution_context.d.ts.map +1 -1
  49. package/dest/public/public_execution_context.js +9 -6
  50. package/dest/simulator/acvm_native.d.ts +20 -0
  51. package/dest/simulator/acvm_native.d.ts.map +1 -0
  52. package/dest/simulator/acvm_native.js +96 -0
  53. package/dest/simulator/acvm_wasm.d.ts +7 -0
  54. package/dest/simulator/acvm_wasm.d.ts.map +1 -0
  55. package/dest/simulator/acvm_wasm.js +23 -0
  56. package/dest/simulator/index.d.ts +4 -0
  57. package/dest/simulator/index.d.ts.map +1 -0
  58. package/dest/simulator/index.js +4 -0
  59. package/dest/simulator/simulation_provider.d.ts +9 -0
  60. package/dest/simulator/simulation_provider.d.ts.map +1 -0
  61. package/dest/simulator/simulation_provider.js +2 -0
  62. package/dest/test/utils.d.ts.map +1 -1
  63. package/dest/test/utils.js +4 -4
  64. package/package.json +6 -5
  65. package/src/acvm/oracle/oracle.ts +10 -2
  66. package/src/acvm/oracle/typed_oracle.ts +5 -1
  67. package/src/avm/avm_execution_environment.ts +20 -1
  68. package/src/avm/avm_gas_cost.ts +94 -0
  69. package/src/avm/avm_machine_state.ts +34 -1
  70. package/src/avm/avm_memory_types.ts +1 -0
  71. package/src/avm/avm_simulator.ts +1 -2
  72. package/src/avm/errors.ts +8 -0
  73. package/src/avm/fixtures/index.ts +21 -5
  74. package/src/avm/journal/journal.ts +10 -1
  75. package/src/avm/opcodes/hashing.ts +27 -20
  76. package/src/avm/opcodes/instruction.ts +45 -2
  77. package/src/client/private_execution.ts +3 -4
  78. package/src/client/unconstrained_execution.ts +1 -1
  79. package/src/client/view_data_oracle.ts +8 -5
  80. package/src/index.ts +1 -0
  81. package/src/public/db.ts +11 -5
  82. package/src/public/executor.ts +3 -1
  83. package/src/public/public_execution_context.ts +8 -5
  84. package/src/simulator/acvm_native.ts +112 -0
  85. package/src/simulator/acvm_wasm.ts +31 -0
  86. package/src/simulator/index.ts +3 -0
  87. package/src/simulator/simulation_provider.ts +10 -0
  88. package/src/test/utils.ts +2 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/simulator",
3
- "version": "0.30.1",
3
+ "version": "0.31.0",
4
4
  "type": "module",
5
5
  "exports": "./dest/index.js",
6
6
  "typedocOptions": {
@@ -30,11 +30,12 @@
30
30
  "rootDir": "./src"
31
31
  },
32
32
  "dependencies": {
33
- "@aztec/circuit-types": "0.30.1",
34
- "@aztec/circuits.js": "0.30.1",
35
- "@aztec/foundation": "0.30.1",
36
- "@aztec/types": "0.30.1",
33
+ "@aztec/circuit-types": "0.31.0",
34
+ "@aztec/circuits.js": "0.31.0",
35
+ "@aztec/foundation": "0.31.0",
36
+ "@aztec/types": "0.31.0",
37
37
  "@noir-lang/acvm_js": "portal:../../noir/packages/acvm_js",
38
+ "@noir-lang/types": "portal:../../noir/packages/types",
38
39
  "levelup": "^5.1.1",
39
40
  "memdown": "^6.1.1",
40
41
  "tslib": "^2.4.0"
@@ -244,8 +244,16 @@ export class Oracle {
244
244
  return toACVMField(exists);
245
245
  }
246
246
 
247
- async getL1ToL2MembershipWitness([entryKey]: ACVMField[]): Promise<ACVMField[]> {
248
- const message = await this.typedOracle.getL1ToL2MembershipWitness(fromACVMField(entryKey));
247
+ async getL1ToL2MembershipWitness(
248
+ [contractAddress]: ACVMField[],
249
+ [messageHash]: ACVMField[],
250
+ [secret]: ACVMField[],
251
+ ): Promise<ACVMField[]> {
252
+ const message = await this.typedOracle.getL1ToL2MembershipWitness(
253
+ AztecAddress.fromString(contractAddress),
254
+ fromACVMField(messageHash),
255
+ fromACVMField(secret),
256
+ );
249
257
  return message.toFields().map(toACVMField);
250
258
  }
251
259
 
@@ -171,7 +171,11 @@ export abstract class TypedOracle {
171
171
  throw new OracleMethodNotAvailableError('checkNullifierExists');
172
172
  }
173
173
 
174
- getL1ToL2MembershipWitness(_entryKey: Fr): Promise<MessageLoadOracleInputs<typeof L1_TO_L2_MSG_TREE_HEIGHT>> {
174
+ getL1ToL2MembershipWitness(
175
+ _contractAddress: AztecAddress,
176
+ _messageHash: Fr,
177
+ _secret: Fr,
178
+ ): Promise<MessageLoadOracleInputs<typeof L1_TO_L2_MSG_TREE_HEIGHT>> {
175
179
  throw new OracleMethodNotAvailableError('getL1ToL2MembershipWitness');
176
180
  }
177
181
 
@@ -1,8 +1,19 @@
1
1
  import { FunctionSelector, GlobalVariables } from '@aztec/circuits.js';
2
2
  import { AztecAddress } from '@aztec/foundation/aztec-address';
3
+ import { pedersenHash } from '@aztec/foundation/crypto';
3
4
  import { EthAddress } from '@aztec/foundation/eth-address';
4
5
  import { Fr } from '@aztec/foundation/fields';
5
6
 
7
+ export class AvmContextInputs {
8
+ static readonly SIZE = 2;
9
+
10
+ constructor(private selector: Fr, private argsHash: Fr) {}
11
+
12
+ public toFields(): Fr[] {
13
+ return [this.selector, this.argsHash];
14
+ }
15
+ }
16
+
6
17
  /**
7
18
  * Contains variables that remain constant during AVM execution
8
19
  * These variables are provided by the public kernel circuit
@@ -40,7 +51,15 @@ export class AvmExecutionEnvironment {
40
51
  // containing all functions, and function selector will become an application-level mechanism
41
52
  // (e.g. first few bytes of calldata + compiler-generated jump table)
42
53
  public readonly temporaryFunctionSelector: FunctionSelector,
43
- ) {}
54
+ ) {
55
+ // We encode some extra inputs (AvmContextInputs) in calldata.
56
+ // This will have to go once we move away from one proof per call.
57
+ const inputs = new AvmContextInputs(
58
+ temporaryFunctionSelector.toField(),
59
+ pedersenHash(calldata.map(word => word.toBuffer())),
60
+ );
61
+ this.calldata = [...inputs.toFields(), ...calldata];
62
+ }
44
63
 
45
64
  public deriveEnvironmentForNestedCall(
46
65
  address: AztecAddress,
@@ -0,0 +1,94 @@
1
+ import { Opcode } from './serialization/instruction_serialization.js';
2
+
3
+ /** Gas cost in L1, L2, and DA for a given instruction. */
4
+ export type GasCost = {
5
+ l1Gas: number;
6
+ l2Gas: number;
7
+ daGas: number;
8
+ };
9
+
10
+ /** Gas cost of zero across all gas dimensions. */
11
+ export const EmptyGasCost = {
12
+ l1Gas: 0,
13
+ l2Gas: 0,
14
+ daGas: 0,
15
+ };
16
+
17
+ /** Dimensions of gas usage: L1, L2, and DA */
18
+ export const GasDimensions = ['l1Gas', 'l2Gas', 'daGas'] as const;
19
+
20
+ /** Temporary default gas cost. We should eventually remove all usage of this variable in favor of actual gas for each opcode. */
21
+ const TemporaryDefaultGasCost = { l1Gas: 0, l2Gas: 10, daGas: 0 };
22
+
23
+ /** Gas costs for each instruction. */
24
+ export const GasCosts: Record<Opcode, GasCost> = {
25
+ [Opcode.ADD]: TemporaryDefaultGasCost,
26
+ [Opcode.SUB]: TemporaryDefaultGasCost,
27
+ [Opcode.MUL]: TemporaryDefaultGasCost,
28
+ [Opcode.DIV]: TemporaryDefaultGasCost,
29
+ [Opcode.FDIV]: TemporaryDefaultGasCost,
30
+ [Opcode.EQ]: TemporaryDefaultGasCost,
31
+ [Opcode.LT]: TemporaryDefaultGasCost,
32
+ [Opcode.LTE]: TemporaryDefaultGasCost,
33
+ [Opcode.AND]: TemporaryDefaultGasCost,
34
+ [Opcode.OR]: TemporaryDefaultGasCost,
35
+ [Opcode.XOR]: TemporaryDefaultGasCost,
36
+ [Opcode.NOT]: TemporaryDefaultGasCost,
37
+ [Opcode.SHL]: TemporaryDefaultGasCost,
38
+ [Opcode.SHR]: TemporaryDefaultGasCost,
39
+ [Opcode.CAST]: TemporaryDefaultGasCost,
40
+ // Execution environment
41
+ [Opcode.ADDRESS]: TemporaryDefaultGasCost,
42
+ [Opcode.STORAGEADDRESS]: TemporaryDefaultGasCost,
43
+ [Opcode.ORIGIN]: TemporaryDefaultGasCost,
44
+ [Opcode.SENDER]: TemporaryDefaultGasCost,
45
+ [Opcode.PORTAL]: TemporaryDefaultGasCost,
46
+ [Opcode.FEEPERL1GAS]: TemporaryDefaultGasCost,
47
+ [Opcode.FEEPERL2GAS]: TemporaryDefaultGasCost,
48
+ [Opcode.FEEPERDAGAS]: TemporaryDefaultGasCost,
49
+ [Opcode.CONTRACTCALLDEPTH]: TemporaryDefaultGasCost,
50
+ [Opcode.CHAINID]: TemporaryDefaultGasCost,
51
+ [Opcode.VERSION]: TemporaryDefaultGasCost,
52
+ [Opcode.BLOCKNUMBER]: TemporaryDefaultGasCost,
53
+ [Opcode.TIMESTAMP]: TemporaryDefaultGasCost,
54
+ [Opcode.COINBASE]: TemporaryDefaultGasCost,
55
+ [Opcode.BLOCKL1GASLIMIT]: TemporaryDefaultGasCost,
56
+ [Opcode.BLOCKL2GASLIMIT]: TemporaryDefaultGasCost,
57
+ [Opcode.BLOCKDAGASLIMIT]: TemporaryDefaultGasCost,
58
+ [Opcode.CALLDATACOPY]: TemporaryDefaultGasCost,
59
+ // Gas
60
+ [Opcode.L1GASLEFT]: TemporaryDefaultGasCost,
61
+ [Opcode.L2GASLEFT]: TemporaryDefaultGasCost,
62
+ [Opcode.DAGASLEFT]: TemporaryDefaultGasCost,
63
+ // Control flow
64
+ [Opcode.JUMP]: TemporaryDefaultGasCost,
65
+ [Opcode.JUMPI]: TemporaryDefaultGasCost,
66
+ [Opcode.INTERNALCALL]: TemporaryDefaultGasCost,
67
+ [Opcode.INTERNALRETURN]: TemporaryDefaultGasCost,
68
+ // Memory
69
+ [Opcode.SET]: TemporaryDefaultGasCost,
70
+ [Opcode.MOV]: TemporaryDefaultGasCost,
71
+ [Opcode.CMOV]: TemporaryDefaultGasCost,
72
+ // World state
73
+ [Opcode.SLOAD]: TemporaryDefaultGasCost,
74
+ [Opcode.SSTORE]: TemporaryDefaultGasCost,
75
+ [Opcode.NOTEHASHEXISTS]: TemporaryDefaultGasCost,
76
+ [Opcode.EMITNOTEHASH]: TemporaryDefaultGasCost,
77
+ [Opcode.NULLIFIEREXISTS]: TemporaryDefaultGasCost,
78
+ [Opcode.EMITNULLIFIER]: TemporaryDefaultGasCost,
79
+ [Opcode.L1TOL2MSGEXISTS]: TemporaryDefaultGasCost,
80
+ [Opcode.HEADERMEMBER]: TemporaryDefaultGasCost,
81
+ [Opcode.EMITUNENCRYPTEDLOG]: TemporaryDefaultGasCost,
82
+ [Opcode.SENDL2TOL1MSG]: TemporaryDefaultGasCost,
83
+ // External calls
84
+ [Opcode.CALL]: TemporaryDefaultGasCost,
85
+ [Opcode.STATICCALL]: TemporaryDefaultGasCost,
86
+ [Opcode.DELEGATECALL]: TemporaryDefaultGasCost,
87
+ [Opcode.RETURN]: TemporaryDefaultGasCost,
88
+ [Opcode.REVERT]: TemporaryDefaultGasCost,
89
+ // Gadgets
90
+ [Opcode.KECCAK]: TemporaryDefaultGasCost,
91
+ [Opcode.POSEIDON]: TemporaryDefaultGasCost,
92
+ [Opcode.SHA256]: TemporaryDefaultGasCost, // temp - may be removed, but alot of contracts rely on i: TemporaryDefaultGasCost,
93
+ [Opcode.PEDERSEN]: TemporaryDefaultGasCost, // temp - may be removed, but alot of contracts rely on i: TemporaryDefaultGasCost,t
94
+ };
@@ -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 ?? 1e6,
93
+ l2GasLeft: overrides?.l2GasLeft ?? 1e6,
94
+ daGasLeft: overrides?.daGasLeft ?? 1e6,
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!
@@ -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).map(word => word.toBuffer());
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 { EmptyGasCost, 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,24 @@ 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
+ public gasCost(): GasCost {
33
+ return GasCosts[this.opcode] ?? EmptyGasCost;
34
+ }
35
+
17
36
  /**
18
37
  * Execute the instruction.
19
38
  * Instruction sub-classes must implement this.
@@ -21,7 +40,7 @@ export abstract class Instruction {
21
40
  * each instruction until the machine state signals "halted".
22
41
  * @param context - The AvmContext in which the instruction executes.
23
42
  */
24
- public abstract execute(context: AvmContext): Promise<void>;
43
+ protected abstract execute(context: AvmContext): Promise<void>;
25
44
 
26
45
  /**
27
46
  * Generate a string representation of the instruction including
@@ -61,4 +80,28 @@ export abstract class Instruction {
61
80
  const args = res.slice(1); // Remove opcode.
62
81
  return new this(...args);
63
82
  }
83
+
84
+ /**
85
+ * Returns the stringified type of the instruction.
86
+ * Instruction sub-classes should have a static `type` property.
87
+ */
88
+ public get type(): string {
89
+ const type = 'type' in this.constructor && (this.constructor.type as string);
90
+ if (!type) {
91
+ throw new Error(`Instruction class ${this.constructor.name} does not have a static 'type' property defined.`);
92
+ }
93
+ return type;
94
+ }
95
+
96
+ /**
97
+ * Returns the opcode of the instruction.
98
+ * Instruction sub-classes should have a static `opcode` property.
99
+ */
100
+ public get opcode(): Opcode {
101
+ const opcode = 'opcode' in this.constructor ? (this.constructor.opcode as Opcode) : undefined;
102
+ if (opcode === undefined || Opcode[opcode] === undefined) {
103
+ throw new Error(`Instruction class ${this.constructor.name} does not have a static 'opcode' property defined.`);
104
+ }
105
+ return opcode;
106
+ }
64
107
  }
@@ -3,7 +3,6 @@ import { FunctionArtifactWithDebugMetadata, decodeReturnValues } from '@aztec/fo
3
3
  import { AztecAddress } from '@aztec/foundation/aztec-address';
4
4
  import { Fr } from '@aztec/foundation/fields';
5
5
  import { createDebugLogger } from '@aztec/foundation/log';
6
- import { to2Fields } from '@aztec/foundation/serialize';
7
6
 
8
7
  import { extractReturnWitness } from '../acvm/deserialize.js';
9
8
  import { Oracle, acvm, extractCallStack } from '../acvm/index.js';
@@ -24,7 +23,7 @@ export async function executePrivateFunction(
24
23
  ): Promise<ExecutionResult> {
25
24
  const functionSelector = functionData.selector;
26
25
  log(`Executing external function ${contractAddress}:${functionSelector}(${artifact.name})`);
27
- const acir = Buffer.from(artifact.bytecode, 'base64');
26
+ const acir = artifact.bytecode;
28
27
  const initialWitness = context.getInitialWitness(artifact);
29
28
  const acvmCallback = new Oracle(context);
30
29
  const { partialWitness } = await acvm(await AcirSimulator.getSolver(), acir, initialWitness, acvmCallback).catch(
@@ -47,9 +46,9 @@ export async function executePrivateFunction(
47
46
  const encryptedLogs = context.getEncryptedLogs();
48
47
  const unencryptedLogs = context.getUnencryptedLogs();
49
48
  // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) --> set this in Noir
50
- publicInputs.encryptedLogsHash = to2Fields(encryptedLogs.hash());
49
+ publicInputs.encryptedLogsHash = Fr.fromBuffer(encryptedLogs.hash());
51
50
  publicInputs.encryptedLogPreimagesLength = new Fr(encryptedLogs.getSerializedLength());
52
- publicInputs.unencryptedLogsHash = to2Fields(unencryptedLogs.hash());
51
+ publicInputs.unencryptedLogsHash = Fr.fromBuffer(unencryptedLogs.hash());
53
52
  publicInputs.unencryptedLogPreimagesLength = new Fr(unencryptedLogs.getSerializedLength());
54
53
 
55
54
  const callStackItem = new PrivateCallStackItem(contractAddress, functionData, publicInputs);
@@ -25,7 +25,7 @@ export async function executeUnconstrainedFunction(
25
25
  const functionSelector = functionData.selector;
26
26
  log(`Executing unconstrained function ${contractAddress}:${functionSelector}`);
27
27
 
28
- const acir = Buffer.from(artifact.bytecode, 'base64');
28
+ const acir = artifact.bytecode;
29
29
  const initialWitness = toACVMWitness(0, args);
30
30
  const { partialWitness } = await acvm(
31
31
  await AcirSimulator.getSolver(),
@@ -225,12 +225,15 @@ export class ViewDataOracle extends TypedOracle {
225
225
  }
226
226
 
227
227
  /**
228
- * Fetches the a message from the db, given its key.
229
- * @param entryKey - A buffer representing the entry key.
230
- * @returns The l1 to l2 message data
228
+ * Fetches a message from the db, given its key.
229
+ * @param contractAddress - Address of a contract by which the message was emitted.
230
+ * @param messageHash - Hash of the message.
231
+ * @param secret - Secret used to compute a nullifier.
232
+ * @dev Contract address and secret are only used to compute the nullifier to get non-nullified messages
233
+ * @returns The l1 to l2 membership witness (index of message in the tree and sibling path).
231
234
  */
232
- public async getL1ToL2MembershipWitness(entryKey: Fr) {
233
- return await this.db.getL1ToL2MembershipWitness(entryKey);
235
+ public async getL1ToL2MembershipWitness(contractAddress: AztecAddress, messageHash: Fr, secret: Fr) {
236
+ return await this.db.getL1ToL2MembershipWitness(contractAddress, messageHash, secret);
234
237
  }
235
238
 
236
239
  /**
package/src/index.ts CHANGED
@@ -2,3 +2,4 @@ export * from './acvm/index.js';
2
2
  export * from './client/index.js';
3
3
  export * from './common/index.js';
4
4
  export * from './public/index.js';
5
+ export * from './simulator/index.js';
package/src/public/db.ts CHANGED
@@ -79,12 +79,18 @@ export interface PublicContractsDB {
79
79
  /** Database interface for providing access to commitment tree, l1 to l2 message tree, and nullifier tree. */
80
80
  export interface CommitmentsDB {
81
81
  /**
82
- * Gets a confirmed L1 to L2 message for the given entry key.
83
- * TODO(Maddiaa): Can be combined with aztec-node method that does the same thing.
84
- * @param entryKey - The entry key.
85
- * @returns - The l1 to l2 message object
82
+ * Fetches a message from the db, given its key.
83
+ * @param contractAddress - Address of a contract by which the message was emitted.
84
+ * @param messageHash - Hash of the message.
85
+ * @param secret - Secret used to compute a nullifier.
86
+ * @dev Contract address and secret are only used to compute the nullifier to get non-nullified messages
87
+ * @returns The l1 to l2 membership witness (index of message in the tree and sibling path).
86
88
  */
87
- getL1ToL2MembershipWitness(entryKey: Fr): Promise<MessageLoadOracleInputs<typeof L1_TO_L2_MSG_TREE_HEIGHT>>;
89
+ getL1ToL2MembershipWitness(
90
+ contractAddress: AztecAddress,
91
+ messageHash: Fr,
92
+ secret: Fr,
93
+ ): Promise<MessageLoadOracleInputs<typeof L1_TO_L2_MSG_TREE_HEIGHT>>;
88
94
 
89
95
  /**
90
96
  * Gets the index of a commitment in the note hash tree.
@@ -229,13 +229,15 @@ export class PublicExecutor {
229
229
  const hostStorage = new HostStorage(this.stateDb, this.contractsDb, this.commitmentsDb);
230
230
  const worldStateJournal = new AvmPersistableStateManager(hostStorage);
231
231
  const executionEnv = temporaryCreateAvmExecutionEnvironment(execution, globalVariables);
232
- const machineState = new AvmMachineState(0, 0, 0);
232
+ // TODO(@spalladino) Load initial gas from the public execution request
233
+ const machineState = new AvmMachineState(100_000, 100_000, 100_000);
233
234
 
234
235
  const context = new AvmContext(worldStateJournal, executionEnv, machineState);
235
236
  const simulator = new AvmSimulator(context);
236
237
 
237
238
  const result = await simulator.execute();
238
239
  const newWorldState = context.persistableState.flush();
240
+ // TODO(@spalladino) Read gas left from machineState and return it
239
241
  return temporaryConvertAvmResults(execution, newWorldState, result);
240
242
  }
241
243