-
Notifications
You must be signed in to change notification settings - Fork 595
Expand file tree
/
Copy pathecc_op_queue.test.cpp
More file actions
193 lines (164 loc) · 8.5 KB
/
ecc_op_queue.test.cpp
File metadata and controls
193 lines (164 loc) · 8.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#include "barretenberg/op_queue/ecc_op_queue.hpp"
#include <gtest/gtest.h>
using namespace bb;
class ECCOpQueueTest {
public:
using Curve = curve::BN254;
using G1 = Curve::AffineElement;
using Fr = Curve::ScalarField;
using Polynomial = bb::Polynomial<Fr>;
// Perform some basic interactions with the ECC op queue to mock the behavior of a single circuit
static void populate_an_arbitrary_subtable_of_ops(const std::shared_ptr<bb::ECCOpQueue>& op_queue)
{
auto P1 = G1::random_element();
auto P2 = G1::random_element();
auto z = Fr::random_element();
op_queue->initialize_new_subtable();
op_queue->add_accumulate(P1);
op_queue->mul_accumulate(P2, z);
op_queue->eq_and_reset();
}
/**
* @brief Check that the table column polynomials reconstructed by the op queue have the correct relationship
*
*/
static void check_table_column_polynomials(const std::shared_ptr<bb::ECCOpQueue>& op_queue,
MergeSettings settings,
std::optional<size_t> ultra_fixed_offset = std::nullopt)
{
// Construct column polynomials corresponding to the full table (T), the previous table (T_prev), and the
// current subtable (t_current)
auto table_polynomials = op_queue->construct_ultra_ops_table_columns();
auto prev_table_polynomials = op_queue->construct_previous_ultra_ops_table_columns();
auto subtable_polynomials = op_queue->construct_current_ultra_ops_subtable_columns();
// Check T(x) = t_current(x) + x^k * T_prev(x) at a single random challenge point
Fr eval_challenge = Fr::random_element();
for (auto [table_poly, prev_table_poly, subtable_poly] :
zip_view(table_polynomials, prev_table_polynomials, subtable_polynomials)) {
const Fr table_eval = table_poly.evaluate(eval_challenge); // T(x)
// Check that the previous table polynomial is constructed correctly according to the merge settings by
// checking the identity at a single point
if (settings == MergeSettings::PREPEND) {
// T(x) = t_current(x) + x^k * T_prev(x), where k is the size of the current subtable
const size_t current_subtable_size = op_queue->get_current_ultra_ops_subtable_num_rows(); // k
const Fr subtable_eval = subtable_poly.evaluate(eval_challenge); // t_current(x)
const Fr shifted_previous_table_eval = prev_table_poly.evaluate(eval_challenge) *
eval_challenge.pow(current_subtable_size); // x^k * T_prev(x)
EXPECT_EQ(table_eval, subtable_eval + shifted_previous_table_eval);
} else {
// APPEND merge performs concatenation directly to end of previous table or at a specified fixed offset
const size_t prev_table_size = op_queue->get_previous_ultra_ops_table_num_rows(); // k
const size_t shift_magnitude = ultra_fixed_offset.has_value()
? ultra_fixed_offset.value() * bb::UltraEccOpsTable::NUM_ROWS_PER_OP
: prev_table_size; // k
// T(x) = T_prev(x) + x^k * t_current(x), where k is the shift magnitude
const Fr prev_table_eval = prev_table_poly.evaluate(eval_challenge); // T_prev(x)
const Fr shifted_subtable_eval =
subtable_poly.evaluate(eval_challenge) * eval_challenge.pow(shift_magnitude); // x^k * t_current(x)
EXPECT_EQ(table_eval, shifted_subtable_eval + prev_table_eval);
}
}
}
/**
* @brief Check that the opcode values are consistent between the ultra ops table and the eccvm ops table
*
* @param op_queue
*/
static void check_opcode_consistency_with_eccvm(const std::shared_ptr<bb::ECCOpQueue>& op_queue)
{
auto ultra_table = op_queue->get_ultra_ops();
auto eccvm_table = op_queue->get_eccvm_ops();
size_t j = 0;
for (const auto& ultra_op : ultra_table) {
if (ultra_op.op_code.value() == 0) {
continue;
}
EXPECT_EQ(ultra_op.op_code.value(), eccvm_table[j++].op_code.value());
}
};
};
TEST(ECCOpQueueTest, Basic)
{
using G1 = ECCOpQueueTest::G1;
ECCOpQueue op_queue;
op_queue.add_accumulate(bb::g1::affine_one);
op_queue.empty_row_for_testing();
op_queue.merge();
const auto& eccvm_ops = op_queue.get_eccvm_ops();
EXPECT_EQ(eccvm_ops[0].base_point, G1::one());
EXPECT_EQ(eccvm_ops[1].op_code.add, false);
}
TEST(ECCOpQueueTest, InternalAccumulatorCorrectness)
{
using G1 = ECCOpQueueTest::G1;
using Fr = ECCOpQueueTest::Fr;
// Compute a simple point accumulation natively
auto P1 = G1::random_element();
auto P2 = G1::random_element();
auto z = Fr::random_element();
auto P_expected = P1 + P2 * z;
// Add the same operations to the ECC op queue; the native computation is performed under the hood.
ECCOpQueue op_queue;
op_queue.add_accumulate(P1);
op_queue.mul_accumulate(P2, z);
// The correct result should now be stored in the accumulator within the op queue
EXPECT_EQ(op_queue.get_accumulator(), P_expected);
// Adding an equality op should reset the accumulator to zero (the point at infinity)
op_queue.eq_and_reset();
EXPECT_TRUE(op_queue.get_accumulator().is_point_at_infinity());
}
// Check that the ECC op queue correctly constructs the table column polynomials for the full table, the previous table,
// and the current subtable via successive prepending of subtables
TEST(ECCOpQueueTest, ColumnPolynomialConstructionPrependOnly)
{
// Instantiate an EccOpQueue and populate it with several subtables of ECC ops
auto op_queue = std::make_shared<bb::ECCOpQueue>();
// Check that the table polynomials have the correct form after each subtable concatenation
const size_t NUM_SUBTABLES = 5;
for (size_t i = 0; i < NUM_SUBTABLES; ++i) {
ECCOpQueueTest::populate_an_arbitrary_subtable_of_ops(op_queue);
MergeSettings settings = MergeSettings::PREPEND;
op_queue->merge(settings);
ECCOpQueueTest::check_table_column_polynomials(op_queue, settings);
}
ECCOpQueueTest::check_opcode_consistency_with_eccvm(op_queue);
}
TEST(ECCOpQueueTest, ColumnPolynomialConstructionPrependThenAppend)
{
// Instantiate an EccOpQueue and populate it with several subtables of ECC ops
auto op_queue = std::make_shared<bb::ECCOpQueue>();
// Check that the table polynomials have the correct form after each subtable concatenation
const size_t NUM_SUBTABLES = 2;
for (size_t i = 0; i < NUM_SUBTABLES; ++i) {
ECCOpQueueTest::populate_an_arbitrary_subtable_of_ops(op_queue);
MergeSettings settings = MergeSettings::PREPEND;
op_queue->merge(settings);
ECCOpQueueTest::check_table_column_polynomials(op_queue, settings);
}
// Do a single append operation
ECCOpQueueTest::populate_an_arbitrary_subtable_of_ops(op_queue);
MergeSettings settings = MergeSettings::APPEND;
op_queue->merge(settings);
ECCOpQueueTest::check_table_column_polynomials(op_queue, settings);
ECCOpQueueTest::check_opcode_consistency_with_eccvm(op_queue);
}
TEST(ECCOpQueueTest, ColumnPolynomialConstructionPrependThenAppendAtFixedOffset)
{
// Instantiate an EccOpQueue and populate it with several subtables of ECC ops
auto op_queue = std::make_shared<bb::ECCOpQueue>();
// Check that the table polynomials have the correct form after each subtable concatenation
const size_t NUM_SUBTABLES = 2;
for (size_t i = 0; i < NUM_SUBTABLES; ++i) {
ECCOpQueueTest::populate_an_arbitrary_subtable_of_ops(op_queue);
MergeSettings settings = MergeSettings::PREPEND;
op_queue->merge(settings);
ECCOpQueueTest::check_table_column_polynomials(op_queue, settings);
}
// Do a single append operation at a fixed offset (sufficiently large as to not overlap with the existing table)
const size_t ultra_fixed_offset = op_queue->get_ultra_ops_table_num_rows() + 20;
ECCOpQueueTest::populate_an_arbitrary_subtable_of_ops(op_queue);
MergeSettings settings = MergeSettings::APPEND;
op_queue->merge(settings, ultra_fixed_offset);
ECCOpQueueTest::check_table_column_polynomials(op_queue, settings, ultra_fixed_offset);
ECCOpQueueTest::check_opcode_consistency_with_eccvm(op_queue);
}