From 428ac1b1ee0381f20e8fffd8b6e2cf614f47d003 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Mon, 10 Jun 2024 09:44:36 -0400 Subject: [PATCH] fix: add G2 subgroup check for `ECPAIRING` (#268) * Rewrite file and misc * Add addition on the twist * Couple tweaks * Tweak * Final tweaks * Reduce overhead and add tests * Add forgotten file * Cleanup * Minor * Add comment as per review --- .../src/cpu/kernel/aggregator.rs | 5 +- .../bn254/curve_arithmetic/curve_add.asm | 4 +- .../curve/bn254/curve_arithmetic/pairing.asm | 9 +- .../curve_arithmetic/twisted_curve_add.asm | 197 ++++++++++++++++++ ...ted_curve.asm => twisted_curve_checks.asm} | 49 +++++ .../twisted_curve_endomorphism.asm | 28 +++ .../curve_arithmetic/twisted_curve_mul.asm | 84 ++++++++ .../curve/bn254/field_arithmetic/inverse.asm | 35 ++++ .../asm/curve/bn254/field_arithmetic/util.asm | 120 ++++++++++- .../src/cpu/kernel/asm/curve/common.asm | 9 + .../src/cpu/kernel/asm/rlp/num_bytes.asm | 22 +- .../src/cpu/kernel/constants/mod.rs | 18 +- .../src/cpu/kernel/tests/bn254.rs | 98 ++++++++- evm_arithmetization/src/extension_tower.rs | 2 +- 14 files changed, 663 insertions(+), 17 deletions(-) create mode 100644 evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve_add.asm rename evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/{twisted_curve.asm => twisted_curve_checks.asm} (65%) create mode 100644 evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve_endomorphism.asm create mode 100644 evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve_mul.asm diff --git a/evm_arithmetization/src/cpu/kernel/aggregator.rs b/evm_arithmetization/src/cpu/kernel/aggregator.rs index 637655255..b6b6b293c 100644 --- a/evm_arithmetization/src/cpu/kernel/aggregator.rs +++ b/evm_arithmetization/src/cpu/kernel/aggregator.rs @@ -64,7 +64,10 @@ pub(crate) fn combined_kernel() -> Kernel { include_str!("asm/curve/bn254/curve_arithmetic/msm.asm"), include_str!("asm/curve/bn254/curve_arithmetic/pairing.asm"), include_str!("asm/curve/bn254/curve_arithmetic/precomputation.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/twisted_curve.asm"), + include_str!("asm/curve/bn254/curve_arithmetic/twisted_curve_add.asm"), + include_str!("asm/curve/bn254/curve_arithmetic/twisted_curve_checks.asm"), + include_str!("asm/curve/bn254/curve_arithmetic/twisted_curve_endomorphism.asm"), + include_str!("asm/curve/bn254/curve_arithmetic/twisted_curve_mul.asm"), include_str!("asm/curve/bn254/field_arithmetic/degree_6_mul.asm"), include_str!("asm/curve/bn254/field_arithmetic/degree_12_mul.asm"), include_str!("asm/curve/bn254/field_arithmetic/frobenius.asm"), diff --git a/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_add.asm b/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_add.asm index a43c4047d..98ded41f7 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_add.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_add.asm @@ -10,12 +10,12 @@ global bn_add: %bn_check // stack: isValid(x0, y0), x0, y0, x1, y1, retdest DUP5 - // stack: x1, isValid(x0, y0), x0, y0, x1, y1, retdest + // stack: y1, isValid(x0, y0), x0, y0, x1, y1, retdest DUP5 // stack: x1, y1, isValid(x0, y0), x0, y0, x1, y1, retdest %bn_check // stack: isValid(x1, y1), isValid(x0, y0), x0, y0, x1, y1, retdest - AND + MUL // Cheaper than AND // stack: isValid(x1, y1) & isValid(x0, y0), x0, y0, x1, y1, retdest %jumpi(bn_add_valid_points) // stack: x0, y0, x1, y1, retdest diff --git a/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/pairing.asm b/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/pairing.asm index 735d001aa..4517f7cb7 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/pairing.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/pairing.asm @@ -68,9 +68,16 @@ bn254_input_check: // stack: inp_j', inp_j, j, k, inp %load_fp254_4 // stack: Q_j, inp_j, j, k, inp + %dup_bn_g2 + // stack: Q_j, Q_j, inp_j, j, k, inp %bn_check_twisted - // stack: valid?, inp_j, j, k, inp ISZERO + // stack: valid_1?, Q_j, inp_j, j, k, inp + %stack (b, Q: 4) -> (Q, b) + %bn_check_twisted_subgroup + ISZERO + // stack: valid_2?, valid_1?, inp_j, j, k, inp + ADD // Cheaper than OR %jumpi(bn_pairing_invalid_input) // stack: inp_j, j, k, inp POP diff --git a/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve_add.asm b/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve_add.asm new file mode 100644 index 000000000..b4ff7a3eb --- /dev/null +++ b/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve_add.asm @@ -0,0 +1,197 @@ +// Elliptic curve addition on the twist of BN254 curve. +// Assumption: (X0,Y0) is a valid point. +// Uses the standard affine addition formula. +global bn_twisted_add: + // stack: X0: 2, Y0: 2, X1: 2, Y1: 2, retdest + + // Check if the first point is the identity. + %dup_fp254_2_2 + // stack: Y0, X0, Y0, X1, Y1, retdest + %dup_fp254_2_2 + // stack: X0, Y0, X0, Y0, X1, Y1, retdest + %bn_check_twisted_ident + // stack: (X0,Y0)==(0,0), X0, Y0, X1, Y1, retdest + %jumpi(bn_twisted_add_fst_zero) + // stack: X0, Y0, X1, Y1, retdest + + // Check if the second point is the identity. + %dup_fp254_2_6 + // stack: Y1, X0, Y0, X1, Y1, retdest + %dup_fp254_2_6 + // stack: X1, Y1, X0, Y0, X1, Y1, retdest + %bn_check_twisted_ident + // stack: (X1,Y1)==(0,0), X0, Y0, X1, Y1, retdest + %jumpi(bn_twisted_add_snd_zero) + // stack: X0, Y0, X1, Y1, retdest + + // Check if both points have the same X-coordinate. + %dup_fp254_2_4 + // stack: X1, X0, Y0, X1, Y1, retdest + %dup_fp254_2_2 + // stack: X0, X1, X0, Y0, X1, Y1, retdest + %eq_fp254_2 + // stack: X0 == X1, X0, Y0, X1, Y1, retdest + %jumpi(bn_twisted_add_equal_first_coord) + // stack: X0, Y0, X1, Y1, retdest + + // Otherwise, we can use the standard formula. + // Compute lambda = (Y0 - Y1)/(X0 - X1) + %dup_fp254_2_6 + // stack: Y1, X0, Y0, X1, Y1, retdest + %dup_fp254_2_4 + // stack: Y0, Y1, X0, Y0, X1, Y1, retdest + %sub_fp254_2 + // stack: Y0 - Y1, X0, Y0, X1, Y1, retdest + %dup_fp254_2_6 + // stack: X1, Y0 - Y1, X0, Y0, X1, Y1, retdest + %dup_fp254_2_4 + // stack: X0, X1, Y0 - Y1, X0, Y0, X1, Y1, retdest + %sub_fp254_2 + // stack: X0 - X1, Y0 - Y1, X0, Y0, X1, Y1, retdest + %divr_fp254_2 + // stack: lambda, X0, Y0, X1, Y1, retdest + %jump(bn_twisted_add_valid_points_with_lambda) + +// BN254 twisted elliptic curve addition. +// Assumption: (X0,Y0) == (0,0) +bn_twisted_add_fst_zero: + // stack: X0: 2, Y0: 2, X1: 2, Y1: 2, retdest + // Just return (X1, Y1) + %stack (X0: 2, Y0: 2, X1: 2, Y1: 2, retdest) -> (retdest, X1, Y1) + JUMP + +// BN254 twisted elliptic curve addition. +// Assumption: (X1,Y1) == (0,0) +bn_twisted_add_snd_zero: + // stack: X0: 2, Y0: 2, X1: 2, Y1: 2, retdest + + // Just return (X0,Y0) + %stack (X0: 2, Y0: 2, X1: 2, Y1: 2, retdest) -> (retdest, X0, Y0) + JUMP + +// BN254 twisted elliptic curve addition. +// Assumption: lambda = (Y0 - Y1)/(X0 - X1) +bn_twisted_add_valid_points_with_lambda: + // stack: lambda: 2, X0: 2, Y0: 2, X1: 2, Y1: 2, retdest + + // Compute X2 = lambda^2 - X1 - X0 + %dup_fp254_2_2 + // stack: X0, lambda, X0, Y0, X1, Y1, retdest + %dup_fp254_2_8 + // stack: X1, X0, lambda, X0, Y0, X1, Y1, retdest + %dup_fp254_2_4 + // stack: lambda, X1, X0, lambda, X0, Y0, X1, Y1, retdest + %dup_fp254_2_0 + // stack: lambda, lambda, X1, X0, lambda, X0, Y0, X1, Y1, retdest + %mul_fp254_2 + // stack: lambda^2, X1, X0, lambda, X0, Y0, X1, Y1, retdest + %sub_fp254_2 + // stack: lambda^2 - X1, X0, lambda, X0, Y0, X1, Y1, retdest + %sub_fp254_2 + // stack: X2, lambda, X0, Y0, X1, Y1, retdest + + // Compute Y2 = lambda*(X1 - X2) - Y1 + %dup_fp254_2_0 + // stack: X2, X2, lambda, X0, Y0, X1, Y1, retdest + %dup_fp254_2_10 + // stack: X1, X2, X2, lambda, X0, Y0, X1, Y1, retdest + %sub_fp254_2 + // stack: X1 - X2, X2, lambda, X0, Y0, X1, Y1, retdest + %dup_fp254_2_4 + // stack: lambda, X1 - X2, X2, lambda, X0, Y0, X1, Y1, retdest + %mul_fp254_2 + // stack: lambda * (X1 - X2), X2, lambda, X0, Y0, X1, Y1, retdest + %dup_fp254_2_12 + // stack: Y1, lambda * (X1 - X2), X2, lambda, X0, Y0, X1, Y1, retdest + %stack (Y1: 2, T: 2) -> (T, Y1) + // stack: lambda * (X1 - X2), Y1, X2, lambda, X0, Y0, X1, Y1, retdest + %sub_fp254_2 + // stack: Y2, X2, lambda, X0, Y0, X1, Y1, retdest + + // Return X2, Y2 + %stack (Y2: 2, X2: 2, lambda: 2, X0: 2, Y0: 2, X1: 2, Y1: 2, retdest) -> (retdest, X2, Y2) + JUMP + +// BN254 twisted elliptic curve addition. +// Assumption: (X0,Y0) and (X1,Y1) are valid points and X0 == X1 +bn_twisted_add_equal_first_coord: + // stack: X0, Y0, X1, Y1, retdest with X0 == X1 + + // Check if the points are equal + %dup_fp254_2_2 + // stack: Y0, X0, Y0, X1, Y1, retdest + %dup_fp254_2_8 + // stack: Y1, Y0, X0, Y0, X1, Y1, retdest + %eq_fp254_2 + // stack: Y1 == Y0, X0, Y0, X1, Y1, retdest + %jumpi(bn_twisted_add_equal_points) + // stack: X0, Y0, X1, Y1, retdest + + // Otherwise, one is the negation of the other so we can return the identity. + %stack (garbage: 8, retdest) -> (retdest, 0, 0, 0, 0) + // stack: retdest, X=0, Y=0 + JUMP + + +// BN254 twisted elliptic curve addition. +// Assumption: X0 == X1 and Y0 == Y1 +// Standard doubling formula. +bn_twisted_add_equal_points: + // stack: X0, Y0, X1, Y1, retdest + + // Compute lambda = 3/2 * X0^2 / Y0 + %dup_fp254_2_0 + // stack: X0, X0, Y0, X1, Y1, retdest + %dup_fp254_2_0 + // stack: X0, X0, X0, Y0, X1, Y1, retdest + %mul_fp254_2 + // stack: X0^2, X0, Y0, X1, Y1, retdest + PUSH 0X183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea5 // 3/2 in the base field + // stack: 3/2, X0^2, X0, Y0, X1, Y1, retdest + %scale_fp254_2 + // stack: 3/2 * X0^2, X0, Y0, X1, Y1, retdest + %dup_fp254_2_4 + // stack: Y0, 3/2 * X0^2, X0, Y0, X1, Y1, retdest + %divr_fp254_2 + // stack: lambda, X0, Y0, X1, Y1, retdest + %jump(bn_twisted_add_valid_points_with_lambda) + +// BN254 twisted elliptic curve doubling. +// Assumption: (X0,Y0) is a valid point. +// Standard doubling formula. +global bn_twisted_double: + // stack: X, Y, retdest + %dup_bn_g2 + // stack: X, Y, X, Y, retdest + %bn_check_twisted_ident + // stack: (X,Y)==(0,0), X, Y, retdest + %jumpi(ec_twisted_double_retself) + %dup_bn_g2 + // stack: X, Y, X, Y, retdest + %jump(bn_twisted_add_equal_points) + +// Convenience macro to call bn_twisted_add and return where we left off. +%macro bn_twisted_add + %stack (X0: 2, Y0: 2, X1: 2, Y1: 2) -> (X0, Y0, X1, Y1, %%after) + %jump(bn_twisted_add) +%%after: +%endmacro + +%macro bn_twisted_sub + // stack: X0: 2, Y0: 2, X1: 2, Y1: 2 + %swap_fp254_2_4 + // stack: Y1, Y0, X1, X0 + PUSH 0 PUSH 0 + %sub_fp254_2 + // stack: -Y1, Y0, X1, X0 + %stack (Y1: 2, Y0: 2, X1: 2, X0: 2) -> (X0, Y0, X1, Y1, %%after) + %jump(bn_twisted_add) +%%after: +%endmacro + +// Convenience macro to call bn_twisted_double and return where we left off. +%macro bn_twisted_double + %stack (X: 2, Y: 2) -> (X, Y, %%after) + %jump(bn_twisted_double) +%%after: +%endmacro diff --git a/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve.asm b/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve_checks.asm similarity index 65% rename from evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve.asm rename to evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve_checks.asm index 859c45fe3..bbd7d06cf 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve_checks.asm @@ -92,3 +92,52 @@ MUL // Cheaper than AND MUL // Cheaper than AND %endmacro + +/// The `ECPAIRING` precompile requires checking that G2 +/// inputs are on the correct prime-order subgroup. +/// This macro performs this check, based on the algorithm +/// detailed in . +%macro bn_check_twisted_subgroup + // stack: Q = (X, Y) + %dup_bn_g2 + // stack: Q, Q + %bn_twisted_mul_by_z + // stack: zQ, Q + %dup_bn_g2 + // stack: zQ, zQ, Q + %swap_bn_g2_2 + // stack: Q, zQ, zQ + %bn_twisted_add + // stack: [z+1]Q, zQ + %swap_bn_g2 + // stack: zQ, [z+1]Q + %bn_endomorphism + // stack: phi(zQ), [z+1]Q + %dup_bn_g2 + // stack: phi(zQ), phi(zQ), [z+1]Q + %bn_endomorphism + // stack: phi^2(zQ), phi(zQ), [z+1]Q + %dup_bn_g2 + // stack: phi^2(zQ), phi^2(zQ), phi(zQ), [z+1]Q + %bn_endomorphism + // stack: phi^3(zQ), phi^2(zQ), phi(zQ), [z+1]Q + %bn_twisted_double + // stack: phi^3([2z]Q), phi^2(zQ), phi(zQ), [z+1]Q + %bn_twisted_sub + // stack: phi^3([2z]Q) - phi^2(zQ), phi(zQ), [z+1]Q + %bn_twisted_sub + // stack: phi^3([2z]Q) - phi^2(zQ) - phi(zQ), [z+1]Q + %bn_twisted_sub + // stack: phi^3([2z]Q) - phi^2(zQ) - phi(zQ) - [z+1]Q + %bn_check_twisted_ident + // stack: is_ident +%endmacro + +// Return [(u256::MAX, u256::MAX), (u256::MAX, u256::MAX)] which is used to indicate the input was invalid. +%macro bn_twisted_invalid_input + // stack: retdest + PUSH @U256_MAX + // stack: u256::MAX, retdest + %stack (max, retdest) -> (retdest, max, max, max, max) + JUMP +%endmacro \ No newline at end of file diff --git a/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve_endomorphism.asm b/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve_endomorphism.asm new file mode 100644 index 000000000..dfe819ef8 --- /dev/null +++ b/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve_endomorphism.asm @@ -0,0 +1,28 @@ +// Implementation of the BN254 twist endomorphism. + +/// Frobenius map over BN254 quadratic extension. +%macro frob_fp254_2 + // stack: X = (x, x_) + %conj_fp254_2 + // stack: frob(X) +%endmacro + +%macro bn_endomorphism + // stack: X: 2, Y: 2 + %frob_fp254_2 + // stack: X', Y + %swap_fp254_2 + // stack: Y, X' + %frob_fp254_2 + // stack: Y', X' + PUSH @BN_ENDO_Y_COORD_IM + PUSH @BN_ENDO_Y_COORD_RE + %mul_fp254_2 + // stack: φ_y.Y', X' + %swap_fp254_2 + // stack: X', φ_y.Y' + PUSH @BN_ENDO_X_COORD_IM + PUSH @BN_ENDO_X_COORD_RE + %mul_fp254_2 + // stack: φ_x.X', φ_y.Y' +%endmacro diff --git a/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve_mul.asm b/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve_mul.asm new file mode 100644 index 000000000..01586b069 --- /dev/null +++ b/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve_mul.asm @@ -0,0 +1,84 @@ +// BN254 elliptic curve scalar multiplication on the twist. +// Uses the naive algorithm. +global bn_twisted_mul: + // stack: X: 2, Y: 2, s, retdest + %dup_bn_g2 + // stack: X, Y, X, Y, s, retdest + %bn_check_twisted_ident + // stack: (X,Y)==(0,0), X, Y, s, retdest + %jumpi(ret_zero_ec_twisted_mul) + // stack: X, Y, s, retdest + %dup_bn_g2 + // stack: X, Y, X, Y, s, retdest + %bn_check_twisted + // stack: isValid(X, Y), X, Y, s, retdest + %jumpi(bn_twisted_mul_valid_point) + // stack: X, Y, s, retdest + %pop5 + %bn_twisted_invalid_input + +bn_twisted_mul_valid_point: + // stack: X, Y, s, retdest + DUP5 + %num_bits + // stack: n, X, Y, s, retdest + %stack (n, X: 2, Y: 2, s, retdest) -> (X, Y, s, n, retdest) + %rep 4 + PUSH 0 // identity point + %endrep +bn_twisted_mul_loop: + // stack: X', Y', X, Y, s, n, retdest + DUP10 + ISZERO + %jumpi(bn_twisted_mul_end) + // stack: X1, Y1, X, Y, s, n, retdest + %bn_twisted_double + // stack: X2, Y2, X, Y, s, n, retdest + DUP9 + // stack: s, X2, Y2, X, Y, s, n, retdest + PUSH 1 DUP12 SUB + // stack: n - 1, s, X2, Y2, X, Y, s, n, retdest + SHR + // stack: s >> n - 1, X2, Y2, X, Y, s, n, retdest + PUSH 1 + AND + // stack: nth_bit, X2, Y2, X, Y, s, n, retdest + %jumpi(bn_twisted_mul_add_base) + // stack: X2, Y2, X, Y, s, n, retdest + SWAP9 + %decrement + SWAP9 + // stack: X2, Y2, X, Y, s, n-1, retdest + %jump(bn_twisted_mul_loop) + +bn_twisted_mul_add_base: + // stack: X2, Y2, X, Y, s, n, retdest + %dup_fp254_2_6 + // stack: Y, X2, Y2, X, Y, s, n, retdest + %dup_fp254_2_6 + // stack: X, Y, X2, Y2, X, Y, s, n, retdest + %bn_twisted_add + // stack: X3, Y3, X, Y, s, n, retdest + SWAP9 + %decrement + SWAP9 + // stack: X3, Y3, X, Y, s, n-1, retdest + %jump(bn_twisted_mul_loop) + +bn_twisted_mul_end: + %stack (AX: 2, AY: 2, X: 2, Y: 2, s, n, retdest) -> (retdest, AX, AY) + JUMP + +// Convenience macro to call bn_twisted_mul and return where we left off. +%macro bn_twisted_mul + %stack (X: 2, Y: 2, s) -> (X, Y, s, %%after) + %jump(bn_twisted_mul) +%%after: +%endmacro + +// Convenience macro to call bn_twisted_mul_by_z and return where we left off. +%macro bn_twisted_mul_by_z + %stack (X: 2, Y: 2) -> (X, Y, 0x44e992b44a6909f1, %%after) + %jump(bn_twisted_mul) +%%after: +%endmacro diff --git a/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/field_arithmetic/inverse.asm b/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/field_arithmetic/inverse.asm index 7c7729057..1c07a15dc 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/field_arithmetic/inverse.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/field_arithmetic/inverse.asm @@ -24,6 +24,41 @@ %endmacro +// Returns reverse order division Y/X, modulo N, in Fp2. +%macro divr_fp254_2 + // stack: X, Y + %inv_fp254_2 + // stack: X^-1, Y + %mul_fp254_2 +%endmacro + +// The inverse of an element X in BN254 quadratic extension field +// is just X'/||X||^2 since ||X||^2 = XX', where X' = conj(X). +%macro inv_fp254_2 + // stack: X = (x, x_) + %dup_fp254_2_0 + // stack: x, x_, x, x_ + DUP1 + // stack: x, x, x_, x, x_ + MULFP254 + // stack: x^2, x_, x, x_ + SWAP1 + // stack: x_, x^2, x, x_ + DUP1 + // stack: x_, x_, x^2, x, x_ + MULFP254 + // stack: x_^2, x^2, x, x_ + ADDFP254 + // stack: ||X||^2, x, x_ + %inv_fp254 + // stack: inv, x, x_ + %scale_fp254_2 + // stack: X/||X||^2 + %conj_fp254_2 + // stack: Y = 1/X +%endmacro + + global inv_fp254_12: // stack: inp, out, retdest %prover_inv_fp254_12 diff --git a/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/field_arithmetic/util.asm b/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/field_arithmetic/util.asm index 897404dbf..b77cace48 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/field_arithmetic/util.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/curve/bn254/field_arithmetic/util.asm @@ -111,6 +111,20 @@ // stack: z, z_ %endmacro +%macro sub_fp254_2 + // stack: x, x_, y, y_ + SWAP3 + // stack: y_, x_, y, x + SWAP1 + // stack: x_, y_, y, x + SUBFP254 + // stack: z_, y, x + SWAP2 + // stack: x, y, z_ + SUBFP254 + // stack: z, z_ +%endmacro + /// Given z = x + iy: Fp254_2, return complex conjugate z': Fp254_2 /// where input is represented z.re, z.im and output as z'.im, z'.re /// cost: 9; note this returns y, x for the output x + yi @@ -364,6 +378,108 @@ // stack: %endmacro +// cost: 2 +%macro dup_fp254_2_0 + // stack: f: 2 + DUP2 + DUP2 + // stack: f: 2, f: 2 +%endmacro + +// cost: 2 +%macro dup_fp254_2_2 + // stack: X: 2, f: 2 + DUP4 + DUP4 + // stack: f: 2, X: 2, f: 2 +%endmacro + +// cost: 2 +%macro dup_fp254_2_4 + // stack: X: 4, f: 2 + DUP6 + DUP6 + // stack: f: 2, X: 4, f: 2 +%endmacro + +// cost: 2 +%macro dup_fp254_2_6 + // stack: X: 6, f: 2 + DUP8 + DUP8 + // stack: f: 2, X: 6, f: 2 +%endmacro + +// cost: 2 +%macro dup_fp254_2_7 + // stack: X: 7, f: 2 + DUP9 + DUP9 + // stack: f: 2, X: 7, f: 2 +%endmacro + +// cost: 2 +%macro dup_fp254_2_8 + // stack: X: 8, f: 2 + DUP10 + DUP10 + // stack: f: 2, X: 8, f: 2 +%endmacro + +// cost: 2 +%macro dup_fp254_2_10 + // stack: X: 10, f: 2 + DUP12 + DUP12 + // stack: f: 2, X: 10, f: 2 +%endmacro + +// cost: 2 +%macro dup_fp254_2_12 + // stack: X: 12, f: 2 + DUP14 + DUP14 + // stack: f: 2, X: 12, f: 2 +%endmacro + +// cost: 4 +%macro dup_bn_g2 + // stack: X: 2, Y: 2 + %dup_fp254_2_2 + %dup_fp254_2_2 + // stack: X: 2, Y: 2, X: 2, Y: 2 +%endmacro + +%macro swap_bn_g2 + // stack: P: 4, Q: 4 + %stack (P: 4, Q: 4) -> (Q, P) + // stack: Q: 4, P: 4 +%endmacro + +%macro swap_bn_g2_2 + // stack: P: 4, T: 4, Q: 4 + %stack (P: 4, T: 4, Q: 4) -> (Q, T, P) + // stack: Q: 4, T: 4, P: 4 +%endmacro + +%macro swap_fp254_2 + // stack: X: 2, Y: 2 + %stack (x, x_, y, y_) -> (y, y_, x, x_) + // stack: Y: 2, X: 2 +%endmacro + +%macro swap_fp254_2_2 + // stack: X: 2, T: 2, Y: 2 + %stack (x, x_, t, t_, y, y_) -> (y, y_, t, t_, x, x_) + // stack: Y: 2, T: 2, X: 2 +%endmacro + +%macro swap_fp254_2_4 + // stack: X: 2, T: 4, Y: 2 + %stack (x, x_, t0, t1, t2, t3, y, y_) -> (y, y_, t0, t1, t2, t3, x, x_) + // stack: Y: 2, T: 4, X: 2 +%endmacro + // cost: 6 %macro dup_fp254_6_0 // stack: f: 6 @@ -374,7 +490,7 @@ DUP6 DUP6 // stack: f: 6, f: 6 -%endmacro +%endmacro // cost: 6 %macro dup_fp254_6_2 @@ -386,7 +502,7 @@ DUP8 DUP8 // stack: f: 6, X: 2, f: 6 -%endmacro +%endmacro // cost: 6 %macro dup_fp254_6_6 diff --git a/evm_arithmetization/src/cpu/kernel/asm/curve/common.asm b/evm_arithmetization/src/cpu/kernel/asm/curve/common.asm index 50f174fac..2899a652a 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/curve/common.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/curve/common.asm @@ -10,10 +10,19 @@ global ret_zero_ec_mul: // stack: retdest, 0, 0 JUMP +global ret_zero_ec_twisted_mul: + // stack: X: 2, Y: 2, s, retdest + %stack (garbage: 5, retdest) -> (retdest, 0, 0, 0, 0) + JUMP + global ec_double_retself: %stack (x, y, retdest) -> (retdest, x, y) JUMP +global ec_twisted_double_retself: + %stack (X: 2, Y: 2, retdest) -> (retdest, X, Y) + JUMP + // Check if (x,y)==(0,0) %macro ec_isidentity // stack: x, y diff --git a/evm_arithmetization/src/cpu/kernel/asm/rlp/num_bytes.asm b/evm_arithmetization/src/cpu/kernel/asm/rlp/num_bytes.asm index de0a7ca96..b1f441fe3 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/rlp/num_bytes.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/rlp/num_bytes.asm @@ -3,13 +3,9 @@ global num_bytes: // stack: x, retdest DUP1 ISZERO %jumpi(return_1) - // Non-deterministically guess the number of bits - PROVER_INPUT(num_bits) - %stack(num_bits, x) -> (num_bits, 1, x, num_bits) - SUB - SHR - // stack: 1, num_bits - %assert_eq_const(1) + // stack: x, retdest + %num_bits + // stack: num_bits, retdest // convert number of bits to number of bytes %add_const(7) %shr_const(3) @@ -28,3 +24,15 @@ return_1: %jump(num_bytes) %%after: %endmacro + +%macro num_bits + // Non-deterministically guess the number of bits + // stack: x + PROVER_INPUT(num_bits) + %stack (num_bits, x) -> (num_bits, x, num_bits) + %decrement + SHR + // stack: 1, num_bits + %assert_eq_const(1) + // stack: num_bits +%endmacro diff --git a/evm_arithmetization/src/cpu/kernel/constants/mod.rs b/evm_arithmetization/src/cpu/kernel/constants/mod.rs index e4e9b04ef..f61686559 100644 --- a/evm_arithmetization/src/cpu/kernel/constants/mod.rs +++ b/evm_arithmetization/src/cpu/kernel/constants/mod.rs @@ -129,7 +129,7 @@ const HASH_CONSTANTS: [(&str, [u8; 32]); 2] = [ ), ]; -const EC_CONSTANTS: [(&str, [u8; 32]); 20] = [ +const EC_CONSTANTS: [(&str, [u8; 32]); 24] = [ ( "U256_MAX", hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), @@ -146,6 +146,22 @@ const EC_CONSTANTS: [(&str, [u8; 32]); 20] = [ "BN_TWISTED_IM", hex!("009713b03af0fed4cd2cafadeed8fdf4a74fa084e52d1852e4a2bd0685c315d2"), ), + ( + "BN_ENDO_X_COORD_RE", + hex!("2fb347984f7911f74c0bec3cf559b143b78cc310c2c3330c99e39557176f553d"), + ), + ( + "BN_ENDO_X_COORD_IM", + hex!("16c9e55061ebae204ba4cc8bd75a079432ae2a1d0b7c9dce1665d51c640fcba2"), + ), + ( + "BN_ENDO_Y_COORD_RE", + hex!("063cf305489af5dcdc5ec698b6e2f9b9dbaae0eda9c95998dc54014671a0135a"), + ), + ( + "BN_ENDO_Y_COORD_IM", + hex!("07c03cbcac41049a0704b5a7ec796f2b21807dc98fa25bd282d37f632623b0e3"), + ), ( "BN_SCALAR", hex!("30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"), diff --git a/evm_arithmetization/src/cpu/kernel/tests/bn254.rs b/evm_arithmetization/src/cpu/kernel/tests/bn254.rs index f6fe90553..3887171d1 100644 --- a/evm_arithmetization/src/cpu/kernel/tests/bn254.rs +++ b/evm_arithmetization/src/cpu/kernel/tests/bn254.rs @@ -1,15 +1,18 @@ use anyhow::Result; +use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use ethereum_types::U256; +use hex_literal::hex; use plonky2::field::goldilocks_field::GoldilocksField as F; use rand::Rng; use super::{run_interpreter_with_memory, InterpreterMemoryInitialization}; -use crate::cpu::kernel::interpreter::Interpreter; +use crate::cpu::kernel::aggregator::KERNEL; +use crate::cpu::kernel::interpreter::{self, Interpreter}; use crate::curve_pairings::{ bn_final_exponent, bn_miller_loop, gen_bn_fp12_sparse, Curve, CyclicGroup, }; use crate::extension_tower::{FieldExt, Fp12, Fp2, Fp6, Stack, BN254}; -use crate::memory::segments::Segment::BnPairing; +use crate::memory::segments::Segment::{self, BnPairing}; fn run_bn_mul_fp6(f: Fp6, g: Fp6, label: &str) -> Fp6 { let mut stack = f.to_stack(); @@ -250,3 +253,94 @@ fn test_bn_pairing() -> Result<()> { assert_eq!(interpreter.stack()[0], U256::one()); Ok(()) } + +fn run_bn_g2_op(p: Curve>, q: Curve>, label: &str) -> Curve> { + let mut stack = p.to_stack(); + if label == "bn_twisted_add" { + stack.extend(&q.to_stack()); + } + stack.push(U256::from(0xdeadbeefu32)); + let setup = InterpreterMemoryInitialization { + label: label.to_string(), + stack, + segment: BnPairing, + memory: vec![], + }; + let interpreter = run_interpreter_with_memory::(setup).unwrap(); + let output: Vec = interpreter.stack().iter().rev().cloned().collect(); + Curve::>::from_stack(&output) +} + +#[test] +fn test_bn_g2_ops() -> Result<()> { + let mut rng = rand::thread_rng(); + let p: Curve> = rng.gen::>>(); + let q: Curve> = rng.gen::>>(); + + let output_add: Curve> = run_bn_g2_op(p, q, "bn_twisted_add"); + let output_double: Curve> = run_bn_g2_op(p, p, "bn_twisted_double"); + + assert_eq!(output_add, p + q); + assert_eq!(output_double, p + p); + + let unit = Curve::>::unit(); + let output_add_unit: Curve> = run_bn_g2_op(unit, unit, "bn_twisted_add"); + let output_double_unit: Curve> = run_bn_g2_op(unit, unit, "bn_twisted_double"); + + assert_eq!(unit, output_add_unit); + assert_eq!(unit, output_double_unit); + + let output_add_with_unit_left: Curve> = run_bn_g2_op(unit, p, "bn_twisted_add"); + let output_add_with_unit_right: Curve> = run_bn_g2_op(p, unit, "bn_twisted_add"); + + assert_eq!(p, output_add_with_unit_left); + assert_eq!(p, output_add_with_unit_right); + + Ok(()) +} + +/// Test cases taken from . +const ECPAIRING_PRECOMPILE_INVALID_INPUTS: [[u8; 192]; 4] = [ + // invalid_g1_point + hex!("30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa"), + // invalid_g2_point + hex!("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffff0000000000000000ffffffffffffffffffff"), + // invalid_g2_subgroup + hex!("000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000000000000000000000000000000000000000000000000000000000020833e47a2eaa8bbe12d33b2da1a4fa8d763f5c567fe0da6c5c9da2e246f2096f28dc125bf7443bc1826c69fe4c7bf30c26ec60882350e784c4848c822726eb43"), + // invalid_g2_subgroup + hex!("111f95e1632a3624dd29bbc012e6462b7836eb9c80e281b9381e103aebe632372b38b76d492b3af692eb99d03cd8dcfd8a8c3a6e4a161037c42f542af5564c41198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a8305b993046905746641a19b500ebbbd30cf0068a845bfbee9de55b8fe57d1dee8243ef33537f73ef4ace4279d86344d93a5dc8c20c69045865c0fa3b924933879"), +]; + +#[test] +fn test_ecpairing_precompile_invalid_input() -> Result<()> { + init_logger(); + + let pairing_label = KERNEL.global_labels["bn254_pairing"]; + let mut stack = vec![1.into(), 0.into(), 100.into(), U256::from(0xdeadbeefu32)]; // k, inp, out, retdest + stack.reverse(); + + for bytes in ECPAIRING_PRECOMPILE_INVALID_INPUTS.iter() { + let mut interpreter: Interpreter = Interpreter::new(pairing_label, stack.clone()); + let preloaded_memory = vec![ + U256::from_big_endian(&bytes[0..32]), // Px + U256::from_big_endian(&bytes[32..64]), // Py + U256::from_big_endian(&bytes[64..96]), // Qx_re + U256::from_big_endian(&bytes[96..128]), // Qx_im + U256::from_big_endian(&bytes[128..160]), // Qy_re + U256::from_big_endian(&bytes[160..192]), // Qy_im + ]; + interpreter.set_memory_segment(Segment::BnPairing, preloaded_memory); + interpreter.run().unwrap(); + + let mut post_stack = interpreter.stack(); + assert!(post_stack.len() == 1); + + assert_eq!(post_stack[0], U256::MAX); // invalid inputs + } + + Ok(()) +} + +fn init_logger() { + let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "debug")); +} diff --git a/evm_arithmetization/src/extension_tower.rs b/evm_arithmetization/src/extension_tower.rs index 3f51235f9..ea178f305 100644 --- a/evm_arithmetization/src/extension_tower.rs +++ b/evm_arithmetization/src/extension_tower.rs @@ -341,7 +341,7 @@ impl Fp2 { /// z -> z^p /// since p == 3 mod 4 and hence /// i^p = i^(4k) * i^3 = 1*(-i) = -i - fn conj(self) -> Self { + pub(crate) fn conj(self) -> Self { Fp2 { re: self.re, im: -self.im,