Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion barretenberg/cpp/pil/vm2/constants_gen.pil
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ namespace constants;
pol AVM_EXEC_OP_ID_NOTEHASH_EXISTS = 32768;
pol AVM_EXEC_OP_ID_EMIT_NOTEHASH = 65536;
pol AVM_EXEC_OP_ID_L1_TO_L2_MESSAGE_EXISTS = 131072;
pol AVM_EXEC_OP_ID_NULLIFIEREXISTS = 262144;
pol AVM_EXEC_OP_ID_NULLIFIER_EXISTS = 262144;
pol AVM_EXEC_OP_ID_EMIT_NULLIFIER = 524288;
pol AVM_EXEC_OP_ID_ALU_ADD = 1;
pol AVM_EXEC_OP_ID_ALU_SUB = 2;
pol AVM_EXEC_OP_ID_ALU_MUL = 4;
Expand Down
2 changes: 1 addition & 1 deletion barretenberg/cpp/pil/vm2/context.pil
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ namespace execution;
pol commit prev_l1_l2_tree_size;

// Next Tree State
pol commit note_hash_tree_root; //TODO: Constrain root, sizes and emitted not changing unless specific opcode selectors are on
pol commit note_hash_tree_root; // TODO: Constrain root, sizes and emitted not changing unless specific opcode selectors are on
pol commit note_hash_tree_size;
pol commit num_note_hashes_emitted;

Expand Down
6 changes: 5 additions & 1 deletion barretenberg/cpp/pil/vm2/execution.pil
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ include "opcodes/notehash_exists.pil";
include "opcodes/emit_notehash.pil";
include "opcodes/l1_to_l2_message_exists.pil";
include "opcodes/nullifier_exists.pil";
include "opcodes/emit_nullifier.pil";

namespace execution;

Expand Down Expand Up @@ -397,6 +398,7 @@ pol commit sel_execute_notehash_exists;
pol commit sel_execute_emit_notehash;
pol commit sel_execute_l1_to_l2_message_exists;
pol commit sel_execute_nullifier_exists;
pol commit sel_execute_emit_nullifier;
sel_execute_get_env_var * (1 - sel_execute_get_env_var) = 0;
sel_execute_mov * (1 - sel_execute_mov) = 0;
sel_execute_jump * (1 - sel_execute_jump) = 0;
Expand All @@ -416,6 +418,7 @@ sel_execute_notehash_exists * (1 - sel_execute_notehash_exists) = 0;
sel_execute_emit_notehash * (1 - sel_execute_emit_notehash) = 0;
sel_execute_l1_to_l2_message_exists * (1 - sel_execute_l1_to_l2_message_exists) = 0;
sel_execute_nullifier_exists * (1 - sel_execute_nullifier_exists) = 0;
sel_execute_emit_nullifier * (1 - sel_execute_emit_nullifier) = 0;

#[EXEC_OP_ID_DECOMPOSITION]
sel_execute_get_env_var * constants.AVM_EXEC_OP_ID_GETENVVAR +
Expand All @@ -436,7 +439,8 @@ sel_execute_sstore * constants.AVM_EXEC_OP_ID_SSTORE +
sel_execute_notehash_exists * constants.AVM_EXEC_OP_ID_NOTEHASH_EXISTS +
sel_execute_emit_notehash * constants.AVM_EXEC_OP_ID_EMIT_NOTEHASH +
sel_execute_l1_to_l2_message_exists * constants.AVM_EXEC_OP_ID_L1_TO_L2_MESSAGE_EXISTS +
sel_execute_nullifier_exists * constants.AVM_EXEC_OP_ID_NULLIFIEREXISTS
sel_execute_nullifier_exists * constants.AVM_EXEC_OP_ID_NULLIFIER_EXISTS +
sel_execute_emit_nullifier * constants.AVM_EXEC_OP_ID_EMIT_NULLIFIER
// We force the selectors to be 0 if we are not executing an opcode
// or when we are not in the execution subtrace.
= sel_should_execute_opcode * sel_execute_execution * subtrace_operation_id;
Expand Down
2 changes: 2 additions & 0 deletions barretenberg/cpp/pil/vm2/opcodes/emit_notehash.pil
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ namespace execution; // this is a virtual gadget that shares rows with the execu
pol REMAINING_NOTE_HASH_WRITES = constants.MAX_NOTE_HASHES_PER_TX -
prev_num_note_hashes_emitted;

// TODO(dbanks12): error if in static context

