Skip to content

Commit 53c070d

Browse files
authored
fix: make translator use ultra rather than eccvm ops (#13489)
While attempting to implement the consistency check between the Merge and Translator Verifier I realised that the TranslatorCircuitBuilder is constructed using the VMops, redundantly converted to UltraOps. This PR addressed the issue and attempts a small cleanup on the `UltraEccOpsTable`. Closes AztecProtocol/barretenberg#1266
1 parent 7b16791 commit 53c070d

File tree

5 files changed

+65
-90
lines changed

5 files changed

+65
-90
lines changed

barretenberg/cpp/src/barretenberg/op_queue/ecc_op_queue.hpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
#include "barretenberg/op_queue/ecc_ops_table.hpp"
66
#include "barretenberg/op_queue/eccvm_row_tracker.hpp"
77
#include "barretenberg/polynomials/polynomial.hpp"
8-
#include "barretenberg/stdlib/primitives/bigfield/constants.hpp"
98
namespace bb {
109

1110
/**
@@ -28,15 +27,17 @@ class ECCOpQueue {
2827
// The operations written to the queue are also performed natively; the result is stored in accumulator
2928
Point accumulator = point_at_infinity;
3029

31-
static constexpr size_t DEFAULT_NON_NATIVE_FIELD_LIMB_BITS = stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION;
32-
3330
EccvmOpsTable eccvm_ops_table; // table of ops in the ECCVM format
3431
UltraEccOpsTable ultra_ops_table; // table of ops in the Ultra-arithmetization format
3532

3633
// Storage for the reconstructed eccvm ops table in contiguous memory. (Intended to be constructed once and for all
3734
// prior to ECCVM construction to avoid repeated prepending of subtables in physical memory).
3835
std::vector<ECCVMOperation> eccvm_ops_reconstructed;
3936

37+
// Storage for the reconstructed ultra ops table in contiguous memory. (Intended to be constructed once and for all
38+
// prior to Translator circuit construction to avoid repeated prepending of subtables in physical memory).
39+
std::vector<UltraOp> ultra_ops_reconstructed;
40+
4041
// Tracks number of muls and size of eccvm in real time as the op queue is updated
4142
EccvmRowTracker eccvm_row_tracker;
4243

@@ -72,9 +73,15 @@ class ECCOpQueue {
7273
// Reconstruct the full table of eccvm ops in contiguous memory from the independent subtables
7374
void construct_full_eccvm_ops_table() { eccvm_ops_reconstructed = eccvm_ops_table.get_reconstructed(); }
7475

76+
// Reconstruct the full table of ultra ops in contiguous memory from the independent subtables
77+
void construct_full_ultra_ops_table() { ultra_ops_reconstructed = ultra_ops_table.get_reconstructed(); }
78+
7579
size_t get_ultra_ops_table_num_rows() const { return ultra_ops_table.ultra_table_size(); }
7680
size_t get_current_ultra_ops_subtable_num_rows() const { return ultra_ops_table.current_ultra_subtable_size(); }
7781

82+
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1339): Consider making the ultra and eccvm ops getters
83+
// more memory efficient
84+
7885
// Get the full table of ECCVM ops in contiguous memory; construct it if it has not been constructed already
7986
std::vector<ECCVMOperation>& get_eccvm_ops()
8087
{
@@ -84,6 +91,14 @@ class ECCOpQueue {
8491
return eccvm_ops_reconstructed;
8592
}
8693

94+
std::vector<UltraOp>& get_ultra_ops()
95+
{
96+
if (ultra_ops_reconstructed.empty()) {
97+
construct_full_ultra_ops_table();
98+
}
99+
return ultra_ops_reconstructed;
100+
}
101+
87102
/**
88103
* @brief Get the number of rows in the 'msm' column section, for all msms in the circuit
89104
*/
@@ -239,7 +254,7 @@ class ECCOpQueue {
239254
ultra_op.op_code = op_code;
240255

241256
// Decompose point coordinates (Fq) into hi-lo chunks (Fr)
242-
const size_t CHUNK_SIZE = 2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS;
257+
const size_t CHUNK_SIZE = 2 * stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION;
243258
uint256_t x_256(point.x);
244259
uint256_t y_256(point.y);
245260
ultra_op.return_is_infinity = point.is_point_at_infinity();

barretenberg/cpp/src/barretenberg/op_queue/ecc_ops_table.hpp

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "barretenberg/ecc/curves/bn254/bn254.hpp"
44
#include "barretenberg/eccvm/eccvm_builder_types.hpp"
55
#include "barretenberg/polynomials/polynomial.hpp"
6+
#include "barretenberg/stdlib/primitives/bigfield/constants.hpp"
67
#include <deque>
78
namespace bb {
89

@@ -35,6 +36,7 @@ struct EccOpCode {
3536

3637
struct UltraOp {
3738
using Fr = curve::BN254::ScalarField;
39+
using Fq = curve::BN254::BaseField;
3840
EccOpCode op_code;
3941
Fr x_lo;
4042
Fr x_hi;
@@ -43,34 +45,32 @@ struct UltraOp {
4345
Fr z_1;
4446
Fr z_2;
4547
bool return_is_infinity;
46-
};
47-
48-
template <typename CycleGroup> struct VMOperation {
49-
EccOpCode op_code = {};
50-
typename CycleGroup::affine_element base_point = typename CycleGroup::affine_element{ 0, 0 };
51-
uint256_t z1 = 0;
52-
uint256_t z2 = 0;
53-
typename CycleGroup::subgroup_field mul_scalar_full = 0;
54-
bool operator==(const VMOperation<CycleGroup>& other) const = default;
5548

5649
/**
5750
* @brief Get the point in standard form i.e. as two coordinates x and y in the base field or as a point at
5851
* infinity whose coordinates are set to (0,0).
5952
*
60-
* @details These are represented as uint265_t to make chunking easier, the function being used in translator
61-
* where each coordinate is chunked to efficiently be represented in the scalar field.
6253
*/
63-
std::array<uint256_t, 2> get_base_point_standard_form() const
54+
std::array<Fq, 2> get_base_point_standard_form() const
6455
{
65-
uint256_t x(base_point.x);
66-
uint256_t y(base_point.y);
67-
if (base_point.is_point_at_infinity()) {
68-
x = 0;
69-
y = 0;
56+
if (return_is_infinity) {
57+
return { Fq(0), Fq(0) };
7058
}
59+
auto x = Fq((uint256_t(x_hi) << 2 * stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION) + uint256_t(x_lo));
60+
auto y = Fq((uint256_t(y_hi) << 2 * stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION) + uint256_t(y_lo));
61+
7162
return { x, y };
7263
}
7364
};
65+
66+
template <typename CycleGroup> struct VMOperation {
67+
EccOpCode op_code = {};
68+
typename CycleGroup::affine_element base_point = typename CycleGroup::affine_element{ 0, 0 };
69+
uint256_t z1 = 0;
70+
uint256_t z2 = 0;
71+
typename CycleGroup::subgroup_field mul_scalar_full = 0;
72+
bool operator==(const VMOperation<CycleGroup>& other) const = default;
73+
};
7474
using ECCVMOperation = VMOperation<curve::BN254::Group>;
7575

7676
/**
@@ -141,7 +141,7 @@ template <typename OpFormat> class EccOpsTable {
141141
}
142142
};
143143

144-
/***
144+
/**
145145
* @brief A VM operation is represented as one row with 6 columns in the ECCVM version of the Op Queue.
146146
* | OP | X | Y | z_1 | z_2 | mul_scalar_full |
147147
*/
@@ -181,6 +181,7 @@ class UltraEccOpsTable {
181181
size_t previous_ultra_table_size() const { return (ultra_table_size() - current_ultra_subtable_size()); }
182182
void create_new_subtable(size_t size_hint = 0) { table.create_new_subtable(size_hint); }
183183
void push(const UltraOp& op) { table.push(op); }
184+
std::vector<UltraOp> get_reconstructed() const { return table.get_reconstructed(); }
184185

185186
// Construct the columns of the full ultra ecc ops table
186187
ColumnPolynomials construct_table_columns() const

barretenberg/cpp/src/barretenberg/translator_vm/translator_circuit_builder.cpp

Lines changed: 22 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -544,89 +544,52 @@ void TranslatorCircuitBuilder::create_accumulation_gate(const AccumulationInput
544544
bb::constexpr_for<0, TOTAL_COUNT, 1>([&]<size_t i>() { ASSERT(std::get<i>(wires).size() == num_gates); });
545545
}
546546

547-
/**
548-
* @brief Given an ECCVM operation, previous accumulator and necessary challenges, compute witnesses for one
549-
* accumulation
550-
*
551-
* @tparam Fq
552-
* @return TranslatorCircuitBuilder::AccumulationInput
553-
*/
554-
555-
TranslatorCircuitBuilder::AccumulationInput TranslatorCircuitBuilder::compute_witness_values_for_one_ecc_op(
556-
const ECCVMOperation& ecc_op,
557-
const Fq previous_accumulator,
558-
const Fq batching_challenge_v,
559-
const Fq evaluation_input_x)
560-
{
561-
// Get the Opcode value
562-
Fr op(ecc_op.op_code.value());
563-
Fr p_x_lo(0);
564-
Fr p_x_hi(0);
565-
Fr p_y_lo(0);
566-
Fr p_y_hi(0);
567-
568-
// Split P.x and P.y into their representations in bn254 transcript
569-
// if we have a point at infinity, set x/y to zero
570-
// in the biggroup_goblin class we use `assert_equal` statements to validate
571-
// the original in-circuit coordinate values are also zero
572-
const auto [x_256, y_256] = ecc_op.get_base_point_standard_form();
573-
574-
p_x_lo = Fr(uint256_t(x_256).slice(0, 2 * NUM_LIMB_BITS));
575-
p_x_hi = Fr(uint256_t(x_256).slice(2 * NUM_LIMB_BITS, 4 * NUM_LIMB_BITS));
576-
p_y_lo = Fr(uint256_t(y_256).slice(0, 2 * NUM_LIMB_BITS));
577-
p_y_hi = Fr(uint256_t(y_256).slice(2 * NUM_LIMB_BITS, 4 * NUM_LIMB_BITS));
578-
579-
// Generate the full witness values
580-
return generate_witness_values(op,
581-
p_x_lo,
582-
p_x_hi,
583-
p_y_lo,
584-
p_y_hi,
585-
Fr(ecc_op.z1),
586-
Fr(ecc_op.z2),
587-
previous_accumulator,
588-
batching_challenge_v,
589-
evaluation_input_x);
590-
}
591-
592-
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1266): Evaluate whether this method can reuse existing data
593-
// in the op queue for improved efficiency
594547
void TranslatorCircuitBuilder::feed_ecc_op_queue_into_circuit(const std::shared_ptr<ECCOpQueue> ecc_op_queue)
595548
{
596549
using Fq = bb::fq;
597-
const auto& eccvm_ops = ecc_op_queue->get_eccvm_ops();
550+
const auto& ultra_ops = ecc_op_queue->get_ultra_ops();
598551
std::vector<Fq> accumulator_trace;
599552
Fq current_accumulator(0);
600-
if (eccvm_ops.empty()) {
553+
if (ultra_ops.empty()) {
601554
return;
602555
}
603-
// Rename for ease of use
604-
auto x = evaluation_input_x;
605-
auto v = batching_challenge_v;
606556

607557
// We need to precompute the accumulators at each step, because in the actual circuit we compute the values starting
608558
// from the later indices. We need to know the previous accumulator to create the gate
609-
for (size_t i = 0; i < eccvm_ops.size(); i++) {
610-
const auto& ecc_op = eccvm_ops[eccvm_ops.size() - 1 - i];
611-
current_accumulator *= x;
612-
const auto [x_256, y_256] = ecc_op.get_base_point_standard_form();
559+
for (size_t i = 0; i < ultra_ops.size(); i++) {
560+
const auto& ultra_op = ultra_ops[ultra_ops.size() - 1 - i];
561+
current_accumulator *= evaluation_input_x;
562+
const auto [x_256, y_256] = ultra_op.get_base_point_standard_form();
613563
current_accumulator +=
614-
(Fq(ecc_op.op_code.value()) + v * (x_256 + v * (y_256 + v * (ecc_op.z1 + v * ecc_op.z2))));
564+
Fq(ultra_op.op_code.value()) +
565+
batching_challenge_v *
566+
(x_256 + batching_challenge_v *
567+
(y_256 + batching_challenge_v *
568+
(uint256_t(ultra_op.z_1) + batching_challenge_v * uint256_t(ultra_op.z_2))));
615569
accumulator_trace.push_back(current_accumulator);
616570
}
617571

618572
// We don't care about the last value since we'll recompute it during witness generation anyway
619573
accumulator_trace.pop_back();
620574

621-
for (const auto& eccvm_op : eccvm_ops) {
575+
for (const auto& ultra_op : ultra_ops) {
622576
Fq previous_accumulator = 0;
623577
// Pop the last value from accumulator trace and use it as previous accumulator
624578
if (!accumulator_trace.empty()) {
625579
previous_accumulator = accumulator_trace.back();
626580
accumulator_trace.pop_back();
627581
}
628582
// Compute witness values
629-
auto one_accumulation_step = compute_witness_values_for_one_ecc_op(eccvm_op, previous_accumulator, v, x);
583+
AccumulationInput one_accumulation_step = generate_witness_values(ultra_op.op_code.value(),
584+
ultra_op.x_lo,
585+
ultra_op.x_hi,
586+
ultra_op.y_lo,
587+
ultra_op.y_hi,
588+
ultra_op.z_1,
589+
ultra_op.z_2,
590+
previous_accumulator,
591+
batching_challenge_v,
592+
evaluation_input_x);
630593

631594
// And put them into the wires
632595
create_accumulation_gate(one_accumulation_step);

barretenberg/cpp/src/barretenberg/translator_vm/translator_circuit_builder.hpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -469,10 +469,6 @@ class TranslatorCircuitBuilder : public CircuitBuilderBase<bb::fr> {
469469
const Fq previous_accumulator,
470470
const Fq batching_challenge_v,
471471
const Fq evaluation_input_x);
472-
static AccumulationInput compute_witness_values_for_one_ecc_op(const ECCVMOperation& ecc_op,
473-
const Fq previous_accumulator,
474-
const Fq batching_challenge_v,
475-
const Fq evaluation_input_x);
476472
};
477473

478474
} // namespace bb

barretenberg/cpp/src/barretenberg/translator_vm/translator_circuit_builder.test.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,16 @@ TEST(TranslatorCircuitBuilder, SeveralOperationCorrectness)
105105
// Get an inverse
106106
Fq x_inv = x.invert();
107107
// Compute the batched evaluation of polynomials (multiplying by inverse to go from lower to higher)
108-
const auto& eccvm_ops = op_queue->get_eccvm_ops();
109-
for (const auto& ecc_op : eccvm_ops) {
108+
const auto& ultra_ops = op_queue->get_ultra_ops();
109+
for (const auto& ecc_op : ultra_ops) {
110110
op_accumulator = op_accumulator * x_inv + ecc_op.op_code.value();
111111
const auto [x_u256, y_u256] = ecc_op.get_base_point_standard_form();
112112
p_x_accumulator = p_x_accumulator * x_inv + x_u256;
113113
p_y_accumulator = p_y_accumulator * x_inv + y_u256;
114-
z_1_accumulator = z_1_accumulator * x_inv + ecc_op.z1;
115-
z_2_accumulator = z_2_accumulator * x_inv + ecc_op.z2;
114+
z_1_accumulator = z_1_accumulator * x_inv + uint256_t(ecc_op.z_1);
115+
z_2_accumulator = z_2_accumulator * x_inv + uint256_t(ecc_op.z_2);
116116
}
117-
Fq x_pow = x.pow(eccvm_ops.size() - 1);
117+
Fq x_pow = x.pow(ultra_ops.size() - 1);
118118

119119
// Multiply by an appropriate power of x to get rid of the inverses
120120
Fq result = ((((z_2_accumulator * batching_challenge + z_1_accumulator) * batching_challenge + p_y_accumulator) *

0 commit comments

Comments
 (0)