From f54bdb2d3389a337076dc4026f6653eb2aafcfbf Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Mon, 8 Dec 2025 16:48:13 +0000 Subject: [PATCH 1/3] feat(avm): dont hash bytecode in fast simulation --- .../standalone/pure_bytecode_manager.cpp | 19 +++---- .../standalone/pure_bytecode_manager.hpp | 2 + .../barretenberg/vm2/simulation_helper.cpp | 50 ++++++++++++++++--- .../avm_proving_tests/avm_proving_tester.ts | 2 +- .../cpp_vs_ts_public_tx_simulator.ts | 7 --- 5 files changed, 51 insertions(+), 29 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.cpp index e19f9d8208b6..8ed92952cd1b 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.cpp @@ -69,19 +69,12 @@ BytecodeId PureTxBytecodeManager::get_bytecode(const AztecAddress& address) auto& klass = maybe_klass.value(); debug("Bytecode for ", address, " successfully retrieved!"); - // TODO(dbanks12): in TS, the PublicContractsDB will hash the bytecode if it has never been hashed there before. - // After that, it caches it. It should only happen once per contract class, but when we are making a callback - // to the TS cache to hash the bytecode there, it might be unnecessarily slow, in which case we could do the same - // hashing and caching here in C++ and avoid callbacks to TS. - std::optional maybe_bytecode_commitment = contract_db.get_bytecode_commitment(current_class_id); - // If we reach this point, class ID and instance both exist which means bytecode commitment must exist. - assert(maybe_bytecode_commitment.has_value()); - BytecodeId bytecode_id = maybe_bytecode_commitment.value(); - - // Check if we've already processed this bytecode. - if (bytecodes.contains(bytecode_id)) { - return bytecode_id; - } + // For fast simulation, we use the class_id as the bytecode_id instead of computing the + // expensive bytecode commitment hash. This is safe because class_id uniquely identifies + // the bytecode. The actual commitment is only needed for trace generation / witgen. + BytecodeId bytecode_id = current_class_id; + // Cache the mapping from class_id to bytecode_id (different class can have same bytecode). + class_id_to_bytecode_id[current_class_id] = bytecode_id; // We now save the bytecode so that we don't repeat this process. bytecodes[bytecode_id] = std::make_shared>(std::move(klass.packed_bytecode)); diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.hpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.hpp index 5b10d6f9be1a..605628510479 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.hpp @@ -42,6 +42,8 @@ class PureTxBytecodeManager : public TxBytecodeManagerInterface { unordered_flat_map>> bytecodes; unordered_flat_set retrieved_class_ids; + // Cache mapping from class_id to bytecode_id to avoid redundant contract DB calls + unordered_flat_map class_id_to_bytecode_id; using InstructionIdentifier = std::tuple; unordered_flat_map instruction_cache; }; diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp index 28cbfa20f081..0e47508663d8 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp @@ -12,6 +12,7 @@ #include "barretenberg/vm2/simulation/gadgets/emit_unencrypted_log.hpp" #include "barretenberg/vm2/simulation/interfaces/db.hpp" #include "barretenberg/vm2/simulation/interfaces/debug_log.hpp" +#include "barretenberg/vm2/simulation/interfaces/update_check.hpp" #include "barretenberg/vm2/simulation/lib/call_stack_metadata_collector.hpp" #include "barretenberg/vm2/simulation/lib/db_types.hpp" #include "barretenberg/vm2/simulation/lib/execution_id_manager.hpp" @@ -386,24 +387,57 @@ TxSimulationResult AvmSimulationHelper::simulate_fast(ContractDBInterface& raw_c tx.non_revertible_accumulated_data.nullifiers.at(0), base_merkle_db, side_effect_tracker); - // NoopUpdateCheck update_check; - // TODO(#18161): Note that if we need to gather hints here, we can't use the NoopUpdateCheck as it will skip - // collecting a required hint for a storage_read. Optionally use Noop if we don't need hints: - - UpdateCheck update_check(poseidon2, range_check, greater_than, merkle_db, update_check_emitter, global_variables); + std::unique_ptr update_check = [&]() -> std::unique_ptr { + if (config.collect_hints) { + // When collecting hints, we need to use UpdateCheck to collect required hints for storage_read. + return std::make_unique( + poseidon2, range_check, greater_than, merkle_db, update_check_emitter, global_variables); + } else { + return std::make_unique(); + } + }(); InstructionInfoDB instruction_info_db; ContractInstanceManager contract_instance_manager( - contract_db, merkle_db, update_check, field_gt, protocol_contracts, contract_instance_retrieval_emitter); + contract_db, merkle_db, *update_check, field_gt, protocol_contracts, contract_instance_retrieval_emitter); + + // These are needed for the TxBytecodeManager, but not for the PureTxBytecodeManager. + std::unique_ptr> bytecode_hashing_emitter; + std::unique_ptr bytecode_hasher; + std::unique_ptr> bytecode_retrieval_emitter; + std::unique_ptr> bytecode_decomposition_emitter; + std::unique_ptr> instruction_fetching_emitter; + std::unique_ptr tx_bytecode_manager = + [&]() -> std::unique_ptr { + if (config.collect_hints) { + // When collecting hints, we need to call the contract DB to get the bytecode commitment. + // The pure bytecode manager doesn't do this, so we use the gadget version. + bytecode_hashing_emitter = std::make_unique>(); + bytecode_hasher = std::make_unique(poseidon2, *bytecode_hashing_emitter); + bytecode_retrieval_emitter = std::make_unique>(); + bytecode_decomposition_emitter = std::make_unique>(); + instruction_fetching_emitter = std::make_unique>(); + return std::make_unique(contract_db, + merkle_db, + *bytecode_hasher, + range_check, + contract_instance_manager, + retrieved_bytecodes_tree_check, + *bytecode_retrieval_emitter, + *bytecode_decomposition_emitter, + *instruction_fetching_emitter); + } else { + return std::make_unique(contract_db, contract_instance_manager); + } + }(); - PureTxBytecodeManager bytecode_manager(contract_db, contract_instance_manager); PureExecutionComponentsProvider execution_components(greater_than, instruction_info_db); PureMemoryProvider memory_provider; CalldataHashingProvider calldata_hashing_provider(poseidon2, calldata_emitter); InternalCallStackManagerProvider internal_call_stack_manager_provider(internal_call_stack_emitter); - ContextProvider context_provider(bytecode_manager, + ContextProvider context_provider(*tx_bytecode_manager, memory_provider, calldata_hashing_provider, internal_call_stack_manager_provider, diff --git a/yarn-project/bb-prover/src/avm_proving_tests/avm_proving_tester.ts b/yarn-project/bb-prover/src/avm_proving_tests/avm_proving_tester.ts index 9526dc5aaf4f..41745a0bd799 100644 --- a/yarn-project/bb-prover/src/avm_proving_tests/avm_proving_tester.ts +++ b/yarn-project/bb-prover/src/avm_proving_tests/avm_proving_tester.ts @@ -94,7 +94,7 @@ const provingConfig: PublicSimulatorConfig = PublicSimulatorConfig.from({ collectCallMetadata: true, collectDebugLogs: false, collectHints: true, // Required for proving! - collectPublicInputs: true, + collectPublicInputs: true, // Required for proving! collectStatistics: false, }); diff --git a/yarn-project/simulator/src/public/public_tx_simulator/cpp_vs_ts_public_tx_simulator.ts b/yarn-project/simulator/src/public/public_tx_simulator/cpp_vs_ts_public_tx_simulator.ts index 8e325faebce7..597c59744785 100644 --- a/yarn-project/simulator/src/public/public_tx_simulator/cpp_vs_ts_public_tx_simulator.ts +++ b/yarn-project/simulator/src/public/public_tx_simulator/cpp_vs_ts_public_tx_simulator.ts @@ -1,5 +1,4 @@ import { type Logger, createLogger, logLevel } from '@aztec/foundation/log'; -import { writeTestData } from '@aztec/foundation/testing/files'; import { avmSimulate } from '@aztec/native'; import { ProtocolContractsList } from '@aztec/protocol-contracts'; import { @@ -126,12 +125,6 @@ export class CppVsTsPublicTxSimulator extends PublicTxSimulator implements Publi // Deserialize the msgpack result this.log.debug(`Deserializing C++ from buffer (size: ${resultBuffer.length})...`); const cppResultJSON: object = deserializeFromMessagePack(resultBuffer); - // Write testdata if AZTEC_WRITE_TESTDATA=1. - writeTestData( - `barretenberg/cpp/src/barretenberg/vm2/testing/tx_result_${txHash}.testdata.bin`, - resultBuffer, - /*raw=*/ true, - ); this.log.debug(`Deserializing C++ result to PublicTxResult...`); const cppResult = PublicTxResult.fromPlainObject(cppResultJSON); this.log.debug(`Done.`); From ef043723f7d8682767f6acadd4c9bac5d8fc541f Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Mon, 8 Dec 2025 17:13:35 +0000 Subject: [PATCH 2/3] remove nonsense map --- .../vm2/simulation/standalone/pure_bytecode_manager.cpp | 2 -- .../vm2/simulation/standalone/pure_bytecode_manager.hpp | 2 -- 2 files changed, 4 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.cpp index 8ed92952cd1b..7a47b6d04b3b 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.cpp @@ -73,8 +73,6 @@ BytecodeId PureTxBytecodeManager::get_bytecode(const AztecAddress& address) // expensive bytecode commitment hash. This is safe because class_id uniquely identifies // the bytecode. The actual commitment is only needed for trace generation / witgen. BytecodeId bytecode_id = current_class_id; - // Cache the mapping from class_id to bytecode_id (different class can have same bytecode). - class_id_to_bytecode_id[current_class_id] = bytecode_id; // We now save the bytecode so that we don't repeat this process. bytecodes[bytecode_id] = std::make_shared>(std::move(klass.packed_bytecode)); diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.hpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.hpp index 605628510479..5b10d6f9be1a 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.hpp @@ -42,8 +42,6 @@ class PureTxBytecodeManager : public TxBytecodeManagerInterface { unordered_flat_map>> bytecodes; unordered_flat_set retrieved_class_ids; - // Cache mapping from class_id to bytecode_id to avoid redundant contract DB calls - unordered_flat_map class_id_to_bytecode_id; using InstructionIdentifier = std::tuple; unordered_flat_map instruction_cache; }; From 73e32b2b96b6af2e0e3b360f9602266006f1a83f Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Mon, 8 Dec 2025 18:08:32 +0000 Subject: [PATCH 3/3] restore cache check --- .../vm2/simulation/standalone/pure_bytecode_manager.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.cpp index 7a47b6d04b3b..07405585ccec 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/pure_bytecode_manager.cpp @@ -74,6 +74,11 @@ BytecodeId PureTxBytecodeManager::get_bytecode(const AztecAddress& address) // the bytecode. The actual commitment is only needed for trace generation / witgen. BytecodeId bytecode_id = current_class_id; + // Check if we've already processed this bytecode (different class can have same bytecode). + if (bytecodes.contains(bytecode_id)) { + return bytecode_id; + } + // We now save the bytecode so that we don't repeat this process. bytecodes[bytecode_id] = std::make_shared>(std::move(klass.packed_bytecode)); return bytecode_id;