pol commit remaining_note_hashes_inv;
// Opcode errors if REMAINING_NOTE_HASH_WRITES is 0
#[EMIT_NOTEHASH_MAX_NOTE_HASH_WRITES_REACHED]
Expand Down
85 changes: 85 additions & 0 deletions barretenberg/cpp/pil/vm2/opcodes/emit_nullifier.pil
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
include "../constants_gen.pil";

/**
* This virtual gadget implements the EmitNullifier opcode, which attempts to write a new nullifier
* to the nullifier tree for the current contract address. It also increments the nullifier tree size
* and the number of nullifiers emitted.
*
* The opcode is gated by the `sel_execute_emit_nullifier` selector, which is set to 1 if the
* EmitNullifier opcode has reached dispatch (there are no earlier errors).
*
* This opcode uses:
* - register[0] as the nullifier input register (FF)
*
* Interactions:
* - The gadget performs a lookup into the nullifier_check gadget to attempt to write the nullifier.
* - This lookup is only performed if no limit error occurs.
*
* Errors:
* - If the max nullifier writes limit has been reached, the operation raises an opcode error.
* - If the nullifier already exists (collision), the operation raises an opcode error.
*
* If there are errors in earlier temporality groups (i.e. the opcode is not dispatched),
* all selectors should be 0 and intermediate values should be 0.
*/
namespace execution; // this is a virtual gadget that shares rows with the execution trace

// No relations will be checked if this identity is satisfied.
#[skippable_if]
sel_execute_emit_nullifier = 0; // from execution.pil.

// =========== VALIDATION ===========

// TODO(dbanks12): error if in static context

pol REMAINING_NULLIFIER_WRITES = constants.MAX_NULLIFIERS_PER_TX -
prev_num_nullifiers_emitted;

pol commit sel_write_nullifier;
// LIMIT_ERROR implies that we do NOT write the nullifier via the lookup later
pol LIMIT_ERROR = 1 - sel_write_nullifier;

pol commit remaining_nullifiers_inv;
// Limit error if REMAINING_NULLIFIER_WRITES is 0
#[EMIT_NULLIFIER_MAX_NULLIFIER_WRITES_REACHED]
sel_execute_emit_nullifier * (REMAINING_NULLIFIER_WRITES * (LIMIT_ERROR * (1 - remaining_nullifiers_inv) + remaining_nullifiers_inv) - 1 + LIMIT_ERROR) = 0;

// A limit error must cause an "opcode error".
// if LIMIT_ERROR == 1, sel_opcode_error must be 1
// but otherwise, we don't force a value for sel_opcode_error and instead let the lookup below set it.
#[OPCODE_ERROR_IF_LIMIT_ERROR]
sel_execute_emit_nullifier * LIMIT_ERROR * (1 - sel_opcode_error) = 0;

// =========== OPCODE EXECUTION ===========

// Lookup into nullifier tree check to attempt write
#[WRITE_NULLIFIER]
sel_write_nullifier {
/*nullifier=*/ register[0], // input: nullifier to write
prev_nullifier_tree_root, // input: pre-write tree root (from context.pil)
/*exists=*/ sel_opcode_error, // output: 1 if nullifier already exists (error)
nullifier_tree_root, // output: new tree root (updates context.pil column!)
prev_nullifier_tree_size, // input: pre-write tree size (from context.pil)
discard, // input: (from execution_discard) will this write eventually be reverted (so it shouldn't be written to public inputs)?
/*nullifier_index=*/ prev_num_nullifiers_emitted, // input: the nullifier index to write to in public inputs
/*should_silo=1*/ sel_write_nullifier, // input: should_silo = 1 (always silo for contract nullifiers)
contract_address // input: contract address for siloing (from context)
} in nullifier_check.write {
nullifier_check.nullifier,
nullifier_check.root,
nullifier_check.exists, // nullifier collision
nullifier_check.write_root, // post-write tree root
nullifier_check.tree_size_before_write,
nullifier_check.discard,
nullifier_check.nullifier_index, // index to write to in public inputs
nullifier_check.should_silo,
nullifier_check.address // contract address for siloing
};

pol SUCCESSFUL_WRITE = sel_write_nullifier * (1 - sel_opcode_error);

#[EMIT_NULLIFIER_TREE_SIZE_INCREASE]
sel_execute_emit_nullifier * (prev_nullifier_tree_size + SUCCESSFUL_WRITE - nullifier_tree_size) = 0;

#[EMIT_NULLIFIER_NUM_NULLIFIERS_EMITTED_INCREASE]
sel_execute_emit_nullifier * (prev_num_nullifiers_emitted + SUCCESSFUL_WRITE - num_nullifiers_emitted) = 0;
2 changes: 2 additions & 0 deletions barretenberg/cpp/pil/vm2/opcodes/sstore.pil
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ namespace execution; // this is a virtual gadget that shares rows with the execu

