Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
public_tx_simulation_tester.cpp
Go to the documentation of this file.
2
3#include <filesystem>
4
5#include "barretenberg/aztec/aztec_constants.hpp"
12
13namespace bb::avm2::testing {
14
15namespace {
16
23
24// A balance large enough to cover the fee for any test transaction.
25const FF FEE_PAYER_BALANCE = FF(uint256_t(1) << 100);
26
27// WorldState is neither copyable nor movable (it holds a mutex), so it must be constructed
28// directly into the owning unique_ptr.
29std::unique_ptr<WorldState> make_world_state(const std::string& data_dir)
30{
31 const std::unordered_map<MerkleTreeId, uint32_t> tree_heights{
32 { MerkleTreeId::NULLIFIER_TREE, NULLIFIER_TREE_HEIGHT },
33 { MerkleTreeId::NOTE_HASH_TREE, NOTE_HASH_TREE_HEIGHT },
34 { MerkleTreeId::PUBLIC_DATA_TREE, PUBLIC_DATA_TREE_HEIGHT },
35 { MerkleTreeId::L1_TO_L2_MESSAGE_TREE, L1_TO_L2_MSG_TREE_HEIGHT },
36 { MerkleTreeId::ARCHIVE, ARCHIVE_HEIGHT },
37 };
38 const std::unordered_map<MerkleTreeId, index_t> tree_prefill{
39 { MerkleTreeId::NULLIFIER_TREE, 128 },
40 { MerkleTreeId::PUBLIC_DATA_TREE, 128 },
41 };
42 return std::make_unique<WorldState>(/*thread_pool_size=*/1,
43 data_dir,
44 /*map_size_kb=*/10240,
45 tree_heights,
46 tree_prefill,
47 /*initial_header_generator_point=*/DOM_SEP__BLOCK_HEADER_HASH);
48}
49
50} // namespace
51
55
61
63{
64 auto it = contract_classes.find(class_id);
65 if (it == contract_classes.end()) {
66 return std::nullopt;
67 }
68 const auto& klass = it->second;
69 return ContractClass{
70 .id = klass.id,
71 .artifact_hash = klass.artifact_hash,
72 .private_functions_root = klass.private_functions_root,
73 .packed_bytecode = klass.packed_bytecode,
74 };
75}
76
78{
79 auto it = contract_classes.find(class_id);
80 return it == contract_classes.end() ? std::nullopt : std::make_optional(it->second.public_bytecode_commitment);
81}
82
87
89{
90 // Not used: tests deploy directly via add_contract_class / add_contract_instance.
91}
92
94{
95 contract_classes.insert({ contract_class.id, contract_class });
96}
97
99{
100 contract_instances.insert({ address, instance });
101}
102
107
109{
110 if (!checkpoints.empty()) {
111 checkpoints.pop();
112 }
113}
114
116{
117 if (!checkpoints.empty()) {
118 contract_classes = std::move(checkpoints.top().contract_classes);
119 contract_instances = std::move(checkpoints.top().contract_instances);
120 checkpoints.pop();
121 }
122}
123
127
132
134{
136 .skip_fee_enforcement = false,
137 .collect_call_metadata = true,
138 };
139}
140
142 : data_dir(crypto::merkle_tree::random_temp_directory())
143{
144 std::filesystem::create_directories(data_dir);
145 ws = make_world_state(data_dir);
146 fork_id = ws->create_fork(std::nullopt);
147}
148
150{
151 ws.reset();
152 std::error_code ec;
153 std::filesystem::remove_all(data_dir, ec);
154}
155
157{
158 return WorldStateRevision{ .forkId = fork_id, .includeUncommitted = true };
159}
160
162{
163 std::vector<uint8_t> bytecode_vec(bytecode.begin(), bytecode.end());
164
165 const FF commitment = simulation::compute_public_bytecode_commitment(bytecode_vec);
166 const FF class_id = simulation::compute_contract_class_id(/*artifact_hash=*/0, /*private_fn_root=*/0, commitment);
167
168 ContractClassWithCommitment contract_class{
169 .id = class_id,
170 .artifact_hash = 0,
171 .private_functions_root = 0,
172 .packed_bytecode = bytecode_vec,
173 .public_bytecode_commitment = commitment,
174 };
175 ContractInstance instance{
176 .salt = salt,
177 .deployer = default_sender(),
178 .current_contract_class_id = class_id,
179 .original_contract_class_id = class_id,
180 .initialization_hash = 0,
181 .immutables_hash = 0,
182 .public_keys =
185 .incoming_viewing_key = AffinePoint::one(),
186 .outgoing_viewing_key_hash = 0,
187 .tagging_key_hash = 0,
188 .message_signing_key_hash = 0,
189 .fallback_key_hash = 0,
190 },
191 };
193
194 contract_db_.add_contract_class(contract_class);
196
197 // Insert the deployment nullifier so the simulator can resolve this (non-protocol) contract.
198 const NullifierLeafValue deployment_nullifier =
199 simulation::unconstrained_silo_nullifier(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, address);
200 ws->insert_indexed_leaves<NullifierLeafValue>(MerkleTreeId::NULLIFIER_TREE, { deployment_nullifier }, fork_id);
201
202 return DeployedContract{ .address = address, .contract_class = contract_class, .instance = instance };
203}
204
206{
207 const FF leaf_slot = Poseidon2::hash({ DOM_SEP__PUBLIC_LEAF_SLOT, address, slot });
208 ws->update_public_data(PublicDataLeafValue(leaf_slot, value), fork_id);
209}
210
212{
213 ws->insert_indexed_leaves<NullifierLeafValue>(
214 MerkleTreeId::NULLIFIER_TREE, { NullifierLeafValue(siloed_nullifier) }, fork_id);
215}
216
218{
219 ws->append_leaves<FF>(MerkleTreeId::NOTE_HASH_TREE, { note_hash }, fork_id);
220}
221
223{
224 ws->append_leaves<FF>(MerkleTreeId::L1_TO_L2_MESSAGE_TREE, { message }, fork_id);
225}
226
228{
229 if (fee_payer.is_zero()) {
230 return;
231 }
232 const FF fee_juice_balance_slot =
233 Poseidon2::hash({ DOM_SEP__PUBLIC_STORAGE_MAP_SLOT, FEE_JUICE_BALANCES_SLOT, fee_payer });
234 const FF leaf_slot = Poseidon2::hash({ DOM_SEP__PUBLIC_LEAF_SLOT, FF(FEE_JUICE_ADDRESS), fee_juice_balance_slot });
235 ws->update_public_data(PublicDataLeafValue(leaf_slot, FEE_PAYER_BALANCE), fork_id);
236}
237
239 const PublicSimulatorConfig& config)
240{
241 const AztecAddress fee_payer = default_sender();
242 fund_fee_payer(fee_payer);
243
244 std::vector<PublicCallRequestWithCalldata> app_logic_enqueued_calls;
245 app_logic_enqueued_calls.reserve(app_calls.size());
246 for (const auto& call : app_calls) {
247 app_logic_enqueued_calls.push_back(PublicCallRequestWithCalldata{
248 .request =
251 .contract_address = call.contract_address,
252 .is_static_call = call.is_static_call,
253 .calldata_hash = simulation::compute_calldata_hash(call.calldata),
254 },
255 .calldata = call.calldata,
256 });
257 }
258
259 // A unique first nullifier per tx, needed for note-nonce computation.
260 const FF first_nullifier =
261 FF(uint256_t("0x00000000000000000000000000000000000000000000000000000000deadbeef")) + FF(tx_count);
262 tx_count++;
263
264 const GlobalVariables globals{
265 .chain_id = 1,
266 .version = 1,
267 .block_number = 1,
268 .slot_number = 1,
269 .timestamp = 1000000,
270 .coinbase = 0,
271 .fee_recipient = 0,
272 .gas_fees = GasFees{ .fee_per_da_gas = 1, .fee_per_l2_gas = 1 },
273 };
274
275 Tx tx{
276 .hash = "0xtest",
277 .gas_settings =
279 .gas_limits = Gas{ .l2_gas = 1000000, .da_gas = 1000000 },
280 .teardown_gas_limits = Gas{ .l2_gas = 1000000, .da_gas = 1000000 },
281 .max_fees_per_gas = GasFees{ .fee_per_da_gas = 1, .fee_per_l2_gas = 1 },
282 .max_priority_fees_per_gas = GasFees{ .fee_per_da_gas = 0, .fee_per_l2_gas = 0 },
283 },
284 .effective_gas_fees = GasFees{ .fee_per_da_gas = 1, .fee_per_l2_gas = 1 },
285 .non_revertible_accumulated_data = AccumulatedData{ .nullifiers = { first_nullifier } },
286 .app_logic_enqueued_calls = app_logic_enqueued_calls,
287 .gas_used_by_private = Gas{ .l2_gas = PUBLIC_TX_L2_GAS_OVERHEAD, .da_gas = TX_DA_GAS_OVERHEAD },
288 .fee_payer = fee_payer,
289 };
290
291 const ProtocolContracts protocol_contracts{};
292
293 AvmSimulationHelper helper;
294 ws->checkpoint(fork_id);
295 try {
296 // Hint collection wraps the DBs in hinting proxies and is required to produce proving
297 // inputs; the fast path is used otherwise.
300 contract_db_, current_revision(), *ws, config, tx, globals, protocol_contracts)
302 contract_db_, current_revision(), *ws, config, tx, globals, protocol_contracts);
303 ws->revert_checkpoint(fork_id);
304 return result;
305 } catch (...) {
306 ws->revert_checkpoint(fork_id);
307 throw;
308 }
309}
310
311} // namespace bb::avm2::testing
TxSimulationResult simulate_for_hint_collection(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, simulation::CancellationTokenPtr cancellation_token=nullptr)
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, simulation::CancellationTokenPtr cancellation_token=nullptr)
world_state::WorldStateRevision current_revision() const
static AztecAddress default_sender()
PublicTxSimulationTester.
void set_public_storage(const AztecAddress &address, const FF &slot, const FF &value)
TxSimulationResult simulate_tx(const std::vector< TestEnqueuedCall > &app_calls, const PublicSimulatorConfig &config=default_config())
std::unique_ptr< world_state::WorldState > ws
DeployedContract deploy_contract(std::span< const uint8_t > bytecode, const FF &salt=0)
void add_contract_instance(const AztecAddress &address, const ContractInstance &instance)
std::optional< ContractInstance > get_contract_instance(const AztecAddress &address) const override
TestContractDB.
std::optional< ContractClass > get_contract_class(const ContractClassId &class_id) const override
std::unordered_map< AztecAddress, ContractInstance > contract_instances
std::unordered_map< ContractClassId, ContractClassWithCommitment > contract_classes
void add_contracts(const ContractDeploymentData &contract_deployment_data) override
std::optional< std::string > get_debug_function_name(const AztecAddress &address, const FunctionSelector &selector) const override
void add_contract_class(const ContractClassWithCommitment &contract_class)
std::optional< FF > get_bytecode_commitment(const ContractClassId &class_id) const override
Native Poseidon2 hash function implementation.
Definition poseidon2.hpp:22
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
static constexpr affine_element one() noexcept
Holds the Merkle trees responsible for storing the state of the Aztec protocol.
std::vector< uint8_t > bytecode
FF compute_public_bytecode_commitment(std::span< const uint8_t > bytecode)
::bb::crypto::merkle_tree::NullifierLeafValue NullifierLeafValue
Definition db.hpp:39
FF compute_contract_class_id(const FF &artifact_hash, const FF &private_fn_root, const FF &public_bytecode_commitment)
::bb::crypto::merkle_tree::PublicDataLeafValue PublicDataLeafValue
Definition db.hpp:38
FF unconstrained_silo_nullifier(const AztecAddress &contract_address, const FF &nullifier)
Definition merkle.cpp:35
FF compute_calldata_hash(std::span< const FF > calldata)
FF compute_contract_address(const ContractInstance &contract_instance)
AvmFlavorSettings::FF FF
Definition field.hpp:10
FF ContractClassId
FF FunctionSelector
std::string random_temp_directory()
Definition fixtures.hpp:37
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::vector< FF > nullifiers
Definition avm_io.hpp:322
uint128_t fee_per_da_gas
std::unordered_map< ContractClassId, ContractClassWithCommitment > contract_classes
VectorField result