Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(avm): Remove function selector from AvmExecutionEnvironment #10532

Merged
merged 4 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions yarn-project/circuit-types/src/simulation_error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export interface FailingFunction {
/**
* The selector of the function that failed.
*/
functionSelector: FunctionSelector;
functionSelector?: FunctionSelector;
/**
* The name of the function that failed.
*/
Expand Down Expand Up @@ -160,6 +160,7 @@ export class SimulationError extends Error {
this.functionErrorStack.forEach(failingFunction => {
if (
failingFunction.contractAddress.equals(contractAddress) &&
!!failingFunction.functionSelector &&
failingFunction.functionSelector.equals(functionSelector)
) {
failingFunction.functionName = functionName;
Expand All @@ -175,7 +176,7 @@ export class SimulationError extends Error {
const stackLines: string[] = [
...functionCallStack.map(failingFunction => {
return `at ${failingFunction.contractName ?? failingFunction.contractAddress.toString()}.${
failingFunction.functionName ?? failingFunction.functionSelector.toString()
failingFunction.functionName ?? failingFunction.functionSelector?.toString() ?? 'unknown'
}`;
}),
...noirCallStack.map(errorLocation =>
Expand Down
6 changes: 4 additions & 2 deletions yarn-project/pxe/src/pxe_service/error_enriching.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export async function enrichSimulationError(err: SimulationError, db: PxeDatabas
if (!mentionedFunctions.has(contractAddress.toString())) {
mentionedFunctions.set(contractAddress.toString(), new Set());
}
mentionedFunctions.get(contractAddress.toString())!.add(functionSelector.toString());
mentionedFunctions.get(contractAddress.toString())!.add(functionSelector?.toString() ?? '');
});

await Promise.all(
Expand Down Expand Up @@ -89,7 +89,9 @@ export async function enrichPublicSimulationError(
err.setNoirCallStack(parsedCallStack);
} catch (err) {
logger.warn(
`Could not resolve noir call stack for ${originalFailingFunction.contractAddress.toString()}:${originalFailingFunction.functionSelector.toString()}: ${err}`,
`Could not resolve noir call stack for ${originalFailingFunction.contractAddress.toString()}:${
originalFailingFunction.functionName?.toString() ?? ''
}: ${err}`,
);
}
}
Expand Down
5 changes: 2 additions & 3 deletions yarn-project/simulator/src/avm/avm_context.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type AztecAddress, FunctionSelector } from '@aztec/circuits.js';
import { type AztecAddress } from '@aztec/circuits.js';
import { type Fr } from '@aztec/foundation/fields';

import { type AvmExecutionEnvironment } from './avm_execution_environment.js';
Expand Down Expand Up @@ -43,13 +43,12 @@ export class AvmContext {
calldata: Fr[],
allocatedGas: Gas,
callType: 'CALL' | 'STATICCALL',
functionSelector: FunctionSelector = FunctionSelector.empty(),
): AvmContext {
const deriveFn =
callType === 'CALL'
? this.environment.deriveEnvironmentForNestedCall
: this.environment.deriveEnvironmentForNestedStaticCall;
const newExecutionEnvironment = deriveFn.call(this.environment, address, calldata, functionSelector);
const newExecutionEnvironment = deriveFn.call(this.environment, address, calldata);
const forkedWorldState = this.persistableState.fork();
const machineState = AvmMachineState.fromState(gasToGasLeft(allocatedGas));
return new AvmContext(forkedWorldState, newExecutionEnvironment, machineState);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { AztecAddress, FunctionSelector } from '@aztec/circuits.js';
import { AztecAddress } from '@aztec/circuits.js';
import { Fr } from '@aztec/foundation/fields';

import { allSameExcept, initExecutionEnvironment } from './fixtures/index.js';

describe('Execution Environment', () => {
const newAddress = AztecAddress.fromNumber(123456);
const calldata = [new Fr(1n), new Fr(2n), new Fr(3n)];
const selector = FunctionSelector.empty();

it('New call should fork execution environment correctly', () => {
const executionEnvironment = initExecutionEnvironment();
const newExecutionEnvironment = executionEnvironment.deriveEnvironmentForNestedCall(newAddress, calldata, selector);
const newExecutionEnvironment = executionEnvironment.deriveEnvironmentForNestedCall(newAddress, calldata);

expect(newExecutionEnvironment).toEqual(
allSameExcept(executionEnvironment, {
Expand All @@ -21,13 +20,9 @@ describe('Execution Environment', () => {
);
});

it('New static call call should fork execution environment correctly', () => {
it('New static call should fork execution environment correctly', () => {
const executionEnvironment = initExecutionEnvironment();
const newExecutionEnvironment = executionEnvironment.deriveEnvironmentForNestedStaticCall(
newAddress,
calldata,
selector,
);
const newExecutionEnvironment = executionEnvironment.deriveEnvironmentForNestedStaticCall(newAddress, calldata);

expect(newExecutionEnvironment).toEqual(
allSameExcept(executionEnvironment, {
Expand Down
37 changes: 6 additions & 31 deletions yarn-project/simulator/src/avm/avm_execution_environment.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FunctionSelector, type GlobalVariables } from '@aztec/circuits.js';
import { type GlobalVariables } from '@aztec/circuits.js';
import { type AztecAddress } from '@aztec/foundation/aztec-address';
import { Fr } from '@aztec/foundation/fields';

Expand All @@ -10,24 +10,17 @@ export class AvmExecutionEnvironment {
constructor(
public readonly address: AztecAddress,
public readonly sender: AztecAddress,
public readonly functionSelector: FunctionSelector, // may be temporary (#7224)
public readonly contractCallDepth: Fr,
public readonly transactionFee: Fr,
public readonly globals: GlobalVariables,
public readonly isStaticCall: boolean,
public readonly calldata: Fr[],
) {}

private deriveEnvironmentForNestedCallInternal(
targetAddress: AztecAddress,
calldata: Fr[],
functionSelector: FunctionSelector,
isStaticCall: boolean,
) {
private deriveEnvironmentForNestedCallInternal(targetAddress: AztecAddress, calldata: Fr[], isStaticCall: boolean) {
return new AvmExecutionEnvironment(
/*address=*/ targetAddress,
/*sender=*/ this.address,
functionSelector,
this.contractCallDepth.add(Fr.ONE),
this.transactionFee,
this.globals,
Expand All @@ -36,29 +29,11 @@ export class AvmExecutionEnvironment {
);
}

public deriveEnvironmentForNestedCall(
targetAddress: AztecAddress,
calldata: Fr[],
functionSelector: FunctionSelector = FunctionSelector.empty(),
): AvmExecutionEnvironment {
return this.deriveEnvironmentForNestedCallInternal(
targetAddress,
calldata,
functionSelector,
/*isStaticCall=*/ false,
);
public deriveEnvironmentForNestedCall(targetAddress: AztecAddress, calldata: Fr[]): AvmExecutionEnvironment {
return this.deriveEnvironmentForNestedCallInternal(targetAddress, calldata, /*isStaticCall=*/ false);
}

public deriveEnvironmentForNestedStaticCall(
targetAddress: AztecAddress,
calldata: Fr[],
functionSelector: FunctionSelector,
): AvmExecutionEnvironment {
return this.deriveEnvironmentForNestedCallInternal(
targetAddress,
calldata,
functionSelector,
/*isStaticCall=*/ true,
);
public deriveEnvironmentForNestedStaticCall(targetAddress: AztecAddress, calldata: Fr[]): AvmExecutionEnvironment {
return this.deriveEnvironmentForNestedCallInternal(targetAddress, calldata, /*isStaticCall=*/ true);
}
}
3 changes: 0 additions & 3 deletions yarn-project/simulator/src/avm/avm_simulator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ describe('AVM simulator: transpiled Noir contracts', () => {
const ephemeralTrees = await AvmEphemeralForest.create(worldStateDB.getMerkleInterface());
const persistableState = initPersistableStateManager({ worldStateDB, trace, merkleTrees: ephemeralTrees });
const environment = initExecutionEnvironment({
functionSelector,
calldata,
globals,
address: contractInstance.address,
Expand Down Expand Up @@ -434,7 +433,6 @@ describe('AVM simulator: transpiled Noir contracts', () => {
describe('Environment getters', () => {
const address = AztecAddress.random();
const sender = AztecAddress.random();
const functionSelector = FunctionSelector.random();
const transactionFee = Fr.random();
const chainId = Fr.random();
const version = Fr.random();
Expand All @@ -453,7 +451,6 @@ describe('AVM simulator: transpiled Noir contracts', () => {
const env = initExecutionEnvironment({
address,
sender,
functionSelector,
transactionFee,
globals,
});
Expand Down
36 changes: 20 additions & 16 deletions yarn-project/simulator/src/avm/avm_simulator.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import {
type AztecAddress,
Fr,
type FunctionSelector,
type GlobalVariables,
MAX_L2_GAS_PER_ENQUEUED_CALL,
} from '@aztec/circuits.js';
import { type AztecAddress, Fr, type GlobalVariables, MAX_L2_GAS_PER_ENQUEUED_CALL } from '@aztec/circuits.js';
import { type Logger, createLogger } from '@aztec/foundation/log';

import { strict as assert } from 'assert';
Expand Down Expand Up @@ -49,24 +43,35 @@ export class AvmSimulator {
private tallyPrintFunction = () => {};
private tallyInstructionFunction = (_a: number, _b: string, _c: Gas) => {};

// Test Purposes only: Logger will not have the proper function name. Use this constructor for testing purposes
// only. Otherwise, use build() below.
constructor(private context: AvmContext, private instructionSet: InstructionSet = INSTRUCTION_SET()) {
assert(
context.machineState.gasLeft.l2Gas <= MAX_L2_GAS_PER_ENQUEUED_CALL,
`Cannot allocate more than ${MAX_L2_GAS_PER_ENQUEUED_CALL} to the AVM for execution of an enqueued call`,
);
this.log = createLogger(`simulator:avm:core(f:${context.environment.functionSelector.toString()})`);
this.log = createLogger(`simulator:avm(calldata[0]: ${context.environment.calldata[0]})`);
// TODO(palla/log): Should tallies be printed on debug, or only on trace?
if (this.log.isLevelEnabled('debug')) {
this.tallyPrintFunction = this.printOpcodeTallies;
this.tallyInstructionFunction = this.tallyInstruction;
}
}

public static create(
// Factory to have a proper function name in the logger. Retrieving the name is asynchronous and
// cannot be done as part of the constructor.
public static async build(context: AvmContext): Promise<AvmSimulator> {
const simulator = new AvmSimulator(context);
const fnName = await context.persistableState.getPublicFunctionDebugName(context.environment);
simulator.log = createLogger(`simulator:avm(f:${fnName})`);

return simulator;
}

public static async create(
stateManager: AvmPersistableStateManager,
address: AztecAddress,
sender: AztecAddress,
functionSelector: FunctionSelector, // may be temporary (#7224)
transactionFee: Fr,
globals: GlobalVariables,
isStaticCall: boolean,
Expand All @@ -76,7 +81,6 @@ export class AvmSimulator {
const avmExecutionEnv = new AvmExecutionEnvironment(
address,
sender,
functionSelector,
/*contractCallDepth=*/ Fr.zero(),
transactionFee,
globals,
Expand All @@ -86,8 +90,7 @@ export class AvmSimulator {

const avmMachineState = new AvmMachineState(allocatedGas);
const avmContext = new AvmContext(stateManager, avmExecutionEnv, avmMachineState);
const instructionSet = INSTRUCTION_SET();
return new AvmSimulator(avmContext, instructionSet);
return await AvmSimulator.build(avmContext);
}

/**
Expand All @@ -98,11 +101,12 @@ export class AvmSimulator {
if (!bytecode) {
// revert, consuming all gas
const message = `No bytecode found at: ${this.context.environment.address}. Reverting...`;
const fnName = await this.context.persistableState.getPublicFunctionDebugName(this.context.environment);
const revertReason = new AvmRevertReason(
message,
/*failingFunction=*/ {
contractAddress: this.context.environment.address,
functionSelector: this.context.environment.functionSelector,
functionName: fnName,
},
/*noirCallStack=*/ [],
);
Expand Down Expand Up @@ -176,7 +180,7 @@ export class AvmSimulator {

const output = machineState.getOutput();
const reverted = machineState.getReverted();
const revertReason = reverted ? revertReasonFromExplicitRevert(output, this.context) : undefined;
const revertReason = reverted ? await revertReasonFromExplicitRevert(output, this.context) : undefined;
const results = new AvmContractCallResult(reverted, output, machineState.gasLeft, revertReason);
this.log.debug(`Context execution results: ${results.toString()}`);

Expand All @@ -190,7 +194,7 @@ export class AvmSimulator {
throw err;
}

const revertReason = revertReasonFromExceptionalHalt(err, this.context);
const revertReason = await revertReasonFromExceptionalHalt(err, this.context);
// Note: "exceptional halts" cannot return data, hence [].
const results = new AvmContractCallResult(/*reverted=*/ true, /*output=*/ [], machineState.gasLeft, revertReason);
this.log.debug(`Context execution results: ${results.toString()}`);
Expand Down
26 changes: 12 additions & 14 deletions yarn-project/simulator/src/avm/errors.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type FailingFunction, type NoirCallStack } from '@aztec/circuit-types';
import { type AztecAddress, Fr, FunctionSelector, PUBLIC_DISPATCH_SELECTOR } from '@aztec/circuits.js';
import { type AztecAddress, type Fr } from '@aztec/circuits.js';

import { ExecutionError } from '../common/errors.js';
import { type AvmContext } from './avm_context.js';
Expand Down Expand Up @@ -138,16 +138,9 @@ export class AvmRevertReason extends ExecutionError {
}
}

function createRevertReason(message: string, revertData: Fr[], context: AvmContext): AvmRevertReason {
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/8985): Properly fix this.
// If the function selector is the public dispatch selector, we need to extract the actual function selector from the calldata.
// We should remove this because the AVM (or public protocol) shouldn't be aware of the public dispatch calling convention.
let functionSelector = context.environment.functionSelector;
async function createRevertReason(message: string, revertData: Fr[], context: AvmContext): Promise<AvmRevertReason> {
// We drop the returnPc information.
const internalCallStack = context.machineState.internalCallStack.map(entry => entry.callPc);
if (functionSelector.toField().equals(new Fr(PUBLIC_DISPATCH_SELECTOR)) && context.environment.calldata.length > 0) {
functionSelector = FunctionSelector.fromField(context.environment.calldata[0]);
}

// If we are reverting due to the same error that we have been tracking, we use the nested error as the cause.
let nestedError = undefined;
Expand All @@ -160,11 +153,13 @@ function createRevertReason(message: string, revertData: Fr[], context: AvmConte
message = context.machineState.collectedRevertInfo.recursiveRevertReason.message;
}

const fnName = await context.persistableState.getPublicFunctionDebugName(context.environment);

return new AvmRevertReason(
message,
/*failingFunction=*/ {
contractAddress: context.environment.address,
functionSelector: functionSelector,
functionName: fnName,
},
/*noirCallStack=*/ [...internalCallStack, context.machineState.pc].map(pc => `0.${pc}`),
/*options=*/ { cause: nestedError },
Expand All @@ -177,8 +172,11 @@ function createRevertReason(message: string, revertData: Fr[], context: AvmConte
* @param haltingError - the lower-level error causing the exceptional halt
* @param context - the context of the AVM execution used to extract the failingFunction and noirCallStack
*/
export function revertReasonFromExceptionalHalt(haltingError: AvmExecutionError, context: AvmContext): AvmRevertReason {
return createRevertReason(haltingError.message, [], context);
export async function revertReasonFromExceptionalHalt(
haltingError: AvmExecutionError,
context: AvmContext,
): Promise<AvmRevertReason> {
return await createRevertReason(haltingError.message, [], context);
}

/**
Expand All @@ -187,6 +185,6 @@ export function revertReasonFromExceptionalHalt(haltingError: AvmExecutionError,
* @param revertData - output data of the explicit REVERT instruction
* @param context - the context of the AVM execution used to extract the failingFunction and noirCallStack
*/
export function revertReasonFromExplicitRevert(revertData: Fr[], context: AvmContext): AvmRevertReason {
return createRevertReason('Assertion failed: ', revertData, context);
export async function revertReasonFromExplicitRevert(revertData: Fr[], context: AvmContext): Promise<AvmRevertReason> {
return await createRevertReason('Assertion failed: ', revertData, context);
}
1 change: 0 additions & 1 deletion yarn-project/simulator/src/avm/fixtures/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ export function initExecutionEnvironment(overrides?: Partial<AvmExecutionEnviron
return new AvmExecutionEnvironment(
overrides?.address ?? AztecAddress.zero(),
overrides?.sender ?? AztecAddress.zero(),
overrides?.functionSelector ?? FunctionSelector.empty(),
overrides?.contractCallDepth ?? Fr.zero(),
overrides?.transactionFee ?? Fr.zero(),
overrides?.globals ?? GlobalVariables.empty(),
Expand Down
5 changes: 4 additions & 1 deletion yarn-project/simulator/src/avm/journal/journal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,6 @@ export class AvmPersistableStateManager {
const functionName = await getPublicFunctionDebugName(
this.worldStateDB,
nestedEnvironment.address,
nestedEnvironment.functionSelector,
nestedEnvironment.calldata,
);

Expand All @@ -706,6 +705,10 @@ export class AvmPersistableStateManager {
public traceEnqueuedCall(publicCallRequest: PublicCallRequest, calldata: Fr[], reverted: boolean) {
this.trace.traceEnqueuedCall(publicCallRequest, calldata, reverted);
}

public async getPublicFunctionDebugName(avmEnvironment: AvmExecutionEnvironment): Promise<string> {
return await getPublicFunctionDebugName(this.worldStateDB, avmEnvironment.address, avmEnvironment.calldata);
}
}

function contractAddressIsCanonical(contractAddress: AztecAddress): boolean {
Expand Down
Loading
Loading