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
22 changes: 7 additions & 15 deletions avm-transpiler/src/transpile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -777,43 +777,35 @@ fn handle_nullifier_exists(
destinations: &[ValueOrArray],
inputs: &[ValueOrArray],
) {
if destinations.len() != 1 || inputs.len() != 2 {
if destinations.len() != 1 || inputs.len() != 1 {
panic!(
"Transpiler expects ForeignCall::CHECKNULLIFIEREXISTS to have 1 destinations and 2 inputs, got {} and {}",
"Transpiler expects ForeignCall::CHECKNULLIFIEREXISTS to have 1 destinations and 1 input, got {} and {}",
destinations.len(),
inputs.len()
);
}
let nullifier_offset_operand = match &inputs[0] {
ValueOrArray::MemoryAddress(offset) => offset,
_ => panic!(
"Transpiler does not know how to handle ForeignCall::EMITNOTEHASH with HeapArray/Vector inputs"
),
};
let address_offset_operand = match &inputs[1] {
let siloed_nullifier_offset_operand = match &inputs[0] {
ValueOrArray::MemoryAddress(offset) => offset,
_ => panic!(
"Transpiler does not know how to handle ForeignCall::EMITNOTEHASH with HeapArray/Vector inputs"
"Transpiler does not know how to handle ForeignCall::NULLIFIEREXISTS with HeapArray/Vector inputs"
),
};
let exists_offset_operand = match &destinations[0] {
ValueOrArray::MemoryAddress(offset) => offset,
_ => panic!(
"Transpiler does not know how to handle ForeignCall::EMITNOTEHASH with HeapArray/Vector inputs"
"Transpiler does not know how to handle ForeignCall::NULLIFIEREXISTS with HeapArray/Vector inputs"
),
};
avm_instrs.push(AvmInstruction {
opcode: AvmOpcode::NULLIFIEREXISTS,
addressing_mode: Some(
AddressingModeBuilder::default()
.direct_operand(nullifier_offset_operand)
.direct_operand(address_offset_operand)
.direct_operand(siloed_nullifier_offset_operand)
.direct_operand(exists_offset_operand)
.build(),
),
operands: vec![
AvmOperand::U16 { value: nullifier_offset_operand.to_u32() as u16 },
AvmOperand::U16 { value: address_offset_operand.to_u32() as u16 },
AvmOperand::U16 { value: siloed_nullifier_offset_operand.to_u32() as u16 },
AvmOperand::U16 { value: exists_offset_operand.to_u32() as u16 },
],
..Default::default()
Expand Down
9 changes: 7 additions & 2 deletions barretenberg/cpp/pil/vm2/ecc.pil
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* This subtrace supports point addition over the Grumpkin curve.
* Given two points, P & Q, this trace computes R = P + Q.
* PRECONDITIONS: The only assumption here is that the inputs P & Q are points on the Grumpkin curve (note that the Point at Infinity is considered on the curve):
* PRECONDITIONS: The only assumption here is that the inputs P & Q are points on the Grumpkin curve (note that the Point at Infinity = (0, 0) is considered on the curve):
* Grumpkin Curve Eqn in SW form: Y^2 = X^3 − 17.
* Note: Grumpkin forms a 2-cycle with BN254, i.e the base field of one is the scalar field of the other and vice-versa.
*
Expand Down Expand Up @@ -40,7 +40,7 @@ namespace ecc;
// P = (p_x, p_y, p_is_inf), Q = (q_x, q_y, q_is_inf), R = (r_x, r_y, r_is_inf),
// where the coordinates satisfy:
// y^2 = x^3 - 17 (unless is_inf is true).
// The point at infinity, O, does not exist on the curve (a property of SW curves). We represent it as:
// The point at infinity, O, does not have valid coordinates (a property of SW curves). We represent it as:
// O = (0, 0, true).
// Note: this is NOT enforced here for inputs, see ecc_mem.pil for example of constraining.
//
Expand Down Expand Up @@ -124,6 +124,11 @@ namespace ecc;
#[DOUBLE_PRED]
double_op - (x_match * y_match) = 0;

// If double_op is true, we check that p_is_inf == q_is_inf. #[DOUBLE_PRED] implies this (since x_match &
// y_match must imply p_is_inf == q_is_inf for points on the curve) however we include an extra check for
// the #[DOUBLE] lookup in scalar_mul, which ignores q_is_inf.
double_op * (p_is_inf - q_is_inf) = 0;

pol commit inv_2_p_y;
(1 - result_infinity) * double_op * (2 * p_y * inv_2_p_y - 1) = 0;

Expand Down
26 changes: 12 additions & 14 deletions barretenberg/cpp/pil/vm2/opcodes/nullifier_exists.pil
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
include "../constants_gen.pil";

/**
* This virtual gadget implements the NullifierExists opcode, which checks if a nullifier exists
* in the nullifier tree for a given contract address.
* This virtual gadget implements the NullifierExists opcode, which checks if a siloed nullifier exists
* in the nullifier tree.
*
* The opcode is gated by the `sel_execute_nullifier_exists` selector, which is set to 1 if the
* NullifierExists opcode has reached dispatch (there are no earlier errors).
*
* This opcode uses:
* - register[0] as the nullifier input register (FF)
* - register[1] as the contract address input register (FF)
* - register[2] as the output register (boolean result to be tagged as u1)
* - register[0] as the siloed nullifier input register (FF)
* - register[1] as the output register (boolean result to be tagged as u1)
*
* Memory reads and writes are handled by standard execution logic.
*
* The gadget performs a lookup into the nullifier_check gadget to determine if the nullifier
* exists for the specified contract address. The result is written to the output register.
* exists. The nullifier is already siloed by the caller (no siloing happens in this opcode).
* The result is written to the output register.
*
* If there are errors in earlier temporality groups (e.g. address resolution or out-of-gas errors),
* all selectors should be 0 and output register & intermediate values should be 0.
Expand All @@ -32,25 +32,23 @@ namespace execution; // this is a virtual gadget that shares rows with the execu
#[NULLIFIER_EXISTS_CHECK]
sel_execute_nullifier_exists {
// Outputs
/*exists=*/ register[2], // output: whether nullifier exists
/*exists=*/ register[1], // output: whether nullifier exists
// Inputs
/*nullifier=*/ register[0], // input: nullifier to check
/*nullifier=*/ register[0], // input: siloed nullifier to check
prev_nullifier_tree_root, // input: tree root from context
/*should_silo=1*/ sel_execute_nullifier_exists, // input: should_silo = 1 (always silo for contract nullifiers)
/*contract_address=*/ register[1] // input: contract address for siloing
/*should_silo=0*/ precomputed.zero // input: should_silo = 0 (nullifier is already siloed, address also omitted)
} in nullifier_check.sel {
// Outputs
nullifier_check.exists,
// Inputs
nullifier_check.nullifier,
nullifier_check.root,
nullifier_check.should_silo,
nullifier_check.address
nullifier_check.should_silo
};

// Tag result (`exists`) as u1 (via mem_tag_reg[2]).
// Tag result (`exists`) as u1 (via mem_tag_reg[1]).
#[NULLIFIER_EXISTS_U1_OUTPUT_TAG]
sel_execute_nullifier_exists * (constants.MEM_TAG_U1 - mem_tag_reg[2]) = 0;
sel_execute_nullifier_exists * (constants.MEM_TAG_U1 - mem_tag_reg[1]) = 0;

// This opcode is infallible and sel_opcode_error is constrained to be zero
// in execution.pil (see #[INFALLIBLE_OPCODES_SUCCESS]).
8 changes: 4 additions & 4 deletions barretenberg/cpp/pil/vm2/scalar_mul.pil
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,10 @@ namespace scalar_mul;
temp_inf' - temp_inf' = 0;

#[DOUBLE]
sel_not_end { temp_x, temp_y, temp_inf, temp_x', temp_y', temp_inf', temp_x', temp_y', temp_inf' }
sel_not_end { temp_x, temp_y, temp_inf, temp_x', temp_y', temp_inf', sel_not_end /* = 1 */ }
in
ecc.sel
{ ecc.r_x, ecc.r_y, ecc.r_is_inf, ecc.p_x, ecc.p_y, ecc.p_is_inf, ecc.q_x, ecc.q_y, ecc.q_is_inf };
{ ecc.r_x, ecc.r_y, ecc.r_is_inf, ecc.p_x, ecc.p_y, ecc.p_is_inf, ecc.double_op };

///////////////////////////////
// Result Computation
Expand All @@ -191,8 +191,8 @@ namespace scalar_mul;

// If end is true, conditional assign res as (bit = 1) point or (bit = 0) infinity (mapped to (0,0)).
// Else, conditional assign res as (bit = 1) res' + temp, constrained by the lookup #[ADD], or unchanged from the next row.
end * (point_x * bit - res_x) = 0;
end * (point_y * bit - res_y) = 0;
end * (point_x * bit + ecc.INFINITY_X * (1 - bit) - res_x) = 0;
end * (point_y * bit + ecc.INFINITY_Y * (1 - bit) - res_y) = 0;
end * ((point_inf - 1) * bit + 1 - res_inf) = 0;

// Needs to be committed because it's used in the #[ADD] lookup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -476,14 +476,12 @@ struct EMITNULLIFIER_Instruction {
MSGPACK_FIELDS(nullifier_address);
};

/// @brief NULLIFIEREXISTS: checks if nullifier exists in the nullifier tree
/// Gets contract's address by GETENVVAR(0)
/// M[result_offset] = NULLIFIEREXISTS(M[nullifier_offset_index], GETENVVAR(0))
/// @brief NULLIFIEREXISTS: checks if a siloed nullifier exists in the nullifier tree
/// M[result_address] = NULLIFIEREXISTS(M[siloed_nullifier_address])
struct NULLIFIEREXISTS_Instruction {
ParamRef nullifier_address;
AddressRef contract_address_address; // absolute address where the contract address will be stored
ParamRef siloed_nullifier_address; // The already-siloed nullifier to check
AddressRef result_address;
MSGPACK_FIELDS(nullifier_address, contract_address_address, result_address);
MSGPACK_FIELDS(siloed_nullifier_address, result_address);
};

/// @brief L1TOL2MSGEXISTS: Check if a L1 to L2 message exists
Expand Down Expand Up @@ -848,8 +846,7 @@ inline std::ostream& operator<<(std::ostream& os, const FuzzInstruction& instruc
},
[&](EMITNULLIFIER_Instruction arg) { os << "EMITNULIFIER_Instruction " << arg.nullifier_address; },
[&](NULLIFIEREXISTS_Instruction arg) {
os << "NULLIFIEREXISTS_Instruction " << arg.nullifier_address << " " << arg.contract_address_address
<< " " << arg.result_address;
os << "NULLIFIEREXISTS_Instruction " << arg.siloed_nullifier_address << " " << arg.result_address;
},
[&](L1TOL2MSGEXISTS_Instruction arg) {
os << "L1TOL2MSGEXISTS_Instruction " << arg.msg_hash_address << " " << arg.leaf_index_address << " "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1029,25 +1029,18 @@ void ProgramBlock::process_nullifierexists_instruction(NULLIFIEREXISTS_Instructi
#ifdef DISABLE_NULLIFIEREXISTS_INSTRUCTION
return;
#endif
auto nullifier_address_operand = memory_manager.get_resolved_address_and_operand_16(instruction.nullifier_address);
auto contract_address_operand =
memory_manager.get_resolved_address_and_operand_16(instruction.contract_address_address);
auto siloed_nullifier_operand =
memory_manager.get_resolved_address_and_operand_16(instruction.siloed_nullifier_address);
auto result_address_operand = memory_manager.get_resolved_address_and_operand_16(instruction.result_address);
if (!nullifier_address_operand.has_value() || !contract_address_operand.has_value() ||
!result_address_operand.has_value()) {
if (!siloed_nullifier_operand.has_value() || !result_address_operand.has_value()) {
return;
}

preprocess_memory_addresses(nullifier_address_operand.value().first);
preprocess_memory_addresses(contract_address_operand.value().first);
preprocess_memory_addresses(siloed_nullifier_operand.value().first);
preprocess_memory_addresses(result_address_operand.value().first);
auto get_contract_address_instruction =
GETENVVAR_Instruction{ .result_address = instruction.contract_address_address, .type = 0 };
this->process_getenvvar_instruction(get_contract_address_instruction);

auto nullifierexists_instruction = bb::avm2::testing::InstructionBuilder(bb::avm2::WireOpCode::NULLIFIEREXISTS)
.operand(nullifier_address_operand.value().second)
.operand(contract_address_operand.value().second)
.operand(siloed_nullifier_operand.value().second)
.operand(result_address_operand.value().second)
.build();
instructions.push_back(nullifierexists_instruction);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,12 +379,11 @@ constexpr GetEnvVarMutationConfig BASIC_GETENVVAR_MUTATION_CONFIGURATION = GetEn
{ GetEnvVarMutationOptions::type, 1 },
});

enum class NullifierExistsMutationOptions { nullifier_address, contract_address_address, result_address };
using NullifierExistsMutationConfig = WeightedSelectionConfig<NullifierExistsMutationOptions, 3>;
enum class NullifierExistsMutationOptions { siloed_nullifier_address, result_address };
using NullifierExistsMutationConfig = WeightedSelectionConfig<NullifierExistsMutationOptions, 2>;

constexpr NullifierExistsMutationConfig BASIC_NULLIFIER_EXISTS_MUTATION_CONFIGURATION = NullifierExistsMutationConfig({
{ NullifierExistsMutationOptions::nullifier_address, 1 },
{ NullifierExistsMutationOptions::contract_address_address, 1 },
{ NullifierExistsMutationOptions::siloed_nullifier_address, 1 },
{ NullifierExistsMutationOptions::result_address, 1 },
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,7 @@ std::vector<FuzzInstruction> InstructionMutator::generate_instruction(std::mt199
case InstructionGenerationOptions::EMITNULLIFIER:
return { EMITNULLIFIER_Instruction{ .nullifier_address = generate_variable_ref(rng) } };
case InstructionGenerationOptions::NULLIFIEREXISTS:
return { NULLIFIEREXISTS_Instruction{ .nullifier_address = generate_variable_ref(rng),
.contract_address_address = generate_address_ref(rng, MAX_16BIT_OPERAND),
return { NULLIFIEREXISTS_Instruction{ .siloed_nullifier_address = generate_variable_ref(rng),
.result_address = generate_address_ref(rng, MAX_16BIT_OPERAND) } };
case InstructionGenerationOptions::L1TOL2MSGEXISTS:
return { L1TOL2MSGEXISTS_Instruction{ .msg_hash_address = generate_variable_ref(rng),
Expand Down Expand Up @@ -1300,11 +1299,8 @@ void InstructionMutator::mutate_nullifier_exists_instruction(NULLIFIEREXISTS_Ins
{
NullifierExistsMutationOptions option = BASIC_NULLIFIER_EXISTS_MUTATION_CONFIGURATION.select(rng);
switch (option) {
case NullifierExistsMutationOptions::nullifier_address:
mutate_param_ref(instruction.nullifier_address, rng, MemoryTag::FF, MAX_16BIT_OPERAND);
break;
case NullifierExistsMutationOptions::contract_address_address:
mutate_address_ref(instruction.contract_address_address, rng, MAX_16BIT_OPERAND);
case NullifierExistsMutationOptions::siloed_nullifier_address:
mutate_param_ref(instruction.siloed_nullifier_address, rng, MemoryTag::FF, MAX_16BIT_OPERAND);
break;
case NullifierExistsMutationOptions::result_address:
mutate_address_ref(instruction.result_address, rng, MAX_16BIT_OPERAND);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@
#define AVM_SSTORE_BASE_L2_GAS 33140
#define AVM_NOTEHASHEXISTS_BASE_L2_GAS 504
#define AVM_EMITNOTEHASH_BASE_L2_GAS 19275
#define AVM_NULLIFIEREXISTS_BASE_L2_GAS 924
#define AVM_NULLIFIEREXISTS_BASE_L2_GAS 903
#define AVM_EMITNULLIFIER_BASE_L2_GAS 30800
#define AVM_L1TOL2MSGEXISTS_BASE_L2_GAS 540
#define AVM_GETCONTRACTINSTANCE_BASE_L2_GAS 6108
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const std::unordered_map<WireOpCode, std::array<uint8_t, NUM_OP_DC_SELECTORS>>&
{ WireOpCode::SSTORE, { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
{ WireOpCode::NOTEHASHEXISTS, { 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
{ WireOpCode::EMITNOTEHASH, { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
{ WireOpCode::NULLIFIEREXISTS, { 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
{ WireOpCode::NULLIFIEREXISTS, { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
{ WireOpCode::EMITNULLIFIER, { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
{ WireOpCode::L1TOL2MSGEXISTS, { 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
{ WireOpCode::GETCONTRACTINSTANCE, { 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
Expand Down Expand Up @@ -352,7 +352,7 @@ const std::unordered_map<WireOpCode, WireInstructionSpec>& get_wire_instruction_
.op_dc_selectors = get_wire_opcode_dc_selectors().at(WireOpCode::EMITNOTEHASH) } },
{ WireOpCode::NULLIFIEREXISTS,
{ .exec_opcode = ExecutionOpCode::NULLIFIEREXISTS,
.size_in_bytes = 8,
.size_in_bytes = 6,
.op_dc_selectors = get_wire_opcode_dc_selectors().at(WireOpCode::NULLIFIEREXISTS) } },
{ WireOpCode::EMITNULLIFIER,
{ .exec_opcode = ExecutionOpCode::EMITNULLIFIER,
Expand Down Expand Up @@ -664,12 +664,9 @@ const std::unordered_map<ExecutionOpCode, ExecInstructionSpec>& get_exec_instruc
.dyn_da = 0 },
.register_info = RegisterInfo().add_input(/*note_hash*/ ValueTag::FF) } },
{ ExecutionOpCode::NULLIFIEREXISTS,
{ .num_addresses = 3,
{ .num_addresses = 2,
.gas_cost = { .opcode_gas = AVM_NULLIFIEREXISTS_BASE_L2_GAS, .base_da = 0, .dyn_l2 = 0, .dyn_da = 0 },
.register_info = RegisterInfo()
.add_inputs({ /*nullifier*/ ValueTag::FF,
/*address*/ ValueTag::FF })
.add_output(/*exists*/) } },
.register_info = RegisterInfo().add_input(/*siloed_nullifier*/ ValueTag::FF).add_output(/*exists*/) } },
{ ExecutionOpCode::EMITNULLIFIER,
{ .num_addresses = 1,
.gas_cost = { .opcode_gas = AVM_EMITNULLIFIER_BASE_L2_GAS,
Expand Down
Loading
Loading