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

fix: on nullifier collision during insertion of revertibles from private, app logic should fail gracefully and proceed to teardown #12422

Open
wants to merge 17 commits into
base: master
Choose a base branch
from

Conversation

dbanks12
Copy link
Collaborator

@dbanks12 dbanks12 commented Mar 3, 2025

No description provided.

Base automatically changed from db/tx-cache to master March 3, 2025 20:39
@dbanks12 dbanks12 requested review from sirasistant and fcarreiro and removed request for sirasistant March 3, 2025 21:28
Comment on lines 38 to 41
await expect(
votingContract.methods.cast_vote(candidate).send({ skipPublicSimulation: true }).wait(),
).rejects.toThrow('Reason: Tx dropped by P2P node.');
).rejects.toThrow(TxStatus.APP_LOGIC_REVERTED);
});
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TxReceipt doesn't have an applogic-revert error message and it doesn't seem trivial to add it unfortunately.

Comment on lines 103 to 118
const revertStart = process.hrtime.bigint();
// FIXME(#12375): TX shouldn't die if revertible insertions fail. Should just revert to snapshot.
await this.insertRevertiblesFromPrivate(context);
// add new contracts to the contracts db so that their functions may be found and called
await this.worldStateDB.addNewRevertibleContracts(tx);
const revertEnd = process.hrtime.bigint();
this.metrics.recordPrivateEffectsInsertion(Number(revertEnd - revertStart) / 1_000, 'revertible');

if (context.hasPhase(TxExecutionPhase.APP_LOGIC)) {
const appLogicResult: ProcessedPhase = await this.simulateAppLogicPhase(context);
processedPhases.push(appLogicResult);
const success = await this.insertRevertiblesFromPrivate(context);
if (success) {
// add new contracts to the contracts db so that their functions may be found and called
await this.worldStateDB.addNewRevertibleContracts(tx);
const revertEnd = process.hrtime.bigint();
this.metrics.recordPrivateEffectsInsertion(Number(revertEnd - revertStart) / 1_000, 'revertible');

// Only proceed with app logic if there was no revert during revertible insertion
if (context.hasPhase(TxExecutionPhase.APP_LOGIC)) {
const appLogicResult: ProcessedPhase = await this.simulateAppLogicPhase(context);
processedPhases.push(appLogicResult);
}
} else {
this.log.debug(`Revertible insertions failed. Skipping app logic.`);
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only proceed to app logic if inserting revertibles succeeds.

Comment on lines -408 to 433
public async insertRevertiblesFromPrivate(context: PublicTxContext) {
public async insertRevertiblesFromPrivate(context: PublicTxContext): /*success=*/ Promise<boolean> {
// Fork the state manager so we can rollback to end of setup if app logic reverts.
await context.state.fork();
const stateManager = context.state.getActiveStateManager();
try {
await stateManager.writeSiloedNullifiersFromPrivate(context.revertibleAccumulatedDataFromPrivate.nullifiers);
} catch (e) {
if (e instanceof NullifierCollisionError) {
// FIXME(#12375): simulator should be able to recover from this and just revert to snapshot.
throw new NullifierCollisionError(
`Nullifier collision encountered when inserting revertible nullifiers from private. Details:\n${e.message}\n.Stack:${e.stack}`,
// Instead of throwing, revert the app_logic phase
context.revert(
TxExecutionPhase.APP_LOGIC,
new SimulationError(
`Nullifier collision encountered when inserting revertible nullifiers from private\nDetails: ${e.message}\nError stack: ${e.stack}`,
[],
),
/*culprit=*/ 'insertRevertiblesFromPrivate',
);
return /*success=*/ false;
} else {
throw e;
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only rethrow if the error is NOT the known nullifier collision. Otherwise just revert app logic with the error and return success=false.

@dbanks12 dbanks12 changed the title fix: on nullifier collision during insertion of revertibles from private, app logic should fail gracefully and proceed to tairdown fix: on nullifier collision during insertion of revertibles from private, app logic should fail gracefully and proceed to teardown Mar 3, 2025
});

// Mock the writeSiloedNullifiersFromPrivate method to throw a NullifierCollisionError
(AvmPersistableStateManager.prototype as any).writeSiloedNullifiersFromPrivate = jest
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, can't we mock it in a... saner way? 🙃

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😅 I think maybe it shouldn't be mocked. I could craft a real-ish test of this instead.

Issue with mocking this function here is that it isn't an arg to PublicTxSimulator but is crafted inside. (this is probably something that could be solved by better dependency injection?)

Comment on lines +1002 to +1005
expect(txResult.processedPhases.length).toBe(2);
expect(txResult.processedPhases[0].phase).toBe(TxExecutionPhase.SETUP);
expect(txResult.processedPhases[1].phase).toBe(TxExecutionPhase.TEARDOWN);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not expect(txResult.?processedPhases).toBe([TxExecutionPhase.SETUP, TxExecutionPhase.TEARDOWN]) ? (or toequals)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I copied other tests, but I agree this is nicer!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants