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: lay plumbing for having simulations throw an error if they cannot be added in a block #7839

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
15 changes: 12 additions & 3 deletions yarn-project/aztec-node/src/aztec-node/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,9 +324,7 @@ export class AztecNodeService implements AztecNode {
const timer = new Timer();
this.log.info(`Received tx ${tx.getTxHash()}`);

const [_, invalidTxs] = await this.txValidator.validateTxs([tx]);
if (invalidTxs.length > 0) {
this.log.warn(`Rejecting tx ${tx.getTxHash()} because of validation errors`);
if (!(await this.isValidTx(tx))) {
this.metrics.receivedTx(timer.ms(), false);
return;
}
Expand Down Expand Up @@ -752,6 +750,17 @@ export class AztecNodeService implements AztecNode {
);
}

public async isValidTx(tx: Tx): Promise<boolean> {
const [_, invalidTxs] = await this.txValidator.validateTxs([tx]);
if (invalidTxs.length > 0) {
this.log.warn(`Rejecting tx ${tx.getTxHash()} because of validation errors`);

return false;
}

return true;
}

public async setConfig(config: Partial<SequencerConfig & ProverConfig>): Promise<void> {
const newConfig = { ...this.config, ...config };
this.sequencer?.updateSequencerConfig(config);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export type SimulateMethodOptions = {
from?: AztecAddress;
/** Gas settings for the simulation. */
gasSettings?: GasSettings;
/** Simulate without checking for the validity of the resulting transaction, e.g. whether it emits any existing nullifiers. */
skipTxValidation?: boolean;
};

/**
Expand Down Expand Up @@ -93,7 +95,7 @@ export class ContractFunctionInteraction extends BaseContractInteraction {
}

const txRequest = await this.create();
const simulatedTx = await this.wallet.simulateTx(txRequest, true, options?.from);
const simulatedTx = await this.wallet.simulateTx(txRequest, true, options?.from, options?.skipTxValidation);

// As account entrypoints are private, for private functions we retrieve the return values from the first nested call
// since we're interested in the first set of values AFTER the account entrypoint
Expand Down
9 changes: 7 additions & 2 deletions yarn-project/aztec.js/src/wallet/base_wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,13 @@ export abstract class BaseWallet implements Wallet {
proveTx(txRequest: TxExecutionRequest, simulatePublic: boolean): Promise<Tx> {
return this.pxe.proveTx(txRequest, simulatePublic, this.scopes);
}
simulateTx(txRequest: TxExecutionRequest, simulatePublic: boolean, msgSender?: AztecAddress): Promise<SimulatedTx> {
return this.pxe.simulateTx(txRequest, simulatePublic, msgSender, this.scopes);
simulateTx(
txRequest: TxExecutionRequest,
simulatePublic: boolean,
msgSender?: AztecAddress,
skipTxValidation?: boolean,
): Promise<SimulatedTx> {
return this.pxe.simulateTx(txRequest, simulatePublic, msgSender, skipTxValidation, this.scopes);
}
sendTx(tx: Tx): Promise<TxHash> {
return this.pxe.sendTx(tx);
Expand Down
8 changes: 8 additions & 0 deletions yarn-project/circuit-types/src/interfaces/aztec-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,14 @@ export interface AztecNode {
**/
simulatePublicCalls(tx: Tx): Promise<PublicSimulationOutput>;

/**
* Returns true if the transaction is valid for inclusion at the current state. Valid transactions can be
* made invalid by *other* transactions if e.g. they emit the same nullifiers, or come become invalid
* due to e.g. the max_block_number property.
* @param tx - The transaction to validate for correctness.
*/
isValidTx(tx: Tx): Promise<boolean>;

/**
* Updates the configuration of this node.
* @param config - Updated configuration to be merged with the current one.
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/circuit-types/src/interfaces/pxe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ export interface PXE {
* @param txRequest - An authenticated tx request ready for simulation
* @param simulatePublic - Whether to simulate the public part of the transaction.
* @param msgSender - (Optional) The message sender to use for the simulation.
* @param skipTxValidation - (Optional) If false, this function throws if the transaction is unable to be included in a block at the current state.
* @param scopes - (Optional) The accounts whose notes we can access in this call. Currently optional and will default to all.
* @returns A simulated transaction object that includes a transaction that is potentially ready
* to be sent to the network for execution, along with public and private return values.
Expand All @@ -190,6 +191,7 @@ export interface PXE {
txRequest: TxExecutionRequest,
simulatePublic: boolean,
msgSender?: AztecAddress,
skipTxValidation?: boolean,
scopes?: AztecAddress[],
): Promise<SimulatedTx>;

Expand Down
7 changes: 7 additions & 0 deletions yarn-project/pxe/src/pxe_service/pxe_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,7 @@ export class PXEService implements PXE {
txRequest: TxExecutionRequest,
simulatePublic: boolean,
msgSender: AztecAddress | undefined = undefined,
skipTxValidation: boolean = true, // TODO(#7956): make the default be false
scopes?: AztecAddress[],
): Promise<SimulatedTx> {
return await this.jobQueue.put(async () => {
Expand All @@ -521,6 +522,12 @@ export class PXEService implements PXE {
simulatedTx.publicOutput = await this.#simulatePublicCalls(simulatedTx.tx);
}

if (!skipTxValidation) {
if (!(await this.node.isValidTx(simulatedTx.tx))) {
throw new Error('The simulated transaction is unable to be added to state and is invalid.');
}
}

// We log only if the msgSender is undefined, as simulating with a different msgSender
// is unlikely to be a real transaction, and likely to be only used to read data.
// Meaning that it will not necessarily have produced a nullifier (and thus have no TxHash)
Expand Down
Loading