296 lines
9.5 KiB
Rust
Raw Normal View History

2022-11-30 22:00:57 -08:00
use ethereum_types::U256;
use plonky2::field::types::PrimeField64;
2022-11-30 22:00:57 -08:00
2023-03-21 19:19:02 -07:00
use crate::extension_tower::BN_BASE;
2022-11-30 22:00:57 -08:00
use crate::util::{addmod, mulmod, submod};
2022-11-30 15:25:07 -08:00
mod addcy;
mod byte;
mod divmod;
Modular operations for the EVM arithmetic unit (#755) * First draft of 256-bit addition. * Update comment. * cargo fmt * Rename addition evaluation file. * Port ALU logic from SZ. * Give a name to some magic numbers. * `addition.rs` -> `add.rs`; fix carry propagation in add; impl sub. * Clippy. * Combine hi and lo parts of the output. * Implement MUL. * Suppress Clippy's attempt to make my code even harder to read. * Next draft of MUL. * Make all limbs (i.e. input and output) 16-bits. * Tidying. * Use iterators instead of building arrays. * Documentation. * Clippy is wrong; also cargo fmt. * Un-refactor equality checking, since it was wrong for sub. * Daniel comments. * Daniel comments. * Rename folder 'alu' -> 'arithmetic'. * Rename file. * Finish changing name ALU -> Arithmetic Unit. * Finish removing dependency on array_zip feature. * Remove operations that will be handled elsewhere. * Rename var; tidy up. * Clean up columns; mark places where range-checks need to be done. * Import all names in 'columns' to reduce verbiage. * cargo fmt * Fix aux_in calculation in mul. * Remove redundant 'allow's; more precise range-check size. * Document functions. * Document MUL instruction verification technique. * Initial tests for ADD. * Minor test fixes; add test for SUB. * Fix bugs in generate functions. * Fix SUB verification; refactor equality verification. * cargo fmt * Add test for MUL and fix some bugs. * Update doc. * Quiet incorrect clippy error. * Initial implementation of ADDMOD and MOD. * Fixes to addmod. * Update doc. * Do 1000 random tests instead of just 1. * Documentation fix. * Working version of ADDMOD. * Working version of MOD. * Name magic number; do multiple MUL tests. * Add code and test for special case; add some docs. * Fix spelling mistake. * Simplify asserts. * Tidy comment. * Remove unused module. * cargo fmt * Check that output is reduced. * Add conversion of canonical `i64` to a `Field64`. * Handle zero modulus within degree constraint. * cargo fmt * Fix some comments. * Check that the top half of the product is zero! * Start of refactor. * Refactoring. * Remove zero and reduction handling from addmod. * Refactoring; renaming; bug fixes. * Reuse intermediate calculations across all modular operations; don't negate quot poly unnecessarily. * Fix bug where last elt of q*m wasn't checked. * Refactoring. * Move circuit poly functions to utils.rs. * Rename ADDMOD stuff to MODULAR. * Rename module addmod -> modular. * Handle zero modulus. * Verify that output is reduced. * Implement recursive version of modular circuits. * clippy * Tidy up i64 -> Field conversion following Jacqui's comments. * cargo fmt * Improved documentation. * Address Jacqui's comments. * Save some gates by using builder.arithmetic_extension().
2022-10-07 17:15:50 +11:00
mod modular;
EVM Arithmetic Stark table (#559) * First draft of 256-bit addition. * Update comment. * cargo fmt * Rename addition evaluation file. * Port ALU logic from SZ. * Give a name to some magic numbers. * `addition.rs` -> `add.rs`; fix carry propagation in add; impl sub. * Clippy. * Combine hi and lo parts of the output. * Implement MUL. * Suppress Clippy's attempt to make my code even harder to read. * Next draft of MUL. * Make all limbs (i.e. input and output) 16-bits. * Tidying. * Use iterators instead of building arrays. * Documentation. * Clippy is wrong; also cargo fmt. * Un-refactor equality checking, since it was wrong for sub. * Daniel comments. * Daniel comments. * Rename folder 'alu' -> 'arithmetic'. * Rename file. * Finish changing name ALU -> Arithmetic Unit. * Finish removing dependency on array_zip feature. * Remove operations that will be handled elsewhere. * Rename var; tidy up. * Clean up columns; mark places where range-checks need to be done. * Import all names in 'columns' to reduce verbiage. * cargo fmt * Fix aux_in calculation in mul. * Remove redundant 'allow's; more precise range-check size. * Document functions. * Document MUL instruction verification technique. * Initial tests for ADD. * Minor test fixes; add test for SUB. * Fix bugs in generate functions. * Fix SUB verification; refactor equality verification. * cargo fmt * Add test for MUL and fix some bugs. * Update doc. * Quiet incorrect clippy error. * Clean up 'decode.rs'. * Fold 'decode.rs' into 'arithmetic_stark.rs'. * Force limb size to divide EVM register size. * Document range-check warning and fix end value calc. * Convert `debug_assert!`s into `assert!`s. * Clean up various kinds of iterator usage. * Remove unnecessary type spec. * Document unexpected use of `collect`.
2022-06-29 11:56:48 +10:00
mod mul;
mod shift;
EVM Arithmetic Stark table (#559) * First draft of 256-bit addition. * Update comment. * cargo fmt * Rename addition evaluation file. * Port ALU logic from SZ. * Give a name to some magic numbers. * `addition.rs` -> `add.rs`; fix carry propagation in add; impl sub. * Clippy. * Combine hi and lo parts of the output. * Implement MUL. * Suppress Clippy's attempt to make my code even harder to read. * Next draft of MUL. * Make all limbs (i.e. input and output) 16-bits. * Tidying. * Use iterators instead of building arrays. * Documentation. * Clippy is wrong; also cargo fmt. * Un-refactor equality checking, since it was wrong for sub. * Daniel comments. * Daniel comments. * Rename folder 'alu' -> 'arithmetic'. * Rename file. * Finish changing name ALU -> Arithmetic Unit. * Finish removing dependency on array_zip feature. * Remove operations that will be handled elsewhere. * Rename var; tidy up. * Clean up columns; mark places where range-checks need to be done. * Import all names in 'columns' to reduce verbiage. * cargo fmt * Fix aux_in calculation in mul. * Remove redundant 'allow's; more precise range-check size. * Document functions. * Document MUL instruction verification technique. * Initial tests for ADD. * Minor test fixes; add test for SUB. * Fix bugs in generate functions. * Fix SUB verification; refactor equality verification. * cargo fmt * Add test for MUL and fix some bugs. * Update doc. * Quiet incorrect clippy error. * Clean up 'decode.rs'. * Fold 'decode.rs' into 'arithmetic_stark.rs'. * Force limb size to divide EVM register size. * Document range-check warning and fix end value calc. * Convert `debug_assert!`s into `assert!`s. * Clean up various kinds of iterator usage. * Remove unnecessary type spec. * Document unexpected use of `collect`.
2022-06-29 11:56:48 +10:00
mod utils;
pub mod arithmetic_stark;
pub(crate) mod columns;
2022-11-30 15:25:07 -08:00
/// An enum representing different binary operations.
///
/// `Shl` and `Shr` are handled differently, by leveraging `Mul` and `Div` respectively.
2022-11-30 15:25:07 -08:00
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum BinaryOperator {
2022-11-30 17:45:31 -08:00
Add,
2022-11-30 15:25:07 -08:00
Mul,
Sub,
Div,
Mod,
Lt,
Gt,
2022-11-30 21:00:48 -08:00
AddFp254,
MulFp254,
SubFp254,
Byte,
Shl, // simulated with MUL
Shr, // simulated with DIV
2022-11-30 15:25:07 -08:00
}
2022-11-30 18:12:31 -08:00
impl BinaryOperator {
/// Computes the result of a binary arithmetic operation given two inputs.
2022-11-30 18:12:31 -08:00
pub(crate) fn result(&self, input0: U256, input1: U256) -> U256 {
match self {
2022-12-03 21:09:57 -08:00
BinaryOperator::Add => input0.overflowing_add(input1).0,
BinaryOperator::Mul => input0.overflowing_mul(input1).0,
BinaryOperator::Shl => {
if input0 < U256::from(256usize) {
input1 << input0
} else {
U256::zero()
}
}
2022-12-03 21:09:57 -08:00
BinaryOperator::Sub => input0.overflowing_sub(input1).0,
BinaryOperator::Div => {
2022-12-03 21:09:57 -08:00
if input1.is_zero() {
U256::zero()
} else {
input0 / input1
}
}
BinaryOperator::Shr => {
if input0 < U256::from(256usize) {
input1 >> input0
} else {
U256::zero()
}
}
2022-12-03 21:09:57 -08:00
BinaryOperator::Mod => {
if input1.is_zero() {
U256::zero()
} else {
input0 % input1
}
}
BinaryOperator::Lt => U256::from((input0 < input1) as u8),
BinaryOperator::Gt => U256::from((input0 > input1) as u8),
2022-12-27 18:42:34 -08:00
BinaryOperator::AddFp254 => addmod(input0, input1, BN_BASE),
BinaryOperator::MulFp254 => mulmod(input0, input1, BN_BASE),
BinaryOperator::SubFp254 => submod(input0, input1, BN_BASE),
BinaryOperator::Byte => {
if input0 >= 32.into() {
U256::zero()
} else {
input1.byte(31 - input0.as_usize()).into()
}
}
2022-11-30 18:12:31 -08:00
}
}
/// Maps a binary arithmetic operation to its associated flag column in the trace.
pub(crate) fn row_filter(&self) -> usize {
match self {
BinaryOperator::Add => columns::IS_ADD,
BinaryOperator::Mul => columns::IS_MUL,
BinaryOperator::Sub => columns::IS_SUB,
BinaryOperator::Div => columns::IS_DIV,
BinaryOperator::Mod => columns::IS_MOD,
BinaryOperator::Lt => columns::IS_LT,
BinaryOperator::Gt => columns::IS_GT,
BinaryOperator::AddFp254 => columns::IS_ADDFP254,
BinaryOperator::MulFp254 => columns::IS_MULFP254,
BinaryOperator::SubFp254 => columns::IS_SUBFP254,
BinaryOperator::Byte => columns::IS_BYTE,
BinaryOperator::Shl => columns::IS_SHL,
BinaryOperator::Shr => columns::IS_SHR,
2022-11-30 18:12:31 -08:00
}
}
}
/// An enum representing different ternary operations.
#[allow(clippy::enum_variant_names)]
2022-11-30 15:25:07 -08:00
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum TernaryOperator {
AddMod,
MulMod,
SubMod,
2022-11-30 15:25:07 -08:00
}
2022-11-30 18:12:31 -08:00
impl TernaryOperator {
/// Computes the result of a ternary arithmetic operation given three inputs.
2022-11-30 18:12:31 -08:00
pub(crate) fn result(&self, input0: U256, input1: U256, input2: U256) -> U256 {
match self {
2022-11-30 21:00:48 -08:00
TernaryOperator::AddMod => addmod(input0, input1, input2),
TernaryOperator::MulMod => mulmod(input0, input1, input2),
TernaryOperator::SubMod => submod(input0, input1, input2),
}
}
/// Maps a ternary arithmetic operation to its associated flag column in the trace.
pub(crate) fn row_filter(&self) -> usize {
match self {
TernaryOperator::AddMod => columns::IS_ADDMOD,
TernaryOperator::MulMod => columns::IS_MULMOD,
TernaryOperator::SubMod => columns::IS_SUBMOD,
2022-11-30 18:12:31 -08:00
}
}
}
/// An enum representing arithmetic operations that can be either binary or ternary.
2022-11-30 15:25:07 -08:00
#[derive(Debug)]
pub(crate) enum Operation {
BinaryOperation {
operator: BinaryOperator,
input0: U256,
input1: U256,
result: U256,
},
TernaryOperation {
operator: TernaryOperator,
input0: U256,
input1: U256,
input2: U256,
result: U256,
},
}
2022-11-30 18:12:31 -08:00
impl Operation {
/// Creates a binary operator with given inputs.
///
/// NB: This works as you would expect, EXCEPT for SHL and SHR,
/// whose inputs need a small amount of preprocessing. Specifically,
/// to create `SHL(shift, value)`, call (note the reversal of
/// argument order):
///
/// `Operation::binary(BinaryOperator::Shl, value, 1 << shift)`
///
/// Similarly, to create `SHR(shift, value)`, call
///
/// `Operation::binary(BinaryOperator::Shr, value, 1 << shift)`
///
/// See witness/operation.rs::append_shift() for an example (indeed
/// the only call site for such inputs).
2022-11-30 18:12:31 -08:00
pub(crate) fn binary(operator: BinaryOperator, input0: U256, input1: U256) -> Self {
let result = operator.result(input0, input1);
Self::BinaryOperation {
operator,
input0,
input1,
result,
}
}
/// Creates a ternary operator with given inputs.
2022-11-30 18:12:31 -08:00
pub(crate) fn ternary(
operator: TernaryOperator,
input0: U256,
input1: U256,
input2: U256,
) -> Self {
let result = operator.result(input0, input1, input2);
Self::TernaryOperation {
operator,
input0,
input1,
input2,
result,
}
}
/// Gets the result of an arithmetic operation.
2022-11-30 18:12:31 -08:00
pub(crate) fn result(&self) -> U256 {
match self {
Operation::BinaryOperation { result, .. } => *result,
Operation::TernaryOperation { result, .. } => *result,
}
}
/// Convert operation into one or two rows of the trace.
///
/// Morally these types should be [F; NUM_ARITH_COLUMNS], but we
/// use vectors because that's what utils::transpose (who consumes
/// the result of this function as part of the range check code)
/// expects.
///
/// The `is_simulated` bool indicates whether we use a native arithmetic
/// operation or simulate one with another. This is used to distinguish
/// SHL and SHR operations that are simulated through MUL and DIV respectively.
fn to_rows<F: PrimeField64>(&self) -> (Vec<F>, Option<Vec<F>>) {
match *self {
Operation::BinaryOperation {
operator,
input0,
input1,
result,
} => binary_op_to_rows(operator, input0, input1, result),
Operation::TernaryOperation {
operator,
input0,
input1,
input2,
result,
} => ternary_op_to_rows(operator.row_filter(), input0, input1, input2, result),
}
}
}
/// Converts a ternary arithmetic operation to one or two rows of the `ArithmeticStark` table.
fn ternary_op_to_rows<F: PrimeField64>(
row_filter: usize,
input0: U256,
input1: U256,
input2: U256,
_result: U256,
) -> (Vec<F>, Option<Vec<F>>) {
let mut row1 = vec![F::ZERO; columns::NUM_ARITH_COLUMNS];
let mut row2 = vec![F::ZERO; columns::NUM_ARITH_COLUMNS];
row1[row_filter] = F::ONE;
modular::generate(&mut row1, &mut row2, row_filter, input0, input1, input2);
(row1, Some(row2))
2022-11-30 18:12:31 -08:00
}
2022-11-30 21:00:48 -08:00
/// Converts a binary arithmetic operation to one or two rows of the `ArithmeticStark` table.
fn binary_op_to_rows<F: PrimeField64>(
op: BinaryOperator,
input0: U256,
input1: U256,
result: U256,
) -> (Vec<F>, Option<Vec<F>>) {
let mut row = vec![F::ZERO; columns::NUM_ARITH_COLUMNS];
row[op.row_filter()] = F::ONE;
match op {
BinaryOperator::Add | BinaryOperator::Sub | BinaryOperator::Lt | BinaryOperator::Gt => {
addcy::generate(&mut row, op.row_filter(), input0, input1);
(row, None)
}
BinaryOperator::Mul => {
mul::generate(&mut row, input0, input1);
(row, None)
}
BinaryOperator::Shl => {
let mut nv = vec![F::ZERO; columns::NUM_ARITH_COLUMNS];
shift::generate(&mut row, &mut nv, true, input0, input1, result);
(row, None)
}
BinaryOperator::Div | BinaryOperator::Mod => {
let mut nv = vec![F::ZERO; columns::NUM_ARITH_COLUMNS];
divmod::generate(&mut row, &mut nv, op.row_filter(), input0, input1, result);
(row, Some(nv))
}
BinaryOperator::Shr => {
let mut nv = vec![F::ZERO; columns::NUM_ARITH_COLUMNS];
shift::generate(&mut row, &mut nv, false, input0, input1, result);
(row, Some(nv))
}
BinaryOperator::AddFp254 | BinaryOperator::MulFp254 | BinaryOperator::SubFp254 => {
2023-02-13 11:31:08 -08:00
ternary_op_to_rows::<F>(op.row_filter(), input0, input1, BN_BASE, result)
}
BinaryOperator::Byte => {
byte::generate(&mut row, input0, input1);
(row, None)
}
}
2022-11-30 18:12:31 -08:00
}