Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 63 additions & 24 deletions barretenberg/cpp/pil/vm2/to_radix_mem.pil
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,21 @@ include "precomputed.pil";
* (2) INVALID_NUM_LIMBS: If num_limbs = 0 while value_to_decompose != 0.
* (3) DST_OUT_OF_BOUNDS_ACCESS: If the writes would access a memory address outside
* of the max AVM memory address (AVM_HIGHEST_MEM_ADDRESS).
* (4) TRUNCATION_ERROR: If the value can't be fully decomposed in the given number of limbs.
*
* N.B The radix is checked to be <= 256 in the execution trace (for dynamic gas computation)
* we do not currently take advantage of any partial checks done but is a future optimisation
* Finally, if is num_limbs = 0 && value_to_decompose = 0 (which are valid inputs), then no memory writes are performed
* Also, if is num_limbs = 0 && value_to_decompose = 0 (which are valid inputs), then no memory writes are performed
* Finally, we check that the value has been fully decomposed in the given number of limbs. In order to do this, we
* check in the start row that the value has been found in the TORADIX subtrace.
*
* This subtrace is connected to the TORADIX subtrace via a lookup. TORADIX is used by
* other subtraces internally (e.g., scalar mul).
*
* NOTE: The TORADIX subtrace performs the decomposition in LITTLE ENDIAN. This is more optimal
* for the internal gadget use. Therefore this subtrace needs to reverse the output of TORADIX
* since the opcode requires BIG ENDIAN.
* since the opcode requires BIG ENDIAN. This allows us to perform the check for truncation error in the start row,
* since the start row of this trace is the last limb in the little endian decomposition.
*/

namespace to_radix_mem;
Expand Down Expand Up @@ -172,12 +176,55 @@ namespace to_radix_mem;
pol commit sel_invalid_num_limbs_err; // err = 1 if num_limbs == 0 while value != 0
sel_invalid_num_limbs_err = sel_num_limbs_is_zero * (1 - sel_value_is_zero);

// err is constrained on the row where start == 1. (each specific error is gated by start)
pol commit err; // Consolidated error flag
err = 1 - (1 - sel_dst_out_of_range_err) * (1 - sel_radix_lt_2_err)

/////////////////////////////////////////////////////
// Dispatch inputs to to_radix
/////////////////////////////////////////////////////
pol commit input_validation_error;
input_validation_error = 1 - (1 - sel_dst_out_of_range_err) * (1 - sel_radix_lt_2_err)
* (1 - sel_radix_gt_256_err) * (1 - sel_invalid_bitwise_radix)
* (1 - sel_invalid_num_limbs_err);

pol commit limb_value;
// In the low level to_radix gadget, found will be on when the number has been reconstructed with the current limbs.
// Refer to the table in the low level to_radix gadget for a visual example.
pol commit value_found;
pol commit sel_should_decompose;
sel_should_decompose * (1 - sel_should_decompose) = 0;

// On the start row, we define sel_should_decompose as !input_validation_error && !num_limbs_is_zero
// We can't inline input_validation_error because of the degree.
start * ((1 - input_validation_error) * (1 - sel_num_limbs_is_zero) - sel_should_decompose) = 0;

// On following rows, we propagate input_validation_error.
#[SEL_SHOULD_DECOMPOSE_CONTINUITY]
NOT_LAST * (sel_should_decompose' - sel_should_decompose) = 0;

pol commit limb_index_to_lookup; // Need this since we want Big-Endian but the gadget is Little-Endian
limb_index_to_lookup = sel_should_decompose * (num_limbs - 1);

#[INPUT_OUTPUT_TO_RADIX]
sel_should_decompose { value_to_decompose, limb_index_to_lookup, radix, limb_value, value_found }
in
to_radix.sel {to_radix.value, to_radix.limb_index, to_radix.radix, to_radix.limb, to_radix.found };

/////////////////////////////////////////////////////
// Error Handling - Check no truncation error
/////////////////////////////////////////////////////
pol commit sel_truncation_error;
sel_truncation_error * (1 - sel_truncation_error) = 0;
// A truncation error happens if in the start row, we look up the to_radix gadget and value_found is off.
// The to_radix gadget is little endian, so the first row that we lookup is the last limb. If it's not found in the last limb,
// it means that the number is truncated with the given number of limbs.
#[TRUNCATION_ERROR]
sel_truncation_error = start * sel_should_decompose * (1 - value_found);

/////////////////////////////////////////////////////
// Error Handling - Consolidated error flag
/////////////////////////////////////////////////////
pol commit err;
err = start * (1 - (1 - input_validation_error) * (1 - sel_truncation_error));

/////////////////////////////////////////////////////
// Control flow management and terminating trace
/////////////////////////////////////////////////////
Expand Down Expand Up @@ -211,33 +258,25 @@ namespace to_radix_mem;
#[LAST_ROW_VALID_COMPUTATION]
NO_ERR_NOR_NUM_LIMBS_ZERO * (NUM_LIMBS_MINUS_ONE * (last * (1 - num_limbs_minus_one_inv) + num_limbs_minus_one_inv) - 1 + last) = 0;

/////////////////////////////////////////////////////
// Dispatch inputs to to_radix and retrieve outputs
/////////////////////////////////////////////////////
pol commit output_limb_value;
pol commit sel_should_exec;
// If the num limbs are zero, we don't dispatch to the gadget or write to memory.
sel_should_exec = sel * (1 - err) * (1 - sel_num_limbs_is_zero);

pol commit limb_index_to_lookup; // Need this since we want Big-Endian but the gadget is Little-Endian
limb_index_to_lookup = sel_should_exec * (num_limbs - 1);
#[INPUT_OUTPUT_TO_RADIX]
sel_should_exec { value_to_decompose, limb_index_to_lookup, radix, output_limb_value }
in
to_radix.sel {to_radix.value, to_radix.limb_index, to_radix.radix, to_radix.limb };

////////////////////////////////////////////////
// Write output to memory
////////////////////////////////////////////////
pol commit sel_should_write_mem;
// We compute sel_should_write_mem in the start row, as no error at all and num_limbs != 0.
start * ((1 - err) * (1 - sel_num_limbs_is_zero) - sel_should_write_mem) = 0;
// On following rows, we propagate sel_should_write_mem.
#[SEL_SHOULD_WRITE_MEM_CONTINUITY]
NOT_LAST * (sel_should_write_mem' - sel_should_write_mem) = 0;

pol commit output_tag;
// Conditional Assignment: is_output_bits ? U1 : U8
output_tag = sel_should_exec * ((constants.MEM_TAG_U1 - constants.MEM_TAG_U8) * is_output_bits + constants.MEM_TAG_U8);
output_tag = sel_should_write_mem * ((constants.MEM_TAG_U1 - constants.MEM_TAG_U8) * is_output_bits + constants.MEM_TAG_U8);

#[WRITE_MEM]
sel_should_exec {
sel_should_write_mem {
execution_clk, space_id,
dst_addr, output_limb_value,
/*mem_tag*/ output_tag, /*rw=1*/ sel_should_exec
dst_addr, limb_value,
/*mem_tag*/ output_tag, /*rw=1*/ sel_should_write_mem
} is
memory.sel_to_radix_write {
memory.clk, memory.space_id,
Expand Down
Loading
Loading