Skip to content

Commit 940351a

Browse files
authored
feat: merge-train/avm (#21054)
BEGIN_COMMIT_OVERRIDE chore!: pre-audit of avm update_check.pil (#20783) END_COMMIT_OVERRIDE
2 parents 81e8ebf + d7a13b5 commit 940351a

9 files changed

Lines changed: 250 additions & 92 deletions

File tree

barretenberg/cpp/pil/vm2/bytecode/update_check.pil

Lines changed: 171 additions & 55 deletions
Large diffs are not rendered by default.

barretenberg/cpp/src/barretenberg/vm2/generated/columns.hpp

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

barretenberg/cpp/src/barretenberg/vm2/generated/relations/lookups_update_check.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,15 @@ struct lookup_update_check_update_hash_public_data_read_settings_ {
7878
static constexpr Column COUNTS = Column::lookup_update_check_update_hash_public_data_read_counts;
7979
static constexpr Column INVERSES = Column::lookup_update_check_update_hash_public_data_read_inv;
8080
static constexpr std::array<ColumnAndShifts, LOOKUP_TUPLE_SIZE> SRC_COLUMNS = {
81-
ColumnAndShifts::update_check_deployer_protocol_contract_address,
82-
ColumnAndShifts::update_check_delayed_public_mutable_hash_slot,
8381
ColumnAndShifts::update_check_update_hash,
82+
ColumnAndShifts::update_check_contract_instance_registry_address,
83+
ColumnAndShifts::update_check_delayed_public_mutable_hash_slot,
8484
ColumnAndShifts::update_check_public_data_tree_root
8585
};
8686
static constexpr std::array<ColumnAndShifts, LOOKUP_TUPLE_SIZE> DST_COLUMNS = {
87+
ColumnAndShifts::public_data_check_value,
8788
ColumnAndShifts::public_data_check_address,
8889
ColumnAndShifts::public_data_check_slot,
89-
ColumnAndShifts::public_data_check_value,
9090
ColumnAndShifts::public_data_check_root
9191
};
9292
};

barretenberg/cpp/src/barretenberg/vm2/generated/relations/update_check_impl.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ void update_checkImpl<FF_>::accumulate(ContainerOverSubrelations& evals,
7070
using View = typename std::tuple_element_t<6, ContainerOverSubrelations>::View;
7171
auto tmp = static_cast<View>(in.get(C::update_check_sel)) *
7272
(CView(constants_CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS) -
73-
static_cast<View>(in.get(C::update_check_deployer_protocol_contract_address)));
73+
static_cast<View>(in.get(C::update_check_contract_instance_registry_address)));
7474
std::get<6>(evals) += (tmp * scaling_factor);
7575
}
7676
{

barretenberg/cpp/src/barretenberg/vm2/simulation/events/update_check.hpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,21 @@ namespace bb::avm2::simulation {
77

88
struct UpdateCheckEvent {
99
// Inputs
10-
AztecAddress address;
11-
FF current_class_id;
12-
FF original_class_id;
13-
FF public_data_tree_root;
14-
uint64_t current_timestamp;
10+
AztecAddress address = 0;
11+
FF current_class_id = 0;
12+
FF original_class_id = 0;
13+
FF public_data_tree_root = 0;
14+
uint64_t current_timestamp = 0;
1515

1616
// Hash
17-
FF update_hash;
17+
FF update_hash = 0;
1818
// Hash preimage
19-
FF update_preimage_metadata;
20-
FF update_preimage_pre_class_id;
21-
FF update_preimage_post_class_id;
19+
FF update_preimage_metadata = 0;
20+
FF update_preimage_pre_class_id = 0;
21+
FF update_preimage_post_class_id = 0;
2222

2323
// Read
24-
FF delayed_public_mutable_slot;
24+
FF delayed_public_mutable_slot = 0;
2525

2626
bool operator==(const UpdateCheckEvent& other) const = default;
2727
};

barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/update_check.cpp

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include "barretenberg/vm2/simulation/gadgets/update_check.hpp"
22

3-
#include "barretenberg/vm2/common/aztec_constants.hpp"
43
#include "barretenberg/vm2/common/constants.hpp"
54
#include "barretenberg/vm2/common/stringify.hpp"
65
#include "barretenberg/vm2/simulation/lib/merkle.hpp"
@@ -9,8 +8,12 @@ namespace bb::avm2::simulation {
98

109
namespace {
1110

12-
using UnconstrainedPoseidon2 = crypto::Poseidon2<crypto::Poseidon2Bn254ScalarFieldParams>;
13-
11+
/**
12+
* @brief Read a leaf value from the public data tree at a given slot without emitting constrained events.
13+
* @param merkle_db The low-level merkle database interface for unconstrained reads.
14+
* @param leaf_slot The slot to read from in the public data tree.
15+
* @return The leaf value if present, 0 otherwise.
16+
*/
1417
FF unconstrained_read(const LowLevelMerkleDBInterface& merkle_db, const FF& leaf_slot)
1518
{
1619
auto [present, index] = merkle_db.get_low_indexed_leaf(world_state::MerkleTreeId::PUBLIC_DATA_TREE, leaf_slot);
@@ -20,6 +23,24 @@ FF unconstrained_read(const LowLevelMerkleDBInterface& merkle_db, const FF& leaf
2023

2124
} // namespace
2225

26+
/**
27+
* @brief Validate that a contract's current class ID is consistent with the delayed public mutable update state.
28+
*
29+
* Reads the delayed public mutable hash from the public data tree. If the hash is zero, the contract was never
30+
* updated and current_class_id must equal original_class_id. Otherwise, reads the preimage (metadata, pre, post)
31+
* in unconstrained mode, verifies it against the hash, decomposes the metadata to extract the timestamp of change,
32+
* and selects the pre or post class ID based on whether the update has taken effect yet.
33+
*
34+
* Emits a single UpdateCheckEvent on success with all fields populated. When the hash is zero, the preimage
35+
* fields (metadata, pre_class_id, post_class_id) are all zero in the emitted event.
36+
*
37+
* @param address The contract address to check.
38+
* @param instance The contract instance containing original and current class IDs to validate.
39+
* @throws std::runtime_error If hash is zero and current_class_id does not match original_class_id.
40+
* @throws std::runtime_error If the stored hash does not match the reconstructed preimage hash (sanity check).
41+
* @throws std::runtime_error If the expected class ID (derived from update state and timestamp) does not match
42+
* current_class_id.
43+
*/
2344
void UpdateCheck::check_current_class_id(const AztecAddress& address, const ContractInstance& instance)
2445
{
2546
// Compute the public data tree slots
@@ -30,22 +51,28 @@ void UpdateCheck::check_current_class_id(const AztecAddress& address, const Cont
3051
// where we store in one public data tree slot the hash of the whole structure. This is nice because in circuits you
3152
// can receive the preimage as a hint and just read 1 storage slot instead of 3. We do that here, we will constrain
3253
// 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);
54+
FF update_hash =
55+
merkle_db.storage_read(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, delayed_public_mutable_hash_slot);
3456

3557
uint256_t update_preimage_metadata = 0;
3658
FF update_preimage_pre_class_id = 0;
3759
FF update_preimage_post_class_id = 0;
3860

3961
uint64_t current_timestamp = globals.timestamp;
4062

41-
if (hash == 0) {
63+
if (update_hash == 0) {
4264
// If the delayed public mutable has never been written, then the contract was never updated. We short circuit
4365
// early.
4466
if (instance.original_contract_class_id != instance.current_contract_class_id) {
4567
throw std::runtime_error("Current class id does not match expected class id");
4668
}
4769
} else {
48-
// Read the preimage from the tree in unconstrained mode
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.
4976
LowLevelMerkleDBInterface& unconstrained_merkle_db = merkle_db.as_unconstrained();
5077

5178
std::vector<FF> update_preimage(3);
@@ -59,7 +86,7 @@ void UpdateCheck::check_current_class_id(const AztecAddress& address, const Cont
5986
// Double check that the unconstrained reads match the hash. This is just a sanity check, if slow, can be
6087
// removed.
6188
FF reconstructed_hash = poseidon2.hash(update_preimage);
62-
if (hash != reconstructed_hash) {
89+
if (update_hash != reconstructed_hash) {
6390
throw std::runtime_error("Stored hash does not match preimage hash");
6491
}
6592

@@ -73,11 +100,14 @@ void UpdateCheck::check_current_class_id(const AztecAddress& address, const Cont
73100
// bit timestamp being packed in 32 bits is a tech debt that is not worth tackling.
74101
uint64_t timestamp_of_change =
75102
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.
76105
range_check.assert_range(update_metadata_hi,
77106
UPDATES_DELAYED_PUBLIC_MUTABLE_METADATA_BIT_SIZE - TIMESTAMP_OF_CHANGE_BIT_SIZE);
78107
range_check.assert_range(timestamp_of_change, TIMESTAMP_OF_CHANGE_BIT_SIZE);
79108

80-
// pre and post can be zero, if they have never been touched. In that case we need to use the original class id.
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.
81111
FF pre_class =
82112
update_preimage_pre_class_id == 0 ? instance.original_contract_class_id : update_preimage_pre_class_id;
83113
FF post_class =
@@ -98,7 +128,7 @@ void UpdateCheck::check_current_class_id(const AztecAddress& address, const Cont
98128
.original_class_id = instance.original_contract_class_id,
99129
.public_data_tree_root = merkle_db.get_tree_state().public_data_tree.tree.root,
100130
.current_timestamp = current_timestamp,
101-
.update_hash = hash,
131+
.update_hash = update_hash,
102132
.update_preimage_metadata = update_preimage_metadata,
103133
.update_preimage_pre_class_id = update_preimage_pre_class_id,
104134
.update_preimage_post_class_id = update_preimage_post_class_id,

barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/update_check.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
#pragma once
22

3+
#include "barretenberg/vm2/common/aztec_types.hpp"
4+
#include "barretenberg/vm2/simulation/events/event_emitter.hpp"
35
#include "barretenberg/vm2/simulation/events/update_check.hpp"
4-
#include "barretenberg/vm2/simulation/gadgets/poseidon2.hpp"
5-
#include "barretenberg/vm2/simulation/gadgets/range_check.hpp"
66
#include "barretenberg/vm2/simulation/interfaces/db.hpp"
7+
#include "barretenberg/vm2/simulation/interfaces/gt.hpp"
8+
#include "barretenberg/vm2/simulation/interfaces/poseidon2.hpp"
9+
#include "barretenberg/vm2/simulation/interfaces/range_check.hpp"
710
#include "barretenberg/vm2/simulation/interfaces/update_check.hpp"
811

912
namespace bb::avm2::simulation {

barretenberg/cpp/src/barretenberg/vm2/tracegen/update_check_trace.cpp

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
11
#include "barretenberg/vm2/tracegen/update_check_trace.hpp"
22

3-
#include <memory>
4-
5-
#include "barretenberg/vm2/common/aztec_constants.hpp"
3+
#include "barretenberg/numeric/uint256/uint256.hpp"
4+
#include "barretenberg/vm2/common/constants.hpp"
5+
#include "barretenberg/vm2/generated/columns.hpp"
66
#include "barretenberg/vm2/generated/relations/lookups_update_check.hpp"
7-
#include "barretenberg/vm2/tracegen/lib/interaction_def.hpp"
87

98
namespace bb::avm2::tracegen {
109

10+
/**
11+
* @brief Process the UpdateCheck events and populate the relevant columns in the trace.
12+
*
13+
* Events are emitted in the following flavors:
14+
* - Hash is zero (never updated): preimage fields (metadata, pre_class_id, post_class_id) are all zero;
15+
* hash_not_zero is false and most conditional columns are inactive.
16+
* - Hash is nonzero (update exists): all fields are populated; metadata is decomposed into
17+
* update_hi_metadata and timestamp_of_change, and the pre/post class ID zero flags are derived.
18+
*
19+
* @param events Container of UpdateCheckEvent to process.
20+
* @param trace The trace container to populate.
21+
*/
1122
void UpdateCheckTraceBuilder::process(
1223
const simulation::EventEmitterInterface<simulation::UpdateCheckEvent>::Container& events, TraceContainer& trace)
1324
{
@@ -17,12 +28,13 @@ void UpdateCheckTraceBuilder::process(
1728

1829
for (const auto& event : events) {
1930
uint256_t update_metadata = static_cast<uint256_t>(event.update_preimage_metadata);
20-
uint256_t update_metadata_hi = update_metadata >> 32;
31+
uint256_t update_metadata_hi = update_metadata >> TIMESTAMP_OF_CHANGE_BIT_SIZE;
2132

2233
// Note that the code below no longer works after 2106 as by that time the timestamp will overflow u32. The 64
2334
// bit timestamp being packed in 32 bits is a tech debt that is not worth tackling.
2435
uint64_t timestamp_of_change = static_cast<uint64_t>(static_cast<uint32_t>(update_metadata & 0xffffffff));
2536

37+
bool hash_not_zero = event.update_hash != 0;
2638
bool timestamp_is_lt_timestamp_of_change = event.current_timestamp < timestamp_of_change;
2739
bool update_pre_class_id_is_zero = event.update_preimage_pre_class_id == 0;
2840
bool update_post_class_id_is_zero = event.update_preimage_post_class_id == 0;
@@ -40,7 +52,7 @@ void UpdateCheckTraceBuilder::process(
4052
{ C::update_check_timestamp_pi_offset, AVM_PUBLIC_INPUTS_GLOBAL_VARIABLES_TIMESTAMP_ROW_IDX },
4153
{ C::update_check_update_hash, event.update_hash },
4254
{ C::update_check_update_hash_inv, event.update_hash }, // Will be inverted in batch later
43-
{ C::update_check_hash_not_zero, event.update_hash != 0 },
55+
{ C::update_check_hash_not_zero, hash_not_zero },
4456
{ C::update_check_update_preimage_metadata, event.update_preimage_metadata },
4557
{ C::update_check_update_preimage_pre_class_id, event.update_preimage_pre_class_id },
4658
{ C::update_check_update_preimage_post_class_id, event.update_preimage_post_class_id },
@@ -49,7 +61,7 @@ void UpdateCheckTraceBuilder::process(
4961
{ C::update_check_delayed_public_mutable_slot, event.delayed_public_mutable_slot },
5062
{ C::update_check_delayed_public_mutable_hash_slot,
5163
event.delayed_public_mutable_slot + UPDATES_DELAYED_PUBLIC_MUTABLE_VALUES_LEN },
52-
{ C::update_check_deployer_protocol_contract_address, CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS },
64+
{ C::update_check_contract_instance_registry_address, CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS },
5365
{ C::update_check_timestamp_of_change, timestamp_of_change },
5466
{ C::update_check_update_hi_metadata, update_metadata_hi },
5567
{ C::update_check_update_hi_metadata_bit_size,
@@ -76,7 +88,7 @@ void UpdateCheckTraceBuilder::process(
7688

7789
const InteractionDefinition UpdateCheckTraceBuilder::interactions =
7890
InteractionDefinition()
79-
.add<lookup_update_check_timestamp_from_public_inputs_settings, InteractionType::LookupGeneric>()
91+
.add<lookup_update_check_timestamp_from_public_inputs_settings, InteractionType::LookupIntoIndexedByRow>()
8092
.add<lookup_update_check_update_hash_poseidon2_settings, InteractionType::LookupSequential>()
8193
.add<lookup_update_check_delayed_public_mutable_slot_poseidon2_settings, InteractionType::LookupSequential>()
8294
.add<lookup_update_check_update_hash_public_data_read_settings, InteractionType::LookupGeneric>()

barretenberg/cpp/src/barretenberg/vm2/tracegen/update_check_trace.hpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
#pragma once
22

3-
#include <memory>
4-
5-
#include "barretenberg/vm2/generated/columns.hpp"
63
#include "barretenberg/vm2/simulation/events/event_emitter.hpp"
74
#include "barretenberg/vm2/simulation/events/update_check.hpp"
85
#include "barretenberg/vm2/tracegen/lib/interaction_def.hpp"

0 commit comments

Comments
 (0)