@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.
- package/dest/acvm/oracle/oracle.d.ts +1 -1
- package/dest/acvm/oracle/oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/oracle.js +3 -3
- package/dest/acvm/oracle/typed_oracle.d.ts +1 -1
- package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/typed_oracle.js +2 -2
- package/dest/avm/avm_execution_environment.d.ts +7 -0
- package/dest/avm/avm_execution_environment.d.ts.map +1 -1
- package/dest/avm/avm_execution_environment.js +16 -1
- package/dest/avm/avm_gas_cost.d.ts +322 -0
- package/dest/avm/avm_gas_cost.d.ts.map +1 -0
- package/dest/avm/avm_gas_cost.js +118 -0
- package/dest/avm/avm_machine_state.d.ts +12 -1
- package/dest/avm/avm_machine_state.d.ts.map +1 -1
- package/dest/avm/avm_machine_state.js +31 -2
- package/dest/avm/avm_memory_types.d.ts.map +1 -1
- package/dest/avm/avm_memory_types.js +2 -1
- package/dest/avm/avm_simulator.d.ts.map +1 -1
- package/dest/avm/avm_simulator.js +2 -2
- package/dest/avm/errors.d.ts +4 -0
- package/dest/avm/errors.d.ts.map +1 -1
- package/dest/avm/errors.js +8 -1
- package/dest/avm/fixtures/index.d.ts +7 -1
- package/dest/avm/fixtures/index.d.ts.map +1 -1
- package/dest/avm/fixtures/index.js +20 -6
- package/dest/avm/journal/journal.d.ts.map +1 -1
- package/dest/avm/journal/journal.js +7 -2
- package/dest/avm/opcodes/addressing_mode.d.ts +1 -1
- package/dest/avm/opcodes/addressing_mode.d.ts.map +1 -1
- package/dest/avm/opcodes/addressing_mode.js +1 -1
- package/dest/avm/opcodes/arithmetic.d.ts +15 -12
- package/dest/avm/opcodes/arithmetic.d.ts.map +1 -1
- package/dest/avm/opcodes/arithmetic.js +25 -36
- package/dest/avm/opcodes/hashing.d.ts +12 -12
- package/dest/avm/opcodes/hashing.d.ts.map +1 -1
- package/dest/avm/opcodes/hashing.js +24 -23
- package/dest/avm/opcodes/instruction.d.ts +24 -2
- package/dest/avm/opcodes/instruction.d.ts.map +1 -1
- package/dest/avm/opcodes/instruction.js +45 -2
- package/dest/avm/opcodes/memory.d.ts +3 -0
- package/dest/avm/opcodes/memory.d.ts.map +1 -1
- package/dest/avm/opcodes/memory.js +8 -1
- package/dest/avm/temporary_executor_migration.js +3 -3
- package/dest/client/client_execution_context.d.ts +3 -3
- package/dest/client/client_execution_context.d.ts.map +1 -1
- package/dest/client/client_execution_context.js +5 -5
- package/dest/client/execution_result.d.ts +5 -5
- package/dest/client/execution_result.d.ts.map +1 -1
- package/dest/client/private_execution.d.ts.map +1 -1
- package/dest/client/private_execution.js +4 -5
- package/dest/client/unconstrained_execution.js +2 -2
- package/dest/client/view_data_oracle.d.ts +8 -5
- package/dest/client/view_data_oracle.d.ts.map +1 -1
- package/dest/client/view_data_oracle.js +9 -6
- package/dest/index.d.ts +1 -0
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +2 -1
- package/dest/public/db.d.ts +7 -5
- package/dest/public/db.d.ts.map +1 -1
- package/dest/public/execution.d.ts +3 -3
- package/dest/public/execution.d.ts.map +1 -1
- package/dest/public/execution.js +1 -1
- package/dest/public/executor.d.ts.map +1 -1
- package/dest/public/executor.js +6 -4
- package/dest/public/public_execution_context.d.ts +9 -6
- package/dest/public/public_execution_context.d.ts.map +1 -1
- package/dest/public/public_execution_context.js +11 -8
- package/dest/simulator/acvm_native.d.ts +20 -0
- package/dest/simulator/acvm_native.d.ts.map +1 -0
- package/dest/simulator/acvm_native.js +96 -0
- package/dest/simulator/acvm_wasm.d.ts +7 -0
- package/dest/simulator/acvm_wasm.d.ts.map +1 -0
- package/dest/simulator/acvm_wasm.js +23 -0
- package/dest/simulator/index.d.ts +4 -0
- package/dest/simulator/index.d.ts.map +1 -0
- package/dest/simulator/index.js +4 -0
- package/dest/simulator/simulation_provider.d.ts +9 -0
- package/dest/simulator/simulation_provider.d.ts.map +1 -0
- package/dest/simulator/simulation_provider.js +2 -0
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +4 -5
- package/dest/utils.js +2 -2
- package/package.json +6 -5
- package/src/acvm/oracle/oracle.ts +10 -2
- package/src/acvm/oracle/typed_oracle.ts +5 -1
- package/src/avm/avm_execution_environment.ts +17 -1
- package/src/avm/avm_gas_cost.ts +132 -0
- package/src/avm/avm_machine_state.ts +34 -1
- package/src/avm/avm_memory_types.ts +1 -0
- package/src/avm/avm_simulator.ts +1 -2
- package/src/avm/errors.ts +8 -0
- package/src/avm/fixtures/index.ts +21 -5
- package/src/avm/journal/journal.ts +10 -1
- package/src/avm/opcodes/addressing_mode.ts +1 -1
- package/src/avm/opcodes/arithmetic.ts +34 -47
- package/src/avm/opcodes/hashing.ts +27 -20
- package/src/avm/opcodes/instruction.ts +49 -2
- package/src/avm/opcodes/memory.ts +9 -0
- package/src/avm/temporary_executor_migration.ts +2 -2
- package/src/client/client_execution_context.ts +7 -5
- package/src/client/execution_result.ts +5 -5
- package/src/client/private_execution.ts +3 -4
- package/src/client/unconstrained_execution.ts +1 -1
- package/src/client/view_data_oracle.ts +8 -5
- package/src/index.ts +1 -0
- package/src/public/db.ts +11 -5
- package/src/public/execution.ts +3 -3
- package/src/public/executor.ts +5 -3
- package/src/public/public_execution_context.ts +10 -7
- package/src/simulator/acvm_native.ts +112 -0
- package/src/simulator/acvm_wasm.ts +31 -0
- package/src/simulator/index.ts +3 -0
- package/src/simulator/simulation_provider.ts +10 -0
- package/src/test/utils.ts +2 -4
- 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
|
|
package/src/avm/avm_simulator.ts
CHANGED
|
@@ -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.
|
|
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
|
|
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 ??
|
|
93
|
-
l2GasLeft: overrides?.l2GasLeft ??
|
|
94
|
-
daGasLeft: overrides?.daGasLeft ??
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
|
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 =
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
22
|
+
protected gasCost(): GasCost {
|
|
23
|
+
const indirectCount = Addressing.fromWire(this.indirect).modePerOperand.filter(
|
|
24
|
+
mode => mode === AddressingMode.INDIRECT,
|
|
25
|
+
).length;
|
|
31
26
|
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
const b = context.machineState.memory.get(this.bOffset);
|
|
33
|
+
protected abstract compute(a: MemoryValue, b: MemoryValue): MemoryValue;
|
|
34
|
+
}
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
export class Add extends ThreeOperandArithmeticInstruction {
|
|
37
|
+
static readonly type: string = 'ADD';
|
|
38
|
+
static readonly opcode = Opcode.ADD;
|
|
42
39
|
|
|
43
|
-
|
|
40
|
+
protected compute(a: MemoryValue, b: MemoryValue): MemoryValue {
|
|
41
|
+
return a.add(b);
|
|
44
42
|
}
|
|
45
43
|
}
|
|
46
44
|
|
|
47
|
-
export class
|
|
48
|
-
static type: string = '
|
|
49
|
-
static readonly opcode = Opcode.
|
|
45
|
+
export class Sub extends ThreeOperandArithmeticInstruction {
|
|
46
|
+
static readonly type: string = 'SUB';
|
|
47
|
+
static readonly opcode = Opcode.SUB;
|
|
50
48
|
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
protected compute(a: MemoryValue, b: MemoryValue): MemoryValue {
|
|
50
|
+
return a.sub(b);
|
|
53
51
|
}
|
|
52
|
+
}
|
|
54
53
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
58
|
+
protected compute(a: MemoryValue, b: MemoryValue): MemoryValue {
|
|
59
|
+
return a.mul(b);
|
|
63
60
|
}
|
|
64
61
|
}
|
|
65
62
|
|
|
66
|
-
export class Div extends
|
|
63
|
+
export class Div extends ThreeOperandArithmeticInstruction {
|
|
67
64
|
static type: string = 'DIV';
|
|
68
65
|
static readonly opcode = Opcode.DIV;
|
|
69
66
|
|
|
70
|
-
|
|
71
|
-
|
|
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
|
|
27
|
-
private
|
|
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 [
|
|
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(
|
|
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(
|
|
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
|
|
63
|
-
private
|
|
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 [
|
|
72
|
-
[this.
|
|
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(
|
|
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
|
|
108
|
-
private
|
|
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 [
|
|
116
|
-
[this.
|
|
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(
|
|
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
|
|
153
|
-
private
|
|
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 [
|
|
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
|
|
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(
|
|
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
|
-
|
|
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 {
|
|
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 =
|
|
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;
|