Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
simulator.cpp
Go to the documentation of this file.
2
3#include <cstdint>
4#include <iomanip>
5#include <iostream>
6#include <nlohmann/json.hpp>
7#include <sys/wait.h>
8#include <unistd.h>
9#include <vector>
10
27
29using namespace bb::avm2;
30using namespace bb::avm2::simulation;
31using namespace bb::avm2::fuzzer;
32using namespace bb::world_state;
33using json = nlohmann::json;
34
35// Helper function to serialize bytecode, calldata, tx, and globals to JSON
36std::string serialize_simulation_request(const std::vector<uint8_t>& bytecode,
37 const std::vector<FF>& calldata,
38 const Tx& tx,
39 const GlobalVariables& globals)
40{
41 json j;
42 j["bytecode"] = base64_encode(bytecode.data(), bytecode.size());
43
44 // Convert FF values to strings for JSON serialization
45 std::vector<std::string> calldata_strings;
46 calldata_strings.reserve(calldata.size());
47 for (const auto& field : calldata) {
48 calldata_strings.push_back(field_to_string(field));
49 }
50 j["inputs"] = calldata_strings;
51
52 // Pass WorldState database path and configuration for TypeScript to open the same DB
53 // Note: TypeScript expects the parent directory and adds "world_state/" subdirectory itself
54 j["ws_data_dir"] = FuzzerWorldStateManager::get_data_dir();
55 j["ws_map_size_kb"] = FuzzerWorldStateManager::get_map_size_kb();
56
57 // Serialize Tx using msgpack and base64 encode
58 auto [tx_buffer, tx_size] = msgpack_encode_buffer(tx);
59 j["tx"] = base64_encode(tx_buffer, tx_size);
60 delete[] tx_buffer;
61
62 // Serialize GlobalVariables using msgpack and base64 encode
63 auto [globals_buffer, globals_size] = msgpack_encode_buffer(globals);
64 j["globals"] = base64_encode(globals_buffer, globals_size);
65 delete[] globals_buffer;
66
67 return j.dump();
68}
69
70// Helper function to create default global variables for testing
72{
73 return GlobalVariables{
75 .version = VERSION,
76 .block_number = BLOCK_NUMBER,
77 .slot_number = SLOT_NUMBER,
78 .timestamp = TIMESTAMP,
79 .coinbase = COINBASE,
80 .fee_recipient = FEE_RECIPIENT,
81 .gas_fees = GasFees{ .fee_per_da_gas = FEE_PER_DA_GAS, .fee_per_l2_gas = FEE_PER_L2_GAS },
82 };
83}
84
85// Creates a default transaction that the single app logic enqueued call can be inserted into
87 const AztecAddress& sender_address,
88 const std::vector<FF>& calldata,
89 [[maybe_unused]] const FF& transaction_fee,
90 bool is_static_call,
91 const Gas& gas_limit)
92{
93 return Tx
94 {
96 .gas_settings = GasSettings{
97 .gas_limits = gas_limit,
98 .max_fees_per_gas = GasFees{ .fee_per_da_gas = FEE_PER_DA_GAS, .fee_per_l2_gas = FEE_PER_L2_GAS },
99 },
100 .effective_gas_fees = EFFECTIVE_GAS_FEES,
101 .non_revertible_accumulated_data = AccumulatedData{
103 // This nullifier is needed to make the nonces for note hashes and expected by simulation_helper
106 },
107 .revertible_accumulated_data = AccumulatedData{
111 },
112 .setup_enqueued_calls = SETUP_ENQUEUED_CALLS,
113 .app_logic_enqueued_calls = {
117 .contract_address = contract_address,
118 .is_static_call = is_static_call,
119 .calldata_hash = 0,
120 },
121 .calldata = calldata,
122 },
123 },
124 .teardown_enqueued_call = TEARDOWN_ENQUEUED_CALLS,
125 .gas_used_by_private = GAS_USED_BY_PRIVATE,
126 .fee_payer = sender_address,
127 };
128}
129
131 const std::vector<uint8_t>& bytecode,
132 const std::vector<FF>& calldata)
133{
134 FuzzerContractDB minimal_contract_db(bytecode);
135
136 const PublicSimulatorConfig config{ .collect_call_metadata = true, .collect_public_inputs = true };
137
138 ProtocolContracts protocol_contracts{};
139
140 auto globals = create_default_globals();
141
143
144 WorldState& ws = ws_mgr.get_world_state();
146
147 AvmSimulationHelper helper;
148 TxSimulationResult result =
149 helper.simulate_fast_with_existing_ws(minimal_contract_db, ws_rev, ws, config, tx, globals, protocol_contracts);
150 bool reverted = result.revert_code != RevertCode::OK;
151 // Just process the top level call's output
152 vinfo(
153 "C++ Simulator result - reverted: ", reverted, ", output size: ", result.call_stack_metadata[0].output.size());
154 std::vector<FF> values = {};
155 if (result.call_stack_metadata.size() != 0) {
156 for (const auto& output : result.call_stack_metadata.at(0).output) {
157 values.push_back(output);
158 }
159 }
160 if (result.public_inputs.has_value()) {
161 return { .reverted = reverted,
162 .output = values,
163 .end_tree_snapshots = result.public_inputs->end_tree_snapshots };
164 }
165 return { .reverted = reverted, .output = values };
166}
167
169JsSimulator::JsSimulator(std::string& simulator_path)
170 : simulator_path(simulator_path)
171 , process("LOG_LEVEL=silent node " + simulator_path + " 2>/dev/null")
172{}
173
175{
176 if (instance == nullptr) {
177 throw std::runtime_error("JsSimulator should be initializing in FUZZ INIT");
178 }
179 return instance;
180}
181
184void JsSimulator::initialize(std::string& simulator_path)
185{
186 if (instance != nullptr) {
187 throw std::runtime_error("JsSimulator already initialized");
188 }
190}
191
193 const std::vector<uint8_t>& bytecode,
194 const std::vector<FF>& calldata)
195{
196 bool logging_enabled = std::getenv("AVM_FUZZER_LOGGING") != nullptr;
197
198 // Create tx and globals to match C++ simulator
199 auto globals = create_default_globals();
201
202 std::string serialized = serialize_simulation_request(bytecode, calldata, tx, globals);
203 if (logging_enabled) {
204 info("Sending request to simulator: ", serialized);
205 }
206
207 // Send the request
208 process.write_line(serialized);
209 std::string response = process.read_line();
210 while (response.empty()) {
211 std::cout << "Empty response, reading again" << std::endl;
212 std::this_thread::sleep_for(std::chrono::milliseconds(10));
213 response = process.read_line();
214 }
215 // Remove the newline character
216 response.erase(response.find_last_not_of('\n') + 1);
217
218 // Response is plain JSON (no longer gzipped/base64 encoded)
219 if (logging_enabled) {
220 info("Received response from simulator: ", response);
221 }
222 json response_json = json::parse(response);
223 bool reverted = response_json["reverted"];
224 std::vector<std::string> output = response_json["output"];
225 std::string revert_reason = response_json.value("revertReason", "");
226 std::vector<FF> output_fields;
227 output_fields.reserve(output.size());
228 for (const auto& field : output) {
229 output_fields.push_back(FF(field));
230 }
231
232 // Parse endTreeSnapshots from JSON response
233 TreeSnapshots end_tree_snapshots;
234 if (response_json.contains("endTreeSnapshots")) {
235 const auto& ets = response_json["endTreeSnapshots"];
237 .root = FF(ets["l1ToL2MessageTree"]["root"].get<std::string>()),
238 .next_available_leaf_index = ets["l1ToL2MessageTree"]["nextAvailableLeafIndex"].get<uint64_t>(),
239 };
240 end_tree_snapshots.note_hash_tree = AppendOnlyTreeSnapshot{
241 .root = FF(ets["noteHashTree"]["root"].get<std::string>()),
242 .next_available_leaf_index = ets["noteHashTree"]["nextAvailableLeafIndex"].get<uint64_t>(),
243 };
244 end_tree_snapshots.nullifier_tree = AppendOnlyTreeSnapshot{
245 .root = FF(ets["nullifierTree"]["root"].get<std::string>()),
246 .next_available_leaf_index = ets["nullifierTree"]["nextAvailableLeafIndex"].get<uint64_t>(),
247 };
248 end_tree_snapshots.public_data_tree = AppendOnlyTreeSnapshot{
249 .root = FF(ets["publicDataTree"]["root"].get<std::string>()),
250 .next_available_leaf_index = ets["publicDataTree"]["nextAvailableLeafIndex"].get<uint64_t>(),
251 };
252 }
253
254 SimulatorResult result = {
255 .reverted = reverted,
256 .output = output_fields,
257 .end_tree_snapshots = end_tree_snapshots,
258 .revert_reason = revert_reason,
259 };
260 return result;
261}
262
264{
265 return result1.reverted == result2.reverted && result1.output == result2.output &&
266 result1.end_tree_snapshots == result2.end_tree_snapshots;
267}
const std::optional< PublicCallRequestWithCalldata > TEARDOWN_ENQUEUED_CALLS
Definition constants.hpp:30
const uint128_t FEE_PER_DA_GAS
Definition constants.hpp:17
const std::vector< ScopedL2ToL1Message > NON_REVERTIBLE_ACCUMULATED_DATA_L2_TO_L1_MESSAGES
Definition constants.hpp:24
const uint32_t BLOCK_NUMBER
Definition constants.hpp:12
const std::vector< FF > NON_REVERTIBLE_ACCUMULATED_DATA_NULLIFIERS
Definition constants.hpp:22
const AztecAddress FEE_RECIPIENT
Definition constants.hpp:16
const Gas GAS_LIMIT
Definition constants.hpp:36
const std::vector< FF > NON_REVERTIBLE_ACCUMULATED_DATA_NOTE_HASHES
Definition constants.hpp:23
const std::vector< PublicCallRequestWithCalldata > SETUP_ENQUEUED_CALLS
Definition constants.hpp:28
const FF TRANSACTION_FEE
Definition constants.hpp:34
const EthAddress COINBASE
Definition constants.hpp:15
const FF MSG_SENDER
Definition constants.hpp:29
const FF SLOT_NUMBER
Definition constants.hpp:13
const std::string TRANSACTION_HASH
Definition constants.hpp:19
const FF CHAIN_ID
Definition constants.hpp:10
const std::vector< ScopedL2ToL1Message > REVERTIBLE_ACCUMULATED_DATA_L2_TO_L1_MESSAGES
Definition constants.hpp:27
const Gas GAS_USED_BY_PRIVATE
Definition constants.hpp:31
const std::vector< FF > REVERTIBLE_ACCUMULATED_DATA_NOTE_HASHES
Definition constants.hpp:26
const FF CONTRACT_ADDRESS
Definition constants.hpp:33
const std::vector< FF > REVERTIBLE_ACCUMULATED_DATA_NULLIFIERS
Definition constants.hpp:25
const uint128_t FEE_PER_L2_GAS
Definition constants.hpp:18
const bool IS_STATIC_CALL
Definition constants.hpp:35
const GasFees EFFECTIVE_GAS_FEES
Definition constants.hpp:20
std::shared_ptr< Napi::ThreadSafeFunction > bytecode
std::string base64_encode(unsigned char const *bytes_to_encode, size_t in_len, bool url)
Definition base64.cpp:117
SimulatorResult simulate(fuzzer::FuzzerWorldStateManager &ws_mgr, const std::vector< uint8_t > &bytecode, const std::vector< FF > &calldata) override
uses the yarn-project/simulator to simulate the bytecode Singleton, because initializing the simulato...
Definition simulator.hpp:44
static JsSimulator * getInstance()
static JsSimulator * instance
Definition simulator.hpp:46
std::string simulator_path
Definition simulator.hpp:47
SimulatorResult simulate(fuzzer::FuzzerWorldStateManager &ws_mgr, const std::vector< uint8_t > &bytecode, const std::vector< FF > &calldata) override
static void initialize(std::string &simulator_path)
JsSimulator(std::string &simulator_path)
Process process
Definition simulator.hpp:49
std::string read_line() const
Reads a line from the process.
Definition process.cpp:56
void write_line(const std::string &line) const
Ends line with a newline character, sends to the process.
Definition process.cpp:49
TxSimulationResult simulate_fast_with_existing_ws(simulation::ContractDBInterface &raw_contract_db, const world_state::WorldStateRevision &world_state_revision, world_state::WorldState &ws, const PublicSimulatorConfig &config, const Tx &tx, const GlobalVariables &global_variables, const ProtocolContracts &protocol_contracts)
world_state::WorldState & get_world_state()
Definition dbs.hpp:121
static const char * get_data_dir()
Definition dbs.hpp:129
world_state::WorldStateRevision get_current_revision() const
Definition dbs.cpp:267
Holds the Merkle trees responsible for storing the state of the Aztec protocol.
#define vinfo(...)
Definition log.hpp:80
void info(Args... args)
Definition log.hpp:75
std::pair< uint8_t *, size_t > msgpack_encode_buffer(auto &&obj, uint8_t *scratch_buf=nullptr, size_t scratch_size=0)
std::string field_to_string(const FF &ff)
Definition stringify.cpp:5
AvmFlavorSettings::FF FF
Definition field.hpp:10
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
bool compare_simulator_results(const SimulatorResult &result1, const SimulatorResult &result2)
std::string serialize_simulation_request(const std::vector< uint8_t > &bytecode, const std::vector< FF > &calldata, const Tx &tx, const GlobalVariables &globals)
Definition simulator.cpp:36
Tx create_default_tx(const AztecAddress &contract_address, const AztecAddress &sender_address, const std::vector< FF > &calldata, const FF &transaction_fee, bool is_static_call, const Gas &gas_limit)
Definition simulator.cpp:86
nlohmann::json json
Definition simulator.cpp:33
GlobalVariables create_default_globals()
Definition simulator.cpp:71
TreeSnapshots end_tree_snapshots
Definition simulator.hpp:17
std::vector< FF > output
Definition simulator.hpp:16
std::vector< FF > note_hashes
Definition avm_io.hpp:318
uint128_t fee_per_da_gas
AppendOnlyTreeSnapshot public_data_tree
AppendOnlyTreeSnapshot l1_to_l2_message_tree
AppendOnlyTreeSnapshot nullifier_tree
AppendOnlyTreeSnapshot note_hash_tree
std::string hash
Definition avm_io.hpp:330
std::vector< CallStackMetadata > call_stack_metadata
Definition avm_io.hpp:554
std::optional< PublicInputs > public_inputs
Definition avm_io.hpp:557
General class for prime fields see Prime field documentation["field documentation"] for general imple...