Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d936b01
fix(avm): Fix relative addressing in fuzzer (#19550)
sirasistant Jan 14, 2026
9e814d3
Merge branch 'next' into merge-train/avm
Jan 14, 2026
e67fc66
feat(avm): avm fuzzer bytecode mutation (#19378)
IlyasRidhuan Jan 14, 2026
98e4015
chore(avm): there is automatic conversion from uint128_t to FF
fcarreiro Jan 14, 2026
78113df
Merge branch 'next' into merge-train/avm
Jan 14, 2026
2df4b2b
Merge branch 'next' into merge-train/avm
Jan 14, 2026
d4f9e7a
Merge branch 'next' into merge-train/avm
Jan 14, 2026
5e6a1d8
chore(avm): ECC pre-audit - normalise infinity points (#19462)
MirandaWood Jan 14, 2026
d260adf
feat(bb-pilcom): single-component graph check (#19578)
defkit Jan 14, 2026
87cbf1e
feat(avm): contract class mutation (#19498)
IlyasRidhuan Jan 14, 2026
eb87e79
Merge branch 'next' into merge-train/avm
Jan 14, 2026
dfd2ac1
chore: support uint128_t in uint256_t construction (#19581)
fcarreiro Jan 14, 2026
ad2658d
Merge branch 'next' into merge-train/avm
Jan 14, 2026
da03768
fix!: remove unused column in update_check.pil (#19557)
dbanks12 Jan 14, 2026
e632bc6
Merge branch 'next' into merge-train/avm
Jan 14, 2026
18d05a3
fix(avm)!: pre-audit review of context.pil (#19549)
jeanmon Jan 14, 2026
43bd70e
Merge branch 'next' into merge-train/avm
Jan 14, 2026
9becf93
fix(avm): Relax fuzzer memory manager asserts (#19591)
sirasistant Jan 14, 2026
81a968d
Merge branch 'next' into merge-train/avm
Jan 14, 2026
0821666
Merge branch 'next' into merge-train/avm
Jan 14, 2026
0b8078e
fix!: sha256.pil missing input propagation constraints (#19590)
dbanks12 Jan 14, 2026
913e20b
Merge branch 'next' into merge-train/avm
Jan 14, 2026
c5bd41f
Merge branch 'next' into merge-train/avm
Jan 14, 2026
ad9393e
Merge branch 'next' into merge-train/avm
Jan 14, 2026
d7d29ac
Merge branch 'next' into merge-train/avm
Jan 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions barretenberg/cpp/pil/vm2/bytecode/update_check.pil
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,6 @@ namespace update_check;
pol commit delayed_public_mutable_hash_slot;
sel * (delayed_public_mutable_slot + constants.UPDATES_DELAYED_PUBLIC_MUTABLE_VALUES_LEN - delayed_public_mutable_hash_slot) = 0;

// TODO: Remove this as a column when we can lookup with constants
pol commit public_leaf_index_domain_separator;
sel * (constants.DOM_SEP__PUBLIC_LEAF_INDEX - public_leaf_index_domain_separator) = 0;

// TODO: Remove this as a column when we can lookup with constants
pol commit deployer_protocol_contract_address;
sel * (constants.CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS - deployer_protocol_contract_address) = 0;
Expand Down
636 changes: 446 additions & 190 deletions barretenberg/cpp/pil/vm2/context.pil

Large diffs are not rendered by default.

17 changes: 12 additions & 5 deletions barretenberg/cpp/pil/vm2/context_stack.pil
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
namespace context_stack;
pol commit sel; // @boolean
sel * (1 - sel) = 0;

#[skippable_if]
sel = 0;
// This subtrace is used to track the context stack. We do not define any constraint
// in this subtrace except for the main selector being boolean. Any active row contains
// relevant context entries which are copied (through permutation) from the execution
// sub-trace (context.pil) while entering a call and read in exiting a nested call.

// Usage from execution subtrace (context.pil):
// The active rows (where `sel == 1`) of this subtrace are defined by the permutation
Expand All @@ -12,14 +12,21 @@ namespace context_stack;
// using the destination selector `sel`.
// Therefore, the "pop/read" lookups can only read from legitimate rows.


pol commit sel; // @boolean
sel * (1 - sel) = 0;

#[skippable_if]
sel = 0;

pol commit entered_context_id; // Unique key/ID for a stack entry.
pol commit context_id;
pol commit parent_id;
pol commit next_pc;
pol commit msg_sender;
pol commit contract_address;
pol commit bytecode_id;
pol commit is_static;
pol commit is_static; // @boolean (on active row through permutation #[CTX_STACK_CALL])

pol commit parent_calldata_addr;
pol commit parent_calldata_size;
Expand Down
1 change: 1 addition & 0 deletions barretenberg/cpp/pil/vm2/ecc.pil
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ namespace ecc;
add_op * (1 - add_op) = 0;

// When the sel is on, we are either doubling, adding or handling the edge case
#[OP_CHECK]
sel = double_op + add_op + INFINITY_PRED;

// Point P in affine form
Expand Down
20 changes: 17 additions & 3 deletions barretenberg/cpp/pil/vm2/ecc_mem.pil
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ include "gt.pil";
* N.B This subtrace writes the values within a single row (i.e. 3 output columns)
*
* This subtrace is connected to the ECC subtrace via a lookup. ECC is used by
* other subtraces internally (e.g., address derivation)
* other subtraces internally (e.g., address derivation). We re-map any input infinity points to (0, 0)
* so ECC correctly manages edge cases. Resulting infinity points are guaranteed to be (0, 0) by the
* ECC subtrace (see #[OUTPUT_X_COORD] and #[OUTPUT_Y_COORD]).
*/

namespace ecc_add_mem;
Expand Down Expand Up @@ -127,10 +129,22 @@ namespace ecc_add_mem;
pol commit sel_should_exec; // @boolean (by definition)
sel_should_exec = sel * (1 - err);

// Needs to be committed columns as they are used in the lookups
pol commit p_x_n, p_y_n;
pol commit q_x_n, q_y_n;

// We re-map input infinity points to (0, 0) for ecc.pil. Output infinities are already constrained to be (0, 0) in the subtrace.
// Note that we cannot use p_x, p_y, etc. because they are read from memory in execution's #[DISPATCH_TO_ECC_ADD].
sel_should_exec * (p_x_n - (1 - p_is_inf) * p_x - p_is_inf * ecc.INFINITY_X) = 0;
sel_should_exec * (p_y_n - (1 - p_is_inf) * p_y - p_is_inf * ecc.INFINITY_Y) = 0;
sel_should_exec * (q_x_n - (1 - q_is_inf) * q_x - q_is_inf * ecc.INFINITY_X) = 0;
sel_should_exec * (q_y_n - (1 - q_is_inf) * q_y - q_is_inf * ecc.INFINITY_Y) = 0;


#[INPUT_OUTPUT_ECC_ADD]
sel_should_exec {
p_x, p_y, p_is_inf,
q_x, q_y, q_is_inf,
p_x_n, p_y_n, p_is_inf,
q_x_n, q_y_n, q_is_inf,
res_x, res_y, res_is_inf
} in
ecc.sel {
Expand Down
2 changes: 0 additions & 2 deletions barretenberg/cpp/pil/vm2/memory.pil
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,6 @@ pol commit sel_to_radix_write; // @boolean
sel_to_radix_write * (1 - sel_to_radix_write) = 0;

// Permutation consistency.
// TODO(#16759): Enable the following relation ensuring that each memory entry is associated with a valid permutation selector.
// DebugLog opcode is generating some memory entries which should not be triggered.
#[ACTIVE_ROW_NEEDS_PERM_SELECTOR]
sel = // Addressing.
sel_addressing_base
Expand Down
22 changes: 22 additions & 0 deletions barretenberg/cpp/pil/vm2/sha256.pil
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,28 @@ namespace sha256;
pol commit h;
start * (1 - err) * (h - init_h) = 0;

// Propagate initial state values across all rounds.
// Initial states must be propagated correctly becuse they are used in the `last` row
// when adding to the compression output. `last` is mutually exclusive to `perform_round` but
// these constraints don't need to use `last` because on the 64th row of `perform_round` we
// will constrain the next row (i.e. the `last` row) to be correct.
#[PROPAGATE_INIT_A]
perform_round * (init_a' - init_a) = 0;
#[PROPAGATE_INIT_B]
perform_round * (init_b' - init_b) = 0;
#[PROPAGATE_INIT_C]
perform_round * (init_c' - init_c) = 0;
#[PROPAGATE_INIT_D]
perform_round * (init_d' - init_d) = 0;
#[PROPAGATE_INIT_E]
perform_round * (init_e' - init_e) = 0;
#[PROPAGATE_INIT_F]
perform_round * (init_f' - init_f) = 0;
#[PROPAGATE_INIT_G]
perform_round * (init_g' - init_g) = 0;
#[PROPAGATE_INIT_H]
perform_round * (init_h' - init_h) = 0;

// ========== COMPUTE W =============
// w is only computed on the 16th round (as 0-15 are populated from the input)
// s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3)
Expand Down
3 changes: 2 additions & 1 deletion barretenberg/cpp/pil/vm2/tx.pil
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,8 @@ namespace tx;
// ordering of the public call requests prescribed by the public inputs. Note that a re-ordering of
// enqueued calls in execution trace would be possible though, but it would not affect the security.
// Such a re-ordering would consist of changing the row positions (clk) but not the temporal ordering
// determined by the context_id's.
// determined by the context_id's. Nevertheless, the constraint #[INCR_NEXT_CONTEXT_ID] in context.pil prevents
// such a re-ordering to happen.
#[DISPATCH_EXEC_END]
should_process_call_request {
next_context_id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,22 @@ void FuzzerContractDB::add_contracts(const ContractDeploymentData& contract_depl
}
}

void FuzzerContractDB::add_contract_class(const ContractClassId& class_id, const ContractClass& contract_class)
void FuzzerContractDB::add_contract_class(const ContractClassId& class_id,
const ContractClassWithCommitment& contract_class)
{
// todo(ilyas): think of a nicer way without both map and vector
// Only push to vector if not already present, otherwise we get duplicates sent to the TS simulator
if (contract_classes.contains(class_id)) {
return;
}
contract_classes_vector.push_back(contract_class);
contract_classes[class_id] = contract_class;
auto klass = ContractClass{
.id = contract_class.id,
.artifact_hash = contract_class.artifact_hash,
.private_functions_root = contract_class.private_functions_root,
.packed_bytecode = contract_class.packed_bytecode,
};
contract_classes_vector.push_back(klass);
contract_classes[class_id] = klass;
}

void FuzzerContractDB::add_contract_instance(const AztecAddress& address, const ContractInstance& contract_instance)
Expand Down Expand Up @@ -237,4 +244,10 @@ void FuzzerWorldStateManager::write_fee_payer_balance(const AztecAddress& fee_pa
ws->update_public_data(PublicDataLeafValue(leaf_slot, balance), fork_id);
}

void FuzzerWorldStateManager::public_data_write(const bb::crypto::merkle_tree::PublicDataLeafValue& public_data)
{
auto fork_id = fork_ids.top();
ws->update_public_data(public_data, fork_id);
}

} // namespace bb::avm2::fuzzer
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class FuzzerContractDB : public simulation::ContractDBInterface {
void add_contracts(const ContractDeploymentData& contract_deployment_data) override;

// Direct methods to add contract class and instance
void add_contract_class(const ContractClassId& class_id, const ContractClass& contract_class);
void add_contract_class(const ContractClassId& class_id, const ContractClassWithCommitment& contract_class);
void add_contract_instance(const AztecAddress& address, const ContractInstance& contract_instance);

void create_checkpoint() override;
Expand Down Expand Up @@ -88,6 +88,7 @@ class FuzzerWorldStateManager {
void reset_world_state();
void register_contract_address(const AztecAddress& contract_address);
void write_fee_payer_balance(const AztecAddress& fee_payer, const FF& balance);
void public_data_write(const bb::crypto::merkle_tree::PublicDataLeafValue& public_data);

world_state::WorldStateRevision get_current_revision() const;
world_state::WorldStateRevision fork();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ void ControlFlow::process_insert_simple_instruction_block(InsertSimpleInstructio
return;
}
auto instruction_block = instruction_blocks->at(instruction.instruction_block_idx % instruction_blocks->size());
for (const auto& instr : instruction_block) {
current_block->process_instruction(instr);
}
current_block->process_instruction_block(instruction_block);
}

void ControlFlow::process_jump_to_new_block(JumpToNewBlock instruction)
Expand All @@ -55,9 +53,7 @@ void ControlFlow::process_jump_to_new_block(JumpToNewBlock instruction)
instruction_blocks->at(instruction.target_program_block_instruction_block_idx % instruction_blocks->size());
ProgramBlock* target_block = new ProgramBlock();
current_block->finalize_with_jump(target_block);
for (const auto& instr : target_instruction_block) {
target_block->process_instruction(instr);
}
target_block->process_instruction_block(target_instruction_block);
current_block = target_block;
}

Expand All @@ -76,12 +72,8 @@ void ControlFlow::process_jump_if_to_new_block(JumpIfToNewBlock instruction)
ProgramBlock* target_then_block = new ProgramBlock();
ProgramBlock* target_else_block = new ProgramBlock();
current_block->finalize_with_jump_if(target_then_block, target_else_block, instruction.condition_offset_index);
for (const auto& instr : target_then_instruction_block) {
target_then_block->process_instruction(instr);
}
for (const auto& instr : target_else_instruction_block) {
target_else_block->process_instruction(instr);
}
target_then_block->process_instruction_block(target_then_instruction_block);
target_else_block->process_instruction_block(target_else_instruction_block);
current_block = target_then_block;
}

Expand Down Expand Up @@ -178,9 +170,7 @@ void ControlFlow::process_insert_internal_call(InsertInternalCall instruction)
instruction_blocks->at(instruction.target_program_block_instruction_block_idx % instruction_blocks->size());
ProgramBlock* target_block = new ProgramBlock();
current_block->insert_internal_call(target_block);
for (const auto& instr : target_instruction_block) {
target_block->process_instruction(instr);
}
target_block->process_instruction_block(target_instruction_block);
target_block->caller = current_block;
current_block = target_block;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

#include "barretenberg/avm_fuzzer/fuzz_lib/instruction.hpp"
#include "barretenberg/avm_fuzzer/fuzz_lib/program_block.hpp"
#include "barretenberg/avm_fuzzer/mutations/instructions/instruction_block.hpp"
#include "barretenberg/serialize/msgpack.hpp"
#include "barretenberg/vm2/testing/instruction_builder.hpp"
#include <vector>

using InstructionBlock = bb::avm2::fuzzer::InstructionBlock;

struct ReturnOptions {
uint8_t return_size;
MemoryTagWrapper return_value_tag;
Expand Down Expand Up @@ -139,7 +142,7 @@ class ControlFlow {
ProgramBlock* current_block;
/// @brief the entry block of the program
ProgramBlock* start_block;
std::vector<std::vector<FuzzInstruction>>* instruction_blocks;
std::vector<InstructionBlock>* instruction_blocks;

/// @brief add instructions to the current block from the instruction block at the given index
/// taken modulo length of the instruction blocks vector
Expand Down Expand Up @@ -200,7 +203,7 @@ class ControlFlow {
std::vector<ProgramBlock*> get_reachable_blocks(ProgramBlock* block);

public:
ControlFlow(std::vector<std::vector<FuzzInstruction>>& instruction_blocks)
ControlFlow(std::vector<InstructionBlock>& instruction_blocks)
: current_block(new ProgramBlock())
, start_block(current_block)
, instruction_blocks(&instruction_blocks)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ SimulatorResult fuzz_against_ts_simulator(FuzzerData& fuzzer_data, FuzzerContext

try {
ws_mgr->checkpoint();
cpp_result = cpp_simulator.simulate(*ws_mgr, contract_db, tx);
cpp_result = cpp_simulator.simulate(*ws_mgr, contract_db, tx, /*public_data_writes=*/{});
ws_mgr->revert();
} catch (const std::exception& e) {
throw std::runtime_error(std::string("CppSimulator threw an exception: ") + e.what());
}

ws_mgr->checkpoint();
auto js_result = js_simulator->simulate(*ws_mgr, contract_db, tx);
auto js_result = js_simulator->simulate(*ws_mgr, contract_db, tx, /*public_data_writes=*/{});

context.reset();

Expand Down
Loading
Loading