Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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