diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index 86fa48739e0e..228f638b797f 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -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() diff --git a/barretenberg/cpp/pil/vm2/ecc.pil b/barretenberg/cpp/pil/vm2/ecc.pil index 6df904d02530..7759d2161547 100644 --- a/barretenberg/cpp/pil/vm2/ecc.pil +++ b/barretenberg/cpp/pil/vm2/ecc.pil @@ -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. * @@ -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. // @@ -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; diff --git a/barretenberg/cpp/pil/vm2/opcodes/nullifier_exists.pil b/barretenberg/cpp/pil/vm2/opcodes/nullifier_exists.pil index 7b8764e09da2..0e0a1f059d82 100644 --- a/barretenberg/cpp/pil/vm2/opcodes/nullifier_exists.pil +++ b/barretenberg/cpp/pil/vm2/opcodes/nullifier_exists.pil @@ -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. @@ -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]). diff --git a/barretenberg/cpp/pil/vm2/scalar_mul.pil b/barretenberg/cpp/pil/vm2/scalar_mul.pil index e31635c423fd..2072db8ee5e4 100644 --- a/barretenberg/cpp/pil/vm2/scalar_mul.pil +++ b/barretenberg/cpp/pil/vm2/scalar_mul.pil @@ -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 @@ -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 diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/instruction.hpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/instruction.hpp index 1127cb908e1c..73f88580e7af 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/instruction.hpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/instruction.hpp @@ -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 @@ -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 << " " diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/program_block.cpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/program_block.cpp index 33d34c6efaf0..2069cc2e7f17 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/program_block.cpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/program_block.cpp @@ -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); diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/configuration.hpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/configuration.hpp index b861c6eb1f29..ad56d3ab67f3 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/configuration.hpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/configuration.hpp @@ -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; +enum class NullifierExistsMutationOptions { siloed_nullifier_address, result_address }; +using NullifierExistsMutationConfig = WeightedSelectionConfig; 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 }, }); diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/instructions/instruction.cpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/instructions/instruction.cpp index f385d4e3ce67..4d7d4ea8f83f 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/instructions/instruction.cpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/instructions/instruction.cpp @@ -236,8 +236,7 @@ std::vector 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), @@ -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); diff --git a/barretenberg/cpp/src/barretenberg/vm2/common/aztec_constants.hpp b/barretenberg/cpp/src/barretenberg/vm2/common/aztec_constants.hpp index 3daf3566b1e1..285169135f3f 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/common/aztec_constants.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/common/aztec_constants.hpp @@ -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 diff --git a/barretenberg/cpp/src/barretenberg/vm2/common/instruction_spec.cpp b/barretenberg/cpp/src/barretenberg/vm2/common/instruction_spec.cpp index 0cbb00a54e8f..10deaff5b9d9 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/common/instruction_spec.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/common/instruction_spec.cpp @@ -76,7 +76,7 @@ const std::unordered_map>& { 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 } }, @@ -352,7 +352,7 @@ const std::unordered_map& 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, @@ -664,12 +664,9 @@ const std::unordered_map& 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, diff --git a/barretenberg/cpp/src/barretenberg/vm2/constraining/avm_fixed_vk.hpp b/barretenberg/cpp/src/barretenberg/vm2/constraining/avm_fixed_vk.hpp index a52bafe06bda..cfcfff2e078a 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/constraining/avm_fixed_vk.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/constraining/avm_fixed_vk.hpp @@ -17,7 +17,7 @@ class AvmHardCodedVKAndHash { using FF = bb::curve::BN254::ScalarField; // Precomputed VK hash (hash of all commitments below). - static FF vk_hash() { return FF(uint256_t("0x17049c998b2045d77a08e3f2514bef84f0270d873e24e19d89b9ef957a52b7cb")); } + static FF vk_hash() { return FF(uint256_t("0x2dad15751fd563ba68a47e276af7722591d67e76ed631efaf571c9a6948542ae")); } static constexpr std::array get_all() { @@ -70,9 +70,9 @@ class AvmHardCodedVKAndHash { uint256_t( "0x090dda25e7d64ab5cabe09fd80fbb731af2a98de7a608157dc10394b4fc022a4")), // precomputed_exec_opcode_dynamic_l2_gas Commitment( - uint256_t("0x21e67fcf50679e466fb930e885e9f43bec952b0f61671f2cfa2f9ee115887002"), + uint256_t("0x2216a1693dcb1cc83f57ea8058f681d71bdf0e6cfc839502cf16fb0a88a5f673"), uint256_t( - "0x033657b3b7eab8d049f258e2cf4616826c65a5e0e3e4cc7c1b8869857c05ae9e")), // precomputed_exec_opcode_opcode_gas + "0x255e6760ed9adda61aca7d0b7d4bb28bb62e3cca6e860009461a9a1708184be2")), // precomputed_exec_opcode_opcode_gas Commitment( uint256_t("0x296def9415d1c96b4d8ab91df5f59ad8522a726f98461b1ab5c4d4c5b22471a4"), uint256_t( @@ -99,9 +99,9 @@ class AvmHardCodedVKAndHash { uint256_t( "0x0000000000000000000000000000000000000000000000000000000000000002")), // precomputed_first_row Commitment( - uint256_t("0x00d3b534945cae272828a9621e350a4f42efe4258f3432b2ba7a535a6f8bd68f"), + uint256_t("0x1e497723c3f95466c480f1ac1addb1e0dc68bb123cae27ee70d00e6d6fcc6896"), uint256_t( - "0x07924546f2d14918d809f440d770e88525d4787c828e1efcb6154e1c2b257da9")), // precomputed_instr_size + "0x24c9a31064fb5f18c18ac3ea4be1a10809765a43b06bcea177fbb171dd547ced")), // precomputed_instr_size Commitment( uint256_t("0x11b710f896157a9557278a1f776cd6c7e1e7e256a572bd080797daaf1d6307d1"), uint256_t( @@ -214,13 +214,13 @@ class AvmHardCodedVKAndHash { uint256_t( "0x0c76ef320d793294cfbf1519c7a124b640859b99d43d051dc828f0053081a4f0")), // precomputed_rw_reg_0_ Commitment( - uint256_t("0x23d96c05f4bc75a456d34d051f876ef99ad7b22c4e21908b13b9f576a9d4c620"), + uint256_t("0x1514f55599854ffc9929fab5629758348be02434c30ff6fdda82f8754e05703e"), uint256_t( - "0x008b8cb54710e5387557d73f2122ddd02a393ced7c57987776b55d6292964d89")), // precomputed_rw_reg_1_ + "0x2cab4a1a6d88bd0b45a60416aa72c8761afe05af7f60b6ae2013d2244c7634fe")), // precomputed_rw_reg_1_ Commitment( - uint256_t("0x039eae92cc21bf3c73b8406d17e8a06154a76ea489dfa0fb6049b9750a40b388"), + uint256_t("0x13acdb89fe8349f7339bb20baa54d50fe9e15b3d515ee14096bfc204ec144222"), uint256_t( - "0x1e2e477b3a65fc69df47516f7d306b81d3205d1e1983aadafe99f6aea755d944")), // precomputed_rw_reg_2_ + "0x176b78b990ea79d06072fb91fd96b2a8472376baf05016f668d2c3162d0a7984")), // precomputed_rw_reg_2_ Commitment::infinity(), // precomputed_rw_reg_3_ Commitment::infinity(), // precomputed_rw_reg_4_ Commitment::infinity(), // precomputed_rw_reg_5_ @@ -261,9 +261,9 @@ class AvmHardCodedVKAndHash { uint256_t( "0x04a79156fabb49e693ddcf07815f53d163489149958311b79a4fcfd2703bf3fd")), // precomputed_sel_mem_op_reg_1_ Commitment( - uint256_t("0x252ca1bf6e5e141f715b94f7c186675aed430fe49c8ec06e46160e41c9086c97"), + uint256_t("0x23194ac869ec4d3bd7e286588b4c021bf60032b57dd3f136308bfd7f7f3f4f37"), uint256_t( - "0x29d3d381d379ce261c1e66817822d796c6e605c276bf1f5993715ee56a5c7b82")), // precomputed_sel_mem_op_reg_2_ + "0x1405fa7e3bf07c30eb87ebce030c9288e67f5897a3d38c6f21d3c035e4a55a92")), // precomputed_sel_mem_op_reg_2_ Commitment( uint256_t("0x1530ccb47d1198320c163380a82ca8cbaf87b2d40ede856d21c60535e2251262"), uint256_t( @@ -338,9 +338,9 @@ class AvmHardCodedVKAndHash { uint256_t( "0x07aa17a6a67bcafb019d4adc0192a41f801563508f1ba7c64cd056731e2a7e01")), // precomputed_sel_op_dc_3 Commitment( - uint256_t("0x074d234606a4d5bb93e0b2ad331eb61bdeaf87a7813bcc2b06494251154d9fb8"), + uint256_t("0x23b6dc02dd758474624a21ac6f25c96e0439e161a2649034e459fc1977c3bf34"), uint256_t( - "0x13e4734b603d75d2e71ba58a0fcf7532b2007296d22365242432bd708f5ed76a")), // precomputed_sel_op_dc_4 + "0x08aaf4df0c48942efb9dd7dd8fc440edf0a1a84a3f20bf593e66f92a1bb39e70")), // precomputed_sel_op_dc_4 Commitment( uint256_t("0x0ddf9e9dd8363fd4119ac1d79553829192ac465e7ee6656f099e40e5a8b709b0"), uint256_t( @@ -370,9 +370,9 @@ class AvmHardCodedVKAndHash { uint256_t( "0x1945936772c40110b3ba7682c358ec4772d42e9b6152a4f8706fda2c4bbe85ff")), // precomputed_sel_op_is_address_1_ Commitment( - uint256_t("0x06ea2e61015bf705c8e4f76bcccf8549ff3c66d1aaaaba0eefa491c2629922b2"), + uint256_t("0x055865fd96b5dec0940fcb6e3abeaba208c5dee83b8a2f459daca685a4bc26c1"), uint256_t( - "0x08381a2b896ad123189cf793ad2a205484c9c269572b8041261b60a358e8eee3")), // precomputed_sel_op_is_address_2_ + "0x1b23b6b6412b0a5c96d195b8fbacb8d362d2fb08c49e523e2064431d2455a408")), // precomputed_sel_op_is_address_2_ Commitment( uint256_t("0x3052e46c51289f5e76d606f7b57dd4f535602a065abdb0c6e9d02355ea1a31aa"), uint256_t( @@ -426,9 +426,9 @@ class AvmHardCodedVKAndHash { uint256_t( "0x20361a4e1e73f07142325b1271d5fb172cb32252b44996dbed0264117cdb7b01")), // precomputed_sel_tag_check_reg_0_ Commitment( - uint256_t("0x061fc7f3ab86d2e539fa6acfa1a57c36ae3cdeb3f94f27fd4621e0b290a3e367"), + uint256_t("0x0e69699ba807e2b1b0c7f43462ec98fdd167798a2225036ccab37fce90d832f0"), uint256_t( - "0x2d6b6d6a6af0eb44678a40ac35a86dd3b2ba2529151d23589c1e65ff502dd888")), // precomputed_sel_tag_check_reg_1_ + "0x22cf1b04a5ba6078f995cb38394bff539fb715f2c6e46e6e3781a51ef5945392")), // precomputed_sel_tag_check_reg_1_ Commitment( uint256_t("0x1530ccb47d1198320c163380a82ca8cbaf87b2d40ede856d21c60535e2251262"), uint256_t( diff --git a/barretenberg/cpp/src/barretenberg/vm2/constraining/relations/nullifier_exists.test.cpp b/barretenberg/cpp/src/barretenberg/vm2/constraining/relations/nullifier_exists.test.cpp index 54423eaf2a03..648f29ebb83c 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/constraining/relations/nullifier_exists.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/constraining/relations/nullifier_exists.test.cpp @@ -36,6 +36,8 @@ using simulation::MockPoseidon2; using simulation::MockRangeCheck; using simulation::NullifierTreeCheck; using simulation::NullifierTreeCheckEvent; +using NullifierLeafValue = crypto::merkle_tree::NullifierLeafValue; +using NullifierTreeLeafPreimage = simulation::NullifierTreeLeafPreimage; using testing::NiceMock; @@ -48,13 +50,11 @@ TEST(NullifierExistsConstrainingTest, PositiveTest) { TestTraceContainer trace({ { { C::execution_sel, 1 }, { C::execution_sel_execute_nullifier_exists, 1 }, - { C::execution_register_0_, /*nullifier=*/FF(0x123456) }, - { C::execution_register_1_, /*address=*/FF(0xdeadbeef) }, - { C::execution_register_2_, /*exists=*/1 }, + { C::execution_register_0_, /*siloed_nullifier=*/FF(0x123456) }, + { C::execution_register_1_, /*exists=*/1 }, { C::execution_prev_nullifier_tree_root, FF(0xabc) }, { C::execution_mem_tag_reg_0_, static_cast(MemoryTag::FF) }, - { C::execution_mem_tag_reg_1_, static_cast(MemoryTag::FF) }, - { C::execution_mem_tag_reg_2_, static_cast(MemoryTag::U1) }, + { C::execution_mem_tag_reg_1_, static_cast(MemoryTag::U1) }, { C::execution_sel_opcode_error, 0 }, { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_NULLIFIER_EXISTS } } }); check_relation(trace); @@ -64,13 +64,11 @@ TEST(NullifierExistsConstrainingTest, PositiveNullifierNotExists) { TestTraceContainer trace({ { { C::execution_sel, 1 }, { C::execution_sel_execute_nullifier_exists, 1 }, - { C::execution_register_0_, /*nullifier=*/FF(0x123456) }, - { C::execution_register_1_, /*address=*/FF(0xdeadbeef) }, - { C::execution_register_2_, /*exists=*/0 }, // nullifier does not exist! + { C::execution_register_0_, /*siloed_nullifier=*/FF(0x123456) }, + { C::execution_register_1_, /*exists=*/0 }, // nullifier does not exist! { C::execution_prev_nullifier_tree_root, FF(0xabc) }, { C::execution_mem_tag_reg_0_, static_cast(MemoryTag::FF) }, - { C::execution_mem_tag_reg_1_, static_cast(MemoryTag::FF) }, - { C::execution_mem_tag_reg_2_, static_cast(MemoryTag::U1) }, + { C::execution_mem_tag_reg_1_, static_cast(MemoryTag::U1) }, { C::execution_sel_opcode_error, 0 }, { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_NULLIFIER_EXISTS } } }); check_relation(trace); @@ -80,13 +78,11 @@ TEST(NullifierExistsConstrainingTest, NegativeInvalidOutputTag) { TestTraceContainer trace({ { { C::execution_sel, 1 }, { C::execution_sel_execute_nullifier_exists, 1 }, - { C::execution_register_0_, /*nullifier=*/FF(0x123456) }, - { C::execution_register_1_, /*address=*/FF(0xdeadbeef) }, - { C::execution_register_2_, /*exists=*/0 }, // nullifier does not exist! + { C::execution_register_0_, /*siloed_nullifier=*/FF(0x123456) }, + { C::execution_register_1_, /*exists=*/0 }, // nullifier does not exist! { C::execution_prev_nullifier_tree_root, FF(0xabc) }, { C::execution_mem_tag_reg_0_, static_cast(MemoryTag::FF) }, - { C::execution_mem_tag_reg_1_, static_cast(MemoryTag::FF) }, - { C::execution_mem_tag_reg_2_, static_cast(MemoryTag::U8) }, // WRONG! + { C::execution_mem_tag_reg_1_, static_cast(MemoryTag::U8) }, // WRONG! { C::execution_sel_opcode_error, 0 }, { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_NULLIFIER_EXISTS } } }); EXPECT_THROW_WITH_MESSAGE( @@ -117,24 +113,27 @@ TEST(NullifierExistsConstrainingTest, Interactions) EventEmitter nullifier_tree_check_event_emitter; NullifierTreeCheck nullifier_tree_check(poseidon2, merkle_check, field_gt, nullifier_tree_check_event_emitter); - FF nullifier = 42; - FF address = 43; + // Siloed nullifier (no siloing happens in the opcode now) + FF siloed_nullifier = 42; + + // For exists=true, the low leaf's nullifier must match the searched nullifier + NullifierTreeLeafPreimage low_leaf = NullifierTreeLeafPreimage(NullifierLeafValue(siloed_nullifier), 0, 0); AppendOnlyTreeSnapshot nullifier_tree_snapshot = AppendOnlyTreeSnapshot{ .root = 42, .next_available_leaf_index = 128, }; - nullifier_tree_check.assert_read(nullifier, address, true, {}, 0, {}, nullifier_tree_snapshot); + // should_silo=false (address unused when not siloing) + nullifier_tree_check.assert_read( + siloed_nullifier, /*contract_address=*/std::nullopt, /*exists=*/true, low_leaf, 0, {}, nullifier_tree_snapshot); TestTraceContainer trace({ { { C::execution_sel_execute_nullifier_exists, 1 }, - { C::execution_register_0_, nullifier }, - { C::execution_register_1_, address }, - { C::execution_register_2_, /*exists=*/1 }, + { C::execution_register_0_, siloed_nullifier }, + { C::execution_register_1_, /*exists=*/1 }, { C::execution_mem_tag_reg_0_, static_cast(MemoryTag::FF) }, - { C::execution_mem_tag_reg_1_, static_cast(MemoryTag::FF) }, - { C::execution_mem_tag_reg_2_, static_cast(MemoryTag::U1) }, + { C::execution_mem_tag_reg_1_, static_cast(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_NULLIFIER_EXISTS }, diff --git a/barretenberg/cpp/src/barretenberg/vm2/generated/relations/ecc.hpp b/barretenberg/cpp/src/barretenberg/vm2/generated/relations/ecc.hpp index 70495c808b21..93df40ba85c2 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/generated/relations/ecc.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/generated/relations/ecc.hpp @@ -14,8 +14,8 @@ template class eccImpl { public: using FF = FF_; - static constexpr std::array SUBRELATION_PARTIAL_LENGTHS = { 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, - 5, 3, 5, 6, 6, 5, 6, 6, 3 }; + static constexpr std::array SUBRELATION_PARTIAL_LENGTHS = { 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, + 5, 3, 3, 5, 6, 6, 5, 6, 6, 3 }; template inline static bool skip(const AllEntities& in) { @@ -40,11 +40,11 @@ template class ecc : public Relation> { static constexpr size_t SR_X_MATCH = 8; static constexpr size_t SR_Y_MATCH = 10; static constexpr size_t SR_DOUBLE_PRED = 11; - static constexpr size_t SR_COMPUTED_LAMBDA = 13; - static constexpr size_t SR_INFINITY_RESULT = 15; - static constexpr size_t SR_OUTPUT_X_COORD = 16; - static constexpr size_t SR_OUTPUT_Y_COORD = 17; - static constexpr size_t SR_OUTPUT_INF_FLAG = 18; + static constexpr size_t SR_COMPUTED_LAMBDA = 14; + static constexpr size_t SR_INFINITY_RESULT = 16; + static constexpr size_t SR_OUTPUT_X_COORD = 17; + static constexpr size_t SR_OUTPUT_Y_COORD = 18; + static constexpr size_t SR_OUTPUT_INF_FLAG = 19; static std::string get_subrelation_label(size_t index) { diff --git a/barretenberg/cpp/src/barretenberg/vm2/generated/relations/ecc_impl.hpp b/barretenberg/cpp/src/barretenberg/vm2/generated/relations/ecc_impl.hpp index a39704ebdbda..5b709819d443 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/generated/relations/ecc_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/generated/relations/ecc_impl.hpp @@ -103,36 +103,42 @@ void eccImpl::accumulate(ContainerOverSubrelations& evals, } { using View = typename std::tuple_element_t<12, ContainerOverSubrelations>::View; + auto tmp = static_cast(in.get(C::ecc_double_op)) * + (static_cast(in.get(C::ecc_p_is_inf)) - static_cast(in.get(C::ecc_q_is_inf))); + std::get<12>(evals) += (tmp * scaling_factor); + } + { + using View = typename std::tuple_element_t<13, ContainerOverSubrelations>::View; auto tmp = (FF(1) - static_cast(in.get(C::ecc_result_infinity))) * static_cast(in.get(C::ecc_double_op)) * (FF(2) * static_cast(in.get(C::ecc_p_y)) * static_cast(in.get(C::ecc_inv_2_p_y)) - FF(1)); - std::get<12>(evals) += (tmp * scaling_factor); + std::get<13>(evals) += (tmp * scaling_factor); } { // COMPUTED_LAMBDA - using View = typename std::tuple_element_t<13, ContainerOverSubrelations>::View; + using View = typename std::tuple_element_t<14, ContainerOverSubrelations>::View; auto tmp = static_cast(in.get(C::ecc_sel)) * (static_cast(in.get(C::ecc_lambda)) - (static_cast(in.get(C::ecc_double_op)) * FF(3) * static_cast(in.get(C::ecc_p_x)) * static_cast(in.get(C::ecc_p_x)) * static_cast(in.get(C::ecc_inv_2_p_y)) + static_cast(in.get(C::ecc_add_op)) * CView(ecc_Y_DIFF) * static_cast(in.get(C::ecc_inv_x_diff)))); - std::get<13>(evals) += (tmp * scaling_factor); + std::get<14>(evals) += (tmp * scaling_factor); } { - using View = typename std::tuple_element_t<14, ContainerOverSubrelations>::View; + using View = typename std::tuple_element_t<15, ContainerOverSubrelations>::View; auto tmp = (static_cast(in.get(C::ecc_use_computed_result)) - static_cast(in.get(C::ecc_sel)) * CView(ecc_BOTH_NON_INF) * (FF(1) - CView(ecc_INVERSE_PRED))); - std::get<14>(evals) += (tmp * scaling_factor); + std::get<15>(evals) += (tmp * scaling_factor); } { // INFINITY_RESULT - using View = typename std::tuple_element_t<15, ContainerOverSubrelations>::View; + using View = typename std::tuple_element_t<16, ContainerOverSubrelations>::View; auto tmp = (static_cast(in.get(C::ecc_result_infinity)) - (CView(ecc_INVERSE_PRED) * CView(ecc_BOTH_NON_INF) + CView(ecc_BOTH_INF))); - std::get<15>(evals) += (tmp * scaling_factor); + std::get<16>(evals) += (tmp * scaling_factor); } { // OUTPUT_X_COORD - using View = typename std::tuple_element_t<16, ContainerOverSubrelations>::View; + using View = typename std::tuple_element_t<17, ContainerOverSubrelations>::View; auto tmp = static_cast(in.get(C::ecc_sel)) * (((static_cast(in.get(C::ecc_r_x)) - CView(ecc_EITHER_INF) * @@ -140,10 +146,10 @@ void eccImpl::accumulate(ContainerOverSubrelations& evals, static_cast(in.get(C::ecc_q_is_inf)) * static_cast(in.get(C::ecc_p_x)))) - static_cast(in.get(C::ecc_result_infinity)) * CView(ecc_INFINITY_X)) - static_cast(in.get(C::ecc_use_computed_result)) * CView(ecc_COMPUTED_R_X)); - std::get<16>(evals) += (tmp * scaling_factor); + std::get<17>(evals) += (tmp * scaling_factor); } { // OUTPUT_Y_COORD - using View = typename std::tuple_element_t<17, ContainerOverSubrelations>::View; + using View = typename std::tuple_element_t<18, ContainerOverSubrelations>::View; auto tmp = static_cast(in.get(C::ecc_sel)) * (((static_cast(in.get(C::ecc_r_y)) - CView(ecc_EITHER_INF) * @@ -151,13 +157,13 @@ void eccImpl::accumulate(ContainerOverSubrelations& evals, static_cast(in.get(C::ecc_q_is_inf)) * static_cast(in.get(C::ecc_p_y)))) - static_cast(in.get(C::ecc_result_infinity)) * CView(ecc_INFINITY_Y)) - static_cast(in.get(C::ecc_use_computed_result)) * CView(ecc_COMPUTED_R_Y)); - std::get<17>(evals) += (tmp * scaling_factor); + std::get<18>(evals) += (tmp * scaling_factor); } { // OUTPUT_INF_FLAG - using View = typename std::tuple_element_t<18, ContainerOverSubrelations>::View; + using View = typename std::tuple_element_t<19, ContainerOverSubrelations>::View; auto tmp = static_cast(in.get(C::ecc_sel)) * (static_cast(in.get(C::ecc_r_is_inf)) - static_cast(in.get(C::ecc_result_infinity))); - std::get<18>(evals) += (tmp * scaling_factor); + std::get<19>(evals) += (tmp * scaling_factor); } } diff --git a/barretenberg/cpp/src/barretenberg/vm2/generated/relations/lookups_nullifier_exists.hpp b/barretenberg/cpp/src/barretenberg/vm2/generated/relations/lookups_nullifier_exists.hpp index 30b3fa3e2693..59b92acb614e 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/generated/relations/lookups_nullifier_exists.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/generated/relations/lookups_nullifier_exists.hpp @@ -16,24 +16,22 @@ namespace bb::avm2 { struct lookup_nullifier_exists_nullifier_exists_check_settings_ { static constexpr std::string_view NAME = "LOOKUP_NULLIFIER_EXISTS_NULLIFIER_EXISTS_CHECK"; static constexpr std::string_view RELATION_NAME = "nullifier_exists"; - static constexpr size_t LOOKUP_TUPLE_SIZE = 5; + static constexpr size_t LOOKUP_TUPLE_SIZE = 4; static constexpr Column SRC_SELECTOR = Column::execution_sel_execute_nullifier_exists; static constexpr Column DST_SELECTOR = Column::nullifier_check_sel; static constexpr Column COUNTS = Column::lookup_nullifier_exists_nullifier_exists_check_counts; static constexpr Column INVERSES = Column::lookup_nullifier_exists_nullifier_exists_check_inv; static constexpr std::array SRC_COLUMNS = { - ColumnAndShifts::execution_register_2_, + ColumnAndShifts::execution_register_1_, ColumnAndShifts::execution_register_0_, ColumnAndShifts::execution_prev_nullifier_tree_root, - ColumnAndShifts::execution_sel_execute_nullifier_exists, - ColumnAndShifts::execution_register_1_ + ColumnAndShifts::precomputed_zero }; static constexpr std::array DST_COLUMNS = { ColumnAndShifts::nullifier_check_exists, ColumnAndShifts::nullifier_check_nullifier, ColumnAndShifts::nullifier_check_root, - ColumnAndShifts::nullifier_check_should_silo, - ColumnAndShifts::nullifier_check_address + ColumnAndShifts::nullifier_check_should_silo }; }; diff --git a/barretenberg/cpp/src/barretenberg/vm2/generated/relations/lookups_scalar_mul.hpp b/barretenberg/cpp/src/barretenberg/vm2/generated/relations/lookups_scalar_mul.hpp index 9a0c4cfc1184..55f174a4a09b 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/generated/relations/lookups_scalar_mul.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/generated/relations/lookups_scalar_mul.hpp @@ -44,22 +44,21 @@ using lookup_scalar_mul_to_radix_relation = lookup_relation_base SRC_COLUMNS = { - ColumnAndShifts::scalar_mul_temp_x, ColumnAndShifts::scalar_mul_temp_y, - ColumnAndShifts::scalar_mul_temp_inf, ColumnAndShifts::scalar_mul_temp_x_shift, - ColumnAndShifts::scalar_mul_temp_y_shift, ColumnAndShifts::scalar_mul_temp_inf_shift, - ColumnAndShifts::scalar_mul_temp_x_shift, ColumnAndShifts::scalar_mul_temp_y_shift, - ColumnAndShifts::scalar_mul_temp_inf_shift + ColumnAndShifts::scalar_mul_temp_x, ColumnAndShifts::scalar_mul_temp_y, + ColumnAndShifts::scalar_mul_temp_inf, ColumnAndShifts::scalar_mul_temp_x_shift, + ColumnAndShifts::scalar_mul_temp_y_shift, ColumnAndShifts::scalar_mul_temp_inf_shift, + ColumnAndShifts::scalar_mul_sel_not_end }; static constexpr std::array DST_COLUMNS = { - ColumnAndShifts::ecc_r_x, ColumnAndShifts::ecc_r_y, ColumnAndShifts::ecc_r_is_inf, - ColumnAndShifts::ecc_p_x, ColumnAndShifts::ecc_p_y, ColumnAndShifts::ecc_p_is_inf, - ColumnAndShifts::ecc_q_x, ColumnAndShifts::ecc_q_y, ColumnAndShifts::ecc_q_is_inf + ColumnAndShifts::ecc_r_x, ColumnAndShifts::ecc_r_y, ColumnAndShifts::ecc_r_is_inf, + ColumnAndShifts::ecc_p_x, ColumnAndShifts::ecc_p_y, ColumnAndShifts::ecc_p_is_inf, + ColumnAndShifts::ecc_double_op }; }; diff --git a/barretenberg/cpp/src/barretenberg/vm2/generated/relations/nullifier_exists_impl.hpp b/barretenberg/cpp/src/barretenberg/vm2/generated/relations/nullifier_exists_impl.hpp index 04d5a00b8052..12233fdaf672 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/generated/relations/nullifier_exists_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/generated/relations/nullifier_exists_impl.hpp @@ -20,7 +20,7 @@ void nullifier_existsImpl::accumulate(ContainerOverSubrelations& evals, { // NULLIFIER_EXISTS_U1_OUTPUT_TAG using View = typename std::tuple_element_t<0, ContainerOverSubrelations>::View; auto tmp = static_cast(in.get(C::execution_sel_execute_nullifier_exists)) * - (CView(constants_MEM_TAG_U1) - static_cast(in.get(C::execution_mem_tag_reg_2_))); + (CView(constants_MEM_TAG_U1) - static_cast(in.get(C::execution_mem_tag_reg_1_))); std::get<0>(evals) += (tmp * scaling_factor); } } diff --git a/barretenberg/cpp/src/barretenberg/vm2/generated/relations/scalar_mul_impl.hpp b/barretenberg/cpp/src/barretenberg/vm2/generated/relations/scalar_mul_impl.hpp index b557a2fba40d..ae73570c9a19 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/generated/relations/scalar_mul_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/generated/relations/scalar_mul_impl.hpp @@ -15,6 +15,8 @@ void scalar_mulImpl::accumulate(ContainerOverSubrelations& evals, { using C = ColumnAndShifts; + const auto ecc_INFINITY_X = FF(0); + const auto ecc_INFINITY_Y = FF(0); const auto scalar_mul_LATCH_CONDITION = in.get(C::scalar_mul_end) + in.get(C::precomputed_first_row); const auto scalar_mul_SHOULD_PASS = in.get(C::scalar_mul_sel_not_end) * (FF(1) - in.get(C::scalar_mul_bit)); @@ -157,14 +159,16 @@ void scalar_mulImpl::accumulate(ContainerOverSubrelations& evals, { using View = typename std::tuple_element_t<22, ContainerOverSubrelations>::View; auto tmp = static_cast(in.get(C::scalar_mul_end)) * - (static_cast(in.get(C::scalar_mul_point_x)) * static_cast(in.get(C::scalar_mul_bit)) - + ((static_cast(in.get(C::scalar_mul_point_x)) * static_cast(in.get(C::scalar_mul_bit)) + + CView(ecc_INFINITY_X) * (FF(1) - static_cast(in.get(C::scalar_mul_bit)))) - static_cast(in.get(C::scalar_mul_res_x))); std::get<22>(evals) += (tmp * scaling_factor); } { using View = typename std::tuple_element_t<23, ContainerOverSubrelations>::View; auto tmp = static_cast(in.get(C::scalar_mul_end)) * - (static_cast(in.get(C::scalar_mul_point_y)) * static_cast(in.get(C::scalar_mul_bit)) - + ((static_cast(in.get(C::scalar_mul_point_y)) * static_cast(in.get(C::scalar_mul_bit)) + + CView(ecc_INFINITY_Y) * (FF(1) - static_cast(in.get(C::scalar_mul_bit)))) - static_cast(in.get(C::scalar_mul_res_y))); std::get<23>(evals) += (tmp * scaling_factor); } diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/execution.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/execution.cpp index 9e948a9d226c..2ee374e33a78 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/execution.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/execution.cpp @@ -1233,36 +1233,31 @@ void Execution::note_hash_exists(ContextInterface& context, } /** - * @brief NULLIFIEREXISTS execution opcode handler: Check if a nullifier exists in the nullifier tree. + * @brief NULLIFIEREXISTS execution opcode handler: Check if a siloed nullifier exists in the nullifier tree. * * @param context The context. - * @param nullifier_offset The resolved address of the nullifier value. - * @param address_offset The resolved address of the address value. - * @param dst_addr The resolved address of the output memory value (boolean value U1). + * @param siloed_nullifier_offset The resolved address of the siloed nullifier value. + * @param exists_offset The resolved address of the output memory value (boolean value U1). * * @throws RegisterValidationException if the tags of the input values do not match the expected tags: * - tag of the memory value at nullifier_offset is not FF. - * - tag of the memory value at address_offset is not FF. * @throws OutOfGasException if the gas limit is exceeded. */ void Execution::nullifier_exists(ContextInterface& context, - MemoryAddress nullifier_offset, - MemoryAddress address_offset, + MemoryAddress siloed_nullifier_offset, MemoryAddress exists_offset) { BB_BENCH_NAME("Execution::nullifier_exists"); constexpr auto opcode = ExecutionOpCode::NULLIFIEREXISTS; auto& memory = context.get_memory(); - const auto& nullifier = memory.get(nullifier_offset); - const auto& address = memory.get(address_offset); - set_and_validate_inputs(opcode, { nullifier, address }); + const auto& siloed_nullifier = memory.get(siloed_nullifier_offset); + set_and_validate_inputs(opcode, { siloed_nullifier }); get_gas_tracker().consume_gas(); - // Check nullifier existence via MerkleDB - // (this also tag checks address and nullifier as FFs) - auto exists = merkle_db.nullifier_exists(address.as_ff(), nullifier.as_ff()); + // Check siloed nullifier existence via MerkleDB + auto exists = merkle_db.siloed_nullifier_exists(siloed_nullifier.as_ff()); // Write result to memory // (assigns tag u1 to result) diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/execution.hpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/execution.hpp index 4b52b737e158..f6f262c91b5d 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/execution.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/execution.hpp @@ -170,8 +170,7 @@ class Execution : public ExecutionInterface { MemoryAddress leaf_index_addr, MemoryAddress dst_addr); void nullifier_exists(ContextInterface& context, - MemoryAddress nullifier_offset, - MemoryAddress address_offset, + MemoryAddress siloed_nullifier_offset, MemoryAddress exists_offset); void emit_nullifier(ContextInterface& context, MemoryAddress nullifier_addr); void get_contract_instance(ContextInterface& context, diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/execution.test.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/execution.test.cpp index e66487434982..40bcaafc62f8 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/execution.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/execution.test.cpp @@ -888,23 +888,20 @@ TEST_F(ExecutionSimulationTest, L1ToL2MessageExistsOutOfRange) TEST_F(ExecutionSimulationTest, NullifierExists) { MemoryAddress nullifier_offset = 10; - MemoryAddress address_offset = 11; MemoryAddress exists_offset = 12; auto nullifier = MemoryValue::from(42); - auto address = MemoryValue::from(7); EXPECT_CALL(context, get_memory()); EXPECT_CALL(memory, get(nullifier_offset)).WillOnce(ReturnRef(nullifier)); - EXPECT_CALL(memory, get(address_offset)).WillOnce(ReturnRef(address)); EXPECT_CALL(gas_tracker, consume_gas(Gas{ 0, 0 })); - EXPECT_CALL(merkle_db, nullifier_exists(address.as_ff(), nullifier.as_ff())).WillOnce(Return(true)); + EXPECT_CALL(merkle_db, siloed_nullifier_exists(nullifier.as_ff())).WillOnce(Return(true)); EXPECT_CALL(memory, set(exists_offset, MemoryValue::from(1))); - execution.nullifier_exists(context, nullifier_offset, address_offset, exists_offset); + execution.nullifier_exists(context, nullifier_offset, exists_offset); } TEST_F(ExecutionSimulationTest, EmitNullifier) diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/serialization.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/serialization.cpp index ee46b26b5fde..c0e9bd91180a 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/serialization.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/serialization.cpp @@ -143,8 +143,7 @@ const std::unordered_map>& get_wire_opcode_ OperandType::INDIRECT8, OperandType::UINT16, } }, - { WireOpCode::NULLIFIEREXISTS, - { OperandType::INDIRECT8, OperandType::UINT16, OperandType::UINT16, OperandType::UINT16 } }, + { WireOpCode::NULLIFIEREXISTS, { OperandType::INDIRECT8, OperandType::UINT16, OperandType::UINT16 } }, { WireOpCode::EMITNULLIFIER, { OperandType::INDIRECT8, diff --git a/barretenberg/cpp/src/barretenberg/vm2/tracegen/execution_trace.test.cpp b/barretenberg/cpp/src/barretenberg/vm2/tracegen/execution_trace.test.cpp index efb03033c28e..b187359a3342 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/tracegen/execution_trace.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/tracegen/execution_trace.test.cpp @@ -1100,26 +1100,21 @@ TEST(ExecutionTraceGenTest, NullifierExists) ExecutionTraceBuilder builder; // constants uint16_t nullifier_offset = 100; - uint16_t address_offset = 200; uint16_t exists_offset = 300; - FF nullifier = 0x123456; - FF address = 0xdeadbeef; + FF siloed_nullifier = 0x123456; bool exists = true; const auto instr = InstructionBuilder(WireOpCode::NULLIFIEREXISTS) .operand(nullifier_offset) - .operand(address_offset) .operand(exists_offset) .build(); - ExecutionEvent ex_event = { - .wire_instruction = instr, - .inputs = { MemoryValue::from_tag(ValueTag::FF, nullifier), MemoryValue::from_tag(ValueTag::FF, address) }, - .output = { MemoryValue::from_tag(ValueTag::U1, exists ? 1 : 0) }, // exists = true - .addressing_event = { .resolution_info = { { .resolved_operand = MemoryValue::from(nullifier) }, - { .resolved_operand = MemoryValue::from(address) }, - { .resolved_operand = - MemoryValue::from(exists_offset) } } } - }; + ExecutionEvent ex_event = { .wire_instruction = instr, + .inputs = { MemoryValue::from_tag(ValueTag::FF, siloed_nullifier) }, + .output = { MemoryValue::from_tag(ValueTag::U1, exists ? 1 : 0) }, // exists = true + .addressing_event = { + .resolution_info = { + { .resolved_operand = MemoryValue::from(siloed_nullifier) }, + { .resolved_operand = MemoryValue::from(exists_offset) } } } }; builder.process({ ex_event }, trace); EXPECT_THAT(trace.as_rows(), @@ -1129,15 +1124,12 @@ TEST(ExecutionTraceGenTest, NullifierExists) // Second row is the nullifier_exists AllOf(ROW_FIELD_EQ(execution_sel, 1), ROW_FIELD_EQ(execution_sel_execute_nullifier_exists, 1), - ROW_FIELD_EQ(execution_rop_0_, nullifier), - ROW_FIELD_EQ(execution_rop_1_, address), - ROW_FIELD_EQ(execution_rop_2_, exists_offset), - ROW_FIELD_EQ(execution_register_0_, nullifier), - ROW_FIELD_EQ(execution_register_1_, address), - ROW_FIELD_EQ(execution_register_2_, exists ? 1 : 0), + ROW_FIELD_EQ(execution_rop_0_, siloed_nullifier), + ROW_FIELD_EQ(execution_rop_1_, exists_offset), + ROW_FIELD_EQ(execution_register_0_, siloed_nullifier), + ROW_FIELD_EQ(execution_register_1_, exists ? 1 : 0), ROW_FIELD_EQ(execution_mem_tag_reg_0_, MEM_TAG_FF), - ROW_FIELD_EQ(execution_mem_tag_reg_1_, MEM_TAG_FF), - ROW_FIELD_EQ(execution_mem_tag_reg_2_, MEM_TAG_U1), + ROW_FIELD_EQ(execution_mem_tag_reg_1_, MEM_TAG_U1), ROW_FIELD_EQ(execution_subtrace_operation_id, AVM_EXEC_OP_ID_NULLIFIER_EXISTS)))); } diff --git a/noir-projects/aztec-nr/aztec/src/context/public_context.nr b/noir-projects/aztec-nr/aztec/src/context/public_context.nr index 194372142265..f40d498b50cb 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -1,6 +1,9 @@ use crate::{ context::gas::GasOpts, - hash::{compute_l1_to_l2_message_hash, compute_l1_to_l2_message_nullifier, compute_secret_hash}, + hash::{ + compute_l1_to_l2_message_hash, compute_l1_to_l2_message_nullifier, compute_secret_hash, + compute_siloed_nullifier, + }, oracle::avm, }; use dep::protocol_types::{ @@ -199,8 +202,9 @@ impl PublicContext { unsiloed_nullifier: Field, contract_address: AztecAddress, ) -> bool { + let siloed_nullifier = compute_siloed_nullifier(contract_address, unsiloed_nullifier); // Safety: AVM opcodes are constrained by the AVM itself - unsafe { avm::nullifier_exists(unsiloed_nullifier, contract_address.to_field()) } == 1 + unsafe { avm::nullifier_exists(siloed_nullifier) } == 1 } /// Consumes a message sent from Ethereum (L1) to Aztec (L2) -- effectively diff --git a/noir-projects/aztec-nr/aztec/src/oracle/avm.nr b/noir-projects/aztec-nr/aztec/src/oracle/avm.nr index 483fd5851284..47377bd15bef 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/avm.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/avm.nr @@ -46,8 +46,8 @@ pub unconstrained fn note_hash_exists(note_hash: Field, leaf_index: u64) -> u1 { pub unconstrained fn emit_note_hash(note_hash: Field) { emit_note_hash_opcode(note_hash) } -pub unconstrained fn nullifier_exists(nullifier: Field, address: Field) -> u1 { - nullifier_exists_opcode(nullifier, address) +pub unconstrained fn nullifier_exists(siloed_nullifier: Field) -> u1 { + nullifier_exists_opcode(siloed_nullifier) } pub unconstrained fn emit_nullifier(nullifier: Field) { emit_nullifier_opcode(nullifier) @@ -161,7 +161,7 @@ unconstrained fn note_hash_exists_opcode(note_hash: Field, leaf_index: u64) -> u unconstrained fn emit_note_hash_opcode(note_hash: Field) {} #[oracle(avmOpcodeNullifierExists)] -unconstrained fn nullifier_exists_opcode(nullifier: Field, address: Field) -> u1 {} +unconstrained fn nullifier_exists_opcode(siloed_nullifier: Field) -> u1 {} #[oracle(avmOpcodeEmitNullifier)] unconstrained fn emit_nullifier_opcode(nullifier: Field) {} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index ba3793fd45c5..876071d00b28 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -1132,7 +1132,7 @@ pub global AVM_SLOAD_BASE_L2_GAS: u32 = 129 * 10; // SLOW_SIM_MUL = 10 pub global AVM_SSTORE_BASE_L2_GAS: u32 = (633 + L2_GAS_DISTRIBUTED_STORAGE_PREMIUM) * 20; // SLOW_SIM_MUL = 20 pub global AVM_NOTEHASHEXISTS_BASE_L2_GAS: u32 = 126 * 4; // SLOW_SIM_MUL = 3 (+1 for slow proving) pub global AVM_EMITNOTEHASH_BASE_L2_GAS: u32 = (261 + L2_GAS_DISTRIBUTED_STORAGE_PREMIUM) * 15; // SLOW_SIM_MUL = 15 -pub global AVM_NULLIFIEREXISTS_BASE_L2_GAS: u32 = 132 * 7; // SLOW_SIM_MUL = 6 (+1 for slow proving) +pub global AVM_NULLIFIEREXISTS_BASE_L2_GAS: u32 = 129 * 7; // SLOW_SIM_MUL = 6 (+1 for slow proving) pub global AVM_EMITNULLIFIER_BASE_L2_GAS: u32 = (516 + L2_GAS_DISTRIBUTED_STORAGE_PREMIUM) * 20; // SLOW_SIM_MUL = 20 pub global AVM_L1TOL2MSGEXISTS_BASE_L2_GAS: u32 = 108 * 5; // SLOW_SIM_MUL = 4 (+1 for slow proving) pub global AVM_GETCONTRACTINSTANCE_BASE_L2_GAS: u32 = 1527 * 4; // SLOW_SIM_MUL = 4 diff --git a/yarn-project/constants/src/constants.gen.ts b/yarn-project/constants/src/constants.gen.ts index 92b895ec548a..d6c3a2be78e2 100644 --- a/yarn-project/constants/src/constants.gen.ts +++ b/yarn-project/constants/src/constants.gen.ts @@ -442,7 +442,7 @@ export const AVM_SLOAD_BASE_L2_GAS = 1290; export const AVM_SSTORE_BASE_L2_GAS = 33140; export const AVM_NOTEHASHEXISTS_BASE_L2_GAS = 504; export const AVM_EMITNOTEHASH_BASE_L2_GAS = 19275; -export const AVM_NULLIFIEREXISTS_BASE_L2_GAS = 924; +export const AVM_NULLIFIEREXISTS_BASE_L2_GAS = 903; export const AVM_EMITNULLIFIER_BASE_L2_GAS = 30800; export const AVM_L1TOL2MSGEXISTS_BASE_L2_GAS = 540; export const AVM_GETCONTRACTINSTANCE_BASE_L2_GAS = 6108; diff --git a/yarn-project/simulator/docs/avm/opcodes/nullifierexists.md b/yarn-project/simulator/docs/avm/opcodes/nullifierexists.md index da9cf1a22478..35feedff9b95 100644 --- a/yarn-project/simulator/docs/avm/opcodes/nullifierexists.md +++ b/yarn-project/simulator/docs/avm/opcodes/nullifierexists.md @@ -2,25 +2,25 @@ # NULLIFIEREXISTS -Check existence of nullifier +Check existence of an already siloed nullifier. Opcode `0x33` ```javascript -M[existsOffset] = nullifierTree.exists(M[addressOffset], M[nullifierOffset]) ? 1 : 0 +M[existsOffset] = nullifierTree.exists(M[siloedNullifierOffset]) ? 1 : 0 ``` ## Details -Performs a read of the Nullifier Tree to query whether the specified nullifier exists for the given contract address. Any contract address can be specified, not just the currently executing contract. Both address and nullifier must be FIELD. Result is Uint1. +Performs a read of the Nullifier Tree to query whether the specified siloed nullifier exists. The nullifier must already be siloed (caller is responsible for computing the siloed nullifier before calling this opcode). The nullifier must be FIELD. Result is Uint1. ## Gas Costs | Component | Value | Scales with | |-----------|-------|-------------| -| L2 Base | 924 | - | +| L2 Base | 903 | - | | DA Base | 0 | - | -| L2 Addressing | 3 | 3 L2 gas per indirect memory offset
3 L2 gas per relative memory offset | +| L2 Addressing | 2 | 3 L2 gas per indirect memory offset
3 L2 gas per relative memory offset | \* See [Gas Metering](../gas.md) for details on how gas costs are computed and applied. @@ -28,8 +28,7 @@ Performs a read of the Nullifier Tree to query whether the specified nullifier e | Name | Type | Description | |------|------|-------------| -| `nullifierOffset` | Memory offset | Memory offset of the nullifier to check | -| `addressOffset` | Memory offset | Memory offset of the contract address | +| `siloedNullifierOffset` | Memory offset | Memory offset of the siloed nullifier to check | | `existsOffset` | Memory offset | Memory offset where the result (0 or 1) will be written | ## Wire Formats @@ -42,14 +41,13 @@ See [Wire Format](../wire-format.md) page for an explanation of wire format vari title: "NULLIFIEREXISTS" config: packet: - bitsPerRow: 64 + bitsPerRow: 48 --- packet-beta 0-7: "Opcode (0x33)" 8-15: "Addressing modes" -16-31: "Operand: nullifierOffset" -32-47: "Operand: addressOffset" -48-63: "Operand: existsOffset" +16-31: "Operand: siloedNullifierOffset" +32-47: "Operand: existsOffset" ``` ## Addressing Modes @@ -57,7 +55,7 @@ See [Addressing](../addressing.md) page for a detailed explanation. 8-bit bitmask: 2 bits per memory offset operand (indirect flag + relative flag) -Memory offset operands (`nullifierOffset`, `addressOffset`, `existsOffset`) are encoded as follows: +Memory offset operands (`siloedNullifierOffset`, `existsOffset`) are encoded as follows: ```mermaid --- @@ -68,20 +66,19 @@ config: bitsPerRow: 8 --- packet-beta - 0: "nullifierOffset is indirect" - 1: "nullifierOffset is relative" - 2: "addressOffset is indirect" - 3: "addressOffset is relative" - 4: "existsOffset is indirect" - 5: "existsOffset is relative" + 0: "siloedNullifierOffset is indirect" + 1: "siloedNullifierOffset is relative" + 2: "existsOffset is indirect" + 3: "existsOffset is relative" + 4: "Unused" + 5: "Unused" 6: "Unused" 7: "Unused" ``` ## Tag Checks -- `T[addressOffset] == FIELD` -- `T[nullifierOffset] == FIELD` +- `T[siloedNullifierOffset] == FIELD` ## Tag Updates @@ -89,7 +86,7 @@ packet-beta ## Error Conditions -- **INVALID_TAG**: Address or nullifier is not FIELD +- **INVALID_TAG**: Siloed Nullifier is not FIELD - **MEMORY_ACCESS_OUT_OF_RANGE**: Memory offset operand exceeds addressable memory --- diff --git a/yarn-project/simulator/src/public/avm/opcodes/accrued_substate.test.ts b/yarn-project/simulator/src/public/avm/opcodes/accrued_substate.test.ts index eb7aa850e279..02bdf4ad514a 100644 --- a/yarn-project/simulator/src/public/avm/opcodes/accrued_substate.test.ts +++ b/yarn-project/simulator/src/public/avm/opcodes/accrued_substate.test.ts @@ -142,13 +142,11 @@ describe('Accrued Substate', () => { NullifierExists.opcode, // opcode 0x01, // indirect ...Buffer.from('1234', 'hex'), // nullifierOffset - ...Buffer.from('0234', 'hex'), // addressOffset ...Buffer.from('4567', 'hex'), // existsOffset ]); const inst = new NullifierExists( /*addressing_mode=*/ 0x01, /*nullifierOffset=*/ 0x1234, - /*addressOffset=*/ 0x0234, /*existsOffset=*/ 0x4567, ); @@ -158,21 +156,17 @@ describe('Accrued Substate', () => { describe.each([[/*exists=*/ false], [/*exists=*/ true]])('Nullifier checks', (exists: boolean) => { const existsStr = exists ? 'DOES exist' : 'does NOT exist'; - it(`Should return ${exists} (and be traced) when noteHash ${existsStr}`, async () => { - const addressOffset = 1; - + it(`Should return ${exists} (and be traced) when nullifier ${existsStr}`, async () => { if (exists) { - mockCheckNullifierExists(treesDB, true, value0); + // The opcode expects a siloed nullifier, so we need to use the pre-siloed one + mockCheckNullifierExists(treesDB, true, siloedNullifier0); } - context.machineState.memory.set(value0Offset, new Field(value0)); // nullifier - context.machineState.memory.set(addressOffset, new Field(address.toField())); - await new NullifierExists( - /*addressing_mode=*/ 0, - /*nullifierOffset=*/ value0Offset, - addressOffset, - existsOffset, - ).execute(context); + // Set pre-computed siloed nullifier in memory (siloing now happens at caller level) + context.machineState.memory.set(value0Offset, new Field(siloedNullifier0)); + await new NullifierExists(/*addressing_mode=*/ 0, /*nullifierOffset=*/ value0Offset, existsOffset).execute( + context, + ); const gotExists = context.machineState.memory.getAs(existsOffset); expect(gotExists).toEqual(new Uint8(exists ? 1 : 0)); diff --git a/yarn-project/simulator/src/public/avm/opcodes/accrued_substate.ts b/yarn-project/simulator/src/public/avm/opcodes/accrued_substate.ts index ca64b8223117..cff74b2893e9 100644 --- a/yarn-project/simulator/src/public/avm/opcodes/accrued_substate.ts +++ b/yarn-project/simulator/src/public/avm/opcodes/accrued_substate.ts @@ -88,18 +88,11 @@ export class NullifierExists extends Instruction { static type: string = 'NULLIFIEREXISTS'; static readonly opcode: Opcode = Opcode.NULLIFIEREXISTS; // Informs (de)serialization. See Instruction.deserialize. - static readonly wireFormat = [ - OperandType.UINT8, - OperandType.UINT8, - OperandType.UINT16, - OperandType.UINT16, - OperandType.UINT16, - ]; + static readonly wireFormat = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT16, OperandType.UINT16]; constructor( private addressingMode: number, - private nullifierOffset: number, - private addressOffset: number, + private siloedNullifierOffset: number, private existsOffset: number, ) { super(); @@ -113,13 +106,12 @@ export class NullifierExists extends Instruction { this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()), ); - const operands = [this.nullifierOffset, this.addressOffset, this.existsOffset]; - const [nullifierOffset, addressOffset, existsOffset] = addressing.resolve(operands, memory); - memory.checkTags(TypeTag.FIELD, nullifierOffset, addressOffset); + const operands = [this.siloedNullifierOffset, this.existsOffset]; + const [siloedNullifierOffset, existsOffset] = addressing.resolve(operands, memory); + memory.checkTag(TypeTag.FIELD, siloedNullifierOffset); - const nullifier = memory.get(nullifierOffset).toFr(); - const address = memory.get(addressOffset).toAztecAddress(); - const exists = await context.persistableState.checkNullifierExists(address, nullifier); + const siloedNullifier = memory.get(siloedNullifierOffset).toFr(); + const exists = await context.persistableState.checkSiloedNullifierExists(siloedNullifier); memory.set(existsOffset, exists ? new Uint1(1) : new Uint1(0)); } diff --git a/yarn-project/simulator/src/public/fixtures/opcode_spammer.ts b/yarn-project/simulator/src/public/fixtures/opcode_spammer.ts index c6e9ba81ffc4..46b2d122f809 100644 --- a/yarn-project/simulator/src/public/fixtures/opcode_spammer.ts +++ b/yarn-project/simulator/src/public/fixtures/opcode_spammer.ts @@ -164,7 +164,7 @@ import { Fr } from '@aztec/foundation/curves/bn254'; import type { Bufferable } from '@aztec/foundation/serialize'; import { type CallStackMetadata, PublicDataWrite, type PublicTxResult } from '@aztec/stdlib/avm'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; -import { computePublicDataTreeLeafSlot, siloNullifier } from '@aztec/stdlib/hash'; +import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash'; import type { MerkleTreeWriteOperations } from '@aztec/stdlib/interfaces/server'; import { MerkleTreeId } from '@aztec/stdlib/trees'; @@ -295,9 +295,8 @@ export interface SpamConfigsForOpcode { export const WARM_NOTE_HASH = new Fr(0xdeadbeefn); export const WARM_L1_TO_L2_MSG = new Fr(0xcafebabedeadbeefn); -/** Warm nullifier constants - uses a fixed address since NULLIFIEREXISTS takes address as parameter */ -export const WARM_NULLIFIER = new Fr(0xdeadbeef0001n); -export const WARM_NULLIFIER_ADDRESS = AztecAddress.fromNumber(0xbeef); +/** Warm nullifier constant - a pre-siloed nullifier value inserted directly into the tree */ +export const WARM_SILOED_NULLIFIER = new Fr(0xdeadbeef0001n); /** Warm storage constants - storage is inserted for the deployed contract's address */ export const WARM_STORAGE_SLOT = new Fr(0xdeadbeef0002n); @@ -331,9 +330,8 @@ export async function insertWarmTreeEntries( // Insert into L1 to L2 message tree await merkleTrees.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, [WARM_L1_TO_L2_MSG]); - // Insert siloed nullifier into nullifier tree - const siloedNullifier = await siloNullifier(WARM_NULLIFIER_ADDRESS, WARM_NULLIFIER); - await merkleTrees.sequentialInsert(MerkleTreeId.NULLIFIER_TREE, [siloedNullifier.toBuffer()]); + // Insert siloed nullifier into nullifier tree (already siloed - used directly by NULLIFIEREXISTS) + await merkleTrees.sequentialInsert(MerkleTreeId.NULLIFIER_TREE, [WARM_SILOED_NULLIFIER.toBuffer()]); // Insert storage value into public data tree const leafSlot = await computePublicDataTreeLeafSlot(contractAddress, WARM_STORAGE_SLOT); @@ -1060,56 +1058,25 @@ export const SPAM_CONFIGS: Partial> = { [Opcode.NULLIFIEREXISTS]: [ { label: 'Non-existent nullifier', + // NULLIFIEREXISTS now takes a siloed nullifier directly (no address parameter) setup: [ - { offset: 0, value: new Field(Fr.random()) }, // random nullifier - { offset: 1, value: new Field(Fr.random()) }, // random address + { offset: 0, value: new Field(Fr.random()) }, // random siloed nullifier (won't exist) ], targetInstructions: () => [ - new NullifierExists(/*addressing_mode=*/ 0, /*nullifierOffset=*/ 0, /*addressOffset=*/ 1, /*existsOffset=*/ 2), + new NullifierExists(/*addressing_mode=*/ 0, /*siloedNullifierOffset=*/ 0, /*existsOffset=*/ 1), ], }, { label: 'Existing nullifier (warm - from tree)', - // Uses pre-inserted nullifier from insertWarmTreeEntries() + // Uses pre-inserted siloed nullifier from insertWarmTreeEntries() + // NULLIFIEREXISTS now takes a siloed nullifier directly setup: [ - { offset: 0, value: new Field(WARM_NULLIFIER) }, // pre-inserted nullifier - { offset: 1, value: new Field(WARM_NULLIFIER_ADDRESS.toField()) }, // address it was siloed with + { offset: 0, value: new Field(WARM_SILOED_NULLIFIER) }, // pre-inserted siloed nullifier ], targetInstructions: () => [ - new NullifierExists(/*addressing_mode=*/ 0, /*nullifierOffset=*/ 0, /*addressOffset=*/ 1, /*existsOffset=*/ 2), + new NullifierExists(/*addressing_mode=*/ 0, /*siloedNullifierOffset=*/ 0, /*existsOffset=*/ 1), ], }, - { - label: 'Existing nullifier (warm - EMITNULLIFIER first)', - // Memory layout: nullifier (incremented), constant 1, current address (from GETENVVAR), revertSize, exists result - setup: [ - { offset: 0, value: new Field(Fr.random()) }, // nullifier (will be incremented) - { offset: 1, value: new Field(1n) }, // constant 1 for ADD - () => [ - // Get current contract address into offset 2 - new GetEnvVar(/*addressing_mode=*/ 0, /*dstOffset=*/ 2, /*varEnum=*/ 0).as( - Opcode.GETENVVAR_16, - GetEnvVar.wireFormat16, - ), - ], - { offset: 3, value: new Uint32(0n) }, // revertSize - ], - targetInstructions: () => [ - new EmitNullifier(/*addressing_mode=*/ 0, /*nullifierOffset=*/ 0), - new NullifierExists(/*addressing_mode=*/ 0, /*nullifierOffset=*/ 0, /*addressOffset=*/ 2, /*existsOffset=*/ 4), - new Add(/*addressing_mode=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 0).as( - Opcode.ADD_8, - Add.wireFormat8, - ), // nullifier++ - ], - cleanupInstructions: () => [ - new Revert(/*addressing_mode=*/ 0, /*retSizeOffset=*/ 3, /*returnOffset=*/ 0).as( - Opcode.REVERT_8, - Revert.wireFormat8, - ), - ], - limit: MAX_NULLIFIERS_PER_TX - 1, - }, ], [Opcode.L1TOL2MSGEXISTS]: [ diff --git a/yarn-project/simulator/src/public/state_manager/state_manager.ts b/yarn-project/simulator/src/public/state_manager/state_manager.ts index b798c8b675a2..b7e0c9ffe294 100644 --- a/yarn-project/simulator/src/public/state_manager/state_manager.ts +++ b/yarn-project/simulator/src/public/state_manager/state_manager.ts @@ -233,7 +233,15 @@ export class PublicPersistableStateManager { public async checkNullifierExists(contractAddress: AztecAddress, nullifier: Fr): Promise { this.log.trace(`Checking existence of nullifier (address=${contractAddress}, nullifier=${nullifier})`); const siloedNullifier = await siloNullifier(contractAddress, nullifier); + return this.checkSiloedNullifierExists(siloedNullifier); + } + /** + * Check if a siloed nullifier exists. + * @param siloedNullifier - the siloed nullifier to check + * @returns exists - whether the nullifier exists in the nullifier set + */ + public async checkSiloedNullifierExists(siloedNullifier: Fr): Promise { if (this.doMerkleOperations) { const exists = await this.treesDB.checkNullifierExists(siloedNullifier); this.log.trace(`Checked siloed nullifier ${siloedNullifier} (exists=${exists})`); diff --git a/yarn-project/txe/src/oracle/interfaces.ts b/yarn-project/txe/src/oracle/interfaces.ts index 9938c0ca621c..35d5bea9555d 100644 --- a/yarn-project/txe/src/oracle/interfaces.ts +++ b/yarn-project/txe/src/oracle/interfaces.ts @@ -33,7 +33,7 @@ export interface IAvmExecutionOracle { avmOpcodeVersion(): Promise; avmOpcodeEmitNullifier(nullifier: Fr): Promise; avmOpcodeEmitNoteHash(noteHash: Fr): Promise; - avmOpcodeNullifierExists(innerNullifier: Fr, targetAddress: AztecAddress): Promise; + avmOpcodeNullifierExists(siloedNullifier: Fr): Promise; avmOpcodeStorageWrite(slot: Fr, value: Fr): Promise; avmOpcodeStorageRead(slot: Fr, contractAddress: AztecAddress): Promise; } diff --git a/yarn-project/txe/src/oracle/txe_oracle_public_context.ts b/yarn-project/txe/src/oracle/txe_oracle_public_context.ts index 4888cb832155..fb9653b91d5f 100644 --- a/yarn-project/txe/src/oracle/txe_oracle_public_context.ts +++ b/yarn-project/txe/src/oracle/txe_oracle_public_context.ts @@ -78,13 +78,11 @@ export class TXEOraclePublicContext implements IAvmExecutionOracle { this.transientUniqueNoteHashes.push(siloedNoteHash); } - async avmOpcodeNullifierExists(innerNullifier: Fr, targetAddress: AztecAddress): Promise { - const nullifier = await siloNullifier(targetAddress, innerNullifier!); - + async avmOpcodeNullifierExists(siloedNullifier: Fr): Promise { const treeIndex = ( - await this.forkedWorldTrees.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]) + await this.forkedWorldTrees.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [siloedNullifier.toBuffer()]) )[0]; - const transientIndex = this.transientSiloedNullifiers.find(n => n.equals(nullifier)); + const transientIndex = this.transientSiloedNullifiers.find(n => n.equals(siloedNullifier)); return treeIndex !== undefined || transientIndex !== undefined; } diff --git a/yarn-project/txe/src/rpc_translator.ts b/yarn-project/txe/src/rpc_translator.ts index 5c7ebd3797ee..1bb2d812a5c5 100644 --- a/yarn-project/txe/src/rpc_translator.ts +++ b/yarn-project/txe/src/rpc_translator.ts @@ -911,11 +911,10 @@ export class RPCTranslator { return toForeignCallResult([]); } - async avmOpcodeNullifierExists(foreignInnerNullifier: ForeignCallSingle, foreignTargetAddress: ForeignCallSingle) { - const innerNullifier = fromSingle(foreignInnerNullifier); - const targetAddress = AztecAddress.fromField(fromSingle(foreignTargetAddress)); + async avmOpcodeNullifierExists(foreignSiloedNullifier: ForeignCallSingle) { + const siloedNullifier = fromSingle(foreignSiloedNullifier); - const exists = await this.handlerAsAvm().avmOpcodeNullifierExists(innerNullifier, targetAddress); + const exists = await this.handlerAsAvm().avmOpcodeNullifierExists(siloedNullifier); return toForeignCallResult([toSingle(new Fr(exists))]); }