diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp index bf522e86ff2..aacf11886bf 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp @@ -1001,6 +1001,12 @@ bigfield bigfield::sqradd(const std::vector& t const auto [quotient_1024, remainder_1024] = (left * right + add_right).divmod(modulus); remainder = bigfield(ctx, uint256_t(remainder_1024.lo.lo)); + // Merge tags + OriginTag new_tag = get_origin_tag(); + for (auto& element : to_add) { + new_tag = OriginTag(new_tag, element.get_origin_tag()); + } + remainder.set_origin_tag(new_tag); return remainder; } else { diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/goblin_field.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/goblin_field.hpp index c26305e7d4d..bd46255933d 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/goblin_field.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/goblin_field.hpp @@ -2,6 +2,7 @@ #include "../bigfield/bigfield.hpp" #include "../circuit_builders/circuit_builders_fwd.hpp" #include "../field/field.hpp" +#include "barretenberg/transcript/origin_tag.hpp" namespace bb::stdlib { @@ -120,6 +121,14 @@ template class goblin_field { // done in the translator circuit void assert_is_in_field(){}; + + OriginTag get_origin_tag() const { return OriginTag(limbs[0].get_origin_tag(), limbs[1].get_origin_tag()); } + + void set_origin_tag(const OriginTag& tag) + { + limbs[0].set_origin_tag(tag); + limbs[1].set_origin_tag(tag); + } }; template inline std::ostream& operator<<(std::ostream& os, goblin_field const& v) { diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.hpp index e033d481f35..eb27853ba67 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.hpp @@ -290,6 +290,18 @@ template class element { void set_point_at_infinity(const bool_ct& is_infinity) { _is_infinity = is_infinity; } element get_standard_form() const; + void set_origin_tag(OriginTag tag) const + { + x.set_origin_tag(tag); + y.set_origin_tag(tag); + _is_infinity.set_origin_tag(tag); + } + + OriginTag get_origin_tag() const + { + return OriginTag(x.get_origin_tag(), y.get_origin_tag(), _is_infinity.get_origin_tag()); + } + Fq x; Fq y; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp index 2f7d5d5dd3b..fd54166a9a9 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp @@ -9,6 +9,8 @@ #include "barretenberg/stdlib/primitives/curves/bn254.hpp" #include "barretenberg/stdlib/primitives/curves/secp256k1.hpp" #include "barretenberg/stdlib/primitives/curves/secp256r1.hpp" +#include "barretenberg/transcript/origin_tag.hpp" +#include using namespace bb; @@ -32,6 +34,7 @@ template struct TestType { typename std::conditional<_use_bigfield, typename Curve::bigfr_ct, typename Curve::ScalarField>::type; }; +STANDARD_TESTING_TAGS template class stdlib_biggroup : public testing::Test { using Curve = typename TestType::Curve; using element_ct = typename TestType::element_ct; @@ -53,6 +56,33 @@ template class stdlib_biggroup : public testing::Test { }; public: + static void test_basic_tag_logic() + { + Builder builder; + affine_element input_a(element::random_element()); + affine_element input_b(element::random_element()); + + element_ct a = element_ct::from_witness(&builder, input_a); + a.set_origin_tag(next_submitted_value_origin_tag); + // Tag is preserved after being set + EXPECT_EQ(a.get_origin_tag(), next_submitted_value_origin_tag); + + // Tags from members are merged + bool_ct pif = bool_ct(witness_ct(&builder, 0)); + pif.set_origin_tag(next_challenge_tag); + a.x.set_origin_tag(submitted_value_origin_tag); + a.y.set_origin_tag(challenge_origin_tag); + a.set_point_at_infinity(pif); + EXPECT_EQ(a.get_origin_tag(), first_second_third_merged_tag); + +#ifndef NDEBUG + // Working with instant death tagged element causes an exception + element_ct b = element_ct::from_witness(&builder, input_b); + b.set_origin_tag(instant_death_tag); + + EXPECT_THROW(b + b, std::runtime_error); +#endif + } static void test_add() { Builder builder; @@ -64,9 +94,16 @@ template class stdlib_biggroup : public testing::Test { element_ct a = element_ct::from_witness(&builder, input_a); element_ct b = element_ct::from_witness(&builder, input_b); + // Set different tags in a and b + a.set_origin_tag(submitted_value_origin_tag); + b.set_origin_tag(challenge_origin_tag); + uint64_t before = builder.get_estimated_num_finalized_gates(); element_ct c = a + b; uint64_t after = builder.get_estimated_num_finalized_gates(); + + // Check that the resulting tag is the union of inputs' tgs + EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag); if (i == num_repetitions - 1) { std::cout << "num gates per add = " << after - before << std::endl; benchmark_info(Builder::NAME_STRING, "Biggroup", "ADD", "Gate Count", after - before); @@ -101,6 +138,15 @@ template class stdlib_biggroup : public testing::Test { element_ct a_negated = element_ct::from_witness(&builder, -input_a); element_ct b = element_ct::from_witness(&builder, input_b); + // Set different tags on all elements + a.set_origin_tag(submitted_value_origin_tag); + b.set_origin_tag(challenge_origin_tag); + a_alternate.set_origin_tag(next_challenge_tag); + // We can't use next_submitted_value tag here or it will break, so construct a tag manually + const auto second_round_challenge_tag = + OriginTag(/*parent_index=*/0, /*child_index=*/2, /*is_submitted=*/false); + a_negated.set_origin_tag(second_round_challenge_tag); + element_ct c = a + b; element_ct d = b + a; element_ct e = b + b; @@ -108,6 +154,14 @@ template class stdlib_biggroup : public testing::Test { element_ct g = a + a_alternate; element_ct h = a + a_negated; + // Check the resulting tags are correct unions of input tags + EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag); + EXPECT_EQ(d.get_origin_tag(), first_two_merged_tag); + EXPECT_EQ(e.get_origin_tag(), challenge_origin_tag); + EXPECT_EQ(f.get_origin_tag(), submitted_value_origin_tag); + EXPECT_EQ(g.get_origin_tag(), first_and_third_merged_tag); + EXPECT_EQ(h.get_origin_tag(), OriginTag(submitted_value_origin_tag, second_round_challenge_tag)); + affine_element c_expected = affine_element(element(input_a) + element(input_b)); affine_element d_expected = affine_element(element(input_b) + element(input_a)); affine_element e_expected = affine_element(element(input_b) + element(input_b)); @@ -125,20 +179,35 @@ template class stdlib_biggroup : public testing::Test { EXPECT_CIRCUIT_CORRECTNESS(builder); } + /** + * @brief Check that converting a point at infinity into standard form ensures the coordinates are zeroes + * + */ static void test_standard_form_of_point_at_infinity() { Builder builder; size_t num_repetitions = 5; for (size_t i = 0; i < num_repetitions; ++i) { + // Check both constant and witness case element_ct input_a(element::random_element()); - element_ct input_b(element::random_element()); + element_ct input_b = element_ct::from_witness(&builder, element::random_element()); + input_a.set_point_at_infinity(true); input_b.set_point_at_infinity(true); + + // Set tags + input_a.set_origin_tag(submitted_value_origin_tag); + input_b.set_origin_tag(challenge_origin_tag); + auto standard_a = input_a.get_standard_form(); auto standard_b = input_b.get_standard_form(); - EXPECT_EQ(standard_a.is_point_at_infinity().get_value(), false); + + // Check that tags are preserved + + EXPECT_EQ(standard_a.get_origin_tag(), submitted_value_origin_tag); + EXPECT_EQ(standard_b.get_origin_tag(), challenge_origin_tag); + + EXPECT_EQ(standard_a.is_point_at_infinity().get_value(), true); EXPECT_EQ(standard_b.is_point_at_infinity().get_value(), true); - fq input_a_x = input_a.x.get_value().lo; - fq input_a_y = input_a.y.get_value().lo; fq standard_a_x = standard_a.x.get_value().lo; fq standard_a_y = standard_a.y.get_value().lo; @@ -146,8 +215,8 @@ template class stdlib_biggroup : public testing::Test { fq standard_b_x = standard_b.x.get_value().lo; fq standard_b_y = standard_b.y.get_value().lo; - EXPECT_EQ(input_a_x, standard_a_x); - EXPECT_EQ(input_a_y, standard_a_y); + EXPECT_EQ(standard_a_x, 0); + EXPECT_EQ(standard_a_y, 0); EXPECT_EQ(standard_b_x, 0); EXPECT_EQ(standard_b_y, 0); } @@ -165,8 +234,15 @@ template class stdlib_biggroup : public testing::Test { element_ct a = element_ct::from_witness(&builder, input_a); element_ct b = element_ct::from_witness(&builder, input_b); + // Set tags + a.set_origin_tag(submitted_value_origin_tag); + b.set_origin_tag(challenge_origin_tag); + element_ct c = a - b; + // Check tags have merged + EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag); + affine_element c_expected(element(input_a) - element(input_b)); uint256_t c_x_u256 = c.x.get_value().lo; @@ -196,6 +272,15 @@ template class stdlib_biggroup : public testing::Test { element_ct a_negated = element_ct::from_witness(&builder, -input_a); element_ct b = element_ct::from_witness(&builder, input_b); + // Set different tags on all elements + a.set_origin_tag(submitted_value_origin_tag); + b.set_origin_tag(challenge_origin_tag); + a_alternate.set_origin_tag(next_challenge_tag); + // We can't use next_submitted_value tag here or it will break, so construct a tag manually + const auto second_round_challenge_tag = + OriginTag(/*parent_index=*/0, /*child_index=*/2, /*is_submitted=*/false); + a_negated.set_origin_tag(second_round_challenge_tag); + element_ct c = a - b; element_ct d = b - a; element_ct e = b - b; @@ -203,6 +288,14 @@ template class stdlib_biggroup : public testing::Test { element_ct g = a - a_alternate; element_ct h = a - a_negated; + // Check the resulting tags are correct unions of input tags + EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag); + EXPECT_EQ(d.get_origin_tag(), first_two_merged_tag); + EXPECT_EQ(e.get_origin_tag(), challenge_origin_tag); + EXPECT_EQ(f.get_origin_tag(), submitted_value_origin_tag); + EXPECT_EQ(g.get_origin_tag(), first_and_third_merged_tag); + EXPECT_EQ(h.get_origin_tag(), OriginTag(submitted_value_origin_tag, second_round_challenge_tag)); + affine_element c_expected = affine_element(element(input_a) - element(input_b)); affine_element d_expected = affine_element(element(input_b) - element(input_a)); affine_element e_expected = affine_element(element(input_b) - element(input_b)); @@ -230,8 +323,13 @@ template class stdlib_biggroup : public testing::Test { element_ct a = element_ct::from_witness(&builder, input_a); + a.set_origin_tag(submitted_value_origin_tag); + element_ct c = a.dbl(); + // Check that the tag is preserved + EXPECT_EQ(c.get_origin_tag(), submitted_value_origin_tag); + affine_element c_expected(element(input_a).dbl()); uint256_t c_x_u256 = c.x.get_value().lo; @@ -258,8 +356,15 @@ template class stdlib_biggroup : public testing::Test { element_ct a = element_ct::from_witness(&builder, input_a); element_ct b = element_ct::from_witness(&builder, input_b); + // Set tags + a.set_origin_tag(submitted_value_origin_tag); + b.set_origin_tag(challenge_origin_tag); + element_ct c = a.montgomery_ladder(b); + // Check that the resulting tag is a union of tags + EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag); + affine_element c_expected(element(input_a).dbl() + element(input_b)); uint256_t c_x_u256 = c.x.get_value().lo; @@ -288,11 +393,17 @@ template class stdlib_biggroup : public testing::Test { element_ct P = element_ct::from_witness(&builder, input); scalar_ct x = scalar_ct::from_witness(&builder, scalar); + // Set input tags + x.set_origin_tag(challenge_origin_tag); + P.set_origin_tag(submitted_value_origin_tag); + std::cerr << "gates before mul " << builder.get_estimated_num_finalized_gates() << std::endl; element_ct c = P * x; std::cerr << "builder aftr mul " << builder.get_estimated_num_finalized_gates() << std::endl; affine_element c_expected(element(input) * scalar); + // Check the result of the multiplication has a tag that's the union of inputs' tags + EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag); fq c_x_result(c.x.get_value().lo); fq c_y_result(c.y.get_value().lo); @@ -323,7 +434,16 @@ template class stdlib_biggroup : public testing::Test { element_ct P_b = element_ct::from_witness(&builder, input_b); scalar_ct x_b = scalar_ct::from_witness(&builder, scalar_b); + // Set tags + P_a.set_origin_tag(submitted_value_origin_tag); + x_a.set_origin_tag(challenge_origin_tag); + P_b.set_origin_tag(next_submitted_value_origin_tag); + x_b.set_origin_tag(next_challenge_tag); + element_ct c = element_ct::batch_mul({ P_a, P_b }, { x_a, x_b }); + + // Check that the resulting tag is a union of all tags + EXPECT_EQ(c.get_origin_tag(), first_to_fourth_merged_tag); element input_c = (element(input_a) * scalar_a); element input_d = (element(input_b) * scalar_b); affine_element expected(input_c + input_d); @@ -353,14 +473,37 @@ template class stdlib_biggroup : public testing::Test { if ((uint256_t(scalar_b).get_bit(0) & 1) == 0) { scalar_b += fr(1); // skew bit is 0 } + OriginTag tag_union{}; + element_ct P_a = element_ct::from_witness(&builder, input_a); + // Set all element tags to submitted tags from sequential rounds + P_a.set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/0, /*is_submitted=*/true)); + tag_union = OriginTag(tag_union, P_a.get_origin_tag()); + scalar_ct x_a = scalar_ct::from_witness(&builder, scalar_a); + // Set all scalar tags to challenge tags from sequential rounds + x_a.set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/0, /*is_submitted=*/false)); + tag_union = OriginTag(tag_union, x_a.get_origin_tag()); + element_ct P_b = element_ct::from_witness(&builder, input_b); + P_b.set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/1, /*is_submitted=*/true)); + tag_union = OriginTag(tag_union, P_b.get_origin_tag()); + scalar_ct x_b = scalar_ct::from_witness(&builder, scalar_b); + x_b.set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/1, /*is_submitted=*/false)); + tag_union = OriginTag(tag_union, x_b.get_origin_tag()); + element_ct P_c = element_ct::from_witness(&builder, input_c); + P_c.set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/2, /*is_submitted=*/true)); + tag_union = OriginTag(tag_union, P_c.get_origin_tag()); + scalar_ct x_c = scalar_ct::from_witness(&builder, scalar_c); + x_c.set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/2, /*is_submitted=*/false)); + tag_union = OriginTag(tag_union, x_c.get_origin_tag()); element_ct c = element_ct::batch_mul({ P_a, P_b, P_c }, { x_a, x_b, x_c }); + // Check that the result tag is a union of inputs' tags + EXPECT_EQ(c.get_origin_tag(), tag_union); element input_e = (element(input_a) * scalar_a); element input_f = (element(input_b) * scalar_b); element input_g = (element(input_c) * scalar_c); @@ -395,16 +538,47 @@ template class stdlib_biggroup : public testing::Test { if ((uint256_t(scalar_b).get_bit(0) & 1) == 0) { scalar_b += fr(1); // skew bit is 0 } + OriginTag tag_union{}; + element_ct P_a = element_ct::from_witness(&builder, input_a); + + // Set element tags to sequential submitted tags + P_a.set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/0, /*is_submitted=*/true)); + tag_union = OriginTag(tag_union, P_a.get_origin_tag()); + + // Set element tags to sequential challenge tags scalar_ct x_a = scalar_ct::from_witness(&builder, scalar_a); + x_a.set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/0, /*is_submitted=*/false)); + tag_union = OriginTag(tag_union, x_a.get_origin_tag()); + element_ct P_b = element_ct::from_witness(&builder, input_b); + P_b.set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/1, /*is_submitted=*/true)); + tag_union = OriginTag(tag_union, P_b.get_origin_tag()); + scalar_ct x_b = scalar_ct::from_witness(&builder, scalar_b); + x_b.set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/1, /*is_submitted=*/false)); + tag_union = OriginTag(tag_union, x_b.get_origin_tag()); + element_ct P_c = element_ct::from_witness(&builder, input_c); + P_c.set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/2, /*is_submitted=*/true)); + tag_union = OriginTag(tag_union, P_c.get_origin_tag()); + scalar_ct x_c = scalar_ct::from_witness(&builder, scalar_c); + x_c.set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/2, /*is_submitted=*/false)); + tag_union = OriginTag(tag_union, x_c.get_origin_tag()); + element_ct P_d = element_ct::from_witness(&builder, input_d); + P_d.set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/3, /*is_submitted=*/true)); + tag_union = OriginTag(tag_union, P_d.get_origin_tag()); + scalar_ct x_d = scalar_ct::from_witness(&builder, scalar_d); + x_d.set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/3, /*is_submitted=*/false)); + tag_union = OriginTag(tag_union, x_d.get_origin_tag()); element_ct c = element_ct::batch_mul({ P_a, P_b, P_c, P_d }, { x_a, x_b, x_c, x_d }); + + // Check that the tag of the batched product is the union of inputs' tags + EXPECT_EQ(c.get_origin_tag(), tag_union); element input_e = (element(input_a) * scalar_a); element input_f = (element(input_b) * scalar_b); element input_g = (element(input_c) * scalar_c); @@ -431,8 +605,17 @@ template class stdlib_biggroup : public testing::Test { scalar_a -= fr(1); // skew bit is 1 } element_ct P_a = element_ct::one(&builder); + + // Set origin tag for element to submitted value in round 0 + P_a.set_origin_tag(submitted_value_origin_tag); scalar_ct x_a = scalar_ct::from_witness(&builder, scalar_a); + + // Set origin tag for scalar to challenge in round 0 + x_a.set_origin_tag(challenge_origin_tag); element_ct c = P_a * x_a; + + // Check that the resulting tag is a union + EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag); affine_element expected(g1::one * scalar_a); fq c_x_result(c.x.get_value().lo); fq c_y_result(c.y.get_value().lo); @@ -459,13 +642,25 @@ template class stdlib_biggroup : public testing::Test { std::vector circuit_points; std::vector circuit_scalars; + OriginTag tag_union{}; for (size_t i = 0; i < num_points; ++i) { circuit_points.push_back(element_ct::from_witness(&builder, points[i])); + + // Set tag to submitted value tag at round i + circuit_points[i].set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true)); + tag_union = OriginTag(tag_union, circuit_points[i].get_origin_tag()); circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i])); + + // Set tag to challenge tag at round i + circuit_scalars[i].set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false)); + tag_union = OriginTag(tag_union, circuit_scalars[i].get_origin_tag()); } element_ct result_point = element_ct::batch_mul(circuit_points, circuit_scalars); + // Check the resulting tag is a union of inputs' tags + EXPECT_EQ(result_point.get_origin_tag(), tag_union); + element expected_point = g1::one; expected_point.self_set_infinity(); for (size_t i = 0; i < num_points; ++i) { @@ -495,14 +690,26 @@ template class stdlib_biggroup : public testing::Test { std::vector circuit_points; std::vector circuit_scalars; + + OriginTag tag_union{}; for (size_t i = 0; i < num_points; ++i) { circuit_points.push_back(element_ct::from_witness(&builder, points[i])); + + // Set tag to submitted value tag at round i + circuit_points[i].set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true)); + tag_union = OriginTag(tag_union, circuit_points[i].get_origin_tag()); circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i])); + + // Set tag to challenge tag at round i + circuit_scalars[i].set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false)); + tag_union = OriginTag(tag_union, circuit_scalars[i].get_origin_tag()); } element_ct result_point2 = element_ct::batch_mul(circuit_points, circuit_scalars, /*max_num_bits=*/0, /*with_edgecases=*/true); + // Check that the result tag is a union of inputs' tags + EXPECT_EQ(result_point2.get_origin_tag(), tag_union); element expected_point = g1::one; expected_point.self_set_infinity(); for (size_t i = 0; i < num_points; ++i) { @@ -537,13 +744,28 @@ template class stdlib_biggroup : public testing::Test { std::vector circuit_points; std::vector circuit_scalars; + + OriginTag tag_union{}; for (size_t i = 0; i < num_points; ++i) { circuit_points.push_back(element_ct::from_witness(&builder, points[i])); + + // Set tag to submitted value tag at round i + circuit_points[i].set_origin_tag( + OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true)); + tag_union = OriginTag(tag_union, circuit_points[i].get_origin_tag()); circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i])); + + // Set tag to challenge tag at round i + circuit_scalars[i].set_origin_tag( + OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false)); + tag_union = OriginTag(tag_union, circuit_scalars[i].get_origin_tag()); } element_ct result_point = element_ct::batch_mul(circuit_points, circuit_scalars, /*max_num_bits=*/0, /*with_edgecases=*/true); + // Check that the result tag is a union of inputs' tags + EXPECT_EQ(result_point.get_origin_tag(), tag_union); + auto expected_point = element::infinity(); for (const auto& point : points) { expected_point += point; @@ -582,13 +804,29 @@ template class stdlib_biggroup : public testing::Test { std::vector circuit_points; std::vector circuit_scalars; + + OriginTag tag_union{}; for (size_t i = 0; i < num_points; ++i) { circuit_points.push_back(element_ct::from_witness(&builder, points[i])); + + // Set tag to submitted value tag at round i + circuit_points[i].set_origin_tag( + OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true)); + tag_union = OriginTag(tag_union, circuit_points[i].get_origin_tag()); circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i])); + + // Set tag to challenge tag at round i + circuit_scalars[i].set_origin_tag( + OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false)); + tag_union = OriginTag(tag_union, circuit_scalars[i].get_origin_tag()); } + element_ct result_point = element_ct::batch_mul(circuit_points, circuit_scalars, /*max_num_bits=*/0, /*with_edgecases=*/true); + // Check that the result tag is a union of inputs' tags + EXPECT_EQ(result_point.get_origin_tag(), tag_union); + element expected_point = points[1]; expected_point = expected_point.normalize(); @@ -615,14 +853,28 @@ template class stdlib_biggroup : public testing::Test { std::vector circuit_points; std::vector circuit_scalars; + OriginTag tag_union{}; for (size_t i = 0; i < num_points; ++i) { circuit_points.push_back(element_ct::from_witness(&builder, points[i])); + + // Set tag to submitted value tag at round i + circuit_points[i].set_origin_tag( + OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true)); + tag_union = OriginTag(tag_union, circuit_points[i].get_origin_tag()); circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i])); + + // Set tag to challenge tag at round i + circuit_scalars[i].set_origin_tag( + OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false)); + tag_union = OriginTag(tag_union, circuit_scalars[i].get_origin_tag()); } element_ct result_point = element_ct::batch_mul(circuit_points, circuit_scalars, /*max_num_bits=*/0, /*with_edgecases=*/true); + // Check that the result tag is a union of inputs' tags + EXPECT_EQ(result_point.get_origin_tag(), tag_union); + element expected_point = points[1]; expected_point = expected_point.normalize(); @@ -702,7 +954,14 @@ template class stdlib_biggroup : public testing::Test { for (size_t i = 0; i < num_repetitions; i++) { fr scalar_val = fr::random_element(); scalar_ct scalar = scalar_ct::from_witness(&builder, scalar_val); + // Set tag for scalar + scalar.set_origin_tag(submitted_value_origin_tag); auto naf = element_ct::compute_naf(scalar); + + for (const auto& bit : naf) { + // Check that the tag is propagated to bits + EXPECT_EQ(bit.get_origin_tag(), submitted_value_origin_tag); + } // scalar = -naf[254] + \sum_{i=0}^{253}(1-2*naf[i]) 2^{253-i} fr reconstructed_val(0); for (size_t i = 0; i < 254; i++) { @@ -720,7 +979,14 @@ template class stdlib_biggroup : public testing::Test { fr scalar_val = fr::random_element(); scalar_ct scalar = scalar_ct::from_witness(&builder, scalar_val); - element_ct::compute_wnaf(scalar); + // Assign origin tag to scalar + scalar.set_origin_tag(submitted_value_origin_tag); + + const auto result = element_ct::compute_wnaf(scalar); + // Check that wnaf entries propagate tag + for (const auto& wnaf_entry : result) { + EXPECT_EQ(wnaf_entry.get_origin_tag(), submitted_value_origin_tag); + } EXPECT_CIRCUIT_CORRECTNESS(builder); } @@ -738,8 +1004,15 @@ template class stdlib_biggroup : public testing::Test { element_ct P = element_ct::from_witness(&builder, input); scalar_ct x = scalar_ct::from_witness(&builder, scalar); + // Set 2 different origin tags + P.set_origin_tag(submitted_value_origin_tag); + x.set_origin_tag(challenge_origin_tag); + std::cerr << "gates before mul " << builder.get_estimated_num_finalized_gates() << std::endl; element_ct c = element_ct::wnaf_batch_mul({ P }, { x }); + + // Check that the final tag is a union of inputs' tags + EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag); std::cerr << "builder aftr mul " << builder.get_estimated_num_finalized_gates() << std::endl; affine_element c_expected(element(input) * scalar); @@ -770,12 +1043,25 @@ template class stdlib_biggroup : public testing::Test { std::vector circuit_points; std::vector circuit_scalars; + OriginTag union_tag{}; for (size_t i = 0; i < num_points; ++i) { circuit_points.push_back(element_ct::from_witness(&builder, points[i])); circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i])); + // Set tags for points to the submitted value tag for round i and for scalars to challenge tag for the + // same round + circuit_points[i].set_origin_tag( + OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true)); + circuit_scalars[i].set_origin_tag( + OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false)); + union_tag = + OriginTag(union_tag, circuit_points[i].get_origin_tag(), circuit_scalars[i].get_origin_tag()); } + element_ct result_point = element_ct::wnaf_batch_mul(circuit_points, circuit_scalars); + // Check that the results' tag is a union of inputs' tags + EXPECT_EQ(result_point.get_origin_tag(), union_tag); + element expected_point = points[0] + points[1]; expected_point = expected_point.normalize(); @@ -802,12 +1088,24 @@ template class stdlib_biggroup : public testing::Test { std::vector circuit_points; std::vector circuit_scalars; + OriginTag union_tag{}; for (size_t i = 0; i < num_points; ++i) { circuit_points.push_back(element_ct::from_witness(&builder, points[i])); circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i])); + // Set tags for points to the submitted value tag for round i and for scalars to challenge tag for the + // same round + circuit_points[i].set_origin_tag( + OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true)); + circuit_scalars[i].set_origin_tag( + OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false)); + union_tag = + OriginTag(union_tag, circuit_points[i].get_origin_tag(), circuit_scalars[i].get_origin_tag()); } element_ct result_point = element_ct::wnaf_batch_mul(circuit_points, circuit_scalars); + // Check resulting tag is a union of inputs' tags + EXPECT_EQ(result_point.get_origin_tag(), union_tag); + element expected_point = points[1]; expected_point = expected_point.normalize(); @@ -834,13 +1132,25 @@ template class stdlib_biggroup : public testing::Test { std::vector circuit_points; std::vector circuit_scalars; + OriginTag union_tag{}; for (size_t i = 0; i < num_points; ++i) { circuit_points.push_back(element_ct::from_witness(&builder, points[i])); circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i])); + // Set tags for points to the submitted value tag for round i and for scalars to challenge tag for the + // same round + circuit_points[i].set_origin_tag( + OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true)); + circuit_scalars[i].set_origin_tag( + OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false)); + union_tag = + OriginTag(union_tag, circuit_points[i].get_origin_tag(), circuit_scalars[i].get_origin_tag()); } element_ct result_point = element_ct::wnaf_batch_mul(circuit_points, circuit_scalars); + // Check that the resulting tag is a union of inputs' tags + EXPECT_EQ(result_point.get_origin_tag(), union_tag); + element expected_point = points[1]; expected_point = expected_point.normalize(); @@ -869,13 +1179,22 @@ template class stdlib_biggroup : public testing::Test { } std::vector circuit_points; std::vector circuit_scalars; + OriginTag union_tag{}; for (size_t i = 0; i < num_points; ++i) { circuit_points.push_back(element_ct::from_witness(&builder, points[i])); circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i])); + // Set tags for points to the submitted value tag for round i and for scalars to challenge tag for the same + // round + circuit_points[i].set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true)); + circuit_scalars[i].set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false)); + union_tag = OriginTag(union_tag, circuit_points[i].get_origin_tag(), circuit_scalars[i].get_origin_tag()); } element_ct result_point = element_ct::batch_mul(circuit_points, circuit_scalars, 128); + // Check that the resulting tag is a union of inputs' tags + EXPECT_EQ(result_point.get_origin_tag(), union_tag); + element expected_point = g1::one; expected_point.self_set_infinity(); for (size_t i = 0; i < num_points; ++i) { @@ -908,10 +1227,18 @@ template class stdlib_biggroup : public testing::Test { element_ct P = element_ct::from_witness(&builder, input); scalar_ct x = scalar_ct::from_witness(&builder, scalar); + // Set different tags to element and scalar + P.set_origin_tag(submitted_value_origin_tag); + x.set_origin_tag(challenge_origin_tag); + std::cerr << "gates before mul " << builder.get_estimated_num_finalized_gates() << std::endl; // Note: need >136 bits to complete this when working over bigfield element_ct c = element_ct::template wnaf_batch_mul<128>({ P }, { x }); std::cerr << "builder aftr mul " << builder.get_estimated_num_finalized_gates() << std::endl; + + // Check the result's tag is a union of inputs' tags + EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag); + affine_element c_expected(element(input) * scalar); fq c_x_result(c.x.get_value().lo); @@ -945,20 +1272,51 @@ template class stdlib_biggroup : public testing::Test { element_ct P2 = element_ct::from_witness(&builder, input2); element_ct P3 = element_ct::from_witness(&builder, input3); element_ct P4 = element_ct::from_witness(&builder, input4); + // Set elements' tags to submitted value tags from sequential rounds + std::vector element_tags = { + OriginTag(/*parent_index=*/0, /*child_index=*/0, /*is_submitted=*/true), + OriginTag(/*parent_index=*/0, /*child_index=*/1, /*is_submitted=*/true), + OriginTag(/*parent_index=*/0, /*child_index=*/2, /*is_submitted=*/true), + OriginTag(/*parent_index=*/0, /*child_index=*/3, /*is_submitted=*/true) + }; + P1.set_origin_tag(element_tags[0]); + P2.set_origin_tag(element_tags[1]); + P3.set_origin_tag(element_tags[2]); + P4.set_origin_tag(element_tags[3]); fr scalar1 = get_128_bit_scalar(); fr scalar2 = get_128_bit_scalar(); fr scalar3 = get_128_bit_scalar(); fr scalar4 = get_128_bit_scalar(); + scalar_ct x1 = scalar_ct::from_witness(&builder, scalar1); scalar_ct x2 = scalar_ct::from_witness(&builder, scalar2); scalar_ct x3 = scalar_ct::from_witness(&builder, scalar3); scalar_ct x4 = scalar_ct::from_witness(&builder, scalar4); + // Set scalars' tags to challenge tags from sequential rounds + std::vector scalar_tags = { + OriginTag(/*parent_index=*/0, /*child_index=*/0, /*is_submitted=*/false), + OriginTag(/*parent_index=*/0, /*child_index=*/1, /*is_submitted=*/false), + OriginTag(/*parent_index=*/0, /*child_index=*/2, /*is_submitted=*/false), + OriginTag(/*parent_index=*/0, /*child_index=*/3, /*is_submitted=*/false) + }; + x1.set_origin_tag(scalar_tags[0]); + x2.set_origin_tag(scalar_tags[1]); + x3.set_origin_tag(scalar_tags[2]); + x4.set_origin_tag(scalar_tags[3]); + + OriginTag union_tag{}; + for (size_t j = 0; j < element_tags.size(); j++) { + union_tag = OriginTag(union_tag, element_tags[j], scalar_tags[j]); + } + std::cerr << "gates before mul " << builder.get_estimated_num_finalized_gates() << std::endl; element_ct c = element_ct::batch_mul({ P1, P2, P3, P4 }, { x1, x2, x3, x4 }, 128); std::cerr << "builder aftr mul " << builder.get_estimated_num_finalized_gates() << std::endl; + // Check that the resulting tag is a union of inputs' tags + EXPECT_EQ(c.get_origin_tag(), union_tag); element out = input1 * scalar1; out += (input2 * scalar2); out += (input3 * scalar3); @@ -1001,18 +1359,38 @@ template class stdlib_biggroup : public testing::Test { std::vector big_circuit_scalars; std::vector small_circuit_points; std::vector small_circuit_scalars; + OriginTag union_tag{}; for (size_t i = 0; i < num_big_points; ++i) { big_circuit_points.push_back(element_ct::from_witness(&builder, big_points[i])); big_circuit_scalars.push_back(scalar_ct::from_witness(&builder, big_scalars[i])); + // Set tags for points to the submitted value tag for round i and for scalars to challenge tag for the same + // round + big_circuit_points[i].set_origin_tag( + OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true)); + big_circuit_scalars[i].set_origin_tag( + OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false)); + union_tag = + OriginTag(union_tag, big_circuit_points[i].get_origin_tag(), big_circuit_scalars[i].get_origin_tag()); } for (size_t i = 0; i < num_small_points; ++i) { small_circuit_points.push_back(element_ct::from_witness(&builder, small_points[i])); small_circuit_scalars.push_back(scalar_ct::from_witness(&builder, small_scalars[i])); + // Set tags for points to the submitted value tag for round i and for scalars to challenge tag for the same + // round + small_circuit_points[i].set_origin_tag( + OriginTag(/*parent_index=*/0, /*child_index=*/i + num_big_points, /*is_submitted=*/true)); + small_circuit_scalars[i].set_origin_tag( + OriginTag(/*parent_index=*/0, /*child_index=*/i + num_big_points, /*is_submitted=*/false)); + union_tag = OriginTag( + union_tag, small_circuit_points[i].get_origin_tag(), small_circuit_scalars[i].get_origin_tag()); } element_ct result_point = element_ct::bn254_endo_batch_mul( big_circuit_points, big_circuit_scalars, small_circuit_points, small_circuit_scalars, 128); + // Check that the resulting tag is a union of input tags + EXPECT_EQ(result_point.get_origin_tag(), union_tag); + element expected_point = g1::one; expected_point.self_set_infinity(); for (size_t i = 0; i < num_big_points; ++i) { @@ -1194,6 +1572,10 @@ using TestTypes = testing::Types element::bn254_endo_batch_mul(const std::vec Fr scalar_k1 = Fr::from_witness(ctx, k1.to_montgomery_form()); Fr scalar_k2 = Fr::from_witness(ctx, k2.to_montgomery_form()); + // Propagate tags + scalar_k1.set_origin_tag(scalar.get_origin_tag()); + scalar_k2.set_origin_tag(scalar.get_origin_tag()); + // Add copy constraint that validates k1 = scalar_k1 - scalar_k2 * \lambda scalar.assert_equal(scalar_k1 - scalar_k2 * lambda); scalars.push_back(scalar_k1); @@ -288,6 +293,15 @@ element element::bn254_endo_batch_mul(const std::vec std::copy(endo_points.begin(), endo_points.end(), std::back_inserter(points)); std::copy(endo_scalars.begin(), endo_scalars.end(), std::back_inserter(scalars)); + // Compute the tag of the result + OriginTag union_tag{}; + for (size_t i = 0; i < points.size(); i++) { + union_tag = OriginTag(union_tag, OriginTag(points[i].get_origin_tag(), scalars[i].get_origin_tag())); + + // Remove tags so they don't interfere during computation + points[i].set_origin_tag(OriginTag()); + scalars[i].set_origin_tag(OriginTag()); + } ASSERT(big_scalars.size() == num_big_points); ASSERT(small_scalars.size() == num_small_points); @@ -422,6 +436,7 @@ element element::bn254_endo_batch_mul(const std::vec // Remove the offset generator point! accumulator = accumulator - offset_generators.second; + accumulator.set_origin_tag(union_tag); // Return our scalar mul output return accumulator; } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.hpp index 11d37f67f4b..6ce5b5c6bbb 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.hpp @@ -10,6 +10,7 @@ #include "barretenberg/ecc/curves/bn254/g1.hpp" #include "barretenberg/ecc/curves/secp256k1/secp256k1.hpp" #include "barretenberg/ecc/curves/secp256r1/secp256r1.hpp" +#include "barretenberg/transcript/origin_tag.hpp" namespace bb::stdlib::element_goblin { @@ -116,6 +117,7 @@ template class goblin_ele { return batch_mul({ *this, other }, { Fr(1), Fr(1) }); } + goblin_element operator-(const goblin_element& other) const { auto builder = get_context(other); @@ -165,6 +167,9 @@ template class goblin_ele y_lo.assert_equal(y.limbs[0]); y_hi.assert_equal(y.limbs[1]); } + + // Set the tag of the result to the union of the tags of inputs + result.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag())); return result; } @@ -278,6 +283,18 @@ template class goblin_ele return result; } + OriginTag get_origin_tag() const + { + return OriginTag(x.get_origin_tag(), y.get_origin_tag(), _is_infinity.get_origin_tag()); + } + + void set_origin_tag(const OriginTag& tag) + { + x.set_origin_tag(tag); + y.set_origin_tag(tag); + _is_infinity.set_origin_tag(tag); + } + Fq x; Fq y; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin_impl.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin_impl.hpp index 5cd94c27c04..91f4c86ee1f 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin_impl.hpp @@ -1,6 +1,7 @@ #pragma once #include "barretenberg/stdlib/primitives/biggroup/biggroup_goblin.hpp" +#include "barretenberg/transcript/origin_tag.hpp" namespace bb::stdlib::element_goblin { /** @@ -39,10 +40,15 @@ goblin_element goblin_element::batch_mul(const std:: // Loop over all points and scalars size_t num_points = points.size(); + + OriginTag tag_union{}; for (size_t i = 0; i < num_points; ++i) { auto& point = points[i]; auto& scalar = scalars[i]; + // Merge tags + + tag_union = OriginTag(tag_union, OriginTag(point.get_origin_tag(), scalar.get_origin_tag())); // Populate the goblin-style ecc op gates for the given mul inputs ecc_op_tuple op_tuple; bool scalar_is_constant_equal_one = scalar.get_witness_index() == IS_CONSTANT && scalar.get_value() == 1; @@ -97,6 +103,9 @@ goblin_element goblin_element::batch_mul(const std:: auto op2_is_infinity = (x_lo.add_two(x_hi, y_lo) + y_hi).is_zero(); result.set_point_at_infinity(op2_is_infinity); + // Set the tag of the result + result.set_origin_tag(tag_union); + return result; } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_impl.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_impl.hpp index 03872a3a945..b133b9e0f9f 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_impl.hpp @@ -3,6 +3,7 @@ #include "../bit_array/bit_array.hpp" #include "../circuit_builders/circuit_builders.hpp" #include "barretenberg/stdlib/primitives/biggroup/biggroup.hpp" +#include "barretenberg/transcript/origin_tag.hpp" namespace bb::stdlib::element_default { @@ -116,6 +117,8 @@ element element::operator+(const element& other) con bool_ct result_is_infinity = infinity_predicate && (!lhs_infinity && !rhs_infinity); result_is_infinity = result_is_infinity || (lhs_infinity && rhs_infinity); result.set_point_at_infinity(result_is_infinity); + + result.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag())); return result; } @@ -186,6 +189,7 @@ element element::operator-(const element& other) con bool_ct result_is_infinity = infinity_predicate && (!lhs_infinity && !rhs_infinity); result_is_infinity = result_is_infinity || (lhs_infinity && rhs_infinity); result.set_point_at_infinity(result_is_infinity); + result.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag())); return result; } @@ -749,6 +753,25 @@ element element::batch_mul(const std::vector) { // TODO(https://github.com/AztecProtocol/barretenberg/issues/663) @@ -760,7 +783,9 @@ element element::batch_mul(const std::vector element::batch_mul(const std::vector> element::compute_wnaf(const Fr& scalar) reconstructed.assert_is_in_field(); reconstructed.assert_equal(scalar); } + + // Set tags of wnaf_entries to the original scalar tag + const auto original_tag = scalar.get_origin_tag(); + for (auto& entry : wnaf_entries) { + entry.set_origin_tag(original_tag); + } return wnaf_entries; } @@ -581,6 +587,11 @@ std::vector> element::compute_naf(const Fr& scalar, cons Fr accumulator = reconstructed_positive - reconstructed_negative; accumulator.assert_equal(scalar); } + // Propagate tags to naf + const auto original_tag = scalar.get_origin_tag(); + for (auto& naf_entry : naf_entries) { + naf_entry.set_origin_tag(original_tag); + } return naf_entries; } } // namespace bb::stdlib::element_default diff --git a/barretenberg/cpp/src/barretenberg/transcript/origin_tag.hpp b/barretenberg/cpp/src/barretenberg/transcript/origin_tag.hpp index 4b08be6a9d6..c0a6dac0fbc 100644 --- a/barretenberg/cpp/src/barretenberg/transcript/origin_tag.hpp +++ b/barretenberg/cpp/src/barretenberg/transcript/origin_tag.hpp @@ -160,16 +160,20 @@ struct OriginTag { OriginTag(OriginTag&& other) = default; OriginTag& operator=(const OriginTag& other) = default; OriginTag& operator=(OriginTag&& other) = default; + ~OriginTag() = default; - OriginTag(size_t, size_t, bool is_submitted [[maybe_unused]] = true) {} + OriginTag(size_t parent_index [[maybe_unused]], + size_t child_index [[maybe_unused]], + bool is_submitted [[maybe_unused]] = true) + {} OriginTag(const OriginTag&, const OriginTag&) {} template OriginTag(const OriginTag&, const T&...) {} bool operator==(const OriginTag& other) const; void poison() {} void unpoison() {} - bool is_poisoned() const { return false; } - bool is_empty() const { return true; }; + static bool is_poisoned() { return false; } + static bool is_empty() { return true; }; }; inline std::ostream& operator<<(std::ostream& os, OriginTag const&) {