@aztec/simulator 0.60.0 → 0.61.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 +2 -0
- package/dest/acvm/oracle/oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/oracle.js +9 -1
- package/dest/acvm/oracle/typed_oracle.d.ts +3 -1
- package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/typed_oracle.js +7 -1
- package/dest/avm/avm_gas.d.ts.map +1 -1
- package/dest/avm/avm_gas.js +6 -3
- package/dest/avm/avm_machine_state.d.ts +2 -0
- package/dest/avm/avm_machine_state.d.ts.map +1 -1
- package/dest/avm/avm_machine_state.js +3 -1
- package/dest/avm/avm_simulator.d.ts +15 -0
- package/dest/avm/avm_simulator.d.ts.map +1 -1
- package/dest/avm/avm_simulator.js +45 -4
- package/dest/avm/fixtures/index.d.ts +1 -1
- package/dest/avm/fixtures/index.d.ts.map +1 -1
- package/dest/avm/fixtures/index.js +7 -7
- package/dest/avm/journal/journal.d.ts +5 -6
- package/dest/avm/journal/journal.d.ts.map +1 -1
- package/dest/avm/journal/journal.js +42 -17
- package/dest/avm/opcodes/contract.d.ts +8 -1
- package/dest/avm/opcodes/contract.d.ts.map +1 -1
- package/dest/avm/opcodes/contract.js +41 -21
- package/dest/avm/opcodes/conversion.d.ts +1 -1
- package/dest/avm/opcodes/conversion.d.ts.map +1 -1
- package/dest/avm/opcodes/conversion.js +12 -9
- package/dest/avm/opcodes/environment_getters.d.ts +1 -1
- package/dest/avm/opcodes/environment_getters.d.ts.map +1 -1
- package/dest/avm/opcodes/environment_getters.js +5 -1
- package/dest/avm/opcodes/external_calls.d.ts +3 -6
- package/dest/avm/opcodes/external_calls.d.ts.map +1 -1
- package/dest/avm/opcodes/external_calls.js +23 -43
- package/dest/avm/opcodes/memory.d.ts +20 -0
- package/dest/avm/opcodes/memory.d.ts.map +1 -1
- package/dest/avm/opcodes/memory.js +59 -3
- package/dest/avm/serialization/bytecode_serialization.d.ts.map +1 -1
- package/dest/avm/serialization/bytecode_serialization.js +5 -3
- package/dest/avm/serialization/instruction_serialization.d.ts +36 -34
- package/dest/avm/serialization/instruction_serialization.d.ts.map +1 -1
- package/dest/avm/serialization/instruction_serialization.js +37 -35
- package/dest/avm/test_utils.d.ts +2 -1
- package/dest/avm/test_utils.d.ts.map +1 -1
- package/dest/avm/test_utils.js +4 -1
- package/dest/client/client_execution_context.d.ts +0 -8
- package/dest/client/client_execution_context.d.ts.map +1 -1
- package/dest/client/client_execution_context.js +1 -18
- package/dest/client/db_oracle.d.ts +17 -1
- package/dest/client/db_oracle.d.ts.map +1 -1
- package/dest/client/db_oracle.js +1 -1
- package/dest/client/view_data_oracle.d.ts +17 -1
- package/dest/client/view_data_oracle.d.ts.map +1 -1
- package/dest/client/view_data_oracle.js +21 -1
- package/dest/common/index.d.ts +0 -1
- package/dest/common/index.d.ts.map +1 -1
- package/dest/common/index.js +1 -2
- package/dest/public/dual_side_effect_trace.d.ts +3 -5
- package/dest/public/dual_side_effect_trace.d.ts.map +1 -1
- package/dest/public/dual_side_effect_trace.js +8 -4
- package/dest/public/enqueued_call_side_effect_trace.d.ts +4 -6
- package/dest/public/enqueued_call_side_effect_trace.d.ts.map +1 -1
- package/dest/public/enqueued_call_side_effect_trace.js +19 -7
- package/dest/public/enqueued_call_simulator.d.ts +2 -2
- package/dest/public/enqueued_call_simulator.d.ts.map +1 -1
- package/dest/public/enqueued_call_simulator.js +21 -29
- package/dest/public/enqueued_calls_processor.d.ts +2 -3
- package/dest/public/enqueued_calls_processor.d.ts.map +1 -1
- package/dest/public/enqueued_calls_processor.js +18 -25
- package/dest/public/public_kernel.d.ts +0 -1
- package/dest/public/public_kernel.d.ts.map +1 -1
- package/dest/public/public_kernel.js +5 -8
- package/dest/public/public_kernel_tail_simulator.d.ts +1 -5
- package/dest/public/public_kernel_tail_simulator.d.ts.map +1 -1
- package/dest/public/public_kernel_tail_simulator.js +6 -12
- package/dest/public/public_processor.js +4 -4
- package/dest/public/side_effect_trace.d.ts +4 -3
- package/dest/public/side_effect_trace.d.ts.map +1 -1
- package/dest/public/side_effect_trace.js +27 -16
- package/dest/public/side_effect_trace_interface.d.ts +3 -3
- package/dest/public/side_effect_trace_interface.d.ts.map +1 -1
- package/package.json +9 -9
- package/src/acvm/oracle/oracle.ts +13 -0
- package/src/acvm/oracle/typed_oracle.ts +9 -0
- package/src/avm/avm_gas.ts +5 -2
- package/src/avm/avm_machine_state.ts +2 -0
- package/src/avm/avm_simulator.ts +69 -6
- package/src/avm/fixtures/index.ts +7 -7
- package/src/avm/journal/journal.ts +62 -19
- package/src/avm/opcodes/contract.ts +45 -21
- package/src/avm/opcodes/conversion.ts +9 -6
- package/src/avm/opcodes/environment_getters.ts +7 -2
- package/src/avm/opcodes/external_calls.ts +21 -45
- package/src/avm/opcodes/memory.ts +69 -2
- package/src/avm/serialization/bytecode_serialization.ts +6 -2
- package/src/avm/serialization/instruction_serialization.ts +3 -1
- package/src/avm/test_utils.ts +5 -1
- package/src/client/client_execution_context.ts +0 -27
- package/src/client/db_oracle.ts +26 -0
- package/src/client/view_data_oracle.ts +31 -1
- package/src/common/index.ts +0 -1
- package/src/public/dual_side_effect_trace.ts +20 -6
- package/src/public/enqueued_call_side_effect_trace.ts +46 -8
- package/src/public/enqueued_call_simulator.ts +42 -26
- package/src/public/enqueued_calls_processor.ts +26 -38
- package/src/public/public_kernel.ts +9 -12
- package/src/public/public_kernel_tail_simulator.ts +6 -15
- package/src/public/public_processor.ts +3 -3
- package/src/public/side_effect_trace.ts +54 -15
- package/src/public/side_effect_trace_interface.ts +9 -4
- package/dest/client/test_utils.d.ts +0 -9
- package/dest/client/test_utils.d.ts.map +0 -1
- package/dest/client/test_utils.js +0 -27
- package/dest/common/side_effect_counter.d.ts +0 -10
- package/dest/common/side_effect_counter.d.ts.map +0 -1
- package/dest/common/side_effect_counter.js +0 -18
- package/dest/rollup/index.d.ts +0 -2
- package/dest/rollup/index.d.ts.map +0 -1
- package/dest/rollup/index.js +0 -2
- package/dest/rollup/rollup.d.ts +0 -101
- package/dest/rollup/rollup.d.ts.map +0 -1
- package/dest/rollup/rollup.js +0 -100
- package/src/client/test_utils.ts +0 -57
- package/src/common/side_effect_counter.ts +0 -17
- package/src/rollup/index.ts +0 -1
- package/src/rollup/rollup.ts +0 -228
package/src/avm/avm_simulator.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { strict as assert } from 'assert';
|
|
|
6
6
|
import { SideEffectLimitReachedError } from '../public/side_effect_errors.js';
|
|
7
7
|
import type { AvmContext } from './avm_context.js';
|
|
8
8
|
import { AvmContractCallResult } from './avm_contract_call_result.js';
|
|
9
|
+
import { type Gas } from './avm_gas.js';
|
|
9
10
|
import { isAvmBytecode } from './bytecode_utils.js';
|
|
10
11
|
import {
|
|
11
12
|
AvmExecutionError,
|
|
@@ -17,9 +18,21 @@ import {
|
|
|
17
18
|
import type { Instruction } from './opcodes/index.js';
|
|
18
19
|
import { decodeFromBytecode } from './serialization/bytecode_serialization.js';
|
|
19
20
|
|
|
21
|
+
type OpcodeTally = {
|
|
22
|
+
count: number;
|
|
23
|
+
gas: Gas;
|
|
24
|
+
};
|
|
25
|
+
type PcTally = {
|
|
26
|
+
opcode: string;
|
|
27
|
+
count: number;
|
|
28
|
+
gas: Gas;
|
|
29
|
+
};
|
|
30
|
+
|
|
20
31
|
export class AvmSimulator {
|
|
21
32
|
private log: DebugLogger;
|
|
22
33
|
private bytecode: Buffer | undefined;
|
|
34
|
+
public opcodeTallies: Map<string, OpcodeTally> = new Map();
|
|
35
|
+
public pcTallies: Map<number, PcTally> = new Map();
|
|
23
36
|
|
|
24
37
|
constructor(private context: AvmContext) {
|
|
25
38
|
assert(
|
|
@@ -33,10 +46,7 @@ export class AvmSimulator {
|
|
|
33
46
|
* Fetch the bytecode and execute it in the current context.
|
|
34
47
|
*/
|
|
35
48
|
public async execute(): Promise<AvmContractCallResult> {
|
|
36
|
-
const bytecode = await this.context.persistableState.getBytecode(
|
|
37
|
-
this.context.environment.address,
|
|
38
|
-
this.context.environment.functionSelector,
|
|
39
|
-
);
|
|
49
|
+
const bytecode = await this.context.persistableState.getBytecode(this.context.environment.address);
|
|
40
50
|
|
|
41
51
|
// This assumes that we will not be able to send messages to accounts without code
|
|
42
52
|
// Pending classes and instances impl details
|
|
@@ -75,6 +85,7 @@ export class AvmSimulator {
|
|
|
75
85
|
try {
|
|
76
86
|
// Execute instruction pointed to by the current program counter
|
|
77
87
|
// continuing until the machine state signifies a halt
|
|
88
|
+
let instrCounter = 0;
|
|
78
89
|
while (!machineState.getHalted()) {
|
|
79
90
|
const instruction = instructions[machineState.pc];
|
|
80
91
|
assert(
|
|
@@ -82,13 +93,26 @@ export class AvmSimulator {
|
|
|
82
93
|
'AVM attempted to execute non-existent instruction. This should never happen (invalid bytecode or AVM simulator bug)!',
|
|
83
94
|
);
|
|
84
95
|
|
|
85
|
-
const
|
|
86
|
-
|
|
96
|
+
const instrStartGas = machineState.gasLeft; // Save gas before executing instruction (for profiling)
|
|
97
|
+
const instrPc = machineState.pc; // Save PC before executing instruction (for profiling)
|
|
98
|
+
|
|
99
|
+
this.log.debug(
|
|
100
|
+
`[PC:${machineState.pc}] [IC:${instrCounter++}] ${instruction.toString()} (gasLeft l2=${
|
|
101
|
+
machineState.l2GasLeft
|
|
102
|
+
} da=${machineState.daGasLeft})`,
|
|
103
|
+
);
|
|
87
104
|
// Execute the instruction.
|
|
88
105
|
// Normal returns and reverts will return normally here.
|
|
89
106
|
// "Exceptional halts" will throw.
|
|
90
107
|
await instruction.execute(this.context);
|
|
91
108
|
|
|
109
|
+
// gas used by this instruction - used for profiling/tallying
|
|
110
|
+
const gasUsed: Gas = {
|
|
111
|
+
l2Gas: instrStartGas.l2Gas - machineState.l2GasLeft,
|
|
112
|
+
daGas: instrStartGas.daGas - machineState.daGasLeft,
|
|
113
|
+
};
|
|
114
|
+
this.tallyInstruction(instrPc, instruction.constructor.name, gasUsed);
|
|
115
|
+
|
|
92
116
|
if (machineState.pc >= instructions.length) {
|
|
93
117
|
this.log.warn('Passed end of program');
|
|
94
118
|
throw new InvalidProgramCounterError(machineState.pc, /*max=*/ instructions.length);
|
|
@@ -100,6 +124,8 @@ export class AvmSimulator {
|
|
|
100
124
|
const revertReason = reverted ? revertReasonFromExplicitRevert(output, this.context) : undefined;
|
|
101
125
|
const results = new AvmContractCallResult(reverted, output, revertReason);
|
|
102
126
|
this.log.debug(`Context execution results: ${results.toString()}`);
|
|
127
|
+
|
|
128
|
+
this.printOpcodeTallies();
|
|
103
129
|
// Return results for processing by calling context
|
|
104
130
|
return results;
|
|
105
131
|
} catch (err: any) {
|
|
@@ -113,8 +139,45 @@ export class AvmSimulator {
|
|
|
113
139
|
// Note: "exceptional halts" cannot return data, hence []
|
|
114
140
|
const results = new AvmContractCallResult(/*reverted=*/ true, /*output=*/ [], revertReason);
|
|
115
141
|
this.log.debug(`Context execution results: ${results.toString()}`);
|
|
142
|
+
|
|
143
|
+
this.printOpcodeTallies();
|
|
116
144
|
// Return results for processing by calling context
|
|
117
145
|
return results;
|
|
118
146
|
}
|
|
119
147
|
}
|
|
148
|
+
|
|
149
|
+
private tallyInstruction(pc: number, opcode: string, gasUsed: Gas) {
|
|
150
|
+
const opcodeTally = this.opcodeTallies.get(opcode) || ({ count: 0, gas: { l2Gas: 0, daGas: 0 } } as OpcodeTally);
|
|
151
|
+
opcodeTally.count++;
|
|
152
|
+
opcodeTally.gas.l2Gas += gasUsed.l2Gas;
|
|
153
|
+
opcodeTally.gas.daGas += gasUsed.daGas;
|
|
154
|
+
this.opcodeTallies.set(opcode, opcodeTally);
|
|
155
|
+
|
|
156
|
+
const pcTally = this.pcTallies.get(pc) || ({ opcode: opcode, count: 0, gas: { l2Gas: 0, daGas: 0 } } as PcTally);
|
|
157
|
+
pcTally.count++;
|
|
158
|
+
pcTally.gas.l2Gas += gasUsed.l2Gas;
|
|
159
|
+
pcTally.gas.daGas += gasUsed.daGas;
|
|
160
|
+
this.pcTallies.set(pc, pcTally);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private printOpcodeTallies() {
|
|
164
|
+
this.log.debug(`Printing tallies per opcode sorted by gas...`);
|
|
165
|
+
// sort descending by L2 gas consumed
|
|
166
|
+
const sortedOpcodes = Array.from(this.opcodeTallies.entries()).sort((a, b) => b[1].gas.l2Gas - a[1].gas.l2Gas);
|
|
167
|
+
for (const [opcode, tally] of sortedOpcodes) {
|
|
168
|
+
// NOTE: don't care to clutter the logs with DA gas for now
|
|
169
|
+
this.log.debug(`${opcode} executed ${tally.count} times consuming a total of ${tally.gas.l2Gas} L2 gas`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
this.log.debug(`Printing tallies per PC sorted by #times each PC was executed...`);
|
|
173
|
+
const sortedPcs = Array.from(this.pcTallies.entries())
|
|
174
|
+
.sort((a, b) => b[1].count - a[1].count)
|
|
175
|
+
.filter((_, i) => i < 20);
|
|
176
|
+
for (const [pc, tally] of sortedPcs) {
|
|
177
|
+
// NOTE: don't care to clutter the logs with DA gas for now
|
|
178
|
+
this.log.debug(
|
|
179
|
+
`PC:${pc} containing opcode ${tally.opcode} executed ${tally.count} times consuming a total of ${tally.gas.l2Gas} L2 gas`,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
120
183
|
}
|
|
@@ -117,6 +117,13 @@ export function randomMemoryFields(length: number): Field[] {
|
|
|
117
117
|
return [...Array(length)].map(_ => new Field(Fr.random()));
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
export function getAvmTestContractFunctionSelector(functionName: string): FunctionSelector {
|
|
121
|
+
const artifact = AvmTestContractArtifact.functions.find(f => f.name === functionName)!;
|
|
122
|
+
assert(!!artifact, `Function ${functionName} not found in AvmTestContractArtifact`);
|
|
123
|
+
const params = artifact.parameters;
|
|
124
|
+
return FunctionSelector.fromNameAndParameters(artifact.name, params);
|
|
125
|
+
}
|
|
126
|
+
|
|
120
127
|
export function getAvmTestContractBytecode(functionName: string): Buffer {
|
|
121
128
|
const artifact = AvmTestContractArtifact.functions.find(f => f.name === functionName)!;
|
|
122
129
|
assert(
|
|
@@ -147,10 +154,3 @@ export function resolveAvmTestContractAssertionMessage(
|
|
|
147
154
|
|
|
148
155
|
return resolveAssertionMessage(revertReason.noirCallStack, debugMetadata);
|
|
149
156
|
}
|
|
150
|
-
|
|
151
|
-
export function getAvmTestContractFunctionSelector(functionName: string): FunctionSelector {
|
|
152
|
-
const artifact = AvmTestContractArtifact.functions.find(f => f.name === functionName)!;
|
|
153
|
-
assert(!!artifact, `Function ${functionName} not found in AvmTestContractArtifact`);
|
|
154
|
-
const params = artifact.parameters;
|
|
155
|
-
return FunctionSelector.fromNameAndParameters(artifact.name, params);
|
|
156
|
-
}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
AztecAddress,
|
|
3
|
+
type Gas,
|
|
4
|
+
SerializableContractInstance,
|
|
5
|
+
computePublicBytecodeCommitment,
|
|
6
|
+
} from '@aztec/circuits.js';
|
|
2
7
|
import { Fr } from '@aztec/foundation/fields';
|
|
3
8
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
4
9
|
|
|
10
|
+
import assert from 'assert';
|
|
11
|
+
|
|
5
12
|
import { getPublicFunctionDebugName } from '../../common/debug_fn_name.js';
|
|
6
13
|
import { type WorldStateDB } from '../../public/public_db_sources.js';
|
|
7
|
-
import { type TracedContractInstance } from '../../public/side_effect_trace.js';
|
|
8
14
|
import { type PublicSideEffectTraceInterface } from '../../public/side_effect_trace_interface.js';
|
|
9
15
|
import { type AvmContractCallResult } from '../avm_contract_call_result.js';
|
|
10
16
|
import { type AvmExecutionEnvironment } from '../avm_execution_environment.js';
|
|
@@ -210,22 +216,26 @@ export class AvmPersistableStateManager {
|
|
|
210
216
|
/**
|
|
211
217
|
* Get a contract instance.
|
|
212
218
|
* @param contractAddress - address of the contract instance to retrieve.
|
|
213
|
-
* @returns the contract instance
|
|
219
|
+
* @returns the contract instance or undefined if it does not exist.
|
|
214
220
|
*/
|
|
215
|
-
public async getContractInstance(contractAddress: Fr): Promise<
|
|
216
|
-
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
221
|
+
public async getContractInstance(contractAddress: Fr): Promise<SerializableContractInstance | undefined> {
|
|
222
|
+
this.log.debug(`Getting contract instance for address ${contractAddress}`);
|
|
223
|
+
const instanceWithAddress = await this.worldStateDB.getContractInstance(AztecAddress.fromField(contractAddress));
|
|
224
|
+
const exists = instanceWithAddress !== undefined;
|
|
225
|
+
|
|
226
|
+
if (exists) {
|
|
227
|
+
const instance = new SerializableContractInstance(instanceWithAddress);
|
|
228
|
+
this.log.debug(
|
|
229
|
+
`Got contract instance (address=${contractAddress}): exists=${exists}, instance=${JSON.stringify(instance)}`,
|
|
230
|
+
);
|
|
231
|
+
this.trace.traceGetContractInstance(contractAddress, exists, instance);
|
|
232
|
+
|
|
233
|
+
return Promise.resolve(instance);
|
|
234
|
+
} else {
|
|
235
|
+
this.log.debug(`Contract instance NOT FOUND (address=${contractAddress})`);
|
|
236
|
+
this.trace.traceGetContractInstance(contractAddress, exists);
|
|
237
|
+
return Promise.resolve(undefined);
|
|
222
238
|
}
|
|
223
|
-
this.log.debug(
|
|
224
|
-
`Get Contract instance (address=${contractAddress}): exists=${exists}, instance=${JSON.stringify(instance)}`,
|
|
225
|
-
);
|
|
226
|
-
const tracedInstance = { ...instance, exists };
|
|
227
|
-
this.trace.traceGetContractInstance(tracedInstance);
|
|
228
|
-
return Promise.resolve(tracedInstance);
|
|
229
239
|
}
|
|
230
240
|
|
|
231
241
|
/**
|
|
@@ -237,10 +247,43 @@ export class AvmPersistableStateManager {
|
|
|
237
247
|
}
|
|
238
248
|
|
|
239
249
|
/**
|
|
240
|
-
* Get a contract's bytecode from the contracts DB
|
|
250
|
+
* Get a contract's bytecode from the contracts DB, also trace the contract class and instance
|
|
241
251
|
*/
|
|
242
|
-
public async getBytecode(contractAddress: AztecAddress
|
|
243
|
-
|
|
252
|
+
public async getBytecode(contractAddress: AztecAddress): Promise<Buffer | undefined> {
|
|
253
|
+
this.log.debug(`Getting bytecode for contract address ${contractAddress}`);
|
|
254
|
+
const instanceWithAddress = await this.worldStateDB.getContractInstance(contractAddress);
|
|
255
|
+
const exists = instanceWithAddress !== undefined;
|
|
256
|
+
|
|
257
|
+
if (exists) {
|
|
258
|
+
const instance = new SerializableContractInstance(instanceWithAddress);
|
|
259
|
+
|
|
260
|
+
const contractClass = await this.worldStateDB.getContractClass(instance.contractClassId);
|
|
261
|
+
assert(
|
|
262
|
+
contractClass,
|
|
263
|
+
`Contract class not found in DB, but a contract instance was found with this class ID (${instance.contractClassId}). This should not happen!`,
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
const contractClassPreimage = {
|
|
267
|
+
artifactHash: contractClass.artifactHash,
|
|
268
|
+
privateFunctionsRoot: contractClass.privateFunctionsRoot,
|
|
269
|
+
publicBytecodeCommitment: computePublicBytecodeCommitment(contractClass.packedBytecode),
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
this.trace.traceGetBytecode(
|
|
273
|
+
contractAddress,
|
|
274
|
+
exists,
|
|
275
|
+
contractClass.packedBytecode,
|
|
276
|
+
instance,
|
|
277
|
+
contractClassPreimage,
|
|
278
|
+
);
|
|
279
|
+
return contractClass.packedBytecode;
|
|
280
|
+
} else {
|
|
281
|
+
// If the contract instance is not found, we assume it has not been deployed.
|
|
282
|
+
// It doesnt matter what the values of the contract instance are in this case, as long as we tag it with exists=false.
|
|
283
|
+
// This will hint to the avm circuit to just perform the non-membership check on the address and disregard the bytecode hash
|
|
284
|
+
this.trace.traceGetBytecode(contractAddress, exists); // bytecode, instance, class undefined
|
|
285
|
+
return undefined;
|
|
286
|
+
}
|
|
244
287
|
}
|
|
245
288
|
|
|
246
289
|
/**
|
|
@@ -1,23 +1,36 @@
|
|
|
1
|
-
import { Fr } from '@aztec/circuits.js';
|
|
2
|
-
|
|
3
1
|
import type { AvmContext } from '../avm_context.js';
|
|
4
|
-
import { Field, TypeTag } from '../avm_memory_types.js';
|
|
2
|
+
import { Field, TypeTag, Uint1 } from '../avm_memory_types.js';
|
|
3
|
+
import { InstructionExecutionError } from '../errors.js';
|
|
5
4
|
import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
|
|
6
5
|
import { Addressing } from './addressing_mode.js';
|
|
7
6
|
import { Instruction } from './instruction.js';
|
|
8
7
|
|
|
8
|
+
export enum ContractInstanceMember {
|
|
9
|
+
DEPLOYER,
|
|
10
|
+
CLASS_ID,
|
|
11
|
+
INIT_HASH,
|
|
12
|
+
}
|
|
13
|
+
|
|
9
14
|
export class GetContractInstance extends Instruction {
|
|
10
15
|
static readonly type: string = 'GETCONTRACTINSTANCE';
|
|
11
16
|
static readonly opcode: Opcode = Opcode.GETCONTRACTINSTANCE;
|
|
12
17
|
// Informs (de)serialization. See Instruction.deserialize.
|
|
13
18
|
static readonly wireFormat: OperandType[] = [
|
|
14
|
-
OperandType.UINT8,
|
|
15
|
-
OperandType.UINT8,
|
|
16
|
-
OperandType.
|
|
17
|
-
OperandType.
|
|
19
|
+
OperandType.UINT8, // opcode
|
|
20
|
+
OperandType.UINT8, // indirect bits
|
|
21
|
+
OperandType.UINT8, // member enum (immediate)
|
|
22
|
+
OperandType.UINT16, // addressOffset
|
|
23
|
+
OperandType.UINT16, // dstOffset
|
|
24
|
+
OperandType.UINT16, // existsOfsset
|
|
18
25
|
];
|
|
19
26
|
|
|
20
|
-
constructor(
|
|
27
|
+
constructor(
|
|
28
|
+
private indirect: number,
|
|
29
|
+
private memberEnum: number,
|
|
30
|
+
private addressOffset: number,
|
|
31
|
+
private dstOffset: number,
|
|
32
|
+
private existsOffset: number,
|
|
33
|
+
) {
|
|
21
34
|
super();
|
|
22
35
|
}
|
|
23
36
|
|
|
@@ -25,27 +38,38 @@ export class GetContractInstance extends Instruction {
|
|
|
25
38
|
const memory = context.machineState.memory.track(this.type);
|
|
26
39
|
context.machineState.consumeGas(this.gasCost());
|
|
27
40
|
|
|
28
|
-
|
|
41
|
+
if (!(this.memberEnum in ContractInstanceMember)) {
|
|
42
|
+
throw new InstructionExecutionError(`Invalid GETCONSTRACTINSTANCE member enum ${this.memberEnum}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const operands = [this.addressOffset, this.dstOffset, this.existsOffset];
|
|
29
46
|
const addressing = Addressing.fromWire(this.indirect, operands.length);
|
|
30
|
-
const [addressOffset, dstOffset] = addressing.resolve(operands, memory);
|
|
47
|
+
const [addressOffset, dstOffset, existsOffset] = addressing.resolve(operands, memory);
|
|
31
48
|
memory.checkTag(TypeTag.FIELD, addressOffset);
|
|
32
49
|
|
|
33
50
|
const address = memory.get(addressOffset).toFr();
|
|
34
51
|
const instance = await context.persistableState.getContractInstance(address);
|
|
52
|
+
const exists = instance !== undefined;
|
|
35
53
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
54
|
+
let memberValue = new Field(0);
|
|
55
|
+
if (exists) {
|
|
56
|
+
switch (this.memberEnum as ContractInstanceMember) {
|
|
57
|
+
case ContractInstanceMember.DEPLOYER:
|
|
58
|
+
memberValue = new Field(instance.deployer.toField());
|
|
59
|
+
break;
|
|
60
|
+
case ContractInstanceMember.CLASS_ID:
|
|
61
|
+
memberValue = new Field(instance.contractClassId.toField());
|
|
62
|
+
break;
|
|
63
|
+
case ContractInstanceMember.INIT_HASH:
|
|
64
|
+
memberValue = new Field(instance.initializationHash);
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
45
68
|
|
|
46
|
-
memory.
|
|
69
|
+
memory.set(existsOffset, new Uint1(exists ? 1 : 0));
|
|
70
|
+
memory.set(dstOffset, memberValue);
|
|
47
71
|
|
|
48
|
-
memory.assert({ reads: 1, writes:
|
|
72
|
+
memory.assert({ reads: 1, writes: 2, addressing });
|
|
49
73
|
context.machineState.incrementPc();
|
|
50
74
|
}
|
|
51
75
|
}
|
|
@@ -5,9 +5,9 @@ import { Opcode, OperandType } from '../serialization/instruction_serialization.
|
|
|
5
5
|
import { Addressing } from './addressing_mode.js';
|
|
6
6
|
import { Instruction } from './instruction.js';
|
|
7
7
|
|
|
8
|
-
export class
|
|
8
|
+
export class ToRadixBE extends Instruction {
|
|
9
9
|
static type: string = 'TORADIXLE';
|
|
10
|
-
static readonly opcode: Opcode = Opcode.
|
|
10
|
+
static readonly opcode: Opcode = Opcode.TORADIXBE;
|
|
11
11
|
|
|
12
12
|
// Informs (de)serialization. See Instruction.deserialize.
|
|
13
13
|
static readonly wireFormat: OperandType[] = [
|
|
@@ -44,15 +44,18 @@ export class ToRadixLE extends Instruction {
|
|
|
44
44
|
|
|
45
45
|
let value: bigint = memory.get(srcOffset).toBigInt();
|
|
46
46
|
const radix: bigint = memory.get(radixOffset).toBigInt();
|
|
47
|
+
if (this.numLimbs < 1) {
|
|
48
|
+
throw new InstructionExecutionError(`ToRadixBE instruction's numLimbs should be > 0 (was ${this.numLimbs})`);
|
|
49
|
+
}
|
|
47
50
|
if (radix > 256) {
|
|
48
|
-
throw new InstructionExecutionError(`
|
|
51
|
+
throw new InstructionExecutionError(`ToRadixBE instruction's radix should be <= 256 (was ${radix})`);
|
|
49
52
|
}
|
|
50
53
|
const radixBN: bigint = BigInt(radix);
|
|
51
|
-
const limbArray =
|
|
54
|
+
const limbArray = new Array(this.numLimbs);
|
|
52
55
|
|
|
53
|
-
for (let i =
|
|
56
|
+
for (let i = this.numLimbs - 1; i >= 0; i--) {
|
|
54
57
|
const limb = value % radixBN;
|
|
55
|
-
limbArray
|
|
58
|
+
limbArray[i] = limb;
|
|
56
59
|
value /= radixBN;
|
|
57
60
|
}
|
|
58
61
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AvmContext } from '../avm_context.js';
|
|
2
2
|
import { Field, Uint32, Uint64 } from '../avm_memory_types.js';
|
|
3
|
+
import { InstructionExecutionError } from '../errors.js';
|
|
3
4
|
import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
|
|
4
5
|
import { Addressing } from './addressing_mode.js';
|
|
5
6
|
import { Instruction } from './instruction.js';
|
|
@@ -63,7 +64,7 @@ export class GetEnvVar extends Instruction {
|
|
|
63
64
|
OperandType.UINT16, // dstOffset
|
|
64
65
|
];
|
|
65
66
|
|
|
66
|
-
constructor(private indirect: number, private varEnum:
|
|
67
|
+
constructor(private indirect: number, private varEnum: number, private dstOffset: number) {
|
|
67
68
|
super();
|
|
68
69
|
}
|
|
69
70
|
|
|
@@ -71,11 +72,15 @@ export class GetEnvVar extends Instruction {
|
|
|
71
72
|
const memory = context.machineState.memory.track(this.type);
|
|
72
73
|
context.machineState.consumeGas(this.gasCost());
|
|
73
74
|
|
|
75
|
+
if (!(this.varEnum in EnvironmentVariable)) {
|
|
76
|
+
throw new InstructionExecutionError(`Invalid GETENVVAR var enum ${this.varEnum}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
74
79
|
const operands = [this.dstOffset];
|
|
75
80
|
const addressing = Addressing.fromWire(this.indirect, operands.length);
|
|
76
81
|
const [dstOffset] = addressing.resolve(operands, memory);
|
|
77
82
|
|
|
78
|
-
memory.set(dstOffset, getValue(this.varEnum, context));
|
|
83
|
+
memory.set(dstOffset, getValue(this.varEnum as EnvironmentVariable, context));
|
|
79
84
|
|
|
80
85
|
memory.assert({ writes: 1, addressing });
|
|
81
86
|
context.machineState.incrementPc();
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { FunctionSelector, Gas } from '@aztec/circuits.js';
|
|
2
|
-
import { padArrayEnd } from '@aztec/foundation/collection';
|
|
1
|
+
import { Fr, FunctionSelector, Gas, PUBLIC_DISPATCH_SELECTOR } from '@aztec/circuits.js';
|
|
3
2
|
|
|
4
3
|
import type { AvmContext } from '../avm_context.js';
|
|
5
4
|
import { type AvmContractCallResult } from '../avm_contract_call_result.js';
|
|
6
5
|
import { gasLeftToGas } from '../avm_gas.js';
|
|
7
|
-
import { Field, TypeTag,
|
|
6
|
+
import { type Field, TypeTag, Uint1 } from '../avm_memory_types.js';
|
|
8
7
|
import { AvmSimulator } from '../avm_simulator.js';
|
|
9
8
|
import { RethrownError } from '../errors.js';
|
|
10
9
|
import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
|
|
@@ -21,58 +20,39 @@ abstract class ExternalCall extends Instruction {
|
|
|
21
20
|
OperandType.UINT16,
|
|
22
21
|
OperandType.UINT16,
|
|
23
22
|
OperandType.UINT16,
|
|
24
|
-
OperandType.UINT16,
|
|
25
|
-
OperandType.UINT16,
|
|
26
|
-
OperandType.UINT16,
|
|
27
23
|
];
|
|
28
24
|
|
|
29
25
|
constructor(
|
|
30
26
|
private indirect: number,
|
|
31
|
-
private gasOffset: number
|
|
27
|
+
private gasOffset: number,
|
|
32
28
|
private addrOffset: number,
|
|
33
29
|
private argsOffset: number,
|
|
34
30
|
private argsSizeOffset: number,
|
|
35
|
-
private retOffset: number,
|
|
36
|
-
private retSize: number,
|
|
37
31
|
private successOffset: number,
|
|
38
|
-
// NOTE: Function selector is likely temporary since eventually public contract bytecode will be one
|
|
39
|
-
// blob containing all functions, and function selector will become an application-level mechanism
|
|
40
|
-
// (e.g. first few bytes of calldata + compiler-generated jump table)
|
|
41
|
-
private functionSelectorOffset: number,
|
|
42
32
|
) {
|
|
43
33
|
super();
|
|
44
34
|
}
|
|
45
35
|
|
|
46
36
|
public async execute(context: AvmContext) {
|
|
47
37
|
const memory = context.machineState.memory.track(this.type);
|
|
48
|
-
const operands = [
|
|
49
|
-
this.gasOffset,
|
|
50
|
-
this.addrOffset,
|
|
51
|
-
this.argsOffset,
|
|
52
|
-
this.argsSizeOffset,
|
|
53
|
-
this.retOffset,
|
|
54
|
-
this.successOffset,
|
|
55
|
-
this.functionSelectorOffset,
|
|
56
|
-
];
|
|
38
|
+
const operands = [this.gasOffset, this.addrOffset, this.argsOffset, this.argsSizeOffset, this.successOffset];
|
|
57
39
|
const addressing = Addressing.fromWire(this.indirect, operands.length);
|
|
58
|
-
const [gasOffset, addrOffset, argsOffset, argsSizeOffset,
|
|
59
|
-
addressing.resolve(operands, memory);
|
|
40
|
+
const [gasOffset, addrOffset, argsOffset, argsSizeOffset, successOffset] = addressing.resolve(operands, memory);
|
|
60
41
|
memory.checkTags(TypeTag.FIELD, gasOffset, gasOffset + 1);
|
|
61
42
|
memory.checkTag(TypeTag.FIELD, addrOffset);
|
|
62
43
|
memory.checkTag(TypeTag.UINT32, argsSizeOffset);
|
|
63
|
-
memory.checkTag(TypeTag.FIELD, functionSelectorOffset);
|
|
64
44
|
|
|
65
45
|
const calldataSize = memory.get(argsSizeOffset).toNumber();
|
|
66
46
|
memory.checkTagsRange(TypeTag.FIELD, argsOffset, calldataSize);
|
|
67
47
|
|
|
68
48
|
const callAddress = memory.getAs<Field>(addrOffset);
|
|
69
49
|
const calldata = memory.getSlice(argsOffset, calldataSize).map(f => f.toFr());
|
|
70
|
-
const functionSelector =
|
|
50
|
+
const functionSelector = new Fr(PUBLIC_DISPATCH_SELECTOR);
|
|
71
51
|
// If we are already in a static call, we propagate the environment.
|
|
72
52
|
const callType = context.environment.isStaticCall ? 'STATICCALL' : this.type;
|
|
73
53
|
|
|
74
54
|
// First we consume the gas for this operation.
|
|
75
|
-
context.machineState.consumeGas(this.gasCost(calldataSize
|
|
55
|
+
context.machineState.consumeGas(this.gasCost(calldataSize));
|
|
76
56
|
// Then we consume the gas allocated for the nested call. The excess will be refunded later.
|
|
77
57
|
// Gas allocation is capped by the amount of gas left in the current context.
|
|
78
58
|
// We have to do some dancing here because the gas allocation is a field,
|
|
@@ -106,18 +86,12 @@ abstract class ExternalCall extends Instruction {
|
|
|
106
86
|
throw new RethrownError(nestedCallResults.revertReason.message, nestedCallResults.revertReason);
|
|
107
87
|
}
|
|
108
88
|
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const convertedReturnData = padArrayEnd(
|
|
113
|
-
returnData.map(f => new Field(f)),
|
|
114
|
-
new Field(0),
|
|
115
|
-
this.retSize,
|
|
116
|
-
);
|
|
89
|
+
// Save return/revert data for later.
|
|
90
|
+
const fullReturnData = nestedCallResults.output;
|
|
91
|
+
context.machineState.nestedReturndata = fullReturnData;
|
|
117
92
|
|
|
118
|
-
// Write our
|
|
119
|
-
memory.set(successOffset, new
|
|
120
|
-
memory.setSlice(retOffset, convertedReturnData);
|
|
93
|
+
// Write our success flag into memory.
|
|
94
|
+
memory.set(successOffset, new Uint1(success ? 1 : 0));
|
|
121
95
|
|
|
122
96
|
// Refund unused gas
|
|
123
97
|
context.machineState.refundGas(gasLeftToGas(nestedContext.machineState));
|
|
@@ -132,7 +106,7 @@ abstract class ExternalCall extends Instruction {
|
|
|
132
106
|
/*avmCallResults=*/ nestedCallResults,
|
|
133
107
|
);
|
|
134
108
|
|
|
135
|
-
memory.assert({ reads: calldataSize +
|
|
109
|
+
memory.assert({ reads: calldataSize + 4, writes: 1, addressing });
|
|
136
110
|
context.machineState.incrementPc();
|
|
137
111
|
}
|
|
138
112
|
|
|
@@ -204,22 +178,24 @@ export class Revert extends Instruction {
|
|
|
204
178
|
OperandType.UINT16,
|
|
205
179
|
];
|
|
206
180
|
|
|
207
|
-
constructor(private indirect: number, private returnOffset: number, private
|
|
181
|
+
constructor(private indirect: number, private returnOffset: number, private retSizeOffset: number) {
|
|
208
182
|
super();
|
|
209
183
|
}
|
|
210
184
|
|
|
211
185
|
public async execute(context: AvmContext): Promise<void> {
|
|
212
186
|
const memory = context.machineState.memory.track(this.type);
|
|
213
|
-
context.machineState.consumeGas(this.gasCost(this.retSize));
|
|
214
187
|
|
|
215
|
-
const operands = [this.returnOffset];
|
|
188
|
+
const operands = [this.returnOffset, this.retSizeOffset];
|
|
216
189
|
const addressing = Addressing.fromWire(this.indirect, operands.length);
|
|
217
|
-
const [returnOffset] = addressing.resolve(operands, memory);
|
|
190
|
+
const [returnOffset, retSizeOffset] = addressing.resolve(operands, memory);
|
|
218
191
|
|
|
219
|
-
|
|
192
|
+
memory.checkTag(TypeTag.UINT32, retSizeOffset);
|
|
193
|
+
const retSize = memory.get(retSizeOffset).toNumber();
|
|
194
|
+
context.machineState.consumeGas(this.gasCost(retSize));
|
|
195
|
+
const output = memory.getSlice(returnOffset, retSize).map(word => word.toFr());
|
|
220
196
|
|
|
221
197
|
context.machineState.revert(output);
|
|
222
|
-
memory.assert({ reads:
|
|
198
|
+
memory.assert({ reads: retSize + 1, addressing });
|
|
223
199
|
}
|
|
224
200
|
}
|
|
225
201
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AvmContext } from '../avm_context.js';
|
|
2
|
-
import { Field, TaggedMemory } from '../avm_memory_types.js';
|
|
2
|
+
import { Field, TaggedMemory, TypeTag, Uint32 } from '../avm_memory_types.js';
|
|
3
3
|
import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
|
|
4
4
|
import { Addressing } from './addressing_mode.js';
|
|
5
5
|
import { Instruction } from './instruction.js';
|
|
@@ -179,11 +179,11 @@ export class CalldataCopy extends Instruction {
|
|
|
179
179
|
|
|
180
180
|
public async execute(context: AvmContext): Promise<void> {
|
|
181
181
|
const memory = context.machineState.memory.track(this.type);
|
|
182
|
-
// We don't need to check tags here because: (1) the calldata is NOT in memory, and (2) we are the ones writing to destination.
|
|
183
182
|
const operands = [this.cdStartOffset, this.copySizeOffset, this.dstOffset];
|
|
184
183
|
const addressing = Addressing.fromWire(this.indirect, operands.length);
|
|
185
184
|
const [cdStartOffset, copySizeOffset, dstOffset] = addressing.resolve(operands, memory);
|
|
186
185
|
|
|
186
|
+
memory.checkTags(TypeTag.UINT32, cdStartOffset, copySizeOffset);
|
|
187
187
|
const cdStart = memory.get(cdStartOffset).toNumber();
|
|
188
188
|
const copySize = memory.get(copySizeOffset).toNumber();
|
|
189
189
|
context.machineState.consumeGas(this.gasCost(copySize));
|
|
@@ -196,3 +196,70 @@ export class CalldataCopy extends Instruction {
|
|
|
196
196
|
context.machineState.incrementPc();
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
|
+
|
|
200
|
+
export class ReturndataSize extends Instruction {
|
|
201
|
+
static readonly type: string = 'RETURNDATASIZE';
|
|
202
|
+
static readonly opcode: Opcode = Opcode.RETURNDATASIZE;
|
|
203
|
+
// Informs (de)serialization. See Instruction.deserialize.
|
|
204
|
+
static readonly wireFormat: OperandType[] = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT16];
|
|
205
|
+
|
|
206
|
+
constructor(private indirect: number, private dstOffset: number) {
|
|
207
|
+
super();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
public async execute(context: AvmContext): Promise<void> {
|
|
211
|
+
const memory = context.machineState.memory.track(this.type);
|
|
212
|
+
const operands = [this.dstOffset];
|
|
213
|
+
const addressing = Addressing.fromWire(this.indirect, operands.length);
|
|
214
|
+
const [dstOffset] = addressing.resolve(operands, memory);
|
|
215
|
+
context.machineState.consumeGas(this.gasCost());
|
|
216
|
+
|
|
217
|
+
memory.set(dstOffset, new Uint32(context.machineState.nestedReturndata.length));
|
|
218
|
+
|
|
219
|
+
memory.assert({ writes: 1, addressing });
|
|
220
|
+
context.machineState.incrementPc();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export class ReturndataCopy extends Instruction {
|
|
225
|
+
static readonly type: string = 'RETURNDATACOPY';
|
|
226
|
+
static readonly opcode: Opcode = Opcode.RETURNDATACOPY;
|
|
227
|
+
// Informs (de)serialization. See Instruction.deserialize.
|
|
228
|
+
static readonly wireFormat: OperandType[] = [
|
|
229
|
+
OperandType.UINT8,
|
|
230
|
+
OperandType.UINT8,
|
|
231
|
+
OperandType.UINT16,
|
|
232
|
+
OperandType.UINT16,
|
|
233
|
+
OperandType.UINT16,
|
|
234
|
+
];
|
|
235
|
+
|
|
236
|
+
constructor(
|
|
237
|
+
private indirect: number,
|
|
238
|
+
private rdStartOffset: number,
|
|
239
|
+
private copySizeOffset: number,
|
|
240
|
+
private dstOffset: number,
|
|
241
|
+
) {
|
|
242
|
+
super();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
public async execute(context: AvmContext): Promise<void> {
|
|
246
|
+
const memory = context.machineState.memory.track(this.type);
|
|
247
|
+
const operands = [this.rdStartOffset, this.copySizeOffset, this.dstOffset];
|
|
248
|
+
const addressing = Addressing.fromWire(this.indirect, operands.length);
|
|
249
|
+
const [rdStartOffset, copySizeOffset, dstOffset] = addressing.resolve(operands, memory);
|
|
250
|
+
|
|
251
|
+
memory.checkTags(TypeTag.UINT32, rdStartOffset, copySizeOffset);
|
|
252
|
+
const rdStart = memory.get(rdStartOffset).toNumber();
|
|
253
|
+
const copySize = memory.get(copySizeOffset).toNumber();
|
|
254
|
+
context.machineState.consumeGas(this.gasCost(copySize));
|
|
255
|
+
|
|
256
|
+
const transformedData = context.machineState.nestedReturndata
|
|
257
|
+
.slice(rdStart, rdStart + copySize)
|
|
258
|
+
.map(f => new Field(f));
|
|
259
|
+
|
|
260
|
+
memory.setSlice(dstOffset, transformedData);
|
|
261
|
+
|
|
262
|
+
memory.assert({ reads: 2, writes: copySize, addressing });
|
|
263
|
+
context.machineState.incrementPc();
|
|
264
|
+
}
|
|
265
|
+
}
|