Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
bytecode_manager.cpp
Go to the documentation of this file.
2
3#include <cassert>
4#include <optional>
5
13
14namespace bb::avm2::simulation {
15
43{
44 BB_BENCH_NAME("TxBytecodeManager::get_bytecode");
45 // Use shared ContractInstanceManager for contract instance retrieval and validation
46 // This handles nullifier checks, address derivation, and update validation
47 auto tree_states = merkle_db.get_tree_state();
49 // Emits ContractInstanceRetrievalEvent, see #[CONTRACT_INSTANCE_RETRIEVAL] in bc_retrieval.pil.
51
52 if (!maybe_instance.has_value()) {
53 // Emits BytecodeRetrievalEvent with contract instance not found error
54 retrieval_events.emit({
55 .bytecode_id = FF(0), // Use default ID for error cases
56 .address = address,
57 .current_class_id = FF(0), // Use default ID for error cases
58 .nullifier_tree_root = tree_states.nullifier_tree.tree.root,
59 .public_data_tree_root = tree_states.public_data_tree.tree.root,
60 .retrieved_bytecodes_snapshot_before = before_snapshot,
61 .retrieved_bytecodes_snapshot_after = before_snapshot,
63 });
64 vinfo("Contract ", field_to_string(address), " is not deployed!");
65 throw BytecodeRetrievalError("Contract " + field_to_string(address) + " is not deployed");
66 }
67
68 ContractInstance instance = maybe_instance.value();
69 ContractClassId current_class_id = instance.current_contract_class_id;
70
71 // Emits RetrievedBytecodesTreeCheckEvent with write == false, see #[IS_NEW_CLASS_CHECK] in bc_retrieval.pil.
73
74 uint32_t retrieved_bytecodes_count = retrieved_bytecodes_tree_check.size();
75
76 if (is_new_class && retrieved_bytecodes_count >= MAX_PUBLIC_CALLS_TO_UNIQUE_CONTRACT_CLASS_IDS) {
77 // Emits BytecodeRetrievalEvent with too many bytecodes error
78 retrieval_events.emit({
79 .bytecode_id = FF(0), // Use default ID for error cases
80 .address = address,
81 .current_class_id = current_class_id,
82 .nullifier_tree_root = tree_states.nullifier_tree.tree.root,
83 .public_data_tree_root = tree_states.public_data_tree.tree.root,
84 .retrieved_bytecodes_snapshot_before = before_snapshot,
85 .retrieved_bytecodes_snapshot_after = before_snapshot,
86 .is_new_class = is_new_class,
88 });
89 throw BytecodeRetrievalError("Can't retrieve more than " +
91 " bytecodes per tx");
92 }
93
94 // Emits RetrievedBytecodesTreeCheckEvent with write == true, see #[RETRIEVED_BYTECODES_INSERTION] in
95 // bc_retrieval.pil.
98
99 // Contract class retrieval and class ID validation
100
101 // Emits ClassIdDerivationEvent if the class exists, see #[CLASS_ID_DERIVATION] in bc_retrieval.pil. Note
102 // that this conditional emission works because if the class does not exist, we throw and do not process retrieval.
104 // Note: we don't need to silo and check the class id because the deployer contract guarantees
105 // that if a contract instance exists, the class has been registered.
106 BB_ASSERT(maybe_klass.has_value(), "Contract class not found");
107 auto& klass = maybe_klass.value(); // WARNING: this class has the whole bytecode.
108
109 // Bytecode hashing (bc_hashing.pil) and decomposition (bc_decomposition.pil)
110
112 // If we reach this point, class ID and instance both exist which means bytecode commitment must exist.
113 BB_ASSERT(maybe_bytecode_commitment.has_value(), "Bytecode commitment not found");
114 BytecodeId bytecode_id = maybe_bytecode_commitment.value();
115 debug("Bytecode for ", address, " successfully retrieved!");
116
117 retrieval_events.emit({
118 .bytecode_id = bytecode_id,
119 .address = address,
120 .current_class_id = current_class_id,
121 .contract_class = klass,
122 .nullifier_tree_root = tree_states.nullifier_tree.tree.root,
123 .public_data_tree_root = tree_states.public_data_tree.tree.root,
124 .retrieved_bytecodes_snapshot_before = before_snapshot,
125 .retrieved_bytecodes_snapshot_after = snapshot_after,
126 .is_new_class = is_new_class,
127 });
128
129 // Check if we've already processed this bytecode by deduplicating by bytecode_id (=commitment). If so, don't do
130 // hashing and decomposition again!
131 if (bytecodes.contains(bytecode_id)) {
132 // Already processed this bytecode - just return
133 return bytecode_id;
134 }
135
136 // First time seeing this bytecode - perform hashing and decomposition.
137 // Emits BytecodeHashingEvent and corresponding Poseidon2HashEvent and Poseidon2PermutationEvent(s).
138 bytecode_hasher.assert_public_bytecode_commitment(bytecode_id, klass.packed_bytecode);
139
140 // We convert the bytecode to a shared_ptr because it will be shared by some events.
141 auto shared_bytecode = std::make_shared<std::vector<uint8_t>>(std::move(klass.packed_bytecode));
142 // Emits BytecodeDecompositionEvent.
143 decomposition_events.emit({ .bytecode_id = bytecode_id, .bytecode = shared_bytecode });
144
145 // We now save the bytecode against its id so that we don't repeat this process.
146 bytecodes.emplace(bytecode_id, std::move(shared_bytecode));
147
148 return bytecode_id;
149}
150
164{
165 return read_instruction(bytecode_id, get_bytecode_data(bytecode_id), pc);
166}
167
193 std::shared_ptr<std::vector<uint8_t>> bytecode_ptr,
194 PC pc)
195{
196 BB_BENCH_NAME("TxBytecodeManager::read_instruction");
197
198 const auto& bytecode = *bytecode_ptr;
199
200 // Keep full error for exception message, but only store enum in event.
202 // Initialise instruction.
204
205 try {
207
208 // If the following code is executed, no error was thrown in deserialize_instruction().
209 if (!check_tag(instruction)) {
211 };
212 } catch (const InstrDeserializationError& error) {
213 // Assign the error. Note that we do not assign any part of the instruction on failure (which may exist for some
214 // errors). This matches circuit behaviour (see #[OP1..7_BYTES_DECOMPOSITION] relations).
215 deserialization_error = error;
216 }
217
218 // We are showing whether bytecode_size > pc or not. If there is no fetching error,
219 // we always have bytecode_size > pc.
220 const auto bytecode_size = bytecode.size();
221 const uint128_t pc_diff = bytecode_size > pc ? bytecode_size - pc - 1 : pc - bytecode_size;
222 // Emits RangeCheckEvent, see #[INSTR_ABS_DIFF_POSITIVE] in instr_fetching.pil.
223 range_check.assert_range(pc_diff, AVM_PC_SIZE_IN_BITS);
224
225 // Emits InstructionFetchingEvent, which will be deduplicated internally (see DeduplicatingEventEmitter used in
226 // simulate_for_witgen).
227 fetching_events.emit({ .bytecode_id = bytecode_id,
228 .pc = pc,
229 .instruction = instruction,
230 .bytecode = std::move(bytecode_ptr),
231 .error = deserialization_error.has_value() ? std::make_optional(deserialization_error->type)
232 : std::nullopt });
233
234 // Communicate error to the caller.
235 if (deserialization_error.has_value()) {
236 std::string error_msg = format("Instruction fetching error");
237 if (deserialization_error.value().message.has_value()) {
238 error_msg = format(error_msg, ": ", deserialization_error.value().message.value());
239 }
240 throw InstructionFetchingError(error_msg);
241 }
242
243 return instruction;
244}
245
247{
248 auto it = bytecodes.find(bytecode_id);
249 BB_ASSERT(it != bytecodes.end(), "Bytecode not found for the given bytecode_id");
250 return it->second;
251}
252
253} // namespace bb::avm2::simulation
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
std::shared_ptr< Napi::ThreadSafeFunction > instance
std::shared_ptr< Napi::ThreadSafeFunction > bytecode
#define MAX_PUBLIC_CALLS_TO_UNIQUE_CONTRACT_CLASS_IDS
#define AVM_PC_SIZE_IN_BITS
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:264
virtual void assert_public_bytecode_commitment(const BytecodeId &bytecode_id, const std::vector< uint8_t > &bytecode)=0
virtual std::optional< FF > get_bytecode_commitment(const ContractClassId &class_id) const =0
virtual std::optional< ContractClass > get_contract_class(const ContractClassId &class_id) const =0
virtual std::optional< ContractInstance > get_contract_instance(const FF &contract_address)=0
Retrieve and validate a contract instance.
virtual TreeStates get_tree_state() const =0
virtual void insert(const FF &class_id)=0
virtual bool contains(const FF &class_id)=0
virtual AppendOnlyTreeSnapshot get_snapshot() const =0
HighLevelMerkleDBInterface & merkle_db
Instruction read_instruction(const BytecodeId &bytecode_id, PC pc) override
Reads and deserializes the instruction given by the pair [ bytecode_id, pc ]. Corresponds to instr_fe...
EventEmitterInterface< BytecodeDecompositionEvent > & decomposition_events
RetrievedBytecodesTreeCheckInterface & retrieved_bytecodes_tree_check
EventEmitterInterface< BytecodeRetrievalEvent > & retrieval_events
EventEmitterInterface< InstructionFetchingEvent > & fetching_events
unordered_flat_map< BytecodeId, std::shared_ptr< std::vector< uint8_t > > > bytecodes
std::shared_ptr< std::vector< uint8_t > > get_bytecode_data(const BytecodeId &bytecode_id) override
BytecodeHashingInterface & bytecode_hasher
ContractInstanceManagerInterface & contract_instance_manager
BytecodeId get_bytecode(const AztecAddress &address) override
Retrieves and validates bytecode from the TxBytecodeManager's ContractDBInterface and emits a Bytecod...
std::string format(Args... args)
Definition log.hpp:23
#define vinfo(...)
Definition log.hpp:94
#define debug(...)
Definition log.hpp:99
Instruction instruction
AVM range check gadget for witness generation.
bool check_tag(const Instruction &instruction)
Checks whether the tag operand of an instruction is a valid MemoryTag. Called by bytecode managers du...
Instruction deserialize_instruction(std::span< const uint8_t > bytecode, size_t pos)
Attempts to deserialize the instruction at position pos in bytecode. Called by bytecode managers duri...
uint32_t PC
AvmFlavorSettings::FF FF
Definition field.hpp:10
FF ContractClassId
std::string field_to_string(const FF &ff)
Definition stringify.cpp:5
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
unsigned __int128 uint128_t
Definition serialize.hpp:45
FF current_class_id