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
6
7namespace bb::avm2::simulation {
8
9namespace {
10
17FF unconstrained_read(const LowLevelMerkleDBInterface& merkle_db, const FF& leaf_slot)
18{
19 auto [present, index] = merkle_db.get_low_indexed_leaf(world_state::MerkleTreeId::PUBLIC_DATA_TREE, leaf_slot);
20 auto preimage = merkle_db.get_leaf_preimage_public_data_tree(index);
21 return present ? preimage.leaf.value : 0;
22}
23
24} // namespace
25
45{
46 // Compute the public data tree slots
47 FF delayed_public_mutable_slot =
49 FF delayed_public_mutable_hash_slot = delayed_public_mutable_slot + UPDATES_DELAYED_PUBLIC_MUTABLE_VALUES_LEN;
50 // Read the hash from the tree. We do a trick with delayed public mutables (updates are delayed public mutables)
51 // where we store in one public data tree slot the hash of the whole structure. This is nice because in circuits you
52 // can receive the preimage as a hint and just read 1 storage slot instead of 3. We do that here, we will constrain
53 // the hash read but then read in unconstrained mode the preimage. The PIL for this gadget constrains the hash.
54 FF update_hash =
56
57 uint256_t update_preimage_metadata = 0;
58 FF update_preimage_pre_class_id = 0;
59 FF update_preimage_post_class_id = 0;
60
61 uint64_t current_timestamp = globals.timestamp;
62
63 if (update_hash == 0) {
64 // If the delayed public mutable has never been written, then the contract was never updated. We short circuit
65 // early.
66 if (instance.original_contract_class_id != instance.current_contract_class_id) {
67 throw std::runtime_error("Current class id does not match expected class id");
68 }
69 } else {
70 // The preimage (metadata, pre_class_id, post_class_id) is read in unconstrained mode because
71 // the PIL constrains hash(metadata, pre, post) == stored_hash, making a single constrained
72 // hash read sufficient. The preimage slots are guaranteed to be consistent with the hash:
73 // all writes go through DelayedPublicMutable::write(), which atomically writes all 4 slots
74 // (3 preimage + hash) in a single storage_write call, and storage is siloed to the
75 // ContractInstanceRegistry contract so no external contract/actor can tamper with these slots.
76 LowLevelMerkleDBInterface& unconstrained_merkle_db = merkle_db.as_unconstrained();
77
78 std::vector<FF> update_preimage(3);
79
80 for (size_t i = 0; i < update_preimage.size(); ++i) {
82 delayed_public_mutable_slot + i);
83 update_preimage[i] = unconstrained_read(unconstrained_merkle_db, leaf_slot);
84 }
85
86 // Double check that the unconstrained reads match the hash. This is just a sanity check, if slow, can be
87 // removed.
88 FF reconstructed_hash = poseidon2.hash(update_preimage);
89 if (update_hash != reconstructed_hash) {
90 throw std::runtime_error("Stored hash does not match preimage hash");
91 }
92
93 update_preimage_metadata = static_cast<uint256_t>(update_preimage[0]);
94 update_preimage_pre_class_id = update_preimage[1];
95 update_preimage_post_class_id = update_preimage[2];
96
97 // Decompose the metadata: we want the least significant 32 bits since that's the timestamp of change.
98 uint128_t update_metadata_hi = static_cast<uint128_t>(update_preimage_metadata >> TIMESTAMP_OF_CHANGE_BIT_SIZE);
99 // Note that the code below no longer works after 2106 as by that time the timestamp will overflow u32. The 64
100 // bit timestamp being packed in 32 bits is a tech debt that is not worth tackling.
101 uint64_t timestamp_of_change =
102 static_cast<uint64_t>(static_cast<uint32_t>(update_preimage_metadata & 0xffffffff));
103 // Constrain that these items fit in their respective bit-sizes so that malicious prover
104 // cannot provide larger values.
105 range_check.assert_range(update_metadata_hi,
107 range_check.assert_range(timestamp_of_change, TIMESTAMP_OF_CHANGE_BIT_SIZE);
108
109 // pre and post can be zero, if they have never been touched (or have been reset). In that case we need to use
110 // the original class id.
111 FF pre_class =
112 update_preimage_pre_class_id == 0 ? instance.original_contract_class_id : update_preimage_pre_class_id;
113 FF post_class =
114 update_preimage_post_class_id == 0 ? instance.original_contract_class_id : update_preimage_post_class_id;
115
116 FF expected_current_class_id = gt.gt(timestamp_of_change, current_timestamp) ? pre_class : post_class;
117
118 if (expected_current_class_id != instance.current_contract_class_id) {
119 throw std::runtime_error(
120 "Current class id: " + field_to_string(instance.current_contract_class_id) +
121 " does not match expected class id: " + field_to_string(expected_current_class_id));
122 }
123 }
124
126 .address = address,
127 .current_class_id = instance.current_contract_class_id,
128 .original_class_id = instance.original_contract_class_id,
129 .public_data_tree_root = merkle_db.get_tree_state().public_data_tree.tree.root,
130 .current_timestamp = current_timestamp,
131 .update_hash = update_hash,
132 .update_preimage_metadata = update_preimage_metadata,
133 .update_preimage_pre_class_id = update_preimage_pre_class_id,
134 .update_preimage_post_class_id = update_preimage_post_class_id,
135 .delayed_public_mutable_slot = delayed_public_mutable_slot,
136 });
137}
138
139} // 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
Validate that a contract's current class ID is consistent with the delayed public mutable update stat...
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