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
12
13namespace bb::avm2::simulation {
14
16{
17 // Use shared ContractInstanceManager for contract instance retrieval and validation
18 // This handles nullifier checks, address derivation, and update validation
19 auto maybe_instance = contract_instance_manager.get_contract_instance(address);
20
21 auto tree_states = merkle_db.get_tree_state();
22 if (!maybe_instance.has_value()) {
23 // Contract instance not found - emit error event and throw
24 retrieval_events.emit({
25 .bytecode_id = FF(0), // Use default ID for error case
26 .address = address,
27 .nullifier_root = tree_states.nullifierTree.tree.root,
28 .public_data_tree_root = tree_states.publicDataTree.tree.root,
29 .error = true,
30 });
31 vinfo("Contract ", field_to_string(address), " is not deployed!");
32 throw BytecodeNotFoundError("Contract " + field_to_string(address) + " is not deployed");
33 }
34
35 ContractClassId current_class_id = maybe_instance.value().current_class_id;
36
37 // Contract class retrieval and class ID validation
39 // Note: we don't need to silo and check the class id because the deployer contract guarrantees
40 // that if a contract instance exists, the class has been registered.
41 assert(maybe_klass.has_value());
42 auto& klass = maybe_klass.value();
43 info("Bytecode for ", address, " successfully retrieved!");
44
45 // Bytecode hashing and decomposition, deduplicated by bytecode_id (commitment)
46 BytecodeId bytecode_id = klass.public_bytecode_commitment;
47
48 // Check if we've already processed this bytecode. If so, don't do hashing and decomposition again!
49 if (bytecodes.contains(bytecode_id)) {
50 // Already processed this bytecode - just emit retrieval event and return
51 retrieval_events.emit({
52 .bytecode_id = bytecode_id,
53 .address = address,
54 .current_class_id = current_class_id,
55 .contract_class = klass,
56 .nullifier_root = tree_states.nullifierTree.tree.root,
57 .public_data_tree_root = tree_states.publicDataTree.tree.root,
58 });
59 return bytecode_id;
60 }
61
62 // First time seeing this bytecode - do hashing and decomposition
63 FF computed_commitment = bytecode_hasher.compute_public_bytecode_commitment(bytecode_id, klass.packed_bytecode);
64 (void)computed_commitment; // Avoid GCC unused parameter warning when asserts are disabled.
65 assert(computed_commitment == klass.public_bytecode_commitment);
66
67 // We convert the bytecode to a shared_ptr because it will be shared by some events.
68 auto shared_bytecode = std::make_shared<std::vector<uint8_t>>(std::move(klass.packed_bytecode));
69 decomposition_events.emit({ .bytecode_id = bytecode_id, .bytecode = shared_bytecode });
70
71 // We now save the bytecode so that we don't repeat this process.
72 bytecodes.emplace(bytecode_id, std::move(shared_bytecode));
73
74 retrieval_events.emit({
75 .bytecode_id = bytecode_id,
76 .address = address,
77 .current_class_id = current_class_id,
78 .contract_class = klass, // WARNING: this class has the whole bytecode.
79 .nullifier_root = tree_states.nullifierTree.tree.root,
80 .public_data_tree_root = tree_states.publicDataTree.tree.root,
81 });
82
83 return bytecode_id;
84}
85
87{
88 // We'll be filling in the event as we progress.
89 InstructionFetchingEvent instr_fetching_event;
90
91 auto it = bytecodes.find(bytecode_id);
92 // This should never happen. It is supposed to be checked in execution.
93 assert(it != bytecodes.end());
94
95 instr_fetching_event.bytecode_id = bytecode_id;
96 instr_fetching_event.pc = pc;
97
98 auto bytecode_ptr = it->second;
99 instr_fetching_event.bytecode = bytecode_ptr;
100
101 const auto& bytecode = *bytecode_ptr;
102
103 try {
104 instr_fetching_event.instruction = deserialize_instruction(bytecode, pc);
105
106 // If the following code is executed, no error was thrown in deserialize_instruction().
107 if (!check_tag(instr_fetching_event.instruction)) {
109 };
110 } catch (const InstrDeserializationError& error) {
111 instr_fetching_event.error = error;
112 }
113
114 // FIXME: remove this once all execution opcodes are supported.
115 if (!instr_fetching_event.error.has_value() &&
116 !EXEC_INSTRUCTION_SPEC.contains(instr_fetching_event.instruction.get_exec_opcode())) {
117 vinfo("Invalid execution opcode: ", instr_fetching_event.instruction.get_exec_opcode(), " at pc: ", pc);
119 }
120
121 // We are showing whether bytecode_size > pc or not. If there is no fetching error,
122 // we always have bytecode_size > pc.
123 const auto bytecode_size = bytecode_ptr->size();
124 const uint128_t pc_diff = bytecode_size > pc ? bytecode_size - pc - 1 : pc - bytecode_size;
125 range_check.assert_range(pc_diff, AVM_PC_SIZE_IN_BITS);
126
127 // The event will be deduplicated internally.
128 fetching_events.emit(InstructionFetchingEvent(instr_fetching_event));
129
130 // Communicate error to the caller.
131 if (instr_fetching_event.error.has_value()) {
132 throw InstructionFetchingError("Instruction fetching error: " +
133 std::to_string(static_cast<int>(instr_fetching_event.error.value())));
134 }
135
136 return instr_fetching_event.instruction;
137}
138
139} // namespace bb::avm2::simulation
#define AVM_PC_SIZE_IN_BITS
virtual FF compute_public_bytecode_commitment(const BytecodeId bytecode_id, const std::vector< uint8_t > &bytecode)=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
HighLevelMerkleDBInterface & merkle_db
EventEmitterInterface< BytecodeDecompositionEvent > & decomposition_events
EventEmitterInterface< BytecodeRetrievalEvent > & retrieval_events
EventEmitterInterface< InstructionFetchingEvent > & fetching_events
unordered_flat_map< BytecodeId, std::shared_ptr< std::vector< uint8_t > > > bytecodes
BytecodeHashingInterface & bytecode_hasher
ContractInstanceManagerInterface & contract_instance_manager
Instruction read_instruction(BytecodeId bytecode_id, uint32_t pc) override
BytecodeId get_bytecode(const AztecAddress &address) override
void vinfo(Args... args)
Definition log.hpp:76
void info(Args... args)
Definition log.hpp:70
bool check_tag(const Instruction &instruction)
Check whether the instruction must have a tag operand and whether the operand value is in the value t...
Instruction deserialize_instruction(std::span< const uint8_t > bytecode, size_t pos)
Parsing of an instruction in the supplied bytecode at byte position pos. This checks that the WireOpC...
FF ContractClassId
std::string field_to_string(const FF &ff)
Definition stringify.cpp:5
AvmFlavorSettings::FF FF
Definition field.hpp:10
const std::unordered_map< ExecutionOpCode, ExecInstructionSpec > EXEC_INSTRUCTION_SPEC
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:44
FF current_class_id
std::optional< InstrDeserializationError > error
std::shared_ptr< std::vector< uint8_t > > bytecode
ExecutionOpCode get_exec_opcode() const