Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
update_check.cpp
Go to the documentation of this file.
2
7
8namespace bb::avm2::simulation {
9
10namespace {
11
13
14FF unconstrained_read(const LowLevelMerkleDBInterface& merkle_db, const FF& leaf_slot)
15{
16 auto [present, index] = merkle_db.get_low_indexed_leaf(world_state::MerkleTreeId::PUBLIC_DATA_TREE, leaf_slot);
17 auto preimage = merkle_db.get_leaf_preimage_public_data_tree(index);
18 return present ? preimage.leaf.value : 0;
19}
20
21} // namespace
22
24{
25 // Compute the public data tree slots
26 FF delayed_public_mutable_slot =
28 FF delayed_public_mutable_hash_slot = delayed_public_mutable_slot + UPDATES_DELAYED_PUBLIC_MUTABLE_VALUES_LEN;
29 // Read the hash from the tree. We do a trick with delayed public mutables (updates are delayed public mutables)
30 // where we store in one public data tree slot the hash of the whole structure. This is nice because in circuits you
31 // can receive the preimage as a hint and just read 1 storage slot instead of 3. We do that here, we will constrain
32 // the hash read but then read in unconstrained mode the preimage. The PIL for this gadget constrains the hash.
33 FF hash = merkle_db.storage_read(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, delayed_public_mutable_hash_slot);
34
35 uint256_t update_preimage_metadata = 0;
36 FF update_preimage_pre_class_id = 0;
37 FF update_preimage_post_class_id = 0;
38
39 uint64_t current_timestamp = globals.timestamp;
40
41 if (hash == 0) {
42 // If the delayed public mutable has never been written, then the contract was never updated. We short circuit
43 // early.
44 if (instance.original_contract_class_id != instance.current_contract_class_id) {
45 throw std::runtime_error("Current class id does not match expected class id");
46 }
47 } else {
48 // Read the preimage from the tree in unconstrained mode
49 LowLevelMerkleDBInterface& unconstrained_merkle_db = merkle_db.as_unconstrained();
50
51 std::vector<FF> update_preimage(3);
52
53 for (size_t i = 0; i < update_preimage.size(); ++i) {
55 delayed_public_mutable_slot + i);
56 update_preimage[i] = unconstrained_read(unconstrained_merkle_db, leaf_slot);
57 }
58
59 // Double check that the unconstrained reads match the hash. This is just a sanity check, if slow, can be
60 // removed.
61 FF reconstructed_hash = poseidon2.hash(update_preimage);
62 if (hash != reconstructed_hash) {
63 throw std::runtime_error("Stored hash does not match preimage hash");
64 }
65
66 update_preimage_metadata = static_cast<uint256_t>(update_preimage[0]);
67 update_preimage_pre_class_id = update_preimage[1];
68 update_preimage_post_class_id = update_preimage[2];
69
70 // Decompose the metadata: we want the least significant 32 bits since that's the timestamp of change.
71 uint128_t update_metadata_hi = static_cast<uint128_t>(update_preimage_metadata >> TIMESTAMP_OF_CHANGE_BIT_SIZE);
72 // Note that the code below no longer works after 2106 as by that time the timestamp will overflow u32. The 64
73 // bit timestamp being packed in 32 bits is a tech debt that is not worth tackling.
74 uint64_t timestamp_of_change =
75 static_cast<uint64_t>(static_cast<uint32_t>(update_preimage_metadata & 0xffffffff));
76 range_check.assert_range(update_metadata_hi,
78 range_check.assert_range(timestamp_of_change, TIMESTAMP_OF_CHANGE_BIT_SIZE);
79
80 // pre and post can be zero, if they have never been touched. In that case we need to use the original class id.
81 FF pre_class =
82 update_preimage_pre_class_id == 0 ? instance.original_contract_class_id : update_preimage_pre_class_id;
83 FF post_class =
84 update_preimage_post_class_id == 0 ? instance.original_contract_class_id : update_preimage_post_class_id;
85
86 FF expected_current_class_id = gt.gt(timestamp_of_change, current_timestamp) ? pre_class : post_class;
87
88 if (expected_current_class_id != instance.current_contract_class_id) {
89 throw std::runtime_error(
90 "Current class id: " + field_to_string(instance.current_contract_class_id) +
91 " does not match expected class id: " + field_to_string(expected_current_class_id));
92 }
93 }
94
96 .address = address,
97 .current_class_id = instance.current_contract_class_id,
98 .original_class_id = instance.original_contract_class_id,
99 .public_data_tree_root = merkle_db.get_tree_state().public_data_tree.tree.root,
100 .current_timestamp = current_timestamp,
101 .update_hash = hash,
102 .update_preimage_metadata = update_preimage_metadata,
103 .update_preimage_pre_class_id = update_preimage_pre_class_id,
104 .update_preimage_post_class_id = update_preimage_post_class_id,
105 .delayed_public_mutable_slot = delayed_public_mutable_slot,
106 });
107}
108
109} // namespace bb::avm2::simulation
std::shared_ptr< Napi::ThreadSafeFunction > instance
#define UPDATES_DELAYED_PUBLIC_MUTABLE_METADATA_BIT_SIZE
#define UPDATES_DELAYED_PUBLIC_MUTABLE_VALUES_LEN
#define DOM_SEP__PUBLIC_STORAGE_MAP_SLOT
#define UPDATED_CLASS_IDS_SLOT
#define TIMESTAMP_OF_CHANGE_BIT_SIZE
#define CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS
StrictMock< MockHighLevelMerkleDB > merkle_db
virtual FF storage_read(const AztecAddress &contract_address, const FF &slot) const =0
virtual TreeStates get_tree_state() const =0
virtual LowLevelMerkleDBInterface & as_unconstrained() const =0
const GlobalVariables & globals
EventEmitterInterface< UpdateCheckEvent > & update_check_events
HighLevelMerkleDBInterface & merkle_db
void check_current_class_id(const AztecAddress &address, const ContractInstance &instance) override
Native Poseidon2 hash function implementation.
Definition poseidon2.hpp:22
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
AVM range check gadget for witness generation.
FF unconstrained_compute_leaf_slot(const AztecAddress &contract_address, const FF &slot)
Definition merkle.cpp:26
AvmFlavorSettings::FF FF
Definition field.hpp:10
std::string field_to_string(const FF &ff)
Definition stringify.cpp:5
unsigned __int128 uint128_t
Definition serialize.hpp:45