diff --git a/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp b/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp index fa8315c32965..d622356ae986 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp @@ -194,8 +194,36 @@ struct EnqueuedCallHint { MSGPACK_FIELDS(msgSender, contractAddress, calldata, isStaticCall); }; +struct AccumulatedData { + // TODO: add as needed. + std::vector noteHashes; + std::vector nullifiers; + + bool operator==(const AccumulatedData& other) const = default; + + MSGPACK_FIELDS(noteHashes, nullifiers); +}; + +// We are currently using this structure as the input to TX simulation. +// That's why I'm not calling it TxHint. We can reconsider if the inner types seem to dirty. +struct Tx { + AccumulatedData nonRevertibleAccumulatedData; + AccumulatedData revertibleAccumulatedData; + std::vector setupEnqueuedCalls; + std::vector appLogicEnqueuedCalls; + std::optional teardownEnqueuedCall; + + bool operator==(const Tx& other) const = default; + + MSGPACK_FIELDS(nonRevertibleAccumulatedData, + revertibleAccumulatedData, + setupEnqueuedCalls, + appLogicEnqueuedCalls, + teardownEnqueuedCall); +}; + struct ExecutionHints { - std::vector enqueuedCalls; + Tx tx; // Contracts. std::vector contractInstances; std::vector contractClasses; @@ -213,7 +241,7 @@ struct ExecutionHints { bool operator==(const ExecutionHints& other) const = default; - MSGPACK_FIELDS(enqueuedCalls, + MSGPACK_FIELDS(tx, contractInstances, contractClasses, bytecodeCommitments, diff --git a/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.testdata.bin b/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.testdata.bin index 2aafe7d48c4b..dd78ef3625fc 100644 Binary files a/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.testdata.bin and b/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.testdata.bin differ diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.cpp index 670979d1af8e..d20d77e36cde 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.cpp @@ -5,18 +5,43 @@ namespace bb::avm2::simulation { void TxExecution::simulate(const Tx& tx) { - // TODO: other inter-enqueued-call stuff will be done here. - for (const auto& call : tx.enqueued_calls) { - info("Executing enqueued call to ", call.contractAddress); + info("Simulating tx with ", + tx.setupEnqueuedCalls.size(), + " setup enqueued calls, ", + tx.appLogicEnqueuedCalls.size(), + " app logic enqueued calls, and ", + tx.teardownEnqueuedCall ? "1 teardown enqueued call" : "no teardown enqueued call"); + + // TODO: This method is currently wrong. We need to lift the context to this level. + + // Insert non-revertibles. + // TODO: We need a context at this level to be able to do the insertions. + + // Setup. + for (const auto& call : tx.setupEnqueuedCalls) { + info("[SETUP] Executing enqueued call to ", call.contractAddress); auto context = make_enqueued_context(call.contractAddress, call.msgSender, call.calldata, call.isStaticCall); - auto result = call_execution.execute(*context); - info("Enqueued call to ", - call.contractAddress, - " was a ", - result.success ? "success" : "failure", - " and it returned ", - context->get_last_rd_size(), - " elements."); + call_execution.execute(*context); + } + + // Insert revertibles. + // TODO: We need a context at this level to be able to do the insertions. + + // App logic. + for (const auto& call : tx.appLogicEnqueuedCalls) { + info("[APP_LOGIC] Executing enqueued call to ", call.contractAddress); + auto context = make_enqueued_context(call.contractAddress, call.msgSender, call.calldata, call.isStaticCall); + call_execution.execute(*context); + } + + // Teardown. + if (tx.teardownEnqueuedCall) { + info("[TEARDOWN] Executing enqueued call to ", tx.teardownEnqueuedCall->contractAddress); + auto context = make_enqueued_context(tx.teardownEnqueuedCall->contractAddress, + tx.teardownEnqueuedCall->msgSender, + tx.teardownEnqueuedCall->calldata, + tx.teardownEnqueuedCall->isStaticCall); + call_execution.execute(*context); } } diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.hpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.hpp index 31ca9b4deb80..7a3c8b9541a3 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.hpp @@ -5,11 +5,6 @@ namespace bb::avm2::simulation { -// Temporary. -struct Tx { - std::vector enqueued_calls; -}; - // In charge of executing a transaction. class TxExecution final { public: diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp index 455e405f0b90..f58dafb983e6 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp @@ -118,7 +118,7 @@ template EventsContainer AvmSimulationHelper::simulate_with_setting TxExecution tx_execution(execution); Sha256 sha256(sha256_compression_emitter); - tx_execution.simulate({ .enqueued_calls = inputs.hints.enqueuedCalls }); + tx_execution.simulate(inputs.hints.tx); return { execution_emitter.dump_events(), alu_emitter.dump_events(), diff --git a/barretenberg/cpp/src/barretenberg/vm2/tracegen/lib/lookup_builder.hpp b/barretenberg/cpp/src/barretenberg/vm2/tracegen/lib/lookup_builder.hpp index ab093588866e..53d4905b14b1 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/tracegen/lib/lookup_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/tracegen/lib/lookup_builder.hpp @@ -6,6 +6,7 @@ #include #include +#include "barretenberg/common/utils.hpp" #include "barretenberg/vm2/common/field.hpp" #include "barretenberg/vm2/common/map.hpp" #include "barretenberg/vm2/generated/columns.hpp" @@ -136,11 +137,8 @@ template class LookupIntoDynamicTableSequential : publ template struct std::hash> { std::size_t operator()(const std::array& arr) const noexcept { - std::size_t hash = 0; - for (const auto& elem : arr) { - hash = std::rotl(hash, 1); - hash ^= std::hash{}(elem); - } - return hash; + return [&arr](std::index_sequence) { + return bb::utils::hash_as_tuple(arr[Is]...); + }(std::make_index_sequence{}); } }; diff --git a/yarn-project/simulator/src/public/public_tx_simulator/public_tx_context.ts b/yarn-project/simulator/src/public/public_tx_simulator/public_tx_context.ts index d53e768721aa..2dbf1556cb72 100644 --- a/yarn-project/simulator/src/public/public_tx_simulator/public_tx_context.ts +++ b/yarn-project/simulator/src/public/public_tx_simulator/public_tx_context.ts @@ -10,7 +10,13 @@ import { padArrayEnd } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/fields'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { assertLength } from '@aztec/foundation/serialize'; -import { type AvmCircuitPublicInputs, AvmExecutionHints, PublicDataWrite, RevertCode } from '@aztec/stdlib/avm'; +import { + type AvmCircuitPublicInputs, + AvmExecutionHints, + AvmTxHint, + PublicDataWrite, + RevertCode, +} from '@aztec/stdlib/avm'; import type { AztecAddress } from '@aztec/stdlib/aztec-address'; import type { SimulationError } from '@aztec/stdlib/errors'; import { computeTransactionFee } from '@aztec/stdlib/fees'; @@ -105,7 +111,7 @@ export class PublicTxContext { const firstNullifier = nonRevertibleAccumulatedDataFromPrivate.nullifiers[0]; // We wrap the DB to collect AVM hints. - const hints = new AvmExecutionHints(); + const hints = new AvmExecutionHints(AvmTxHint.fromTx(tx)); const hintingContractsDB = new HintingPublicContractsDB(contractsDB, hints); const hintingTreesDB = new HintingPublicTreesDB(treesDB, hints); diff --git a/yarn-project/simulator/src/public/public_tx_simulator/public_tx_simulator.ts b/yarn-project/simulator/src/public/public_tx_simulator/public_tx_simulator.ts index c8a070f05a72..e14a2d235912 100644 --- a/yarn-project/simulator/src/public/public_tx_simulator/public_tx_simulator.ts +++ b/yarn-project/simulator/src/public/public_tx_simulator/public_tx_simulator.ts @@ -5,7 +5,6 @@ import { computeFeePayerBalanceStorageSlot } from '@aztec/protocol-contracts/fee import { AvmCircuitInputs, AvmCircuitPublicInputs, - AvmEnqueuedCallHint, AvmExecutionHints, type AvmProvingRequest, type RevertCode, @@ -72,6 +71,7 @@ export class PublicTxSimulator { const txHash = await this.computeTxHash(tx); this.log.debug(`Simulating ${tx.publicFunctionCalldata.length} public calls for tx ${txHash}`, { txHash }); + const context = await PublicTxContext.create( this.treesDB, this.contractsDB, @@ -265,18 +265,7 @@ export class PublicTxSimulator { const allocatedGas = context.getGasLeftAtPhase(phase); - // The reason we need enqueued hints at all (and cannot just use the public inputs) is - // because they don't have the actual calldata, just the hash of it. - // If/when we pass the whole TX to C++, we can remove this class of hints. stateManager.traceEnqueuedCall(callRequest.request); - context.hints.enqueuedCalls.push( - new AvmEnqueuedCallHint( - callRequest.request.msgSender, - contractAddress, - callRequest.calldata, - callRequest.request.isStaticCall, - ), - ); const result = await this.simulateEnqueuedCallInternal( context.state.getActiveStateManager(), diff --git a/yarn-project/stdlib/src/avm/avm.ts b/yarn-project/stdlib/src/avm/avm.ts index cc54d03c8992..6bcf0c808c41 100644 --- a/yarn-project/stdlib/src/avm/avm.ts +++ b/yarn-project/stdlib/src/avm/avm.ts @@ -10,6 +10,7 @@ import { AppendOnlyTreeSnapshot } from '../trees/append_only_tree_snapshot.js'; import { MerkleTreeId } from '../trees/merkle_tree_id.js'; import { NullifierLeafPreimage } from '../trees/nullifier_leaf.js'; import { PublicDataTreeLeafPreimage } from '../trees/public_data_leaf.js'; +import type { Tx } from '../tx/index.js'; import { AvmCircuitPublicInputs } from './avm_circuit_public_inputs.js'; import { serializeWithMessagePack } from './message_pack.js'; @@ -287,9 +288,92 @@ export class AvmEnqueuedCallHint { } } +export class AvmTxHint { + constructor( + public readonly nonRevertibleAccumulatedData: { + noteHashes: Fr[]; + nullifiers: Fr[]; + // TODO: add as needed. + }, + public readonly revertibleAccumulatedData: { + noteHashes: Fr[]; + nullifiers: Fr[]; + // TODO: add as needed. + }, + public readonly setupEnqueuedCalls: AvmEnqueuedCallHint[], + public readonly appLogicEnqueuedCalls: AvmEnqueuedCallHint[], + // We need this to be null and not undefined because that's what + // MessagePack expects for an std::optional. + public readonly teardownEnqueuedCall: AvmEnqueuedCallHint | null, + ) {} + + static fromTx(tx: Tx): AvmTxHint { + const setupCallRequests = tx.getNonRevertiblePublicCallRequestsWithCalldata(); + const appLogicCallRequests = tx.getRevertiblePublicCallRequestsWithCalldata(); + const teardownCallRequest = tx.getTeardownPublicCallRequestWithCalldata(); + + return new AvmTxHint( + { + noteHashes: tx.data.forPublic!.nonRevertibleAccumulatedData.noteHashes.filter(x => !x.isZero()), + nullifiers: tx.data.forPublic!.nonRevertibleAccumulatedData.nullifiers.filter(x => !x.isZero()), + }, + { + noteHashes: tx.data.forPublic!.revertibleAccumulatedData.noteHashes.filter(x => !x.isZero()), + nullifiers: tx.data.forPublic!.revertibleAccumulatedData.nullifiers.filter(x => !x.isZero()), + }, + setupCallRequests.map( + call => + new AvmEnqueuedCallHint( + call.request.msgSender, + call.request.contractAddress, + call.calldata, + call.request.isStaticCall, + ), + ), + appLogicCallRequests.map( + call => + new AvmEnqueuedCallHint( + call.request.msgSender, + call.request.contractAddress, + call.calldata, + call.request.isStaticCall, + ), + ), + teardownCallRequest + ? new AvmEnqueuedCallHint( + teardownCallRequest.request.msgSender, + teardownCallRequest.request.contractAddress, + teardownCallRequest.calldata, + teardownCallRequest.request.isStaticCall, + ) + : null, + ); + } + + static empty() { + return new AvmTxHint({ noteHashes: [], nullifiers: [] }, { noteHashes: [], nullifiers: [] }, [], [], null); + } + + static get schema() { + return z.object({ + nonRevertibleAccumulatedData: z.object({ + noteHashes: schemas.Fr.array(), + nullifiers: schemas.Fr.array(), + }), + revertibleAccumulatedData: z.object({ + noteHashes: schemas.Fr.array(), + nullifiers: schemas.Fr.array(), + }), + setupEnqueuedCalls: AvmEnqueuedCallHint.schema.array(), + appLogicEnqueuedCalls: AvmEnqueuedCallHint.schema.array(), + teardownEnqueuedCall: AvmEnqueuedCallHint.schema.nullable(), + }); + } +} + export class AvmExecutionHints { constructor( - public readonly enqueuedCalls: AvmEnqueuedCallHint[] = [], + public tx: AvmTxHint, // Contract hints. public readonly contractInstances: AvmContractInstanceHint[] = [], public readonly contractClasses: AvmContractClassHint[] = [], @@ -305,13 +389,13 @@ export class AvmExecutionHints { ) {} static empty() { - return new AvmExecutionHints(); + return new AvmExecutionHints(AvmTxHint.empty()); } static get schema() { return z .object({ - enqueuedCalls: AvmEnqueuedCallHint.schema.array(), + tx: AvmTxHint.schema, contractInstances: AvmContractInstanceHint.schema.array(), contractClasses: AvmContractClassHint.schema.array(), bytecodeCommitments: AvmBytecodeCommitmentHint.schema.array(), @@ -325,7 +409,7 @@ export class AvmExecutionHints { }) .transform( ({ - enqueuedCalls, + tx, contractInstances, contractClasses, bytecodeCommitments, @@ -338,7 +422,7 @@ export class AvmExecutionHints { sequentialInsertHintsNullifierTree, }) => new AvmExecutionHints( - enqueuedCalls, + tx, contractInstances, contractClasses, bytecodeCommitments, diff --git a/yarn-project/stdlib/src/tests/factories.ts b/yarn-project/stdlib/src/tests/factories.ts index 06c1eab9aa47..3ea4d6b57b0b 100644 --- a/yarn-project/stdlib/src/tests/factories.ts +++ b/yarn-project/stdlib/src/tests/factories.ts @@ -69,6 +69,7 @@ import { AvmGetSiblingPathHint, AvmSequentialInsertHintNullifierTree, AvmSequentialInsertHintPublicDataTree, + AvmTxHint, RevertCode, } from '../avm/index.js'; import { PublicDataHint } from '../avm/public_data_hint.js'; @@ -1427,6 +1428,22 @@ export function makeAvmEnqueuedCallHint(seed = 0): AvmEnqueuedCallHint { ); } +export function makeAvmTxHint(seed = 0): AvmTxHint { + return new AvmTxHint( + { + noteHashes: makeArray((seed % 20) + 4, i => new Fr(i), seed + 0x1000), + nullifiers: makeArray((seed % 20) + 4, i => new Fr(i), seed + 0x2000), + }, + { + noteHashes: makeArray((seed % 20) + 4, i => new Fr(i), seed + 0x3000), + nullifiers: makeArray((seed % 20) + 4, i => new Fr(i), seed + 0x4000), + }, + makeArray((seed % 20) + 4, i => makeAvmEnqueuedCallHint(i), seed + 0x5000), // setupEnqueuedCalls + makeArray((seed % 20) + 4, i => makeAvmEnqueuedCallHint(i), seed + 0x6000), // appLogicEnqueuedCalls + makeAvmEnqueuedCallHint(seed + 0x7000), // teardownEnqueuedCall + ); +} + /** * Creates arbitrary AvmExecutionHints. * @param seed - The seed to use for generating the hints. @@ -1441,7 +1458,7 @@ export async function makeAvmExecutionHints( const baseLength = lengthOffset + (seed % lengthSeedMod); const fields = { - enqueuedCalls: makeArray(baseLength, makeAvmEnqueuedCallHint, seed + 0x4100), + tx: makeAvmTxHint(seed + 0x4100), contractInstances: makeArray(baseLength + 2, makeAvmContractInstanceHint, seed + 0x4700), contractClasses: makeArray(baseLength + 5, makeAvmContractClassHint, seed + 0x4900), bytecodeCommitments: await makeArrayAsync(baseLength + 5, makeAvmBytecodeCommitmentHint, seed + 0x4900), @@ -1468,7 +1485,7 @@ export async function makeAvmExecutionHints( }; return new AvmExecutionHints( - fields.enqueuedCalls, + fields.tx, fields.contractInstances, fields.contractClasses, fields.bytecodeCommitments,