Skip to content

Commit

Permalink
fix(avm-simulator): always set revertReason when reverting (#6297)
Browse files Browse the repository at this point in the history
Part of the current setup seems to assume that a simulation reverts if
and only if there's a revertReason. This is why some e2e tests were
failing to see the revert (and throw an exception) when the revert
message was empty.

Example

```ts
/**
 * Makes a processed tx out of source tx.
 * @param tx - Source tx.
 * @param kernelOutput - Output of the kernel circuit simulation for this tx.
 * @param proof - Proof of the kernel circuit for this tx.
 */
export function makeProcessedTx(
  tx: Tx,
  kernelOutput: KernelCircuitPublicInputs,
  proof: Proof,
  publicKernelRequests: PublicKernelRequest[],
  revertReason?: SimulationError,
  gasUsed: ProcessedTx['gasUsed'] = {},
): ProcessedTx {
  return {
    hash: tx.getTxHash(),
    data: kernelOutput,
    proof,
    encryptedLogs: revertReason ? EncryptedTxL2Logs.empty() : tx.encryptedLogs,
    unencryptedLogs: revertReason ? UnencryptedTxL2Logs.empty() : tx.unencryptedLogs,
    isEmpty: false,
    revertReason,
    publicKernelRequests,
    gasUsed,
  };
}
```

cc @just-mitch because I see his name in some parts of the code.
  • Loading branch information
fcarreiro authored May 9, 2024
1 parent 8e111f8 commit cc59981
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 17 deletions.
7 changes: 4 additions & 3 deletions yarn-project/end-to-end/src/e2e_avm_simulator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ describe('e2e_avm_simulator', () => {
});
});

describe('ACVM interoperability', () => {
describe.skip('ACVM interoperability', () => {
let avmContract: AvmAcvmInteropTestContract;

beforeEach(async () => {
Expand All @@ -136,7 +136,7 @@ describe('e2e_avm_simulator', () => {
expect(await avmContract.methods.call_avm_from_acvm().simulate()).toEqual(123456n);
});

it.skip('Can call ACVM function from AVM', async () => {
it('Can call ACVM function from AVM', async () => {
expect(await avmContract.methods.call_acvm_from_avm().simulate()).toEqual(123456n);
});

Expand All @@ -146,7 +146,7 @@ describe('e2e_avm_simulator', () => {
await avmContract.methods.assert_unsiloed_nullifier_acvm(nullifier).send().wait();
});

it.skip('AVM nested call to ACVM sees settled nullifiers', async () => {
it('AVM nested call to ACVM sees settled nullifiers', async () => {
const nullifier = new Fr(123456);
await avmContract.methods.new_nullifier(nullifier).send().wait();
await avmContract.methods
Expand All @@ -155,6 +155,7 @@ describe('e2e_avm_simulator', () => {
.wait();
});

// TODO: Enable (or delete) authwit tests once the AVM is fully functional.
describe.skip('Authwit', () => {
it('Works if authwit provided', async () => {
const recipient = AztecAddress.random();
Expand Down
22 changes: 14 additions & 8 deletions yarn-project/simulator/src/avm/avm_machine_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,20 @@ export class AvmMachineState {
throw new Error('Execution results are not ready! Execution is ongoing.');
}
let revertReason = undefined;
if (this.reverted && this.output.length > 0) {
try {
// We remove the first element which is the 'error selector'.
const revertOutput = this.output.slice(1);
// Try to interpret the output as a text string.
revertReason = new Error('Assertion failed: ' + String.fromCharCode(...revertOutput.map(fr => fr.toNumber())));
} catch (e) {
revertReason = new Error('<no output>');
if (this.reverted) {
if (this.output.length === 0) {
revertReason = new Error('Assertion failed.');
} else {
try {
// We remove the first element which is the 'error selector'.
const revertOutput = this.output.slice(1);
// Try to interpret the output as a text string.
revertReason = new Error(
'Assertion failed: ' + String.fromCharCode(...revertOutput.map(fr => fr.toNumber())),
);
} catch (e) {
revertReason = new Error('Assertion failed: <cannot interpret as string>');
}
}
}
return new AvmContractCallResults(this.reverted, this.output, revertReason);
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/simulator/src/avm/avm_simulator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ describe('AVM simulator: transpiled Noir contracts', () => {
const bytecode = getAvmTestContractBytecode('u128_from_integer_overflow');
const results = await new AvmSimulator(initContext()).executeBytecode(bytecode);
expect(results.reverted).toBe(true);
expect(results.revertReason?.message).toEqual(undefined);
expect(results.revertReason?.message).toEqual('Assertion failed.');
// Note: compiler intrinsic messages (like below) are not known to the AVM
//expect(results.revertReason?.message).toEqual("Assertion failed: call to assert_max_bit_size 'self.__assert_max_bit_size(bit_size)'");
});
Expand Down
10 changes: 9 additions & 1 deletion yarn-project/simulator/src/public/abstract_phase_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,10 +283,18 @@ export abstract class AbstractPhaseManager {
)
: current;

// Sanity check for a current upstream assumption.
// Consumers of the result seem to expect "reverted <=> revertReason !== undefined".
const functionSelector = result.execution.functionData.selector.toString();
if (result.reverted && !result.revertReason) {
throw new Error(
`Simulation of ${result.execution.contractAddress.toString()}:${functionSelector} reverted with no reason.`,
);
}

// Accumulate gas used in this execution
gasUsed = gasUsed.add(Gas.from(result.startGasLeft).sub(Gas.from(result.endGasLeft)));

const functionSelector = result.execution.functionData.selector.toString();
if (result.reverted && !PhaseIsRevertible[this.phase]) {
this.log.debug(
`Simulation error on ${result.execution.contractAddress.toString()}:${functionSelector} with reason: ${
Expand Down
4 changes: 0 additions & 4 deletions yarn-project/simulator/src/public/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,6 @@ async function executePublicFunctionAcvm(
})();

if (reverted) {
if (!revertReason) {
throw new Error('Reverted but no revert reason');
}

return {
execution,
returnValues: [],
Expand Down

0 comments on commit cc59981

Please sign in to comment.