// =========== VALIDATION ===========

// TODO(dbanks12): error if in static context

pol commit max_data_writes_reached;
max_data_writes_reached * (1 - max_data_writes_reached) = 0;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@
#define AVM_EXEC_OP_ID_NOTEHASH_EXISTS 32768
#define AVM_EXEC_OP_ID_EMIT_NOTEHASH 65536
#define AVM_EXEC_OP_ID_L1_TO_L2_MESSAGE_EXISTS 131072
#define AVM_EXEC_OP_ID_NULLIFIEREXISTS 262144
#define AVM_EXEC_OP_ID_NULLIFIER_EXISTS 262144
#define AVM_EXEC_OP_ID_EMIT_NULLIFIER 524288
#define AVM_EXEC_OP_ID_ALU_ADD 1
#define AVM_EXEC_OP_ID_ALU_SUB 2
#define AVM_EXEC_OP_ID_ALU_MUL 4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,13 @@ const std::unordered_map<ExecutionOpCode, ExecInstructionSpec> EXEC_INSTRUCTION_
.add_inputs({ /*nullifier*/ ValueTag::FF,
/*address*/ ValueTag::FF })
.add_output(/*exists*/) } },
{ ExecutionOpCode::EMITNULLIFIER,
{ .num_addresses = 1,
.gas_cost = { .opcode_gas = AVM_EMITNULLIFIER_BASE_L2_GAS,
.base_da = AVM_EMITNULLIFIER_BASE_DA_GAS,
.dyn_l2 = 0,
.dyn_da = 0 },
.register_info = RegisterInfo().add_input(/*nullifier*/ ValueTag::FF) } },
{ ExecutionOpCode::GETCONTRACTINSTANCE,
{ .num_addresses = 2,
.gas_cost = { .opcode_gas = AVM_GETCONTRACTINSTANCE_BASE_L2_GAS, .base_da = 0, .dyn_l2 = 0, .dyn_da = 0 },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ TEST(NullifierExistsConstrainingTest, PositiveTest)
{ C::execution_mem_tag_reg_1_, static_cast<uint8_t>(MemoryTag::FF) },
{ C::execution_mem_tag_reg_2_, static_cast<uint8_t>(MemoryTag::U1) },
{ C::execution_sel_opcode_error, 0 },
{ C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_NULLIFIEREXISTS } } });
{ C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_NULLIFIER_EXISTS } } });
check_relation<nullifier_exists>(trace);
}

Expand All @@ -70,7 +70,7 @@ TEST(NullifierExistsConstrainingTest, PositiveNullifierNotExists)
{ C::execution_mem_tag_reg_1_, static_cast<uint8_t>(MemoryTag::FF) },
{ C::execution_mem_tag_reg_2_, static_cast<uint8_t>(MemoryTag::U1) },
{ C::execution_sel_opcode_error, 0 },
{ C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_NULLIFIEREXISTS } } });
{ C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_NULLIFIER_EXISTS } } });
check_relation<nullifier_exists>(trace);
}

Expand All @@ -86,7 +86,7 @@ TEST(NullifierExistsConstrainingTest, NegativeInvalidOutputTag)
{ C::execution_mem_tag_reg_1_, static_cast<uint8_t>(MemoryTag::FF) },
{ C::execution_mem_tag_reg_2_, static_cast<uint8_t>(MemoryTag::U8) }, // WRONG!
{ C::execution_sel_opcode_error, 0 },
{ C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_NULLIFIEREXISTS } } });
{ C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_NULLIFIER_EXISTS } } });
EXPECT_THROW_WITH_MESSAGE(
check_relation<nullifier_exists>(trace, nullifier_exists::SR_NULLIFIER_EXISTS_U1_OUTPUT_TAG),
"NULLIFIER_EXISTS_U1_OUTPUT_TAG");
Expand Down Expand Up @@ -135,7 +135,7 @@ TEST(NullifierExistsConstrainingTest, Interactions)
{ C::execution_mem_tag_reg_2_, static_cast<uint8_t>(MemoryTag::U1) },
{ C::execution_prev_nullifier_tree_root, nullifier_tree_snapshot.root },
{ C::execution_sel_opcode_error, 0 },
{ C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_NULLIFIEREXISTS },
{ C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_NULLIFIER_EXISTS },
} });

NullifierTreeCheckTraceBuilder nullifier_tree_check_trace_builder;
Expand Down
Loading
Loading