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 = poseidon2.hash({ UPDATED_CLASS_IDS_SLOT, address });
27 FF delayed_public_mutable_hash_slot = delayed_public_mutable_slot + UPDATES_DELAYED_PUBLIC_MUTABLE_VALUES_LEN;
28 // Read the hash from the tree. We do a trick with delayed public mutables (updates are delayed public mutables)
29 // where we store in one public data tree slot the hash of the whole structure. This is nice because in circuits you
30 // can receive the preimage as a hint and just read 1 storage slot instead of 3. We do that here, we will constrain
31 // the hash read but then read in unconstrained mode the preimage. The PIL for this gadget constrains the hash.
33
34 uint256_t update_preimage_metadata = 0;
35 FF update_preimage_pre_class_id = 0;
36 FF update_preimage_post_class_id = 0;
37
38 uint64_t current_timestamp = globals.timestamp;
39
40 if (hash == 0) {
41 // If the delayed public mutable has never been written, then the contract was never updated. We short circuit
42 // early.
43 if (instance.original_class_id != instance.current_class_id) {
44 throw std::runtime_error("Current class id does not match expected class id");
45 }
46 } else {
47 // Read the preimage from the tree in unconstrained mode
48 LowLevelMerkleDBInterface& unconstrained_merkle_db = merkle_db.as_unconstrained();
49
50 std::vector<FF> update_preimage(3);
51
52 for (size_t i = 0; i < update_preimage.size(); ++i) {
54 delayed_public_mutable_slot + i);
55 update_preimage[i] = unconstrained_read(unconstrained_merkle_db, leaf_slot);
56 }
57
58 // Double check that the unconstrained reads match the hash. This is just a sanity check, if slow, can be
59 // removed.
60 FF reconstructed_hash = poseidon2.hash(update_preimage);
61 if (hash != reconstructed_hash) {
62 throw std::runtime_error("Stored hash does not match preimage hash");
63 }
64
65 update_preimage_metadata = static_cast<uint256_t>(update_preimage[0]);
66 update_preimage_pre_class_id = update_preimage[1];
67 update_preimage_post_class_id = update_preimage[2];
68
69 // Decompose the metadata: we want the least significant 32 bits since that's the timestamp of change.
70 uint128_t update_metadata_hi = static_cast<uint128_t>(update_preimage_metadata >> TIMESTAMP_OF_CHANGE_BIT_SIZE);
71 // Note that the code below no longer works after 2106 as by that time the timestamp will overflow u32. The 64
72 // bit timestamp being packed in 32 bits is a tech debt that is not worth tackling.
73 uint64_t timestamp_of_change =
74 static_cast<uint64_t>(static_cast<uint32_t>(update_preimage_metadata & 0xffffffff));
75 range_check.assert_range(update_metadata_hi,
77 range_check.assert_range(timestamp_of_change, TIMESTAMP_OF_CHANGE_BIT_SIZE);
78
79 // pre and post can be zero, if they have never been touched. In that case we need to use the original class id.
80 FF pre_class = update_preimage_pre_class_id == 0 ? instance.original_class_id : update_preimage_pre_class_id;
81 FF post_class = update_preimage_post_class_id == 0 ? instance.original_class_id : update_preimage_post_class_id;
82
83 FF expected_current_class_id = current_timestamp < timestamp_of_change ? pre_class : post_class;
84 uint64_t timestamp_of_change_subtraction = current_timestamp < timestamp_of_change
85 ? timestamp_of_change - 1 - current_timestamp
86 : current_timestamp - timestamp_of_change;
87
88 range_check.assert_range(timestamp_of_change_subtraction, TIMESTAMP_OF_CHANGE_BIT_SIZE);
89
90 if (expected_current_class_id != instance.current_class_id) {
91 throw std::runtime_error(
92 "Current class id: " + field_to_string(instance.current_class_id) +
93 " does not match expected class id: " + field_to_string(expected_current_class_id));
94 }
95 }
96
98 .address = address,
99 .current_class_id = instance.current_class_id,
100 .original_class_id = instance.original_class_id,
101 .public_data_tree_root = merkle_db.get_tree_state().publicDataTree.tree.root,
102 .current_timestamp = current_timestamp,
103 .update_hash = hash,
104 .update_preimage_metadata = update_preimage_metadata,
105 .update_preimage_pre_class_id = update_preimage_pre_class_id,
106 .update_preimage_post_class_id = update_preimage_post_class_id,
107 .delayed_public_mutable_slot = delayed_public_mutable_slot,
108 });
109}
110
111} // namespace bb::avm2::simulation
#define UPDATES_DELAYED_PUBLIC_MUTABLE_METADATA_BIT_SIZE
#define UPDATES_DELAYED_PUBLIC_MUTABLE_VALUES_LEN
#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
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
void hash(State &state) noexcept
FF unconstrained_compute_leaf_slot(const AztecAddress &contract_address, const FF &slot)
Definition merkle.cpp:26
std::string field_to_string(const FF &ff)
Definition stringify.cpp:5
AvmFlavorSettings::FF FF
Definition field.hpp:10
typename Flavor::FF FF
unsigned __int128 uint128_t
Definition serialize.hpp:44
ContractClassId original_class_id
ContractClassId current_class_id