From 8195e85886c759de549055daa3fe07e7c1952e26 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Tue, 19 Nov 2024 03:20:18 +0000 Subject: [PATCH] refactor: remove PublicExecutor --- .../token_contract/src/test/refunds.nr | 24 +- .../token_contract/src/test/utils.nr | 10 +- .../aztec-node/src/aztec-node/server.ts | 5 +- .../bb-prover/src/avm_proving.test.ts | 3 +- .../src/public_execution_request.ts | 14 +- .../circuit-types/src/stats/metrics.ts | 6 - yarn-project/circuit-types/src/stats/stats.ts | 2 - yarn-project/circuit-types/src/tx/tx.ts | 16 ++ .../circuits.js/src/structs/call_context.ts | 10 + .../src/avm_integration.test.ts | 3 +- .../prover-client/src/mocks/test_context.ts | 60 +++-- yarn-project/prover-node/src/factory.ts | 4 - .../prover-node/src/prover-node.test.ts | 5 +- yarn-project/prover-node/src/prover-node.ts | 3 +- .../scripts/src/benchmarks/aggregate.ts | 1 - .../scripts/src/benchmarks/markdown.ts | 3 +- .../src/client/sequencer-client.ts | 3 +- .../src/avm/avm_contract_call_result.ts | 41 +++- .../simulator/src/avm/avm_simulator.test.ts | 1 - .../simulator/src/avm/avm_simulator.ts | 44 +++- .../simulator/src/avm/journal/journal.ts | 8 +- .../src/avm/opcodes/external_calls.ts | 1 - .../src/public/dual_side_effect_trace.ts | 17 +- .../public/enqueued_call_side_effect_trace.ts | 29 +-- yarn-project/simulator/src/public/executor.ts | 154 ------------ .../simulator/src/public/executor_metrics.ts | 10 +- yarn-project/simulator/src/public/index.ts | 6 +- .../simulator/src/public/public_processor.ts | 21 +- .../simulator/src/public/public_tx_context.ts | 15 +- .../src/public/public_tx_simulator.test.ts | 147 ++++++++---- .../src/public/public_tx_simulator.ts | 223 ++++++++++++++---- .../src/public/side_effect_trace.test.ts | 6 +- .../simulator/src/public/side_effect_trace.ts | 26 +- .../src/public/side_effect_trace_interface.ts | 12 +- .../src/public/transitional_adapters.ts | 24 +- yarn-project/txe/src/oracle/txe_oracle.ts | 163 +++++++------ .../txe/src/txe_service/txe_service.ts | 10 +- 37 files changed, 620 insertions(+), 510 deletions(-) delete mode 100644 yarn-project/simulator/src/public/executor.ts diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/refunds.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/refunds.nr index d326fe250be..2fa5a00240c 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test/refunds.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/refunds.nr @@ -1,20 +1,30 @@ use crate::{test::utils, Token}; use aztec::oracle::random::random; +use aztec::protocol_types::abis::gas::Gas; +use aztec::protocol_types::abis::gas_fees::GasFees; use dep::authwit::cheatcodes as authwit_cheatcodes; use std::test::OracleMock; #[test] unconstrained fn setup_refund_success() { + // Gas used to compute transaction fee + // TXE oracle uses gas_used = Gas(1,1) when crafting TX + let txe_expected_gas_used = Gas::new(1, 1); + // TXE oracle uses default gas fees + let txe_gas_fees = GasFees::default(); + let expected_tx_fee = txe_expected_gas_used.compute_fee(txe_gas_fees); + + // Fund account with enough to cover tx fee plus some + let funded_amount = 1_000 + expected_tx_fee; + let (env, token_contract_address, owner, recipient, mint_amount) = - utils::setup_and_mint_to_private(true); + utils::setup_and_mint_amount_to_private(true, funded_amount); // Renaming owner and recipient to match naming in Token let user = owner; let fee_payer = recipient; - let funded_amount = 1_000; - // We use the same randomness for both the fee payer, the user and the nonce as we currently don't have // `OracleMock::mock_once()` let fee_payer_randomness = 123; @@ -44,19 +54,19 @@ unconstrained fn setup_refund_success() { env, token_contract_address, fee_payer, - 1, + expected_tx_fee, fee_payer_randomness, ); utils::add_token_note( env, token_contract_address, user, - funded_amount - 1, + funded_amount - expected_tx_fee, user_randomness, ); - utils::check_private_balance(token_contract_address, user, mint_amount - 1); - utils::check_private_balance(token_contract_address, fee_payer, 1) + utils::check_private_balance(token_contract_address, user, mint_amount - expected_tx_fee); + utils::check_private_balance(token_contract_address, fee_payer, expected_tx_fee) } // This test should be reworked when final support for partial notes is in diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr index 9ccc8ed6b7b..838399225bf 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr @@ -56,19 +56,25 @@ pub unconstrained fn setup_and_mint_to_public( (env, token_contract_address, owner, recipient, mint_amount) } -pub unconstrained fn setup_and_mint_to_private( +pub unconstrained fn setup_and_mint_amount_to_private( with_account_contracts: bool, + mint_amount: Field, ) -> (&mut TestEnvironment, AztecAddress, AztecAddress, AztecAddress, Field) { // Setup the tokens and mint public balance let (env, token_contract_address, owner, recipient) = setup(with_account_contracts); // Mint some tokens - let mint_amount = 10000; mint_to_private(env, token_contract_address, owner, mint_amount); (env, token_contract_address, owner, recipient, mint_amount) } +pub unconstrained fn setup_and_mint_to_private( + with_account_contracts: bool, +) -> (&mut TestEnvironment, AztecAddress, AztecAddress, AztecAddress, Field) { + setup_and_mint_amount_to_private(with_account_contracts, 10000) +} + pub unconstrained fn mint_to_private( env: &mut TestEnvironment, token_contract_address: AztecAddress, diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index f3cc8bda08f..c71e811edc0 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -71,7 +71,7 @@ import { } from '@aztec/p2p'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; import { GlobalVariableBuilder, SequencerClient } from '@aztec/sequencer-client'; -import { PublicProcessorFactory, createSimulationProvider } from '@aztec/simulator'; +import { PublicProcessorFactory } from '@aztec/simulator'; import { type TelemetryClient } from '@aztec/telemetry-client'; import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; import { createValidatorClient } from '@aztec/validator-client'; @@ -160,8 +160,6 @@ export class AztecNodeService implements AztecNode { // start both and wait for them to sync from the block source await Promise.all([p2pClient.start(), worldStateSynchronizer.start()]); - const simulationProvider = await createSimulationProvider(config, log); - const validatorClient = createValidatorClient(config, p2pClient, telemetry); // now create the sequencer @@ -175,7 +173,6 @@ export class AztecNodeService implements AztecNode { archiver, archiver, archiver, - simulationProvider, telemetry, ); diff --git a/yarn-project/bb-prover/src/avm_proving.test.ts b/yarn-project/bb-prover/src/avm_proving.test.ts index e3d7607fc82..6923c705762 100644 --- a/yarn-project/bb-prover/src/avm_proving.test.ts +++ b/yarn-project/bb-prover/src/avm_proving.test.ts @@ -147,9 +147,8 @@ const proveAndVerifyAvmTestContract = async ( const pxResult = trace.toPublicFunctionCallResult( environment, startGas, - /*endGasLeft=*/ Gas.from(context.machineState.gasLeft), /*bytecode=*/ simulator.getBytecode()!, - avmResult, + avmResult.finalize(), functionName, ); diff --git a/yarn-project/circuit-types/src/public_execution_request.ts b/yarn-project/circuit-types/src/public_execution_request.ts index df60e8e8213..7cf834cb1c8 100644 --- a/yarn-project/circuit-types/src/public_execution_request.ts +++ b/yarn-project/circuit-types/src/public_execution_request.ts @@ -1,4 +1,4 @@ -import { CallContext, type PublicCallRequest, Vector } from '@aztec/circuits.js'; +import { CallContext, PublicCallRequest, Vector } from '@aztec/circuits.js'; import { computeVarArgsHash } from '@aztec/circuits.js/hash'; import { Fr } from '@aztec/foundation/fields'; import { schemas } from '@aztec/foundation/schemas'; @@ -82,9 +82,19 @@ export class PublicExecutionRequest { ); } + toCallRequest(): PublicCallRequest { + return new PublicCallRequest( + this.callContext.msgSender, + this.callContext.contractAddress, + this.callContext.functionSelector, + this.callContext.isStaticCall, + computeVarArgsHash(this.args), + ); + } + [inspect.custom]() { return `PublicExecutionRequest { - callContext: ${this.callContext} + callContext: ${inspect(this.callContext)} args: ${this.args} }`; } diff --git a/yarn-project/circuit-types/src/stats/metrics.ts b/yarn-project/circuit-types/src/stats/metrics.ts index 70d41f4a999..c1af812a2b9 100644 --- a/yarn-project/circuit-types/src/stats/metrics.ts +++ b/yarn-project/circuit-types/src/stats/metrics.ts @@ -37,12 +37,6 @@ export const Metrics = [ description: 'Time to simulate an AVM program.', events: ['avm-simulation'], }, - { - name: 'avm_simulation_bytecode_size_in_bytes', - groupBy: 'app-circuit-name', - description: 'Uncompressed bytecode size for an AVM program.', - events: ['avm-simulation'], - }, { name: 'proof_construction_time_sha256_ms', groupBy: 'threads', diff --git a/yarn-project/circuit-types/src/stats/stats.ts b/yarn-project/circuit-types/src/stats/stats.ts index f9f94709845..a200e9a588c 100644 --- a/yarn-project/circuit-types/src/stats/stats.ts +++ b/yarn-project/circuit-types/src/stats/stats.ts @@ -123,8 +123,6 @@ export type AvmSimulationStats = { appCircuitName: string; /** Duration in ms. */ duration: number; - /** Uncompressed bytecode size. */ - bytecodeSize: number; }; /** Stats for witness generation. */ diff --git a/yarn-project/circuit-types/src/tx/tx.ts b/yarn-project/circuit-types/src/tx/tx.ts index ae0c49e5011..e290ad2a46f 100644 --- a/yarn-project/circuit-types/src/tx/tx.ts +++ b/yarn-project/circuit-types/src/tx/tx.ts @@ -119,6 +119,22 @@ export class Tx extends Gossipable { ); } + static newWithTxData( + data: PrivateKernelTailCircuitPublicInputs, + publicTeardownExecutionRequest?: PublicExecutionRequest, + ) { + return new Tx( + data, + ClientIvcProof.empty(), + EncryptedNoteTxL2Logs.empty(), + EncryptedTxL2Logs.empty(), + UnencryptedTxL2Logs.empty(), + ContractClassTxL2Logs.empty(), + [], + publicTeardownExecutionRequest ? publicTeardownExecutionRequest : PublicExecutionRequest.empty(), + ); + } + /** * Serializes the Tx object into a Buffer. * @returns Buffer representation of the Tx object. diff --git a/yarn-project/circuits.js/src/structs/call_context.ts b/yarn-project/circuits.js/src/structs/call_context.ts index f2761bc4d1f..e16c6d1a96c 100644 --- a/yarn-project/circuits.js/src/structs/call_context.ts +++ b/yarn-project/circuits.js/src/structs/call_context.ts @@ -5,6 +5,7 @@ import { schemas } from '@aztec/foundation/schemas'; import { BufferReader, FieldReader, serializeToBuffer, serializeToFields } from '@aztec/foundation/serialize'; import { type FieldsOf } from '@aztec/foundation/types'; +import { inspect } from 'util'; import { z } from 'zod'; import { CALL_CONTEXT_LENGTH } from '../constants.gen.js'; @@ -125,4 +126,13 @@ export class CallContext { callContext.isStaticCall === this.isStaticCall ); } + + [inspect.custom]() { + return `CallContext { + msgSender: ${this.msgSender} + contractAddress: ${this.contractAddress} + functionSelector: ${this.functionSelector} + isStaticCall: ${this.isStaticCall} + }`; + } } diff --git a/yarn-project/ivc-integration/src/avm_integration.test.ts b/yarn-project/ivc-integration/src/avm_integration.test.ts index 909d4c77818..4bbb4e19633 100644 --- a/yarn-project/ivc-integration/src/avm_integration.test.ts +++ b/yarn-project/ivc-integration/src/avm_integration.test.ts @@ -238,9 +238,8 @@ const proveAvmTestContract = async ( const pxResult = trace.toPublicFunctionCallResult( environment, startGas, - /*endGasLeft=*/ Gas.from(context.machineState.gasLeft), /*bytecode=*/ simulator.getBytecode()!, - avmResult, + avmResult.finalize(), functionName, ); diff --git a/yarn-project/prover-client/src/mocks/test_context.ts b/yarn-project/prover-client/src/mocks/test_context.ts index 75360e62178..59eab78431c 100644 --- a/yarn-project/prover-client/src/mocks/test_context.ts +++ b/yarn-project/prover-client/src/mocks/test_context.ts @@ -13,10 +13,8 @@ import { type Fr } from '@aztec/foundation/fields'; import { type DebugLogger } from '@aztec/foundation/log'; import { openTmpStore } from '@aztec/kv-store/utils'; import { - type PublicExecutionResult, - PublicExecutionResultBuilder, - type PublicExecutor, PublicProcessor, + PublicTxSimulator, type SimulationProvider, WASMSimulator, type WorldStateDB, @@ -25,10 +23,12 @@ import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; import { MerkleTrees } from '@aztec/world-state'; import { NativeWorldStateService } from '@aztec/world-state/native'; +import { jest } from '@jest/globals'; import * as fs from 'fs/promises'; import { type MockProxy, mock } from 'jest-mock-extended'; import { TestCircuitProver } from '../../../bb-prover/src/test/test_circuit_prover.js'; +import { AvmFinalizedCallResult } from '../../../simulator/src/avm/avm_contract_call_result.js'; import { type AvmPersistableStateManager } from '../../../simulator/src/avm/journal/journal.js'; import { ProvingOrchestrator } from '../orchestrator/index.js'; import { MemoryProvingQueue } from '../prover-agent/memory-proving-queue.js'; @@ -37,7 +37,7 @@ import { getEnvironmentConfig, getSimulationProvider, makeGlobals } from './fixt export class TestContext { constructor( - public publicExecutor: MockProxy, + public publicTxSimulator: PublicTxSimulator, public worldStateDB: MockProxy, public publicProcessor: PublicProcessor, public simulationProvider: SimulationProvider, @@ -66,7 +66,6 @@ export class TestContext { const directoriesToCleanup: string[] = []; const globalVariables = makeGlobals(blockNumber); - const publicExecutor = mock(); const worldStateDB = mock(); const telemetry = new NoopTelemetryClient(); @@ -84,12 +83,13 @@ export class TestContext { proverDb = await ws.getLatest(); } - const processor = PublicProcessor.create( + const publicTxSimulator = new PublicTxSimulator(publicDb, worldStateDB, telemetry, globalVariables); + const processor = new PublicProcessor( publicDb, - publicExecutor, globalVariables, Header.empty(), worldStateDB, + publicTxSimulator, telemetry, ); @@ -124,7 +124,7 @@ export class TestContext { agent.start(queue); return new this( - publicExecutor, + publicTxSimulator, worldStateDB, processor, simulationProvider, @@ -154,25 +154,24 @@ export class TestContext { ) { const defaultExecutorImplementation = ( _stateManager: AvmPersistableStateManager, - execution: PublicExecutionRequest, - _globalVariables: GlobalVariables, + executionRequest: PublicExecutionRequest, allocatedGas: Gas, - _transactionFee?: Fr, + _transactionFee: Fr, + _fnName: string, ) => { for (const tx of txs) { const allCalls = tx.publicTeardownFunctionCall.isEmpty() ? tx.enqueuedPublicFunctionCalls : [...tx.enqueuedPublicFunctionCalls, tx.publicTeardownFunctionCall]; for (const request of allCalls) { - if (execution.callContext.equals(request.callContext)) { - const result = PublicExecutionResultBuilder.empty().build({ - endGasLeft: allocatedGas, - }); - return Promise.resolve(result); + if (executionRequest.callContext.equals(request.callContext)) { + return Promise.resolve( + new AvmFinalizedCallResult(/*reverted=*/ false, /*output=*/ [], /*gasLeft=*/ allocatedGas), + ); } } } - throw new Error(`Unexpected execution request: ${execution}`); + throw new Error(`Unexpected execution request: ${executionRequest}`); }; return await this.processPublicFunctionsWithMockExecutorImplementation( txs, @@ -183,21 +182,36 @@ export class TestContext { ); } - public async processPublicFunctionsWithMockExecutorImplementation( + private async processPublicFunctionsWithMockExecutorImplementation( txs: Tx[], maxTransactions: number, txHandler?: ProcessedTxHandler, txValidator?: TxValidator, executorMock?: ( stateManager: AvmPersistableStateManager, - execution: PublicExecutionRequest, - globalVariables: GlobalVariables, + executionRequest: PublicExecutionRequest, allocatedGas: Gas, - transactionFee?: Fr, - ) => Promise, + transactionFee: Fr, + fnName: string, + ) => Promise, ) { + // Mock the internal private function. Borrowed from https://stackoverflow.com/a/71033167 + const simulateInternal: jest.SpiedFunction< + ( + stateManager: AvmPersistableStateManager, + executionResult: any, + allocatedGas: Gas, + transactionFee: any, + fnName: any, + ) => Promise + > = jest.spyOn( + this.publicTxSimulator as unknown as { + simulateEnqueuedCallInternal: PublicTxSimulator['simulateEnqueuedCallInternal']; + }, + 'simulateEnqueuedCallInternal', + ); if (executorMock) { - this.publicExecutor.simulate.mockImplementation(executorMock); + simulateInternal.mockImplementation(executorMock); } return await this.publicProcessor.process(txs, maxTransactions, txHandler, txValidator); } diff --git a/yarn-project/prover-node/src/factory.ts b/yarn-project/prover-node/src/factory.ts index 5632ba0428d..d722c4e1e03 100644 --- a/yarn-project/prover-node/src/factory.ts +++ b/yarn-project/prover-node/src/factory.ts @@ -7,7 +7,6 @@ import { type DataStoreConfig } from '@aztec/kv-store/config'; import { RollupAbi } from '@aztec/l1-artifacts'; import { createProverClient } from '@aztec/prover-client'; import { L1Publisher } from '@aztec/sequencer-client'; -import { createSimulationProvider } from '@aztec/simulator'; import { type TelemetryClient } from '@aztec/telemetry-client'; import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; import { createWorldStateSynchronizer } from '@aztec/world-state'; @@ -43,8 +42,6 @@ export async function createProverNode( const worldStateSynchronizer = await createWorldStateSynchronizer(worldStateConfig, archiver, telemetry); await worldStateSynchronizer.start(); - const simulationProvider = await createSimulationProvider(config, log); - const prover = await createProverClient(config, telemetry); // REFACTOR: Move publisher out of sequencer package and into an L1-related package @@ -82,7 +79,6 @@ export async function createProverNode( archiver, worldStateSynchronizer, proverCoordination, - simulationProvider, quoteProvider, quoteSigner, claimsMonitor, diff --git a/yarn-project/prover-node/src/prover-node.test.ts b/yarn-project/prover-node/src/prover-node.test.ts index 3b1f88a1215..c023aff2c10 100644 --- a/yarn-project/prover-node/src/prover-node.test.ts +++ b/yarn-project/prover-node/src/prover-node.test.ts @@ -25,7 +25,7 @@ import { } from '@aztec/p2p'; import { createBootstrapNode, createTestLibP2PService } from '@aztec/p2p/mocks'; import { type L1Publisher } from '@aztec/sequencer-client'; -import { type PublicProcessorFactory, type SimulationProvider } from '@aztec/simulator'; +import { type PublicProcessorFactory } from '@aztec/simulator'; import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; import { jest } from '@jest/globals'; @@ -48,7 +48,6 @@ describe('prover-node', () => { let contractDataSource: MockProxy; let worldState: MockProxy; let coordination: MockProxy | ProverCoordination; - let simulator: MockProxy; let quoteProvider: MockProxy; let quoteSigner: MockProxy; let bondManager: MockProxy; @@ -97,7 +96,6 @@ describe('prover-node', () => { contractDataSource, worldState, coordination, - simulator, quoteProvider, quoteSigner, claimsMonitor, @@ -115,7 +113,6 @@ describe('prover-node', () => { contractDataSource = mock(); worldState = mock(); coordination = mock(); - simulator = mock(); quoteProvider = mock(); quoteSigner = mock(); bondManager = mock(); diff --git a/yarn-project/prover-node/src/prover-node.ts b/yarn-project/prover-node/src/prover-node.ts index 1c0c5f6611f..7f3ea2eb35e 100644 --- a/yarn-project/prover-node/src/prover-node.ts +++ b/yarn-project/prover-node/src/prover-node.ts @@ -18,7 +18,7 @@ import { compact } from '@aztec/foundation/collection'; import { createDebugLogger } from '@aztec/foundation/log'; import { type Maybe } from '@aztec/foundation/types'; import { type L1Publisher } from '@aztec/sequencer-client'; -import { PublicProcessorFactory, type SimulationProvider } from '@aztec/simulator'; +import { PublicProcessorFactory } from '@aztec/simulator'; import { type TelemetryClient } from '@aztec/telemetry-client'; import { type BondManager } from './bond/bond-manager.js'; @@ -56,7 +56,6 @@ export class ProverNode implements ClaimsMonitorHandler, EpochMonitorHandler, Pr private readonly contractDataSource: ContractDataSource, private readonly worldState: WorldStateSynchronizer, private readonly coordination: ProverCoordination & Maybe, - private readonly _simulator: SimulationProvider, private readonly quoteProvider: QuoteProvider, private readonly quoteSigner: QuoteSigner, private readonly claimsMonitor: ClaimsMonitor, diff --git a/yarn-project/scripts/src/benchmarks/aggregate.ts b/yarn-project/scripts/src/benchmarks/aggregate.ts index 5b82bd208ce..6ad9da18c81 100644 --- a/yarn-project/scripts/src/benchmarks/aggregate.ts +++ b/yarn-project/scripts/src/benchmarks/aggregate.ts @@ -160,7 +160,6 @@ function processCircuitProving(entry: CircuitProvingStats, results: BenchmarkCol function processAvmSimulation(entry: AvmSimulationStats, results: BenchmarkCollectedResults) { append(results, 'avm_simulation_time_ms', entry.appCircuitName, entry.duration); - append(results, 'avm_simulation_bytecode_size_in_bytes', entry.appCircuitName, entry.bytecodeSize); } function processDbAccess(entry: PublicDBAccessStats, results: BenchmarkCollectedResults) { diff --git a/yarn-project/scripts/src/benchmarks/markdown.ts b/yarn-project/scripts/src/benchmarks/markdown.ts index de306a4434a..5c7d55390d2 100644 --- a/yarn-project/scripts/src/benchmarks/markdown.ts +++ b/yarn-project/scripts/src/benchmarks/markdown.ts @@ -192,7 +192,6 @@ export function getMarkdown(prNumber: number) { const kernelCircuitMetrics = Metrics.filter(m => m.groupBy === 'protocol-circuit-name').map(m => m.name); const appCircuitMetrics = Metrics.filter(m => m.groupBy === 'app-circuit-name') .filter(m => m.name !== 'avm_simulation_time_ms') - .filter(m => m.name !== 'avm_simulation_bytecode_size_in_bytes') .map(m => m.name); const metricsByClassesRegistered = Metrics.filter(m => m.groupBy === 'classes-registered').map(m => m.name); const metricsByFeePaymentMethod = Metrics.filter(m => m.groupBy === 'fee-payment-method').map(m => m.name); @@ -261,7 +260,7 @@ ${getTableContent( Time to simulate various public functions in the AVM. ${getTableContent( - transpose(pick(benchmark, ['avm_simulation_time_ms', 'avm_simulation_bytecode_size_in_bytes'])), + transpose(pick(benchmark, ['avm_simulation_time_ms'])), transpose(baseBenchmark), '', 'Function', diff --git a/yarn-project/sequencer-client/src/client/sequencer-client.ts b/yarn-project/sequencer-client/src/client/sequencer-client.ts index eae0bd3729d..1cccefe6050 100644 --- a/yarn-project/sequencer-client/src/client/sequencer-client.ts +++ b/yarn-project/sequencer-client/src/client/sequencer-client.ts @@ -2,7 +2,7 @@ import { type L1ToL2MessageSource, type L2BlockSource, type WorldStateSynchroniz import { type ContractDataSource } from '@aztec/circuits.js'; import { type EthAddress } from '@aztec/foundation/eth-address'; import { type P2P } from '@aztec/p2p'; -import { PublicProcessorFactory, type SimulationProvider } from '@aztec/simulator'; +import { PublicProcessorFactory } from '@aztec/simulator'; import { type TelemetryClient } from '@aztec/telemetry-client'; import { type ValidatorClient } from '@aztec/validator-client'; @@ -40,7 +40,6 @@ export class SequencerClient { contractDataSource: ContractDataSource, l2BlockSource: L2BlockSource, l1ToL2MessageSource: L1ToL2MessageSource, - _simulationProvider: SimulationProvider, telemetryClient: TelemetryClient, ) { const publisher = new L1Publisher(config, telemetryClient); diff --git a/yarn-project/simulator/src/avm/avm_contract_call_result.ts b/yarn-project/simulator/src/avm/avm_contract_call_result.ts index 7db56bc44de..bb1df3ff702 100644 --- a/yarn-project/simulator/src/avm/avm_contract_call_result.ts +++ b/yarn-project/simulator/src/avm/avm_contract_call_result.ts @@ -1,15 +1,52 @@ +import { type SimulationError } from '@aztec/circuit-types'; +import { Gas } from '@aztec/circuits.js'; import { type Fr } from '@aztec/foundation/fields'; +import { inspect } from 'util'; + +import { createSimulationError } from '../common/errors.js'; +import { type Gas as AvmGas } from './avm_gas.js'; import { type AvmRevertReason } from './errors.js'; /** * Results of an contract call's execution in the AVM. */ export class AvmContractCallResult { - constructor(public reverted: boolean, public output: Fr[], public revertReason?: AvmRevertReason) {} + constructor( + public reverted: boolean, + public output: Fr[], + public gasLeft: AvmGas, + public revertReason?: AvmRevertReason, + ) {} + + toString(): string { + let resultsStr = `reverted: ${this.reverted}, output: ${this.output}, gasLeft: ${inspect(this.gasLeft)}`; + if (this.revertReason) { + resultsStr += `, revertReason: ${this.revertReason}`; + } + return resultsStr; + } + + finalize(): AvmFinalizedCallResult { + const revertReason = this.revertReason ? createSimulationError(this.revertReason, this.output) : undefined; + return new AvmFinalizedCallResult(this.reverted, this.output, Gas.from(this.gasLeft), revertReason); + } +} + +/** + * Similar to AvmContractCallResult, but with a SimulationError and standard Gas + * which are useful for consumption external to core AVM simulation. + */ +export class AvmFinalizedCallResult { + constructor( + public reverted: boolean, + public output: Fr[], + public gasLeft: Gas, + public revertReason?: SimulationError, + ) {} toString(): string { - let resultsStr = `reverted: ${this.reverted}, output: ${this.output}`; + let resultsStr = `reverted: ${this.reverted}, output: ${this.output}, gasLeft: ${inspect(this.gasLeft)}`; if (this.revertReason) { resultsStr += `, revertReason: ${this.revertReason}`; } diff --git a/yarn-project/simulator/src/avm/avm_simulator.test.ts b/yarn-project/simulator/src/avm/avm_simulator.test.ts index 7291fad8f21..6697b235a65 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.test.ts @@ -905,7 +905,6 @@ describe('AVM simulator: transpiled Noir contracts', () => { //calldata: expect.arrayContaining(environment.calldata), // top-level call forwards args }), /*startGasLeft=*/ expect.anything(), - /*endGasLeft=*/ expect.anything(), /*bytecode=*/ expect.anything(), /*avmCallResults=*/ expect.anything(), // we don't have the NESTED call's results to check /*functionName=*/ expect.anything(), diff --git a/yarn-project/simulator/src/avm/avm_simulator.ts b/yarn-project/simulator/src/avm/avm_simulator.ts index dd97bf194dd..37c5e82feeb 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.ts @@ -1,12 +1,20 @@ -import { MAX_L2_GAS_PER_ENQUEUED_CALL } from '@aztec/circuits.js'; +import { + type AztecAddress, + Fr, + type FunctionSelector, + type GlobalVariables, + MAX_L2_GAS_PER_ENQUEUED_CALL, +} from '@aztec/circuits.js'; import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { strict as assert } from 'assert'; import { SideEffectLimitReachedError } from '../public/side_effect_errors.js'; -import type { AvmContext } from './avm_context.js'; +import { AvmContext } from './avm_context.js'; import { AvmContractCallResult } from './avm_contract_call_result.js'; +import { AvmExecutionEnvironment } from './avm_execution_environment.js'; import { type Gas } from './avm_gas.js'; +import { AvmMachineState } from './avm_machine_state.js'; import { isAvmBytecode } from './bytecode_utils.js'; import { AvmExecutionError, @@ -15,6 +23,7 @@ import { revertReasonFromExceptionalHalt, revertReasonFromExplicitRevert, } from './errors.js'; +import { type AvmPersistableStateManager } from './journal/journal.js'; import { decodeInstructionFromBytecode } from './serialization/bytecode_serialization.js'; type OpcodeTally = { @@ -41,6 +50,33 @@ export class AvmSimulator { this.log = createDebugLogger(`aztec:avm_simulator:core(f:${context.environment.functionSelector.toString()})`); } + public static create( + stateManager: AvmPersistableStateManager, + address: AztecAddress, + sender: AztecAddress, + functionSelector: FunctionSelector, // may be temporary (#7224) + transactionFee: Fr, + globals: GlobalVariables, + isStaticCall: boolean, + calldata: Fr[], + allocatedGas: Gas, + ) { + const avmExecutionEnv = new AvmExecutionEnvironment( + address, + sender, + functionSelector, + /*contractCallDepth=*/ Fr.zero(), + transactionFee, + globals, + isStaticCall, + calldata, + ); + + const avmMachineState = new AvmMachineState(allocatedGas); + const avmContext = new AvmContext(stateManager, avmExecutionEnv, avmMachineState); + return new AvmSimulator(avmContext); + } + /** * Fetch the bytecode and execute it in the current context. */ @@ -119,7 +155,7 @@ export class AvmSimulator { const output = machineState.getOutput(); const reverted = machineState.getReverted(); const revertReason = reverted ? revertReasonFromExplicitRevert(output, this.context) : undefined; - const results = new AvmContractCallResult(reverted, output, revertReason); + const results = new AvmContractCallResult(reverted, output, machineState.gasLeft, revertReason); this.log.debug(`Context execution results: ${results.toString()}`); this.printOpcodeTallies(); @@ -134,7 +170,7 @@ export class AvmSimulator { const revertReason = revertReasonFromExceptionalHalt(err, this.context); // Note: "exceptional halts" cannot return data, hence []. - const results = new AvmContractCallResult(/*reverted=*/ true, /*output=*/ [], revertReason); + const results = new AvmContractCallResult(/*reverted=*/ true, /*output=*/ [], machineState.gasLeft, revertReason); this.log.debug(`Context execution results: ${results.toString()}`); this.printOpcodeTallies(); diff --git a/yarn-project/simulator/src/avm/journal/journal.ts b/yarn-project/simulator/src/avm/journal/journal.ts index 855b52b82a4..637ec02d7fe 100644 --- a/yarn-project/simulator/src/avm/journal/journal.ts +++ b/yarn-project/simulator/src/avm/journal/journal.ts @@ -116,7 +116,11 @@ export class AvmPersistableStateManager { private _merge(forkedState: AvmPersistableStateManager, reverted: boolean) { // sanity check to avoid merging the same forked trace twice - assert(!this.alreadyMergedIntoParent, 'Cannot merge forked state that has already been merged into its parent!'); + assert( + !forkedState.alreadyMergedIntoParent, + 'Cannot merge forked state that has already been merged into its parent!', + ); + forkedState.alreadyMergedIntoParent = true; this.publicStorage.acceptAndMerge(forkedState.publicStorage); this.nullifiers.acceptAndMerge(forkedState.nullifiers); this.trace.merge(forkedState.trace, reverted); @@ -504,7 +508,6 @@ export class AvmPersistableStateManager { forkedState: AvmPersistableStateManager, nestedEnvironment: AvmExecutionEnvironment, startGasLeft: Gas, - endGasLeft: Gas, bytecode: Buffer, avmCallResults: AvmContractCallResult, ) { @@ -521,7 +524,6 @@ export class AvmPersistableStateManager { forkedState.trace, nestedEnvironment, startGasLeft, - endGasLeft, bytecode, avmCallResults, functionName, diff --git a/yarn-project/simulator/src/avm/opcodes/external_calls.ts b/yarn-project/simulator/src/avm/opcodes/external_calls.ts index 318d8c5ffcb..33b75009017 100644 --- a/yarn-project/simulator/src/avm/opcodes/external_calls.ts +++ b/yarn-project/simulator/src/avm/opcodes/external_calls.ts @@ -107,7 +107,6 @@ abstract class ExternalCall extends Instruction { /*nestedState=*/ nestedContext.persistableState, /*nestedEnvironment=*/ nestedContext.environment, /*startGasLeft=*/ Gas.from(allocatedGas), - /*endGasLeft=*/ Gas.from(nestedContext.machineState.gasLeft), /*bytecode=*/ simulator.getBytecode()!, /*avmCallResults=*/ nestedCallResults, ); diff --git a/yarn-project/simulator/src/public/dual_side_effect_trace.ts b/yarn-project/simulator/src/public/dual_side_effect_trace.ts index c875ebdf9de..ff396d68496 100644 --- a/yarn-project/simulator/src/public/dual_side_effect_trace.ts +++ b/yarn-project/simulator/src/public/dual_side_effect_trace.ts @@ -12,7 +12,7 @@ import { type Fr } from '@aztec/foundation/fields'; import { assert } from 'console'; -import { type AvmContractCallResult } from '../avm/avm_contract_call_result.js'; +import { type AvmContractCallResult, type AvmFinalizedCallResult } from '../avm/avm_contract_call_result.js'; import { type AvmExecutionEnvironment } from '../avm/avm_execution_environment.js'; import { type PublicEnqueuedCallSideEffectTrace } from './enqueued_call_side_effect_trace.js'; import { type EnqueuedPublicCallExecutionResultWithSideEffects, type PublicFunctionCallResult } from './execution.js'; @@ -191,8 +191,6 @@ export class DualSideEffectTrace implements PublicSideEffectTraceInterface { nestedEnvironment: AvmExecutionEnvironment, /** How much gas was available for this public execution. */ startGasLeft: Gas, - /** How much gas was left after this public execution. */ - endGasLeft: Gas, /** Bytecode used for this execution. */ bytecode: Buffer, /** The call's results */ @@ -204,7 +202,6 @@ export class DualSideEffectTrace implements PublicSideEffectTraceInterface { nestedCallTrace.innerCallTrace, nestedEnvironment, startGasLeft, - endGasLeft, bytecode, avmCallResults, functionName, @@ -213,7 +210,6 @@ export class DualSideEffectTrace implements PublicSideEffectTraceInterface { nestedCallTrace.enqueuedCallTrace, nestedEnvironment, startGasLeft, - endGasLeft, bytecode, avmCallResults, functionName, @@ -235,12 +231,10 @@ export class DualSideEffectTrace implements PublicSideEffectTraceInterface { * Convert this trace to a PublicExecutionResult for use externally to the simulator. */ public toPublicEnqueuedCallExecutionResult( - /** How much gas was left after this public execution. */ - endGasLeft: Gas, /** The call's results */ - avmCallResults: AvmContractCallResult, + avmCallResults: AvmFinalizedCallResult, ): EnqueuedPublicCallExecutionResultWithSideEffects { - return this.enqueuedCallTrace.toPublicEnqueuedCallExecutionResult(endGasLeft, avmCallResults); + return this.enqueuedCallTrace.toPublicEnqueuedCallExecutionResult(avmCallResults); } /** * Convert this trace to a PublicExecutionResult for use externally to the simulator. @@ -250,19 +244,16 @@ export class DualSideEffectTrace implements PublicSideEffectTraceInterface { avmEnvironment: AvmExecutionEnvironment, /** How much gas was available for this public execution. */ startGasLeft: Gas, - /** How much gas was left after this public execution. */ - endGasLeft: Gas, /** Bytecode used for this execution. */ bytecode: Buffer, /** The call's results */ - avmCallResults: AvmContractCallResult, + avmCallResults: AvmFinalizedCallResult, /** Function name for logging */ functionName: string = 'unknown', ): PublicFunctionCallResult { return this.innerCallTrace.toPublicFunctionCallResult( avmEnvironment, startGasLeft, - endGasLeft, bytecode, avmCallResults, functionName, diff --git a/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.ts b/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.ts index bcaef703b84..6cc11d08fd6 100644 --- a/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.ts +++ b/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.ts @@ -62,9 +62,8 @@ import { createDebugLogger } from '@aztec/foundation/log'; import { assert } from 'console'; -import { type AvmContractCallResult } from '../avm/avm_contract_call_result.js'; +import { type AvmContractCallResult, type AvmFinalizedCallResult } from '../avm/avm_contract_call_result.js'; import { type AvmExecutionEnvironment } from '../avm/avm_execution_environment.js'; -import { createSimulationError } from '../common/errors.js'; import { type EnqueuedPublicCallExecutionResultWithSideEffects, type PublicFunctionCallResult } from './execution.js'; import { SideEffectLimitReachedError } from './side_effect_errors.js'; import { type PublicSideEffectTraceInterface } from './side_effect_trace_interface.js'; @@ -188,7 +187,10 @@ export class PublicEnqueuedCallSideEffectTrace implements PublicSideEffectTraceI public merge(forkedTrace: this, reverted: boolean = false) { // sanity check to avoid merging the same forked trace twice - assert(!this.alreadyMergedIntoParent, 'Cannot merge a forked trace that has already been merged into its parent!'); + assert( + !forkedTrace.alreadyMergedIntoParent, + 'Cannot merge a forked trace that has already been merged into its parent!', + ); forkedTrace.alreadyMergedIntoParent = true; // TODO(dbanks12): accept & merge forked trace's hints! @@ -510,8 +512,6 @@ export class PublicEnqueuedCallSideEffectTrace implements PublicSideEffectTraceI nestedEnvironment: AvmExecutionEnvironment, /** How much gas was available for this public execution. */ startGasLeft: Gas, - /** How much gas was left after this public execution. */ - endGasLeft: Gas, /** Bytecode used for this execution. */ _bytecode: Buffer, /** The call's results */ @@ -524,7 +524,10 @@ export class PublicEnqueuedCallSideEffectTrace implements PublicSideEffectTraceI // Store end side effect counter before it gets updated by absorbing nested call trace const endSideEffectCounter = new Fr(this.sideEffectCounter); - const gasUsed = new Gas(startGasLeft.daGas - endGasLeft.daGas, startGasLeft.l2Gas - endGasLeft.l2Gas); + const gasUsed = new Gas( + startGasLeft.daGas - avmCallResults.gasLeft.daGas, + startGasLeft.l2Gas - avmCallResults.gasLeft.l2Gas, + ); this.avmCircuitHints.externalCalls.items.push( new AvmExternalCallHint( @@ -576,19 +579,15 @@ export class PublicEnqueuedCallSideEffectTrace implements PublicSideEffectTraceI * Get the results of public execution. */ public toPublicEnqueuedCallExecutionResult( - /** How much gas was left after this public execution. */ - endGasLeft: Gas, /** The call's results */ - avmCallResults: AvmContractCallResult, + avmCallResults: AvmFinalizedCallResult, ): EnqueuedPublicCallExecutionResultWithSideEffects { return { - endGasLeft, + endGasLeft: Gas.from(avmCallResults.gasLeft), endSideEffectCounter: new Fr(this.sideEffectCounter), returnValues: avmCallResults.output, reverted: avmCallResults.reverted, - revertReason: avmCallResults.revertReason - ? createSimulationError(avmCallResults.revertReason, avmCallResults.output) - : undefined, + revertReason: avmCallResults.revertReason, sideEffects: { publicDataWrites: this.publicDataWrites, noteHashes: this.noteHashes, @@ -652,12 +651,10 @@ export class PublicEnqueuedCallSideEffectTrace implements PublicSideEffectTraceI _avmEnvironment: AvmExecutionEnvironment, /** How much gas was available for this public execution. */ _startGasLeft: Gas, - /** How much gas was left after this public execution. */ - _endGasLeft: Gas, /** Bytecode used for this execution. */ _bytecode: Buffer, /** The call's results */ - _avmCallResults: AvmContractCallResult, + _avmCallResults: AvmFinalizedCallResult, /** Function name for logging */ _functionName: string = 'unknown', ): PublicFunctionCallResult { diff --git a/yarn-project/simulator/src/public/executor.ts b/yarn-project/simulator/src/public/executor.ts deleted file mode 100644 index 162d20b7a4c..00000000000 --- a/yarn-project/simulator/src/public/executor.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { type PublicExecutionRequest } from '@aztec/circuit-types'; -import { type AvmSimulationStats } from '@aztec/circuit-types/stats'; -import { Fr, Gas, type GlobalVariables } from '@aztec/circuits.js'; -import { createDebugLogger } from '@aztec/foundation/log'; -import { Timer } from '@aztec/foundation/timer'; -import { type TelemetryClient } from '@aztec/telemetry-client'; - -import { AvmContext } from '../avm/avm_context.js'; -import { AvmExecutionEnvironment } from '../avm/avm_execution_environment.js'; -import { AvmMachineState } from '../avm/avm_machine_state.js'; -import { AvmSimulator } from '../avm/avm_simulator.js'; -import { AvmPersistableStateManager } from '../avm/journal/index.js'; -import { getPublicFunctionDebugName } from '../common/debug_fn_name.js'; -import { DualSideEffectTrace } from './dual_side_effect_trace.js'; -import { PublicEnqueuedCallSideEffectTrace } from './enqueued_call_side_effect_trace.js'; -import { - type EnqueuedPublicCallExecutionResult, - type EnqueuedPublicCallExecutionResultWithSideEffects, -} from './execution.js'; -import { ExecutorMetrics } from './executor_metrics.js'; -import { type WorldStateDB } from './public_db_sources.js'; -import { PublicSideEffectTrace } from './side_effect_trace.js'; - -/** - * Handles execution of public functions. - */ -export class PublicExecutor { - metrics: ExecutorMetrics; - - constructor(private readonly worldStateDB: WorldStateDB, client: TelemetryClient) { - this.metrics = new ExecutorMetrics(client, 'PublicExecutor'); - } - - static readonly log = createDebugLogger('aztec:simulator:public_executor'); - - /** - * Simulate a public execution request. - * @param executionRequest - The execution to run. - * @param globalVariables - The global variables to use. - * @param allocatedGas - The gas available at the start of this enqueued call. - * @param transactionFee - Fee offered for this TX. - * @returns The result of execution. - */ - public async simulate( - stateManager: AvmPersistableStateManager, - executionRequest: PublicExecutionRequest, - globalVariables: GlobalVariables, - allocatedGas: Gas, - transactionFee: Fr = Fr.ZERO, - ): Promise { - const address = executionRequest.callContext.contractAddress; - const selector = executionRequest.callContext.functionSelector; - const fnName = await getPublicFunctionDebugName(this.worldStateDB, address, selector, executionRequest.args); - - PublicExecutor.log.verbose( - `[AVM] Executing public external function ${fnName}@${address} with ${allocatedGas.l2Gas} allocated L2 gas.`, - ); - const timer = new Timer(); - - const avmExecutionEnv = createAvmExecutionEnvironment(executionRequest, globalVariables, transactionFee); - - const avmMachineState = new AvmMachineState(allocatedGas); - const avmContext = new AvmContext(stateManager, avmExecutionEnv, avmMachineState); - const simulator = new AvmSimulator(avmContext); - const avmCallResult = await simulator.execute(); - const bytecode = simulator.getBytecode()!; - - PublicExecutor.log.verbose( - `[AVM] ${fnName} returned, reverted: ${avmCallResult.reverted}${ - avmCallResult.reverted ? ', reason: ' + avmCallResult.revertReason : '' - }.`, - { - eventName: 'avm-simulation', - appCircuitName: fnName, - duration: timer.ms(), - bytecodeSize: bytecode!.length, - } satisfies AvmSimulationStats, - ); - - const publicExecutionResult = stateManager.trace.toPublicEnqueuedCallExecutionResult( - /*endGasLeft=*/ Gas.from(avmContext.machineState.gasLeft), - avmCallResult, - ); - - if (avmCallResult.reverted) { - this.metrics.recordFunctionSimulationFailure(); - } else { - this.metrics.recordFunctionSimulation(bytecode.length, timer.ms()); - } - - PublicExecutor.log.verbose( - `[AVM] ${fnName} simulation complete. Reverted=${avmCallResult.reverted}. Consumed ${ - allocatedGas.l2Gas - avmContext.machineState.gasLeft.l2Gas - } L2 gas, ending with ${avmContext.machineState.gasLeft.l2Gas} L2 gas left.`, - ); - - return publicExecutionResult; - } - - /** - * Simulate an enqueued call on its own, and include its side effects in its results. - * @param executionRequest - The execution to run. - * @param globalVariables - The global variables to use. - * @param allocatedGas - The gas available at the start of this enqueued call. - * @param transactionFee - Fee offered for this TX. - * @param startSideEffectCounter - The start counter to initialize the side effect trace with. - * @returns The result of execution including side effect vectors. - * FIXME: this function is only used by the TXE. Ideally we would not support this as an external interface. - * Avoid using this interface as it it shouldn't really exist in the first place. - */ - public async simulateIsolatedEnqueuedCall( - executionRequest: PublicExecutionRequest, - globalVariables: GlobalVariables, - allocatedGas: Gas, - transactionFee: Fr = Fr.ONE, - startSideEffectCounter: number = 0, - ): Promise { - const innerCallTrace = new PublicSideEffectTrace(startSideEffectCounter); - const enqueuedCallTrace = new PublicEnqueuedCallSideEffectTrace(startSideEffectCounter); - const trace = new DualSideEffectTrace(innerCallTrace, enqueuedCallTrace); - const stateManager = new AvmPersistableStateManager(this.worldStateDB, trace); - return (await this.simulate( - stateManager, - executionRequest, - globalVariables, - allocatedGas, - transactionFee, - )) as EnqueuedPublicCallExecutionResultWithSideEffects; - } -} - -/** - * Convert a PublicExecutionRequest object to an AvmExecutionEnvironment - * - * @param executionRequest - * @param globalVariables - * @returns - */ -export function createAvmExecutionEnvironment( - executionRequest: PublicExecutionRequest, - globalVariables: GlobalVariables, - transactionFee: Fr, -): AvmExecutionEnvironment { - return new AvmExecutionEnvironment( - executionRequest.callContext.contractAddress, - executionRequest.callContext.msgSender, - executionRequest.callContext.functionSelector, - /*contractCallDepth=*/ Fr.zero(), - transactionFee, - globalVariables, - executionRequest.callContext.isStaticCall, - executionRequest.args, - ); -} diff --git a/yarn-project/simulator/src/public/executor_metrics.ts b/yarn-project/simulator/src/public/executor_metrics.ts index 1d4a05566a8..4b648dfe4ef 100644 --- a/yarn-project/simulator/src/public/executor_metrics.ts +++ b/yarn-project/simulator/src/public/executor_metrics.ts @@ -10,7 +10,6 @@ import { export class ExecutorMetrics { private fnCount: UpDownCounter; private fnDuration: Histogram; - private bytecodeSize: Histogram; constructor(client: TelemetryClient, name = 'PublicExecutor') { const meter = client.getMeter(name); @@ -24,19 +23,12 @@ export class ExecutorMetrics { unit: 'ms', valueType: ValueType.INT, }); - - this.bytecodeSize = meter.createHistogram(Metrics.PUBLIC_EXECUTION_SIMULATION_BYTECODE_SIZE, { - description: 'Size of the function bytecode', - unit: 'By', - valueType: ValueType.INT, - }); } - recordFunctionSimulation(bytecodeSize: number, durationMs: number) { + recordFunctionSimulation(durationMs: number) { this.fnCount.add(1, { [Attributes.OK]: true, }); - this.bytecodeSize.record(bytecodeSize); this.fnDuration.record(Math.ceil(durationMs)); } diff --git a/yarn-project/simulator/src/public/index.ts b/yarn-project/simulator/src/public/index.ts index 41cb04956e0..b7a3a5929f5 100644 --- a/yarn-project/simulator/src/public/index.ts +++ b/yarn-project/simulator/src/public/index.ts @@ -1,10 +1,6 @@ export * from './db_interfaces.js'; export * from './public_tx_simulator.js'; -export { - type EnqueuedPublicCallExecutionResult as PublicExecutionResult, - type PublicFunctionCallResult, -} from './execution.js'; -export { PublicExecutor } from './executor.js'; +export { type EnqueuedPublicCallExecutionResult, type PublicFunctionCallResult } from './execution.js'; export * from './fee_payment.js'; export * from './public_db_sources.js'; export { PublicProcessor, PublicProcessorFactory } from './public_processor.js'; diff --git a/yarn-project/simulator/src/public/public_processor.ts b/yarn-project/simulator/src/public/public_processor.ts index 421b9edd14e..853d3bf2989 100644 --- a/yarn-project/simulator/src/public/public_processor.ts +++ b/yarn-project/simulator/src/public/public_processor.ts @@ -31,7 +31,6 @@ import { Timer } from '@aztec/foundation/timer'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; import { Attributes, type TelemetryClient, type Tracer, trackSpan } from '@aztec/telemetry-client'; -import { PublicExecutor } from './executor.js'; import { computeFeePayerBalanceLeafSlot, computeFeePayerBalanceStorageSlot } from './fee_payment.js'; import { WorldStateDB } from './public_db_sources.js'; import { PublicProcessorMetrics } from './public_processor_metrics.js'; @@ -54,18 +53,17 @@ export class PublicProcessorFactory { maybeHistoricalHeader: Header | undefined, globalVariables: GlobalVariables, ): PublicProcessor { - const { telemetryClient } = this; const historicalHeader = maybeHistoricalHeader ?? merkleTree.getInitialHeader(); const worldStateDB = new WorldStateDB(merkleTree, this.contractDataSource); - const publicExecutor = new PublicExecutor(worldStateDB, telemetryClient); + const publicTxSimulator = new PublicTxSimulator(merkleTree, worldStateDB, this.telemetryClient, globalVariables); - return PublicProcessor.create( + return new PublicProcessor( merkleTree, - publicExecutor, globalVariables, historicalHeader, worldStateDB, + publicTxSimulator, this.telemetryClient, ); } @@ -89,19 +87,6 @@ export class PublicProcessor { this.metrics = new PublicProcessorMetrics(telemetryClient, 'PublicProcessor'); } - static create( - db: MerkleTreeWriteOperations, - publicExecutor: PublicExecutor, - globalVariables: GlobalVariables, - historicalHeader: Header, - worldStateDB: WorldStateDB, - telemetryClient: TelemetryClient, - ) { - const publicTxSimulator = PublicTxSimulator.create(db, publicExecutor, globalVariables, worldStateDB); - - return new PublicProcessor(db, globalVariables, historicalHeader, worldStateDB, publicTxSimulator, telemetryClient); - } - get tracer(): Tracer { return this.metrics.tracer; } diff --git a/yarn-project/simulator/src/public/public_tx_context.ts b/yarn-project/simulator/src/public/public_tx_context.ts index 7126c5175ce..4cb1390e2dc 100644 --- a/yarn-project/simulator/src/public/public_tx_context.ts +++ b/yarn-project/simulator/src/public/public_tx_context.ts @@ -25,10 +25,10 @@ import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { strict as assert } from 'assert'; import { inspect } from 'util'; +import { type AvmFinalizedCallResult } from '../avm/avm_contract_call_result.js'; import { AvmPersistableStateManager } from '../avm/index.js'; import { DualSideEffectTrace } from './dual_side_effect_trace.js'; import { PublicEnqueuedCallSideEffectTrace, SideEffectArrayLengths } from './enqueued_call_side_effect_trace.js'; -import { type EnqueuedPublicCallExecutionResult } from './execution.js'; import { type WorldStateDB } from './public_db_sources.js'; import { PublicSideEffectTrace } from './side_effect_trace.js'; import { generateAvmCircuitPublicInputs, generateAvmProvingRequest } from './transitional_adapters.js'; @@ -340,26 +340,27 @@ export class PublicTxContext { } // TODO(dbanks12): remove once AVM proves entire public tx - async updateProvingRequest( + updateProvingRequest( real: boolean, phase: TxExecutionPhase, - worldStateDB: WorldStateDB, + fnName: string, stateManager: AvmPersistableStateManager, executionRequest: PublicExecutionRequest, - result: EnqueuedPublicCallExecutionResult, + result: AvmFinalizedCallResult, allocatedGas: Gas, ) { if (this.avmProvingRequest === undefined) { // Propagate the very first avmProvingRequest of the tx for now. // Eventually this will be the proof for the entire public portion of the transaction. - this.avmProvingRequest = await generateAvmProvingRequest( + this.avmProvingRequest = generateAvmProvingRequest( real, - worldStateDB, + fnName, stateManager, this.historicalHeader, this.globalVariables, executionRequest, - result, + // TODO(dbanks12): do we need this return type unless we are doing an isolated call? + stateManager.trace.toPublicEnqueuedCallExecutionResult(result), allocatedGas, this.getTransactionFee(phase), ); diff --git a/yarn-project/simulator/src/public/public_tx_simulator.test.ts b/yarn-project/simulator/src/public/public_tx_simulator.test.ts index 48ef94ea6f6..813c074cda4 100644 --- a/yarn-project/simulator/src/public/public_tx_simulator.test.ts +++ b/yarn-project/simulator/src/public/public_tx_simulator.test.ts @@ -27,12 +27,13 @@ import { fr } from '@aztec/circuits.js/testing'; import { type AztecKVStore } from '@aztec/kv-store'; import { openTmpStore } from '@aztec/kv-store/utils'; import { type AppendOnlyTree, Poseidon, StandardTree, newTree } from '@aztec/merkle-tree'; +import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; +import { jest } from '@jest/globals'; import { type MockProxy, mock } from 'jest-mock-extended'; +import { AvmFinalizedCallResult } from '../avm/avm_contract_call_result.js'; import { type AvmPersistableStateManager } from '../avm/journal/journal.js'; -import { PublicExecutionResultBuilder } from '../mocks/fixtures.js'; -import { type PublicExecutor } from './executor.js'; import { type WorldStateDB } from './public_db_sources.js'; import { PublicTxSimulator } from './public_tx_simulator.js'; @@ -50,14 +51,22 @@ describe('public_tx_simulator', () => { const enqueuedCallGasUsed = new Gas(12, 34); let db: MockProxy; - let publicExecutor: MockProxy; let worldStateDB: MockProxy; let root: Buffer; let publicDataTree: AppendOnlyTree; - let processor: PublicTxSimulator; let treeStore: AztecKVStore; + let simulator: PublicTxSimulator; + let simulateInternal: jest.SpiedFunction< + ( + stateManager: AvmPersistableStateManager, + executionResult: any, + allocatedGas: Gas, + transactionFee: any, + fnName: any, + ) => Promise + >; const mockTxWithPublicCalls = ({ numberOfSetupCalls = 0, @@ -89,34 +98,51 @@ describe('public_tx_simulator', () => { }; const mockPublicExecutor = ( - mockedSimulatorExecutions: (( - stateManager: AvmPersistableStateManager, - resultBuilder: PublicExecutionResultBuilder, - ) => Promise)[], + mockedSimulatorExecutions: ((stateManager: AvmPersistableStateManager) => Promise)[], ) => { for (const executeSimulator of mockedSimulatorExecutions) { - publicExecutor.simulate.mockImplementationOnce( - async (stateManager: AvmPersistableStateManager, _executionResult, _globalVariables, availableGas) => { - const builder = PublicExecutionResultBuilder.empty(); - await executeSimulator(stateManager, builder); - return builder.build({ - endGasLeft: availableGas.sub(enqueuedCallGasUsed), - }); + simulateInternal.mockImplementationOnce( + async ( + stateManager: AvmPersistableStateManager, + _executionResult: any, + allocatedGas: Gas, + _transactionFee: any, + _fnName: any, + ) => { + const revertReason = await executeSimulator(stateManager); + if (revertReason === undefined) { + return Promise.resolve( + new AvmFinalizedCallResult( + /*reverted=*/ false, + /*output=*/ [], + /*gasLeft=*/ allocatedGas.sub(enqueuedCallGasUsed), + ), + ); + } else { + return Promise.resolve( + new AvmFinalizedCallResult( + /*reverted=*/ true, + /*output=*/ [], + /*gasLeft=*/ allocatedGas.sub(enqueuedCallGasUsed), + revertReason, + ), + ); + } }, ); } }; const expectAvailableGasForCalls = (availableGases: Gas[]) => { - expect(publicExecutor.simulate).toHaveBeenCalledTimes(availableGases.length); + expect(simulateInternal).toHaveBeenCalledTimes(availableGases.length); availableGases.forEach((availableGas, i) => { - expect(publicExecutor.simulate).toHaveBeenNthCalledWith( + expect(simulateInternal).toHaveBeenNthCalledWith( i + 1, expect.anything(), // AvmPersistableStateManager expect.anything(), // publicExecutionRequest - expect.anything(), // globalVariables Gas.from(availableGas), expect.anything(), // txFee + expect.anything(), // fnName ); }); }; @@ -126,14 +152,6 @@ describe('public_tx_simulator', () => { worldStateDB = mock(); root = Buffer.alloc(32, 5); - publicExecutor = mock(); - publicExecutor.simulate.mockImplementation((_stateManager, _executionResult, _globalVariables, availableGas) => { - const result = PublicExecutionResultBuilder.empty().build({ - endGasLeft: availableGas.sub(enqueuedCallGasUsed), - }); - return Promise.resolve(result); - }); - treeStore = openTmpStore(); publicDataTree = await newTree( @@ -163,13 +181,37 @@ describe('public_tx_simulator', () => { db.getPreviousValueIndex.mockResolvedValue({ index: 0n, alreadyPresent: true }); db.getLeafPreimage.mockResolvedValue(new PublicDataTreeLeafPreimage(new Fr(0), new Fr(0), new Fr(0), 0n)); - processor = PublicTxSimulator.create( + simulator = new PublicTxSimulator( db, - publicExecutor, - GlobalVariables.from({ ...GlobalVariables.empty(), gasFees }), worldStateDB, + new NoopTelemetryClient(), + GlobalVariables.from({ ...GlobalVariables.empty(), gasFees }), /*realAvmProvingRequest=*/ false, ); + + // Mock the internal private function. Borrowed from https://stackoverflow.com/a/71033167 + simulateInternal = jest.spyOn( + simulator as unknown as { simulateEnqueuedCallInternal: PublicTxSimulator['simulateEnqueuedCallInternal'] }, + 'simulateEnqueuedCallInternal', + ); + simulateInternal.mockImplementation( + ( + _stateManager: AvmPersistableStateManager, + _executionResult: any, + allocatedGas: Gas, + _transactionFee: any, + _fnName: any, + ) => { + return Promise.resolve( + new AvmFinalizedCallResult( + /*reverted=*/ false, + /*output=*/ [], + /*gasLeft=*/ allocatedGas.sub(enqueuedCallGasUsed), + /*revertReason=*/ undefined, + ), + ); + }, + ); }); afterEach(async () => { @@ -181,7 +223,7 @@ describe('public_tx_simulator', () => { numberOfSetupCalls: 2, }); - const txResult = await processor.simulate(tx); + const txResult = await simulator.simulate(tx); expect(txResult.processedPhases).toEqual([ expect.objectContaining({ phase: TxExecutionPhase.SETUP, revertReason: undefined }), @@ -216,7 +258,7 @@ describe('public_tx_simulator', () => { numberOfAppLogicCalls: 2, }); - const txResult = await processor.simulate(tx); + const txResult = await simulator.simulate(tx); expect(txResult.processedPhases).toEqual([ expect.objectContaining({ phase: TxExecutionPhase.APP_LOGIC, revertReason: undefined }), @@ -251,7 +293,7 @@ describe('public_tx_simulator', () => { hasPublicTeardownCall: true, }); - const txResult = await processor.simulate(tx); + const txResult = await simulator.simulate(tx); expect(txResult.processedPhases).toEqual([ expect.objectContaining({ phase: TxExecutionPhase.TEARDOWN, revertReason: undefined }), @@ -286,7 +328,7 @@ describe('public_tx_simulator', () => { hasPublicTeardownCall: true, }); - const txResult = await processor.simulate(tx); + const txResult = await simulator.simulate(tx); expect(txResult.processedPhases).toHaveLength(3); expect(txResult.processedPhases).toEqual([ @@ -364,9 +406,9 @@ describe('public_tx_simulator', () => { }, ]); - const txResult = await processor.simulate(tx); + const txResult = await simulator.simulate(tx); - expect(publicExecutor.simulate).toHaveBeenCalledTimes(3); + expect(simulateInternal).toHaveBeenCalledTimes(3); const output = txResult.avmProvingRequest!.inputs.output; @@ -394,13 +436,18 @@ describe('public_tx_simulator', () => { const setupFailureMsg = 'Simulation Failed in setup'; const setupFailure = new SimulationError(setupFailureMsg, []); - publicExecutor.simulate.mockResolvedValueOnce( - PublicExecutionResultBuilder.empty().withReverted(setupFailure).build(), + simulateInternal.mockResolvedValueOnce( + new AvmFinalizedCallResult( + /*reverted=*/ true, + /*output=*/ [], + /*gasLeft=*/ Gas.empty(), + /*revertReason=*/ setupFailure, + ), ); - await expect(processor.simulate(tx)).rejects.toThrow(setupFailureMsg); + await expect(simulator.simulate(tx)).rejects.toThrow(setupFailureMsg); - expect(publicExecutor.simulate).toHaveBeenCalledTimes(1); + expect(simulateInternal).toHaveBeenCalledTimes(1); }); it('includes a transaction that reverts in app logic only', async function () { @@ -423,9 +470,9 @@ describe('public_tx_simulator', () => { await stateManager.writeNullifier(contractAddress, new Fr(2)); await stateManager.writeNullifier(contractAddress, new Fr(3)); }, - async (stateManager: AvmPersistableStateManager, resultBuilder: PublicExecutionResultBuilder) => { + async (stateManager: AvmPersistableStateManager) => { await stateManager.writeNullifier(contractAddress, new Fr(4)); - resultBuilder.withReverted(appLogicFailure); + return Promise.resolve(appLogicFailure); }, // TEARDOWN async (stateManager: AvmPersistableStateManager) => { @@ -433,7 +480,7 @@ describe('public_tx_simulator', () => { }, ]); - const txResult = await processor.simulate(tx); + const txResult = await simulator.simulate(tx); expect(txResult.processedPhases).toHaveLength(3); expect(txResult.processedPhases).toEqual([ @@ -505,13 +552,13 @@ describe('public_tx_simulator', () => { await stateManager.writeNullifier(contractAddress, new Fr(4)); }, // TEARDOWN - async (stateManager: AvmPersistableStateManager, resultBuilder: PublicExecutionResultBuilder) => { + async (stateManager: AvmPersistableStateManager) => { await stateManager.writeNullifier(contractAddress, new Fr(5)); - resultBuilder.withReverted(teardownFailure); + return Promise.resolve(teardownFailure); }, ]); - const txResult = await processor.simulate(tx); + const txResult = await simulator.simulate(tx); expect(txResult.processedPhases).toEqual([ expect.objectContaining({ phase: TxExecutionPhase.SETUP, revertReason: undefined }), @@ -582,18 +629,18 @@ describe('public_tx_simulator', () => { await stateManager.writeNullifier(contractAddress, new Fr(2)); await stateManager.writeNullifier(contractAddress, new Fr(3)); }, - async (stateManager: AvmPersistableStateManager, resultBuilder: PublicExecutionResultBuilder) => { + async (stateManager: AvmPersistableStateManager) => { await stateManager.writeNullifier(contractAddress, new Fr(4)); - resultBuilder.withReverted(appLogicFailure); + return Promise.resolve(appLogicFailure); }, // TEARDOWN - async (stateManager: AvmPersistableStateManager, resultBuilder: PublicExecutionResultBuilder) => { + async (stateManager: AvmPersistableStateManager) => { await stateManager.writeNullifier(contractAddress, new Fr(5)); - resultBuilder.withReverted(teardownFailure); + return Promise.resolve(teardownFailure); }, ]); - const txResult = await processor.simulate(tx); + const txResult = await simulator.simulate(tx); expect(txResult.processedPhases).toHaveLength(3); expect(txResult.processedPhases).toEqual([ diff --git a/yarn-project/simulator/src/public/public_tx_simulator.ts b/yarn-project/simulator/src/public/public_tx_simulator.ts index 0e1cc6fea0f..a4d31296500 100644 --- a/yarn-project/simulator/src/public/public_tx_simulator.ts +++ b/yarn-project/simulator/src/public/public_tx_simulator.ts @@ -3,17 +3,29 @@ import { type GasUsed, type MerkleTreeReadOperations, NestedProcessReturnValues, + type PublicExecutionRequest, type SimulationError, type Tx, TxExecutionPhase, UnencryptedFunctionL2Logs, } from '@aztec/circuit-types'; -import { Gas, type GlobalVariables, MAX_L2_GAS_PER_ENQUEUED_CALL, type RevertCode } from '@aztec/circuits.js'; +import { type AvmSimulationStats } from '@aztec/circuit-types/stats'; +import { + type Fr, + Gas, + type GlobalVariables, + MAX_L2_GAS_PER_ENQUEUED_CALL, + type PublicCallRequest, + type RevertCode, +} from '@aztec/circuits.js'; import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { Timer } from '@aztec/foundation/timer'; +import { type TelemetryClient } from '@aztec/telemetry-client'; -import { type EnqueuedPublicCallExecutionResult } from './execution.js'; -import { type PublicExecutor } from './executor.js'; +import { type AvmFinalizedCallResult } from '../avm/avm_contract_call_result.js'; +import { type AvmPersistableStateManager, AvmSimulator } from '../avm/index.js'; +import { getPublicFunctionDebugName } from '../common/debug_fn_name.js'; +import { ExecutorMetrics } from './executor_metrics.js'; import { type WorldStateDB } from './public_db_sources.js'; import { PublicTxContext } from './public_tx_context.js'; @@ -36,28 +48,26 @@ export type PublicTxResult = { }; export class PublicTxSimulator { + metrics: ExecutorMetrics; + private log: DebugLogger; constructor( private db: MerkleTreeReadOperations, - private globalVariables: GlobalVariables, private worldStateDB: WorldStateDB, - private publicExecutor: PublicExecutor, + client: TelemetryClient, + private globalVariables: GlobalVariables, private realAvmProvingRequests: boolean = true, ) { this.log = createDebugLogger(`aztec:public_tx_simulator`); + this.metrics = new ExecutorMetrics(client, 'PublicTxSimulator'); } - static create( - db: MerkleTreeReadOperations, - publicExecutor: PublicExecutor, - globalVariables: GlobalVariables, - worldStateDB: WorldStateDB, - realAvmProvingRequests: boolean = true, - ) { - return new PublicTxSimulator(db, globalVariables, worldStateDB, publicExecutor, realAvmProvingRequests); - } - + /** + * Simulate a transaction's public portion including all of its phases. + * @param tx - The transaction to simulate. + * @returns The result of the transaction's public execution. + */ async simulate(tx: Tx): Promise { this.log.verbose(`Processing tx ${tx.getTxHash()}`); @@ -117,10 +127,20 @@ export class PublicTxSimulator { }; } + /** + * Simulate the setup phase of a transaction's public execution. + * @param context - WILL BE MUTATED. The context of the currently executing public transaction portion + * @returns The phase result. + */ private async simulateSetupPhase(context: PublicTxContext): Promise { return await this.simulatePhase(TxExecutionPhase.SETUP, context); } + /** + * Simulate the app logic phase of a transaction's public execution. + * @param context - WILL BE MUTATED. The context of the currently executing public transaction portion + * @returns The phase result. + */ private async simulateAppLogicPhase(context: PublicTxContext): Promise { // Fork the state manager so that we can rollback state if app logic or teardown reverts. // Don't need to fork for setup since it's non-revertible (if setup fails, transaction is thrown out). @@ -141,6 +161,11 @@ export class PublicTxSimulator { return result; } + /** + * Simulate the teardown phase of a transaction's public execution. + * @param context - WILL BE MUTATED. The context of the currently executing public transaction portion + * @returns The phase result. + */ private async simulateTeardownPhase(context: PublicTxContext): Promise { if (!context.state.isForked()) { // If state isn't forked (app logic was empty or reverted), fork now @@ -161,10 +186,15 @@ export class PublicTxSimulator { return result; } + /** + * Simulate a phase of a transaction's public execution. + * @param phase - The current phase + * @param context - WILL BE MUTATED. The context of the currently executing public transaction portion + * @returns The phase result. + */ private async simulatePhase(phase: TxExecutionPhase, context: PublicTxContext): Promise { const callRequests = context.getCallRequestsForPhase(phase); const executionRequests = context.getExecutionRequestsForPhase(phase); - const txStateManager = context.state.getActiveStateManager(); this.log.debug(`Beginning processing in phase ${TxExecutionPhase[phase]} for tx ${context.getTxHash()}`); @@ -180,44 +210,13 @@ export class PublicTxSimulator { const callRequest = callRequests[i]; const executionRequest = executionRequests[i]; - const availableGas = context.getGasLeftForPhase(phase); - // Gas allocated to an enqueued call can be different from the available gas - // if there is more gas available than the max allocation per enqueued call. - const allocatedGas = new Gas( - /*daGas=*/ availableGas.daGas, - /*l2Gas=*/ Math.min(availableGas.l2Gas, MAX_L2_GAS_PER_ENQUEUED_CALL), - ); - - const enqueuedCallResult = (await this.publicExecutor.simulate( - txStateManager, - executionRequest, - this.globalVariables, // todo get from context - allocatedGas, - context.getTransactionFee(phase), - )) as EnqueuedPublicCallExecutionResult; - - // TODO(dbanks12): remove once AVM proves entire public tx - await context.updateProvingRequest( - this.realAvmProvingRequests, - phase, - this.worldStateDB, - txStateManager, - executionRequest, - enqueuedCallResult, - allocatedGas, - ); - - txStateManager.traceEnqueuedCall(callRequest, executionRequest.args, enqueuedCallResult.reverted!); + const enqueuedCallResult = await this.simulateEnqueuedCall(phase, context, callRequest, executionRequest); - const gasUsed = allocatedGas.sub(Gas.from(enqueuedCallResult.endGasLeft)); - context.consumeGas(phase, gasUsed); - returnValues.push(new NestedProcessReturnValues(enqueuedCallResult.returnValues)); + returnValues.push(new NestedProcessReturnValues(enqueuedCallResult.output)); if (enqueuedCallResult.reverted) { reverted = true; - const culprit = `${executionRequest.callContext.contractAddress}:${executionRequest.callContext.functionSelector}`; revertReason = enqueuedCallResult.revertReason; - context.revert(phase, enqueuedCallResult.revertReason, culprit); // throws if in setup (non-revertible) phase } } @@ -229,4 +228,130 @@ export class PublicTxSimulator { revertReason, }; } + + /** + * Simulate an enqueued public call. + * @param phase - The current phase of public execution + * @param context - WILL BE MUTATED. The context of the currently executing public transaction portion + * @param callRequest - The enqueued call to execute + * @param executionRequest - The execution request (includes args) + * @returns The result of execution. + */ + private async simulateEnqueuedCall( + phase: TxExecutionPhase, + context: PublicTxContext, + callRequest: PublicCallRequest, + executionRequest: PublicExecutionRequest, + ): Promise { + const stateManager = context.state.getActiveStateManager(); + const address = executionRequest.callContext.contractAddress; + const selector = executionRequest.callContext.functionSelector; + const fnName = await getPublicFunctionDebugName(this.worldStateDB, address, selector, executionRequest.args); + + const availableGas = context.getGasLeftForPhase(phase); + // Gas allocated to an enqueued call can be different from the available gas + // if there is more gas available than the max allocation per enqueued call. + const allocatedGas = new Gas( + /*daGas=*/ availableGas.daGas, + /*l2Gas=*/ Math.min(availableGas.l2Gas, MAX_L2_GAS_PER_ENQUEUED_CALL), + ); + + const result = await this.simulateEnqueuedCallInternal( + context.state.getActiveStateManager(), + executionRequest, + allocatedGas, + context.getTransactionFee(phase), + fnName, + ); + + const gasUsed = allocatedGas.sub(result.gasLeft); + context.consumeGas(phase, gasUsed); + this.log.verbose( + `[AVM] Enqueued public call consumed ${gasUsed.l2Gas} L2 gas ending with ${result.gasLeft.l2Gas} L2 gas left.`, + ); + + // TODO(dbanks12): remove once AVM proves entire public tx + context.updateProvingRequest( + this.realAvmProvingRequests, + phase, + fnName, + stateManager, + executionRequest, + result, + allocatedGas, + ); + + stateManager.traceEnqueuedCall(callRequest, executionRequest.args, result.reverted); + + if (result.reverted) { + const culprit = `${executionRequest.callContext.contractAddress}:${executionRequest.callContext.functionSelector}`; + context.revert(phase, result.revertReason, culprit); // throws if in setup (non-revertible) phase + } + + return result; + } + + /** + * Simulate an enqueued public call, without modifying the context (PublicTxContext). + * Resulting modifcations to the context can be applied by the caller. + * + * This function can be mocked for testing to skip actual AVM simulation + * while still simulating phases and generating a proving request. + * + * @param stateManager - The state manager for AvmSimulation + * @param context - The context of the currently executing public transaction portion + * @param executionRequest - The execution request (includes args) + * @param allocatedGas - The gas allocated to the enqueued call + * @param fnName - The name of the function + * @returns The result of execution. + */ + private async simulateEnqueuedCallInternal( + stateManager: AvmPersistableStateManager, + executionRequest: PublicExecutionRequest, + allocatedGas: Gas, + transactionFee: Fr, + fnName: string, + ): Promise { + const address = executionRequest.callContext.contractAddress; + const sender = executionRequest.callContext.msgSender; + const selector = executionRequest.callContext.functionSelector; + + this.log.verbose( + `[AVM] Executing enqueued public call to external function ${fnName}@${address} with ${allocatedGas.l2Gas} allocated L2 gas.`, + ); + const timer = new Timer(); + + const simulator = AvmSimulator.create( + stateManager, + address, + sender, + selector, + transactionFee, + this.globalVariables, + executionRequest.callContext.isStaticCall, + executionRequest.args, + allocatedGas, + ); + const avmCallResult = await simulator.execute(); + const result = avmCallResult.finalize(); + + this.log.verbose( + `[AVM] Simulation of enqueued public call ${fnName} completed. reverted: ${result.reverted}${ + result.reverted ? ', reason: ' + result.revertReason : '' + }.`, + { + eventName: 'avm-simulation', + appCircuitName: fnName, + duration: timer.ms(), + } satisfies AvmSimulationStats, + ); + + if (result.reverted) { + this.metrics.recordFunctionSimulationFailure(); + } else { + this.metrics.recordFunctionSimulation(timer.ms()); + } + + return result; + } } diff --git a/yarn-project/simulator/src/public/side_effect_trace.test.ts b/yarn-project/simulator/src/public/side_effect_trace.test.ts index 2a57e7df088..ccbcea0267c 100644 --- a/yarn-project/simulator/src/public/side_effect_trace.test.ts +++ b/yarn-project/simulator/src/public/side_effect_trace.test.ts @@ -51,7 +51,7 @@ describe('Side Effect Trace', () => { transactionFee, }); const reverted = false; - const avmCallResults = new AvmContractCallResult(reverted, returnValues); + const avmCallResults = new AvmContractCallResult(reverted, returnValues, endGasLeft); let startCounter: number; let startCounterFr: Fr; @@ -66,7 +66,7 @@ describe('Side Effect Trace', () => { }); const toPxResult = (trc: PublicSideEffectTrace) => { - return trc.toPublicFunctionCallResult(avmEnvironment, startGasLeft, endGasLeft, bytecode, avmCallResults); + return trc.toPublicFunctionCallResult(avmEnvironment, startGasLeft, bytecode, avmCallResults.finalize()); }; it('Should trace storage reads', () => { @@ -427,7 +427,7 @@ describe('Side Effect Trace', () => { nestedTrace.traceGetContractInstance(address, /*exists=*/ false, contractInstance); testCounter++; - trace.traceNestedCall(nestedTrace, avmEnvironment, startGasLeft, endGasLeft, bytecode, avmCallResults); + trace.traceNestedCall(nestedTrace, avmEnvironment, startGasLeft, bytecode, avmCallResults); // parent trace adopts nested call's counter expect(trace.getCounter()).toBe(testCounter); diff --git a/yarn-project/simulator/src/public/side_effect_trace.ts b/yarn-project/simulator/src/public/side_effect_trace.ts index 6c1bed9ee8c..2ec074470e7 100644 --- a/yarn-project/simulator/src/public/side_effect_trace.ts +++ b/yarn-project/simulator/src/public/side_effect_trace.ts @@ -49,9 +49,8 @@ import { createDebugLogger } from '@aztec/foundation/log'; import { assert } from 'console'; -import { type AvmContractCallResult } from '../avm/avm_contract_call_result.js'; +import { type AvmContractCallResult, type AvmFinalizedCallResult } from '../avm/avm_contract_call_result.js'; import { type AvmExecutionEnvironment } from '../avm/avm_execution_environment.js'; -import { createSimulationError } from '../common/errors.js'; import { type EnqueuedPublicCallExecutionResultWithSideEffects, type PublicFunctionCallResult, @@ -380,8 +379,6 @@ export class PublicSideEffectTrace implements PublicSideEffectTraceInterface { nestedEnvironment: AvmExecutionEnvironment, /** How much gas was available for this public execution. */ startGasLeft: Gas, - /** How much gas was left after this public execution. */ - endGasLeft: Gas, /** Bytecode used for this execution. */ bytecode: Buffer, /** The call's results */ @@ -397,9 +394,8 @@ export class PublicSideEffectTrace implements PublicSideEffectTraceInterface { const result = nestedCallTrace.toPublicFunctionCallResult( nestedEnvironment, startGasLeft, - endGasLeft, bytecode, - avmCallResults, + avmCallResults.finalize(), functionName, ); this.sideEffectCounter = result.endSideEffectCounter.toNumber(); @@ -410,8 +406,8 @@ export class PublicSideEffectTrace implements PublicSideEffectTraceInterface { this.nestedExecutions.push(result); const gasUsed = new Gas( - result.startGasLeft.daGas - result.endGasLeft.daGas, - result.startGasLeft.l2Gas - result.endGasLeft.l2Gas, + result.startGasLeft.daGas - avmCallResults.gasLeft.daGas, + result.startGasLeft.l2Gas - avmCallResults.gasLeft.l2Gas, ); this.publicCallRequests.push(resultToPublicCallRequest(result)); @@ -450,12 +446,10 @@ export class PublicSideEffectTrace implements PublicSideEffectTraceInterface { avmEnvironment: AvmExecutionEnvironment, /** How much gas was available for this public execution. */ startGasLeft: Gas, - /** How much gas was left after this public execution. */ - endGasLeft: Gas, /** Bytecode used for this execution. */ bytecode: Buffer, /** The call's results */ - avmCallResults: AvmContractCallResult, + avmCallResults: AvmFinalizedCallResult, /** Function name for logging */ functionName: string = 'unknown', ): PublicFunctionCallResult { @@ -465,16 +459,14 @@ export class PublicSideEffectTrace implements PublicSideEffectTraceInterface { startSideEffectCounter: new Fr(this.startSideEffectCounter), endSideEffectCounter: new Fr(this.sideEffectCounter), startGasLeft, - endGasLeft, + endGasLeft: avmCallResults.gasLeft, transactionFee: avmEnvironment.transactionFee, bytecode, calldata: avmEnvironment.calldata, returnValues: avmCallResults.output, reverted: avmCallResults.reverted, - revertReason: avmCallResults.revertReason - ? createSimulationError(avmCallResults.revertReason, avmCallResults.output) - : undefined, + revertReason: avmCallResults.revertReason, contractStorageReads: this.contractStorageReads, contractStorageUpdateRequests: this.contractStorageUpdateRequests, @@ -500,10 +492,8 @@ export class PublicSideEffectTrace implements PublicSideEffectTraceInterface { } public toPublicEnqueuedCallExecutionResult( - /** How much gas was left after this public execution. */ - _endGasLeft: Gas, /** The call's results */ - _avmCallResults: AvmContractCallResult, + _avmCallResults: AvmFinalizedCallResult, ): EnqueuedPublicCallExecutionResultWithSideEffects { throw new Error('Not implemented'); } diff --git a/yarn-project/simulator/src/public/side_effect_trace_interface.ts b/yarn-project/simulator/src/public/side_effect_trace_interface.ts index 8c3c5ae4ac7..98bdd9f809e 100644 --- a/yarn-project/simulator/src/public/side_effect_trace_interface.ts +++ b/yarn-project/simulator/src/public/side_effect_trace_interface.ts @@ -10,7 +10,7 @@ import { import { type AztecAddress } from '@aztec/foundation/aztec-address'; import { type Fr } from '@aztec/foundation/fields'; -import { type AvmContractCallResult } from '../avm/avm_contract_call_result.js'; +import { type AvmContractCallResult, type AvmFinalizedCallResult } from '../avm/avm_contract_call_result.js'; import { type AvmExecutionEnvironment } from '../avm/avm_execution_environment.js'; import { type EnqueuedPublicCallExecutionResultWithSideEffects, type PublicFunctionCallResult } from './execution.js'; @@ -83,8 +83,6 @@ export interface PublicSideEffectTraceInterface { nestedEnvironment: AvmExecutionEnvironment, /** How much gas was available for this public execution. */ startGasLeft: Gas, - /** How much gas was left after this public execution. */ - endGasLeft: Gas, /** Bytecode used for this execution. */ bytecode: Buffer, /** The call's results */ @@ -101,22 +99,18 @@ export interface PublicSideEffectTraceInterface { reverted: boolean, ): void; toPublicEnqueuedCallExecutionResult( - /** How much gas was left after this public execution. */ - endGasLeft: Gas, /** The call's results */ - avmCallResults: AvmContractCallResult, + avmCallResults: AvmFinalizedCallResult, ): EnqueuedPublicCallExecutionResultWithSideEffects; toPublicFunctionCallResult( /** The execution environment of the nested call. */ avmEnvironment: AvmExecutionEnvironment, /** How much gas was available for this public execution. */ startGasLeft: Gas, - /** How much gas was left after this public execution. */ - endGasLeft: Gas, /** Bytecode used for this execution. */ bytecode: Buffer, /** The call's results */ - avmCallResults: AvmContractCallResult, + avmCallResults: AvmFinalizedCallResult, /** Function name for logging */ functionName: string, ): PublicFunctionCallResult; diff --git a/yarn-project/simulator/src/public/transitional_adapters.ts b/yarn-project/simulator/src/public/transitional_adapters.ts index 248b6cfd7e9..a088f5fa000 100644 --- a/yarn-project/simulator/src/public/transitional_adapters.ts +++ b/yarn-project/simulator/src/public/transitional_adapters.ts @@ -48,13 +48,11 @@ import { computeNoteHashNonce, computeUniqueNoteHash, computeVarArgsHash, siloNo import { padArrayEnd } from '@aztec/foundation/collection'; import { assertLength } from '@aztec/foundation/serialize'; -import { AvmContractCallResult } from '../avm/avm_contract_call_result.js'; +import { AvmFinalizedCallResult } from '../avm/avm_contract_call_result.js'; +import { AvmExecutionEnvironment } from '../avm/avm_execution_environment.js'; import { type AvmPersistableStateManager } from '../avm/journal/journal.js'; -import { getPublicFunctionDebugName } from '../common/debug_fn_name.js'; import { type PublicEnqueuedCallSideEffectTrace } from './enqueued_call_side_effect_trace.js'; import { type EnqueuedPublicCallExecutionResult, type PublicFunctionCallResult } from './execution.js'; -import { createAvmExecutionEnvironment } from './executor.js'; -import { type WorldStateDB } from './public_db_sources.js'; export function generateAvmCircuitPublicInputs( trace: PublicEnqueuedCallSideEffectTrace, @@ -196,9 +194,9 @@ export function generateAvmCircuitPublicInputs( return avmCircuitPublicInputs; } -export async function generateAvmProvingRequest( +export function generateAvmProvingRequest( real: boolean, - worldStateDB: WorldStateDB, + fnName: string, stateManager: AvmPersistableStateManager, historicalHeader: Header, globalVariables: GlobalVariables, @@ -206,16 +204,19 @@ export async function generateAvmProvingRequest( result: EnqueuedPublicCallExecutionResult, allocatedGas: Gas, transactionFee: Fr, -): Promise { - const fnName = await getPublicFunctionDebugName( - worldStateDB, +): AvmProvingRequest { + const avmExecutionEnv = new AvmExecutionEnvironment( executionRequest.callContext.contractAddress, + executionRequest.callContext.msgSender, executionRequest.callContext.functionSelector, + /*contractCallDepth=*/ Fr.zero(), + transactionFee, + globalVariables, + executionRequest.callContext.isStaticCall, executionRequest.args, ); - const avmExecutionEnv = createAvmExecutionEnvironment(executionRequest, globalVariables, transactionFee); - const avmCallResult = new AvmContractCallResult(result.reverted, result.returnValues); + const avmCallResult = new AvmFinalizedCallResult(result.reverted, result.returnValues, result.endGasLeft); // Generate an AVM proving request let avmProvingRequest: AvmProvingRequest; @@ -223,7 +224,6 @@ export async function generateAvmProvingRequest( const deprecatedFunctionCallResult = stateManager.trace.toPublicFunctionCallResult( avmExecutionEnv, /*startGasLeft=*/ allocatedGas, - /*endGasLeft=*/ Gas.from(result.endGasLeft), Buffer.alloc(0), avmCallResult, fnName, diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index b6c0f55b7ec..b357b40f8f2 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -8,16 +8,19 @@ import { PublicDataWitness, PublicExecutionRequest, SimulationError, + Tx, type UnencryptedL2Log, } from '@aztec/circuit-types'; import { type CircuitWitnessGenerationStats } from '@aztec/circuit-types/stats'; import { CallContext, - CombinedConstantData, type ContractInstance, type ContractInstanceWithAddress, DEFAULT_GAS_LIMIT, Gas, + GasFees, + GasSettings, + GlobalVariables, Header, IndexedTaggingSecret, type KeyValidationRequest, @@ -30,10 +33,15 @@ import { PUBLIC_DATA_SUBTREE_HEIGHT, type PUBLIC_DATA_TREE_HEIGHT, PUBLIC_DISPATCH_SELECTOR, + PartialPrivateTailPublicInputsForPublic, PrivateContextInputs, + PrivateKernelTailCircuitPublicInputs, PublicDataTreeLeaf, type PublicDataTreeLeafPreimage, - type PublicDataUpdateRequest, + type PublicDataWrite, + RollupValidationRequests, + TxConstantData, + TxContext, computeContractClassId, computeTaggingSecret, deriveKeys, @@ -62,7 +70,8 @@ import { type NoteData, Oracle, type PackedValuesCache, - PublicExecutor, + type PublicTxResult, + PublicTxSimulator, type TypedOracle, acvm, createSimulationError, @@ -76,7 +85,6 @@ import { import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; import { MerkleTreeSnapshotOperationsFacade, type MerkleTrees } from '@aztec/world-state'; -import { type EnqueuedPublicCallExecutionResultWithSideEffects } from '../../../simulator/src/public/execution.js'; import { type TXEDatabase } from '../util/txe_database.js'; import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_source.js'; import { TXEWorldStateDB } from '../util/txe_world_state_db.js'; @@ -222,26 +230,17 @@ export class TXE implements TypedOracle { return this.txeDatabase.addAuthWitness(authWitness.requestHash, authWitness.witness); } - async addPublicDataWrites(writes: PublicDataUpdateRequest[]) { + async addPublicDataWrites(writes: PublicDataWrite[]) { const db = await this.trees.getLatest(); - - // only insert the last write to a given slot - const uniqueWritesSet = new Map(); - for (const write of writes) { - uniqueWritesSet.set(write.leafSlot.toBigInt(), write); - } - const uniqueWrites = Array.from(uniqueWritesSet.values()); - await db.batchInsert( MerkleTreeId.PUBLIC_DATA_TREE, - uniqueWrites.map(w => new PublicDataTreeLeaf(w.leafSlot, w.newValue).toBuffer()), + writes.map(w => new PublicDataTreeLeaf(w.leafSlot, w.value).toBuffer()), 0, ); } async addSiloedNullifiers(siloedNullifiers: Fr[]) { const db = await this.trees.getLatest(); - await db.batchInsert( MerkleTreeId.NULLIFIER_TREE, siloedNullifiers.map(n => n.toBuffer()), @@ -254,11 +253,14 @@ export class TXE implements TypedOracle { await this.addSiloedNullifiers(siloedNullifiers); } - async addNoteHashes(contractAddress: AztecAddress, noteHashes: Fr[]) { + async addSiloedNoteHashes(siloedNoteHashes: Fr[]) { const db = await this.trees.getLatest(); - const siloedNoteHashes = noteHashes.map(noteHash => siloNoteHash(contractAddress, noteHash)); await db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, siloedNoteHashes); } + async addNoteHashes(contractAddress: AztecAddress, noteHashes: Fr[]) { + const siloedNoteHashes = noteHashes.map(noteHash => siloNoteHash(contractAddress, noteHash)); + await this.addSiloedNoteHashes(siloedNoteHashes); + } // TypedOracle @@ -655,39 +657,30 @@ export class TXE implements TypedOracle { return `${artifact.name}:${f.name}`; } - private async executePublicFunction(args: Fr[], callContext: CallContext) { - const execution = new PublicExecutionRequest(callContext, args); + private async executePublicFunction(args: Fr[], callContext: CallContext, isTeardown: boolean = false) { + const executionRequest = new PublicExecutionRequest(callContext, args); const db = await this.trees.getLatest(); - const previousBlockState = await this.#getTreesAt(this.blockNumber - 1); - - const combinedConstantData = CombinedConstantData.empty(); - combinedConstantData.globalVariables.chainId = this.chainId; - combinedConstantData.globalVariables.version = this.version; - combinedConstantData.globalVariables.blockNumber = new Fr(this.blockNumber); - combinedConstantData.historicalHeader.globalVariables.chainId = this.chainId; - combinedConstantData.historicalHeader.globalVariables.version = this.version; - combinedConstantData.historicalHeader.globalVariables.blockNumber = new Fr(this.blockNumber - 1); - combinedConstantData.historicalHeader.state = await db.getStateReference(); - combinedConstantData.historicalHeader.lastArchive.root = Fr.fromBuffer( - (await previousBlockState.getTreeInfo(MerkleTreeId.ARCHIVE)).root, - ); - combinedConstantData.txContext.chainId = this.chainId; - combinedConstantData.txContext.version = this.version; + const globalVariables = GlobalVariables.empty(); + globalVariables.chainId = this.chainId; + globalVariables.chainId = this.chainId; + globalVariables.version = this.version; + globalVariables.blockNumber = new Fr(this.blockNumber); + globalVariables.gasFees = GasFees.default(); - const simulator = new PublicExecutor( + const simulator = new PublicTxSimulator( + db, new TXEWorldStateDB(db, new TXEPublicContractDataSource(this)), new NoopTelemetryClient(), + globalVariables, + /*realAvmProvingRequests=*/ false, ); - const executionResult = await simulator.simulateIsolatedEnqueuedCall( - execution, - combinedConstantData.globalVariables, - /*allocatedGas*/ new Gas(DEFAULT_GAS_LIMIT, MAX_L2_GAS_PER_ENQUEUED_CALL), - /*transactionFee=*/ Fr.ONE, - /*startSideEffectCounter=*/ this.sideEffectCounter, - ); - return Promise.resolve(executionResult); + + const tx = this.createTxForPublicCall(executionRequest, isTeardown); + + const result = await simulator.simulate(tx); + return Promise.resolve(result); } async enqueuePublicFunctionCall( @@ -696,6 +689,7 @@ export class TXE implements TypedOracle { argsHash: Fr, _sideEffectCounter: number, isStaticCall: boolean, + isTeardown = false, ): Promise { // Store and modify env const currentContractAddress = this.contractAddress; @@ -715,10 +709,10 @@ export class TXE implements TypedOracle { const args = [this.functionSelector.toField(), ...this.packedValuesCache.unpack(argsHash)]; const newArgsHash = this.packedValuesCache.pack(args); - const executionResult = await this.executePublicFunction(args, callContext); + const executionResult = await this.executePublicFunction(args, callContext, isTeardown); // Poor man's revert handling - if (executionResult.reverted) { + if (!executionResult.revertCode.isOK()) { if (executionResult.revertReason && executionResult.revertReason instanceof SimulationError) { await enrichPublicSimulationError( executionResult.revertReason, @@ -733,13 +727,13 @@ export class TXE implements TypedOracle { } // Apply side effects - this.sideEffectCounter = executionResult.endSideEffectCounter.toNumber(); - await this.addPublicDataWrites(executionResult.sideEffects.publicDataWrites); - await this.addNoteHashes( - targetContractAddress, - executionResult.sideEffects.noteHashes.map(noteHash => noteHash.value), - ); - await this.addSiloedNullifiers(executionResult.sideEffects.nullifiers.map(nullifier => nullifier.value)); + const sideEffects = executionResult.avmProvingRequest.inputs.output.accumulatedData; + const publicDataWrites = sideEffects.publicDataWrites.filter(s => !s.isEmpty()); + const noteHashes = sideEffects.noteHashes.filter(s => !s.isEmpty()); + const nullifiers = sideEffects.nullifiers.filter(s => !s.isEmpty()); + await this.addPublicDataWrites(publicDataWrites); + await this.addSiloedNoteHashes(noteHashes); + await this.addSiloedNullifiers(nullifiers); this.setContractAddress(currentContractAddress); this.setMsgSender(currentMessageSender); @@ -763,6 +757,7 @@ export class TXE implements TypedOracle { argsHash, sideEffectCounter, isStaticCall, + /*isTeardown=*/ true, ); } @@ -811,11 +806,7 @@ export class TXE implements TypedOracle { // AVM oracles - async avmOpcodeCall( - targetContractAddress: AztecAddress, - args: Fr[], - isStaticCall: boolean, - ): Promise { + async avmOpcodeCall(targetContractAddress: AztecAddress, args: Fr[], isStaticCall: boolean): Promise { // Store and modify env const currentContractAddress = this.contractAddress; const currentMessageSender = this.msgSender; @@ -831,17 +822,17 @@ export class TXE implements TypedOracle { const executionResult = await this.executePublicFunction(args, callContext); // Save return/revert data for later. - this.nestedCallReturndata = executionResult.returnValues; + this.nestedCallReturndata = executionResult.processedPhases[0]!.returnValues[0].values!; // Apply side effects - if (!executionResult.reverted) { - this.sideEffectCounter = executionResult.endSideEffectCounter.toNumber(); - await this.addPublicDataWrites(executionResult.sideEffects.publicDataWrites); - await this.addNoteHashes( - targetContractAddress, - executionResult.sideEffects.noteHashes.map(noteHash => noteHash.value), - ); - await this.addSiloedNullifiers(executionResult.sideEffects.nullifiers.map(nullifier => nullifier.value)); + if (executionResult.revertCode.isOK()) { + const sideEffects = executionResult.avmProvingRequest.inputs.output.accumulatedData; + const publicDataWrites = sideEffects.publicDataWrites.filter(s => !s.isEmpty()); + const noteHashes = sideEffects.noteHashes.filter(s => !s.isEmpty()); + const nullifiers = sideEffects.nullifiers.filter(s => !s.isEmpty()); + await this.addPublicDataWrites(publicDataWrites); + await this.addSiloedNoteHashes(noteHashes); + await this.addSiloedNullifiers(nullifiers); } this.setContractAddress(currentContractAddress); @@ -896,4 +887,44 @@ export class TXE implements TypedOracle { return preimage.value; } + + /** + * Craft a carrier transaction for a public call. + */ + private createTxForPublicCall(executionRequest: PublicExecutionRequest, teardown: boolean): Tx { + const callRequest = executionRequest.toCallRequest(); + // use max limits + const gasLimits = new Gas(DEFAULT_GAS_LIMIT, MAX_L2_GAS_PER_ENQUEUED_CALL); + + const forPublic = PartialPrivateTailPublicInputsForPublic.empty(); + // TODO(#9269): Remove this fake nullifier method as we move away from 1st nullifier as hash. + forPublic.nonRevertibleAccumulatedData.nullifiers[0] = Fr.random(); // fake tx nullifier + if (teardown) { + forPublic.publicTeardownCallRequest = callRequest; + } else { + forPublic.revertibleAccumulatedData.publicCallRequests[0] = callRequest; + } + + // When setting up a teardown call, we tell it that + // private execution "used" Gas(1, 1) so it can compute a tx fee. + const gasUsedByPrivate = teardown ? new Gas(1, 1) : Gas.empty(); + const teardownGasLimits = teardown ? gasLimits : Gas.empty(); + const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty()); + const txContext = new TxContext(Fr.zero(), Fr.zero(), gasSettings); + const constantData = new TxConstantData(Header.empty(), txContext, Fr.zero(), Fr.zero()); + + const txData = new PrivateKernelTailCircuitPublicInputs( + constantData, + RollupValidationRequests.empty(), + /*gasUsed=*/ gasUsedByPrivate, + AztecAddress.zero(), + forPublic, + ); + const tx = teardown ? Tx.newWithTxData(txData, executionRequest) : Tx.newWithTxData(txData); + if (!teardown) { + tx.enqueuedPublicFunctionCalls[0] = executionRequest; + } + + return tx; + } } diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 75c7f5b5a20..76dab61ef05 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -222,7 +222,7 @@ export class TXEService { const parsedSelector = fromSingle(functionSelector); const extendedArgs = [parsedSelector, ...fromArray(args)]; const result = await (this.typedOracle as TXE).avmOpcodeCall(parsedAddress, extendedArgs, false); - if (!result.reverted) { + if (result.revertCode.isOK()) { throw new ExpectedFailureError('Public call did not revert'); } @@ -738,7 +738,7 @@ export class TXEService { ); // Poor man's revert handling - if (result.reverted) { + if (!result.revertCode.isOK()) { if (result.revertReason && result.revertReason instanceof SimulationError) { await enrichPublicSimulationError( result.revertReason, @@ -752,7 +752,7 @@ export class TXEService { } } - return toForeignCallResult([toSingle(new Fr(!result.reverted))]); + return toForeignCallResult([toSingle(new Fr(result.revertCode.isOK()))]); } async avmOpcodeStaticCall( @@ -768,7 +768,7 @@ export class TXEService { ); // Poor man's revert handling - if (result.reverted) { + if (!result.revertCode.isOK()) { if (result.revertReason && result.revertReason instanceof SimulationError) { await enrichPublicSimulationError( result.revertReason, @@ -782,6 +782,6 @@ export class TXEService { } } - return toForeignCallResult([toSingle(new Fr(!result.reverted))]); + return toForeignCallResult([toSingle(new Fr(result.revertCode.isOK()))]); } }